1 // Copyright 2020 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/ic/unary-op-assembler.h"
6
7 #include "src/common/globals.h"
8
9 namespace v8 {
10 namespace internal {
11
12 namespace {
13
14 class UnaryOpAssemblerImpl final : public CodeStubAssembler {
15 public:
UnaryOpAssemblerImpl(compiler::CodeAssemblerState * state)16 explicit UnaryOpAssemblerImpl(compiler::CodeAssemblerState* state)
17 : CodeStubAssembler(state) {}
18
BitwiseNot(TNode<Context> context,TNode<Object> value,TNode<UintPtrT> slot,TNode<HeapObject> maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode)19 TNode<Object> BitwiseNot(TNode<Context> context, TNode<Object> value,
20 TNode<UintPtrT> slot,
21 TNode<HeapObject> maybe_feedback_vector,
22 UpdateFeedbackMode update_feedback_mode) {
23 // TODO(jgruber): Make this implementation more consistent with other unary
24 // ops (i.e. have them all use UnaryOpWithFeedback or some other common
25 // mechanism).
26 TVARIABLE(Word32T, var_word32);
27 TVARIABLE(Smi, var_feedback);
28 TVARIABLE(BigInt, var_bigint);
29 TVARIABLE(Object, var_result);
30 Label if_number(this), if_bigint(this, Label::kDeferred), out(this);
31 TaggedToWord32OrBigIntWithFeedback(context, value, &if_number, &var_word32,
32 &if_bigint, &var_bigint, &var_feedback);
33
34 // Number case.
35 BIND(&if_number);
36 var_result =
37 ChangeInt32ToTagged(Signed(Word32BitwiseNot(var_word32.value())));
38 TNode<Smi> result_type = SelectSmiConstant(
39 TaggedIsSmi(var_result.value()), BinaryOperationFeedback::kSignedSmall,
40 BinaryOperationFeedback::kNumber);
41 UpdateFeedback(SmiOr(result_type, var_feedback.value()),
42 maybe_feedback_vector, slot, update_feedback_mode);
43 Goto(&out);
44
45 // BigInt case.
46 BIND(&if_bigint);
47 UpdateFeedback(SmiConstant(BinaryOperationFeedback::kBigInt),
48 maybe_feedback_vector, slot, update_feedback_mode);
49 var_result =
50 CallRuntime(Runtime::kBigIntUnaryOp, context, var_bigint.value(),
51 SmiConstant(Operation::kBitwiseNot));
52 Goto(&out);
53
54 BIND(&out);
55 return var_result.value();
56 }
57
Decrement(TNode<Context> context,TNode<Object> value,TNode<UintPtrT> slot,TNode<HeapObject> maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode)58 TNode<Object> Decrement(TNode<Context> context, TNode<Object> value,
59 TNode<UintPtrT> slot,
60 TNode<HeapObject> maybe_feedback_vector,
61 UpdateFeedbackMode update_feedback_mode) {
62 return IncrementOrDecrement<Operation::kDecrement>(
63 context, value, slot, maybe_feedback_vector, update_feedback_mode);
64 }
65
Increment(TNode<Context> context,TNode<Object> value,TNode<UintPtrT> slot,TNode<HeapObject> maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode)66 TNode<Object> Increment(TNode<Context> context, TNode<Object> value,
67 TNode<UintPtrT> slot,
68 TNode<HeapObject> maybe_feedback_vector,
69 UpdateFeedbackMode update_feedback_mode) {
70 return IncrementOrDecrement<Operation::kIncrement>(
71 context, value, slot, maybe_feedback_vector, update_feedback_mode);
72 }
73
Negate(TNode<Context> context,TNode<Object> value,TNode<UintPtrT> slot,TNode<HeapObject> maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode)74 TNode<Object> Negate(TNode<Context> context, TNode<Object> value,
75 TNode<UintPtrT> slot,
76 TNode<HeapObject> maybe_feedback_vector,
77 UpdateFeedbackMode update_feedback_mode) {
78 SmiOperation smi_op = [=](TNode<Smi> smi_value,
79 TVariable<Smi>* var_feedback, Label* do_float_op,
80 TVariable<Float64T>* var_float) {
81 TVARIABLE(Number, var_result);
82 Label if_zero(this), if_min_smi(this), end(this);
83 // Return -0 if operand is 0.
84 GotoIf(SmiEqual(smi_value, SmiConstant(0)), &if_zero);
85
86 // Special-case the minimum Smi to avoid overflow.
87 GotoIf(SmiEqual(smi_value, SmiConstant(Smi::kMinValue)), &if_min_smi);
88
89 // Else simply subtract operand from 0.
90 CombineFeedback(var_feedback, BinaryOperationFeedback::kSignedSmall);
91 var_result = SmiSub(SmiConstant(0), smi_value);
92 Goto(&end);
93
94 BIND(&if_zero);
95 CombineFeedback(var_feedback, BinaryOperationFeedback::kNumber);
96 var_result = MinusZeroConstant();
97 Goto(&end);
98
99 BIND(&if_min_smi);
100 *var_float = SmiToFloat64(smi_value);
101 Goto(do_float_op);
102
103 BIND(&end);
104 return var_result.value();
105 };
106 FloatOperation float_op = [=](TNode<Float64T> float_value) {
107 return Float64Neg(float_value);
108 };
109 BigIntOperation bigint_op = [=](TNode<Context> context,
110 TNode<HeapObject> bigint_value) {
111 return CAST(CallRuntime(Runtime::kBigIntUnaryOp, context, bigint_value,
112 SmiConstant(Operation::kNegate)));
113 };
114 return UnaryOpWithFeedback(context, value, slot, maybe_feedback_vector,
115 smi_op, float_op, bigint_op,
116 update_feedback_mode);
117 }
118
119 private:
120 using SmiOperation = std::function<TNode<Number>(
121 TNode<Smi> /* smi_value */, TVariable<Smi>* /* var_feedback */,
122 Label* /* do_float_op */, TVariable<Float64T>* /* var_float */)>;
123 using FloatOperation =
124 std::function<TNode<Float64T>(TNode<Float64T> /* float_value */)>;
125 using BigIntOperation = std::function<TNode<HeapObject>(
126 TNode<Context> /* context */, TNode<HeapObject> /* bigint_value */)>;
127
UnaryOpWithFeedback(TNode<Context> context,TNode<Object> value,TNode<UintPtrT> slot,TNode<HeapObject> maybe_feedback_vector,const SmiOperation & smi_op,const FloatOperation & float_op,const BigIntOperation & bigint_op,UpdateFeedbackMode update_feedback_mode)128 TNode<Object> UnaryOpWithFeedback(TNode<Context> context, TNode<Object> value,
129 TNode<UintPtrT> slot,
130 TNode<HeapObject> maybe_feedback_vector,
131 const SmiOperation& smi_op,
132 const FloatOperation& float_op,
133 const BigIntOperation& bigint_op,
134 UpdateFeedbackMode update_feedback_mode) {
135 TVARIABLE(Object, var_value, value);
136 TVARIABLE(Object, var_result);
137 TVARIABLE(Float64T, var_float_value);
138 TVARIABLE(Smi, var_feedback, SmiConstant(BinaryOperationFeedback::kNone));
139 Label start(this, {&var_value, &var_feedback}), end(this);
140 Label do_float_op(this, &var_float_value);
141 Goto(&start);
142 // We might have to try again after ToNumeric conversion.
143 BIND(&start);
144 {
145 Label if_smi(this), if_heapnumber(this), if_oddball(this);
146 Label if_bigint(this, Label::kDeferred);
147 Label if_other(this, Label::kDeferred);
148 value = var_value.value();
149 GotoIf(TaggedIsSmi(value), &if_smi);
150
151 TNode<HeapObject> value_heap_object = CAST(value);
152 TNode<Map> map = LoadMap(value_heap_object);
153 GotoIf(IsHeapNumberMap(map), &if_heapnumber);
154 TNode<Uint16T> instance_type = LoadMapInstanceType(map);
155 GotoIf(IsBigIntInstanceType(instance_type), &if_bigint);
156 Branch(InstanceTypeEqual(instance_type, ODDBALL_TYPE), &if_oddball,
157 &if_other);
158
159 BIND(&if_smi);
160 {
161 var_result =
162 smi_op(CAST(value), &var_feedback, &do_float_op, &var_float_value);
163 Goto(&end);
164 }
165
166 BIND(&if_heapnumber);
167 {
168 var_float_value = LoadHeapNumberValue(value_heap_object);
169 Goto(&do_float_op);
170 }
171
172 BIND(&if_bigint);
173 {
174 var_result = bigint_op(context, value_heap_object);
175 CombineFeedback(&var_feedback, BinaryOperationFeedback::kBigInt);
176 Goto(&end);
177 }
178
179 BIND(&if_oddball);
180 {
181 // We do not require an Or with earlier feedback here because once we
182 // convert the value to a number, we cannot reach this path. We can
183 // only reach this path on the first pass when the feedback is kNone.
184 CSA_DCHECK(this, SmiEqual(var_feedback.value(),
185 SmiConstant(BinaryOperationFeedback::kNone)));
186 OverwriteFeedback(&var_feedback,
187 BinaryOperationFeedback::kNumberOrOddball);
188 var_value =
189 LoadObjectField(value_heap_object, Oddball::kToNumberOffset);
190 Goto(&start);
191 }
192
193 BIND(&if_other);
194 {
195 // We do not require an Or with earlier feedback here because once we
196 // convert the value to a number, we cannot reach this path. We can
197 // only reach this path on the first pass when the feedback is kNone.
198 CSA_DCHECK(this, SmiEqual(var_feedback.value(),
199 SmiConstant(BinaryOperationFeedback::kNone)));
200 OverwriteFeedback(&var_feedback, BinaryOperationFeedback::kAny);
201 var_value = CallBuiltin(Builtin::kNonNumberToNumeric, context,
202 value_heap_object);
203 Goto(&start);
204 }
205 }
206
207 BIND(&do_float_op);
208 {
209 CombineFeedback(&var_feedback, BinaryOperationFeedback::kNumber);
210 var_result =
211 AllocateHeapNumberWithValue(float_op(var_float_value.value()));
212 Goto(&end);
213 }
214
215 BIND(&end);
216 UpdateFeedback(var_feedback.value(), maybe_feedback_vector, slot,
217 update_feedback_mode);
218 return var_result.value();
219 }
220
221 template <Operation kOperation>
IncrementOrDecrement(TNode<Context> context,TNode<Object> value,TNode<UintPtrT> slot,TNode<HeapObject> maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode)222 TNode<Object> IncrementOrDecrement(TNode<Context> context,
223 TNode<Object> value, TNode<UintPtrT> slot,
224 TNode<HeapObject> maybe_feedback_vector,
225 UpdateFeedbackMode update_feedback_mode) {
226 STATIC_ASSERT(kOperation == Operation::kIncrement ||
227 kOperation == Operation::kDecrement);
228 static constexpr int kAddValue =
229 (kOperation == Operation::kIncrement) ? 1 : -1;
230
231 SmiOperation smi_op = [=](TNode<Smi> smi_value,
232 TVariable<Smi>* var_feedback, Label* do_float_op,
233 TVariable<Float64T>* var_float) {
234 Label if_overflow(this), out(this);
235 TNode<Smi> result =
236 TrySmiAdd(smi_value, SmiConstant(kAddValue), &if_overflow);
237 CombineFeedback(var_feedback, BinaryOperationFeedback::kSignedSmall);
238 Goto(&out);
239
240 BIND(&if_overflow);
241 *var_float = SmiToFloat64(smi_value);
242 Goto(do_float_op);
243
244 BIND(&out);
245 return result;
246 };
247 FloatOperation float_op = [=](TNode<Float64T> float_value) {
248 return Float64Add(float_value, Float64Constant(kAddValue));
249 };
250 BigIntOperation bigint_op = [=](TNode<Context> context,
251 TNode<HeapObject> bigint_value) {
252 return CAST(CallRuntime(Runtime::kBigIntUnaryOp, context, bigint_value,
253 SmiConstant(kOperation)));
254 };
255 return UnaryOpWithFeedback(context, value, slot, maybe_feedback_vector,
256 smi_op, float_op, bigint_op,
257 update_feedback_mode);
258 }
259 };
260
261 } // namespace
262
Generate_BitwiseNotWithFeedback(TNode<Context> context,TNode<Object> value,TNode<UintPtrT> slot,TNode<HeapObject> maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode)263 TNode<Object> UnaryOpAssembler::Generate_BitwiseNotWithFeedback(
264 TNode<Context> context, TNode<Object> value, TNode<UintPtrT> slot,
265 TNode<HeapObject> maybe_feedback_vector,
266 UpdateFeedbackMode update_feedback_mode) {
267 UnaryOpAssemblerImpl a(state_);
268 return a.BitwiseNot(context, value, slot, maybe_feedback_vector,
269 update_feedback_mode);
270 }
271
Generate_DecrementWithFeedback(TNode<Context> context,TNode<Object> value,TNode<UintPtrT> slot,TNode<HeapObject> maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode)272 TNode<Object> UnaryOpAssembler::Generate_DecrementWithFeedback(
273 TNode<Context> context, TNode<Object> value, TNode<UintPtrT> slot,
274 TNode<HeapObject> maybe_feedback_vector,
275 UpdateFeedbackMode update_feedback_mode) {
276 UnaryOpAssemblerImpl a(state_);
277 return a.Decrement(context, value, slot, maybe_feedback_vector,
278 update_feedback_mode);
279 }
280
Generate_IncrementWithFeedback(TNode<Context> context,TNode<Object> value,TNode<UintPtrT> slot,TNode<HeapObject> maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode)281 TNode<Object> UnaryOpAssembler::Generate_IncrementWithFeedback(
282 TNode<Context> context, TNode<Object> value, TNode<UintPtrT> slot,
283 TNode<HeapObject> maybe_feedback_vector,
284 UpdateFeedbackMode update_feedback_mode) {
285 UnaryOpAssemblerImpl a(state_);
286 return a.Increment(context, value, slot, maybe_feedback_vector,
287 update_feedback_mode);
288 }
289
Generate_NegateWithFeedback(TNode<Context> context,TNode<Object> value,TNode<UintPtrT> slot,TNode<HeapObject> maybe_feedback_vector,UpdateFeedbackMode update_feedback_mode)290 TNode<Object> UnaryOpAssembler::Generate_NegateWithFeedback(
291 TNode<Context> context, TNode<Object> value, TNode<UintPtrT> slot,
292 TNode<HeapObject> maybe_feedback_vector,
293 UpdateFeedbackMode update_feedback_mode) {
294 UnaryOpAssemblerImpl a(state_);
295 return a.Negate(context, value, slot, maybe_feedback_vector,
296 update_feedback_mode);
297 }
298
299 } // namespace internal
300 } // namespace v8
301