blob: 4cacb58edb3a649219c7032ff08b0624e3ef6aa3 [file] [log] [blame]
// 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 <list>
#include <memory>
#include <unordered_map>
#include <vector>
#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 ControlFlowGraph;
class Block {
public:
explicit Block(ControlFlowGraph* cfg, size_t id,
base::Optional<Stack<const Type*>> input_types,
bool is_deferred)
: cfg_(cfg),
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<const Type*>& InputTypes() const { return *input_types_; }
void SetInputTypes(const Stack<const Type*>& input_types);
void Retype() {
Stack<const Type*> current_stack = InputTypes();
for (const Instruction& instruction : instructions()) {
instruction.TypeInstruction(&current_stack, cfg_);
}
}
std::vector<Instruction>& instructions() { return instructions_; }
const std::vector<Instruction>& 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_; }
void MergeInputDefinitions(const Stack<DefinitionLocation>& input_definitions,
Worklist<Block*>* worklist) {
if (!input_definitions_) {
input_definitions_ = input_definitions;
if (worklist) worklist->Enqueue(this);
return;
}
DCHECK_EQ(input_definitions_->Size(), input_definitions.Size());
bool changed = false;
for (BottomOffset i = {0}; i < input_definitions.AboveTop(); ++i) {
auto& current = input_definitions_->Peek(i);
auto& input = input_definitions.Peek(i);
if (current == input) continue;
if (current == DefinitionLocation::Phi(this, i.offset)) continue;
input_definitions_->Poke(i, DefinitionLocation::Phi(this, i.offset));
changed = true;
}
if (changed && worklist) worklist->Enqueue(this);
}
bool HasInputDefinitions() const {
return input_definitions_ != base::nullopt;
}
const Stack<DefinitionLocation>& InputDefinitions() const {
DCHECK(HasInputDefinitions());
return *input_definitions_;
}
bool IsDead() const { return !HasInputDefinitions(); }
private:
ControlFlowGraph* cfg_;
std::vector<Instruction> instructions_;
base::Optional<Stack<const Type*>> input_types_;
base::Optional<Stack<DefinitionLocation>> input_definitions_;
const size_t id_;
bool is_deferred_;
};
class ControlFlowGraph {
public:
explicit ControlFlowGraph(Stack<const Type*> input_types) {
start_ = NewBlock(std::move(input_types), false);
PlaceBlock(start_);
}
Block* NewBlock(base::Optional<Stack<const Type*>> input_types,
bool is_deferred) {
blocks_.emplace_back(this, next_block_id_++, std::move(input_types),
is_deferred);
return &blocks_.back();
}
void PlaceBlock(Block* block) { placed_blocks_.push_back(block); }
template <typename UnaryPredicate>
void UnplaceBlockIf(UnaryPredicate&& predicate) {
auto newEnd = std::remove_if(placed_blocks_.begin(), placed_blocks_.end(),
std::forward<UnaryPredicate>(predicate));
placed_blocks_.erase(newEnd, placed_blocks_.end());
}
Block* start() const { return start_; }
base::Optional<Block*> 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<Block*>& blocks() const { return placed_blocks_; }
size_t NumberOfBlockIds() const { return next_block_id_; }
std::size_t ParameterCount() const {
return start_ ? start_->InputTypes().Size() : 0;
}
private:
std::list<Block> blocks_;
Block* start_;
std::vector<Block*> placed_blocks_;
base::Optional<Block*> end_;
base::Optional<const Type*> return_type_;
size_t next_block_id_ = 0;
};
class CfgAssembler {
public:
explicit CfgAssembler(Stack<const Type*> input_types)
: current_stack_(std::move(input_types)), cfg_(current_stack_) {}
const ControlFlowGraph& Result() {
if (!CurrentBlockIsComplete()) {
cfg_.set_end(current_block_);
}
OptimizeCfg();
DCHECK(CfgIsComplete());
ComputeInputDefinitions();
return cfg_;
}
Block* NewBlock(
base::Optional<Stack<const Type*>> input_types = base::nullopt,
bool is_deferred = false) {
return cfg_.NewBlock(std::move(input_types), is_deferred);
}
bool CurrentBlockIsComplete() const { return current_block_->IsComplete(); }
bool CfgIsComplete() const {
return std::all_of(
cfg_.blocks().begin(), cfg_.blocks().end(), [this](Block* block) {
return (cfg_.end() && *cfg_.end() == block) || block->IsComplete();
});
}
void Emit(Instruction instruction) {
instruction.TypeInstruction(&current_stack_, &cfg_);
current_block_->Add(std::move(instruction));
}
const Stack<const Type*>& 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<const Type*> type);
void Poke(StackRange destination, StackRange origin,
base::Optional<const Type*> type);
void Print(std::string s);
void AssertionFailure(std::string message);
void Unreachable();
void DebugBreak();
void PrintCurrentStack(std::ostream& s) { s << "stack: " << current_stack_; }
void OptimizeCfg();
void ComputeInputDefinitions();
private:
friend class CfgAssemblerScopedTemporaryBlock;
Stack<const Type*> current_stack_;
ControlFlowGraph cfg_;
Block* current_block_ = cfg_.start();
};
class CfgAssemblerScopedTemporaryBlock {
public:
CfgAssemblerScopedTemporaryBlock(CfgAssembler* assembler, Block* block)
: assembler_(assembler), saved_block_(block) {
saved_stack_ = block->InputTypes();
DCHECK(!assembler->CurrentBlockIsComplete());
std::swap(saved_block_, assembler->current_block_);
std::swap(saved_stack_, assembler->current_stack_);
assembler->cfg_.PlaceBlock(block);
}
~CfgAssemblerScopedTemporaryBlock() {
DCHECK(assembler_->CurrentBlockIsComplete());
std::swap(saved_block_, assembler_->current_block_);
std::swap(saved_stack_, assembler_->current_stack_);
}
private:
CfgAssembler* assembler_;
Stack<const Type*> saved_stack_;
Block* saved_block_;
};
} // namespace torque
} // namespace internal
} // namespace v8
#endif // V8_TORQUE_CFG_H_