• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 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/interpreter/interpreter-intrinsics.h"
6 
7 #include "src/code-factory.h"
8 
9 namespace v8 {
10 namespace internal {
11 namespace interpreter {
12 
13 using compiler::Node;
14 
15 #define __ assembler_->
16 
IntrinsicsHelper(InterpreterAssembler * assembler)17 IntrinsicsHelper::IntrinsicsHelper(InterpreterAssembler* assembler)
18     : isolate_(assembler->isolate()),
19       zone_(assembler->zone()),
20       assembler_(assembler) {}
21 
22 // static
IsSupported(Runtime::FunctionId function_id)23 bool IntrinsicsHelper::IsSupported(Runtime::FunctionId function_id) {
24   switch (function_id) {
25 #define SUPPORTED(name, lower_case, count) case Runtime::kInline##name:
26     INTRINSICS_LIST(SUPPORTED)
27     return true;
28 #undef SUPPORTED
29     default:
30       return false;
31   }
32 }
33 
34 // static
FromRuntimeId(Runtime::FunctionId function_id)35 IntrinsicsHelper::IntrinsicId IntrinsicsHelper::FromRuntimeId(
36     Runtime::FunctionId function_id) {
37   switch (function_id) {
38 #define TO_RUNTIME_ID(name, lower_case, count) \
39   case Runtime::kInline##name:                 \
40     return IntrinsicId::k##name;
41     INTRINSICS_LIST(TO_RUNTIME_ID)
42 #undef TO_RUNTIME_ID
43     default:
44       UNREACHABLE();
45       return static_cast<IntrinsicsHelper::IntrinsicId>(-1);
46   }
47 }
48 
49 // static
ToRuntimeId(IntrinsicsHelper::IntrinsicId intrinsic_id)50 Runtime::FunctionId IntrinsicsHelper::ToRuntimeId(
51     IntrinsicsHelper::IntrinsicId intrinsic_id) {
52   switch (intrinsic_id) {
53 #define TO_INTRINSIC_ID(name, lower_case, count) \
54   case IntrinsicId::k##name:                     \
55     return Runtime::kInline##name;
56     INTRINSICS_LIST(TO_INTRINSIC_ID)
57 #undef TO_INTRINSIC_ID
58     default:
59       UNREACHABLE();
60       return static_cast<Runtime::FunctionId>(-1);
61   }
62 }
63 
InvokeIntrinsic(Node * function_id,Node * context,Node * first_arg_reg,Node * arg_count)64 Node* IntrinsicsHelper::InvokeIntrinsic(Node* function_id, Node* context,
65                                         Node* first_arg_reg, Node* arg_count) {
66   InterpreterAssembler::Label abort(assembler_), end(assembler_);
67   InterpreterAssembler::Variable result(assembler_,
68                                         MachineRepresentation::kTagged);
69 
70 #define MAKE_LABEL(name, lower_case, count) \
71   InterpreterAssembler::Label lower_case(assembler_);
72   INTRINSICS_LIST(MAKE_LABEL)
73 #undef MAKE_LABEL
74 
75 #define LABEL_POINTER(name, lower_case, count) &lower_case,
76   InterpreterAssembler::Label* labels[] = {INTRINSICS_LIST(LABEL_POINTER)};
77 #undef LABEL_POINTER
78 
79 #define CASE(name, lower_case, count) \
80   static_cast<int32_t>(IntrinsicId::k##name),
81   int32_t cases[] = {INTRINSICS_LIST(CASE)};
82 #undef CASE
83 
84   __ Switch(function_id, &abort, cases, labels, arraysize(cases));
85 #define HANDLE_CASE(name, lower_case, expected_arg_count)   \
86   __ Bind(&lower_case);                                     \
87   if (FLAG_debug_code && expected_arg_count >= 0) {         \
88     AbortIfArgCountMismatch(expected_arg_count, arg_count); \
89   }                                                         \
90   result.Bind(name(first_arg_reg, arg_count, context));     \
91   __ Goto(&end);
92   INTRINSICS_LIST(HANDLE_CASE)
93 #undef HANDLE_CASE
94 
95   __ Bind(&abort);
96   {
97     __ Abort(BailoutReason::kUnexpectedFunctionIDForInvokeIntrinsic);
98     result.Bind(__ UndefinedConstant());
99     __ Goto(&end);
100   }
101 
102   __ Bind(&end);
103   return result.value();
104 }
105 
CompareInstanceType(Node * object,int type,InstanceTypeCompareMode mode)106 Node* IntrinsicsHelper::CompareInstanceType(Node* object, int type,
107                                             InstanceTypeCompareMode mode) {
108   InterpreterAssembler::Variable return_value(assembler_,
109                                               MachineRepresentation::kTagged);
110   Node* instance_type = __ LoadInstanceType(object);
111 
112   InterpreterAssembler::Label if_true(assembler_), if_false(assembler_),
113       end(assembler_);
114   if (mode == kInstanceTypeEqual) {
115     return __ Word32Equal(instance_type, __ Int32Constant(type));
116   } else {
117     DCHECK(mode == kInstanceTypeGreaterThanOrEqual);
118     return __ Int32GreaterThanOrEqual(instance_type, __ Int32Constant(type));
119   }
120 }
121 
IsInstanceType(Node * input,int type)122 Node* IntrinsicsHelper::IsInstanceType(Node* input, int type) {
123   InterpreterAssembler::Variable return_value(assembler_,
124                                               MachineRepresentation::kTagged);
125   InterpreterAssembler::Label if_not_smi(assembler_), return_true(assembler_),
126       return_false(assembler_), end(assembler_);
127   Node* arg = __ LoadRegister(input);
128   __ GotoIf(__ TaggedIsSmi(arg), &return_false);
129 
130   Node* condition = CompareInstanceType(arg, type, kInstanceTypeEqual);
131   __ Branch(condition, &return_true, &return_false);
132 
133   __ Bind(&return_true);
134   {
135     return_value.Bind(__ BooleanConstant(true));
136     __ Goto(&end);
137   }
138 
139   __ Bind(&return_false);
140   {
141     return_value.Bind(__ BooleanConstant(false));
142     __ Goto(&end);
143   }
144 
145   __ Bind(&end);
146   return return_value.value();
147 }
148 
IsJSReceiver(Node * input,Node * arg_count,Node * context)149 Node* IntrinsicsHelper::IsJSReceiver(Node* input, Node* arg_count,
150                                      Node* context) {
151   InterpreterAssembler::Variable return_value(assembler_,
152                                               MachineRepresentation::kTagged);
153   InterpreterAssembler::Label return_true(assembler_), return_false(assembler_),
154       end(assembler_);
155 
156   Node* arg = __ LoadRegister(input);
157   __ GotoIf(__ TaggedIsSmi(arg), &return_false);
158 
159   STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
160   Node* condition = CompareInstanceType(arg, FIRST_JS_RECEIVER_TYPE,
161                                         kInstanceTypeGreaterThanOrEqual);
162   __ Branch(condition, &return_true, &return_false);
163 
164   __ Bind(&return_true);
165   {
166     return_value.Bind(__ BooleanConstant(true));
167     __ Goto(&end);
168   }
169 
170   __ Bind(&return_false);
171   {
172     return_value.Bind(__ BooleanConstant(false));
173     __ Goto(&end);
174   }
175 
176   __ Bind(&end);
177   return return_value.value();
178 }
179 
IsArray(Node * input,Node * arg_count,Node * context)180 Node* IntrinsicsHelper::IsArray(Node* input, Node* arg_count, Node* context) {
181   return IsInstanceType(input, JS_ARRAY_TYPE);
182 }
183 
IsJSProxy(Node * input,Node * arg_count,Node * context)184 Node* IntrinsicsHelper::IsJSProxy(Node* input, Node* arg_count, Node* context) {
185   return IsInstanceType(input, JS_PROXY_TYPE);
186 }
187 
IsRegExp(Node * input,Node * arg_count,Node * context)188 Node* IntrinsicsHelper::IsRegExp(Node* input, Node* arg_count, Node* context) {
189   return IsInstanceType(input, JS_REGEXP_TYPE);
190 }
191 
IsTypedArray(Node * input,Node * arg_count,Node * context)192 Node* IntrinsicsHelper::IsTypedArray(Node* input, Node* arg_count,
193                                      Node* context) {
194   return IsInstanceType(input, JS_TYPED_ARRAY_TYPE);
195 }
196 
IsSmi(Node * input,Node * arg_count,Node * context)197 Node* IntrinsicsHelper::IsSmi(Node* input, Node* arg_count, Node* context) {
198   InterpreterAssembler::Variable return_value(assembler_,
199                                               MachineRepresentation::kTagged);
200   InterpreterAssembler::Label if_smi(assembler_), if_not_smi(assembler_),
201       end(assembler_);
202 
203   Node* arg = __ LoadRegister(input);
204 
205   __ Branch(__ TaggedIsSmi(arg), &if_smi, &if_not_smi);
206   __ Bind(&if_smi);
207   {
208     return_value.Bind(__ BooleanConstant(true));
209     __ Goto(&end);
210   }
211 
212   __ Bind(&if_not_smi);
213   {
214     return_value.Bind(__ BooleanConstant(false));
215     __ Goto(&end);
216   }
217 
218   __ Bind(&end);
219   return return_value.value();
220 }
221 
IntrinsicAsStubCall(Node * args_reg,Node * context,Callable const & callable)222 Node* IntrinsicsHelper::IntrinsicAsStubCall(Node* args_reg, Node* context,
223                                             Callable const& callable) {
224   int param_count = callable.descriptor().GetParameterCount();
225   Node** args = zone()->NewArray<Node*>(param_count + 1);  // 1 for context
226   for (int i = 0; i < param_count; i++) {
227     args[i] = __ LoadRegister(args_reg);
228     args_reg = __ NextRegister(args_reg);
229   }
230   args[param_count] = context;
231 
232   return __ CallStubN(callable, args);
233 }
234 
HasProperty(Node * input,Node * arg_count,Node * context)235 Node* IntrinsicsHelper::HasProperty(Node* input, Node* arg_count,
236                                     Node* context) {
237   return IntrinsicAsStubCall(input, context,
238                              CodeFactory::HasProperty(isolate()));
239 }
240 
NewObject(Node * input,Node * arg_count,Node * context)241 Node* IntrinsicsHelper::NewObject(Node* input, Node* arg_count, Node* context) {
242   return IntrinsicAsStubCall(input, context,
243                              CodeFactory::FastNewObject(isolate()));
244 }
245 
NumberToString(Node * input,Node * arg_count,Node * context)246 Node* IntrinsicsHelper::NumberToString(Node* input, Node* arg_count,
247                                        Node* context) {
248   return IntrinsicAsStubCall(input, context,
249                              CodeFactory::NumberToString(isolate()));
250 }
251 
RegExpExec(Node * input,Node * arg_count,Node * context)252 Node* IntrinsicsHelper::RegExpExec(Node* input, Node* arg_count,
253                                    Node* context) {
254   return IntrinsicAsStubCall(input, context,
255                              CodeFactory::RegExpExec(isolate()));
256 }
257 
SubString(Node * input,Node * arg_count,Node * context)258 Node* IntrinsicsHelper::SubString(Node* input, Node* arg_count, Node* context) {
259   return IntrinsicAsStubCall(input, context, CodeFactory::SubString(isolate()));
260 }
261 
ToString(Node * input,Node * arg_count,Node * context)262 Node* IntrinsicsHelper::ToString(Node* input, Node* arg_count, Node* context) {
263   return IntrinsicAsStubCall(input, context, CodeFactory::ToString(isolate()));
264 }
265 
ToLength(Node * input,Node * arg_count,Node * context)266 Node* IntrinsicsHelper::ToLength(Node* input, Node* arg_count, Node* context) {
267   return IntrinsicAsStubCall(input, context, CodeFactory::ToLength(isolate()));
268 }
269 
ToInteger(Node * input,Node * arg_count,Node * context)270 Node* IntrinsicsHelper::ToInteger(Node* input, Node* arg_count, Node* context) {
271   return IntrinsicAsStubCall(input, context, CodeFactory::ToInteger(isolate()));
272 }
273 
ToNumber(Node * input,Node * arg_count,Node * context)274 Node* IntrinsicsHelper::ToNumber(Node* input, Node* arg_count, Node* context) {
275   return IntrinsicAsStubCall(input, context, CodeFactory::ToNumber(isolate()));
276 }
277 
ToObject(Node * input,Node * arg_count,Node * context)278 Node* IntrinsicsHelper::ToObject(Node* input, Node* arg_count, Node* context) {
279   return IntrinsicAsStubCall(input, context, CodeFactory::ToObject(isolate()));
280 }
281 
Call(Node * args_reg,Node * arg_count,Node * context)282 Node* IntrinsicsHelper::Call(Node* args_reg, Node* arg_count, Node* context) {
283   // First argument register contains the function target.
284   Node* function = __ LoadRegister(args_reg);
285 
286   // Receiver is the second runtime call argument.
287   Node* receiver_reg = __ NextRegister(args_reg);
288   Node* receiver_arg = __ RegisterLocation(receiver_reg);
289 
290   // Subtract function and receiver from arg count.
291   Node* function_and_receiver_count = __ Int32Constant(2);
292   Node* target_args_count = __ Int32Sub(arg_count, function_and_receiver_count);
293 
294   if (FLAG_debug_code) {
295     InterpreterAssembler::Label arg_count_positive(assembler_);
296     Node* comparison = __ Int32LessThan(target_args_count, __ Int32Constant(0));
297     __ GotoUnless(comparison, &arg_count_positive);
298     __ Abort(kWrongArgumentCountForInvokeIntrinsic);
299     __ Goto(&arg_count_positive);
300     __ Bind(&arg_count_positive);
301   }
302 
303   Node* result = __ CallJS(function, context, receiver_arg, target_args_count,
304                            TailCallMode::kDisallow);
305   return result;
306 }
307 
ValueOf(Node * args_reg,Node * arg_count,Node * context)308 Node* IntrinsicsHelper::ValueOf(Node* args_reg, Node* arg_count,
309                                 Node* context) {
310   InterpreterAssembler::Variable return_value(assembler_,
311                                               MachineRepresentation::kTagged);
312   InterpreterAssembler::Label done(assembler_);
313 
314   Node* object = __ LoadRegister(args_reg);
315   return_value.Bind(object);
316 
317   // If the object is a smi return the object.
318   __ GotoIf(__ TaggedIsSmi(object), &done);
319 
320   // If the object is not a value type, return the object.
321   Node* condition =
322       CompareInstanceType(object, JS_VALUE_TYPE, kInstanceTypeEqual);
323   __ GotoUnless(condition, &done);
324 
325   // If the object is a value type, return the value field.
326   return_value.Bind(__ LoadObjectField(object, JSValue::kValueOffset));
327   __ Goto(&done);
328 
329   __ Bind(&done);
330   return return_value.value();
331 }
332 
ClassOf(Node * args_reg,Node * arg_count,Node * context)333 Node* IntrinsicsHelper::ClassOf(Node* args_reg, Node* arg_count,
334                                 Node* context) {
335   InterpreterAssembler::Variable return_value(assembler_,
336                                               MachineRepresentation::kTagged);
337   InterpreterAssembler::Label done(assembler_), null(assembler_),
338       function(assembler_), non_function_constructor(assembler_);
339 
340   Node* object = __ LoadRegister(args_reg);
341 
342   // If the object is not a JSReceiver, we return null.
343   __ GotoIf(__ TaggedIsSmi(object), &null);
344   STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
345   Node* is_js_receiver = CompareInstanceType(object, FIRST_JS_RECEIVER_TYPE,
346                                              kInstanceTypeGreaterThanOrEqual);
347   __ GotoUnless(is_js_receiver, &null);
348 
349   // Return 'Function' for JSFunction and JSBoundFunction objects.
350   Node* is_function = CompareInstanceType(object, FIRST_FUNCTION_TYPE,
351                                           kInstanceTypeGreaterThanOrEqual);
352   STATIC_ASSERT(LAST_FUNCTION_TYPE == LAST_TYPE);
353   __ GotoIf(is_function, &function);
354 
355   // Check if the constructor in the map is a JS function.
356   Node* constructor = __ LoadMapConstructor(__ LoadMap(object));
357   Node* constructor_is_js_function =
358       CompareInstanceType(constructor, JS_FUNCTION_TYPE, kInstanceTypeEqual);
359   __ GotoUnless(constructor_is_js_function, &non_function_constructor);
360 
361   // Grab the instance class name from the constructor function.
362   Node* shared =
363       __ LoadObjectField(constructor, JSFunction::kSharedFunctionInfoOffset);
364   return_value.Bind(
365       __ LoadObjectField(shared, SharedFunctionInfo::kInstanceClassNameOffset));
366   __ Goto(&done);
367 
368   // Non-JS objects have class null.
369   __ Bind(&null);
370   {
371     return_value.Bind(__ LoadRoot(Heap::kNullValueRootIndex));
372     __ Goto(&done);
373   }
374 
375   // Functions have class 'Function'.
376   __ Bind(&function);
377   {
378     return_value.Bind(__ LoadRoot(Heap::kFunction_stringRootIndex));
379     __ Goto(&done);
380   }
381 
382   // Objects with a non-function constructor have class 'Object'.
383   __ Bind(&non_function_constructor);
384   {
385     return_value.Bind(__ LoadRoot(Heap::kObject_stringRootIndex));
386     __ Goto(&done);
387   }
388 
389   __ Bind(&done);
390   return return_value.value();
391 }
392 
AbortIfArgCountMismatch(int expected,Node * actual)393 void IntrinsicsHelper::AbortIfArgCountMismatch(int expected, Node* actual) {
394   InterpreterAssembler::Label match(assembler_);
395   Node* comparison = __ Word32Equal(actual, __ Int32Constant(expected));
396   __ GotoIf(comparison, &match);
397   __ Abort(kWrongArgumentCountForInvokeIntrinsic);
398   __ Goto(&match);
399   __ Bind(&match);
400 }
401 
402 }  // namespace interpreter
403 }  // namespace internal
404 }  // namespace v8
405