1// Copyright 2020 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/torque/cc-generator.h" 6 7#include "src/common/globals.h" 8#include "src/torque/global-context.h" 9#include "src/torque/type-oracle.h" 10#include "src/torque/types.h" 11#include "src/torque/utils.h" 12 13namespace v8 { 14namespace internal { 15namespace torque { 16 17base::Optional<Stack<std::string>> CCGenerator::EmitGraph( 18 Stack<std::string> parameters) { 19 for (BottomOffset i = {0}; i < parameters.AboveTop(); ++i) { 20 SetDefinitionVariable(DefinitionLocation::Parameter(i.offset), 21 parameters.Peek(i)); 22 } 23 24 // Redirect the output of non-declarations into a buffer and only output 25 // declarations right away. 26 std::stringstream out_buffer; 27 std::ostream* old_out = out_; 28 out_ = &out_buffer; 29 30 EmitInstruction(GotoInstruction{cfg_.start()}, ¶meters); 31 32 for (Block* block : cfg_.blocks()) { 33 if (cfg_.end() && *cfg_.end() == block) continue; 34 if (block->IsDead()) continue; 35 EmitBlock(block); 36 } 37 38 base::Optional<Stack<std::string>> result; 39 if (cfg_.end()) { 40 result = EmitBlock(*cfg_.end()); 41 } 42 43 // All declarations have been printed now, so we can append the buffered 44 // output and redirect back to the original output stream. 45 out_ = old_out; 46 out() << out_buffer.str(); 47 48 return result; 49} 50 51Stack<std::string> CCGenerator::EmitBlock(const Block* block) { 52 out() << "\n"; 53 out() << " " << BlockName(block) << ":\n"; 54 55 Stack<std::string> stack; 56 57 for (BottomOffset i = {0}; i < block->InputTypes().AboveTop(); ++i) { 58 const auto& def = block->InputDefinitions().Peek(i); 59 stack.Push(DefinitionToVariable(def)); 60 if (def.IsPhiFromBlock(block)) { 61 decls() << " " 62 << (is_cc_debug_ ? block->InputTypes().Peek(i)->GetDebugType() 63 : block->InputTypes().Peek(i)->GetRuntimeType()) 64 << " " << stack.Top() << "{}; USE(" << stack.Top() << ");\n"; 65 } 66 } 67 68 for (const Instruction& instruction : block->instructions()) { 69 TorqueCodeGenerator::EmitInstruction(instruction, &stack); 70 } 71 return stack; 72} 73 74void CCGenerator::EmitSourcePosition(SourcePosition pos, bool always_emit) { 75 const std::string& file = SourceFileMap::AbsolutePath(pos.source); 76 if (always_emit || !previous_position_.CompareStartIgnoreColumn(pos)) { 77 // Lines in Torque SourcePositions are zero-based, while the 78 // CodeStubAssembler and downwind systems are one-based. 79 out() << " // " << file << ":" << (pos.start.line + 1) << "\n"; 80 previous_position_ = pos; 81 } 82} 83 84void CCGenerator::EmitInstruction( 85 const PushUninitializedInstruction& instruction, 86 Stack<std::string>* stack) { 87 ReportError("Not supported in C++ output: PushUninitialized"); 88} 89 90void CCGenerator::EmitInstruction( 91 const PushBuiltinPointerInstruction& instruction, 92 Stack<std::string>* stack) { 93 ReportError("Not supported in C++ output: PushBuiltinPointer"); 94} 95 96void CCGenerator::EmitInstruction( 97 const NamespaceConstantInstruction& instruction, 98 Stack<std::string>* stack) { 99 ReportError("Not supported in C++ output: NamespaceConstantInstruction"); 100} 101 102std::vector<std::string> CCGenerator::ProcessArgumentsCommon( 103 const TypeVector& parameter_types, 104 std::vector<std::string> constexpr_arguments, Stack<std::string>* stack) { 105 std::vector<std::string> args; 106 for (auto it = parameter_types.rbegin(); it != parameter_types.rend(); ++it) { 107 const Type* type = *it; 108 if (type->IsConstexpr()) { 109 args.push_back(std::move(constexpr_arguments.back())); 110 constexpr_arguments.pop_back(); 111 } else { 112 std::stringstream s; 113 size_t slot_count = LoweredSlotCount(type); 114 VisitResult arg = VisitResult(type, stack->TopRange(slot_count)); 115 EmitCCValue(arg, *stack, s); 116 args.push_back(s.str()); 117 stack->PopMany(slot_count); 118 } 119 } 120 std::reverse(args.begin(), args.end()); 121 return args; 122} 123 124void CCGenerator::EmitInstruction(const CallIntrinsicInstruction& instruction, 125 Stack<std::string>* stack) { 126 TypeVector parameter_types = 127 instruction.intrinsic->signature().parameter_types.types; 128 std::vector<std::string> args = ProcessArgumentsCommon( 129 parameter_types, instruction.constexpr_arguments, stack); 130 131 Stack<std::string> pre_call_stack = *stack; 132 const Type* return_type = instruction.intrinsic->signature().return_type; 133 std::vector<std::string> results; 134 135 const auto lowered = LowerType(return_type); 136 for (std::size_t i = 0; i < lowered.size(); ++i) { 137 results.push_back(DefinitionToVariable(instruction.GetValueDefinition(i))); 138 stack->Push(results.back()); 139 decls() << " " 140 << (is_cc_debug_ ? lowered[i]->GetDebugType() 141 : lowered[i]->GetRuntimeType()) 142 << " " << stack->Top() << "{}; USE(" << stack->Top() << ");\n"; 143 } 144 145 out() << " "; 146 if (return_type->StructSupertype()) { 147 out() << "std::tie("; 148 PrintCommaSeparatedList(out(), results); 149 out() << ") = "; 150 } else { 151 if (results.size() == 1) { 152 out() << results[0] << " = "; 153 } 154 } 155 156 if (instruction.intrinsic->ExternalName() == "%RawDownCast") { 157 if (parameter_types.size() != 1) { 158 ReportError("%RawDownCast must take a single parameter"); 159 } 160 const Type* original_type = parameter_types[0]; 161 bool is_subtype = 162 return_type->IsSubtypeOf(original_type) || 163 (original_type == TypeOracle::GetUninitializedHeapObjectType() && 164 return_type->IsSubtypeOf(TypeOracle::GetHeapObjectType())); 165 if (!is_subtype) { 166 ReportError("%RawDownCast error: ", *return_type, " is not a subtype of ", 167 *original_type); 168 } 169 if (!original_type->StructSupertype() && 170 return_type->GetRuntimeType() != original_type->GetRuntimeType()) { 171 out() << "static_cast<" << return_type->GetRuntimeType() << ">"; 172 } 173 } else if (instruction.intrinsic->ExternalName() == "%GetClassMapConstant") { 174 ReportError("C++ generator doesn't yet support %GetClassMapConstant"); 175 } else if (instruction.intrinsic->ExternalName() == "%FromConstexpr") { 176 if (parameter_types.size() != 1 || !parameter_types[0]->IsConstexpr()) { 177 ReportError( 178 "%FromConstexpr must take a single parameter with constexpr " 179 "type"); 180 } 181 if (return_type->IsConstexpr()) { 182 ReportError("%FromConstexpr must return a non-constexpr type"); 183 } 184 if (return_type->IsSubtypeOf(TypeOracle::GetSmiType())) { 185 if (is_cc_debug_) { 186 out() << "Internals::IntToSmi"; 187 } else { 188 out() << "Smi::FromInt"; 189 } 190 } 191 // Wrap the raw constexpr value in a static_cast to ensure that 192 // enums get properly casted to their backing integral value. 193 out() << "(CastToUnderlyingTypeIfEnum"; 194 } else { 195 ReportError("no built in intrinsic with name " + 196 instruction.intrinsic->ExternalName()); 197 } 198 199 out() << "("; 200 PrintCommaSeparatedList(out(), args); 201 if (instruction.intrinsic->ExternalName() == "%FromConstexpr") { 202 out() << ")"; 203 } 204 out() << ");\n"; 205} 206 207void CCGenerator::EmitInstruction(const CallCsaMacroInstruction& instruction, 208 Stack<std::string>* stack) { 209 TypeVector parameter_types = 210 instruction.macro->signature().parameter_types.types; 211 std::vector<std::string> args = ProcessArgumentsCommon( 212 parameter_types, instruction.constexpr_arguments, stack); 213 214 Stack<std::string> pre_call_stack = *stack; 215 const Type* return_type = instruction.macro->signature().return_type; 216 std::vector<std::string> results; 217 218 const auto lowered = LowerType(return_type); 219 for (std::size_t i = 0; i < lowered.size(); ++i) { 220 results.push_back(DefinitionToVariable(instruction.GetValueDefinition(i))); 221 stack->Push(results.back()); 222 decls() << " " 223 << (is_cc_debug_ ? lowered[i]->GetDebugType() 224 : lowered[i]->GetRuntimeType()) 225 << " " << stack->Top() << "{}; USE(" << stack->Top() << ");\n"; 226 } 227 228 // We should have inlined any calls requiring complex control flow. 229 CHECK(!instruction.catch_block); 230 out() << (is_cc_debug_ ? " ASSIGN_OR_RETURN(" : " "); 231 if (return_type->StructSupertype().has_value()) { 232 out() << "std::tie("; 233 PrintCommaSeparatedList(out(), results); 234 out() << (is_cc_debug_ ? "), " : ") = "); 235 } else { 236 if (results.size() == 1) { 237 out() << results[0] << (is_cc_debug_ ? ", " : " = "); 238 } else { 239 DCHECK_EQ(0, results.size()); 240 } 241 } 242 243 if (is_cc_debug_) { 244 out() << instruction.macro->CCDebugName() << "(accessor"; 245 if (!args.empty()) out() << ", "; 246 } else { 247 out() << instruction.macro->CCName() << "("; 248 } 249 PrintCommaSeparatedList(out(), args); 250 if (is_cc_debug_) { 251 out() << "));\n"; 252 } else { 253 out() << ");\n"; 254 } 255} 256 257void CCGenerator::EmitInstruction( 258 const CallCsaMacroAndBranchInstruction& instruction, 259 Stack<std::string>* stack) { 260 ReportError("Not supported in C++ output: CallCsaMacroAndBranch"); 261} 262 263void CCGenerator::EmitInstruction(const MakeLazyNodeInstruction& instruction, 264 Stack<std::string>* stack) { 265 ReportError("Not supported in C++ output: MakeLazyNode"); 266} 267 268void CCGenerator::EmitInstruction(const CallBuiltinInstruction& instruction, 269 Stack<std::string>* stack) { 270 ReportError("Not supported in C++ output: CallBuiltin"); 271} 272 273void CCGenerator::EmitInstruction( 274 const CallBuiltinPointerInstruction& instruction, 275 Stack<std::string>* stack) { 276 ReportError("Not supported in C++ output: CallBuiltinPointer"); 277} 278 279void CCGenerator::EmitInstruction(const CallRuntimeInstruction& instruction, 280 Stack<std::string>* stack) { 281 ReportError("Not supported in C++ output: CallRuntime"); 282} 283 284void CCGenerator::EmitInstruction(const BranchInstruction& instruction, 285 Stack<std::string>* stack) { 286 out() << " if (" << stack->Pop() << ") {\n"; 287 EmitGoto(instruction.if_true, stack, " "); 288 out() << " } else {\n"; 289 EmitGoto(instruction.if_false, stack, " "); 290 out() << " }\n"; 291} 292 293void CCGenerator::EmitInstruction(const ConstexprBranchInstruction& instruction, 294 Stack<std::string>* stack) { 295 out() << " if ((" << instruction.condition << ")) {\n"; 296 EmitGoto(instruction.if_true, stack, " "); 297 out() << " } else {\n"; 298 EmitGoto(instruction.if_false, stack, " "); 299 out() << " }\n"; 300} 301 302void CCGenerator::EmitGoto(const Block* destination, Stack<std::string>* stack, 303 std::string indentation) { 304 const auto& destination_definitions = destination->InputDefinitions(); 305 DCHECK_EQ(stack->Size(), destination_definitions.Size()); 306 for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) { 307 DefinitionLocation def = destination_definitions.Peek(i); 308 if (def.IsPhiFromBlock(destination)) { 309 out() << indentation << DefinitionToVariable(def) << " = " 310 << stack->Peek(i) << ";\n"; 311 } 312 } 313 out() << indentation << "goto " << BlockName(destination) << ";\n"; 314} 315 316void CCGenerator::EmitInstruction(const GotoInstruction& instruction, 317 Stack<std::string>* stack) { 318 EmitGoto(instruction.destination, stack, " "); 319} 320 321void CCGenerator::EmitInstruction(const GotoExternalInstruction& instruction, 322 Stack<std::string>* stack) { 323 ReportError("Not supported in C++ output: GotoExternal"); 324} 325 326void CCGenerator::EmitInstruction(const ReturnInstruction& instruction, 327 Stack<std::string>* stack) { 328 ReportError("Not supported in C++ output: Return"); 329} 330 331void CCGenerator::EmitInstruction( 332 const PrintConstantStringInstruction& instruction, 333 Stack<std::string>* stack) { 334 out() << " std::cout << " << StringLiteralQuote(instruction.message) 335 << ";\n"; 336} 337 338void CCGenerator::EmitInstruction(const AbortInstruction& instruction, 339 Stack<std::string>* stack) { 340 switch (instruction.kind) { 341 case AbortInstruction::Kind::kUnreachable: 342 DCHECK(instruction.message.empty()); 343 out() << " UNREACHABLE();\n"; 344 break; 345 case AbortInstruction::Kind::kDebugBreak: 346 DCHECK(instruction.message.empty()); 347 out() << " base::OS::DebugBreak();\n"; 348 break; 349 case AbortInstruction::Kind::kAssertionFailure: { 350 std::string file = StringLiteralQuote( 351 SourceFileMap::PathFromV8Root(instruction.pos.source)); 352 out() << " CHECK(false, \"Failed Torque assertion: '\"" 353 << StringLiteralQuote(instruction.message) << "\"' at \"" << file 354 << "\":\"" 355 << StringLiteralQuote( 356 std::to_string(instruction.pos.start.line + 1)) 357 << ");\n"; 358 break; 359 } 360 } 361} 362 363void CCGenerator::EmitInstruction(const UnsafeCastInstruction& instruction, 364 Stack<std::string>* stack) { 365 const std::string str = "static_cast<" + 366 instruction.destination_type->GetRuntimeType() + 367 ">(" + stack->Top() + ")"; 368 stack->Poke(stack->AboveTop() - 1, str); 369 SetDefinitionVariable(instruction.GetValueDefinition(), str); 370} 371 372void CCGenerator::EmitInstruction(const LoadReferenceInstruction& instruction, 373 Stack<std::string>* stack) { 374 std::string result_name = 375 DefinitionToVariable(instruction.GetValueDefinition()); 376 377 std::string offset = stack->Pop(); 378 std::string object = stack->Pop(); 379 stack->Push(result_name); 380 381 if (!is_cc_debug_) { 382 std::string result_type = instruction.type->GetRuntimeType(); 383 decls() << " " << result_type << " " << result_name << "{}; USE(" 384 << result_name << ");\n"; 385 out() << " " << result_name << " = "; 386 if (instruction.type->IsSubtypeOf(TypeOracle::GetTaggedType())) { 387 // Currently, all of the tagged loads we emit are for smi values, so there 388 // is no point in providing an PtrComprCageBase. If at some point we start 389 // emitting loads for tagged fields which might be HeapObjects, then we 390 // should plumb an PtrComprCageBase through the generated functions that 391 // need it. 392 if (!instruction.type->IsSubtypeOf(TypeOracle::GetSmiType())) { 393 Error( 394 "Not supported in C++ output: LoadReference on non-smi tagged " 395 "value"); 396 } 397 398 // References and slices can cause some values to have the Torque type 399 // HeapObject|TaggedZeroPattern, which is output as "Object". TaggedField 400 // requires HeapObject, so we need a cast. 401 out() << "TaggedField<" << result_type 402 << ">::load(*static_cast<HeapObject*>(&" << object 403 << "), static_cast<int>(" << offset << "));\n"; 404 } else { 405 out() << "(" << object << ").ReadField<" << result_type << ">(" << offset 406 << ");\n"; 407 } 408 } else { 409 std::string result_type = instruction.type->GetDebugType(); 410 decls() << " " << result_type << " " << result_name << "{}; USE(" 411 << result_name << ");\n"; 412 if (instruction.type->IsSubtypeOf(TypeOracle::GetTaggedType())) { 413 out() << " READ_TAGGED_FIELD_OR_FAIL(" << result_name << ", accessor, " 414 << object << ", static_cast<int>(" << offset << "));\n"; 415 } else { 416 out() << " READ_FIELD_OR_FAIL(" << result_type << ", " << result_name 417 << ", accessor, " << object << ", " << offset << ");\n"; 418 } 419 } 420} 421 422void CCGenerator::EmitInstruction(const StoreReferenceInstruction& instruction, 423 Stack<std::string>* stack) { 424 ReportError("Not supported in C++ output: StoreReference"); 425} 426 427namespace { 428std::string GetBitFieldSpecialization(const Type* container, 429 const BitField& field) { 430 std::stringstream stream; 431 stream << "base::BitField<" 432 << field.name_and_type.type->GetConstexprGeneratedTypeName() << ", " 433 << field.offset << ", " << field.num_bits << ", " 434 << container->GetConstexprGeneratedTypeName() << ">"; 435 return stream.str(); 436} 437} // namespace 438 439void CCGenerator::EmitInstruction(const LoadBitFieldInstruction& instruction, 440 Stack<std::string>* stack) { 441 std::string result_name = 442 DefinitionToVariable(instruction.GetValueDefinition()); 443 444 std::string bit_field_struct = stack->Pop(); 445 stack->Push(result_name); 446 447 const Type* struct_type = instruction.bit_field_struct_type; 448 449 decls() << " " << instruction.bit_field.name_and_type.type->GetRuntimeType() 450 << " " << result_name << "{}; USE(" << result_name << ");\n"; 451 452 base::Optional<const Type*> smi_tagged_type = 453 Type::MatchUnaryGeneric(struct_type, TypeOracle::GetSmiTaggedGeneric()); 454 if (smi_tagged_type) { 455 // Get the untagged value and its type. 456 if (is_cc_debug_) { 457 bit_field_struct = "Internals::SmiValue(" + bit_field_struct + ")"; 458 } else { 459 bit_field_struct = bit_field_struct + ".value()"; 460 } 461 struct_type = *smi_tagged_type; 462 } 463 464 out() << " " << result_name << " = CastToUnderlyingTypeIfEnum(" 465 << GetBitFieldSpecialization(struct_type, instruction.bit_field) 466 << "::decode(" << bit_field_struct << "));\n"; 467} 468 469void CCGenerator::EmitInstruction(const StoreBitFieldInstruction& instruction, 470 Stack<std::string>* stack) { 471 ReportError("Not supported in C++ output: StoreBitField"); 472} 473 474namespace { 475 476void CollectAllFields(const VisitResult& result, 477 const Stack<std::string>& values, 478 std::vector<std::string>& all_fields) { 479 if (!result.IsOnStack()) { 480 all_fields.push_back(result.constexpr_value()); 481 } else if (auto struct_type = result.type()->StructSupertype()) { 482 for (const Field& field : (*struct_type)->fields()) { 483 CollectAllFields(ProjectStructField(result, field.name_and_type.name), 484 values, all_fields); 485 } 486 } else { 487 DCHECK_EQ(1, result.stack_range().Size()); 488 all_fields.push_back(values.Peek(result.stack_range().begin())); 489 } 490} 491 492} // namespace 493 494// static 495void CCGenerator::EmitCCValue(VisitResult result, 496 const Stack<std::string>& values, 497 std::ostream& out) { 498 std::vector<std::string> all_fields; 499 CollectAllFields(result, values, all_fields); 500 if (all_fields.size() == 1) { 501 out << all_fields[0]; 502 } else { 503 out << "std::make_tuple("; 504 PrintCommaSeparatedList(out, all_fields); 505 out << ")"; 506 } 507} 508 509} // namespace torque 510} // namespace internal 511} // namespace v8 512