44e9622aea
The function literal consists of a list of statements. Each statement is associated with a statement position including break location. The only exception to this rule is when the function immediately throws if scope resolution found an illegal redeclaration. Make sure that we add a break location for this case as well. The debugger relies on this. R=bmeurer@chromium.org, vogelheim@chromium.org BUG=v8:4690 LOG=N Review URL: https://codereview.chromium.org/1759603002 Cr-Commit-Position: refs/heads/master@{#34422}
432 lines
17 KiB
C++
432 lines
17 KiB
C++
// 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/bytecode-register-allocator.h"
|
|
#include "src/interpreter/bytecodes.h"
|
|
#include "src/interpreter/constant-array-builder.h"
|
|
#include "src/interpreter/handler-table-builder.h"
|
|
#include "src/interpreter/register-translator.h"
|
|
#include "src/interpreter/source-position-table.h"
|
|
#include "src/zone-containers.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
class Isolate;
|
|
|
|
namespace interpreter {
|
|
|
|
class BytecodeLabel;
|
|
class Register;
|
|
|
|
class BytecodeArrayBuilder final : public ZoneObject, private RegisterMover {
|
|
public:
|
|
BytecodeArrayBuilder(Isolate* isolate, Zone* zone, int parameter_count,
|
|
int context_count, int locals_count,
|
|
FunctionLiteral* literal = nullptr);
|
|
~BytecodeArrayBuilder();
|
|
|
|
Handle<BytecodeArray> ToBytecodeArray();
|
|
|
|
// Get the number of parameters expected by function.
|
|
int parameter_count() const {
|
|
DCHECK_GE(parameter_count_, 0);
|
|
return parameter_count_;
|
|
}
|
|
|
|
// Get the number of locals required for bytecode array.
|
|
int locals_count() const {
|
|
DCHECK_GE(local_register_count_, 0);
|
|
return local_register_count_;
|
|
}
|
|
|
|
// Get number of contexts required for bytecode array.
|
|
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(); }
|
|
|
|
// Returns the number of fixed and temporary registers.
|
|
int fixed_and_temporary_register_count() const {
|
|
return fixed_register_count() + temporary_register_count();
|
|
}
|
|
|
|
int temporary_register_count() const {
|
|
return temporary_register_allocator()->allocation_count();
|
|
}
|
|
|
|
// Returns the number of registers used for translating wide
|
|
// register operands into byte sized register operands.
|
|
int translation_register_count() const {
|
|
return RegisterTranslator::RegisterCountAdjustment(
|
|
fixed_and_temporary_register_count(), parameter_count());
|
|
}
|
|
|
|
Register Parameter(int parameter_index) const;
|
|
|
|
// Return true if the register |reg| represents a parameter or a
|
|
// local.
|
|
bool RegisterIsParameterOrLocal(Register reg) const;
|
|
|
|
// Returns true if the register |reg| is a live temporary register.
|
|
bool TemporaryRegisterIsLive(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,
|
|
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);
|
|
|
|
// Named load property.
|
|
BytecodeArrayBuilder& LoadNamedProperty(Register object,
|
|
const Handle<Name> name,
|
|
int feedback_slot);
|
|
// Keyed load property. The key should be in the accumulator.
|
|
BytecodeArrayBuilder& LoadKeyedProperty(Register object, int feedback_slot);
|
|
|
|
// Store properties. The value to be stored should be in the accumulator.
|
|
BytecodeArrayBuilder& StoreNamedProperty(Register object,
|
|
const Handle<Name> 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_args| and all subsequent
|
|
// arguments should be in registers <receiver_args + 1> to
|
|
// <receiver_args + receiver_arg_count - 1>.
|
|
BytecodeArrayBuilder& Call(
|
|
Register callable, Register receiver_args, size_t receiver_arg_count,
|
|
int feedback_slot, TailCallMode tail_call_mode = TailCallMode::kDisallow);
|
|
|
|
BytecodeArrayBuilder& TailCall(Register callable, Register receiver_args,
|
|
size_t receiver_arg_count, int feedback_slot) {
|
|
return Call(callable, receiver_args, receiver_arg_count, feedback_slot,
|
|
TailCallMode::kAllow);
|
|
}
|
|
|
|
// Call the new operator. The accumulator holds the |new_target|.
|
|
// The |constructor| is in a register followed by |arg_count|
|
|
// consecutive arguments starting at |first_arg| for the constuctor
|
|
// invocation.
|
|
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 + arg_count - 1>.
|
|
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 + arg_count - 1>. 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_args| and all subsequent arguments should be in registers
|
|
// <receiver + 1> to <receiver + receiver_args_count - 1>.
|
|
BytecodeArrayBuilder& CallJSRuntime(int context_index, Register receiver_args,
|
|
size_t receiver_args_count);
|
|
|
|
// Operators (register holds the lhs value, accumulator holds the rhs value).
|
|
BytecodeArrayBuilder& BinaryOperation(Token::Value binop, Register reg);
|
|
|
|
// Count Operators (value stored in accumulator).
|
|
BytecodeArrayBuilder& CountOperation(Token::Value op);
|
|
|
|
// 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);
|
|
|
|
// Tests.
|
|
BytecodeArrayBuilder& CompareOperation(Token::Value op, Register reg);
|
|
|
|
// 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& JumpIfNotHole(BytecodeLabel* label);
|
|
BytecodeArrayBuilder& JumpIfNull(BytecodeLabel* label);
|
|
BytecodeArrayBuilder& JumpIfUndefined(BytecodeLabel* label);
|
|
|
|
BytecodeArrayBuilder& StackCheck();
|
|
|
|
BytecodeArrayBuilder& Throw();
|
|
BytecodeArrayBuilder& ReThrow();
|
|
BytecodeArrayBuilder& Return();
|
|
|
|
// Debugger.
|
|
BytecodeArrayBuilder& Debugger();
|
|
|
|
// Complex flow control.
|
|
BytecodeArrayBuilder& ForInPrepare(Register cache_info_triple);
|
|
BytecodeArrayBuilder& ForInDone(Register index, Register cache_length);
|
|
BytecodeArrayBuilder& ForInNext(Register receiver, Register index,
|
|
Register cache_type_array_pair,
|
|
int feedback_slot);
|
|
BytecodeArrayBuilder& ForInStep(Register index);
|
|
|
|
// Exception handling.
|
|
BytecodeArrayBuilder& MarkHandler(int handler_id, bool will_catch);
|
|
BytecodeArrayBuilder& MarkTryBegin(int handler_id, Register context);
|
|
BytecodeArrayBuilder& MarkTryEnd(int handler_id);
|
|
|
|
// Creates a new handler table entry and returns a {hander_id} identifying the
|
|
// entry, so that it can be referenced by above exception handling support.
|
|
int NewHandlerEntry() { return handler_table_builder()->NewHandlerEntry(); }
|
|
|
|
void InitializeReturnPosition(FunctionLiteral* literal);
|
|
|
|
void SetStatementPosition(Statement* stmt);
|
|
void SetExpressionPosition(Expression* expr);
|
|
void SetExpressionAsStatementPosition(Expression* expr);
|
|
|
|
// Accessors
|
|
Zone* zone() const { return zone_; }
|
|
TemporaryRegisterAllocator* temporary_register_allocator() {
|
|
return &temporary_allocator_;
|
|
}
|
|
const TemporaryRegisterAllocator* temporary_register_allocator() const {
|
|
return &temporary_allocator_;
|
|
}
|
|
|
|
void EnsureReturn();
|
|
|
|
private:
|
|
class PreviousBytecodeHelper;
|
|
friend class BytecodeRegisterAllocator;
|
|
|
|
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 BytecodeForStoreIC(LanguageMode language_mode);
|
|
static Bytecode BytecodeForKeyedStoreIC(LanguageMode language_mode);
|
|
static Bytecode BytecodeForLoadGlobal(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 Bytecode BytecodeForCall(TailCallMode tail_call_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 FitsInReg8OperandUntranslated(Register value);
|
|
static bool FitsInReg16Operand(Register value);
|
|
static bool FitsInReg16OperandUntranslated(Register value);
|
|
|
|
// RegisterMover interface.
|
|
void MoveRegisterUntranslated(Register from, Register to) override;
|
|
|
|
static Bytecode GetJumpWithConstantOperand(Bytecode jump_smi8_operand);
|
|
static Bytecode GetJumpWithConstantWideOperand(Bytecode jump_smi8_operand);
|
|
static Bytecode GetJumpWithToBoolean(Bytecode jump_smi8_operand);
|
|
|
|
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();
|
|
|
|
bool OperandIsValid(Bytecode bytecode, int operand_index,
|
|
uint32_t operand_value) const;
|
|
bool RegisterIsValid(Register reg, OperandType reg_type) const;
|
|
|
|
bool LastBytecodeInSameBlock() const;
|
|
bool NeedToBooleanCast();
|
|
bool IsRegisterInAccumulator(Register reg);
|
|
|
|
// Set position for return.
|
|
void SetReturnPosition();
|
|
|
|
// Gets a constant pool entry for the |object|.
|
|
size_t GetConstantPoolEntry(Handle<Object> object);
|
|
|
|
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_;
|
|
}
|
|
HandlerTableBuilder* handler_table_builder() {
|
|
return &handler_table_builder_;
|
|
}
|
|
SourcePositionTableBuilder* source_position_table_builder() {
|
|
return &source_position_table_builder_;
|
|
}
|
|
RegisterTranslator* register_translator() { return ®ister_translator_; }
|
|
|
|
Isolate* isolate_;
|
|
Zone* zone_;
|
|
ZoneVector<uint8_t> bytecodes_;
|
|
bool bytecode_generated_;
|
|
ConstantArrayBuilder constant_array_builder_;
|
|
HandlerTableBuilder handler_table_builder_;
|
|
SourcePositionTableBuilder source_position_table_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 return_position_;
|
|
TemporaryRegisterAllocator temporary_allocator_;
|
|
RegisterTranslator register_translator_;
|
|
|
|
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_
|