• 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)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