| // 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() || return_type->IsReferenceType(); |
| 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) { |
| const Field& field = |
| instruction.class_type->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 if (result.type()->IsReferenceType()) { |
| DCHECK_EQ(2, result.stack_range().Size()); |
| size_t offset = result.stack_range().begin().offset; |
| out << "CodeStubAssembler::Reference{" << values.Peek(BottomOffset{offset}) |
| << ", " << values.Peek(BottomOffset{offset + 1}) << "}"; |
| } 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 |