Home | History | Annotate | Download | only in interpreter
      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 
     17 IntrinsicsHelper::IntrinsicsHelper(InterpreterAssembler* assembler)
     18     : isolate_(assembler->isolate()),
     19       zone_(assembler->zone()),
     20       assembler_(assembler) {}
     21 
     22 // static
     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
     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
     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 
     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 
    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 
    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 
    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 
    180 Node* IntrinsicsHelper::IsArray(Node* input, Node* arg_count, Node* context) {
    181   return IsInstanceType(input, JS_ARRAY_TYPE);
    182 }
    183 
    184 Node* IntrinsicsHelper::IsJSProxy(Node* input, Node* arg_count, Node* context) {
    185   return IsInstanceType(input, JS_PROXY_TYPE);
    186 }
    187 
    188 Node* IntrinsicsHelper::IsRegExp(Node* input, Node* arg_count, Node* context) {
    189   return IsInstanceType(input, JS_REGEXP_TYPE);
    190 }
    191 
    192 Node* IntrinsicsHelper::IsTypedArray(Node* input, Node* arg_count,
    193                                      Node* context) {
    194   return IsInstanceType(input, JS_TYPED_ARRAY_TYPE);
    195 }
    196 
    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 
    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 
    235 Node* IntrinsicsHelper::HasProperty(Node* input, Node* arg_count,
    236                                     Node* context) {
    237   return IntrinsicAsStubCall(input, context,
    238                              CodeFactory::HasProperty(isolate()));
    239 }
    240 
    241 Node* IntrinsicsHelper::NewObject(Node* input, Node* arg_count, Node* context) {
    242   return IntrinsicAsStubCall(input, context,
    243                              CodeFactory::FastNewObject(isolate()));
    244 }
    245 
    246 Node* IntrinsicsHelper::NumberToString(Node* input, Node* arg_count,
    247                                        Node* context) {
    248   return IntrinsicAsStubCall(input, context,
    249                              CodeFactory::NumberToString(isolate()));
    250 }
    251 
    252 Node* IntrinsicsHelper::RegExpExec(Node* input, Node* arg_count,
    253                                    Node* context) {
    254   return IntrinsicAsStubCall(input, context,
    255                              CodeFactory::RegExpExec(isolate()));
    256 }
    257 
    258 Node* IntrinsicsHelper::SubString(Node* input, Node* arg_count, Node* context) {
    259   return IntrinsicAsStubCall(input, context, CodeFactory::SubString(isolate()));
    260 }
    261 
    262 Node* IntrinsicsHelper::ToString(Node* input, Node* arg_count, Node* context) {
    263   return IntrinsicAsStubCall(input, context, CodeFactory::ToString(isolate()));
    264 }
    265 
    266 Node* IntrinsicsHelper::ToLength(Node* input, Node* arg_count, Node* context) {
    267   return IntrinsicAsStubCall(input, context, CodeFactory::ToLength(isolate()));
    268 }
    269 
    270 Node* IntrinsicsHelper::ToInteger(Node* input, Node* arg_count, Node* context) {
    271   return IntrinsicAsStubCall(input, context, CodeFactory::ToInteger(isolate()));
    272 }
    273 
    274 Node* IntrinsicsHelper::ToNumber(Node* input, Node* arg_count, Node* context) {
    275   return IntrinsicAsStubCall(input, context, CodeFactory::ToNumber(isolate()));
    276 }
    277 
    278 Node* IntrinsicsHelper::ToObject(Node* input, Node* arg_count, Node* context) {
    279   return IntrinsicAsStubCall(input, context, CodeFactory::ToObject(isolate()));
    280 }
    281 
    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 
    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 
    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 
    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