// Copyright 2018 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/torque/csa-generator.h" #include "src/common/globals.h" #include "src/torque/type-oracle.h" #include "src/torque/utils.h" namespace v8 { namespace internal { namespace torque { base::Optional<Stack<std::string>> CSAGenerator::EmitGraph( Stack<std::string> parameters) { for (Block* block : cfg_.blocks()) { out_ << " compiler::CodeAssemblerParameterizedLabel<"; PrintCommaSeparatedList(out_, block->InputTypes(), [](const Type* t) { return t->GetGeneratedTNodeTypeName(); }); out_ << "> " << BlockName(block) << "(&ca_, compiler::CodeAssemblerLabel::" << (block->IsDeferred() ? "kDeferred" : "kNonDeferred") << ");\n"; } EmitInstruction(GotoInstruction{cfg_.start()}, ¶meters); for (Block* block : cfg_.blocks()) { if (cfg_.end() && *cfg_.end() == block) continue; out_ << "\n if (" << BlockName(block) << ".is_used()) {\n"; EmitBlock(block); out_ << " }\n"; } if (cfg_.end()) { out_ << "\n"; return EmitBlock(*cfg_.end()); } return base::nullopt; } Stack<std::string> CSAGenerator::EmitBlock(const Block* block) { Stack<std::string> stack; for (const Type* t : block->InputTypes()) { stack.Push(FreshNodeName()); out_ << " compiler::TNode<" << t->GetGeneratedTNodeTypeName() << "> " << stack.Top() << ";\n"; } out_ << " ca_.Bind(&" << BlockName(block); for (const std::string& name : stack) { out_ << ", &" << name; } out_ << ");\n"; for (const Instruction& instruction : block->instructions()) { EmitInstruction(instruction, &stack); } return stack; } void CSAGenerator::EmitSourcePosition(SourcePosition pos, bool always_emit) { const std::string& file = SourceFileMap::AbsolutePath(pos.source); if (always_emit || !previous_position_.CompareStartIgnoreColumn(pos)) { // Lines in Torque SourcePositions are zero-based, while the // CodeStubAssembler and downwind systems are one-based. out_ << " ca_.SetSourcePosition(\"" << file << "\", " << (pos.start.line + 1) << ");\n"; previous_position_ = pos; } } void CSAGenerator::EmitInstruction(const Instruction& instruction, Stack<std::string>* stack) { EmitSourcePosition(instruction->pos); switch (instruction.kind()) { #define ENUM_ITEM(T) \ case InstructionKind::k##T: \ return EmitInstruction(instruction.Cast<T>(), stack); TORQUE_INSTRUCTION_LIST(ENUM_ITEM) #undef ENUM_ITEM } } void CSAGenerator::EmitInstruction(const PeekInstruction& instruction, Stack<std::string>* stack) { stack->Push(stack->Peek(instruction.slot)); } void CSAGenerator::EmitInstruction(const PokeInstruction& instruction, Stack<std::string>* stack) { stack->Poke(instruction.slot, stack->Top()); stack->Pop(); } void CSAGenerator::EmitInstruction(const DeleteRangeInstruction& instruction, Stack<std::string>* stack) { stack->DeleteRange(instruction.range); } void CSAGenerator::EmitInstruction( const PushUninitializedInstruction& instruction, Stack<std::string>* stack) { // TODO(tebbi): This can trigger an error in CSA if it is used. Instead, we // should prevent usage of uninitialized in the type system. This // requires "if constexpr" being evaluated at Torque time. stack->Push("ca_.Uninitialized<" + instruction.type->GetGeneratedTNodeTypeName() + ">()"); } void CSAGenerator::EmitInstruction( const PushBuiltinPointerInstruction& instruction, Stack<std::string>* stack) { stack->Push("ca_.UncheckedCast<BuiltinPtr>(ca_.SmiConstant(Builtins::k" + instruction.external_name + "))"); } void CSAGenerator::EmitInstruction( const NamespaceConstantInstruction& instruction, Stack<std::string>* stack) { const Type* type = instruction.constant->type(); std::vector<std::string> results; for (const Type* lowered : LowerType(type)) { results.push_back(FreshNodeName()); stack->Push(results.back()); out_ << " compiler::TNode<" << lowered->GetGeneratedTNodeTypeName() << "> " << stack->Top() << ";\n"; out_ << " USE(" << stack->Top() << ");\n"; } out_ << " "; if (type->IsStructType()) { out_ << "std::tie("; PrintCommaSeparatedList(out_, results); out_ << ") = "; } else if (results.size() == 1) { out_ << results[0] << " = "; } out_ << instruction.constant->external_name() << "(state_)"; if (type->IsStructType()) { out_ << ".Flatten();\n"; } else { out_ << ";\n"; } } void CSAGenerator::ProcessArgumentsCommon( const TypeVector& parameter_types, std::vector<std::string>* args, std::vector<std::string>* constexpr_arguments, Stack<std::string>* stack) { for (auto it = parameter_types.rbegin(); it != parameter_types.rend(); ++it) { const Type* type = *it; VisitResult arg; if (type->IsConstexpr()) { args->push_back(std::move(constexpr_arguments->back())); constexpr_arguments->pop_back(); } else { std::stringstream s; size_t slot_count = LoweredSlotCount(type); VisitResult arg = VisitResult(type, stack->TopRange(slot_count)); EmitCSAValue(arg, *stack, s); args->push_back(s.str()); stack->PopMany(slot_count); } } std::reverse(args->begin(), args->end()); } void CSAGenerator::EmitInstruction(const CallIntrinsicInstruction& instruction, Stack<std::string>* stack) { std::vector<std::string> constexpr_arguments = instruction.constexpr_arguments; std::vector<std::string> args; TypeVector parameter_types = instruction.intrinsic->signature().parameter_types.types; ProcessArgumentsCommon(parameter_types, &args, &constexpr_arguments, stack); Stack<std::string> pre_call_stack = *stack; const Type* return_type = instruction.intrinsic->signature().return_type; std::vector<std::string> results; for (const Type* type : LowerType(return_type)) { results.push_back(FreshNodeName()); stack->Push(results.back()); out_ << " compiler::TNode<" << type->GetGeneratedTNodeTypeName() << "> " << stack->Top() << ";\n"; out_ << " USE(" << stack->Top() << ");\n"; } out_ << " "; if (return_type->IsStructType()) { out_ << "std::tie("; PrintCommaSeparatedList(out_, results); out_ << ") = "; } else { if (results.size() == 1) { out_ << results[0] << " = "; } } if (instruction.intrinsic->ExternalName() == "%RawDownCast") { if (parameter_types.size() != 1) { ReportError("%RawDownCast must take a single parameter"); } if (!return_type->IsSubtypeOf(parameter_types[0])) { ReportError("%RawDownCast error: ", *return_type, " is not a subtype of ", *parameter_types[0]); } if (return_type->IsSubtypeOf(TypeOracle::GetTaggedType())) { if (return_type->GetGeneratedTNodeTypeName() != parameter_types[0]->GetGeneratedTNodeTypeName()) { out_ << "TORQUE_CAST"; } } } else if (instruction.intrinsic->ExternalName() == "%FromConstexpr") { if (parameter_types.size() != 1 || !parameter_types[0]->IsConstexpr()) { ReportError( "%FromConstexpr must take a single parameter with constexpr " "type"); } if (return_type->IsConstexpr()) { ReportError("%FromConstexpr must return a non-constexpr type"); } if (return_type->IsSubtypeOf(TypeOracle::GetSmiType())) { out_ << "ca_.SmiConstant"; } else if (return_type->IsSubtypeOf(TypeOracle::GetNumberType())) { out_ << "ca_.NumberConstant"; } else if (return_type->IsSubtypeOf(TypeOracle::GetStringType())) { out_ << "ca_.StringConstant"; } else if (return_type->IsSubtypeOf(TypeOracle::GetObjectType())) { ReportError( "%FromConstexpr cannot cast to subclass of HeapObject unless it's a " "String or Number"); } else if (return_type->IsSubtypeOf(TypeOracle::GetIntPtrType())) { out_ << "ca_.IntPtrConstant"; } else if (return_type->IsSubtypeOf(TypeOracle::GetUIntPtrType())) { out_ << "ca_.UintPtrConstant"; } else if (return_type->IsSubtypeOf(TypeOracle::GetInt32Type())) { out_ << "ca_.Int32Constant"; } else { std::stringstream s; s << "%FromConstexpr does not support return type " << *return_type; ReportError(s.str()); } } else if (instruction.intrinsic->ExternalName() == "%GetAllocationBaseSize") { if (instruction.specialization_types.size() != 1) { ReportError( "incorrect number of specialization classes for " "%GetAllocationBaseSize (should be one)"); } const ClassType* class_type = ClassType::cast(instruction.specialization_types[0]); // Special case classes that may not always have a fixed size (e.g. // JSObjects). Their size must be fetched from the map. if (class_type != TypeOracle::GetJSObjectType()) { out_ << "CodeStubAssembler(state_).IntPtrConstant(("; args[0] = std::to_string(class_type->size()); } else { out_ << "CodeStubAssembler(state_).TimesTaggedSize(CodeStubAssembler(" "state_).LoadMapInstanceSizeInWords("; } } else if (instruction.intrinsic->ExternalName() == "%Allocate") { out_ << "ca_.UncheckedCast<" << return_type->GetGeneratedTNodeTypeName() << ">(CodeStubAssembler(state_).Allocate"; } else if (instruction.intrinsic->ExternalName() == "%GetStructMap") { out_ << "CodeStubAssembler(state_).GetStructMap"; } else { ReportError("no built in intrinsic with name " + instruction.intrinsic->ExternalName()); } out_ << "("; PrintCommaSeparatedList(out_, args); if (instruction.intrinsic->ExternalName() == "%Allocate") out_ << ")"; if (instruction.intrinsic->ExternalName() == "%GetAllocationBaseSize") out_ << "))"; if (return_type->IsStructType()) { out_ << ").Flatten();\n"; } else { out_ << ");\n"; } if (instruction.intrinsic->ExternalName() == "%Allocate") { out_ << " CodeStubAssembler(state_).InitializeFieldsWithRoot(" << results[0] << ", "; out_ << "CodeStubAssembler(state_).IntPtrConstant(" << std::to_string(ClassType::cast(return_type)->size()) << "), "; PrintCommaSeparatedList(out_, args); out_ << ", RootIndex::kUndefinedValue);\n"; } } void CSAGenerator::EmitInstruction(const CallCsaMacroInstruction& instruction, Stack<std::string>* stack) { std::vector<std::string> constexpr_arguments = instruction.constexpr_arguments; std::vector<std::string> args; TypeVector parameter_types = instruction.macro->signature().parameter_types.types; ProcessArgumentsCommon(parameter_types, &args, &constexpr_arguments, stack); Stack<std::string> pre_call_stack = *stack; const Type* return_type = instruction.macro->signature().return_type; std::vector<std::string> results; for (const Type* type : LowerType(return_type)) { results.push_back(FreshNodeName()); stack->Push(results.back()); out_ << " compiler::TNode<" << type->GetGeneratedTNodeTypeName() << "> " << stack->Top() << ";\n"; out_ << " USE(" << stack->Top() << ");\n"; } std::string catch_name = PreCallableExceptionPreparation(instruction.catch_block); out_ << " "; bool needs_flattening = return_type->IsStructType(); if (needs_flattening) { out_ << "std::tie("; PrintCommaSeparatedList(out_, results); out_ << ") = "; } else { if (results.size() == 1) { out_ << results[0] << " = "; } else { DCHECK_EQ(0, results.size()); } } if (ExternMacro* extern_macro = ExternMacro::DynamicCast(instruction.macro)) { out_ << extern_macro->external_assembler_name() << "(state_)."; } else { args.insert(args.begin(), "state_"); } out_ << instruction.macro->ExternalName() << "("; PrintCommaSeparatedList(out_, args); if (needs_flattening) { out_ << ").Flatten();\n"; } else { out_ << ");\n"; } PostCallableExceptionPreparation(catch_name, return_type, instruction.catch_block, &pre_call_stack); } void CSAGenerator::EmitInstruction( const CallCsaMacroAndBranchInstruction& instruction, Stack<std::string>* stack) { std::vector<std::string> constexpr_arguments = instruction.constexpr_arguments; std::vector<std::string> args; TypeVector parameter_types = instruction.macro->signature().parameter_types.types; ProcessArgumentsCommon(parameter_types, &args, &constexpr_arguments, stack); Stack<std::string> pre_call_stack = *stack; std::vector<std::string> results; const Type* return_type = instruction.macro->signature().return_type; if (return_type != TypeOracle::GetNeverType()) { for (const Type* type : LowerType(instruction.macro->signature().return_type)) { results.push_back(FreshNodeName()); out_ << " compiler::TNode<" << type->GetGeneratedTNodeTypeName() << "> " << results.back() << ";\n"; out_ << " USE(" << results.back() << ");\n"; } } std::vector<std::string> label_names; std::vector<std::vector<std::string>> var_names; const LabelDeclarationVector& labels = instruction.macro->signature().labels; DCHECK_EQ(labels.size(), instruction.label_blocks.size()); for (size_t i = 0; i < labels.size(); ++i) { TypeVector label_parameters = labels[i].types; label_names.push_back("label" + std::to_string(i)); var_names.push_back({}); for (size_t j = 0; j < label_parameters.size(); ++j) { var_names[i].push_back("result_" + std::to_string(i) + "_" + std::to_string(j)); out_ << " compiler::TypedCodeAssemblerVariable<" << label_parameters[j]->GetGeneratedTNodeTypeName() << "> " << var_names[i][j] << "(&ca_);\n"; } out_ << " compiler::CodeAssemblerLabel " << label_names[i] << "(&ca_);\n"; } std::string catch_name = PreCallableExceptionPreparation(instruction.catch_block); out_ << " "; if (results.size() == 1) { out_ << results[0] << " = "; } else if (results.size() > 1) { out_ << "std::tie("; PrintCommaSeparatedList(out_, results); out_ << ") = "; } if (ExternMacro* extern_macro = ExternMacro::DynamicCast(instruction.macro)) { out_ << extern_macro->external_assembler_name() << "(state_)."; } else { args.insert(args.begin(), "state_"); } out_ << instruction.macro->ExternalName() << "("; PrintCommaSeparatedList(out_, args); bool first = args.empty(); for (size_t i = 0; i < label_names.size(); ++i) { if (!first) out_ << ", "; out_ << "&" << label_names[i]; first = false; for (size_t j = 0; j < var_names[i].size(); ++j) { out_ << ", &" << var_names[i][j]; } } if (return_type->IsStructType()) { out_ << ").Flatten();\n"; } else { out_ << ");\n"; } PostCallableExceptionPreparation(catch_name, return_type, instruction.catch_block, &pre_call_stack); if (instruction.return_continuation) { out_ << " ca_.Goto(&" << BlockName(*instruction.return_continuation); for (const std::string& value : *stack) { out_ << ", " << value; } for (const std::string& result : results) { out_ << ", " << result; } out_ << ");\n"; } for (size_t i = 0; i < label_names.size(); ++i) { out_ << " if (" << label_names[i] << ".is_used()) {\n"; out_ << " ca_.Bind(&" << label_names[i] << ");\n"; out_ << " ca_.Goto(&" << BlockName(instruction.label_blocks[i]); for (const std::string& value : *stack) { out_ << ", " << value; } for (const std::string& var : var_names[i]) { out_ << ", " << var << ".value()"; } out_ << ");\n"; out_ << " }\n"; } } void CSAGenerator::EmitInstruction(const CallBuiltinInstruction& instruction, Stack<std::string>* stack) { std::vector<std::string> arguments = stack->PopMany(instruction.argc); std::vector<const Type*> result_types = LowerType(instruction.builtin->signature().return_type); if (instruction.is_tailcall) { out_ << " CodeStubAssembler(state_).TailCallBuiltin(Builtins::k" << instruction.builtin->ExternalName() << ", "; PrintCommaSeparatedList(out_, arguments); out_ << ");\n"; } else { std::string result_name = FreshNodeName(); if (result_types.size() == 1) { out_ << " compiler::TNode<" << result_types[0]->GetGeneratedTNodeTypeName() << "> " << result_name << ";\n"; } std::string catch_name = PreCallableExceptionPreparation(instruction.catch_block); Stack<std::string> pre_call_stack = *stack; if (result_types.size() == 1) { std::string generated_type = result_types[0]->GetGeneratedTNodeTypeName(); stack->Push(result_name); out_ << " " << result_name << " = "; if (generated_type != "Object") out_ << "TORQUE_CAST("; out_ << "CodeStubAssembler(state_).CallBuiltin(Builtins::k" << instruction.builtin->ExternalName() << ", "; PrintCommaSeparatedList(out_, arguments); if (generated_type != "Object") out_ << ")"; out_ << ");\n"; out_ << " USE(" << result_name << ");\n"; } else { DCHECK_EQ(0, result_types.size()); // TODO(tebbi): Actually, builtins have to return a value, so we should // not have to handle this case. out_ << " CodeStubAssembler(state_).CallBuiltin(Builtins::k" << instruction.builtin->ExternalName() << ", "; PrintCommaSeparatedList(out_, arguments); out_ << ");\n"; } PostCallableExceptionPreparation( catch_name, result_types.size() == 0 ? TypeOracle::GetVoidType() : result_types[0], instruction.catch_block, &pre_call_stack); } } void CSAGenerator::EmitInstruction( const CallBuiltinPointerInstruction& instruction, Stack<std::string>* stack) { std::vector<std::string> function_and_arguments = stack->PopMany(1 + instruction.argc); std::vector<const Type*> result_types = LowerType(instruction.type->return_type()); if (result_types.size() != 1) { ReportError("builtins must have exactly one result"); } if (instruction.is_tailcall) { ReportError("tail-calls to builtin pointers are not supported"); } stack->Push(FreshNodeName()); std::string generated_type = result_types[0]->GetGeneratedTNodeTypeName(); out_ << " compiler::TNode<" << generated_type << "> " << stack->Top() << " = "; if (generated_type != "Object") out_ << "TORQUE_CAST("; out_ << "CodeStubAssembler(state_).CallBuiltinPointer(Builtins::" "CallableFor(ca_." "isolate()," "ExampleBuiltinForTorqueFunctionPointerType(" << instruction.type->function_pointer_type_id() << ")).descriptor(), "; PrintCommaSeparatedList(out_, function_and_arguments); out_ << ")"; if (generated_type != "Object") out_ << ")"; out_ << "; \n"; out_ << " USE(" << stack->Top() << ");\n"; } std::string CSAGenerator::PreCallableExceptionPreparation( base::Optional<Block*> catch_block) { std::string catch_name; if (catch_block) { catch_name = FreshCatchName(); out_ << " compiler::CodeAssemblerExceptionHandlerLabel " << catch_name << "__label(&ca_, compiler::CodeAssemblerLabel::kDeferred);\n"; out_ << " { compiler::CodeAssemblerScopedExceptionHandler s(&ca_, &" << catch_name << "__label);\n"; } return catch_name; } void CSAGenerator::PostCallableExceptionPreparation( const std::string& catch_name, const Type* return_type, base::Optional<Block*> catch_block, Stack<std::string>* stack) { if (catch_block) { std::string block_name = BlockName(*catch_block); out_ << " }\n"; out_ << " if (" << catch_name << "__label.is_used()) {\n"; out_ << " compiler::CodeAssemblerLabel " << catch_name << "_skip(&ca_);\n"; if (!return_type->IsNever()) { out_ << " ca_.Goto(&" << catch_name << "_skip);\n"; } out_ << " compiler::TNode<Object> " << catch_name << "_exception_object;\n"; out_ << " ca_.Bind(&" << catch_name << "__label, &" << catch_name << "_exception_object);\n"; out_ << " ca_.Goto(&" << block_name; for (size_t i = 0; i < stack->Size(); ++i) { out_ << ", " << stack->begin()[i]; } out_ << ", " << catch_name << "_exception_object);\n"; if (!return_type->IsNever()) { out_ << " ca_.Bind(&" << catch_name << "_skip);\n"; } out_ << " }\n"; } } void CSAGenerator::EmitInstruction(const CallRuntimeInstruction& instruction, Stack<std::string>* stack) { std::vector<std::string> arguments = stack->PopMany(instruction.argc); const Type* return_type = instruction.runtime_function->signature().return_type; std::vector<const Type*> result_types; if (return_type != TypeOracle::GetNeverType()) { result_types = LowerType(return_type); } if (result_types.size() > 1) { ReportError("runtime function must have at most one result"); } if (instruction.is_tailcall) { out_ << " CodeStubAssembler(state_).TailCallRuntime(Runtime::k" << instruction.runtime_function->ExternalName() << ", "; PrintCommaSeparatedList(out_, arguments); out_ << ");\n"; } else { std::string result_name = FreshNodeName(); if (result_types.size() == 1) { out_ << " compiler::TNode<" << result_types[0]->GetGeneratedTNodeTypeName() << "> " << result_name << ";\n"; } std::string catch_name = PreCallableExceptionPreparation(instruction.catch_block); Stack<std::string> pre_call_stack = *stack; if (result_types.size() == 1) { std::string generated_type = result_types[0]->GetGeneratedTNodeTypeName(); stack->Push(result_name); out_ << " " << result_name << " = "; if (generated_type != "Object") out_ << "TORQUE_CAST("; out_ << "CodeStubAssembler(state_).CallRuntime(Runtime::k" << instruction.runtime_function->ExternalName() << ", "; PrintCommaSeparatedList(out_, arguments); out_ << ")"; if (generated_type != "Object") out_ << ")"; out_ << "; \n"; out_ << " USE(" << result_name << ");\n"; } else { DCHECK_EQ(0, result_types.size()); out_ << " CodeStubAssembler(state_).CallRuntime(Runtime::k" << instruction.runtime_function->ExternalName() << ", "; PrintCommaSeparatedList(out_, arguments); out_ << ");\n"; if (return_type == TypeOracle::GetNeverType()) { out_ << " CodeStubAssembler(state_).Unreachable();\n"; } else { DCHECK(return_type == TypeOracle::GetVoidType()); } } PostCallableExceptionPreparation(catch_name, return_type, instruction.catch_block, &pre_call_stack); } } void CSAGenerator::EmitInstruction(const BranchInstruction& instruction, Stack<std::string>* stack) { out_ << " ca_.Branch(" << stack->Pop() << ", &" << BlockName(instruction.if_true) << ", &" << BlockName(instruction.if_false); for (const std::string& value : *stack) { out_ << ", " << value; } out_ << ");\n"; } void CSAGenerator::EmitInstruction( const ConstexprBranchInstruction& instruction, Stack<std::string>* stack) { out_ << " if ((" << instruction.condition << ")) {\n"; out_ << " ca_.Goto(&" << BlockName(instruction.if_true); for (const std::string& value : *stack) { out_ << ", " << value; } out_ << ");\n"; out_ << " } else {\n"; out_ << " ca_.Goto(&" << BlockName(instruction.if_false); for (const std::string& value : *stack) { out_ << ", " << value; } out_ << ");\n"; out_ << " }\n"; } void CSAGenerator::EmitInstruction(const GotoInstruction& instruction, Stack<std::string>* stack) { out_ << " ca_.Goto(&" << BlockName(instruction.destination); for (const std::string& value : *stack) { out_ << ", " << value; } out_ << ");\n"; } void CSAGenerator::EmitInstruction(const GotoExternalInstruction& instruction, Stack<std::string>* stack) { for (auto it = instruction.variable_names.rbegin(); it != instruction.variable_names.rend(); ++it) { out_ << " *" << *it << " = " << stack->Pop() << ";\n"; } out_ << " ca_.Goto(" << instruction.destination << ");\n"; } void CSAGenerator::EmitInstruction(const ReturnInstruction& instruction, Stack<std::string>* stack) { if (*linkage_ == Builtin::kVarArgsJavaScript) { out_ << " " << ARGUMENTS_VARIABLE_STRING << ".PopAndReturn("; } else { out_ << " CodeStubAssembler(state_).Return("; } out_ << stack->Pop() << ");\n"; } void CSAGenerator::EmitInstruction( const PrintConstantStringInstruction& instruction, Stack<std::string>* stack) { out_ << " CodeStubAssembler(state_).Print(" << StringLiteralQuote(instruction.message) << ");\n"; } void CSAGenerator::EmitInstruction(const AbortInstruction& instruction, Stack<std::string>* stack) { switch (instruction.kind) { case AbortInstruction::Kind::kUnreachable: DCHECK(instruction.message.empty()); out_ << " CodeStubAssembler(state_).Unreachable();\n"; break; case AbortInstruction::Kind::kDebugBreak: DCHECK(instruction.message.empty()); out_ << " CodeStubAssembler(state_).DebugBreak();\n"; break; case AbortInstruction::Kind::kAssertionFailure: { std::string file = StringLiteralQuote( SourceFileMap::PathFromV8Root(instruction.pos.source)); out_ << " CodeStubAssembler(state_).FailAssert(" << StringLiteralQuote(instruction.message) << ", " << file << ", " << instruction.pos.start.line + 1 << ");\n"; break; } } } void CSAGenerator::EmitInstruction(const UnsafeCastInstruction& instruction, Stack<std::string>* stack) { stack->Poke(stack->AboveTop() - 1, "ca_.UncheckedCast<" + instruction.destination_type->GetGeneratedTNodeTypeName() + ">(" + stack->Top() + ")"); } void CSAGenerator::EmitInstruction( const CreateFieldReferenceInstruction& instruction, Stack<std::string>* stack) { base::Optional<const ClassType*> class_type = instruction.type->ClassSupertype(); if (!class_type.has_value()) { ReportError("Cannot create field reference of type ", instruction.type, " which does not inherit from a class type"); } const Field& field = class_type.value()->LookupField(instruction.field_name); std::string offset_name = FreshNodeName(); stack->Push(offset_name); out_ << " compiler::TNode<IntPtrT> " << offset_name << " = ca_.IntPtrConstant("; out_ << field.aggregate->GetGeneratedTNodeTypeName() << "::k" << CamelifyString(field.name_and_type.name) << "Offset"; out_ << ");\n" << " USE(" << stack->Top() << ");\n"; } void CSAGenerator::EmitInstruction(const LoadReferenceInstruction& instruction, Stack<std::string>* stack) { std::string result_name = FreshNodeName(); std::string offset = stack->Pop(); std::string object = stack->Pop(); stack->Push(result_name); out_ << " " << instruction.type->GetGeneratedTypeName() << result_name << " = CodeStubAssembler(state_).LoadReference<" << instruction.type->GetGeneratedTNodeTypeName() << ">(CodeStubAssembler::Reference{" << object << ", " << offset << "});\n"; } void CSAGenerator::EmitInstruction(const StoreReferenceInstruction& instruction, Stack<std::string>* stack) { std::string value = stack->Pop(); std::string offset = stack->Pop(); std::string object = stack->Pop(); out_ << " CodeStubAssembler(state_).StoreReference(CodeStubAssembler::" "Reference{" << object << ", " << offset << "}, " << value << ");\n"; } // static void CSAGenerator::EmitCSAValue(VisitResult result, const Stack<std::string>& values, std::ostream& out) { if (!result.IsOnStack()) { out << result.constexpr_value(); } else if (auto* struct_type = StructType::DynamicCast(result.type())) { out << struct_type->GetGeneratedTypeName() << "{"; bool first = true; for (auto& field : struct_type->fields()) { if (!first) { out << ", "; } first = false; EmitCSAValue(ProjectStructField(result, field.name_and_type.name), values, out); } out << "}"; } else { DCHECK_EQ(1, result.stack_range().Size()); out << "compiler::TNode<" << result.type()->GetGeneratedTNodeTypeName() << ">{" << values.Peek(result.stack_range().begin()) << "}"; } } } // namespace torque } // namespace internal } // namespace v8