1// Copyright 2017 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/implementation-visitor.h" 6 7#include <algorithm> 8#include <iomanip> 9#include <string> 10 11#include "src/base/optional.h" 12#include "src/common/globals.h" 13#include "src/numbers/integer-literal-inl.h" 14#include "src/torque/cc-generator.h" 15#include "src/torque/cfg.h" 16#include "src/torque/constants.h" 17#include "src/torque/cpp-builder.h" 18#include "src/torque/csa-generator.h" 19#include "src/torque/declaration-visitor.h" 20#include "src/torque/global-context.h" 21#include "src/torque/kythe-data.h" 22#include "src/torque/parameter-difference.h" 23#include "src/torque/server-data.h" 24#include "src/torque/source-positions.h" 25#include "src/torque/type-inference.h" 26#include "src/torque/type-visitor.h" 27#include "src/torque/types.h" 28#include "src/torque/utils.h" 29 30namespace v8 { 31namespace internal { 32namespace torque { 33 34uint64_t next_unique_binding_index = 0; 35 36// Sadly, 'using std::string_literals::operator""s;' is bugged in MSVC (see 37// https://developercommunity.visualstudio.com/t/Incorrect-warning-when-using-standard-st/673948). 38// TODO(nicohartmann@): Change to 'using std::string_literals::operator""s;' 39// once this is fixed. 40using namespace std::string_literals; // NOLINT(build/namespaces) 41 42namespace { 43const char* BuiltinIncludesMarker = "// __BUILTIN_INCLUDES_MARKER__\n"; 44} // namespace 45 46VisitResult ImplementationVisitor::Visit(Expression* expr) { 47 CurrentSourcePosition::Scope scope(expr->pos); 48 switch (expr->kind) { 49#define ENUM_ITEM(name) \ 50 case AstNode::Kind::k##name: \ 51 return Visit(name::cast(expr)); 52 AST_EXPRESSION_NODE_KIND_LIST(ENUM_ITEM) 53#undef ENUM_ITEM 54 default: 55 UNREACHABLE(); 56 } 57} 58 59const Type* ImplementationVisitor::Visit(Statement* stmt) { 60 CurrentSourcePosition::Scope scope(stmt->pos); 61 StackScope stack_scope(this); 62 const Type* result; 63 switch (stmt->kind) { 64#define ENUM_ITEM(name) \ 65 case AstNode::Kind::k##name: \ 66 result = Visit(name::cast(stmt)); \ 67 break; 68 AST_STATEMENT_NODE_KIND_LIST(ENUM_ITEM) 69#undef ENUM_ITEM 70 default: 71 UNREACHABLE(); 72 } 73 DCHECK_EQ(result == TypeOracle::GetNeverType(), 74 assembler().CurrentBlockIsComplete()); 75 return result; 76} 77 78void ImplementationVisitor::BeginGeneratedFiles() { 79 std::set<SourceId> contains_class_definitions; 80 for (const ClassType* type : TypeOracle::GetClasses()) { 81 if (type->ShouldGenerateCppClassDefinitions()) { 82 contains_class_definitions.insert(type->AttributedToFile()); 83 } 84 } 85 86 for (SourceId source : SourceFileMap::AllSources()) { 87 auto& streams = GlobalContext::GeneratedPerFile(source); 88 // Output beginning of CSA .cc file. 89 { 90 cpp::File& file = streams.csa_cc; 91 92 for (const std::string& include_path : GlobalContext::CppIncludes()) { 93 file << "#include " << StringLiteralQuote(include_path) << "\n"; 94 } 95 96 file << "// Required Builtins:\n"; 97 file << "#include \"torque-generated/" + 98 SourceFileMap::PathFromV8RootWithoutExtension(source) + 99 "-tq-csa.h\"\n"; 100 // Now that required include files are collected while generting the file, 101 // we only know the full set at the end. Insert a marker here that is 102 // replaced with the list of includes at the very end. 103 // TODO(nicohartmann@): This is not the most beautiful way to do this, 104 // replace once the cpp file builder is available, where this can be 105 // handled easily. 106 file << BuiltinIncludesMarker; 107 file << "\n"; 108 109 streams.csa_cc.BeginNamespace("v8", "internal"); 110 streams.csa_ccfile << "\n"; 111 } 112 // Output beginning of CSA .h file. 113 { 114 cpp::File& file = streams.csa_header; 115 std::string header_define = 116 "V8_GEN_TORQUE_GENERATED_" + 117 UnderlinifyPath(SourceFileMap::PathFromV8Root(source)) + "_CSA_H_"; 118 streams.csa_header.BeginIncludeGuard(header_define); 119 file << "#include \"src/builtins/torque-csa-header-includes.h\"\n"; 120 file << "\n"; 121 122 streams.csa_header.BeginNamespace("v8", "internal"); 123 streams.csa_headerfile << "\n"; 124 } 125 // Output beginning of class definition .cc file. 126 { 127 cpp::File& file = streams.class_definition_cc; 128 if (contains_class_definitions.count(source) != 0) { 129 file << "#include \"" 130 << SourceFileMap::PathFromV8RootWithoutExtension(source) 131 << "-inl.h\"\n\n"; 132 file << "#include \"torque-generated/class-verifiers.h\"\n"; 133 file << "#include \"src/objects/instance-type-inl.h\"\n\n"; 134 } 135 136 streams.class_definition_cc.BeginNamespace("v8", "internal"); 137 streams.class_definition_ccfile << "\n"; 138 } 139 } 140} 141 142void ImplementationVisitor::EndGeneratedFiles() { 143 for (SourceId file : SourceFileMap::AllSources()) { 144 auto& streams = GlobalContext::GeneratedPerFile(file); 145 146 // Output ending of CSA .cc file. 147 streams.csa_cc.EndNamespace("v8", "internal"); 148 149 // Output ending of CSA .h file. 150 { 151 std::string header_define = 152 "V8_GEN_TORQUE_GENERATED_" + 153 UnderlinifyPath(SourceFileMap::PathFromV8Root(file)) + "_CSA_H_"; 154 155 streams.csa_header.EndNamespace("v8", "internal"); 156 streams.csa_headerfile << "\n"; 157 streams.csa_header.EndIncludeGuard(header_define); 158 } 159 160 // Output ending of class definition .cc file. 161 streams.class_definition_cc.EndNamespace("v8", "internal"); 162 } 163} 164 165void ImplementationVisitor::BeginDebugMacrosFile() { 166 // TODO(torque-builer): Can use builder for debug_macros_*_ 167 std::ostream& source = debug_macros_cc_; 168 std::ostream& header = debug_macros_h_; 169 170 source << "#include \"torque-generated/debug-macros.h\"\n\n"; 171 source << "#include \"src/objects/swiss-name-dictionary.h\"\n"; 172 source << "#include \"src/objects/ordered-hash-table.h\"\n"; 173 source << "#include \"tools/debug_helper/debug-macro-shims.h\"\n"; 174 source << "#include \"include/v8-internal.h\"\n"; 175 source << "\n"; 176 177 source << "namespace v8 {\n" 178 << "namespace internal {\n" 179 << "namespace debug_helper_internal {\n" 180 << "\n"; 181 182 const char* kHeaderDefine = "V8_GEN_TORQUE_GENERATED_DEBUG_MACROS_H_"; 183 header << "#ifndef " << kHeaderDefine << "\n"; 184 header << "#define " << kHeaderDefine << "\n\n"; 185 header << "#include \"tools/debug_helper/debug-helper-internal.h\"\n"; 186 header << "#include \"src/numbers/integer-literal.h\"\n"; 187 header << "\n"; 188 189 header << "namespace v8 {\n" 190 << "namespace internal {\n" 191 << "namespace debug_helper_internal {\n" 192 << "\n"; 193} 194 195void ImplementationVisitor::EndDebugMacrosFile() { 196 // TODO(torque-builder): Can use builder for debug_macros_*_ 197 std::ostream& source = debug_macros_cc_; 198 std::ostream& header = debug_macros_h_; 199 200 source << "} // namespace internal\n" 201 << "} // namespace v8\n" 202 << "} // namespace debug_helper_internal\n" 203 << "\n"; 204 205 header << "\n} // namespace internal\n" 206 << "} // namespace v8\n" 207 << "} // namespace debug_helper_internal\n" 208 << "\n"; 209 header << "#endif // V8_GEN_TORQUE_GENERATED_DEBUG_MACROS_H_\n"; 210} 211 212void ImplementationVisitor::Visit(NamespaceConstant* decl) { 213 Signature signature{{}, base::nullopt, {{}, false}, 0, decl->type(), 214 {}, false}; 215 216 BindingsManagersScope bindings_managers_scope; 217 218 cpp::Function f = 219 GenerateFunction(nullptr, decl->external_name(), signature, {}); 220 221 f.PrintDeclaration(csa_headerfile()); 222 223 f.PrintDefinition(csa_ccfile(), [&](std::ostream& stream) { 224 stream << " compiler::CodeAssembler ca_(state_);\n"; 225 226 DCHECK(!signature.return_type->IsVoidOrNever()); 227 228 assembler_ = CfgAssembler(Stack<const Type*>{}); 229 230 VisitResult expression_result = Visit(decl->body()); 231 VisitResult return_result = 232 GenerateImplicitConvert(signature.return_type, expression_result); 233 234 CSAGenerator csa_generator{assembler().Result(), stream}; 235 Stack<std::string> values = *csa_generator.EmitGraph(Stack<std::string>{}); 236 237 assembler_ = base::nullopt; 238 239 stream << " return "; 240 CSAGenerator::EmitCSAValue(return_result, values, stream); 241 stream << ";"; 242 }); 243} 244 245void ImplementationVisitor::Visit(TypeAlias* alias) { 246 if (alias->IsRedeclaration()) return; 247 if (const ClassType* class_type = ClassType::DynamicCast(alias->type())) { 248 if (class_type->IsExtern() && !class_type->nspace()->IsDefaultNamespace()) { 249 Error( 250 "extern classes are currently only supported in the default " 251 "namespace"); 252 } 253 } 254} 255 256class ImplementationVisitor::MacroInliningScope { 257 public: 258 MacroInliningScope(ImplementationVisitor* visitor, const Macro* macro) 259 : visitor_(visitor), macro_(macro) { 260 if (!visitor_->inlining_macros_.insert(macro).second) { 261 // Recursive macro expansion would just keep going until stack overflow. 262 // To avoid crashes, throw an error immediately. 263 ReportError("Recursive macro call to ", *macro); 264 } 265 } 266 ~MacroInliningScope() { visitor_->inlining_macros_.erase(macro_); } 267 268 private: 269 ImplementationVisitor* visitor_; 270 const Macro* macro_; 271}; 272 273VisitResult ImplementationVisitor::InlineMacro( 274 Macro* macro, base::Optional<LocationReference> this_reference, 275 const std::vector<VisitResult>& arguments, 276 const std::vector<Block*> label_blocks) { 277 MacroInliningScope macro_inlining_scope(this, macro); 278 CurrentScope::Scope current_scope(macro); 279 BindingsManagersScope bindings_managers_scope; 280 CurrentCallable::Scope current_callable(macro); 281 CurrentReturnValue::Scope current_return_value; 282 const Signature& signature = macro->signature(); 283 const Type* return_type = macro->signature().return_type; 284 bool can_return = return_type != TypeOracle::GetNeverType(); 285 286 BlockBindings<LocalValue> parameter_bindings(&ValueBindingsManager::Get()); 287 BlockBindings<LocalLabel> label_bindings(&LabelBindingsManager::Get()); 288 DCHECK_EQ(macro->signature().parameter_names.size(), 289 arguments.size() + (this_reference ? 1 : 0)); 290 DCHECK_EQ(this_reference.has_value(), macro->IsMethod()); 291 292 // Bind the this for methods. Methods that modify a struct-type "this" must 293 // only be called if the this is in a variable, in which case the 294 // LocalValue is non-const. Otherwise, the LocalValue used for the parameter 295 // binding is const, and thus read-only, which will cause errors if 296 // modified, e.g. when called by a struct method that sets the structs 297 // fields. This prevents using temporary struct values for anything other 298 // than read operations. 299 if (this_reference) { 300 DCHECK(macro->IsMethod()); 301 parameter_bindings.Add(kThisParameterName, LocalValue{*this_reference}, 302 true); 303 // TODO(v8:12261): Tracking 'this'-binding for kythe led to a few weird 304 // issues. Review to fully support 'this' in methods. 305 } 306 307 size_t count = 0; 308 for (auto arg : arguments) { 309 if (this_reference && count == signature.implicit_count) count++; 310 const bool mark_as_used = signature.implicit_count > count; 311 const Identifier* name = macro->parameter_names()[count++]; 312 Binding<LocalValue>* binding = 313 parameter_bindings.Add(name, 314 LocalValue{LocationReference::Temporary( 315 arg, "parameter " + name->value)}, 316 mark_as_used); 317 if (GlobalContext::collect_kythe_data()) { 318 KytheData::AddBindingDefinition(binding); 319 } 320 } 321 322 DCHECK_EQ(label_blocks.size(), signature.labels.size()); 323 for (size_t i = 0; i < signature.labels.size(); ++i) { 324 const LabelDeclaration& label_info = signature.labels[i]; 325 Binding<LocalLabel>* binding = label_bindings.Add( 326 label_info.name, LocalLabel{label_blocks[i], label_info.types}); 327 if (GlobalContext::collect_kythe_data()) { 328 KytheData::AddBindingDefinition(binding); 329 } 330 } 331 332 Block* macro_end; 333 base::Optional<Binding<LocalLabel>> macro_end_binding; 334 if (can_return) { 335 Stack<const Type*> stack = assembler().CurrentStack(); 336 std::vector<const Type*> lowered_return_types = LowerType(return_type); 337 stack.PushMany(lowered_return_types); 338 if (!return_type->IsConstexpr()) { 339 SetReturnValue(VisitResult(return_type, 340 stack.TopRange(lowered_return_types.size()))); 341 } 342 // The stack copy used to initialize the _macro_end block is only used 343 // as a template for the actual gotos generated by return statements. It 344 // doesn't correspond to any real return values, and thus shouldn't contain 345 // top types, because these would pollute actual return value types that get 346 // unioned with them for return statements, erroneously forcing them to top. 347 for (auto i = stack.begin(); i != stack.end(); ++i) { 348 if ((*i)->IsTopType()) { 349 *i = TopType::cast(*i)->source_type(); 350 } 351 } 352 macro_end = assembler().NewBlock(std::move(stack)); 353 macro_end_binding.emplace(&LabelBindingsManager::Get(), kMacroEndLabelName, 354 LocalLabel{macro_end, {return_type}}); 355 } else { 356 SetReturnValue(VisitResult::NeverResult()); 357 } 358 359 const Type* result = Visit(*macro->body()); 360 361 if (result->IsNever()) { 362 if (!return_type->IsNever() && !macro->HasReturns()) { 363 std::stringstream s; 364 s << "macro " << macro->ReadableName() 365 << " that never returns must have return type never"; 366 ReportError(s.str()); 367 } 368 } else { 369 if (return_type->IsNever()) { 370 std::stringstream s; 371 s << "macro " << macro->ReadableName() 372 << " has implicit return at end of its declartion but return type " 373 "never"; 374 ReportError(s.str()); 375 } else if (!macro->signature().return_type->IsVoid()) { 376 std::stringstream s; 377 s << "macro " << macro->ReadableName() 378 << " expects to return a value but doesn't on all paths"; 379 ReportError(s.str()); 380 } 381 } 382 if (!result->IsNever()) { 383 assembler().Goto(macro_end); 384 } 385 386 if (macro->HasReturns() || !result->IsNever()) { 387 assembler().Bind(macro_end); 388 } 389 390 return GetAndClearReturnValue(); 391} 392 393void ImplementationVisitor::VisitMacroCommon(Macro* macro) { 394 CurrentCallable::Scope current_callable(macro); 395 const Signature& signature = macro->signature(); 396 const Type* return_type = macro->signature().return_type; 397 bool can_return = return_type != TypeOracle::GetNeverType(); 398 bool has_return_value = 399 can_return && return_type != TypeOracle::GetVoidType(); 400 401 cpp::Function f = GenerateMacroFunctionDeclaration(macro); 402 f.PrintDeclaration(csa_headerfile()); 403 csa_headerfile() << "\n"; 404 405 cpp::File csa_cc(csa_ccfile()); 406 407 // Avoid multiple-definition errors since it is possible for multiple 408 // generated -inl.inc files to all contain function definitions for the same 409 // Torque macro. 410 base::Optional<cpp::IncludeGuardScope> include_guard; 411 if (output_type_ == OutputType::kCC) { 412 include_guard.emplace(&csa_cc, "V8_INTERNAL_DEFINED_"s + macro->CCName()); 413 } else if (output_type_ == OutputType::kCCDebug) { 414 include_guard.emplace(&csa_cc, 415 "V8_INTERNAL_DEFINED_"s + macro->CCDebugName()); 416 } 417 418 f.PrintBeginDefinition(csa_ccfile()); 419 420 if (output_type_ == OutputType::kCC) { 421 // For now, generated C++ is only for field offset computations. If we ever 422 // generate C++ code that can allocate, then it should be handlified. 423 csa_ccfile() << " DisallowGarbageCollection no_gc;\n"; 424 } else if (output_type_ == OutputType::kCSA) { 425 csa_ccfile() << " compiler::CodeAssembler ca_(state_);\n"; 426 csa_ccfile() 427 << " compiler::CodeAssembler::SourcePositionScope pos_scope(&ca_);\n"; 428 } 429 430 Stack<std::string> lowered_parameters; 431 Stack<const Type*> lowered_parameter_types; 432 433 std::vector<VisitResult> arguments; 434 435 base::Optional<LocationReference> this_reference; 436 if (Method* method = Method::DynamicCast(macro)) { 437 const Type* this_type = method->aggregate_type(); 438 LowerParameter(this_type, ExternalParameterName(kThisParameterName), 439 &lowered_parameters); 440 StackRange range = lowered_parameter_types.PushMany(LowerType(this_type)); 441 VisitResult this_result = VisitResult(this_type, range); 442 // For classes, mark 'this' as a temporary to prevent assignment to it. 443 // Note that using a VariableAccess for non-class types is technically 444 // incorrect because changes to the 'this' variable do not get reflected 445 // to the caller. Therefore struct methods should always be inlined and a 446 // C++ version should never be generated, since it would be incorrect. 447 // However, in order to be able to type- and semantics-check even unused 448 // struct methods, set the this_reference to be the local variable copy of 449 // the passed-in this, which allows the visitor to at least find and report 450 // errors. 451 this_reference = 452 (this_type->IsClassType()) 453 ? LocationReference::Temporary(this_result, "this parameter") 454 : LocationReference::VariableAccess(this_result); 455 } 456 457 for (size_t i = 0; i < macro->signature().parameter_names.size(); ++i) { 458 if (this_reference && i == macro->signature().implicit_count) continue; 459 const std::string& name = macro->parameter_names()[i]->value; 460 std::string external_name = ExternalParameterName(name); 461 const Type* type = macro->signature().types()[i]; 462 463 if (type->IsConstexpr()) { 464 arguments.push_back(VisitResult(type, external_name)); 465 } else { 466 LowerParameter(type, external_name, &lowered_parameters); 467 StackRange range = lowered_parameter_types.PushMany(LowerType(type)); 468 arguments.push_back(VisitResult(type, range)); 469 } 470 } 471 472 DCHECK_EQ(lowered_parameters.Size(), lowered_parameter_types.Size()); 473 assembler_ = CfgAssembler(lowered_parameter_types); 474 475 std::vector<Block*> label_blocks; 476 for (const LabelDeclaration& label_info : signature.labels) { 477 Stack<const Type*> label_input_stack; 478 for (const Type* type : label_info.types) { 479 label_input_stack.PushMany(LowerType(type)); 480 } 481 Block* block = assembler().NewBlock(std::move(label_input_stack)); 482 label_blocks.push_back(block); 483 } 484 485 VisitResult return_value = 486 InlineMacro(macro, this_reference, arguments, label_blocks); 487 Block* end = assembler().NewBlock(); 488 if (return_type != TypeOracle::GetNeverType()) { 489 assembler().Goto(end); 490 } 491 492 for (size_t i = 0; i < label_blocks.size(); ++i) { 493 Block* label_block = label_blocks[i]; 494 const LabelDeclaration& label_info = signature.labels[i]; 495 assembler().Bind(label_block); 496 std::vector<std::string> label_parameter_variables; 497 for (size_t j = 0; j < label_info.types.size(); ++j) { 498 LowerLabelParameter(label_info.types[j], 499 ExternalLabelParameterName(label_info.name->value, j), 500 &label_parameter_variables); 501 } 502 assembler().Emit(GotoExternalInstruction{ 503 ExternalLabelName(label_info.name->value), label_parameter_variables}); 504 } 505 506 if (return_type != TypeOracle::GetNeverType()) { 507 assembler().Bind(end); 508 } 509 510 base::Optional<Stack<std::string>> values; 511 if (output_type_ == OutputType::kCC) { 512 CCGenerator cc_generator{assembler().Result(), csa_ccfile()}; 513 values = cc_generator.EmitGraph(lowered_parameters); 514 } else if (output_type_ == OutputType::kCCDebug) { 515 CCGenerator cc_generator{assembler().Result(), csa_ccfile(), true}; 516 values = cc_generator.EmitGraph(lowered_parameters); 517 } else { 518 CSAGenerator csa_generator{assembler().Result(), csa_ccfile()}; 519 values = csa_generator.EmitGraph(lowered_parameters); 520 } 521 522 assembler_ = base::nullopt; 523 524 if (has_return_value) { 525 csa_ccfile() << " return "; 526 if (output_type_ == OutputType::kCCDebug) { 527 csa_ccfile() << "{d::MemoryAccessResult::kOk, "; 528 CCGenerator::EmitCCValue(return_value, *values, csa_ccfile()); 529 csa_ccfile() << "}"; 530 } else if (output_type_ == OutputType::kCC) { 531 CCGenerator::EmitCCValue(return_value, *values, csa_ccfile()); 532 } else { 533 CSAGenerator::EmitCSAValue(return_value, *values, csa_ccfile()); 534 } 535 csa_ccfile() << ";\n"; 536 } 537 f.PrintEndDefinition(csa_ccfile()); 538 539 include_guard.reset(); 540} 541 542void ImplementationVisitor::Visit(TorqueMacro* macro) { 543 VisitMacroCommon(macro); 544} 545 546void ImplementationVisitor::Visit(Method* method) { 547 DCHECK(!method->IsExternal()); 548 VisitMacroCommon(method); 549} 550 551namespace { 552 553std::string AddParameter(size_t i, Builtin* builtin, 554 Stack<std::string>* parameters, 555 Stack<const Type*>* parameter_types, 556 BlockBindings<LocalValue>* parameter_bindings, 557 bool mark_as_used) { 558 const Identifier* name = builtin->signature().parameter_names[i]; 559 const Type* type = builtin->signature().types()[i]; 560 std::string external_name = "parameter" + std::to_string(i); 561 parameters->Push(external_name); 562 StackRange range = parameter_types->PushMany(LowerType(type)); 563 Binding<LocalValue>* binding = parameter_bindings->Add( 564 name, 565 LocalValue{LocationReference::Temporary(VisitResult(type, range), 566 "parameter " + name->value)}, 567 mark_as_used); 568 if (GlobalContext::collect_kythe_data()) { 569 KytheData::AddBindingDefinition(binding); 570 } 571 return external_name; 572} 573 574} // namespace 575 576void ImplementationVisitor::Visit(Builtin* builtin) { 577 if (builtin->IsExternal()) return; 578 CurrentScope::Scope current_scope(builtin); 579 CurrentCallable::Scope current_callable(builtin); 580 CurrentReturnValue::Scope current_return_value; 581 582 const std::string& name = builtin->ExternalName(); 583 const Signature& signature = builtin->signature(); 584 csa_ccfile() << "TF_BUILTIN(" << name << ", CodeStubAssembler) {\n" 585 << " compiler::CodeAssemblerState* state_ = state();" 586 << " compiler::CodeAssembler ca_(state());\n"; 587 588 Stack<const Type*> parameter_types; 589 Stack<std::string> parameters; 590 591 BindingsManagersScope bindings_managers_scope; 592 593 BlockBindings<LocalValue> parameter_bindings(&ValueBindingsManager::Get()); 594 595 if (builtin->IsVarArgsJavaScript() || builtin->IsFixedArgsJavaScript()) { 596 if (builtin->IsVarArgsJavaScript()) { 597 DCHECK(signature.parameter_types.var_args); 598 if (signature.ExplicitCount() > 0) { 599 Error("Cannot mix explicit parameters with varargs.") 600 .Position(signature.parameter_names[signature.implicit_count]->pos); 601 } 602 603 csa_ccfile() << " TNode<Word32T> argc = UncheckedParameter<Word32T>(" 604 << "Descriptor::kJSActualArgumentsCount);\n"; 605 csa_ccfile() << " TNode<IntPtrT> " 606 "arguments_length(ChangeInt32ToIntPtr(UncheckedCast<" 607 "Int32T>(argc)));\n"; 608 csa_ccfile() << " TNode<RawPtrT> arguments_frame = " 609 "UncheckedCast<RawPtrT>(LoadFramePointer());\n"; 610 csa_ccfile() 611 << " TorqueStructArguments " 612 "torque_arguments(GetFrameArguments(arguments_frame, " 613 "arguments_length, FrameArgumentsArgcType::kCountIncludesReceiver" 614 << "));\n"; 615 csa_ccfile() 616 << " CodeStubArguments arguments(this, torque_arguments);\n"; 617 618 parameters.Push("torque_arguments.frame"); 619 parameters.Push("torque_arguments.base"); 620 parameters.Push("torque_arguments.length"); 621 parameters.Push("torque_arguments.actual_count"); 622 const Type* arguments_type = TypeOracle::GetArgumentsType(); 623 StackRange range = parameter_types.PushMany(LowerType(arguments_type)); 624 parameter_bindings.Add(*signature.arguments_variable, 625 LocalValue{LocationReference::Temporary( 626 VisitResult(arguments_type, range), 627 "parameter " + *signature.arguments_variable)}, 628 true); 629 } 630 631 for (size_t i = 0; i < signature.implicit_count; ++i) { 632 const std::string& param_name = signature.parameter_names[i]->value; 633 SourcePosition param_pos = signature.parameter_names[i]->pos; 634 std::string generated_name = AddParameter( 635 i, builtin, ¶meters, ¶meter_types, ¶meter_bindings, true); 636 const Type* actual_type = signature.parameter_types.types[i]; 637 std::vector<const Type*> expected_types; 638 if (param_name == "context") { 639 csa_ccfile() << " TNode<NativeContext> " << generated_name 640 << " = UncheckedParameter<NativeContext>(" 641 << "Descriptor::kContext);\n"; 642 csa_ccfile() << " USE(" << generated_name << ");\n"; 643 expected_types = {TypeOracle::GetNativeContextType(), 644 TypeOracle::GetContextType()}; 645 } else if (param_name == "receiver") { 646 csa_ccfile() 647 << " TNode<Object> " << generated_name << " = " 648 << (builtin->IsVarArgsJavaScript() 649 ? "arguments.GetReceiver()" 650 : "UncheckedParameter<Object>(Descriptor::kReceiver)") 651 << ";\n"; 652 csa_ccfile() << " USE(" << generated_name << ");\n"; 653 expected_types = {TypeOracle::GetJSAnyType()}; 654 } else if (param_name == "newTarget") { 655 csa_ccfile() << " TNode<Object> " << generated_name 656 << " = UncheckedParameter<Object>(" 657 << "Descriptor::kJSNewTarget);\n"; 658 csa_ccfile() << "USE(" << generated_name << ");\n"; 659 expected_types = {TypeOracle::GetJSAnyType()}; 660 } else if (param_name == "target") { 661 csa_ccfile() << " TNode<JSFunction> " << generated_name 662 << " = UncheckedParameter<JSFunction>(" 663 << "Descriptor::kJSTarget);\n"; 664 csa_ccfile() << "USE(" << generated_name << ");\n"; 665 expected_types = {TypeOracle::GetJSFunctionType()}; 666 } else { 667 Error( 668 "Unexpected implicit parameter \"", param_name, 669 "\" for JavaScript calling convention, " 670 "expected \"context\", \"receiver\", \"target\", or \"newTarget\"") 671 .Position(param_pos); 672 expected_types = {actual_type}; 673 } 674 if (std::find(expected_types.begin(), expected_types.end(), 675 actual_type) == expected_types.end()) { 676 Error("According to JavaScript calling convention, expected parameter ", 677 param_name, " to have type ", PrintList(expected_types, " or "), 678 " but found type ", *actual_type) 679 .Position(param_pos); 680 } 681 } 682 683 for (size_t i = signature.implicit_count; 684 i < signature.parameter_names.size(); ++i) { 685 const std::string& parameter_name = signature.parameter_names[i]->value; 686 const Type* type = signature.types()[i]; 687 const bool mark_as_used = signature.implicit_count > i; 688 std::string var = AddParameter(i, builtin, ¶meters, ¶meter_types, 689 ¶meter_bindings, mark_as_used); 690 csa_ccfile() << " " << type->GetGeneratedTypeName() << " " << var 691 << " = " 692 << "UncheckedParameter<" << type->GetGeneratedTNodeTypeName() 693 << ">(Descriptor::k" << CamelifyString(parameter_name) 694 << ");\n"; 695 csa_ccfile() << " USE(" << var << ");\n"; 696 } 697 698 } else { 699 DCHECK(builtin->IsStub()); 700 701 for (size_t i = 0; i < signature.parameter_names.size(); ++i) { 702 const std::string& parameter_name = signature.parameter_names[i]->value; 703 const Type* type = signature.types()[i]; 704 const bool mark_as_used = signature.implicit_count > i; 705 std::string var = AddParameter(i, builtin, ¶meters, ¶meter_types, 706 ¶meter_bindings, mark_as_used); 707 csa_ccfile() << " " << type->GetGeneratedTypeName() << " " << var 708 << " = " 709 << "UncheckedParameter<" << type->GetGeneratedTNodeTypeName() 710 << ">(Descriptor::k" << CamelifyString(parameter_name) 711 << ");\n"; 712 csa_ccfile() << " USE(" << var << ");\n"; 713 } 714 } 715 assembler_ = CfgAssembler(parameter_types); 716 const Type* body_result = Visit(*builtin->body()); 717 if (body_result != TypeOracle::GetNeverType()) { 718 ReportError("control reaches end of builtin, expected return of a value"); 719 } 720 CSAGenerator csa_generator{assembler().Result(), csa_ccfile(), 721 builtin->kind()}; 722 csa_generator.EmitGraph(parameters); 723 assembler_ = base::nullopt; 724 csa_ccfile() << "}\n\n"; 725} 726 727const Type* ImplementationVisitor::Visit(VarDeclarationStatement* stmt) { 728 BlockBindings<LocalValue> block_bindings(&ValueBindingsManager::Get()); 729 return Visit(stmt, &block_bindings); 730} 731 732const Type* ImplementationVisitor::Visit( 733 VarDeclarationStatement* stmt, BlockBindings<LocalValue>* block_bindings) { 734 // const qualified variables are required to be initialized properly. 735 if (stmt->const_qualified && !stmt->initializer) { 736 ReportError("local constant \"", stmt->name, "\" is not initialized."); 737 } 738 739 base::Optional<const Type*> type; 740 if (stmt->type) { 741 type = TypeVisitor::ComputeType(*stmt->type); 742 } 743 base::Optional<VisitResult> init_result; 744 if (stmt->initializer) { 745 StackScope scope(this); 746 init_result = Visit(*stmt->initializer); 747 if (type) { 748 init_result = GenerateImplicitConvert(*type, *init_result); 749 } 750 type = init_result->type(); 751 if ((*type)->IsConstexpr() && !stmt->const_qualified) { 752 Error("Use 'const' instead of 'let' for variable '", stmt->name->value, 753 "' of constexpr type '", (*type)->ToString(), "'.") 754 .Position(stmt->name->pos) 755 .Throw(); 756 } 757 init_result = scope.Yield(*init_result); 758 } else { 759 DCHECK(type.has_value()); 760 if ((*type)->IsConstexpr()) { 761 ReportError("constexpr variables need an initializer"); 762 } 763 TypeVector lowered_types = LowerType(*type); 764 for (const Type* t : lowered_types) { 765 assembler().Emit(PushUninitializedInstruction{TypeOracle::GetTopType( 766 "uninitialized variable '" + stmt->name->value + "' of type " + 767 t->ToString() + " originally defined at " + 768 PositionAsString(stmt->pos), 769 t)}); 770 } 771 init_result = 772 VisitResult(*type, assembler().TopRange(lowered_types.size())); 773 } 774 LocationReference ref = stmt->const_qualified 775 ? LocationReference::Temporary( 776 *init_result, "const " + stmt->name->value) 777 : LocationReference::VariableAccess(*init_result); 778 block_bindings->Add(stmt->name, LocalValue{std::move(ref)}); 779 return TypeOracle::GetVoidType(); 780} 781 782const Type* ImplementationVisitor::Visit(TailCallStatement* stmt) { 783 return Visit(stmt->call, true).type(); 784} 785 786VisitResult ImplementationVisitor::Visit(ConditionalExpression* expr) { 787 Block* true_block = assembler().NewBlock(assembler().CurrentStack()); 788 Block* false_block = assembler().NewBlock(assembler().CurrentStack()); 789 Block* done_block = assembler().NewBlock(); 790 Block* true_conversion_block = assembler().NewBlock(); 791 GenerateExpressionBranch(expr->condition, true_block, false_block); 792 793 VisitResult left; 794 VisitResult right; 795 796 { 797 // The code for both paths of the conditional need to be generated first 798 // before evaluating the conditional expression because the common type of 799 // the result of both the true and false of the condition needs to be known 800 // to convert both branches to a common type. 801 assembler().Bind(true_block); 802 StackScope left_scope(this); 803 left = Visit(expr->if_true); 804 assembler().Goto(true_conversion_block); 805 806 const Type* common_type; 807 { 808 assembler().Bind(false_block); 809 StackScope right_scope(this); 810 right = Visit(expr->if_false); 811 common_type = GetCommonType(left.type(), right.type()); 812 right = right_scope.Yield(GenerateImplicitConvert(common_type, right)); 813 assembler().Goto(done_block); 814 } 815 816 assembler().Bind(true_conversion_block); 817 left = left_scope.Yield(GenerateImplicitConvert(common_type, left)); 818 assembler().Goto(done_block); 819 } 820 821 assembler().Bind(done_block); 822 CHECK_EQ(left, right); 823 return left; 824} 825 826VisitResult ImplementationVisitor::Visit(LogicalOrExpression* expr) { 827 StackScope outer_scope(this); 828 VisitResult left_result = Visit(expr->left); 829 830 if (left_result.type()->IsConstexprBool()) { 831 VisitResult right_result = Visit(expr->right); 832 if (!right_result.type()->IsConstexprBool()) { 833 ReportError( 834 "expected type constexpr bool on right-hand side of operator " 835 "||"); 836 } 837 return VisitResult(TypeOracle::GetConstexprBoolType(), 838 std::string("(") + left_result.constexpr_value() + 839 " || " + right_result.constexpr_value() + ")"); 840 } 841 842 Block* true_block = assembler().NewBlock(); 843 Block* false_block = assembler().NewBlock(); 844 Block* done_block = assembler().NewBlock(); 845 846 left_result = GenerateImplicitConvert(TypeOracle::GetBoolType(), left_result); 847 GenerateBranch(left_result, true_block, false_block); 848 849 assembler().Bind(true_block); 850 VisitResult true_result = GenerateBoolConstant(true); 851 assembler().Goto(done_block); 852 853 assembler().Bind(false_block); 854 VisitResult false_result; 855 { 856 StackScope false_block_scope(this); 857 false_result = false_block_scope.Yield( 858 GenerateImplicitConvert(TypeOracle::GetBoolType(), Visit(expr->right))); 859 } 860 assembler().Goto(done_block); 861 862 assembler().Bind(done_block); 863 DCHECK_EQ(true_result, false_result); 864 return outer_scope.Yield(true_result); 865} 866 867VisitResult ImplementationVisitor::Visit(LogicalAndExpression* expr) { 868 StackScope outer_scope(this); 869 VisitResult left_result = Visit(expr->left); 870 871 if (left_result.type()->IsConstexprBool()) { 872 VisitResult right_result = Visit(expr->right); 873 if (!right_result.type()->IsConstexprBool()) { 874 ReportError( 875 "expected type constexpr bool on right-hand side of operator " 876 "&&"); 877 } 878 return VisitResult(TypeOracle::GetConstexprBoolType(), 879 std::string("(") + left_result.constexpr_value() + 880 " && " + right_result.constexpr_value() + ")"); 881 } 882 883 Block* true_block = assembler().NewBlock(); 884 Block* false_block = assembler().NewBlock(); 885 Block* done_block = assembler().NewBlock(); 886 887 left_result = GenerateImplicitConvert(TypeOracle::GetBoolType(), left_result); 888 GenerateBranch(left_result, true_block, false_block); 889 890 assembler().Bind(true_block); 891 VisitResult true_result; 892 { 893 StackScope true_block_scope(this); 894 VisitResult right_result = Visit(expr->right); 895 if (TryGetSourceForBitfieldExpression(expr->left) != nullptr && 896 TryGetSourceForBitfieldExpression(expr->right) != nullptr && 897 TryGetSourceForBitfieldExpression(expr->left)->value == 898 TryGetSourceForBitfieldExpression(expr->right)->value) { 899 Lint( 900 "Please use & rather than && when checking multiple bitfield " 901 "values, to avoid complexity in generated code."); 902 } 903 true_result = true_block_scope.Yield( 904 GenerateImplicitConvert(TypeOracle::GetBoolType(), right_result)); 905 } 906 assembler().Goto(done_block); 907 908 assembler().Bind(false_block); 909 VisitResult false_result = GenerateBoolConstant(false); 910 assembler().Goto(done_block); 911 912 assembler().Bind(done_block); 913 DCHECK_EQ(true_result, false_result); 914 return outer_scope.Yield(true_result); 915} 916 917VisitResult ImplementationVisitor::Visit(IncrementDecrementExpression* expr) { 918 StackScope scope(this); 919 LocationReference location_ref = GetLocationReference(expr->location); 920 VisitResult current_value = GenerateFetchFromLocation(location_ref); 921 VisitResult one = {TypeOracle::GetConstInt31Type(), "1"}; 922 Arguments args; 923 args.parameters = {current_value, one}; 924 VisitResult assignment_value = GenerateCall( 925 expr->op == IncrementDecrementOperator::kIncrement ? "+" : "-", args); 926 GenerateAssignToLocation(location_ref, assignment_value); 927 return scope.Yield(expr->postfix ? current_value : assignment_value); 928} 929 930VisitResult ImplementationVisitor::Visit(AssignmentExpression* expr) { 931 StackScope scope(this); 932 LocationReference location_ref = GetLocationReference(expr->location); 933 VisitResult assignment_value; 934 if (expr->op) { 935 VisitResult location_value = GenerateFetchFromLocation(location_ref); 936 assignment_value = Visit(expr->value); 937 Arguments args; 938 args.parameters = {location_value, assignment_value}; 939 assignment_value = GenerateCall(*expr->op, args); 940 GenerateAssignToLocation(location_ref, assignment_value); 941 } else { 942 assignment_value = Visit(expr->value); 943 GenerateAssignToLocation(location_ref, assignment_value); 944 } 945 return scope.Yield(assignment_value); 946} 947 948VisitResult ImplementationVisitor::Visit(FloatingPointLiteralExpression* expr) { 949 const Type* result_type = TypeOracle::GetConstFloat64Type(); 950 std::stringstream str; 951 str << std::setprecision(std::numeric_limits<double>::digits10 + 1) 952 << expr->value; 953 return VisitResult{result_type, str.str()}; 954} 955 956VisitResult ImplementationVisitor::Visit(IntegerLiteralExpression* expr) { 957 const Type* result_type = TypeOracle::GetIntegerLiteralType(); 958 std::stringstream str; 959 str << "IntegerLiteral(" 960 << (expr->value.is_negative() ? "true, 0x" : "false, 0x") << std::hex 961 << expr->value.absolute_value() << std::dec << "ull)"; 962 return VisitResult{result_type, str.str()}; 963} 964 965VisitResult ImplementationVisitor::Visit(AssumeTypeImpossibleExpression* expr) { 966 VisitResult result = Visit(expr->expression); 967 const Type* result_type = SubtractType( 968 result.type(), TypeVisitor::ComputeType(expr->excluded_type)); 969 if (result_type->IsNever()) { 970 ReportError("unreachable code"); 971 } 972 CHECK_EQ(LowerType(result_type), TypeVector{result_type}); 973 assembler().Emit(UnsafeCastInstruction{result_type}); 974 result.SetType(result_type); 975 return result; 976} 977 978VisitResult ImplementationVisitor::Visit(StringLiteralExpression* expr) { 979 return VisitResult{ 980 TypeOracle::GetConstStringType(), 981 "\"" + expr->literal.substr(1, expr->literal.size() - 2) + "\""}; 982} 983 984VisitResult ImplementationVisitor::GetBuiltinCode(Builtin* builtin) { 985 if (builtin->IsExternal() || builtin->kind() != Builtin::kStub) { 986 ReportError( 987 "creating function pointers is only allowed for internal builtins with " 988 "stub linkage"); 989 } 990 const Type* type = TypeOracle::GetBuiltinPointerType( 991 builtin->signature().parameter_types.types, 992 builtin->signature().return_type); 993 assembler().Emit( 994 PushBuiltinPointerInstruction{builtin->ExternalName(), type}); 995 return VisitResult(type, assembler().TopRange(1)); 996} 997 998VisitResult ImplementationVisitor::Visit(LocationExpression* expr) { 999 StackScope scope(this); 1000 return scope.Yield(GenerateFetchFromLocation(GetLocationReference(expr))); 1001} 1002 1003VisitResult ImplementationVisitor::Visit(FieldAccessExpression* expr) { 1004 StackScope scope(this); 1005 LocationReference location = GetLocationReference(expr); 1006 if (location.IsBitFieldAccess()) { 1007 if (auto* identifier = IdentifierExpression::DynamicCast(expr->object)) { 1008 bitfield_expressions_[expr] = identifier->name; 1009 } 1010 } 1011 return scope.Yield(GenerateFetchFromLocation(location)); 1012} 1013 1014const Type* ImplementationVisitor::Visit(GotoStatement* stmt) { 1015 Binding<LocalLabel>* label = LookupLabel(stmt->label->value); 1016 size_t parameter_count = label->parameter_types.size(); 1017 if (stmt->arguments.size() != parameter_count) { 1018 ReportError("goto to label has incorrect number of parameters (expected ", 1019 parameter_count, " found ", stmt->arguments.size(), ")"); 1020 } 1021 1022 if (GlobalContext::collect_language_server_data()) { 1023 LanguageServerData::AddDefinition(stmt->label->pos, 1024 label->declaration_position()); 1025 } 1026 if (GlobalContext::collect_kythe_data()) { 1027 KytheData::AddBindingUse(stmt->label->pos, label); 1028 } 1029 1030 size_t i = 0; 1031 StackRange arguments = assembler().TopRange(0); 1032 for (Expression* e : stmt->arguments) { 1033 StackScope scope(this); 1034 VisitResult result = Visit(e); 1035 const Type* parameter_type = label->parameter_types[i++]; 1036 result = GenerateImplicitConvert(parameter_type, result); 1037 arguments.Extend(scope.Yield(result).stack_range()); 1038 } 1039 1040 assembler().Goto(label->block, arguments.Size()); 1041 return TypeOracle::GetNeverType(); 1042} 1043 1044const Type* ImplementationVisitor::Visit(IfStatement* stmt) { 1045 bool has_else = stmt->if_false.has_value(); 1046 1047 if (stmt->is_constexpr) { 1048 VisitResult expression_result = Visit(stmt->condition); 1049 1050 if (!(expression_result.type() == TypeOracle::GetConstexprBoolType())) { 1051 std::stringstream stream; 1052 stream << "expression should return type constexpr bool " 1053 << "but returns type " << *expression_result.type(); 1054 ReportError(stream.str()); 1055 } 1056 1057 Block* true_block = assembler().NewBlock(); 1058 Block* false_block = assembler().NewBlock(); 1059 Block* done_block = assembler().NewBlock(); 1060 1061 assembler().Emit(ConstexprBranchInstruction{ 1062 expression_result.constexpr_value(), true_block, false_block}); 1063 1064 assembler().Bind(true_block); 1065 const Type* left_result = Visit(stmt->if_true); 1066 if (left_result == TypeOracle::GetVoidType()) { 1067 assembler().Goto(done_block); 1068 } 1069 1070 assembler().Bind(false_block); 1071 const Type* right_result = TypeOracle::GetVoidType(); 1072 if (has_else) { 1073 right_result = Visit(*stmt->if_false); 1074 } 1075 if (right_result == TypeOracle::GetVoidType()) { 1076 assembler().Goto(done_block); 1077 } 1078 1079 if (left_result->IsNever() != right_result->IsNever()) { 1080 std::stringstream stream; 1081 stream << "either both or neither branches in a constexpr if statement " 1082 "must reach their end at" 1083 << PositionAsString(stmt->pos); 1084 ReportError(stream.str()); 1085 } 1086 1087 if (left_result != TypeOracle::GetNeverType()) { 1088 assembler().Bind(done_block); 1089 } 1090 return left_result; 1091 } else { 1092 Block* true_block = assembler().NewBlock(assembler().CurrentStack(), 1093 IsDeferred(stmt->if_true)); 1094 Block* false_block = 1095 assembler().NewBlock(assembler().CurrentStack(), 1096 stmt->if_false && IsDeferred(*stmt->if_false)); 1097 GenerateExpressionBranch(stmt->condition, true_block, false_block); 1098 1099 Block* done_block; 1100 bool live = false; 1101 if (has_else) { 1102 done_block = assembler().NewBlock(); 1103 } else { 1104 done_block = false_block; 1105 live = true; 1106 } 1107 1108 assembler().Bind(true_block); 1109 { 1110 const Type* result = Visit(stmt->if_true); 1111 if (result == TypeOracle::GetVoidType()) { 1112 live = true; 1113 assembler().Goto(done_block); 1114 } 1115 } 1116 1117 if (has_else) { 1118 assembler().Bind(false_block); 1119 const Type* result = Visit(*stmt->if_false); 1120 if (result == TypeOracle::GetVoidType()) { 1121 live = true; 1122 assembler().Goto(done_block); 1123 } 1124 } 1125 1126 if (live) { 1127 assembler().Bind(done_block); 1128 } 1129 return live ? TypeOracle::GetVoidType() : TypeOracle::GetNeverType(); 1130 } 1131} 1132 1133const Type* ImplementationVisitor::Visit(WhileStatement* stmt) { 1134 Block* body_block = assembler().NewBlock(assembler().CurrentStack()); 1135 Block* exit_block = assembler().NewBlock(assembler().CurrentStack()); 1136 1137 Block* header_block = assembler().NewBlock(); 1138 assembler().Goto(header_block); 1139 1140 assembler().Bind(header_block); 1141 GenerateExpressionBranch(stmt->condition, body_block, exit_block); 1142 1143 assembler().Bind(body_block); 1144 { 1145 BreakContinueActivator activator{exit_block, header_block}; 1146 const Type* body_result = Visit(stmt->body); 1147 if (body_result != TypeOracle::GetNeverType()) { 1148 assembler().Goto(header_block); 1149 } 1150 } 1151 1152 assembler().Bind(exit_block); 1153 return TypeOracle::GetVoidType(); 1154} 1155 1156const Type* ImplementationVisitor::Visit(BlockStatement* block) { 1157 BlockBindings<LocalValue> block_bindings(&ValueBindingsManager::Get()); 1158 const Type* type = TypeOracle::GetVoidType(); 1159 for (Statement* s : block->statements) { 1160 CurrentSourcePosition::Scope source_position(s->pos); 1161 if (type->IsNever()) { 1162 ReportError("statement after non-returning statement"); 1163 } 1164 if (auto* var_declaration = VarDeclarationStatement::DynamicCast(s)) { 1165 type = Visit(var_declaration, &block_bindings); 1166 } else { 1167 type = Visit(s); 1168 } 1169 } 1170 return type; 1171} 1172 1173const Type* ImplementationVisitor::Visit(DebugStatement* stmt) { 1174#if defined(DEBUG) 1175 assembler().Emit(PrintConstantStringInstruction{"halting because of '" + 1176 stmt->reason + "' at " + 1177 PositionAsString(stmt->pos)}); 1178#endif 1179 assembler().Emit(AbortInstruction{stmt->never_continues 1180 ? AbortInstruction::Kind::kUnreachable 1181 : AbortInstruction::Kind::kDebugBreak}); 1182 if (stmt->never_continues) { 1183 return TypeOracle::GetNeverType(); 1184 } else { 1185 return TypeOracle::GetVoidType(); 1186 } 1187} 1188 1189namespace { 1190 1191std::string FormatAssertSource(const std::string& str) { 1192 // Replace all whitespace characters with a space character. 1193 std::string str_no_newlines = str; 1194 std::replace_if( 1195 str_no_newlines.begin(), str_no_newlines.end(), 1196 [](unsigned char c) { return isspace(c); }, ' '); 1197 1198 // str might include indentation, squash multiple space characters into one. 1199 std::string result; 1200 std::unique_copy(str_no_newlines.begin(), str_no_newlines.end(), 1201 std::back_inserter(result), 1202 [](char a, char b) { return a == ' ' && b == ' '; }); 1203 return result; 1204} 1205 1206} // namespace 1207 1208const Type* ImplementationVisitor::Visit(AssertStatement* stmt) { 1209 if (stmt->kind == AssertStatement::AssertKind::kStaticAssert) { 1210 std::string message = 1211 "static_assert(" + stmt->source + ") at " + ToString(stmt->pos); 1212 GenerateCall(QualifiedName({"", TORQUE_INTERNAL_NAMESPACE_STRING}, 1213 STATIC_ASSERT_MACRO_STRING), 1214 Arguments{{Visit(stmt->expression), 1215 VisitResult(TypeOracle::GetConstexprStringType(), 1216 StringLiteralQuote(message))}, 1217 {}}); 1218 return TypeOracle::GetVoidType(); 1219 } 1220 bool do_check = stmt->kind != AssertStatement::AssertKind::kDcheck || 1221 GlobalContext::force_assert_statements(); 1222#if defined(DEBUG) 1223 do_check = true; 1224#endif 1225 Block* resume_block; 1226 1227 if (!do_check) { 1228 Block* unreachable_block = assembler().NewBlock(assembler().CurrentStack()); 1229 resume_block = assembler().NewBlock(assembler().CurrentStack()); 1230 assembler().Goto(resume_block); 1231 assembler().Bind(unreachable_block); 1232 } 1233 1234 // CSA_DCHECK & co. are not used here on purpose for two reasons. First, 1235 // Torque allows and handles two types of expressions in the if protocol 1236 // automagically, ones that return TNode<BoolT> and those that use the 1237 // BranchIf(..., Label* true, Label* false) idiom. Because the machinery to 1238 // handle this is embedded in the expression handling and to it's not 1239 // possible to make the decision to use CSA_DCHECK or CSA_DCHECK_BRANCH 1240 // isn't trivial up-front. Secondly, on failure, the assert text should be 1241 // the corresponding Torque code, not the -gen.cc code, which would be the 1242 // case when using CSA_DCHECK_XXX. 1243 Block* true_block = assembler().NewBlock(assembler().CurrentStack()); 1244 Block* false_block = assembler().NewBlock(assembler().CurrentStack(), true); 1245 GenerateExpressionBranch(stmt->expression, true_block, false_block); 1246 1247 assembler().Bind(false_block); 1248 1249 assembler().Emit(AbortInstruction{ 1250 AbortInstruction::Kind::kAssertionFailure, 1251 "Torque assert '" + FormatAssertSource(stmt->source) + "' failed"}); 1252 1253 assembler().Bind(true_block); 1254 1255 if (!do_check) { 1256 assembler().Bind(resume_block); 1257 } 1258 1259 return TypeOracle::GetVoidType(); 1260} 1261 1262const Type* ImplementationVisitor::Visit(ExpressionStatement* stmt) { 1263 const Type* type = Visit(stmt->expression).type(); 1264 return type->IsNever() ? type : TypeOracle::GetVoidType(); 1265} 1266 1267const Type* ImplementationVisitor::Visit(ReturnStatement* stmt) { 1268 Callable* current_callable = CurrentCallable::Get(); 1269 if (current_callable->signature().return_type->IsNever()) { 1270 std::stringstream s; 1271 s << "cannot return from a function with return type never"; 1272 ReportError(s.str()); 1273 } 1274 LocalLabel* end = 1275 current_callable->IsMacro() ? LookupLabel(kMacroEndLabelName) : nullptr; 1276 if (current_callable->HasReturnValue()) { 1277 if (!stmt->value) { 1278 std::stringstream s; 1279 s << "return expression needs to be specified for a return type of " 1280 << *current_callable->signature().return_type; 1281 ReportError(s.str()); 1282 } 1283 VisitResult expression_result = Visit(*stmt->value); 1284 VisitResult return_result = GenerateImplicitConvert( 1285 current_callable->signature().return_type, expression_result); 1286 if (current_callable->IsMacro()) { 1287 if (return_result.IsOnStack()) { 1288 StackRange return_value_range = 1289 GenerateLabelGoto(end, return_result.stack_range()); 1290 SetReturnValue(VisitResult(return_result.type(), return_value_range)); 1291 } else { 1292 GenerateLabelGoto(end); 1293 SetReturnValue(return_result); 1294 } 1295 } else if (current_callable->IsBuiltin()) { 1296 assembler().Emit(ReturnInstruction{ 1297 LoweredSlotCount(current_callable->signature().return_type)}); 1298 } else { 1299 UNREACHABLE(); 1300 } 1301 } else { 1302 if (stmt->value) { 1303 std::stringstream s; 1304 s << "return expression can't be specified for a void or never return " 1305 "type"; 1306 ReportError(s.str()); 1307 } 1308 GenerateLabelGoto(end); 1309 } 1310 current_callable->IncrementReturns(); 1311 return TypeOracle::GetNeverType(); 1312} 1313 1314VisitResult ImplementationVisitor::Visit(TryLabelExpression* expr) { 1315 size_t parameter_count = expr->label_block->parameters.names.size(); 1316 std::vector<VisitResult> parameters; 1317 1318 Block* label_block = nullptr; 1319 Block* done_block = assembler().NewBlock(); 1320 VisitResult try_result; 1321 1322 { 1323 CurrentSourcePosition::Scope source_position(expr->label_block->pos); 1324 if (expr->label_block->parameters.has_varargs) { 1325 ReportError("cannot use ... for label parameters"); 1326 } 1327 Stack<const Type*> label_input_stack = assembler().CurrentStack(); 1328 TypeVector parameter_types; 1329 for (size_t i = 0; i < parameter_count; ++i) { 1330 const Type* type = 1331 TypeVisitor::ComputeType(expr->label_block->parameters.types[i]); 1332 parameter_types.push_back(type); 1333 if (type->IsConstexpr()) { 1334 ReportError("no constexpr type allowed for label arguments"); 1335 } 1336 StackRange range = label_input_stack.PushMany(LowerType(type)); 1337 parameters.push_back(VisitResult(type, range)); 1338 } 1339 label_block = assembler().NewBlock(label_input_stack, 1340 IsDeferred(expr->label_block->body)); 1341 1342 Binding<LocalLabel> label_binding{&LabelBindingsManager::Get(), 1343 expr->label_block->label, 1344 LocalLabel{label_block, parameter_types}}; 1345 1346 // Visit try 1347 StackScope stack_scope(this); 1348 try_result = Visit(expr->try_expression); 1349 if (try_result.type() != TypeOracle::GetNeverType()) { 1350 try_result = stack_scope.Yield(try_result); 1351 assembler().Goto(done_block); 1352 } 1353 } 1354 1355 // Visit and output the code for the label block. If the label block falls 1356 // through, then the try must not return a value. Also, if the try doesn't 1357 // fall through, but the label does, then overall the try-label block 1358 // returns type void. 1359 assembler().Bind(label_block); 1360 const Type* label_result; 1361 { 1362 BlockBindings<LocalValue> parameter_bindings(&ValueBindingsManager::Get()); 1363 for (size_t i = 0; i < parameter_count; ++i) { 1364 Identifier* name = expr->label_block->parameters.names[i]; 1365 parameter_bindings.Add(name, 1366 LocalValue{LocationReference::Temporary( 1367 parameters[i], "parameter " + name->value)}); 1368 } 1369 1370 label_result = Visit(expr->label_block->body); 1371 } 1372 if (!try_result.type()->IsVoidOrNever() && label_result->IsVoid()) { 1373 ReportError( 1374 "otherwise clauses cannot fall through in a non-void expression"); 1375 } 1376 if (label_result != TypeOracle::GetNeverType()) { 1377 assembler().Goto(done_block); 1378 } 1379 if (label_result->IsVoid() && try_result.type()->IsNever()) { 1380 try_result = 1381 VisitResult(TypeOracle::GetVoidType(), try_result.stack_range()); 1382 } 1383 1384 if (!try_result.type()->IsNever()) { 1385 assembler().Bind(done_block); 1386 } 1387 return try_result; 1388} 1389 1390VisitResult ImplementationVisitor::Visit(StatementExpression* expr) { 1391 return VisitResult{Visit(expr->statement), assembler().TopRange(0)}; 1392} 1393 1394InitializerResults ImplementationVisitor::VisitInitializerResults( 1395 const ClassType* class_type, 1396 const std::vector<NameAndExpression>& initializers) { 1397 InitializerResults result; 1398 for (const NameAndExpression& initializer : initializers) { 1399 result.names.push_back(initializer.name); 1400 Expression* e = initializer.expression; 1401 const Field& field = class_type->LookupField(initializer.name->value); 1402 bool has_index = field.index.has_value(); 1403 if (SpreadExpression* s = SpreadExpression::DynamicCast(e)) { 1404 if (!has_index) { 1405 ReportError( 1406 "spread expressions can only be used to initialize indexed class " 1407 "fields ('", 1408 initializer.name->value, "' is not)"); 1409 } 1410 e = s->spreadee; 1411 } else if (has_index) { 1412 ReportError("the indexed class field '", initializer.name->value, 1413 "' must be initialized with a spread operator"); 1414 } 1415 result.field_value_map[field.name_and_type.name] = Visit(e); 1416 } 1417 return result; 1418} 1419 1420LocationReference ImplementationVisitor::GenerateFieldReference( 1421 VisitResult object, const Field& field, const ClassType* class_type, 1422 bool treat_optional_as_indexed) { 1423 if (field.index.has_value()) { 1424 LocationReference slice = LocationReference::HeapSlice( 1425 GenerateCall(class_type->GetSliceMacroName(field), {{object}, {}})); 1426 if (field.index->optional && !treat_optional_as_indexed) { 1427 // This field was declared using optional syntax, so any reference to it 1428 // is implicitly a reference to the first item. 1429 return GenerateReferenceToItemInHeapSlice( 1430 slice, {TypeOracle::GetConstInt31Type(), "0"}); 1431 } else { 1432 return slice; 1433 } 1434 } 1435 DCHECK(field.offset.has_value()); 1436 StackRange result_range = assembler().TopRange(0); 1437 result_range.Extend(GenerateCopy(object).stack_range()); 1438 VisitResult offset = 1439 VisitResult(TypeOracle::GetConstInt31Type(), ToString(*field.offset)); 1440 offset = GenerateImplicitConvert(TypeOracle::GetIntPtrType(), offset); 1441 result_range.Extend(offset.stack_range()); 1442 const Type* type = TypeOracle::GetReferenceType(field.name_and_type.type, 1443 field.const_qualified); 1444 return LocationReference::HeapReference(VisitResult(type, result_range)); 1445} 1446 1447// This is used to generate field references during initialization, where we can 1448// re-use the offsets used for computing the allocation size. 1449LocationReference ImplementationVisitor::GenerateFieldReferenceForInit( 1450 VisitResult object, const Field& field, 1451 const LayoutForInitialization& layout) { 1452 StackRange result_range = assembler().TopRange(0); 1453 result_range.Extend(GenerateCopy(object).stack_range()); 1454 VisitResult offset = GenerateImplicitConvert( 1455 TypeOracle::GetIntPtrType(), layout.offsets.at(field.name_and_type.name)); 1456 result_range.Extend(offset.stack_range()); 1457 if (field.index) { 1458 VisitResult length = 1459 GenerateCopy(layout.array_lengths.at(field.name_and_type.name)); 1460 result_range.Extend(length.stack_range()); 1461 const Type* slice_type = 1462 TypeOracle::GetMutableSliceType(field.name_and_type.type); 1463 return LocationReference::HeapSlice(VisitResult(slice_type, result_range)); 1464 } else { 1465 // Const fields are writable during initialization. 1466 VisitResult heap_reference( 1467 TypeOracle::GetMutableReferenceType(field.name_and_type.type), 1468 result_range); 1469 return LocationReference::HeapReference(heap_reference); 1470 } 1471} 1472 1473void ImplementationVisitor::InitializeClass( 1474 const ClassType* class_type, VisitResult allocate_result, 1475 const InitializerResults& initializer_results, 1476 const LayoutForInitialization& layout) { 1477 if (const ClassType* super = class_type->GetSuperClass()) { 1478 InitializeClass(super, allocate_result, initializer_results, layout); 1479 } 1480 1481 for (Field f : class_type->fields()) { 1482 VisitResult initializer_value = 1483 initializer_results.field_value_map.at(f.name_and_type.name); 1484 LocationReference field = 1485 GenerateFieldReferenceForInit(allocate_result, f, layout); 1486 if (f.index) { 1487 DCHECK(field.IsHeapSlice()); 1488 VisitResult slice = field.GetVisitResult(); 1489 GenerateCall(QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING}, 1490 "InitializeFieldsFromIterator"), 1491 {{slice, initializer_value}, {}}); 1492 } else { 1493 GenerateAssignToLocation(field, initializer_value); 1494 } 1495 } 1496} 1497 1498VisitResult ImplementationVisitor::GenerateArrayLength( 1499 Expression* array_length, Namespace* nspace, 1500 const std::map<std::string, LocalValue>& bindings) { 1501 StackScope stack_scope(this); 1502 CurrentSourcePosition::Scope pos_scope(array_length->pos); 1503 // Switch to the namespace where the class was declared. 1504 CurrentScope::Scope current_scope_scope(nspace); 1505 // Reset local bindings and install local binding for the preceding fields. 1506 BindingsManagersScope bindings_managers_scope; 1507 BlockBindings<LocalValue> field_bindings(&ValueBindingsManager::Get()); 1508 for (auto& p : bindings) { 1509 field_bindings.Add(p.first, LocalValue{p.second}, true); 1510 } 1511 VisitResult length = Visit(array_length); 1512 VisitResult converted_length = 1513 GenerateCall("Convert", Arguments{{length}, {}}, 1514 {TypeOracle::GetIntPtrType(), length.type()}, false); 1515 return stack_scope.Yield(converted_length); 1516} 1517 1518VisitResult ImplementationVisitor::GenerateArrayLength(VisitResult object, 1519 const Field& field) { 1520 DCHECK(field.index); 1521 1522 StackScope stack_scope(this); 1523 const ClassType* class_type = *object.type()->ClassSupertype(); 1524 std::map<std::string, LocalValue> bindings; 1525 bool before_current = true; 1526 for (Field f : class_type->ComputeAllFields()) { 1527 if (field.name_and_type.name == f.name_and_type.name) { 1528 before_current = false; 1529 } 1530 // We can't generate field references eagerly here, because some preceding 1531 // fields might be optional, and attempting to get a reference to an 1532 // optional field can crash the program if the field isn't present. 1533 // Instead, we use the lazy form of LocalValue to only generate field 1534 // references if they are used in the length expression. 1535 bindings.insert( 1536 {f.name_and_type.name, 1537 f.const_qualified 1538 ? (before_current 1539 ? LocalValue{[=]() { 1540 return GenerateFieldReference(object, f, class_type); 1541 }} 1542 : LocalValue("Array lengths may only refer to fields " 1543 "defined earlier")) 1544 : LocalValue( 1545 "Non-const fields cannot be used for array lengths.")}); 1546 } 1547 return stack_scope.Yield( 1548 GenerateArrayLength(field.index->expr, class_type->nspace(), bindings)); 1549} 1550 1551VisitResult ImplementationVisitor::GenerateArrayLength( 1552 const ClassType* class_type, const InitializerResults& initializer_results, 1553 const Field& field) { 1554 DCHECK(field.index); 1555 1556 StackScope stack_scope(this); 1557 std::map<std::string, LocalValue> bindings; 1558 for (Field f : class_type->ComputeAllFields()) { 1559 if (f.index) break; 1560 const std::string& fieldname = f.name_and_type.name; 1561 VisitResult value = initializer_results.field_value_map.at(fieldname); 1562 bindings.insert( 1563 {fieldname, 1564 f.const_qualified 1565 ? LocalValue{LocationReference::Temporary( 1566 value, "initial field " + fieldname)} 1567 : LocalValue( 1568 "Non-const fields cannot be used for array lengths.")}); 1569 } 1570 return stack_scope.Yield( 1571 GenerateArrayLength(field.index->expr, class_type->nspace(), bindings)); 1572} 1573 1574LayoutForInitialization ImplementationVisitor::GenerateLayoutForInitialization( 1575 const ClassType* class_type, 1576 const InitializerResults& initializer_results) { 1577 LayoutForInitialization layout; 1578 VisitResult offset; 1579 for (Field f : class_type->ComputeAllFields()) { 1580 if (f.offset.has_value()) { 1581 offset = 1582 VisitResult(TypeOracle::GetConstInt31Type(), ToString(*f.offset)); 1583 } 1584 layout.offsets[f.name_and_type.name] = offset; 1585 if (f.index) { 1586 size_t element_size; 1587 std::string element_size_string; 1588 std::tie(element_size, element_size_string) = 1589 *SizeOf(f.name_and_type.type); 1590 VisitResult array_element_size = 1591 VisitResult(TypeOracle::GetConstInt31Type(), element_size_string); 1592 VisitResult array_length = 1593 GenerateArrayLength(class_type, initializer_results, f); 1594 layout.array_lengths[f.name_and_type.name] = array_length; 1595 Arguments arguments; 1596 arguments.parameters = {offset, array_length, array_element_size}; 1597 offset = GenerateCall(QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING}, 1598 "AddIndexedFieldSizeToObjectSize"), 1599 arguments); 1600 } else { 1601 DCHECK(f.offset.has_value()); 1602 } 1603 } 1604 if (class_type->size().SingleValue()) { 1605 layout.size = VisitResult(TypeOracle::GetConstInt31Type(), 1606 ToString(*class_type->size().SingleValue())); 1607 } else { 1608 layout.size = offset; 1609 } 1610 if ((size_t{1} << class_type->size().AlignmentLog2()) < 1611 TargetArchitecture::TaggedSize()) { 1612 Arguments arguments; 1613 arguments.parameters = {layout.size}; 1614 layout.size = GenerateCall( 1615 QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING}, "AlignTagged"), 1616 arguments); 1617 } 1618 return layout; 1619} 1620 1621VisitResult ImplementationVisitor::Visit(NewExpression* expr) { 1622 StackScope stack_scope(this); 1623 const Type* type = TypeVisitor::ComputeType(expr->type); 1624 const ClassType* class_type = ClassType::DynamicCast(type); 1625 if (class_type == nullptr) { 1626 ReportError("type for new expression must be a class, \"", *type, 1627 "\" is not"); 1628 } 1629 1630 if (!class_type->AllowInstantiation()) { 1631 // Classes that are only used for testing should never be instantiated. 1632 ReportError(*class_type, 1633 " cannot be allocated with new (it's used for testing)"); 1634 } 1635 1636 InitializerResults initializer_results = 1637 VisitInitializerResults(class_type, expr->initializers); 1638 1639 const Field& map_field = class_type->LookupField("map"); 1640 if (*map_field.offset != 0) { 1641 ReportError("class initializers must have a map as first parameter"); 1642 } 1643 const std::map<std::string, VisitResult>& initializer_fields = 1644 initializer_results.field_value_map; 1645 auto it_object_map = initializer_fields.find(map_field.name_and_type.name); 1646 VisitResult object_map; 1647 if (class_type->IsExtern()) { 1648 if (it_object_map == initializer_fields.end()) { 1649 ReportError("Constructor for ", class_type->name(), 1650 " needs Map argument!"); 1651 } 1652 object_map = it_object_map->second; 1653 } else { 1654 if (it_object_map != initializer_fields.end()) { 1655 ReportError( 1656 "Constructor for ", class_type->name(), 1657 " must not specify Map argument; it is automatically inserted."); 1658 } 1659 Arguments get_struct_map_arguments; 1660 get_struct_map_arguments.parameters.push_back( 1661 VisitResult(TypeOracle::GetConstexprInstanceTypeType(), 1662 CapifyStringWithUnderscores(class_type->name()) + "_TYPE")); 1663 object_map = GenerateCall( 1664 QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING}, "GetInstanceTypeMap"), 1665 get_struct_map_arguments, {}, false); 1666 CurrentSourcePosition::Scope current_pos(expr->pos); 1667 initializer_results.names.insert(initializer_results.names.begin(), 1668 MakeNode<Identifier>("map")); 1669 initializer_results.field_value_map[map_field.name_and_type.name] = 1670 object_map; 1671 } 1672 1673 CheckInitializersWellformed(class_type->name(), 1674 class_type->ComputeAllFields(), 1675 expr->initializers, !class_type->IsExtern()); 1676 1677 LayoutForInitialization layout = 1678 GenerateLayoutForInitialization(class_type, initializer_results); 1679 1680 Arguments allocate_arguments; 1681 allocate_arguments.parameters.push_back(layout.size); 1682 allocate_arguments.parameters.push_back(object_map); 1683 allocate_arguments.parameters.push_back( 1684 GenerateBoolConstant(expr->pretenured)); 1685 VisitResult allocate_result = GenerateCall( 1686 QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING}, "AllocateFromNew"), 1687 allocate_arguments, {class_type}, false); 1688 DCHECK(allocate_result.IsOnStack()); 1689 1690 InitializeClass(class_type, allocate_result, initializer_results, layout); 1691 1692 return stack_scope.Yield(GenerateCall( 1693 "%RawDownCast", Arguments{{allocate_result}, {}}, {class_type})); 1694} 1695 1696const Type* ImplementationVisitor::Visit(BreakStatement* stmt) { 1697 base::Optional<Binding<LocalLabel>*> break_label = 1698 TryLookupLabel(kBreakLabelName); 1699 if (!break_label) { 1700 ReportError("break used outside of loop"); 1701 } 1702 assembler().Goto((*break_label)->block); 1703 return TypeOracle::GetNeverType(); 1704} 1705 1706const Type* ImplementationVisitor::Visit(ContinueStatement* stmt) { 1707 base::Optional<Binding<LocalLabel>*> continue_label = 1708 TryLookupLabel(kContinueLabelName); 1709 if (!continue_label) { 1710 ReportError("continue used outside of loop"); 1711 } 1712 assembler().Goto((*continue_label)->block); 1713 return TypeOracle::GetNeverType(); 1714} 1715 1716const Type* ImplementationVisitor::Visit(ForLoopStatement* stmt) { 1717 BlockBindings<LocalValue> loop_bindings(&ValueBindingsManager::Get()); 1718 1719 if (stmt->var_declaration) Visit(*stmt->var_declaration, &loop_bindings); 1720 1721 Block* body_block = assembler().NewBlock(assembler().CurrentStack()); 1722 Block* exit_block = assembler().NewBlock(assembler().CurrentStack()); 1723 1724 Block* header_block = assembler().NewBlock(); 1725 assembler().Goto(header_block); 1726 assembler().Bind(header_block); 1727 1728 // The continue label is where "continue" statements jump to. If no action 1729 // expression is provided, we jump directly to the header. 1730 Block* continue_block = header_block; 1731 1732 // The action label is only needed when an action expression was provided. 1733 Block* action_block = nullptr; 1734 if (stmt->action) { 1735 action_block = assembler().NewBlock(); 1736 1737 // The action expression needs to be executed on a continue. 1738 continue_block = action_block; 1739 } 1740 1741 if (stmt->test) { 1742 GenerateExpressionBranch(*stmt->test, body_block, exit_block); 1743 } else { 1744 assembler().Goto(body_block); 1745 } 1746 1747 assembler().Bind(body_block); 1748 { 1749 BreakContinueActivator activator(exit_block, continue_block); 1750 const Type* body_result = Visit(stmt->body); 1751 if (body_result != TypeOracle::GetNeverType()) { 1752 assembler().Goto(continue_block); 1753 } 1754 } 1755 1756 if (stmt->action) { 1757 assembler().Bind(action_block); 1758 const Type* action_result = Visit(*stmt->action); 1759 if (action_result != TypeOracle::GetNeverType()) { 1760 assembler().Goto(header_block); 1761 } 1762 } 1763 1764 assembler().Bind(exit_block); 1765 return TypeOracle::GetVoidType(); 1766} 1767 1768VisitResult ImplementationVisitor::Visit(SpreadExpression* expr) { 1769 ReportError( 1770 "spread operators are only currently supported in indexed class field " 1771 "initialization expressions"); 1772} 1773 1774void ImplementationVisitor::GenerateImplementation(const std::string& dir) { 1775 for (SourceId file : SourceFileMap::AllSources()) { 1776 std::string base_filename = 1777 dir + "/" + SourceFileMap::PathFromV8RootWithoutExtension(file); 1778 GlobalContext::PerFileStreams& streams = 1779 GlobalContext::GeneratedPerFile(file); 1780 1781 std::string csa_cc = streams.csa_ccfile.str(); 1782 // Insert missing builtin includes where the marker is. 1783 { 1784 auto pos = csa_cc.find(BuiltinIncludesMarker); 1785 CHECK_NE(pos, std::string::npos); 1786 std::string includes; 1787 for (const SourceId& include : streams.required_builtin_includes) { 1788 std::string include_file = 1789 SourceFileMap::PathFromV8RootWithoutExtension(include); 1790 includes += "#include \"torque-generated/"; 1791 includes += include_file; 1792 includes += "-tq-csa.h\"\n"; 1793 } 1794 csa_cc.replace(pos, strlen(BuiltinIncludesMarker), std::move(includes)); 1795 } 1796 1797 // TODO(torque-builder): Pass file directly. 1798 WriteFile(base_filename + "-tq-csa.cc", std::move(csa_cc)); 1799 WriteFile(base_filename + "-tq-csa.h", streams.csa_headerfile.str()); 1800 WriteFile(base_filename + "-tq.inc", 1801 streams.class_definition_headerfile.str()); 1802 WriteFile( 1803 base_filename + "-tq-inl.inc", 1804 streams.class_definition_inline_headerfile_macro_declarations.str() + 1805 streams.class_definition_inline_headerfile_macro_definitions.str() + 1806 streams.class_definition_inline_headerfile.str()); 1807 WriteFile(base_filename + "-tq.cc", streams.class_definition_ccfile.str()); 1808 } 1809 1810 WriteFile(dir + "/debug-macros.h", debug_macros_h_.str()); 1811 WriteFile(dir + "/debug-macros.cc", debug_macros_cc_.str()); 1812} 1813 1814cpp::Function ImplementationVisitor::GenerateMacroFunctionDeclaration( 1815 Macro* macro) { 1816 return GenerateFunction(nullptr, 1817 output_type_ == OutputType::kCC 1818 ? macro->CCName() 1819 : output_type_ == OutputType::kCCDebug 1820 ? macro->CCDebugName() 1821 : macro->ExternalName(), 1822 macro->signature(), macro->parameter_names()); 1823} 1824 1825cpp::Function ImplementationVisitor::GenerateFunction( 1826 cpp::Class* owner, const std::string& name, const Signature& signature, 1827 const NameVector& parameter_names, bool pass_code_assembler_state, 1828 std::vector<std::string>* generated_parameter_names) { 1829 cpp::Function f(owner, name); 1830 f.SetInline(output_type_ == OutputType::kCC); 1831 1832 // Set return type. 1833 // TODO(torque-builder): Consider an overload of SetReturnType that handles 1834 // this. 1835 if (signature.return_type->IsVoidOrNever()) { 1836 f.SetReturnType("void"); 1837 } else if (output_type_ == OutputType::kCCDebug) { 1838 f.SetReturnType(std::string("Value<") + 1839 signature.return_type->GetDebugType() + ">"); 1840 } else if (output_type_ == OutputType::kCC) { 1841 f.SetReturnType(signature.return_type->GetRuntimeType()); 1842 } else { 1843 DCHECK_EQ(output_type_, OutputType::kCSA); 1844 f.SetReturnType(signature.return_type->GetGeneratedTypeName()); 1845 } 1846 1847 bool ignore_first_parameter = true; 1848 if (output_type_ == OutputType::kCCDebug) { 1849 f.AddParameter("d::MemoryAccessor", "accessor"); 1850 } else if (output_type_ == OutputType::kCSA && pass_code_assembler_state) { 1851 f.AddParameter("compiler::CodeAssemblerState*", "state_"); 1852 } else { 1853 ignore_first_parameter = false; 1854 } 1855 1856 // TODO(torque-builder): Consider an overload for AddParameter that handles 1857 // this. 1858 DCHECK_GE(signature.types().size(), parameter_names.size()); 1859 for (std::size_t i = 0; i < signature.types().size(); ++i) { 1860 const Type* parameter_type = signature.types()[i]; 1861 std::string type; 1862 if (output_type_ == OutputType::kCC) { 1863 type = parameter_type->GetRuntimeType(); 1864 } else if (output_type_ == OutputType::kCCDebug) { 1865 type = parameter_type->GetDebugType(); 1866 } else { 1867 DCHECK_EQ(output_type_, OutputType::kCSA); 1868 type = parameter_type->GetGeneratedTypeName(); 1869 } 1870 f.AddParameter(std::move(type), 1871 ExternalParameterName(i < parameter_names.size() 1872 ? parameter_names[i]->value 1873 : std::to_string(i))); 1874 } 1875 1876 for (const LabelDeclaration& label_info : signature.labels) { 1877 if (output_type_ == OutputType::kCC || 1878 output_type_ == OutputType::kCCDebug) { 1879 ReportError("Macros that generate runtime code can't have label exits"); 1880 } 1881 f.AddParameter("compiler::CodeAssemblerLabel*", 1882 ExternalLabelName(label_info.name->value)); 1883 size_t i = 0; 1884 for (const Type* type : label_info.types) { 1885 std::string generated_type_name; 1886 if (type->StructSupertype()) { 1887 generated_type_name = "\n#error no structs allowed in labels\n"; 1888 } else { 1889 generated_type_name = "compiler::TypedCodeAssemblerVariable<"; 1890 generated_type_name += type->GetGeneratedTNodeTypeName(); 1891 generated_type_name += ">*"; 1892 } 1893 f.AddParameter(generated_type_name, 1894 ExternalLabelParameterName(label_info.name->value, i)); 1895 ++i; 1896 } 1897 } 1898 1899 if (generated_parameter_names) { 1900 *generated_parameter_names = f.GetParameterNames(); 1901 if (ignore_first_parameter) { 1902 DCHECK(!generated_parameter_names->empty()); 1903 generated_parameter_names->erase(generated_parameter_names->begin()); 1904 } 1905 } 1906 return f; 1907} 1908 1909namespace { 1910 1911void FailCallableLookup( 1912 const std::string& reason, const QualifiedName& name, 1913 const TypeVector& parameter_types, 1914 const std::vector<Binding<LocalLabel>*>& labels, 1915 const std::vector<Signature>& candidates, 1916 const std::vector<std::pair<GenericCallable*, std::string>> 1917 inapplicable_generics) { 1918 std::stringstream stream; 1919 stream << "\n" << reason << ": \n " << name << "(" << parameter_types << ")"; 1920 if (labels.size() != 0) { 1921 stream << " labels "; 1922 for (size_t i = 0; i < labels.size(); ++i) { 1923 stream << labels[i]->name() << "(" << labels[i]->parameter_types << ")"; 1924 } 1925 } 1926 stream << "\ncandidates are:"; 1927 for (const Signature& signature : candidates) { 1928 stream << "\n " << name; 1929 PrintSignature(stream, signature, false); 1930 } 1931 if (inapplicable_generics.size() != 0) { 1932 stream << "\nfailed to instantiate all of these generic declarations:"; 1933 for (auto& failure : inapplicable_generics) { 1934 GenericCallable* generic = failure.first; 1935 const std::string& fail_reason = failure.second; 1936 stream << "\n " << generic->name() << " defined at " 1937 << PositionAsString(generic->Position()) << ":\n " 1938 << fail_reason << "\n"; 1939 } 1940 } 1941 ReportError(stream.str()); 1942} 1943 1944Callable* GetOrCreateSpecialization( 1945 const SpecializationKey<GenericCallable>& key) { 1946 if (base::Optional<Callable*> specialization = 1947 key.generic->GetSpecialization(key.specialized_types)) { 1948 return *specialization; 1949 } 1950 return DeclarationVisitor::SpecializeImplicit(key); 1951} 1952 1953} // namespace 1954 1955base::Optional<Binding<LocalValue>*> ImplementationVisitor::TryLookupLocalValue( 1956 const std::string& name) { 1957 return ValueBindingsManager::Get().TryLookup(name); 1958} 1959 1960base::Optional<Binding<LocalLabel>*> ImplementationVisitor::TryLookupLabel( 1961 const std::string& name) { 1962 return LabelBindingsManager::Get().TryLookup(name); 1963} 1964 1965Binding<LocalLabel>* ImplementationVisitor::LookupLabel( 1966 const std::string& name) { 1967 base::Optional<Binding<LocalLabel>*> label = TryLookupLabel(name); 1968 if (!label) ReportError("cannot find label ", name); 1969 return *label; 1970} 1971 1972Block* ImplementationVisitor::LookupSimpleLabel(const std::string& name) { 1973 LocalLabel* label = LookupLabel(name); 1974 if (!label->parameter_types.empty()) { 1975 ReportError("label ", name, 1976 "was expected to have no parameters, but has parameters (", 1977 label->parameter_types, ")"); 1978 } 1979 return label->block; 1980} 1981 1982// Try to lookup a callable with the provided argument types. Do not report 1983// an error if no matching callable was found, but return false instead. 1984// This is used to test the presence of overloaded field accessors. 1985bool ImplementationVisitor::TestLookupCallable( 1986 const QualifiedName& name, const TypeVector& parameter_types) { 1987 return LookupCallable(name, Declarations::TryLookup(name), parameter_types, 1988 {}, {}, true) != nullptr; 1989} 1990 1991TypeArgumentInference ImplementationVisitor::InferSpecializationTypes( 1992 GenericCallable* generic, const TypeVector& explicit_specialization_types, 1993 const TypeVector& explicit_arguments) { 1994 std::vector<base::Optional<const Type*>> all_arguments; 1995 const ParameterList& parameters = generic->declaration()->parameters; 1996 for (size_t i = 0; i < parameters.implicit_count; ++i) { 1997 base::Optional<Binding<LocalValue>*> val = 1998 TryLookupLocalValue(parameters.names[i]->value); 1999 all_arguments.push_back( 2000 val ? (*val)->GetLocationReference(*val).ReferencedType() 2001 : base::nullopt); 2002 } 2003 for (const Type* explicit_argument : explicit_arguments) { 2004 all_arguments.push_back(explicit_argument); 2005 } 2006 return generic->InferSpecializationTypes(explicit_specialization_types, 2007 all_arguments); 2008} 2009 2010template <class Container> 2011Callable* ImplementationVisitor::LookupCallable( 2012 const QualifiedName& name, const Container& declaration_container, 2013 const TypeVector& parameter_types, 2014 const std::vector<Binding<LocalLabel>*>& labels, 2015 const TypeVector& specialization_types, bool silence_errors) { 2016 Callable* result = nullptr; 2017 2018 std::vector<Declarable*> overloads; 2019 std::vector<Signature> overload_signatures; 2020 std::vector<std::pair<GenericCallable*, std::string>> inapplicable_generics; 2021 for (auto* declarable : declaration_container) { 2022 if (GenericCallable* generic = GenericCallable::DynamicCast(declarable)) { 2023 TypeArgumentInference inference = InferSpecializationTypes( 2024 generic, specialization_types, parameter_types); 2025 if (inference.HasFailed()) { 2026 inapplicable_generics.push_back( 2027 std::make_pair(generic, inference.GetFailureReason())); 2028 continue; 2029 } 2030 overloads.push_back(generic); 2031 overload_signatures.push_back( 2032 DeclarationVisitor::MakeSpecializedSignature( 2033 SpecializationKey<GenericCallable>{generic, 2034 inference.GetResult()})); 2035 } else if (Callable* callable = Callable::DynamicCast(declarable)) { 2036 overloads.push_back(callable); 2037 overload_signatures.push_back(callable->signature()); 2038 } 2039 } 2040 // Indices of candidates in overloads/overload_signatures. 2041 std::vector<size_t> candidates; 2042 for (size_t i = 0; i < overloads.size(); ++i) { 2043 const Signature& signature = overload_signatures[i]; 2044 if (IsCompatibleSignature(signature, parameter_types, labels.size())) { 2045 candidates.push_back(i); 2046 } 2047 } 2048 2049 if (overloads.empty() && inapplicable_generics.empty()) { 2050 if (silence_errors) return nullptr; 2051 std::stringstream stream; 2052 stream << "no matching declaration found for " << name; 2053 ReportError(stream.str()); 2054 } else if (candidates.empty()) { 2055 if (silence_errors) return nullptr; 2056 FailCallableLookup("cannot find suitable callable with name", name, 2057 parameter_types, labels, overload_signatures, 2058 inapplicable_generics); 2059 } 2060 2061 auto is_better_candidate = [&](size_t a, size_t b) { 2062 return ParameterDifference(overload_signatures[a].GetExplicitTypes(), 2063 parameter_types) 2064 .StrictlyBetterThan(ParameterDifference( 2065 overload_signatures[b].GetExplicitTypes(), parameter_types)); 2066 }; 2067 2068 size_t best = *std::min_element(candidates.begin(), candidates.end(), 2069 is_better_candidate); 2070 // This check is contained in libstdc++'s std::min_element. 2071 DCHECK(!is_better_candidate(best, best)); 2072 for (size_t candidate : candidates) { 2073 if (candidate != best && !is_better_candidate(best, candidate)) { 2074 std::vector<Signature> candidate_signatures; 2075 candidate_signatures.reserve(candidates.size()); 2076 for (size_t i : candidates) { 2077 candidate_signatures.push_back(overload_signatures[i]); 2078 } 2079 FailCallableLookup("ambiguous callable ", name, parameter_types, labels, 2080 candidate_signatures, inapplicable_generics); 2081 } 2082 } 2083 2084 if (GenericCallable* generic = 2085 GenericCallable::DynamicCast(overloads[best])) { 2086 TypeArgumentInference inference = InferSpecializationTypes( 2087 generic, specialization_types, parameter_types); 2088 result = GetOrCreateSpecialization( 2089 SpecializationKey<GenericCallable>{generic, inference.GetResult()}); 2090 } else { 2091 result = Callable::cast(overloads[best]); 2092 } 2093 2094 size_t caller_size = parameter_types.size(); 2095 size_t callee_size = 2096 result->signature().types().size() - result->signature().implicit_count; 2097 if (caller_size != callee_size && 2098 !result->signature().parameter_types.var_args) { 2099 std::stringstream stream; 2100 stream << "parameter count mismatch calling " << *result << " - expected " 2101 << std::to_string(callee_size) << ", found " 2102 << std::to_string(caller_size); 2103 ReportError(stream.str()); 2104 } 2105 2106 return result; 2107} 2108 2109template <class Container> 2110Callable* ImplementationVisitor::LookupCallable( 2111 const QualifiedName& name, const Container& declaration_container, 2112 const Arguments& arguments, const TypeVector& specialization_types) { 2113 return LookupCallable(name, declaration_container, 2114 arguments.parameters.ComputeTypeVector(), 2115 arguments.labels, specialization_types); 2116} 2117 2118Method* ImplementationVisitor::LookupMethod( 2119 const std::string& name, const AggregateType* receiver_type, 2120 const Arguments& arguments, const TypeVector& specialization_types) { 2121 TypeVector types(arguments.parameters.ComputeTypeVector()); 2122 types.insert(types.begin(), receiver_type); 2123 return Method::cast(LookupCallable({{}, name}, receiver_type->Methods(name), 2124 types, arguments.labels, 2125 specialization_types)); 2126} 2127 2128const Type* ImplementationVisitor::GetCommonType(const Type* left, 2129 const Type* right) { 2130 const Type* common_type; 2131 if (IsAssignableFrom(left, right)) { 2132 common_type = left; 2133 } else if (IsAssignableFrom(right, left)) { 2134 common_type = right; 2135 } else { 2136 common_type = TypeOracle::GetUnionType(left, right); 2137 } 2138 common_type = common_type->NonConstexprVersion(); 2139 return common_type; 2140} 2141 2142VisitResult ImplementationVisitor::GenerateCopy(const VisitResult& to_copy) { 2143 if (to_copy.IsOnStack()) { 2144 return VisitResult(to_copy.type(), 2145 assembler().Peek(to_copy.stack_range(), to_copy.type())); 2146 } 2147 return to_copy; 2148} 2149 2150VisitResult ImplementationVisitor::Visit(StructExpression* expr) { 2151 StackScope stack_scope(this); 2152 2153 auto& initializers = expr->initializers; 2154 std::vector<VisitResult> values; 2155 std::vector<const Type*> term_argument_types; 2156 values.reserve(initializers.size()); 2157 term_argument_types.reserve(initializers.size()); 2158 2159 // Compute values and types of all initializer arguments 2160 for (const NameAndExpression& initializer : initializers) { 2161 VisitResult value = Visit(initializer.expression); 2162 values.push_back(value); 2163 term_argument_types.push_back(value.type()); 2164 } 2165 2166 // Compute and check struct type from given struct name and argument types 2167 const Type* type = TypeVisitor::ComputeTypeForStructExpression( 2168 expr->type, term_argument_types); 2169 if (const auto* struct_type = StructType::DynamicCast(type)) { 2170 CheckInitializersWellformed(struct_type->name(), struct_type->fields(), 2171 initializers); 2172 2173 // Implicitly convert values and thereby build the struct on the stack 2174 StackRange struct_range = assembler().TopRange(0); 2175 auto& fields = struct_type->fields(); 2176 for (size_t i = 0; i < values.size(); i++) { 2177 values[i] = 2178 GenerateImplicitConvert(fields[i].name_and_type.type, values[i]); 2179 struct_range.Extend(values[i].stack_range()); 2180 } 2181 2182 return stack_scope.Yield(VisitResult(struct_type, struct_range)); 2183 } else { 2184 const auto* bitfield_struct_type = BitFieldStructType::cast(type); 2185 CheckInitializersWellformed(bitfield_struct_type->name(), 2186 bitfield_struct_type->fields(), initializers); 2187 2188 // Create a zero and cast it to the desired bitfield struct type. 2189 VisitResult result{TypeOracle::GetConstInt32Type(), "0"}; 2190 result = GenerateImplicitConvert(TypeOracle::GetInt32Type(), result); 2191 result = GenerateCall("Unsigned", Arguments{{result}, {}}, {}); 2192 result = GenerateCall("%RawDownCast", Arguments{{result}, {}}, 2193 {bitfield_struct_type}); 2194 2195 // Set each field in the result. If these fields are constexpr, then all of 2196 // this initialization will end up reduced to a single value during TurboFan 2197 // optimization. 2198 auto& fields = bitfield_struct_type->fields(); 2199 for (size_t i = 0; i < values.size(); i++) { 2200 values[i] = 2201 GenerateImplicitConvert(fields[i].name_and_type.type, values[i]); 2202 result = GenerateSetBitField(bitfield_struct_type, fields[i], result, 2203 values[i], /*starts_as_zero=*/true); 2204 } 2205 2206 return stack_scope.Yield(result); 2207 } 2208} 2209 2210VisitResult ImplementationVisitor::GenerateSetBitField( 2211 const Type* bitfield_struct_type, const BitField& bitfield, 2212 VisitResult bitfield_struct, VisitResult value, bool starts_as_zero) { 2213 GenerateCopy(bitfield_struct); 2214 GenerateCopy(value); 2215 assembler().Emit( 2216 StoreBitFieldInstruction{bitfield_struct_type, bitfield, starts_as_zero}); 2217 return VisitResult(bitfield_struct_type, assembler().TopRange(1)); 2218} 2219 2220LocationReference ImplementationVisitor::GetLocationReference( 2221 Expression* location) { 2222 switch (location->kind) { 2223 case AstNode::Kind::kIdentifierExpression: 2224 return GetLocationReference(static_cast<IdentifierExpression*>(location)); 2225 case AstNode::Kind::kFieldAccessExpression: 2226 return GetLocationReference( 2227 static_cast<FieldAccessExpression*>(location)); 2228 case AstNode::Kind::kElementAccessExpression: 2229 return GetLocationReference( 2230 static_cast<ElementAccessExpression*>(location)); 2231 case AstNode::Kind::kDereferenceExpression: 2232 return GetLocationReference( 2233 static_cast<DereferenceExpression*>(location)); 2234 default: 2235 return LocationReference::Temporary(Visit(location), "expression"); 2236 } 2237} 2238 2239LocationReference ImplementationVisitor::GetLocationReference( 2240 FieldAccessExpression* expr) { 2241 return GenerateFieldAccess(GetLocationReference(expr->object), 2242 expr->field->value, false, expr->field->pos); 2243} 2244 2245LocationReference ImplementationVisitor::GenerateFieldAccess( 2246 LocationReference reference, const std::string& fieldname, 2247 bool ignore_stuct_field_constness, base::Optional<SourcePosition> pos) { 2248 if (reference.IsVariableAccess() && 2249 reference.variable().type()->StructSupertype()) { 2250 const StructType* type = *reference.variable().type()->StructSupertype(); 2251 const Field& field = type->LookupField(fieldname); 2252 if (GlobalContext::collect_language_server_data() && pos.has_value()) { 2253 LanguageServerData::AddDefinition(*pos, field.pos); 2254 } 2255 if (GlobalContext::collect_kythe_data() && pos.has_value()) { 2256 KytheData::AddClassFieldUse(*pos, &field); 2257 } 2258 if (field.const_qualified) { 2259 VisitResult t_value = ProjectStructField(reference.variable(), fieldname); 2260 return LocationReference::Temporary( 2261 t_value, "for constant field '" + field.name_and_type.name + "'"); 2262 } else { 2263 return LocationReference::VariableAccess( 2264 ProjectStructField(reference.variable(), fieldname)); 2265 } 2266 } 2267 if (reference.IsTemporary() && 2268 reference.temporary().type()->StructSupertype()) { 2269 if (GlobalContext::collect_language_server_data() && pos.has_value()) { 2270 const StructType* type = *reference.temporary().type()->StructSupertype(); 2271 const Field& field = type->LookupField(fieldname); 2272 LanguageServerData::AddDefinition(*pos, field.pos); 2273 } 2274 return LocationReference::Temporary( 2275 ProjectStructField(reference.temporary(), fieldname), 2276 reference.temporary_description()); 2277 } 2278 if (base::Optional<const Type*> referenced_type = 2279 reference.ReferencedType()) { 2280 if ((*referenced_type)->IsBitFieldStructType()) { 2281 const BitFieldStructType* bitfield_struct = 2282 BitFieldStructType::cast(*referenced_type); 2283 const BitField& field = bitfield_struct->LookupField(fieldname); 2284 return LocationReference::BitFieldAccess(reference, field); 2285 } 2286 if (const auto type_wrapped_in_smi = Type::MatchUnaryGeneric( 2287 (*referenced_type), TypeOracle::GetSmiTaggedGeneric())) { 2288 const BitFieldStructType* bitfield_struct = 2289 BitFieldStructType::DynamicCast(*type_wrapped_in_smi); 2290 if (bitfield_struct == nullptr) { 2291 ReportError( 2292 "When a value of type SmiTagged<T> is used in a field access " 2293 "expression, T is expected to be a bitfield struct type. Instead, " 2294 "T " 2295 "is ", 2296 **type_wrapped_in_smi); 2297 } 2298 const BitField& field = bitfield_struct->LookupField(fieldname); 2299 return LocationReference::BitFieldAccess(reference, field); 2300 } 2301 } 2302 if (reference.IsHeapReference()) { 2303 VisitResult ref = reference.heap_reference(); 2304 bool is_const; 2305 auto generic_type = 2306 TypeOracle::MatchReferenceGeneric(ref.type(), &is_const); 2307 if (!generic_type) { 2308 ReportError( 2309 "Left-hand side of field access expression is marked as a reference " 2310 "but is not of type Reference<...>. Found type: ", 2311 ref.type()->ToString()); 2312 } 2313 if (auto struct_type = (*generic_type)->StructSupertype()) { 2314 const Field& field = (*struct_type)->LookupField(fieldname); 2315 // Update the Reference's type to refer to the field type within the 2316 // struct. 2317 ref.SetType(TypeOracle::GetReferenceType( 2318 field.name_and_type.type, 2319 is_const || 2320 (field.const_qualified && !ignore_stuct_field_constness))); 2321 if (!field.offset.has_value()) { 2322 Error("accessing field with unknown offset").Throw(); 2323 } 2324 if (*field.offset != 0) { 2325 // Copy the Reference struct up the stack and update the new copy's 2326 // |offset| value to point to the struct field. 2327 StackScope scope(this); 2328 ref = GenerateCopy(ref); 2329 VisitResult ref_offset = ProjectStructField(ref, "offset"); 2330 VisitResult struct_offset{ 2331 TypeOracle::GetIntPtrType()->ConstexprVersion(), 2332 std::to_string(*field.offset)}; 2333 VisitResult updated_offset = 2334 GenerateCall("+", Arguments{{ref_offset, struct_offset}, {}}); 2335 assembler().Poke(ref_offset.stack_range(), updated_offset.stack_range(), 2336 ref_offset.type()); 2337 ref = scope.Yield(ref); 2338 } 2339 return LocationReference::HeapReference(ref); 2340 } 2341 } 2342 VisitResult object_result = GenerateFetchFromLocation(reference); 2343 if (base::Optional<const ClassType*> class_type = 2344 object_result.type()->ClassSupertype()) { 2345 // This is a hack to distinguish the situation where we want to use 2346 // overloaded field accessors from when we want to create a reference. 2347 bool has_explicit_overloads = TestLookupCallable( 2348 QualifiedName{"." + fieldname}, {object_result.type()}); 2349 if ((*class_type)->HasField(fieldname) && !has_explicit_overloads) { 2350 const Field& field = (*class_type)->LookupField(fieldname); 2351 if (GlobalContext::collect_language_server_data() && pos.has_value()) { 2352 LanguageServerData::AddDefinition(*pos, field.pos); 2353 } 2354 if (GlobalContext::collect_kythe_data()) { 2355 KytheData::AddClassFieldUse(*pos, &field); 2356 } 2357 return GenerateFieldReference(object_result, field, *class_type); 2358 } 2359 } 2360 return LocationReference::FieldAccess(object_result, fieldname); 2361} 2362 2363LocationReference ImplementationVisitor::GetLocationReference( 2364 ElementAccessExpression* expr) { 2365 LocationReference reference = GetLocationReference(expr->array); 2366 VisitResult index = Visit(expr->index); 2367 if (reference.IsHeapSlice()) { 2368 return GenerateReferenceToItemInHeapSlice(reference, index); 2369 } else { 2370 return LocationReference::ArrayAccess(GenerateFetchFromLocation(reference), 2371 index); 2372 } 2373} 2374 2375LocationReference ImplementationVisitor::GenerateReferenceToItemInHeapSlice( 2376 LocationReference slice, VisitResult index) { 2377 DCHECK(slice.IsHeapSlice()); 2378 Arguments arguments{{index}, {}}; 2379 const StructType* slice_type = *slice.heap_slice().type()->StructSupertype(); 2380 Method* method = LookupMethod("AtIndex", slice_type, arguments, {}); 2381 // The reference has to be treated like a normal value when calling methods 2382 // on the underlying slice implementation. 2383 LocationReference slice_value = 2384 LocationReference::Temporary(slice.GetVisitResult(), "slice as value"); 2385 return LocationReference::HeapReference( 2386 GenerateCall(method, std::move(slice_value), arguments, {}, false)); 2387} 2388 2389LocationReference ImplementationVisitor::GetLocationReference( 2390 IdentifierExpression* expr) { 2391 if (expr->namespace_qualification.empty()) { 2392 if (base::Optional<Binding<LocalValue>*> value = 2393 TryLookupLocalValue(expr->name->value)) { 2394 if (GlobalContext::collect_language_server_data()) { 2395 LanguageServerData::AddDefinition(expr->name->pos, 2396 (*value)->declaration_position()); 2397 } 2398 if (GlobalContext::collect_kythe_data()) { 2399 if (!expr->IsThis()) { 2400 DCHECK_EQ(expr->name->pos.end.column - expr->name->pos.start.column, 2401 expr->name->value.length()); 2402 KytheData::AddBindingUse(expr->name->pos, *value); 2403 } 2404 } 2405 if (expr->generic_arguments.size() != 0) { 2406 ReportError("cannot have generic parameters on local name ", 2407 expr->name); 2408 } 2409 return (*value)->GetLocationReference(*value); 2410 } 2411 } 2412 2413 if (expr->IsThis()) { 2414 ReportError("\"this\" cannot be qualified"); 2415 } 2416 QualifiedName name = 2417 QualifiedName(expr->namespace_qualification, expr->name->value); 2418 if (base::Optional<Builtin*> builtin = Declarations::TryLookupBuiltin(name)) { 2419 if (GlobalContext::collect_language_server_data()) { 2420 LanguageServerData::AddDefinition(expr->name->pos, 2421 (*builtin)->Position()); 2422 } 2423 // TODO(v8:12261): Consider collecting KytheData here. 2424 return LocationReference::Temporary(GetBuiltinCode(*builtin), 2425 "builtin " + expr->name->value); 2426 } 2427 if (expr->generic_arguments.size() != 0) { 2428 GenericCallable* generic = Declarations::LookupUniqueGeneric(name); 2429 Callable* specialization = 2430 GetOrCreateSpecialization(SpecializationKey<GenericCallable>{ 2431 generic, TypeVisitor::ComputeTypeVector(expr->generic_arguments)}); 2432 if (Builtin* builtin = Builtin::DynamicCast(specialization)) { 2433 DCHECK(!builtin->IsExternal()); 2434 return LocationReference::Temporary(GetBuiltinCode(builtin), 2435 "builtin " + expr->name->value); 2436 } else { 2437 ReportError("cannot create function pointer for non-builtin ", 2438 generic->name()); 2439 } 2440 } 2441 Value* value = Declarations::LookupValue(name); 2442 CHECK(value->Position().source.IsValid()); 2443 if (auto stream = CurrentFileStreams::Get()) { 2444 stream->required_builtin_includes.insert(value->Position().source); 2445 } 2446 if (GlobalContext::collect_language_server_data()) { 2447 LanguageServerData::AddDefinition(expr->name->pos, value->name()->pos); 2448 } 2449 if (auto* constant = NamespaceConstant::DynamicCast(value)) { 2450 if (GlobalContext::collect_kythe_data()) { 2451 KytheData::AddConstantUse(expr->name->pos, constant); 2452 } 2453 if (constant->type()->IsConstexpr()) { 2454 return LocationReference::Temporary( 2455 VisitResult(constant->type(), constant->external_name() + "(state_)"), 2456 "namespace constant " + expr->name->value); 2457 } 2458 assembler().Emit(NamespaceConstantInstruction{constant}); 2459 StackRange stack_range = 2460 assembler().TopRange(LoweredSlotCount(constant->type())); 2461 return LocationReference::Temporary( 2462 VisitResult(constant->type(), stack_range), 2463 "namespace constant " + expr->name->value); 2464 } 2465 ExternConstant* constant = ExternConstant::cast(value); 2466 if (GlobalContext::collect_kythe_data()) { 2467 KytheData::AddConstantUse(expr->name->pos, constant); 2468 } 2469 return LocationReference::Temporary(constant->value(), 2470 "extern value " + expr->name->value); 2471} 2472 2473LocationReference ImplementationVisitor::GetLocationReference( 2474 DereferenceExpression* expr) { 2475 VisitResult ref = Visit(expr->reference); 2476 if (!TypeOracle::MatchReferenceGeneric(ref.type())) { 2477 Error("Operator * expects a reference type but found a value of type ", 2478 *ref.type()) 2479 .Throw(); 2480 } 2481 return LocationReference::HeapReference(ref); 2482} 2483 2484VisitResult ImplementationVisitor::GenerateFetchFromLocation( 2485 const LocationReference& reference) { 2486 if (reference.IsTemporary()) { 2487 return GenerateCopy(reference.temporary()); 2488 } else if (reference.IsVariableAccess()) { 2489 return GenerateCopy(reference.variable()); 2490 } else if (reference.IsHeapReference()) { 2491 const Type* referenced_type = *reference.ReferencedType(); 2492 if (referenced_type == TypeOracle::GetFloat64OrHoleType()) { 2493 return GenerateCall(QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING}, 2494 "LoadFloat64OrHole"), 2495 Arguments{{reference.heap_reference()}, {}}); 2496 } else if (auto struct_type = referenced_type->StructSupertype()) { 2497 StackRange result_range = assembler().TopRange(0); 2498 for (const Field& field : (*struct_type)->fields()) { 2499 StackScope scope(this); 2500 const std::string& fieldname = field.name_and_type.name; 2501 VisitResult field_value = scope.Yield(GenerateFetchFromLocation( 2502 GenerateFieldAccess(reference, fieldname))); 2503 result_range.Extend(field_value.stack_range()); 2504 } 2505 return VisitResult(referenced_type, result_range); 2506 } else { 2507 GenerateCopy(reference.heap_reference()); 2508 assembler().Emit(LoadReferenceInstruction{referenced_type}); 2509 DCHECK_EQ(1, LoweredSlotCount(referenced_type)); 2510 return VisitResult(referenced_type, assembler().TopRange(1)); 2511 } 2512 } else if (reference.IsBitFieldAccess()) { 2513 // First fetch the bitfield struct, then get the bits out of it. 2514 VisitResult bit_field_struct = 2515 GenerateFetchFromLocation(reference.bit_field_struct_location()); 2516 assembler().Emit(LoadBitFieldInstruction{bit_field_struct.type(), 2517 reference.bit_field()}); 2518 return VisitResult(*reference.ReferencedType(), assembler().TopRange(1)); 2519 } else { 2520 if (reference.IsHeapSlice()) { 2521 ReportError( 2522 "fetching a value directly from an indexed field isn't allowed"); 2523 } 2524 DCHECK(reference.IsCallAccess()); 2525 return GenerateCall(reference.eval_function(), 2526 Arguments{reference.call_arguments(), {}}); 2527 } 2528} 2529 2530void ImplementationVisitor::GenerateAssignToLocation( 2531 const LocationReference& reference, const VisitResult& assignment_value) { 2532 if (reference.IsCallAccess()) { 2533 Arguments arguments{reference.call_arguments(), {}}; 2534 arguments.parameters.push_back(assignment_value); 2535 GenerateCall(reference.assign_function(), arguments); 2536 } else if (reference.IsVariableAccess()) { 2537 VisitResult variable = reference.variable(); 2538 VisitResult converted_value = 2539 GenerateImplicitConvert(variable.type(), assignment_value); 2540 assembler().Poke(variable.stack_range(), converted_value.stack_range(), 2541 variable.type()); 2542 2543 // Local variables are detected by the existence of a binding. Assignment 2544 // to local variables is recorded to support lint errors. 2545 if (reference.binding()) { 2546 (*reference.binding())->SetWritten(); 2547 } 2548 } else if (reference.IsHeapSlice()) { 2549 ReportError("assigning a value directly to an indexed field isn't allowed"); 2550 } else if (reference.IsHeapReference()) { 2551 const Type* referenced_type = *reference.ReferencedType(); 2552 if (reference.IsConst()) { 2553 Error("cannot assign to const value of type ", *referenced_type).Throw(); 2554 } 2555 if (referenced_type == TypeOracle::GetFloat64OrHoleType()) { 2556 GenerateCall( 2557 QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING}, 2558 "StoreFloat64OrHole"), 2559 Arguments{{reference.heap_reference(), assignment_value}, {}}); 2560 } else if (auto struct_type = referenced_type->StructSupertype()) { 2561 if (!assignment_value.type()->IsSubtypeOf(referenced_type)) { 2562 ReportError("Cannot assign to ", *referenced_type, 2563 " with value of type ", *assignment_value.type()); 2564 } 2565 for (const Field& field : (*struct_type)->fields()) { 2566 const std::string& fieldname = field.name_and_type.name; 2567 // Allow assignment of structs even if they contain const fields. 2568 // Const on struct fields just disallows direct writes to them. 2569 bool ignore_stuct_field_constness = true; 2570 GenerateAssignToLocation( 2571 GenerateFieldAccess(reference, fieldname, 2572 ignore_stuct_field_constness), 2573 ProjectStructField(assignment_value, fieldname)); 2574 } 2575 } else { 2576 GenerateCopy(reference.heap_reference()); 2577 VisitResult converted_assignment_value = 2578 GenerateImplicitConvert(referenced_type, assignment_value); 2579 if (referenced_type == TypeOracle::GetFloat64Type()) { 2580 VisitResult silenced_float_value = GenerateCall( 2581 "Float64SilenceNaN", Arguments{{assignment_value}, {}}); 2582 assembler().Poke(converted_assignment_value.stack_range(), 2583 silenced_float_value.stack_range(), referenced_type); 2584 } 2585 assembler().Emit(StoreReferenceInstruction{referenced_type}); 2586 } 2587 } else if (reference.IsBitFieldAccess()) { 2588 // First fetch the bitfield struct, then set the updated bits, then store 2589 // it back to where we found it. 2590 VisitResult bit_field_struct = 2591 GenerateFetchFromLocation(reference.bit_field_struct_location()); 2592 VisitResult converted_value = 2593 GenerateImplicitConvert(*reference.ReferencedType(), assignment_value); 2594 VisitResult updated_bit_field_struct = 2595 GenerateSetBitField(bit_field_struct.type(), reference.bit_field(), 2596 bit_field_struct, converted_value); 2597 GenerateAssignToLocation(reference.bit_field_struct_location(), 2598 updated_bit_field_struct); 2599 } else { 2600 DCHECK(reference.IsTemporary()); 2601 ReportError("cannot assign to const-bound or temporary ", 2602 reference.temporary_description()); 2603 } 2604} 2605 2606VisitResult ImplementationVisitor::GeneratePointerCall( 2607 Expression* callee, const Arguments& arguments, bool is_tailcall) { 2608 StackScope scope(this); 2609 TypeVector parameter_types(arguments.parameters.ComputeTypeVector()); 2610 VisitResult callee_result = Visit(callee); 2611 if (!callee_result.type()->IsBuiltinPointerType()) { 2612 std::stringstream stream; 2613 stream << "Expected a function pointer type but found " 2614 << *callee_result.type(); 2615 ReportError(stream.str()); 2616 } 2617 const BuiltinPointerType* type = 2618 BuiltinPointerType::cast(callee_result.type()); 2619 2620 if (type->parameter_types().size() != parameter_types.size()) { 2621 std::stringstream stream; 2622 stream << "parameter count mismatch calling function pointer with Type: " 2623 << *type << " - expected " 2624 << std::to_string(type->parameter_types().size()) << ", found " 2625 << std::to_string(parameter_types.size()); 2626 ReportError(stream.str()); 2627 } 2628 2629 ParameterTypes types{type->parameter_types(), false}; 2630 Signature sig; 2631 sig.parameter_types = types; 2632 if (!IsCompatibleSignature(sig, parameter_types, 0)) { 2633 std::stringstream stream; 2634 stream << "parameters do not match function pointer signature. Expected: (" 2635 << type->parameter_types() << ") but got: (" << parameter_types 2636 << ")"; 2637 ReportError(stream.str()); 2638 } 2639 2640 callee_result = GenerateCopy(callee_result); 2641 StackRange arg_range = assembler().TopRange(0); 2642 for (size_t current = 0; current < arguments.parameters.size(); ++current) { 2643 const Type* to_type = type->parameter_types()[current]; 2644 arg_range.Extend( 2645 GenerateImplicitConvert(to_type, arguments.parameters[current]) 2646 .stack_range()); 2647 } 2648 2649 assembler().Emit( 2650 CallBuiltinPointerInstruction{is_tailcall, type, arg_range.Size()}); 2651 2652 if (is_tailcall) { 2653 return VisitResult::NeverResult(); 2654 } 2655 DCHECK_EQ(1, LoweredSlotCount(type->return_type())); 2656 return scope.Yield(VisitResult(type->return_type(), assembler().TopRange(1))); 2657} 2658 2659void ImplementationVisitor::AddCallParameter( 2660 Callable* callable, VisitResult parameter, const Type* parameter_type, 2661 std::vector<VisitResult>* converted_arguments, StackRange* argument_range, 2662 std::vector<std::string>* constexpr_arguments, bool inline_macro) { 2663 VisitResult converted; 2664 if ((converted_arguments->size() < callable->signature().implicit_count) && 2665 parameter.type()->IsTopType()) { 2666 converted = GenerateCopy(parameter); 2667 } else { 2668 converted = GenerateImplicitConvert(parameter_type, parameter); 2669 } 2670 converted_arguments->push_back(converted); 2671 if (!inline_macro) { 2672 if (converted.IsOnStack()) { 2673 argument_range->Extend(converted.stack_range()); 2674 } else { 2675 constexpr_arguments->push_back(converted.constexpr_value()); 2676 } 2677 } 2678} 2679 2680namespace { 2681std::pair<std::string, std::string> GetClassInstanceTypeRange( 2682 const ClassType* class_type) { 2683 std::pair<std::string, std::string> result; 2684 if (class_type->InstanceTypeRange()) { 2685 auto instance_type_range = *class_type->InstanceTypeRange(); 2686 std::string instance_type_string_first = 2687 "static_cast<InstanceType>(" + 2688 std::to_string(instance_type_range.first) + ")"; 2689 std::string instance_type_string_second = 2690 "static_cast<InstanceType>(" + 2691 std::to_string(instance_type_range.second) + ")"; 2692 result = 2693 std::make_pair(instance_type_string_first, instance_type_string_second); 2694 } else { 2695 ReportError( 2696 "%Min/MaxInstanceType must take a class type that is either a string " 2697 "or has a generated instance type range"); 2698 } 2699 return result; 2700} 2701} // namespace 2702 2703VisitResult ImplementationVisitor::GenerateCall( 2704 Callable* callable, base::Optional<LocationReference> this_reference, 2705 Arguments arguments, const TypeVector& specialization_types, 2706 bool is_tailcall) { 2707 CHECK(callable->Position().source.IsValid()); 2708 if (auto stream = CurrentFileStreams::Get()) { 2709 stream->required_builtin_includes.insert(callable->Position().source); 2710 } 2711 2712 const Type* return_type = callable->signature().return_type; 2713 2714 if (is_tailcall) { 2715 if (Builtin* builtin = Builtin::DynamicCast(CurrentCallable::Get())) { 2716 const Type* outer_return_type = builtin->signature().return_type; 2717 if (!return_type->IsSubtypeOf(outer_return_type)) { 2718 Error("Cannot tailcall, type of result is ", *return_type, 2719 " but should be a subtype of ", *outer_return_type, "."); 2720 } 2721 } else { 2722 Error("Tail calls are only allowed from builtins"); 2723 } 2724 } 2725 2726 bool inline_macro = callable->ShouldBeInlined(output_type_); 2727 std::vector<VisitResult> implicit_arguments; 2728 for (size_t i = 0; i < callable->signature().implicit_count; ++i) { 2729 std::string implicit_name = callable->signature().parameter_names[i]->value; 2730 base::Optional<Binding<LocalValue>*> val = 2731 TryLookupLocalValue(implicit_name); 2732 if (val) { 2733 implicit_arguments.push_back( 2734 GenerateFetchFromLocation((*val)->GetLocationReference(*val))); 2735 } else { 2736 VisitResult unititialized = VisitResult::TopTypeResult( 2737 "implicit parameter '" + implicit_name + 2738 "' is not defined when invoking " + callable->ReadableName() + 2739 " at " + PositionAsString(CurrentSourcePosition::Get()), 2740 callable->signature().parameter_types.types[i]); 2741 implicit_arguments.push_back(unititialized); 2742 } 2743 const Type* type = implicit_arguments.back().type(); 2744 if (const TopType* top_type = TopType::DynamicCast(type)) { 2745 if (!callable->IsMacro() || callable->IsExternal()) { 2746 ReportError( 2747 "unititialized implicit parameters can only be passed to " 2748 "Torque-defined macros: the ", 2749 top_type->reason()); 2750 } 2751 inline_macro = true; 2752 } 2753 } 2754 2755 std::vector<VisitResult> converted_arguments; 2756 StackRange argument_range = assembler().TopRange(0); 2757 std::vector<std::string> constexpr_arguments; 2758 2759 size_t current = 0; 2760 for (; current < callable->signature().implicit_count; ++current) { 2761 AddCallParameter(callable, implicit_arguments[current], 2762 callable->signature().parameter_types.types[current], 2763 &converted_arguments, &argument_range, 2764 &constexpr_arguments, inline_macro); 2765 } 2766 2767 if (this_reference) { 2768 DCHECK(callable->IsMethod()); 2769 Method* method = Method::cast(callable); 2770 // By now, the this reference should either be a variable, a temporary or 2771 // a Slice. In either case the fetch of the VisitResult should succeed. 2772 VisitResult this_value = this_reference->GetVisitResult(); 2773 if (inline_macro) { 2774 if (!this_value.type()->IsSubtypeOf(method->aggregate_type())) { 2775 ReportError("this parameter must be a subtype of ", 2776 *method->aggregate_type(), " but it is of type ", 2777 *this_value.type()); 2778 } 2779 } else { 2780 AddCallParameter(callable, this_value, method->aggregate_type(), 2781 &converted_arguments, &argument_range, 2782 &constexpr_arguments, inline_macro); 2783 } 2784 ++current; 2785 } 2786 2787 for (auto arg : arguments.parameters) { 2788 const Type* to_type = (current >= callable->signature().types().size()) 2789 ? TypeOracle::GetObjectType() 2790 : callable->signature().types()[current++]; 2791 AddCallParameter(callable, arg, to_type, &converted_arguments, 2792 &argument_range, &constexpr_arguments, inline_macro); 2793 } 2794 2795 size_t label_count = callable->signature().labels.size(); 2796 if (label_count != arguments.labels.size()) { 2797 std::stringstream s; 2798 s << "unexpected number of otherwise labels for " 2799 << callable->ReadableName() << " (expected " 2800 << std::to_string(label_count) << " found " 2801 << std::to_string(arguments.labels.size()) << ")"; 2802 ReportError(s.str()); 2803 } 2804 2805 if (callable->IsTransitioning()) { 2806 if (!CurrentCallable::Get()->IsTransitioning()) { 2807 std::stringstream s; 2808 s << *CurrentCallable::Get() 2809 << " isn't marked transitioning but calls the transitioning " 2810 << *callable; 2811 ReportError(s.str()); 2812 } 2813 } 2814 2815 if (auto* builtin = Builtin::DynamicCast(callable)) { 2816 base::Optional<Block*> catch_block = GetCatchBlock(); 2817 assembler().Emit(CallBuiltinInstruction{ 2818 is_tailcall, builtin, argument_range.Size(), catch_block}); 2819 GenerateCatchBlock(catch_block); 2820 if (is_tailcall) { 2821 return VisitResult::NeverResult(); 2822 } else { 2823 size_t slot_count = LoweredSlotCount(return_type); 2824 if (builtin->IsStub()) { 2825 if (slot_count < 1 || slot_count > 2) { 2826 ReportError( 2827 "Builtin with stub linkage is expected to return one or two " 2828 "values but returns ", 2829 slot_count); 2830 } 2831 } else { 2832 if (slot_count != 1) { 2833 ReportError( 2834 "Builtin with JS linkage is expected to return one value but " 2835 "returns ", 2836 slot_count); 2837 } 2838 } 2839 return VisitResult(return_type, assembler().TopRange(slot_count)); 2840 } 2841 } else if (auto* macro = Macro::DynamicCast(callable)) { 2842 if (is_tailcall) { 2843 ReportError("can't tail call a macro"); 2844 } 2845 2846 macro->SetUsed(); 2847 2848 // If we're currently generating a C++ macro and it's calling another macro, 2849 // then we need to make sure that we also generate C++ code for the called 2850 // macro within the same -inl.inc file. 2851 if ((output_type_ == OutputType::kCC || 2852 output_type_ == OutputType::kCCDebug) && 2853 !inline_macro) { 2854 if (auto* torque_macro = TorqueMacro::DynamicCast(macro)) { 2855 auto* streams = CurrentFileStreams::Get(); 2856 SourceId file = streams ? streams->file : SourceId::Invalid(); 2857 GlobalContext::EnsureInCCOutputList(torque_macro, file); 2858 } 2859 } 2860 2861 // TODO(torque-builder): Consider a function builder here. 2862 if (return_type->IsConstexpr()) { 2863 DCHECK_EQ(0, arguments.labels.size()); 2864 std::stringstream result; 2865 result << "("; 2866 bool first = true; 2867 switch (output_type_) { 2868 case OutputType::kCSA: { 2869 if (auto* extern_macro = ExternMacro::DynamicCast(macro)) { 2870 result << extern_macro->external_assembler_name() << "(state_)." 2871 << extern_macro->ExternalName() << "("; 2872 } else { 2873 result << macro->ExternalName() << "(state_"; 2874 first = false; 2875 } 2876 break; 2877 } 2878 case OutputType::kCC: { 2879 auto* extern_macro = ExternMacro::DynamicCast(macro); 2880 CHECK_NOT_NULL(extern_macro); 2881 result << extern_macro->CCName() << "("; 2882 break; 2883 } 2884 case OutputType::kCCDebug: { 2885 auto* extern_macro = ExternMacro::DynamicCast(macro); 2886 CHECK_NOT_NULL(extern_macro); 2887 result << extern_macro->CCDebugName() << "(accessor"; 2888 first = false; 2889 break; 2890 } 2891 } 2892 for (VisitResult arg : converted_arguments) { 2893 DCHECK(!arg.IsOnStack()); 2894 if (!first) { 2895 result << ", "; 2896 } 2897 first = false; 2898 result << arg.constexpr_value(); 2899 } 2900 result << "))"; 2901 return VisitResult(return_type, result.str()); 2902 } else if (inline_macro) { 2903 std::vector<Block*> label_blocks; 2904 for (Binding<LocalLabel>* label : arguments.labels) { 2905 label_blocks.push_back(label->block); 2906 } 2907 return InlineMacro(macro, this_reference, converted_arguments, 2908 label_blocks); 2909 } else if (arguments.labels.empty() && 2910 return_type != TypeOracle::GetNeverType()) { 2911 base::Optional<Block*> catch_block = GetCatchBlock(); 2912 assembler().Emit( 2913 CallCsaMacroInstruction{macro, constexpr_arguments, catch_block}); 2914 GenerateCatchBlock(catch_block); 2915 size_t return_slot_count = LoweredSlotCount(return_type); 2916 return VisitResult(return_type, assembler().TopRange(return_slot_count)); 2917 } else { 2918 base::Optional<Block*> return_continuation; 2919 if (return_type != TypeOracle::GetNeverType()) { 2920 return_continuation = assembler().NewBlock(); 2921 } 2922 2923 std::vector<Block*> label_blocks; 2924 2925 for (size_t i = 0; i < label_count; ++i) { 2926 label_blocks.push_back(assembler().NewBlock()); 2927 } 2928 base::Optional<Block*> catch_block = GetCatchBlock(); 2929 assembler().Emit(CallCsaMacroAndBranchInstruction{ 2930 macro, constexpr_arguments, return_continuation, label_blocks, 2931 catch_block}); 2932 GenerateCatchBlock(catch_block); 2933 2934 for (size_t i = 0; i < label_count; ++i) { 2935 Binding<LocalLabel>* label = arguments.labels[i]; 2936 size_t callee_label_parameters = 2937 callable->signature().labels[i].types.size(); 2938 if (label->parameter_types.size() != callee_label_parameters) { 2939 std::stringstream s; 2940 s << "label " << label->name() 2941 << " doesn't have the right number of parameters (found " 2942 << std::to_string(label->parameter_types.size()) << " expected " 2943 << std::to_string(callee_label_parameters) << ")"; 2944 ReportError(s.str()); 2945 } 2946 assembler().Bind(label_blocks[i]); 2947 assembler().Goto( 2948 label->block, 2949 LowerParameterTypes(callable->signature().labels[i].types).size()); 2950 2951 size_t j = 0; 2952 for (auto t : callable->signature().labels[i].types) { 2953 const Type* parameter_type = label->parameter_types[j]; 2954 if (!t->IsSubtypeOf(parameter_type)) { 2955 ReportError("mismatch of label parameters (label expects ", 2956 *parameter_type, " but macro produces ", *t, 2957 " for parameter ", i + 1, ")"); 2958 } 2959 j++; 2960 } 2961 } 2962 2963 if (return_continuation) { 2964 assembler().Bind(*return_continuation); 2965 size_t return_slot_count = LoweredSlotCount(return_type); 2966 return VisitResult(return_type, 2967 assembler().TopRange(return_slot_count)); 2968 } else { 2969 return VisitResult::NeverResult(); 2970 } 2971 } 2972 } else if (auto* runtime_function = RuntimeFunction::DynamicCast(callable)) { 2973 base::Optional<Block*> catch_block = GetCatchBlock(); 2974 assembler().Emit(CallRuntimeInstruction{ 2975 is_tailcall, runtime_function, argument_range.Size(), catch_block}); 2976 GenerateCatchBlock(catch_block); 2977 if (is_tailcall || return_type == TypeOracle::GetNeverType()) { 2978 return VisitResult::NeverResult(); 2979 } else { 2980 size_t slot_count = LoweredSlotCount(return_type); 2981 DCHECK_LE(slot_count, 1); 2982 // TODO(turbofan): Actually, runtime functions have to return a value, so 2983 // we should assert slot_count == 1 here. 2984 return VisitResult(return_type, assembler().TopRange(slot_count)); 2985 } 2986 } else if (auto* intrinsic = Intrinsic::DynamicCast(callable)) { 2987 if (intrinsic->ExternalName() == "%SizeOf") { 2988 if (specialization_types.size() != 1) { 2989 ReportError("%SizeOf must take a single type parameter"); 2990 } 2991 const Type* type = specialization_types[0]; 2992 std::string size_string; 2993 if (base::Optional<std::tuple<size_t, std::string>> size = SizeOf(type)) { 2994 size_string = std::get<1>(*size); 2995 } else { 2996 Error("size of ", *type, " is not known."); 2997 } 2998 return VisitResult(return_type, size_string); 2999 } else if (intrinsic->ExternalName() == "%ClassHasMapConstant") { 3000 const Type* type = specialization_types[0]; 3001 const ClassType* class_type = ClassType::DynamicCast(type); 3002 if (!class_type) { 3003 ReportError("%ClassHasMapConstant must take a class type parameter"); 3004 } 3005 // If the class isn't actually used as the parameter to a TNode, 3006 // then we can't rely on the class existing in C++ or being of the same 3007 // type (e.g. it could be a template), so don't use the template CSA 3008 // machinery for accessing the class' map. 3009 if (class_type->name() != class_type->GetGeneratedTNodeTypeName()) { 3010 return VisitResult(return_type, std::string("false")); 3011 } else { 3012 return VisitResult( 3013 return_type, 3014 std::string("CodeStubAssembler(state_).ClassHasMapConstant<") + 3015 class_type->name() + ">()"); 3016 } 3017 } else if (intrinsic->ExternalName() == "%MinInstanceType") { 3018 if (specialization_types.size() != 1) { 3019 ReportError("%MinInstanceType must take a single type parameter"); 3020 } 3021 const Type* type = specialization_types[0]; 3022 const ClassType* class_type = ClassType::DynamicCast(type); 3023 if (!class_type) { 3024 ReportError("%MinInstanceType must take a class type parameter"); 3025 } 3026 std::pair<std::string, std::string> instance_types = 3027 GetClassInstanceTypeRange(class_type); 3028 return VisitResult(return_type, instance_types.first); 3029 } else if (intrinsic->ExternalName() == "%MaxInstanceType") { 3030 if (specialization_types.size() != 1) { 3031 ReportError("%MaxInstanceType must take a single type parameter"); 3032 } 3033 const Type* type = specialization_types[0]; 3034 const ClassType* class_type = ClassType::DynamicCast(type); 3035 if (!class_type) { 3036 ReportError("%MaxInstanceType must take a class type parameter"); 3037 } 3038 std::pair<std::string, std::string> instance_types = 3039 GetClassInstanceTypeRange(class_type); 3040 return VisitResult(return_type, instance_types.second); 3041 } else if (intrinsic->ExternalName() == "%RawConstexprCast") { 3042 if (intrinsic->signature().parameter_types.types.size() != 1 || 3043 constexpr_arguments.size() != 1) { 3044 ReportError( 3045 "%RawConstexprCast must take a single parameter with constexpr " 3046 "type"); 3047 } 3048 if (!return_type->IsConstexpr()) { 3049 std::stringstream s; 3050 s << *return_type 3051 << " return type for %RawConstexprCast is not constexpr"; 3052 ReportError(s.str()); 3053 } 3054 std::stringstream result; 3055 result << "static_cast<" << return_type->GetGeneratedTypeName() << ">("; 3056 result << constexpr_arguments[0]; 3057 result << ")"; 3058 return VisitResult(return_type, result.str()); 3059 } else if (intrinsic->ExternalName() == "%IndexedFieldLength") { 3060 const Type* type = specialization_types[0]; 3061 const ClassType* class_type = ClassType::DynamicCast(type); 3062 if (!class_type) { 3063 ReportError("%IndexedFieldLength must take a class type parameter"); 3064 } 3065 const Field& field = 3066 class_type->LookupField(StringLiteralUnquote(constexpr_arguments[0])); 3067 return GenerateArrayLength(VisitResult(type, argument_range), field); 3068 } else if (intrinsic->ExternalName() == "%MakeLazy") { 3069 if (specialization_types[0]->IsStructType()) { 3070 ReportError("%MakeLazy can't use macros that return structs"); 3071 } 3072 std::string getter_name = StringLiteralUnquote(constexpr_arguments[0]); 3073 3074 // Normally the parser would split namespace names for us, but we 3075 // sidestepped it by putting the macro name in a string literal. 3076 QualifiedName qualified_getter_name = QualifiedName::Parse(getter_name); 3077 3078 // converted_arguments contains all of the arguments to %MakeLazy. We're 3079 // looking for a function that takes all but the first. 3080 Arguments arguments_to_getter; 3081 arguments_to_getter.parameters.insert( 3082 arguments_to_getter.parameters.begin(), 3083 converted_arguments.begin() + 1, converted_arguments.end()); 3084 3085 Callable* callable_macro = LookupCallable( 3086 qualified_getter_name, Declarations::Lookup(qualified_getter_name), 3087 arguments_to_getter, {}); 3088 Macro* getter = Macro::DynamicCast(callable_macro); 3089 if (!getter || getter->IsMethod()) { 3090 ReportError( 3091 "%MakeLazy expects a macro, not builtin or other type of callable"); 3092 } 3093 if (!getter->signature().labels.empty()) { 3094 ReportError("%MakeLazy requires a macro with no labels"); 3095 } 3096 if (!getter->signature().return_type->IsSubtypeOf( 3097 specialization_types[0])) { 3098 ReportError("%MakeLazy expected return type ", *specialization_types[0], 3099 " but found ", *getter->signature().return_type); 3100 } 3101 if (getter->signature().implicit_count > 0) { 3102 ReportError("Implicit parameters are not yet supported in %MakeLazy"); 3103 } 3104 3105 getter->SetUsed(); // Prevent warnings about unused macros. 3106 3107 // Now that we've looked up the getter macro, we have to convert the 3108 // arguments again, so that, for example, constexpr arguments can be 3109 // coerced to non-constexpr types and put on the stack. 3110 3111 std::vector<VisitResult> converted_arguments_for_getter; 3112 StackRange argument_range_for_getter = assembler().TopRange(0); 3113 std::vector<std::string> constexpr_arguments_for_getter; 3114 3115 size_t arg_count = 0; 3116 for (auto arg : arguments_to_getter.parameters) { 3117 DCHECK_LT(arg_count, getter->signature().types().size()); 3118 const Type* to_type = getter->signature().types()[arg_count++]; 3119 AddCallParameter(getter, arg, to_type, &converted_arguments_for_getter, 3120 &argument_range_for_getter, 3121 &constexpr_arguments_for_getter, 3122 /*inline_macro=*/false); 3123 } 3124 3125 // Now that the arguments are prepared, emit the instruction that consumes 3126 // them. 3127 assembler().Emit(MakeLazyNodeInstruction{getter, return_type, 3128 constexpr_arguments_for_getter}); 3129 return VisitResult(return_type, assembler().TopRange(1)); 3130 } else if (intrinsic->ExternalName() == "%FieldSlice") { 3131 const Type* type = specialization_types[0]; 3132 const ClassType* class_type = ClassType::DynamicCast(type); 3133 if (!class_type) { 3134 ReportError("The first type parameter to %FieldSlice must be a class"); 3135 } 3136 const Field& field = 3137 class_type->LookupField(StringLiteralUnquote(constexpr_arguments[0])); 3138 const Type* expected_slice_type = 3139 field.const_qualified 3140 ? TypeOracle::GetConstSliceType(field.name_and_type.type) 3141 : TypeOracle::GetMutableSliceType(field.name_and_type.type); 3142 const Type* declared_slice_type = specialization_types[1]; 3143 if (expected_slice_type != declared_slice_type) { 3144 Error( 3145 "The second type parameter to %FieldSlice must be the precise " 3146 "slice type for the named field"); 3147 } 3148 LocationReference ref = GenerateFieldReference( 3149 VisitResult(type, argument_range), field, class_type, 3150 /*treat_optional_as_indexed=*/true); 3151 if (!ref.IsHeapSlice()) { 3152 ReportError("%FieldSlice expected an indexed or optional field"); 3153 } 3154 return ref.heap_slice(); 3155 } else { 3156 assembler().Emit(CallIntrinsicInstruction{intrinsic, specialization_types, 3157 constexpr_arguments}); 3158 size_t return_slot_count = 3159 LoweredSlotCount(intrinsic->signature().return_type); 3160 return VisitResult(return_type, assembler().TopRange(return_slot_count)); 3161 } 3162 } else { 3163 UNREACHABLE(); 3164 } 3165} 3166 3167VisitResult ImplementationVisitor::GenerateCall( 3168 const QualifiedName& callable_name, Arguments arguments, 3169 const TypeVector& specialization_types, bool is_tailcall) { 3170 Callable* callable = 3171 LookupCallable(callable_name, Declarations::Lookup(callable_name), 3172 arguments, specialization_types); 3173 return GenerateCall(callable, base::nullopt, arguments, specialization_types, 3174 is_tailcall); 3175} 3176 3177VisitResult ImplementationVisitor::Visit(CallExpression* expr, 3178 bool is_tailcall) { 3179 StackScope scope(this); 3180 3181 if (expr->callee->name->value == "&" && expr->arguments.size() == 1) { 3182 if (auto* loc_expr = LocationExpression::DynamicCast(expr->arguments[0])) { 3183 LocationReference ref = GetLocationReference(loc_expr); 3184 if (ref.IsHeapReference()) return scope.Yield(ref.heap_reference()); 3185 if (ref.IsHeapSlice()) return scope.Yield(ref.heap_slice()); 3186 } 3187 ReportError("Unable to create a heap reference."); 3188 } 3189 3190 Arguments arguments; 3191 QualifiedName name = QualifiedName(expr->callee->namespace_qualification, 3192 expr->callee->name->value); 3193 TypeVector specialization_types = 3194 TypeVisitor::ComputeTypeVector(expr->callee->generic_arguments); 3195 bool has_template_arguments = !specialization_types.empty(); 3196 for (Expression* arg : expr->arguments) 3197 arguments.parameters.push_back(Visit(arg)); 3198 arguments.labels = LabelsFromIdentifiers(expr->labels); 3199 if (!has_template_arguments && name.namespace_qualification.empty() && 3200 TryLookupLocalValue(name.name)) { 3201 return scope.Yield( 3202 GeneratePointerCall(expr->callee, arguments, is_tailcall)); 3203 } else { 3204 if (GlobalContext::collect_language_server_data()) { 3205 Callable* callable = LookupCallable(name, Declarations::Lookup(name), 3206 arguments, specialization_types); 3207 LanguageServerData::AddDefinition(expr->callee->name->pos, 3208 callable->IdentifierPosition()); 3209 } 3210 if (GlobalContext::collect_kythe_data()) { 3211 Callable* callable = LookupCallable(name, Declarations::Lookup(name), 3212 arguments, specialization_types); 3213 Callable* caller = CurrentCallable::Get(); 3214 KytheData::AddCall(caller, expr->callee->name->pos, callable); 3215 } 3216 if (expr->callee->name->value == "!" && arguments.parameters.size() == 1) { 3217 PropagateBitfieldMark(expr->arguments[0], expr); 3218 } 3219 if (expr->callee->name->value == "==" && arguments.parameters.size() == 2) { 3220 if (arguments.parameters[0].type()->IsConstexpr()) { 3221 PropagateBitfieldMark(expr->arguments[1], expr); 3222 } else if (arguments.parameters[1].type()->IsConstexpr()) { 3223 PropagateBitfieldMark(expr->arguments[0], expr); 3224 } 3225 } 3226 return scope.Yield( 3227 GenerateCall(name, arguments, specialization_types, is_tailcall)); 3228 } 3229} 3230 3231VisitResult ImplementationVisitor::Visit(CallMethodExpression* expr) { 3232 StackScope scope(this); 3233 Arguments arguments; 3234 std::string method_name = expr->method->name->value; 3235 TypeVector specialization_types = 3236 TypeVisitor::ComputeTypeVector(expr->method->generic_arguments); 3237 LocationReference target = GetLocationReference(expr->target); 3238 if (!target.IsVariableAccess()) { 3239 VisitResult result = GenerateFetchFromLocation(target); 3240 target = LocationReference::Temporary(result, "this parameter"); 3241 } 3242 const AggregateType* target_type = 3243 (*target.ReferencedType())->AggregateSupertype().value_or(nullptr); 3244 if (!target_type) { 3245 ReportError("target of method call not a struct or class type"); 3246 } 3247 for (Expression* arg : expr->arguments) { 3248 arguments.parameters.push_back(Visit(arg)); 3249 } 3250 arguments.labels = LabelsFromIdentifiers(expr->labels); 3251 TypeVector argument_types = arguments.parameters.ComputeTypeVector(); 3252 DCHECK_EQ(expr->method->namespace_qualification.size(), 0); 3253 QualifiedName qualified_name = QualifiedName(method_name); 3254 Callable* callable = LookupMethod(method_name, target_type, arguments, {}); 3255 if (GlobalContext::collect_language_server_data()) { 3256 LanguageServerData::AddDefinition(expr->method->name->pos, 3257 callable->IdentifierPosition()); 3258 } 3259 if (GlobalContext::collect_kythe_data()) { 3260 Callable* caller = CurrentCallable::Get(); 3261 KytheData::AddCall(caller, expr->method->name->pos, callable); 3262 } 3263 return scope.Yield(GenerateCall(callable, target, arguments, {}, false)); 3264} 3265 3266VisitResult ImplementationVisitor::Visit(IntrinsicCallExpression* expr) { 3267 StackScope scope(this); 3268 Arguments arguments; 3269 TypeVector specialization_types = 3270 TypeVisitor::ComputeTypeVector(expr->generic_arguments); 3271 for (Expression* arg : expr->arguments) 3272 arguments.parameters.push_back(Visit(arg)); 3273 return scope.Yield( 3274 GenerateCall(expr->name->value, arguments, specialization_types, false)); 3275} 3276 3277void ImplementationVisitor::GenerateBranch(const VisitResult& condition, 3278 Block* true_block, 3279 Block* false_block) { 3280 DCHECK_EQ(condition, 3281 VisitResult(TypeOracle::GetBoolType(), assembler().TopRange(1))); 3282 assembler().Branch(true_block, false_block); 3283} 3284 3285VisitResult ImplementationVisitor::GenerateBoolConstant(bool constant) { 3286 return GenerateImplicitConvert(TypeOracle::GetBoolType(), 3287 VisitResult(TypeOracle::GetConstexprBoolType(), 3288 constant ? "true" : "false")); 3289} 3290 3291void ImplementationVisitor::GenerateExpressionBranch(Expression* expression, 3292 Block* true_block, 3293 Block* false_block) { 3294 StackScope stack_scope(this); 3295 VisitResult expression_result = this->Visit(expression); 3296 expression_result = stack_scope.Yield( 3297 GenerateImplicitConvert(TypeOracle::GetBoolType(), expression_result)); 3298 GenerateBranch(expression_result, true_block, false_block); 3299} 3300 3301VisitResult ImplementationVisitor::GenerateImplicitConvert( 3302 const Type* destination_type, VisitResult source) { 3303 StackScope scope(this); 3304 if (source.type() == TypeOracle::GetNeverType()) { 3305 ReportError("it is not allowed to use a value of type never"); 3306 } 3307 3308 if (destination_type == source.type()) { 3309 return scope.Yield(GenerateCopy(source)); 3310 } 3311 3312 if (auto from = TypeOracle::ImplicitlyConvertableFrom(destination_type, 3313 source.type())) { 3314 return scope.Yield(GenerateCall(kFromConstexprMacroName, 3315 Arguments{{source}, {}}, 3316 {destination_type, *from}, false)); 3317 } else if (IsAssignableFrom(destination_type, source.type())) { 3318 source.SetType(destination_type); 3319 return scope.Yield(GenerateCopy(source)); 3320 } else { 3321 std::stringstream s; 3322 if (const TopType* top_type = TopType::DynamicCast(source.type())) { 3323 s << "undefined expression of type " << *destination_type << ": the " 3324 << top_type->reason(); 3325 } else { 3326 s << "cannot use expression of type " << *source.type() 3327 << " as a value of type " << *destination_type; 3328 } 3329 ReportError(s.str()); 3330 } 3331} 3332 3333StackRange ImplementationVisitor::GenerateLabelGoto( 3334 LocalLabel* label, base::Optional<StackRange> arguments) { 3335 return assembler().Goto(label->block, arguments ? arguments->Size() : 0); 3336} 3337 3338std::vector<Binding<LocalLabel>*> ImplementationVisitor::LabelsFromIdentifiers( 3339 const std::vector<Identifier*>& names) { 3340 std::vector<Binding<LocalLabel>*> result; 3341 result.reserve(names.size()); 3342 for (const auto& name : names) { 3343 Binding<LocalLabel>* label = LookupLabel(name->value); 3344 result.push_back(label); 3345 3346 // Link up labels in "otherwise" part of the call expression with 3347 // either the label in the signature of the calling macro or the label 3348 // block ofa surrounding "try". 3349 if (GlobalContext::collect_language_server_data()) { 3350 LanguageServerData::AddDefinition(name->pos, 3351 label->declaration_position()); 3352 } 3353 // TODO(v8:12261): Might have to track KytheData here. 3354 } 3355 return result; 3356} 3357 3358StackRange ImplementationVisitor::LowerParameter( 3359 const Type* type, const std::string& parameter_name, 3360 Stack<std::string>* lowered_parameters) { 3361 if (base::Optional<const StructType*> struct_type = type->StructSupertype()) { 3362 StackRange range = lowered_parameters->TopRange(0); 3363 for (auto& field : (*struct_type)->fields()) { 3364 StackRange parameter_range = LowerParameter( 3365 field.name_and_type.type, 3366 parameter_name + "." + field.name_and_type.name, lowered_parameters); 3367 range.Extend(parameter_range); 3368 } 3369 return range; 3370 } else { 3371 lowered_parameters->Push(parameter_name); 3372 return lowered_parameters->TopRange(1); 3373 } 3374} 3375 3376void ImplementationVisitor::LowerLabelParameter( 3377 const Type* type, const std::string& parameter_name, 3378 std::vector<std::string>* lowered_parameters) { 3379 if (base::Optional<const StructType*> struct_type = type->StructSupertype()) { 3380 for (auto& field : (*struct_type)->fields()) { 3381 LowerLabelParameter( 3382 field.name_and_type.type, 3383 "&((*" + parameter_name + ")." + field.name_and_type.name + ")", 3384 lowered_parameters); 3385 } 3386 } else { 3387 lowered_parameters->push_back(parameter_name); 3388 } 3389} 3390 3391std::string ImplementationVisitor::ExternalLabelName( 3392 const std::string& label_name) { 3393 return "label_" + label_name; 3394} 3395 3396std::string ImplementationVisitor::ExternalLabelParameterName( 3397 const std::string& label_name, size_t i) { 3398 return "label_" + label_name + "_parameter_" + std::to_string(i); 3399} 3400 3401std::string ImplementationVisitor::ExternalParameterName( 3402 const std::string& name) { 3403 return std::string("p_") + name; 3404} 3405 3406DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::ValueBindingsManager) 3407DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::LabelBindingsManager) 3408DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::CurrentCallable) 3409DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::CurrentFileStreams) 3410DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::CurrentReturnValue) 3411 3412bool IsCompatibleSignature(const Signature& sig, const TypeVector& types, 3413 size_t label_count) { 3414 auto i = sig.parameter_types.types.begin() + sig.implicit_count; 3415 if ((sig.parameter_types.types.size() - sig.implicit_count) > types.size()) 3416 return false; 3417 if (sig.labels.size() != label_count) return false; 3418 for (auto current : types) { 3419 if (i == sig.parameter_types.types.end()) { 3420 if (!sig.parameter_types.var_args) return false; 3421 if (!IsAssignableFrom(TypeOracle::GetObjectType(), current)) return false; 3422 } else { 3423 if (!IsAssignableFrom(*i++, current)) return false; 3424 } 3425 } 3426 return true; 3427} 3428 3429base::Optional<Block*> ImplementationVisitor::GetCatchBlock() { 3430 base::Optional<Block*> catch_block; 3431 if (base::Optional<Binding<LocalLabel>*> catch_handler = 3432 TryLookupLabel(kCatchLabelName)) { 3433 catch_block = assembler().NewBlock(base::nullopt, true); 3434 } 3435 return catch_block; 3436} 3437 3438void ImplementationVisitor::GenerateCatchBlock( 3439 base::Optional<Block*> catch_block) { 3440 if (catch_block) { 3441 base::Optional<Binding<LocalLabel>*> catch_handler = 3442 TryLookupLabel(kCatchLabelName); 3443 // Reset the local scopes to prevent the macro calls below from using the 3444 // current catch handler. 3445 BindingsManagersScope bindings_managers_scope; 3446 if (assembler().CurrentBlockIsComplete()) { 3447 assembler().Bind(*catch_block); 3448 GenerateCall(QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING}, 3449 "GetAndResetPendingMessage"), 3450 Arguments{{}, {}}, {}, false); 3451 assembler().Goto((*catch_handler)->block, 2); 3452 } else { 3453 CfgAssemblerScopedTemporaryBlock temp(&assembler(), *catch_block); 3454 GenerateCall(QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING}, 3455 "GetAndResetPendingMessage"), 3456 Arguments{{}, {}}, {}, false); 3457 assembler().Goto((*catch_handler)->block, 2); 3458 } 3459 } 3460} 3461void ImplementationVisitor::VisitAllDeclarables() { 3462 CurrentCallable::Scope current_callable(nullptr); 3463 const std::vector<std::unique_ptr<Declarable>>& all_declarables = 3464 GlobalContext::AllDeclarables(); 3465 3466 // This has to be an index-based loop because all_declarables can be extended 3467 // during the loop. 3468 for (size_t i = 0; i < all_declarables.size(); ++i) { 3469 try { 3470 Visit(all_declarables[i].get()); 3471 } catch (TorqueAbortCompilation&) { 3472 // Recover from compile errors here. The error is recorded already. 3473 } 3474 } 3475 3476 // Do the same for macros which generate C++ code. 3477 output_type_ = OutputType::kCC; 3478 const std::vector<std::pair<TorqueMacro*, SourceId>>& cc_macros = 3479 GlobalContext::AllMacrosForCCOutput(); 3480 for (size_t i = 0; i < cc_macros.size(); ++i) { 3481 try { 3482 Visit(static_cast<Declarable*>(cc_macros[i].first), cc_macros[i].second); 3483 } catch (TorqueAbortCompilation&) { 3484 // Recover from compile errors here. The error is recorded already. 3485 } 3486 } 3487 3488 // Do the same for macros which generate C++ debug code. 3489 // The set of macros is the same as C++ macros. 3490 output_type_ = OutputType::kCCDebug; 3491 for (size_t i = 0; i < cc_macros.size(); ++i) { 3492 try { 3493 Visit(static_cast<Declarable*>(cc_macros[i].first), cc_macros[i].second); 3494 } catch (TorqueAbortCompilation&) { 3495 // Recover from compile errors here. The error is recorded already. 3496 } 3497 } 3498 output_type_ = OutputType::kCSA; 3499} 3500 3501void ImplementationVisitor::Visit(Declarable* declarable, 3502 base::Optional<SourceId> file) { 3503 CurrentScope::Scope current_scope(declarable->ParentScope()); 3504 CurrentSourcePosition::Scope current_source_position(declarable->Position()); 3505 CurrentFileStreams::Scope current_file_streams( 3506 &GlobalContext::GeneratedPerFile(file ? *file 3507 : declarable->Position().source)); 3508 if (Callable* callable = Callable::DynamicCast(declarable)) { 3509 if (!callable->ShouldGenerateExternalCode(output_type_)) 3510 CurrentFileStreams::Get() = nullptr; 3511 } 3512 switch (declarable->kind()) { 3513 case Declarable::kExternMacro: 3514 return Visit(ExternMacro::cast(declarable)); 3515 case Declarable::kTorqueMacro: 3516 return Visit(TorqueMacro::cast(declarable)); 3517 case Declarable::kMethod: 3518 return Visit(Method::cast(declarable)); 3519 case Declarable::kBuiltin: 3520 return Visit(Builtin::cast(declarable)); 3521 case Declarable::kTypeAlias: 3522 return Visit(TypeAlias::cast(declarable)); 3523 case Declarable::kNamespaceConstant: 3524 return Visit(NamespaceConstant::cast(declarable)); 3525 case Declarable::kRuntimeFunction: 3526 case Declarable::kIntrinsic: 3527 case Declarable::kExternConstant: 3528 case Declarable::kNamespace: 3529 case Declarable::kGenericCallable: 3530 case Declarable::kGenericType: 3531 return; 3532 } 3533} 3534 3535std::string MachineTypeString(const Type* type) { 3536 if (type->IsSubtypeOf(TypeOracle::GetSmiType())) { 3537 return "MachineType::TaggedSigned()"; 3538 } 3539 if (type->IsSubtypeOf(TypeOracle::GetHeapObjectType())) { 3540 return "MachineType::TaggedPointer()"; 3541 } 3542 if (type->IsSubtypeOf(TypeOracle::GetTaggedType())) { 3543 return "MachineType::AnyTagged()"; 3544 } 3545 return "MachineTypeOf<" + type->GetGeneratedTNodeTypeName() + ">::value"; 3546} 3547 3548void ImplementationVisitor::GenerateBuiltinDefinitionsAndInterfaceDescriptors( 3549 const std::string& output_directory) { 3550 std::stringstream builtin_definitions; 3551 std::string builtin_definitions_file_name = "builtin-definitions.h"; 3552 3553 // This file contains plain interface descriptor definitions and has to be 3554 // included in the middle of interface-descriptors.h. Thus it is not a normal 3555 // header file and uses the .inc suffix instead of the .h suffix. 3556 std::stringstream interface_descriptors; 3557 std::string interface_descriptors_file_name = "interface-descriptors.inc"; 3558 { 3559 IncludeGuardScope builtin_definitions_include_guard( 3560 builtin_definitions, builtin_definitions_file_name); 3561 3562 builtin_definitions 3563 << "\n" 3564 "#define BUILTIN_LIST_FROM_TORQUE(CPP, TFJ, TFC, TFS, TFH, " 3565 "ASM) " 3566 "\\\n"; 3567 for (auto& declarable : GlobalContext::AllDeclarables()) { 3568 Builtin* builtin = Builtin::DynamicCast(declarable.get()); 3569 if (!builtin || builtin->IsExternal()) continue; 3570 if (builtin->IsStub()) { 3571 builtin_definitions << "TFC(" << builtin->ExternalName() << ", " 3572 << builtin->ExternalName(); 3573 std::string descriptor_name = builtin->ExternalName() + "Descriptor"; 3574 bool has_context_parameter = builtin->signature().HasContextParameter(); 3575 size_t kFirstNonContextParameter = has_context_parameter ? 1 : 0; 3576 TypeVector return_types = LowerType(builtin->signature().return_type); 3577 3578 interface_descriptors << "class " << descriptor_name 3579 << " : public StaticCallInterfaceDescriptor<" 3580 << descriptor_name << "> {\n"; 3581 3582 interface_descriptors << " public:\n"; 3583 3584 if (has_context_parameter) { 3585 interface_descriptors << " DEFINE_RESULT_AND_PARAMETERS("; 3586 } else { 3587 interface_descriptors << " DEFINE_RESULT_AND_PARAMETERS_NO_CONTEXT("; 3588 } 3589 interface_descriptors << return_types.size(); 3590 for (size_t i = kFirstNonContextParameter; 3591 i < builtin->parameter_names().size(); ++i) { 3592 Identifier* parameter = builtin->parameter_names()[i]; 3593 interface_descriptors << ", k" << CamelifyString(parameter->value); 3594 } 3595 interface_descriptors << ")\n"; 3596 3597 interface_descriptors << " DEFINE_RESULT_AND_PARAMETER_TYPES("; 3598 PrintCommaSeparatedList(interface_descriptors, return_types, 3599 MachineTypeString); 3600 for (size_t i = kFirstNonContextParameter; 3601 i < builtin->parameter_names().size(); ++i) { 3602 const Type* type = builtin->signature().parameter_types.types[i]; 3603 interface_descriptors << ", " << MachineTypeString(type); 3604 } 3605 interface_descriptors << ")\n"; 3606 3607 interface_descriptors << " DECLARE_DEFAULT_DESCRIPTOR(" 3608 << descriptor_name << ")\n"; 3609 interface_descriptors << "};\n\n"; 3610 } else { 3611 builtin_definitions << "TFJ(" << builtin->ExternalName(); 3612 if (builtin->IsVarArgsJavaScript()) { 3613 builtin_definitions << ", kDontAdaptArgumentsSentinel"; 3614 } else { 3615 DCHECK(builtin->IsFixedArgsJavaScript()); 3616 // FixedArg javascript builtins need to offer the parameter 3617 // count. 3618 int parameter_count = 3619 static_cast<int>(builtin->signature().ExplicitCount()); 3620 builtin_definitions << ", JSParameterCount(" << parameter_count 3621 << ")"; 3622 // And the receiver is explicitly declared. 3623 builtin_definitions << ", kReceiver"; 3624 for (size_t i = builtin->signature().implicit_count; 3625 i < builtin->parameter_names().size(); ++i) { 3626 Identifier* parameter = builtin->parameter_names()[i]; 3627 builtin_definitions << ", k" << CamelifyString(parameter->value); 3628 } 3629 } 3630 } 3631 builtin_definitions << ") \\\n"; 3632 } 3633 builtin_definitions << "\n"; 3634 3635 builtin_definitions 3636 << "#define TORQUE_FUNCTION_POINTER_TYPE_TO_BUILTIN_MAP(V) \\\n"; 3637 for (const BuiltinPointerType* type : 3638 TypeOracle::AllBuiltinPointerTypes()) { 3639 Builtin* example_builtin = 3640 Declarations::FindSomeInternalBuiltinWithType(type); 3641 if (!example_builtin) { 3642 CurrentSourcePosition::Scope current_source_position( 3643 SourcePosition{CurrentSourceFile::Get(), LineAndColumn::Invalid(), 3644 LineAndColumn::Invalid()}); 3645 ReportError("unable to find any builtin with type \"", *type, "\""); 3646 } 3647 builtin_definitions << " V(" << type->function_pointer_type_id() << "," 3648 << example_builtin->ExternalName() << ")\\\n"; 3649 } 3650 builtin_definitions << "\n"; 3651 } 3652 WriteFile(output_directory + "/" + builtin_definitions_file_name, 3653 builtin_definitions.str()); 3654 WriteFile(output_directory + "/" + interface_descriptors_file_name, 3655 interface_descriptors.str()); 3656} 3657 3658namespace { 3659 3660enum class FieldSectionType : uint32_t { 3661 kNoSection = 0, 3662 kWeakSection = 1 << 0, 3663 kStrongSection = 2 << 0, 3664 kScalarSection = 3 << 0 3665}; 3666 3667bool IsPointerSection(FieldSectionType type) { 3668 return type == FieldSectionType::kWeakSection || 3669 type == FieldSectionType::kStrongSection; 3670} 3671 3672using FieldSections = base::Flags<FieldSectionType>; 3673 3674std::string ToString(FieldSectionType type) { 3675 switch (type) { 3676 case FieldSectionType::kNoSection: 3677 return "NoSection"; 3678 case FieldSectionType::kWeakSection: 3679 return "WeakFields"; 3680 case FieldSectionType::kStrongSection: 3681 return "StrongFields"; 3682 case FieldSectionType::kScalarSection: 3683 return "ScalarFields"; 3684 } 3685 UNREACHABLE(); 3686} 3687 3688class FieldOffsetsGenerator { 3689 public: 3690 explicit FieldOffsetsGenerator(const ClassType* type) : type_(type) {} 3691 3692 virtual void WriteField(const Field& f, const std::string& size_string) = 0; 3693 virtual void WriteFieldOffsetGetter(const Field& f) = 0; 3694 virtual void WriteMarker(const std::string& marker) = 0; 3695 3696 virtual ~FieldOffsetsGenerator() { CHECK(is_finished_); } 3697 3698 void RecordOffsetFor(const Field& f) { 3699 CHECK(!is_finished_); 3700 UpdateSection(f); 3701 3702 // Emit kHeaderSize before any indexed field. 3703 if (f.index.has_value() && !header_size_emitted_) { 3704 WriteMarker("kHeaderSize"); 3705 header_size_emitted_ = true; 3706 } 3707 3708 // We don't know statically how much space an indexed field takes, so report 3709 // it as zero. 3710 std::string size_string = "0"; 3711 if (!f.index.has_value()) { 3712 size_t field_size; 3713 std::tie(field_size, size_string) = f.GetFieldSizeInformation(); 3714 } 3715 if (f.offset.has_value()) { 3716 WriteField(f, size_string); 3717 } else { 3718 WriteFieldOffsetGetter(f); 3719 } 3720 } 3721 3722 void Finish() { 3723 End(current_section_); 3724 if (!(completed_sections_ & FieldSectionType::kWeakSection)) { 3725 Begin(FieldSectionType::kWeakSection); 3726 End(FieldSectionType::kWeakSection); 3727 } 3728 if (!(completed_sections_ & FieldSectionType::kStrongSection)) { 3729 Begin(FieldSectionType::kStrongSection); 3730 End(FieldSectionType::kStrongSection); 3731 } 3732 is_finished_ = true; 3733 3734 // In the presence of indexed fields, we already emitted kHeaderSize before 3735 // the indexed field. 3736 if (!type_->IsShape() && !header_size_emitted_) { 3737 WriteMarker("kHeaderSize"); 3738 } 3739 if (!type_->IsAbstract() && type_->HasStaticSize()) { 3740 WriteMarker("kSize"); 3741 } 3742 } 3743 3744 protected: 3745 const ClassType* type_; 3746 3747 private: 3748 FieldSectionType GetSectionFor(const Field& f) { 3749 const Type* field_type = f.name_and_type.type; 3750 if (field_type == TypeOracle::GetVoidType()) { 3751 // Allow void type for marker constants of size zero. 3752 return current_section_; 3753 } 3754 StructType::Classification struct_contents = 3755 StructType::ClassificationFlag::kEmpty; 3756 if (auto field_as_struct = field_type->StructSupertype()) { 3757 struct_contents = (*field_as_struct)->ClassifyContents(); 3758 } 3759 if ((struct_contents & StructType::ClassificationFlag::kStrongTagged) && 3760 (struct_contents & StructType::ClassificationFlag::kWeakTagged)) { 3761 // It's okay for a struct to contain both strong and weak data. We'll just 3762 // treat the whole thing as weak. This is required for DescriptorEntry. 3763 struct_contents &= ~StructType::Classification( 3764 StructType::ClassificationFlag::kStrongTagged); 3765 } 3766 bool struct_contains_tagged_fields = 3767 (struct_contents & StructType::ClassificationFlag::kStrongTagged) || 3768 (struct_contents & StructType::ClassificationFlag::kWeakTagged); 3769 if (struct_contains_tagged_fields && 3770 (struct_contents & StructType::ClassificationFlag::kUntagged)) { 3771 // We can't declare what section a struct goes in if it has multiple 3772 // categories of data within. 3773 Error( 3774 "Classes do not support fields which are structs containing both " 3775 "tagged and untagged data.") 3776 .Position(f.pos); 3777 } 3778 if ((field_type->IsSubtypeOf(TypeOracle::GetStrongTaggedType()) || 3779 struct_contents == StructType::ClassificationFlag::kStrongTagged) && 3780 !f.custom_weak_marking) { 3781 return FieldSectionType::kStrongSection; 3782 } else if (field_type->IsSubtypeOf(TypeOracle::GetTaggedType()) || 3783 struct_contains_tagged_fields) { 3784 return FieldSectionType::kWeakSection; 3785 } else { 3786 return FieldSectionType::kScalarSection; 3787 } 3788 } 3789 void UpdateSection(const Field& f) { 3790 FieldSectionType type = GetSectionFor(f); 3791 if (current_section_ == type) return; 3792 if (IsPointerSection(type)) { 3793 if (completed_sections_ & type) { 3794 std::stringstream s; 3795 s << "cannot declare field " << f.name_and_type.name << " in class " 3796 << type_->name() << ", because section " << ToString(type) 3797 << " to which it belongs has already been finished."; 3798 Error(s.str()).Position(f.pos); 3799 } 3800 } 3801 End(current_section_); 3802 current_section_ = type; 3803 Begin(current_section_); 3804 } 3805 void Begin(FieldSectionType type) { 3806 DCHECK(type != FieldSectionType::kNoSection); 3807 if (!IsPointerSection(type)) return; 3808 WriteMarker("kStartOf" + ToString(type) + "Offset"); 3809 } 3810 void End(FieldSectionType type) { 3811 if (!IsPointerSection(type)) return; 3812 completed_sections_ |= type; 3813 WriteMarker("kEndOf" + ToString(type) + "Offset"); 3814 } 3815 3816 FieldSectionType current_section_ = FieldSectionType::kNoSection; 3817 FieldSections completed_sections_ = FieldSectionType::kNoSection; 3818 bool is_finished_ = false; 3819 bool header_size_emitted_ = false; 3820}; 3821 3822void GenerateClassExport(const ClassType* type, std::ostream& header, 3823 std::ostream& inl_header) { 3824 const ClassType* super = type->GetSuperClass(); 3825 std::string parent = "TorqueGenerated" + type->name() + "<" + type->name() + 3826 ", " + super->name() + ">"; 3827 header << "class " << type->name() << " : public " << parent << " {\n"; 3828 header << " public:\n"; 3829 if (type->ShouldGenerateBodyDescriptor()) { 3830 header << " class BodyDescriptor;\n"; 3831 } 3832 header << " TQ_OBJECT_CONSTRUCTORS(" << type->name() << ")\n"; 3833 header << "};\n\n"; 3834 inl_header << "TQ_OBJECT_CONSTRUCTORS_IMPL(" << type->name() << ")\n"; 3835} 3836 3837} // namespace 3838 3839void ImplementationVisitor::GenerateVisitorLists( 3840 const std::string& output_directory) { 3841 std::stringstream header; 3842 std::string file_name = "visitor-lists.h"; 3843 { 3844 IncludeGuardScope include_guard(header, file_name); 3845 3846 header << "#define TORQUE_INSTANCE_TYPE_TO_BODY_DESCRIPTOR_LIST(V)\\\n"; 3847 for (const ClassType* type : TypeOracle::GetClasses()) { 3848 if (type->ShouldGenerateBodyDescriptor() && type->OwnInstanceType()) { 3849 std::string type_name = 3850 CapifyStringWithUnderscores(type->name()) + "_TYPE"; 3851 header << "V(" << type_name << "," << type->name() << ")\\\n"; 3852 } 3853 } 3854 header << "\n"; 3855 3856 header << "#define TORQUE_DATA_ONLY_VISITOR_ID_LIST(V)\\\n"; 3857 for (const ClassType* type : TypeOracle::GetClasses()) { 3858 if (type->ShouldGenerateBodyDescriptor() && type->HasNoPointerSlots()) { 3859 header << "V(" << type->name() << ")\\\n"; 3860 } 3861 } 3862 header << "\n"; 3863 3864 header << "#define TORQUE_POINTER_VISITOR_ID_LIST(V)\\\n"; 3865 for (const ClassType* type : TypeOracle::GetClasses()) { 3866 if (type->ShouldGenerateBodyDescriptor() && !type->HasNoPointerSlots()) { 3867 header << "V(" << type->name() << ")\\\n"; 3868 } 3869 } 3870 header << "\n"; 3871 } 3872 const std::string output_header_path = output_directory + "/" + file_name; 3873 WriteFile(output_header_path, header.str()); 3874} 3875 3876void ImplementationVisitor::GenerateBitFields( 3877 const std::string& output_directory) { 3878 std::stringstream header; 3879 std::string file_name = "bit-fields.h"; 3880 { 3881 IncludeGuardScope include_guard(header, file_name); 3882 header << "#include \"src/base/bit-field.h\"\n\n"; 3883 NamespaceScope namespaces(header, {"v8", "internal"}); 3884 3885 for (const auto& type : TypeOracle::GetBitFieldStructTypes()) { 3886 bool all_single_bits = true; // Track whether every field is one bit. 3887 3888 header << "#define DEFINE_TORQUE_GENERATED_" 3889 << CapifyStringWithUnderscores(type->name()) << "() \\\n"; 3890 std::string type_name = type->GetConstexprGeneratedTypeName(); 3891 for (const auto& field : type->fields()) { 3892 const char* suffix = field.num_bits == 1 ? "Bit" : "Bits"; 3893 all_single_bits = all_single_bits && field.num_bits == 1; 3894 std::string field_type_name = 3895 field.name_and_type.type->GetConstexprGeneratedTypeName(); 3896 header << " using " << CamelifyString(field.name_and_type.name) 3897 << suffix << " = base::BitField<" << field_type_name << ", " 3898 << field.offset << ", " << field.num_bits << ", " << type_name 3899 << ">; \\\n"; 3900 } 3901 3902 // If every field is one bit, we can also generate a convenient enum. 3903 if (all_single_bits) { 3904 header << " enum Flag: " << type_name << " { \\\n"; 3905 header << " kNone = 0, \\\n"; 3906 for (const auto& field : type->fields()) { 3907 header << " k" << CamelifyString(field.name_and_type.name) << " = " 3908 << type_name << "{1} << " << field.offset << ", \\\n"; 3909 } 3910 header << " }; \\\n"; 3911 header << " using Flags = base::Flags<Flag>; \\\n"; 3912 header << " static constexpr int kFlagCount = " 3913 << type->fields().size() << "; \\\n"; 3914 } 3915 3916 header << "\n"; 3917 } 3918 } 3919 const std::string output_header_path = output_directory + "/" + file_name; 3920 WriteFile(output_header_path, header.str()); 3921} 3922 3923namespace { 3924 3925class ClassFieldOffsetGenerator : public FieldOffsetsGenerator { 3926 public: 3927 ClassFieldOffsetGenerator(std::ostream& header, std::ostream& inline_header, 3928 const ClassType* type, std::string gen_name, 3929 const ClassType* parent) 3930 : FieldOffsetsGenerator(type), 3931 hdr_(header), 3932 inl_(inline_header), 3933 previous_field_end_((parent && parent->IsShape()) ? "P::kSize" 3934 : "P::kHeaderSize"), 3935 gen_name_(gen_name) {} 3936 3937 void WriteField(const Field& f, const std::string& size_string) override { 3938 hdr_ << " // " << f.pos << "\n"; 3939 std::string field = "k" + CamelifyString(f.name_and_type.name) + "Offset"; 3940 std::string field_end = field + "End"; 3941 hdr_ << " static constexpr int " << field << " = " << previous_field_end_ 3942 << ";\n"; 3943 hdr_ << " static constexpr int " << field_end << " = " << field << " + " 3944 << size_string << " - 1;\n"; 3945 previous_field_end_ = field_end + " + 1"; 3946 } 3947 3948 void WriteFieldOffsetGetter(const Field& f) override { 3949 // A static constexpr int is more convenient than a getter if the offset is 3950 // known. 3951 DCHECK(!f.offset.has_value()); 3952 3953 std::string function_name = CamelifyString(f.name_and_type.name) + "Offset"; 3954 3955 std::vector<cpp::TemplateParameter> params = {cpp::TemplateParameter("D"), 3956 cpp::TemplateParameter("P")}; 3957 cpp::Class owner(std::move(params), gen_name_); 3958 3959 auto getter = cpp::Function::DefaultGetter("int", &owner, function_name); 3960 getter.PrintDeclaration(hdr_); 3961 getter.PrintDefinition(inl_, [&](std::ostream& stream) { 3962 // Item 1 in a flattened slice is the offset. 3963 stream << " return static_cast<int>(std::get<1>(" 3964 << Callable::PrefixNameForCCOutput(type_->GetSliceMacroName(f)) 3965 << "(*static_cast<const D*>(this))));\n"; 3966 }); 3967 } 3968 void WriteMarker(const std::string& marker) override { 3969 hdr_ << " static constexpr int " << marker << " = " << previous_field_end_ 3970 << ";\n"; 3971 } 3972 3973 private: 3974 std::ostream& hdr_; 3975 std::ostream& inl_; 3976 std::string previous_field_end_; 3977 std::string gen_name_; 3978}; 3979 3980class CppClassGenerator { 3981 public: 3982 CppClassGenerator(const ClassType* type, std::ostream& header, 3983 std::ostream& inl_header, std::ostream& impl) 3984 : type_(type), 3985 super_(type->GetSuperClass()), 3986 name_(type->name()), 3987 gen_name_("TorqueGenerated" + name_), 3988 gen_name_T_(gen_name_ + "<D, P>"), 3989 gen_name_I_(gen_name_ + "<" + name_ + ", " + super_->name() + ">"), 3990 hdr_(header), 3991 inl_(inl_header), 3992 impl_(impl) {} 3993 const std::string template_decl() const { 3994 return "template <class D, class P>"; 3995 } 3996 3997 void GenerateClass(); 3998 void GenerateCppObjectDefinitionAsserts(); 3999 4000 private: 4001 SourcePosition Position(); 4002 4003 void GenerateClassConstructors(); 4004 4005 // Generates getter and setter runtime member functions for the given class 4006 // field. Traverses depth-first through any nested struct fields to generate 4007 // accessors for them also; struct_fields represents the stack of currently 4008 // active struct fields. 4009 void GenerateFieldAccessors(const Field& class_field, 4010 std::vector<const Field*>& struct_fields); 4011 void EmitLoadFieldStatement(std::ostream& stream, const Field& class_field, 4012 std::vector<const Field*>& struct_fields); 4013 void EmitStoreFieldStatement(std::ostream& stream, const Field& class_field, 4014 std::vector<const Field*>& struct_fields); 4015 4016 void GenerateClassCasts(); 4017 4018 std::string GetFieldOffsetForAccessor(const Field& f); 4019 4020 // Gets the C++ type name that should be used in accessors for referring to 4021 // the value of a class field. 4022 std::string GetTypeNameForAccessor(const Field& f); 4023 4024 bool CanContainHeapObjects(const Type* t); 4025 4026 const ClassType* type_; 4027 const ClassType* super_; 4028 const std::string name_; 4029 const std::string gen_name_; 4030 const std::string gen_name_T_; 4031 const std::string gen_name_I_; 4032 std::ostream& hdr_; 4033 std::ostream& inl_; 4034 std::ostream& impl_; 4035}; 4036 4037base::Optional<std::vector<Field>> GetOrderedUniqueIndexFields( 4038 const ClassType& type) { 4039 std::vector<Field> result; 4040 std::set<std::string> index_names; 4041 for (const Field& field : type.ComputeAllFields()) { 4042 if (field.index) { 4043 auto name_and_type = ExtractSimpleFieldArraySize(type, field.index->expr); 4044 if (!name_and_type) { 4045 return base::nullopt; 4046 } 4047 index_names.insert(name_and_type->name); 4048 } 4049 } 4050 4051 for (const Field& field : type.ComputeAllFields()) { 4052 if (index_names.count(field.name_and_type.name) != 0) { 4053 result.push_back(field); 4054 } 4055 } 4056 4057 return result; 4058} 4059 4060void CppClassGenerator::GenerateClass() { 4061 // Is<name>_NonInline(HeapObject) 4062 if (!type_->IsShape()) { 4063 cpp::Function f("Is"s + name_ + "_NonInline"); 4064 f.SetDescription("Alias for HeapObject::Is"s + name_ + 4065 "() that avoids inlining."); 4066 f.SetExport(true); 4067 f.SetReturnType("bool"); 4068 f.AddParameter("HeapObject", "o"); 4069 4070 f.PrintDeclaration(hdr_); 4071 hdr_ << "\n"; 4072 f.PrintDefinition(impl_, [&](std::ostream& stream) { 4073 stream << " return o.Is" << name_ << "();\n"; 4074 }); 4075 } 4076 hdr_ << "// Definition " << Position() << "\n"; 4077 hdr_ << template_decl() << "\n"; 4078 hdr_ << "class " << gen_name_ << " : public P {\n"; 4079 hdr_ << " static_assert(\n" 4080 << " std::is_same<" << name_ << ", D>::value,\n" 4081 << " \"Use this class as direct base for " << name_ << ".\");\n"; 4082 hdr_ << " static_assert(\n" 4083 << " std::is_same<" << super_->name() << ", P>::value,\n" 4084 << " \"Pass in " << super_->name() 4085 << " as second template parameter for " << gen_name_ << ".\");\n\n"; 4086 hdr_ << " public: \n"; 4087 hdr_ << " using Super = P;\n"; 4088 hdr_ << " using TorqueGeneratedClass = " << gen_name_ << "<D,P>;\n\n"; 4089 if (!type_->ShouldExport() && !type_->IsExtern()) { 4090 hdr_ << " protected: // not extern or @export\n"; 4091 } 4092 for (const Field& f : type_->fields()) { 4093 CurrentSourcePosition::Scope scope(f.pos); 4094 std::vector<const Field*> struct_fields; 4095 GenerateFieldAccessors(f, struct_fields); 4096 } 4097 if (!type_->ShouldExport() && !type_->IsExtern()) { 4098 hdr_ << " public:\n"; 4099 } 4100 4101 GenerateClassCasts(); 4102 4103 std::vector<cpp::TemplateParameter> templateArgs = { 4104 cpp::TemplateParameter("D"), cpp::TemplateParameter("P")}; 4105 cpp::Class c(std::move(templateArgs), gen_name_); 4106 4107 if (type_->ShouldGeneratePrint()) { 4108 hdr_ << " DECL_PRINTER(" << name_ << ")\n\n"; 4109 } 4110 4111 if (type_->ShouldGenerateVerify()) { 4112 IfDefScope hdr_scope(hdr_, "VERIFY_HEAP"); 4113 // V8_EXPORT_PRIVATE void Verify(Isolate*); 4114 cpp::Function f(&c, name_ + "Verify"); 4115 f.SetExport(); 4116 f.SetReturnType("void"); 4117 f.AddParameter("Isolate*", "isolate"); 4118 f.PrintDeclaration(hdr_); 4119 4120 IfDefScope impl_scope(impl_, "VERIFY_HEAP"); 4121 impl_ << "\ntemplate <>\n"; 4122 impl_ << "void " << gen_name_I_ << "::" << name_ 4123 << "Verify(Isolate* isolate) {\n"; 4124 impl_ << " TorqueGeneratedClassVerifiers::" << name_ << "Verify(" << name_ 4125 << "::cast(*this), " 4126 "isolate);\n"; 4127 impl_ << "}\n\n"; 4128 impl_ << "\n"; 4129 } 4130 4131 hdr_ << "\n"; 4132 ClassFieldOffsetGenerator g(hdr_, inl_, type_, gen_name_, 4133 type_->GetSuperClass()); 4134 for (auto f : type_->fields()) { 4135 CurrentSourcePosition::Scope scope(f.pos); 4136 g.RecordOffsetFor(f); 4137 } 4138 g.Finish(); 4139 hdr_ << "\n"; 4140 4141 auto index_fields = GetOrderedUniqueIndexFields(*type_); 4142 4143 if (!index_fields.has_value()) { 4144 hdr_ << " // SizeFor implementations not generated due to complex array " 4145 "lengths\n\n"; 4146 4147 const Field& last_field = type_->LastField(); 4148 std::string last_field_item_size = 4149 std::get<1>(*SizeOf(last_field.name_and_type.type)); 4150 4151 // int AllocatedSize() const 4152 { 4153 cpp::Function f = 4154 cpp::Function::DefaultGetter("int", &c, "AllocatedSize"); 4155 f.PrintDeclaration(hdr_); 4156 4157 f.PrintDefinition(inl_, [&](std::ostream& stream) { 4158 stream << " auto slice = " 4159 << Callable::PrefixNameForCCOutput( 4160 type_->GetSliceMacroName(last_field)) 4161 << "(*static_cast<const D*>(this));\n"; 4162 stream << " return static_cast<int>(std::get<1>(slice)) + " 4163 << last_field_item_size 4164 << " * static_cast<int>(std::get<2>(slice));\n"; 4165 }); 4166 } 4167 } else if (type_->ShouldGenerateBodyDescriptor() || 4168 (!type_->IsAbstract() && 4169 !type_->IsSubtypeOf(TypeOracle::GetJSObjectType()))) { 4170 cpp::Function f(&c, "SizeFor"); 4171 f.SetReturnType("int32_t"); 4172 f.SetFlags(cpp::Function::kStatic | cpp::Function::kConstexpr | 4173 cpp::Function::kV8Inline); 4174 for (const Field& field : *index_fields) { 4175 f.AddParameter("int", field.name_and_type.name); 4176 } 4177 f.PrintInlineDefinition(hdr_, [&](std::ostream& stream) { 4178 if (index_fields->empty()) { 4179 stream << " DCHECK(kHeaderSize == kSize && kHeaderSize == " 4180 << *type_->size().SingleValue() << ");\n"; 4181 } 4182 stream << " int32_t size = kHeaderSize;\n"; 4183 for (const Field& field : type_->ComputeAllFields()) { 4184 if (field.index) { 4185 auto index_name_and_type = 4186 *ExtractSimpleFieldArraySize(*type_, field.index->expr); 4187 stream << " size += " << index_name_and_type.name << " * " 4188 << std::get<0>(field.GetFieldSizeInformation()) << ";\n"; 4189 } 4190 } 4191 if (type_->size().Alignment() < TargetArchitecture::TaggedSize()) { 4192 stream << " size = OBJECT_POINTER_ALIGN(size);\n"; 4193 } 4194 stream << " return size;\n"; 4195 }); 4196 4197 // V8_INLINE int32_t AllocatedSize() const 4198 { 4199 cpp::Function allocated_size_f = 4200 cpp::Function::DefaultGetter("int32_t", &c, "AllocatedSize"); 4201 allocated_size_f.SetFlag(cpp::Function::kV8Inline); 4202 allocated_size_f.PrintInlineDefinition(hdr_, [&](std::ostream& stream) { 4203 stream << " return SizeFor("; 4204 bool first = true; 4205 for (auto field : *index_fields) { 4206 if (!first) stream << ", "; 4207 stream << "this->" << field.name_and_type.name << "()"; 4208 first = false; 4209 } 4210 stream << ");\n"; 4211 }); 4212 } 4213 } 4214 4215 hdr_ << " friend class Factory;\n\n"; 4216 4217 GenerateClassConstructors(); 4218 4219 hdr_ << "};\n\n"; 4220 4221 if (type_->ShouldGenerateFullClassDefinition()) { 4222 // If this class extends from another class which is defined in the same tq 4223 // file, and that other class doesn't generate a full class definition, then 4224 // the resulting .inc file would be uncompilable due to ordering 4225 // requirements: the generated file must go before the hand-written 4226 // definition of the base class, but it must also go after that same 4227 // hand-written definition. 4228 base::Optional<const ClassType*> parent = type_->parent()->ClassSupertype(); 4229 while (parent) { 4230 if ((*parent)->ShouldGenerateCppClassDefinitions() && 4231 !(*parent)->ShouldGenerateFullClassDefinition() && 4232 (*parent)->AttributedToFile() == type_->AttributedToFile()) { 4233 Error("Exported ", *type_, 4234 " cannot be in the same file as its parent extern ", **parent); 4235 } 4236 parent = (*parent)->parent()->ClassSupertype(); 4237 } 4238 4239 GenerateClassExport(type_, hdr_, inl_); 4240 } 4241} 4242 4243void CppClassGenerator::GenerateCppObjectDefinitionAsserts() { 4244 hdr_ << "// Definition " << Position() << "\n" 4245 << template_decl() << "\n" 4246 << "class " << gen_name_ << "Asserts {\n"; 4247 4248 ClassFieldOffsetGenerator g(hdr_, inl_, type_, gen_name_, 4249 type_->GetSuperClass()); 4250 for (auto f : type_->fields()) { 4251 CurrentSourcePosition::Scope scope(f.pos); 4252 g.RecordOffsetFor(f); 4253 } 4254 g.Finish(); 4255 hdr_ << "\n"; 4256 4257 for (auto f : type_->fields()) { 4258 std::string field = "k" + CamelifyString(f.name_and_type.name) + "Offset"; 4259 std::string type = f.name_and_type.type->SimpleName(); 4260 hdr_ << " static_assert(" << field << " == D::" << field << ",\n" 4261 << " \"Values of " << name_ << "::" << field 4262 << " defined in Torque and C++ do not match\");\n" 4263 << " static_assert(StaticStringsEqual(\"" << type << "\", D::k" 4264 << CamelifyString(f.name_and_type.name) << "TqFieldType),\n" 4265 << " \"Types of " << name_ << "::" << field 4266 << " specified in Torque and C++ do not match\");\n"; 4267 } 4268 hdr_ << " static_assert(kSize == D::kSize);\n"; 4269 4270 hdr_ << "};\n\n"; 4271} 4272 4273void CppClassGenerator::GenerateClassCasts() { 4274 cpp::Class owner({cpp::TemplateParameter("D"), cpp::TemplateParameter("P")}, 4275 gen_name_); 4276 cpp::Function f(&owner, "cast"); 4277 f.SetFlags(cpp::Function::kV8Inline | cpp::Function::kStatic); 4278 f.SetReturnType("D"); 4279 f.AddParameter("Object", "object"); 4280 4281 // V8_INLINE static D cast(Object) 4282 f.PrintDeclaration(hdr_); 4283 f.PrintDefinition(inl_, [](std::ostream& stream) { 4284 stream << " return D(object.ptr());\n"; 4285 }); 4286 // V8_INLINE static D unchecked_cast(Object) 4287 f.SetName("unchecked_cast"); 4288 f.PrintInlineDefinition(hdr_, [](std::ostream& stream) { 4289 stream << " return bit_cast<D>(object);\n"; 4290 }); 4291} 4292 4293SourcePosition CppClassGenerator::Position() { return type_->GetPosition(); } 4294 4295void CppClassGenerator::GenerateClassConstructors() { 4296 const ClassType* typecheck_type = type_; 4297 while (typecheck_type->IsShape()) { 4298 typecheck_type = typecheck_type->GetSuperClass(); 4299 4300 // Shapes have already been checked earlier to inherit from JSObject, so we 4301 // should have found an appropriate type. 4302 DCHECK(typecheck_type); 4303 } 4304 4305 hdr_ << " template <class DAlias = D>\n"; 4306 hdr_ << " constexpr " << gen_name_ << "() : P() {\n"; 4307 hdr_ << " static_assert(\n"; 4308 hdr_ << " std::is_base_of<" << gen_name_ << ", DAlias>::value,\n"; 4309 hdr_ << " \"class " << gen_name_ 4310 << " should be used as direct base for " << name_ << ".\");\n"; 4311 hdr_ << " }\n\n"; 4312 4313 hdr_ << " protected:\n"; 4314 hdr_ << " inline explicit " << gen_name_ << "(Address ptr);\n"; 4315 hdr_ << " // Special-purpose constructor for subclasses that have fast " 4316 "paths where\n"; 4317 hdr_ << " // their ptr() is a Smi.\n"; 4318 hdr_ << " inline explicit " << gen_name_ 4319 << "(Address ptr, HeapObject::AllowInlineSmiStorage allow_smi);\n"; 4320 4321 inl_ << "template<class D, class P>\n"; 4322 inl_ << "inline " << gen_name_T_ << "::" << gen_name_ << "(Address ptr)\n"; 4323 inl_ << " : P(ptr) {\n"; 4324 inl_ << " SLOW_DCHECK(Is" << typecheck_type->name() 4325 << "_NonInline(*this));\n"; 4326 inl_ << "}\n"; 4327 4328 inl_ << "template<class D, class P>\n"; 4329 inl_ << "inline " << gen_name_T_ << "::" << gen_name_ 4330 << "(Address ptr, HeapObject::AllowInlineSmiStorage allow_smi)\n"; 4331 inl_ << " : P(ptr, allow_smi) {\n"; 4332 inl_ << " SLOW_DCHECK(" 4333 << "(allow_smi == HeapObject::AllowInlineSmiStorage::kAllowBeingASmi" 4334 " && this->IsSmi()) || Is" 4335 << typecheck_type->name() << "_NonInline(*this));\n"; 4336 inl_ << "}\n"; 4337} 4338 4339namespace { 4340std::string GenerateRuntimeTypeCheck(const Type* type, 4341 const std::string& value) { 4342 bool maybe_object = !type->IsSubtypeOf(TypeOracle::GetStrongTaggedType()); 4343 std::stringstream type_check; 4344 bool at_start = true; 4345 // If weak pointers are allowed, then start by checking for a cleared value. 4346 if (maybe_object) { 4347 type_check << value << ".IsCleared()"; 4348 at_start = false; 4349 } 4350 for (const TypeChecker& runtime_type : type->GetTypeCheckers()) { 4351 if (!at_start) type_check << " || "; 4352 at_start = false; 4353 if (maybe_object) { 4354 bool strong = runtime_type.weak_ref_to.empty(); 4355 if (strong && runtime_type.type == WEAK_HEAP_OBJECT) { 4356 // Rather than a generic Weak<T>, this is the basic type WeakHeapObject. 4357 // We can't validate anything more about the type of the object pointed 4358 // to, so just check that it's weak. 4359 type_check << value << ".IsWeak()"; 4360 } else { 4361 type_check << "(" << (strong ? "!" : "") << value << ".IsWeak() && " 4362 << value << ".GetHeapObjectOrSmi().Is" 4363 << (strong ? runtime_type.type : runtime_type.weak_ref_to) 4364 << "())"; 4365 } 4366 } else { 4367 type_check << value << ".Is" << runtime_type.type << "()"; 4368 } 4369 } 4370 return type_check.str(); 4371} 4372 4373void GenerateBoundsDCheck(std::ostream& os, const std::string& index, 4374 const ClassType* type, const Field& f) { 4375 os << " DCHECK_GE(" << index << ", 0);\n"; 4376 std::string length_expression; 4377 if (base::Optional<NameAndType> array_length = 4378 ExtractSimpleFieldArraySize(*type, f.index->expr)) { 4379 length_expression = "this ->" + array_length->name + "()"; 4380 } else { 4381 // The length is element 2 in the flattened field slice. 4382 length_expression = 4383 "static_cast<int>(std::get<2>(" + 4384 Callable::PrefixNameForCCOutput(type->GetSliceMacroName(f)) + 4385 "(*static_cast<const D*>(this))))"; 4386 } 4387 os << " DCHECK_LT(" << index << ", " << length_expression << ");\n"; 4388} 4389 4390bool CanGenerateFieldAccessors(const Type* field_type) { 4391 // float64_or_hole should be treated like float64. For now, we don't need it. 4392 // TODO(v8:10391) Generate accessors for external pointers. 4393 return field_type != TypeOracle::GetVoidType() && 4394 field_type != TypeOracle::GetFloat64OrHoleType() && 4395 !field_type->IsSubtypeOf(TypeOracle::GetExternalPointerType()); 4396} 4397} // namespace 4398 4399// TODO(sigurds): Keep in sync with DECL_ACCESSORS and ACCESSORS macro. 4400void CppClassGenerator::GenerateFieldAccessors( 4401 const Field& class_field, std::vector<const Field*>& struct_fields) { 4402 const Field& innermost_field = 4403 struct_fields.empty() ? class_field : *struct_fields.back(); 4404 const Type* field_type = innermost_field.name_and_type.type; 4405 if (!CanGenerateFieldAccessors(field_type)) return; 4406 4407 if (const StructType* struct_type = StructType::DynamicCast(field_type)) { 4408 struct_fields.resize(struct_fields.size() + 1); 4409 for (const Field& struct_field : struct_type->fields()) { 4410 struct_fields[struct_fields.size() - 1] = &struct_field; 4411 GenerateFieldAccessors(class_field, struct_fields); 4412 } 4413 struct_fields.resize(struct_fields.size() - 1); 4414 return; 4415 } 4416 4417 bool indexed = class_field.index && !class_field.index->optional; 4418 std::string type_name = GetTypeNameForAccessor(innermost_field); 4419 bool can_contain_heap_objects = CanContainHeapObjects(field_type); 4420 4421 // Assemble an accessor name by accumulating together all of the nested field 4422 // names. 4423 std::string name = class_field.name_and_type.name; 4424 for (const Field* nested_struct_field : struct_fields) { 4425 name += "_" + nested_struct_field->name_and_type.name; 4426 } 4427 4428 // Generate declarations in header. 4429 if (can_contain_heap_objects && !field_type->IsClassType() && 4430 !field_type->IsStructType() && 4431 field_type != TypeOracle::GetObjectType()) { 4432 hdr_ << " // Torque type: " << field_type->ToString() << "\n"; 4433 } 4434 4435 std::vector<cpp::TemplateParameter> templateParameters = { 4436 cpp::TemplateParameter("D"), cpp::TemplateParameter("P")}; 4437 cpp::Class owner(std::move(templateParameters), gen_name_); 4438 4439 // getter 4440 { 4441 auto getter = cpp::Function::DefaultGetter(type_name, &owner, name); 4442 if (indexed) { 4443 getter.AddParameter("int", "i"); 4444 } 4445 const char* tag_argument; 4446 switch (class_field.read_synchronization) { 4447 case FieldSynchronization::kNone: 4448 tag_argument = ""; 4449 break; 4450 case FieldSynchronization::kRelaxed: 4451 getter.AddParameter("RelaxedLoadTag"); 4452 tag_argument = ", kRelaxedLoad"; 4453 break; 4454 case FieldSynchronization::kAcquireRelease: 4455 getter.AddParameter("AcquireLoadTag"); 4456 tag_argument = ", kAcquireLoad"; 4457 break; 4458 } 4459 4460 getter.PrintDeclaration(hdr_); 4461 4462 // For tagged data, generate the extra getter that derives an 4463 // PtrComprCageBase from the current object's pointer. 4464 if (can_contain_heap_objects) { 4465 getter.PrintDefinition(inl_, [&](auto& stream) { 4466 stream 4467 << " PtrComprCageBase cage_base = GetPtrComprCageBase(*this);\n"; 4468 stream << " return " << gen_name_ << "::" << name << "(cage_base" 4469 << (indexed ? ", i" : "") << tag_argument << ");\n"; 4470 }); 4471 4472 getter.InsertParameter(0, "PtrComprCageBase", "cage_base"); 4473 getter.PrintDeclaration(hdr_); 4474 } 4475 4476 getter.PrintDefinition(inl_, [&](auto& stream) { 4477 stream << " " << type_name << " value;\n"; 4478 EmitLoadFieldStatement(stream, class_field, struct_fields); 4479 stream << " return value;\n"; 4480 }); 4481 } 4482 4483 // setter 4484 { 4485 auto setter = cpp::Function::DefaultSetter( 4486 &owner, std::string("set_") + name, type_name, "value"); 4487 if (indexed) { 4488 setter.InsertParameter(0, "int", "i"); 4489 } 4490 switch (class_field.write_synchronization) { 4491 case FieldSynchronization::kNone: 4492 break; 4493 case FieldSynchronization::kRelaxed: 4494 setter.AddParameter("RelaxedStoreTag"); 4495 break; 4496 case FieldSynchronization::kAcquireRelease: 4497 setter.AddParameter("ReleaseStoreTag"); 4498 break; 4499 } 4500 if (can_contain_heap_objects) { 4501 setter.AddParameter("WriteBarrierMode", "mode", "UPDATE_WRITE_BARRIER"); 4502 } 4503 setter.PrintDeclaration(hdr_); 4504 4505 setter.PrintDefinition(inl_, [&](auto& stream) { 4506 EmitStoreFieldStatement(stream, class_field, struct_fields); 4507 }); 4508 } 4509 4510 hdr_ << "\n"; 4511} 4512 4513std::string CppClassGenerator::GetFieldOffsetForAccessor(const Field& f) { 4514 if (f.offset.has_value()) { 4515 return "k" + CamelifyString(f.name_and_type.name) + "Offset"; 4516 } 4517 return CamelifyString(f.name_and_type.name) + "Offset()"; 4518} 4519 4520std::string CppClassGenerator::GetTypeNameForAccessor(const Field& f) { 4521 const Type* field_type = f.name_and_type.type; 4522 if (!field_type->IsSubtypeOf(TypeOracle::GetTaggedType())) { 4523 const Type* constexpr_version = field_type->ConstexprVersion(); 4524 if (!constexpr_version) { 4525 Error("Field accessor for ", type_->name(), ":: ", f.name_and_type.name, 4526 " cannot be generated because its type ", *field_type, 4527 " is neither a subclass of Object nor does the type have a " 4528 "constexpr " 4529 "version.") 4530 .Position(f.pos) 4531 .Throw(); 4532 } 4533 return constexpr_version->GetGeneratedTypeName(); 4534 } 4535 if (field_type->IsSubtypeOf(TypeOracle::GetSmiType())) { 4536 // Follow the convention to create Smi accessors with type int. 4537 return "int"; 4538 } 4539 return field_type->UnhandlifiedCppTypeName(); 4540} 4541 4542bool CppClassGenerator::CanContainHeapObjects(const Type* t) { 4543 return t->IsSubtypeOf(TypeOracle::GetTaggedType()) && 4544 !t->IsSubtypeOf(TypeOracle::GetSmiType()); 4545} 4546 4547void CppClassGenerator::EmitLoadFieldStatement( 4548 std::ostream& stream, const Field& class_field, 4549 std::vector<const Field*>& struct_fields) { 4550 const Field& innermost_field = 4551 struct_fields.empty() ? class_field : *struct_fields.back(); 4552 const Type* field_type = innermost_field.name_and_type.type; 4553 std::string type_name = GetTypeNameForAccessor(innermost_field); 4554 const std::string class_field_size = 4555 std::get<1>(class_field.GetFieldSizeInformation()); 4556 4557 // field_offset contains both the offset from the beginning of the object to 4558 // the class field and the combined offsets of any nested struct fields 4559 // within, but not the index adjustment. 4560 std::string field_offset = GetFieldOffsetForAccessor(class_field); 4561 for (const Field* nested_struct_field : struct_fields) { 4562 field_offset += " + " + std::to_string(*nested_struct_field->offset); 4563 } 4564 4565 std::string offset = field_offset; 4566 if (class_field.index) { 4567 const char* index = class_field.index->optional ? "0" : "i"; 4568 GenerateBoundsDCheck(stream, index, type_, class_field); 4569 stream << " int offset = " << field_offset << " + " << index << " * " 4570 << class_field_size << ";\n"; 4571 offset = "offset"; 4572 } 4573 4574 stream << " value = "; 4575 4576 if (!field_type->IsSubtypeOf(TypeOracle::GetTaggedType())) { 4577 if (class_field.read_synchronization == 4578 FieldSynchronization::kAcquireRelease) { 4579 ReportError("Torque doesn't support @cppAcquireRead on untagged data"); 4580 } else if (class_field.read_synchronization == 4581 FieldSynchronization::kRelaxed) { 4582 ReportError("Torque doesn't support @cppRelaxedRead on untagged data"); 4583 } 4584 stream << "this->template ReadField<" << type_name << ">(" << offset 4585 << ");\n"; 4586 } else { 4587 const char* load; 4588 switch (class_field.read_synchronization) { 4589 case FieldSynchronization::kNone: 4590 load = "load"; 4591 break; 4592 case FieldSynchronization::kRelaxed: 4593 load = "Relaxed_Load"; 4594 break; 4595 case FieldSynchronization::kAcquireRelease: 4596 load = "Acquire_Load"; 4597 break; 4598 } 4599 bool is_smi = field_type->IsSubtypeOf(TypeOracle::GetSmiType()); 4600 const std::string load_type = is_smi ? "Smi" : type_name; 4601 const char* postfix = is_smi ? ".value()" : ""; 4602 const char* optional_cage_base = is_smi ? "" : "cage_base, "; 4603 4604 stream << "TaggedField<" << load_type << ">::" << load << "(" 4605 << optional_cage_base << "*this, " << offset << ")" << postfix 4606 << ";\n"; 4607 } 4608 4609 if (CanContainHeapObjects(field_type)) { 4610 stream << " DCHECK(" << GenerateRuntimeTypeCheck(field_type, "value") 4611 << ");\n"; 4612 } 4613} 4614 4615void CppClassGenerator::EmitStoreFieldStatement( 4616 std::ostream& stream, const Field& class_field, 4617 std::vector<const Field*>& struct_fields) { 4618 const Field& innermost_field = 4619 struct_fields.empty() ? class_field : *struct_fields.back(); 4620 const Type* field_type = innermost_field.name_and_type.type; 4621 std::string type_name = GetTypeNameForAccessor(innermost_field); 4622 const std::string class_field_size = 4623 std::get<1>(class_field.GetFieldSizeInformation()); 4624 4625 // field_offset contains both the offset from the beginning of the object to 4626 // the class field and the combined offsets of any nested struct fields 4627 // within, but not the index adjustment. 4628 std::string field_offset = GetFieldOffsetForAccessor(class_field); 4629 for (const Field* nested_struct_field : struct_fields) { 4630 field_offset += " + " + std::to_string(*nested_struct_field->offset); 4631 } 4632 4633 std::string offset = field_offset; 4634 if (class_field.index) { 4635 const char* index = class_field.index->optional ? "0" : "i"; 4636 GenerateBoundsDCheck(stream, index, type_, class_field); 4637 stream << " int offset = " << field_offset << " + " << index << " * " 4638 << class_field_size << ";\n"; 4639 offset = "offset"; 4640 } 4641 4642 if (!field_type->IsSubtypeOf(TypeOracle::GetTaggedType())) { 4643 stream << " this->template WriteField<" << type_name << ">(" << offset 4644 << ", value);\n"; 4645 } else { 4646 bool strong_pointer = field_type->IsSubtypeOf(TypeOracle::GetObjectType()); 4647 bool is_smi = field_type->IsSubtypeOf(TypeOracle::GetSmiType()); 4648 const char* write_macro; 4649 if (!strong_pointer) { 4650 if (class_field.write_synchronization == 4651 FieldSynchronization::kAcquireRelease) { 4652 ReportError("Torque doesn't support @releaseWrite on weak fields"); 4653 } 4654 write_macro = "RELAXED_WRITE_WEAK_FIELD"; 4655 } else { 4656 switch (class_field.write_synchronization) { 4657 case FieldSynchronization::kNone: 4658 write_macro = "WRITE_FIELD"; 4659 break; 4660 case FieldSynchronization::kRelaxed: 4661 write_macro = "RELAXED_WRITE_FIELD"; 4662 break; 4663 case FieldSynchronization::kAcquireRelease: 4664 write_macro = "RELEASE_WRITE_FIELD"; 4665 break; 4666 } 4667 } 4668 const std::string value_to_write = is_smi ? "Smi::FromInt(value)" : "value"; 4669 4670 if (!is_smi) { 4671 stream << " SLOW_DCHECK(" 4672 << GenerateRuntimeTypeCheck(field_type, "value") << ");\n"; 4673 } 4674 stream << " " << write_macro << "(*this, " << offset << ", " 4675 << value_to_write << ");\n"; 4676 if (!is_smi) { 4677 const char* write_barrier = strong_pointer 4678 ? "CONDITIONAL_WRITE_BARRIER" 4679 : "CONDITIONAL_WEAK_WRITE_BARRIER"; 4680 stream << " " << write_barrier << "(*this, " << offset 4681 << ", value, mode);\n"; 4682 } 4683 } 4684} 4685 4686void GenerateStructLayoutDescription(std::ostream& header, 4687 const StructType* type) { 4688 header << "struct TorqueGenerated" << CamelifyString(type->name()) 4689 << "Offsets {\n"; 4690 for (const Field& field : type->fields()) { 4691 header << " static constexpr int k" 4692 << CamelifyString(field.name_and_type.name) 4693 << "Offset = " << *field.offset << ";\n"; 4694 } 4695 header << " static constexpr int kSize = " << type->PackedSize() << ";\n"; 4696 header << "};\n\n"; 4697} 4698 4699} // namespace 4700 4701void ImplementationVisitor::GenerateClassDefinitions( 4702 const std::string& output_directory) { 4703 std::stringstream factory_header; 4704 std::stringstream factory_impl; 4705 std::string factory_basename = "factory"; 4706 4707 std::stringstream forward_declarations; 4708 std::string forward_declarations_filename = "class-forward-declarations.h"; 4709 4710 { 4711 factory_impl << "#include \"src/heap/factory-base.h\"\n"; 4712 factory_impl << "#include \"src/heap/factory-base-inl.h\"\n"; 4713 factory_impl << "#include \"src/heap/heap.h\"\n"; 4714 factory_impl << "#include \"src/heap/heap-inl.h\"\n"; 4715 factory_impl << "#include \"src/execution/isolate.h\"\n"; 4716 factory_impl << "#include " 4717 "\"src/objects/all-objects-inl.h\"\n\n"; 4718 NamespaceScope factory_impl_namespaces(factory_impl, {"v8", "internal"}); 4719 factory_impl << "\n"; 4720 4721 IncludeGuardScope include_guard(forward_declarations, 4722 forward_declarations_filename); 4723 NamespaceScope forward_declarations_namespaces(forward_declarations, 4724 {"v8", "internal"}); 4725 4726 std::set<const StructType*, TypeLess> structs_used_in_classes; 4727 4728 // Emit forward declarations. 4729 for (const ClassType* type : TypeOracle::GetClasses()) { 4730 CurrentSourcePosition::Scope position_activator(type->GetPosition()); 4731 auto& streams = GlobalContext::GeneratedPerFile(type->AttributedToFile()); 4732 std::ostream& header = streams.class_definition_headerfile; 4733 std::string name = type->ShouldGenerateCppClassDefinitions() 4734 ? type->name() 4735 : type->GetGeneratedTNodeTypeName(); 4736 if (type->ShouldGenerateCppClassDefinitions()) { 4737 header << "class " << name << ";\n"; 4738 } 4739 forward_declarations << "class " << name << ";\n"; 4740 } 4741 4742 for (const ClassType* type : TypeOracle::GetClasses()) { 4743 CurrentSourcePosition::Scope position_activator(type->GetPosition()); 4744 auto& streams = GlobalContext::GeneratedPerFile(type->AttributedToFile()); 4745 std::ostream& header = streams.class_definition_headerfile; 4746 std::ostream& inline_header = streams.class_definition_inline_headerfile; 4747 std::ostream& implementation = streams.class_definition_ccfile; 4748 4749 if (type->ShouldGenerateCppClassDefinitions()) { 4750 CppClassGenerator g(type, header, inline_header, implementation); 4751 g.GenerateClass(); 4752 } else if (type->ShouldGenerateCppObjectDefinitionAsserts()) { 4753 CppClassGenerator g(type, header, inline_header, implementation); 4754 g.GenerateCppObjectDefinitionAsserts(); 4755 } 4756 for (const Field& f : type->fields()) { 4757 const Type* field_type = f.name_and_type.type; 4758 if (auto field_as_struct = field_type->StructSupertype()) { 4759 structs_used_in_classes.insert(*field_as_struct); 4760 } 4761 } 4762 if (type->ShouldGenerateFactoryFunction()) { 4763 std::string return_type = type->HandlifiedCppTypeName(); 4764 std::string function_name = "New" + type->name(); 4765 std::stringstream parameters; 4766 for (const Field& f : type->ComputeAllFields()) { 4767 if (f.name_and_type.name == "map") continue; 4768 if (!f.index) { 4769 std::string type_string = 4770 f.name_and_type.type->HandlifiedCppTypeName(); 4771 parameters << type_string << " " << f.name_and_type.name << ", "; 4772 } 4773 } 4774 parameters << "AllocationType allocation_type"; 4775 4776 factory_header << return_type << " " << function_name << "(" 4777 << parameters.str() << ");\n"; 4778 factory_impl << "template <typename Impl>\n"; 4779 factory_impl << return_type 4780 << " TorqueGeneratedFactory<Impl>::" << function_name 4781 << "(" << parameters.str() << ") {\n"; 4782 4783 factory_impl << " int size = "; 4784 const ClassType* super = type->GetSuperClass(); 4785 std::string gen_name = "TorqueGenerated" + type->name(); 4786 std::string gen_name_T = 4787 gen_name + "<" + type->name() + ", " + super->name() + ">"; 4788 factory_impl << gen_name_T << "::SizeFor("; 4789 4790 bool first = true; 4791 auto index_fields = GetOrderedUniqueIndexFields(*type); 4792 CHECK(index_fields.has_value()); 4793 for (auto index_field : *index_fields) { 4794 if (!first) { 4795 factory_impl << ", "; 4796 } 4797 factory_impl << index_field.name_and_type.name; 4798 first = false; 4799 } 4800 4801 factory_impl << ");\n"; 4802 factory_impl << " Map map = factory()->read_only_roots()." 4803 << SnakeifyString(type->name()) << "_map();"; 4804 factory_impl << " HeapObject raw_object =\n"; 4805 factory_impl << " factory()->AllocateRawWithImmortalMap(size, " 4806 "allocation_type, map);\n"; 4807 factory_impl << " " << type->UnhandlifiedCppTypeName() 4808 << " result = " << type->UnhandlifiedCppTypeName() 4809 << "::cast(raw_object);\n"; 4810 factory_impl << " DisallowGarbageCollection no_gc;"; 4811 factory_impl << " WriteBarrierMode write_barrier_mode =\n" 4812 << " allocation_type == AllocationType::kYoung\n" 4813 << " ? SKIP_WRITE_BARRIER : UPDATE_WRITE_BARRIER;\n" 4814 << " USE(write_barrier_mode);\n"; 4815 4816 for (const Field& f : type->ComputeAllFields()) { 4817 if (f.name_and_type.name == "map") continue; 4818 if (!f.index) { 4819 factory_impl << " result.TorqueGeneratedClass::set_" 4820 << SnakeifyString(f.name_and_type.name) << "("; 4821 if (f.name_and_type.type->IsSubtypeOf( 4822 TypeOracle::GetTaggedType()) && 4823 !f.name_and_type.type->IsSubtypeOf(TypeOracle::GetSmiType())) { 4824 factory_impl << "*" << f.name_and_type.name 4825 << ", write_barrier_mode"; 4826 } else { 4827 factory_impl << f.name_and_type.name; 4828 } 4829 factory_impl << ");\n"; 4830 } 4831 } 4832 4833 factory_impl << " return handle(result, factory()->isolate());\n"; 4834 factory_impl << "}\n\n"; 4835 4836 factory_impl << "template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) " 4837 << return_type 4838 << "TorqueGeneratedFactory<Factory>::" << function_name 4839 << "(" << parameters.str() << ");\n"; 4840 factory_impl << "template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) " 4841 << return_type << "TorqueGeneratedFactory<LocalFactory>::" 4842 << function_name << "(" << parameters.str() << ");\n"; 4843 4844 factory_impl << "\n\n"; 4845 } 4846 } 4847 4848 for (const StructType* type : structs_used_in_classes) { 4849 CurrentSourcePosition::Scope position_activator(type->GetPosition()); 4850 std::ostream& header = 4851 GlobalContext::GeneratedPerFile(type->GetPosition().source) 4852 .class_definition_headerfile; 4853 if (type != TypeOracle::GetFloat64OrHoleType()) { 4854 GenerateStructLayoutDescription(header, type); 4855 } 4856 } 4857 } 4858 WriteFile(output_directory + "/" + factory_basename + ".inc", 4859 factory_header.str()); 4860 WriteFile(output_directory + "/" + factory_basename + ".cc", 4861 factory_impl.str()); 4862 WriteFile(output_directory + "/" + forward_declarations_filename, 4863 forward_declarations.str()); 4864} 4865 4866namespace { 4867void GeneratePrintDefinitionsForClass(std::ostream& impl, const ClassType* type, 4868 const std::string& gen_name, 4869 const std::string& gen_name_T, 4870 const std::string template_params) { 4871 impl << template_params << "\n"; 4872 impl << "void " << gen_name_T << "::" << type->name() 4873 << "Print(std::ostream& os) {\n"; 4874 impl << " this->PrintHeader(os, \"" << type->name() << "\");\n"; 4875 auto hierarchy = type->GetHierarchy(); 4876 std::map<std::string, const AggregateType*> field_names; 4877 for (const AggregateType* aggregate_type : hierarchy) { 4878 for (const Field& f : aggregate_type->fields()) { 4879 if (f.name_and_type.name == "map" || f.index.has_value() || 4880 !CanGenerateFieldAccessors(f.name_and_type.type)) { 4881 continue; 4882 } 4883 std::string getter = f.name_and_type.name; 4884 if (aggregate_type != type) { 4885 // We must call getters directly on the class that provided them, 4886 // because a subclass could have hidden them. 4887 getter = aggregate_type->name() + "::TorqueGeneratedClass::" + getter; 4888 } 4889 if (f.name_and_type.type->IsSubtypeOf(TypeOracle::GetSmiType()) || 4890 !f.name_and_type.type->IsSubtypeOf(TypeOracle::GetTaggedType())) { 4891 impl << " os << \"\\n - " << f.name_and_type.name << ": \" << "; 4892 if (f.name_and_type.type->StructSupertype()) { 4893 // TODO(turbofan): Print struct fields too. 4894 impl << "\" <struct field printing still unimplemented>\";\n"; 4895 } else { 4896 impl << "this->" << getter; 4897 switch (f.read_synchronization) { 4898 case FieldSynchronization::kNone: 4899 impl << "();\n"; 4900 break; 4901 case FieldSynchronization::kRelaxed: 4902 impl << "(kRelaxedLoad);\n"; 4903 break; 4904 case FieldSynchronization::kAcquireRelease: 4905 impl << "(kAcquireLoad);\n"; 4906 break; 4907 } 4908 } 4909 } else { 4910 impl << " os << \"\\n - " << f.name_and_type.name << ": \" << " 4911 << "Brief(this->" << getter; 4912 switch (f.read_synchronization) { 4913 case FieldSynchronization::kNone: 4914 impl << "());\n"; 4915 break; 4916 case FieldSynchronization::kRelaxed: 4917 impl << "(kRelaxedLoad));\n"; 4918 break; 4919 case FieldSynchronization::kAcquireRelease: 4920 impl << "(kAcquireLoad));\n"; 4921 break; 4922 } 4923 } 4924 } 4925 } 4926 impl << " os << '\\n';\n"; 4927 impl << "}\n\n"; 4928} 4929} // namespace 4930 4931void ImplementationVisitor::GeneratePrintDefinitions( 4932 const std::string& output_directory) { 4933 std::stringstream impl; 4934 std::string file_name = "objects-printer.cc"; 4935 { 4936 IfDefScope object_print(impl, "OBJECT_PRINT"); 4937 4938 impl << "#include <iosfwd>\n\n"; 4939 impl << "#include \"src/objects/all-objects-inl.h\"\n\n"; 4940 4941 NamespaceScope impl_namespaces(impl, {"v8", "internal"}); 4942 4943 for (const ClassType* type : TypeOracle::GetClasses()) { 4944 if (!type->ShouldGeneratePrint()) continue; 4945 DCHECK(type->ShouldGenerateCppClassDefinitions()); 4946 const ClassType* super = type->GetSuperClass(); 4947 std::string gen_name = "TorqueGenerated" + type->name(); 4948 std::string gen_name_T = 4949 gen_name + "<" + type->name() + ", " + super->name() + ">"; 4950 std::string template_decl = "template <>"; 4951 GeneratePrintDefinitionsForClass(impl, type, gen_name, gen_name_T, 4952 template_decl); 4953 } 4954 } 4955 4956 std::string new_contents(impl.str()); 4957 WriteFile(output_directory + "/" + file_name, new_contents); 4958} 4959 4960base::Optional<std::string> MatchSimpleBodyDescriptor(const ClassType* type) { 4961 std::vector<ObjectSlotKind> slots = type->ComputeHeaderSlotKinds(); 4962 if (!type->HasStaticSize()) { 4963 slots.push_back(*type->ComputeArraySlotKind()); 4964 } 4965 4966 // Skip the map slot. 4967 size_t i = 1; 4968 while (i < slots.size() && slots[i] == ObjectSlotKind::kNoPointer) ++i; 4969 if (i == slots.size()) return "DataOnlyBodyDescriptor"; 4970 bool has_weak_pointers = false; 4971 size_t start_index = i; 4972 for (; i < slots.size(); ++i) { 4973 if (slots[i] == ObjectSlotKind::kStrongPointer) { 4974 continue; 4975 } else if (slots[i] == ObjectSlotKind::kMaybeObjectPointer) { 4976 has_weak_pointers = true; 4977 } else if (slots[i] == ObjectSlotKind::kNoPointer) { 4978 break; 4979 } else { 4980 return base::nullopt; 4981 } 4982 } 4983 size_t end_index = i; 4984 for (; i < slots.size(); ++i) { 4985 if (slots[i] != ObjectSlotKind::kNoPointer) return base::nullopt; 4986 } 4987 size_t start_offset = start_index * TargetArchitecture::TaggedSize(); 4988 size_t end_offset = end_index * TargetArchitecture::TaggedSize(); 4989 // We pick a suffix-range body descriptor even in cases where the object size 4990 // is fixed, to reduce the amount of code executed for object visitation. 4991 if (end_index == slots.size()) { 4992 return ToString("SuffixRange", has_weak_pointers ? "Weak" : "", 4993 "BodyDescriptor<", start_offset, ">"); 4994 } 4995 if (!has_weak_pointers) { 4996 return ToString("FixedRangeBodyDescriptor<", start_offset, ", ", end_offset, 4997 ">"); 4998 } 4999 return base::nullopt; 5000} 5001 5002void ImplementationVisitor::GenerateBodyDescriptors( 5003 const std::string& output_directory) { 5004 std::string file_name = "objects-body-descriptors-inl.inc"; 5005 std::stringstream h_contents; 5006 5007 for (const ClassType* type : TypeOracle::GetClasses()) { 5008 std::string name = type->name(); 5009 if (!type->ShouldGenerateBodyDescriptor()) continue; 5010 5011 bool has_array_fields = !type->HasStaticSize(); 5012 std::vector<ObjectSlotKind> header_slot_kinds = 5013 type->ComputeHeaderSlotKinds(); 5014 base::Optional<ObjectSlotKind> array_slot_kind = 5015 type->ComputeArraySlotKind(); 5016 DCHECK_EQ(has_array_fields, array_slot_kind.has_value()); 5017 5018 h_contents << "class " << name << "::BodyDescriptor final : public "; 5019 if (auto descriptor_name = MatchSimpleBodyDescriptor(type)) { 5020 h_contents << *descriptor_name << " {\n"; 5021 h_contents << " public:\n"; 5022 } else { 5023 h_contents << "BodyDescriptorBase {\n"; 5024 h_contents << " public:\n"; 5025 5026 h_contents << " static bool IsValidSlot(Map map, HeapObject obj, int " 5027 "offset) {\n"; 5028 if (has_array_fields) { 5029 h_contents << " if (offset < kHeaderSize) {\n"; 5030 } 5031 h_contents << " bool valid_slots[] = {"; 5032 for (ObjectSlotKind slot : header_slot_kinds) { 5033 h_contents << (slot != ObjectSlotKind::kNoPointer ? "1" : "0") << ","; 5034 } 5035 h_contents << "};\n" 5036 << " return valid_slots[static_cast<unsigned " 5037 "int>(offset)/kTaggedSize];\n"; 5038 if (has_array_fields) { 5039 h_contents << " }\n"; 5040 bool array_is_tagged = *array_slot_kind != ObjectSlotKind::kNoPointer; 5041 h_contents << " return " << (array_is_tagged ? "true" : "false") 5042 << ";\n"; 5043 } 5044 h_contents << " }\n\n"; 5045 5046 h_contents << " template <typename ObjectVisitor>\n"; 5047 h_contents 5048 << " static inline void IterateBody(Map map, HeapObject obj, " 5049 "int object_size, ObjectVisitor* v) {\n"; 5050 5051 std::vector<ObjectSlotKind> slots = std::move(header_slot_kinds); 5052 if (has_array_fields) slots.push_back(*array_slot_kind); 5053 5054 // Skip the map slot. 5055 slots.erase(slots.begin()); 5056 size_t start_offset = TargetArchitecture::TaggedSize(); 5057 5058 size_t end_offset = start_offset; 5059 ObjectSlotKind section_kind; 5060 for (size_t i = 0; i <= slots.size(); ++i) { 5061 base::Optional<ObjectSlotKind> next_section_kind; 5062 bool finished_section = false; 5063 if (i == 0) { 5064 next_section_kind = slots[i]; 5065 } else if (i < slots.size()) { 5066 if (auto combined = Combine(section_kind, slots[i])) { 5067 next_section_kind = *combined; 5068 } else { 5069 next_section_kind = slots[i]; 5070 finished_section = true; 5071 } 5072 } else { 5073 finished_section = true; 5074 } 5075 if (finished_section) { 5076 bool is_array_slot = i == slots.size() && has_array_fields; 5077 bool multiple_slots = 5078 is_array_slot || 5079 (end_offset - start_offset > TargetArchitecture::TaggedSize()); 5080 base::Optional<std::string> iterate_command; 5081 switch (section_kind) { 5082 case ObjectSlotKind::kStrongPointer: 5083 iterate_command = "IteratePointer"; 5084 break; 5085 case ObjectSlotKind::kMaybeObjectPointer: 5086 iterate_command = "IterateMaybeWeakPointer"; 5087 break; 5088 case ObjectSlotKind::kCustomWeakPointer: 5089 iterate_command = "IterateCustomWeakPointer"; 5090 break; 5091 case ObjectSlotKind::kNoPointer: 5092 break; 5093 } 5094 if (iterate_command) { 5095 if (multiple_slots) *iterate_command += "s"; 5096 h_contents << " " << *iterate_command << "(obj, " 5097 << start_offset; 5098 if (multiple_slots) { 5099 h_contents << ", " 5100 << (i == slots.size() ? "object_size" 5101 : std::to_string(end_offset)); 5102 } 5103 h_contents << ", v);\n"; 5104 } 5105 start_offset = end_offset; 5106 } 5107 if (i < slots.size()) section_kind = *next_section_kind; 5108 end_offset += TargetArchitecture::TaggedSize(); 5109 } 5110 5111 h_contents << " }\n\n"; 5112 } 5113 5114 h_contents 5115 << " static inline int SizeOf(Map map, HeapObject raw_object) {\n"; 5116 if (type->size().SingleValue()) { 5117 h_contents << " return " << *type->size().SingleValue() << ";\n"; 5118 } else { 5119 // We use an unchecked_cast here because this is used for concurrent 5120 // marking, where we shouldn't re-read the map. 5121 h_contents << " return " << name 5122 << "::unchecked_cast(raw_object).AllocatedSize();\n"; 5123 } 5124 h_contents << " }\n\n"; 5125 5126 h_contents << "};\n"; 5127 } 5128 5129 WriteFile(output_directory + "/" + file_name, h_contents.str()); 5130} 5131 5132namespace { 5133 5134// Generate verification code for a single piece of class data, which might be 5135// nested within a struct or might be a single element in an indexed field (or 5136// both). 5137void GenerateFieldValueVerifier(const std::string& class_name, bool indexed, 5138 std::string offset, const Field& leaf_field, 5139 std::string indexed_field_size, 5140 std::ostream& cc_contents, bool is_map) { 5141 const Type* field_type = leaf_field.name_and_type.type; 5142 5143 bool maybe_object = 5144 !field_type->IsSubtypeOf(TypeOracle::GetStrongTaggedType()); 5145 const char* object_type = maybe_object ? "MaybeObject" : "Object"; 5146 const char* verify_fn = 5147 maybe_object ? "VerifyMaybeObjectPointer" : "VerifyPointer"; 5148 if (indexed) { 5149 offset += " + i * " + indexed_field_size; 5150 } 5151 // Name the local var based on the field name for nicer CHECK output. 5152 const std::string value = leaf_field.name_and_type.name + "__value"; 5153 5154 // Read the field. 5155 if (is_map) { 5156 cc_contents << " " << object_type << " " << value << " = o.map();\n"; 5157 } else { 5158 cc_contents << " " << object_type << " " << value << " = TaggedField<" 5159 << object_type << ">::load(o, " << offset << ");\n"; 5160 } 5161 5162 // Call VerifyPointer or VerifyMaybeObjectPointer on it. 5163 cc_contents << " " << object_type << "::" << verify_fn << "(isolate, " 5164 << value << ");\n"; 5165 5166 // Check that the value is of an appropriate type. We can skip this part for 5167 // the Object type because it would not check anything beyond what we already 5168 // checked with VerifyPointer. 5169 if (field_type != TypeOracle::GetObjectType()) { 5170 cc_contents << " CHECK(" << GenerateRuntimeTypeCheck(field_type, value) 5171 << ");\n"; 5172 } 5173} 5174 5175void GenerateClassFieldVerifier(const std::string& class_name, 5176 const ClassType& class_type, const Field& f, 5177 std::ostream& h_contents, 5178 std::ostream& cc_contents) { 5179 const Type* field_type = f.name_and_type.type; 5180 5181 // We only verify tagged types, not raw numbers or pointers. Structs 5182 // consisting of tagged types are also included. 5183 if (!field_type->IsSubtypeOf(TypeOracle::GetTaggedType()) && 5184 !field_type->StructSupertype()) 5185 return; 5186 if (field_type == TypeOracle::GetFloat64OrHoleType()) return; 5187 // Do not verify if the field may be uninitialized. 5188 if (TypeOracle::GetUninitializedType()->IsSubtypeOf(field_type)) return; 5189 5190 std::string field_start_offset; 5191 if (f.index) { 5192 field_start_offset = f.name_and_type.name + "__offset"; 5193 std::string length = f.name_and_type.name + "__length"; 5194 cc_contents << " intptr_t " << field_start_offset << ", " << length 5195 << ";\n"; 5196 cc_contents << " std::tie(std::ignore, " << field_start_offset << ", " 5197 << length << ") = " 5198 << Callable::PrefixNameForCCOutput( 5199 class_type.GetSliceMacroName(f)) 5200 << "(o);\n"; 5201 5202 // Slices use intptr, but TaggedField<T>.load() uses int, so verify that 5203 // such a cast is valid. 5204 cc_contents << " CHECK_EQ(" << field_start_offset << ", static_cast<int>(" 5205 << field_start_offset << "));\n"; 5206 cc_contents << " CHECK_EQ(" << length << ", static_cast<int>(" << length 5207 << "));\n"; 5208 field_start_offset = "static_cast<int>(" + field_start_offset + ")"; 5209 length = "static_cast<int>(" + length + ")"; 5210 5211 cc_contents << " for (int i = 0; i < " << length << "; ++i) {\n"; 5212 } else { 5213 // Non-indexed fields have known offsets. 5214 field_start_offset = std::to_string(*f.offset); 5215 cc_contents << " {\n"; 5216 } 5217 5218 if (auto struct_type = field_type->StructSupertype()) { 5219 for (const Field& struct_field : (*struct_type)->fields()) { 5220 if (struct_field.name_and_type.type->IsSubtypeOf( 5221 TypeOracle::GetTaggedType())) { 5222 GenerateFieldValueVerifier( 5223 class_name, f.index.has_value(), 5224 field_start_offset + " + " + std::to_string(*struct_field.offset), 5225 struct_field, std::to_string((*struct_type)->PackedSize()), 5226 cc_contents, f.name_and_type.name == "map"); 5227 } 5228 } 5229 } else { 5230 GenerateFieldValueVerifier(class_name, f.index.has_value(), 5231 field_start_offset, f, "kTaggedSize", 5232 cc_contents, f.name_and_type.name == "map"); 5233 } 5234 5235 cc_contents << " }\n"; 5236} 5237 5238} // namespace 5239 5240void ImplementationVisitor::GenerateClassVerifiers( 5241 const std::string& output_directory) { 5242 std::string file_name = "class-verifiers"; 5243 std::stringstream h_contents; 5244 std::stringstream cc_contents; 5245 { 5246 IncludeGuardScope include_guard(h_contents, file_name + ".h"); 5247 IfDefScope verify_heap_h(h_contents, "VERIFY_HEAP"); 5248 IfDefScope verify_heap_cc(cc_contents, "VERIFY_HEAP"); 5249 5250 h_contents << "#include \"src/base/macros.h\"\n\n"; 5251 5252 cc_contents << "#include \"torque-generated/" << file_name << ".h\"\n\n"; 5253 cc_contents << "#include \"src/objects/all-objects-inl.h\"\n"; 5254 5255 IncludeObjectMacrosScope object_macros(cc_contents); 5256 5257 NamespaceScope h_namespaces(h_contents, {"v8", "internal"}); 5258 NamespaceScope cc_namespaces(cc_contents, {"v8", "internal"}); 5259 5260 cc_contents 5261 << "#include \"torque-generated/test/torque/test-torque-tq-inl.inc\"\n"; 5262 5263 // Generate forward declarations to avoid including any headers. 5264 h_contents << "class Isolate;\n"; 5265 for (const ClassType* type : TypeOracle::GetClasses()) { 5266 if (!type->ShouldGenerateVerify()) continue; 5267 h_contents << "class " << type->name() << ";\n"; 5268 } 5269 5270 const char* verifier_class = "TorqueGeneratedClassVerifiers"; 5271 5272 h_contents << "class V8_EXPORT_PRIVATE " << verifier_class << "{\n"; 5273 h_contents << " public:\n"; 5274 5275 for (const ClassType* type : TypeOracle::GetClasses()) { 5276 std::string name = type->name(); 5277 if (!type->ShouldGenerateVerify()) continue; 5278 5279 std::string method_name = name + "Verify"; 5280 5281 h_contents << " static void " << method_name << "(" << name 5282 << " o, Isolate* isolate);\n"; 5283 5284 cc_contents << "void " << verifier_class << "::" << method_name << "(" 5285 << name << " o, Isolate* isolate) {\n"; 5286 5287 // First, do any verification for the super class. Not all classes have 5288 // verifiers, so skip to the nearest super class that has one. 5289 const ClassType* super_type = type->GetSuperClass(); 5290 while (super_type && !super_type->ShouldGenerateVerify()) { 5291 super_type = super_type->GetSuperClass(); 5292 } 5293 if (super_type) { 5294 std::string super_name = super_type->name(); 5295 cc_contents << " o." << super_name << "Verify(isolate);\n"; 5296 } 5297 5298 // Second, verify that this object is what it claims to be. 5299 cc_contents << " CHECK(o.Is" << name << "(isolate));\n"; 5300 5301 // Third, verify its properties. 5302 for (auto f : type->fields()) { 5303 GenerateClassFieldVerifier(name, *type, f, h_contents, cc_contents); 5304 } 5305 5306 cc_contents << "}\n"; 5307 } 5308 5309 h_contents << "};\n"; 5310 } 5311 WriteFile(output_directory + "/" + file_name + ".h", h_contents.str()); 5312 WriteFile(output_directory + "/" + file_name + ".cc", cc_contents.str()); 5313} 5314 5315void ImplementationVisitor::GenerateEnumVerifiers( 5316 const std::string& output_directory) { 5317 std::string file_name = "enum-verifiers"; 5318 std::stringstream cc_contents; 5319 { 5320 cc_contents << "#include \"src/compiler/code-assembler.h\"\n"; 5321 for (const std::string& include_path : GlobalContext::CppIncludes()) { 5322 cc_contents << "#include " << StringLiteralQuote(include_path) << "\n"; 5323 } 5324 cc_contents << "\n"; 5325 5326 NamespaceScope cc_namespaces(cc_contents, {"v8", "internal", ""}); 5327 5328 cc_contents << "class EnumVerifier {\n"; 5329 for (const auto& desc : GlobalContext::Get().ast()->EnumDescriptions()) { 5330 cc_contents << " // " << desc.name << " (" << desc.pos << ")\n"; 5331 cc_contents << " void VerifyEnum_" << desc.name << "(" 5332 << desc.constexpr_generates 5333 << " x) {\n" 5334 " switch(x) {\n"; 5335 for (const auto& entry : desc.entries) { 5336 cc_contents << " case " << entry << ": break;\n"; 5337 } 5338 if (desc.is_open) cc_contents << " default: break;\n"; 5339 cc_contents << " }\n }\n\n"; 5340 } 5341 cc_contents << "};\n"; 5342 } 5343 5344 WriteFile(output_directory + "/" + file_name + ".cc", cc_contents.str()); 5345} 5346 5347void ImplementationVisitor::GenerateExportedMacrosAssembler( 5348 const std::string& output_directory) { 5349 std::string file_name = "exported-macros-assembler"; 5350 std::stringstream h_contents; 5351 std::stringstream cc_contents; 5352 { 5353 IncludeGuardScope include_guard(h_contents, file_name + ".h"); 5354 5355 h_contents << "#include \"src/compiler/code-assembler.h\"\n"; 5356 h_contents << "#include \"src/execution/frames.h\"\n"; 5357 h_contents << "#include \"torque-generated/csa-types.h\"\n"; 5358 5359 for (const std::string& include_path : GlobalContext::CppIncludes()) { 5360 cc_contents << "#include " << StringLiteralQuote(include_path) << "\n"; 5361 } 5362 cc_contents << "#include \"torque-generated/" << file_name << ".h\"\n"; 5363 5364 for (SourceId file : SourceFileMap::AllSources()) { 5365 cc_contents << "#include \"torque-generated/" + 5366 SourceFileMap::PathFromV8RootWithoutExtension(file) + 5367 "-tq-csa.h\"\n"; 5368 } 5369 5370 NamespaceScope h_namespaces(h_contents, {"v8", "internal"}); 5371 NamespaceScope cc_namespaces(cc_contents, {"v8", "internal"}); 5372 5373 h_contents << "class V8_EXPORT_PRIVATE " 5374 "TorqueGeneratedExportedMacrosAssembler {\n" 5375 << " public:\n" 5376 << " explicit TorqueGeneratedExportedMacrosAssembler" 5377 "(compiler::CodeAssemblerState* state) : state_(state) {\n" 5378 << " USE(state_);\n" 5379 << " }\n"; 5380 5381 for (auto& declarable : GlobalContext::AllDeclarables()) { 5382 TorqueMacro* macro = TorqueMacro::DynamicCast(declarable.get()); 5383 if (!(macro && macro->IsExportedToCSA())) continue; 5384 CurrentSourcePosition::Scope position_activator(macro->Position()); 5385 5386 cpp::Class assembler("TorqueGeneratedExportedMacrosAssembler"); 5387 std::vector<std::string> generated_parameter_names; 5388 cpp::Function f = GenerateFunction( 5389 &assembler, macro->ReadableName(), macro->signature(), 5390 macro->parameter_names(), false, &generated_parameter_names); 5391 5392 f.PrintDeclaration(h_contents); 5393 f.PrintDefinition(cc_contents, [&](std::ostream& stream) { 5394 stream << "return " << macro->ExternalName() << "(state_"; 5395 for (const auto& name : generated_parameter_names) { 5396 stream << ", " << name; 5397 } 5398 stream << ");"; 5399 }); 5400 } 5401 5402 h_contents << " private:\n" 5403 << " compiler::CodeAssemblerState* state_;\n" 5404 << "};\n"; 5405 } 5406 WriteFile(output_directory + "/" + file_name + ".h", h_contents.str()); 5407 WriteFile(output_directory + "/" + file_name + ".cc", cc_contents.str()); 5408} 5409 5410namespace { 5411 5412void CollectAllFields(const std::string& path, const Field& field, 5413 std::vector<std::string>& result) { 5414 if (field.name_and_type.type->StructSupertype()) { 5415 std::string next_path = path + field.name_and_type.name + "."; 5416 const StructType* struct_type = 5417 StructType::DynamicCast(field.name_and_type.type); 5418 for (const auto& inner_field : struct_type->fields()) { 5419 CollectAllFields(next_path, inner_field, result); 5420 } 5421 } else { 5422 result.push_back(path + field.name_and_type.name); 5423 } 5424} 5425 5426} // namespace 5427 5428void ImplementationVisitor::GenerateCSATypes( 5429 const std::string& output_directory) { 5430 std::string file_name = "csa-types"; 5431 std::stringstream h_contents; 5432 { 5433 IncludeGuardScope include_guard(h_contents, file_name + ".h"); 5434 h_contents << "#include \"src/compiler/code-assembler.h\"\n\n"; 5435 5436 NamespaceScope h_namespaces(h_contents, {"v8", "internal"}); 5437 5438 // Generates headers for all structs in a topologically-sorted order, since 5439 // TypeOracle keeps them in the order of their resolution 5440 for (const auto& type : TypeOracle::GetAggregateTypes()) { 5441 const StructType* struct_type = StructType::DynamicCast(type.get()); 5442 if (!struct_type) continue; 5443 h_contents << "struct " << struct_type->GetGeneratedTypeNameImpl() 5444 << " {\n"; 5445 for (auto& field : struct_type->fields()) { 5446 h_contents << " " << field.name_and_type.type->GetGeneratedTypeName(); 5447 h_contents << " " << field.name_and_type.name << ";\n"; 5448 } 5449 h_contents << "\n std::tuple<"; 5450 bool first = true; 5451 for (const Type* lowered_type : LowerType(struct_type)) { 5452 if (!first) { 5453 h_contents << ", "; 5454 } 5455 first = false; 5456 h_contents << lowered_type->GetGeneratedTypeName(); 5457 } 5458 std::vector<std::string> all_fields; 5459 for (auto& field : struct_type->fields()) { 5460 CollectAllFields("", field, all_fields); 5461 } 5462 h_contents << "> Flatten() const {\n" 5463 " return std::make_tuple("; 5464 PrintCommaSeparatedList(h_contents, all_fields); 5465 h_contents << ");\n"; 5466 h_contents << " }\n"; 5467 h_contents << "};\n"; 5468 } 5469 } 5470 WriteFile(output_directory + "/" + file_name + ".h", h_contents.str()); 5471} 5472 5473void ReportAllUnusedMacros() { 5474 for (const auto& declarable : GlobalContext::AllDeclarables()) { 5475 if (!declarable->IsMacro() || declarable->IsExternMacro()) continue; 5476 5477 Macro* macro = Macro::cast(declarable.get()); 5478 if (macro->IsUsed()) continue; 5479 5480 if (macro->IsTorqueMacro() && TorqueMacro::cast(macro)->IsExportedToCSA()) { 5481 continue; 5482 } 5483 // TODO(gsps): Mark methods of generic structs used if they are used in any 5484 // instantiation 5485 if (Method* method = Method::DynamicCast(macro)) { 5486 if (StructType* struct_type = 5487 StructType::DynamicCast(method->aggregate_type())) { 5488 if (struct_type->GetSpecializedFrom().has_value()) { 5489 continue; 5490 } 5491 } 5492 } 5493 5494 std::vector<std::string> ignored_prefixes = {"Convert<", "Cast<", 5495 "FromConstexpr<"}; 5496 const std::string name = macro->ReadableName(); 5497 const bool ignore = 5498 StartsWithSingleUnderscore(name) || 5499 std::any_of(ignored_prefixes.begin(), ignored_prefixes.end(), 5500 [&name](const std::string& prefix) { 5501 return StringStartsWith(name, prefix); 5502 }); 5503 5504 if (!ignore) { 5505 Lint("Macro '", macro->ReadableName(), "' is never used.") 5506 .Position(macro->IdentifierPosition()); 5507 } 5508 } 5509} 5510 5511} // namespace torque 5512} // namespace internal 5513} // namespace v8 5514