• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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