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#include "src/objects-inl.h"
9
10namespace v8 {
11namespace internal {
12namespace interpreter {
13
14using compiler::Node;
15
16#define __ assembler_->
17
18IntrinsicsHelper::IntrinsicsHelper(InterpreterAssembler* assembler)
19    : isolate_(assembler->isolate()),
20      zone_(assembler->zone()),
21      assembler_(assembler) {}
22
23// static
24bool IntrinsicsHelper::IsSupported(Runtime::FunctionId function_id) {
25  switch (function_id) {
26#define SUPPORTED(name, lower_case, count) case Runtime::kInline##name:
27    INTRINSICS_LIST(SUPPORTED)
28    return true;
29#undef SUPPORTED
30    default:
31      return false;
32  }
33}
34
35// static
36IntrinsicsHelper::IntrinsicId IntrinsicsHelper::FromRuntimeId(
37    Runtime::FunctionId function_id) {
38  switch (function_id) {
39#define TO_RUNTIME_ID(name, lower_case, count) \
40  case Runtime::kInline##name:                 \
41    return IntrinsicId::k##name;
42    INTRINSICS_LIST(TO_RUNTIME_ID)
43#undef TO_RUNTIME_ID
44    default:
45      UNREACHABLE();
46      return static_cast<IntrinsicsHelper::IntrinsicId>(-1);
47  }
48}
49
50// static
51Runtime::FunctionId IntrinsicsHelper::ToRuntimeId(
52    IntrinsicsHelper::IntrinsicId intrinsic_id) {
53  switch (intrinsic_id) {
54#define TO_INTRINSIC_ID(name, lower_case, count) \
55  case IntrinsicId::k##name:                     \
56    return Runtime::kInline##name;
57    INTRINSICS_LIST(TO_INTRINSIC_ID)
58#undef TO_INTRINSIC_ID
59    default:
60      UNREACHABLE();
61      return static_cast<Runtime::FunctionId>(-1);
62  }
63}
64
65Node* IntrinsicsHelper::InvokeIntrinsic(Node* function_id, Node* context,
66                                        Node* first_arg_reg, Node* arg_count) {
67  InterpreterAssembler::Label abort(assembler_), end(assembler_);
68  InterpreterAssembler::Variable result(assembler_,
69                                        MachineRepresentation::kTagged);
70
71#define MAKE_LABEL(name, lower_case, count) \
72  InterpreterAssembler::Label lower_case(assembler_);
73  INTRINSICS_LIST(MAKE_LABEL)
74#undef MAKE_LABEL
75
76#define LABEL_POINTER(name, lower_case, count) &lower_case,
77  InterpreterAssembler::Label* labels[] = {INTRINSICS_LIST(LABEL_POINTER)};
78#undef LABEL_POINTER
79
80#define CASE(name, lower_case, count) \
81  static_cast<int32_t>(IntrinsicId::k##name),
82  int32_t cases[] = {INTRINSICS_LIST(CASE)};
83#undef CASE
84
85  __ Switch(function_id, &abort, cases, labels, arraysize(cases));
86#define HANDLE_CASE(name, lower_case, expected_arg_count)   \
87  __ Bind(&lower_case);                                     \
88  if (FLAG_debug_code && expected_arg_count >= 0) {         \
89    AbortIfArgCountMismatch(expected_arg_count, arg_count); \
90  }                                                         \
91  result.Bind(name(first_arg_reg, arg_count, context));     \
92  __ Goto(&end);
93  INTRINSICS_LIST(HANDLE_CASE)
94#undef HANDLE_CASE
95
96  __ Bind(&abort);
97  {
98    __ Abort(BailoutReason::kUnexpectedFunctionIDForInvokeIntrinsic);
99    result.Bind(__ UndefinedConstant());
100    __ Goto(&end);
101  }
102
103  __ Bind(&end);
104  return result.value();
105}
106
107Node* IntrinsicsHelper::CompareInstanceType(Node* object, int type,
108                                            InstanceTypeCompareMode mode) {
109  Node* instance_type = __ LoadInstanceType(object);
110
111  if (mode == kInstanceTypeEqual) {
112    return __ Word32Equal(instance_type, __ Int32Constant(type));
113  } else {
114    DCHECK(mode == kInstanceTypeGreaterThanOrEqual);
115    return __ Int32GreaterThanOrEqual(instance_type, __ Int32Constant(type));
116  }
117}
118
119Node* IntrinsicsHelper::IsInstanceType(Node* input, int type) {
120  InterpreterAssembler::Variable return_value(assembler_,
121                                              MachineRepresentation::kTagged);
122  // TODO(ishell): Use Select here.
123  InterpreterAssembler::Label if_not_smi(assembler_), return_true(assembler_),
124      return_false(assembler_), end(assembler_);
125  Node* arg = __ LoadRegister(input);
126  __ GotoIf(__ TaggedIsSmi(arg), &return_false);
127
128  Node* condition = CompareInstanceType(arg, type, kInstanceTypeEqual);
129  __ Branch(condition, &return_true, &return_false);
130
131  __ Bind(&return_true);
132  {
133    return_value.Bind(__ BooleanConstant(true));
134    __ Goto(&end);
135  }
136
137  __ Bind(&return_false);
138  {
139    return_value.Bind(__ BooleanConstant(false));
140    __ Goto(&end);
141  }
142
143  __ Bind(&end);
144  return return_value.value();
145}
146
147Node* IntrinsicsHelper::IsJSReceiver(Node* input, Node* arg_count,
148                                     Node* context) {
149  // TODO(ishell): Use Select here.
150  // TODO(ishell): Use CSA::IsJSReceiverInstanceType here.
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::IsTypedArray(Node* input, Node* arg_count,
189                                     Node* context) {
190  return IsInstanceType(input, JS_TYPED_ARRAY_TYPE);
191}
192
193Node* IntrinsicsHelper::IsSmi(Node* input, Node* arg_count, Node* context) {
194  // TODO(ishell): Use SelectBooleanConstant here.
195  InterpreterAssembler::Variable return_value(assembler_,
196                                              MachineRepresentation::kTagged);
197  InterpreterAssembler::Label if_smi(assembler_), if_not_smi(assembler_),
198      end(assembler_);
199
200  Node* arg = __ LoadRegister(input);
201
202  __ Branch(__ TaggedIsSmi(arg), &if_smi, &if_not_smi);
203  __ Bind(&if_smi);
204  {
205    return_value.Bind(__ BooleanConstant(true));
206    __ Goto(&end);
207  }
208
209  __ Bind(&if_not_smi);
210  {
211    return_value.Bind(__ BooleanConstant(false));
212    __ Goto(&end);
213  }
214
215  __ Bind(&end);
216  return return_value.value();
217}
218
219Node* IntrinsicsHelper::IntrinsicAsStubCall(Node* args_reg, Node* context,
220                                            Callable const& callable) {
221  int param_count = callable.descriptor().GetParameterCount();
222  int input_count = param_count + 2;  // +2 for target and context
223  Node** args = zone()->NewArray<Node*>(input_count);
224  int index = 0;
225  args[index++] = __ HeapConstant(callable.code());
226  for (int i = 0; i < param_count; i++) {
227    args[index++] = __ LoadRegister(args_reg);
228    args_reg = __ NextRegister(args_reg);
229  }
230  args[index++] = context;
231  return __ CallStubN(callable.descriptor(), 1, input_count, args);
232}
233
234Node* IntrinsicsHelper::CreateIterResultObject(Node* input, Node* arg_count,
235                                               Node* context) {
236  return IntrinsicAsStubCall(input, context,
237                             CodeFactory::CreateIterResultObject(isolate()));
238}
239
240Node* IntrinsicsHelper::HasProperty(Node* input, Node* arg_count,
241                                    Node* context) {
242  return IntrinsicAsStubCall(input, context,
243                             CodeFactory::HasProperty(isolate()));
244}
245
246Node* IntrinsicsHelper::SubString(Node* input, Node* arg_count, Node* context) {
247  return IntrinsicAsStubCall(input, context, CodeFactory::SubString(isolate()));
248}
249
250Node* IntrinsicsHelper::ToString(Node* input, Node* arg_count, Node* context) {
251  return IntrinsicAsStubCall(input, context, CodeFactory::ToString(isolate()));
252}
253
254Node* IntrinsicsHelper::ToLength(Node* input, Node* arg_count, Node* context) {
255  return IntrinsicAsStubCall(input, context, CodeFactory::ToLength(isolate()));
256}
257
258Node* IntrinsicsHelper::ToInteger(Node* input, Node* arg_count, Node* context) {
259  return IntrinsicAsStubCall(input, context, CodeFactory::ToInteger(isolate()));
260}
261
262Node* IntrinsicsHelper::ToNumber(Node* input, Node* arg_count, Node* context) {
263  return IntrinsicAsStubCall(input, context, CodeFactory::ToNumber(isolate()));
264}
265
266Node* IntrinsicsHelper::ToObject(Node* input, Node* arg_count, Node* context) {
267  return IntrinsicAsStubCall(input, context, CodeFactory::ToObject(isolate()));
268}
269
270Node* IntrinsicsHelper::Call(Node* args_reg, Node* arg_count, Node* context) {
271  // First argument register contains the function target.
272  Node* function = __ LoadRegister(args_reg);
273
274  // Receiver is the second runtime call argument.
275  Node* receiver_reg = __ NextRegister(args_reg);
276  Node* receiver_arg = __ RegisterLocation(receiver_reg);
277
278  // Subtract function and receiver from arg count.
279  Node* function_and_receiver_count = __ Int32Constant(2);
280  Node* target_args_count = __ Int32Sub(arg_count, function_and_receiver_count);
281
282  if (FLAG_debug_code) {
283    InterpreterAssembler::Label arg_count_positive(assembler_);
284    Node* comparison = __ Int32LessThan(target_args_count, __ Int32Constant(0));
285    __ GotoIfNot(comparison, &arg_count_positive);
286    __ Abort(kWrongArgumentCountForInvokeIntrinsic);
287    __ Goto(&arg_count_positive);
288    __ Bind(&arg_count_positive);
289  }
290
291  Node* result = __ CallJS(function, context, receiver_arg, target_args_count,
292                           TailCallMode::kDisallow);
293  return result;
294}
295
296Node* IntrinsicsHelper::ClassOf(Node* args_reg, Node* arg_count,
297                                Node* context) {
298  Node* value = __ LoadRegister(args_reg);
299  return __ ClassOf(value);
300}
301
302Node* IntrinsicsHelper::CreateAsyncFromSyncIterator(Node* args_reg,
303                                                    Node* arg_count,
304                                                    Node* context) {
305  InterpreterAssembler::Label not_receiver(
306      assembler_, InterpreterAssembler::Label::kDeferred);
307  InterpreterAssembler::Label done(assembler_);
308  InterpreterAssembler::Variable return_value(assembler_,
309                                              MachineRepresentation::kTagged);
310
311  Node* sync_iterator = __ LoadRegister(args_reg);
312
313  __ GotoIf(__ TaggedIsSmi(sync_iterator), &not_receiver);
314  __ GotoIfNot(__ IsJSReceiver(sync_iterator), &not_receiver);
315
316  Node* const native_context = __ LoadNativeContext(context);
317  Node* const map = __ LoadContextElement(
318      native_context, Context::ASYNC_FROM_SYNC_ITERATOR_MAP_INDEX);
319  Node* const iterator = __ AllocateJSObjectFromMap(map);
320
321  __ StoreObjectFieldNoWriteBarrier(
322      iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset, sync_iterator);
323
324  return_value.Bind(iterator);
325  __ Goto(&done);
326
327  __ Bind(&not_receiver);
328  {
329    return_value.Bind(
330        __ CallRuntime(Runtime::kThrowSymbolIteratorInvalid, context));
331
332    // Unreachable due to the Throw in runtime call.
333    __ Goto(&done);
334  }
335
336  __ Bind(&done);
337  return return_value.value();
338}
339
340void IntrinsicsHelper::AbortIfArgCountMismatch(int expected, Node* actual) {
341  InterpreterAssembler::Label match(assembler_);
342  Node* comparison = __ Word32Equal(actual, __ Int32Constant(expected));
343  __ GotoIf(comparison, &match);
344  __ Abort(kWrongArgumentCountForInvokeIntrinsic);
345  __ Goto(&match);
346  __ Bind(&match);
347}
348
349}  // namespace interpreter
350}  // namespace internal
351}  // namespace v8
352