| File: | out/../deps/v8/src/torque/implementation-visitor.cc |
| Warning: | line 3335, column 27 Access to field 'block' results in a dereference of a null pointer (loaded from variable 'label') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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 | ||||
| 30 | namespace v8 { | |||
| 31 | namespace internal { | |||
| 32 | namespace torque { | |||
| 33 | ||||
| 34 | uint64_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. | |||
| 40 | using namespace std::string_literals; // NOLINT(build/namespaces) | |||
| 41 | ||||
| 42 | namespace { | |||
| 43 | const char* BuiltinIncludesMarker = "// __BUILTIN_INCLUDES_MARKER__\n"; | |||
| 44 | } // namespace | |||
| 45 | ||||
| 46 | VisitResult 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)ENUM_ITEM(CallExpression) ENUM_ITEM(CallMethodExpression) ENUM_ITEM (IntrinsicCallExpression) ENUM_ITEM(StructExpression) ENUM_ITEM (LogicalOrExpression) ENUM_ITEM(LogicalAndExpression) ENUM_ITEM (SpreadExpression) ENUM_ITEM(ConditionalExpression) ENUM_ITEM (IdentifierExpression) ENUM_ITEM(StringLiteralExpression) ENUM_ITEM (IntegerLiteralExpression) ENUM_ITEM(FloatingPointLiteralExpression ) ENUM_ITEM(FieldAccessExpression) ENUM_ITEM(ElementAccessExpression ) ENUM_ITEM(DereferenceExpression) ENUM_ITEM(AssignmentExpression ) ENUM_ITEM(IncrementDecrementExpression) ENUM_ITEM(NewExpression ) ENUM_ITEM(AssumeTypeImpossibleExpression) ENUM_ITEM(StatementExpression ) ENUM_ITEM(TryLabelExpression) | |||
| 53 | #undef ENUM_ITEM | |||
| 54 | default: | |||
| 55 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 56 | } | |||
| 57 | } | |||
| 58 | ||||
| 59 | const 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)ENUM_ITEM(BlockStatement) ENUM_ITEM(ExpressionStatement) ENUM_ITEM (IfStatement) ENUM_ITEM(WhileStatement) ENUM_ITEM(ForLoopStatement ) ENUM_ITEM(BreakStatement) ENUM_ITEM(ContinueStatement) ENUM_ITEM (ReturnStatement) ENUM_ITEM(DebugStatement) ENUM_ITEM(AssertStatement ) ENUM_ITEM(TailCallStatement) ENUM_ITEM(VarDeclarationStatement ) ENUM_ITEM(GotoStatement) | |||
| 69 | #undef ENUM_ITEM | |||
| 70 | default: | |||
| 71 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 72 | } | |||
| 73 | DCHECK_EQ(result == TypeOracle::GetNeverType(),((void) 0) | |||
| 74 | assembler().CurrentBlockIsComplete())((void) 0); | |||
| 75 | return result; | |||
| 76 | } | |||
| 77 | ||||
| 78 | void 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 | ||||
| 142 | void 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 | ||||
| 165 | void 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 | ||||
| 195 | void 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 | ||||
| 212 | void 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())((void) 0); | |||
| 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 | ||||
| 245 | void 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 | ||||
| 256 | class 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 | ||||
| 273 | VisitResult 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(),((void) 0) | |||
| 289 | arguments.size() + (this_reference ? 1 : 0))((void) 0); | |||
| 290 | DCHECK_EQ(this_reference.has_value(), macro->IsMethod())((void) 0); | |||
| 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())((void) 0); | |||
| 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())((void) 0); | |||
| 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 | ||||
| 393 | void 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())((void) 0); | |||
| 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 | ||||
| 542 | void ImplementationVisitor::Visit(TorqueMacro* macro) { | |||
| 543 | VisitMacroCommon(macro); | |||
| 544 | } | |||
| 545 | ||||
| 546 | void ImplementationVisitor::Visit(Method* method) { | |||
| 547 | DCHECK(!method->IsExternal())((void) 0); | |||
| 548 | VisitMacroCommon(method); | |||
| 549 | } | |||
| 550 | ||||
| 551 | namespace { | |||
| 552 | ||||
| 553 | std::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 | ||||
| 576 | void 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)((void) 0); | |||
| 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())((void) 0); | |||
| 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 | ||||
| 727 | const Type* ImplementationVisitor::Visit(VarDeclarationStatement* stmt) { | |||
| 728 | BlockBindings<LocalValue> block_bindings(&ValueBindingsManager::Get()); | |||
| 729 | return Visit(stmt, &block_bindings); | |||
| 730 | } | |||
| 731 | ||||
| 732 | const 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())((void) 0); | |||
| 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 | ||||
| 782 | const Type* ImplementationVisitor::Visit(TailCallStatement* stmt) { | |||
| 783 | return Visit(stmt->call, true).type(); | |||
| 784 | } | |||
| 785 | ||||
| 786 | VisitResult 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)do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base ::pass_value_or_ref<decltype(left)>::type, typename ::v8 ::base::pass_value_or_ref<decltype(right)>::type>((left ), (right)); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal ("Check failed: %s.", "left" " " "==" " " "right"); } } while (false); } while (false); | |||
| 823 | return left; | |||
| 824 | } | |||
| 825 | ||||
| 826 | VisitResult 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)((void) 0); | |||
| 864 | return outer_scope.Yield(true_result); | |||
| 865 | } | |||
| 866 | ||||
| 867 | VisitResult 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)((void) 0); | |||
| 914 | return outer_scope.Yield(true_result); | |||
| 915 | } | |||
| 916 | ||||
| 917 | VisitResult 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 | ||||
| 930 | VisitResult 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 | ||||
| 948 | VisitResult 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 | ||||
| 956 | VisitResult 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 | ||||
| 965 | VisitResult 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})do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base ::pass_value_or_ref<decltype(LowerType(result_type))>:: type, typename ::v8::base::pass_value_or_ref<decltype(TypeVector {result_type})>::type>((LowerType(result_type)), (TypeVector {result_type})); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "LowerType(result_type)" " " "==" " " "TypeVector{result_type}"); } } while (false); } while (false); | |||
| 973 | assembler().Emit(UnsafeCastInstruction{result_type}); | |||
| 974 | result.SetType(result_type); | |||
| 975 | return result; | |||
| 976 | } | |||
| 977 | ||||
| 978 | VisitResult ImplementationVisitor::Visit(StringLiteralExpression* expr) { | |||
| 979 | return VisitResult{ | |||
| 980 | TypeOracle::GetConstStringType(), | |||
| 981 | "\"" + expr->literal.substr(1, expr->literal.size() - 2) + "\""}; | |||
| 982 | } | |||
| 983 | ||||
| 984 | VisitResult 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 | ||||
| 998 | VisitResult ImplementationVisitor::Visit(LocationExpression* expr) { | |||
| 999 | StackScope scope(this); | |||
| 1000 | return scope.Yield(GenerateFetchFromLocation(GetLocationReference(expr))); | |||
| 1001 | } | |||
| 1002 | ||||
| 1003 | VisitResult 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 | ||||
| 1014 | const 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 | ||||
| 1044 | const 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 | ||||
| 1133 | const 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 | ||||
| 1156 | const 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 | ||||
| 1173 | const 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 | ||||
| 1189 | namespace { | |||
| 1190 | ||||
| 1191 | std::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 | ||||
| 1208 | const 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 | ||||
| 1262 | const Type* ImplementationVisitor::Visit(ExpressionStatement* stmt) { | |||
| 1263 | const Type* type = Visit(stmt->expression).type(); | |||
| 1264 | return type->IsNever() ? type : TypeOracle::GetVoidType(); | |||
| 1265 | } | |||
| 1266 | ||||
| 1267 | const 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()V8_Fatal("unreachable code"); | |||
| 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 | ||||
| 1314 | VisitResult 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 | ||||
| 1390 | VisitResult ImplementationVisitor::Visit(StatementExpression* expr) { | |||
| 1391 | return VisitResult{Visit(expr->statement), assembler().TopRange(0)}; | |||
| 1392 | } | |||
| 1393 | ||||
| 1394 | InitializerResults 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 | ||||
| 1420 | LocationReference 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())((void) 0); | |||
| 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. | |||
| 1449 | LocationReference 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 | ||||
| 1473 | void 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())((void) 0); | |||
| 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 | ||||
| 1498 | VisitResult 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 | ||||
| 1518 | VisitResult ImplementationVisitor::GenerateArrayLength(VisitResult object, | |||
| 1519 | const Field& field) { | |||
| 1520 | DCHECK(field.index)((void) 0); | |||
| 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 | ||||
| 1551 | VisitResult ImplementationVisitor::GenerateArrayLength( | |||
| 1552 | const ClassType* class_type, const InitializerResults& initializer_results, | |||
| 1553 | const Field& field) { | |||
| 1554 | DCHECK(field.index)((void) 0); | |||
| 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 | ||||
| 1574 | LayoutForInitialization 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())((void) 0); | |||
| 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 | ||||
| 1621 | VisitResult 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())((void) 0); | |||
| 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 | ||||
| 1696 | const 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 | ||||
| 1706 | const 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 | ||||
| 1716 | const 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 | ||||
| 1768 | VisitResult ImplementationVisitor::Visit(SpreadExpression* expr) { | |||
| 1769 | ReportError( | |||
| 1770 | "spread operators are only currently supported in indexed class field " | |||
| 1771 | "initialization expressions"); | |||
| 1772 | } | |||
| 1773 | ||||
| 1774 | void 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)do { bool _cmp = ::v8::base::CmpNEImpl< typename ::v8::base ::pass_value_or_ref<decltype(pos)>::type, typename ::v8 ::base::pass_value_or_ref<decltype(std::string::npos)>:: type>((pos), (std::string::npos)); do { if ((__builtin_expect (!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "pos" " " "!=" " " "std::string::npos"); } } while (false); } while (false); | |||
| 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 | ||||
| 1814 | cpp::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 | ||||
| 1825 | cpp::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)((void) 0); | |||
| 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())((void) 0); | |||
| 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)((void) 0); | |||
| 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())((void) 0); | |||
| 1903 | generated_parameter_names->erase(generated_parameter_names->begin()); | |||
| 1904 | } | |||
| 1905 | } | |||
| 1906 | return f; | |||
| 1907 | } | |||
| 1908 | ||||
| 1909 | namespace { | |||
| 1910 | ||||
| 1911 | void 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 | ||||
| 1944 | Callable* 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 | ||||
| 1955 | base::Optional<Binding<LocalValue>*> ImplementationVisitor::TryLookupLocalValue( | |||
| 1956 | const std::string& name) { | |||
| 1957 | return ValueBindingsManager::Get().TryLookup(name); | |||
| 1958 | } | |||
| 1959 | ||||
| 1960 | base::Optional<Binding<LocalLabel>*> ImplementationVisitor::TryLookupLabel( | |||
| 1961 | const std::string& name) { | |||
| 1962 | return LabelBindingsManager::Get().TryLookup(name); | |||
| 1963 | } | |||
| 1964 | ||||
| 1965 | Binding<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 | ||||
| 1972 | Block* 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. | |||
| 1985 | bool ImplementationVisitor::TestLookupCallable( | |||
| 1986 | const QualifiedName& name, const TypeVector& parameter_types) { | |||
| 1987 | return LookupCallable(name, Declarations::TryLookup(name), parameter_types, | |||
| 1988 | {}, {}, true) != nullptr; | |||
| 1989 | } | |||
| 1990 | ||||
| 1991 | TypeArgumentInference 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 | ||||
| 2010 | template <class Container> | |||
| 2011 | Callable* 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))((void) 0); | |||
| 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 | ||||
| 2109 | template <class Container> | |||
| 2110 | Callable* 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 | ||||
| 2118 | Method* 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 | ||||
| 2128 | const 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 | ||||
| 2142 | VisitResult 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 | ||||
| 2150 | VisitResult 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 | ||||
| 2210 | VisitResult 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 | ||||
| 2220 | LocationReference 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 | ||||
| 2239 | LocationReference ImplementationVisitor::GetLocationReference( | |||
| 2240 | FieldAccessExpression* expr) { | |||
| 2241 | return GenerateFieldAccess(GetLocationReference(expr->object), | |||
| 2242 | expr->field->value, false, expr->field->pos); | |||
| 2243 | } | |||
| 2244 | ||||
| 2245 | LocationReference 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 | ||||
| 2363 | LocationReference 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 | ||||
| 2375 | LocationReference ImplementationVisitor::GenerateReferenceToItemInHeapSlice( | |||
| 2376 | LocationReference slice, VisitResult index) { | |||
| 2377 | DCHECK(slice.IsHeapSlice())((void) 0); | |||
| 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 | ||||
| 2389 | LocationReference 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,((void) 0) | |||
| 2401 | expr->name->value.length())((void) 0); | |||
| 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())((void) 0); | |||
| 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())do { if ((__builtin_expect(!!(!(value->Position().source.IsValid ())), 0))) { V8_Fatal("Check failed: %s.", "value->Position().source.IsValid()" ); } } while (false); | |||
| 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 | ||||
| 2473 | LocationReference 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 | ||||
| 2484 | VisitResult 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))((void) 0); | |||
| 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())((void) 0); | |||
| 2525 | return GenerateCall(reference.eval_function(), | |||
| 2526 | Arguments{reference.call_arguments(), {}}); | |||
| 2527 | } | |||
| 2528 | } | |||
| 2529 | ||||
| 2530 | void 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())((void) 0); | |||
| 2601 | ReportError("cannot assign to const-bound or temporary ", | |||
| 2602 | reference.temporary_description()); | |||
| 2603 | } | |||
| 2604 | } | |||
| 2605 | ||||
| 2606 | VisitResult 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()))((void) 0); | |||
| 2656 | return scope.Yield(VisitResult(type->return_type(), assembler().TopRange(1))); | |||
| 2657 | } | |||
| 2658 | ||||
| 2659 | void 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 | ||||
| 2680 | namespace { | |||
| 2681 | std::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 | ||||
| 2703 | VisitResult 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())do { if ((__builtin_expect(!!(!(callable->Position().source .IsValid())), 0))) { V8_Fatal("Check failed: %s.", "callable->Position().source.IsValid()" ); } } while (false); | |||
| 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())((void) 0); | |||
| 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())((void) 0); | |||
| 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)do { if ((__builtin_expect(!!(!((extern_macro) != nullptr)), 0 ))) { V8_Fatal("Check failed: %s.", "(extern_macro) != nullptr" ); } } while (false); | |||
| 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)do { if ((__builtin_expect(!!(!((extern_macro) != nullptr)), 0 ))) { V8_Fatal("Check failed: %s.", "(extern_macro) != nullptr" ); } } while (false); | |||
| 2887 | result << extern_macro->CCDebugName() << "(accessor"; | |||
| 2888 | first = false; | |||
| 2889 | break; | |||
| 2890 | } | |||
| 2891 | } | |||
| 2892 | for (VisitResult arg : converted_arguments) { | |||
| 2893 | DCHECK(!arg.IsOnStack())((void) 0); | |||
| 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)((void) 0); | |||
| 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())((void) 0); | |||
| 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()V8_Fatal("unreachable code"); | |||
| 3164 | } | |||
| 3165 | } | |||
| 3166 | ||||
| 3167 | VisitResult 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 | ||||
| 3177 | VisitResult 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 | ||||
| 3231 | VisitResult 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)((void) 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 | ||||
| 3266 | VisitResult 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 | ||||
| 3277 | void ImplementationVisitor::GenerateBranch(const VisitResult& condition, | |||
| 3278 | Block* true_block, | |||
| 3279 | Block* false_block) { | |||
| 3280 | DCHECK_EQ(condition,((void) 0) | |||
| 3281 | VisitResult(TypeOracle::GetBoolType(), assembler().TopRange(1)))((void) 0); | |||
| 3282 | assembler().Branch(true_block, false_block); | |||
| 3283 | } | |||
| 3284 | ||||
| 3285 | VisitResult ImplementationVisitor::GenerateBoolConstant(bool constant) { | |||
| 3286 | return GenerateImplicitConvert(TypeOracle::GetBoolType(), | |||
| 3287 | VisitResult(TypeOracle::GetConstexprBoolType(), | |||
| 3288 | constant ? "true" : "false")); | |||
| 3289 | } | |||
| 3290 | ||||
| 3291 | void 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 | ||||
| 3301 | VisitResult 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 | ||||
| 3333 | StackRange ImplementationVisitor::GenerateLabelGoto( | |||
| 3334 | LocalLabel* label, base::Optional<StackRange> arguments) { | |||
| 3335 | return assembler().Goto(label->block, arguments ? arguments->Size() : 0); | |||
| ||||
| 3336 | } | |||
| 3337 | ||||
| 3338 | std::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 | ||||
| 3358 | StackRange 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 | ||||
| 3376 | void 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 | ||||
| 3391 | std::string ImplementationVisitor::ExternalLabelName( | |||
| 3392 | const std::string& label_name) { | |||
| 3393 | return "label_" + label_name; | |||
| 3394 | } | |||
| 3395 | ||||
| 3396 | std::string ImplementationVisitor::ExternalLabelParameterName( | |||
| 3397 | const std::string& label_name, size_t i) { | |||
| 3398 | return "label_" + label_name + "_parameter_" + std::to_string(i); | |||
| 3399 | } | |||
| 3400 | ||||
| 3401 | std::string ImplementationVisitor::ExternalParameterName( | |||
| 3402 | const std::string& name) { | |||
| 3403 | return std::string("p_") + name; | |||
| 3404 | } | |||
| 3405 | ||||
| 3406 | DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::ValueBindingsManager)template <> ImplementationVisitor::ValueBindingsManager ::Scope*& ContextualVariableTop<ImplementationVisitor:: ValueBindingsManager>() { static thread_local ImplementationVisitor ::ValueBindingsManager::Scope* top = nullptr; return top; } | |||
| 3407 | DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::LabelBindingsManager)template <> ImplementationVisitor::LabelBindingsManager ::Scope*& ContextualVariableTop<ImplementationVisitor:: LabelBindingsManager>() { static thread_local ImplementationVisitor ::LabelBindingsManager::Scope* top = nullptr; return top; } | |||
| 3408 | DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::CurrentCallable)template <> ImplementationVisitor::CurrentCallable::Scope *& ContextualVariableTop<ImplementationVisitor::CurrentCallable >() { static thread_local ImplementationVisitor::CurrentCallable ::Scope* top = nullptr; return top; } | |||
| 3409 | DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::CurrentFileStreams)template <> ImplementationVisitor::CurrentFileStreams:: Scope*& ContextualVariableTop<ImplementationVisitor::CurrentFileStreams >() { static thread_local ImplementationVisitor::CurrentFileStreams ::Scope* top = nullptr; return top; } | |||
| 3410 | DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::CurrentReturnValue)template <> ImplementationVisitor::CurrentReturnValue:: Scope*& ContextualVariableTop<ImplementationVisitor::CurrentReturnValue >() { static thread_local ImplementationVisitor::CurrentReturnValue ::Scope* top = nullptr; return top; } | |||
| 3411 | ||||
| 3412 | bool 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 | ||||
| 3429 | base::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 | ||||
| 3438 | void 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 | } | |||
| 3461 | void 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 | ||||
| 3501 | void 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 | ||||
| 3535 | std::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 | ||||
| 3548 | void 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())((void) 0); | |||
| 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 | ||||
| 3658 | namespace { | |||
| 3659 | ||||
| 3660 | enum class FieldSectionType : uint32_t { | |||
| 3661 | kNoSection = 0, | |||
| 3662 | kWeakSection = 1 << 0, | |||
| 3663 | kStrongSection = 2 << 0, | |||
| 3664 | kScalarSection = 3 << 0 | |||
| 3665 | }; | |||
| 3666 | ||||
| 3667 | bool IsPointerSection(FieldSectionType type) { | |||
| 3668 | return type == FieldSectionType::kWeakSection || | |||
| 3669 | type == FieldSectionType::kStrongSection; | |||
| 3670 | } | |||
| 3671 | ||||
| 3672 | using FieldSections = base::Flags<FieldSectionType>; | |||
| 3673 | ||||
| 3674 | std::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()V8_Fatal("unreachable code"); | |||
| 3686 | } | |||
| 3687 | ||||
| 3688 | class 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_)do { if ((__builtin_expect(!!(!(is_finished_)), 0))) { V8_Fatal ("Check failed: %s.", "is_finished_"); } } while (false); } | |||
| 3697 | ||||
| 3698 | void RecordOffsetFor(const Field& f) { | |||
| 3699 | CHECK(!is_finished_)do { if ((__builtin_expect(!!(!(!is_finished_)), 0))) { V8_Fatal ("Check failed: %s.", "!is_finished_"); } } while (false); | |||
| 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)((void) 0); | |||
| 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 | ||||
| 3822 | void 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 | ||||
| 3839 | void 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 | ||||
| 3876 | void 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 | ||||
| 3923 | namespace { | |||
| 3924 | ||||
| 3925 | class 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())((void) 0); | |||
| 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 | ||||
| 3980 | class 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 | ||||
| 4037 | base::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 | ||||
| 4060 | void 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 | ||||
| 4243 | void 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 | ||||
| 4273 | void 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 | ||||
| 4293 | SourcePosition CppClassGenerator::Position() { return type_->GetPosition(); } | |||
| 4294 | ||||
| 4295 | void 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)((void) 0); | |||
| 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 | ||||
| 4339 | namespace { | |||
| 4340 | std::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 | ||||
| 4373 | void 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 | ||||
| 4390 | bool 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. | |||
| 4400 | void 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 | ||||
| 4513 | std::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 | ||||
| 4520 | std::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 | ||||
| 4542 | bool CppClassGenerator::CanContainHeapObjects(const Type* t) { | |||
| 4543 | return t->IsSubtypeOf(TypeOracle::GetTaggedType()) && | |||
| 4544 | !t->IsSubtypeOf(TypeOracle::GetSmiType()); | |||
| 4545 | } | |||
| 4546 | ||||
| 4547 | void 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 | ||||
| 4615 | void 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 | ||||
| 4686 | void 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 | ||||
| 4701 | void 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())do { if ((__builtin_expect(!!(!(index_fields.has_value())), 0 ))) { V8_Fatal("Check failed: %s.", "index_fields.has_value()" ); } } while (false); | |||
| 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 | ||||
| 4866 | namespace { | |||
| 4867 | void 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 | ||||
| 4931 | void 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())((void) 0); | |||
| 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 | ||||
| 4960 | base::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 | ||||
| 5002 | void 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())((void) 0); | |||
| 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 | ||||
| 5132 | namespace { | |||
| 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). | |||
| 5137 | void 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 | ||||
| 5175 | void 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 | ||||
| 5240 | void 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 | ||||
| 5315 | void 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 | ||||
| 5347 | void 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 | ||||
| 5410 | namespace { | |||
| 5411 | ||||
| 5412 | void 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 | ||||
| 5428 | void 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 | ||||
| 5473 | void 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 |