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