HELLO·Android
系统源代码
IT资讯
技术文章
我的收藏
注册
登录
-
我收藏的文章
创建代码块
我的代码块
我的账号
Android 10
|
10.0.0_r6
下载
查看原文件
收藏
根目录
external
v8
src
interpreter
interpreter-generator.cc
// 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-generator.h" #include
#include
#include "src/builtins/builtins-arguments-gen.h" #include "src/builtins/builtins-constructor-gen.h" #include "src/code-events.h" #include "src/code-factory.h" #include "src/debug/debug.h" #include "src/ic/accessor-assembler.h" #include "src/ic/binary-op-assembler.h" #include "src/interpreter/bytecode-flags.h" #include "src/interpreter/bytecodes.h" #include "src/interpreter/interpreter-assembler.h" #include "src/interpreter/interpreter-intrinsics-generator.h" #include "src/objects-inl.h" #include "src/objects/js-generator.h" #include "src/objects/module.h" namespace v8 { namespace internal { namespace interpreter { namespace { using compiler::Node; typedef CodeStubAssembler::Label Label; typedef CodeStubAssembler::Variable Variable; #define IGNITION_HANDLER(Name, BaseAssembler) \ class Name##Assembler : public BaseAssembler { \ public: \ explicit Name##Assembler(compiler::CodeAssemblerState* state, \ Bytecode bytecode, OperandScale scale) \ : BaseAssembler(state, bytecode, scale) {} \ static void Generate(compiler::CodeAssemblerState* state, \ OperandScale scale); \ \ private: \ void GenerateImpl(); \ DISALLOW_COPY_AND_ASSIGN(Name##Assembler); \ }; \ void Name##Assembler::Generate(compiler::CodeAssemblerState* state, \ OperandScale scale) { \ Name##Assembler assembler(state, Bytecode::k##Name, scale); \ state->SetInitialDebugInformation(#Name, __FILE__, __LINE__); \ assembler.GenerateImpl(); \ } \ void Name##Assembler::GenerateImpl() // LdaZero // // Load literal '0' into the accumulator. IGNITION_HANDLER(LdaZero, InterpreterAssembler) { Node* zero_value = NumberConstant(0.0); SetAccumulator(zero_value); Dispatch(); } // LdaSmi
// // Load an integer literal into the accumulator as a Smi. IGNITION_HANDLER(LdaSmi, InterpreterAssembler) { Node* smi_int = BytecodeOperandImmSmi(0); SetAccumulator(smi_int); Dispatch(); } // LdaConstant
// // Load constant literal at |idx| in the constant pool into the accumulator. IGNITION_HANDLER(LdaConstant, InterpreterAssembler) { Node* constant = LoadConstantPoolEntryAtOperandIndex(0); SetAccumulator(constant); Dispatch(); } // LdaUndefined // // Load Undefined into the accumulator. IGNITION_HANDLER(LdaUndefined, InterpreterAssembler) { SetAccumulator(UndefinedConstant()); Dispatch(); } // LdaNull // // Load Null into the accumulator. IGNITION_HANDLER(LdaNull, InterpreterAssembler) { SetAccumulator(NullConstant()); Dispatch(); } // LdaTheHole // // Load TheHole into the accumulator. IGNITION_HANDLER(LdaTheHole, InterpreterAssembler) { SetAccumulator(TheHoleConstant()); Dispatch(); } // LdaTrue // // Load True into the accumulator. IGNITION_HANDLER(LdaTrue, InterpreterAssembler) { SetAccumulator(TrueConstant()); Dispatch(); } // LdaFalse // // Load False into the accumulator. IGNITION_HANDLER(LdaFalse, InterpreterAssembler) { SetAccumulator(FalseConstant()); Dispatch(); } // Ldar
// // Load accumulator with value from register
. IGNITION_HANDLER(Ldar, InterpreterAssembler) { Node* value = LoadRegisterAtOperandIndex(0); SetAccumulator(value); Dispatch(); } // Star
// // Store accumulator to register
. IGNITION_HANDLER(Star, InterpreterAssembler) { Node* accumulator = GetAccumulator(); StoreRegisterAtOperandIndex(accumulator, 0); Dispatch(); } // Mov
// // Stores the value of register
to register
. IGNITION_HANDLER(Mov, InterpreterAssembler) { Node* src_value = LoadRegisterAtOperandIndex(0); StoreRegisterAtOperandIndex(src_value, 1); Dispatch(); } class InterpreterLoadGlobalAssembler : public InterpreterAssembler { public: InterpreterLoadGlobalAssembler(CodeAssemblerState* state, Bytecode bytecode, OperandScale operand_scale) : InterpreterAssembler(state, bytecode, operand_scale) {} void LdaGlobal(int slot_operand_index, int name_operand_index, TypeofMode typeof_mode) { TNode
feedback_vector = LoadFeedbackVector(); Node* feedback_slot = BytecodeOperandIdx(slot_operand_index); AccessorAssembler accessor_asm(state()); ExitPoint exit_point(this, [=](Node* result) { SetAccumulator(result); Dispatch(); }); LazyNode
lazy_context = [=] { return CAST(GetContext()); }; LazyNode
lazy_name = [=] { Node* name = LoadConstantPoolEntryAtOperandIndex(name_operand_index); return CAST(name); }; accessor_asm.LoadGlobalIC(feedback_vector, feedback_slot, lazy_context, lazy_name, typeof_mode, &exit_point, CodeStubAssembler::INTPTR_PARAMETERS); } }; // LdaGlobal
// // Load the global with name in constant pool entry
into the // accumulator using FeedBackVector slot
outside of a typeof. IGNITION_HANDLER(LdaGlobal, InterpreterLoadGlobalAssembler) { static const int kNameOperandIndex = 0; static const int kSlotOperandIndex = 1; LdaGlobal(kSlotOperandIndex, kNameOperandIndex, NOT_INSIDE_TYPEOF); } // LdaGlobalInsideTypeof
// // Load the global with name in constant pool entry
into the // accumulator using FeedBackVector slot
inside of a typeof. IGNITION_HANDLER(LdaGlobalInsideTypeof, InterpreterLoadGlobalAssembler) { static const int kNameOperandIndex = 0; static const int kSlotOperandIndex = 1; LdaGlobal(kSlotOperandIndex, kNameOperandIndex, INSIDE_TYPEOF); } // StaGlobal
// // Store the value in the accumulator into the global with name in constant pool // entry
using FeedBackVector slot
. IGNITION_HANDLER(StaGlobal, InterpreterAssembler) { Node* context = GetContext(); // Store the global via the StoreGlobalIC. Node* name = LoadConstantPoolEntryAtOperandIndex(0); Node* value = GetAccumulator(); Node* raw_slot = BytecodeOperandIdx(1); Node* smi_slot = SmiTag(raw_slot); Node* feedback_vector = LoadFeedbackVector(); CallBuiltin(Builtins::kStoreGlobalIC, context, name, value, smi_slot, feedback_vector); Dispatch(); } // LdaContextSlot
// // Load the object in |slot_index| of the context at |depth| in the context // chain starting at |context| into the accumulator. IGNITION_HANDLER(LdaContextSlot, InterpreterAssembler) { Node* context = LoadRegisterAtOperandIndex(0); Node* slot_index = BytecodeOperandIdx(1); Node* depth = BytecodeOperandUImm(2); Node* slot_context = GetContextAtDepth(context, depth); Node* result = LoadContextElement(slot_context, slot_index); SetAccumulator(result); Dispatch(); } // LdaImmutableContextSlot
// // Load the object in |slot_index| of the context at |depth| in the context // chain starting at |context| into the accumulator. IGNITION_HANDLER(LdaImmutableContextSlot, InterpreterAssembler) { Node* context = LoadRegisterAtOperandIndex(0); Node* slot_index = BytecodeOperandIdx(1); Node* depth = BytecodeOperandUImm(2); Node* slot_context = GetContextAtDepth(context, depth); Node* result = LoadContextElement(slot_context, slot_index); SetAccumulator(result); Dispatch(); } // LdaCurrentContextSlot
// // Load the object in |slot_index| of the current context into the accumulator. IGNITION_HANDLER(LdaCurrentContextSlot, InterpreterAssembler) { Node* slot_index = BytecodeOperandIdx(0); Node* slot_context = GetContext(); Node* result = LoadContextElement(slot_context, slot_index); SetAccumulator(result); Dispatch(); } // LdaImmutableCurrentContextSlot
// // Load the object in |slot_index| of the current context into the accumulator. IGNITION_HANDLER(LdaImmutableCurrentContextSlot, InterpreterAssembler) { Node* slot_index = BytecodeOperandIdx(0); Node* slot_context = GetContext(); Node* result = LoadContextElement(slot_context, slot_index); SetAccumulator(result); Dispatch(); } // StaContextSlot
// // Stores the object in the accumulator into |slot_index| of the context at // |depth| in the context chain starting at |context|. IGNITION_HANDLER(StaContextSlot, InterpreterAssembler) { Node* value = GetAccumulator(); Node* context = LoadRegisterAtOperandIndex(0); Node* slot_index = BytecodeOperandIdx(1); Node* depth = BytecodeOperandUImm(2); Node* slot_context = GetContextAtDepth(context, depth); StoreContextElement(slot_context, slot_index, value); Dispatch(); } // StaCurrentContextSlot
// // Stores the object in the accumulator into |slot_index| of the current // context. IGNITION_HANDLER(StaCurrentContextSlot, InterpreterAssembler) { Node* value = GetAccumulator(); Node* slot_index = BytecodeOperandIdx(0); Node* slot_context = GetContext(); StoreContextElement(slot_context, slot_index, value); Dispatch(); } // LdaLookupSlot
// // Lookup the object with the name in constant pool entry |name_index| // dynamically. IGNITION_HANDLER(LdaLookupSlot, InterpreterAssembler) { Node* name = LoadConstantPoolEntryAtOperandIndex(0); Node* context = GetContext(); Node* result = CallRuntime(Runtime::kLoadLookupSlot, context, name); SetAccumulator(result); Dispatch(); } // LdaLookupSlotInsideTypeof
// // Lookup the object with the name in constant pool entry |name_index| // dynamically without causing a NoReferenceError. IGNITION_HANDLER(LdaLookupSlotInsideTypeof, InterpreterAssembler) { Node* name = LoadConstantPoolEntryAtOperandIndex(0); Node* context = GetContext(); Node* result = CallRuntime(Runtime::kLoadLookupSlotInsideTypeof, context, name); SetAccumulator(result); Dispatch(); } class InterpreterLookupContextSlotAssembler : public InterpreterAssembler { public: InterpreterLookupContextSlotAssembler(CodeAssemblerState* state, Bytecode bytecode, OperandScale operand_scale) : InterpreterAssembler(state, bytecode, operand_scale) {} void LookupContextSlot(Runtime::FunctionId function_id) { Node* context = GetContext(); Node* slot_index = BytecodeOperandIdx(1); Node* depth = BytecodeOperandUImm(2); Label slowpath(this, Label::kDeferred); // Check for context extensions to allow the fast path. GotoIfHasContextExtensionUpToDepth(context, depth, &slowpath); // Fast path does a normal load context. { Node* slot_context = GetContextAtDepth(context, depth); Node* result = LoadContextElement(slot_context, slot_index); SetAccumulator(result); Dispatch(); } // Slow path when we have to call out to the runtime. BIND(&slowpath); { Node* name = LoadConstantPoolEntryAtOperandIndex(0); Node* result = CallRuntime(function_id, context, name); SetAccumulator(result); Dispatch(); } } }; // LdaLookupSlot
// // Lookup the object with the name in constant pool entry |name_index| // dynamically. IGNITION_HANDLER(LdaLookupContextSlot, InterpreterLookupContextSlotAssembler) { LookupContextSlot(Runtime::kLoadLookupSlot); } // LdaLookupSlotInsideTypeof
// // Lookup the object with the name in constant pool entry |name_index| // dynamically without causing a NoReferenceError. IGNITION_HANDLER(LdaLookupContextSlotInsideTypeof, InterpreterLookupContextSlotAssembler) { LookupContextSlot(Runtime::kLoadLookupSlotInsideTypeof); } class InterpreterLookupGlobalAssembler : public InterpreterLoadGlobalAssembler { public: InterpreterLookupGlobalAssembler(CodeAssemblerState* state, Bytecode bytecode, OperandScale operand_scale) : InterpreterLoadGlobalAssembler(state, bytecode, operand_scale) {} void LookupGlobalSlot(Runtime::FunctionId function_id) { Node* context = GetContext(); Node* depth = BytecodeOperandUImm(2); Label slowpath(this, Label::kDeferred); // Check for context extensions to allow the fast path GotoIfHasContextExtensionUpToDepth(context, depth, &slowpath); // Fast path does a normal load global { static const int kNameOperandIndex = 0; static const int kSlotOperandIndex = 1; TypeofMode typeof_mode = function_id == Runtime::kLoadLookupSlotInsideTypeof ? INSIDE_TYPEOF : NOT_INSIDE_TYPEOF; LdaGlobal(kSlotOperandIndex, kNameOperandIndex, typeof_mode); } // Slow path when we have to call out to the runtime BIND(&slowpath); { Node* name = LoadConstantPoolEntryAtOperandIndex(0); Node* result = CallRuntime(function_id, context, name); SetAccumulator(result); Dispatch(); } } }; // LdaLookupGlobalSlot
// // Lookup the object with the name in constant pool entry |name_index| // dynamically. IGNITION_HANDLER(LdaLookupGlobalSlot, InterpreterLookupGlobalAssembler) { LookupGlobalSlot(Runtime::kLoadLookupSlot); } // LdaLookupGlobalSlotInsideTypeof
// // Lookup the object with the name in constant pool entry |name_index| // dynamically without causing a NoReferenceError. IGNITION_HANDLER(LdaLookupGlobalSlotInsideTypeof, InterpreterLookupGlobalAssembler) { LookupGlobalSlot(Runtime::kLoadLookupSlotInsideTypeof); } // StaLookupSlotSloppy
// // Store the object in accumulator to the object with the name in constant // pool entry |name_index|. IGNITION_HANDLER(StaLookupSlot, InterpreterAssembler) { Node* value = GetAccumulator(); Node* name = LoadConstantPoolEntryAtOperandIndex(0); Node* bytecode_flags = BytecodeOperandFlag(1); Node* context = GetContext(); Variable var_result(this, MachineRepresentation::kTagged); Label sloppy(this), strict(this), end(this); DCHECK_EQ(0, LanguageMode::kSloppy); DCHECK_EQ(1, LanguageMode::kStrict); DCHECK_EQ(0, static_cast
(LookupHoistingMode::kNormal)); DCHECK_EQ(1, static_cast
(LookupHoistingMode::kLegacySloppy)); Branch(IsSetWord32
(bytecode_flags), &strict, &sloppy); BIND(&strict); { CSA_ASSERT(this, IsClearWord32
( bytecode_flags)); var_result.Bind( CallRuntime(Runtime::kStoreLookupSlot_Strict, context, name, value)); Goto(&end); } BIND(&sloppy); { Label hoisting(this), ordinary(this); Branch(IsSetWord32
( bytecode_flags), &hoisting, &ordinary); BIND(&hoisting); { var_result.Bind(CallRuntime(Runtime::kStoreLookupSlot_SloppyHoisting, context, name, value)); Goto(&end); } BIND(&ordinary); { var_result.Bind( CallRuntime(Runtime::kStoreLookupSlot_Sloppy, context, name, value)); Goto(&end); } } BIND(&end); { SetAccumulator(var_result.value()); Dispatch(); } } // LdaNamedProperty
// // Calls the LoadIC at FeedBackVector slot
for
and the name at // constant pool entry
. IGNITION_HANDLER(LdaNamedProperty, InterpreterAssembler) { Node* feedback_vector = LoadFeedbackVector(); Node* feedback_slot = BytecodeOperandIdx(2); Node* smi_slot = SmiTag(feedback_slot); // Load receiver. Node* recv = LoadRegisterAtOperandIndex(0); // Load the name. // TODO(jgruber): Not needed for monomorphic smi handler constant/field case. Node* name = LoadConstantPoolEntryAtOperandIndex(1); Node* context = GetContext(); Label done(this); Variable var_result(this, MachineRepresentation::kTagged); ExitPoint exit_point(this, &done, &var_result); AccessorAssembler::LoadICParameters params(context, recv, name, smi_slot, feedback_vector); AccessorAssembler accessor_asm(state()); accessor_asm.LoadIC_BytecodeHandler(¶ms, &exit_point); BIND(&done); { SetAccumulator(var_result.value()); Dispatch(); } } // KeyedLoadIC
// // Calls the KeyedLoadIC at FeedBackVector slot
for
and the key // in the accumulator. IGNITION_HANDLER(LdaKeyedProperty, InterpreterAssembler) { Node* object = LoadRegisterAtOperandIndex(0); Node* name = GetAccumulator(); Node* raw_slot = BytecodeOperandIdx(1); Node* smi_slot = SmiTag(raw_slot); Node* feedback_vector = LoadFeedbackVector(); Node* context = GetContext(); Node* result = CallBuiltin(Builtins::kKeyedLoadIC, context, object, name, smi_slot, feedback_vector); SetAccumulator(result); Dispatch(); } class InterpreterStoreNamedPropertyAssembler : public InterpreterAssembler { public: InterpreterStoreNamedPropertyAssembler(CodeAssemblerState* state, Bytecode bytecode, OperandScale operand_scale) : InterpreterAssembler(state, bytecode, operand_scale) {} void StaNamedProperty(Callable ic) { Node* code_target = HeapConstant(ic.code()); Node* object = LoadRegisterAtOperandIndex(0); Node* name = LoadConstantPoolEntryAtOperandIndex(1); Node* value = GetAccumulator(); Node* raw_slot = BytecodeOperandIdx(2); Node* smi_slot = SmiTag(raw_slot); Node* feedback_vector = LoadFeedbackVector(); Node* context = GetContext(); Node* result = CallStub(ic.descriptor(), code_target, context, object, name, value, smi_slot, feedback_vector); // To avoid special logic in the deoptimizer to re-materialize the value in // the accumulator, we overwrite the accumulator after the IC call. It // doesn't really matter what we write to the accumulator here, since we // restore to the correct value on the outside. Storing the result means we // don't need to keep unnecessary state alive across the callstub. SetAccumulator(result); Dispatch(); } }; // StaNamedProperty
// // Calls the StoreIC at FeedBackVector slot
for
and // the name in constant pool entry
with the value in the // accumulator. IGNITION_HANDLER(StaNamedProperty, InterpreterStoreNamedPropertyAssembler) { Callable ic = Builtins::CallableFor(isolate(), Builtins::kStoreIC); StaNamedProperty(ic); } // StaNamedOwnProperty
// // Calls the StoreOwnIC at FeedBackVector slot
for
and // the name in constant pool entry
with the value in the // accumulator. IGNITION_HANDLER(StaNamedOwnProperty, InterpreterStoreNamedPropertyAssembler) { Callable ic = CodeFactory::StoreOwnICInOptimizedCode(isolate()); StaNamedProperty(ic); } // StaKeyedProperty
// // Calls the KeyedStoreIC at FeedbackVector slot
for
and // the key
with the value in the accumulator. IGNITION_HANDLER(StaKeyedProperty, InterpreterAssembler) { Node* object = LoadRegisterAtOperandIndex(0); Node* name = LoadRegisterAtOperandIndex(1); Node* value = GetAccumulator(); Node* raw_slot = BytecodeOperandIdx(2); Node* smi_slot = SmiTag(raw_slot); Node* feedback_vector = LoadFeedbackVector(); Node* context = GetContext(); Node* result = CallBuiltin(Builtins::kKeyedStoreIC, context, object, name, value, smi_slot, feedback_vector); // To avoid special logic in the deoptimizer to re-materialize the value in // the accumulator, we overwrite the accumulator after the IC call. It // doesn't really matter what we write to the accumulator here, since we // restore to the correct value on the outside. Storing the result means we // don't need to keep unnecessary state alive across the callstub. SetAccumulator(result); Dispatch(); } // StaInArrayLiteral
// // Calls the StoreInArrayLiteralIC at FeedbackVector slot
for
and // the key
with the value in the accumulator. IGNITION_HANDLER(StaInArrayLiteral, InterpreterAssembler) { Node* array = LoadRegisterAtOperandIndex(0); Node* index = LoadRegisterAtOperandIndex(1); Node* value = GetAccumulator(); Node* raw_slot = BytecodeOperandIdx(2); Node* smi_slot = SmiTag(raw_slot); Node* feedback_vector = LoadFeedbackVector(); Node* context = GetContext(); Node* result = CallBuiltin(Builtins::kStoreInArrayLiteralIC, context, array, index, value, smi_slot, feedback_vector); // To avoid special logic in the deoptimizer to re-materialize the value in // the accumulator, we overwrite the accumulator after the IC call. It // doesn't really matter what we write to the accumulator here, since we // restore to the correct value on the outside. Storing the result means we // don't need to keep unnecessary state alive across the callstub. SetAccumulator(result); Dispatch(); } // StaDataPropertyInLiteral
// // Define a property
with value from the accumulator in
. // Property attributes and whether set_function_name are stored in // DataPropertyInLiteralFlags
. // // This definition is not observable and is used only for definitions // in object or class literals. IGNITION_HANDLER(StaDataPropertyInLiteral, InterpreterAssembler) { Node* object = LoadRegisterAtOperandIndex(0); Node* name = LoadRegisterAtOperandIndex(1); Node* value = GetAccumulator(); Node* flags = SmiFromInt32(BytecodeOperandFlag(2)); Node* vector_index = SmiTag(BytecodeOperandIdx(3)); Node* feedback_vector = LoadFeedbackVector(); Node* context = GetContext(); CallRuntime(Runtime::kDefineDataPropertyInLiteral, context, object, name, value, flags, feedback_vector, vector_index); Dispatch(); } IGNITION_HANDLER(CollectTypeProfile, InterpreterAssembler) { Node* position = BytecodeOperandImmSmi(0); Node* value = GetAccumulator(); Node* feedback_vector = LoadFeedbackVector(); Node* context = GetContext(); CallRuntime(Runtime::kCollectTypeProfile, context, position, value, feedback_vector); Dispatch(); } // LdaModuleVariable
// // Load the contents of a module variable into the accumulator. The variable is // identified by
.
is the depth of the current context // relative to the module context. IGNITION_HANDLER(LdaModuleVariable, InterpreterAssembler) { Node* cell_index = BytecodeOperandImmIntPtr(0); Node* depth = BytecodeOperandUImm(1); Node* module_context = GetContextAtDepth(GetContext(), depth); Node* module = LoadContextElement(module_context, Context::EXTENSION_INDEX); Label if_export(this), if_import(this), end(this); Branch(IntPtrGreaterThan(cell_index, IntPtrConstant(0)), &if_export, &if_import); BIND(&if_export); { TNode
regular_exports = CAST(LoadObjectField(module, Module::kRegularExportsOffset)); // The actual array index is (cell_index - 1). Node* export_index = IntPtrSub(cell_index, IntPtrConstant(1)); Node* cell = LoadFixedArrayElement(regular_exports, export_index); SetAccumulator(LoadObjectField(cell, Cell::kValueOffset)); Goto(&end); } BIND(&if_import); { TNode
regular_imports = CAST(LoadObjectField(module, Module::kRegularImportsOffset)); // The actual array index is (-cell_index - 1). Node* import_index = IntPtrSub(IntPtrConstant(-1), cell_index); Node* cell = LoadFixedArrayElement(regular_imports, import_index); SetAccumulator(LoadObjectField(cell, Cell::kValueOffset)); Goto(&end); } BIND(&end); Dispatch(); } // StaModuleVariable
// // Store accumulator to the module variable identified by
. //
is the depth of the current context relative to the module context. IGNITION_HANDLER(StaModuleVariable, InterpreterAssembler) { Node* value = GetAccumulator(); Node* cell_index = BytecodeOperandImmIntPtr(0); Node* depth = BytecodeOperandUImm(1); Node* module_context = GetContextAtDepth(GetContext(), depth); Node* module = LoadContextElement(module_context, Context::EXTENSION_INDEX); Label if_export(this), if_import(this), end(this); Branch(IntPtrGreaterThan(cell_index, IntPtrConstant(0)), &if_export, &if_import); BIND(&if_export); { TNode
regular_exports = CAST(LoadObjectField(module, Module::kRegularExportsOffset)); // The actual array index is (cell_index - 1). Node* export_index = IntPtrSub(cell_index, IntPtrConstant(1)); Node* cell = LoadFixedArrayElement(regular_exports, export_index); StoreObjectField(cell, Cell::kValueOffset, value); Goto(&end); } BIND(&if_import); { // Not supported (probably never). Abort(AbortReason::kUnsupportedModuleOperation); Goto(&end); } BIND(&end); Dispatch(); } // PushContext
// // Saves the current context in
, and pushes the accumulator as the // new current context. IGNITION_HANDLER(PushContext, InterpreterAssembler) { Node* new_context = GetAccumulator(); Node* old_context = GetContext(); StoreRegisterAtOperandIndex(old_context, 0); SetContext(new_context); Dispatch(); } // PopContext
// // Pops the current context and sets
as the new context. IGNITION_HANDLER(PopContext, InterpreterAssembler) { Node* context = LoadRegisterAtOperandIndex(0); SetContext(context); Dispatch(); } class InterpreterBinaryOpAssembler : public InterpreterAssembler { public: InterpreterBinaryOpAssembler(CodeAssemblerState* state, Bytecode bytecode, OperandScale operand_scale) : InterpreterAssembler(state, bytecode, operand_scale) {} typedef Node* (BinaryOpAssembler::*BinaryOpGenerator)(Node* context, Node* left, Node* right, Node* slot, Node* vector, bool lhs_is_smi); void BinaryOpWithFeedback(BinaryOpGenerator generator) { Node* lhs = LoadRegisterAtOperandIndex(0); Node* rhs = GetAccumulator(); Node* context = GetContext(); Node* slot_index = BytecodeOperandIdx(1); Node* feedback_vector = LoadFeedbackVector(); BinaryOpAssembler binop_asm(state()); Node* result = (binop_asm.*generator)(context, lhs, rhs, slot_index, feedback_vector, false); SetAccumulator(result); Dispatch(); } void BinaryOpSmiWithFeedback(BinaryOpGenerator generator) { Node* lhs = GetAccumulator(); Node* rhs = BytecodeOperandImmSmi(0); Node* context = GetContext(); Node* slot_index = BytecodeOperandIdx(1); Node* feedback_vector = LoadFeedbackVector(); BinaryOpAssembler binop_asm(state()); Node* result = (binop_asm.*generator)(context, lhs, rhs, slot_index, feedback_vector, true); SetAccumulator(result); Dispatch(); } }; // Add
// // Add register
to accumulator. IGNITION_HANDLER(Add, InterpreterBinaryOpAssembler) { BinaryOpWithFeedback(&BinaryOpAssembler::Generate_AddWithFeedback); } // Sub
// // Subtract register
from accumulator. IGNITION_HANDLER(Sub, InterpreterBinaryOpAssembler) { BinaryOpWithFeedback(&BinaryOpAssembler::Generate_SubtractWithFeedback); } // Mul
// // Multiply accumulator by register
. IGNITION_HANDLER(Mul, InterpreterBinaryOpAssembler) { BinaryOpWithFeedback(&BinaryOpAssembler::Generate_MultiplyWithFeedback); } // Div
// // Divide register
by accumulator. IGNITION_HANDLER(Div, InterpreterBinaryOpAssembler) { BinaryOpWithFeedback(&BinaryOpAssembler::Generate_DivideWithFeedback); } // Mod
// // Modulo register
by accumulator. IGNITION_HANDLER(Mod, InterpreterBinaryOpAssembler) { BinaryOpWithFeedback(&BinaryOpAssembler::Generate_ModulusWithFeedback); } // Exp
// // Exponentiate register
(base) with accumulator (exponent). IGNITION_HANDLER(Exp, InterpreterBinaryOpAssembler) { BinaryOpWithFeedback(&BinaryOpAssembler::Generate_ExponentiateWithFeedback); } // AddSmi
// // Adds an immediate value
to the value in the accumulator. IGNITION_HANDLER(AddSmi, InterpreterBinaryOpAssembler) { BinaryOpSmiWithFeedback(&BinaryOpAssembler::Generate_AddWithFeedback); } // SubSmi
// // Subtracts an immediate value
from the value in the accumulator. IGNITION_HANDLER(SubSmi, InterpreterBinaryOpAssembler) { BinaryOpSmiWithFeedback(&BinaryOpAssembler::Generate_SubtractWithFeedback); } // MulSmi
// // Multiplies an immediate value
to the value in the accumulator. IGNITION_HANDLER(MulSmi, InterpreterBinaryOpAssembler) { BinaryOpSmiWithFeedback(&BinaryOpAssembler::Generate_MultiplyWithFeedback); } // DivSmi
// // Divides the value in the accumulator by immediate value
. IGNITION_HANDLER(DivSmi, InterpreterBinaryOpAssembler) { BinaryOpSmiWithFeedback(&BinaryOpAssembler::Generate_DivideWithFeedback); } // ModSmi
// // Modulo accumulator by immediate value
. IGNITION_HANDLER(ModSmi, InterpreterBinaryOpAssembler) { BinaryOpSmiWithFeedback(&BinaryOpAssembler::Generate_ModulusWithFeedback); } // ExpSmi
// // Exponentiate accumulator (base) with immediate value
(exponent). IGNITION_HANDLER(ExpSmi, InterpreterBinaryOpAssembler) { BinaryOpSmiWithFeedback( &BinaryOpAssembler::Generate_ExponentiateWithFeedback); } class InterpreterBitwiseBinaryOpAssembler : public InterpreterAssembler { public: InterpreterBitwiseBinaryOpAssembler(CodeAssemblerState* state, Bytecode bytecode, OperandScale operand_scale) : InterpreterAssembler(state, bytecode, operand_scale) {} void BitwiseBinaryOpWithFeedback(Operation bitwise_op) { Node* left = LoadRegisterAtOperandIndex(0); Node* right = GetAccumulator(); Node* context = GetContext(); Node* slot_index = BytecodeOperandIdx(1); Node* feedback_vector = LoadFeedbackVector(); TVARIABLE(Smi, var_left_feedback); TVARIABLE(Smi, var_right_feedback); VARIABLE(var_left_word32, MachineRepresentation::kWord32); VARIABLE(var_right_word32, MachineRepresentation::kWord32); VARIABLE(var_left_bigint, MachineRepresentation::kTagged, left); VARIABLE(var_right_bigint, MachineRepresentation::kTagged); Label if_left_number(this), do_number_op(this); Label if_left_bigint(this), do_bigint_op(this); TaggedToWord32OrBigIntWithFeedback(context, left, &if_left_number, &var_left_word32, &if_left_bigint, &var_left_bigint, &var_left_feedback); BIND(&if_left_number); TaggedToWord32OrBigIntWithFeedback(context, right, &do_number_op, &var_right_word32, &do_bigint_op, &var_right_bigint, &var_right_feedback); BIND(&do_number_op); TNode
result = BitwiseOp(var_left_word32.value(), var_right_word32.value(), bitwise_op); TNode
result_type = SelectSmiConstant( TaggedIsSmi(result), BinaryOperationFeedback::kSignedSmall, BinaryOperationFeedback::kNumber); TNode
input_feedback = SmiOr(var_left_feedback.value(), var_right_feedback.value()); UpdateFeedback(SmiOr(result_type, input_feedback), feedback_vector, slot_index); SetAccumulator(result); Dispatch(); // BigInt cases. BIND(&if_left_bigint); TaggedToNumericWithFeedback(context, right, &do_bigint_op, &var_right_bigint, &var_right_feedback); BIND(&do_bigint_op); SetAccumulator( CallRuntime(Runtime::kBigIntBinaryOp, context, var_left_bigint.value(), var_right_bigint.value(), SmiConstant(bitwise_op))); UpdateFeedback(SmiOr(var_left_feedback.value(), var_right_feedback.value()), feedback_vector, slot_index); Dispatch(); } void BitwiseBinaryOpWithSmi(Operation bitwise_op) { Node* left = GetAccumulator(); Node* right = BytecodeOperandImmSmi(0); Node* slot_index = BytecodeOperandIdx(1); Node* feedback_vector = LoadFeedbackVector(); Node* context = GetContext(); TVARIABLE(Smi, var_left_feedback); VARIABLE(var_left_word32, MachineRepresentation::kWord32); VARIABLE(var_left_bigint, MachineRepresentation::kTagged); Label do_smi_op(this), if_bigint_mix(this); TaggedToWord32OrBigIntWithFeedback(context, left, &do_smi_op, &var_left_word32, &if_bigint_mix, &var_left_bigint, &var_left_feedback); BIND(&do_smi_op); TNode
result = BitwiseOp(var_left_word32.value(), SmiToInt32(right), bitwise_op); TNode
result_type = SelectSmiConstant( TaggedIsSmi(result), BinaryOperationFeedback::kSignedSmall, BinaryOperationFeedback::kNumber); UpdateFeedback(SmiOr(result_type, var_left_feedback.value()), feedback_vector, slot_index); SetAccumulator(result); Dispatch(); BIND(&if_bigint_mix); UpdateFeedback(var_left_feedback.value(), feedback_vector, slot_index); ThrowTypeError(context, MessageTemplate::kBigIntMixedTypes); } }; // BitwiseOr
// // BitwiseOr register
to accumulator. IGNITION_HANDLER(BitwiseOr, InterpreterBitwiseBinaryOpAssembler) { BitwiseBinaryOpWithFeedback(Operation::kBitwiseOr); } // BitwiseXor
// // BitwiseXor register
to accumulator. IGNITION_HANDLER(BitwiseXor, InterpreterBitwiseBinaryOpAssembler) { BitwiseBinaryOpWithFeedback(Operation::kBitwiseXor); } // BitwiseAnd
// // BitwiseAnd register
to accumulator. IGNITION_HANDLER(BitwiseAnd, InterpreterBitwiseBinaryOpAssembler) { BitwiseBinaryOpWithFeedback(Operation::kBitwiseAnd); } // ShiftLeft
// // Left shifts register
by the count specified in the accumulator. // Register
is converted to an int32 and the accumulator to uint32 // before the operation. 5 lsb bits from the accumulator are used as count // i.e.
<< (accumulator & 0x1F). IGNITION_HANDLER(ShiftLeft, InterpreterBitwiseBinaryOpAssembler) { BitwiseBinaryOpWithFeedback(Operation::kShiftLeft); } // ShiftRight
// // Right shifts register
by the count specified in the accumulator. // Result is sign extended. Register
is converted to an int32 and the // accumulator to uint32 before the operation. 5 lsb bits from the accumulator // are used as count i.e.
>> (accumulator & 0x1F). IGNITION_HANDLER(ShiftRight, InterpreterBitwiseBinaryOpAssembler) { BitwiseBinaryOpWithFeedback(Operation::kShiftRight); } // ShiftRightLogical
// // Right Shifts register
by the count specified in the accumulator. // Result is zero-filled. The accumulator and register
are converted to // uint32 before the operation 5 lsb bits from the accumulator are used as // count i.e.
<< (accumulator & 0x1F). IGNITION_HANDLER(ShiftRightLogical, InterpreterBitwiseBinaryOpAssembler) { BitwiseBinaryOpWithFeedback(Operation::kShiftRightLogical); } // BitwiseOrSmi
// // BitwiseOrSmi accumulator with
. IGNITION_HANDLER(BitwiseOrSmi, InterpreterBitwiseBinaryOpAssembler) { BitwiseBinaryOpWithSmi(Operation::kBitwiseOr); } // BitwiseXorSmi
// // BitwiseXorSmi accumulator with
. IGNITION_HANDLER(BitwiseXorSmi, InterpreterBitwiseBinaryOpAssembler) { BitwiseBinaryOpWithSmi(Operation::kBitwiseXor); } // BitwiseAndSmi
// // BitwiseAndSmi accumulator with
. IGNITION_HANDLER(BitwiseAndSmi, InterpreterBitwiseBinaryOpAssembler) { BitwiseBinaryOpWithSmi(Operation::kBitwiseAnd); } // BitwiseNot
// // Perform bitwise-not on the accumulator. IGNITION_HANDLER(BitwiseNot, InterpreterAssembler) { Node* operand = GetAccumulator(); Node* slot_index = BytecodeOperandIdx(0); Node* feedback_vector = LoadFeedbackVector(); Node* context = GetContext(); VARIABLE(var_word32, MachineRepresentation::kWord32); TVARIABLE(Smi, var_feedback); VARIABLE(var_bigint, MachineRepresentation::kTagged); Label if_number(this), if_bigint(this); TaggedToWord32OrBigIntWithFeedback(context, operand, &if_number, &var_word32, &if_bigint, &var_bigint, &var_feedback); // Number case. BIND(&if_number); TNode
result = ChangeInt32ToTagged(Signed(Word32BitwiseNot(var_word32.value()))); TNode
result_type = SelectSmiConstant( TaggedIsSmi(result), BinaryOperationFeedback::kSignedSmall, BinaryOperationFeedback::kNumber); UpdateFeedback(SmiOr(result_type, var_feedback.value()), feedback_vector, slot_index); SetAccumulator(result); Dispatch(); // BigInt case. BIND(&if_bigint); UpdateFeedback(SmiConstant(BinaryOperationFeedback::kBigInt), feedback_vector, slot_index); SetAccumulator(CallRuntime(Runtime::kBigIntUnaryOp, context, var_bigint.value(), SmiConstant(Operation::kBitwiseNot))); Dispatch(); } // ShiftLeftSmi
// // Left shifts accumulator by the count specified in
. // The accumulator is converted to an int32 before the operation. The 5 // lsb bits from
are used as count i.e.
<< (
& 0x1F). IGNITION_HANDLER(ShiftLeftSmi, InterpreterBitwiseBinaryOpAssembler) { BitwiseBinaryOpWithSmi(Operation::kShiftLeft); } // ShiftRightSmi
// // Right shifts accumulator by the count specified in
. Result is sign // extended. The accumulator is converted to an int32 before the operation. The // 5 lsb bits from
are used as count i.e.
>> (
& 0x1F). IGNITION_HANDLER(ShiftRightSmi, InterpreterBitwiseBinaryOpAssembler) { BitwiseBinaryOpWithSmi(Operation::kShiftRight); } // ShiftRightLogicalSmi
// // Right shifts accumulator by the count specified in
. Result is zero // extended. The accumulator is converted to an int32 before the operation. The // 5 lsb bits from
are used as count i.e.
>>> (
& 0x1F). IGNITION_HANDLER(ShiftRightLogicalSmi, InterpreterBitwiseBinaryOpAssembler) { BitwiseBinaryOpWithSmi(Operation::kShiftRightLogical); } class UnaryNumericOpAssembler : public InterpreterAssembler { public: UnaryNumericOpAssembler(CodeAssemblerState* state, Bytecode bytecode, OperandScale operand_scale) : InterpreterAssembler(state, bytecode, operand_scale) {} virtual ~UnaryNumericOpAssembler() {} // Must return a tagged value. virtual TNode
SmiOp(TNode
smi_value, Variable* var_feedback, Label* do_float_op, Variable* var_float) = 0; // Must return a Float64 value. virtual Node* FloatOp(Node* float_value) = 0; // Must return a tagged value. virtual Node* BigIntOp(Node* bigint_value) = 0; void UnaryOpWithFeedback() { VARIABLE(var_value, MachineRepresentation::kTagged, GetAccumulator()); Node* slot_index = BytecodeOperandIdx(0); Node* feedback_vector = LoadFeedbackVector(); VARIABLE(var_result, MachineRepresentation::kTagged); VARIABLE(var_float_value, MachineRepresentation::kFloat64); TVARIABLE(Smi, var_feedback, SmiConstant(BinaryOperationFeedback::kNone)); Variable* loop_vars[] = {&var_value, &var_feedback}; Label start(this, arraysize(loop_vars), loop_vars), end(this); Label do_float_op(this, &var_float_value); Goto(&start); // We might have to try again after ToNumeric conversion. BIND(&start); { Label if_smi(this), if_heapnumber(this), if_bigint(this); Label if_oddball(this), if_other(this); Node* value = var_value.value(); GotoIf(TaggedIsSmi(value), &if_smi); Node* map = LoadMap(value); GotoIf(IsHeapNumberMap(map), &if_heapnumber); Node* instance_type = LoadMapInstanceType(map); GotoIf(IsBigIntInstanceType(instance_type), &if_bigint); Branch(InstanceTypeEqual(instance_type, ODDBALL_TYPE), &if_oddball, &if_other); BIND(&if_smi); { var_result.Bind( SmiOp(CAST(value), &var_feedback, &do_float_op, &var_float_value)); Goto(&end); } BIND(&if_heapnumber); { var_float_value.Bind(LoadHeapNumberValue(value)); Goto(&do_float_op); } BIND(&if_bigint); { var_result.Bind(BigIntOp(value)); CombineFeedback(&var_feedback, BinaryOperationFeedback::kBigInt); Goto(&end); } BIND(&if_oddball); { // We do not require an Or with earlier feedback here because once we // convert the value to a number, we cannot reach this path. We can // only reach this path on the first pass when the feedback is kNone. CSA_ASSERT(this, SmiEqual(var_feedback.value(), SmiConstant(BinaryOperationFeedback::kNone))); OverwriteFeedback(&var_feedback, BinaryOperationFeedback::kNumberOrOddball); var_value.Bind(LoadObjectField(value, Oddball::kToNumberOffset)); Goto(&start); } BIND(&if_other); { // We do not require an Or with earlier feedback here because once we // convert the value to a number, we cannot reach this path. We can // only reach this path on the first pass when the feedback is kNone. CSA_ASSERT(this, SmiEqual(var_feedback.value(), SmiConstant(BinaryOperationFeedback::kNone))); OverwriteFeedback(&var_feedback, BinaryOperationFeedback::kAny); var_value.Bind( CallBuiltin(Builtins::kNonNumberToNumeric, GetContext(), value)); Goto(&start); } } BIND(&do_float_op); { CombineFeedback(&var_feedback, BinaryOperationFeedback::kNumber); var_result.Bind( AllocateHeapNumberWithValue(FloatOp(var_float_value.value()))); Goto(&end); } BIND(&end); UpdateFeedback(var_feedback.value(), feedback_vector, slot_index); SetAccumulator(var_result.value()); Dispatch(); } }; class NegateAssemblerImpl : public UnaryNumericOpAssembler { public: explicit NegateAssemblerImpl(CodeAssemblerState* state, Bytecode bytecode, OperandScale operand_scale) : UnaryNumericOpAssembler(state, bytecode, operand_scale) {} TNode
SmiOp(TNode
smi_value, Variable* var_feedback, Label* do_float_op, Variable* var_float) override { TVARIABLE(Number, var_result); Label if_zero(this), if_min_smi(this), end(this); // Return -0 if operand is 0. GotoIf(SmiEqual(smi_value, SmiConstant(0)), &if_zero); // Special-case the minimum Smi to avoid overflow. GotoIf(SmiEqual(smi_value, SmiConstant(Smi::kMinValue)), &if_min_smi); // Else simply subtract operand from 0. CombineFeedback(var_feedback, BinaryOperationFeedback::kSignedSmall); var_result = SmiSub(SmiConstant(0), smi_value); Goto(&end); BIND(&if_zero); CombineFeedback(var_feedback, BinaryOperationFeedback::kNumber); var_result = MinusZeroConstant(); Goto(&end); BIND(&if_min_smi); var_float->Bind(SmiToFloat64(smi_value)); Goto(do_float_op); BIND(&end); return var_result.value(); } Node* FloatOp(Node* float_value) override { return Float64Neg(float_value); } Node* BigIntOp(Node* bigint_value) override { return CallRuntime(Runtime::kBigIntUnaryOp, GetContext(), bigint_value, SmiConstant(Operation::kNegate)); } }; // Negate
// // Perform arithmetic negation on the accumulator. IGNITION_HANDLER(Negate, NegateAssemblerImpl) { UnaryOpWithFeedback(); } // ToName
// // Convert the object referenced by the accumulator to a name. IGNITION_HANDLER(ToName, InterpreterAssembler) { Node* object = GetAccumulator(); Node* context = GetContext(); Node* result = ToName(context, object); StoreRegisterAtOperandIndex(result, 0); Dispatch(); } // ToNumber
// // Convert the object referenced by the accumulator to a number. IGNITION_HANDLER(ToNumber, InterpreterAssembler) { ToNumberOrNumeric(Object::Conversion::kToNumber); } // ToNumeric
// // Convert the object referenced by the accumulator to a numeric. IGNITION_HANDLER(ToNumeric, InterpreterAssembler) { ToNumberOrNumeric(Object::Conversion::kToNumeric); } // ToObject
// // Convert the object referenced by the accumulator to a JSReceiver. IGNITION_HANDLER(ToObject, InterpreterAssembler) { Node* accumulator = GetAccumulator(); Node* context = GetContext(); Node* result = CallBuiltin(Builtins::kToObject, context, accumulator); StoreRegisterAtOperandIndex(result, 0); Dispatch(); } // ToString // // Convert the accumulator to a String. IGNITION_HANDLER(ToString, InterpreterAssembler) { SetAccumulator(ToString_Inline(GetContext(), GetAccumulator())); Dispatch(); } class IncDecAssembler : public UnaryNumericOpAssembler { public: explicit IncDecAssembler(CodeAssemblerState* state, Bytecode bytecode, OperandScale operand_scale) : UnaryNumericOpAssembler(state, bytecode, operand_scale) {} Operation op() { DCHECK(op_ == Operation::kIncrement || op_ == Operation::kDecrement); return op_; } TNode
SmiOp(TNode
value, Variable* var_feedback, Label* do_float_op, Variable* var_float) override { TNode
one = SmiConstant(1); Label if_overflow(this), if_notoverflow(this); TNode
result = op() == Operation::kIncrement ? TrySmiAdd(value, one, &if_overflow) : TrySmiSub(value, one, &if_overflow); Goto(&if_notoverflow); BIND(&if_overflow); { var_float->Bind(SmiToFloat64(value)); Goto(do_float_op); } BIND(&if_notoverflow); CombineFeedback(var_feedback, BinaryOperationFeedback::kSignedSmall); return result; } Node* FloatOp(Node* float_value) override { return op() == Operation::kIncrement ? Float64Add(float_value, Float64Constant(1.0)) : Float64Sub(float_value, Float64Constant(1.0)); } Node* BigIntOp(Node* bigint_value) override { return CallRuntime(Runtime::kBigIntUnaryOp, GetContext(), bigint_value, SmiConstant(op())); } void IncWithFeedback() { op_ = Operation::kIncrement; UnaryOpWithFeedback(); } void DecWithFeedback() { op_ = Operation::kDecrement; UnaryOpWithFeedback(); } private: Operation op_ = Operation::kEqual; // Dummy initialization. }; // Inc // // Increments value in the accumulator by one. IGNITION_HANDLER(Inc, IncDecAssembler) { IncWithFeedback(); } // Dec // // Decrements value in the accumulator by one. IGNITION_HANDLER(Dec, IncDecAssembler) { DecWithFeedback(); } // LogicalNot // // Perform logical-not on the accumulator, first casting the // accumulator to a boolean value if required. // ToBooleanLogicalNot IGNITION_HANDLER(ToBooleanLogicalNot, InterpreterAssembler) { Node* value = GetAccumulator(); Variable result(this, MachineRepresentation::kTagged); Label if_true(this), if_false(this), end(this); BranchIfToBooleanIsTrue(value, &if_true, &if_false); BIND(&if_true); { result.Bind(FalseConstant()); Goto(&end); } BIND(&if_false); { result.Bind(TrueConstant()); Goto(&end); } BIND(&end); SetAccumulator(result.value()); Dispatch(); } // LogicalNot // // Perform logical-not on the accumulator, which must already be a boolean // value. IGNITION_HANDLER(LogicalNot, InterpreterAssembler) { Node* value = GetAccumulator(); Variable result(this, MachineRepresentation::kTagged); Label if_true(this), if_false(this), end(this); Node* true_value = TrueConstant(); Node* false_value = FalseConstant(); Branch(WordEqual(value, true_value), &if_true, &if_false); BIND(&if_true); { result.Bind(false_value); Goto(&end); } BIND(&if_false); { CSA_ASSERT(this, WordEqual(value, false_value)); result.Bind(true_value); Goto(&end); } BIND(&end); SetAccumulator(result.value()); Dispatch(); } // TypeOf // // Load the accumulator with the string representating type of the // object in the accumulator. IGNITION_HANDLER(TypeOf, InterpreterAssembler) { Node* value = GetAccumulator(); Node* result = Typeof(value); SetAccumulator(result); Dispatch(); } // DeletePropertyStrict // // Delete the property specified in the accumulator from the object // referenced by the register operand following strict mode semantics. IGNITION_HANDLER(DeletePropertyStrict, InterpreterAssembler) { Node* object = LoadRegisterAtOperandIndex(0); Node* key = GetAccumulator(); Node* context = GetContext(); Node* result = CallBuiltin(Builtins::kDeleteProperty, context, object, key, SmiConstant(Smi::FromEnum(LanguageMode::kStrict))); SetAccumulator(result); Dispatch(); } // DeletePropertySloppy // // Delete the property specified in the accumulator from the object // referenced by the register operand following sloppy mode semantics. IGNITION_HANDLER(DeletePropertySloppy, InterpreterAssembler) { Node* object = LoadRegisterAtOperandIndex(0); Node* key = GetAccumulator(); Node* context = GetContext(); Node* result = CallBuiltin(Builtins::kDeleteProperty, context, object, key, SmiConstant(Smi::FromEnum(LanguageMode::kSloppy))); SetAccumulator(result); Dispatch(); } // GetSuperConstructor // // Get the super constructor from the object referenced by the accumulator. // The result is stored in register |reg|. IGNITION_HANDLER(GetSuperConstructor, InterpreterAssembler) { Node* active_function = GetAccumulator(); Node* context = GetContext(); Node* result = GetSuperConstructor(context, active_function); StoreRegisterAtOperandIndex(result, 0); Dispatch(); } class InterpreterJSCallAssembler : public InterpreterAssembler { public: InterpreterJSCallAssembler(CodeAssemblerState* state, Bytecode bytecode, OperandScale operand_scale) : InterpreterAssembler(state, bytecode, operand_scale) {} // Generates code to perform a JS call that collects type feedback. void JSCall(ConvertReceiverMode receiver_mode) { Node* function = LoadRegisterAtOperandIndex(0); RegListNodePair args = GetRegisterListAtOperandIndex(1); Node* slot_id = BytecodeOperandIdx(3); Node* feedback_vector = LoadFeedbackVector(); Node* context = GetContext(); // Collect the {function} feedback. CollectCallFeedback(function, context, feedback_vector, slot_id); // Call the function and dispatch to the next handler. CallJSAndDispatch(function, context, args, receiver_mode); } // Generates code to perform a JS call with a known number of arguments that // collects type feedback. void JSCallN(int arg_count, ConvertReceiverMode receiver_mode) { // Indices and counts of operands on the bytecode. const int kFirstArgumentOperandIndex = 1; const int kReceiverOperandCount = (receiver_mode == ConvertReceiverMode::kNullOrUndefined) ? 0 : 1; const int kRecieverAndArgOperandCount = kReceiverOperandCount + arg_count; const int kSlotOperandIndex = kFirstArgumentOperandIndex + kRecieverAndArgOperandCount; Node* function = LoadRegisterAtOperandIndex(0); Node* slot_id = BytecodeOperandIdx(kSlotOperandIndex); Node* feedback_vector = LoadFeedbackVector(); Node* context = GetContext(); // Collect the {function} feedback. CollectCallFeedback(function, context, feedback_vector, slot_id); switch (kRecieverAndArgOperandCount) { case 0: CallJSAndDispatch(function, context, Int32Constant(arg_count), receiver_mode); break; case 1: CallJSAndDispatch( function, context, Int32Constant(arg_count), receiver_mode, LoadRegisterAtOperandIndex(kFirstArgumentOperandIndex)); break; case 2: CallJSAndDispatch( function, context, Int32Constant(arg_count), receiver_mode, LoadRegisterAtOperandIndex(kFirstArgumentOperandIndex), LoadRegisterAtOperandIndex(kFirstArgumentOperandIndex + 1)); break; case 3: CallJSAndDispatch( function, context, Int32Constant(arg_count), receiver_mode, LoadRegisterAtOperandIndex(kFirstArgumentOperandIndex), LoadRegisterAtOperandIndex(kFirstArgumentOperandIndex + 1), LoadRegisterAtOperandIndex(kFirstArgumentOperandIndex + 2)); break; default: UNREACHABLE(); } } }; // Call
// // Call a JSfunction or Callable in |callable| with the |receiver| and // |arg_count| arguments in subsequent registers. Collect type feedback // into |feedback_slot_id| IGNITION_HANDLER(CallAnyReceiver, InterpreterJSCallAssembler) { JSCall(ConvertReceiverMode::kAny); } IGNITION_HANDLER(CallProperty, InterpreterJSCallAssembler) { JSCall(ConvertReceiverMode::kNotNullOrUndefined); } IGNITION_HANDLER(CallProperty0, InterpreterJSCallAssembler) { JSCallN(0, ConvertReceiverMode::kNotNullOrUndefined); } IGNITION_HANDLER(CallProperty1, InterpreterJSCallAssembler) { JSCallN(1, ConvertReceiverMode::kNotNullOrUndefined); } IGNITION_HANDLER(CallProperty2, InterpreterJSCallAssembler) { JSCallN(2, ConvertReceiverMode::kNotNullOrUndefined); } IGNITION_HANDLER(CallUndefinedReceiver, InterpreterJSCallAssembler) { JSCall(ConvertReceiverMode::kNullOrUndefined); } IGNITION_HANDLER(CallUndefinedReceiver0, InterpreterJSCallAssembler) { JSCallN(0, ConvertReceiverMode::kNullOrUndefined); } IGNITION_HANDLER(CallUndefinedReceiver1, InterpreterJSCallAssembler) { JSCallN(1, ConvertReceiverMode::kNullOrUndefined); } IGNITION_HANDLER(CallUndefinedReceiver2, InterpreterJSCallAssembler) { JSCallN(2, ConvertReceiverMode::kNullOrUndefined); } // CallRuntime
// // Call the runtime function |function_id| with the first argument in // register |first_arg| and |arg_count| arguments in subsequent // registers. IGNITION_HANDLER(CallRuntime, InterpreterAssembler) { Node* function_id = BytecodeOperandRuntimeId(0); RegListNodePair args = GetRegisterListAtOperandIndex(1); Node* context = GetContext(); Node* result = CallRuntimeN(function_id, context, args); SetAccumulator(result); Dispatch(); } // InvokeIntrinsic
// // Implements the semantic equivalent of calling the runtime function // |function_id| with the first argument in |first_arg| and |arg_count| // arguments in subsequent registers. IGNITION_HANDLER(InvokeIntrinsic, InterpreterAssembler) { Node* function_id = BytecodeOperandIntrinsicId(0); RegListNodePair args = GetRegisterListAtOperandIndex(1); Node* context = GetContext(); Node* result = GenerateInvokeIntrinsic(this, function_id, context, args); SetAccumulator(result); Dispatch(); } // CallRuntimeForPair
// // Call the runtime function |function_id| which returns a pair, with the // first argument in register |first_arg| and |arg_count| arguments in // subsequent registers. Returns the result in
and //
IGNITION_HANDLER(CallRuntimeForPair, InterpreterAssembler) { // Call the runtime function. Node* function_id = BytecodeOperandRuntimeId(0); RegListNodePair args = GetRegisterListAtOperandIndex(1); Node* context = GetContext(); Node* result_pair = CallRuntimeN(function_id, context, args, 2); // Store the results in
and
Node* result0 = Projection(0, result_pair); Node* result1 = Projection(1, result_pair); StoreRegisterPairAtOperandIndex(result0, result1, 3); Dispatch(); } // CallJSRuntime
// // Call the JS runtime function that has the |context_index| with the receiver // in register |receiver| and |arg_count| arguments in subsequent registers. IGNITION_HANDLER(CallJSRuntime, InterpreterAssembler) { Node* context_index = BytecodeOperandNativeContextIndex(0); RegListNodePair args = GetRegisterListAtOperandIndex(1); // Get the function to call from the native context. Node* context = GetContext(); Node* native_context = LoadNativeContext(context); Node* function = LoadContextElement(native_context, context_index); // Call the function. CallJSAndDispatch(function, context, args, ConvertReceiverMode::kNullOrUndefined); } // CallWithSpread
// // Call a JSfunction or Callable in |callable| with the receiver in // |first_arg| and |arg_count - 1| arguments in subsequent registers. The // final argument is always a spread. // IGNITION_HANDLER(CallWithSpread, InterpreterAssembler) { Node* callable = LoadRegisterAtOperandIndex(0); RegListNodePair args = GetRegisterListAtOperandIndex(1); Node* slot_id = BytecodeOperandIdx(3); Node* feedback_vector = LoadFeedbackVector(); Node* context = GetContext(); // Call into Runtime function CallWithSpread which does everything. CallJSWithSpreadAndDispatch(callable, context, args, slot_id, feedback_vector); } // ConstructWithSpread
// // Call the constructor in |constructor| with the first argument in register // |first_arg| and |arg_count| arguments in subsequent registers. The final // argument is always a spread. The new.target is in the accumulator. // IGNITION_HANDLER(ConstructWithSpread, InterpreterAssembler) { Node* new_target = GetAccumulator(); Node* constructor = LoadRegisterAtOperandIndex(0); RegListNodePair args = GetRegisterListAtOperandIndex(1); Node* slot_id = BytecodeOperandIdx(3); Node* feedback_vector = LoadFeedbackVector(); Node* context = GetContext(); Node* result = ConstructWithSpread(constructor, context, new_target, args, slot_id, feedback_vector); SetAccumulator(result); Dispatch(); } // Construct
// // Call operator construct with |constructor| and the first argument in // register |first_arg| and |arg_count| arguments in subsequent // registers. The new.target is in the accumulator. // IGNITION_HANDLER(Construct, InterpreterAssembler) { Node* new_target = GetAccumulator(); Node* constructor = LoadRegisterAtOperandIndex(0); RegListNodePair args = GetRegisterListAtOperandIndex(1); Node* slot_id = BytecodeOperandIdx(3); Node* feedback_vector = LoadFeedbackVector(); Node* context = GetContext(); Node* result = Construct(constructor, context, new_target, args, slot_id, feedback_vector); SetAccumulator(result); Dispatch(); } class InterpreterCompareOpAssembler : public InterpreterAssembler { public: InterpreterCompareOpAssembler(CodeAssemblerState* state, Bytecode bytecode, OperandScale operand_scale) : InterpreterAssembler(state, bytecode, operand_scale) {} void CompareOpWithFeedback(Operation compare_op) { Node* lhs = LoadRegisterAtOperandIndex(0); Node* rhs = GetAccumulator(); Node* context = GetContext(); Variable var_type_feedback(this, MachineRepresentation::kTagged); Node* result; switch (compare_op) { case Operation::kEqual: result = Equal(lhs, rhs, context, &var_type_feedback); break; case Operation::kStrictEqual: result = StrictEqual(lhs, rhs, &var_type_feedback); break; case Operation::kLessThan: case Operation::kGreaterThan: case Operation::kLessThanOrEqual: case Operation::kGreaterThanOrEqual: result = RelationalComparison(compare_op, lhs, rhs, context, &var_type_feedback); break; default: UNREACHABLE(); } Node* slot_index = BytecodeOperandIdx(1); Node* feedback_vector = LoadFeedbackVector(); UpdateFeedback(var_type_feedback.value(), feedback_vector, slot_index); SetAccumulator(result); Dispatch(); } }; // TestEqual
// // Test if the value in the
register equals the accumulator. IGNITION_HANDLER(TestEqual, InterpreterCompareOpAssembler) { CompareOpWithFeedback(Operation::kEqual); } // TestEqualStrict
// // Test if the value in the
register is strictly equal to the accumulator. IGNITION_HANDLER(TestEqualStrict, InterpreterCompareOpAssembler) { CompareOpWithFeedback(Operation::kStrictEqual); } // TestLessThan
// // Test if the value in the
register is less than the accumulator. IGNITION_HANDLER(TestLessThan, InterpreterCompareOpAssembler) { CompareOpWithFeedback(Operation::kLessThan); } // TestGreaterThan
// // Test if the value in the
register is greater than the accumulator. IGNITION_HANDLER(TestGreaterThan, InterpreterCompareOpAssembler) { CompareOpWithFeedback(Operation::kGreaterThan); } // TestLessThanOrEqual
// // Test if the value in the
register is less than or equal to the // accumulator. IGNITION_HANDLER(TestLessThanOrEqual, InterpreterCompareOpAssembler) { CompareOpWithFeedback(Operation::kLessThanOrEqual); } // TestGreaterThanOrEqual
// // Test if the value in the
register is greater than or equal to the // accumulator. IGNITION_HANDLER(TestGreaterThanOrEqual, InterpreterCompareOpAssembler) { CompareOpWithFeedback(Operation::kGreaterThanOrEqual); } // TestReferenceEqual
// // Test if the value in the
register is equal to the accumulator // by means of simple comparison. For SMIs and simple reference comparisons. IGNITION_HANDLER(TestReferenceEqual, InterpreterAssembler) { Node* lhs = LoadRegisterAtOperandIndex(0); Node* rhs = GetAccumulator(); Node* result = SelectBooleanConstant(WordEqual(lhs, rhs)); SetAccumulator(result); Dispatch(); } // TestIn
// // Test if the object referenced by the register operand is a property of the // object referenced by the accumulator. IGNITION_HANDLER(TestIn, InterpreterAssembler) { Node* property = LoadRegisterAtOperandIndex(0); Node* object = GetAccumulator(); Node* context = GetContext(); SetAccumulator(HasProperty(context, object, property, kHasProperty)); Dispatch(); } // TestInstanceOf
// // Test if the object referenced by the
register is an an instance of type // referenced by the accumulator. IGNITION_HANDLER(TestInstanceOf, InterpreterAssembler) { Node* object = LoadRegisterAtOperandIndex(0); Node* callable = GetAccumulator(); Node* slot_id = BytecodeOperandIdx(1); Node* feedback_vector = LoadFeedbackVector(); Node* context = GetContext(); // Record feedback for the {callable} in the {feedback_vector}. CollectCallableFeedback(callable, context, feedback_vector, slot_id); // Perform the actual instanceof operation. SetAccumulator(InstanceOf(object, callable, context)); Dispatch(); } // TestUndetectable // // Test if the value in the accumulator is undetectable (null, undefined or // document.all). IGNITION_HANDLER(TestUndetectable, InterpreterAssembler) { Label return_false(this), end(this); Node* object = GetAccumulator(); // If the object is an Smi then return false. SetAccumulator(FalseConstant()); GotoIf(TaggedIsSmi(object), &end); // If it is a HeapObject, load the map and check for undetectable bit. Node* result = SelectBooleanConstant(IsUndetectableMap(LoadMap(object))); SetAccumulator(result); Goto(&end); BIND(&end); Dispatch(); } // TestNull // // Test if the value in accumulator is strictly equal to null. IGNITION_HANDLER(TestNull, InterpreterAssembler) { Node* object = GetAccumulator(); Node* result = SelectBooleanConstant(WordEqual(object, NullConstant())); SetAccumulator(result); Dispatch(); } // TestUndefined // // Test if the value in the accumulator is strictly equal to undefined. IGNITION_HANDLER(TestUndefined, InterpreterAssembler) { Node* object = GetAccumulator(); Node* result = SelectBooleanConstant(WordEqual(object, UndefinedConstant())); SetAccumulator(result); Dispatch(); } // TestTypeOf
// // Tests if the object in the
is typeof the literal represented // by |literal_flag|. IGNITION_HANDLER(TestTypeOf, InterpreterAssembler) { Node* object = GetAccumulator(); Node* literal_flag = BytecodeOperandFlag(0); #define MAKE_LABEL(name, lower_case) Label if_##lower_case(this); TYPEOF_LITERAL_LIST(MAKE_LABEL) #undef MAKE_LABEL #define LABEL_POINTER(name, lower_case) &if_##lower_case, Label* labels[] = {TYPEOF_LITERAL_LIST(LABEL_POINTER)}; #undef LABEL_POINTER #define CASE(name, lower_case) \ static_cast
(TestTypeOfFlags::LiteralFlag::k##name), int32_t cases[] = {TYPEOF_LITERAL_LIST(CASE)}; #undef CASE Label if_true(this), if_false(this), end(this); // We juse use the final label as the default and properly CSA_ASSERT // that the {literal_flag} is valid here; this significantly improves // the generated code (compared to having a default label that aborts). unsigned const num_cases = arraysize(cases); CSA_ASSERT(this, Uint32LessThan(literal_flag, Int32Constant(num_cases))); Switch(literal_flag, labels[num_cases - 1], cases, labels, num_cases - 1); BIND(&if_number); { Comment("IfNumber"); GotoIfNumber(object, &if_true); Goto(&if_false); } BIND(&if_string); { Comment("IfString"); GotoIf(TaggedIsSmi(object), &if_false); Branch(IsString(object), &if_true, &if_false); } BIND(&if_symbol); { Comment("IfSymbol"); GotoIf(TaggedIsSmi(object), &if_false); Branch(IsSymbol(object), &if_true, &if_false); } BIND(&if_boolean); { Comment("IfBoolean"); GotoIf(WordEqual(object, TrueConstant()), &if_true); Branch(WordEqual(object, FalseConstant()), &if_true, &if_false); } BIND(&if_bigint); { Comment("IfBigInt"); GotoIf(TaggedIsSmi(object), &if_false); Branch(IsBigInt(object), &if_true, &if_false); } BIND(&if_undefined); { Comment("IfUndefined"); GotoIf(TaggedIsSmi(object), &if_false); // Check it is not null and the map has the undetectable bit set. GotoIf(IsNull(object), &if_false); Branch(IsUndetectableMap(LoadMap(object)), &if_true, &if_false); } BIND(&if_function); { Comment("IfFunction"); GotoIf(TaggedIsSmi(object), &if_false); // Check if callable bit is set and not undetectable. Node* map_bitfield = LoadMapBitField(LoadMap(object)); Node* callable_undetectable = Word32And(map_bitfield, Int32Constant(Map::IsUndetectableBit::kMask | Map::IsCallableBit::kMask)); Branch(Word32Equal(callable_undetectable, Int32Constant(Map::IsCallableBit::kMask)), &if_true, &if_false); } BIND(&if_object); { Comment("IfObject"); GotoIf(TaggedIsSmi(object), &if_false); // If the object is null then return true. GotoIf(IsNull(object), &if_true); // Check if the object is a receiver type and is not undefined or callable. Node* map = LoadMap(object); GotoIfNot(IsJSReceiverMap(map), &if_false); Node* map_bitfield = LoadMapBitField(map); Node* callable_undetectable = Word32And(map_bitfield, Int32Constant(Map::IsUndetectableBit::kMask | Map::IsCallableBit::kMask)); Branch(Word32Equal(callable_undetectable, Int32Constant(0)), &if_true, &if_false); } BIND(&if_other); { // Typeof doesn't return any other string value. Goto(&if_false); } BIND(&if_false); { SetAccumulator(FalseConstant()); Goto(&end); } BIND(&if_true); { SetAccumulator(TrueConstant()); Goto(&end); } BIND(&end); Dispatch(); } // Jump
// // Jump by the number of bytes represented by the immediate operand |imm|. IGNITION_HANDLER(Jump, InterpreterAssembler) { Node* relative_jump = BytecodeOperandUImmWord(0); Jump(relative_jump); } // JumpConstant
// // Jump by the number of bytes in the Smi in the |idx| entry in the constant // pool. IGNITION_HANDLER(JumpConstant, InterpreterAssembler) { Node* relative_jump = LoadAndUntagConstantPoolEntryAtOperandIndex(0); Jump(relative_jump); } // JumpIfTrue
// // Jump by the number of bytes represented by an immediate operand if the // accumulator contains true. This only works for boolean inputs, and // will misbehave if passed arbitrary input values. IGNITION_HANDLER(JumpIfTrue, InterpreterAssembler) { Node* accumulator = GetAccumulator(); Node* relative_jump = BytecodeOperandUImmWord(0); CSA_ASSERT(this, TaggedIsNotSmi(accumulator)); CSA_ASSERT(this, IsBoolean(accumulator)); JumpIfWordEqual(accumulator, TrueConstant(), relative_jump); } // JumpIfTrueConstant
// // Jump by the number of bytes in the Smi in the |idx| entry in the constant // pool if the accumulator contains true. This only works for boolean inputs, // and will misbehave if passed arbitrary input values. IGNITION_HANDLER(JumpIfTrueConstant, InterpreterAssembler) { Node* accumulator = GetAccumulator(); Node* relative_jump = LoadAndUntagConstantPoolEntryAtOperandIndex(0); CSA_ASSERT(this, TaggedIsNotSmi(accumulator)); CSA_ASSERT(this, IsBoolean(accumulator)); JumpIfWordEqual(accumulator, TrueConstant(), relative_jump); } // JumpIfFalse
// // Jump by the number of bytes represented by an immediate operand if the // accumulator contains false. This only works for boolean inputs, and // will misbehave if passed arbitrary input values. IGNITION_HANDLER(JumpIfFalse, InterpreterAssembler) { Node* accumulator = GetAccumulator(); Node* relative_jump = BytecodeOperandUImmWord(0); CSA_ASSERT(this, TaggedIsNotSmi(accumulator)); CSA_ASSERT(this, IsBoolean(accumulator)); JumpIfWordEqual(accumulator, FalseConstant(), relative_jump); } // JumpIfFalseConstant
// // Jump by the number of bytes in the Smi in the |idx| entry in the constant // pool if the accumulator contains false. This only works for boolean inputs, // and will misbehave if passed arbitrary input values. IGNITION_HANDLER(JumpIfFalseConstant, InterpreterAssembler) { Node* accumulator = GetAccumulator(); Node* relative_jump = LoadAndUntagConstantPoolEntryAtOperandIndex(0); CSA_ASSERT(this, TaggedIsNotSmi(accumulator)); CSA_ASSERT(this, IsBoolean(accumulator)); JumpIfWordEqual(accumulator, FalseConstant(), relative_jump); } // JumpIfToBooleanTrue
// // Jump by the number of bytes represented by an immediate operand if the object // referenced by the accumulator is true when the object is cast to boolean. IGNITION_HANDLER(JumpIfToBooleanTrue, InterpreterAssembler) { Node* value = GetAccumulator(); Node* relative_jump = BytecodeOperandUImmWord(0); Label if_true(this), if_false(this); BranchIfToBooleanIsTrue(value, &if_true, &if_false); BIND(&if_true); Jump(relative_jump); BIND(&if_false); Dispatch(); } // JumpIfToBooleanTrueConstant
// // Jump by the number of bytes in the Smi in the |idx| entry in the constant // pool if the object referenced by the accumulator is true when the object is // cast to boolean. IGNITION_HANDLER(JumpIfToBooleanTrueConstant, InterpreterAssembler) { Node* value = GetAccumulator(); Node* relative_jump = LoadAndUntagConstantPoolEntryAtOperandIndex(0); Label if_true(this), if_false(this); BranchIfToBooleanIsTrue(value, &if_true, &if_false); BIND(&if_true); Jump(relative_jump); BIND(&if_false); Dispatch(); } // JumpIfToBooleanFalse