// 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_PIPELINE_H_ #define V8_INTERPRETER_BYTECODE_PIPELINE_H_ #include "src/base/compiler-specific.h" #include "src/globals.h" #include "src/interpreter/bytecode-register-allocator.h" #include "src/interpreter/bytecode-register.h" #include "src/interpreter/bytecodes.h" #include "src/objects.h" #include "src/zone/zone-containers.h" namespace v8 { namespace internal { namespace interpreter { class BytecodeLabel; class BytecodeNode; class BytecodeSourceInfo; // Interface for bytecode pipeline stages. class BytecodePipelineStage { public: virtual ~BytecodePipelineStage() {} // Write bytecode node |node| into pipeline. The node is only valid // for the duration of the call. Callee's should clone it if // deferring Write() to the next stage. virtual void Write(BytecodeNode* node) = 0; // Write jump bytecode node |node| which jumps to |label| into pipeline. // The node and label are only valid for the duration of the call. This call // implicitly ends the current basic block so should always write to the next // stage. virtual void WriteJump(BytecodeNode* node, BytecodeLabel* label) = 0; // Binds |label| to the current bytecode location. This call implicitly // ends the current basic block and so any deferred bytecodes should be // written to the next stage. virtual void BindLabel(BytecodeLabel* label) = 0; // Binds |label| to the location of |target|. This call implicitly // ends the current basic block and so any deferred bytecodes should be // written to the next stage. virtual void BindLabel(const BytecodeLabel& target, BytecodeLabel* label) = 0; // Flush the pipeline and generate a bytecode array. virtual Handle<BytecodeArray> ToBytecodeArray( Isolate* isolate, int register_count, int parameter_count, Handle<FixedArray> handler_table) = 0; }; // Source code position information. class BytecodeSourceInfo final { public: static const int kUninitializedPosition = -1; BytecodeSourceInfo() : position_type_(PositionType::kNone), source_position_(kUninitializedPosition) {} BytecodeSourceInfo(int source_position, bool is_statement) : position_type_(is_statement ? PositionType::kStatement : PositionType::kExpression), source_position_(source_position) { DCHECK_GE(source_position, 0); } // Makes instance into a statement position. void MakeStatementPosition(int source_position) { // Statement positions can be replaced by other statement // positions. For example , "for (x = 0; x < 3; ++x) 7;" has a // statement position associated with 7 but no bytecode associated // with it. Then Next is emitted after the body and has // statement position and overrides the existing one. position_type_ = PositionType::kStatement; source_position_ = source_position; } // Makes instance into an expression position. Instance should not // be a statement position otherwise it could be lost and impair the // debugging experience. void MakeExpressionPosition(int source_position) { DCHECK(!is_statement()); position_type_ = PositionType::kExpression; source_position_ = source_position; } // Forces an instance into an expression position. void ForceExpressionPosition(int source_position) { position_type_ = PositionType::kExpression; source_position_ = source_position; } int source_position() const { DCHECK(is_valid()); return source_position_; } bool is_statement() const { return position_type_ == PositionType::kStatement; } bool is_expression() const { return position_type_ == PositionType::kExpression; } bool is_valid() const { return position_type_ != PositionType::kNone; } void set_invalid() { position_type_ = PositionType::kNone; source_position_ = kUninitializedPosition; } bool operator==(const BytecodeSourceInfo& other) const { return position_type_ == other.position_type_ && source_position_ == other.source_position_; } bool operator!=(const BytecodeSourceInfo& other) const { return position_type_ != other.position_type_ || source_position_ != other.source_position_; } private: enum class PositionType : uint8_t { kNone, kExpression, kStatement }; PositionType position_type_; int source_position_; }; // A container for a generated bytecode, it's operands, and source information. // These must be allocated by a BytecodeNodeAllocator instance. class V8_EXPORT_PRIVATE BytecodeNode final : NON_EXPORTED_BASE(ZoneObject) { public: INLINE(BytecodeNode(Bytecode bytecode, BytecodeSourceInfo source_info = BytecodeSourceInfo())) : bytecode_(bytecode), operand_count_(0), operand_scale_(OperandScale::kSingle), source_info_(source_info) { DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count()); } INLINE(BytecodeNode(Bytecode bytecode, uint32_t operand0, BytecodeSourceInfo source_info = BytecodeSourceInfo())) : bytecode_(bytecode), operand_count_(1), operand_scale_(OperandScale::kSingle), source_info_(source_info) { DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count()); SetOperand(0, operand0); } INLINE(BytecodeNode(Bytecode bytecode, uint32_t operand0, uint32_t operand1, BytecodeSourceInfo source_info = BytecodeSourceInfo())) : bytecode_(bytecode), operand_count_(2), operand_scale_(OperandScale::kSingle), source_info_(source_info) { DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count()); SetOperand(0, operand0); SetOperand(1, operand1); } INLINE(BytecodeNode(Bytecode bytecode, uint32_t operand0, uint32_t operand1, uint32_t operand2, BytecodeSourceInfo source_info = BytecodeSourceInfo())) : bytecode_(bytecode), operand_count_(3), operand_scale_(OperandScale::kSingle), source_info_(source_info) { DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count()); SetOperand(0, operand0); SetOperand(1, operand1); SetOperand(2, operand2); } INLINE(BytecodeNode(Bytecode bytecode, uint32_t operand0, uint32_t operand1, uint32_t operand2, uint32_t operand3, BytecodeSourceInfo source_info = BytecodeSourceInfo())) : bytecode_(bytecode), operand_count_(4), operand_scale_(OperandScale::kSingle), source_info_(source_info) { DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count()); SetOperand(0, operand0); SetOperand(1, operand1); SetOperand(2, operand2); SetOperand(3, operand3); } // Replace the bytecode of this node with |bytecode| and keep the operands. void replace_bytecode(Bytecode bytecode) { DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode_), Bytecodes::NumberOfOperands(bytecode)); bytecode_ = bytecode; } void set_bytecode(Bytecode bytecode) { DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), 0); bytecode_ = bytecode; operand_count_ = 0; operand_scale_ = OperandScale::kSingle; } void set_bytecode(Bytecode bytecode, uint32_t operand0) { DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), 1); bytecode_ = bytecode; operand_count_ = 1; operand_scale_ = OperandScale::kSingle; SetOperand(0, operand0); } void set_bytecode(Bytecode bytecode, uint32_t operand0, uint32_t operand1) { DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), 2); bytecode_ = bytecode; operand_count_ = 2; operand_scale_ = OperandScale::kSingle; SetOperand(0, operand0); SetOperand(1, operand1); } void set_bytecode(Bytecode bytecode, uint32_t operand0, uint32_t operand1, uint32_t operand2) { DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), 3); bytecode_ = bytecode; operand_count_ = 3; operand_scale_ = OperandScale::kSingle; SetOperand(0, operand0); SetOperand(1, operand1); SetOperand(2, operand2); } // Print to stream |os|. void Print(std::ostream& os) const; // Transform to a node representing |new_bytecode| which has one // operand more than the current bytecode. void Transform(Bytecode new_bytecode, uint32_t extra_operand) { DCHECK_EQ(Bytecodes::NumberOfOperands(new_bytecode), Bytecodes::NumberOfOperands(bytecode()) + 1); DCHECK(Bytecodes::NumberOfOperands(bytecode()) < 1 || Bytecodes::GetOperandType(new_bytecode, 0) == Bytecodes::GetOperandType(bytecode(), 0)); DCHECK(Bytecodes::NumberOfOperands(bytecode()) < 2 || Bytecodes::GetOperandType(new_bytecode, 1) == Bytecodes::GetOperandType(bytecode(), 1)); DCHECK(Bytecodes::NumberOfOperands(bytecode()) < 3 || Bytecodes::GetOperandType(new_bytecode, 2) == Bytecodes::GetOperandType(bytecode(), 2)); DCHECK(Bytecodes::NumberOfOperands(bytecode()) < 4); bytecode_ = new_bytecode; operand_count_++; SetOperand(operand_count() - 1, extra_operand); } Bytecode bytecode() const { return bytecode_; } uint32_t operand(int i) const { DCHECK_LT(i, operand_count()); return operands_[i]; } const uint32_t* operands() const { return operands_; } int operand_count() const { return operand_count_; } OperandScale operand_scale() const { return operand_scale_; } const BytecodeSourceInfo& source_info() const { return source_info_; } void set_source_info(BytecodeSourceInfo source_info) { source_info_ = source_info; } bool operator==(const BytecodeNode& other) const; bool operator!=(const BytecodeNode& other) const { return !(*this == other); } private: INLINE(void UpdateScaleForOperand(int operand_index, uint32_t operand)) { if (Bytecodes::OperandIsScalableSignedByte(bytecode(), operand_index)) { operand_scale_ = std::max(operand_scale_, Bytecodes::ScaleForSignedOperand(operand)); } else if (Bytecodes::OperandIsScalableUnsignedByte(bytecode(), operand_index)) { operand_scale_ = std::max(operand_scale_, Bytecodes::ScaleForUnsignedOperand(operand)); } } INLINE(void SetOperand(int operand_index, uint32_t operand)) { operands_[operand_index] = operand; UpdateScaleForOperand(operand_index, operand); } Bytecode bytecode_; uint32_t operands_[Bytecodes::kMaxOperands]; int operand_count_; OperandScale operand_scale_; BytecodeSourceInfo source_info_; }; V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, const BytecodeSourceInfo& info); V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, const BytecodeNode& node); } // namespace interpreter } // namespace internal } // namespace v8 #endif // V8_INTERPRETER_BYTECODE_PIPELINE_H_