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