// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/interpreter/interpreter-intrinsics.h" #include "src/code-factory.h" namespace v8 { namespace internal { namespace interpreter { using compiler::Node; #define __ assembler_-> IntrinsicsHelper::IntrinsicsHelper(InterpreterAssembler* assembler) : isolate_(assembler->isolate()), zone_(assembler->zone()), assembler_(assembler) {} // static bool IntrinsicsHelper::IsSupported(Runtime::FunctionId function_id) { switch (function_id) { #define SUPPORTED(name, lower_case, count) case Runtime::kInline##name: INTRINSICS_LIST(SUPPORTED) return true; #undef SUPPORTED default: return false; } } // static IntrinsicsHelper::IntrinsicId IntrinsicsHelper::FromRuntimeId( Runtime::FunctionId function_id) { switch (function_id) { #define TO_RUNTIME_ID(name, lower_case, count) \ case Runtime::kInline##name: \ return IntrinsicId::k##name; INTRINSICS_LIST(TO_RUNTIME_ID) #undef TO_RUNTIME_ID default: UNREACHABLE(); return static_cast<IntrinsicsHelper::IntrinsicId>(-1); } } // static Runtime::FunctionId IntrinsicsHelper::ToRuntimeId( IntrinsicsHelper::IntrinsicId intrinsic_id) { switch (intrinsic_id) { #define TO_INTRINSIC_ID(name, lower_case, count) \ case IntrinsicId::k##name: \ return Runtime::kInline##name; INTRINSICS_LIST(TO_INTRINSIC_ID) #undef TO_INTRINSIC_ID default: UNREACHABLE(); return static_cast<Runtime::FunctionId>(-1); } } Node* IntrinsicsHelper::InvokeIntrinsic(Node* function_id, Node* context, Node* first_arg_reg, Node* arg_count) { InterpreterAssembler::Label abort(assembler_), end(assembler_); InterpreterAssembler::Variable result(assembler_, MachineRepresentation::kTagged); #define MAKE_LABEL(name, lower_case, count) \ InterpreterAssembler::Label lower_case(assembler_); INTRINSICS_LIST(MAKE_LABEL) #undef MAKE_LABEL #define LABEL_POINTER(name, lower_case, count) &lower_case, InterpreterAssembler::Label* labels[] = {INTRINSICS_LIST(LABEL_POINTER)}; #undef LABEL_POINTER #define CASE(name, lower_case, count) \ static_cast<int32_t>(IntrinsicId::k##name), int32_t cases[] = {INTRINSICS_LIST(CASE)}; #undef CASE __ Switch(function_id, &abort, cases, labels, arraysize(cases)); #define HANDLE_CASE(name, lower_case, expected_arg_count) \ __ Bind(&lower_case); \ if (FLAG_debug_code && expected_arg_count >= 0) { \ AbortIfArgCountMismatch(expected_arg_count, arg_count); \ } \ result.Bind(name(first_arg_reg, arg_count, context)); \ __ Goto(&end); INTRINSICS_LIST(HANDLE_CASE) #undef HANDLE_CASE __ Bind(&abort); { __ Abort(BailoutReason::kUnexpectedFunctionIDForInvokeIntrinsic); result.Bind(__ UndefinedConstant()); __ Goto(&end); } __ Bind(&end); return result.value(); } Node* IntrinsicsHelper::CompareInstanceType(Node* object, int type, InstanceTypeCompareMode mode) { InterpreterAssembler::Variable return_value(assembler_, MachineRepresentation::kTagged); Node* instance_type = __ LoadInstanceType(object); InterpreterAssembler::Label if_true(assembler_), if_false(assembler_), end(assembler_); if (mode == kInstanceTypeEqual) { return __ Word32Equal(instance_type, __ Int32Constant(type)); } else { DCHECK(mode == kInstanceTypeGreaterThanOrEqual); return __ Int32GreaterThanOrEqual(instance_type, __ Int32Constant(type)); } } Node* IntrinsicsHelper::IsInstanceType(Node* input, int type) { InterpreterAssembler::Variable return_value(assembler_, MachineRepresentation::kTagged); InterpreterAssembler::Label if_not_smi(assembler_), return_true(assembler_), return_false(assembler_), end(assembler_); Node* arg = __ LoadRegister(input); __ GotoIf(__ TaggedIsSmi(arg), &return_false); Node* condition = CompareInstanceType(arg, type, kInstanceTypeEqual); __ Branch(condition, &return_true, &return_false); __ Bind(&return_true); { return_value.Bind(__ BooleanConstant(true)); __ Goto(&end); } __ Bind(&return_false); { return_value.Bind(__ BooleanConstant(false)); __ Goto(&end); } __ Bind(&end); return return_value.value(); } Node* IntrinsicsHelper::IsJSReceiver(Node* input, Node* arg_count, Node* context) { InterpreterAssembler::Variable return_value(assembler_, MachineRepresentation::kTagged); InterpreterAssembler::Label return_true(assembler_), return_false(assembler_), end(assembler_); Node* arg = __ LoadRegister(input); __ GotoIf(__ TaggedIsSmi(arg), &return_false); STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE); Node* condition = CompareInstanceType(arg, FIRST_JS_RECEIVER_TYPE, kInstanceTypeGreaterThanOrEqual); __ Branch(condition, &return_true, &return_false); __ Bind(&return_true); { return_value.Bind(__ BooleanConstant(true)); __ Goto(&end); } __ Bind(&return_false); { return_value.Bind(__ BooleanConstant(false)); __ Goto(&end); } __ Bind(&end); return return_value.value(); } Node* IntrinsicsHelper::IsArray(Node* input, Node* arg_count, Node* context) { return IsInstanceType(input, JS_ARRAY_TYPE); } Node* IntrinsicsHelper::IsJSProxy(Node* input, Node* arg_count, Node* context) { return IsInstanceType(input, JS_PROXY_TYPE); } Node* IntrinsicsHelper::IsRegExp(Node* input, Node* arg_count, Node* context) { return IsInstanceType(input, JS_REGEXP_TYPE); } Node* IntrinsicsHelper::IsTypedArray(Node* input, Node* arg_count, Node* context) { return IsInstanceType(input, JS_TYPED_ARRAY_TYPE); } Node* IntrinsicsHelper::IsSmi(Node* input, Node* arg_count, Node* context) { InterpreterAssembler::Variable return_value(assembler_, MachineRepresentation::kTagged); InterpreterAssembler::Label if_smi(assembler_), if_not_smi(assembler_), end(assembler_); Node* arg = __ LoadRegister(input); __ Branch(__ TaggedIsSmi(arg), &if_smi, &if_not_smi); __ Bind(&if_smi); { return_value.Bind(__ BooleanConstant(true)); __ Goto(&end); } __ Bind(&if_not_smi); { return_value.Bind(__ BooleanConstant(false)); __ Goto(&end); } __ Bind(&end); return return_value.value(); } Node* IntrinsicsHelper::IntrinsicAsStubCall(Node* args_reg, Node* context, Callable const& callable) { int param_count = callable.descriptor().GetParameterCount(); Node** args = zone()->NewArray<Node*>(param_count + 1); // 1 for context for (int i = 0; i < param_count; i++) { args[i] = __ LoadRegister(args_reg); args_reg = __ NextRegister(args_reg); } args[param_count] = context; return __ CallStubN(callable, args); } Node* IntrinsicsHelper::HasProperty(Node* input, Node* arg_count, Node* context) { return IntrinsicAsStubCall(input, context, CodeFactory::HasProperty(isolate())); } Node* IntrinsicsHelper::NewObject(Node* input, Node* arg_count, Node* context) { return IntrinsicAsStubCall(input, context, CodeFactory::FastNewObject(isolate())); } Node* IntrinsicsHelper::NumberToString(Node* input, Node* arg_count, Node* context) { return IntrinsicAsStubCall(input, context, CodeFactory::NumberToString(isolate())); } Node* IntrinsicsHelper::RegExpExec(Node* input, Node* arg_count, Node* context) { return IntrinsicAsStubCall(input, context, CodeFactory::RegExpExec(isolate())); } Node* IntrinsicsHelper::SubString(Node* input, Node* arg_count, Node* context) { return IntrinsicAsStubCall(input, context, CodeFactory::SubString(isolate())); } Node* IntrinsicsHelper::ToString(Node* input, Node* arg_count, Node* context) { return IntrinsicAsStubCall(input, context, CodeFactory::ToString(isolate())); } Node* IntrinsicsHelper::ToLength(Node* input, Node* arg_count, Node* context) { return IntrinsicAsStubCall(input, context, CodeFactory::ToLength(isolate())); } Node* IntrinsicsHelper::ToInteger(Node* input, Node* arg_count, Node* context) { return IntrinsicAsStubCall(input, context, CodeFactory::ToInteger(isolate())); } Node* IntrinsicsHelper::ToNumber(Node* input, Node* arg_count, Node* context) { return IntrinsicAsStubCall(input, context, CodeFactory::ToNumber(isolate())); } Node* IntrinsicsHelper::ToObject(Node* input, Node* arg_count, Node* context) { return IntrinsicAsStubCall(input, context, CodeFactory::ToObject(isolate())); } Node* IntrinsicsHelper::Call(Node* args_reg, Node* arg_count, Node* context) { // First argument register contains the function target. Node* function = __ LoadRegister(args_reg); // Receiver is the second runtime call argument. Node* receiver_reg = __ NextRegister(args_reg); Node* receiver_arg = __ RegisterLocation(receiver_reg); // Subtract function and receiver from arg count. Node* function_and_receiver_count = __ Int32Constant(2); Node* target_args_count = __ Int32Sub(arg_count, function_and_receiver_count); if (FLAG_debug_code) { InterpreterAssembler::Label arg_count_positive(assembler_); Node* comparison = __ Int32LessThan(target_args_count, __ Int32Constant(0)); __ GotoUnless(comparison, &arg_count_positive); __ Abort(kWrongArgumentCountForInvokeIntrinsic); __ Goto(&arg_count_positive); __ Bind(&arg_count_positive); } Node* result = __ CallJS(function, context, receiver_arg, target_args_count, TailCallMode::kDisallow); return result; } Node* IntrinsicsHelper::ValueOf(Node* args_reg, Node* arg_count, Node* context) { InterpreterAssembler::Variable return_value(assembler_, MachineRepresentation::kTagged); InterpreterAssembler::Label done(assembler_); Node* object = __ LoadRegister(args_reg); return_value.Bind(object); // If the object is a smi return the object. __ GotoIf(__ TaggedIsSmi(object), &done); // If the object is not a value type, return the object. Node* condition = CompareInstanceType(object, JS_VALUE_TYPE, kInstanceTypeEqual); __ GotoUnless(condition, &done); // If the object is a value type, return the value field. return_value.Bind(__ LoadObjectField(object, JSValue::kValueOffset)); __ Goto(&done); __ Bind(&done); return return_value.value(); } Node* IntrinsicsHelper::ClassOf(Node* args_reg, Node* arg_count, Node* context) { InterpreterAssembler::Variable return_value(assembler_, MachineRepresentation::kTagged); InterpreterAssembler::Label done(assembler_), null(assembler_), function(assembler_), non_function_constructor(assembler_); Node* object = __ LoadRegister(args_reg); // If the object is not a JSReceiver, we return null. __ GotoIf(__ TaggedIsSmi(object), &null); STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); Node* is_js_receiver = CompareInstanceType(object, FIRST_JS_RECEIVER_TYPE, kInstanceTypeGreaterThanOrEqual); __ GotoUnless(is_js_receiver, &null); // Return 'Function' for JSFunction and JSBoundFunction objects. Node* is_function = CompareInstanceType(object, FIRST_FUNCTION_TYPE, kInstanceTypeGreaterThanOrEqual); STATIC_ASSERT(LAST_FUNCTION_TYPE == LAST_TYPE); __ GotoIf(is_function, &function); // Check if the constructor in the map is a JS function. Node* constructor = __ LoadMapConstructor(__ LoadMap(object)); Node* constructor_is_js_function = CompareInstanceType(constructor, JS_FUNCTION_TYPE, kInstanceTypeEqual); __ GotoUnless(constructor_is_js_function, &non_function_constructor); // Grab the instance class name from the constructor function. Node* shared = __ LoadObjectField(constructor, JSFunction::kSharedFunctionInfoOffset); return_value.Bind( __ LoadObjectField(shared, SharedFunctionInfo::kInstanceClassNameOffset)); __ Goto(&done); // Non-JS objects have class null. __ Bind(&null); { return_value.Bind(__ LoadRoot(Heap::kNullValueRootIndex)); __ Goto(&done); } // Functions have class 'Function'. __ Bind(&function); { return_value.Bind(__ LoadRoot(Heap::kFunction_stringRootIndex)); __ Goto(&done); } // Objects with a non-function constructor have class 'Object'. __ Bind(&non_function_constructor); { return_value.Bind(__ LoadRoot(Heap::kObject_stringRootIndex)); __ Goto(&done); } __ Bind(&done); return return_value.value(); } void IntrinsicsHelper::AbortIfArgCountMismatch(int expected, Node* actual) { InterpreterAssembler::Label match(assembler_); Node* comparison = __ Word32Equal(actual, __ Int32Constant(expected)); __ GotoIf(comparison, &match); __ Abort(kWrongArgumentCountForInvokeIntrinsic); __ Goto(&match); __ Bind(&match); } } // namespace interpreter } // namespace internal } // namespace v8