Commit a4008bf0 authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

[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: 's avatarDaniel Clifford <danno@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56391}
parent 42f17e7d
......@@ -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,
......
// 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_
// 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
// 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_
// 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
// 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(); }
......
......@@ -4,6 +4,7 @@
#include <algorithm>
#include "src/torque/csa-generator.h"
#include "src/torque/implementation-visitor.h"
#include "src/torque/parameter-difference.h"
......@@ -26,19 +27,20 @@ VisitResult ImplementationVisitor::Visit(Expression* expr) {
const Type* ImplementationVisitor::Visit(Statement* stmt) {
CurrentSourcePosition::Scope scope(stmt->pos);
GenerateIndent();
source_out() << "// " << CurrentPositionAsString() << "\n";
const Type* result;
switch (stmt->kind) {
#define ENUM_ITEM(name) \
case AstNode::Kind::k##name: \
return Visit(name::cast(stmt));
#define ENUM_ITEM(name) \
case AstNode::Kind::k##name: \
result = Visit(name::cast(stmt)); \
break;
AST_STATEMENT_NODE_KIND_LIST(ENUM_ITEM)
#undef ENUM_ITEM
default:
UNIMPLEMENTED();
UNREACHABLE();
}
UNREACHABLE();
return nullptr;
DCHECK_EQ(result == TypeOracle::GetNeverType(),
assembler().CurrentBlockIsComplete());
return result;
}
void ImplementationVisitor::Visit(Declaration* decl) {
......@@ -72,7 +74,7 @@ void ImplementationVisitor::BeginModuleFile(Module* module) {
std::ostream& header = module->header_stream();
if (module->IsDefault()) {
source << "#include \"src/code-stub-assembler.h\"";
source << "#include \"src/torque-assembler.h\"";
} else {
source << "#include \"src/builtins/builtins-" +
DashifyString(module->name()) + "-gen.h\"";
......@@ -103,7 +105,7 @@ void ImplementationVisitor::BeginModuleFile(Module* module) {
header << "#ifndef " << headerDefine << "\n";
header << "#define " << headerDefine << "\n\n";
if (module->IsDefault()) {
header << "#include \"src/code-stub-assembler.h\"";
header << "#include \"src/torque-assembler.h\"";
} else {
header << "#include \"src/builtins/builtins-" +
DashifyString(module->name()) + "-gen.h\"\n";
......@@ -176,12 +178,20 @@ void ImplementationVisitor::Visit(ConstDeclaration* decl) {
DCHECK(!signature.return_type->IsVoidOrNever());
assembler_ = CfgAssembler(Stack<const Type*>{});
VisitResult expression_result = Visit(decl->expression);
VisitResult return_result =
GenerateImplicitConvert(signature.return_type, expression_result);
GenerateIndent();
source_out() << "return " << return_result.RValue() << ";\n";
CSAGenerator csa_generator{assembler().Result(), source_out()};
Stack<std::string> values = *csa_generator.EmitGraph(Stack<std::string>{});
assembler_ = base::nullopt;
source_out() << "return ";
CSAGenerator::EmitCSAValue(return_result, values, source_out());
source_out() << ";\n";
source_out() << "}\n\n";
}
......@@ -193,13 +203,41 @@ void ImplementationVisitor::Visit(StructDeclaration* decl) {
header_out() << " " << field.type->GetGeneratedTypeName();
header_out() << " " << field.name << ";\n";
}
header_out() << " } "
<< ";\n";
header_out() << "\n std::tuple<";
bool first = true;
for (const Type* type : LowerType(struct_type)) {
if (!first) {
header_out() << ", ";
}
first = false;
header_out() << type->GetGeneratedTypeName();
}
header_out() << "> Flatten() const {\n"
<< " return std::tuple_cat(";
first = true;
for (auto& field : struct_type->fields()) {
if (!first) {
header_out() << ", ";
}
first = false;
if (field.type->IsStructType()) {
header_out() << field.name << ".Flatten()";
} else {
header_out() << "std::make_tuple(" << field.name << ")";
}
}
header_out() << ");\n";
header_out() << " }\n";
header_out() << " };\n";
}
void ImplementationVisitor::Visit(TorqueMacroDeclaration* decl,
const Signature& sig, Statement* body) {
Signature signature = MakeSignature(decl->signature.get());
const Type* return_type = signature.return_type;
bool can_return = return_type != TypeOracle::GetNeverType();
bool has_return_value =
can_return && return_type != TypeOracle::GetVoidType();
std::string name = GetGeneratedCallableName(
decl->name, declarations()->GetCurrentSpecializationTypeNamesVector());
const TypeVector& list = signature.types();
......@@ -216,15 +254,43 @@ void ImplementationVisitor::Visit(TorqueMacroDeclaration* decl,
source_out(), GetDSLAssemblerName(CurrentModule()) + "::", macro);
source_out() << " {\n";
const Variable* result_var = nullptr;
if (macro->HasReturnValue()) {
result_var =
GeneratePredeclaredVariableDeclaration(kReturnValueVariable, {});
Stack<std::string> lowered_parameters;
Stack<const Type*> lowered_parameter_types;
for (const std::string& name : macro->parameter_names()) {
Parameter* parameter = Parameter::cast(declarations()->LookupValue(name));
const Type* type = parameter->type();
if (type->IsConstexpr()) {
parameter->set_value(
VisitResult(parameter->type(), parameter->external_name()));
} else {
LowerParameter(type, parameter->external_name(), &lowered_parameters);
StackRange range = lowered_parameter_types.PushMany(LowerType(type));
parameter->set_value(VisitResult(type, range));
}
}
DCHECK_EQ(lowered_parameters.Size(), lowered_parameter_types.Size());
assembler_ = CfgAssembler(lowered_parameter_types);
for (const LabelDeclaration& label_info : sig.labels) {
Label* label = declarations()->LookupLabel(label_info.name);
Stack<const Type*> label_input_stack;
for (Variable* v : label->GetParameters()) {
label_input_stack.PushMany(LowerType(v->type()));
}
CreateBlockForLabel(label, label_input_stack);
}
Label* macro_end = declarations()->DeclareLabel("macro_end");
GenerateLabelDefinition(macro_end, decl);
if (can_return) {
Stack<const Type*> result_stack;
CreateBlockForLabel(macro_end,
Stack<const Type*>{LowerType(signature.return_type)});
}
const Type* result = Visit(body);
if (result->IsNever()) {
if (!macro->signature().return_type->IsNever() && !macro->HasReturns()) {
std::stringstream s;
......@@ -246,23 +312,54 @@ void ImplementationVisitor::Visit(TorqueMacroDeclaration* decl,
ReportError(s.str());
}
}
if (macro->HasReturns()) {
if (!result->IsNever()) {
GenerateLabelGoto(macro_end);
if (!result->IsNever()) {
GenerateLabelGoto(macro_end);
}
for (const LabelDeclaration& label_info : sig.labels) {
Label* label = declarations()->LookupLabel(label_info.name);
GenerateLabelBind(label);
std::vector<std::string> label_parameter_variables;
for (size_t i = 0; i < label->GetParameterCount(); ++i) {
label_parameter_variables.push_back(
ExternalLabelParameterName(label, i));
}
assembler().Emit(GotoExternalInstruction{label->external_label_name(),
label_parameter_variables});
}
if (macro->HasReturns() || !result->IsNever()) {
GenerateLabelBind(macro_end);
}
if (result_var != nullptr) {
GenerateIndent();
source_out() << "return "
<< RValueFlattenStructs(
VisitResult(result_var->type(), result_var))
<< ";\n";
CSAGenerator csa_generator{assembler().Result(), source_out()};
base::Optional<Stack<std::string>> values =
csa_generator.EmitGraph(lowered_parameters);
assembler_ = base::nullopt;
if (has_return_value) {
source_out() << " return ";
CSAGenerator::EmitCSAValue(GetAndClearReturnValue(), *values,
source_out());
source_out() << ";\n";
}
source_out() << "}\n\n";
}
}
namespace {
std::string AddParameter(Value* parameter, size_t i,
Stack<std::string>* parameters,
Stack<const Type*>* parameter_types) {
std::string name = "parameter" + std::to_string(i);
parameters->Push(name);
StackRange range = parameter_types->PushMany(LowerType(parameter->type()));
parameter->set_value(VisitResult(parameter->type(), range));
return name;
}
} // namespace
void ImplementationVisitor::Visit(TorqueBuiltinDeclaration* decl,
const Signature& signature, Statement* body) {
std::string name = GetGeneratedCallableName(
......@@ -272,15 +369,17 @@ void ImplementationVisitor::Visit(TorqueBuiltinDeclaration* decl,
Builtin* builtin = declarations()->LookupBuiltin(name);
CurrentCallableActivator activator(global_context_, builtin, decl);
Stack<const Type*> parameter_types;
Stack<std::string> parameters;
// Context
const Value* val =
Value* val =
declarations()->LookupValue(decl->signature->parameters.names[0]);
GenerateIndent();
source_out() << "TNode<Context> " << val->value()
std::string parameter0 = AddParameter(val, 0, &parameters, &parameter_types);
source_out() << " TNode<Context> " << parameter0
<< " = UncheckedCast<Context>(Parameter("
<< "Descriptor::kContext));\n";
GenerateIndent();
source_out() << "USE(" << val->value() << ");\n";
source_out() << " USE(" << parameter0 << ");\n";
size_t first = 1;
if (builtin->IsVarArgsJavaScript()) {
......@@ -288,41 +387,82 @@ void ImplementationVisitor::Visit(TorqueBuiltinDeclaration* decl,
ExternConstant* arguments =
ExternConstant::cast(declarations()->LookupValue(
decl->signature->parameters.arguments_variable));
std::string arguments_name = arguments->value();
GenerateIndent();
std::string arguments_name = arguments->value().constexpr_value();
source_out()
<< "Node* argc = Parameter(Descriptor::kJSActualArgumentsCount);\n";
GenerateIndent();
source_out() << "CodeStubArguments arguments_impl(this, "
<< " Node* argc = Parameter(Descriptor::kJSActualArgumentsCount);\n";
source_out() << " CodeStubArguments arguments_impl(this, "
"ChangeInt32ToIntPtr(argc));\n";
const Value* receiver =
Value* receiver =
declarations()->LookupValue(decl->signature->parameters.names[1]);
GenerateIndent();
source_out() << "TNode<Object> " << receiver->value()
std::string parameter1 =
AddParameter(receiver, 1, &parameters, &parameter_types);
source_out() << " TNode<Object> " << parameter1
<< " = arguments_impl.GetReceiver();\n";
GenerateIndent();
source_out() << "auto arguments = &arguments_impl;\n";
GenerateIndent();
source_out() << "auto " << CSAGenerator::ARGUMENTS_VARIABLE_STRING
<< " = &arguments_impl;\n";
source_out() << "USE(arguments);\n";
GenerateIndent();
source_out() << "USE(" << receiver->value() << ");\n";
source_out() << "USE(" << parameter1 << ");\n";
first = 2;
}
GenerateParameterList(decl->signature->parameters.names, first);
Visit(body);
for (size_t i = 0; i < decl->signature->parameters.names.size(); ++i) {
if (i < first) continue;
const std::string& parameter_name = decl->signature->parameters.names[i];
Value* parameter = declarations()->LookupValue(parameter_name);
std::string var = AddParameter(parameter, i, &parameters, &parameter_types);
source_out() << " " << parameter->type()->GetGeneratedTypeName() << " "
<< var << " = "
<< "UncheckedCast<"
<< parameter->type()->GetGeneratedTNodeTypeName()
<< ">(Parameter(Descriptor::k"
<< CamelifyString(parameter_name) << "));\n";
source_out() << " USE(" << var << ");\n";
}
assembler_ = CfgAssembler(parameter_types);
const Type* body_result = Visit(body);
if (body_result != TypeOracle::GetNeverType()) {
ReportError("control reaches end of builtin, expected return of a value");
}
CSAGenerator csa_generator{assembler().Result(), source_out(),
builtin->kind()};
csa_generator.EmitGraph(parameters);
assembler_ = base::nullopt;
source_out() << "}\n\n";
}
const Type* ImplementationVisitor::Visit(VarDeclarationStatement* stmt) {
base::Optional<const Type*> type;
if (stmt->type) type = declarations()->GetType(*stmt->type);
base::Optional<VisitResult> init_result;
if (stmt->initializer) {
StackScope scope(this);
init_result = Visit(*stmt->initializer);
if (type) {
init_result = GenerateImplicitConvert(*type, *init_result);
}
init_result = scope.Yield(*init_result);
} else {
DCHECK(type.has_value());
if ((*type)->IsConstexpr()) {
ReportError("constexpr variables need an initializer");
}
TypeVector lowered_types = LowerType(*type);
for (const Type* type : lowered_types) {
assembler().Emit(PushUninitializedInstruction{type});
}
init_result =
VisitResult(*type, assembler().TopRange(lowered_types.size()));
}
base::Optional<const Type*> type;
if (stmt->type) type = declarations()->GetType(*stmt->type);
GenerateVariableDeclaration(stmt, stmt->name, stmt->const_qualified, type,
init_result);
Variable* var;
if (stmt->const_qualified) {
var = declarations()->DeclareVariable(stmt->name, init_result->type(),
stmt->const_qualified);
} else {
var = Variable::cast(declarations()->LookupValue(stmt->name));
}
var->set_value(*init_result);
return TypeOracle::GetVoidType();
}
......@@ -331,72 +471,61 @@ const Type* ImplementationVisitor::Visit(TailCallStatement* stmt) {
}
VisitResult ImplementationVisitor::Visit(ConditionalExpression* expr) {
std::string f1 = NewTempVariable();
std::string f2 = NewTempVariable();
// The code for both paths of the conditional need to be generated first in
// lambdas before evaluating the conditional expression because the common
// type of the result of both the true and false of the condition needs to be
// known when declaring the variable to hold the result of the conditional.
VisitResult left, right;
GenerateIndent();
source_out() << "auto " << f1 << " = [=]() ";
{
ScopedIndent indent(this, false);
source_out() << "\n";
left = Visit(expr->if_true);
GenerateIndent();
source_out() << "return " << RValueFlattenStructs(left) << ";\n";
}
source_out() << ";\n";
GenerateIndent();
source_out() << "auto " << f2 << " = [=]() ";
Label* true_label;
Label* false_label;
Block* done_block = assembler().NewBlock();
Block* true_conversion_block = assembler().NewBlock();
{
ScopedIndent indent(this, false);
source_out() << "\n";
right = Visit(expr->if_false);
GenerateIndent();
source_out() << "return " << RValueFlattenStructs(right) << ";\n";
Declarations::NodeScopeActivator scope(declarations(), expr->condition);
true_label = declarations()->LookupLabel(kTrueLabelName);
CreateBlockForLabel(true_label, assembler().CurrentStack());
false_label = declarations()->LookupLabel(kFalseLabelName);
CreateBlockForLabel(false_label, assembler().CurrentStack());
done_block = assembler().NewBlock();
{
StackScope condition_scope(this);
VisitResult condition_result = Visit(expr->condition);
if (!condition_result.type()->IsNever()) {
condition_result = condition_scope.Yield(GenerateImplicitConvert(
TypeOracle::GetBoolType(), condition_result));
assembler().Branch(true_label->block(), false_label->block());
}
}
}
source_out() << ";\n";
const Type* common_type = GetCommonType(left.type(), right.type());
std::string result_var = NewTempVariable();
Variable* result =
GenerateVariableDeclaration(expr, result_var, false, common_type);
VisitResult left;
VisitResult right;
{
ScopedIndent indent(this);
Declarations::NodeScopeActivator scope(declarations(), expr->condition);
// The code for both paths of the conditional need to be generated first
// before evaluating the conditional expression because the common type of
// the result of both the true and false of the condition needs to be known
// to convert both branches to a common type.
assembler().Bind(true_label->block());
StackScope left_scope(this);
left = Visit(expr->if_true);
assembler().Goto(true_conversion_block);
Label* true_label = declarations()->LookupLabel(kTrueLabelName);
GenerateLabelDefinition(true_label);
Label* false_label = declarations()->LookupLabel(kFalseLabelName);
GenerateLabelDefinition(false_label);
Label* done_label = declarations()->DeclarePrivateLabel(kDoneLabelName);
GenerateLabelDefinition(done_label, expr);
VisitResult condition_result = Visit(expr->condition);
if (!condition_result.type()->IsNever()) {
condition_result =
GenerateImplicitConvert(TypeOracle::GetBoolType(), condition_result);
GenerateBranch(condition_result, true_label, false_label);
const Type* common_type;
{
assembler().Bind(false_label->block());
StackScope right_scope(this);
right = Visit(expr->if_false);
common_type = GetCommonType(left.type(), right.type());
right = right_scope.Yield(GenerateImplicitConvert(common_type, right));
assembler().Goto(done_block);
}
GenerateLabelBind(true_label);
GenerateIndent();
VisitResult left_result = {right.type(), f1 + "()"};
GenerateAssignToVariable(result, left_result);
GenerateLabelGoto(done_label);
GenerateLabelBind(false_label);
GenerateIndent();
VisitResult right_result = {right.type(), f2 + "()"};
GenerateAssignToVariable(result, right_result);
GenerateLabelGoto(done_label);
GenerateLabelBind(done_label);
assembler().Bind(true_conversion_block);
left = left_scope.Yield(GenerateImplicitConvert(common_type, left));
assembler().Goto(done_block);
}
return VisitResult(common_type, result);
assembler().Bind(done_block);
CHECK_EQ(left, right);
return left;
}
VisitResult ImplementationVisitor::Visit(LogicalOrExpression* expr) {
......@@ -404,15 +533,14 @@ VisitResult ImplementationVisitor::Visit(LogicalOrExpression* expr) {
{
Declarations::NodeScopeActivator scope(declarations(), expr->left);
Label* false_label = declarations()->LookupLabel(kFalseLabelName);
GenerateLabelDefinition(false_label);
CreateBlockForLabel(false_label, assembler().CurrentStack());
left_result = Visit(expr->left);
if (left_result.type()->IsBool()) {
Label* true_label = declarations()->LookupLabel(kTrueLabelName);
GenerateIndent();
source_out() << "GotoIf(" << RValueFlattenStructs(left_result) << ", "
<< true_label->generated() << ");\n";
assembler().Branch(true_label->block(), false_label->block());
assembler().Bind(false_label->block());
} else if (left_result.type()->IsNever()) {
GenerateLabelBind(false_label);
assembler().Bind(false_label->block());
} else if (!left_result.type()->IsConstexprBool()) {
ReportError(
"expected type bool, constexpr bool, or never on left-hand side of "
......@@ -427,19 +555,17 @@ VisitResult ImplementationVisitor::Visit(LogicalOrExpression* expr) {
"expected type constexpr bool on right-hand side of operator "
"||");
}
return VisitResult(left_result.type(),
std::string("(") + RValueFlattenStructs(left_result) +
" || " + RValueFlattenStructs(right_result) + ")");
return VisitResult(TypeOracle::GetConstexprBoolType(),
std::string("(") + left_result.constexpr_value() +
" || " + right_result.constexpr_value() + ")");
}
VisitResult right_result = Visit(expr->right);
if (right_result.type()->IsBool()) {
Label* true_label = declarations()->LookupLabel(kTrueLabelName);
Label* false_label = declarations()->LookupLabel(kFalseLabelName);
source_out() << "Branch(" << RValueFlattenStructs(right_result) << ", "
<< true_label->generated() << ", " << false_label->generated()
<< ");\n";
return VisitResult(TypeOracle::GetNeverType(), "/*never*/");
assembler().Branch(true_label->block(), false_label->block());
return VisitResult::NeverResult();
} else if (!right_result.type()->IsNever()) {
ReportError(
"expected type bool or never on right-hand side of operator ||");
......@@ -452,15 +578,14 @@ VisitResult ImplementationVisitor::Visit(LogicalAndExpression* expr) {
{
Declarations::NodeScopeActivator scope(declarations(), expr->left);
Label* true_label = declarations()->LookupLabel(kTrueLabelName);
GenerateLabelDefinition(true_label);
CreateBlockForLabel(true_label, assembler().CurrentStack());
left_result = Visit(expr->left);
if (left_result.type()->IsBool()) {
Label* false_label = declarations()->LookupLabel(kFalseLabelName);
GenerateIndent();
source_out() << "GotoIfNot(" << RValueFlattenStructs(left_result) << ", "
<< false_label->generated() << ");\n";
assembler().Branch(true_label->block(), false_label->block());
assembler().Bind(true_label->block());
} else if (left_result.type()->IsNever()) {
GenerateLabelBind(true_label);
assembler().Bind(true_label->block());
} else if (!left_result.type()->IsConstexprBool()) {
ReportError(
"expected type bool, constexpr bool, or never on left-hand side of "
......@@ -475,19 +600,17 @@ VisitResult ImplementationVisitor::Visit(LogicalAndExpression* expr) {
"expected type constexpr bool on right-hand side of operator "
"&&");
}
return VisitResult(left_result.type(),
std::string("(") + RValueFlattenStructs(left_result) +
" && " + RValueFlattenStructs(right_result) + ")");
return VisitResult(TypeOracle::GetConstexprBoolType(),
std::string("(") + left_result.constexpr_value() +
" && " + right_result.constexpr_value() + ")");
}
VisitResult right_result = Visit(expr->right);
if (right_result.type()->IsBool()) {
Label* true_label = declarations()->LookupLabel(kTrueLabelName);
Label* false_label = declarations()->LookupLabel(kFalseLabelName);
source_out() << "Branch(" << RValueFlattenStructs(right_result) << ", "
<< true_label->generated() << ", " << false_label->generated()
<< ");\n";
return VisitResult(TypeOracle::GetNeverType(), "/*never*/");
assembler().Branch(true_label->block(), false_label->block());
return VisitResult::NeverResult();
} else if (!right_result.type()->IsNever()) {
ReportError(
"expected type bool or never on right-hand side of operator &&");
......@@ -496,38 +619,34 @@ VisitResult ImplementationVisitor::Visit(LogicalAndExpression* expr) {
}
VisitResult ImplementationVisitor::Visit(IncrementDecrementExpression* expr) {
VisitResult value_copy;
auto location_ref = GetLocationReference(expr->location);
VisitResult current_value =
GenerateFetchFromLocation(expr->location, location_ref);
if (expr->postfix) {
value_copy = GenerateCopy(current_value);
}
StackScope scope(this);
LocationReference location_ref = GetLocationReference(expr->location);
VisitResult current_value = GenerateFetchFromLocation(location_ref);
VisitResult one = {TypeOracle::GetConstInt31Type(), "1"};
Arguments args;
args.parameters = {current_value, one};
VisitResult assignment_value = GenerateCall(
expr->op == IncrementDecrementOperator::kIncrement ? "+" : "-", args);
GenerateAssignToLocation(expr->location, location_ref, assignment_value);
return expr->postfix ? value_copy : assignment_value;
GenerateAssignToLocation(location_ref, assignment_value);
return scope.Yield(expr->postfix ? current_value : assignment_value);
}
VisitResult ImplementationVisitor::Visit(AssignmentExpression* expr) {
StackScope scope(this);
LocationReference location_ref = GetLocationReference(expr->location);
VisitResult assignment_value;
if (expr->op) {
VisitResult location_value =
GenerateFetchFromLocation(expr->location, location_ref);
VisitResult location_value = GenerateFetchFromLocation(location_ref);
assignment_value = Visit(expr->value);
Arguments args;
args.parameters = {location_value, assignment_value};
assignment_value = GenerateCall(*expr->op, args);
GenerateAssignToLocation(expr->location, location_ref, assignment_value);
GenerateAssignToLocation(location_ref, assignment_value);
} else {
assignment_value = Visit(expr->value);
GenerateAssignToLocation(expr->location, location_ref, assignment_value);
GenerateAssignToLocation(location_ref, assignment_value);
}
return assignment_value;
return scope.Yield(assignment_value);
}
VisitResult ImplementationVisitor::Visit(NumberLiteralExpression* expr) {
......@@ -543,9 +662,7 @@ VisitResult ImplementationVisitor::Visit(NumberLiteralExpression* expr) {
result_type = declarations()->LookupType(CONST_INT32_TYPE_STRING);
}
}
std::string temp = GenerateNewTempVariable(result_type);
source_out() << expr->number << ";\n";
return VisitResult{result_type, temp};
return VisitResult{result_type, expr->number};
}
VisitResult ImplementationVisitor::Visit(AssumeTypeImpossibleExpression* expr) {
......@@ -555,16 +672,16 @@ VisitResult ImplementationVisitor::Visit(AssumeTypeImpossibleExpression* expr) {
if (result_type->IsNever()) {
ReportError("unreachable code");
}
return VisitResult{result_type, "UncheckedCast<" +
result_type->GetGeneratedTNodeTypeName() +
">(" + result.RValue() + ")"};
CHECK_EQ(LowerType(result_type), TypeVector{result_type});
assembler().Emit(UnsafeCastInstruction{result_type});
result.SetType(result_type);
return result;
}
VisitResult ImplementationVisitor::Visit(StringLiteralExpression* expr) {
std::string temp = GenerateNewTempVariable(TypeOracle::GetConstStringType());
source_out() << "\"" << expr->literal.substr(1, expr->literal.size() - 2)
<< "\";\n";
return VisitResult{TypeOracle::GetConstStringType(), temp};
return VisitResult{
TypeOracle::GetConstStringType(),
"\"" + expr->literal.substr(1, expr->literal.size() - 2) + "\""};
}
VisitResult ImplementationVisitor::GetBuiltinCode(Builtin* builtin) {
......@@ -576,13 +693,12 @@ VisitResult ImplementationVisitor::GetBuiltinCode(Builtin* builtin) {
const Type* type = TypeOracle::GetFunctionPointerType(
builtin->signature().parameter_types.types,
builtin->signature().return_type);
std::string code =
"HeapConstant(Builtins::CallableFor(isolate(), Builtins::k" +
builtin->name() + ").code())";
return VisitResult(type, code);
assembler().Emit(PushCodePointerInstruction{builtin->name(), type});
return VisitResult(type, assembler().TopRange(1));
}
VisitResult ImplementationVisitor::Visit(IdentifierExpression* expr) {
StackScope scope(this);
std::string name = expr->name;
if (expr->generic_arguments.size() != 0) {
GenericList* generic_list = declarations()->LookupGeneric(expr->name);
......@@ -597,10 +713,10 @@ VisitResult ImplementationVisitor::Visit(IdentifierExpression* expr) {
}
if (Builtin* builtin = Builtin::DynamicCast(declarations()->Lookup(name))) {
return GetBuiltinCode(builtin);
return scope.Yield(GetBuiltinCode(builtin));
}
return GenerateFetchFromLocation(expr, GetLocationReference(expr));
return scope.Yield(GenerateFetchFromLocation(GetLocationReference(expr)));
}
const Type* ImplementationVisitor::Visit(GotoStatement* stmt) {
......@@ -615,20 +731,21 @@ const Type* ImplementationVisitor::Visit(GotoStatement* stmt) {
}
size_t i = 0;
StackRange arguments = assembler().TopRange(0);
for (Expression* e : stmt->arguments) {
StackScope scope(this);
VisitResult result = Visit(e);
Variable* var = label->GetParameter(i++);
GenerateAssignToVariable(var, result);
result = GenerateImplicitConvert(var->type(), result);
arguments.Extend(scope.Yield(result).stack_range());
}
GenerateLabelGoto(label);
GenerateLabelGoto(label, arguments);
label->MarkUsed();
return TypeOracle::GetNeverType();
}
const Type* ImplementationVisitor::Visit(IfStatement* stmt) {
ScopedIndent indent(this);
bool has_else = stmt->if_false.has_value();
if (stmt->is_constexpr) {
......@@ -641,23 +758,33 @@ const Type* ImplementationVisitor::Visit(IfStatement* stmt) {
ReportError(stream.str());
}
Block* true_block = assembler().NewBlock();
Block* false_block = assembler().NewBlock();
Block* done_block = assembler().NewBlock();
assembler().Emit(ConstexprBranchInstruction{
expression_result.constexpr_value(), true_block, false_block});
assembler().Bind(true_block);
const Type* left_result;
const Type* right_result = TypeOracle::GetVoidType();
{
GenerateIndent();
source_out() << "if ((" << RValueFlattenStructs(expression_result)
<< ")) ";
ScopedIndent indent(this, false);
source_out() << "\n";
StackScope stack_scope(this);
left_result = Visit(stmt->if_true);
}
if (left_result == TypeOracle::GetVoidType()) {
assembler().Goto(done_block);
}
assembler().Bind(false_block);
const Type* right_result = TypeOracle::GetVoidType();
if (has_else) {
source_out() << " else ";
ScopedIndent indent(this, false);
source_out() << "\n";
StackScope stack_scope(this);
right_result = Visit(*stmt->if_false);
}
if (right_result == TypeOracle::GetVoidType()) {
assembler().Goto(done_block);
}
if (left_result->IsNever() != right_result->IsNever()) {
std::stringstream stream;
stream << "either both or neither branches in a constexpr if statement "
......@@ -666,8 +793,9 @@ const Type* ImplementationVisitor::Visit(IfStatement* stmt) {
ReportError(stream.str());
}
source_out() << "\n";
if (left_result != TypeOracle::GetNeverType()) {
assembler().Bind(done_block);
}
return left_result;
} else {
Label* true_label = nullptr;
......@@ -675,56 +803,54 @@ const Type* ImplementationVisitor::Visit(IfStatement* stmt) {
{
Declarations::NodeScopeActivator scope(declarations(), &*stmt->condition);
true_label = declarations()->LookupLabel(kTrueLabelName);
GenerateLabelDefinition(true_label);
CreateBlockForLabel(true_label, assembler().CurrentStack());
false_label = declarations()->LookupLabel(kFalseLabelName);
GenerateLabelDefinition(false_label, !has_else ? stmt : nullptr);
CreateBlockForLabel(false_label, assembler().CurrentStack());
}
Label* done_label = nullptr;
Block* done_block;
bool live = false;
if (has_else) {
done_label = declarations()->DeclarePrivateLabel("if_done_label");
GenerateLabelDefinition(done_label, stmt);
done_block = assembler().NewBlock();
} else {
done_label = false_label;
done_block = false_label->block();
live = true;
}
std::vector<Statement*> blocks = {stmt->if_true};
std::vector<Label*> labels = {true_label, false_label};
if (has_else) blocks.push_back(*stmt->if_false);
if (GenerateExpressionBranch(stmt->condition, labels, blocks, done_label)) {
if (GenerateExpressionBranch(stmt->condition, labels, blocks, done_block)) {
live = true;
}
if (live) {
GenerateLabelBind(done_label);
assembler().Bind(done_block);
}
return live ? TypeOracle::GetVoidType() : TypeOracle::GetNeverType();
}
}
const Type* ImplementationVisitor::Visit(WhileStatement* stmt) {
ScopedIndent indent(this);
Label* body_label = nullptr;
Label* exit_label = nullptr;
{
Declarations::NodeScopeActivator scope(declarations(), stmt->condition);
body_label = declarations()->LookupLabel(kTrueLabelName);
GenerateLabelDefinition(body_label);
CreateBlockForLabel(body_label, assembler().CurrentStack());
exit_label = declarations()->LookupLabel(kFalseLabelName);
GenerateLabelDefinition(exit_label);
CreateBlockForLabel(exit_label, assembler().CurrentStack());
}
Label* header_label = declarations()->DeclarePrivateLabel("header");
GenerateLabelDefinition(header_label, stmt);
GenerateLabelGoto(header_label);
GenerateLabelBind(header_label);
Block* header_block = assembler().NewBlock();
assembler().Goto(header_block);
assembler().Bind(header_block);
Declarations::NodeScopeActivator scope(declarations(), stmt->body);
BreakContinueActivator activator(global_context_, exit_label, header_label);
BreakContinueActivator activator(global_context_, exit_label->block(),
header_block);
GenerateExpressionBranch(stmt->condition, {body_label, exit_label},
{stmt->body}, header_label);
{stmt->body}, header_block);
GenerateLabelBind(exit_label);
return TypeOracle::GetVoidType();
......@@ -732,7 +858,6 @@ const Type* ImplementationVisitor::Visit(WhileStatement* stmt) {
const Type* ImplementationVisitor::Visit(BlockStatement* block) {
Declarations::NodeScopeActivator scope(declarations(), block);
ScopedIndent indent(this);
const Type* type = TypeOracle::GetVoidType();
for (Statement* s : block->statements) {
if (type->IsNever()) {
......@@ -747,17 +872,14 @@ const Type* ImplementationVisitor::Visit(BlockStatement* block) {
const Type* ImplementationVisitor::Visit(DebugStatement* stmt) {
#if defined(DEBUG)
GenerateIndent();
source_out() << "Print(\""
<< "halting because of '" << stmt->reason << "' at "
<< PositionAsString(stmt->pos) << "\");\n";
assembler().Emit(PrintConstantStringInstruction{"halting because of '" +
stmt->reason + "' at " +
PositionAsString(stmt->pos)});
#endif
GenerateIndent();
assembler().Emit(DebugBreakInstruction{stmt->never_continues});
if (stmt->never_continues) {
source_out() << "Unreachable();\n";
return TypeOracle::GetNeverType();
} else {
source_out() << "DebugBreak();\n";
return TypeOracle::GetVoidType();
}
}
......@@ -799,9 +921,9 @@ const Type* ImplementationVisitor::Visit(AssertStatement* stmt) {
Label* false_label = nullptr;
Declarations::NodeScopeActivator scope(declarations(), stmt->expression);
true_label = declarations()->LookupLabel(kTrueLabelName);
GenerateLabelDefinition(true_label);
CreateBlockForLabel(true_label, assembler().CurrentStack());
false_label = declarations()->LookupLabel(kFalseLabelName);
GenerateLabelDefinition(false_label);
CreateBlockForLabel(false_label, assembler().CurrentStack());
VisitResult expression_result = Visit(stmt->expression);
if (expression_result.type() == TypeOracle::GetBoolType()) {
......@@ -816,12 +938,10 @@ const Type* ImplementationVisitor::Visit(AssertStatement* stmt) {
}
GenerateLabelBind(false_label);
GenerateIndent();
source_out() << "Print(\""
<< "assert '" << FormatAssertSource(stmt->source)
<< "' failed at " << PositionAsString(stmt->pos) << "\");\n";
GenerateIndent();
source_out() << "Unreachable();\n";
assembler().Emit(PrintConstantStringInstruction{
"assert '" + FormatAssertSource(stmt->source) + "' failed at " +
PositionAsString(stmt->pos)});
assembler().Emit(DebugBreakInstruction{true});
GenerateLabelBind(true_label);
}
......@@ -854,20 +974,16 @@ const Type* ImplementationVisitor::Visit(ReturnStatement* stmt) {
VisitResult return_result = GenerateImplicitConvert(
current_callable->signature().return_type, expression_result);
if (current_callable->IsMacro()) {
Variable* var =
Variable::cast(declarations()->LookupValue(kReturnValueVariable));
GenerateAssignToVariable(var, return_result);
GenerateLabelGoto(end);
} else if (current_callable->IsBuiltin()) {
if (Builtin::cast(current_callable)->IsVarArgsJavaScript()) {
GenerateIndent();
source_out() << "arguments->PopAndReturn("
<< RValueFlattenStructs(return_result) << ");\n";
if (return_result.IsOnStack()) {
StackRange return_value_range =
GenerateLabelGoto(end, return_result.stack_range());
SetReturnValue(VisitResult(return_result.type(), return_value_range));
} else {
GenerateIndent();
source_out() << "Return(" << RValueFlattenStructs(return_result)
<< ");\n";
GenerateLabelGoto(end);
SetReturnValue(return_result);
}
} else if (current_callable->IsBuiltin()) {
assembler().Emit(ReturnInstruction{});
} else {
UNREACHABLE();
}
......@@ -886,6 +1002,7 @@ const Type* ImplementationVisitor::Visit(ReturnStatement* stmt) {
const Type* ImplementationVisitor::Visit(ForOfLoopStatement* stmt) {
Declarations::NodeScopeActivator scope(declarations(), stmt);
StackScope stack_scope(this);
VisitResult expression_result = Visit(stmt->iterable);
VisitResult begin = stmt->begin
......@@ -896,63 +1013,76 @@ const Type* ImplementationVisitor::Visit(ForOfLoopStatement* stmt) {
? Visit(*stmt->end)
: GenerateCall(".length", {{expression_result}, {}});
Label* body_label = declarations()->DeclarePrivateLabel("body");
GenerateLabelDefinition(body_label);
Label* increment_label = declarations()->DeclarePrivateLabel("increment");
GenerateLabelDefinition(increment_label);
Label* exit_label = declarations()->DeclarePrivateLabel("exit");
GenerateLabelDefinition(exit_label);
const Type* common_type = GetCommonType(begin.type(), end.type());
Variable* index_var = GenerateVariableDeclaration(
stmt, std::string(kForIndexValueVariable) + "_" + NewTempVariable(),
false, common_type, begin);
VisitResult index = GenerateImplicitConvert(common_type, begin);
VisitResult index_for_read = {index_var->type(), index_var};
Block* body_block = assembler().NewBlock();
Block* increment_block = assembler().NewBlock(assembler().CurrentStack());
Block* exit_block = assembler().NewBlock(assembler().CurrentStack());
Label* header_label = declarations()->DeclarePrivateLabel("header");
GenerateLabelDefinition(header_label, stmt);
Block* header_block = assembler().NewBlock();
GenerateLabelGoto(header_label);
assembler().Goto(header_block);
GenerateLabelBind(header_label);
assembler().Bind(header_block);
BreakContinueActivator activator(global_context_, exit_label,
increment_label);
BreakContinueActivator activator(global_context_, exit_block,
increment_block);
{
StackScope comparison_scope(this);
VisitResult result = GenerateCall("<", {{index, end}, {}});
if (result.type() != TypeOracle::GetBoolType()) {
ReportError("operator < with arguments(", *index.type(), ", ",
*end.type(),
") used in for-of loop has to return type bool, but "
"returned type ",
*result.type());
}
comparison_scope.Yield(result);
}
assembler().Branch(body_block, exit_block);
VisitResult result = GenerateCall("<", {{index_for_read, end}, {}});
GenerateBranch(result, body_label, exit_label);
assembler().Bind(body_block);
{
StackScope body_scope(this);
GenerateLabelBind(body_label);
VisitResult element_result =
GenerateCall("[]", {{expression_result, index_for_read}, {}});
base::Optional<const Type*> declared_type;
if (stmt->var_declaration->type)
declared_type = declarations()->GetType(*stmt->var_declaration->type);
GenerateVariableDeclaration(
stmt->var_declaration, stmt->var_declaration->name,
stmt->var_declaration->const_qualified, declared_type, element_result);
Visit(stmt->body);
GenerateLabelGoto(increment_label);
VisitResult element_result;
{
StackScope element_scope(this);
VisitResult result = GenerateCall("[]", {{expression_result, index}, {}});
if (stmt->var_declaration->type) {
const Type* declared_type =
declarations()->GetType(*stmt->var_declaration->type);
result = GenerateImplicitConvert(declared_type, result);
}
element_result = element_scope.Yield(result);
}
Variable* element_var = Variable::cast(
declarations()->LookupValue(stmt->var_declaration->name));
element_var->set_value(element_result);
Visit(stmt->body);
}
assembler().Goto(increment_block);
GenerateLabelBind(increment_label);
Arguments increment_args;
increment_args.parameters = {index_for_read,
{TypeOracle::GetConstInt31Type(), "1"}};
VisitResult increment_result = GenerateCall("+", increment_args);
assembler().Bind(increment_block);
{
Arguments increment_args;
increment_args.parameters = {index, {TypeOracle::GetConstInt31Type(), "1"}};
VisitResult increment_result = GenerateCall("+", increment_args);
GenerateAssignToVariable(index_var, increment_result);
GenerateAssignToLocation(LocationReference::VariableAccess(index),
increment_result);
}
GenerateLabelGoto(header_label);
assembler().Goto(header_block);
GenerateLabelBind(exit_label);
assembler().Bind(exit_block);
return TypeOracle::GetVoidType();
}
const Type* ImplementationVisitor::Visit(TryLabelStatement* stmt) {
ScopedIndent indent(this);
Label* try_done = declarations()->DeclarePrivateLabel("try_done");
GenerateLabelDefinition(try_done);
Block* done_block = assembler().NewBlock();
const Type* try_result = TypeOracle::GetNeverType();
std::vector<Label*> labels;
......@@ -961,32 +1091,27 @@ const Type* ImplementationVisitor::Visit(TryLabelStatement* stmt) {
// Activate a new scope to see handler labels
Declarations::NodeScopeActivator scope(declarations(), stmt);
for (LabelBlock* block : stmt->label_blocks) {
CurrentSourcePosition::Scope scope(block->pos);
CurrentSourcePosition::Scope source_position(block->pos);
Label* label = declarations()->LookupLabel(block->label);
labels.push_back(label);
GenerateLabelDefinition(label);
}
size_t i = 0;
for (auto label : labels) {
Declarations::NodeScopeActivator scope(declarations(),
stmt->label_blocks[i]->body);
for (auto& v : label->GetParameters()) {
GenerateVariableDeclaration(stmt, v->name(), false, v->type());
Declarations::NodeScopeActivator scope(declarations(), block->body);
Stack<const Type*> label_input_stack = assembler().CurrentStack();
for (Variable* v : label->GetParameters()) {
StackRange range = label_input_stack.PushMany(LowerType(v->type()));
v->set_value(VisitResult(v->type(), range));
v->Define();
}
++i;
CreateBlockForLabel(label, label_input_stack);
}
Label* try_begin_label = declarations()->DeclarePrivateLabel("try_begin");
GenerateLabelDefinition(try_begin_label);
GenerateLabelGoto(try_begin_label);
// Visit try
if (GenerateLabeledStatementBlocks({stmt->try_block},
std::vector<Label*>({try_begin_label}),
try_done)) {
try_result = TypeOracle::GetVoidType();
{
StackScope stack_scope(this);
try_result = Visit(stmt->try_block);
}
if (try_result != TypeOracle::GetNeverType()) {
assembler().Goto(done_block);
}
}
......@@ -1006,84 +1131,83 @@ const Type* ImplementationVisitor::Visit(TryLabelStatement* stmt) {
label_iterator++;
}
// Visit and output the code for each catch block, one-by-one.
// Visit and output the code for each label block, one-by-one.
std::vector<Statement*> bodies;
for (LabelBlock* block : stmt->label_blocks) bodies.push_back(block->body);
if (GenerateLabeledStatementBlocks(bodies, labels, try_done)) {
if (GenerateLabeledStatementBlocks(bodies, labels, done_block)) {
try_result = TypeOracle::GetVoidType();
}
if (!try_result->IsNever()) {
GenerateLabelBind(try_done);
assembler().Bind(done_block);
}
return try_result;
}
const Type* ImplementationVisitor::Visit(BreakStatement* stmt) {
Label* break_label = global_context_.GetCurrentBreak();
if (break_label == nullptr) {
Block* break_block = global_context_.GetCurrentBreak();
if (break_block == nullptr) {
ReportError("break used outside of loop");
}
GenerateLabelGoto(break_label);
assembler().Goto(break_block);
return TypeOracle::GetNeverType();
}
const Type* ImplementationVisitor::Visit(ContinueStatement* stmt) {
Label* continue_label = global_context_.GetCurrentContinue();
if (continue_label == nullptr) {
Block* continue_block = global_context_.GetCurrentContinue();
if (continue_block == nullptr) {
ReportError("continue used outside of loop");
}
GenerateLabelGoto(continue_label);
assembler().Goto(continue_block);
return TypeOracle::GetNeverType();
}
const Type* ImplementationVisitor::Visit(ForLoopStatement* stmt) {
Declarations::NodeScopeActivator scope(declarations(), stmt);
StackScope stack_scope(this);
if (stmt->var_declaration) Visit(*stmt->var_declaration);
Label* body_label = declarations()->LookupLabel(kTrueLabelName);
GenerateLabelDefinition(body_label);
CreateBlockForLabel(body_label, assembler().CurrentStack());
Label* exit_label = declarations()->LookupLabel(kFalseLabelName);
GenerateLabelDefinition(exit_label);
CreateBlockForLabel(exit_label, assembler().CurrentStack());
Label* header_label = declarations()->DeclarePrivateLabel("header");
GenerateLabelDefinition(header_label, stmt);
GenerateLabelGoto(header_label);
GenerateLabelBind(header_label);
Block* header_block = assembler().NewBlock();
assembler().Goto(header_block);
assembler().Bind(header_block);
// The continue label is where "continue" statements jump to. If no action
// expression is provided, we jump directly to the header.
Label* continue_label = header_label;
Block* continue_block = header_block;
// The action label is only needed when an action expression was provided.
Label* action_label = nullptr;
Block* action_block = nullptr;
if (stmt->action) {
action_label = declarations()->DeclarePrivateLabel("action");
GenerateLabelDefinition(action_label);
action_block = assembler().NewBlock();
// The action expression needs to be executed on a continue.
continue_label = action_label;
continue_block = action_block;
}
BreakContinueActivator activator(global_context_, exit_label, continue_label);
BreakContinueActivator activator(global_context_, exit_label->block(),
continue_block);
std::vector<Label*> labels = {body_label, exit_label};
bool generate_action = true;
if (stmt->test) {
generate_action = GenerateExpressionBranch(*stmt->test, labels,
{stmt->body}, continue_label);
{stmt->body}, continue_block);
} else {
GenerateLabelGoto(body_label);
generate_action =
GenerateLabeledStatementBlocks({stmt->body}, labels, continue_label);
GenerateLabeledStatementBlocks({stmt->body}, labels, continue_block);
}
if (generate_action && stmt->action) {
ScopedIndent indent(this);
GenerateLabelBind(action_label);
assembler().Bind(action_block);
Visit(*stmt->action);
GenerateLabelGoto(header_label);
assembler().Goto(header_block);
}
GenerateLabelBind(exit_label);
......@@ -1105,7 +1229,7 @@ void ImplementationVisitor::GenerateImplementation(const std::string& dir,
std::string ImplementationVisitor::GetBaseAssemblerName(Module* module) {
if (module == global_context_.GetDefaultModule()) {
return "CodeStubAssembler";
return "TorqueAssembler";
} else {
std::string assembler_name(CamelifyString(module->name()) +
"BuiltinsAssembler");
......@@ -1119,12 +1243,6 @@ std::string ImplementationVisitor::GetDSLAssemblerName(Module* module) {
return assembler_name;
}
void ImplementationVisitor::GenerateIndent() {
for (size_t i = 0; i <= indent_; ++i) {
source_out() << " ";
}
}
void ImplementationVisitor::GenerateMacroFunctionDeclaration(
std::ostream& o, const std::string& macro_prefix, Macro* macro) {
GenerateFunctionDeclaration(o, macro_prefix, macro->name(),
......@@ -1158,11 +1276,12 @@ void ImplementationVisitor::GenerateFunctionDeclaration(
if (!first) {
o << ", ";
}
const Value* parameter = declarations()->LookupValue(name);
const Parameter* parameter =
Parameter::cast(declarations()->LookupValue(name));
const Type* parameter_type = *type_iterator;
const std::string& generated_type_name =
parameter_type->GetGeneratedTypeName();
o << generated_type_name << " " << parameter->value();
o << generated_type_name << " " << parameter->external_name();
type_iterator++;
first = false;
}
......@@ -1172,13 +1291,15 @@ void ImplementationVisitor::GenerateFunctionDeclaration(
if (!first) {
o << ", ";
}
o << "Label* " << label->generated();
o << "Label* " << label->external_label_name();
size_t i = 0;
for (Variable* var : label->GetParameters()) {
std::string generated_type_name("TVariable<");
generated_type_name += var->type()->GetGeneratedTNodeTypeName();
generated_type_name += ">*";
o << ", ";
o << generated_type_name << " " << var->value();
o << generated_type_name << " " << ExternalLabelParameterName(label, i);
++i;
}
}
......@@ -1310,43 +1431,6 @@ Callable* ImplementationVisitor::LookupCall(
return result;
}
void ImplementationVisitor::GetFlattenedStructsVars(
const Variable* base, std::set<const Variable*>* vars) {
const Type* type = base->type();
if (base->IsConst()) return;
if (type->IsStructType()) {
const StructType* struct_type = StructType::cast(type);
for (auto& field : struct_type->fields()) {
std::string field_var_name = base->name() + "." + field.name;
GetFlattenedStructsVars(
Variable::cast(declarations()->LookupValue(field_var_name)), vars);
}
} else {
vars->insert(base);
}
}
void ImplementationVisitor::GenerateChangedVarsFromControlSplit(AstNode* node) {
const std::set<const Variable*>& changed_vars =
global_context_.GetControlSplitChangedVariables(
node, declarations()->GetCurrentSpecializationTypeNamesVector());
std::set<const Variable*> flattened_vars;
for (auto v : changed_vars) {
GetFlattenedStructsVars(v, &flattened_vars);
}
std::vector<const Variable*> flattened_vars_sorted(flattened_vars.begin(),
flattened_vars.end());
auto compare_variables = [](const Variable* a, const Variable* b) {
return a->value() < b->value();
};
std::sort(flattened_vars_sorted.begin(), flattened_vars_sorted.end(),
compare_variables);
source_out() << "{";
PrintCommaSeparatedList(source_out(), flattened_vars_sorted,
[](const Variable* v) { return v->value(); });
source_out() << "}";
}
const Type* ImplementationVisitor::GetCommonType(const Type* left,
const Type* right) {
const Type* common_type;
......@@ -1362,11 +1446,11 @@ const Type* ImplementationVisitor::GetCommonType(const Type* left,
}
VisitResult ImplementationVisitor::GenerateCopy(const VisitResult& to_copy) {
std::string temp = GenerateNewTempVariable(to_copy.type());
source_out() << RValueFlattenStructs(to_copy) << ";\n";
GenerateIndent();
source_out() << "USE(" << temp << ");\n";
return VisitResult(to_copy.type(), temp);
if (to_copy.IsOnStack()) {
return VisitResult(to_copy.type(),
assembler().Peek(to_copy.stack_range(), to_copy.type()));
}
return to_copy;
}
VisitResult ImplementationVisitor::Visit(StructExpression* decl) {
......@@ -1384,23 +1468,19 @@ VisitResult ImplementationVisitor::Visit(StructExpression* decl) {
<< ")";
ReportError(s.str());
}
std::vector<VisitResult> expression_results;
for (auto& field : struct_type->fields()) {
VisitResult value = Visit(decl->expressions[expression_results.size()]);
StackRange stack_range = assembler().TopRange(0);
for (size_t i = 0; i < struct_type->fields().size(); ++i) {
const NameAndType& field = struct_type->fields()[i];
StackScope scope(this);
VisitResult value = Visit(decl->expressions[i]);
value = GenerateImplicitConvert(field.type, value);
expression_results.push_back(value);
stack_range.Extend(scope.Yield(value).stack_range());
}
std::string result_var_name = GenerateNewTempVariable(struct_type);
source_out() << "{";
PrintCommaSeparatedList(
source_out(), expression_results,
[&](const VisitResult& result) { return RValueFlattenStructs(result); });
source_out() << "};\n";
return VisitResult(struct_type, result_var_name);
return VisitResult(struct_type, stack_range);
}
LocationReference ImplementationVisitor::GetLocationReference(
LocationExpression* location) {
Expression* location) {
switch (location->kind) {
case AstNode::Kind::kIdentifierExpression:
return GetLocationReference(static_cast<IdentifierExpression*>(location));
......@@ -1411,232 +1491,92 @@ LocationReference ImplementationVisitor::GetLocationReference(
return GetLocationReference(
static_cast<ElementAccessExpression*>(location));
default:
UNREACHABLE();
return LocationReference::Temporary(Visit(location), "expression");
}
}
LocationReference ImplementationVisitor::GetLocationReference(
FieldAccessExpression* expr) {
VisitResult result = Visit(expr->object);
if (result.type()->IsStructType()) {
if (result.declarable()) {
return LocationReference(
declarations()->LookupValue((*result.declarable())->name() + "." +
expr->field),
{}, {});
}
LocationReference reference = GetLocationReference(expr->object);
if (reference.IsVariableAccess() &&
reference.variable().type()->IsStructType()) {
return LocationReference::VariableAccess(
ProjectStructField(reference.variable(), expr->field));
}
return LocationReference(nullptr, result, {});
}
std::string ImplementationVisitor::RValueFlattenStructs(
const VisitResult& result) {
if (result.declarable()) {
const Value* value = *result.declarable();
const Type* type = value->type();
if (const StructType* struct_type = StructType::DynamicCast(type)) {
std::stringstream s;
s << struct_type->name() << "{";
PrintCommaSeparatedList(
s, struct_type->fields(), [&](const NameAndType& field) {
std::string field_declaration = value->name() + "." + field.name;
Variable* field_variable =
Variable::cast(declarations()->LookupValue(field_declaration));
return RValueFlattenStructs(
VisitResult(field_variable->type(), field_variable));
});
s << "}";
return s.str();
}
if (reference.IsTemporary() && reference.temporary().type()->IsStructType()) {
return LocationReference::Temporary(
ProjectStructField(reference.temporary(), expr->field),
reference.temporary_description());
}
return result.RValue();
return LocationReference::FieldAccess(GenerateFetchFromLocation(reference),
expr->field);
}
VisitResult ImplementationVisitor::GenerateFetchFromLocation(
LocationExpression* location, const LocationReference& reference) {
switch (location->kind) {
case AstNode::Kind::kIdentifierExpression:
return GenerateFetchFromLocation(
static_cast<IdentifierExpression*>(location), reference);
case AstNode::Kind::kFieldAccessExpression:
return GenerateFetchFromLocation(
static_cast<FieldAccessExpression*>(location), reference);
case AstNode::Kind::kElementAccessExpression:
return GenerateFetchFromLocation(
static_cast<ElementAccessExpression*>(location), reference);
default:
UNREACHABLE();
}
}
VisitResult ImplementationVisitor::GenerateFetchFromLocation(
FieldAccessExpression* expr, const LocationReference& reference) {
if (reference.value != nullptr) {
return GenerateFetchFromLocation(reference);
}
const Type* type = reference.base.type();
if (const StructType* struct_type = StructType::DynamicCast(type)) {
return VisitResult(struct_type->GetFieldType(expr->field),
reference.base.RValue() + "." + expr->field);
} else {
Arguments arguments;
arguments.parameters = {reference.base};
return GenerateCall(std::string(".") + expr->field, arguments);
}
LocationReference ImplementationVisitor::GetLocationReference(
ElementAccessExpression* expr) {
VisitResult array = Visit(expr->array);
VisitResult index = Visit(expr->index);
return LocationReference::ArrayAccess(array, index);
}
void ImplementationVisitor::GenerateAssignToVariable(Variable* var,
const VisitResult& value) {
if (var->type()->IsStructType()) {
if (value.type() != var->type()) {
std::stringstream s;
s << "incompatable assignment from type " << *value.type() << " to "
<< *var->type();
ReportError(s.str());
}
const StructType* struct_type = StructType::cast(var->type());
for (auto& field : struct_type->fields()) {
std::string field_declaration = var->name() + "." + field.name;
Variable* field_variable =
Variable::cast(declarations()->LookupValue(field_declaration));
if (value.declarable() && (*value.declarable())->IsVariable()) {
Variable* source_field = Variable::cast(declarations()->LookupValue(
Variable::cast((*value.declarable()))->name() + "." + field.name));
GenerateAssignToVariable(
field_variable, VisitResult{source_field->type(), source_field});
} else {
GenerateAssignToVariable(
field_variable, VisitResult{field_variable->type(),
value.RValue() + "." + field.name});
}
LocationReference ImplementationVisitor::GetLocationReference(
IdentifierExpression* expr) {
Value* value = declarations()->LookupValue(expr->name);
if (auto* constant = ModuleConstant::DynamicCast(value)) {
if (constant->type()->IsConstexpr()) {
return LocationReference::Temporary(
VisitResult(constant->type(), constant->constant_name() + "()"),
"module constant " + expr->name);
}
} else {
VisitResult casted_value = GenerateImplicitConvert(var->type(), value);
GenerateIndent();
VisitResult var_value = {var->type(), var};
source_out() << var_value.LValue() << " = "
<< RValueFlattenStructs(casted_value) << ";\n";
assembler().Emit(ModuleConstantInstruction{constant});
StackRange stack_range =
assembler().TopRange(LoweredSlotCount(constant->type()));
return LocationReference::Temporary(
VisitResult(constant->type(), stack_range),
"module constant " + expr->name);
}
var->Define();
}
void ImplementationVisitor::GenerateAssignToLocation(
LocationExpression* location, const LocationReference& reference,
const VisitResult& assignment_value) {
if (reference.value != nullptr) {
Value* value = reference.value;
if (value->IsConst()) {
std::stringstream s;
s << "\"" << value->name()
<< "\" is declared const (maybe implicitly) and cannot be assigned to";
ReportError(s.str());
}
Variable* var = Variable::cast(value);
GenerateAssignToVariable(var, assignment_value);
} else if (auto access = FieldAccessExpression::DynamicCast(location)) {
GenerateCall(std::string(".") + access->field + "=",
{{reference.base, assignment_value}, {}});
} else {
DCHECK_NOT_NULL(ElementAccessExpression::cast(location));
GenerateCall("[]=",
{{reference.base, reference.index, assignment_value}, {}});
if (value->IsConst()) {
return LocationReference::Temporary(value->value(),
"constant value " + expr->name);
}
DCHECK(value->IsVariable());
return LocationReference::VariableAccess(value->value());
}
void ImplementationVisitor::GenerateVariableDeclaration(const Variable* var) {
const Type* var_type = var->type();
if (var_type->IsStructType()) {
const StructType* struct_type = StructType::cast(var_type);
for (auto& field : struct_type->fields()) {
GenerateVariableDeclaration(Variable::cast(
declarations()->LookupValue(var->name() + "." + field.name)));
}
VisitResult ImplementationVisitor::GenerateFetchFromLocation(
const LocationReference& reference) {
if (reference.IsTemporary()) {
return GenerateCopy(reference.temporary());
} else if (reference.IsVariableAccess()) {
return GenerateCopy(reference.variable());
} else {
std::string value = var->value();
GenerateIndent();
if (var_type->IsConstexpr()) {
source_out() << var_type->GetGeneratedTypeName();
source_out() << " " << value << "_impl;\n";
} else if (var->IsConst()) {
source_out() << "TNode<" << var->type()->GetGeneratedTNodeTypeName();
source_out() << "> " << var->value() << "_impl;\n";
} else {
source_out() << "TVARIABLE(";
source_out() << var_type->GetGeneratedTNodeTypeName();
source_out() << ", " << value << "_impl);\n";
}
GenerateIndent();
source_out() << "auto " << value << " = &" << value << "_impl;\n";
GenerateIndent();
source_out() << "USE(" << value << ");\n";
}
}
Variable* ImplementationVisitor::GeneratePredeclaredVariableDeclaration(
const std::string& name,
const base::Optional<VisitResult>& initialization) {
Variable* variable = Variable::cast(declarations()->LookupValue(name));
GenerateVariableDeclaration(variable);
if (initialization) {
GenerateAssignToVariable(variable, *initialization);
DCHECK(reference.IsCallAccess());
return GenerateCall(reference.eval_function(),
Arguments{reference.call_arguments(), {}});
}
return variable;
}
Variable* ImplementationVisitor::GenerateVariableDeclaration(
AstNode* node, const std::string& name, bool is_const,
const base::Optional<const Type*>& type,
const base::Optional<VisitResult>& initialization) {
Variable* variable = nullptr;
if (declarations()->IsDeclaredInCurrentScope(name)) {
variable = Variable::cast(declarations()->LookupValue(name));
void ImplementationVisitor::GenerateAssignToLocation(
const LocationReference& reference, const VisitResult& assignment_value) {
if (reference.IsCallAccess()) {
Arguments arguments{reference.call_arguments(), {}};
arguments.parameters.push_back(assignment_value);
GenerateCall(reference.assign_function(), arguments);
} else if (reference.IsVariableAccess()) {
VisitResult variable = reference.variable();
VisitResult converted_value =
GenerateImplicitConvert(variable.type(), assignment_value);
assembler().Poke(variable.stack_range(), converted_value.stack_range(),
variable.type());
} else {
variable = declarations()->DeclareVariable(
name, type ? *type : initialization->type(), is_const);
if (!is_const) {
// Because the variable is being defined during code generation, it must
// be assumed that it changes along all control split paths because it's
// no longer possible to run the control-flow anlaysis in the declaration
// pass over the variable.
global_context_.MarkVariableChanged(
node, declarations()->GetCurrentSpecializationTypeNamesVector(),
variable);
}
}
GenerateVariableDeclaration(variable);
if (initialization) {
GenerateAssignToVariable(variable, *initialization);
}
return variable;
}
void ImplementationVisitor::GenerateParameter(
const std::string& parameter_name) {
const Value* val = declarations()->LookupValue(parameter_name);
std::string var = val->value();
GenerateIndent();
source_out() << val->type()->GetGeneratedTypeName() << " " << var << " = ";
source_out() << "UncheckedCast<" << val->type()->GetGeneratedTNodeTypeName()
<< ">(Parameter(Descriptor::k" << CamelifyString(parameter_name)
<< "));\n";
GenerateIndent();
source_out() << "USE(" << var << ");\n";
}
void ImplementationVisitor::GenerateParameterList(const NameVector& list,
size_t first) {
for (const auto& p : list) {
if (first == 0) {
GenerateParameter(p);
} else {
first--;
}
DCHECK(reference.IsTemporary());
ReportError("cannot assign to ", reference.temporary_description());
}
}
VisitResult ImplementationVisitor::GeneratePointerCall(
Expression* callee, const Arguments& arguments, bool is_tailcall) {
StackScope scope(this);
TypeVector parameter_types(arguments.parameters.GetTypeVector());
VisitResult callee_result = Visit(callee);
if (!callee_result.type()->IsFunctionPointerType()) {
......@@ -1668,28 +1608,13 @@ VisitResult ImplementationVisitor::GeneratePointerCall(
ReportError(stream.str());
}
std::vector<std::string> variables;
callee_result = GenerateCopy(callee_result);
StackRange arg_range = assembler().TopRange(0);
for (size_t current = 0; current < arguments.parameters.size(); ++current) {
const Type* to_type = type->parameter_types()[current];
VisitResult result =
GenerateImplicitConvert(to_type, arguments.parameters[current]);
variables.push_back(RValueFlattenStructs(result));
}
std::string result_variable_name;
bool no_result = type->return_type()->IsVoidOrNever() || is_tailcall;
if (no_result) {
GenerateIndent();
} else {
const Type* return_type = type->return_type();
result_variable_name = GenerateNewTempVariable(return_type);
if (return_type->IsStructType()) {
source_out() << "(";
} else {
source_out() << "UncheckedCast<";
source_out() << type->return_type()->GetGeneratedTNodeTypeName();
source_out() << ">(";
}
arg_range.Extend(
GenerateImplicitConvert(to_type, arguments.parameters[current])
.stack_range());
}
Builtin* example_builtin =
......@@ -1700,27 +1625,14 @@ VisitResult ImplementationVisitor::GeneratePointerCall(
ReportError(stream.str());
}
if (is_tailcall) {
source_out() << "TailCallStub(";
} else {
source_out() << "CallStub(";
}
source_out() << "Builtins::CallableFor(isolate(), Builtins::k"
<< example_builtin->name() << ").descriptor(), "
<< RValueFlattenStructs(callee_result) << ", ";
assembler().Emit(CallBuiltinPointerInstruction{is_tailcall, example_builtin,
arg_range.Size()});
size_t total_parameters = 0;
for (size_t i = 0; i < arguments.parameters.size(); ++i) {
if (total_parameters++ != 0) {
source_out() << ", ";
}
source_out() << variables[i];
}
if (!no_result) {
source_out() << ")";
if (is_tailcall) {
return VisitResult::NeverResult();
}
source_out() << ");\n";
return VisitResult(type->return_type(), result_variable_name);
DCHECK_EQ(1, LoweredSlotCount(type->return_type()));
return scope.Yield(VisitResult(type->return_type(), assembler().TopRange(1)));
}
VisitResult ImplementationVisitor::GenerateCall(
......@@ -1739,67 +1651,29 @@ VisitResult ImplementationVisitor::GenerateCall(
arguments.labels.push_back(false_label);
}
const Type* result_type = callable->signature().return_type;
const Type* return_type = callable->signature().return_type;
std::vector<std::string> variables;
std::vector<VisitResult> converted_arguments;
StackRange argument_range = assembler().TopRange(0);
std::vector<std::string> constexpr_arguments;
for (size_t current = 0; current < arguments.parameters.size(); ++current) {
const Type* to_type = (current >= callable->signature().types().size())
? TypeOracle::GetObjectType()
: callable->signature().types()[current];
VisitResult result =
VisitResult converted =
GenerateImplicitConvert(to_type, arguments.parameters[current]);
variables.push_back(RValueFlattenStructs(result));
}
std::string result_variable_name;
if (result_type->IsVoidOrNever() || is_tailcall) {
GenerateIndent();
} else {
result_variable_name = GenerateNewTempVariable(result_type);
if (!result_type->IsConstexpr()) {
if (result_type->IsStructType()) {
source_out() << "(";
} else {
source_out() << "UncheckedCast<";
source_out() << result_type->GetGeneratedTNodeTypeName();
source_out() << ">(";
}
}
}
if (callable->IsBuiltin()) {
if (is_tailcall) {
source_out() << "TailCallBuiltin(Builtins::k" << callable->name() << ", ";
} else {
source_out() << "CallBuiltin(Builtins::k" << callable->name() << ", ";
}
} else if (callable->IsMacro()) {
if (is_tailcall) {
std::stringstream stream;
stream << "can't tail call a macro";
ReportError(stream.str());
}
source_out() << callable->name() << "(";
} else if (callable->IsRuntimeFunction()) {
if (is_tailcall) {
source_out() << "TailCallRuntime(Runtime::k" << callable->name() << ", ";
converted_arguments.push_back(converted);
if (converted.IsOnStack()) {
argument_range.Extend(converted.stack_range());
} else {
source_out() << "CallRuntime(Runtime::k" << callable->name() << ", ";
constexpr_arguments.push_back(converted.constexpr_value());
}
} else {
UNREACHABLE();
}
if (global_context_.verbose()) {
std::cout << "generating code for call to " << callable_name << "\n";
}
size_t total_parameters = 0;
for (size_t i = 0; i < arguments.parameters.size(); ++i) {
if (total_parameters++ != 0) {
source_out() << ", ";
}
source_out() << variables[i];
}
size_t label_count = callable->signature().labels.size();
if (label_count != arguments.labels.size()) {
std::stringstream s;
......@@ -1808,49 +1682,114 @@ VisitResult ImplementationVisitor::GenerateCall(
<< std::to_string(arguments.labels.size()) << ")";
ReportError(s.str());
}
for (size_t i = 0; i < label_count; ++i) {
if (total_parameters++ != 0) {
source_out() << ", ";
if (auto* builtin = Builtin::DynamicCast(callable)) {
assembler().Emit(
CallBuiltinInstruction{is_tailcall, builtin, argument_range.Size()});
if (is_tailcall) {
return VisitResult::NeverResult();
} else {
size_t slot_count = LoweredSlotCount(return_type);
DCHECK_LE(slot_count, 1);
// TODO(tebbi): Actually, builtins have to return a value, so we should
// assert slot_count == 1 here.
return VisitResult(return_type, assembler().TopRange(slot_count));
}
Label* label = arguments.labels[i];
size_t callee_label_parameters =
callable->signature().labels[i].types.size();
if (label->GetParameterCount() != callee_label_parameters) {
std::stringstream s;
s << "label " << label->name()
<< " doesn't have the right number of parameters (found "
<< std::to_string(label->GetParameterCount()) << " expected "
<< std::to_string(callee_label_parameters) << ")";
ReportError(s.str());
} else if (auto* macro = Macro::DynamicCast(callable)) {
if (is_tailcall) {
ReportError("can't tail call a macro");
}
source_out() << label->generated();
size_t j = 0;
for (auto t : callable->signature().labels[i].types) {
source_out() << ", ";
Variable* variable = label->GetParameter(j);
if (!(variable->type() == t)) {
std::stringstream s;
s << "mismatch of label parameters (expected " << *t << " got "
<< *label->GetParameter(j)->type() << " for parameter "
<< std::to_string(i + 1) << ")";
ReportError(s.str());
if (return_type->IsConstexpr()) {
DCHECK_EQ(0, arguments.labels.size());
std::stringstream result;
result << "(" << macro->name() << "(";
bool first = true;
for (VisitResult arg : arguments.parameters) {
DCHECK(!arg.IsOnStack());
if (!first) {
result << ", ";
}
first = false;
result << arg.constexpr_value();
}
result << "))";
return VisitResult(return_type, result.str());
} else if (arguments.labels.empty() &&
return_type != TypeOracle::GetNeverType()) {
assembler().Emit(CallCsaMacroInstruction{macro, constexpr_arguments});
size_t return_slot_count = LoweredSlotCount(return_type);
return VisitResult(return_type, assembler().TopRange(return_slot_count));
} else {
base::Optional<Block*> return_continuation;
if (return_type != TypeOracle::GetNeverType()) {
return_continuation = assembler().NewBlock();
}
j++;
source_out() << variable->value();
}
label->MarkUsed();
}
if (global_context_.verbose()) {
std::cout << "finished generating code for call to " << callable_name
<< "\n";
}
if (!result_type->IsVoidOrNever() && !is_tailcall &&
!result_type->IsConstexpr()) {
source_out() << ")";
std::vector<Block*> label_blocks;
for (size_t i = 0; i < label_count; ++i) {
label_blocks.push_back(assembler().NewBlock());
}
assembler().Emit(CallCsaMacroAndBranchInstruction{
macro, constexpr_arguments, return_continuation, label_blocks});
for (size_t i = 0; i < label_count; ++i) {
Label* label = arguments.labels[i];
size_t callee_label_parameters =
callable->signature().labels[i].types.size();
if (label->GetParameterCount() != callee_label_parameters) {
std::stringstream s;
s << "label " << label->name()
<< " doesn't have the right number of parameters (found "
<< std::to_string(label->GetParameterCount()) << " expected "
<< std::to_string(callee_label_parameters) << ")";
ReportError(s.str());
}
assembler().Bind(label_blocks[i]);
assembler().Goto(
label->block(),
LowerParameterTypes(callable->signature().labels[i].types).size());
size_t j = 0;
for (auto t : callable->signature().labels[i].types) {
Variable* variable = label->GetParameter(j);
if (!(variable->type() == t)) {
std::stringstream s;
s << "mismatch of label parameters (expected " << *t << " got "
<< *label->GetParameter(j)->type() << " for parameter "
<< std::to_string(i + 1) << ")";
ReportError(s.str());
}
j++;
}
label->MarkUsed();
}
if (return_continuation) {
assembler().Bind(*return_continuation);
size_t return_slot_count = LoweredSlotCount(return_type);
return VisitResult(return_type,
assembler().TopRange(return_slot_count));
} else {
return VisitResult::NeverResult();
}
}
} else if (auto* runtime_function = RuntimeFunction::DynamicCast(callable)) {
assembler().Emit(CallRuntimeInstruction{is_tailcall, runtime_function,
argument_range.Size()});
if (is_tailcall) {
return VisitResult::NeverResult();
} else {
size_t slot_count = LoweredSlotCount(return_type);
DCHECK_LE(slot_count, 1);
// TODO(tebbi): Actually, runtime functions have to return a value, so
// we should assert slot_count == 1 here.
return VisitResult(return_type, assembler().TopRange(slot_count));
}
} else {
UNREACHABLE();
}
source_out() << ");\n";
return VisitResult(result_type, result_variable_name);
}
void ImplementationVisitor::Visit(StandardDeclaration* decl) {
......@@ -1887,6 +1826,7 @@ void ImplementationVisitor::Visit(SpecializationDeclaration* decl) {
VisitResult ImplementationVisitor::Visit(CallExpression* expr,
bool is_tailcall) {
StackScope scope(this);
Arguments arguments;
std::string name = expr->callee.name;
TypeVector specialization_types =
......@@ -1898,34 +1838,28 @@ VisitResult ImplementationVisitor::Visit(CallExpression* expr,
VisitResult result;
if (!has_template_arguments &&
declarations()->Lookup(expr->callee.name)->IsValue()) {
result = GeneratePointerCall(&expr->callee, arguments, is_tailcall);
return scope.Yield(
GeneratePointerCall(&expr->callee, arguments, is_tailcall));
} else {
result = GenerateCall(name, arguments, specialization_types, is_tailcall);
}
if (!result.type()->IsVoidOrNever()) {
GenerateIndent();
source_out() << "USE(" << RValueFlattenStructs(result) << ");\n";
return scope.Yield(
GenerateCall(name, arguments, specialization_types, is_tailcall));
}
if (is_tailcall) {
result = {TypeOracle::GetNeverType(), ""};
}
return result;
}
bool ImplementationVisitor::GenerateLabeledStatementBlocks(
const std::vector<Statement*>& blocks,
const std::vector<Label*>& statement_labels, Label* merge_label) {
const std::vector<Label*>& statement_labels, Block* merge_block) {
bool live = false;
auto label_iterator = statement_labels.begin();
for (Statement* block : blocks) {
GenerateIndent();
source_out() << "if (" << (*label_iterator)->generated()
<< "->is_used())\n";
ScopedIndent indent(this);
GenerateLabelBind(*label_iterator++);
if (!Visit(block)->IsNever()) {
GenerateLabelGoto(merge_label);
const Type* stmt_result;
{
StackScope stack_scope(this);
stmt_result = Visit(block);
}
if (stmt_result != TypeOracle::GetNeverType()) {
assembler().Goto(merge_block);
live = true;
}
}
......@@ -1935,15 +1869,14 @@ bool ImplementationVisitor::GenerateLabeledStatementBlocks(
void ImplementationVisitor::GenerateBranch(const VisitResult& condition,
Label* true_label,
Label* false_label) {
GenerateIndent();
source_out() << "Branch(" << RValueFlattenStructs(condition) << ", "
<< true_label->generated() << ", " << false_label->generated()
<< ");\n";
DCHECK_EQ(condition,
VisitResult(TypeOracle::GetBoolType(), assembler().TopRange(1)));
assembler().Branch(true_label->block(), false_label->block());
}
bool ImplementationVisitor::GenerateExpressionBranch(
Expression* expression, const std::vector<Label*>& statement_labels,
const std::vector<Statement*>& statement_blocks, Label* merge_label) {
const std::vector<Statement*>& statement_blocks, Block* merge_block) {
// Activate a new scope to define True/False catch labels
Declarations::NodeScopeActivator scope(declarations(), expression);
......@@ -1960,27 +1893,28 @@ bool ImplementationVisitor::GenerateExpressionBranch(
}
return GenerateLabeledStatementBlocks(statement_blocks, statement_labels,
merge_label);
merge_block);
}
VisitResult ImplementationVisitor::GenerateImplicitConvert(
const Type* destination_type, VisitResult source) {
StackScope scope(this);
if (source.type() == TypeOracle::GetNeverType()) {
ReportError("it is not allowed to use a value of type never");
}
if (destination_type == source.type()) {
return source;
return scope.Yield(GenerateCopy(source));
}
if (TypeOracle::IsImplicitlyConvertableFrom(destination_type,
source.type())) {
std::string name =
GetGeneratedCallableName(kFromConstexprMacroName, {destination_type});
return GenerateCall(name, {{source}, {}}, {}, false);
return scope.Yield(GenerateCall(name, {{source}, {}}, {}, false));
} else if (IsAssignableFrom(destination_type, source.type())) {
source.SetType(destination_type);
return source;
return scope.Yield(GenerateCopy(source));
} else {
std::stringstream s;
s << "cannot use expression of type " << *source.type()
......@@ -1989,50 +1923,18 @@ VisitResult ImplementationVisitor::GenerateImplicitConvert(
}
}
std::string ImplementationVisitor::NewTempVariable() {
std::string name("t");
name += std::to_string(next_temp_++);
return name;
}
std::string ImplementationVisitor::GenerateNewTempVariable(const Type* type) {
std::string temp = NewTempVariable();
GenerateIndent();
source_out() << type->GetGeneratedTypeName() << " " << temp << " = ";
return temp;
}
void ImplementationVisitor::GenerateLabelDefinition(Label* label,
AstNode* node) {
std::string label_string = label->generated();
std::string label_string_impl = label_string + "_impl";
GenerateIndent();
source_out() << "Label " + label_string_impl + "(this";
if (node != nullptr) {
source_out() << ", ";
GenerateChangedVarsFromControlSplit(node);
}
if (label->IsDeferred()) {
source_out() << ", compiler::CodeAssemblerLabel::kDeferred";
} else {
source_out() << ", compiler::CodeAssemblerLabel::kNonDeferred";
}
source_out() << ");\n";
GenerateIndent();
source_out() << "Label* " + label_string + " = &" << label_string_impl
<< ";\n";
GenerateIndent();
source_out() << "USE(" << label_string << ");\n";
void ImplementationVisitor::CreateBlockForLabel(Label* label,
Stack<const Type*> stack) {
label->set_block(assembler().NewBlock(std::move(stack), label->IsDeferred()));
}
void ImplementationVisitor::GenerateLabelBind(Label* label) {
GenerateIndent();
source_out() << "BIND(" << label->generated() << ");\n";
assembler().Bind(label->block());
}
void ImplementationVisitor::GenerateLabelGoto(Label* label) {
GenerateIndent();
source_out() << "Goto(" << label->generated() << ");\n";
StackRange ImplementationVisitor::GenerateLabelGoto(
Label* label, base::Optional<StackRange> arguments) {
return assembler().Goto(label->block(), arguments ? arguments->Size() : 0);
}
std::vector<Label*> ImplementationVisitor::LabelsFromIdentifiers(
......@@ -2045,6 +1947,29 @@ std::vector<Label*> ImplementationVisitor::LabelsFromIdentifiers(
return result;
}
StackRange ImplementationVisitor::LowerParameter(
const Type* type, const std::string& parameter_name,
Stack<std::string>* lowered_parameters) {
if (type->IsStructType()) {
const StructType* struct_type = StructType::cast(type);
StackRange range = lowered_parameters->TopRange(0);
for (auto& field : struct_type->fields()) {
StackRange parameter_range = LowerParameter(
field.type, parameter_name + "." + field.name, lowered_parameters);
range.Extend(parameter_range);
}
return range;
} else {
lowered_parameters->Push(parameter_name);
return lowered_parameters->TopRange(1);
}
}
std::string ImplementationVisitor::ExternalLabelParameterName(Label* label,
size_t i) {
return label->external_label_name() + "_parameter_" + std::to_string(i);
}
} // namespace torque
} // namespace internal
} // namespace v8
......@@ -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));
}
std::string RValueFlattenStructs(const VisitResult& result);
LocationReference GetLocationReference(ElementAccessExpression* expr);
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();
}
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()));
}
~ScopedIndent() {
visitor_->indent_--;
visitor_->GenerateIndent();
visitor_->source_out() << "}";
if (new_lines_) visitor_->source_out() << "\n";
~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
......
// 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
// 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);
}
}
} // 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 "implicit_cast<" + type()->GetGeneratedTypeName() + ">(" + 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
......
// 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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment