| // Copyright 2015 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_ |
| #define V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_ |
| |
| #include "src/ast/ast.h" |
| #include "src/interpreter/bytecodes.h" |
| #include "src/interpreter/constant-array-builder.h" |
| #include "src/zone-containers.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| class Isolate; |
| |
| namespace interpreter { |
| |
| class BytecodeLabel; |
| class ConstantArrayBuilder; |
| class Register; |
| |
| // TODO(rmcilroy): Unify this with CreateArgumentsParameters::Type in Turbofan |
| // when rest parameters implementation has settled down. |
| enum class CreateArgumentsType { kMappedArguments, kUnmappedArguments }; |
| |
| class BytecodeArrayBuilder final { |
| public: |
| BytecodeArrayBuilder(Isolate* isolate, Zone* zone); |
| ~BytecodeArrayBuilder(); |
| |
| Handle<BytecodeArray> ToBytecodeArray(); |
| |
| // Set the number of parameters expected by function. |
| void set_parameter_count(int number_of_params); |
| int parameter_count() const { |
| DCHECK_GE(parameter_count_, 0); |
| return parameter_count_; |
| } |
| |
| // Set the number of locals required for bytecode array. |
| void set_locals_count(int number_of_locals); |
| int locals_count() const { |
| DCHECK_GE(local_register_count_, 0); |
| return local_register_count_; |
| } |
| |
| // Set number of contexts required for bytecode array. |
| void set_context_count(int number_of_contexts); |
| int context_count() const { |
| DCHECK_GE(context_register_count_, 0); |
| return context_register_count_; |
| } |
| |
| Register first_context_register() const; |
| Register last_context_register() const; |
| |
| // Returns the number of fixed (non-temporary) registers. |
| int fixed_register_count() const { return context_count() + locals_count(); } |
| |
| Register Parameter(int parameter_index) const; |
| |
| // Return true if the register |reg| represents a parameter or a |
| // local. |
| bool RegisterIsParameterOrLocal(Register reg) const; |
| |
| // Return true if the register |reg| represents a temporary register. |
| bool RegisterIsTemporary(Register reg) const; |
| |
| // Constant loads to accumulator. |
| BytecodeArrayBuilder& LoadLiteral(v8::internal::Smi* value); |
| BytecodeArrayBuilder& LoadLiteral(Handle<Object> object); |
| BytecodeArrayBuilder& LoadUndefined(); |
| BytecodeArrayBuilder& LoadNull(); |
| BytecodeArrayBuilder& LoadTheHole(); |
| BytecodeArrayBuilder& LoadTrue(); |
| BytecodeArrayBuilder& LoadFalse(); |
| BytecodeArrayBuilder& LoadBooleanConstant(bool value); |
| |
| // Global loads to the accumulator and stores from the accumulator. |
| BytecodeArrayBuilder& LoadGlobal(const Handle<String> name, int feedback_slot, |
| LanguageMode language_mode, |
| TypeofMode typeof_mode); |
| BytecodeArrayBuilder& StoreGlobal(const Handle<String> name, |
| int feedback_slot, |
| LanguageMode language_mode); |
| |
| // Load the object at |slot_index| in |context| into the accumulator. |
| BytecodeArrayBuilder& LoadContextSlot(Register context, int slot_index); |
| |
| // Stores the object in the accumulator into |slot_index| of |context|. |
| BytecodeArrayBuilder& StoreContextSlot(Register context, int slot_index); |
| |
| // Register-accumulator transfers. |
| BytecodeArrayBuilder& LoadAccumulatorWithRegister(Register reg); |
| BytecodeArrayBuilder& StoreAccumulatorInRegister(Register reg); |
| |
| // Register-register transfer. |
| BytecodeArrayBuilder& MoveRegister(Register from, Register to); |
| BytecodeArrayBuilder& ExchangeRegisters(Register reg0, Register reg1); |
| |
| // Named load property. |
| BytecodeArrayBuilder& LoadNamedProperty(Register object, |
| const Handle<String> name, |
| int feedback_slot, |
| LanguageMode language_mode); |
| // Keyed load property. The key should be in the accumulator. |
| BytecodeArrayBuilder& LoadKeyedProperty(Register object, int feedback_slot, |
| LanguageMode language_mode); |
| |
| // Store properties. The value to be stored should be in the accumulator. |
| BytecodeArrayBuilder& StoreNamedProperty(Register object, |
| const Handle<String> name, |
| int feedback_slot, |
| LanguageMode language_mode); |
| BytecodeArrayBuilder& StoreKeyedProperty(Register object, Register key, |
| int feedback_slot, |
| LanguageMode language_mode); |
| |
| // Lookup the variable with |name|. |
| BytecodeArrayBuilder& LoadLookupSlot(const Handle<String> name, |
| TypeofMode typeof_mode); |
| |
| // Store value in the accumulator into the variable with |name|. |
| BytecodeArrayBuilder& StoreLookupSlot(const Handle<String> name, |
| LanguageMode language_mode); |
| |
| // Create a new closure for the SharedFunctionInfo. |
| BytecodeArrayBuilder& CreateClosure(Handle<SharedFunctionInfo> shared_info, |
| PretenureFlag tenured); |
| |
| // Create a new arguments object in the accumulator. |
| BytecodeArrayBuilder& CreateArguments(CreateArgumentsType type); |
| |
| // Literals creation. Constant elements should be in the accumulator. |
| BytecodeArrayBuilder& CreateRegExpLiteral(Handle<String> pattern, |
| int literal_index, int flags); |
| BytecodeArrayBuilder& CreateArrayLiteral(Handle<FixedArray> constant_elements, |
| int literal_index, int flags); |
| BytecodeArrayBuilder& CreateObjectLiteral( |
| Handle<FixedArray> constant_properties, int literal_index, int flags); |
| |
| // Push the context in accumulator as the new context, and store in register |
| // |context|. |
| BytecodeArrayBuilder& PushContext(Register context); |
| |
| // Pop the current context and replace with |context|. |
| BytecodeArrayBuilder& PopContext(Register context); |
| |
| // Call a JS function. The JSFunction or Callable to be called should be in |
| // |callable|, the receiver should be in |receiver| and all subsequent |
| // arguments should be in registers <receiver + 1> to |
| // <receiver + 1 + arg_count>. |
| BytecodeArrayBuilder& Call(Register callable, Register receiver, |
| size_t arg_count, int feedback_slot); |
| |
| // Call the new operator. The |constructor| register is followed by |
| // |arg_count| consecutive registers containing arguments to be |
| // applied to the constructor. |
| BytecodeArrayBuilder& New(Register constructor, Register first_arg, |
| size_t arg_count); |
| |
| // Call the runtime function with |function_id|. The first argument should be |
| // in |first_arg| and all subsequent arguments should be in registers |
| // <first_arg + 1> to <first_arg + 1 + arg_count>. |
| BytecodeArrayBuilder& CallRuntime(Runtime::FunctionId function_id, |
| Register first_arg, size_t arg_count); |
| |
| // Call the runtime function with |function_id| that returns a pair of values. |
| // The first argument should be in |first_arg| and all subsequent arguments |
| // should be in registers <first_arg + 1> to <first_arg + 1 + arg_count>. The |
| // return values will be returned in <first_return> and <first_return + 1>. |
| BytecodeArrayBuilder& CallRuntimeForPair(Runtime::FunctionId function_id, |
| Register first_arg, size_t arg_count, |
| Register first_return); |
| |
| // Call the JS runtime function with |context_index|. The the receiver should |
| // be in |receiver| and all subsequent arguments should be in registers |
| // <receiver + 1> to <receiver + 1 + arg_count>. |
| BytecodeArrayBuilder& CallJSRuntime(int context_index, Register receiver, |
| size_t arg_count); |
| |
| // Operators (register holds the lhs value, accumulator holds the rhs value). |
| BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg, |
| Strength strength); |
| |
| // Count Operators (value stored in accumulator). |
| BytecodeArrayBuilder& CountOperation(Token::Value op, Strength strength); |
| |
| // Unary Operators. |
| BytecodeArrayBuilder& LogicalNot(); |
| BytecodeArrayBuilder& TypeOf(); |
| |
| // Deletes property from an object. This expects that accumulator contains |
| // the key to be deleted and the register contains a reference to the object. |
| BytecodeArrayBuilder& Delete(Register object, LanguageMode language_mode); |
| BytecodeArrayBuilder& DeleteLookupSlot(); |
| |
| // Tests. |
| BytecodeArrayBuilder& CompareOperation(Token::Value op, Register reg, |
| Strength strength); |
| |
| // Casts. |
| BytecodeArrayBuilder& CastAccumulatorToBoolean(); |
| BytecodeArrayBuilder& CastAccumulatorToJSObject(); |
| BytecodeArrayBuilder& CastAccumulatorToName(); |
| BytecodeArrayBuilder& CastAccumulatorToNumber(); |
| |
| // Flow Control. |
| BytecodeArrayBuilder& Bind(BytecodeLabel* label); |
| BytecodeArrayBuilder& Bind(const BytecodeLabel& target, BytecodeLabel* label); |
| |
| BytecodeArrayBuilder& Jump(BytecodeLabel* label); |
| BytecodeArrayBuilder& JumpIfTrue(BytecodeLabel* label); |
| BytecodeArrayBuilder& JumpIfFalse(BytecodeLabel* label); |
| BytecodeArrayBuilder& JumpIfNull(BytecodeLabel* label); |
| BytecodeArrayBuilder& JumpIfUndefined(BytecodeLabel* label); |
| |
| BytecodeArrayBuilder& Throw(); |
| BytecodeArrayBuilder& Return(); |
| |
| // Complex flow control. |
| BytecodeArrayBuilder& ForInPrepare(Register cache_type, Register cache_array, |
| Register cache_length); |
| BytecodeArrayBuilder& ForInDone(Register index, Register cache_length); |
| BytecodeArrayBuilder& ForInNext(Register receiver, Register cache_type, |
| Register cache_array, Register index); |
| BytecodeArrayBuilder& ForInStep(Register index); |
| |
| // Accessors |
| Zone* zone() const { return zone_; } |
| |
| private: |
| ZoneVector<uint8_t>* bytecodes() { return &bytecodes_; } |
| const ZoneVector<uint8_t>* bytecodes() const { return &bytecodes_; } |
| Isolate* isolate() const { return isolate_; } |
| ConstantArrayBuilder* constant_array_builder() { |
| return &constant_array_builder_; |
| } |
| const ConstantArrayBuilder* constant_array_builder() const { |
| return &constant_array_builder_; |
| } |
| |
| static Bytecode BytecodeForBinaryOperation(Token::Value op); |
| static Bytecode BytecodeForCountOperation(Token::Value op); |
| static Bytecode BytecodeForCompareOperation(Token::Value op); |
| static Bytecode BytecodeForWideOperands(Bytecode bytecode); |
| static Bytecode BytecodeForLoadIC(LanguageMode language_mode); |
| static Bytecode BytecodeForKeyedLoadIC(LanguageMode language_mode); |
| static Bytecode BytecodeForStoreIC(LanguageMode language_mode); |
| static Bytecode BytecodeForKeyedStoreIC(LanguageMode language_mode); |
| static Bytecode BytecodeForLoadGlobal(LanguageMode language_mode, |
| TypeofMode typeof_mode); |
| static Bytecode BytecodeForStoreGlobal(LanguageMode language_mode); |
| static Bytecode BytecodeForStoreLookupSlot(LanguageMode language_mode); |
| static Bytecode BytecodeForCreateArguments(CreateArgumentsType type); |
| static Bytecode BytecodeForDelete(LanguageMode language_mode); |
| |
| static bool FitsInIdx8Operand(int value); |
| static bool FitsInIdx8Operand(size_t value); |
| static bool FitsInImm8Operand(int value); |
| static bool FitsInIdx16Operand(int value); |
| static bool FitsInIdx16Operand(size_t value); |
| static bool FitsInReg8Operand(Register value); |
| static bool FitsInReg16Operand(Register value); |
| |
| static Bytecode GetJumpWithConstantOperand(Bytecode jump_smi8_operand); |
| static Bytecode GetJumpWithConstantWideOperand(Bytecode jump_smi8_operand); |
| static Bytecode GetJumpWithToBoolean(Bytecode jump_smi8_operand); |
| |
| Register MapRegister(Register reg); |
| Register MapRegisters(Register reg, Register args_base, int args_length = 1); |
| |
| template <size_t N> |
| INLINE(void Output(Bytecode bytecode, uint32_t(&operands)[N])); |
| void Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1, |
| uint32_t operand2, uint32_t operand3); |
| void Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1, |
| uint32_t operand2); |
| void Output(Bytecode bytecode, uint32_t operand0, uint32_t operand1); |
| void Output(Bytecode bytecode, uint32_t operand0); |
| void Output(Bytecode bytecode); |
| |
| BytecodeArrayBuilder& OutputJump(Bytecode jump_bytecode, |
| BytecodeLabel* label); |
| void PatchJump(const ZoneVector<uint8_t>::iterator& jump_target, |
| const ZoneVector<uint8_t>::iterator& jump_location); |
| void PatchIndirectJumpWith8BitOperand( |
| const ZoneVector<uint8_t>::iterator& jump_location, int delta); |
| void PatchIndirectJumpWith16BitOperand( |
| const ZoneVector<uint8_t>::iterator& jump_location, int delta); |
| |
| void LeaveBasicBlock(); |
| void EnsureReturn(); |
| |
| bool OperandIsValid(Bytecode bytecode, int operand_index, |
| uint32_t operand_value) const; |
| bool LastBytecodeInSameBlock() const; |
| |
| bool NeedToBooleanCast(); |
| bool IsRegisterInAccumulator(Register reg); |
| |
| bool RegisterIsValid(Register reg) const; |
| |
| // Temporary register management. |
| int BorrowTemporaryRegister(); |
| int BorrowTemporaryRegisterNotInRange(int start_index, int end_index); |
| void ReturnTemporaryRegister(int reg_index); |
| int PrepareForConsecutiveTemporaryRegisters(size_t count); |
| void BorrowConsecutiveTemporaryRegister(int reg_index); |
| bool TemporaryRegisterIsLive(Register reg) const; |
| |
| Register first_temporary_register() const; |
| Register last_temporary_register() const; |
| |
| // Gets a constant pool entry for the |object|. |
| size_t GetConstantPoolEntry(Handle<Object> object); |
| |
| Isolate* isolate_; |
| Zone* zone_; |
| ZoneVector<uint8_t> bytecodes_; |
| bool bytecode_generated_; |
| ConstantArrayBuilder constant_array_builder_; |
| size_t last_block_end_; |
| size_t last_bytecode_start_; |
| bool exit_seen_in_block_; |
| int unbound_jumps_; |
| |
| int parameter_count_; |
| int local_register_count_; |
| int context_register_count_; |
| int temporary_register_count_; |
| ZoneSet<int> free_temporaries_; |
| |
| class PreviousBytecodeHelper; |
| friend class BytecodeRegisterAllocator; |
| |
| DISALLOW_COPY_AND_ASSIGN(BytecodeArrayBuilder); |
| }; |
| |
| |
| // A label representing a branch target in a bytecode array. When a |
| // label is bound, it represents a known position in the bytecode |
| // array. For labels that are forward references there can be at most |
| // one reference whilst it is unbound. |
| class BytecodeLabel final { |
| public: |
| BytecodeLabel() : bound_(false), offset_(kInvalidOffset) {} |
| |
| bool is_bound() const { return bound_; } |
| size_t offset() const { return offset_; } |
| |
| private: |
| static const size_t kInvalidOffset = static_cast<size_t>(-1); |
| |
| void bind_to(size_t offset) { |
| DCHECK(!bound_ && offset != kInvalidOffset); |
| offset_ = offset; |
| bound_ = true; |
| } |
| |
| void set_referrer(size_t offset) { |
| DCHECK(!bound_ && offset != kInvalidOffset && offset_ == kInvalidOffset); |
| offset_ = offset; |
| } |
| |
| bool is_forward_target() const { |
| return offset() != kInvalidOffset && !is_bound(); |
| } |
| |
| // There are three states for a label: |
| // bound_ offset_ |
| // UNSET false kInvalidOffset |
| // FORWARD_TARGET false Offset of referring jump |
| // BACKWARD_TARGET true Offset of label in bytecode array when bound |
| bool bound_; |
| size_t offset_; |
| |
| friend class BytecodeArrayBuilder; |
| }; |
| |
| } // namespace interpreter |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_INTERPRETER_BYTECODE_ARRAY_BUILDER_H_ |