[torque] add an intermediate representation to Torque
Bug: v8:7793 Change-Id: I5261122faf422987968ee1e405966f878ff910a1 Reviewed-on: https://chromium-review.googlesource.com/c/1245766 Commit-Queue: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Daniel Clifford <danno@chromium.org> Reviewed-by: Jaroslav Sevcik <jarin@chromium.org> Cr-Commit-Position: refs/heads/master@{#56391}
This commit is contained in:
parent
42f17e7d95
commit
a4008bf009
7
BUILD.gn
7
BUILD.gn
@ -2498,6 +2498,7 @@ v8_source_set("v8_base") {
|
||||
"src/strtod.cc",
|
||||
"src/strtod.h",
|
||||
"src/third_party/utf8-decoder/utf8-decoder.h",
|
||||
"src/torque-assembler.h",
|
||||
"src/tracing/trace-event.cc",
|
||||
"src/tracing/trace-event.h",
|
||||
"src/tracing/traced-value.cc",
|
||||
@ -2993,7 +2994,11 @@ v8_source_set("torque_base") {
|
||||
|
||||
sources = [
|
||||
"src/torque/ast.h",
|
||||
"src/torque/cfg.cc",
|
||||
"src/torque/cfg.h",
|
||||
"src/torque/contextual.h",
|
||||
"src/torque/csa-generator.cc",
|
||||
"src/torque/csa-generator.h",
|
||||
"src/torque/declarable.cc",
|
||||
"src/torque/declarable.h",
|
||||
"src/torque/declaration-visitor.cc",
|
||||
@ -3007,6 +3012,8 @@ v8_source_set("torque_base") {
|
||||
"src/torque/global-context.h",
|
||||
"src/torque/implementation-visitor.cc",
|
||||
"src/torque/implementation-visitor.h",
|
||||
"src/torque/instructions.cc",
|
||||
"src/torque/instructions.h",
|
||||
"src/torque/scope.cc",
|
||||
"src/torque/scope.h",
|
||||
"src/torque/source-positions.cc",
|
||||
|
@ -277,6 +277,12 @@ struct Use {
|
||||
(void)unused_tmp_array_for_use_macro; \
|
||||
} while (false)
|
||||
|
||||
// Evaluate the instantiations of an expression with parameter packs.
|
||||
// Since USE has left-to-right evaluation order of it's arguments,
|
||||
// the parameter pack is iterated from left to right and side effects
|
||||
// have defined behavior.
|
||||
#define ITERATE_PACK(...) USE(0, ((__VA_ARGS__), 0)...)
|
||||
|
||||
} // namespace base
|
||||
} // namespace v8
|
||||
|
||||
|
@ -1754,6 +1754,43 @@ void CodeAssemblerLabel::UpdateVariablesAfterBind() {
|
||||
bound_ = true;
|
||||
}
|
||||
|
||||
void CodeAssemblerParameterizedLabelBase::AddInputs(std::vector<Node*> inputs) {
|
||||
if (!phi_nodes_.empty()) {
|
||||
DCHECK_EQ(inputs.size(), phi_nodes_.size());
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
state_->raw_assembler_->AppendPhiInput(phi_nodes_[i], inputs[i]);
|
||||
}
|
||||
} else {
|
||||
DCHECK_EQ(inputs.size(), phi_inputs_.size());
|
||||
for (size_t i = 0; i < inputs.size(); ++i) {
|
||||
phi_inputs_[i].push_back(inputs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Node* CodeAssemblerParameterizedLabelBase::CreatePhi(
|
||||
MachineRepresentation rep, const std::vector<Node*>& inputs) {
|
||||
for (Node* input : inputs) {
|
||||
// We use {nullptr} as a sentinel for an uninitialized value. We must not
|
||||
// create phi nodes for these.
|
||||
if (input == nullptr) return nullptr;
|
||||
}
|
||||
return state_->raw_assembler_->Phi(rep, static_cast<int>(inputs.size()),
|
||||
&inputs.front());
|
||||
}
|
||||
|
||||
const std::vector<Node*>& CodeAssemblerParameterizedLabelBase::CreatePhis(
|
||||
std::vector<MachineRepresentation> representations) {
|
||||
DCHECK(is_used());
|
||||
DCHECK(phi_nodes_.empty());
|
||||
phi_nodes_.reserve(phi_inputs_.size());
|
||||
DCHECK_EQ(representations.size(), phi_inputs_.size());
|
||||
for (size_t i = 0; i < phi_inputs_.size(); ++i) {
|
||||
phi_nodes_.push_back(CreatePhi(representations[i], phi_inputs_[i]));
|
||||
}
|
||||
return phi_nodes_;
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
|
||||
Smi* CheckObjectType(Object* value, Smi* type, String* location) {
|
||||
|
@ -52,6 +52,7 @@ class PromiseFulfillReactionJobTask;
|
||||
class PromiseReaction;
|
||||
class PromiseReactionJobTask;
|
||||
class PromiseRejectReactionJobTask;
|
||||
class TorqueAssembler;
|
||||
class Zone;
|
||||
|
||||
template <typename T>
|
||||
@ -1421,6 +1422,60 @@ class CodeAssemblerLabel {
|
||||
std::map<CodeAssemblerVariable::Impl*, std::vector<Node*>> variable_merges_;
|
||||
};
|
||||
|
||||
class CodeAssemblerParameterizedLabelBase {
|
||||
public:
|
||||
bool is_used() const { return plain_label_.is_used(); }
|
||||
explicit CodeAssemblerParameterizedLabelBase(CodeAssembler* assembler,
|
||||
size_t arity,
|
||||
CodeAssemblerLabel::Type type)
|
||||
: state_(assembler->state()),
|
||||
phi_inputs_(arity),
|
||||
plain_label_(assembler, type) {}
|
||||
|
||||
protected:
|
||||
CodeAssemblerLabel* plain_label() { return &plain_label_; }
|
||||
void AddInputs(std::vector<Node*> inputs);
|
||||
Node* CreatePhi(MachineRepresentation rep, const std::vector<Node*>& inputs);
|
||||
const std::vector<Node*>& CreatePhis(
|
||||
std::vector<MachineRepresentation> representations);
|
||||
|
||||
private:
|
||||
CodeAssemblerState* state_;
|
||||
std::vector<std::vector<Node*>> phi_inputs_;
|
||||
std::vector<Node*> phi_nodes_;
|
||||
CodeAssemblerLabel plain_label_;
|
||||
};
|
||||
|
||||
template <class... Types>
|
||||
class CodeAssemblerParameterizedLabel
|
||||
: public CodeAssemblerParameterizedLabelBase {
|
||||
public:
|
||||
static constexpr size_t kArity = sizeof...(Types);
|
||||
explicit CodeAssemblerParameterizedLabel(CodeAssembler* assembler,
|
||||
CodeAssemblerLabel::Type type)
|
||||
: CodeAssemblerParameterizedLabelBase(assembler, kArity, type) {}
|
||||
|
||||
private:
|
||||
friend class internal::TorqueAssembler;
|
||||
|
||||
void AddInputs(TNode<Types>... inputs) {
|
||||
CodeAssemblerParameterizedLabelBase::AddInputs(
|
||||
std::vector<Node*>{inputs...});
|
||||
}
|
||||
void CreatePhis(TNode<Types>*... results) {
|
||||
const std::vector<Node*>& phi_nodes =
|
||||
CodeAssemblerParameterizedLabelBase::CreatePhis(
|
||||
{MachineRepresentationOf<Types>::value...});
|
||||
auto it = phi_nodes.begin();
|
||||
USE(it);
|
||||
ITERATE_PACK(AssignPhi(results, *(it++)));
|
||||
}
|
||||
template <class T>
|
||||
static void AssignPhi(TNode<T>* result, Node* phi) {
|
||||
if (phi != nullptr) *result = TNode<T>::UncheckedCast(phi);
|
||||
}
|
||||
};
|
||||
|
||||
class V8_EXPORT_PRIVATE CodeAssemblerState {
|
||||
public:
|
||||
// Create with CallStub linkage.
|
||||
@ -1454,6 +1509,7 @@ class V8_EXPORT_PRIVATE CodeAssemblerState {
|
||||
friend class CodeAssemblerLabel;
|
||||
friend class CodeAssemblerVariable;
|
||||
friend class CodeAssemblerTester;
|
||||
friend class CodeAssemblerParameterizedLabelBase;
|
||||
|
||||
CodeAssemblerState(Isolate* isolate, Zone* zone,
|
||||
CallDescriptor* call_descriptor, Code::Kind kind,
|
||||
|
58
src/torque-assembler.h
Normal file
58
src/torque-assembler.h
Normal file
@ -0,0 +1,58 @@
|
||||
// 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_ASSEMBLER_H_
|
||||
#define V8_TORQUE_ASSEMBLER_H_
|
||||
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
#include "src/code-stub-assembler.h"
|
||||
|
||||
#include "src/base/optional.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
class TorqueAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
using CodeStubAssembler::CodeStubAssembler;
|
||||
|
||||
protected:
|
||||
template <class... Ts>
|
||||
using PLabel = compiler::CodeAssemblerParameterizedLabel<Ts...>;
|
||||
|
||||
template <class T>
|
||||
TNode<T> Uninitialized() {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class... T, class... Args>
|
||||
void Goto(PLabel<T...>* label, Args... args) {
|
||||
label->AddInputs(args...);
|
||||
CodeStubAssembler::Goto(label->plain_label());
|
||||
}
|
||||
using CodeStubAssembler::Goto;
|
||||
template <class... T>
|
||||
void Bind(PLabel<T...>* label, TNode<T>*... phis) {
|
||||
Bind(label->plain_label());
|
||||
label->CreatePhis(phis...);
|
||||
}
|
||||
void Bind(Label* label) { CodeAssembler::Bind(label); }
|
||||
using CodeStubAssembler::Bind;
|
||||
template <class... T, class... Args>
|
||||
void Branch(TNode<BoolT> condition, PLabel<T...>* if_true,
|
||||
PLabel<T...>* if_false, Args... args) {
|
||||
if_true->AddInputs(args...);
|
||||
if_false->AddInputs(args...);
|
||||
CodeStubAssembler::Branch(condition, if_true->plain_label(),
|
||||
if_false->plain_label());
|
||||
}
|
||||
using CodeStubAssembler::Branch;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_TORQUE_ASSEMBLER_H_
|
134
src/torque/cfg.cc
Normal file
134
src/torque/cfg.cc
Normal file
@ -0,0 +1,134 @@
|
||||
// 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.
|
||||
|
||||
#include "src/torque/cfg.h"
|
||||
|
||||
#include "src/torque/type-oracle.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace torque {
|
||||
|
||||
void Block::SetInputTypes(const Stack<const Type*>& input_types) {
|
||||
if (!input_types_) {
|
||||
input_types_ = input_types;
|
||||
} else if (*input_types_ != input_types) {
|
||||
std::stringstream error;
|
||||
error << "incompatible types at branch:\n";
|
||||
for (intptr_t i = std::max(input_types_->Size(), input_types.Size()) - 1;
|
||||
i >= 0; --i) {
|
||||
base::Optional<const Type*> left;
|
||||
base::Optional<const Type*> right;
|
||||
if (static_cast<size_t>(i) < input_types.Size()) {
|
||||
left = input_types.Peek(BottomOffset{static_cast<size_t>(i)});
|
||||
}
|
||||
if (static_cast<size_t>(i) < input_types_->Size()) {
|
||||
right = input_types_->Peek(BottomOffset{static_cast<size_t>(i)});
|
||||
}
|
||||
if (left && right && *left == *right) {
|
||||
error << **left << "\n";
|
||||
} else {
|
||||
if (left) {
|
||||
error << **left;
|
||||
} else {
|
||||
error << "/*missing*/";
|
||||
}
|
||||
error << " => ";
|
||||
if (right) {
|
||||
error << **right;
|
||||
} else {
|
||||
error << "/*missing*/";
|
||||
}
|
||||
error << "\n";
|
||||
}
|
||||
}
|
||||
ReportError(error.str());
|
||||
}
|
||||
}
|
||||
|
||||
void CfgAssembler::Bind(Block* block) {
|
||||
DCHECK(current_block_->IsComplete());
|
||||
DCHECK(block->instructions().empty());
|
||||
DCHECK(block->HasInputTypes());
|
||||
current_block_ = block;
|
||||
current_stack_ = block->InputTypes();
|
||||
cfg_.PlaceBlock(block);
|
||||
}
|
||||
|
||||
void CfgAssembler::Goto(Block* block) {
|
||||
if (block->HasInputTypes()) {
|
||||
DropTo(block->InputTypes().AboveTop());
|
||||
}
|
||||
Emit(GotoInstruction{block});
|
||||
}
|
||||
|
||||
StackRange CfgAssembler::Goto(Block* block, size_t preserved_slots) {
|
||||
DCHECK(block->HasInputTypes());
|
||||
DCHECK_GE(CurrentStack().Size(), block->InputTypes().Size());
|
||||
Emit(DeleteRangeInstruction{
|
||||
StackRange{block->InputTypes().AboveTop() - preserved_slots,
|
||||
CurrentStack().AboveTop() - preserved_slots}});
|
||||
StackRange preserved_slot_range = TopRange(preserved_slots);
|
||||
Emit(GotoInstruction{block});
|
||||
return preserved_slot_range;
|
||||
}
|
||||
|
||||
void CfgAssembler::Branch(Block* if_true, Block* if_false) {
|
||||
Emit(BranchInstruction{if_true, if_false});
|
||||
}
|
||||
|
||||
// Delete the specified range of slots, moving upper slots to fill the gap.
|
||||
void CfgAssembler::DeleteRange(StackRange range) {
|
||||
DCHECK_LE(range.end(), current_stack_.AboveTop());
|
||||
if (range.Size() == 0) return;
|
||||
Emit(DeleteRangeInstruction{range});
|
||||
}
|
||||
|
||||
void CfgAssembler::DropTo(BottomOffset new_level) {
|
||||
DeleteRange(StackRange{new_level, CurrentStack().AboveTop()});
|
||||
}
|
||||
|
||||
StackRange CfgAssembler::Peek(StackRange range,
|
||||
base::Optional<const Type*> type) {
|
||||
std::vector<const Type*> lowered_types;
|
||||
if (type) {
|
||||
lowered_types = LowerType(*type);
|
||||
DCHECK_EQ(lowered_types.size(), range.Size());
|
||||
}
|
||||
for (size_t i = 0; i < range.Size(); ++i) {
|
||||
Emit(PeekInstruction{
|
||||
range.begin() + i,
|
||||
type ? lowered_types[i] : base::Optional<const Type*>{}});
|
||||
}
|
||||
return TopRange(range.Size());
|
||||
}
|
||||
|
||||
void CfgAssembler::Poke(StackRange destination, StackRange origin,
|
||||
base::Optional<const Type*> type) {
|
||||
DCHECK_EQ(destination.Size(), origin.Size());
|
||||
DCHECK_LE(destination.end(), origin.begin());
|
||||
DCHECK_EQ(origin.end(), CurrentStack().AboveTop());
|
||||
std::vector<const Type*> lowered_types;
|
||||
if (type) {
|
||||
lowered_types = LowerType(*type);
|
||||
DCHECK_EQ(lowered_types.size(), origin.Size());
|
||||
}
|
||||
for (intptr_t i = origin.Size() - 1; i >= 0; --i) {
|
||||
Emit(PokeInstruction{
|
||||
destination.begin() + i,
|
||||
type ? lowered_types[i] : base::Optional<const Type*>{}});
|
||||
}
|
||||
}
|
||||
|
||||
void CfgAssembler::Print(std::string s) {
|
||||
Emit(PrintConstantStringInstruction{std::move(s)});
|
||||
}
|
||||
|
||||
void CfgAssembler::Unreachable() { Emit(DebugBreakInstruction{true}); }
|
||||
|
||||
void CfgAssembler::DebugBreak() { Emit(DebugBreakInstruction{false}); }
|
||||
|
||||
} // namespace torque
|
||||
} // namespace internal
|
||||
} // namespace v8
|
149
src/torque/cfg.h
Normal file
149
src/torque/cfg.h
Normal file
@ -0,0 +1,149 @@
|
||||
// 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 Block {
|
||||
public:
|
||||
explicit Block(size_t id, base::Optional<Stack<const Type*>> 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<const Type*>& InputTypes() const { return *input_types_; }
|
||||
void SetInputTypes(const Stack<const Type*>& input_types);
|
||||
|
||||
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_; }
|
||||
|
||||
private:
|
||||
std::vector<Instruction> instructions_;
|
||||
base::Optional<Stack<const Type*>> input_types_;
|
||||
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(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<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_; }
|
||||
|
||||
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_);
|
||||
}
|
||||
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(); }
|
||||
|
||||
void Emit(Instruction instruction) {
|
||||
instruction.TypeInstruction(¤t_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 Unreachable();
|
||||
void DebugBreak();
|
||||
|
||||
private:
|
||||
Stack<const Type*> current_stack_;
|
||||
ControlFlowGraph cfg_;
|
||||
Block* current_block_ = cfg_.start();
|
||||
};
|
||||
|
||||
} // namespace torque
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_TORQUE_CFG_H_
|
487
src/torque/csa-generator.cc
Normal file
487
src/torque/csa-generator.cc
Normal file
@ -0,0 +1,487 @@
|
||||
// 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.
|
||||
|
||||
#include "src/torque/csa-generator.h"
|
||||
|
||||
#include "src/torque/type-oracle.h"
|
||||
#include "src/torque/utils.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace torque {
|
||||
|
||||
base::Optional<Stack<std::string>> CSAGenerator::EmitGraph(
|
||||
Stack<std::string> parameters) {
|
||||
for (Block* block : cfg_.blocks()) {
|
||||
out_ << " PLabel<";
|
||||
PrintCommaSeparatedList(out_, block->InputTypes(), [](const Type* t) {
|
||||
return t->GetGeneratedTNodeTypeName();
|
||||
});
|
||||
out_ << "> " << BlockName(block) << "(this, compiler::CodeAssemblerLabel::"
|
||||
<< (block->IsDeferred() ? "kDeferred" : "kNonDeferred") << ");\n";
|
||||
}
|
||||
|
||||
EmitInstruction(GotoInstruction{cfg_.start()}, ¶meters);
|
||||
for (Block* block : cfg_.blocks()) {
|
||||
if (cfg_.end() && *cfg_.end() == block) continue;
|
||||
out_ << "\n if (" << BlockName(block) << ".is_used()) {\n";
|
||||
EmitBlock(block);
|
||||
out_ << " }\n";
|
||||
}
|
||||
if (cfg_.end()) {
|
||||
out_ << "\n";
|
||||
return EmitBlock(*cfg_.end());
|
||||
}
|
||||
return base::nullopt;
|
||||
}
|
||||
|
||||
Stack<std::string> CSAGenerator::EmitBlock(const Block* block) {
|
||||
Stack<std::string> stack;
|
||||
for (const Type* t : block->InputTypes()) {
|
||||
stack.Push(FreshNodeName());
|
||||
out_ << " TNode<" << t->GetGeneratedTNodeTypeName() << "> "
|
||||
<< stack.Top() << ";\n";
|
||||
}
|
||||
out_ << " Bind(&" << BlockName(block);
|
||||
for (const std::string& name : stack) {
|
||||
out_ << ", &" << name;
|
||||
}
|
||||
out_ << ");\n";
|
||||
for (const Instruction& instruction : block->instructions()) {
|
||||
EmitInstruction(instruction, &stack);
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(const Instruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
switch (instruction.kind()) {
|
||||
#define ENUM_ITEM(T) \
|
||||
case InstructionKind::k##T: \
|
||||
return EmitInstruction(instruction.Cast<T>(), stack);
|
||||
TORQUE_INSTRUCTION_LIST(ENUM_ITEM)
|
||||
#undef ENUM_ITEM
|
||||
}
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(const PeekInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
stack->Push(stack->Peek(instruction.slot));
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(const PokeInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
stack->Poke(instruction.slot, stack->Top());
|
||||
stack->Pop();
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(const DeleteRangeInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
stack->DeleteRange(instruction.range);
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(
|
||||
const PushUninitializedInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
// TODO(tebbi): This can trigger an error in CSA if it is used. Instead, we
|
||||
// should prevent usage of uninitialized in the type system. This
|
||||
// requires "if constexpr" being evaluated at Torque time.
|
||||
stack->Push("Uninitialized<" + instruction.type->GetGeneratedTNodeTypeName() +
|
||||
">()");
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(
|
||||
const PushCodePointerInstruction& instruction, Stack<std::string>* stack) {
|
||||
stack->Push(
|
||||
"UncheckedCast<Code>(HeapConstant(Builtins::CallableFor(isolate(), "
|
||||
"Builtins::k" +
|
||||
instruction.external_name + ").code()))");
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(const ModuleConstantInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
const Type* type = instruction.constant->type();
|
||||
std::vector<std::string> results;
|
||||
for (const Type* lowered : LowerType(type)) {
|
||||
results.push_back(FreshNodeName());
|
||||
stack->Push(results.back());
|
||||
out_ << " TNode<" << lowered->GetGeneratedTNodeTypeName() << "> "
|
||||
<< stack->Top() << ";\n";
|
||||
out_ << " USE(" << stack->Top() << ");\n";
|
||||
}
|
||||
out_ << " ";
|
||||
if (type->IsStructType()) {
|
||||
out_ << "std::tie(";
|
||||
PrintCommaSeparatedList(out_, results);
|
||||
out_ << ") = ";
|
||||
} else if (results.size() == 1) {
|
||||
out_ << results[0] << " = ";
|
||||
}
|
||||
out_ << instruction.constant->constant_name() << "()";
|
||||
if (type->IsStructType()) {
|
||||
out_ << ".Flatten();\n";
|
||||
} else {
|
||||
out_ << ";\n";
|
||||
}
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(const CallCsaMacroInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
std::vector<std::string> constexpr_arguments =
|
||||
instruction.constexpr_arguments;
|
||||
std::vector<std::string> args;
|
||||
TypeVector parameter_types =
|
||||
instruction.macro->signature().parameter_types.types;
|
||||
for (auto it = parameter_types.rbegin(); it != parameter_types.rend(); ++it) {
|
||||
const Type* type = *it;
|
||||
VisitResult arg;
|
||||
if (type->IsConstexpr()) {
|
||||
args.push_back(std::move(constexpr_arguments.back()));
|
||||
constexpr_arguments.pop_back();
|
||||
} else {
|
||||
std::stringstream s;
|
||||
size_t slot_count = LoweredSlotCount(type);
|
||||
VisitResult arg = VisitResult(type, stack->TopRange(slot_count));
|
||||
EmitCSAValue(arg, *stack, s);
|
||||
args.push_back(s.str());
|
||||
stack->PopMany(slot_count);
|
||||
}
|
||||
}
|
||||
std::reverse(args.begin(), args.end());
|
||||
|
||||
const Type* return_type = instruction.macro->signature().return_type;
|
||||
std::vector<std::string> results;
|
||||
for (const Type* type : LowerType(return_type)) {
|
||||
results.push_back(FreshNodeName());
|
||||
stack->Push(results.back());
|
||||
out_ << " TNode<" << type->GetGeneratedTNodeTypeName() << "> "
|
||||
<< stack->Top() << ";\n";
|
||||
out_ << " USE(" << stack->Top() << ");\n";
|
||||
}
|
||||
out_ << " ";
|
||||
if (return_type->IsStructType()) {
|
||||
out_ << "std::tie(";
|
||||
PrintCommaSeparatedList(out_, results);
|
||||
out_ << ") = ";
|
||||
} else {
|
||||
if (results.size() == 1) {
|
||||
out_ << results[0] << " = UncheckedCast<"
|
||||
<< return_type->GetGeneratedTNodeTypeName() << ">(";
|
||||
}
|
||||
}
|
||||
out_ << instruction.macro->name() << "(";
|
||||
PrintCommaSeparatedList(out_, args);
|
||||
if (return_type->IsStructType()) {
|
||||
out_ << ").Flatten();\n";
|
||||
} else {
|
||||
if (results.size() == 1) out_ << ")";
|
||||
out_ << ");\n";
|
||||
}
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(
|
||||
const CallCsaMacroAndBranchInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
std::vector<std::string> constexpr_arguments =
|
||||
instruction.constexpr_arguments;
|
||||
std::vector<std::string> args;
|
||||
TypeVector parameter_types =
|
||||
instruction.macro->signature().parameter_types.types;
|
||||
for (auto it = parameter_types.rbegin(); it != parameter_types.rend(); ++it) {
|
||||
const Type* type = *it;
|
||||
VisitResult arg;
|
||||
if (type->IsConstexpr()) {
|
||||
args.push_back(std::move(constexpr_arguments.back()));
|
||||
constexpr_arguments.pop_back();
|
||||
} else {
|
||||
std::stringstream s;
|
||||
size_t slot_count = LoweredSlotCount(type);
|
||||
VisitResult arg = VisitResult(type, stack->TopRange(slot_count));
|
||||
EmitCSAValue(arg, *stack, s);
|
||||
args.push_back(s.str());
|
||||
stack->PopMany(slot_count);
|
||||
}
|
||||
}
|
||||
std::reverse(args.begin(), args.end());
|
||||
|
||||
std::vector<std::string> results;
|
||||
const Type* return_type = instruction.macro->signature().return_type;
|
||||
if (return_type != TypeOracle::GetNeverType()) {
|
||||
for (const Type* type :
|
||||
LowerType(instruction.macro->signature().return_type)) {
|
||||
results.push_back(FreshNodeName());
|
||||
out_ << " TNode<" << type->GetGeneratedTNodeTypeName() << "> "
|
||||
<< results.back() << ";\n";
|
||||
out_ << " USE(" << results.back() << ");\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> label_names;
|
||||
std::vector<std::vector<std::string>> var_names;
|
||||
const LabelDeclarationVector& labels = instruction.macro->signature().labels;
|
||||
DCHECK_EQ(labels.size(), instruction.label_blocks.size());
|
||||
for (size_t i = 0; i < labels.size(); ++i) {
|
||||
TypeVector label_parameters = labels[i].types;
|
||||
label_names.push_back("label" + std::to_string(i));
|
||||
var_names.push_back({});
|
||||
for (size_t j = 0; j < label_parameters.size(); ++j) {
|
||||
var_names[i].push_back("result_" + std::to_string(i) + "_" +
|
||||
std::to_string(j));
|
||||
out_ << " TVariable<"
|
||||
<< label_parameters[j]->GetGeneratedTNodeTypeName() << "> "
|
||||
<< var_names[i][j] << "(this);\n";
|
||||
}
|
||||
out_ << " Label " << label_names[i] << "(this);\n";
|
||||
}
|
||||
|
||||
out_ << " ";
|
||||
if (results.size() == 1) {
|
||||
out_ << results[0] << " = ";
|
||||
} else if (results.size() > 1) {
|
||||
out_ << "std::tie(";
|
||||
PrintCommaSeparatedList(out_, results);
|
||||
out_ << ") = ";
|
||||
}
|
||||
out_ << instruction.macro->name() << "(";
|
||||
PrintCommaSeparatedList(out_, args);
|
||||
bool first = args.empty();
|
||||
for (size_t i = 0; i < label_names.size(); ++i) {
|
||||
if (!first) out_ << ", ";
|
||||
out_ << "&" << label_names[i];
|
||||
first = false;
|
||||
for (size_t j = 0; j < var_names[i].size(); ++j) {
|
||||
out_ << ", &" << var_names[i][j];
|
||||
}
|
||||
}
|
||||
out_ << ");\n";
|
||||
if (instruction.return_continuation) {
|
||||
out_ << " Goto(&" << BlockName(*instruction.return_continuation);
|
||||
for (const std::string& value : *stack) {
|
||||
out_ << ", " << value;
|
||||
}
|
||||
for (const std::string& result : results) {
|
||||
out_ << ", " << result;
|
||||
}
|
||||
out_ << ");\n";
|
||||
}
|
||||
for (size_t i = 0; i < label_names.size(); ++i) {
|
||||
out_ << " if (" << label_names[i] << ".is_used()) {\n";
|
||||
out_ << " Bind(&" << label_names[i] << ");\n";
|
||||
out_ << " Goto(&" << BlockName(instruction.label_blocks[i]);
|
||||
for (const std::string& value : *stack) {
|
||||
out_ << ", " << value;
|
||||
}
|
||||
for (const std::string& var : var_names[i]) {
|
||||
out_ << ", " << var << ".value()";
|
||||
}
|
||||
out_ << ");\n";
|
||||
|
||||
out_ << " }\n";
|
||||
}
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(const CallBuiltinInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
std::vector<std::string> arguments = stack->PopMany(instruction.argc);
|
||||
std::vector<const Type*> result_types =
|
||||
LowerType(instruction.builtin->signature().return_type);
|
||||
if (instruction.is_tailcall) {
|
||||
out_ << " TailCallBuiltin(Builtins::k" << instruction.builtin->name()
|
||||
<< ", ";
|
||||
PrintCommaSeparatedList(out_, arguments);
|
||||
out_ << ");\n";
|
||||
} else {
|
||||
if (result_types.size() == 1) {
|
||||
std::string generated_type = result_types[0]->GetGeneratedTNodeTypeName();
|
||||
stack->Push(FreshNodeName());
|
||||
out_ << " TNode<" << generated_type << "> " << stack->Top() << " = ";
|
||||
if (generated_type != "Object") out_ << "CAST(";
|
||||
out_ << "CallBuiltin(Builtins::k" << instruction.builtin->name() << ", ";
|
||||
PrintCommaSeparatedList(out_, arguments);
|
||||
if (generated_type != "Object") out_ << ")";
|
||||
out_ << ");\n";
|
||||
out_ << " USE(" << stack->Top() << ");\n";
|
||||
} else {
|
||||
DCHECK_EQ(0, result_types.size());
|
||||
// TODO(tebbi): Actually, builtins have to return a value, so we should
|
||||
// not have to handle this case.
|
||||
out_ << " CallBuiltin(Builtins::k" << instruction.builtin->name()
|
||||
<< ", ";
|
||||
PrintCommaSeparatedList(out_, arguments);
|
||||
out_ << ");\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(
|
||||
const CallBuiltinPointerInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
std::vector<std::string> function_and_arguments =
|
||||
stack->PopMany(1 + instruction.argc);
|
||||
std::vector<const Type*> result_types =
|
||||
LowerType(instruction.example_builtin->signature().return_type);
|
||||
if (result_types.size() != 1) {
|
||||
ReportError("builtins must have exactly one result");
|
||||
}
|
||||
if (instruction.is_tailcall) {
|
||||
out_ << " Tail (Builtins::CallableFor(isolate(), Builtins::k"
|
||||
<< instruction.example_builtin->name() << ").descriptor(), ";
|
||||
PrintCommaSeparatedList(out_, function_and_arguments);
|
||||
out_ << ");\n";
|
||||
} else {
|
||||
stack->Push(FreshNodeName());
|
||||
std::string generated_type = result_types[0]->GetGeneratedTNodeTypeName();
|
||||
out_ << " TNode<" << generated_type << "> " << stack->Top() << " = ";
|
||||
if (generated_type != "Object") out_ << "CAST(";
|
||||
out_ << "CallStub(Builtins::CallableFor(isolate(), Builtins::k"
|
||||
<< instruction.example_builtin->name() << ").descriptor(), ";
|
||||
PrintCommaSeparatedList(out_, function_and_arguments);
|
||||
out_ << ")";
|
||||
if (generated_type != "Object") out_ << ")";
|
||||
out_ << "; \n";
|
||||
out_ << " USE(" << stack->Top() << ");\n";
|
||||
}
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(const CallRuntimeInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
std::vector<std::string> arguments = stack->PopMany(instruction.argc);
|
||||
std::vector<const Type*> result_types =
|
||||
LowerType(instruction.runtime_function->signature().return_type);
|
||||
if (result_types.size() > 1) {
|
||||
ReportError("runtime function must have at most one result");
|
||||
}
|
||||
if (instruction.is_tailcall) {
|
||||
out_ << " TailCallRuntime(Runtime::k"
|
||||
<< instruction.runtime_function->name() << ", ";
|
||||
PrintCommaSeparatedList(out_, arguments);
|
||||
out_ << ");\n";
|
||||
} else {
|
||||
if (result_types.size() == 1) {
|
||||
stack->Push(FreshNodeName());
|
||||
out_ << " TNode<" << result_types[0]->GetGeneratedTNodeTypeName()
|
||||
<< "> " << stack->Top() << " = CAST(CallRuntime(Runtime::k"
|
||||
<< instruction.runtime_function->name() << ", ";
|
||||
PrintCommaSeparatedList(out_, arguments);
|
||||
out_ << "));\n";
|
||||
out_ << " USE(" << stack->Top() << ");\n";
|
||||
} else {
|
||||
DCHECK_EQ(0, result_types.size());
|
||||
// TODO(tebbi): Actually, runtime functions have to return a value, so we
|
||||
// should not have to handle this case.
|
||||
out_ << " CallRuntime(Runtime::k"
|
||||
<< instruction.runtime_function->name() << ", ";
|
||||
PrintCommaSeparatedList(out_, arguments);
|
||||
out_ << ");\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(const BranchInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
out_ << " Branch(" << stack->Pop() << ", &"
|
||||
<< BlockName(instruction.if_true) << ", &"
|
||||
<< BlockName(instruction.if_false);
|
||||
for (const std::string& value : *stack) {
|
||||
out_ << ", " << value;
|
||||
}
|
||||
out_ << ");\n";
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(
|
||||
const ConstexprBranchInstruction& instruction, Stack<std::string>* stack) {
|
||||
out_ << " if (" << instruction.condition << ") {\n";
|
||||
out_ << " Goto(&" << BlockName(instruction.if_true);
|
||||
for (const std::string& value : *stack) {
|
||||
out_ << ", " << value;
|
||||
}
|
||||
out_ << ");\n";
|
||||
out_ << " } else {\n";
|
||||
out_ << " Goto(&" << BlockName(instruction.if_false);
|
||||
for (const std::string& value : *stack) {
|
||||
out_ << ", " << value;
|
||||
}
|
||||
out_ << ");\n";
|
||||
|
||||
out_ << " }\n";
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(const GotoInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
out_ << " Goto(&" << BlockName(instruction.destination);
|
||||
for (const std::string& value : *stack) {
|
||||
out_ << ", " << value;
|
||||
}
|
||||
out_ << ");\n";
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(const GotoExternalInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
for (auto it = instruction.variable_names.rbegin();
|
||||
it != instruction.variable_names.rend(); ++it) {
|
||||
out_ << " *" << *it << " = " << stack->Pop() << ";\n";
|
||||
}
|
||||
out_ << " Goto(" << instruction.destination << ");\n";
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(const ReturnInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
if (*linkage_ == Builtin::kVarArgsJavaScript) {
|
||||
out_ << " " << ARGUMENTS_VARIABLE_STRING << "->PopAndReturn(";
|
||||
} else {
|
||||
out_ << " Return(";
|
||||
}
|
||||
out_ << stack->Pop() << ");\n";
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(
|
||||
const PrintConstantStringInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
out_ << " Print(" << StringLiteralQuote(instruction.message) << ");\n";
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(const DebugBreakInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
if (instruction.never_continues) {
|
||||
out_ << " Unreachable();\n";
|
||||
} else {
|
||||
out_ << " DebugBreak();\n";
|
||||
}
|
||||
}
|
||||
|
||||
void CSAGenerator::EmitInstruction(const UnsafeCastInstruction& instruction,
|
||||
Stack<std::string>* stack) {
|
||||
stack->Poke(stack->AboveTop() - 1,
|
||||
"UncheckedCast<" +
|
||||
instruction.destination_type->GetGeneratedTNodeTypeName() +
|
||||
">(" + stack->Top() + ")");
|
||||
}
|
||||
|
||||
// static
|
||||
void CSAGenerator::EmitCSAValue(VisitResult result,
|
||||
const Stack<std::string>& values,
|
||||
std::ostream& out) {
|
||||
if (!result.IsOnStack()) {
|
||||
out << result.constexpr_value();
|
||||
} else if (auto* struct_type = StructType::DynamicCast(result.type())) {
|
||||
out << struct_type->name() << "{";
|
||||
bool first = true;
|
||||
for (auto& field : struct_type->fields()) {
|
||||
if (!first) {
|
||||
out << ", ";
|
||||
}
|
||||
first = false;
|
||||
EmitCSAValue(ProjectStructField(result, field.name), values, out);
|
||||
}
|
||||
out << "}";
|
||||
} else {
|
||||
DCHECK_EQ(1, result.stack_range().Size());
|
||||
out << "TNode<" << result.type()->GetGeneratedTNodeTypeName() << ">{"
|
||||
<< values.Peek(result.stack_range().begin()) << "}";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace torque
|
||||
} // namespace internal
|
||||
} // namespace v8
|
53
src/torque/csa-generator.h
Normal file
53
src/torque/csa-generator.h
Normal file
@ -0,0 +1,53 @@
|
||||
// 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_CSA_GENERATOR_H_
|
||||
#define V8_TORQUE_CSA_GENERATOR_H_
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "src/torque/cfg.h"
|
||||
#include "src/torque/declarable.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace torque {
|
||||
|
||||
class CSAGenerator {
|
||||
public:
|
||||
CSAGenerator(const ControlFlowGraph& cfg, std::ostream& out,
|
||||
base::Optional<Builtin::Kind> linkage = base::nullopt)
|
||||
: cfg_(cfg), out_(out), linkage_(linkage) {}
|
||||
base::Optional<Stack<std::string>> EmitGraph(Stack<std::string> parameters);
|
||||
|
||||
static constexpr const char* ARGUMENTS_VARIABLE_STRING = "arguments";
|
||||
|
||||
static void EmitCSAValue(VisitResult result, const Stack<std::string>& values,
|
||||
std::ostream& out);
|
||||
|
||||
private:
|
||||
const ControlFlowGraph& cfg_;
|
||||
std::ostream& out_;
|
||||
size_t fresh_id_ = 0;
|
||||
base::Optional<Builtin::Kind> linkage_;
|
||||
|
||||
std::string FreshNodeName() { return "tmp" + std::to_string(fresh_id_++); }
|
||||
std::string BlockName(const Block* block) {
|
||||
return "block" + std::to_string(block->id());
|
||||
}
|
||||
|
||||
Stack<std::string> EmitBlock(const Block* block);
|
||||
void EmitInstruction(const Instruction& instruction,
|
||||
Stack<std::string>* stack);
|
||||
#define EMIT_INSTRUCTION_DECLARATION(T) \
|
||||
void EmitInstruction(const T& instruction, Stack<std::string>* stack);
|
||||
TORQUE_INSTRUCTION_LIST(EMIT_INSTRUCTION_DECLARATION)
|
||||
#undef EMIT_INSTRUCTION_DECLARATION
|
||||
};
|
||||
|
||||
} // namespace torque
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_TORQUE_CSA_GENERATOR_H_
|
@ -34,18 +34,6 @@ std::ostream& operator<<(std::ostream& os, const RuntimeFunction& b) {
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string Variable::RValue() const {
|
||||
if (!IsDefined()) {
|
||||
ReportError("Reading uninitialized variable.");
|
||||
}
|
||||
if (type()->IsStructType()) {
|
||||
return value();
|
||||
}
|
||||
std::string result = "(*" + value() + ")";
|
||||
if (!IsConst()) result += ".value()";
|
||||
return result;
|
||||
}
|
||||
|
||||
void PrintLabel(std::ostream& os, const Label& l, bool with_names) {
|
||||
os << l.name();
|
||||
if (l.GetParameterCount() != 0) {
|
||||
|
@ -18,9 +18,10 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace torque {
|
||||
|
||||
class Block;
|
||||
class Generic;
|
||||
class Scope;
|
||||
class ScopeChain;
|
||||
class Generic;
|
||||
|
||||
class Declarable {
|
||||
public:
|
||||
@ -88,13 +89,17 @@ class Declarable {
|
||||
|
||||
class Value : public Declarable {
|
||||
public:
|
||||
DECLARE_DECLARABLE_BOILERPLATE(Value, value);
|
||||
const std::string& name() const { return name_; }
|
||||
virtual bool IsConst() const { return true; }
|
||||
virtual std::string value() const = 0;
|
||||
virtual std::string RValue() const { return value(); }
|
||||
DECLARE_DECLARABLE_BOILERPLATE(Value, value);
|
||||
VisitResult value() const { return *value_; }
|
||||
const Type* type() const { return type_; }
|
||||
|
||||
void set_value(VisitResult value) {
|
||||
DCHECK(!value_);
|
||||
value_ = value;
|
||||
}
|
||||
|
||||
protected:
|
||||
Value(Kind kind, const Type* type, const std::string& name)
|
||||
: Declarable(kind), type_(type), name_(name) {}
|
||||
@ -102,40 +107,44 @@ class Value : public Declarable {
|
||||
private:
|
||||
const Type* type_;
|
||||
std::string name_;
|
||||
base::Optional<VisitResult> value_;
|
||||
};
|
||||
|
||||
class Parameter : public Value {
|
||||
public:
|
||||
DECLARE_DECLARABLE_BOILERPLATE(Parameter, parameter);
|
||||
std::string value() const override { return var_name_; }
|
||||
|
||||
const std::string& external_name() const { return external_name_; }
|
||||
|
||||
private:
|
||||
friend class Declarations;
|
||||
Parameter(const std::string& name, const Type* type,
|
||||
const std::string& var_name)
|
||||
: Value(Declarable::kParameter, type, name), var_name_(var_name) {}
|
||||
Parameter(const std::string& name, std::string external_name,
|
||||
const Type* type)
|
||||
: Value(Declarable::kParameter, type, name),
|
||||
external_name_(external_name) {}
|
||||
|
||||
std::string var_name_;
|
||||
std::string external_name_;
|
||||
};
|
||||
|
||||
class ModuleConstant : public Value {
|
||||
public:
|
||||
DECLARE_DECLARABLE_BOILERPLATE(ModuleConstant, constant);
|
||||
std::string value() const override { UNREACHABLE(); }
|
||||
std::string RValue() const override { return name() + "()"; }
|
||||
|
||||
const std::string& constant_name() const { return constant_name_; }
|
||||
|
||||
private:
|
||||
friend class Declarations;
|
||||
explicit ModuleConstant(const std::string& name, const Type* type)
|
||||
: Value(Declarable::kModuleConstant, type, name) {}
|
||||
explicit ModuleConstant(std::string constant_name, const Type* type)
|
||||
: Value(Declarable::kModuleConstant, type, constant_name),
|
||||
constant_name_(std::move(constant_name)) {}
|
||||
|
||||
std::string constant_name_;
|
||||
};
|
||||
|
||||
class Variable : public Value {
|
||||
public:
|
||||
DECLARE_DECLARABLE_BOILERPLATE(Variable, variable);
|
||||
bool IsConst() const override { return const_; }
|
||||
std::string value() const override { return value_; }
|
||||
std::string RValue() const override;
|
||||
void Define() {
|
||||
if (defined_ && IsConst()) {
|
||||
ReportError("Cannot re-define a const-bound variable.");
|
||||
@ -146,10 +155,8 @@ class Variable : public Value {
|
||||
|
||||
private:
|
||||
friend class Declarations;
|
||||
Variable(const std::string& name, const std::string& value, const Type* type,
|
||||
bool is_const)
|
||||
Variable(std::string name, const Type* type, bool is_const)
|
||||
: Value(Declarable::kVariable, type, name),
|
||||
value_(value),
|
||||
defined_(false),
|
||||
const_(is_const) {
|
||||
DCHECK_IMPLIES(type->IsConstexpr(), IsConst());
|
||||
@ -163,8 +170,20 @@ class Variable : public Value {
|
||||
class Label : public Declarable {
|
||||
public:
|
||||
void AddVariable(Variable* var) { parameters_.push_back(var); }
|
||||
std::string name() const { return name_; }
|
||||
std::string generated() const { return generated_; }
|
||||
Block* block() const { return *block_; }
|
||||
void set_block(Block* block) {
|
||||
DCHECK(!block_);
|
||||
block_ = block;
|
||||
}
|
||||
const std::string& external_label_name() const {
|
||||
return *external_label_name_;
|
||||
}
|
||||
const std::string& name() const { return name_; }
|
||||
void set_external_label_name(std::string external_label_name) {
|
||||
DCHECK(!block_);
|
||||
DCHECK(!external_label_name_);
|
||||
external_label_name_ = std::move(external_label_name);
|
||||
}
|
||||
Variable* GetParameter(size_t i) const { return parameters_[i]; }
|
||||
size_t GetParameterCount() const { return parameters_.size(); }
|
||||
const std::vector<Variable*>& GetParameters() const { return parameters_; }
|
||||
@ -176,15 +195,15 @@ class Label : public Declarable {
|
||||
|
||||
private:
|
||||
friend class Declarations;
|
||||
explicit Label(const std::string& name, bool deferred = false)
|
||||
explicit Label(std::string name, bool deferred = false)
|
||||
: Declarable(Declarable::kLabel),
|
||||
name_(name),
|
||||
generated_("label_" + name + "_" + std::to_string(next_id_++)),
|
||||
name_(std::move(name)),
|
||||
used_(false),
|
||||
deferred_(deferred) {}
|
||||
|
||||
std::string name_;
|
||||
std::string generated_;
|
||||
base::Optional<Block*> block_;
|
||||
base::Optional<std::string> external_label_name_;
|
||||
std::vector<Variable*> parameters_;
|
||||
static size_t next_id_;
|
||||
bool used_;
|
||||
@ -194,15 +213,13 @@ class Label : public Declarable {
|
||||
class ExternConstant : public Value {
|
||||
public:
|
||||
DECLARE_DECLARABLE_BOILERPLATE(ExternConstant, constant);
|
||||
std::string value() const override { return value_; }
|
||||
|
||||
private:
|
||||
friend class Declarations;
|
||||
explicit ExternConstant(const std::string& name, const Type* type,
|
||||
const std::string& value)
|
||||
: Value(Declarable::kExternConstant, type, name), value_(value) {}
|
||||
|
||||
std::string value_;
|
||||
explicit ExternConstant(std::string name, const Type* type, std::string value)
|
||||
: Value(Declarable::kExternConstant, type, std::move(name)) {
|
||||
set_value(VisitResult(type, std::move(value)));
|
||||
}
|
||||
};
|
||||
|
||||
class Callable : public Declarable {
|
||||
|
@ -174,22 +174,10 @@ void DeclarationVisitor::Visit(TorqueMacroDeclaration* decl,
|
||||
CurrentCallableActivator activator(global_context_, macro, decl);
|
||||
|
||||
DeclareSignature(signature);
|
||||
Variable* return_variable = nullptr;
|
||||
if (!signature.return_type->IsVoidOrNever()) {
|
||||
return_variable =
|
||||
DeclareVariable(kReturnValueVariable, signature.return_type,
|
||||
signature.return_type->IsConstexpr());
|
||||
}
|
||||
|
||||
PushControlSplit();
|
||||
if (body != nullptr) {
|
||||
Visit(body);
|
||||
}
|
||||
auto changed_vars = PopControlSplit();
|
||||
if (return_variable) changed_vars.insert(return_variable);
|
||||
global_context_.AddControlSplitChangedVariables(
|
||||
decl, declarations()->GetCurrentSpecializationTypeNamesVector(),
|
||||
changed_vars);
|
||||
}
|
||||
|
||||
void DeclarationVisitor::Visit(ConstDeclaration* decl) {
|
||||
@ -273,28 +261,13 @@ void DeclarationVisitor::Visit(ReturnStatement* stmt) {
|
||||
Variable* DeclarationVisitor::DeclareVariable(const std::string& name,
|
||||
const Type* type, bool is_const) {
|
||||
Variable* result = declarations()->DeclareVariable(name, type, is_const);
|
||||
if (type->IsStructType()) {
|
||||
const StructType* struct_type = StructType::cast(type);
|
||||
for (auto& field : struct_type->fields()) {
|
||||
std::string field_var_name = name + "." + field.name;
|
||||
DeclareVariable(field_var_name, field.type, is_const);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Parameter* DeclarationVisitor::DeclareParameter(const std::string& name,
|
||||
const Type* type) {
|
||||
Parameter* result = declarations()->DeclareParameter(
|
||||
return declarations()->DeclareParameter(
|
||||
name, GetParameterVariableFromName(name), type);
|
||||
if (type->IsStructType()) {
|
||||
const StructType* struct_type = StructType::cast(type);
|
||||
for (auto& field : struct_type->fields()) {
|
||||
std::string field_var_name = name + "." + field.name;
|
||||
DeclareParameter(field_var_name, field.type);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void DeclarationVisitor::Visit(VarDeclarationStatement* stmt) {
|
||||
@ -389,39 +362,20 @@ void DeclarationVisitor::DeclareExpressionForBranch(
|
||||
|
||||
void DeclarationVisitor::Visit(ConditionalExpression* expr) {
|
||||
DeclareExpressionForBranch(expr->condition);
|
||||
PushControlSplit();
|
||||
Visit(expr->if_true);
|
||||
Visit(expr->if_false);
|
||||
auto changed_vars = PopControlSplit();
|
||||
global_context_.AddControlSplitChangedVariables(
|
||||
expr, declarations()->GetCurrentSpecializationTypeNamesVector(),
|
||||
changed_vars);
|
||||
}
|
||||
|
||||
void DeclarationVisitor::Visit(IfStatement* stmt) {
|
||||
if (!stmt->is_constexpr) {
|
||||
PushControlSplit();
|
||||
}
|
||||
DeclareExpressionForBranch(stmt->condition, stmt->if_true, stmt->if_false);
|
||||
Visit(stmt->if_true);
|
||||
if (stmt->if_false) Visit(*stmt->if_false);
|
||||
if (!stmt->is_constexpr) {
|
||||
auto changed_vars = PopControlSplit();
|
||||
global_context_.AddControlSplitChangedVariables(
|
||||
stmt, declarations()->GetCurrentSpecializationTypeNamesVector(),
|
||||
changed_vars);
|
||||
}
|
||||
}
|
||||
|
||||
void DeclarationVisitor::Visit(WhileStatement* stmt) {
|
||||
Declarations::NodeScopeActivator scope(declarations(), stmt);
|
||||
DeclareExpressionForBranch(stmt->condition);
|
||||
PushControlSplit();
|
||||
Visit(stmt->body);
|
||||
auto changed_vars = PopControlSplit();
|
||||
global_context_.AddControlSplitChangedVariables(
|
||||
stmt, declarations()->GetCurrentSpecializationTypeNamesVector(),
|
||||
changed_vars);
|
||||
}
|
||||
|
||||
void DeclarationVisitor::Visit(ForOfLoopStatement* stmt) {
|
||||
@ -431,18 +385,12 @@ void DeclarationVisitor::Visit(ForOfLoopStatement* stmt) {
|
||||
Visit(stmt->iterable);
|
||||
if (stmt->begin) Visit(*stmt->begin);
|
||||
if (stmt->end) Visit(*stmt->end);
|
||||
PushControlSplit();
|
||||
Visit(stmt->body);
|
||||
auto changed_vars = PopControlSplit();
|
||||
global_context_.AddControlSplitChangedVariables(
|
||||
stmt, declarations()->GetCurrentSpecializationTypeNamesVector(),
|
||||
changed_vars);
|
||||
}
|
||||
|
||||
void DeclarationVisitor::Visit(ForLoopStatement* stmt) {
|
||||
Declarations::NodeScopeActivator scope(declarations(), stmt);
|
||||
if (stmt->var_declaration) Visit(*stmt->var_declaration);
|
||||
PushControlSplit();
|
||||
|
||||
// Same as DeclareExpressionForBranch, but without the extra scope.
|
||||
// If no test expression is present we can not use it for the scope.
|
||||
@ -452,10 +400,6 @@ void DeclarationVisitor::Visit(ForLoopStatement* stmt) {
|
||||
|
||||
Visit(stmt->body);
|
||||
if (stmt->action) Visit(*stmt->action);
|
||||
auto changed_vars = PopControlSplit();
|
||||
global_context_.AddControlSplitChangedVariables(
|
||||
stmt, declarations()->GetCurrentSpecializationTypeNamesVector(),
|
||||
changed_vars);
|
||||
}
|
||||
|
||||
void DeclarationVisitor::Visit(TryLabelStatement* stmt) {
|
||||
@ -592,34 +536,6 @@ void DeclarationVisitor::Visit(TypeDeclaration* decl) {
|
||||
}
|
||||
}
|
||||
|
||||
void DeclarationVisitor::MarkLocationModified(Expression* location) {
|
||||
if (IdentifierExpression* id = IdentifierExpression::cast(location)) {
|
||||
const Value* value = declarations()->LookupValue(id->name);
|
||||
if (value->IsVariable()) {
|
||||
const Variable* variable = Variable::cast(value);
|
||||
bool was_live = MarkVariableModified(variable);
|
||||
if (was_live && global_context_.verbose()) {
|
||||
std::cout << *variable << " was modified in control split at "
|
||||
<< PositionAsString(id->pos) << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DeclarationVisitor::MarkVariableModified(const Variable* variable) {
|
||||
auto e = live_and_changed_variables_.rend();
|
||||
auto c = live_and_changed_variables_.rbegin();
|
||||
bool was_live_in_preceeding_split = false;
|
||||
while (c != e) {
|
||||
if (c->live.find(variable) != c->live.end()) {
|
||||
c->changed.insert(variable);
|
||||
was_live_in_preceeding_split = true;
|
||||
}
|
||||
c++;
|
||||
}
|
||||
return was_live_in_preceeding_split;
|
||||
}
|
||||
|
||||
void DeclarationVisitor::DeclareSignature(const Signature& signature) {
|
||||
auto type_iterator = signature.parameter_types.types.begin();
|
||||
for (const auto& name : signature.parameter_names) {
|
||||
@ -631,6 +547,7 @@ void DeclarationVisitor::DeclareSignature(const Signature& signature) {
|
||||
for (auto& label : signature.labels) {
|
||||
auto label_params = label.types;
|
||||
Label* new_label = declarations()->DeclareLabel(label.name);
|
||||
new_label->set_external_label_name("label_" + label.name);
|
||||
size_t i = 0;
|
||||
for (auto var_type : label_params) {
|
||||
if (var_type->IsConstexpr()) {
|
||||
|
@ -124,7 +124,6 @@ class DeclarationVisitor : public FileVisitor {
|
||||
void Visit(ForOfLoopStatement* stmt);
|
||||
|
||||
void Visit(AssignmentExpression* expr) {
|
||||
MarkLocationModified(expr->location);
|
||||
Visit(expr->location);
|
||||
Visit(expr->value);
|
||||
}
|
||||
@ -135,7 +134,6 @@ class DeclarationVisitor : public FileVisitor {
|
||||
void Visit(ForLoopStatement* stmt);
|
||||
|
||||
void Visit(IncrementDecrementExpression* expr) {
|
||||
MarkLocationModified(expr->location);
|
||||
Visit(expr->location);
|
||||
}
|
||||
|
||||
@ -145,29 +143,10 @@ class DeclarationVisitor : public FileVisitor {
|
||||
void GenerateHeader(std::string& file_name);
|
||||
|
||||
private:
|
||||
struct LiveAndChanged {
|
||||
std::set<const Variable*> live;
|
||||
std::set<const Variable*> changed;
|
||||
};
|
||||
|
||||
void PushControlSplit() {
|
||||
LiveAndChanged live_and_changed;
|
||||
live_and_changed.live = declarations()->GetLiveVariables();
|
||||
live_and_changed_variables_.push_back(live_and_changed);
|
||||
}
|
||||
|
||||
Variable* DeclareVariable(const std::string& name, const Type* type,
|
||||
bool is_const);
|
||||
Parameter* DeclareParameter(const std::string& name, const Type* type);
|
||||
|
||||
std::set<const Variable*> PopControlSplit() {
|
||||
auto result = live_and_changed_variables_.back().changed;
|
||||
live_and_changed_variables_.pop_back();
|
||||
return result;
|
||||
}
|
||||
|
||||
void MarkLocationModified(Expression* location);
|
||||
bool MarkVariableModified(const Variable* variable);
|
||||
void DeclareSignature(const Signature& signature);
|
||||
void DeclareSpecializedTypes(const SpecializationKey& key);
|
||||
|
||||
@ -177,7 +156,6 @@ class DeclarationVisitor : public FileVisitor {
|
||||
|
||||
Declarations::ModuleScopeActivator scope_;
|
||||
std::vector<Builtin*> torque_builtins_;
|
||||
std::vector<LiveAndChanged> live_and_changed_variables_;
|
||||
};
|
||||
|
||||
} // namespace torque
|
||||
|
@ -306,45 +306,29 @@ RuntimeFunction* Declarations::DeclareRuntimeFunction(
|
||||
|
||||
Variable* Declarations::CreateVariable(const std::string& var, const Type* type,
|
||||
bool is_const) {
|
||||
std::string name(var + "_" +
|
||||
std::to_string(GetNextUniqueDeclarationNumber()));
|
||||
std::replace(name.begin(), name.end(), '.', '_');
|
||||
return RegisterDeclarable(
|
||||
std::unique_ptr<Variable>(new Variable(var, name, type, is_const)));
|
||||
std::unique_ptr<Variable>(new Variable(var, type, is_const)));
|
||||
}
|
||||
|
||||
Variable* Declarations::DeclareVariable(const std::string& var,
|
||||
const Type* type, bool is_const) {
|
||||
std::string name(var + "_" +
|
||||
std::to_string(GetNextUniqueDeclarationNumber()));
|
||||
std::replace(name.begin(), name.end(), '.', '_');
|
||||
CheckAlreadyDeclared(var, "variable");
|
||||
Variable* result = new Variable(var, name, type, is_const);
|
||||
Variable* result = new Variable(var, type, is_const);
|
||||
Declare(var, std::unique_ptr<Declarable>(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
Parameter* Declarations::DeclareParameter(const std::string& name,
|
||||
const std::string& var_name,
|
||||
std::string external_name,
|
||||
const Type* type) {
|
||||
CheckAlreadyDeclared(name, "parameter");
|
||||
Parameter* result = new Parameter(name, type, var_name);
|
||||
Declare(name, std::unique_ptr<Declarable>(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
Label* Declarations::DeclarePrivateLabel(const std::string& raw_name) {
|
||||
std::string name =
|
||||
raw_name + "_" + std::to_string(GetNextUniqueDeclarationNumber());
|
||||
CheckAlreadyDeclared(name, "label");
|
||||
Label* result = new Label(name);
|
||||
Parameter* result = new Parameter(name, std::move(external_name), type);
|
||||
Declare(name, std::unique_ptr<Declarable>(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
void Declarations::DeclareExternConstant(const std::string& name,
|
||||
const Type* type,
|
||||
const std::string& value) {
|
||||
const Type* type, std::string value) {
|
||||
CheckAlreadyDeclared(name, "constant, parameter or arguments");
|
||||
ExternConstant* result = new ExternConstant(name, type, value);
|
||||
Declare(name, std::unique_ptr<Declarable>(result));
|
||||
|
@ -97,13 +97,10 @@ class Declarations {
|
||||
bool is_const);
|
||||
|
||||
Parameter* DeclareParameter(const std::string& name,
|
||||
const std::string& mangled_name,
|
||||
const Type* type);
|
||||
|
||||
Label* DeclarePrivateLabel(const std::string& name);
|
||||
std::string external_name, const Type* type);
|
||||
|
||||
void DeclareExternConstant(const std::string& name, const Type* type,
|
||||
const std::string& value);
|
||||
std::string value);
|
||||
ModuleConstant* DeclareModuleConstant(const std::string& name,
|
||||
const Type* type);
|
||||
|
||||
|
@ -51,10 +51,6 @@ class FileVisitor {
|
||||
};
|
||||
|
||||
protected:
|
||||
static constexpr const char* kReturnValueVariable = "_return";
|
||||
static constexpr const char* kDoneLabelName = "_done";
|
||||
static constexpr const char* kForIndexValueVariable = "_for_index";
|
||||
|
||||
Module* CurrentModule() const { return module_; }
|
||||
|
||||
friend class ScopedModuleActivator;
|
||||
|
@ -65,34 +65,12 @@ class GlobalContext {
|
||||
void SetVerbose() { verbose_ = true; }
|
||||
bool verbose() const { return verbose_; }
|
||||
|
||||
void AddControlSplitChangedVariables(const AstNode* node,
|
||||
const TypeVector& specialization_types,
|
||||
const std::set<const Variable*>& vars) {
|
||||
auto key = std::make_pair(node, specialization_types);
|
||||
control_split_changed_variables_[key] = vars;
|
||||
}
|
||||
|
||||
const std::set<const Variable*>& GetControlSplitChangedVariables(
|
||||
const AstNode* node, const TypeVector& specialization_types) {
|
||||
auto key = std::make_pair(node, specialization_types);
|
||||
assert(control_split_changed_variables_.find(key) !=
|
||||
control_split_changed_variables_.end());
|
||||
return control_split_changed_variables_.find(key)->second;
|
||||
}
|
||||
|
||||
void MarkVariableChanged(const AstNode* node,
|
||||
const TypeVector& specialization_types,
|
||||
Variable* var) {
|
||||
auto key = std::make_pair(node, specialization_types);
|
||||
control_split_changed_variables_[key].insert(var);
|
||||
}
|
||||
|
||||
friend class CurrentCallableActivator;
|
||||
friend class BreakContinueActivator;
|
||||
|
||||
Callable* GetCurrentCallable() const { return current_callable_; }
|
||||
Label* GetCurrentBreak() const { return break_continue_stack_.back().first; }
|
||||
Label* GetCurrentContinue() const {
|
||||
Block* GetCurrentBreak() const { return break_continue_stack_.back().first; }
|
||||
Block* GetCurrentContinue() const {
|
||||
return break_continue_stack_.back().second;
|
||||
}
|
||||
|
||||
@ -104,11 +82,9 @@ class GlobalContext {
|
||||
int next_label_number_;
|
||||
Declarations declarations_;
|
||||
Callable* current_callable_;
|
||||
std::vector<std::pair<Label*, Label*>> break_continue_stack_;
|
||||
std::vector<std::pair<Block*, Block*>> break_continue_stack_;
|
||||
std::map<std::string, std::unique_ptr<Module>> modules_;
|
||||
Module* default_module_;
|
||||
std::map<std::pair<const AstNode*, TypeVector>, std::set<const Variable*>>
|
||||
control_split_changed_variables_;
|
||||
Ast ast_;
|
||||
};
|
||||
|
||||
@ -132,10 +108,10 @@ class CurrentCallableActivator {
|
||||
|
||||
class BreakContinueActivator {
|
||||
public:
|
||||
BreakContinueActivator(GlobalContext& context, Label* break_label,
|
||||
Label* continue_label)
|
||||
BreakContinueActivator(GlobalContext& context, Block* break_block,
|
||||
Block* continue_block)
|
||||
: context_(context) {
|
||||
context_.break_continue_stack_.push_back({break_label, continue_label});
|
||||
context_.break_continue_stack_.push_back({break_block, continue_block});
|
||||
}
|
||||
~BreakContinueActivator() { context_.break_continue_stack_.pop_back(); }
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,7 @@
|
||||
|
||||
#include "src/base/macros.h"
|
||||
#include "src/torque/ast.h"
|
||||
#include "src/torque/cfg.h"
|
||||
#include "src/torque/file-visitor.h"
|
||||
#include "src/torque/global-context.h"
|
||||
#include "src/torque/types.h"
|
||||
@ -18,18 +19,95 @@ namespace v8 {
|
||||
namespace internal {
|
||||
namespace torque {
|
||||
|
||||
struct LocationReference {
|
||||
LocationReference(Value* value, VisitResult base, VisitResult index)
|
||||
: value(value), base(std::move(base)), index(std::move(index)) {}
|
||||
Value* value;
|
||||
VisitResult base;
|
||||
VisitResult index;
|
||||
// LocationReference is the representation of an l-value, so a value that might
|
||||
// allow for assignment. For uniformity, this class can also represent
|
||||
// unassignable temporaries. Assignable values fall in two categories:
|
||||
// - stack ranges that represent mutable variables, including structs.
|
||||
// - field or element access expressions that generate operator calls.
|
||||
class LocationReference {
|
||||
public:
|
||||
// An assignable stack range.
|
||||
static LocationReference VariableAccess(VisitResult variable) {
|
||||
DCHECK(variable.IsOnStack());
|
||||
LocationReference result;
|
||||
result.variable_ = std::move(variable);
|
||||
return result;
|
||||
}
|
||||
// An unassignable value. {description} is only used for error messages.
|
||||
static LocationReference Temporary(VisitResult temporary,
|
||||
std::string description) {
|
||||
LocationReference result;
|
||||
result.temporary_ = std::move(temporary);
|
||||
result.temporary_description_ = std::move(description);
|
||||
return result;
|
||||
}
|
||||
static LocationReference ArrayAccess(VisitResult base, VisitResult offset) {
|
||||
LocationReference result;
|
||||
result.eval_function_ = std::string{"[]"};
|
||||
result.assign_function_ = std::string{"[]="};
|
||||
result.call_arguments_ = {base, offset};
|
||||
return result;
|
||||
}
|
||||
static LocationReference FieldAccess(VisitResult object,
|
||||
std::string fieldname) {
|
||||
LocationReference result;
|
||||
result.eval_function_ = "." + fieldname;
|
||||
result.assign_function_ = "." + fieldname + "=";
|
||||
result.call_arguments_ = {object};
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsConst() const { return temporary_.has_value(); }
|
||||
|
||||
bool IsVariableAccess() const { return variable_.has_value(); }
|
||||
const VisitResult& variable() const {
|
||||
DCHECK(IsVariableAccess());
|
||||
return *variable_;
|
||||
}
|
||||
bool IsTemporary() const { return temporary_.has_value(); }
|
||||
const VisitResult& temporary() const {
|
||||
DCHECK(IsTemporary());
|
||||
return *temporary_;
|
||||
}
|
||||
// For error reporting.
|
||||
const std::string& temporary_description() const {
|
||||
DCHECK(IsTemporary());
|
||||
return *temporary_description_;
|
||||
}
|
||||
|
||||
bool IsCallAccess() const {
|
||||
bool is_call_access = eval_function_.has_value();
|
||||
DCHECK_EQ(is_call_access, assign_function_.has_value());
|
||||
return is_call_access;
|
||||
}
|
||||
const VisitResultVector& call_arguments() const {
|
||||
DCHECK(IsCallAccess());
|
||||
return call_arguments_;
|
||||
}
|
||||
const std::string& eval_function() const {
|
||||
DCHECK(IsCallAccess());
|
||||
return *eval_function_;
|
||||
}
|
||||
const std::string& assign_function() const {
|
||||
DCHECK(IsCallAccess());
|
||||
return *assign_function_;
|
||||
}
|
||||
|
||||
private:
|
||||
base::Optional<VisitResult> variable_;
|
||||
base::Optional<VisitResult> temporary_;
|
||||
base::Optional<std::string> temporary_description_;
|
||||
base::Optional<std::string> eval_function_;
|
||||
base::Optional<std::string> assign_function_;
|
||||
VisitResultVector call_arguments_;
|
||||
|
||||
LocationReference() = default;
|
||||
};
|
||||
|
||||
class ImplementationVisitor : public FileVisitor {
|
||||
public:
|
||||
explicit ImplementationVisitor(GlobalContext& global_context)
|
||||
: FileVisitor(global_context), indent_(0), next_temp_(0) {}
|
||||
: FileVisitor(global_context) {}
|
||||
|
||||
void Visit(Ast* ast) { Visit(ast->default_module()); }
|
||||
|
||||
@ -39,44 +117,23 @@ class ImplementationVisitor : public FileVisitor {
|
||||
|
||||
VisitResult Visit(StructExpression* decl);
|
||||
|
||||
LocationReference GetLocationReference(LocationExpression* location);
|
||||
LocationReference GetLocationReference(IdentifierExpression* expr) {
|
||||
return LocationReference(declarations()->LookupValue(expr->name), {}, {});
|
||||
}
|
||||
LocationReference GetLocationReference(Expression* location);
|
||||
LocationReference GetLocationReference(IdentifierExpression* expr);
|
||||
LocationReference GetLocationReference(FieldAccessExpression* expr);
|
||||
LocationReference GetLocationReference(ElementAccessExpression* expr) {
|
||||
return LocationReference({}, Visit(expr->array), Visit(expr->index));
|
||||
}
|
||||
LocationReference GetLocationReference(ElementAccessExpression* expr);
|
||||
|
||||
std::string RValueFlattenStructs(const VisitResult& result);
|
||||
|
||||
VisitResult GenerateFetchFromLocation(const LocationReference& reference) {
|
||||
const Value* value = reference.value;
|
||||
return VisitResult(value->type(), value);
|
||||
}
|
||||
VisitResult GenerateFetchFromLocation(LocationExpression* location,
|
||||
const LocationReference& reference);
|
||||
VisitResult GenerateFetchFromLocation(IdentifierExpression* expr,
|
||||
const LocationReference& reference) {
|
||||
return GenerateFetchFromLocation(reference);
|
||||
}
|
||||
VisitResult GenerateFetchFromLocation(FieldAccessExpression* expr,
|
||||
const LocationReference& reference);
|
||||
VisitResult GenerateFetchFromLocation(ElementAccessExpression* expr,
|
||||
const LocationReference& reference) {
|
||||
Arguments arguments;
|
||||
arguments.parameters = {reference.base, reference.index};
|
||||
return GenerateCall("[]", arguments);
|
||||
}
|
||||
VisitResult GenerateFetchFromLocation(const LocationReference& reference);
|
||||
|
||||
VisitResult GetBuiltinCode(Builtin* builtin);
|
||||
|
||||
VisitResult Visit(IdentifierExpression* expr);
|
||||
VisitResult Visit(FieldAccessExpression* expr) {
|
||||
return GenerateFetchFromLocation(expr, GetLocationReference(expr));
|
||||
StackScope scope(this);
|
||||
return scope.Yield(GenerateFetchFromLocation(GetLocationReference(expr)));
|
||||
}
|
||||
VisitResult Visit(ElementAccessExpression* expr) {
|
||||
return GenerateFetchFromLocation(expr, GetLocationReference(expr));
|
||||
StackScope scope(this);
|
||||
return scope.Yield(GenerateFetchFromLocation(GetLocationReference(expr)));
|
||||
}
|
||||
|
||||
void Visit(ModuleDeclaration* decl);
|
||||
@ -146,64 +203,71 @@ class ImplementationVisitor : public FileVisitor {
|
||||
|
||||
std::string GetDSLAssemblerName(Module* module);
|
||||
|
||||
void GenerateIndent();
|
||||
|
||||
class ScopedIndent {
|
||||
// {StackScope} records the stack height at creation time and reconstructs it
|
||||
// when being destructed by emitting a {DeleteRangeInstruction}, except for
|
||||
// the slots protected by {StackScope::Yield}. Calling {Yield(v)} deletes all
|
||||
// slots above the initial stack height except for the slots of {v}, which are
|
||||
// moved to form the only slots above the initial height and marks them to
|
||||
// survive destruction of the {StackScope}. A typical pattern is the
|
||||
// following:
|
||||
//
|
||||
// VisitResult result;
|
||||
// {
|
||||
// StackScope stack_scope(this);
|
||||
// // ... create temporary slots ...
|
||||
// result = stack_scope.Yield(surviving_slots);
|
||||
// }
|
||||
class StackScope {
|
||||
public:
|
||||
explicit ScopedIndent(ImplementationVisitor* visitor, bool new_lines = true)
|
||||
: new_lines_(new_lines), visitor_(visitor) {
|
||||
if (new_lines) visitor->GenerateIndent();
|
||||
visitor->source_out() << "{";
|
||||
if (new_lines) visitor->source_out() << "\n";
|
||||
visitor->indent_++;
|
||||
explicit StackScope(ImplementationVisitor* visitor) : visitor_(visitor) {
|
||||
base_ = visitor_->assembler().CurrentStack().AboveTop();
|
||||
}
|
||||
~ScopedIndent() {
|
||||
visitor_->indent_--;
|
||||
visitor_->GenerateIndent();
|
||||
visitor_->source_out() << "}";
|
||||
if (new_lines_) visitor_->source_out() << "\n";
|
||||
VisitResult Yield(VisitResult result) {
|
||||
DCHECK(!yield_called_);
|
||||
yield_called_ = true;
|
||||
if (!result.IsOnStack()) {
|
||||
if (!visitor_->assembler().CurrentBlockIsComplete()) {
|
||||
visitor_->assembler().DropTo(base_);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
DCHECK_LE(base_, result.stack_range().begin());
|
||||
DCHECK_LE(result.stack_range().end(),
|
||||
visitor_->assembler().CurrentStack().AboveTop());
|
||||
visitor_->assembler().DropTo(result.stack_range().end());
|
||||
visitor_->assembler().DeleteRange(
|
||||
StackRange{base_, result.stack_range().begin()});
|
||||
base_ = visitor_->assembler().CurrentStack().AboveTop();
|
||||
return VisitResult(result.type(), visitor_->assembler().TopRange(
|
||||
result.stack_range().Size()));
|
||||
}
|
||||
|
||||
~StackScope() {
|
||||
if (yield_called_) {
|
||||
DCHECK_IMPLIES(
|
||||
!visitor_->assembler().CurrentBlockIsComplete(),
|
||||
base_ == visitor_->assembler().CurrentStack().AboveTop());
|
||||
} else if (!visitor_->assembler().CurrentBlockIsComplete()) {
|
||||
visitor_->assembler().DropTo(base_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool new_lines_;
|
||||
ImplementationVisitor* visitor_;
|
||||
BottomOffset base_;
|
||||
bool yield_called_ = false;
|
||||
};
|
||||
|
||||
Callable* LookupCall(const std::string& name, const Arguments& arguments,
|
||||
const TypeVector& specialization_types);
|
||||
|
||||
bool GenerateChangedVarFromControlSplit(const Variable* v, bool first = true);
|
||||
|
||||
void GetFlattenedStructsVars(const Variable* base,
|
||||
std::set<const Variable*>* vars);
|
||||
|
||||
void GenerateChangedVarsFromControlSplit(AstNode* node);
|
||||
|
||||
const Type* GetCommonType(const Type* left, const Type* right);
|
||||
|
||||
VisitResult GenerateCopy(const VisitResult& to_copy);
|
||||
|
||||
void GenerateAssignToVariable(Variable* var, const VisitResult& value);
|
||||
|
||||
void GenerateAssignToLocation(LocationExpression* location,
|
||||
const LocationReference& reference,
|
||||
void GenerateAssignToLocation(const LocationReference& reference,
|
||||
const VisitResult& assignment_value);
|
||||
|
||||
void GenerateVariableDeclaration(const Variable* var);
|
||||
|
||||
Variable* GeneratePredeclaredVariableDeclaration(
|
||||
const std::string& name,
|
||||
const base::Optional<VisitResult>& initialization);
|
||||
|
||||
Variable* GenerateVariableDeclaration(
|
||||
AstNode* node, const std::string& name, bool is_const,
|
||||
const base::Optional<const Type*>& type,
|
||||
const base::Optional<VisitResult>& initialization = {});
|
||||
|
||||
void GenerateParameter(const std::string& parameter_name);
|
||||
|
||||
void GenerateParameterList(const NameVector& list, size_t first = 0);
|
||||
|
||||
VisitResult GenerateCall(const std::string& callable_name,
|
||||
Arguments parameters,
|
||||
const TypeVector& specialization_types = {},
|
||||
@ -213,7 +277,7 @@ class ImplementationVisitor : public FileVisitor {
|
||||
|
||||
bool GenerateLabeledStatementBlocks(
|
||||
const std::vector<Statement*>& blocks,
|
||||
const std::vector<Label*>& statement_labels, Label* merge_label);
|
||||
const std::vector<Label*>& statement_labels, Block* merge_block);
|
||||
|
||||
void GenerateBranch(const VisitResult& condition, Label* true_label,
|
||||
Label* false_label);
|
||||
@ -221,7 +285,7 @@ class ImplementationVisitor : public FileVisitor {
|
||||
bool GenerateExpressionBranch(Expression* expression,
|
||||
const std::vector<Label*>& statement_labels,
|
||||
const std::vector<Statement*>& statement_blocks,
|
||||
Label* merge_label);
|
||||
Block* merge_block);
|
||||
|
||||
void GenerateMacroFunctionDeclaration(std::ostream& o,
|
||||
const std::string& macro_prefix,
|
||||
@ -242,25 +306,40 @@ class ImplementationVisitor : public FileVisitor {
|
||||
Visit(callable, MakeSignature(signature), body);
|
||||
}
|
||||
|
||||
std::string NewTempVariable();
|
||||
|
||||
std::string GenerateNewTempVariable(const Type* type);
|
||||
|
||||
void GenerateLabelDefinition(Label* label, AstNode* node = nullptr);
|
||||
void CreateBlockForLabel(Label* label, Stack<const Type*> stack);
|
||||
|
||||
void GenerateLabelBind(Label* label);
|
||||
|
||||
void GenerateLabelGoto(Label* label);
|
||||
StackRange GenerateLabelGoto(Label* label,
|
||||
base::Optional<StackRange> arguments = {});
|
||||
|
||||
std::vector<Label*> LabelsFromIdentifiers(
|
||||
const std::vector<std::string>& names);
|
||||
|
||||
StackRange LowerParameter(const Type* type, const std::string& parameter_name,
|
||||
Stack<std::string>* lowered_parameters);
|
||||
|
||||
std::string ExternalLabelParameterName(Label* label, size_t i);
|
||||
|
||||
std::ostream& source_out() { return module_->source_stream(); }
|
||||
|
||||
std::ostream& header_out() { return module_->header_stream(); }
|
||||
|
||||
size_t indent_;
|
||||
int32_t next_temp_;
|
||||
CfgAssembler& assembler() { return *assembler_; }
|
||||
|
||||
void SetReturnValue(VisitResult return_value) {
|
||||
DCHECK_IMPLIES(return_value_, *return_value_ == return_value);
|
||||
return_value_ = std::move(return_value);
|
||||
}
|
||||
|
||||
VisitResult GetAndClearReturnValue() {
|
||||
VisitResult return_value = *return_value_;
|
||||
return_value_ = base::nullopt;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
base::Optional<CfgAssembler> assembler_;
|
||||
base::Optional<VisitResult> return_value_;
|
||||
};
|
||||
|
||||
} // namespace torque
|
||||
|
204
src/torque/instructions.cc
Normal file
204
src/torque/instructions.cc
Normal file
@ -0,0 +1,204 @@
|
||||
// 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.
|
||||
|
||||
#include "src/torque/instructions.h"
|
||||
#include "src/torque/cfg.h"
|
||||
#include "src/torque/type-oracle.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace torque {
|
||||
|
||||
#define TORQUE_INSTRUCTION_BOILERPLATE_DEFINITIONS(Name) \
|
||||
const InstructionKind Name::kKind = InstructionKind::k##Name; \
|
||||
std::unique_ptr<InstructionBase> Name::Clone() const { \
|
||||
return std::unique_ptr<InstructionBase>(new Name(*this)); \
|
||||
} \
|
||||
void Name::Assign(const InstructionBase& other) { \
|
||||
*this = static_cast<const Name&>(other); \
|
||||
}
|
||||
TORQUE_INSTRUCTION_LIST(TORQUE_INSTRUCTION_BOILERPLATE_DEFINITIONS)
|
||||
#undef TORQUE_INSTRUCTION_BOILERPLATE_DEFINITIONS
|
||||
|
||||
void PeekInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
const Type* type = stack->Peek(slot);
|
||||
if (widened_type) {
|
||||
if (!type->IsSubtypeOf(*widened_type)) {
|
||||
ReportError("type ", type, " is not a subtype of ", *widened_type);
|
||||
}
|
||||
type = *widened_type;
|
||||
}
|
||||
stack->Push(type);
|
||||
}
|
||||
|
||||
void PokeInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
const Type* type = stack->Top();
|
||||
if (widened_type) {
|
||||
if (!type->IsSubtypeOf(*widened_type)) {
|
||||
ReportError("type ", type, " is not a subtype of ", *widened_type);
|
||||
}
|
||||
type = *widened_type;
|
||||
}
|
||||
stack->Poke(slot, type);
|
||||
stack->Pop();
|
||||
}
|
||||
|
||||
void DeleteRangeInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
stack->DeleteRange(range);
|
||||
}
|
||||
|
||||
void PushUninitializedInstruction::TypeInstruction(
|
||||
Stack<const Type*>* stack, ControlFlowGraph* cfg) const {
|
||||
stack->Push(type);
|
||||
}
|
||||
|
||||
void PushCodePointerInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
stack->Push(type);
|
||||
}
|
||||
|
||||
void ModuleConstantInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
stack->PushMany(LowerType(constant->type()));
|
||||
}
|
||||
|
||||
void CallCsaMacroInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
std::vector<const Type*> parameter_types =
|
||||
LowerParameterTypes(macro->signature().parameter_types);
|
||||
for (intptr_t i = parameter_types.size() - 1; i >= 0; --i) {
|
||||
const Type* arg_type = stack->Pop();
|
||||
const Type* parameter_type = parameter_types.back();
|
||||
parameter_types.pop_back();
|
||||
if (arg_type != parameter_type) {
|
||||
ReportError("parameter ", i, ": expected type ", *parameter_type,
|
||||
" but found type ", *arg_type);
|
||||
}
|
||||
}
|
||||
if (!parameter_types.empty()) ReportError("missing arguments");
|
||||
|
||||
stack->PushMany(LowerType(macro->signature().return_type));
|
||||
}
|
||||
|
||||
void CallCsaMacroAndBranchInstruction::TypeInstruction(
|
||||
Stack<const Type*>* stack, ControlFlowGraph* cfg) const {
|
||||
std::vector<const Type*> parameter_types =
|
||||
LowerParameterTypes(macro->signature().parameter_types);
|
||||
for (intptr_t i = parameter_types.size() - 1; i >= 0; --i) {
|
||||
const Type* arg_type = stack->Pop();
|
||||
const Type* parameter_type = parameter_types.back();
|
||||
parameter_types.pop_back();
|
||||
if (arg_type != parameter_type) {
|
||||
ReportError("parameter ", i, ": expected type ", *parameter_type,
|
||||
" but found type ", *arg_type);
|
||||
}
|
||||
}
|
||||
if (!parameter_types.empty()) ReportError("missing arguments");
|
||||
|
||||
if (label_blocks.size() != macro->signature().labels.size()) {
|
||||
ReportError("wrong number of labels");
|
||||
}
|
||||
for (size_t i = 0; i < label_blocks.size(); ++i) {
|
||||
Stack<const Type*> continuation_stack = *stack;
|
||||
continuation_stack.PushMany(
|
||||
LowerParameterTypes(macro->signature().labels[i].types));
|
||||
label_blocks[i]->SetInputTypes(std::move(continuation_stack));
|
||||
}
|
||||
|
||||
if (macro->signature().return_type != TypeOracle::GetNeverType()) {
|
||||
Stack<const Type*> return_stack = *stack;
|
||||
return_stack.PushMany(LowerType(macro->signature().return_type));
|
||||
if (return_continuation == base::nullopt) {
|
||||
ReportError("missing return continuation.");
|
||||
}
|
||||
(*return_continuation)->SetInputTypes(return_stack);
|
||||
} else {
|
||||
if (return_continuation != base::nullopt) {
|
||||
ReportError("unreachable return continuation.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CallBuiltinInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
std::vector<const Type*> argument_types = stack->PopMany(argc);
|
||||
if (argument_types !=
|
||||
LowerParameterTypes(builtin->signature().parameter_types)) {
|
||||
ReportError("wrong argument types");
|
||||
}
|
||||
stack->PushMany(LowerType(builtin->signature().return_type));
|
||||
}
|
||||
|
||||
void CallBuiltinPointerInstruction::TypeInstruction(
|
||||
Stack<const Type*>* stack, ControlFlowGraph* cfg) const {
|
||||
std::vector<const Type*> argument_types = stack->PopMany(argc);
|
||||
const FunctionPointerType* f = FunctionPointerType::DynamicCast(stack->Pop());
|
||||
if (!f) ReportError("expected function pointer type");
|
||||
if (argument_types != LowerParameterTypes(f->parameter_types())) {
|
||||
ReportError("wrong argument types");
|
||||
}
|
||||
stack->PushMany(LowerType(f->return_type()));
|
||||
}
|
||||
|
||||
void CallRuntimeInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
std::vector<const Type*> argument_types = stack->PopMany(argc);
|
||||
if (argument_types !=
|
||||
LowerParameterTypes(runtime_function->signature().parameter_types,
|
||||
argc)) {
|
||||
ReportError("wrong argument types");
|
||||
}
|
||||
stack->PushMany(LowerType(runtime_function->signature().return_type));
|
||||
}
|
||||
|
||||
void BranchInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
const Type* condition_type = stack->Pop();
|
||||
if (condition_type != TypeOracle::GetBoolType()) {
|
||||
ReportError("condition has to have type bool");
|
||||
}
|
||||
if_true->SetInputTypes(*stack);
|
||||
if_false->SetInputTypes(*stack);
|
||||
}
|
||||
|
||||
void ConstexprBranchInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
if_true->SetInputTypes(*stack);
|
||||
if_false->SetInputTypes(*stack);
|
||||
}
|
||||
|
||||
void GotoInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
destination->SetInputTypes(*stack);
|
||||
}
|
||||
|
||||
void GotoExternalInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
if (variable_names.size() != stack->Size()) {
|
||||
ReportError("goto external label with wrong parameter count.");
|
||||
}
|
||||
}
|
||||
|
||||
void ReturnInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
cfg->SetReturnType(stack->Pop());
|
||||
}
|
||||
|
||||
void PrintConstantStringInstruction::TypeInstruction(
|
||||
Stack<const Type*>* stack, ControlFlowGraph* cfg) const {}
|
||||
|
||||
void DebugBreakInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {}
|
||||
|
||||
void UnsafeCastInstruction::TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const {
|
||||
stack->Poke(stack->AboveTop() - 1, destination_type);
|
||||
}
|
||||
|
||||
} // namespace torque
|
||||
} // namespace internal
|
||||
} // namespace v8
|
354
src/torque/instructions.h
Normal file
354
src/torque/instructions.h
Normal file
@ -0,0 +1,354 @@
|
||||
// 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_INSTRUCTIONS_H_
|
||||
#define V8_TORQUE_INSTRUCTIONS_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "src/torque/ast.h"
|
||||
#include "src/torque/source-positions.h"
|
||||
#include "src/torque/types.h"
|
||||
#include "src/torque/utils.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace torque {
|
||||
|
||||
class Block;
|
||||
class Builtin;
|
||||
class ControlFlowGraph;
|
||||
class Macro;
|
||||
class ModuleConstant;
|
||||
class RuntimeFunction;
|
||||
|
||||
#define TORQUE_INSTRUCTION_LIST(V) \
|
||||
V(PeekInstruction) \
|
||||
V(PokeInstruction) \
|
||||
V(DeleteRangeInstruction) \
|
||||
V(PushUninitializedInstruction) \
|
||||
V(PushCodePointerInstruction) \
|
||||
V(CallCsaMacroInstruction) \
|
||||
V(ModuleConstantInstruction) \
|
||||
V(CallCsaMacroAndBranchInstruction) \
|
||||
V(CallBuiltinInstruction) \
|
||||
V(CallRuntimeInstruction) \
|
||||
V(CallBuiltinPointerInstruction) \
|
||||
V(BranchInstruction) \
|
||||
V(ConstexprBranchInstruction) \
|
||||
V(GotoInstruction) \
|
||||
V(GotoExternalInstruction) \
|
||||
V(ReturnInstruction) \
|
||||
V(PrintConstantStringInstruction) \
|
||||
V(DebugBreakInstruction) \
|
||||
V(UnsafeCastInstruction)
|
||||
|
||||
#define TORQUE_INSTRUCTION_BOILERPLATE() \
|
||||
static const InstructionKind kKind; \
|
||||
std::unique_ptr<InstructionBase> Clone() const override; \
|
||||
void Assign(const InstructionBase& other) override; \
|
||||
void TypeInstruction(Stack<const Type*>* stack, ControlFlowGraph* cfg) \
|
||||
const override;
|
||||
|
||||
enum class InstructionKind {
|
||||
#define ENUM_ITEM(name) k##name,
|
||||
TORQUE_INSTRUCTION_LIST(ENUM_ITEM)
|
||||
#undef ENUM_ITEM
|
||||
};
|
||||
|
||||
struct InstructionBase {
|
||||
InstructionBase() : pos(CurrentSourcePosition::Get()) {}
|
||||
virtual std::unique_ptr<InstructionBase> Clone() const = 0;
|
||||
virtual void Assign(const InstructionBase& other) = 0;
|
||||
virtual ~InstructionBase() = default;
|
||||
|
||||
virtual void TypeInstruction(Stack<const Type*>* stack,
|
||||
ControlFlowGraph* cfg) const = 0;
|
||||
virtual bool IsBlockTerminator() const { return false; }
|
||||
virtual void AppendSuccessorBlocks(std::vector<Block*>* block_list) const {}
|
||||
|
||||
SourcePosition pos;
|
||||
};
|
||||
|
||||
class Instruction {
|
||||
public:
|
||||
template <class T>
|
||||
Instruction(T instr) // NOLINT(runtime/explicit)
|
||||
: kind_(T::kKind), instruction_(new T(std::move(instr))) {}
|
||||
|
||||
template <class T>
|
||||
T& Cast() {
|
||||
DCHECK(Is<T>());
|
||||
return static_cast<T&>(*instruction_);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
const T& Cast() const {
|
||||
DCHECK(Is<T>());
|
||||
return static_cast<const T&>(*instruction_);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool Is() const {
|
||||
return kind_ == T::kKind;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T* DynamicCast() {
|
||||
if (Is<T>()) return &Cast<T>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
const T* DynamicCast() const {
|
||||
if (Is<T>()) return &Cast<T>();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Instruction(const Instruction& other)
|
||||
: kind_(other.kind_), instruction_(other.instruction_->Clone()) {}
|
||||
Instruction& operator=(const Instruction& other) {
|
||||
if (kind_ == other.kind_) {
|
||||
instruction_->Assign(*other.instruction_);
|
||||
} else {
|
||||
kind_ = other.kind_;
|
||||
instruction_ = other.instruction_->Clone();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
InstructionKind kind() const { return kind_; }
|
||||
void TypeInstruction(Stack<const Type*>* stack, ControlFlowGraph* cfg) const {
|
||||
return instruction_->TypeInstruction(stack, cfg);
|
||||
}
|
||||
|
||||
InstructionBase* operator->() { return instruction_.get(); }
|
||||
const InstructionBase* operator->() const { return instruction_.get(); }
|
||||
|
||||
private:
|
||||
InstructionKind kind_;
|
||||
std::unique_ptr<InstructionBase> instruction_;
|
||||
};
|
||||
|
||||
struct PeekInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
|
||||
PeekInstruction(BottomOffset slot, base::Optional<const Type*> widened_type)
|
||||
: slot(slot), widened_type(widened_type) {}
|
||||
|
||||
BottomOffset slot;
|
||||
base::Optional<const Type*> widened_type;
|
||||
};
|
||||
|
||||
struct PokeInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
|
||||
PokeInstruction(BottomOffset slot, base::Optional<const Type*> widened_type)
|
||||
: slot(slot), widened_type(widened_type) {}
|
||||
|
||||
BottomOffset slot;
|
||||
base::Optional<const Type*> widened_type;
|
||||
};
|
||||
|
||||
// Preserve the top {preserved_slots} number of slots, and delete
|
||||
// {deleted_slots} number or slots below.
|
||||
struct DeleteRangeInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
explicit DeleteRangeInstruction(StackRange range) : range(range) {}
|
||||
|
||||
StackRange range;
|
||||
};
|
||||
|
||||
struct PushUninitializedInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
explicit PushUninitializedInstruction(const Type* type) : type(type) {}
|
||||
|
||||
const Type* type;
|
||||
};
|
||||
|
||||
struct PushCodePointerInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
PushCodePointerInstruction(std::string external_name, const Type* type)
|
||||
: external_name(std::move(external_name)), type(type) {
|
||||
DCHECK(type->IsFunctionPointerType());
|
||||
}
|
||||
|
||||
std::string external_name;
|
||||
const Type* type;
|
||||
};
|
||||
|
||||
struct ModuleConstantInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
explicit ModuleConstantInstruction(ModuleConstant* constant)
|
||||
: constant(constant) {}
|
||||
|
||||
ModuleConstant* constant;
|
||||
};
|
||||
|
||||
struct CallCsaMacroInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
CallCsaMacroInstruction(Macro* macro,
|
||||
std::vector<std::string> constexpr_arguments)
|
||||
: macro(macro), constexpr_arguments(constexpr_arguments) {}
|
||||
|
||||
Macro* macro;
|
||||
std::vector<std::string> constexpr_arguments;
|
||||
};
|
||||
|
||||
struct CallCsaMacroAndBranchInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
CallCsaMacroAndBranchInstruction(Macro* macro,
|
||||
std::vector<std::string> constexpr_arguments,
|
||||
base::Optional<Block*> return_continuation,
|
||||
std::vector<Block*> label_blocks)
|
||||
: macro(macro),
|
||||
constexpr_arguments(constexpr_arguments),
|
||||
return_continuation(return_continuation),
|
||||
label_blocks(label_blocks) {}
|
||||
bool IsBlockTerminator() const override { return true; }
|
||||
void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override {
|
||||
if (return_continuation) block_list->push_back(*return_continuation);
|
||||
for (Block* block : label_blocks) block_list->push_back(block);
|
||||
}
|
||||
|
||||
Macro* macro;
|
||||
std::vector<std::string> constexpr_arguments;
|
||||
base::Optional<Block*> return_continuation;
|
||||
std::vector<Block*> label_blocks;
|
||||
};
|
||||
|
||||
struct CallBuiltinInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
bool IsBlockTerminator() const override { return is_tailcall; }
|
||||
CallBuiltinInstruction(bool is_tailcall, Builtin* builtin, size_t argc)
|
||||
: is_tailcall(is_tailcall), builtin(builtin), argc(argc) {}
|
||||
|
||||
bool is_tailcall;
|
||||
Builtin* builtin;
|
||||
size_t argc;
|
||||
};
|
||||
|
||||
struct CallBuiltinPointerInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
bool IsBlockTerminator() const override { return is_tailcall; }
|
||||
CallBuiltinPointerInstruction(bool is_tailcall, Builtin* example_builtin,
|
||||
size_t argc)
|
||||
: is_tailcall(is_tailcall),
|
||||
example_builtin(example_builtin),
|
||||
argc(argc) {}
|
||||
|
||||
bool is_tailcall;
|
||||
Builtin* example_builtin;
|
||||
size_t argc;
|
||||
};
|
||||
|
||||
struct CallRuntimeInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
bool IsBlockTerminator() const override { return is_tailcall; }
|
||||
|
||||
CallRuntimeInstruction(bool is_tailcall, RuntimeFunction* runtime_function,
|
||||
size_t argc)
|
||||
: is_tailcall(is_tailcall),
|
||||
runtime_function(runtime_function),
|
||||
argc(argc) {}
|
||||
|
||||
bool is_tailcall;
|
||||
RuntimeFunction* runtime_function;
|
||||
size_t argc;
|
||||
};
|
||||
|
||||
struct BranchInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
bool IsBlockTerminator() const override { return true; }
|
||||
void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override {
|
||||
block_list->push_back(if_true);
|
||||
block_list->push_back(if_false);
|
||||
}
|
||||
|
||||
BranchInstruction(Block* if_true, Block* if_false)
|
||||
: if_true(if_true), if_false(if_false) {}
|
||||
|
||||
Block* if_true;
|
||||
Block* if_false;
|
||||
};
|
||||
|
||||
struct ConstexprBranchInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
bool IsBlockTerminator() const override { return true; }
|
||||
void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override {
|
||||
block_list->push_back(if_true);
|
||||
block_list->push_back(if_false);
|
||||
}
|
||||
|
||||
ConstexprBranchInstruction(std::string condition, Block* if_true,
|
||||
Block* if_false)
|
||||
: condition(condition), if_true(if_true), if_false(if_false) {}
|
||||
|
||||
std::string condition;
|
||||
Block* if_true;
|
||||
Block* if_false;
|
||||
};
|
||||
|
||||
struct GotoInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
bool IsBlockTerminator() const override { return true; }
|
||||
void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override {
|
||||
block_list->push_back(destination);
|
||||
}
|
||||
|
||||
explicit GotoInstruction(Block* destination) : destination(destination) {}
|
||||
|
||||
Block* destination;
|
||||
};
|
||||
|
||||
struct GotoExternalInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
bool IsBlockTerminator() const override { return true; }
|
||||
|
||||
GotoExternalInstruction(std::string destination,
|
||||
std::vector<std::string> variable_names)
|
||||
: destination(std::move(destination)),
|
||||
variable_names(std::move(variable_names)) {}
|
||||
|
||||
std::string destination;
|
||||
std::vector<std::string> variable_names;
|
||||
};
|
||||
|
||||
struct ReturnInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
bool IsBlockTerminator() const override { return true; }
|
||||
};
|
||||
|
||||
struct PrintConstantStringInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
explicit PrintConstantStringInstruction(std::string message) {
|
||||
// The normal way to write this triggers a bug in Clang on Windows.
|
||||
this->message = std::move(message);
|
||||
}
|
||||
|
||||
std::string message;
|
||||
};
|
||||
|
||||
struct DebugBreakInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
bool IsBlockTerminator() const override { return never_continues; }
|
||||
explicit DebugBreakInstruction(bool never_continues)
|
||||
: never_continues(never_continues) {}
|
||||
|
||||
bool never_continues;
|
||||
};
|
||||
|
||||
struct UnsafeCastInstruction : InstructionBase {
|
||||
TORQUE_INSTRUCTION_BOILERPLATE()
|
||||
explicit UnsafeCastInstruction(const Type* destination_type)
|
||||
: destination_type(destination_type) {}
|
||||
|
||||
const Type* destination_type;
|
||||
};
|
||||
|
||||
} // namespace torque
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_TORQUE_INSTRUCTIONS_H_
|
@ -293,28 +293,66 @@ bool operator<(const Type& a, const Type& b) {
|
||||
return a.MangledName() < b.MangledName();
|
||||
}
|
||||
|
||||
VisitResult::VisitResult(const Type* type, const Value* declarable)
|
||||
: type_(type), value_(), declarable_(declarable) {}
|
||||
|
||||
std::string VisitResult::LValue() const {
|
||||
return std::string("*") + (declarable_ ? (*declarable_)->value() : value_);
|
||||
VisitResult ProjectStructField(VisitResult structure,
|
||||
const std::string& fieldname) {
|
||||
DCHECK(structure.IsOnStack());
|
||||
BottomOffset begin = structure.stack_range().begin();
|
||||
const StructType* type = StructType::cast(structure.type());
|
||||
for (auto& field : type->fields()) {
|
||||
BottomOffset end = begin + LoweredSlotCount(field.type);
|
||||
if (field.name == fieldname) {
|
||||
return VisitResult(field.type, StackRange{begin, end});
|
||||
}
|
||||
begin = end;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
std::string VisitResult::RValue() const {
|
||||
std::string result;
|
||||
if (declarable()) {
|
||||
auto value = *declarable();
|
||||
if (value->IsVariable() && !Variable::cast(value)->IsDefined()) {
|
||||
std::stringstream s;
|
||||
s << "\"" << value->name() << "\" is used before it is defined";
|
||||
ReportError(s.str());
|
||||
namespace {
|
||||
void AppendLoweredTypes(const Type* type, std::vector<const Type*>* result) {
|
||||
DCHECK_NE(type, TypeOracle::GetNeverType());
|
||||
if (type->IsConstexpr()) return;
|
||||
if (type == TypeOracle::GetVoidType()) return;
|
||||
if (auto* s = StructType::DynamicCast(type)) {
|
||||
for (const NameAndType& field : s->fields()) {
|
||||
AppendLoweredTypes(field.type, result);
|
||||
}
|
||||
result = value->RValue();
|
||||
} else {
|
||||
result = value_;
|
||||
result->push_back(type);
|
||||
}
|
||||
return "implicit_cast<" + type()->GetGeneratedTypeName() + ">(" + result +
|
||||
")";
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TypeVector LowerType(const Type* type) {
|
||||
TypeVector result;
|
||||
AppendLoweredTypes(type, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t LoweredSlotCount(const Type* type) { return LowerType(type).size(); }
|
||||
|
||||
TypeVector LowerParameterTypes(const TypeVector& parameters) {
|
||||
std::vector<const Type*> result;
|
||||
for (const Type* t : parameters) {
|
||||
AppendLoweredTypes(t, &result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TypeVector LowerParameterTypes(const ParameterTypes& parameter_types,
|
||||
size_t arg_count) {
|
||||
std::vector<const Type*> result = LowerParameterTypes(parameter_types.types);
|
||||
for (size_t i = parameter_types.types.size(); i < arg_count; ++i) {
|
||||
DCHECK(parameter_types.var_args);
|
||||
AppendLoweredTypes(TypeOracle::GetObjectType(), &result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
VisitResult VisitResult::NeverResult() {
|
||||
VisitResult result;
|
||||
result.type_ = TypeOracle::GetNeverType();
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace torque
|
||||
|
@ -345,21 +345,34 @@ inline std::ostream& operator<<(std::ostream& os, const Type& t) {
|
||||
class VisitResult {
|
||||
public:
|
||||
VisitResult() = default;
|
||||
VisitResult(const Type* type, const std::string& value)
|
||||
: type_(type), value_(value), declarable_{} {}
|
||||
VisitResult(const Type* type, const Value* declarable);
|
||||
VisitResult(const Type* type, const std::string& constexpr_value)
|
||||
: type_(type), constexpr_value_(constexpr_value) {
|
||||
DCHECK(type->IsConstexpr());
|
||||
}
|
||||
static VisitResult NeverResult();
|
||||
VisitResult(const Type* type, StackRange stack_range)
|
||||
: type_(type), stack_range_(stack_range) {
|
||||
DCHECK(!type->IsConstexpr());
|
||||
}
|
||||
const Type* type() const { return type_; }
|
||||
base::Optional<const Value*> declarable() const { return declarable_; }
|
||||
std::string LValue() const;
|
||||
std::string RValue() const;
|
||||
const std::string& constexpr_value() const { return *constexpr_value_; }
|
||||
const StackRange& stack_range() const { return *stack_range_; }
|
||||
void SetType(const Type* new_type) { type_ = new_type; }
|
||||
bool IsOnStack() const { return stack_range_ != base::nullopt; }
|
||||
bool operator==(const VisitResult& other) const {
|
||||
return type_ == other.type_ && constexpr_value_ == other.constexpr_value_ &&
|
||||
stack_range_ == other.stack_range_;
|
||||
}
|
||||
|
||||
private:
|
||||
const Type* type_ = nullptr;
|
||||
std::string value_;
|
||||
base::Optional<const Value*> declarable_;
|
||||
base::Optional<std::string> constexpr_value_;
|
||||
base::Optional<StackRange> stack_range_;
|
||||
};
|
||||
|
||||
VisitResult ProjectStructField(VisitResult structure,
|
||||
const std::string& fieldname);
|
||||
|
||||
class VisitResultVector : public std::vector<VisitResult> {
|
||||
public:
|
||||
VisitResultVector() : std::vector<VisitResult>() {}
|
||||
@ -420,6 +433,12 @@ bool IsAssignableFrom(const Type* to, const Type* from);
|
||||
bool IsCompatibleSignature(const Signature& sig, const TypeVector& types,
|
||||
const std::vector<Label*>& labels);
|
||||
|
||||
TypeVector LowerType(const Type* type);
|
||||
size_t LoweredSlotCount(const Type* type);
|
||||
TypeVector LowerParameterTypes(const TypeVector& parameters);
|
||||
TypeVector LowerParameterTypes(const ParameterTypes& parameter_types,
|
||||
size_t vararg_count = 0);
|
||||
|
||||
} // namespace torque
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "src/base/logging.h"
|
||||
#include "src/torque/ast.h"
|
||||
#include "src/torque/utils.h"
|
||||
|
||||
@ -78,9 +79,9 @@ std::string CurrentPositionAsString() {
|
||||
|
||||
DEFINE_CONTEXTUAL_VARIABLE(LintErrorStatus)
|
||||
|
||||
[[noreturn]] void ReportError(const std::string& error) {
|
||||
[[noreturn]] void ReportErrorString(const std::string& error) {
|
||||
std::cerr << CurrentPositionAsString() << ": Torque error: " << error << "\n";
|
||||
std::abort();
|
||||
v8::base::OS::Abort();
|
||||
}
|
||||
|
||||
void LintError(const std::string& error) {
|
||||
|
@ -32,7 +32,6 @@ class LintErrorStatus : public ContextualClass<LintErrorStatus> {
|
||||
bool has_lint_errors_;
|
||||
};
|
||||
|
||||
[[noreturn]] void ReportError(const std::string& error);
|
||||
void LintError(const std::string& error);
|
||||
|
||||
// Prints a LintError with the format "{type} '{name}' doesn't follow
|
||||
@ -46,6 +45,14 @@ bool IsSnakeCase(const std::string& s);
|
||||
bool IsValidModuleConstName(const std::string& s);
|
||||
bool IsValidTypeName(const std::string& s);
|
||||
|
||||
[[noreturn]] void ReportErrorString(const std::string& error);
|
||||
template <class... Args>
|
||||
[[noreturn]] void ReportError(Args&&... args) {
|
||||
std::stringstream s;
|
||||
USE((s << std::forward<Args>(args))...);
|
||||
ReportErrorString(s.str());
|
||||
}
|
||||
|
||||
std::string CamelifyString(const std::string& underscore_string);
|
||||
std::string DashifyString(const std::string& underscore_string);
|
||||
|
||||
@ -106,6 +113,149 @@ void PrintCommaSeparatedList(std::ostream& os, const T& list) {
|
||||
}
|
||||
}
|
||||
|
||||
struct BottomOffset {
|
||||
size_t offset;
|
||||
BottomOffset& operator++() {
|
||||
++offset;
|
||||
return *this;
|
||||
}
|
||||
BottomOffset operator+(size_t x) const { return BottomOffset{offset + x}; }
|
||||
BottomOffset operator-(size_t x) const {
|
||||
DCHECK_LE(x, offset);
|
||||
return BottomOffset{offset - x};
|
||||
}
|
||||
bool operator<(const BottomOffset& other) const {
|
||||
return offset < other.offset;
|
||||
}
|
||||
bool operator<=(const BottomOffset& other) const {
|
||||
return offset <= other.offset;
|
||||
}
|
||||
bool operator==(const BottomOffset& other) const {
|
||||
return offset == other.offset;
|
||||
}
|
||||
bool operator!=(const BottomOffset& other) const {
|
||||
return offset != other.offset;
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, BottomOffset from_bottom) {
|
||||
return out << "BottomOffset{" << from_bottom.offset << "}";
|
||||
}
|
||||
|
||||
// An iterator-style range of stack slots.
|
||||
class StackRange {
|
||||
public:
|
||||
StackRange(BottomOffset begin, BottomOffset end) : begin_(begin), end_(end) {
|
||||
DCHECK_LE(begin_, end_);
|
||||
}
|
||||
|
||||
bool operator==(const StackRange& other) const {
|
||||
return begin_ == other.begin_ && end_ == other.end_;
|
||||
}
|
||||
|
||||
void Extend(StackRange adjacent) {
|
||||
DCHECK_EQ(end_, adjacent.begin_);
|
||||
end_ = adjacent.end_;
|
||||
}
|
||||
|
||||
size_t Size() const { return end_.offset - begin_.offset; }
|
||||
BottomOffset begin() const { return begin_; }
|
||||
BottomOffset end() const { return end_; }
|
||||
|
||||
private:
|
||||
BottomOffset begin_;
|
||||
BottomOffset end_;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class Stack {
|
||||
public:
|
||||
using value_type = T;
|
||||
Stack() = default;
|
||||
Stack(std::initializer_list<T> initializer)
|
||||
: Stack(std::vector<T>(initializer)) {}
|
||||
explicit Stack(std::vector<T> v) : elements_(std::move(v)) {}
|
||||
size_t Size() const { return elements_.size(); }
|
||||
const T& Peek(BottomOffset from_bottom) const {
|
||||
return elements_.at(from_bottom.offset);
|
||||
}
|
||||
void Poke(BottomOffset from_bottom, T x) {
|
||||
elements_.at(from_bottom.offset) = std::move(x);
|
||||
}
|
||||
void Push(T x) { elements_.push_back(std::move(x)); }
|
||||
StackRange TopRange(size_t slot_count) const {
|
||||
DCHECK_GE(Size(), slot_count);
|
||||
return StackRange{AboveTop() - slot_count, AboveTop()};
|
||||
}
|
||||
StackRange PushMany(const std::vector<T>& v) {
|
||||
for (const T& x : v) {
|
||||
Push(x);
|
||||
}
|
||||
return TopRange(v.size());
|
||||
}
|
||||
const T& Top() const { return Peek(AboveTop() - 1); }
|
||||
T Pop() {
|
||||
T result = std::move(elements_.back());
|
||||
elements_.pop_back();
|
||||
return result;
|
||||
}
|
||||
std::vector<T> PopMany(size_t count) {
|
||||
DCHECK_GE(elements_.size(), count);
|
||||
std::vector<T> result;
|
||||
result.reserve(count);
|
||||
for (auto it = elements_.end() - count; it != elements_.end(); ++it) {
|
||||
result.push_back(std::move(*it));
|
||||
}
|
||||
elements_.resize(elements_.size() - count);
|
||||
return result;
|
||||
}
|
||||
// The invalid offset above the top element. This is useful for StackRange.
|
||||
BottomOffset AboveTop() const { return BottomOffset{Size()}; }
|
||||
// Delete the slots in {range}, moving higher slots to fill the gap.
|
||||
void DeleteRange(StackRange range) {
|
||||
DCHECK_LE(range.end(), AboveTop());
|
||||
for (BottomOffset i = range.begin();
|
||||
i < std::min(range.end(), AboveTop() - range.Size()); ++i) {
|
||||
elements_[i.offset] = std::move(elements_[i.offset + range.Size()]);
|
||||
}
|
||||
elements_.resize(elements_.size() - range.Size());
|
||||
}
|
||||
|
||||
bool operator==(const Stack& other) const {
|
||||
return elements_ == other.elements_;
|
||||
}
|
||||
bool operator!=(const Stack& other) const {
|
||||
return elements_ != other.elements_;
|
||||
}
|
||||
|
||||
T* begin() { return elements_.data(); }
|
||||
T* end() { return begin() + elements_.size(); }
|
||||
const T* begin() const { return elements_.data(); }
|
||||
const T* end() const { return begin() + elements_.size(); }
|
||||
|
||||
private:
|
||||
std::vector<T> elements_;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
T* CheckNotNull(T* x) {
|
||||
CHECK_NOT_NULL(x);
|
||||
return x;
|
||||
}
|
||||
|
||||
class ToString {
|
||||
public:
|
||||
template <class T>
|
||||
ToString& operator<<(T&& x) {
|
||||
s_ << std::forward<T>(x);
|
||||
return *this;
|
||||
}
|
||||
operator std::string() { return s_.str(); }
|
||||
|
||||
private:
|
||||
std::stringstream s_;
|
||||
};
|
||||
|
||||
} // namespace torque
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
// Copyright 2012 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.
|
||||
|
@ -143,11 +143,12 @@ module test {
|
||||
|
||||
GenericMacroTestWithLabels<Object>(param2: Object): Object
|
||||
labels Y {
|
||||
return param2;
|
||||
return Cast<Smi>(param2) otherwise Y;
|
||||
}
|
||||
|
||||
macro TestMacroSpecialization() {
|
||||
try {
|
||||
const smi0: Smi = 0;
|
||||
check(GenericMacroTest<Smi>(0) == Undefined);
|
||||
check(GenericMacroTest<Smi>(1) == Undefined);
|
||||
check(GenericMacroTest<Object>(Null) == Null);
|
||||
@ -155,8 +156,11 @@ module test {
|
||||
check(GenericMacroTest<Object>(True) == True);
|
||||
check(GenericMacroTestWithLabels<Smi>(0) otherwise Fail == Undefined);
|
||||
check(GenericMacroTestWithLabels<Smi>(0) otherwise Fail == Undefined);
|
||||
check(GenericMacroTestWithLabels<Object>(Null) otherwise Fail == Null);
|
||||
check(GenericMacroTestWithLabels<Object>(False) otherwise Fail == False);
|
||||
check(GenericMacroTestWithLabels<Object>(smi0) otherwise Fail == smi0);
|
||||
try {
|
||||
GenericMacroTestWithLabels<Object>(False) otherwise Expected;
|
||||
}
|
||||
label Expected {}
|
||||
}
|
||||
label Fail {
|
||||
unreachable;
|
||||
|
Loading…
Reference in New Issue
Block a user