// Copyright 2017 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-generator.h" #include "src/allocation.h" #include "src/builtins/builtins.h" #include "src/code-factory.h" #include "src/frames.h" #include "src/heap/factory-inl.h" #include "src/interpreter/bytecodes.h" #include "src/interpreter/interpreter-assembler.h" #include "src/interpreter/interpreter-intrinsics.h" #include "src/objects-inl.h" #include "src/objects/js-generator.h" #include "src/objects/module.h" namespace v8 { namespace internal { namespace interpreter { using compiler::Node; template using TNode = compiler::TNode; class IntrinsicsGenerator { public: explicit IntrinsicsGenerator(InterpreterAssembler* assembler) : isolate_(assembler->isolate()), zone_(assembler->zone()), assembler_(assembler) {} Node* InvokeIntrinsic(Node* function_id, Node* context, const InterpreterAssembler::RegListNodePair& args); private: enum InstanceTypeCompareMode { kInstanceTypeEqual, kInstanceTypeGreaterThanOrEqual }; Node* IsInstanceType(Node* input, int type); Node* CompareInstanceType(Node* map, int type, InstanceTypeCompareMode mode); Node* IntrinsicAsStubCall(const InterpreterAssembler::RegListNodePair& args, Node* context, Callable const& callable); Node* IntrinsicAsBuiltinCall( const InterpreterAssembler::RegListNodePair& args, Node* context, Builtins::Name name); void AbortIfArgCountMismatch(int expected, compiler::Node* actual); #define DECLARE_INTRINSIC_HELPER(name, lower_case, count) \ Node* name(const InterpreterAssembler::RegListNodePair& args, Node* context); INTRINSICS_LIST(DECLARE_INTRINSIC_HELPER) #undef DECLARE_INTRINSIC_HELPER Isolate* isolate() { return isolate_; } Zone* zone() { return zone_; } Factory* factory() { return isolate()->factory(); } Isolate* isolate_; Zone* zone_; InterpreterAssembler* assembler_; DISALLOW_COPY_AND_ASSIGN(IntrinsicsGenerator); }; Node* GenerateInvokeIntrinsic( InterpreterAssembler* assembler, Node* function_id, Node* context, const InterpreterAssembler::RegListNodePair& args) { IntrinsicsGenerator generator(assembler); return generator.InvokeIntrinsic(function_id, context, args); } #define __ assembler_-> Node* IntrinsicsGenerator::InvokeIntrinsic( Node* function_id, Node* context, const InterpreterAssembler::RegListNodePair& args) { 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(IntrinsicsHelper::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, args.reg_count()); \ } \ Node* value = name(args, context); \ if (value) { \ result.Bind(value); \ __ Goto(&end); \ } \ } INTRINSICS_LIST(HANDLE_CASE) #undef HANDLE_CASE __ BIND(&abort); { __ Abort(AbortReason::kUnexpectedFunctionIDForInvokeIntrinsic); result.Bind(__ UndefinedConstant()); __ Goto(&end); } __ BIND(&end); return result.value(); } Node* IntrinsicsGenerator::CompareInstanceType(Node* object, int type, InstanceTypeCompareMode mode) { Node* instance_type = __ LoadInstanceType(object); if (mode == kInstanceTypeEqual) { return __ Word32Equal(instance_type, __ Int32Constant(type)); } else { DCHECK_EQ(mode, kInstanceTypeGreaterThanOrEqual); return __ Int32GreaterThanOrEqual(instance_type, __ Int32Constant(type)); } } Node* IntrinsicsGenerator::IsInstanceType(Node* input, int type) { TNode result = __ Select( __ TaggedIsSmi(input), [=] { return __ FalseConstant(); }, [=] { return __ SelectBooleanConstant( CompareInstanceType(input, type, kInstanceTypeEqual)); }); return result; } Node* IntrinsicsGenerator::IsJSReceiver( const InterpreterAssembler::RegListNodePair& args, Node* context) { Node* input = __ LoadRegisterFromRegisterList(args, 0); TNode result = __ Select( __ TaggedIsSmi(input), [=] { return __ FalseConstant(); }, [=] { return __ SelectBooleanConstant(__ IsJSReceiver(input)); }); return result; } Node* IntrinsicsGenerator::IsArray( const InterpreterAssembler::RegListNodePair& args, Node* context) { Node* input = __ LoadRegisterFromRegisterList(args, 0); return IsInstanceType(input, JS_ARRAY_TYPE); } Node* IntrinsicsGenerator::IsTypedArray( const InterpreterAssembler::RegListNodePair& args, Node* context) { Node* input = __ LoadRegisterFromRegisterList(args, 0); return IsInstanceType(input, JS_TYPED_ARRAY_TYPE); } Node* IntrinsicsGenerator::IsSmi( const InterpreterAssembler::RegListNodePair& args, Node* context) { Node* input = __ LoadRegisterFromRegisterList(args, 0); return __ SelectBooleanConstant(__ TaggedIsSmi(input)); } Node* IntrinsicsGenerator::IntrinsicAsStubCall( const InterpreterAssembler::RegListNodePair& args, Node* context, Callable const& callable) { int param_count = callable.descriptor().GetParameterCount(); int input_count = param_count + 2; // +2 for target and context Node** stub_args = zone()->NewArray(input_count); int index = 0; stub_args[index++] = __ HeapConstant(callable.code()); for (int i = 0; i < param_count; i++) { stub_args[index++] = __ LoadRegisterFromRegisterList(args, i); } stub_args[index++] = context; return __ CallStubN(callable.descriptor(), 1, input_count, stub_args); } Node* IntrinsicsGenerator::IntrinsicAsBuiltinCall( const InterpreterAssembler::RegListNodePair& args, Node* context, Builtins::Name name) { Callable callable = Builtins::CallableFor(isolate_, name); return IntrinsicAsStubCall(args, context, callable); } Node* IntrinsicsGenerator::CreateIterResultObject( const InterpreterAssembler::RegListNodePair& args, Node* context) { return IntrinsicAsStubCall( args, context, Builtins::CallableFor(isolate(), Builtins::kCreateIterResultObject)); } Node* IntrinsicsGenerator::HasProperty( const InterpreterAssembler::RegListNodePair& args, Node* context) { return IntrinsicAsStubCall( args, context, Builtins::CallableFor(isolate(), Builtins::kHasProperty)); } Node* IntrinsicsGenerator::RejectPromise( const InterpreterAssembler::RegListNodePair& args, Node* context) { return IntrinsicAsStubCall( args, context, Builtins::CallableFor(isolate(), Builtins::kRejectPromise)); } Node* IntrinsicsGenerator::ResolvePromise( const InterpreterAssembler::RegListNodePair& args, Node* context) { return IntrinsicAsStubCall( args, context, Builtins::CallableFor(isolate(), Builtins::kResolvePromise)); } Node* IntrinsicsGenerator::ToString( const InterpreterAssembler::RegListNodePair& args, Node* context) { return IntrinsicAsStubCall( args, context, Builtins::CallableFor(isolate(), Builtins::kToString)); } Node* IntrinsicsGenerator::ToLength( const InterpreterAssembler::RegListNodePair& args, Node* context) { return IntrinsicAsStubCall( args, context, Builtins::CallableFor(isolate(), Builtins::kToLength)); } Node* IntrinsicsGenerator::ToObject( const InterpreterAssembler::RegListNodePair& args, Node* context) { return IntrinsicAsStubCall( args, context, Builtins::CallableFor(isolate(), Builtins::kToObject)); } Node* IntrinsicsGenerator::Call( const InterpreterAssembler::RegListNodePair& args, Node* context) { // First argument register contains the function target. Node* function = __ LoadRegisterFromRegisterList(args, 0); // The arguments for the target function are from the second runtime call // argument. InterpreterAssembler::RegListNodePair target_args( __ RegisterLocationInRegisterList(args, 1), __ Int32Sub(args.reg_count(), __ Int32Constant(1))); if (FLAG_debug_code) { InterpreterAssembler::Label arg_count_positive(assembler_); Node* comparison = __ Int32LessThan(target_args.reg_count(), __ Int32Constant(0)); __ GotoIfNot(comparison, &arg_count_positive); __ Abort(AbortReason::kWrongArgumentCountForInvokeIntrinsic); __ Goto(&arg_count_positive); __ BIND(&arg_count_positive); } __ CallJSAndDispatch(function, context, target_args, ConvertReceiverMode::kAny); return nullptr; // We never return from the CallJSAndDispatch above. } Node* IntrinsicsGenerator::CreateAsyncFromSyncIterator( const InterpreterAssembler::RegListNodePair& args, Node* context) { InterpreterAssembler::Label not_receiver( assembler_, InterpreterAssembler::Label::kDeferred); InterpreterAssembler::Label done(assembler_); InterpreterAssembler::Variable return_value(assembler_, MachineRepresentation::kTagged); Node* sync_iterator = __ LoadRegisterFromRegisterList(args, 0); __ GotoIf(__ TaggedIsSmi(sync_iterator), ¬_receiver); __ GotoIfNot(__ IsJSReceiver(sync_iterator), ¬_receiver); Node* const next = __ GetProperty(context, sync_iterator, factory()->next_string()); Node* const native_context = __ LoadNativeContext(context); Node* const map = __ LoadContextElement( native_context, Context::ASYNC_FROM_SYNC_ITERATOR_MAP_INDEX); Node* const iterator = __ AllocateJSObjectFromMap(map); __ StoreObjectFieldNoWriteBarrier( iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset, sync_iterator); __ StoreObjectFieldNoWriteBarrier(iterator, JSAsyncFromSyncIterator::kNextOffset, next); return_value.Bind(iterator); __ Goto(&done); __ BIND(¬_receiver); { return_value.Bind( __ CallRuntime(Runtime::kThrowSymbolIteratorInvalid, context)); // Unreachable due to the Throw in runtime call. __ Goto(&done); } __ BIND(&done); return return_value.value(); } Node* IntrinsicsGenerator::CreateJSGeneratorObject( const InterpreterAssembler::RegListNodePair& args, Node* context) { return IntrinsicAsBuiltinCall(args, context, Builtins::kCreateGeneratorObject); } Node* IntrinsicsGenerator::GeneratorGetResumeMode( const InterpreterAssembler::RegListNodePair& args, Node* context) { Node* generator = __ LoadRegisterFromRegisterList(args, 0); Node* const value = __ LoadObjectField(generator, JSGeneratorObject::kResumeModeOffset); return value; } Node* IntrinsicsGenerator::GeneratorClose( const InterpreterAssembler::RegListNodePair& args, Node* context) { Node* generator = __ LoadRegisterFromRegisterList(args, 0); __ StoreObjectFieldNoWriteBarrier( generator, JSGeneratorObject::kContinuationOffset, __ SmiConstant(JSGeneratorObject::kGeneratorClosed)); return __ UndefinedConstant(); } Node* IntrinsicsGenerator::GetImportMetaObject( const InterpreterAssembler::RegListNodePair& args, Node* context) { Node* const module_context = __ LoadModuleContext(context); Node* const module = __ LoadContextElement(module_context, Context::EXTENSION_INDEX); Node* const import_meta = __ LoadObjectField(module, Module::kImportMetaOffset); InterpreterAssembler::Variable return_value(assembler_, MachineRepresentation::kTagged); return_value.Bind(import_meta); InterpreterAssembler::Label end(assembler_); __ GotoIfNot(__ IsTheHole(import_meta), &end); return_value.Bind(__ CallRuntime(Runtime::kGetImportMetaObject, context)); __ Goto(&end); __ BIND(&end); return return_value.value(); } Node* IntrinsicsGenerator::AsyncGeneratorReject( const InterpreterAssembler::RegListNodePair& args, Node* context) { return IntrinsicAsBuiltinCall(args, context, Builtins::kAsyncGeneratorReject); } Node* IntrinsicsGenerator::AsyncGeneratorResolve( const InterpreterAssembler::RegListNodePair& args, Node* context) { return IntrinsicAsBuiltinCall(args, context, Builtins::kAsyncGeneratorResolve); } Node* IntrinsicsGenerator::AsyncGeneratorYield( const InterpreterAssembler::RegListNodePair& args, Node* context) { return IntrinsicAsBuiltinCall(args, context, Builtins::kAsyncGeneratorYield); } void IntrinsicsGenerator::AbortIfArgCountMismatch(int expected, Node* actual) { InterpreterAssembler::Label match(assembler_); Node* comparison = __ Word32Equal(actual, __ Int32Constant(expected)); __ GotoIf(comparison, &match); __ Abort(AbortReason::kWrongArgumentCountForInvokeIntrinsic); __ Goto(&match); __ BIND(&match); } #undef __ } // namespace interpreter } // namespace internal } // namespace v8