| // 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); |
| } |
| |
| #define DEFINE_BYTECODE_NODE_CREATOR(Name, ...) \ |
| template <typename... Operands> \ |
| INLINE(static BytecodeNode Name(BytecodeSourceInfo source_info, \ |
| Operands... operands)) { \ |
| return Create<Bytecode::k##Name, __VA_ARGS__>(source_info, operands...); \ |
| } |
| BYTECODE_LIST(DEFINE_BYTECODE_NODE_CREATOR) |
| #undef DEFINE_BYTECODE_NODE_CREATOR |
| |
| // 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 update_operand0(uint32_t operand0) { SetOperand(0, operand0); } |
| |
| // 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: |
| template <Bytecode bytecode, AccumulatorUse accumulator_use, |
| OperandType... operand_types> |
| friend class BytecodeNodeBuilder; |
| |
| INLINE(BytecodeNode(Bytecode bytecode, int operand_count, |
| OperandScale operand_scale, |
| BytecodeSourceInfo source_info, uint32_t operand0 = 0, |
| uint32_t operand1 = 0, uint32_t operand2 = 0, |
| uint32_t operand3 = 0)) |
| : bytecode_(bytecode), |
| operand_count_(operand_count), |
| operand_scale_(operand_scale), |
| source_info_(source_info) { |
| DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count); |
| operands_[0] = operand0; |
| operands_[1] = operand1; |
| operands_[2] = operand2; |
| operands_[3] = operand3; |
| } |
| |
| template <Bytecode bytecode, AccumulatorUse accum_use> |
| INLINE(static BytecodeNode Create(BytecodeSourceInfo source_info)) { |
| return BytecodeNode(bytecode, 0, OperandScale::kSingle, source_info); |
| } |
| |
| template <Bytecode bytecode, AccumulatorUse accum_use, |
| OperandType operand0_type> |
| INLINE(static BytecodeNode Create(BytecodeSourceInfo source_info, |
| uint32_t operand0)) { |
| DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 0), operand0_type); |
| OperandScale scale = OperandScale::kSingle; |
| scale = std::max(scale, ScaleForOperand<operand0_type>(operand0)); |
| return BytecodeNode(bytecode, 1, scale, source_info, operand0); |
| } |
| |
| template <Bytecode bytecode, AccumulatorUse accum_use, |
| OperandType operand0_type, OperandType operand1_type> |
| INLINE(static BytecodeNode Create(BytecodeSourceInfo source_info, |
| uint32_t operand0, uint32_t operand1)) { |
| DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 0), operand0_type); |
| DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 1), operand1_type); |
| OperandScale scale = OperandScale::kSingle; |
| scale = std::max(scale, ScaleForOperand<operand0_type>(operand0)); |
| scale = std::max(scale, ScaleForOperand<operand1_type>(operand1)); |
| return BytecodeNode(bytecode, 2, scale, source_info, operand0, operand1); |
| } |
| |
| template <Bytecode bytecode, AccumulatorUse accum_use, |
| OperandType operand0_type, OperandType operand1_type, |
| OperandType operand2_type> |
| INLINE(static BytecodeNode Create(BytecodeSourceInfo source_info, |
| uint32_t operand0, uint32_t operand1, |
| uint32_t operand2)) { |
| DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 0), operand0_type); |
| DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 1), operand1_type); |
| DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 2), operand2_type); |
| OperandScale scale = OperandScale::kSingle; |
| scale = std::max(scale, ScaleForOperand<operand0_type>(operand0)); |
| scale = std::max(scale, ScaleForOperand<operand1_type>(operand1)); |
| scale = std::max(scale, ScaleForOperand<operand2_type>(operand2)); |
| return BytecodeNode(bytecode, 3, scale, source_info, operand0, operand1, |
| operand2); |
| } |
| |
| template <Bytecode bytecode, AccumulatorUse accum_use, |
| OperandType operand0_type, OperandType operand1_type, |
| OperandType operand2_type, OperandType operand3_type> |
| INLINE(static BytecodeNode Create(BytecodeSourceInfo source_info, |
| uint32_t operand0, uint32_t operand1, |
| uint32_t operand2, uint32_t operand3)) { |
| DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 0), operand0_type); |
| DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 1), operand1_type); |
| DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 2), operand2_type); |
| DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 3), operand3_type); |
| OperandScale scale = OperandScale::kSingle; |
| scale = std::max(scale, ScaleForOperand<operand0_type>(operand0)); |
| scale = std::max(scale, ScaleForOperand<operand1_type>(operand1)); |
| scale = std::max(scale, ScaleForOperand<operand2_type>(operand2)); |
| scale = std::max(scale, ScaleForOperand<operand3_type>(operand3)); |
| return BytecodeNode(bytecode, 4, scale, source_info, operand0, operand1, |
| operand2, operand3); |
| } |
| |
| template <OperandType operand_type> |
| INLINE(static OperandScale ScaleForOperand(uint32_t operand)) { |
| if (BytecodeOperands::IsScalableUnsignedByte(operand_type)) { |
| return Bytecodes::ScaleForUnsignedOperand(operand); |
| } else if (BytecodeOperands::IsScalableSignedByte(operand_type)) { |
| return Bytecodes::ScaleForSignedOperand(operand); |
| } else { |
| return OperandScale::kSingle; |
| } |
| } |
| |
| 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_ |