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