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
9namespace v8 {
10namespace internal {
11namespace interpreter {
12
13using compiler::Node;
14
15#define __ assembler_->
16
17IntrinsicsHelper::IntrinsicsHelper(InterpreterAssembler* assembler)
18    : isolate_(assembler->isolate()),
19      zone_(assembler->zone()),
20      assembler_(assembler) {}
21
22// static
23bool 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
35IntrinsicsHelper::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
50Runtime::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
64Node* 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
106Node* 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
122Node* 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
149Node* 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
180Node* IntrinsicsHelper::IsArray(Node* input, Node* arg_count, Node* context) {
181  return IsInstanceType(input, JS_ARRAY_TYPE);
182}
183
184Node* IntrinsicsHelper::IsJSProxy(Node* input, Node* arg_count, Node* context) {
185  return IsInstanceType(input, JS_PROXY_TYPE);
186}
187
188Node* IntrinsicsHelper::IsRegExp(Node* input, Node* arg_count, Node* context) {
189  return IsInstanceType(input, JS_REGEXP_TYPE);
190}
191
192Node* IntrinsicsHelper::IsTypedArray(Node* input, Node* arg_count,
193                                     Node* context) {
194  return IsInstanceType(input, JS_TYPED_ARRAY_TYPE);
195}
196
197Node* 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
222Node* 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
235Node* IntrinsicsHelper::HasProperty(Node* input, Node* arg_count,
236                                    Node* context) {
237  return IntrinsicAsStubCall(input, context,
238                             CodeFactory::HasProperty(isolate()));
239}
240
241Node* IntrinsicsHelper::NewObject(Node* input, Node* arg_count, Node* context) {
242  return IntrinsicAsStubCall(input, context,
243                             CodeFactory::FastNewObject(isolate()));
244}
245
246Node* IntrinsicsHelper::NumberToString(Node* input, Node* arg_count,
247                                       Node* context) {
248  return IntrinsicAsStubCall(input, context,
249                             CodeFactory::NumberToString(isolate()));
250}
251
252Node* IntrinsicsHelper::RegExpExec(Node* input, Node* arg_count,
253                                   Node* context) {
254  return IntrinsicAsStubCall(input, context,
255                             CodeFactory::RegExpExec(isolate()));
256}
257
258Node* IntrinsicsHelper::SubString(Node* input, Node* arg_count, Node* context) {
259  return IntrinsicAsStubCall(input, context, CodeFactory::SubString(isolate()));
260}
261
262Node* IntrinsicsHelper::ToString(Node* input, Node* arg_count, Node* context) {
263  return IntrinsicAsStubCall(input, context, CodeFactory::ToString(isolate()));
264}
265
266Node* IntrinsicsHelper::ToLength(Node* input, Node* arg_count, Node* context) {
267  return IntrinsicAsStubCall(input, context, CodeFactory::ToLength(isolate()));
268}
269
270Node* IntrinsicsHelper::ToInteger(Node* input, Node* arg_count, Node* context) {
271  return IntrinsicAsStubCall(input, context, CodeFactory::ToInteger(isolate()));
272}
273
274Node* IntrinsicsHelper::ToNumber(Node* input, Node* arg_count, Node* context) {
275  return IntrinsicAsStubCall(input, context, CodeFactory::ToNumber(isolate()));
276}
277
278Node* IntrinsicsHelper::ToObject(Node* input, Node* arg_count, Node* context) {
279  return IntrinsicAsStubCall(input, context, CodeFactory::ToObject(isolate()));
280}
281
282Node* 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
308Node* 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
333Node* 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
393void 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