// 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 #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 \n"; header << " using TNode = compiler::TNode;\n"; header << " template \n"; header << " using SloppyTNode = compiler::SloppyTNode;\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(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 " << val->value() << " = UncheckedCast(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 " << 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 init_result; if (stmt->initializer) { init_result = Visit(*stmt->initializer); } base::Optional 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(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 blocks = {stmt->if_true}; std::vector 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 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 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 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({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 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 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& 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& 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 candidates; std::vector 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* 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& changed_vars = global_context_.GetControlSplitChangedVariables( node, declarations()->GetCurrentSpecializationTypeNamesVector()); std::set flattened_vars; for (auto v : changed_vars) { GetFlattenedStructsVars(v, &flattened_vars); } std::vector 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 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(location)); case AstNode::Kind::kFieldAccessExpression: return GetLocationReference( static_cast(location)); case AstNode::Kind::kElementAccessExpression: return GetLocationReference( static_cast(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(location), reference); case AstNode::Kind::kFieldAccessExpression: return GenerateFetchFromLocation( static_cast(location), reference); case AstNode::Kind::kElementAccessExpression: return GenerateFetchFromLocation( static_cast(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& 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& type, const base::Optional& 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 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 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& blocks, const std::vector& 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& statement_labels, const std::vector& 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 ImplementationVisitor::LabelsFromIdentifiers( const std::vector& names) { std::vector result; for (auto name : names) { result.push_back(declarations()->LookupLabel(name)); } return result; } } // namespace torque } // namespace internal } // namespace v8