// Copyright 2017 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 <algorithm>
#include "src/torque/implementation-visitor.h"
#include "src/torque/parameter-difference.h"
namespace v8 {
namespace internal {
namespace torque {
VisitResult ImplementationVisitor::Visit(Expression* expr) {
CurrentSourcePosition::Scope scope(expr->pos);
switch (expr->kind) {
#define ENUM_ITEM(name) \
case AstNode::Kind::k##name: \
return Visit(name::cast(expr));
AST_EXPRESSION_NODE_KIND_LIST(ENUM_ITEM)
#undef ENUM_ITEM
default:
UNREACHABLE();
}
}
const Type* ImplementationVisitor::Visit(Statement* stmt) {
CurrentSourcePosition::Scope scope(stmt->pos);
GenerateIndent();
source_out() << "// " << CurrentPositionAsString() << "\n";
switch (stmt->kind) {
#define ENUM_ITEM(name) \
case AstNode::Kind::k##name: \
return Visit(name::cast(stmt));
AST_STATEMENT_NODE_KIND_LIST(ENUM_ITEM)
#undef ENUM_ITEM
default:
UNIMPLEMENTED();
}
UNREACHABLE();
return nullptr;
}
void ImplementationVisitor::Visit(Declaration* decl) {
CurrentSourcePosition::Scope scope(decl->pos);
switch (decl->kind) {
#define ENUM_ITEM(name) \
case AstNode::Kind::k##name: \
return Visit(name::cast(decl));
AST_DECLARATION_NODE_KIND_LIST(ENUM_ITEM)
#undef ENUM_ITEM
default:
UNIMPLEMENTED();
}
}
void ImplementationVisitor::Visit(CallableNode* decl,
const Signature& signature, Statement* body) {
switch (decl->kind) {
#define ENUM_ITEM(name) \
case AstNode::Kind::k##name: \
return Visit(name::cast(decl), signature, body);
AST_CALLABLE_NODE_KIND_LIST(ENUM_ITEM)
#undef ENUM_ITEM
default:
UNIMPLEMENTED();
}
}
void ImplementationVisitor::BeginModuleFile(Module* module) {
std::ostream& source = module->source_stream();
std::ostream& header = module->header_stream();
if (module->IsDefault()) {
source << "#include \"src/code-stub-assembler.h\"";
} else {
source << "#include \"src/builtins/builtins-" +
DashifyString(module->name()) + "-gen.h\"";
}
source << "\n";
source << "#include \"src/builtins/builtins-utils-gen.h\"\n";
source << "#include \"src/builtins/builtins.h\"\n";
source << "#include \"src/code-factory.h\"\n";
source << "#include \"src/elements-kind.h\"\n";
source << "#include \"src/heap/factory-inl.h\"\n";
source << "#include \"src/objects.h\"\n";
source << "#include \"src/objects/bigint.h\"\n";
source << "#include \"builtins-" + DashifyString(module->name()) +
"-from-dsl-gen.h\"\n\n";
source << "namespace v8 {\n"
<< "namespace internal {\n"
<< "\n"
<< "using Node = compiler::Node;\n"
<< "\n";
std::string upper_name(module->name());
transform(upper_name.begin(), upper_name.end(), upper_name.begin(),
::toupper);
std::string headerDefine =
std::string("V8_TORQUE_") + upper_name + "_FROM_DSL_BASE_H__";
header << "#ifndef " << headerDefine << "\n";
header << "#define " << headerDefine << "\n\n";
if (module->IsDefault()) {
header << "#include \"src/code-stub-assembler.h\"";
} else {
header << "#include \"src/builtins/builtins-" +
DashifyString(module->name()) + "-gen.h\"\n";
}
header << "\n\n ";
header << "namespace v8 {\n"
<< "namespace internal {\n"
<< "\n";
header << "class " << GetDSLAssemblerName(module) << ": public "
<< GetBaseAssemblerName(module) << " {\n";
header << " public:\n";
header << " explicit " << GetDSLAssemblerName(module)
<< "(compiler::CodeAssemblerState* state) : "
<< GetBaseAssemblerName(module) << "(state) {}\n";
header << "\n";
header << " using Node = compiler::Node;\n";
header << " template <class T>\n";
header << " using TNode = compiler::TNode<T>;\n";
header << " template <class T>\n";
header << " using SloppyTNode = compiler::SloppyTNode<T>;\n\n";
}
void ImplementationVisitor::EndModuleFile(Module* module) {
std::ostream& source = module->source_stream();
std::ostream& header = module->header_stream();
DrainSpecializationQueue();
std::string upper_name(module->name());
transform(upper_name.begin(), upper_name.end(), upper_name.begin(),
::toupper);
std::string headerDefine =
std::string("V8_TORQUE_") + upper_name + "_FROM_DSL_BASE_H__";
source << "} // namespace internal\n"
<< "} // namespace v8\n"
<< "\n";
header << "};\n\n";
header << "} // namespace internal\n"
<< "} // namespace v8\n"
<< "\n";
header << "#endif // " << headerDefine << "\n";
}
void ImplementationVisitor::Visit(ModuleDeclaration* decl) {
Module* module = decl->GetModule();
Module* saved_module = module_;
module_ = module;
Declarations::ModuleScopeActivator scope(declarations(), decl->GetModule());
for (auto& child : decl->declarations) Visit(child);
module_ = saved_module;
}
void ImplementationVisitor::Visit(ConstDeclaration* decl) {
Signature signature = MakeSignatureFromReturnType(decl->type);
std::string name = decl->name;
header_out() << " ";
GenerateFunctionDeclaration(header_out(), "", name, signature, {});
header_out() << ";\n";
GenerateFunctionDeclaration(source_out(),
GetDSLAssemblerName(CurrentModule()) + "::", name,
signature, {});
source_out() << " {\n";
DCHECK(!signature.return_type->IsVoidOrNever());
VisitResult expression_result = Visit(decl->expression);
VisitResult return_result =
GenerateImplicitConvert(signature.return_type, expression_result);
GenerateIndent();
source_out() << "return " << return_result.RValue() << ";\n";
source_out() << "}\n\n";
}
void ImplementationVisitor::Visit(StructDeclaration* decl) {
header_out() << " struct " << decl->name << " {\n";
const StructType* struct_type =
static_cast<const StructType*>(declarations()->LookupType(decl->name));
for (auto& field : struct_type->fields()) {
header_out() << " " << field.type->GetGeneratedTypeName();
header_out() << " " << field.name << ";\n";
}
header_out() << " } "
<< ";\n";
}
void ImplementationVisitor::Visit(TorqueMacroDeclaration* decl,
const Signature& sig, Statement* body) {
Signature signature = MakeSignature(decl->signature.get());
std::string name = GetGeneratedCallableName(
decl->name, declarations()->GetCurrentSpecializationTypeNamesVector());
const TypeVector& list = signature.types();
Macro* macro = declarations()->LookupMacro(name, list);
CurrentCallableActivator activator(global_context_, macro, decl);
if (body != nullptr) {
header_out() << " ";
GenerateMacroFunctionDeclaration(header_out(), "", macro);
header_out() << ";\n";
GenerateMacroFunctionDeclaration(
source_out(), GetDSLAssemblerName(CurrentModule()) + "::", macro);
source_out() << " {\n";
const Variable* result_var = nullptr;
if (macro->HasReturnValue()) {
result_var =
GeneratePredeclaredVariableDeclaration(kReturnValueVariable, {});
}
Label* macro_end = declarations()->DeclareLabel("macro_end");
GenerateLabelDefinition(macro_end, decl);
const Type* result = Visit(body);
if (result->IsNever()) {
if (!macro->signature().return_type->IsNever() && !macro->HasReturns()) {
std::stringstream s;
s << "macro " << decl->name
<< " that never returns must have return type never";
ReportError(s.str());
}
} else {
if (macro->signature().return_type->IsNever()) {
std::stringstream s;
s << "macro " << decl->name
<< " has implicit return at end of its declartion but return type "
"never";
ReportError(s.str());
} else if (!macro->signature().return_type->IsVoid()) {
std::stringstream s;
s << "macro " << decl->name
<< " expects to return a value but doesn't on all paths";
ReportError(s.str());
}
}
if (macro->HasReturns()) {
if (!result->IsNever()) {
GenerateLabelGoto(macro_end);
}
GenerateLabelBind(macro_end);
}
if (result_var != nullptr) {
GenerateIndent();
source_out() << "return "
<< RValueFlattenStructs(
VisitResult(result_var->type(), result_var))
<< ";\n";
}
source_out() << "}\n\n";
}
}
void ImplementationVisitor::Visit(TorqueBuiltinDeclaration* decl,
const Signature& signature, Statement* body) {
std::string name = GetGeneratedCallableName(
decl->name, declarations()->GetCurrentSpecializationTypeNamesVector());
source_out() << "TF_BUILTIN(" << name << ", "
<< GetDSLAssemblerName(CurrentModule()) << ") {\n";
Builtin* builtin = declarations()->LookupBuiltin(name);
CurrentCallableActivator activator(global_context_, builtin, decl);
// Context
const Value* val =
declarations()->LookupValue(decl->signature->parameters.names[0]);
GenerateIndent();
source_out() << "TNode<Context> " << val->value()
<< " = UncheckedCast<Context>(Parameter("
<< "Descriptor::kContext));\n";
GenerateIndent();
source_out() << "USE(" << val->value() << ");\n";
size_t first = 1;
if (builtin->IsVarArgsJavaScript()) {
assert(decl->signature->parameters.has_varargs);
ExternConstant* arguments =
ExternConstant::cast(declarations()->LookupValue(
decl->signature->parameters.arguments_variable));
std::string arguments_name = arguments->value();
GenerateIndent();
source_out()
<< "Node* argc = Parameter(Descriptor::kJSActualArgumentsCount);\n";
GenerateIndent();
source_out() << "CodeStubArguments arguments_impl(this, "
"ChangeInt32ToIntPtr(argc));\n";
const Value* receiver =
declarations()->LookupValue(decl->signature->parameters.names[1]);
GenerateIndent();
source_out() << "TNode<Object> " << receiver->value()
<< " = arguments_impl.GetReceiver();\n";
GenerateIndent();
source_out() << "auto arguments = &arguments_impl;\n";
GenerateIndent();
source_out() << "USE(arguments);\n";
GenerateIndent();
source_out() << "USE(" << receiver->value() << ");\n";
first = 2;
}
GenerateParameterList(decl->signature->parameters.names, first);
Visit(body);
source_out() << "}\n\n";
}
const Type* ImplementationVisitor::Visit(VarDeclarationStatement* stmt) {
base::Optional<VisitResult> init_result;
if (stmt->initializer) {
init_result = Visit(*stmt->initializer);
}
base::Optional<const Type*> type;
if (stmt->type) type = declarations()->GetType(*stmt->type);
GenerateVariableDeclaration(stmt, stmt->name, stmt->const_qualified, type,
init_result);
return TypeOracle::GetVoidType();
}
const Type* ImplementationVisitor::Visit(TailCallStatement* stmt) {
return Visit(stmt->call, true).type();
}
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 << " = [=]() ";
{
ScopedIndent indent(this, false);
source_out() << "\n";
right = Visit(expr->if_false);
GenerateIndent();
source_out() << "return " << RValueFlattenStructs(right) << ";\n";
}
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);
{
ScopedIndent indent(this);
Declarations::NodeScopeActivator scope(declarations(), expr->condition);
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);
}
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);
}
return VisitResult(common_type, result);
}
VisitResult ImplementationVisitor::Visit(LogicalOrExpression* expr) {
VisitResult left_result;
{
Declarations::NodeScopeActivator scope(declarations(), expr->left);
Label* false_label = declarations()->LookupLabel(kFalseLabelName);
GenerateLabelDefinition(false_label);
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";
} else if (!left_result.type()->IsConstexprBool()) {
GenerateLabelBind(false_label);
}
}
VisitResult right_result = Visit(expr->right);
if (right_result.type() != left_result.type()) {
std::stringstream stream;
stream << "types of left and right expression of logical OR don't match (\""
<< *left_result.type() << "\" vs. \"" << *right_result.type()
<< "\")";
ReportError(stream.str());
}
if (left_result.type()->IsConstexprBool()) {
return VisitResult(left_result.type(),
std::string("(") + RValueFlattenStructs(left_result) +
" || " + RValueFlattenStructs(right_result) + ")");
} else {
return right_result;
}
}
VisitResult ImplementationVisitor::Visit(LogicalAndExpression* expr) {
VisitResult left_result;
{
Declarations::NodeScopeActivator scope(declarations(), expr->left);
Label* true_label = declarations()->LookupLabel(kTrueLabelName);
GenerateLabelDefinition(true_label);
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";
} else if (!left_result.type()->IsConstexprBool()) {
GenerateLabelBind(true_label);
}
}
VisitResult right_result = Visit(expr->right);
if (right_result.type() != left_result.type()) {
std::stringstream stream;
stream
<< "types of left and right expression of logical AND don't match (\""
<< *left_result.type() << "\" vs. \"" << *right_result.type() << "\")";
ReportError(stream.str());
}
if (left_result.type()->IsConstexprBool()) {
return VisitResult(left_result.type(),
std::string("(") + RValueFlattenStructs(left_result) +
" && " + RValueFlattenStructs(right_result) + ")");
} else {
return right_result;
}
}
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);
}
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;
}
VisitResult ImplementationVisitor::Visit(AssignmentExpression* expr) {
LocationReference location_ref = GetLocationReference(expr->location);
VisitResult assignment_value;
if (expr->op) {
VisitResult location_value =
GenerateFetchFromLocation(expr->location, 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);
} else {
assignment_value = Visit(expr->value);
GenerateAssignToLocation(expr->location, location_ref, assignment_value);
}
return assignment_value;
}
VisitResult ImplementationVisitor::Visit(NumberLiteralExpression* expr) {
// TODO(tebbi): Do not silently loose precision; support 64bit literals.
double d = std::stod(expr->number.c_str());
int32_t i = static_cast<int32_t>(d);
const Type* result_type =
declarations()->LookupType(CONST_FLOAT64_TYPE_STRING);
if (i == d) {
if ((i >> 30) == (i >> 31)) {
result_type = declarations()->LookupType(CONST_INT31_TYPE_STRING);
} else {
result_type = declarations()->LookupType(CONST_INT32_TYPE_STRING);
}
}
std::string temp = GenerateNewTempVariable(result_type);
source_out() << expr->number << ";\n";
return VisitResult{result_type, temp};
}
VisitResult ImplementationVisitor::Visit(AssumeTypeImpossibleExpression* expr) {
VisitResult result = Visit(expr->expression);
const Type* result_type =
SubtractType(result.type(), declarations()->GetType(expr->excluded_type));
if (result_type->IsNever()) {
ReportError("unreachable code");
}
return VisitResult{result_type, "UncheckedCast<" +
result_type->GetGeneratedTNodeTypeName() +
">(" + result.RValue() + ")"};
}
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};
}
VisitResult ImplementationVisitor::GetBuiltinCode(Builtin* builtin) {
if (builtin->IsExternal() || builtin->kind() != Builtin::kStub) {
ReportError(
"creating function pointers is only allowed for internal builtins with "
"stub linkage");
}
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);
}
VisitResult ImplementationVisitor::Visit(IdentifierExpression* expr) {
std::string name = expr->name;
if (expr->generic_arguments.size() != 0) {
GenericList* generic_list = declarations()->LookupGeneric(expr->name);
for (Generic* generic : generic_list->list()) {
TypeVector specialization_types = GetTypeVector(expr->generic_arguments);
name = GetGeneratedCallableName(name, specialization_types);
CallableNode* callable = generic->declaration()->callable;
QueueGenericSpecialization({generic, specialization_types}, callable,
callable->signature.get(),
generic->declaration()->body);
}
}
if (Builtin* builtin = Builtin::DynamicCast(declarations()->Lookup(name))) {
return GetBuiltinCode(builtin);
}
return GenerateFetchFromLocation(expr, GetLocationReference(expr));
}
const Type* ImplementationVisitor::Visit(GotoStatement* stmt) {
Label* label = declarations()->LookupLabel(stmt->label);
if (stmt->arguments.size() != label->GetParameterCount()) {
std::stringstream stream;
stream << "goto to label has incorrect number of parameters (expected "
<< std::to_string(label->GetParameterCount()) << " found "
<< std::to_string(stmt->arguments.size()) << ")";
ReportError(stream.str());
}
size_t i = 0;
for (Expression* e : stmt->arguments) {
VisitResult result = Visit(e);
Variable* var = label->GetParameter(i++);
GenerateAssignToVariable(var, result);
}
GenerateLabelGoto(label);
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) {
VisitResult expression_result = Visit(stmt->condition);
if (!(expression_result.type() == TypeOracle::GetConstexprBoolType())) {
std::stringstream stream;
stream << "expression should return type constexpr bool "
<< "but returns type " << *expression_result.type();
ReportError(stream.str());
}
const Type* left_result;
const Type* right_result = TypeOracle::GetVoidType();
{
GenerateIndent();
source_out() << "if ((" << RValueFlattenStructs(expression_result)
<< ")) ";
ScopedIndent indent(this, false);
source_out() << "\n";
left_result = Visit(stmt->if_true);
}
if (has_else) {
source_out() << " else ";
ScopedIndent indent(this, false);
source_out() << "\n";
right_result = Visit(*stmt->if_false);
}
if (left_result->IsNever() != right_result->IsNever()) {
std::stringstream stream;
stream << "either both or neither branches in a constexpr if statement "
"must reach their end at"
<< PositionAsString(stmt->pos);
ReportError(stream.str());
}
source_out() << "\n";
return left_result;
} else {
Label* true_label = nullptr;
Label* false_label = nullptr;
{
Declarations::NodeScopeActivator scope(declarations(), &*stmt->condition);
true_label = declarations()->LookupLabel(kTrueLabelName);
GenerateLabelDefinition(true_label);
false_label = declarations()->LookupLabel(kFalseLabelName);
GenerateLabelDefinition(false_label, !has_else ? stmt : nullptr);
}
Label* done_label = nullptr;
bool live = false;
if (has_else) {
done_label = declarations()->DeclarePrivateLabel("if_done_label");
GenerateLabelDefinition(done_label, stmt);
} else {
done_label = false_label;
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)) {
live = true;
}
if (live) {
GenerateLabelBind(done_label);
}
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);
exit_label = declarations()->LookupLabel(kFalseLabelName);
GenerateLabelDefinition(exit_label);
}
Label* header_label = declarations()->DeclarePrivateLabel("header");
GenerateLabelDefinition(header_label, stmt);
GenerateLabelGoto(header_label);
GenerateLabelBind(header_label);
Declarations::NodeScopeActivator scope(declarations(), stmt->body);
BreakContinueActivator activator(global_context_, exit_label, header_label);
GenerateExpressionBranch(stmt->condition, {body_label, exit_label},
{stmt->body}, header_label);
GenerateLabelBind(exit_label);
return TypeOracle::GetVoidType();
}
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()) {
std::stringstream stream;
stream << "statement after non-returning statement";
ReportError(stream.str());
}
type = Visit(s);
}
return type;
}
const Type* ImplementationVisitor::Visit(DebugStatement* stmt) {
#if defined(DEBUG)
GenerateIndent();
source_out() << "Print(\""
<< "halting because of '" << stmt->reason << "' at "
<< PositionAsString(stmt->pos) << "\");\n";
#endif
GenerateIndent();
if (stmt->never_continues) {
source_out() << "Unreachable();\n";
return TypeOracle::GetNeverType();
} else {
source_out() << "DebugBreak();\n";
return TypeOracle::GetVoidType();
}
}
namespace {
std::string FormatAssertSource(const std::string& str) {
// Replace all whitespace characters with a space character.
std::string str_no_newlines = str;
std::replace_if(str_no_newlines.begin(), str_no_newlines.end(),
[](unsigned char c) { return isspace(c); }, ' ');
// str might include indentation, squash multiple space characters into one.
std::string result;
std::unique_copy(str_no_newlines.begin(), str_no_newlines.end(),
std::back_inserter(result),
[](char a, char b) { return a == ' ' && b == ' '; });
return result;
}
} // namespace
const Type* ImplementationVisitor::Visit(AssertStatement* stmt) {
bool do_check = !stmt->debug_only;
#if defined(DEBUG)
do_check = true;
#endif
if (do_check) {
// CSA_ASSERT & co. are not used here on purpose for two reasons. First,
// Torque allows and handles two types of expressions in the if protocol
// automagically, ones that return TNode<BoolT> and those that use the
// BranchIf(..., Label* true, Label* false) idiom. Because the machinery to
// handle this is embedded in the expression handling and to it's not
// possible to make the decision to use CSA_ASSERT or CSA_ASSERT_BRANCH
// isn't trivial up-front. Secondly, on failure, the assert text should be
// the corresponding Torque code, not the -gen.cc code, which would be the
// case when using CSA_ASSERT_XXX.
Label* true_label = nullptr;
Label* false_label = nullptr;
Declarations::NodeScopeActivator scope(declarations(), stmt->expression);
true_label = declarations()->LookupLabel(kTrueLabelName);
GenerateLabelDefinition(true_label);
false_label = declarations()->LookupLabel(kFalseLabelName);
GenerateLabelDefinition(false_label);
VisitResult expression_result = Visit(stmt->expression);
if (expression_result.type() == TypeOracle::GetBoolType()) {
GenerateBranch(expression_result, true_label, false_label);
} else {
if (expression_result.type() != TypeOracle::GetNeverType()) {
std::stringstream s;
s << "unexpected return type " << *expression_result.type()
<< " for branch expression";
ReportError(s.str());
}
}
GenerateLabelBind(false_label);
GenerateIndent();
source_out() << "Print(\""
<< "assert '" << FormatAssertSource(stmt->source)
<< "' failed at " << PositionAsString(stmt->pos) << "\");\n";
GenerateIndent();
source_out() << "Unreachable();\n";
GenerateLabelBind(true_label);
}
return TypeOracle::GetVoidType();
}
const Type* ImplementationVisitor::Visit(ExpressionStatement* stmt) {
const Type* type = Visit(stmt->expression).type();
return type->IsNever() ? type : TypeOracle::GetVoidType();
}
const Type* ImplementationVisitor::Visit(ReturnStatement* stmt) {
Callable* current_callable = global_context_.GetCurrentCallable();
if (current_callable->signature().return_type->IsNever()) {
std::stringstream s;
s << "cannot return from a function with return type never";
ReportError(s.str());
}
Label* end = current_callable->IsMacro()
? declarations()->LookupLabel("macro_end")
: nullptr;
if (current_callable->HasReturnValue()) {
if (!stmt->value) {
std::stringstream s;
s << "return expression needs to be specified for a return type of "
<< *current_callable->signature().return_type;
ReportError(s.str());
}
VisitResult expression_result = Visit(*stmt->value);
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";
} else {
GenerateIndent();
source_out() << "Return(" << RValueFlattenStructs(return_result)
<< ");\n";
}
} else {
UNREACHABLE();
}
} else {
if (stmt->value) {
std::stringstream s;
s << "return expression can't be specified for a void or never return "
"type";
ReportError(s.str());
}
GenerateLabelGoto(end);
}
current_callable->IncrementReturns();
return TypeOracle::GetNeverType();
}
const Type* ImplementationVisitor::Visit(ForOfLoopStatement* stmt) {
Declarations::NodeScopeActivator scope(declarations(), stmt);
VisitResult expression_result = Visit(stmt->iterable);
VisitResult begin = stmt->begin
? Visit(*stmt->begin)
: VisitResult(TypeOracle::GetConstInt31Type(), "0");
VisitResult end = stmt->end
? 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_for_read = {index_var->type(), index_var};
Label* header_label = declarations()->DeclarePrivateLabel("header");
GenerateLabelDefinition(header_label, stmt);
GenerateLabelGoto(header_label);
GenerateLabelBind(header_label);
BreakContinueActivator activator(global_context_, exit_label,
increment_label);
VisitResult result = GenerateCall("<", {{index_for_read, end}, {}});
GenerateBranch(result, body_label, exit_label);
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);
GenerateLabelBind(increment_label);
Arguments increment_args;
increment_args.parameters = {index_for_read,
{TypeOracle::GetConstInt31Type(), "1"}};
VisitResult increment_result = GenerateCall("+", increment_args);
GenerateAssignToVariable(index_var, increment_result);
GenerateLabelGoto(header_label);
GenerateLabelBind(exit_label);
return TypeOracle::GetVoidType();
}
const Type* ImplementationVisitor::Visit(TryLabelStatement* stmt) {
ScopedIndent indent(this);
Label* try_done = declarations()->DeclarePrivateLabel("try_done");
GenerateLabelDefinition(try_done);
const Type* try_result = TypeOracle::GetNeverType();
std::vector<Label*> labels;
// Output labels for the goto handlers and for the merge after the try.
{
// Activate a new scope to see handler labels
Declarations::NodeScopeActivator scope(declarations(), stmt);
for (LabelBlock* block : stmt->label_blocks) {
CurrentSourcePosition::Scope scope(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());
v->Define();
}
++i;
}
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();
}
}
// Make sure that each label clause is actually used. It's not just a friendly
// thing to do, it will cause problems downstream in the compiler if there are
// bound labels that are never jumped to.
auto label_iterator = stmt->label_blocks.begin();
for (auto label : labels) {
CurrentSourcePosition::Scope scope((*label_iterator)->pos);
if (!label->IsUsed()) {
std::stringstream s;
s << "label ";
s << (*label_iterator)->label;
s << " has a handler block but is never referred to in try block";
ReportError(s.str());
}
label_iterator++;
}
// Visit and output the code for each catch 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)) {
try_result = TypeOracle::GetVoidType();
}
if (!try_result->IsNever()) {
GenerateLabelBind(try_done);
}
return try_result;
}
const Type* ImplementationVisitor::Visit(BreakStatement* stmt) {
Label* break_label = global_context_.GetCurrentBreak();
if (break_label == nullptr) {
ReportError("break used outside of loop");
}
GenerateLabelGoto(break_label);
return TypeOracle::GetNeverType();
}
const Type* ImplementationVisitor::Visit(ContinueStatement* stmt) {
Label* continue_label = global_context_.GetCurrentContinue();
if (continue_label == nullptr) {
ReportError("continue used outside of loop");
}
GenerateLabelGoto(continue_label);
return TypeOracle::GetNeverType();
}
const Type* ImplementationVisitor::Visit(ForLoopStatement* stmt) {
Declarations::NodeScopeActivator scope(declarations(), stmt);
if (stmt->var_declaration) Visit(*stmt->var_declaration);
Label* body_label = declarations()->LookupLabel(kTrueLabelName);
GenerateLabelDefinition(body_label);
Label* exit_label = declarations()->LookupLabel(kFalseLabelName);
GenerateLabelDefinition(exit_label);
Label* header_label = declarations()->DeclarePrivateLabel("header");
GenerateLabelDefinition(header_label, stmt);
GenerateLabelGoto(header_label);
GenerateLabelBind(header_label);
// 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;
// The action label is only needed when an action expression was provided.
Label* action_label = nullptr;
if (stmt->action) {
action_label = declarations()->DeclarePrivateLabel("action");
GenerateLabelDefinition(action_label);
// The action expression needs to be executed on a continue.
continue_label = action_label;
}
BreakContinueActivator activator(global_context_, exit_label, continue_label);
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);
} else {
GenerateLabelGoto(body_label);
generate_action =
GenerateLabeledStatementBlocks({stmt->body}, labels, continue_label);
}
if (generate_action && stmt->action) {
ScopedIndent indent(this);
GenerateLabelBind(action_label);
Visit(*stmt->action);
GenerateLabelGoto(header_label);
}
GenerateLabelBind(exit_label);
return TypeOracle::GetVoidType();
}
void ImplementationVisitor::GenerateImplementation(const std::string& dir,
Module* module) {
std::string new_source(module->source());
std::string base_file_name =
"builtins-" + DashifyString(module->name()) + "-from-dsl-gen";
std::string source_file_name = dir + "/" + base_file_name + ".cc";
ReplaceFileContentsIfDifferent(source_file_name, new_source);
std::string new_header(module->header());
std::string header_file_name = dir + "/" + base_file_name + ".h";
ReplaceFileContentsIfDifferent(header_file_name, new_header);
}
std::string ImplementationVisitor::GetBaseAssemblerName(Module* module) {
if (module == global_context_.GetDefaultModule()) {
return "CodeStubAssembler";
} else {
std::string assembler_name(CamelifyString(module->name()) +
"BuiltinsAssembler");
return assembler_name;
}
}
std::string ImplementationVisitor::GetDSLAssemblerName(Module* module) {
std::string assembler_name(CamelifyString(module->name()) +
"BuiltinsFromDSLAssembler");
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(),
macro->signature(), macro->parameter_names());
}
void ImplementationVisitor::GenerateFunctionDeclaration(
std::ostream& o, const std::string& macro_prefix, const std::string& name,
const Signature& signature, const NameVector& parameter_names) {
if (global_context_.verbose()) {
std::cout << "generating source for declaration " << name << "\n";
}
// Quite a hack here. Make sure that TNode is namespace qualified if the
// macro/constant name is also qualified.
std::string return_type_name(signature.return_type->GetGeneratedTypeName());
if (const StructType* struct_type =
StructType::DynamicCast(signature.return_type)) {
o << GetDSLAssemblerName(struct_type->module()) << "::";
} else if (macro_prefix != "" && (return_type_name.length() > 5) &&
(return_type_name.substr(0, 5) == "TNode")) {
o << "compiler::";
}
o << return_type_name;
o << " " << macro_prefix << name << "(";
DCHECK_EQ(signature.types().size(), parameter_names.size());
auto type_iterator = signature.types().begin();
bool first = true;
for (const std::string& name : parameter_names) {
if (!first) {
o << ", ";
}
const Value* parameter = declarations()->LookupValue(name);
const Type* parameter_type = *type_iterator;
const std::string& generated_type_name =
parameter_type->GetGeneratedTypeName();
o << generated_type_name << " " << parameter->value();
type_iterator++;
first = false;
}
for (const LabelDeclaration& label_info : signature.labels) {
Label* label = declarations()->LookupLabel(label_info.name);
if (!first) {
o << ", ";
}
o << "Label* " << label->generated();
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 << ")";
}
namespace {
void PrintMacroSignatures(std::stringstream& s, const std::string& name,
const std::vector<Macro*>& macros) {
for (Macro* m : macros) {
s << "\n " << name;
PrintSignature(s, m->signature(), false);
}
}
void FailMacroLookup(const std::string& reason, const std::string& name,
const Arguments& arguments,
const std::vector<Macro*>& candidates) {
std::stringstream stream;
stream << "\n"
<< reason << ": \n " << name << "("
<< arguments.parameters.GetTypeVector() << ")";
if (arguments.labels.size() != 0) {
stream << " labels ";
for (auto l : arguments.labels) {
PrintLabel(stream, *l, false);
}
}
stream << "\ncandidates are:";
PrintMacroSignatures(stream, name, candidates);
ReportError(stream.str());
}
} // namespace
Callable* ImplementationVisitor::LookupCall(
const std::string& name, const Arguments& arguments,
const TypeVector& specialization_types) {
Callable* result = nullptr;
TypeVector parameter_types(arguments.parameters.GetTypeVector());
bool has_template_arguments = !specialization_types.empty();
std::string mangled_name = name;
if (has_template_arguments) {
mangled_name = GetGeneratedCallableName(name, specialization_types);
}
Declarable* declarable = declarations()->Lookup(mangled_name);
if (declarable->IsBuiltin()) {
result = Builtin::cast(declarable);
} else if (declarable->IsRuntimeFunction()) {
result = RuntimeFunction::cast(declarable);
} else if (declarable->IsMacroList()) {
std::vector<Macro*> candidates;
std::vector<Macro*> macros_with_same_name;
for (Macro* m : MacroList::cast(declarable)->list()) {
bool try_bool_context =
arguments.labels.size() == 0 &&
m->signature().return_type == TypeOracle::GetNeverType();
Label* true_label = nullptr;
Label* false_label = nullptr;
if (try_bool_context) {
true_label = declarations()->TryLookupLabel(kTrueLabelName);
false_label = declarations()->TryLookupLabel(kFalseLabelName);
}
if (IsCompatibleSignature(m->signature(), parameter_types,
arguments.labels) ||
(true_label && false_label &&
IsCompatibleSignature(m->signature(), parameter_types,
{true_label, false_label}))) {
candidates.push_back(m);
} else {
macros_with_same_name.push_back(m);
}
}
if (candidates.empty() && macros_with_same_name.empty()) {
std::stringstream stream;
stream << "no matching declaration found for " << name;
ReportError(stream.str());
} else if (candidates.empty()) {
FailMacroLookup("cannot find macro with name", name, arguments,
macros_with_same_name);
}
auto is_better_candidate = [&](Macro* a, Macro* b) {
return ParameterDifference(a->signature().parameter_types.types,
parameter_types)
.StrictlyBetterThan(ParameterDifference(
b->signature().parameter_types.types, parameter_types));
};
Macro* best = *std::min_element(candidates.begin(), candidates.end(),
is_better_candidate);
for (Macro* candidate : candidates) {
if (candidate != best && !is_better_candidate(best, candidate)) {
FailMacroLookup("ambiguous macro", name, arguments, candidates);
}
}
result = best;
} else {
std::stringstream stream;
stream << "can't call " << declarable->type_name() << " " << name
<< " because it's not callable"
<< ": call parameters were (" << parameter_types << ")";
ReportError(stream.str());
}
size_t caller_size = parameter_types.size();
size_t callee_size = result->signature().types().size();
if (caller_size != callee_size &&
!result->signature().parameter_types.var_args) {
std::stringstream stream;
stream << "parameter count mismatch calling " << *result << " - expected "
<< std::to_string(callee_size) << ", found "
<< std::to_string(caller_size);
ReportError(stream.str());
}
if (has_template_arguments) {
Generic* generic = *result->generic();
CallableNode* callable = generic->declaration()->callable;
if (generic->declaration()->body) {
QueueGenericSpecialization({generic, specialization_types}, callable,
callable->signature.get(),
generic->declaration()->body);
}
}
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;
if (IsAssignableFrom(left, right)) {
common_type = left;
} else if (IsAssignableFrom(right, left)) {
common_type = right;
} else {
common_type = TypeOracle::GetUnionType(left, right);
}
common_type = common_type->NonConstexprVersion();
return common_type;
}
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);
}
VisitResult ImplementationVisitor::Visit(StructExpression* decl) {
const Type* raw_type = declarations()->LookupType(decl->name);
if (!raw_type->IsStructType()) {
std::stringstream s;
s << decl->name << " is not a struct but used like one ";
ReportError(s.str());
}
const StructType* struct_type = StructType::cast(raw_type);
if (struct_type->fields().size() != decl->expressions.size()) {
std::stringstream s;
s << "initializer count mismatch for struct " << decl->name << " (expected "
<< struct_type->fields().size() << ", found " << decl->expressions.size()
<< ")";
ReportError(s.str());
}
std::vector<VisitResult> expression_results;
for (auto& field : struct_type->fields()) {
VisitResult value = Visit(decl->expressions[expression_results.size()]);
value = GenerateImplicitConvert(field.type, value);
expression_results.push_back(value);
}
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);
}
LocationReference ImplementationVisitor::GetLocationReference(
LocationExpression* location) {
switch (location->kind) {
case AstNode::Kind::kIdentifierExpression:
return GetLocationReference(static_cast<IdentifierExpression*>(location));
case AstNode::Kind::kFieldAccessExpression:
return GetLocationReference(
static_cast<FieldAccessExpression*>(location));
case AstNode::Kind::kElementAccessExpression:
return GetLocationReference(
static_cast<ElementAccessExpression*>(location));
default:
UNREACHABLE();
}
}
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),
{}, {});
}
}
return LocationReference(nullptr, result, {});
}
std::string ImplementationVisitor::RValueFlattenStructs(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();
}
}
return result.RValue();
}
VisitResult ImplementationVisitor::GenerateFetchFromLocation(
LocationExpression* location, 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, 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);
}
}
void ImplementationVisitor::GenerateAssignToVariable(Variable* var,
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});
}
}
} else {
VisitResult casted_value = GenerateImplicitConvert(var->type(), value);
GenerateIndent();
VisitResult var_value = {var->type(), var};
source_out() << var_value.LValue() << " = "
<< RValueFlattenStructs(casted_value) << ";\n";
}
var->Define();
}
void ImplementationVisitor::GenerateAssignToLocation(
LocationExpression* location, const LocationReference& reference,
VisitResult assignment_value) {
if (reference.value != nullptr) {
Value* value = reference.value;
Variable* var = Variable::cast(value);
if (var->IsConst()) {
std::stringstream s;
s << "\"" << var->name()
<< "\" is declared const (maybe implicitly) and cannot be assigned to";
ReportError(s.str());
}
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}, {}});
}
}
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)));
}
} 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);
}
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));
} 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 (auto p : list) {
if (first == 0) {
GenerateParameter(p);
} else {
first--;
}
}
}
VisitResult ImplementationVisitor::GeneratePointerCall(
Expression* callee, const Arguments& arguments, bool is_tailcall) {
TypeVector parameter_types(arguments.parameters.GetTypeVector());
VisitResult callee_result = Visit(callee);
if (!callee_result.type()->IsFunctionPointerType()) {
std::stringstream stream;
stream << "Expected a function pointer type but found "
<< *callee_result.type();
ReportError(stream.str());
}
const FunctionPointerType* type =
FunctionPointerType::cast(callee_result.type());
if (type->parameter_types().size() != parameter_types.size()) {
std::stringstream stream;
stream << "parameter count mismatch calling function pointer with Type: "
<< *type << " - expected "
<< std::to_string(type->parameter_types().size()) << ", found "
<< std::to_string(parameter_types.size());
ReportError(stream.str());
}
ParameterTypes types{type->parameter_types(), false};
Signature sig;
sig.parameter_types = types;
if (!IsCompatibleSignature(sig, parameter_types, {})) {
std::stringstream stream;
stream << "parameters do not match function pointer signature. Expected: ("
<< type->parameter_types() << ") but got: (" << parameter_types
<< ")";
ReportError(stream.str());
}
std::vector<std::string> variables;
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() << ">(";
}
}
Builtin* example_builtin =
declarations()->FindSomeInternalBuiltinWithType(type);
if (!example_builtin) {
std::stringstream stream;
stream << "unable to find any builtin with type \"" << *type << "\"";
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) << ", ";
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() << ")";
}
source_out() << ");\n";
return VisitResult(type->return_type(), result_variable_name);
}
VisitResult ImplementationVisitor::GenerateCall(
const std::string& callable_name, Arguments arguments,
const TypeVector& specialization_types, bool is_tailcall) {
Callable* callable =
LookupCall(callable_name, arguments, specialization_types);
// Operators used in a branching context can also be function calls that never
// return but have a True and False label
if (arguments.labels.size() == 0 &&
callable->signature().labels.size() == 2) {
Label* true_label = declarations()->LookupLabel(kTrueLabelName);
arguments.labels.push_back(true_label);
Label* false_label = declarations()->LookupLabel(kFalseLabelName);
arguments.labels.push_back(false_label);
}
const Type* result_type = callable->signature().return_type;
std::vector<std::string> variables;
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 =
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() << ", ";
} else {
source_out() << "CallRuntime(Runtime::k" << callable->name() << ", ";
}
} 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;
s << "unexpected number of otherwise labels for " << callable->name()
<< " (expected " << std::to_string(label_count) << " found "
<< std::to_string(arguments.labels.size()) << ")";
ReportError(s.str());
}
for (size_t i = 0; i < label_count; ++i) {
if (total_parameters++ != 0) {
source_out() << ", ";
}
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());
}
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());
}
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() << ")";
}
source_out() << ");\n";
return VisitResult(result_type, result_variable_name);
}
void ImplementationVisitor::Visit(StandardDeclaration* decl) {
Signature signature = MakeSignature(decl->callable->signature.get());
Visit(decl->callable, signature, decl->body);
}
void ImplementationVisitor::Visit(SpecializationDeclaration* decl) {
Signature signature_with_types = MakeSignature(decl->signature.get());
Declarations::NodeScopeActivator specialization_activator(declarations(),
decl);
GenericList* generic_list = declarations()->LookupGeneric(decl->name);
for (Generic* generic : generic_list->list()) {
CallableNode* callable = generic->declaration()->callable;
Signature generic_signature_with_types =
MakeSignature(callable->signature.get());
if (signature_with_types.HasSameTypesAs(generic_signature_with_types)) {
TypeVector specialization_types = GetTypeVector(decl->generic_parameters);
SpecializeGeneric({{generic, specialization_types},
callable,
decl->signature.get(),
decl->body,
decl->pos});
return;
}
}
// Because the DeclarationVisitor already performed the same lookup
// as above to find aspecialization match and already threw if it didn't
// find one, failure to find a match here should never happen.
// TODO(danno): Remember the specialization found in the declaration visitor
// so that the lookup doesn't have to be repeated here.
UNREACHABLE();
}
VisitResult ImplementationVisitor::Visit(CallExpression* expr,
bool is_tailcall) {
Arguments arguments;
std::string name = expr->callee.name;
TypeVector specialization_types =
GetTypeVector(expr->callee.generic_arguments);
bool has_template_arguments = !specialization_types.empty();
for (Expression* arg : expr->arguments)
arguments.parameters.push_back(Visit(arg));
arguments.labels = LabelsFromIdentifiers(expr->labels);
VisitResult result;
if (!has_template_arguments &&
declarations()->Lookup(expr->callee.name)->IsValue()) {
result = 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";
}
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) {
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);
live = true;
}
}
return live;
}
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";
}
bool ImplementationVisitor::GenerateExpressionBranch(
Expression* expression, const std::vector<Label*>& statement_labels,
const std::vector<Statement*>& statement_blocks, Label* merge_label) {
// Activate a new scope to define True/False catch labels
Declarations::NodeScopeActivator scope(declarations(), expression);
VisitResult expression_result = Visit(expression);
if (expression_result.type() == TypeOracle::GetBoolType()) {
GenerateBranch(expression_result, statement_labels[0], statement_labels[1]);
} else {
if (expression_result.type() != TypeOracle::GetNeverType()) {
std::stringstream s;
s << "unexpected return type " << *expression_result.type()
<< " for branch expression";
ReportError(s.str());
}
}
return GenerateLabeledStatementBlocks(statement_blocks, statement_labels,
merge_label);
}
VisitResult ImplementationVisitor::GenerateImplicitConvert(
const Type* destination_type, VisitResult source) {
if (destination_type == source.type()) {
return source;
}
if (TypeOracle::IsImplicitlyConvertableFrom(destination_type,
source.type())) {
std::string name =
GetGeneratedCallableName(kFromConstexprMacroName, {destination_type});
return GenerateCall(name, {{source}, {}}, {}, false);
} else if (IsAssignableFrom(destination_type, source.type())) {
source.SetType(destination_type);
return source;
} else {
std::stringstream s;
s << "cannot use expression of type " << *source.type()
<< " as a value of type " << *destination_type;
ReportError(s.str());
}
}
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);
}
source_out() << ");\n";
GenerateIndent();
source_out() << "Label* " + label_string + " = &" << label_string_impl
<< ";\n";
GenerateIndent();
source_out() << "USE(" << label_string << ");\n";
}
void ImplementationVisitor::GenerateLabelBind(Label* label) {
GenerateIndent();
source_out() << "BIND(" << label->generated() << ");\n";
}
void ImplementationVisitor::GenerateLabelGoto(Label* label) {
GenerateIndent();
source_out() << "Goto(" << label->generated() << ");\n";
}
std::vector<Label*> ImplementationVisitor::LabelsFromIdentifiers(
const std::vector<std::string>& names) {
std::vector<Label*> result;
for (auto name : names) {
result.push_back(declarations()->LookupLabel(name));
}
return result;
}
} // namespace torque
} // namespace internal
} // namespace v8