// Copyright 2018 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_TORQUE_CFG_H_ #define V8_TORQUE_CFG_H_ #include #include #include #include #include "src/torque/ast.h" #include "src/torque/instructions.h" #include "src/torque/source-positions.h" #include "src/torque/types.h" namespace v8 { namespace internal { namespace torque { class Block { public: explicit Block(size_t id, base::Optional> input_types, bool is_deferred) : input_types_(std::move(input_types)), id_(id), is_deferred_(is_deferred) {} void Add(Instruction instruction) { DCHECK(!IsComplete()); instructions_.push_back(std::move(instruction)); } bool HasInputTypes() const { return input_types_ != base::nullopt; } const Stack& InputTypes() const { return *input_types_; } void SetInputTypes(const Stack& input_types); const std::vector& instructions() const { return instructions_; } bool IsComplete() const { return !instructions_.empty() && instructions_.back()->IsBlockTerminator(); } size_t id() const { return id_; } bool IsDeferred() const { return is_deferred_; } private: std::vector instructions_; base::Optional> input_types_; const size_t id_; bool is_deferred_; }; class ControlFlowGraph { public: explicit ControlFlowGraph(Stack input_types) { start_ = NewBlock(std::move(input_types), false); PlaceBlock(start_); } Block* NewBlock(base::Optional> input_types, bool is_deferred) { blocks_.emplace_back(next_block_id_++, std::move(input_types), is_deferred); return &blocks_.back(); } void PlaceBlock(Block* block) { placed_blocks_.push_back(block); } Block* start() const { return start_; } base::Optional end() const { return end_; } void set_end(Block* end) { end_ = end; } void SetReturnType(const Type* t) { if (!return_type_) { return_type_ = t; return; } if (t != *return_type_) { ReportError("expected return type ", **return_type_, " instead of ", *t); } } const std::vector& blocks() const { return placed_blocks_; } private: std::list blocks_; Block* start_; std::vector placed_blocks_; base::Optional end_; base::Optional return_type_; size_t next_block_id_ = 0; }; class CfgAssembler { public: explicit CfgAssembler(Stack input_types) : current_stack_(std::move(input_types)), cfg_(current_stack_) {} const ControlFlowGraph& Result() { if (!CurrentBlockIsComplete()) { cfg_.set_end(current_block_); } return cfg_; } Block* NewBlock( base::Optional> input_types = base::nullopt, bool is_deferred = false) { return cfg_.NewBlock(std::move(input_types), is_deferred); } bool CurrentBlockIsComplete() const { return current_block_->IsComplete(); } void Emit(Instruction instruction) { instruction.TypeInstruction(¤t_stack_, &cfg_); current_block_->Add(std::move(instruction)); } const Stack& CurrentStack() const { return current_stack_; } StackRange TopRange(size_t slot_count) const { return CurrentStack().TopRange(slot_count); } void Bind(Block* block); void Goto(Block* block); // Goto block while keeping {preserved_slots} many slots on the top and // deleting additional the slots below these to match the input type of the // target block. // Returns the StackRange of the preserved slots in the target block. StackRange Goto(Block* block, size_t preserved_slots); // The condition must be of type bool and on the top of stack. It is removed // from the stack before branching. void Branch(Block* if_true, Block* if_false); // Delete the specified range of slots, moving upper slots to fill the gap. void DeleteRange(StackRange range); void DropTo(BottomOffset new_level); StackRange Peek(StackRange range, base::Optional type); void Poke(StackRange destination, StackRange origin, base::Optional type); void Print(std::string s); void Unreachable(); void DebugBreak(); private: Stack current_stack_; ControlFlowGraph cfg_; Block* current_block_ = cfg_.start(); }; } // namespace torque } // namespace internal } // namespace v8 #endif // V8_TORQUE_CFG_H_