| // 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/global-context.h" |
| #include "src/torque/type-oracle.h" |
| #include "src/torque/types.h" |
| #include "src/torque/utils.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace torque { |
| |
| base::Optional<Stack<std::string>> CSAGenerator::EmitGraph( |
| Stack<std::string> parameters) { |
| for (BottomOffset i = {0}; i < parameters.AboveTop(); ++i) { |
| SetDefinitionVariable(DefinitionLocation::Parameter(i.offset), |
| parameters.Peek(i)); |
| } |
| |
| for (Block* block : cfg_.blocks()) { |
| if (block->IsDead()) continue; |
| |
| out() << " compiler::CodeAssemblerParameterizedLabel<"; |
| bool first = true; |
| DCHECK_EQ(block->InputTypes().Size(), block->InputDefinitions().Size()); |
| for (BottomOffset i = {0}; i < block->InputTypes().AboveTop(); ++i) { |
| if (block->InputDefinitions().Peek(i).IsPhiFromBlock(block)) { |
| if (!first) out() << ", "; |
| out() << block->InputTypes().Peek(i)->GetGeneratedTNodeTypeName(); |
| first = false; |
| } |
| } |
| 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; |
| if (block->IsDead()) continue; |
| out() << "\n"; |
| |
| // Redirect the output of non-declarations into a buffer and only output |
| // declarations right away. |
| std::stringstream out_buffer; |
| std::ostream* old_out = out_; |
| out_ = &out_buffer; |
| |
| out() << " if (" << BlockName(block) << ".is_used()) {\n"; |
| EmitBlock(block); |
| out() << " }\n"; |
| |
| // All declarations have been printed now, so we can append the buffered |
| // output and redirect back to the original output stream. |
| out_ = old_out; |
| out() << out_buffer.str(); |
| } |
| if (cfg_.end()) { |
| out() << "\n"; |
| return EmitBlock(*cfg_.end()); |
| } |
| return base::nullopt; |
| } |
| |
| Stack<std::string> CSAGenerator::EmitBlock(const Block* block) { |
| Stack<std::string> stack; |
| std::stringstream phi_names; |
| |
| for (BottomOffset i = {0}; i < block->InputTypes().AboveTop(); ++i) { |
| const auto& def = block->InputDefinitions().Peek(i); |
| stack.Push(DefinitionToVariable(def)); |
| if (def.IsPhiFromBlock(block)) { |
| decls() << " TNode<" |
| << block->InputTypes().Peek(i)->GetGeneratedTNodeTypeName() |
| << "> " << stack.Top() << ";\n"; |
| phi_names << ", &" << stack.Top(); |
| } |
| } |
| out() << " ca_.Bind(&" << BlockName(block) << phi_names.str() << ");\n"; |
| |
| for (const Instruction& instruction : block->instructions()) { |
| TorqueCodeGenerator::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 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. |
| const std::string str = "ca_.Uninitialized<" + |
| instruction.type->GetGeneratedTNodeTypeName() + ">()"; |
| stack->Push(str); |
| SetDefinitionVariable(instruction.GetValueDefinition(), str); |
| } |
| |
| void CSAGenerator::EmitInstruction( |
| const PushBuiltinPointerInstruction& instruction, |
| Stack<std::string>* stack) { |
| const std::string str = |
| "ca_.UncheckedCast<BuiltinPtr>(ca_.SmiConstant(Builtins::k" + |
| instruction.external_name + "))"; |
| stack->Push(str); |
| SetDefinitionVariable(instruction.GetValueDefinition(), str); |
| } |
| |
| void CSAGenerator::EmitInstruction( |
| const NamespaceConstantInstruction& instruction, |
| Stack<std::string>* stack) { |
| const Type* type = instruction.constant->type(); |
| std::vector<std::string> results; |
| |
| const auto lowered = LowerType(type); |
| for (std::size_t i = 0; i < lowered.size(); ++i) { |
| results.push_back(DefinitionToVariable(instruction.GetValueDefinition(i))); |
| stack->Push(results.back()); |
| decls() << " TNode<" << lowered[i]->GetGeneratedTNodeTypeName() << "> " |
| << stack->Top() << ";\n"; |
| } |
| |
| out() << " "; |
| if (type->StructSupertype()) { |
| out() << "std::tie("; |
| PrintCommaSeparatedList(out(), results); |
| out() << ") = "; |
| } else if (results.size() == 1) { |
| out() << results[0] << " = "; |
| } |
| out() << instruction.constant->external_name() << "(state_)"; |
| if (type->StructSupertype()) { |
| out() << ".Flatten();\n"; |
| } else { |
| out() << ";\n"; |
| } |
| } |
| |
| std::vector<std::string> CSAGenerator::ProcessArgumentsCommon( |
| const TypeVector& parameter_types, |
| std::vector<std::string> constexpr_arguments, Stack<std::string>* stack) { |
| std::vector<std::string> args; |
| 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()); |
| return args; |
| } |
| |
| void CSAGenerator::EmitInstruction(const CallIntrinsicInstruction& instruction, |
| Stack<std::string>* stack) { |
| TypeVector parameter_types = |
| instruction.intrinsic->signature().parameter_types.types; |
| std::vector<std::string> args = ProcessArgumentsCommon( |
| parameter_types, instruction.constexpr_arguments, stack); |
| |
| Stack<std::string> pre_call_stack = *stack; |
| const Type* return_type = instruction.intrinsic->signature().return_type; |
| std::vector<std::string> results; |
| |
| const auto lowered = LowerType(return_type); |
| for (std::size_t i = 0; i < lowered.size(); ++i) { |
| results.push_back(DefinitionToVariable(instruction.GetValueDefinition(i))); |
| stack->Push(results.back()); |
| decls() << " TNode<" << lowered[i]->GetGeneratedTNodeTypeName() << "> " |
| << stack->Top() << ";\n"; |
| } |
| |
| out() << " "; |
| if (return_type->StructSupertype()) { |
| 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"); |
| } |
| const Type* original_type = parameter_types[0]; |
| bool is_subtype = |
| return_type->IsSubtypeOf(original_type) || |
| (original_type == TypeOracle::GetUninitializedHeapObjectType() && |
| return_type->IsSubtypeOf(TypeOracle::GetHeapObjectType())); |
| if (!is_subtype) { |
| ReportError("%RawDownCast error: ", *return_type, " is not a subtype of ", |
| *original_type); |
| } |
| if (!original_type->StructSupertype() && |
| return_type->GetGeneratedTNodeTypeName() != |
| original_type->GetGeneratedTNodeTypeName()) { |
| if (return_type->IsSubtypeOf(TypeOracle::GetTaggedType())) { |
| out() << "TORQUE_CAST"; |
| } else { |
| out() << "ca_.UncheckedCast<" |
| << return_type->GetGeneratedTNodeTypeName() << ">"; |
| } |
| } |
| } else if (instruction.intrinsic->ExternalName() == "%GetClassMapConstant") { |
| if (parameter_types.size() != 0) { |
| ReportError("%GetClassMapConstant must not take parameters"); |
| } |
| if (instruction.specialization_types.size() != 1) { |
| ReportError( |
| "%GetClassMapConstant must take a single class as specialization " |
| "parameter"); |
| } |
| const ClassType* class_type = |
| ClassType::DynamicCast(instruction.specialization_types[0]); |
| if (!class_type) { |
| ReportError("%GetClassMapConstant must take a class type parameter"); |
| } |
| // If the class isn't actually used as the parameter to a TNode, |
| // then we can't rely on the class existing in C++ or being of the same |
| // type (e.g. it could be a template), so don't use the template CSA |
| // machinery for accessing the class' map. |
| std::string class_name = |
| class_type->name() != class_type->GetGeneratedTNodeTypeName() |
| ? std::string("void") |
| : class_type->name(); |
| |
| out() << std::string("CodeStubAssembler(state_).GetClassMapConstant<") + |
| class_name + ">"; |
| } 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 if (return_type->IsSubtypeOf(TypeOracle::GetUint32Type())) { |
| out() << "ca_.Uint32Constant"; |
| } else if (return_type->IsSubtypeOf(TypeOracle::GetBoolType())) { |
| out() << "ca_.BoolConstant"; |
| } else { |
| std::stringstream s; |
| s << "%FromConstexpr does not support return type " << *return_type; |
| ReportError(s.str()); |
| } |
| // Wrap the raw constexpr value in a static_cast to ensure that |
| // enums get properly casted to their backing integral value. |
| out() << "(CastToUnderlyingTypeIfEnum"; |
| } else { |
| ReportError("no built in intrinsic with name " + |
| instruction.intrinsic->ExternalName()); |
| } |
| |
| out() << "("; |
| PrintCommaSeparatedList(out(), args); |
| if (instruction.intrinsic->ExternalName() == "%FromConstexpr") { |
| out() << ")"; |
| } |
| if (return_type->StructSupertype()) { |
| out() << ").Flatten();\n"; |
| } else { |
| out() << ");\n"; |
| } |
| } |
| |
| void CSAGenerator::EmitInstruction(const CallCsaMacroInstruction& instruction, |
| Stack<std::string>* stack) { |
| TypeVector parameter_types = |
| instruction.macro->signature().parameter_types.types; |
| std::vector<std::string> args = ProcessArgumentsCommon( |
| parameter_types, instruction.constexpr_arguments, stack); |
| |
| Stack<std::string> pre_call_stack = *stack; |
| const Type* return_type = instruction.macro->signature().return_type; |
| std::vector<std::string> results; |
| |
| const auto lowered = LowerType(return_type); |
| for (std::size_t i = 0; i < lowered.size(); ++i) { |
| results.push_back(DefinitionToVariable(instruction.GetValueDefinition(i))); |
| stack->Push(results.back()); |
| decls() << " TNode<" << lowered[i]->GetGeneratedTNodeTypeName() << "> " |
| << stack->Top() << ";\n"; |
| } |
| |
| std::string catch_name = |
| PreCallableExceptionPreparation(instruction.catch_block); |
| out() << " "; |
| bool needs_flattening = return_type->StructSupertype().has_value(); |
| 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, |
| instruction.GetExceptionObjectDefinition()); |
| } |
| |
| void CSAGenerator::EmitInstruction( |
| const CallCsaMacroAndBranchInstruction& instruction, |
| Stack<std::string>* stack) { |
| TypeVector parameter_types = |
| instruction.macro->signature().parameter_types.types; |
| std::vector<std::string> args = ProcessArgumentsCommon( |
| parameter_types, instruction.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()) { |
| const auto lowered = LowerType(return_type); |
| for (std::size_t i = 0; i < lowered.size(); ++i) { |
| results.push_back( |
| DefinitionToVariable(instruction.GetValueDefinition(i))); |
| decls() << " TNode<" << lowered[i]->GetGeneratedTNodeTypeName() << "> " |
| << 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(FreshLabelName()); |
| var_names.push_back({}); |
| for (size_t j = 0; j < label_parameters.size(); ++j) { |
| var_names[i].push_back(FreshNodeName()); |
| const auto def = instruction.GetLabelValueDefinition(i, j); |
| SetDefinitionVariable(def, var_names[i].back() + ".value()"); |
| decls() << " 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->StructSupertype()) { |
| out() << ").Flatten();\n"; |
| } else { |
| out() << ");\n"; |
| } |
| |
| PostCallableExceptionPreparation(catch_name, return_type, |
| instruction.catch_block, &pre_call_stack, |
| instruction.GetExceptionObjectDefinition()); |
| |
| if (instruction.return_continuation) { |
| out() << " ca_.Goto(&" << BlockName(*instruction.return_continuation); |
| DCHECK_EQ(stack->Size() + results.size(), |
| (*instruction.return_continuation)->InputDefinitions().Size()); |
| |
| const auto& input_definitions = |
| (*instruction.return_continuation)->InputDefinitions(); |
| for (BottomOffset i = {0}; i < input_definitions.AboveTop(); ++i) { |
| if (input_definitions.Peek(i).IsPhiFromBlock( |
| *instruction.return_continuation)) { |
| out() << ", " |
| << (i < stack->AboveTop() ? stack->Peek(i) : results[i.offset]); |
| } |
| } |
| out() << ");\n"; |
| } |
| for (size_t l = 0; l < label_names.size(); ++l) { |
| out() << " if (" << label_names[l] << ".is_used()) {\n"; |
| out() << " ca_.Bind(&" << label_names[l] << ");\n"; |
| out() << " ca_.Goto(&" << BlockName(instruction.label_blocks[l]); |
| DCHECK_EQ(stack->Size() + var_names[l].size(), |
| instruction.label_blocks[l]->InputDefinitions().Size()); |
| |
| const auto& label_definitions = |
| instruction.label_blocks[l]->InputDefinitions(); |
| |
| BottomOffset i = {0}; |
| for (; i < stack->AboveTop(); ++i) { |
| if (label_definitions.Peek(i).IsPhiFromBlock( |
| instruction.label_blocks[l])) { |
| out() << ", " << stack->Peek(i); |
| } |
| } |
| for (std::size_t k = 0; k < var_names[l].size(); ++k, ++i) { |
| if (label_definitions.Peek(i).IsPhiFromBlock( |
| instruction.label_blocks[l])) { |
| out() << ", " << var_names[l][k] << ".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(); |
| if (!instruction.builtin->signature().HasContextParameter()) { |
| // Add dummy context parameter to satisfy the TailCallBuiltin signature. |
| out() << ", TNode<Object>()"; |
| } |
| for (const std::string& argument : arguments) { |
| out() << ", " << argument; |
| } |
| out() << ");\n"; |
| } else { |
| std::string result_name; |
| if (result_types.size() == 1) { |
| result_name = DefinitionToVariable(instruction.GetValueDefinition(0)); |
| decls() << " TNode<" << result_types[0]->GetGeneratedTNodeTypeName() |
| << "> " << result_name << ";\n"; |
| } |
| std::string catch_name = |
| PreCallableExceptionPreparation(instruction.catch_block); |
| Stack<std::string> pre_call_stack = *stack; |
| |
| DCHECK_EQ(1, result_types.size()); |
| 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(); |
| if (!instruction.builtin->signature().HasContextParameter()) { |
| // Add dummy context parameter to satisfy the CallBuiltin signature. |
| out() << ", TNode<Object>()"; |
| } |
| for (const std::string& argument : arguments) { |
| out() << ", " << argument; |
| } |
| if (generated_type != "Object") out() << ")"; |
| out() << ");\n"; |
| |
| PostCallableExceptionPreparation( |
| catch_name, |
| result_types.size() == 0 ? TypeOracle::GetVoidType() : result_types[0], |
| instruction.catch_block, &pre_call_stack, |
| instruction.GetExceptionObjectDefinition()); |
| } |
| } |
| |
| void CSAGenerator::EmitInstruction( |
| const CallBuiltinPointerInstruction& instruction, |
| Stack<std::string>* stack) { |
| std::vector<std::string> arguments = stack->PopMany(instruction.argc); |
| std::string function = stack->Pop(); |
| 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"); |
| } |
| |
| DCHECK_EQ(1, instruction.GetValueDefinitionCount()); |
| stack->Push(DefinitionToVariable(instruction.GetValueDefinition(0))); |
| std::string generated_type = result_types[0]->GetGeneratedTNodeTypeName(); |
| decls() << " TNode<" << generated_type << "> " << stack->Top() << ";\n"; |
| out() << 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(), " |
| << function; |
| if (!instruction.type->HasContextParameter()) { |
| // Add dummy context parameter to satisfy the CallBuiltinPointer signature. |
| out() << ", TNode<Object>()"; |
| } |
| for (const std::string& argument : arguments) { |
| out() << ", " << argument; |
| } |
| out() << ")"; |
| if (generated_type != "Object") out() << ")"; |
| out() << ";\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::ScopedExceptionHandler 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, |
| const base::Optional<DefinitionLocation>& exception_object_definition) { |
| if (catch_block) { |
| DCHECK(exception_object_definition); |
| 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"; |
| } |
| decls() << " TNode<Object> " |
| << DefinitionToVariable(*exception_object_definition) << ";\n"; |
| out() << " ca_.Bind(&" << catch_name << "__label, &" |
| << DefinitionToVariable(*exception_object_definition) << ");\n"; |
| out() << " ca_.Goto(&" << block_name; |
| |
| DCHECK_EQ(stack->Size() + 1, (*catch_block)->InputDefinitions().Size()); |
| const auto& input_definitions = (*catch_block)->InputDefinitions(); |
| for (BottomOffset i = {0}; i < input_definitions.AboveTop(); ++i) { |
| if (input_definitions.Peek(i).IsPhiFromBlock(*catch_block)) { |
| if (i < stack->AboveTop()) { |
| out() << ", " << stack->Peek(i); |
| } else { |
| DCHECK_EQ(i, stack->AboveTop()); |
| out() << ", " << DefinitionToVariable(*exception_object_definition); |
| } |
| } |
| } |
| out() << ");\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; |
| if (result_types.size() == 1) { |
| result_name = DefinitionToVariable(instruction.GetValueDefinition(0)); |
| decls() << " 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"; |
| } 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, |
| instruction.GetExceptionObjectDefinition()); |
| } |
| } |
| |
| void CSAGenerator::EmitInstruction(const BranchInstruction& instruction, |
| Stack<std::string>* stack) { |
| out() << " ca_.Branch(" << stack->Pop() << ", &" |
| << BlockName(instruction.if_true) << ", std::vector<Node*>{"; |
| |
| const auto& true_definitions = instruction.if_true->InputDefinitions(); |
| DCHECK_EQ(stack->Size(), true_definitions.Size()); |
| bool first = true; |
| for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) { |
| if (true_definitions.Peek(i).IsPhiFromBlock(instruction.if_true)) { |
| if (!first) out() << ", "; |
| out() << stack->Peek(i); |
| first = false; |
| } |
| } |
| |
| out() << "}, &" << BlockName(instruction.if_false) << ", std::vector<Node*>{"; |
| |
| const auto& false_definitions = instruction.if_false->InputDefinitions(); |
| DCHECK_EQ(stack->Size(), false_definitions.Size()); |
| first = true; |
| for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) { |
| if (false_definitions.Peek(i).IsPhiFromBlock(instruction.if_false)) { |
| if (!first) out() << ", "; |
| out() << stack->Peek(i); |
| first = false; |
| } |
| } |
| |
| out() << "});\n"; |
| } |
| |
| void CSAGenerator::EmitInstruction( |
| const ConstexprBranchInstruction& instruction, Stack<std::string>* stack) { |
| out() << " if ((" << instruction.condition << ")) {\n"; |
| out() << " ca_.Goto(&" << BlockName(instruction.if_true); |
| |
| const auto& true_definitions = instruction.if_true->InputDefinitions(); |
| DCHECK_EQ(stack->Size(), true_definitions.Size()); |
| for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) { |
| if (true_definitions.Peek(i).IsPhiFromBlock(instruction.if_true)) { |
| out() << ", " << stack->Peek(i); |
| } |
| } |
| |
| out() << ");\n"; |
| out() << " } else {\n"; |
| out() << " ca_.Goto(&" << BlockName(instruction.if_false); |
| |
| const auto& false_definitions = instruction.if_false->InputDefinitions(); |
| DCHECK_EQ(stack->Size(), false_definitions.Size()); |
| for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) { |
| if (false_definitions.Peek(i).IsPhiFromBlock(instruction.if_false)) { |
| out() << ", " << stack->Peek(i); |
| } |
| } |
| |
| out() << ");\n"; |
| out() << " }\n"; |
| } |
| |
| void CSAGenerator::EmitInstruction(const GotoInstruction& instruction, |
| Stack<std::string>* stack) { |
| out() << " ca_.Goto(&" << BlockName(instruction.destination); |
| const auto& destination_definitions = |
| instruction.destination->InputDefinitions(); |
| DCHECK_EQ(stack->Size(), destination_definitions.Size()); |
| for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) { |
| if (destination_definitions.Peek(i).IsPhiFromBlock( |
| instruction.destination)) { |
| out() << ", " << stack->Peek(i); |
| } |
| } |
| 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() << " {\n"; |
| out() << " auto pos_stack = ca_.GetMacroSourcePositionStack();\n"; |
| out() << " pos_stack.push_back({" << file << ", " |
| << instruction.pos.start.line + 1 << "});\n"; |
| out() << " CodeStubAssembler(state_).FailAssert(" |
| << StringLiteralQuote(instruction.message) << ", pos_stack);\n"; |
| out() << " }\n"; |
| break; |
| } |
| } |
| } |
| |
| void CSAGenerator::EmitInstruction(const UnsafeCastInstruction& instruction, |
| Stack<std::string>* stack) { |
| const std::string str = |
| "ca_.UncheckedCast<" + |
| instruction.destination_type->GetGeneratedTNodeTypeName() + ">(" + |
| stack->Top() + ")"; |
| stack->Poke(stack->AboveTop() - 1, str); |
| SetDefinitionVariable(instruction.GetValueDefinition(), str); |
| } |
| |
| void CSAGenerator::EmitInstruction(const LoadReferenceInstruction& instruction, |
| Stack<std::string>* stack) { |
| std::string result_name = |
| DefinitionToVariable(instruction.GetValueDefinition()); |
| |
| std::string offset = stack->Pop(); |
| std::string object = stack->Pop(); |
| stack->Push(result_name); |
| |
| decls() << " " << instruction.type->GetGeneratedTypeName() << " " |
| << result_name << ";\n"; |
| out() << " " << 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<" |
| << instruction.type->GetGeneratedTNodeTypeName() |
| << ">(CodeStubAssembler::" |
| "Reference{" |
| << object << ", " << offset << "}, " << value << ");\n"; |
| } |
| |
| namespace { |
| std::string GetBitFieldSpecialization(const Type* container, |
| const BitField& field) { |
| auto smi_tagged_type = |
| Type::MatchUnaryGeneric(container, TypeOracle::GetSmiTaggedGeneric()); |
| std::string container_type = smi_tagged_type |
| ? "uintptr_t" |
| : container->GetConstexprGeneratedTypeName(); |
| int offset = smi_tagged_type |
| ? field.offset + TargetArchitecture::SmiTagAndShiftSize() |
| : field.offset; |
| std::stringstream stream; |
| stream << "base::BitField<" |
| << field.name_and_type.type->GetConstexprGeneratedTypeName() << ", " |
| << offset << ", " << field.num_bits << ", " << container_type << ">"; |
| return stream.str(); |
| } |
| } // namespace |
| |
| void CSAGenerator::EmitInstruction(const LoadBitFieldInstruction& instruction, |
| Stack<std::string>* stack) { |
| std::string result_name = |
| DefinitionToVariable(instruction.GetValueDefinition()); |
| |
| std::string bit_field_struct = stack->Pop(); |
| stack->Push(result_name); |
| |
| const Type* struct_type = instruction.bit_field_struct_type; |
| const Type* field_type = instruction.bit_field.name_and_type.type; |
| auto smi_tagged_type = |
| Type::MatchUnaryGeneric(struct_type, TypeOracle::GetSmiTaggedGeneric()); |
| bool struct_is_pointer_size = |
| IsPointerSizeIntegralType(struct_type) || smi_tagged_type; |
| DCHECK_IMPLIES(!struct_is_pointer_size, Is32BitIntegralType(struct_type)); |
| bool field_is_pointer_size = IsPointerSizeIntegralType(field_type); |
| DCHECK_IMPLIES(!field_is_pointer_size, Is32BitIntegralType(field_type)); |
| std::string struct_word_type = struct_is_pointer_size ? "WordT" : "Word32T"; |
| std::string decoder = |
| struct_is_pointer_size |
| ? (field_is_pointer_size ? "DecodeWord" : "DecodeWord32FromWord") |
| : (field_is_pointer_size ? "DecodeWordFromWord32" : "DecodeWord32"); |
| |
| decls() << " " << field_type->GetGeneratedTypeName() << " " << result_name |
| << ";\n"; |
| |
| if (smi_tagged_type) { |
| // If the container is a SMI, then UncheckedCast is insufficient and we must |
| // use a bit cast. |
| bit_field_struct = |
| "ca_.BitcastTaggedToWordForTagAndSmiBits(" + bit_field_struct + ")"; |
| } |
| |
| out() << " " << result_name << " = ca_.UncheckedCast<" |
| << field_type->GetGeneratedTNodeTypeName() |
| << ">(CodeStubAssembler(state_)." << decoder << "<" |
| << GetBitFieldSpecialization(struct_type, instruction.bit_field) |
| << ">(ca_.UncheckedCast<" << struct_word_type << ">(" |
| << bit_field_struct << ")));\n"; |
| } |
| |
| void CSAGenerator::EmitInstruction(const StoreBitFieldInstruction& instruction, |
| Stack<std::string>* stack) { |
| std::string result_name = |
| DefinitionToVariable(instruction.GetValueDefinition()); |
| |
| std::string value = stack->Pop(); |
| std::string bit_field_struct = stack->Pop(); |
| stack->Push(result_name); |
| |
| const Type* struct_type = instruction.bit_field_struct_type; |
| const Type* field_type = instruction.bit_field.name_and_type.type; |
| auto smi_tagged_type = |
| Type::MatchUnaryGeneric(struct_type, TypeOracle::GetSmiTaggedGeneric()); |
| bool struct_is_pointer_size = |
| IsPointerSizeIntegralType(struct_type) || smi_tagged_type; |
| DCHECK_IMPLIES(!struct_is_pointer_size, Is32BitIntegralType(struct_type)); |
| bool field_is_pointer_size = IsPointerSizeIntegralType(field_type); |
| DCHECK_IMPLIES(!field_is_pointer_size, Is32BitIntegralType(field_type)); |
| std::string struct_word_type = struct_is_pointer_size ? "WordT" : "Word32T"; |
| std::string field_word_type = field_is_pointer_size ? "UintPtrT" : "Uint32T"; |
| std::string encoder = |
| struct_is_pointer_size |
| ? (field_is_pointer_size ? "UpdateWord" : "UpdateWord32InWord") |
| : (field_is_pointer_size ? "UpdateWordInWord32" : "UpdateWord32"); |
| |
| decls() << " " << struct_type->GetGeneratedTypeName() << " " << result_name |
| << ";\n"; |
| |
| if (smi_tagged_type) { |
| // If the container is a SMI, then UncheckedCast is insufficient and we must |
| // use a bit cast. |
| bit_field_struct = |
| "ca_.BitcastTaggedToWordForTagAndSmiBits(" + bit_field_struct + ")"; |
| } |
| |
| std::string result_expression = |
| "CodeStubAssembler(state_)." + encoder + "<" + |
| GetBitFieldSpecialization(struct_type, instruction.bit_field) + |
| ">(ca_.UncheckedCast<" + struct_word_type + ">(" + bit_field_struct + |
| "), ca_.UncheckedCast<" + field_word_type + ">(" + value + ")" + |
| (instruction.starts_as_zero ? ", true" : "") + ")"; |
| |
| if (smi_tagged_type) { |
| result_expression = |
| "ca_.BitcastWordToTaggedSigned(" + result_expression + ")"; |
| } |
| |
| out() << " " << result_name << " = ca_.UncheckedCast<" |
| << struct_type->GetGeneratedTNodeTypeName() << ">(" << result_expression |
| << ");\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 = result.type()->StructSupertype()) { |
| 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 << "TNode<" << result.type()->GetGeneratedTNodeTypeName() << ">{" |
| << values.Peek(result.stack_range().begin()) << "}"; |
| } |
| } |
| |
| } // namespace torque |
| } // namespace internal |
| } // namespace v8 |