[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:
Tobias Tebbi 2018-10-04 22:45:31 +02:00 committed by Commit Bot
parent 42f17e7d95
commit a4008bf009
27 changed files with 2718 additions and 1103 deletions

View File

@ -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",

View File

@ -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

View File

@ -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) {

View File

@ -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
View 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
View 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
View 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(&current_stack_, &cfg_);
current_block_->Add(std::move(instruction));
}
const Stack<const Type*>& CurrentStack() const { return current_stack_; }
StackRange TopRange(size_t slot_count) const {
return CurrentStack().TopRange(slot_count);
}
void Bind(Block* block);
void Goto(Block* block);
// Goto block while keeping {preserved_slots} many slots on the top and
// deleting additional the slots below these to match the input type of the
// target block.
// Returns the StackRange of the preserved slots in the target block.
StackRange Goto(Block* block, size_t preserved_slots);
// The condition must be of type bool and on the top of stack. It is removed
// from the stack before branching.
void Branch(Block* if_true, Block* if_false);
// Delete the specified range of slots, moving upper slots to fill the gap.
void DeleteRange(StackRange range);
void DropTo(BottomOffset new_level);
StackRange Peek(StackRange range, base::Optional<const Type*> type);
void Poke(StackRange destination, StackRange origin,
base::Optional<const Type*> type);
void Print(std::string s);
void 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
View 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()}, &parameters);
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

View 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_

View File

@ -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) {

View File

@ -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 {

View File

@ -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()) {

View File

@ -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

View File

@ -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));

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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
View 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
View 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_

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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.

View 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;