| // 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 <array> |
| #include <tuple> |
| |
| #include "src/builtins/builtins-arguments-gen.h" |
| #include "src/builtins/builtins-constructor-gen.h" |
| #include "src/builtins/builtins-conversion-gen.h" |
| #include "src/builtins/builtins-forin-gen.h" |
| #include "src/builtins/builtins-string-gen.h" |
| #include "src/code-events.h" |
| #include "src/code-factory.h" |
| #include "src/factory.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" |
| |
| 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 <imm> |
| // |
| // Load an integer literal into the accumulator as a Smi. |
| IGNITION_HANDLER(LdaSmi, InterpreterAssembler) { |
| Node* smi_int = BytecodeOperandImmSmi(0); |
| SetAccumulator(smi_int); |
| Dispatch(); |
| } |
| |
| // LdaConstant <idx> |
| // |
| // Load constant literal at |idx| in the constant pool into the accumulator. |
| IGNITION_HANDLER(LdaConstant, InterpreterAssembler) { |
| Node* index = BytecodeOperandIdx(0); |
| Node* constant = LoadConstantPoolEntry(index); |
| 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 <src> |
| // |
| // Load accumulator with value from register <src>. |
| IGNITION_HANDLER(Ldar, InterpreterAssembler) { |
| Node* reg_index = BytecodeOperandReg(0); |
| Node* value = LoadRegister(reg_index); |
| SetAccumulator(value); |
| Dispatch(); |
| } |
| |
| // Star <dst> |
| // |
| // Store accumulator to register <dst>. |
| IGNITION_HANDLER(Star, InterpreterAssembler) { |
| Node* reg_index = BytecodeOperandReg(0); |
| Node* accumulator = GetAccumulator(); |
| StoreRegister(accumulator, reg_index); |
| Dispatch(); |
| } |
| |
| // Mov <src> <dst> |
| // |
| // Stores the value of register <src> to register <dst>. |
| IGNITION_HANDLER(Mov, InterpreterAssembler) { |
| Node* src_index = BytecodeOperandReg(0); |
| Node* src_value = LoadRegister(src_index); |
| Node* dst_index = BytecodeOperandReg(1); |
| StoreRegister(src_value, dst_index); |
| 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) { |
| // Must be kept in sync with AccessorAssembler::LoadGlobalIC. |
| |
| // Load the global via the LoadGlobalIC. |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* feedback_slot = BytecodeOperandIdx(slot_operand_index); |
| |
| AccessorAssembler accessor_asm(state()); |
| |
| Label try_handler(this, Label::kDeferred), miss(this, Label::kDeferred); |
| |
| // Fast path without frame construction for the data case. |
| { |
| Label done(this); |
| Variable var_result(this, MachineRepresentation::kTagged); |
| ExitPoint exit_point(this, &done, &var_result); |
| |
| accessor_asm.LoadGlobalIC_TryPropertyCellCase( |
| feedback_vector, feedback_slot, &exit_point, &try_handler, &miss, |
| CodeStubAssembler::INTPTR_PARAMETERS); |
| |
| BIND(&done); |
| SetAccumulator(var_result.value()); |
| Dispatch(); |
| } |
| |
| // Slow path with frame construction. |
| { |
| Label done(this); |
| Variable var_result(this, MachineRepresentation::kTagged); |
| ExitPoint exit_point(this, &done, &var_result); |
| |
| BIND(&try_handler); |
| { |
| Node* context = GetContext(); |
| Node* smi_slot = SmiTag(feedback_slot); |
| Node* name_index = BytecodeOperandIdx(name_operand_index); |
| Node* name = LoadConstantPoolEntry(name_index); |
| |
| AccessorAssembler::LoadICParameters params(context, nullptr, name, |
| smi_slot, feedback_vector); |
| accessor_asm.LoadGlobalIC_TryHandlerCase(¶ms, typeof_mode, |
| &exit_point, &miss); |
| } |
| |
| BIND(&miss); |
| { |
| Node* context = GetContext(); |
| Node* smi_slot = SmiTag(feedback_slot); |
| Node* name_index = BytecodeOperandIdx(name_operand_index); |
| Node* name = LoadConstantPoolEntry(name_index); |
| |
| AccessorAssembler::LoadICParameters params(context, nullptr, name, |
| smi_slot, feedback_vector); |
| accessor_asm.LoadGlobalIC_MissCase(¶ms, &exit_point); |
| } |
| |
| BIND(&done); |
| { |
| SetAccumulator(var_result.value()); |
| Dispatch(); |
| } |
| } |
| } |
| }; |
| |
| // LdaGlobal <name_index> <slot> |
| // |
| // Load the global with name in constant pool entry <name_index> into the |
| // accumulator using FeedBackVector slot <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 <name_index> <slot> |
| // |
| // Load the global with name in constant pool entry <name_index> into the |
| // accumulator using FeedBackVector slot <slot> inside of a typeof. |
| IGNITION_HANDLER(LdaGlobalInsideTypeof, InterpreterLoadGlobalAssembler) { |
| static const int kNameOperandIndex = 0; |
| static const int kSlotOperandIndex = 1; |
| |
| LdaGlobal(kSlotOperandIndex, kNameOperandIndex, INSIDE_TYPEOF); |
| } |
| |
| class InterpreterStoreGlobalAssembler : public InterpreterAssembler { |
| public: |
| InterpreterStoreGlobalAssembler(CodeAssemblerState* state, Bytecode bytecode, |
| OperandScale operand_scale) |
| : InterpreterAssembler(state, bytecode, operand_scale) {} |
| |
| void StaGlobal(Callable ic) { |
| // Get the global object. |
| Node* context = GetContext(); |
| Node* native_context = LoadNativeContext(context); |
| Node* global = LoadContextElement(native_context, Context::EXTENSION_INDEX); |
| |
| // Store the global via the StoreIC. |
| Node* code_target = HeapConstant(ic.code()); |
| Node* constant_index = BytecodeOperandIdx(0); |
| Node* name = LoadConstantPoolEntry(constant_index); |
| Node* value = GetAccumulator(); |
| Node* raw_slot = BytecodeOperandIdx(1); |
| Node* smi_slot = SmiTag(raw_slot); |
| Node* feedback_vector = LoadFeedbackVector(); |
| CallStub(ic.descriptor(), code_target, context, global, name, value, |
| smi_slot, feedback_vector); |
| Dispatch(); |
| } |
| }; |
| |
| // StaGlobalSloppy <name_index> <slot> |
| // |
| // Store the value in the accumulator into the global with name in constant pool |
| // entry <name_index> using FeedBackVector slot <slot> in sloppy mode. |
| IGNITION_HANDLER(StaGlobalSloppy, InterpreterStoreGlobalAssembler) { |
| Callable ic = CodeFactory::StoreGlobalICInOptimizedCode(isolate(), SLOPPY); |
| StaGlobal(ic); |
| } |
| |
| // StaGlobalStrict <name_index> <slot> |
| // |
| // Store the value in the accumulator into the global with name in constant pool |
| // entry <name_index> using FeedBackVector slot <slot> in strict mode. |
| IGNITION_HANDLER(StaGlobalStrict, InterpreterStoreGlobalAssembler) { |
| Callable ic = CodeFactory::StoreGlobalICInOptimizedCode(isolate(), STRICT); |
| StaGlobal(ic); |
| } |
| |
| // LdaContextSlot <context> <slot_index> <depth> |
| // |
| // 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* reg_index = BytecodeOperandReg(0); |
| Node* context = LoadRegister(reg_index); |
| 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 <context> <slot_index> <depth> |
| // |
| // 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) { |
| // Same as LdaContextSlot, should never be called. |
| UNREACHABLE(); |
| } |
| |
| // LdaCurrentContextSlot <slot_index> |
| // |
| // 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 <slot_index> |
| // |
| // Load the object in |slot_index| of the current context into the accumulator. |
| IGNITION_HANDLER(LdaImmutableCurrentContextSlot, InterpreterAssembler) { |
| // Same as LdaCurrentContextSlot, should never be called. |
| UNREACHABLE(); |
| } |
| |
| // StaContextSlot <context> <slot_index> <depth> |
| // |
| // 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* reg_index = BytecodeOperandReg(0); |
| Node* context = LoadRegister(reg_index); |
| Node* slot_index = BytecodeOperandIdx(1); |
| Node* depth = BytecodeOperandUImm(2); |
| Node* slot_context = GetContextAtDepth(context, depth); |
| StoreContextElement(slot_context, slot_index, value); |
| Dispatch(); |
| } |
| |
| // StaCurrentContextSlot <slot_index> |
| // |
| // 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 <name_index> |
| // |
| // Lookup the object with the name in constant pool entry |name_index| |
| // dynamically. |
| IGNITION_HANDLER(LdaLookupSlot, InterpreterAssembler) { |
| Node* name_index = BytecodeOperandIdx(0); |
| Node* name = LoadConstantPoolEntry(name_index); |
| Node* context = GetContext(); |
| Node* result = CallRuntime(Runtime::kLoadLookupSlot, context, name); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // LdaLookupSlotInsideTypeof <name_index> |
| // |
| // Lookup the object with the name in constant pool entry |name_index| |
| // dynamically without causing a NoReferenceError. |
| IGNITION_HANDLER(LdaLookupSlotInsideTypeof, InterpreterAssembler) { |
| Node* name_index = BytecodeOperandIdx(0); |
| Node* name = LoadConstantPoolEntry(name_index); |
| 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* name_index = BytecodeOperandIdx(0); |
| 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 = LoadConstantPoolEntry(name_index); |
| Node* result = CallRuntime(function_id, context, name); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| } |
| }; |
| |
| // LdaLookupSlot <name_index> |
| // |
| // Lookup the object with the name in constant pool entry |name_index| |
| // dynamically. |
| IGNITION_HANDLER(LdaLookupContextSlot, InterpreterLookupContextSlotAssembler) { |
| LookupContextSlot(Runtime::kLoadLookupSlot); |
| } |
| |
| // LdaLookupSlotInsideTypeof <name_index> |
| // |
| // 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_index = BytecodeOperandIdx(0); |
| Node* name = LoadConstantPoolEntry(name_index); |
| Node* result = CallRuntime(function_id, context, name); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| } |
| }; |
| |
| // LdaLookupGlobalSlot <name_index> <feedback_slot> <depth> |
| // |
| // Lookup the object with the name in constant pool entry |name_index| |
| // dynamically. |
| IGNITION_HANDLER(LdaLookupGlobalSlot, InterpreterLookupGlobalAssembler) { |
| LookupGlobalSlot(Runtime::kLoadLookupSlot); |
| } |
| |
| // LdaLookupGlobalSlotInsideTypeof <name_index> <feedback_slot> <depth> |
| // |
| // 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 <name_index> <flags> |
| // |
| // 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* index = BytecodeOperandIdx(0); |
| Node* bytecode_flags = BytecodeOperandFlag(1); |
| Node* name = LoadConstantPoolEntry(index); |
| Node* context = GetContext(); |
| Variable var_result(this, MachineRepresentation::kTagged); |
| |
| Label sloppy(this), strict(this), end(this); |
| DCHECK_EQ(0, SLOPPY); |
| DCHECK_EQ(1, STRICT); |
| DCHECK_EQ(0, static_cast<int>(LookupHoistingMode::kNormal)); |
| DCHECK_EQ(1, static_cast<int>(LookupHoistingMode::kLegacySloppy)); |
| Branch(IsSetWord32<StoreLookupSlotFlags::LanguageModeBit>(bytecode_flags), |
| &strict, &sloppy); |
| |
| BIND(&strict); |
| { |
| CSA_ASSERT(this, IsClearWord32<StoreLookupSlotFlags::LookupHoistingModeBit>( |
| bytecode_flags)); |
| var_result.Bind( |
| CallRuntime(Runtime::kStoreLookupSlot_Strict, context, name, value)); |
| Goto(&end); |
| } |
| |
| BIND(&sloppy); |
| { |
| Label hoisting(this), ordinary(this); |
| Branch(IsSetWord32<StoreLookupSlotFlags::LookupHoistingModeBit>( |
| 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 <object> <name_index> <slot> |
| // |
| // Calls the LoadIC at FeedBackVector slot <slot> for <object> and the name at |
| // constant pool entry <name_index>. |
| IGNITION_HANDLER(LdaNamedProperty, InterpreterAssembler) { |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* feedback_slot = BytecodeOperandIdx(2); |
| Node* smi_slot = SmiTag(feedback_slot); |
| |
| // Load receiver. |
| Node* register_index = BytecodeOperandReg(0); |
| Node* recv = LoadRegister(register_index); |
| |
| // Load the name. |
| // TODO(jgruber): Not needed for monomorphic smi handler constant/field case. |
| Node* constant_index = BytecodeOperandIdx(1); |
| Node* name = LoadConstantPoolEntry(constant_index); |
| |
| 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 <object> <slot> |
| // |
| // Calls the KeyedLoadIC at FeedBackVector slot <slot> for <object> and the key |
| // in the accumulator. |
| IGNITION_HANDLER(LdaKeyedProperty, InterpreterAssembler) { |
| Callable ic = Builtins::CallableFor(isolate(), Builtins::kKeyedLoadIC); |
| Node* code_target = HeapConstant(ic.code()); |
| Node* reg_index = BytecodeOperandReg(0); |
| Node* object = LoadRegister(reg_index); |
| Node* name = GetAccumulator(); |
| Node* raw_slot = BytecodeOperandIdx(1); |
| Node* smi_slot = SmiTag(raw_slot); |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* context = GetContext(); |
| Node* result = CallStub(ic.descriptor(), code_target, 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_reg_index = BytecodeOperandReg(0); |
| Node* object = LoadRegister(object_reg_index); |
| Node* constant_index = BytecodeOperandIdx(1); |
| Node* name = LoadConstantPoolEntry(constant_index); |
| Node* value = GetAccumulator(); |
| Node* raw_slot = BytecodeOperandIdx(2); |
| Node* smi_slot = SmiTag(raw_slot); |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* context = GetContext(); |
| CallStub(ic.descriptor(), code_target, context, object, name, value, |
| smi_slot, feedback_vector); |
| Dispatch(); |
| } |
| }; |
| |
| // StaNamedPropertySloppy <object> <name_index> <slot> |
| // |
| // Calls the sloppy mode StoreIC at FeedBackVector slot <slot> for <object> and |
| // the name in constant pool entry <name_index> with the value in the |
| // accumulator. |
| IGNITION_HANDLER(StaNamedPropertySloppy, |
| InterpreterStoreNamedPropertyAssembler) { |
| Callable ic = CodeFactory::StoreICInOptimizedCode(isolate(), SLOPPY); |
| StaNamedProperty(ic); |
| } |
| |
| // StaNamedPropertyStrict <object> <name_index> <slot> |
| // |
| // Calls the strict mode StoreIC at FeedBackVector slot <slot> for <object> and |
| // the name in constant pool entry <name_index> with the value in the |
| // accumulator. |
| IGNITION_HANDLER(StaNamedPropertyStrict, |
| InterpreterStoreNamedPropertyAssembler) { |
| Callable ic = CodeFactory::StoreICInOptimizedCode(isolate(), STRICT); |
| StaNamedProperty(ic); |
| } |
| |
| // StaNamedOwnProperty <object> <name_index> <slot> |
| // |
| // Calls the StoreOwnIC at FeedBackVector slot <slot> for <object> and |
| // the name in constant pool entry <name_index> with the value in the |
| // accumulator. |
| IGNITION_HANDLER(StaNamedOwnProperty, InterpreterStoreNamedPropertyAssembler) { |
| Callable ic = CodeFactory::StoreOwnICInOptimizedCode(isolate()); |
| StaNamedProperty(ic); |
| } |
| |
| class InterpreterStoreKeyedPropertyAssembler : public InterpreterAssembler { |
| public: |
| InterpreterStoreKeyedPropertyAssembler(CodeAssemblerState* state, |
| Bytecode bytecode, |
| OperandScale operand_scale) |
| : InterpreterAssembler(state, bytecode, operand_scale) {} |
| |
| void StaKeyedProperty(Callable ic) { |
| Node* code_target = HeapConstant(ic.code()); |
| Node* object_reg_index = BytecodeOperandReg(0); |
| Node* object = LoadRegister(object_reg_index); |
| Node* name_reg_index = BytecodeOperandReg(1); |
| Node* name = LoadRegister(name_reg_index); |
| Node* value = GetAccumulator(); |
| Node* raw_slot = BytecodeOperandIdx(2); |
| Node* smi_slot = SmiTag(raw_slot); |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* context = GetContext(); |
| CallStub(ic.descriptor(), code_target, context, object, name, value, |
| smi_slot, feedback_vector); |
| Dispatch(); |
| } |
| }; |
| |
| // StaKeyedPropertySloppy <object> <key> <slot> |
| // |
| // Calls the sloppy mode KeyStoreIC at FeedBackVector slot <slot> for <object> |
| // and the key <key> with the value in the accumulator. |
| IGNITION_HANDLER(StaKeyedPropertySloppy, |
| InterpreterStoreKeyedPropertyAssembler) { |
| Callable ic = CodeFactory::KeyedStoreICInOptimizedCode(isolate(), SLOPPY); |
| StaKeyedProperty(ic); |
| } |
| |
| // StaKeyedPropertyStrict <object> <key> <slot> |
| // |
| // Calls the strict mode KeyStoreIC at FeedBackVector slot <slot> for <object> |
| // and the key <key> with the value in the accumulator. |
| IGNITION_HANDLER(StaKeyedPropertyStrict, |
| InterpreterStoreKeyedPropertyAssembler) { |
| Callable ic = CodeFactory::KeyedStoreICInOptimizedCode(isolate(), STRICT); |
| StaKeyedProperty(ic); |
| } |
| |
| // StaDataPropertyInLiteral <object> <name> <flags> |
| // |
| // Define a property <name> with value from the accumulator in <object>. |
| // Property attributes and whether set_function_name are stored in |
| // DataPropertyInLiteralFlags <flags>. |
| // |
| // This definition is not observable and is used only for definitions |
| // in object or class literals. |
| IGNITION_HANDLER(StaDataPropertyInLiteral, InterpreterAssembler) { |
| Node* object = LoadRegister(BytecodeOperandReg(0)); |
| Node* name = LoadRegister(BytecodeOperandReg(1)); |
| Node* value = GetAccumulator(); |
| Node* flags = SmiFromWord32(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 <cell_index> <depth> |
| // |
| // Load the contents of a module variable into the accumulator. The variable is |
| // identified by <cell_index>. <depth> 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); |
| { |
| Node* regular_exports = |
| 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); |
| { |
| Node* regular_imports = |
| 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 <cell_index> <depth> |
| // |
| // Store accumulator to the module variable identified by <cell_index>. |
| // <depth> 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); |
| { |
| Node* regular_exports = |
| 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(kUnsupportedModuleOperation); |
| Goto(&end); |
| } |
| |
| BIND(&end); |
| Dispatch(); |
| } |
| |
| // PushContext <context> |
| // |
| // Saves the current context in <context>, and pushes the accumulator as the |
| // new current context. |
| IGNITION_HANDLER(PushContext, InterpreterAssembler) { |
| Node* reg_index = BytecodeOperandReg(0); |
| Node* new_context = GetAccumulator(); |
| Node* old_context = GetContext(); |
| StoreRegister(old_context, reg_index); |
| SetContext(new_context); |
| Dispatch(); |
| } |
| |
| // PopContext <context> |
| // |
| // Pops the current context and sets <context> as the new context. |
| IGNITION_HANDLER(PopContext, InterpreterAssembler) { |
| Node* reg_index = BytecodeOperandReg(0); |
| Node* context = LoadRegister(reg_index); |
| 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, |
| Node* function, bool lhs_is_smi); |
| |
| void BinaryOpWithFeedback(BinaryOpGenerator generator) { |
| Node* reg_index = BytecodeOperandReg(0); |
| Node* lhs = LoadRegister(reg_index); |
| Node* rhs = GetAccumulator(); |
| Node* context = GetContext(); |
| Node* slot_index = BytecodeOperandIdx(1); |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* function = LoadRegister(Register::function_closure()); |
| |
| BinaryOpAssembler binop_asm(state()); |
| Node* result = (binop_asm.*generator)(context, lhs, rhs, slot_index, |
| feedback_vector, function, 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(); |
| Node* function = LoadRegister(Register::function_closure()); |
| |
| BinaryOpAssembler binop_asm(state()); |
| Node* result = (binop_asm.*generator)(context, lhs, rhs, slot_index, |
| feedback_vector, function, true); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| }; |
| |
| // Add <src> |
| // |
| // Add register <src> to accumulator. |
| IGNITION_HANDLER(Add, InterpreterBinaryOpAssembler) { |
| BinaryOpWithFeedback(&BinaryOpAssembler::Generate_AddWithFeedback); |
| } |
| |
| // Sub <src> |
| // |
| // Subtract register <src> from accumulator. |
| IGNITION_HANDLER(Sub, InterpreterBinaryOpAssembler) { |
| BinaryOpWithFeedback(&BinaryOpAssembler::Generate_SubtractWithFeedback); |
| } |
| |
| // Mul <src> |
| // |
| // Multiply accumulator by register <src>. |
| IGNITION_HANDLER(Mul, InterpreterBinaryOpAssembler) { |
| BinaryOpWithFeedback(&BinaryOpAssembler::Generate_MultiplyWithFeedback); |
| } |
| |
| // Div <src> |
| // |
| // Divide register <src> by accumulator. |
| IGNITION_HANDLER(Div, InterpreterBinaryOpAssembler) { |
| BinaryOpWithFeedback(&BinaryOpAssembler::Generate_DivideWithFeedback); |
| } |
| |
| // Mod <src> |
| // |
| // Modulo register <src> by accumulator. |
| IGNITION_HANDLER(Mod, InterpreterBinaryOpAssembler) { |
| BinaryOpWithFeedback(&BinaryOpAssembler::Generate_ModulusWithFeedback); |
| } |
| |
| // AddSmi <imm> |
| // |
| // Adds an immediate value <imm> to the value in the accumulator. |
| IGNITION_HANDLER(AddSmi, InterpreterBinaryOpAssembler) { |
| BinaryOpSmiWithFeedback(&BinaryOpAssembler::Generate_AddWithFeedback); |
| } |
| |
| // SubSmi <imm> |
| // |
| // Subtracts an immediate value <imm> from the value in the accumulator. |
| IGNITION_HANDLER(SubSmi, InterpreterBinaryOpAssembler) { |
| BinaryOpSmiWithFeedback(&BinaryOpAssembler::Generate_SubtractWithFeedback); |
| } |
| |
| // MulSmi <imm> |
| // |
| // Multiplies an immediate value <imm> to the value in the accumulator. |
| IGNITION_HANDLER(MulSmi, InterpreterBinaryOpAssembler) { |
| BinaryOpSmiWithFeedback(&BinaryOpAssembler::Generate_MultiplyWithFeedback); |
| } |
| |
| // DivSmi <imm> |
| // |
| // Divides the value in the accumulator by immediate value <imm>. |
| IGNITION_HANDLER(DivSmi, InterpreterBinaryOpAssembler) { |
| BinaryOpSmiWithFeedback(&BinaryOpAssembler::Generate_DivideWithFeedback); |
| } |
| |
| // ModSmi <imm> |
| // |
| // Modulo accumulator by immediate value <imm>. |
| IGNITION_HANDLER(ModSmi, InterpreterBinaryOpAssembler) { |
| BinaryOpSmiWithFeedback(&BinaryOpAssembler::Generate_ModulusWithFeedback); |
| } |
| |
| class InterpreterBitwiseBinaryOpAssembler : public InterpreterAssembler { |
| public: |
| InterpreterBitwiseBinaryOpAssembler(CodeAssemblerState* state, |
| Bytecode bytecode, |
| OperandScale operand_scale) |
| : InterpreterAssembler(state, bytecode, operand_scale) {} |
| |
| void BitwiseBinaryOpWithFeedback(Token::Value bitwise_op) { |
| Node* reg_index = BytecodeOperandReg(0); |
| Node* lhs = LoadRegister(reg_index); |
| Node* rhs = GetAccumulator(); |
| Node* context = GetContext(); |
| Node* slot_index = BytecodeOperandIdx(1); |
| Node* feedback_vector = LoadFeedbackVector(); |
| |
| Variable var_lhs_type_feedback(this, MachineRepresentation::kTaggedSigned), |
| var_rhs_type_feedback(this, MachineRepresentation::kTaggedSigned); |
| Node* lhs_value = TruncateTaggedToWord32WithFeedback( |
| context, lhs, &var_lhs_type_feedback); |
| Node* rhs_value = TruncateTaggedToWord32WithFeedback( |
| context, rhs, &var_rhs_type_feedback); |
| Node* result = nullptr; |
| |
| switch (bitwise_op) { |
| case Token::BIT_OR: { |
| Node* value = Word32Or(lhs_value, rhs_value); |
| result = ChangeInt32ToTagged(value); |
| } break; |
| case Token::BIT_AND: { |
| Node* value = Word32And(lhs_value, rhs_value); |
| result = ChangeInt32ToTagged(value); |
| } break; |
| case Token::BIT_XOR: { |
| Node* value = Word32Xor(lhs_value, rhs_value); |
| result = ChangeInt32ToTagged(value); |
| } break; |
| case Token::SHL: { |
| Node* value = |
| Word32Shl(lhs_value, Word32And(rhs_value, Int32Constant(0x1f))); |
| result = ChangeInt32ToTagged(value); |
| } break; |
| case Token::SHR: { |
| Node* value = |
| Word32Shr(lhs_value, Word32And(rhs_value, Int32Constant(0x1f))); |
| result = ChangeUint32ToTagged(value); |
| } break; |
| case Token::SAR: { |
| Node* value = |
| Word32Sar(lhs_value, Word32And(rhs_value, Int32Constant(0x1f))); |
| result = ChangeInt32ToTagged(value); |
| } break; |
| default: |
| UNREACHABLE(); |
| } |
| |
| Node* result_type = SelectSmiConstant(TaggedIsSmi(result), |
| BinaryOperationFeedback::kSignedSmall, |
| BinaryOperationFeedback::kNumber); |
| |
| if (FLAG_debug_code) { |
| Label ok(this); |
| GotoIf(TaggedIsSmi(result), &ok); |
| Node* result_map = LoadMap(result); |
| AbortIfWordNotEqual(result_map, HeapNumberMapConstant(), |
| kExpectedHeapNumber); |
| Goto(&ok); |
| BIND(&ok); |
| } |
| |
| Node* input_feedback = |
| SmiOr(var_lhs_type_feedback.value(), var_rhs_type_feedback.value()); |
| Node* function = LoadRegister(Register::function_closure()); |
| UpdateFeedback(SmiOr(result_type, input_feedback), feedback_vector, |
| slot_index, function); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| }; |
| |
| // BitwiseOr <src> |
| // |
| // BitwiseOr register <src> to accumulator. |
| IGNITION_HANDLER(BitwiseOr, InterpreterBitwiseBinaryOpAssembler) { |
| BitwiseBinaryOpWithFeedback(Token::BIT_OR); |
| } |
| |
| // BitwiseXor <src> |
| // |
| // BitwiseXor register <src> to accumulator. |
| IGNITION_HANDLER(BitwiseXor, InterpreterBitwiseBinaryOpAssembler) { |
| BitwiseBinaryOpWithFeedback(Token::BIT_XOR); |
| } |
| |
| // BitwiseAnd <src> |
| // |
| // BitwiseAnd register <src> to accumulator. |
| IGNITION_HANDLER(BitwiseAnd, InterpreterBitwiseBinaryOpAssembler) { |
| BitwiseBinaryOpWithFeedback(Token::BIT_AND); |
| } |
| |
| // ShiftLeft <src> |
| // |
| // Left shifts register <src> by the count specified in the accumulator. |
| // Register <src> 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. <src> << (accumulator & 0x1F). |
| IGNITION_HANDLER(ShiftLeft, InterpreterBitwiseBinaryOpAssembler) { |
| BitwiseBinaryOpWithFeedback(Token::SHL); |
| } |
| |
| // ShiftRight <src> |
| // |
| // Right shifts register <src> by the count specified in the accumulator. |
| // Result is sign extended. Register <src> 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. <src> >> (accumulator & 0x1F). |
| IGNITION_HANDLER(ShiftRight, InterpreterBitwiseBinaryOpAssembler) { |
| BitwiseBinaryOpWithFeedback(Token::SAR); |
| } |
| |
| // ShiftRightLogical <src> |
| // |
| // Right Shifts register <src> by the count specified in the accumulator. |
| // Result is zero-filled. The accumulator and register <src> are converted to |
| // uint32 before the operation 5 lsb bits from the accumulator are used as |
| // count i.e. <src> << (accumulator & 0x1F). |
| IGNITION_HANDLER(ShiftRightLogical, InterpreterBitwiseBinaryOpAssembler) { |
| BitwiseBinaryOpWithFeedback(Token::SHR); |
| } |
| |
| // BitwiseOr <imm> |
| // |
| // BitwiseOr accumulator with <imm>. |
| IGNITION_HANDLER(BitwiseOrSmi, InterpreterAssembler) { |
| Node* left = GetAccumulator(); |
| Node* right = BytecodeOperandImmSmi(0); |
| Node* slot_index = BytecodeOperandIdx(1); |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* context = GetContext(); |
| |
| Variable var_lhs_type_feedback(this, MachineRepresentation::kTaggedSigned); |
| Node* lhs_value = |
| TruncateTaggedToWord32WithFeedback(context, left, &var_lhs_type_feedback); |
| Node* rhs_value = SmiToWord32(right); |
| Node* value = Word32Or(lhs_value, rhs_value); |
| Node* result = ChangeInt32ToTagged(value); |
| Node* result_type = SelectSmiConstant(TaggedIsSmi(result), |
| BinaryOperationFeedback::kSignedSmall, |
| BinaryOperationFeedback::kNumber); |
| Node* function = LoadRegister(Register::function_closure()); |
| UpdateFeedback(SmiOr(result_type, var_lhs_type_feedback.value()), |
| feedback_vector, slot_index, function); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // BitwiseXor <imm> |
| // |
| // BitwiseXor accumulator with <imm>. |
| IGNITION_HANDLER(BitwiseXorSmi, InterpreterAssembler) { |
| Node* left = GetAccumulator(); |
| Node* right = BytecodeOperandImmSmi(0); |
| Node* slot_index = BytecodeOperandIdx(1); |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* context = GetContext(); |
| |
| Variable var_lhs_type_feedback(this, MachineRepresentation::kTaggedSigned); |
| Node* lhs_value = |
| TruncateTaggedToWord32WithFeedback(context, left, &var_lhs_type_feedback); |
| Node* rhs_value = SmiToWord32(right); |
| Node* value = Word32Xor(lhs_value, rhs_value); |
| Node* result = ChangeInt32ToTagged(value); |
| Node* result_type = SelectSmiConstant(TaggedIsSmi(result), |
| BinaryOperationFeedback::kSignedSmall, |
| BinaryOperationFeedback::kNumber); |
| Node* function = LoadRegister(Register::function_closure()); |
| UpdateFeedback(SmiOr(result_type, var_lhs_type_feedback.value()), |
| feedback_vector, slot_index, function); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // BitwiseAnd <imm> |
| // |
| // BitwiseAnd accumulator with <imm>. |
| IGNITION_HANDLER(BitwiseAndSmi, InterpreterAssembler) { |
| Node* left = GetAccumulator(); |
| Node* right = BytecodeOperandImmSmi(0); |
| Node* slot_index = BytecodeOperandIdx(1); |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* context = GetContext(); |
| |
| Variable var_lhs_type_feedback(this, MachineRepresentation::kTaggedSigned); |
| Node* lhs_value = |
| TruncateTaggedToWord32WithFeedback(context, left, &var_lhs_type_feedback); |
| Node* rhs_value = SmiToWord32(right); |
| Node* value = Word32And(lhs_value, rhs_value); |
| Node* result = ChangeInt32ToTagged(value); |
| Node* result_type = SelectSmiConstant(TaggedIsSmi(result), |
| BinaryOperationFeedback::kSignedSmall, |
| BinaryOperationFeedback::kNumber); |
| Node* function = LoadRegister(Register::function_closure()); |
| UpdateFeedback(SmiOr(result_type, var_lhs_type_feedback.value()), |
| feedback_vector, slot_index, function); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // ShiftLeftSmi <imm> |
| // |
| // Left shifts accumulator by the count specified in <imm>. |
| // The accumulator is converted to an int32 before the operation. The 5 |
| // lsb bits from <imm> are used as count i.e. <src> << (<imm> & 0x1F). |
| IGNITION_HANDLER(ShiftLeftSmi, InterpreterAssembler) { |
| Node* left = GetAccumulator(); |
| Node* right = BytecodeOperandImmSmi(0); |
| Node* slot_index = BytecodeOperandIdx(1); |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* context = GetContext(); |
| |
| Variable var_lhs_type_feedback(this, MachineRepresentation::kTaggedSigned); |
| Node* lhs_value = |
| TruncateTaggedToWord32WithFeedback(context, left, &var_lhs_type_feedback); |
| Node* rhs_value = SmiToWord32(right); |
| Node* shift_count = Word32And(rhs_value, Int32Constant(0x1f)); |
| Node* value = Word32Shl(lhs_value, shift_count); |
| Node* result = ChangeInt32ToTagged(value); |
| Node* result_type = SelectSmiConstant(TaggedIsSmi(result), |
| BinaryOperationFeedback::kSignedSmall, |
| BinaryOperationFeedback::kNumber); |
| Node* function = LoadRegister(Register::function_closure()); |
| UpdateFeedback(SmiOr(result_type, var_lhs_type_feedback.value()), |
| feedback_vector, slot_index, function); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // ShiftRightSmi <imm> |
| // |
| // Right shifts accumulator by the count specified in <imm>. Result is sign |
| // extended. The accumulator is converted to an int32 before the operation. The |
| // 5 lsb bits from <imm> are used as count i.e. <src> << (<imm> & 0x1F). |
| IGNITION_HANDLER(ShiftRightSmi, InterpreterAssembler) { |
| Node* left = GetAccumulator(); |
| Node* right = BytecodeOperandImmSmi(0); |
| Node* slot_index = BytecodeOperandIdx(1); |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* context = GetContext(); |
| |
| Variable var_lhs_type_feedback(this, MachineRepresentation::kTaggedSigned); |
| Node* lhs_value = |
| TruncateTaggedToWord32WithFeedback(context, left, &var_lhs_type_feedback); |
| Node* rhs_value = SmiToWord32(right); |
| Node* shift_count = Word32And(rhs_value, Int32Constant(0x1f)); |
| Node* value = Word32Sar(lhs_value, shift_count); |
| Node* result = ChangeInt32ToTagged(value); |
| Node* result_type = SelectSmiConstant(TaggedIsSmi(result), |
| BinaryOperationFeedback::kSignedSmall, |
| BinaryOperationFeedback::kNumber); |
| Node* function = LoadRegister(Register::function_closure()); |
| UpdateFeedback(SmiOr(result_type, var_lhs_type_feedback.value()), |
| feedback_vector, slot_index, function); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // ShiftRightLogicalSmi <imm> |
| // |
| // Right shifts accumulator by the count specified in <imm>. Result is zero |
| // extended. The accumulator is converted to an int32 before the operation. The |
| // 5 lsb bits from <imm> are used as count i.e. <src> << (<imm> & 0x1F). |
| IGNITION_HANDLER(ShiftRightLogicalSmi, InterpreterAssembler) { |
| Node* left = GetAccumulator(); |
| Node* right = BytecodeOperandImmSmi(0); |
| Node* slot_index = BytecodeOperandIdx(1); |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* context = GetContext(); |
| |
| Variable var_lhs_type_feedback(this, MachineRepresentation::kTaggedSigned); |
| Node* lhs_value = |
| TruncateTaggedToWord32WithFeedback(context, left, &var_lhs_type_feedback); |
| Node* rhs_value = SmiToWord32(right); |
| Node* shift_count = Word32And(rhs_value, Int32Constant(0x1f)); |
| Node* value = Word32Shr(lhs_value, shift_count); |
| Node* result = ChangeUint32ToTagged(value); |
| Node* result_type = SelectSmiConstant(TaggedIsSmi(result), |
| BinaryOperationFeedback::kSignedSmall, |
| BinaryOperationFeedback::kNumber); |
| Node* function = LoadRegister(Register::function_closure()); |
| UpdateFeedback(SmiOr(result_type, var_lhs_type_feedback.value()), |
| feedback_vector, slot_index, function); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // ToName <dst> |
| // |
| // 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); |
| StoreRegister(result, BytecodeOperandReg(0)); |
| Dispatch(); |
| } |
| |
| // ToNumber <dst> <slot> |
| // |
| // Convert the object referenced by the accumulator to a number. |
| IGNITION_HANDLER(ToNumber, InterpreterAssembler) { |
| Node* object = GetAccumulator(); |
| Node* context = GetContext(); |
| |
| // Convert the {object} to a Number and collect feedback for the {object}. |
| Variable var_type_feedback(this, MachineRepresentation::kTaggedSigned); |
| Variable var_result(this, MachineRepresentation::kTagged); |
| Label if_done(this), if_objectissmi(this), if_objectisnumber(this), |
| if_objectisother(this, Label::kDeferred); |
| |
| GotoIf(TaggedIsSmi(object), &if_objectissmi); |
| Branch(IsHeapNumber(object), &if_objectisnumber, &if_objectisother); |
| |
| BIND(&if_objectissmi); |
| { |
| var_result.Bind(object); |
| var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kSignedSmall)); |
| Goto(&if_done); |
| } |
| |
| BIND(&if_objectisnumber); |
| { |
| var_result.Bind(object); |
| var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kNumber)); |
| Goto(&if_done); |
| } |
| |
| BIND(&if_objectisother); |
| { |
| // Convert the {object} to a Number. |
| var_result.Bind(CallBuiltin(Builtins::kNonNumberToNumber, context, object)); |
| var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kAny)); |
| Goto(&if_done); |
| } |
| |
| BIND(&if_done); |
| StoreRegister(var_result.value(), BytecodeOperandReg(0)); |
| |
| // Record the type feedback collected for {object}. |
| Node* slot_index = BytecodeOperandIdx(1); |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* function = LoadRegister(Register::function_closure()); |
| UpdateFeedback(var_type_feedback.value(), feedback_vector, slot_index, |
| function); |
| |
| Dispatch(); |
| } |
| |
| // ToObject <dst> |
| // |
| // Convert the object referenced by the accumulator to a JSReceiver. |
| IGNITION_HANDLER(ToObject, InterpreterAssembler) { |
| Callable callable = Builtins::CallableFor(isolate(), Builtins::kToObject); |
| Node* target = HeapConstant(callable.code()); |
| Node* accumulator = GetAccumulator(); |
| Node* context = GetContext(); |
| Node* result = CallStub(callable.descriptor(), target, context, accumulator); |
| StoreRegister(result, BytecodeOperandReg(0)); |
| Dispatch(); |
| } |
| |
| // ToPrimitiveToString <dst> |
| // |
| // Convert the object referenced by the accumulator to a primitive, and then |
| // convert the operand to a string, in preparation to be used by StringConcat. |
| IGNITION_HANDLER(ToPrimitiveToString, InterpreterAssembler) { |
| VARIABLE(feedback, MachineRepresentation::kTagged); |
| ConversionBuiltinsAssembler conversions_assembler(state()); |
| Node* result = conversions_assembler.ToPrimitiveToString( |
| GetContext(), GetAccumulator(), &feedback); |
| |
| Node* function = LoadRegister(Register::function_closure()); |
| UpdateFeedback(feedback.value(), LoadFeedbackVector(), BytecodeOperandIdx(1), |
| function); |
| StoreRegister(result, BytecodeOperandReg(0)); |
| Dispatch(); |
| } |
| |
| // StringConcat <first_reg> <reg_count> |
| // |
| // Concatenates the string values in registers <first_reg> to |
| // <first_reg> + <reg_count - 1> and saves the result in the accumulator. |
| IGNITION_HANDLER(StringConcat, InterpreterAssembler) { |
| Label call_runtime(this, Label::kDeferred), done(this); |
| |
| Node* first_reg_ptr = RegisterLocation(BytecodeOperandReg(0)); |
| Node* reg_count = BytecodeOperandCount(1); |
| Node* context = GetContext(); |
| |
| VARIABLE(result, MachineRepresentation::kTagged); |
| StringBuiltinsAssembler string_assembler(state()); |
| result.Bind(string_assembler.ConcatenateStrings(context, first_reg_ptr, |
| reg_count, &call_runtime)); |
| Goto(&done); |
| |
| BIND(&call_runtime); |
| { |
| Comment("Call runtime."); |
| Node* runtime_id = Int32Constant(Runtime::kStringConcat); |
| result.Bind(CallRuntimeN(runtime_id, context, first_reg_ptr, reg_count)); |
| Goto(&done); |
| } |
| |
| BIND(&done); |
| SetAccumulator(result.value()); |
| Dispatch(); |
| } |
| |
| // Inc |
| // |
| // Increments value in the accumulator by one. |
| IGNITION_HANDLER(Inc, InterpreterAssembler) { |
| Node* value = GetAccumulator(); |
| Node* context = GetContext(); |
| Node* slot_index = BytecodeOperandIdx(0); |
| Node* feedback_vector = LoadFeedbackVector(); |
| |
| // Shared entry for floating point increment. |
| Label do_finc(this), end(this); |
| Variable var_finc_value(this, MachineRepresentation::kFloat64); |
| |
| // We might need to try again due to ToNumber conversion. |
| Variable value_var(this, MachineRepresentation::kTagged); |
| Variable result_var(this, MachineRepresentation::kTagged); |
| Variable var_type_feedback(this, MachineRepresentation::kTaggedSigned); |
| Variable* loop_vars[] = {&value_var, &var_type_feedback}; |
| Label start(this, 2, loop_vars); |
| value_var.Bind(value); |
| var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kNone)); |
| Goto(&start); |
| BIND(&start); |
| { |
| value = value_var.value(); |
| |
| Label if_issmi(this), if_isnotsmi(this); |
| Branch(TaggedIsSmi(value), &if_issmi, &if_isnotsmi); |
| |
| BIND(&if_issmi); |
| { |
| // Try fast Smi addition first. |
| Node* one = SmiConstant(1); |
| Node* pair = IntPtrAddWithOverflow(BitcastTaggedToWord(value), |
| BitcastTaggedToWord(one)); |
| Node* overflow = Projection(1, pair); |
| |
| // Check if the Smi addition overflowed. |
| Label if_overflow(this), if_notoverflow(this); |
| Branch(overflow, &if_overflow, &if_notoverflow); |
| |
| BIND(&if_notoverflow); |
| var_type_feedback.Bind( |
| SmiOr(var_type_feedback.value(), |
| SmiConstant(BinaryOperationFeedback::kSignedSmall))); |
| result_var.Bind(BitcastWordToTaggedSigned(Projection(0, pair))); |
| Goto(&end); |
| |
| BIND(&if_overflow); |
| { |
| var_finc_value.Bind(SmiToFloat64(value)); |
| Goto(&do_finc); |
| } |
| } |
| |
| BIND(&if_isnotsmi); |
| { |
| // Check if the value is a HeapNumber. |
| Label if_valueisnumber(this), if_valuenotnumber(this, Label::kDeferred); |
| Node* value_map = LoadMap(value); |
| Branch(IsHeapNumberMap(value_map), &if_valueisnumber, &if_valuenotnumber); |
| |
| BIND(&if_valueisnumber); |
| { |
| // Load the HeapNumber value. |
| var_finc_value.Bind(LoadHeapNumberValue(value)); |
| Goto(&do_finc); |
| } |
| |
| BIND(&if_valuenotnumber); |
| { |
| // 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_type_feedback.value(), |
| SmiConstant(BinaryOperationFeedback::kNone))); |
| |
| Label if_valueisoddball(this), if_valuenotoddball(this); |
| Node* instance_type = LoadMapInstanceType(value_map); |
| Node* is_oddball = |
| Word32Equal(instance_type, Int32Constant(ODDBALL_TYPE)); |
| Branch(is_oddball, &if_valueisoddball, &if_valuenotoddball); |
| |
| BIND(&if_valueisoddball); |
| { |
| // Convert Oddball to Number and check again. |
| value_var.Bind(LoadObjectField(value, Oddball::kToNumberOffset)); |
| var_type_feedback.Bind( |
| SmiConstant(BinaryOperationFeedback::kNumberOrOddball)); |
| Goto(&start); |
| } |
| |
| BIND(&if_valuenotoddball); |
| { |
| // Convert to a Number first and try again. |
| var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kAny)); |
| value_var.Bind( |
| CallBuiltin(Builtins::kNonNumberToNumber, context, value)); |
| Goto(&start); |
| } |
| } |
| } |
| } |
| |
| BIND(&do_finc); |
| { |
| Node* finc_value = var_finc_value.value(); |
| Node* one = Float64Constant(1.0); |
| Node* finc_result = Float64Add(finc_value, one); |
| var_type_feedback.Bind( |
| SmiOr(var_type_feedback.value(), |
| SmiConstant(BinaryOperationFeedback::kNumber))); |
| result_var.Bind(AllocateHeapNumberWithValue(finc_result)); |
| Goto(&end); |
| } |
| |
| BIND(&end); |
| Node* function = LoadRegister(Register::function_closure()); |
| UpdateFeedback(var_type_feedback.value(), feedback_vector, slot_index, |
| function); |
| |
| SetAccumulator(result_var.value()); |
| Dispatch(); |
| } |
| |
| // Dec |
| // |
| // Decrements value in the accumulator by one. |
| IGNITION_HANDLER(Dec, InterpreterAssembler) { |
| Node* value = GetAccumulator(); |
| Node* context = GetContext(); |
| Node* slot_index = BytecodeOperandIdx(0); |
| Node* feedback_vector = LoadFeedbackVector(); |
| |
| // Shared entry for floating point decrement. |
| Label do_fdec(this), end(this); |
| Variable var_fdec_value(this, MachineRepresentation::kFloat64); |
| |
| // We might need to try again due to ToNumber conversion. |
| Variable value_var(this, MachineRepresentation::kTagged); |
| Variable result_var(this, MachineRepresentation::kTagged); |
| Variable var_type_feedback(this, MachineRepresentation::kTaggedSigned); |
| Variable* loop_vars[] = {&value_var, &var_type_feedback}; |
| Label start(this, 2, loop_vars); |
| var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kNone)); |
| value_var.Bind(value); |
| Goto(&start); |
| BIND(&start); |
| { |
| value = value_var.value(); |
| |
| Label if_issmi(this), if_isnotsmi(this); |
| Branch(TaggedIsSmi(value), &if_issmi, &if_isnotsmi); |
| |
| BIND(&if_issmi); |
| { |
| // Try fast Smi subtraction first. |
| Node* one = SmiConstant(1); |
| Node* pair = IntPtrSubWithOverflow(BitcastTaggedToWord(value), |
| BitcastTaggedToWord(one)); |
| Node* overflow = Projection(1, pair); |
| |
| // Check if the Smi subtraction overflowed. |
| Label if_overflow(this), if_notoverflow(this); |
| Branch(overflow, &if_overflow, &if_notoverflow); |
| |
| BIND(&if_notoverflow); |
| var_type_feedback.Bind( |
| SmiOr(var_type_feedback.value(), |
| SmiConstant(BinaryOperationFeedback::kSignedSmall))); |
| result_var.Bind(BitcastWordToTaggedSigned(Projection(0, pair))); |
| Goto(&end); |
| |
| BIND(&if_overflow); |
| { |
| var_fdec_value.Bind(SmiToFloat64(value)); |
| Goto(&do_fdec); |
| } |
| } |
| |
| BIND(&if_isnotsmi); |
| { |
| // Check if the value is a HeapNumber. |
| Label if_valueisnumber(this), if_valuenotnumber(this, Label::kDeferred); |
| Node* value_map = LoadMap(value); |
| Branch(IsHeapNumberMap(value_map), &if_valueisnumber, &if_valuenotnumber); |
| |
| BIND(&if_valueisnumber); |
| { |
| // Load the HeapNumber value. |
| var_fdec_value.Bind(LoadHeapNumberValue(value)); |
| Goto(&do_fdec); |
| } |
| |
| BIND(&if_valuenotnumber); |
| { |
| // 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_type_feedback.value(), |
| SmiConstant(BinaryOperationFeedback::kNone))); |
| |
| Label if_valueisoddball(this), if_valuenotoddball(this); |
| Node* instance_type = LoadMapInstanceType(value_map); |
| Node* is_oddball = |
| Word32Equal(instance_type, Int32Constant(ODDBALL_TYPE)); |
| Branch(is_oddball, &if_valueisoddball, &if_valuenotoddball); |
| |
| BIND(&if_valueisoddball); |
| { |
| // Convert Oddball to Number and check again. |
| value_var.Bind(LoadObjectField(value, Oddball::kToNumberOffset)); |
| var_type_feedback.Bind( |
| SmiConstant(BinaryOperationFeedback::kNumberOrOddball)); |
| Goto(&start); |
| } |
| |
| BIND(&if_valuenotoddball); |
| { |
| // Convert to a Number first and try again. |
| var_type_feedback.Bind(SmiConstant(BinaryOperationFeedback::kAny)); |
| value_var.Bind( |
| CallBuiltin(Builtins::kNonNumberToNumber, context, value)); |
| Goto(&start); |
| } |
| } |
| } |
| } |
| |
| BIND(&do_fdec); |
| { |
| Node* fdec_value = var_fdec_value.value(); |
| Node* one = Float64Constant(1.0); |
| Node* fdec_result = Float64Sub(fdec_value, one); |
| var_type_feedback.Bind( |
| SmiOr(var_type_feedback.value(), |
| SmiConstant(BinaryOperationFeedback::kNumber))); |
| result_var.Bind(AllocateHeapNumberWithValue(fdec_result)); |
| Goto(&end); |
| } |
| |
| BIND(&end); |
| Node* function = LoadRegister(Register::function_closure()); |
| UpdateFeedback(var_type_feedback.value(), feedback_vector, slot_index, |
| function); |
| |
| SetAccumulator(result_var.value()); |
| Dispatch(); |
| } |
| |
| // 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); |
| Node* true_value = BooleanConstant(true); |
| Node* false_value = BooleanConstant(false); |
| BranchIfToBooleanIsTrue(value, &if_true, &if_false); |
| BIND(&if_true); |
| { |
| result.Bind(false_value); |
| Goto(&end); |
| } |
| BIND(&if_false); |
| { |
| result.Bind(true_value); |
| 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 = BooleanConstant(true); |
| Node* false_value = BooleanConstant(false); |
| Branch(WordEqual(value, true_value), &if_true, &if_false); |
| BIND(&if_true); |
| { |
| result.Bind(false_value); |
| Goto(&end); |
| } |
| BIND(&if_false); |
| { |
| if (FLAG_debug_code) { |
| AbortIfWordNotEqual(value, false_value, |
| BailoutReason::kExpectedBooleanValue); |
| } |
| 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* reg_index = BytecodeOperandReg(0); |
| Node* object = LoadRegister(reg_index); |
| Node* key = GetAccumulator(); |
| Node* context = GetContext(); |
| Node* result = CallBuiltin(Builtins::kDeleteProperty, context, object, key, |
| SmiConstant(STRICT)); |
| 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* reg_index = BytecodeOperandReg(0); |
| Node* object = LoadRegister(reg_index); |
| Node* key = GetAccumulator(); |
| Node* context = GetContext(); |
| Node* result = CallBuiltin(Builtins::kDeleteProperty, context, object, key, |
| SmiConstant(SLOPPY)); |
| 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(active_function, context); |
| Node* reg = BytecodeOperandReg(0); |
| StoreRegister(result, reg); |
| 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_reg = BytecodeOperandReg(0); |
| Node* function = LoadRegister(function_reg); |
| Node* first_arg_reg = BytecodeOperandReg(1); |
| Node* first_arg = RegisterLocation(first_arg_reg); |
| Node* arg_list_count = BytecodeOperandCount(2); |
| Node* args_count; |
| if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { |
| // The receiver is implied, so it is not in the argument list. |
| args_count = arg_list_count; |
| } else { |
| // Subtract the receiver from the argument count. |
| Node* receiver_count = Int32Constant(1); |
| args_count = Int32Sub(arg_list_count, receiver_count); |
| } |
| Node* slot_id = BytecodeOperandIdx(3); |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* context = GetContext(); |
| Node* result = CallJSWithFeedback(function, context, first_arg, args_count, |
| slot_id, feedback_vector, receiver_mode); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // 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 kSlotOperandIndex = |
| kFirstArgumentOperandIndex + kReceiverOperandCount + arg_count; |
| // Indices and counts of parameters to the call stub. |
| const int kBoilerplateParameterCount = 7; |
| const int kReceiverParameterIndex = 5; |
| const int kReceiverParameterCount = 1; |
| // Only used in a DCHECK. |
| USE(kReceiverParameterCount); |
| |
| Node* function_reg = BytecodeOperandReg(0); |
| Node* function = LoadRegister(function_reg); |
| std::array<Node*, Bytecodes::kMaxOperands + kBoilerplateParameterCount> |
| temp; |
| Callable call_ic = CodeFactory::CallIC(isolate()); |
| temp[0] = HeapConstant(call_ic.code()); |
| temp[1] = function; |
| temp[2] = Int32Constant(arg_count); |
| temp[3] = BytecodeOperandIdxInt32(kSlotOperandIndex); |
| temp[4] = LoadFeedbackVector(); |
| |
| int parameter_index = kReceiverParameterIndex; |
| if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) { |
| // The first argument parameter (the receiver) is implied to be undefined. |
| Node* undefined_value = |
| HeapConstant(isolate()->factory()->undefined_value()); |
| temp[parameter_index++] = undefined_value; |
| } |
| // The bytecode argument operands are copied into the remaining argument |
| // parameters. |
| for (int i = 0; i < (kReceiverOperandCount + arg_count); ++i) { |
| Node* reg = BytecodeOperandReg(kFirstArgumentOperandIndex + i); |
| temp[parameter_index++] = LoadRegister(reg); |
| } |
| |
| DCHECK_EQ(parameter_index, |
| kReceiverParameterIndex + kReceiverParameterCount + arg_count); |
| temp[parameter_index] = GetContext(); |
| |
| Node* result = CallStubN(call_ic.descriptor(), 1, |
| arg_count + kBoilerplateParameterCount, &temp[0]); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| }; |
| |
| // Call <callable> <receiver> <arg_count> <feedback_slot_id> |
| // |
| // 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 <function_id> <first_arg> <arg_count> |
| // |
| // 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); |
| Node* first_arg_reg = BytecodeOperandReg(1); |
| Node* first_arg = RegisterLocation(first_arg_reg); |
| Node* args_count = BytecodeOperandCount(2); |
| Node* context = GetContext(); |
| Node* result = CallRuntimeN(function_id, context, first_arg, args_count); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // InvokeIntrinsic <function_id> <first_arg> <arg_count> |
| // |
| // 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); |
| Node* first_arg_reg = BytecodeOperandReg(1); |
| Node* arg_count = BytecodeOperandCount(2); |
| Node* context = GetContext(); |
| Node* result = GenerateInvokeIntrinsic(this, function_id, context, |
| first_arg_reg, arg_count); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // CallRuntimeForPair <function_id> <first_arg> <arg_count> <first_return> |
| // |
| // 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 <first_return> and |
| // <first_return + 1> |
| IGNITION_HANDLER(CallRuntimeForPair, InterpreterAssembler) { |
| // Call the runtime function. |
| Node* function_id = BytecodeOperandRuntimeId(0); |
| Node* first_arg_reg = BytecodeOperandReg(1); |
| Node* first_arg = RegisterLocation(first_arg_reg); |
| Node* args_count = BytecodeOperandCount(2); |
| Node* context = GetContext(); |
| Node* result_pair = |
| CallRuntimeN(function_id, context, first_arg, args_count, 2); |
| // Store the results in <first_return> and <first_return + 1> |
| Node* first_return_reg = BytecodeOperandReg(3); |
| Node* second_return_reg = NextRegister(first_return_reg); |
| Node* result0 = Projection(0, result_pair); |
| Node* result1 = Projection(1, result_pair); |
| StoreRegister(result0, first_return_reg); |
| StoreRegister(result1, second_return_reg); |
| Dispatch(); |
| } |
| |
| // CallJSRuntime <context_index> <receiver> <arg_count> |
| // |
| // 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 = BytecodeOperandIdx(0); |
| Node* receiver_reg = BytecodeOperandReg(1); |
| Node* first_arg = RegisterLocation(receiver_reg); |
| Node* receiver_args_count = BytecodeOperandCount(2); |
| Node* receiver_count = Int32Constant(1); |
| Node* args_count = Int32Sub(receiver_args_count, receiver_count); |
| |
| // 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. |
| Node* result = CallJS(function, context, first_arg, args_count, |
| ConvertReceiverMode::kAny); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // CallWithSpread <callable> <first_arg> <arg_count> |
| // |
| // 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_reg = BytecodeOperandReg(0); |
| Node* callable = LoadRegister(callable_reg); |
| Node* receiver_reg = BytecodeOperandReg(1); |
| Node* receiver_arg = RegisterLocation(receiver_reg); |
| Node* receiver_args_count = BytecodeOperandCount(2); |
| Node* receiver_count = Int32Constant(1); |
| Node* args_count = Int32Sub(receiver_args_count, receiver_count); |
| Node* context = GetContext(); |
| |
| // Call into Runtime function CallWithSpread which does everything. |
| Node* result = CallJSWithSpread(callable, context, receiver_arg, args_count); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // ConstructWithSpread <first_arg> <arg_count> |
| // |
| // 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_reg = BytecodeOperandReg(0); |
| Node* constructor = LoadRegister(constructor_reg); |
| Node* first_arg_reg = BytecodeOperandReg(1); |
| Node* first_arg = RegisterLocation(first_arg_reg); |
| Node* args_count = BytecodeOperandCount(2); |
| Node* context = GetContext(); |
| Node* result = ConstructWithSpread(constructor, context, new_target, |
| first_arg, args_count); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // Construct <constructor> <first_arg> <arg_count> |
| // |
| // 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_reg = BytecodeOperandReg(0); |
| Node* constructor = LoadRegister(constructor_reg); |
| Node* first_arg_reg = BytecodeOperandReg(1); |
| Node* first_arg = RegisterLocation(first_arg_reg); |
| Node* args_count = BytecodeOperandCount(2); |
| Node* slot_id = BytecodeOperandIdx(3); |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* context = GetContext(); |
| Node* result = Construct(constructor, context, new_target, first_arg, |
| args_count, 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(Token::Value compare_op) { |
| Node* reg_index = BytecodeOperandReg(0); |
| Node* lhs = LoadRegister(reg_index); |
| Node* rhs = GetAccumulator(); |
| Node* context = GetContext(); |
| |
| Variable var_type_feedback(this, MachineRepresentation::kTagged); |
| Node* result; |
| switch (compare_op) { |
| case Token::EQ: |
| result = Equal(lhs, rhs, context, &var_type_feedback); |
| break; |
| case Token::EQ_STRICT: |
| result = StrictEqual(lhs, rhs, &var_type_feedback); |
| break; |
| case Token::LT: |
| result = RelationalComparison(CodeStubAssembler::kLessThan, lhs, rhs, |
| context, &var_type_feedback); |
| break; |
| case Token::GT: |
| result = RelationalComparison(CodeStubAssembler::kGreaterThan, lhs, rhs, |
| context, &var_type_feedback); |
| break; |
| case Token::LTE: |
| result = RelationalComparison(CodeStubAssembler::kLessThanOrEqual, lhs, |
| rhs, context, &var_type_feedback); |
| break; |
| case Token::GTE: |
| result = RelationalComparison(CodeStubAssembler::kGreaterThanOrEqual, |
| lhs, rhs, context, &var_type_feedback); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| |
| Node* slot_index = BytecodeOperandIdx(1); |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* function = LoadRegister(Register::function_closure()); |
| UpdateFeedback(var_type_feedback.value(), feedback_vector, slot_index, |
| function); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| }; |
| |
| // TestEqual <src> |
| // |
| // Test if the value in the <src> register equals the accumulator. |
| IGNITION_HANDLER(TestEqual, InterpreterCompareOpAssembler) { |
| CompareOpWithFeedback(Token::Value::EQ); |
| } |
| |
| // TestEqualStrict <src> |
| // |
| // Test if the value in the <src> register is strictly equal to the accumulator. |
| IGNITION_HANDLER(TestEqualStrict, InterpreterCompareOpAssembler) { |
| CompareOpWithFeedback(Token::Value::EQ_STRICT); |
| } |
| |
| // TestLessThan <src> |
| // |
| // Test if the value in the <src> register is less than the accumulator. |
| IGNITION_HANDLER(TestLessThan, InterpreterCompareOpAssembler) { |
| CompareOpWithFeedback(Token::Value::LT); |
| } |
| |
| // TestGreaterThan <src> |
| // |
| // Test if the value in the <src> register is greater than the accumulator. |
| IGNITION_HANDLER(TestGreaterThan, InterpreterCompareOpAssembler) { |
| CompareOpWithFeedback(Token::Value::GT); |
| } |
| |
| // TestLessThanOrEqual <src> |
| // |
| // Test if the value in the <src> register is less than or equal to the |
| // accumulator. |
| IGNITION_HANDLER(TestLessThanOrEqual, InterpreterCompareOpAssembler) { |
| CompareOpWithFeedback(Token::Value::LTE); |
| } |
| |
| // TestGreaterThanOrEqual <src> |
| // |
| // Test if the value in the <src> register is greater than or equal to the |
| // accumulator. |
| IGNITION_HANDLER(TestGreaterThanOrEqual, InterpreterCompareOpAssembler) { |
| CompareOpWithFeedback(Token::Value::GTE); |
| } |
| |
| // TestEqualStrictNoFeedback <src> |
| // |
| // Test if the value in the <src> register is strictly equal to the accumulator. |
| // Type feedback is not collected. |
| IGNITION_HANDLER(TestEqualStrictNoFeedback, InterpreterAssembler) { |
| Node* reg_index = BytecodeOperandReg(0); |
| Node* lhs = LoadRegister(reg_index); |
| Node* rhs = GetAccumulator(); |
| // TODO(5310): This is called only when lhs and rhs are Smis (for ex: |
| // try-finally or generators) or strings (only when visiting |
| // ClassLiteralProperties). We should be able to optimize this and not perform |
| // the full strict equality. |
| Node* result = StrictEqual(lhs, rhs); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // TestIn <src> |
| // |
| // Test if the object referenced by the register operand is a property of the |
| // object referenced by the accumulator. |
| IGNITION_HANDLER(TestIn, InterpreterAssembler) { |
| Node* reg_index = BytecodeOperandReg(0); |
| Node* property = LoadRegister(reg_index); |
| Node* object = GetAccumulator(); |
| Node* context = GetContext(); |
| SetAccumulator(HasProperty(object, property, context)); |
| Dispatch(); |
| } |
| |
| // TestInstanceOf <src> |
| // |
| // Test if the object referenced by the <src> register is an an instance of type |
| // referenced by the accumulator. |
| IGNITION_HANDLER(TestInstanceOf, InterpreterAssembler) { |
| Node* reg_index = BytecodeOperandReg(0); |
| Node* name = LoadRegister(reg_index); |
| Node* object = GetAccumulator(); |
| Node* context = GetContext(); |
| SetAccumulator(InstanceOf(name, object, 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(BooleanConstant(false)); |
| 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* null_value = HeapConstant(isolate()->factory()->null_value()); |
| Node* result = SelectBooleanConstant(WordEqual(object, null_value)); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // TestUndefined |
| // |
| // Test if the value in the accumulator is strictly equal to undefined. |
| IGNITION_HANDLER(TestUndefined, InterpreterAssembler) { |
| Node* object = GetAccumulator(); |
| Node* undefined_value = HeapConstant(isolate()->factory()->undefined_value()); |
| Node* result = SelectBooleanConstant(WordEqual(object, undefined_value)); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // TestTypeOf <literal_flag> |
| // |
| // Tests if the object in the <accumulator> 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<int32_t>(TestTypeOfFlags::LiteralFlag::k##name), |
| int32_t cases[] = {TYPEOF_LITERAL_LIST(CASE)}; |
| #undef CASE |
| |
| Label if_true(this), if_false(this), end(this), abort(this, Label::kDeferred); |
| |
| Switch(literal_flag, &abort, cases, labels, arraysize(cases)); |
| |
| BIND(&abort); |
| { |
| Comment("Abort"); |
| Abort(BailoutReason::kUnexpectedTestTypeofLiteralFlag); |
| Goto(&if_false); |
| } |
| 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, BooleanConstant(true)), &if_true); |
| Branch(WordEqual(object, BooleanConstant(false)), &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(1 << Map::kIsUndetectable | 1 << Map::kIsCallable)); |
| Branch(Word32Equal(callable_undetectable, |
| Int32Constant(1 << Map::kIsCallable)), |
| &if_true, &if_false); |
| } |
| BIND(&if_object); |
| { |
| Comment("IfObject"); |
| GotoIf(TaggedIsSmi(object), &if_false); |
| |
| // If the object is null then return true. |
| GotoIf(WordEqual(object, NullConstant()), &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(1 << Map::kIsUndetectable | 1 << Map::kIsCallable)); |
| 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(BooleanConstant(false)); |
| Goto(&end); |
| } |
| BIND(&if_true); |
| { |
| SetAccumulator(BooleanConstant(true)); |
| Goto(&end); |
| } |
| BIND(&end); |
| Dispatch(); |
| } |
| |
| // Jump <imm> |
| // |
| // 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 <idx> |
| // |
| // Jump by the number of bytes in the Smi in the |idx| entry in the constant |
| // pool. |
| IGNITION_HANDLER(JumpConstant, InterpreterAssembler) { |
| Node* index = BytecodeOperandIdx(0); |
| Node* relative_jump = LoadAndUntagConstantPoolEntry(index); |
| Jump(relative_jump); |
| } |
| |
| // JumpIfTrue <imm> |
| // |
| // 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); |
| Node* true_value = BooleanConstant(true); |
| CSA_ASSERT(this, TaggedIsNotSmi(accumulator)); |
| CSA_ASSERT(this, IsBoolean(accumulator)); |
| JumpIfWordEqual(accumulator, true_value, relative_jump); |
| } |
| |
| // JumpIfTrueConstant <idx> |
| // |
| // 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* index = BytecodeOperandIdx(0); |
| Node* relative_jump = LoadAndUntagConstantPoolEntry(index); |
| Node* true_value = BooleanConstant(true); |
| CSA_ASSERT(this, TaggedIsNotSmi(accumulator)); |
| CSA_ASSERT(this, IsBoolean(accumulator)); |
| JumpIfWordEqual(accumulator, true_value, relative_jump); |
| } |
| |
| // JumpIfFalse <imm> |
| // |
| // 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); |
| Node* false_value = BooleanConstant(false); |
| CSA_ASSERT(this, TaggedIsNotSmi(accumulator)); |
| CSA_ASSERT(this, IsBoolean(accumulator)); |
| JumpIfWordEqual(accumulator, false_value, relative_jump); |
| } |
| |
| // JumpIfFalseConstant <idx> |
| // |
| // 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* index = BytecodeOperandIdx(0); |
| Node* relative_jump = LoadAndUntagConstantPoolEntry(index); |
| Node* false_value = BooleanConstant(false); |
| CSA_ASSERT(this, TaggedIsNotSmi(accumulator)); |
| CSA_ASSERT(this, IsBoolean(accumulator)); |
| JumpIfWordEqual(accumulator, false_value, relative_jump); |
| } |
| |
| // JumpIfToBooleanTrue <imm> |
| // |
| // 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 <idx> |
| // |
| // 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* index = BytecodeOperandIdx(0); |
| Node* relative_jump = LoadAndUntagConstantPoolEntry(index); |
| Label if_true(this), if_false(this); |
| BranchIfToBooleanIsTrue(value, &if_true, &if_false); |
| BIND(&if_true); |
| Jump(relative_jump); |
| BIND(&if_false); |
| Dispatch(); |
| } |
| |
| // JumpIfToBooleanFalse <imm> |
| // |
| // Jump by the number of bytes represented by an immediate operand if the object |
| // referenced by the accumulator is false when the object is cast to boolean. |
| IGNITION_HANDLER(JumpIfToBooleanFalse, 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); |
| Dispatch(); |
| BIND(&if_false); |
| Jump(relative_jump); |
| } |
| |
| // JumpIfToBooleanFalseConstant <idx> |
| // |
| // 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 false when the object is |
| // cast to boolean. |
| IGNITION_HANDLER(JumpIfToBooleanFalseConstant, InterpreterAssembler) { |
| Node* value = GetAccumulator(); |
| Node* index = BytecodeOperandIdx(0); |
| Node* relative_jump = LoadAndUntagConstantPoolEntry(index); |
| Label if_true(this), if_false(this); |
| BranchIfToBooleanIsTrue(value, &if_true, &if_false); |
| BIND(&if_true); |
| Dispatch(); |
| BIND(&if_false); |
| Jump(relative_jump); |
| } |
| |
| // JumpIfNull <imm> |
| // |
| // Jump by the number of bytes represented by an immediate operand if the object |
| // referenced by the accumulator is the null constant. |
| IGNITION_HANDLER(JumpIfNull, InterpreterAssembler) { |
| Node* accumulator = GetAccumulator(); |
| Node* null_value = HeapConstant(isolate()->factory()->null_value()); |
| Node* relative_jump = BytecodeOperandUImmWord(0); |
| JumpIfWordEqual(accumulator, null_value, relative_jump); |
| } |
| |
| // JumpIfNullConstant <idx> |
| // |
| // 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 the null constant. |
| IGNITION_HANDLER(JumpIfNullConstant, InterpreterAssembler) { |
| Node* accumulator = GetAccumulator(); |
| Node* null_value = HeapConstant(isolate()->factory()->null_value()); |
| Node* index = BytecodeOperandIdx(0); |
| Node* relative_jump = LoadAndUntagConstantPoolEntry(index); |
| JumpIfWordEqual(accumulator, null_value, relative_jump); |
| } |
| |
| // JumpIfNotNull <imm> |
| // |
| // Jump by the number of bytes represented by an immediate operand if the object |
| // referenced by the accumulator is not the null constant. |
| IGNITION_HANDLER(JumpIfNotNull, InterpreterAssembler) { |
| Node* accumulator = GetAccumulator(); |
| Node* null_value = HeapConstant(isolate()->factory()->null_value()); |
| Node* relative_jump = BytecodeOperandUImmWord(0); |
| JumpIfWordNotEqual(accumulator, null_value, relative_jump); |
| } |
| |
| // JumpIfNotNullConstant <idx> |
| // |
| // 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 not the null constant. |
| IGNITION_HANDLER(JumpIfNotNullConstant, InterpreterAssembler) { |
| Node* accumulator = GetAccumulator(); |
| Node* null_value = HeapConstant(isolate()->factory()->null_value()); |
| Node* index = BytecodeOperandIdx(0); |
| Node* relative_jump = LoadAndUntagConstantPoolEntry(index); |
| JumpIfWordNotEqual(accumulator, null_value, relative_jump); |
| } |
| |
| // JumpIfUndefined <imm> |
| // |
| // Jump by the number of bytes represented by an immediate operand if the object |
| // referenced by the accumulator is the undefined constant. |
| IGNITION_HANDLER(JumpIfUndefined, InterpreterAssembler) { |
| Node* accumulator = GetAccumulator(); |
| Node* undefined_value = HeapConstant(isolate()->factory()->undefined_value()); |
| Node* relative_jump = BytecodeOperandUImmWord(0); |
| JumpIfWordEqual(accumulator, undefined_value, relative_jump); |
| } |
| |
| // JumpIfUndefinedConstant <idx> |
| // |
| // 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 the undefined constant. |
| IGNITION_HANDLER(JumpIfUndefinedConstant, InterpreterAssembler) { |
| Node* accumulator = GetAccumulator(); |
| Node* undefined_value = HeapConstant(isolate()->factory()->undefined_value()); |
| Node* index = BytecodeOperandIdx(0); |
| Node* relative_jump = LoadAndUntagConstantPoolEntry(index); |
| JumpIfWordEqual(accumulator, undefined_value, relative_jump); |
| } |
| |
| // JumpIfNotUndefined <imm> |
| // |
| // Jump by the number of bytes represented by an immediate operand if the object |
| // referenced by the accumulator is not the undefined constant. |
| IGNITION_HANDLER(JumpIfNotUndefined, InterpreterAssembler) { |
| Node* accumulator = GetAccumulator(); |
| Node* undefined_value = HeapConstant(isolate()->factory()->undefined_value()); |
| Node* relative_jump = BytecodeOperandUImmWord(0); |
| JumpIfWordNotEqual(accumulator, undefined_value, relative_jump); |
| } |
| |
| // JumpIfNotUndefinedConstant <idx> |
| // |
| // 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 not the undefined |
| // constant. |
| IGNITION_HANDLER(JumpIfNotUndefinedConstant, InterpreterAssembler) { |
| Node* accumulator = GetAccumulator(); |
| Node* undefined_value = HeapConstant(isolate()->factory()->undefined_value()); |
| Node* index = BytecodeOperandIdx(0); |
| Node* relative_jump = LoadAndUntagConstantPoolEntry(index); |
| JumpIfWordNotEqual(accumulator, undefined_value, relative_jump); |
| } |
| |
| // JumpIfJSReceiver <imm> |
| // |
| // Jump by the number of bytes represented by an immediate operand if the object |
| // referenced by the accumulator is a JSReceiver. |
| IGNITION_HANDLER(JumpIfJSReceiver, InterpreterAssembler) { |
| Node* accumulator = GetAccumulator(); |
| Node* relative_jump = BytecodeOperandUImmWord(0); |
| |
| Label if_object(this), if_notobject(this, Label::kDeferred), if_notsmi(this); |
| Branch(TaggedIsSmi(accumulator), &if_notobject, &if_notsmi); |
| |
| BIND(&if_notsmi); |
| Branch(IsJSReceiver(accumulator), &if_object, &if_notobject); |
| BIND(&if_object); |
| Jump(relative_jump); |
| |
| BIND(&if_notobject); |
| Dispatch(); |
| } |
| |
| // JumpIfJSReceiverConstant <idx> |
| // |
| // 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 a JSReceiver. |
| IGNITION_HANDLER(JumpIfJSReceiverConstant, InterpreterAssembler) { |
| Node* accumulator = GetAccumulator(); |
| Node* index = BytecodeOperandIdx(0); |
| Node* relative_jump = LoadAndUntagConstantPoolEntry(index); |
| |
| Label if_object(this), if_notobject(this), if_notsmi(this); |
| Branch(TaggedIsSmi(accumulator), &if_notobject, &if_notsmi); |
| |
| BIND(&if_notsmi); |
| Branch(IsJSReceiver(accumulator), &if_object, &if_notobject); |
| |
| BIND(&if_object); |
| Jump(relative_jump); |
| |
| BIND(&if_notobject); |
| Dispatch(); |
| } |
| |
| // JumpLoop <imm> <loop_depth> |
| // |
| // Jump by the number of bytes represented by the immediate operand |imm|. Also |
| // performs a loop nesting check and potentially triggers OSR in case the |
| // current OSR level matches (or exceeds) the specified |loop_depth|. |
| IGNITION_HANDLER(JumpLoop, InterpreterAssembler) { |
| Node* relative_jump = BytecodeOperandUImmWord(0); |
| Node* loop_depth = BytecodeOperandImm(1); |
| Node* osr_level = LoadOSRNestingLevel(); |
| |
| // Check if OSR points at the given {loop_depth} are armed by comparing it to |
| // the current {osr_level} loaded from the header of the BytecodeArray. |
| Label ok(this), osr_armed(this, Label::kDeferred); |
| Node* condition = Int32GreaterThanOrEqual(loop_depth, osr_level); |
| Branch(condition, &ok, &osr_armed); |
| |
| BIND(&ok); |
| JumpBackward(relative_jump); |
| |
| BIND(&osr_armed); |
| { |
| Callable callable = CodeFactory::InterpreterOnStackReplacement(isolate()); |
| Node* target = HeapConstant(callable.code()); |
| Node* context = GetContext(); |
| CallStub(callable.descriptor(), target, context); |
| JumpBackward(relative_jump); |
| } |
| } |
| |
| // SwitchOnSmiNoFeedback <table_start> <table_length> <case_value_base> |
| // |
| // Jump by the number of bytes defined by a Smi in a table in the constant pool, |
| // where the table starts at |table_start| and has |table_length| entries. |
| // The table is indexed by the accumulator, minus |case_value_base|. If the |
| // case_value falls outside of the table |table_length|, fall-through to the |
| // next bytecode. |
| IGNITION_HANDLER(SwitchOnSmiNoFeedback, InterpreterAssembler) { |
| Node* acc = GetAccumulator(); |
| Node* table_start = BytecodeOperandIdx(0); |
| Node* table_length = BytecodeOperandUImmWord(1); |
| Node* case_value_base = BytecodeOperandImmIntPtr(2); |
| |
| Label fall_through(this); |
| |
| // The accumulator must be a Smi. |
| // TODO(leszeks): Add a bytecode with type feedback that allows other |
| // accumulator values. |
| CSA_ASSERT(this, TaggedIsSmi(acc)); |
| |
| Node* case_value = IntPtrSub(SmiUntag(acc), case_value_base); |
| GotoIf(IntPtrLessThan(case_value, IntPtrConstant(0)), &fall_through); |
| GotoIf(IntPtrGreaterThanOrEqual(case_value, table_length), &fall_through); |
| Node* entry = IntPtrAdd(table_start, case_value); |
| Node* relative_jump = LoadAndUntagConstantPoolEntry(entry); |
| Jump(relative_jump); |
| |
| BIND(&fall_through); |
| Dispatch(); |
| } |
| |
| // CreateRegExpLiteral <pattern_idx> <literal_idx> <flags> |
| // |
| // Creates a regular expression literal for literal index <literal_idx> with |
| // <flags> and the pattern in <pattern_idx>. |
| IGNITION_HANDLER(CreateRegExpLiteral, InterpreterAssembler) { |
| Node* index = BytecodeOperandIdx(0); |
| Node* pattern = LoadConstantPoolEntry(index); |
| Node* literal_index = BytecodeOperandIdxSmi(1); |
| Node* flags = SmiFromWord32(BytecodeOperandFlag(2)); |
| Node* closure = LoadRegister(Register::function_closure()); |
| Node* context = GetContext(); |
| ConstructorBuiltinsAssembler constructor_assembler(state()); |
| Node* result = constructor_assembler.EmitFastCloneRegExp( |
| closure, literal_index, pattern, flags, context); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // CreateArrayLiteral <element_idx> <literal_idx> <flags> |
| // |
| // Creates an array literal for literal index <literal_idx> with |
| // CreateArrayLiteral flags <flags> and constant elements in <element_idx>. |
| IGNITION_HANDLER(CreateArrayLiteral, InterpreterAssembler) { |
| Node* literal_index = BytecodeOperandIdxSmi(1); |
| Node* closure = LoadRegister(Register::function_closure()); |
| Node* context = GetContext(); |
| Node* bytecode_flags = BytecodeOperandFlag(2); |
| |
| Label fast_shallow_clone(this), call_runtime(this, Label::kDeferred); |
| Branch(IsSetWord32<CreateArrayLiteralFlags::FastCloneSupportedBit>( |
| bytecode_flags), |
| &fast_shallow_clone, &call_runtime); |
| |
| BIND(&fast_shallow_clone); |
| { |
| ConstructorBuiltinsAssembler constructor_assembler(state()); |
| Node* result = constructor_assembler.EmitFastCloneShallowArray( |
| closure, literal_index, context, &call_runtime, TRACK_ALLOCATION_SITE); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| BIND(&call_runtime); |
| { |
| Node* flags_raw = DecodeWordFromWord32<CreateArrayLiteralFlags::FlagsBits>( |
| bytecode_flags); |
| Node* flags = SmiTag(flags_raw); |
| Node* index = BytecodeOperandIdx(0); |
| Node* constant_elements = LoadConstantPoolEntry(index); |
| Node* result = CallRuntime(Runtime::kCreateArrayLiteral, context, closure, |
| literal_index, constant_elements, flags); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| } |
| |
| // CreateObjectLiteral <element_idx> <literal_idx> <flags> |
| // |
| // Creates an object literal for literal index <literal_idx> with |
| // CreateObjectLiteralFlags <flags> and constant elements in <element_idx>. |
| IGNITION_HANDLER(CreateObjectLiteral, InterpreterAssembler) { |
| Node* literal_index = BytecodeOperandIdxSmi(1); |
| Node* bytecode_flags = BytecodeOperandFlag(2); |
| Node* closure = LoadRegister(Register::function_closure()); |
| |
| // Check if we can do a fast clone or have to call the runtime. |
| Label if_fast_clone(this), if_not_fast_clone(this, Label::kDeferred); |
| Branch(IsSetWord32<CreateObjectLiteralFlags::FastCloneSupportedBit>( |
| bytecode_flags), |
| &if_fast_clone, &if_not_fast_clone); |
| |
| BIND(&if_fast_clone); |
| { |
| // If we can do a fast clone do the fast-path in FastCloneShallowObjectStub. |
| ConstructorBuiltinsAssembler constructor_assembler(state()); |
| Node* result = constructor_assembler.EmitFastCloneShallowObject( |
| &if_not_fast_clone, closure, literal_index); |
| StoreRegister(result, BytecodeOperandReg(3)); |
| Dispatch(); |
| } |
| |
| BIND(&if_not_fast_clone); |
| { |
| // If we can't do a fast clone, call into the runtime. |
| Node* index = BytecodeOperandIdx(0); |
| Node* boilerplate_description = LoadConstantPoolEntry(index); |
| Node* context = GetContext(); |
| |
| Node* flags_raw = DecodeWordFromWord32<CreateObjectLiteralFlags::FlagsBits>( |
| bytecode_flags); |
| Node* flags = SmiTag(flags_raw); |
| |
| Node* result = CallRuntime(Runtime::kCreateObjectLiteral, context, closure, |
| literal_index, boilerplate_description, flags); |
| StoreRegister(result, BytecodeOperandReg(3)); |
| // TODO(klaasb) build a single dispatch once the call is inlined |
| Dispatch(); |
| } |
| } |
| |
| // CreateClosure <index> <slot> <tenured> |
| // |
| // Creates a new closure for SharedFunctionInfo at position |index| in the |
| // constant pool and with the PretenureFlag <tenured>. |
| IGNITION_HANDLER(CreateClosure, InterpreterAssembler) { |
| Node* index = BytecodeOperandIdx(0); |
| Node* shared = LoadConstantPoolEntry(index); |
| Node* flags = BytecodeOperandFlag(2); |
| Node* context = GetContext(); |
| |
| Label call_runtime(this, Label::kDeferred); |
| GotoIfNot(IsSetWord32<CreateClosureFlags::FastNewClosureBit>(flags), |
| &call_runtime); |
| ConstructorBuiltinsAssembler constructor_assembler(state()); |
| Node* vector_index = BytecodeOperandIdx(1); |
| vector_index = SmiTag(vector_index); |
| Node* feedback_vector = LoadFeedbackVector(); |
| SetAccumulator(constructor_assembler.EmitFastNewClosure( |
| shared, feedback_vector, vector_index, context)); |
| Dispatch(); |
| |
| BIND(&call_runtime); |
| { |
| Node* tenured_raw = |
| DecodeWordFromWord32<CreateClosureFlags::PretenuredBit>(flags); |
| Node* tenured = SmiTag(tenured_raw); |
| feedback_vector = LoadFeedbackVector(); |
| vector_index = BytecodeOperandIdx(1); |
| vector_index = SmiTag(vector_index); |
| Node* result = CallRuntime(Runtime::kInterpreterNewClosure, context, shared, |
| feedback_vector, vector_index, tenured); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| } |
| |
| // CreateBlockContext <index> |
| // |
| // Creates a new block context with the scope info constant at |index| and the |
| // closure in the accumulator. |
| IGNITION_HANDLER(CreateBlockContext, InterpreterAssembler) { |
| Node* index = BytecodeOperandIdx(0); |
| Node* scope_info = LoadConstantPoolEntry(index); |
| Node* closure = GetAccumulator(); |
| Node* context = GetContext(); |
| SetAccumulator( |
| CallRuntime(Runtime::kPushBlockContext, context, scope_info, closure)); |
| Dispatch(); |
| } |
| |
| // CreateCatchContext <exception> <name_idx> <scope_info_idx> |
| // |
| // Creates a new context for a catch block with the |exception| in a register, |
| // the variable name at |name_idx|, the ScopeInfo at |scope_info_idx|, and the |
| // closure in the accumulator. |
| IGNITION_HANDLER(CreateCatchContext, InterpreterAssembler) { |
| Node* exception_reg = BytecodeOperandReg(0); |
| Node* exception = LoadRegister(exception_reg); |
| Node* name_idx = BytecodeOperandIdx(1); |
| Node* name = LoadConstantPoolEntry(name_idx); |
| Node* scope_info_idx = BytecodeOperandIdx(2); |
| Node* scope_info = LoadConstantPoolEntry(scope_info_idx); |
| Node* closure = GetAccumulator(); |
| Node* context = GetContext(); |
| SetAccumulator(CallRuntime(Runtime::kPushCatchContext, context, name, |
| exception, scope_info, closure)); |
| Dispatch(); |
| } |
| |
| // CreateFunctionContext <slots> |
| // |
| // Creates a new context with number of |slots| for the function closure. |
| IGNITION_HANDLER(CreateFunctionContext, InterpreterAssembler) { |
| Node* closure = LoadRegister(Register::function_closure()); |
| Node* slots = BytecodeOperandUImm(0); |
| Node* context = GetContext(); |
| ConstructorBuiltinsAssembler constructor_assembler(state()); |
| SetAccumulator(constructor_assembler.EmitFastNewFunctionContext( |
| closure, slots, context, FUNCTION_SCOPE)); |
| Dispatch(); |
| } |
| |
| // CreateEvalContext <slots> |
| // |
| // Creates a new context with number of |slots| for an eval closure. |
| IGNITION_HANDLER(CreateEvalContext, InterpreterAssembler) { |
| Node* closure = LoadRegister(Register::function_closure()); |
| Node* slots = BytecodeOperandUImm(0); |
| Node* context = GetContext(); |
| ConstructorBuiltinsAssembler constructor_assembler(state()); |
| SetAccumulator(constructor_assembler.EmitFastNewFunctionContext( |
| closure, slots, context, EVAL_SCOPE)); |
| Dispatch(); |
| } |
| |
| // CreateWithContext <register> <scope_info_idx> |
| // |
| // Creates a new context with the ScopeInfo at |scope_info_idx| for a |
| // with-statement with the object in |register| and the closure in the |
| // accumulator. |
| IGNITION_HANDLER(CreateWithContext, InterpreterAssembler) { |
| Node* reg_index = BytecodeOperandReg(0); |
| Node* object = LoadRegister(reg_index); |
| Node* scope_info_idx = BytecodeOperandIdx(1); |
| Node* scope_info = LoadConstantPoolEntry(scope_info_idx); |
| Node* closure = GetAccumulator(); |
| Node* context = GetContext(); |
| SetAccumulator(CallRuntime(Runtime::kPushWithContext, context, object, |
| scope_info, closure)); |
| Dispatch(); |
| } |
| |
| // CreateMappedArguments |
| // |
| // Creates a new mapped arguments object. |
| IGNITION_HANDLER(CreateMappedArguments, InterpreterAssembler) { |
| Node* closure = LoadRegister(Register::function_closure()); |
| Node* context = GetContext(); |
| |
| Label if_duplicate_parameters(this, Label::kDeferred); |
| Label if_not_duplicate_parameters(this); |
| |
| // Check if function has duplicate parameters. |
| // TODO(rmcilroy): Remove this check when FastNewSloppyArgumentsStub supports |
| // duplicate parameters. |
| Node* shared_info = |
| LoadObjectField(closure, JSFunction::kSharedFunctionInfoOffset); |
| Node* compiler_hints = |
| LoadObjectField(shared_info, SharedFunctionInfo::kCompilerHintsOffset, |
| MachineType::Uint32()); |
| Node* has_duplicate_parameters = |
| IsSetWord32<SharedFunctionInfo::HasDuplicateParametersBit>( |
| compiler_hints); |
| Branch(has_duplicate_parameters, &if_duplicate_parameters, |
| &if_not_duplicate_parameters); |
| |
| BIND(&if_not_duplicate_parameters); |
| { |
| ArgumentsBuiltinsAssembler constructor_assembler(state()); |
| Node* result = |
| constructor_assembler.EmitFastNewSloppyArguments(context, closure); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| BIND(&if_duplicate_parameters); |
| { |
| Node* result = |
| CallRuntime(Runtime::kNewSloppyArguments_Generic, context, closure); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| } |
| |
| // CreateUnmappedArguments |
| // |
| // Creates a new unmapped arguments object. |
| IGNITION_HANDLER(CreateUnmappedArguments, InterpreterAssembler) { |
| Node* context = GetContext(); |
| Node* closure = LoadRegister(Register::function_closure()); |
| ArgumentsBuiltinsAssembler builtins_assembler(state()); |
| Node* result = |
| builtins_assembler.EmitFastNewStrictArguments(context, closure); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // CreateRestParameter |
| // |
| // Creates a new rest parameter array. |
| IGNITION_HANDLER(CreateRestParameter, InterpreterAssembler) { |
| Node* closure = LoadRegister(Register::function_closure()); |
| Node* context = GetContext(); |
| ArgumentsBuiltinsAssembler builtins_assembler(state()); |
| Node* result = builtins_assembler.EmitFastNewRestParameter(context, closure); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // StackCheck |
| // |
| // Performs a stack guard check. |
| IGNITION_HANDLER(StackCheck, InterpreterAssembler) { |
| Label ok(this), stack_check_interrupt(this, Label::kDeferred); |
| |
| Node* interrupt = StackCheckTriggeredInterrupt(); |
| Branch(interrupt, &stack_check_interrupt, &ok); |
| |
| BIND(&ok); |
| Dispatch(); |
| |
| BIND(&stack_check_interrupt); |
| { |
| Node* context = GetContext(); |
| CallRuntime(Runtime::kStackGuard, context); |
| Dispatch(); |
| } |
| } |
| |
| // SetPendingMessage |
| // |
| // Sets the pending message to the value in the accumulator, and returns the |
| // previous pending message in the accumulator. |
| IGNITION_HANDLER(SetPendingMessage, InterpreterAssembler) { |
| Node* pending_message = ExternalConstant( |
| ExternalReference::address_of_pending_message_obj(isolate())); |
| Node* previous_message = Load(MachineType::TaggedPointer(), pending_message); |
| Node* new_message = GetAccumulator(); |
| StoreNoWriteBarrier(MachineRepresentation::kTaggedPointer, pending_message, |
| new_message); |
| SetAccumulator(previous_message); |
| Dispatch(); |
| } |
| |
| // Throw |
| // |
| // Throws the exception in the accumulator. |
| IGNITION_HANDLER(Throw, InterpreterAssembler) { |
| Node* exception = GetAccumulator(); |
| Node* context = GetContext(); |
| CallRuntime(Runtime::kThrow, context, exception); |
| // We shouldn't ever return from a throw. |
| Abort(kUnexpectedReturnFromThrow); |
| } |
| |
| // ReThrow |
| // |
| // Re-throws the exception in the accumulator. |
| IGNITION_HANDLER(ReThrow, InterpreterAssembler) { |
| Node* exception = GetAccumulator(); |
| Node* context = GetContext(); |
| CallRuntime(Runtime::kReThrow, context, exception); |
| // We shouldn't ever return from a throw. |
| Abort(kUnexpectedReturnFromThrow); |
| } |
| |
| // Return |
| // |
| // Return the value in the accumulator. |
| IGNITION_HANDLER(Return, InterpreterAssembler) { |
| UpdateInterruptBudgetOnReturn(); |
| Node* accumulator = GetAccumulator(); |
| Return(accumulator); |
| } |
| |
| // ThrowReferenceErrorIfHole <variable_name> |
| // |
| // Throws an exception if the value in the accumulator is TheHole. |
| IGNITION_HANDLER(ThrowReferenceErrorIfHole, InterpreterAssembler) { |
| Node* value = GetAccumulator(); |
| Node* the_hole_value = HeapConstant(isolate()->factory()->the_hole_value()); |
| |
| Label throw_error(this, Label::kDeferred); |
| GotoIf(WordEqual(value, the_hole_value), &throw_error); |
| Dispatch(); |
| |
| BIND(&throw_error); |
| { |
| Node* name = LoadConstantPoolEntry(BytecodeOperandIdx(0)); |
| CallRuntime(Runtime::kThrowReferenceError, GetContext(), name); |
| // We shouldn't ever return from a throw. |
| Abort(kUnexpectedReturnFromThrow); |
| } |
| } |
| |
| // ThrowSuperNotCalledIfHole |
| // |
| // Throws an exception if the value in the accumulator is TheHole. |
| IGNITION_HANDLER(ThrowSuperNotCalledIfHole, InterpreterAssembler) { |
| Node* value = GetAccumulator(); |
| Node* the_hole_value = HeapConstant(isolate()->factory()->the_hole_value()); |
| |
| Label throw_error(this, Label::kDeferred); |
| GotoIf(WordEqual(value, the_hole_value), &throw_error); |
| Dispatch(); |
| |
| BIND(&throw_error); |
| { |
| CallRuntime(Runtime::kThrowSuperNotCalled, GetContext()); |
| // We shouldn't ever return from a throw. |
| Abort(kUnexpectedReturnFromThrow); |
| } |
| } |
| |
| // ThrowSuperAlreadyCalledIfNotHole |
| // |
| // Throws SuperAleradyCalled exception if the value in the accumulator is not |
| // TheHole. |
| IGNITION_HANDLER(ThrowSuperAlreadyCalledIfNotHole, InterpreterAssembler) { |
| Node* value = GetAccumulator(); |
| Node* the_hole_value = HeapConstant(isolate()->factory()->the_hole_value()); |
| |
| Label throw_error(this, Label::kDeferred); |
| GotoIf(WordNotEqual(value, the_hole_value), &throw_error); |
| Dispatch(); |
| |
| BIND(&throw_error); |
| { |
| CallRuntime(Runtime::kThrowSuperAlreadyCalledError, GetContext()); |
| // We shouldn't ever return from a throw. |
| Abort(kUnexpectedReturnFromThrow); |
| } |
| } |
| |
| // Debugger |
| // |
| // Call runtime to handle debugger statement. |
| IGNITION_HANDLER(Debugger, InterpreterAssembler) { |
| Node* context = GetContext(); |
| CallStub(CodeFactory::HandleDebuggerStatement(isolate()), context); |
| Dispatch(); |
| } |
| |
| // DebugBreak |
| // |
| // Call runtime to handle a debug break. |
| #define DEBUG_BREAK(Name, ...) \ |
| IGNITION_HANDLER(Name, InterpreterAssembler) { \ |
| Node* context = GetContext(); \ |
| Node* accumulator = GetAccumulator(); \ |
| Node* original_handler = \ |
| CallRuntime(Runtime::kDebugBreakOnBytecode, context, accumulator); \ |
| MaybeDropFrames(context); \ |
| DispatchToBytecodeHandler(original_handler); \ |
| } |
| DEBUG_BREAK_BYTECODE_LIST(DEBUG_BREAK); |
| #undef DEBUG_BREAK |
| |
| // IncBlockCounter <slot> |
| // |
| // Increment the execution count for the given slot. Used for block code |
| // coverage. |
| IGNITION_HANDLER(IncBlockCounter, InterpreterAssembler) { |
| Node* closure = LoadRegister(Register::function_closure()); |
| Node* coverage_array_slot = BytecodeOperandIdxSmi(0); |
| Node* context = GetContext(); |
| |
| CallRuntime(Runtime::kIncBlockCounter, context, closure, coverage_array_slot); |
| |
| Dispatch(); |
| } |
| |
| class InterpreterForInPrepareAssembler : public InterpreterAssembler { |
| public: |
| InterpreterForInPrepareAssembler(CodeAssemblerState* state, Bytecode bytecode, |
| OperandScale operand_scale) |
| : InterpreterAssembler(state, bytecode, operand_scale) {} |
| |
| void BuildForInPrepareResult(Node* output_register, Node* cache_type, |
| Node* cache_array, Node* cache_length) { |
| StoreRegister(cache_type, output_register); |
| output_register = NextRegister(output_register); |
| StoreRegister(cache_array, output_register); |
| output_register = NextRegister(output_register); |
| StoreRegister(cache_length, output_register); |
| } |
| }; |
| |
| // ForInPrepare <receiver> <cache_info_triple> |
| // |
| // Returns state for for..in loop execution based on the object in the register |
| // |receiver|. The object must not be null or undefined and must have been |
| // converted to a receiver already. |
| // The result is output in registers |cache_info_triple| to |
| // |cache_info_triple + 2|, with the registers holding cache_type, cache_array, |
| // and cache_length respectively. |
| IGNITION_HANDLER(ForInPrepare, InterpreterForInPrepareAssembler) { |
| Node* object_register = BytecodeOperandReg(0); |
| Node* output_register = BytecodeOperandReg(1); |
| Node* receiver = LoadRegister(object_register); |
| Node* context = GetContext(); |
| |
| Node* cache_type; |
| Node* cache_array; |
| Node* cache_length; |
| Label call_runtime(this, Label::kDeferred), |
| nothing_to_iterate(this, Label::kDeferred); |
| |
| ForInBuiltinsAssembler forin_assembler(state()); |
| std::tie(cache_type, cache_array, cache_length) = |
| forin_assembler.EmitForInPrepare(receiver, context, &call_runtime, |
| ¬hing_to_iterate); |
| |
| BuildForInPrepareResult(output_register, cache_type, cache_array, |
| cache_length); |
| Dispatch(); |
| |
| BIND(&call_runtime); |
| { |
| Node* result_triple = |
| CallRuntime(Runtime::kForInPrepare, context, receiver); |
| Node* cache_type = Projection(0, result_triple); |
| Node* cache_array = Projection(1, result_triple); |
| Node* cache_length = Projection(2, result_triple); |
| BuildForInPrepareResult(output_register, cache_type, cache_array, |
| cache_length); |
| Dispatch(); |
| } |
| BIND(¬hing_to_iterate); |
| { |
| // Receiver is null or undefined or descriptors are zero length. |
| Node* zero = SmiConstant(0); |
| BuildForInPrepareResult(output_register, zero, zero, zero); |
| Dispatch(); |
| } |
| } |
| |
| // ForInNext <receiver> <index> <cache_info_pair> |
| // |
| // Returns the next enumerable property in the the accumulator. |
| IGNITION_HANDLER(ForInNext, InterpreterAssembler) { |
| Node* receiver_reg = BytecodeOperandReg(0); |
| Node* receiver = LoadRegister(receiver_reg); |
| Node* index_reg = BytecodeOperandReg(1); |
| Node* index = LoadRegister(index_reg); |
| Node* cache_type_reg = BytecodeOperandReg(2); |
| Node* cache_type = LoadRegister(cache_type_reg); |
| Node* cache_array_reg = NextRegister(cache_type_reg); |
| Node* cache_array = LoadRegister(cache_array_reg); |
| |
| // Load the next key from the enumeration array. |
| Node* key = LoadFixedArrayElement(cache_array, index, 0, |
| CodeStubAssembler::SMI_PARAMETERS); |
| |
| // Check if we can use the for-in fast path potentially using the enum cache. |
| Label if_fast(this), if_slow(this, Label::kDeferred); |
| Node* receiver_map = LoadMap(receiver); |
| Branch(WordEqual(receiver_map, cache_type), &if_fast, &if_slow); |
| BIND(&if_fast); |
| { |
| // Enum cache in use for {receiver}, the {key} is definitely valid. |
| SetAccumulator(key); |
| Dispatch(); |
| } |
| BIND(&if_slow); |
| { |
| // Record the fact that we hit the for-in slow path. |
| Node* vector_index = BytecodeOperandIdx(3); |
| Node* feedback_vector = LoadFeedbackVector(); |
| Node* megamorphic_sentinel = |
| HeapConstant(FeedbackVector::MegamorphicSentinel(isolate())); |
| StoreFixedArrayElement(feedback_vector, vector_index, megamorphic_sentinel, |
| SKIP_WRITE_BARRIER); |
| |
| // Need to filter the {key} for the {receiver}. |
| Node* context = GetContext(); |
| Node* result = CallBuiltin(Builtins::kForInFilter, context, key, receiver); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| } |
| |
| // ForInContinue <index> <cache_length> |
| // |
| // Returns false if the end of the enumerable properties has been reached. |
| IGNITION_HANDLER(ForInContinue, InterpreterAssembler) { |
| Node* index_reg = BytecodeOperandReg(0); |
| Node* index = LoadRegister(index_reg); |
| Node* cache_length_reg = BytecodeOperandReg(1); |
| Node* cache_length = LoadRegister(cache_length_reg); |
| |
| // Check if {index} is at {cache_length} already. |
| Label if_true(this), if_false(this), end(this); |
| Branch(WordEqual(index, cache_length), &if_true, &if_false); |
| BIND(&if_true); |
| { |
| SetAccumulator(BooleanConstant(false)); |
| Goto(&end); |
| } |
| BIND(&if_false); |
| { |
| SetAccumulator(BooleanConstant(true)); |
| Goto(&end); |
| } |
| BIND(&end); |
| Dispatch(); |
| } |
| |
| // ForInStep <index> |
| // |
| // Increments the loop counter in register |index| and stores the result |
| // in the accumulator. |
| IGNITION_HANDLER(ForInStep, InterpreterAssembler) { |
| Node* index_reg = BytecodeOperandReg(0); |
| Node* index = LoadRegister(index_reg); |
| Node* one = SmiConstant(1); |
| Node* result = SmiAdd(index, one); |
| SetAccumulator(result); |
| Dispatch(); |
| } |
| |
| // Wide |
| // |
| // Prefix bytecode indicating next bytecode has wide (16-bit) operands. |
| IGNITION_HANDLER(Wide, InterpreterAssembler) { |
| DispatchWide(OperandScale::kDouble); |
| } |
| |
| // ExtraWide |
| // |
| // Prefix bytecode indicating next bytecode has extra-wide (32-bit) operands. |
| IGNITION_HANDLER(ExtraWide, InterpreterAssembler) { |
| DispatchWide(OperandScale::kQuadruple); |
| } |
| |
| // Illegal |
| // |
| // An invalid bytecode aborting execution if dispatched. |
| IGNITION_HANDLER(Illegal, InterpreterAssembler) { Abort(kInvalidBytecode); } |
| |
| // Nop |
| // |
| // No operation. |
| IGNITION_HANDLER(Nop, InterpreterAssembler) { Dispatch(); } |
| |
| // SuspendGenerator <generator> <first input register> <register count> |
| // |
| // Exports the register file and stores it into the generator. Also stores the |
| // current context, the state given in the accumulator, and the current bytecode |
| // offset (for debugging purposes) into the generator. |
| IGNITION_HANDLER(SuspendGenerator, InterpreterAssembler) { |
| Node* generator_reg = BytecodeOperandReg(0); |
| |
| Node* generator = LoadRegister(generator_reg); |
| |
| Label if_stepping(this, Label::kDeferred), ok(this); |
| Node* step_action_address = ExternalConstant( |
| ExternalReference::debug_last_step_action_address(isolate())); |
| Node* step_action = Load(MachineType::Int8(), step_action_address); |
| STATIC_ASSERT(StepIn > StepNext); |
| STATIC_ASSERT(LastStepAction == StepIn); |
| Node* step_next = Int32Constant(StepNext); |
| Branch(Int32LessThanOrEqual(step_next, step_action), &if_stepping, &ok); |
| BIND(&ok); |
| |
| Node* array = |
| LoadObjectField(generator, JSGeneratorObject::kRegisterFileOffset); |
| Node* context = GetContext(); |
| Node* state = GetAccumulator(); |
| |
| // Bytecode operand 1 should be always 0 (we are always store registers |
| // from the beginning). |
| CSA_ASSERT(this, WordEqual(BytecodeOperandReg(1), |
| IntPtrConstant(Register(0).ToOperand()))); |
| // Bytecode operand 2 is the number of registers to store to the generator. |
| Node* register_count = ChangeUint32ToWord(BytecodeOperandCount(2)); |
| ExportRegisterFile(array, register_count); |
| StoreObjectField(generator, JSGeneratorObject::kContextOffset, context); |
| StoreObjectField(generator, JSGeneratorObject::kContinuationOffset, state); |
| |
| // Store the bytecode offset in the [input_or_debug_pos] field, to be used by |
| // the inspector. |
| Node* offset = SmiTag(BytecodeOffset()); |
| StoreObjectField(generator, JSGeneratorObject::kInputOrDebugPosOffset, |
| offset); |
| Dispatch(); |
| |
| BIND(&if_stepping); |
| { |
| Node* context = GetContext(); |
| CallRuntime(Runtime::kDebugRecordGenerator, context, generator); |
| Goto(&ok); |
| } |
| } |
| |
| // RestoreGeneratorState <generator> |
| // |
| // Loads the generator's state and stores it in the accumulator, |
| // before overwriting it with kGeneratorExecuting. |
| IGNITION_HANDLER(RestoreGeneratorState, InterpreterAssembler) { |
| Node* generator_reg = BytecodeOperandReg(0); |
| Node* generator = LoadRegister(generator_reg); |
| |
| Node* old_state = |
| LoadObjectField(generator, JSGeneratorObject::kContinuationOffset); |
| Node* new_state = Int32Constant(JSGeneratorObject::kGeneratorExecuting); |
| StoreObjectField(generator, JSGeneratorObject::kContinuationOffset, |
| SmiTag(new_state)); |
| SetAccumulator(old_state); |
| |
| Dispatch(); |
| } |
| |
| // RestoreGeneratorRegisters <generator> <first output register> <register |
| // count> |
| // |
| // Imports the register file stored in the generator. |
| IGNITION_HANDLER(RestoreGeneratorRegisters, InterpreterAssembler) { |
| Node* generator_reg = BytecodeOperandReg(0); |
| // Bytecode operand 1 is the start register. It should always be 0, so let's |
| // ignore it. |
| CSA_ASSERT(this, WordEqual(BytecodeOperandReg(1), |
| IntPtrConstant(Register(0).ToOperand()))); |
| // Bytecode operand 2 is the number of registers to store to the generator. |
| Node* register_count = ChangeUint32ToWord(BytecodeOperandCount(2)); |
| |
| Node* generator = LoadRegister(generator_reg); |
| |
| ImportRegisterFile( |
| LoadObjectField(generator, JSGeneratorObject::kRegisterFileOffset), |
| register_count); |
| |
| Dispatch(); |
| } |
| |
| } // namespace |
| |
| Handle<Code> GenerateBytecodeHandler(Isolate* isolate, Bytecode bytecode, |
| OperandScale operand_scale) { |
| Zone zone(isolate->allocator(), ZONE_NAME); |
| InterpreterDispatchDescriptor descriptor(isolate); |
| compiler::CodeAssemblerState state( |
| isolate, &zone, descriptor, Code::ComputeFlags(Code::BYTECODE_HANDLER), |
| Bytecodes::ToString(bytecode), Bytecodes::ReturnCount(bytecode)); |
| |
| switch (bytecode) { |
| #define CALL_GENERATOR(Name, ...) \ |
| case Bytecode::k##Name: \ |
| Name##Assembler::Generate(&state, operand_scale); \ |
| break; |
| BYTECODE_LIST(CALL_GENERATOR); |
| #undef CALL_GENERATOR |
| } |
| |
| Handle<Code> code = compiler::CodeAssembler::GenerateCode(&state); |
| PROFILE(isolate, CodeCreateEvent( |
| CodeEventListener::BYTECODE_HANDLER_TAG, |
| AbstractCode::cast(*code), |
| Bytecodes::ToString(bytecode, operand_scale).c_str())); |
| #ifdef ENABLE_DISASSEMBLER |
| if (FLAG_trace_ignition_codegen) { |
| OFStream os(stdout); |
| code->Disassemble(Bytecodes::ToString(bytecode), os); |
| os << std::flush; |
| } |
| #endif // ENABLE_DISASSEMBLER |
| return code; |
| } |
| |
| } // namespace interpreter |
| } // namespace internal |
| } // namespace v8 |