Bug Summary

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')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name implementation-visitor.cc -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/home/maurizio/node-v18.6.0/out -resource-dir /usr/local/lib/clang/16.0.0 -D _GLIBCXX_USE_CXX11_ABI=1 -D NODE_OPENSSL_CONF_NAME=nodejs_conf -D NODE_OPENSSL_HAS_QUIC -D V8_GYP_BUILD -D V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP=64 -D __STDC_FORMAT_MACROS -D OPENSSL_NO_PINSHARED -D OPENSSL_THREADS -D V8_TARGET_ARCH_X64 -D V8_HAVE_TARGET_OS -D V8_TARGET_OS_LINUX -D V8_EMBEDDER_STRING="-node.8" -D ENABLE_DISASSEMBLER -D V8_PROMISE_INTERNAL_FIELD_COUNT=1 -D V8_SHORT_BUILTIN_CALLS -D OBJECT_PRINT -D V8_INTL_SUPPORT -D V8_ATOMIC_OBJECT_FIELD_WRITES -D V8_ENABLE_LAZY_SOURCE_POSITIONS -D V8_USE_SIPHASH -D V8_SHARED_RO_HEAP -D V8_WIN64_UNWINDING_INFO -D V8_ENABLE_REGEXP_INTERPRETER_THREADED_DISPATCH -D V8_SNAPSHOT_COMPRESSION -D V8_ENABLE_WEBASSEMBLY -D V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS -D V8_ALLOCATION_FOLDING -D V8_ALLOCATION_SITE_TRACKING -D V8_SCRIPTORMODULE_LEGACY_LIFETIME -D V8_ADVANCED_BIGINT_ALGORITHMS -I ../deps/v8 -I ../deps/v8/include -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8 -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/x86_64-redhat-linux -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/backward -internal-isystem /usr/local/lib/clang/16.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-unused-parameter -Wno-return-type -std=gnu++17 -fdeprecated-macro -fdebug-compilation-dir=/home/maurizio/node-v18.6.0/out -ferror-limit 19 -fno-rtti -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-08-22-142216-507842-1 -x c++ ../deps/v8/src/torque/implementation-visitor.cc
1// Copyright 2017 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/torque/implementation-visitor.h"
6
7#include <algorithm>
8#include <iomanip>
9#include <string>
10
11#include "src/base/optional.h"
12#include "src/common/globals.h"
13#include "src/numbers/integer-literal-inl.h"
14#include "src/torque/cc-generator.h"
15#include "src/torque/cfg.h"
16#include "src/torque/constants.h"
17#include "src/torque/cpp-builder.h"
18#include "src/torque/csa-generator.h"
19#include "src/torque/declaration-visitor.h"
20#include "src/torque/global-context.h"
21#include "src/torque/kythe-data.h"
22#include "src/torque/parameter-difference.h"
23#include "src/torque/server-data.h"
24#include "src/torque/source-positions.h"
25#include "src/torque/type-inference.h"
26#include "src/torque/type-visitor.h"
27#include "src/torque/types.h"
28#include "src/torque/utils.h"
29
30namespace v8 {
31namespace internal {
32namespace torque {
33
34uint64_t next_unique_binding_index = 0;
35
36// Sadly, 'using std::string_literals::operator""s;' is bugged in MSVC (see
37// https://developercommunity.visualstudio.com/t/Incorrect-warning-when-using-standard-st/673948).
38// TODO(nicohartmann@): Change to 'using std::string_literals::operator""s;'
39// once this is fixed.
40using namespace std::string_literals; // NOLINT(build/namespaces)
41
42namespace {
43const char* BuiltinIncludesMarker = "// __BUILTIN_INCLUDES_MARKER__\n";
44} // namespace
45
46VisitResult ImplementationVisitor::Visit(Expression* expr) {
47 CurrentSourcePosition::Scope scope(expr->pos);
48 switch (expr->kind) {
49#define ENUM_ITEM(name) \
50 case AstNode::Kind::k##name: \
51 return Visit(name::cast(expr));
52 AST_EXPRESSION_NODE_KIND_LIST(ENUM_ITEM)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
59const Type* ImplementationVisitor::Visit(Statement* stmt) {
60 CurrentSourcePosition::Scope scope(stmt->pos);
61 StackScope stack_scope(this);
62 const Type* result;
63 switch (stmt->kind) {
8
Control jumps to 'case kReturnStatement:' at line 68
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)
9
Calling 'ImplementationVisitor::Visit'
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
78void ImplementationVisitor::BeginGeneratedFiles() {
79 std::set<SourceId> contains_class_definitions;
80 for (const ClassType* type : TypeOracle::GetClasses()) {
81 if (type->ShouldGenerateCppClassDefinitions()) {
82 contains_class_definitions.insert(type->AttributedToFile());
83 }
84 }
85
86 for (SourceId source : SourceFileMap::AllSources()) {
87 auto& streams = GlobalContext::GeneratedPerFile(source);
88 // Output beginning of CSA .cc file.
89 {
90 cpp::File& file = streams.csa_cc;
91
92 for (const std::string& include_path : GlobalContext::CppIncludes()) {
93 file << "#include " << StringLiteralQuote(include_path) << "\n";
94 }
95
96 file << "// Required Builtins:\n";
97 file << "#include \"torque-generated/" +
98 SourceFileMap::PathFromV8RootWithoutExtension(source) +
99 "-tq-csa.h\"\n";
100 // Now that required include files are collected while generting the file,
101 // we only know the full set at the end. Insert a marker here that is
102 // replaced with the list of includes at the very end.
103 // TODO(nicohartmann@): This is not the most beautiful way to do this,
104 // replace once the cpp file builder is available, where this can be
105 // handled easily.
106 file << BuiltinIncludesMarker;
107 file << "\n";
108
109 streams.csa_cc.BeginNamespace("v8", "internal");
110 streams.csa_ccfile << "\n";
111 }
112 // Output beginning of CSA .h file.
113 {
114 cpp::File& file = streams.csa_header;
115 std::string header_define =
116 "V8_GEN_TORQUE_GENERATED_" +
117 UnderlinifyPath(SourceFileMap::PathFromV8Root(source)) + "_CSA_H_";
118 streams.csa_header.BeginIncludeGuard(header_define);
119 file << "#include \"src/builtins/torque-csa-header-includes.h\"\n";
120 file << "\n";
121
122 streams.csa_header.BeginNamespace("v8", "internal");
123 streams.csa_headerfile << "\n";
124 }
125 // Output beginning of class definition .cc file.
126 {
127 cpp::File& file = streams.class_definition_cc;
128 if (contains_class_definitions.count(source) != 0) {
129 file << "#include \""
130 << SourceFileMap::PathFromV8RootWithoutExtension(source)
131 << "-inl.h\"\n\n";
132 file << "#include \"torque-generated/class-verifiers.h\"\n";
133 file << "#include \"src/objects/instance-type-inl.h\"\n\n";
134 }
135
136 streams.class_definition_cc.BeginNamespace("v8", "internal");
137 streams.class_definition_ccfile << "\n";
138 }
139 }
140}
141
142void ImplementationVisitor::EndGeneratedFiles() {
143 for (SourceId file : SourceFileMap::AllSources()) {
144 auto& streams = GlobalContext::GeneratedPerFile(file);
145
146 // Output ending of CSA .cc file.
147 streams.csa_cc.EndNamespace("v8", "internal");
148
149 // Output ending of CSA .h file.
150 {
151 std::string header_define =
152 "V8_GEN_TORQUE_GENERATED_" +
153 UnderlinifyPath(SourceFileMap::PathFromV8Root(file)) + "_CSA_H_";
154
155 streams.csa_header.EndNamespace("v8", "internal");
156 streams.csa_headerfile << "\n";
157 streams.csa_header.EndIncludeGuard(header_define);
158 }
159
160 // Output ending of class definition .cc file.
161 streams.class_definition_cc.EndNamespace("v8", "internal");
162 }
163}
164
165void ImplementationVisitor::BeginDebugMacrosFile() {
166 // TODO(torque-builer): Can use builder for debug_macros_*_
167 std::ostream& source = debug_macros_cc_;
168 std::ostream& header = debug_macros_h_;
169
170 source << "#include \"torque-generated/debug-macros.h\"\n\n";
171 source << "#include \"src/objects/swiss-name-dictionary.h\"\n";
172 source << "#include \"src/objects/ordered-hash-table.h\"\n";
173 source << "#include \"tools/debug_helper/debug-macro-shims.h\"\n";
174 source << "#include \"include/v8-internal.h\"\n";
175 source << "\n";
176
177 source << "namespace v8 {\n"
178 << "namespace internal {\n"
179 << "namespace debug_helper_internal {\n"
180 << "\n";
181
182 const char* kHeaderDefine = "V8_GEN_TORQUE_GENERATED_DEBUG_MACROS_H_";
183 header << "#ifndef " << kHeaderDefine << "\n";
184 header << "#define " << kHeaderDefine << "\n\n";
185 header << "#include \"tools/debug_helper/debug-helper-internal.h\"\n";
186 header << "#include \"src/numbers/integer-literal.h\"\n";
187 header << "\n";
188
189 header << "namespace v8 {\n"
190 << "namespace internal {\n"
191 << "namespace debug_helper_internal {\n"
192 << "\n";
193}
194
195void ImplementationVisitor::EndDebugMacrosFile() {
196 // TODO(torque-builder): Can use builder for debug_macros_*_
197 std::ostream& source = debug_macros_cc_;
198 std::ostream& header = debug_macros_h_;
199
200 source << "} // namespace internal\n"
201 << "} // namespace v8\n"
202 << "} // namespace debug_helper_internal\n"
203 << "\n";
204
205 header << "\n} // namespace internal\n"
206 << "} // namespace v8\n"
207 << "} // namespace debug_helper_internal\n"
208 << "\n";
209 header << "#endif // V8_GEN_TORQUE_GENERATED_DEBUG_MACROS_H_\n";
210}
211
212void ImplementationVisitor::Visit(NamespaceConstant* decl) {
213 Signature signature{{}, base::nullopt, {{}, false}, 0, decl->type(),
214 {}, false};
215
216 BindingsManagersScope bindings_managers_scope;
217
218 cpp::Function f =
219 GenerateFunction(nullptr, decl->external_name(), signature, {});
220
221 f.PrintDeclaration(csa_headerfile());
222
223 f.PrintDefinition(csa_ccfile(), [&](std::ostream& stream) {
224 stream << " compiler::CodeAssembler ca_(state_);\n";
225
226 DCHECK(!signature.return_type->IsVoidOrNever())((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
245void ImplementationVisitor::Visit(TypeAlias* alias) {
246 if (alias->IsRedeclaration()) return;
247 if (const ClassType* class_type = ClassType::DynamicCast(alias->type())) {
248 if (class_type->IsExtern() && !class_type->nspace()->IsDefaultNamespace()) {
249 Error(
250 "extern classes are currently only supported in the default "
251 "namespace");
252 }
253 }
254}
255
256class ImplementationVisitor::MacroInliningScope {
257 public:
258 MacroInliningScope(ImplementationVisitor* visitor, const Macro* macro)
259 : visitor_(visitor), macro_(macro) {
260 if (!visitor_->inlining_macros_.insert(macro).second) {
261 // Recursive macro expansion would just keep going until stack overflow.
262 // To avoid crashes, throw an error immediately.
263 ReportError("Recursive macro call to ", *macro);
264 }
265 }
266 ~MacroInliningScope() { visitor_->inlining_macros_.erase(macro_); }
267
268 private:
269 ImplementationVisitor* visitor_;
270 const Macro* macro_;
271};
272
273VisitResult ImplementationVisitor::InlineMacro(
274 Macro* macro, base::Optional<LocationReference> this_reference,
275 const std::vector<VisitResult>& arguments,
276 const std::vector<Block*> label_blocks) {
277 MacroInliningScope macro_inlining_scope(this, macro);
278 CurrentScope::Scope current_scope(macro);
279 BindingsManagersScope bindings_managers_scope;
280 CurrentCallable::Scope current_callable(macro);
281 CurrentReturnValue::Scope current_return_value;
282 const Signature& signature = macro->signature();
283 const Type* return_type = macro->signature().return_type;
284 bool can_return = return_type != TypeOracle::GetNeverType();
1
Assuming the condition is false
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) {
2
Assuming the condition is false
3
Taking false branch
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) {
4
Assuming the condition is false
5
Loop condition is false. Execution continues on line 332
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
5.1
'can_return' is false
) {
6
Taking false branch
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());
7
Calling 'ImplementationVisitor::Visit'
360
361 if (result->IsNever()) {
362 if (!return_type->IsNever() && !macro->HasReturns()) {
363 std::stringstream s;
364 s << "macro " << macro->ReadableName()
365 << " that never returns must have return type never";
366 ReportError(s.str());
367 }
368 } else {
369 if (return_type->IsNever()) {
370 std::stringstream s;
371 s << "macro " << macro->ReadableName()
372 << " has implicit return at end of its declartion but return type "
373 "never";
374 ReportError(s.str());
375 } else if (!macro->signature().return_type->IsVoid()) {
376 std::stringstream s;
377 s << "macro " << macro->ReadableName()
378 << " expects to return a value but doesn't on all paths";
379 ReportError(s.str());
380 }
381 }
382 if (!result->IsNever()) {
383 assembler().Goto(macro_end);
384 }
385
386 if (macro->HasReturns() || !result->IsNever()) {
387 assembler().Bind(macro_end);
388 }
389
390 return GetAndClearReturnValue();
391}
392
393void ImplementationVisitor::VisitMacroCommon(Macro* macro) {
394 CurrentCallable::Scope current_callable(macro);
395 const Signature& signature = macro->signature();
396 const Type* return_type = macro->signature().return_type;
397 bool can_return = return_type != TypeOracle::GetNeverType();
398 bool has_return_value =
399 can_return && return_type != TypeOracle::GetVoidType();
400
401 cpp::Function f = GenerateMacroFunctionDeclaration(macro);
402 f.PrintDeclaration(csa_headerfile());
403 csa_headerfile() << "\n";
404
405 cpp::File csa_cc(csa_ccfile());
406
407 // Avoid multiple-definition errors since it is possible for multiple
408 // generated -inl.inc files to all contain function definitions for the same
409 // Torque macro.
410 base::Optional<cpp::IncludeGuardScope> include_guard;
411 if (output_type_ == OutputType::kCC) {
412 include_guard.emplace(&csa_cc, "V8_INTERNAL_DEFINED_"s + macro->CCName());
413 } else if (output_type_ == OutputType::kCCDebug) {
414 include_guard.emplace(&csa_cc,
415 "V8_INTERNAL_DEFINED_"s + macro->CCDebugName());
416 }
417
418 f.PrintBeginDefinition(csa_ccfile());
419
420 if (output_type_ == OutputType::kCC) {
421 // For now, generated C++ is only for field offset computations. If we ever
422 // generate C++ code that can allocate, then it should be handlified.
423 csa_ccfile() << " DisallowGarbageCollection no_gc;\n";
424 } else if (output_type_ == OutputType::kCSA) {
425 csa_ccfile() << " compiler::CodeAssembler ca_(state_);\n";
426 csa_ccfile()
427 << " compiler::CodeAssembler::SourcePositionScope pos_scope(&ca_);\n";
428 }
429
430 Stack<std::string> lowered_parameters;
431 Stack<const Type*> lowered_parameter_types;
432
433 std::vector<VisitResult> arguments;
434
435 base::Optional<LocationReference> this_reference;
436 if (Method* method = Method::DynamicCast(macro)) {
437 const Type* this_type = method->aggregate_type();
438 LowerParameter(this_type, ExternalParameterName(kThisParameterName),
439 &lowered_parameters);
440 StackRange range = lowered_parameter_types.PushMany(LowerType(this_type));
441 VisitResult this_result = VisitResult(this_type, range);
442 // For classes, mark 'this' as a temporary to prevent assignment to it.
443 // Note that using a VariableAccess for non-class types is technically
444 // incorrect because changes to the 'this' variable do not get reflected
445 // to the caller. Therefore struct methods should always be inlined and a
446 // C++ version should never be generated, since it would be incorrect.
447 // However, in order to be able to type- and semantics-check even unused
448 // struct methods, set the this_reference to be the local variable copy of
449 // the passed-in this, which allows the visitor to at least find and report
450 // errors.
451 this_reference =
452 (this_type->IsClassType())
453 ? LocationReference::Temporary(this_result, "this parameter")
454 : LocationReference::VariableAccess(this_result);
455 }
456
457 for (size_t i = 0; i < macro->signature().parameter_names.size(); ++i) {
458 if (this_reference && i == macro->signature().implicit_count) continue;
459 const std::string& name = macro->parameter_names()[i]->value;
460 std::string external_name = ExternalParameterName(name);
461 const Type* type = macro->signature().types()[i];
462
463 if (type->IsConstexpr()) {
464 arguments.push_back(VisitResult(type, external_name));
465 } else {
466 LowerParameter(type, external_name, &lowered_parameters);
467 StackRange range = lowered_parameter_types.PushMany(LowerType(type));
468 arguments.push_back(VisitResult(type, range));
469 }
470 }
471
472 DCHECK_EQ(lowered_parameters.Size(), lowered_parameter_types.Size())((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
542void ImplementationVisitor::Visit(TorqueMacro* macro) {
543 VisitMacroCommon(macro);
544}
545
546void ImplementationVisitor::Visit(Method* method) {
547 DCHECK(!method->IsExternal())((void) 0);
548 VisitMacroCommon(method);
549}
550
551namespace {
552
553std::string AddParameter(size_t i, Builtin* builtin,
554 Stack<std::string>* parameters,
555 Stack<const Type*>* parameter_types,
556 BlockBindings<LocalValue>* parameter_bindings,
557 bool mark_as_used) {
558 const Identifier* name = builtin->signature().parameter_names[i];
559 const Type* type = builtin->signature().types()[i];
560 std::string external_name = "parameter" + std::to_string(i);
561 parameters->Push(external_name);
562 StackRange range = parameter_types->PushMany(LowerType(type));
563 Binding<LocalValue>* binding = parameter_bindings->Add(
564 name,
565 LocalValue{LocationReference::Temporary(VisitResult(type, range),
566 "parameter " + name->value)},
567 mark_as_used);
568 if (GlobalContext::collect_kythe_data()) {
569 KytheData::AddBindingDefinition(binding);
570 }
571 return external_name;
572}
573
574} // namespace
575
576void ImplementationVisitor::Visit(Builtin* builtin) {
577 if (builtin->IsExternal()) return;
578 CurrentScope::Scope current_scope(builtin);
579 CurrentCallable::Scope current_callable(builtin);
580 CurrentReturnValue::Scope current_return_value;
581
582 const std::string& name = builtin->ExternalName();
583 const Signature& signature = builtin->signature();
584 csa_ccfile() << "TF_BUILTIN(" << name << ", CodeStubAssembler) {\n"
585 << " compiler::CodeAssemblerState* state_ = state();"
586 << " compiler::CodeAssembler ca_(state());\n";
587
588 Stack<const Type*> parameter_types;
589 Stack<std::string> parameters;
590
591 BindingsManagersScope bindings_managers_scope;
592
593 BlockBindings<LocalValue> parameter_bindings(&ValueBindingsManager::Get());
594
595 if (builtin->IsVarArgsJavaScript() || builtin->IsFixedArgsJavaScript()) {
596 if (builtin->IsVarArgsJavaScript()) {
597 DCHECK(signature.parameter_types.var_args)((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, &parameters, &parameter_types, &parameter_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, &parameters, &parameter_types,
689 &parameter_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, &parameters, &parameter_types,
706 &parameter_bindings, mark_as_used);
707 csa_ccfile() << " " << type->GetGeneratedTypeName() << " " << var
708 << " = "
709 << "UncheckedParameter<" << type->GetGeneratedTNodeTypeName()
710 << ">(Descriptor::k" << CamelifyString(parameter_name)
711 << ");\n";
712 csa_ccfile() << " USE(" << var << ");\n";
713 }
714 }
715 assembler_ = CfgAssembler(parameter_types);
716 const Type* body_result = Visit(*builtin->body());
717 if (body_result != TypeOracle::GetNeverType()) {
718 ReportError("control reaches end of builtin, expected return of a value");
719 }
720 CSAGenerator csa_generator{assembler().Result(), csa_ccfile(),
721 builtin->kind()};
722 csa_generator.EmitGraph(parameters);
723 assembler_ = base::nullopt;
724 csa_ccfile() << "}\n\n";
725}
726
727const Type* ImplementationVisitor::Visit(VarDeclarationStatement* stmt) {
728 BlockBindings<LocalValue> block_bindings(&ValueBindingsManager::Get());
729 return Visit(stmt, &block_bindings);
730}
731
732const Type* ImplementationVisitor::Visit(
733 VarDeclarationStatement* stmt, BlockBindings<LocalValue>* block_bindings) {
734 // const qualified variables are required to be initialized properly.
735 if (stmt->const_qualified && !stmt->initializer) {
736 ReportError("local constant \"", stmt->name, "\" is not initialized.");
737 }
738
739 base::Optional<const Type*> type;
740 if (stmt->type) {
741 type = TypeVisitor::ComputeType(*stmt->type);
742 }
743 base::Optional<VisitResult> init_result;
744 if (stmt->initializer) {
745 StackScope scope(this);
746 init_result = Visit(*stmt->initializer);
747 if (type) {
748 init_result = GenerateImplicitConvert(*type, *init_result);
749 }
750 type = init_result->type();
751 if ((*type)->IsConstexpr() && !stmt->const_qualified) {
752 Error("Use 'const' instead of 'let' for variable '", stmt->name->value,
753 "' of constexpr type '", (*type)->ToString(), "'.")
754 .Position(stmt->name->pos)
755 .Throw();
756 }
757 init_result = scope.Yield(*init_result);
758 } else {
759 DCHECK(type.has_value())((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
782const Type* ImplementationVisitor::Visit(TailCallStatement* stmt) {
783 return Visit(stmt->call, true).type();
784}
785
786VisitResult ImplementationVisitor::Visit(ConditionalExpression* expr) {
787 Block* true_block = assembler().NewBlock(assembler().CurrentStack());
788 Block* false_block = assembler().NewBlock(assembler().CurrentStack());
789 Block* done_block = assembler().NewBlock();
790 Block* true_conversion_block = assembler().NewBlock();
791 GenerateExpressionBranch(expr->condition, true_block, false_block);
792
793 VisitResult left;
794 VisitResult right;
795
796 {
797 // The code for both paths of the conditional need to be generated first
798 // before evaluating the conditional expression because the common type of
799 // the result of both the true and false of the condition needs to be known
800 // to convert both branches to a common type.
801 assembler().Bind(true_block);
802 StackScope left_scope(this);
803 left = Visit(expr->if_true);
804 assembler().Goto(true_conversion_block);
805
806 const Type* common_type;
807 {
808 assembler().Bind(false_block);
809 StackScope right_scope(this);
810 right = Visit(expr->if_false);
811 common_type = GetCommonType(left.type(), right.type());
812 right = right_scope.Yield(GenerateImplicitConvert(common_type, right));
813 assembler().Goto(done_block);
814 }
815
816 assembler().Bind(true_conversion_block);
817 left = left_scope.Yield(GenerateImplicitConvert(common_type, left));
818 assembler().Goto(done_block);
819 }
820
821 assembler().Bind(done_block);
822 CHECK_EQ(left, right)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
826VisitResult ImplementationVisitor::Visit(LogicalOrExpression* expr) {
827 StackScope outer_scope(this);
828 VisitResult left_result = Visit(expr->left);
829
830 if (left_result.type()->IsConstexprBool()) {
831 VisitResult right_result = Visit(expr->right);
832 if (!right_result.type()->IsConstexprBool()) {
833 ReportError(
834 "expected type constexpr bool on right-hand side of operator "
835 "||");
836 }
837 return VisitResult(TypeOracle::GetConstexprBoolType(),
838 std::string("(") + left_result.constexpr_value() +
839 " || " + right_result.constexpr_value() + ")");
840 }
841
842 Block* true_block = assembler().NewBlock();
843 Block* false_block = assembler().NewBlock();
844 Block* done_block = assembler().NewBlock();
845
846 left_result = GenerateImplicitConvert(TypeOracle::GetBoolType(), left_result);
847 GenerateBranch(left_result, true_block, false_block);
848
849 assembler().Bind(true_block);
850 VisitResult true_result = GenerateBoolConstant(true);
851 assembler().Goto(done_block);
852
853 assembler().Bind(false_block);
854 VisitResult false_result;
855 {
856 StackScope false_block_scope(this);
857 false_result = false_block_scope.Yield(
858 GenerateImplicitConvert(TypeOracle::GetBoolType(), Visit(expr->right)));
859 }
860 assembler().Goto(done_block);
861
862 assembler().Bind(done_block);
863 DCHECK_EQ(true_result, false_result)((void) 0);
864 return outer_scope.Yield(true_result);
865}
866
867VisitResult ImplementationVisitor::Visit(LogicalAndExpression* expr) {
868 StackScope outer_scope(this);
869 VisitResult left_result = Visit(expr->left);
870
871 if (left_result.type()->IsConstexprBool()) {
872 VisitResult right_result = Visit(expr->right);
873 if (!right_result.type()->IsConstexprBool()) {
874 ReportError(
875 "expected type constexpr bool on right-hand side of operator "
876 "&&");
877 }
878 return VisitResult(TypeOracle::GetConstexprBoolType(),
879 std::string("(") + left_result.constexpr_value() +
880 " && " + right_result.constexpr_value() + ")");
881 }
882
883 Block* true_block = assembler().NewBlock();
884 Block* false_block = assembler().NewBlock();
885 Block* done_block = assembler().NewBlock();
886
887 left_result = GenerateImplicitConvert(TypeOracle::GetBoolType(), left_result);
888 GenerateBranch(left_result, true_block, false_block);
889
890 assembler().Bind(true_block);
891 VisitResult true_result;
892 {
893 StackScope true_block_scope(this);
894 VisitResult right_result = Visit(expr->right);
895 if (TryGetSourceForBitfieldExpression(expr->left) != nullptr &&
896 TryGetSourceForBitfieldExpression(expr->right) != nullptr &&
897 TryGetSourceForBitfieldExpression(expr->left)->value ==
898 TryGetSourceForBitfieldExpression(expr->right)->value) {
899 Lint(
900 "Please use & rather than && when checking multiple bitfield "
901 "values, to avoid complexity in generated code.");
902 }
903 true_result = true_block_scope.Yield(
904 GenerateImplicitConvert(TypeOracle::GetBoolType(), right_result));
905 }
906 assembler().Goto(done_block);
907
908 assembler().Bind(false_block);
909 VisitResult false_result = GenerateBoolConstant(false);
910 assembler().Goto(done_block);
911
912 assembler().Bind(done_block);
913 DCHECK_EQ(true_result, false_result)((void) 0);
914 return outer_scope.Yield(true_result);
915}
916
917VisitResult ImplementationVisitor::Visit(IncrementDecrementExpression* expr) {
918 StackScope scope(this);
919 LocationReference location_ref = GetLocationReference(expr->location);
920 VisitResult current_value = GenerateFetchFromLocation(location_ref);
921 VisitResult one = {TypeOracle::GetConstInt31Type(), "1"};
922 Arguments args;
923 args.parameters = {current_value, one};
924 VisitResult assignment_value = GenerateCall(
925 expr->op == IncrementDecrementOperator::kIncrement ? "+" : "-", args);
926 GenerateAssignToLocation(location_ref, assignment_value);
927 return scope.Yield(expr->postfix ? current_value : assignment_value);
928}
929
930VisitResult ImplementationVisitor::Visit(AssignmentExpression* expr) {
931 StackScope scope(this);
932 LocationReference location_ref = GetLocationReference(expr->location);
933 VisitResult assignment_value;
934 if (expr->op) {
935 VisitResult location_value = GenerateFetchFromLocation(location_ref);
936 assignment_value = Visit(expr->value);
937 Arguments args;
938 args.parameters = {location_value, assignment_value};
939 assignment_value = GenerateCall(*expr->op, args);
940 GenerateAssignToLocation(location_ref, assignment_value);
941 } else {
942 assignment_value = Visit(expr->value);
943 GenerateAssignToLocation(location_ref, assignment_value);
944 }
945 return scope.Yield(assignment_value);
946}
947
948VisitResult ImplementationVisitor::Visit(FloatingPointLiteralExpression* expr) {
949 const Type* result_type = TypeOracle::GetConstFloat64Type();
950 std::stringstream str;
951 str << std::setprecision(std::numeric_limits<double>::digits10 + 1)
952 << expr->value;
953 return VisitResult{result_type, str.str()};
954}
955
956VisitResult ImplementationVisitor::Visit(IntegerLiteralExpression* expr) {
957 const Type* result_type = TypeOracle::GetIntegerLiteralType();
958 std::stringstream str;
959 str << "IntegerLiteral("
960 << (expr->value.is_negative() ? "true, 0x" : "false, 0x") << std::hex
961 << expr->value.absolute_value() << std::dec << "ull)";
962 return VisitResult{result_type, str.str()};
963}
964
965VisitResult ImplementationVisitor::Visit(AssumeTypeImpossibleExpression* expr) {
966 VisitResult result = Visit(expr->expression);
967 const Type* result_type = SubtractType(
968 result.type(), TypeVisitor::ComputeType(expr->excluded_type));
969 if (result_type->IsNever()) {
970 ReportError("unreachable code");
971 }
972 CHECK_EQ(LowerType(result_type), TypeVector{result_type})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
978VisitResult ImplementationVisitor::Visit(StringLiteralExpression* expr) {
979 return VisitResult{
980 TypeOracle::GetConstStringType(),
981 "\"" + expr->literal.substr(1, expr->literal.size() - 2) + "\""};
982}
983
984VisitResult ImplementationVisitor::GetBuiltinCode(Builtin* builtin) {
985 if (builtin->IsExternal() || builtin->kind() != Builtin::kStub) {
986 ReportError(
987 "creating function pointers is only allowed for internal builtins with "
988 "stub linkage");
989 }
990 const Type* type = TypeOracle::GetBuiltinPointerType(
991 builtin->signature().parameter_types.types,
992 builtin->signature().return_type);
993 assembler().Emit(
994 PushBuiltinPointerInstruction{builtin->ExternalName(), type});
995 return VisitResult(type, assembler().TopRange(1));
996}
997
998VisitResult ImplementationVisitor::Visit(LocationExpression* expr) {
999 StackScope scope(this);
1000 return scope.Yield(GenerateFetchFromLocation(GetLocationReference(expr)));
1001}
1002
1003VisitResult ImplementationVisitor::Visit(FieldAccessExpression* expr) {
1004 StackScope scope(this);
1005 LocationReference location = GetLocationReference(expr);
1006 if (location.IsBitFieldAccess()) {
1007 if (auto* identifier = IdentifierExpression::DynamicCast(expr->object)) {
1008 bitfield_expressions_[expr] = identifier->name;
1009 }
1010 }
1011 return scope.Yield(GenerateFetchFromLocation(location));
1012}
1013
1014const Type* ImplementationVisitor::Visit(GotoStatement* stmt) {
1015 Binding<LocalLabel>* label = LookupLabel(stmt->label->value);
1016 size_t parameter_count = label->parameter_types.size();
1017 if (stmt->arguments.size() != parameter_count) {
1018 ReportError("goto to label has incorrect number of parameters (expected ",
1019 parameter_count, " found ", stmt->arguments.size(), ")");
1020 }
1021
1022 if (GlobalContext::collect_language_server_data()) {
1023 LanguageServerData::AddDefinition(stmt->label->pos,
1024 label->declaration_position());
1025 }
1026 if (GlobalContext::collect_kythe_data()) {
1027 KytheData::AddBindingUse(stmt->label->pos, label);
1028 }
1029
1030 size_t i = 0;
1031 StackRange arguments = assembler().TopRange(0);
1032 for (Expression* e : stmt->arguments) {
1033 StackScope scope(this);
1034 VisitResult result = Visit(e);
1035 const Type* parameter_type = label->parameter_types[i++];
1036 result = GenerateImplicitConvert(parameter_type, result);
1037 arguments.Extend(scope.Yield(result).stack_range());
1038 }
1039
1040 assembler().Goto(label->block, arguments.Size());
1041 return TypeOracle::GetNeverType();
1042}
1043
1044const Type* ImplementationVisitor::Visit(IfStatement* stmt) {
1045 bool has_else = stmt->if_false.has_value();
1046
1047 if (stmt->is_constexpr) {
1048 VisitResult expression_result = Visit(stmt->condition);
1049
1050 if (!(expression_result.type() == TypeOracle::GetConstexprBoolType())) {
1051 std::stringstream stream;
1052 stream << "expression should return type constexpr bool "
1053 << "but returns type " << *expression_result.type();
1054 ReportError(stream.str());
1055 }
1056
1057 Block* true_block = assembler().NewBlock();
1058 Block* false_block = assembler().NewBlock();
1059 Block* done_block = assembler().NewBlock();
1060
1061 assembler().Emit(ConstexprBranchInstruction{
1062 expression_result.constexpr_value(), true_block, false_block});
1063
1064 assembler().Bind(true_block);
1065 const Type* left_result = Visit(stmt->if_true);
1066 if (left_result == TypeOracle::GetVoidType()) {
1067 assembler().Goto(done_block);
1068 }
1069
1070 assembler().Bind(false_block);
1071 const Type* right_result = TypeOracle::GetVoidType();
1072 if (has_else) {
1073 right_result = Visit(*stmt->if_false);
1074 }
1075 if (right_result == TypeOracle::GetVoidType()) {
1076 assembler().Goto(done_block);
1077 }
1078
1079 if (left_result->IsNever() != right_result->IsNever()) {
1080 std::stringstream stream;
1081 stream << "either both or neither branches in a constexpr if statement "
1082 "must reach their end at"
1083 << PositionAsString(stmt->pos);
1084 ReportError(stream.str());
1085 }
1086
1087 if (left_result != TypeOracle::GetNeverType()) {
1088 assembler().Bind(done_block);
1089 }
1090 return left_result;
1091 } else {
1092 Block* true_block = assembler().NewBlock(assembler().CurrentStack(),
1093 IsDeferred(stmt->if_true));
1094 Block* false_block =
1095 assembler().NewBlock(assembler().CurrentStack(),
1096 stmt->if_false && IsDeferred(*stmt->if_false));
1097 GenerateExpressionBranch(stmt->condition, true_block, false_block);
1098
1099 Block* done_block;
1100 bool live = false;
1101 if (has_else) {
1102 done_block = assembler().NewBlock();
1103 } else {
1104 done_block = false_block;
1105 live = true;
1106 }
1107
1108 assembler().Bind(true_block);
1109 {
1110 const Type* result = Visit(stmt->if_true);
1111 if (result == TypeOracle::GetVoidType()) {
1112 live = true;
1113 assembler().Goto(done_block);
1114 }
1115 }
1116
1117 if (has_else) {
1118 assembler().Bind(false_block);
1119 const Type* result = Visit(*stmt->if_false);
1120 if (result == TypeOracle::GetVoidType()) {
1121 live = true;
1122 assembler().Goto(done_block);
1123 }
1124 }
1125
1126 if (live) {
1127 assembler().Bind(done_block);
1128 }
1129 return live ? TypeOracle::GetVoidType() : TypeOracle::GetNeverType();
1130 }
1131}
1132
1133const Type* ImplementationVisitor::Visit(WhileStatement* stmt) {
1134 Block* body_block = assembler().NewBlock(assembler().CurrentStack());
1135 Block* exit_block = assembler().NewBlock(assembler().CurrentStack());
1136
1137 Block* header_block = assembler().NewBlock();
1138 assembler().Goto(header_block);
1139
1140 assembler().Bind(header_block);
1141 GenerateExpressionBranch(stmt->condition, body_block, exit_block);
1142
1143 assembler().Bind(body_block);
1144 {
1145 BreakContinueActivator activator{exit_block, header_block};
1146 const Type* body_result = Visit(stmt->body);
1147 if (body_result != TypeOracle::GetNeverType()) {
1148 assembler().Goto(header_block);
1149 }
1150 }
1151
1152 assembler().Bind(exit_block);
1153 return TypeOracle::GetVoidType();
1154}
1155
1156const Type* ImplementationVisitor::Visit(BlockStatement* block) {
1157 BlockBindings<LocalValue> block_bindings(&ValueBindingsManager::Get());
1158 const Type* type = TypeOracle::GetVoidType();
1159 for (Statement* s : block->statements) {
1160 CurrentSourcePosition::Scope source_position(s->pos);
1161 if (type->IsNever()) {
1162 ReportError("statement after non-returning statement");
1163 }
1164 if (auto* var_declaration = VarDeclarationStatement::DynamicCast(s)) {
1165 type = Visit(var_declaration, &block_bindings);
1166 } else {
1167 type = Visit(s);
1168 }
1169 }
1170 return type;
1171}
1172
1173const Type* ImplementationVisitor::Visit(DebugStatement* stmt) {
1174#if defined(DEBUG)
1175 assembler().Emit(PrintConstantStringInstruction{"halting because of '" +
1176 stmt->reason + "' at " +
1177 PositionAsString(stmt->pos)});
1178#endif
1179 assembler().Emit(AbortInstruction{stmt->never_continues
1180 ? AbortInstruction::Kind::kUnreachable
1181 : AbortInstruction::Kind::kDebugBreak});
1182 if (stmt->never_continues) {
1183 return TypeOracle::GetNeverType();
1184 } else {
1185 return TypeOracle::GetVoidType();
1186 }
1187}
1188
1189namespace {
1190
1191std::string FormatAssertSource(const std::string& str) {
1192 // Replace all whitespace characters with a space character.
1193 std::string str_no_newlines = str;
1194 std::replace_if(
1195 str_no_newlines.begin(), str_no_newlines.end(),
1196 [](unsigned char c) { return isspace(c); }, ' ');
1197
1198 // str might include indentation, squash multiple space characters into one.
1199 std::string result;
1200 std::unique_copy(str_no_newlines.begin(), str_no_newlines.end(),
1201 std::back_inserter(result),
1202 [](char a, char b) { return a == ' ' && b == ' '; });
1203 return result;
1204}
1205
1206} // namespace
1207
1208const Type* ImplementationVisitor::Visit(AssertStatement* stmt) {
1209 if (stmt->kind == AssertStatement::AssertKind::kStaticAssert) {
1210 std::string message =
1211 "static_assert(" + stmt->source + ") at " + ToString(stmt->pos);
1212 GenerateCall(QualifiedName({"", TORQUE_INTERNAL_NAMESPACE_STRING},
1213 STATIC_ASSERT_MACRO_STRING),
1214 Arguments{{Visit(stmt->expression),
1215 VisitResult(TypeOracle::GetConstexprStringType(),
1216 StringLiteralQuote(message))},
1217 {}});
1218 return TypeOracle::GetVoidType();
1219 }
1220 bool do_check = stmt->kind != AssertStatement::AssertKind::kDcheck ||
1221 GlobalContext::force_assert_statements();
1222#if defined(DEBUG)
1223 do_check = true;
1224#endif
1225 Block* resume_block;
1226
1227 if (!do_check) {
1228 Block* unreachable_block = assembler().NewBlock(assembler().CurrentStack());
1229 resume_block = assembler().NewBlock(assembler().CurrentStack());
1230 assembler().Goto(resume_block);
1231 assembler().Bind(unreachable_block);
1232 }
1233
1234 // CSA_DCHECK & co. are not used here on purpose for two reasons. First,
1235 // Torque allows and handles two types of expressions in the if protocol
1236 // automagically, ones that return TNode<BoolT> and those that use the
1237 // BranchIf(..., Label* true, Label* false) idiom. Because the machinery to
1238 // handle this is embedded in the expression handling and to it's not
1239 // possible to make the decision to use CSA_DCHECK or CSA_DCHECK_BRANCH
1240 // isn't trivial up-front. Secondly, on failure, the assert text should be
1241 // the corresponding Torque code, not the -gen.cc code, which would be the
1242 // case when using CSA_DCHECK_XXX.
1243 Block* true_block = assembler().NewBlock(assembler().CurrentStack());
1244 Block* false_block = assembler().NewBlock(assembler().CurrentStack(), true);
1245 GenerateExpressionBranch(stmt->expression, true_block, false_block);
1246
1247 assembler().Bind(false_block);
1248
1249 assembler().Emit(AbortInstruction{
1250 AbortInstruction::Kind::kAssertionFailure,
1251 "Torque assert '" + FormatAssertSource(stmt->source) + "' failed"});
1252
1253 assembler().Bind(true_block);
1254
1255 if (!do_check) {
1256 assembler().Bind(resume_block);
1257 }
1258
1259 return TypeOracle::GetVoidType();
1260}
1261
1262const Type* ImplementationVisitor::Visit(ExpressionStatement* stmt) {
1263 const Type* type = Visit(stmt->expression).type();
1264 return type->IsNever() ? type : TypeOracle::GetVoidType();
1265}
1266
1267const Type* ImplementationVisitor::Visit(ReturnStatement* stmt) {
1268 Callable* current_callable = CurrentCallable::Get();
1269 if (current_callable->signature().return_type->IsNever()) {
10
Assuming the condition is false
1270 std::stringstream s;
1271 s << "cannot return from a function with return type never";
1272 ReportError(s.str());
1273 }
1274 LocalLabel* end =
11
Taking false branch
13
'end' initialized to a null pointer value
1275 current_callable->IsMacro() ? LookupLabel(kMacroEndLabelName) : nullptr;
12
'?' condition is false
1276 if (current_callable->HasReturnValue()) {
14
Taking false branch
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) {
15
Assuming the condition is false
16
Taking false branch
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);
17
Passing null pointer value via 1st parameter 'label'
18
Calling 'ImplementationVisitor::GenerateLabelGoto'
1309 }
1310 current_callable->IncrementReturns();
1311 return TypeOracle::GetNeverType();
1312}
1313
1314VisitResult ImplementationVisitor::Visit(TryLabelExpression* expr) {
1315 size_t parameter_count = expr->label_block->parameters.names.size();
1316 std::vector<VisitResult> parameters;
1317
1318 Block* label_block = nullptr;
1319 Block* done_block = assembler().NewBlock();
1320 VisitResult try_result;
1321
1322 {
1323 CurrentSourcePosition::Scope source_position(expr->label_block->pos);
1324 if (expr->label_block->parameters.has_varargs) {
1325 ReportError("cannot use ... for label parameters");
1326 }
1327 Stack<const Type*> label_input_stack = assembler().CurrentStack();
1328 TypeVector parameter_types;
1329 for (size_t i = 0; i < parameter_count; ++i) {
1330 const Type* type =
1331 TypeVisitor::ComputeType(expr->label_block->parameters.types[i]);
1332 parameter_types.push_back(type);
1333 if (type->IsConstexpr()) {
1334 ReportError("no constexpr type allowed for label arguments");
1335 }
1336 StackRange range = label_input_stack.PushMany(LowerType(type));
1337 parameters.push_back(VisitResult(type, range));
1338 }
1339 label_block = assembler().NewBlock(label_input_stack,
1340 IsDeferred(expr->label_block->body));
1341
1342 Binding<LocalLabel> label_binding{&LabelBindingsManager::Get(),
1343 expr->label_block->label,
1344 LocalLabel{label_block, parameter_types}};
1345
1346 // Visit try
1347 StackScope stack_scope(this);
1348 try_result = Visit(expr->try_expression);
1349 if (try_result.type() != TypeOracle::GetNeverType()) {
1350 try_result = stack_scope.Yield(try_result);
1351 assembler().Goto(done_block);
1352 }
1353 }
1354
1355 // Visit and output the code for the label block. If the label block falls
1356 // through, then the try must not return a value. Also, if the try doesn't
1357 // fall through, but the label does, then overall the try-label block
1358 // returns type void.
1359 assembler().Bind(label_block);
1360 const Type* label_result;
1361 {
1362 BlockBindings<LocalValue> parameter_bindings(&ValueBindingsManager::Get());
1363 for (size_t i = 0; i < parameter_count; ++i) {
1364 Identifier* name = expr->label_block->parameters.names[i];
1365 parameter_bindings.Add(name,
1366 LocalValue{LocationReference::Temporary(
1367 parameters[i], "parameter " + name->value)});
1368 }
1369
1370 label_result = Visit(expr->label_block->body);
1371 }
1372 if (!try_result.type()->IsVoidOrNever() && label_result->IsVoid()) {
1373 ReportError(
1374 "otherwise clauses cannot fall through in a non-void expression");
1375 }
1376 if (label_result != TypeOracle::GetNeverType()) {
1377 assembler().Goto(done_block);
1378 }
1379 if (label_result->IsVoid() && try_result.type()->IsNever()) {
1380 try_result =
1381 VisitResult(TypeOracle::GetVoidType(), try_result.stack_range());
1382 }
1383
1384 if (!try_result.type()->IsNever()) {
1385 assembler().Bind(done_block);
1386 }
1387 return try_result;
1388}
1389
1390VisitResult ImplementationVisitor::Visit(StatementExpression* expr) {
1391 return VisitResult{Visit(expr->statement), assembler().TopRange(0)};
1392}
1393
1394InitializerResults ImplementationVisitor::VisitInitializerResults(
1395 const ClassType* class_type,
1396 const std::vector<NameAndExpression>& initializers) {
1397 InitializerResults result;
1398 for (const NameAndExpression& initializer : initializers) {
1399 result.names.push_back(initializer.name);
1400 Expression* e = initializer.expression;
1401 const Field& field = class_type->LookupField(initializer.name->value);
1402 bool has_index = field.index.has_value();
1403 if (SpreadExpression* s = SpreadExpression::DynamicCast(e)) {
1404 if (!has_index) {
1405 ReportError(
1406 "spread expressions can only be used to initialize indexed class "
1407 "fields ('",
1408 initializer.name->value, "' is not)");
1409 }
1410 e = s->spreadee;
1411 } else if (has_index) {
1412 ReportError("the indexed class field '", initializer.name->value,
1413 "' must be initialized with a spread operator");
1414 }
1415 result.field_value_map[field.name_and_type.name] = Visit(e);
1416 }
1417 return result;
1418}
1419
1420LocationReference ImplementationVisitor::GenerateFieldReference(
1421 VisitResult object, const Field& field, const ClassType* class_type,
1422 bool treat_optional_as_indexed) {
1423 if (field.index.has_value()) {
1424 LocationReference slice = LocationReference::HeapSlice(
1425 GenerateCall(class_type->GetSliceMacroName(field), {{object}, {}}));
1426 if (field.index->optional && !treat_optional_as_indexed) {
1427 // This field was declared using optional syntax, so any reference to it
1428 // is implicitly a reference to the first item.
1429 return GenerateReferenceToItemInHeapSlice(
1430 slice, {TypeOracle::GetConstInt31Type(), "0"});
1431 } else {
1432 return slice;
1433 }
1434 }
1435 DCHECK(field.offset.has_value())((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.
1449LocationReference ImplementationVisitor::GenerateFieldReferenceForInit(
1450 VisitResult object, const Field& field,
1451 const LayoutForInitialization& layout) {
1452 StackRange result_range = assembler().TopRange(0);
1453 result_range.Extend(GenerateCopy(object).stack_range());
1454 VisitResult offset = GenerateImplicitConvert(
1455 TypeOracle::GetIntPtrType(), layout.offsets.at(field.name_and_type.name));
1456 result_range.Extend(offset.stack_range());
1457 if (field.index) {
1458 VisitResult length =
1459 GenerateCopy(layout.array_lengths.at(field.name_and_type.name));
1460 result_range.Extend(length.stack_range());
1461 const Type* slice_type =
1462 TypeOracle::GetMutableSliceType(field.name_and_type.type);
1463 return LocationReference::HeapSlice(VisitResult(slice_type, result_range));
1464 } else {
1465 // Const fields are writable during initialization.
1466 VisitResult heap_reference(
1467 TypeOracle::GetMutableReferenceType(field.name_and_type.type),
1468 result_range);
1469 return LocationReference::HeapReference(heap_reference);
1470 }
1471}
1472
1473void ImplementationVisitor::InitializeClass(
1474 const ClassType* class_type, VisitResult allocate_result,
1475 const InitializerResults& initializer_results,
1476 const LayoutForInitialization& layout) {
1477 if (const ClassType* super = class_type->GetSuperClass()) {
1478 InitializeClass(super, allocate_result, initializer_results, layout);
1479 }
1480
1481 for (Field f : class_type->fields()) {
1482 VisitResult initializer_value =
1483 initializer_results.field_value_map.at(f.name_and_type.name);
1484 LocationReference field =
1485 GenerateFieldReferenceForInit(allocate_result, f, layout);
1486 if (f.index) {
1487 DCHECK(field.IsHeapSlice())((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
1498VisitResult ImplementationVisitor::GenerateArrayLength(
1499 Expression* array_length, Namespace* nspace,
1500 const std::map<std::string, LocalValue>& bindings) {
1501 StackScope stack_scope(this);
1502 CurrentSourcePosition::Scope pos_scope(array_length->pos);
1503 // Switch to the namespace where the class was declared.
1504 CurrentScope::Scope current_scope_scope(nspace);
1505 // Reset local bindings and install local binding for the preceding fields.
1506 BindingsManagersScope bindings_managers_scope;
1507 BlockBindings<LocalValue> field_bindings(&ValueBindingsManager::Get());
1508 for (auto& p : bindings) {
1509 field_bindings.Add(p.first, LocalValue{p.second}, true);
1510 }
1511 VisitResult length = Visit(array_length);
1512 VisitResult converted_length =
1513 GenerateCall("Convert", Arguments{{length}, {}},
1514 {TypeOracle::GetIntPtrType(), length.type()}, false);
1515 return stack_scope.Yield(converted_length);
1516}
1517
1518VisitResult ImplementationVisitor::GenerateArrayLength(VisitResult object,
1519 const Field& field) {
1520 DCHECK(field.index)((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
1551VisitResult 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
1574LayoutForInitialization ImplementationVisitor::GenerateLayoutForInitialization(
1575 const ClassType* class_type,
1576 const InitializerResults& initializer_results) {
1577 LayoutForInitialization layout;
1578 VisitResult offset;
1579 for (Field f : class_type->ComputeAllFields()) {
1580 if (f.offset.has_value()) {
1581 offset =
1582 VisitResult(TypeOracle::GetConstInt31Type(), ToString(*f.offset));
1583 }
1584 layout.offsets[f.name_and_type.name] = offset;
1585 if (f.index) {
1586 size_t element_size;
1587 std::string element_size_string;
1588 std::tie(element_size, element_size_string) =
1589 *SizeOf(f.name_and_type.type);
1590 VisitResult array_element_size =
1591 VisitResult(TypeOracle::GetConstInt31Type(), element_size_string);
1592 VisitResult array_length =
1593 GenerateArrayLength(class_type, initializer_results, f);
1594 layout.array_lengths[f.name_and_type.name] = array_length;
1595 Arguments arguments;
1596 arguments.parameters = {offset, array_length, array_element_size};
1597 offset = GenerateCall(QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING},
1598 "AddIndexedFieldSizeToObjectSize"),
1599 arguments);
1600 } else {
1601 DCHECK(f.offset.has_value())((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
1621VisitResult ImplementationVisitor::Visit(NewExpression* expr) {
1622 StackScope stack_scope(this);
1623 const Type* type = TypeVisitor::ComputeType(expr->type);
1624 const ClassType* class_type = ClassType::DynamicCast(type);
1625 if (class_type == nullptr) {
1626 ReportError("type for new expression must be a class, \"", *type,
1627 "\" is not");
1628 }
1629
1630 if (!class_type->AllowInstantiation()) {
1631 // Classes that are only used for testing should never be instantiated.
1632 ReportError(*class_type,
1633 " cannot be allocated with new (it's used for testing)");
1634 }
1635
1636 InitializerResults initializer_results =
1637 VisitInitializerResults(class_type, expr->initializers);
1638
1639 const Field& map_field = class_type->LookupField("map");
1640 if (*map_field.offset != 0) {
1641 ReportError("class initializers must have a map as first parameter");
1642 }
1643 const std::map<std::string, VisitResult>& initializer_fields =
1644 initializer_results.field_value_map;
1645 auto it_object_map = initializer_fields.find(map_field.name_and_type.name);
1646 VisitResult object_map;
1647 if (class_type->IsExtern()) {
1648 if (it_object_map == initializer_fields.end()) {
1649 ReportError("Constructor for ", class_type->name(),
1650 " needs Map argument!");
1651 }
1652 object_map = it_object_map->second;
1653 } else {
1654 if (it_object_map != initializer_fields.end()) {
1655 ReportError(
1656 "Constructor for ", class_type->name(),
1657 " must not specify Map argument; it is automatically inserted.");
1658 }
1659 Arguments get_struct_map_arguments;
1660 get_struct_map_arguments.parameters.push_back(
1661 VisitResult(TypeOracle::GetConstexprInstanceTypeType(),
1662 CapifyStringWithUnderscores(class_type->name()) + "_TYPE"));
1663 object_map = GenerateCall(
1664 QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING}, "GetInstanceTypeMap"),
1665 get_struct_map_arguments, {}, false);
1666 CurrentSourcePosition::Scope current_pos(expr->pos);
1667 initializer_results.names.insert(initializer_results.names.begin(),
1668 MakeNode<Identifier>("map"));
1669 initializer_results.field_value_map[map_field.name_and_type.name] =
1670 object_map;
1671 }
1672
1673 CheckInitializersWellformed(class_type->name(),
1674 class_type->ComputeAllFields(),
1675 expr->initializers, !class_type->IsExtern());
1676
1677 LayoutForInitialization layout =
1678 GenerateLayoutForInitialization(class_type, initializer_results);
1679
1680 Arguments allocate_arguments;
1681 allocate_arguments.parameters.push_back(layout.size);
1682 allocate_arguments.parameters.push_back(object_map);
1683 allocate_arguments.parameters.push_back(
1684 GenerateBoolConstant(expr->pretenured));
1685 VisitResult allocate_result = GenerateCall(
1686 QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING}, "AllocateFromNew"),
1687 allocate_arguments, {class_type}, false);
1688 DCHECK(allocate_result.IsOnStack())((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
1696const Type* ImplementationVisitor::Visit(BreakStatement* stmt) {
1697 base::Optional<Binding<LocalLabel>*> break_label =
1698 TryLookupLabel(kBreakLabelName);
1699 if (!break_label) {
1700 ReportError("break used outside of loop");
1701 }
1702 assembler().Goto((*break_label)->block);
1703 return TypeOracle::GetNeverType();
1704}
1705
1706const Type* ImplementationVisitor::Visit(ContinueStatement* stmt) {
1707 base::Optional<Binding<LocalLabel>*> continue_label =
1708 TryLookupLabel(kContinueLabelName);
1709 if (!continue_label) {
1710 ReportError("continue used outside of loop");
1711 }
1712 assembler().Goto((*continue_label)->block);
1713 return TypeOracle::GetNeverType();
1714}
1715
1716const Type* ImplementationVisitor::Visit(ForLoopStatement* stmt) {
1717 BlockBindings<LocalValue> loop_bindings(&ValueBindingsManager::Get());
1718
1719 if (stmt->var_declaration) Visit(*stmt->var_declaration, &loop_bindings);
1720
1721 Block* body_block = assembler().NewBlock(assembler().CurrentStack());
1722 Block* exit_block = assembler().NewBlock(assembler().CurrentStack());
1723
1724 Block* header_block = assembler().NewBlock();
1725 assembler().Goto(header_block);
1726 assembler().Bind(header_block);
1727
1728 // The continue label is where "continue" statements jump to. If no action
1729 // expression is provided, we jump directly to the header.
1730 Block* continue_block = header_block;
1731
1732 // The action label is only needed when an action expression was provided.
1733 Block* action_block = nullptr;
1734 if (stmt->action) {
1735 action_block = assembler().NewBlock();
1736
1737 // The action expression needs to be executed on a continue.
1738 continue_block = action_block;
1739 }
1740
1741 if (stmt->test) {
1742 GenerateExpressionBranch(*stmt->test, body_block, exit_block);
1743 } else {
1744 assembler().Goto(body_block);
1745 }
1746
1747 assembler().Bind(body_block);
1748 {
1749 BreakContinueActivator activator(exit_block, continue_block);
1750 const Type* body_result = Visit(stmt->body);
1751 if (body_result != TypeOracle::GetNeverType()) {
1752 assembler().Goto(continue_block);
1753 }
1754 }
1755
1756 if (stmt->action) {
1757 assembler().Bind(action_block);
1758 const Type* action_result = Visit(*stmt->action);
1759 if (action_result != TypeOracle::GetNeverType()) {
1760 assembler().Goto(header_block);
1761 }
1762 }
1763
1764 assembler().Bind(exit_block);
1765 return TypeOracle::GetVoidType();
1766}
1767
1768VisitResult ImplementationVisitor::Visit(SpreadExpression* expr) {
1769 ReportError(
1770 "spread operators are only currently supported in indexed class field "
1771 "initialization expressions");
1772}
1773
1774void ImplementationVisitor::GenerateImplementation(const std::string& dir) {
1775 for (SourceId file : SourceFileMap::AllSources()) {
1776 std::string base_filename =
1777 dir + "/" + SourceFileMap::PathFromV8RootWithoutExtension(file);
1778 GlobalContext::PerFileStreams& streams =
1779 GlobalContext::GeneratedPerFile(file);
1780
1781 std::string csa_cc = streams.csa_ccfile.str();
1782 // Insert missing builtin includes where the marker is.
1783 {
1784 auto pos = csa_cc.find(BuiltinIncludesMarker);
1785 CHECK_NE(pos, std::string::npos)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
1814cpp::Function ImplementationVisitor::GenerateMacroFunctionDeclaration(
1815 Macro* macro) {
1816 return GenerateFunction(nullptr,
1817 output_type_ == OutputType::kCC
1818 ? macro->CCName()
1819 : output_type_ == OutputType::kCCDebug
1820 ? macro->CCDebugName()
1821 : macro->ExternalName(),
1822 macro->signature(), macro->parameter_names());
1823}
1824
1825cpp::Function ImplementationVisitor::GenerateFunction(
1826 cpp::Class* owner, const std::string& name, const Signature& signature,
1827 const NameVector& parameter_names, bool pass_code_assembler_state,
1828 std::vector<std::string>* generated_parameter_names) {
1829 cpp::Function f(owner, name);
1830 f.SetInline(output_type_ == OutputType::kCC);
1831
1832 // Set return type.
1833 // TODO(torque-builder): Consider an overload of SetReturnType that handles
1834 // this.
1835 if (signature.return_type->IsVoidOrNever()) {
1836 f.SetReturnType("void");
1837 } else if (output_type_ == OutputType::kCCDebug) {
1838 f.SetReturnType(std::string("Value<") +
1839 signature.return_type->GetDebugType() + ">");
1840 } else if (output_type_ == OutputType::kCC) {
1841 f.SetReturnType(signature.return_type->GetRuntimeType());
1842 } else {
1843 DCHECK_EQ(output_type_, OutputType::kCSA)((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
1909namespace {
1910
1911void FailCallableLookup(
1912 const std::string& reason, const QualifiedName& name,
1913 const TypeVector& parameter_types,
1914 const std::vector<Binding<LocalLabel>*>& labels,
1915 const std::vector<Signature>& candidates,
1916 const std::vector<std::pair<GenericCallable*, std::string>>
1917 inapplicable_generics) {
1918 std::stringstream stream;
1919 stream << "\n" << reason << ": \n " << name << "(" << parameter_types << ")";
1920 if (labels.size() != 0) {
1921 stream << " labels ";
1922 for (size_t i = 0; i < labels.size(); ++i) {
1923 stream << labels[i]->name() << "(" << labels[i]->parameter_types << ")";
1924 }
1925 }
1926 stream << "\ncandidates are:";
1927 for (const Signature& signature : candidates) {
1928 stream << "\n " << name;
1929 PrintSignature(stream, signature, false);
1930 }
1931 if (inapplicable_generics.size() != 0) {
1932 stream << "\nfailed to instantiate all of these generic declarations:";
1933 for (auto& failure : inapplicable_generics) {
1934 GenericCallable* generic = failure.first;
1935 const std::string& fail_reason = failure.second;
1936 stream << "\n " << generic->name() << " defined at "
1937 << PositionAsString(generic->Position()) << ":\n "
1938 << fail_reason << "\n";
1939 }
1940 }
1941 ReportError(stream.str());
1942}
1943
1944Callable* GetOrCreateSpecialization(
1945 const SpecializationKey<GenericCallable>& key) {
1946 if (base::Optional<Callable*> specialization =
1947 key.generic->GetSpecialization(key.specialized_types)) {
1948 return *specialization;
1949 }
1950 return DeclarationVisitor::SpecializeImplicit(key);
1951}
1952
1953} // namespace
1954
1955base::Optional<Binding<LocalValue>*> ImplementationVisitor::TryLookupLocalValue(
1956 const std::string& name) {
1957 return ValueBindingsManager::Get().TryLookup(name);
1958}
1959
1960base::Optional<Binding<LocalLabel>*> ImplementationVisitor::TryLookupLabel(
1961 const std::string& name) {
1962 return LabelBindingsManager::Get().TryLookup(name);
1963}
1964
1965Binding<LocalLabel>* ImplementationVisitor::LookupLabel(
1966 const std::string& name) {
1967 base::Optional<Binding<LocalLabel>*> label = TryLookupLabel(name);
1968 if (!label) ReportError("cannot find label ", name);
1969 return *label;
1970}
1971
1972Block* ImplementationVisitor::LookupSimpleLabel(const std::string& name) {
1973 LocalLabel* label = LookupLabel(name);
1974 if (!label->parameter_types.empty()) {
1975 ReportError("label ", name,
1976 "was expected to have no parameters, but has parameters (",
1977 label->parameter_types, ")");
1978 }
1979 return label->block;
1980}
1981
1982// Try to lookup a callable with the provided argument types. Do not report
1983// an error if no matching callable was found, but return false instead.
1984// This is used to test the presence of overloaded field accessors.
1985bool ImplementationVisitor::TestLookupCallable(
1986 const QualifiedName& name, const TypeVector& parameter_types) {
1987 return LookupCallable(name, Declarations::TryLookup(name), parameter_types,
1988 {}, {}, true) != nullptr;
1989}
1990
1991TypeArgumentInference ImplementationVisitor::InferSpecializationTypes(
1992 GenericCallable* generic, const TypeVector& explicit_specialization_types,
1993 const TypeVector& explicit_arguments) {
1994 std::vector<base::Optional<const Type*>> all_arguments;
1995 const ParameterList& parameters = generic->declaration()->parameters;
1996 for (size_t i = 0; i < parameters.implicit_count; ++i) {
1997 base::Optional<Binding<LocalValue>*> val =
1998 TryLookupLocalValue(parameters.names[i]->value);
1999 all_arguments.push_back(
2000 val ? (*val)->GetLocationReference(*val).ReferencedType()
2001 : base::nullopt);
2002 }
2003 for (const Type* explicit_argument : explicit_arguments) {
2004 all_arguments.push_back(explicit_argument);
2005 }
2006 return generic->InferSpecializationTypes(explicit_specialization_types,
2007 all_arguments);
2008}
2009
2010template <class Container>
2011Callable* ImplementationVisitor::LookupCallable(
2012 const QualifiedName& name, const Container& declaration_container,
2013 const TypeVector& parameter_types,
2014 const std::vector<Binding<LocalLabel>*>& labels,
2015 const TypeVector& specialization_types, bool silence_errors) {
2016 Callable* result = nullptr;
2017
2018 std::vector<Declarable*> overloads;
2019 std::vector<Signature> overload_signatures;
2020 std::vector<std::pair<GenericCallable*, std::string>> inapplicable_generics;
2021 for (auto* declarable : declaration_container) {
2022 if (GenericCallable* generic = GenericCallable::DynamicCast(declarable)) {
2023 TypeArgumentInference inference = InferSpecializationTypes(
2024 generic, specialization_types, parameter_types);
2025 if (inference.HasFailed()) {
2026 inapplicable_generics.push_back(
2027 std::make_pair(generic, inference.GetFailureReason()));
2028 continue;
2029 }
2030 overloads.push_back(generic);
2031 overload_signatures.push_back(
2032 DeclarationVisitor::MakeSpecializedSignature(
2033 SpecializationKey<GenericCallable>{generic,
2034 inference.GetResult()}));
2035 } else if (Callable* callable = Callable::DynamicCast(declarable)) {
2036 overloads.push_back(callable);
2037 overload_signatures.push_back(callable->signature());
2038 }
2039 }
2040 // Indices of candidates in overloads/overload_signatures.
2041 std::vector<size_t> candidates;
2042 for (size_t i = 0; i < overloads.size(); ++i) {
2043 const Signature& signature = overload_signatures[i];
2044 if (IsCompatibleSignature(signature, parameter_types, labels.size())) {
2045 candidates.push_back(i);
2046 }
2047 }
2048
2049 if (overloads.empty() && inapplicable_generics.empty()) {
2050 if (silence_errors) return nullptr;
2051 std::stringstream stream;
2052 stream << "no matching declaration found for " << name;
2053 ReportError(stream.str());
2054 } else if (candidates.empty()) {
2055 if (silence_errors) return nullptr;
2056 FailCallableLookup("cannot find suitable callable with name", name,
2057 parameter_types, labels, overload_signatures,
2058 inapplicable_generics);
2059 }
2060
2061 auto is_better_candidate = [&](size_t a, size_t b) {
2062 return ParameterDifference(overload_signatures[a].GetExplicitTypes(),
2063 parameter_types)
2064 .StrictlyBetterThan(ParameterDifference(
2065 overload_signatures[b].GetExplicitTypes(), parameter_types));
2066 };
2067
2068 size_t best = *std::min_element(candidates.begin(), candidates.end(),
2069 is_better_candidate);
2070 // This check is contained in libstdc++'s std::min_element.
2071 DCHECK(!is_better_candidate(best, best))((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
2109template <class Container>
2110Callable* ImplementationVisitor::LookupCallable(
2111 const QualifiedName& name, const Container& declaration_container,
2112 const Arguments& arguments, const TypeVector& specialization_types) {
2113 return LookupCallable(name, declaration_container,
2114 arguments.parameters.ComputeTypeVector(),
2115 arguments.labels, specialization_types);
2116}
2117
2118Method* ImplementationVisitor::LookupMethod(
2119 const std::string& name, const AggregateType* receiver_type,
2120 const Arguments& arguments, const TypeVector& specialization_types) {
2121 TypeVector types(arguments.parameters.ComputeTypeVector());
2122 types.insert(types.begin(), receiver_type);
2123 return Method::cast(LookupCallable({{}, name}, receiver_type->Methods(name),
2124 types, arguments.labels,
2125 specialization_types));
2126}
2127
2128const Type* ImplementationVisitor::GetCommonType(const Type* left,
2129 const Type* right) {
2130 const Type* common_type;
2131 if (IsAssignableFrom(left, right)) {
2132 common_type = left;
2133 } else if (IsAssignableFrom(right, left)) {
2134 common_type = right;
2135 } else {
2136 common_type = TypeOracle::GetUnionType(left, right);
2137 }
2138 common_type = common_type->NonConstexprVersion();
2139 return common_type;
2140}
2141
2142VisitResult ImplementationVisitor::GenerateCopy(const VisitResult& to_copy) {
2143 if (to_copy.IsOnStack()) {
2144 return VisitResult(to_copy.type(),
2145 assembler().Peek(to_copy.stack_range(), to_copy.type()));
2146 }
2147 return to_copy;
2148}
2149
2150VisitResult ImplementationVisitor::Visit(StructExpression* expr) {
2151 StackScope stack_scope(this);
2152
2153 auto& initializers = expr->initializers;
2154 std::vector<VisitResult> values;
2155 std::vector<const Type*> term_argument_types;
2156 values.reserve(initializers.size());
2157 term_argument_types.reserve(initializers.size());
2158
2159 // Compute values and types of all initializer arguments
2160 for (const NameAndExpression& initializer : initializers) {
2161 VisitResult value = Visit(initializer.expression);
2162 values.push_back(value);
2163 term_argument_types.push_back(value.type());
2164 }
2165
2166 // Compute and check struct type from given struct name and argument types
2167 const Type* type = TypeVisitor::ComputeTypeForStructExpression(
2168 expr->type, term_argument_types);
2169 if (const auto* struct_type = StructType::DynamicCast(type)) {
2170 CheckInitializersWellformed(struct_type->name(), struct_type->fields(),
2171 initializers);
2172
2173 // Implicitly convert values and thereby build the struct on the stack
2174 StackRange struct_range = assembler().TopRange(0);
2175 auto& fields = struct_type->fields();
2176 for (size_t i = 0; i < values.size(); i++) {
2177 values[i] =
2178 GenerateImplicitConvert(fields[i].name_and_type.type, values[i]);
2179 struct_range.Extend(values[i].stack_range());
2180 }
2181
2182 return stack_scope.Yield(VisitResult(struct_type, struct_range));
2183 } else {
2184 const auto* bitfield_struct_type = BitFieldStructType::cast(type);
2185 CheckInitializersWellformed(bitfield_struct_type->name(),
2186 bitfield_struct_type->fields(), initializers);
2187
2188 // Create a zero and cast it to the desired bitfield struct type.
2189 VisitResult result{TypeOracle::GetConstInt32Type(), "0"};
2190 result = GenerateImplicitConvert(TypeOracle::GetInt32Type(), result);
2191 result = GenerateCall("Unsigned", Arguments{{result}, {}}, {});
2192 result = GenerateCall("%RawDownCast", Arguments{{result}, {}},
2193 {bitfield_struct_type});
2194
2195 // Set each field in the result. If these fields are constexpr, then all of
2196 // this initialization will end up reduced to a single value during TurboFan
2197 // optimization.
2198 auto& fields = bitfield_struct_type->fields();
2199 for (size_t i = 0; i < values.size(); i++) {
2200 values[i] =
2201 GenerateImplicitConvert(fields[i].name_and_type.type, values[i]);
2202 result = GenerateSetBitField(bitfield_struct_type, fields[i], result,
2203 values[i], /*starts_as_zero=*/true);
2204 }
2205
2206 return stack_scope.Yield(result);
2207 }
2208}
2209
2210VisitResult ImplementationVisitor::GenerateSetBitField(
2211 const Type* bitfield_struct_type, const BitField& bitfield,
2212 VisitResult bitfield_struct, VisitResult value, bool starts_as_zero) {
2213 GenerateCopy(bitfield_struct);
2214 GenerateCopy(value);
2215 assembler().Emit(
2216 StoreBitFieldInstruction{bitfield_struct_type, bitfield, starts_as_zero});
2217 return VisitResult(bitfield_struct_type, assembler().TopRange(1));
2218}
2219
2220LocationReference ImplementationVisitor::GetLocationReference(
2221 Expression* location) {
2222 switch (location->kind) {
2223 case AstNode::Kind::kIdentifierExpression:
2224 return GetLocationReference(static_cast<IdentifierExpression*>(location));
2225 case AstNode::Kind::kFieldAccessExpression:
2226 return GetLocationReference(
2227 static_cast<FieldAccessExpression*>(location));
2228 case AstNode::Kind::kElementAccessExpression:
2229 return GetLocationReference(
2230 static_cast<ElementAccessExpression*>(location));
2231 case AstNode::Kind::kDereferenceExpression:
2232 return GetLocationReference(
2233 static_cast<DereferenceExpression*>(location));
2234 default:
2235 return LocationReference::Temporary(Visit(location), "expression");
2236 }
2237}
2238
2239LocationReference ImplementationVisitor::GetLocationReference(
2240 FieldAccessExpression* expr) {
2241 return GenerateFieldAccess(GetLocationReference(expr->object),
2242 expr->field->value, false, expr->field->pos);
2243}
2244
2245LocationReference ImplementationVisitor::GenerateFieldAccess(
2246 LocationReference reference, const std::string& fieldname,
2247 bool ignore_stuct_field_constness, base::Optional<SourcePosition> pos) {
2248 if (reference.IsVariableAccess() &&
2249 reference.variable().type()->StructSupertype()) {
2250 const StructType* type = *reference.variable().type()->StructSupertype();
2251 const Field& field = type->LookupField(fieldname);
2252 if (GlobalContext::collect_language_server_data() && pos.has_value()) {
2253 LanguageServerData::AddDefinition(*pos, field.pos);
2254 }
2255 if (GlobalContext::collect_kythe_data() && pos.has_value()) {
2256 KytheData::AddClassFieldUse(*pos, &field);
2257 }
2258 if (field.const_qualified) {
2259 VisitResult t_value = ProjectStructField(reference.variable(), fieldname);
2260 return LocationReference::Temporary(
2261 t_value, "for constant field '" + field.name_and_type.name + "'");
2262 } else {
2263 return LocationReference::VariableAccess(
2264 ProjectStructField(reference.variable(), fieldname));
2265 }
2266 }
2267 if (reference.IsTemporary() &&
2268 reference.temporary().type()->StructSupertype()) {
2269 if (GlobalContext::collect_language_server_data() && pos.has_value()) {
2270 const StructType* type = *reference.temporary().type()->StructSupertype();
2271 const Field& field = type->LookupField(fieldname);
2272 LanguageServerData::AddDefinition(*pos, field.pos);
2273 }
2274 return LocationReference::Temporary(
2275 ProjectStructField(reference.temporary(), fieldname),
2276 reference.temporary_description());
2277 }
2278 if (base::Optional<const Type*> referenced_type =
2279 reference.ReferencedType()) {
2280 if ((*referenced_type)->IsBitFieldStructType()) {
2281 const BitFieldStructType* bitfield_struct =
2282 BitFieldStructType::cast(*referenced_type);
2283 const BitField& field = bitfield_struct->LookupField(fieldname);
2284 return LocationReference::BitFieldAccess(reference, field);
2285 }
2286 if (const auto type_wrapped_in_smi = Type::MatchUnaryGeneric(
2287 (*referenced_type), TypeOracle::GetSmiTaggedGeneric())) {
2288 const BitFieldStructType* bitfield_struct =
2289 BitFieldStructType::DynamicCast(*type_wrapped_in_smi);
2290 if (bitfield_struct == nullptr) {
2291 ReportError(
2292 "When a value of type SmiTagged<T> is used in a field access "
2293 "expression, T is expected to be a bitfield struct type. Instead, "
2294 "T "
2295 "is ",
2296 **type_wrapped_in_smi);
2297 }
2298 const BitField& field = bitfield_struct->LookupField(fieldname);
2299 return LocationReference::BitFieldAccess(reference, field);
2300 }
2301 }
2302 if (reference.IsHeapReference()) {
2303 VisitResult ref = reference.heap_reference();
2304 bool is_const;
2305 auto generic_type =
2306 TypeOracle::MatchReferenceGeneric(ref.type(), &is_const);
2307 if (!generic_type) {
2308 ReportError(
2309 "Left-hand side of field access expression is marked as a reference "
2310 "but is not of type Reference<...>. Found type: ",
2311 ref.type()->ToString());
2312 }
2313 if (auto struct_type = (*generic_type)->StructSupertype()) {
2314 const Field& field = (*struct_type)->LookupField(fieldname);
2315 // Update the Reference's type to refer to the field type within the
2316 // struct.
2317 ref.SetType(TypeOracle::GetReferenceType(
2318 field.name_and_type.type,
2319 is_const ||
2320 (field.const_qualified && !ignore_stuct_field_constness)));
2321 if (!field.offset.has_value()) {
2322 Error("accessing field with unknown offset").Throw();
2323 }
2324 if (*field.offset != 0) {
2325 // Copy the Reference struct up the stack and update the new copy's
2326 // |offset| value to point to the struct field.
2327 StackScope scope(this);
2328 ref = GenerateCopy(ref);
2329 VisitResult ref_offset = ProjectStructField(ref, "offset");
2330 VisitResult struct_offset{
2331 TypeOracle::GetIntPtrType()->ConstexprVersion(),
2332 std::to_string(*field.offset)};
2333 VisitResult updated_offset =
2334 GenerateCall("+", Arguments{{ref_offset, struct_offset}, {}});
2335 assembler().Poke(ref_offset.stack_range(), updated_offset.stack_range(),
2336 ref_offset.type());
2337 ref = scope.Yield(ref);
2338 }
2339 return LocationReference::HeapReference(ref);
2340 }
2341 }
2342 VisitResult object_result = GenerateFetchFromLocation(reference);
2343 if (base::Optional<const ClassType*> class_type =
2344 object_result.type()->ClassSupertype()) {
2345 // This is a hack to distinguish the situation where we want to use
2346 // overloaded field accessors from when we want to create a reference.
2347 bool has_explicit_overloads = TestLookupCallable(
2348 QualifiedName{"." + fieldname}, {object_result.type()});
2349 if ((*class_type)->HasField(fieldname) && !has_explicit_overloads) {
2350 const Field& field = (*class_type)->LookupField(fieldname);
2351 if (GlobalContext::collect_language_server_data() && pos.has_value()) {
2352 LanguageServerData::AddDefinition(*pos, field.pos);
2353 }
2354 if (GlobalContext::collect_kythe_data()) {
2355 KytheData::AddClassFieldUse(*pos, &field);
2356 }
2357 return GenerateFieldReference(object_result, field, *class_type);
2358 }
2359 }
2360 return LocationReference::FieldAccess(object_result, fieldname);
2361}
2362
2363LocationReference ImplementationVisitor::GetLocationReference(
2364 ElementAccessExpression* expr) {
2365 LocationReference reference = GetLocationReference(expr->array);
2366 VisitResult index = Visit(expr->index);
2367 if (reference.IsHeapSlice()) {
2368 return GenerateReferenceToItemInHeapSlice(reference, index);
2369 } else {
2370 return LocationReference::ArrayAccess(GenerateFetchFromLocation(reference),
2371 index);
2372 }
2373}
2374
2375LocationReference ImplementationVisitor::GenerateReferenceToItemInHeapSlice(
2376 LocationReference slice, VisitResult index) {
2377 DCHECK(slice.IsHeapSlice())((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
2389LocationReference ImplementationVisitor::GetLocationReference(
2390 IdentifierExpression* expr) {
2391 if (expr->namespace_qualification.empty()) {
2392 if (base::Optional<Binding<LocalValue>*> value =
2393 TryLookupLocalValue(expr->name->value)) {
2394 if (GlobalContext::collect_language_server_data()) {
2395 LanguageServerData::AddDefinition(expr->name->pos,
2396 (*value)->declaration_position());
2397 }
2398 if (GlobalContext::collect_kythe_data()) {
2399 if (!expr->IsThis()) {
2400 DCHECK_EQ(expr->name->pos.end.column - expr->name->pos.start.column,((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
2473LocationReference ImplementationVisitor::GetLocationReference(
2474 DereferenceExpression* expr) {
2475 VisitResult ref = Visit(expr->reference);
2476 if (!TypeOracle::MatchReferenceGeneric(ref.type())) {
2477 Error("Operator * expects a reference type but found a value of type ",
2478 *ref.type())
2479 .Throw();
2480 }
2481 return LocationReference::HeapReference(ref);
2482}
2483
2484VisitResult ImplementationVisitor::GenerateFetchFromLocation(
2485 const LocationReference& reference) {
2486 if (reference.IsTemporary()) {
2487 return GenerateCopy(reference.temporary());
2488 } else if (reference.IsVariableAccess()) {
2489 return GenerateCopy(reference.variable());
2490 } else if (reference.IsHeapReference()) {
2491 const Type* referenced_type = *reference.ReferencedType();
2492 if (referenced_type == TypeOracle::GetFloat64OrHoleType()) {
2493 return GenerateCall(QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING},
2494 "LoadFloat64OrHole"),
2495 Arguments{{reference.heap_reference()}, {}});
2496 } else if (auto struct_type = referenced_type->StructSupertype()) {
2497 StackRange result_range = assembler().TopRange(0);
2498 for (const Field& field : (*struct_type)->fields()) {
2499 StackScope scope(this);
2500 const std::string& fieldname = field.name_and_type.name;
2501 VisitResult field_value = scope.Yield(GenerateFetchFromLocation(
2502 GenerateFieldAccess(reference, fieldname)));
2503 result_range.Extend(field_value.stack_range());
2504 }
2505 return VisitResult(referenced_type, result_range);
2506 } else {
2507 GenerateCopy(reference.heap_reference());
2508 assembler().Emit(LoadReferenceInstruction{referenced_type});
2509 DCHECK_EQ(1, LoweredSlotCount(referenced_type))((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
2530void ImplementationVisitor::GenerateAssignToLocation(
2531 const LocationReference& reference, const VisitResult& assignment_value) {
2532 if (reference.IsCallAccess()) {
2533 Arguments arguments{reference.call_arguments(), {}};
2534 arguments.parameters.push_back(assignment_value);
2535 GenerateCall(reference.assign_function(), arguments);
2536 } else if (reference.IsVariableAccess()) {
2537 VisitResult variable = reference.variable();
2538 VisitResult converted_value =
2539 GenerateImplicitConvert(variable.type(), assignment_value);
2540 assembler().Poke(variable.stack_range(), converted_value.stack_range(),
2541 variable.type());
2542
2543 // Local variables are detected by the existence of a binding. Assignment
2544 // to local variables is recorded to support lint errors.
2545 if (reference.binding()) {
2546 (*reference.binding())->SetWritten();
2547 }
2548 } else if (reference.IsHeapSlice()) {
2549 ReportError("assigning a value directly to an indexed field isn't allowed");
2550 } else if (reference.IsHeapReference()) {
2551 const Type* referenced_type = *reference.ReferencedType();
2552 if (reference.IsConst()) {
2553 Error("cannot assign to const value of type ", *referenced_type).Throw();
2554 }
2555 if (referenced_type == TypeOracle::GetFloat64OrHoleType()) {
2556 GenerateCall(
2557 QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING},
2558 "StoreFloat64OrHole"),
2559 Arguments{{reference.heap_reference(), assignment_value}, {}});
2560 } else if (auto struct_type = referenced_type->StructSupertype()) {
2561 if (!assignment_value.type()->IsSubtypeOf(referenced_type)) {
2562 ReportError("Cannot assign to ", *referenced_type,
2563 " with value of type ", *assignment_value.type());
2564 }
2565 for (const Field& field : (*struct_type)->fields()) {
2566 const std::string& fieldname = field.name_and_type.name;
2567 // Allow assignment of structs even if they contain const fields.
2568 // Const on struct fields just disallows direct writes to them.
2569 bool ignore_stuct_field_constness = true;
2570 GenerateAssignToLocation(
2571 GenerateFieldAccess(reference, fieldname,
2572 ignore_stuct_field_constness),
2573 ProjectStructField(assignment_value, fieldname));
2574 }
2575 } else {
2576 GenerateCopy(reference.heap_reference());
2577 VisitResult converted_assignment_value =
2578 GenerateImplicitConvert(referenced_type, assignment_value);
2579 if (referenced_type == TypeOracle::GetFloat64Type()) {
2580 VisitResult silenced_float_value = GenerateCall(
2581 "Float64SilenceNaN", Arguments{{assignment_value}, {}});
2582 assembler().Poke(converted_assignment_value.stack_range(),
2583 silenced_float_value.stack_range(), referenced_type);
2584 }
2585 assembler().Emit(StoreReferenceInstruction{referenced_type});
2586 }
2587 } else if (reference.IsBitFieldAccess()) {
2588 // First fetch the bitfield struct, then set the updated bits, then store
2589 // it back to where we found it.
2590 VisitResult bit_field_struct =
2591 GenerateFetchFromLocation(reference.bit_field_struct_location());
2592 VisitResult converted_value =
2593 GenerateImplicitConvert(*reference.ReferencedType(), assignment_value);
2594 VisitResult updated_bit_field_struct =
2595 GenerateSetBitField(bit_field_struct.type(), reference.bit_field(),
2596 bit_field_struct, converted_value);
2597 GenerateAssignToLocation(reference.bit_field_struct_location(),
2598 updated_bit_field_struct);
2599 } else {
2600 DCHECK(reference.IsTemporary())((void) 0);
2601 ReportError("cannot assign to const-bound or temporary ",
2602 reference.temporary_description());
2603 }
2604}
2605
2606VisitResult ImplementationVisitor::GeneratePointerCall(
2607 Expression* callee, const Arguments& arguments, bool is_tailcall) {
2608 StackScope scope(this);
2609 TypeVector parameter_types(arguments.parameters.ComputeTypeVector());
2610 VisitResult callee_result = Visit(callee);
2611 if (!callee_result.type()->IsBuiltinPointerType()) {
2612 std::stringstream stream;
2613 stream << "Expected a function pointer type but found "
2614 << *callee_result.type();
2615 ReportError(stream.str());
2616 }
2617 const BuiltinPointerType* type =
2618 BuiltinPointerType::cast(callee_result.type());
2619
2620 if (type->parameter_types().size() != parameter_types.size()) {
2621 std::stringstream stream;
2622 stream << "parameter count mismatch calling function pointer with Type: "
2623 << *type << " - expected "
2624 << std::to_string(type->parameter_types().size()) << ", found "
2625 << std::to_string(parameter_types.size());
2626 ReportError(stream.str());
2627 }
2628
2629 ParameterTypes types{type->parameter_types(), false};
2630 Signature sig;
2631 sig.parameter_types = types;
2632 if (!IsCompatibleSignature(sig, parameter_types, 0)) {
2633 std::stringstream stream;
2634 stream << "parameters do not match function pointer signature. Expected: ("
2635 << type->parameter_types() << ") but got: (" << parameter_types
2636 << ")";
2637 ReportError(stream.str());
2638 }
2639
2640 callee_result = GenerateCopy(callee_result);
2641 StackRange arg_range = assembler().TopRange(0);
2642 for (size_t current = 0; current < arguments.parameters.size(); ++current) {
2643 const Type* to_type = type->parameter_types()[current];
2644 arg_range.Extend(
2645 GenerateImplicitConvert(to_type, arguments.parameters[current])
2646 .stack_range());
2647 }
2648
2649 assembler().Emit(
2650 CallBuiltinPointerInstruction{is_tailcall, type, arg_range.Size()});
2651
2652 if (is_tailcall) {
2653 return VisitResult::NeverResult();
2654 }
2655 DCHECK_EQ(1, LoweredSlotCount(type->return_type()))((void) 0);
2656 return scope.Yield(VisitResult(type->return_type(), assembler().TopRange(1)));
2657}
2658
2659void ImplementationVisitor::AddCallParameter(
2660 Callable* callable, VisitResult parameter, const Type* parameter_type,
2661 std::vector<VisitResult>* converted_arguments, StackRange* argument_range,
2662 std::vector<std::string>* constexpr_arguments, bool inline_macro) {
2663 VisitResult converted;
2664 if ((converted_arguments->size() < callable->signature().implicit_count) &&
2665 parameter.type()->IsTopType()) {
2666 converted = GenerateCopy(parameter);
2667 } else {
2668 converted = GenerateImplicitConvert(parameter_type, parameter);
2669 }
2670 converted_arguments->push_back(converted);
2671 if (!inline_macro) {
2672 if (converted.IsOnStack()) {
2673 argument_range->Extend(converted.stack_range());
2674 } else {
2675 constexpr_arguments->push_back(converted.constexpr_value());
2676 }
2677 }
2678}
2679
2680namespace {
2681std::pair<std::string, std::string> GetClassInstanceTypeRange(
2682 const ClassType* class_type) {
2683 std::pair<std::string, std::string> result;
2684 if (class_type->InstanceTypeRange()) {
2685 auto instance_type_range = *class_type->InstanceTypeRange();
2686 std::string instance_type_string_first =
2687 "static_cast<InstanceType>(" +
2688 std::to_string(instance_type_range.first) + ")";
2689 std::string instance_type_string_second =
2690 "static_cast<InstanceType>(" +
2691 std::to_string(instance_type_range.second) + ")";
2692 result =
2693 std::make_pair(instance_type_string_first, instance_type_string_second);
2694 } else {
2695 ReportError(
2696 "%Min/MaxInstanceType must take a class type that is either a string "
2697 "or has a generated instance type range");
2698 }
2699 return result;
2700}
2701} // namespace
2702
2703VisitResult ImplementationVisitor::GenerateCall(
2704 Callable* callable, base::Optional<LocationReference> this_reference,
2705 Arguments arguments, const TypeVector& specialization_types,
2706 bool is_tailcall) {
2707 CHECK(callable->Position().source.IsValid())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
3167VisitResult ImplementationVisitor::GenerateCall(
3168 const QualifiedName& callable_name, Arguments arguments,
3169 const TypeVector& specialization_types, bool is_tailcall) {
3170 Callable* callable =
3171 LookupCallable(callable_name, Declarations::Lookup(callable_name),
3172 arguments, specialization_types);
3173 return GenerateCall(callable, base::nullopt, arguments, specialization_types,
3174 is_tailcall);
3175}
3176
3177VisitResult ImplementationVisitor::Visit(CallExpression* expr,
3178 bool is_tailcall) {
3179 StackScope scope(this);
3180
3181 if (expr->callee->name->value == "&" && expr->arguments.size() == 1) {
3182 if (auto* loc_expr = LocationExpression::DynamicCast(expr->arguments[0])) {
3183 LocationReference ref = GetLocationReference(loc_expr);
3184 if (ref.IsHeapReference()) return scope.Yield(ref.heap_reference());
3185 if (ref.IsHeapSlice()) return scope.Yield(ref.heap_slice());
3186 }
3187 ReportError("Unable to create a heap reference.");
3188 }
3189
3190 Arguments arguments;
3191 QualifiedName name = QualifiedName(expr->callee->namespace_qualification,
3192 expr->callee->name->value);
3193 TypeVector specialization_types =
3194 TypeVisitor::ComputeTypeVector(expr->callee->generic_arguments);
3195 bool has_template_arguments = !specialization_types.empty();
3196 for (Expression* arg : expr->arguments)
3197 arguments.parameters.push_back(Visit(arg));
3198 arguments.labels = LabelsFromIdentifiers(expr->labels);
3199 if (!has_template_arguments && name.namespace_qualification.empty() &&
3200 TryLookupLocalValue(name.name)) {
3201 return scope.Yield(
3202 GeneratePointerCall(expr->callee, arguments, is_tailcall));
3203 } else {
3204 if (GlobalContext::collect_language_server_data()) {
3205 Callable* callable = LookupCallable(name, Declarations::Lookup(name),
3206 arguments, specialization_types);
3207 LanguageServerData::AddDefinition(expr->callee->name->pos,
3208 callable->IdentifierPosition());
3209 }
3210 if (GlobalContext::collect_kythe_data()) {
3211 Callable* callable = LookupCallable(name, Declarations::Lookup(name),
3212 arguments, specialization_types);
3213 Callable* caller = CurrentCallable::Get();
3214 KytheData::AddCall(caller, expr->callee->name->pos, callable);
3215 }
3216 if (expr->callee->name->value == "!" && arguments.parameters.size() == 1) {
3217 PropagateBitfieldMark(expr->arguments[0], expr);
3218 }
3219 if (expr->callee->name->value == "==" && arguments.parameters.size() == 2) {
3220 if (arguments.parameters[0].type()->IsConstexpr()) {
3221 PropagateBitfieldMark(expr->arguments[1], expr);
3222 } else if (arguments.parameters[1].type()->IsConstexpr()) {
3223 PropagateBitfieldMark(expr->arguments[0], expr);
3224 }
3225 }
3226 return scope.Yield(
3227 GenerateCall(name, arguments, specialization_types, is_tailcall));
3228 }
3229}
3230
3231VisitResult ImplementationVisitor::Visit(CallMethodExpression* expr) {
3232 StackScope scope(this);
3233 Arguments arguments;
3234 std::string method_name = expr->method->name->value;
3235 TypeVector specialization_types =
3236 TypeVisitor::ComputeTypeVector(expr->method->generic_arguments);
3237 LocationReference target = GetLocationReference(expr->target);
3238 if (!target.IsVariableAccess()) {
3239 VisitResult result = GenerateFetchFromLocation(target);
3240 target = LocationReference::Temporary(result, "this parameter");
3241 }
3242 const AggregateType* target_type =
3243 (*target.ReferencedType())->AggregateSupertype().value_or(nullptr);
3244 if (!target_type) {
3245 ReportError("target of method call not a struct or class type");
3246 }
3247 for (Expression* arg : expr->arguments) {
3248 arguments.parameters.push_back(Visit(arg));
3249 }
3250 arguments.labels = LabelsFromIdentifiers(expr->labels);
3251 TypeVector argument_types = arguments.parameters.ComputeTypeVector();
3252 DCHECK_EQ(expr->method->namespace_qualification.size(), 0)((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
3266VisitResult ImplementationVisitor::Visit(IntrinsicCallExpression* expr) {
3267 StackScope scope(this);
3268 Arguments arguments;
3269 TypeVector specialization_types =
3270 TypeVisitor::ComputeTypeVector(expr->generic_arguments);
3271 for (Expression* arg : expr->arguments)
3272 arguments.parameters.push_back(Visit(arg));
3273 return scope.Yield(
3274 GenerateCall(expr->name->value, arguments, specialization_types, false));
3275}
3276
3277void ImplementationVisitor::GenerateBranch(const VisitResult& condition,
3278 Block* true_block,
3279 Block* false_block) {
3280 DCHECK_EQ(condition,((void) 0)
3281 VisitResult(TypeOracle::GetBoolType(), assembler().TopRange(1)))((void) 0);
3282 assembler().Branch(true_block, false_block);
3283}
3284
3285VisitResult ImplementationVisitor::GenerateBoolConstant(bool constant) {
3286 return GenerateImplicitConvert(TypeOracle::GetBoolType(),
3287 VisitResult(TypeOracle::GetConstexprBoolType(),
3288 constant ? "true" : "false"));
3289}
3290
3291void ImplementationVisitor::GenerateExpressionBranch(Expression* expression,
3292 Block* true_block,
3293 Block* false_block) {
3294 StackScope stack_scope(this);
3295 VisitResult expression_result = this->Visit(expression);
3296 expression_result = stack_scope.Yield(
3297 GenerateImplicitConvert(TypeOracle::GetBoolType(), expression_result));
3298 GenerateBranch(expression_result, true_block, false_block);
3299}
3300
3301VisitResult ImplementationVisitor::GenerateImplicitConvert(
3302 const Type* destination_type, VisitResult source) {
3303 StackScope scope(this);
3304 if (source.type() == TypeOracle::GetNeverType()) {
3305 ReportError("it is not allowed to use a value of type never");
3306 }
3307
3308 if (destination_type == source.type()) {
3309 return scope.Yield(GenerateCopy(source));
3310 }
3311
3312 if (auto from = TypeOracle::ImplicitlyConvertableFrom(destination_type,
3313 source.type())) {
3314 return scope.Yield(GenerateCall(kFromConstexprMacroName,
3315 Arguments{{source}, {}},
3316 {destination_type, *from}, false));
3317 } else if (IsAssignableFrom(destination_type, source.type())) {
3318 source.SetType(destination_type);
3319 return scope.Yield(GenerateCopy(source));
3320 } else {
3321 std::stringstream s;
3322 if (const TopType* top_type = TopType::DynamicCast(source.type())) {
3323 s << "undefined expression of type " << *destination_type << ": the "
3324 << top_type->reason();
3325 } else {
3326 s << "cannot use expression of type " << *source.type()
3327 << " as a value of type " << *destination_type;
3328 }
3329 ReportError(s.str());
3330 }
3331}
3332
3333StackRange ImplementationVisitor::GenerateLabelGoto(
3334 LocalLabel* label, base::Optional<StackRange> arguments) {
3335 return assembler().Goto(label->block, arguments ? arguments->Size() : 0);
19
Access to field 'block' results in a dereference of a null pointer (loaded from variable 'label')
3336}
3337
3338std::vector<Binding<LocalLabel>*> ImplementationVisitor::LabelsFromIdentifiers(
3339 const std::vector<Identifier*>& names) {
3340 std::vector<Binding<LocalLabel>*> result;
3341 result.reserve(names.size());
3342 for (const auto& name : names) {
3343 Binding<LocalLabel>* label = LookupLabel(name->value);
3344 result.push_back(label);
3345
3346 // Link up labels in "otherwise" part of the call expression with
3347 // either the label in the signature of the calling macro or the label
3348 // block ofa surrounding "try".
3349 if (GlobalContext::collect_language_server_data()) {
3350 LanguageServerData::AddDefinition(name->pos,
3351 label->declaration_position());
3352 }
3353 // TODO(v8:12261): Might have to track KytheData here.
3354 }
3355 return result;
3356}
3357
3358StackRange ImplementationVisitor::LowerParameter(
3359 const Type* type, const std::string& parameter_name,
3360 Stack<std::string>* lowered_parameters) {
3361 if (base::Optional<const StructType*> struct_type = type->StructSupertype()) {
3362 StackRange range = lowered_parameters->TopRange(0);
3363 for (auto& field : (*struct_type)->fields()) {
3364 StackRange parameter_range = LowerParameter(
3365 field.name_and_type.type,
3366 parameter_name + "." + field.name_and_type.name, lowered_parameters);
3367 range.Extend(parameter_range);
3368 }
3369 return range;
3370 } else {
3371 lowered_parameters->Push(parameter_name);
3372 return lowered_parameters->TopRange(1);
3373 }
3374}
3375
3376void ImplementationVisitor::LowerLabelParameter(
3377 const Type* type, const std::string& parameter_name,
3378 std::vector<std::string>* lowered_parameters) {
3379 if (base::Optional<const StructType*> struct_type = type->StructSupertype()) {
3380 for (auto& field : (*struct_type)->fields()) {
3381 LowerLabelParameter(
3382 field.name_and_type.type,
3383 "&((*" + parameter_name + ")." + field.name_and_type.name + ")",
3384 lowered_parameters);
3385 }
3386 } else {
3387 lowered_parameters->push_back(parameter_name);
3388 }
3389}
3390
3391std::string ImplementationVisitor::ExternalLabelName(
3392 const std::string& label_name) {
3393 return "label_" + label_name;
3394}
3395
3396std::string ImplementationVisitor::ExternalLabelParameterName(
3397 const std::string& label_name, size_t i) {
3398 return "label_" + label_name + "_parameter_" + std::to_string(i);
3399}
3400
3401std::string ImplementationVisitor::ExternalParameterName(
3402 const std::string& name) {
3403 return std::string("p_") + name;
3404}
3405
3406DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::ValueBindingsManager)template <> ImplementationVisitor::ValueBindingsManager
::Scope*& ContextualVariableTop<ImplementationVisitor::
ValueBindingsManager>() { static thread_local ImplementationVisitor
::ValueBindingsManager::Scope* top = nullptr; return top; }
3407DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::LabelBindingsManager)template <> ImplementationVisitor::LabelBindingsManager
::Scope*& ContextualVariableTop<ImplementationVisitor::
LabelBindingsManager>() { static thread_local ImplementationVisitor
::LabelBindingsManager::Scope* top = nullptr; return top; }
3408DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::CurrentCallable)template <> ImplementationVisitor::CurrentCallable::Scope
*& ContextualVariableTop<ImplementationVisitor::CurrentCallable
>() { static thread_local ImplementationVisitor::CurrentCallable
::Scope* top = nullptr; return top; }
3409DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::CurrentFileStreams)template <> ImplementationVisitor::CurrentFileStreams::
Scope*& ContextualVariableTop<ImplementationVisitor::CurrentFileStreams
>() { static thread_local ImplementationVisitor::CurrentFileStreams
::Scope* top = nullptr; return top; }
3410DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::CurrentReturnValue)template <> ImplementationVisitor::CurrentReturnValue::
Scope*& ContextualVariableTop<ImplementationVisitor::CurrentReturnValue
>() { static thread_local ImplementationVisitor::CurrentReturnValue
::Scope* top = nullptr; return top; }
3411
3412bool IsCompatibleSignature(const Signature& sig, const TypeVector& types,
3413 size_t label_count) {
3414 auto i = sig.parameter_types.types.begin() + sig.implicit_count;
3415 if ((sig.parameter_types.types.size() - sig.implicit_count) > types.size())
3416 return false;
3417 if (sig.labels.size() != label_count) return false;
3418 for (auto current : types) {
3419 if (i == sig.parameter_types.types.end()) {
3420 if (!sig.parameter_types.var_args) return false;
3421 if (!IsAssignableFrom(TypeOracle::GetObjectType(), current)) return false;
3422 } else {
3423 if (!IsAssignableFrom(*i++, current)) return false;
3424 }
3425 }
3426 return true;
3427}
3428
3429base::Optional<Block*> ImplementationVisitor::GetCatchBlock() {
3430 base::Optional<Block*> catch_block;
3431 if (base::Optional<Binding<LocalLabel>*> catch_handler =
3432 TryLookupLabel(kCatchLabelName)) {
3433 catch_block = assembler().NewBlock(base::nullopt, true);
3434 }
3435 return catch_block;
3436}
3437
3438void ImplementationVisitor::GenerateCatchBlock(
3439 base::Optional<Block*> catch_block) {
3440 if (catch_block) {
3441 base::Optional<Binding<LocalLabel>*> catch_handler =
3442 TryLookupLabel(kCatchLabelName);
3443 // Reset the local scopes to prevent the macro calls below from using the
3444 // current catch handler.
3445 BindingsManagersScope bindings_managers_scope;
3446 if (assembler().CurrentBlockIsComplete()) {
3447 assembler().Bind(*catch_block);
3448 GenerateCall(QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING},
3449 "GetAndResetPendingMessage"),
3450 Arguments{{}, {}}, {}, false);
3451 assembler().Goto((*catch_handler)->block, 2);
3452 } else {
3453 CfgAssemblerScopedTemporaryBlock temp(&assembler(), *catch_block);
3454 GenerateCall(QualifiedName({TORQUE_INTERNAL_NAMESPACE_STRING},
3455 "GetAndResetPendingMessage"),
3456 Arguments{{}, {}}, {}, false);
3457 assembler().Goto((*catch_handler)->block, 2);
3458 }
3459 }
3460}
3461void ImplementationVisitor::VisitAllDeclarables() {
3462 CurrentCallable::Scope current_callable(nullptr);
3463 const std::vector<std::unique_ptr<Declarable>>& all_declarables =
3464 GlobalContext::AllDeclarables();
3465
3466 // This has to be an index-based loop because all_declarables can be extended
3467 // during the loop.
3468 for (size_t i = 0; i < all_declarables.size(); ++i) {
3469 try {
3470 Visit(all_declarables[i].get());
3471 } catch (TorqueAbortCompilation&) {
3472 // Recover from compile errors here. The error is recorded already.
3473 }
3474 }
3475
3476 // Do the same for macros which generate C++ code.
3477 output_type_ = OutputType::kCC;
3478 const std::vector<std::pair<TorqueMacro*, SourceId>>& cc_macros =
3479 GlobalContext::AllMacrosForCCOutput();
3480 for (size_t i = 0; i < cc_macros.size(); ++i) {
3481 try {
3482 Visit(static_cast<Declarable*>(cc_macros[i].first), cc_macros[i].second);
3483 } catch (TorqueAbortCompilation&) {
3484 // Recover from compile errors here. The error is recorded already.
3485 }
3486 }
3487
3488 // Do the same for macros which generate C++ debug code.
3489 // The set of macros is the same as C++ macros.
3490 output_type_ = OutputType::kCCDebug;
3491 for (size_t i = 0; i < cc_macros.size(); ++i) {
3492 try {
3493 Visit(static_cast<Declarable*>(cc_macros[i].first), cc_macros[i].second);
3494 } catch (TorqueAbortCompilation&) {
3495 // Recover from compile errors here. The error is recorded already.
3496 }
3497 }
3498 output_type_ = OutputType::kCSA;
3499}
3500
3501void ImplementationVisitor::Visit(Declarable* declarable,
3502 base::Optional<SourceId> file) {
3503 CurrentScope::Scope current_scope(declarable->ParentScope());
3504 CurrentSourcePosition::Scope current_source_position(declarable->Position());
3505 CurrentFileStreams::Scope current_file_streams(
3506 &GlobalContext::GeneratedPerFile(file ? *file
3507 : declarable->Position().source));
3508 if (Callable* callable = Callable::DynamicCast(declarable)) {
3509 if (!callable->ShouldGenerateExternalCode(output_type_))
3510 CurrentFileStreams::Get() = nullptr;
3511 }
3512 switch (declarable->kind()) {
3513 case Declarable::kExternMacro:
3514 return Visit(ExternMacro::cast(declarable));
3515 case Declarable::kTorqueMacro:
3516 return Visit(TorqueMacro::cast(declarable));
3517 case Declarable::kMethod:
3518 return Visit(Method::cast(declarable));
3519 case Declarable::kBuiltin:
3520 return Visit(Builtin::cast(declarable));
3521 case Declarable::kTypeAlias:
3522 return Visit(TypeAlias::cast(declarable));
3523 case Declarable::kNamespaceConstant:
3524 return Visit(NamespaceConstant::cast(declarable));
3525 case Declarable::kRuntimeFunction:
3526 case Declarable::kIntrinsic:
3527 case Declarable::kExternConstant:
3528 case Declarable::kNamespace:
3529 case Declarable::kGenericCallable:
3530 case Declarable::kGenericType:
3531 return;
3532 }
3533}
3534
3535std::string MachineTypeString(const Type* type) {
3536 if (type->IsSubtypeOf(TypeOracle::GetSmiType())) {
3537 return "MachineType::TaggedSigned()";
3538 }
3539 if (type->IsSubtypeOf(TypeOracle::GetHeapObjectType())) {
3540 return "MachineType::TaggedPointer()";
3541 }
3542 if (type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
3543 return "MachineType::AnyTagged()";
3544 }
3545 return "MachineTypeOf<" + type->GetGeneratedTNodeTypeName() + ">::value";
3546}
3547
3548void ImplementationVisitor::GenerateBuiltinDefinitionsAndInterfaceDescriptors(
3549 const std::string& output_directory) {
3550 std::stringstream builtin_definitions;
3551 std::string builtin_definitions_file_name = "builtin-definitions.h";
3552
3553 // This file contains plain interface descriptor definitions and has to be
3554 // included in the middle of interface-descriptors.h. Thus it is not a normal
3555 // header file and uses the .inc suffix instead of the .h suffix.
3556 std::stringstream interface_descriptors;
3557 std::string interface_descriptors_file_name = "interface-descriptors.inc";
3558 {
3559 IncludeGuardScope builtin_definitions_include_guard(
3560 builtin_definitions, builtin_definitions_file_name);
3561
3562 builtin_definitions
3563 << "\n"
3564 "#define BUILTIN_LIST_FROM_TORQUE(CPP, TFJ, TFC, TFS, TFH, "
3565 "ASM) "
3566 "\\\n";
3567 for (auto& declarable : GlobalContext::AllDeclarables()) {
3568 Builtin* builtin = Builtin::DynamicCast(declarable.get());
3569 if (!builtin || builtin->IsExternal()) continue;
3570 if (builtin->IsStub()) {
3571 builtin_definitions << "TFC(" << builtin->ExternalName() << ", "
3572 << builtin->ExternalName();
3573 std::string descriptor_name = builtin->ExternalName() + "Descriptor";
3574 bool has_context_parameter = builtin->signature().HasContextParameter();
3575 size_t kFirstNonContextParameter = has_context_parameter ? 1 : 0;
3576 TypeVector return_types = LowerType(builtin->signature().return_type);
3577
3578 interface_descriptors << "class " << descriptor_name
3579 << " : public StaticCallInterfaceDescriptor<"
3580 << descriptor_name << "> {\n";
3581
3582 interface_descriptors << " public:\n";
3583
3584 if (has_context_parameter) {
3585 interface_descriptors << " DEFINE_RESULT_AND_PARAMETERS(";
3586 } else {
3587 interface_descriptors << " DEFINE_RESULT_AND_PARAMETERS_NO_CONTEXT(";
3588 }
3589 interface_descriptors << return_types.size();
3590 for (size_t i = kFirstNonContextParameter;
3591 i < builtin->parameter_names().size(); ++i) {
3592 Identifier* parameter = builtin->parameter_names()[i];
3593 interface_descriptors << ", k" << CamelifyString(parameter->value);
3594 }
3595 interface_descriptors << ")\n";
3596
3597 interface_descriptors << " DEFINE_RESULT_AND_PARAMETER_TYPES(";
3598 PrintCommaSeparatedList(interface_descriptors, return_types,
3599 MachineTypeString);
3600 for (size_t i = kFirstNonContextParameter;
3601 i < builtin->parameter_names().size(); ++i) {
3602 const Type* type = builtin->signature().parameter_types.types[i];
3603 interface_descriptors << ", " << MachineTypeString(type);
3604 }
3605 interface_descriptors << ")\n";
3606
3607 interface_descriptors << " DECLARE_DEFAULT_DESCRIPTOR("
3608 << descriptor_name << ")\n";
3609 interface_descriptors << "};\n\n";
3610 } else {
3611 builtin_definitions << "TFJ(" << builtin->ExternalName();
3612 if (builtin->IsVarArgsJavaScript()) {
3613 builtin_definitions << ", kDontAdaptArgumentsSentinel";
3614 } else {
3615 DCHECK(builtin->IsFixedArgsJavaScript())((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
3658namespace {
3659
3660enum class FieldSectionType : uint32_t {
3661 kNoSection = 0,
3662 kWeakSection = 1 << 0,
3663 kStrongSection = 2 << 0,
3664 kScalarSection = 3 << 0
3665};
3666
3667bool IsPointerSection(FieldSectionType type) {
3668 return type == FieldSectionType::kWeakSection ||
3669 type == FieldSectionType::kStrongSection;
3670}
3671
3672using FieldSections = base::Flags<FieldSectionType>;
3673
3674std::string ToString(FieldSectionType type) {
3675 switch (type) {
3676 case FieldSectionType::kNoSection:
3677 return "NoSection";
3678 case FieldSectionType::kWeakSection:
3679 return "WeakFields";
3680 case FieldSectionType::kStrongSection:
3681 return "StrongFields";
3682 case FieldSectionType::kScalarSection:
3683 return "ScalarFields";
3684 }
3685 UNREACHABLE()V8_Fatal("unreachable code");
3686}
3687
3688class FieldOffsetsGenerator {
3689 public:
3690 explicit FieldOffsetsGenerator(const ClassType* type) : type_(type) {}
3691
3692 virtual void WriteField(const Field& f, const std::string& size_string) = 0;
3693 virtual void WriteFieldOffsetGetter(const Field& f) = 0;
3694 virtual void WriteMarker(const std::string& marker) = 0;
3695
3696 virtual ~FieldOffsetsGenerator() { CHECK(is_finished_)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
3822void GenerateClassExport(const ClassType* type, std::ostream& header,
3823 std::ostream& inl_header) {
3824 const ClassType* super = type->GetSuperClass();
3825 std::string parent = "TorqueGenerated" + type->name() + "<" + type->name() +
3826 ", " + super->name() + ">";
3827 header << "class " << type->name() << " : public " << parent << " {\n";
3828 header << " public:\n";
3829 if (type->ShouldGenerateBodyDescriptor()) {
3830 header << " class BodyDescriptor;\n";
3831 }
3832 header << " TQ_OBJECT_CONSTRUCTORS(" << type->name() << ")\n";
3833 header << "};\n\n";
3834 inl_header << "TQ_OBJECT_CONSTRUCTORS_IMPL(" << type->name() << ")\n";
3835}
3836
3837} // namespace
3838
3839void ImplementationVisitor::GenerateVisitorLists(
3840 const std::string& output_directory) {
3841 std::stringstream header;
3842 std::string file_name = "visitor-lists.h";
3843 {
3844 IncludeGuardScope include_guard(header, file_name);
3845
3846 header << "#define TORQUE_INSTANCE_TYPE_TO_BODY_DESCRIPTOR_LIST(V)\\\n";
3847 for (const ClassType* type : TypeOracle::GetClasses()) {
3848 if (type->ShouldGenerateBodyDescriptor() && type->OwnInstanceType()) {
3849 std::string type_name =
3850 CapifyStringWithUnderscores(type->name()) + "_TYPE";
3851 header << "V(" << type_name << "," << type->name() << ")\\\n";
3852 }
3853 }
3854 header << "\n";
3855
3856 header << "#define TORQUE_DATA_ONLY_VISITOR_ID_LIST(V)\\\n";
3857 for (const ClassType* type : TypeOracle::GetClasses()) {
3858 if (type->ShouldGenerateBodyDescriptor() && type->HasNoPointerSlots()) {
3859 header << "V(" << type->name() << ")\\\n";
3860 }
3861 }
3862 header << "\n";
3863
3864 header << "#define TORQUE_POINTER_VISITOR_ID_LIST(V)\\\n";
3865 for (const ClassType* type : TypeOracle::GetClasses()) {
3866 if (type->ShouldGenerateBodyDescriptor() && !type->HasNoPointerSlots()) {
3867 header << "V(" << type->name() << ")\\\n";
3868 }
3869 }
3870 header << "\n";
3871 }
3872 const std::string output_header_path = output_directory + "/" + file_name;
3873 WriteFile(output_header_path, header.str());
3874}
3875
3876void ImplementationVisitor::GenerateBitFields(
3877 const std::string& output_directory) {
3878 std::stringstream header;
3879 std::string file_name = "bit-fields.h";
3880 {
3881 IncludeGuardScope include_guard(header, file_name);
3882 header << "#include \"src/base/bit-field.h\"\n\n";
3883 NamespaceScope namespaces(header, {"v8", "internal"});
3884
3885 for (const auto& type : TypeOracle::GetBitFieldStructTypes()) {
3886 bool all_single_bits = true; // Track whether every field is one bit.
3887
3888 header << "#define DEFINE_TORQUE_GENERATED_"
3889 << CapifyStringWithUnderscores(type->name()) << "() \\\n";
3890 std::string type_name = type->GetConstexprGeneratedTypeName();
3891 for (const auto& field : type->fields()) {
3892 const char* suffix = field.num_bits == 1 ? "Bit" : "Bits";
3893 all_single_bits = all_single_bits && field.num_bits == 1;
3894 std::string field_type_name =
3895 field.name_and_type.type->GetConstexprGeneratedTypeName();
3896 header << " using " << CamelifyString(field.name_and_type.name)
3897 << suffix << " = base::BitField<" << field_type_name << ", "
3898 << field.offset << ", " << field.num_bits << ", " << type_name
3899 << ">; \\\n";
3900 }
3901
3902 // If every field is one bit, we can also generate a convenient enum.
3903 if (all_single_bits) {
3904 header << " enum Flag: " << type_name << " { \\\n";
3905 header << " kNone = 0, \\\n";
3906 for (const auto& field : type->fields()) {
3907 header << " k" << CamelifyString(field.name_and_type.name) << " = "
3908 << type_name << "{1} << " << field.offset << ", \\\n";
3909 }
3910 header << " }; \\\n";
3911 header << " using Flags = base::Flags<Flag>; \\\n";
3912 header << " static constexpr int kFlagCount = "
3913 << type->fields().size() << "; \\\n";
3914 }
3915
3916 header << "\n";
3917 }
3918 }
3919 const std::string output_header_path = output_directory + "/" + file_name;
3920 WriteFile(output_header_path, header.str());
3921}
3922
3923namespace {
3924
3925class ClassFieldOffsetGenerator : public FieldOffsetsGenerator {
3926 public:
3927 ClassFieldOffsetGenerator(std::ostream& header, std::ostream& inline_header,
3928 const ClassType* type, std::string gen_name,
3929 const ClassType* parent)
3930 : FieldOffsetsGenerator(type),
3931 hdr_(header),
3932 inl_(inline_header),
3933 previous_field_end_((parent && parent->IsShape()) ? "P::kSize"
3934 : "P::kHeaderSize"),
3935 gen_name_(gen_name) {}
3936
3937 void WriteField(const Field& f, const std::string& size_string) override {
3938 hdr_ << " // " << f.pos << "\n";
3939 std::string field = "k" + CamelifyString(f.name_and_type.name) + "Offset";
3940 std::string field_end = field + "End";
3941 hdr_ << " static constexpr int " << field << " = " << previous_field_end_
3942 << ";\n";
3943 hdr_ << " static constexpr int " << field_end << " = " << field << " + "
3944 << size_string << " - 1;\n";
3945 previous_field_end_ = field_end + " + 1";
3946 }
3947
3948 void WriteFieldOffsetGetter(const Field& f) override {
3949 // A static constexpr int is more convenient than a getter if the offset is
3950 // known.
3951 DCHECK(!f.offset.has_value())((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
3980class CppClassGenerator {
3981 public:
3982 CppClassGenerator(const ClassType* type, std::ostream& header,
3983 std::ostream& inl_header, std::ostream& impl)
3984 : type_(type),
3985 super_(type->GetSuperClass()),
3986 name_(type->name()),
3987 gen_name_("TorqueGenerated" + name_),
3988 gen_name_T_(gen_name_ + "<D, P>"),
3989 gen_name_I_(gen_name_ + "<" + name_ + ", " + super_->name() + ">"),
3990 hdr_(header),
3991 inl_(inl_header),
3992 impl_(impl) {}
3993 const std::string template_decl() const {
3994 return "template <class D, class P>";
3995 }
3996
3997 void GenerateClass();
3998 void GenerateCppObjectDefinitionAsserts();
3999
4000 private:
4001 SourcePosition Position();
4002
4003 void GenerateClassConstructors();
4004
4005 // Generates getter and setter runtime member functions for the given class
4006 // field. Traverses depth-first through any nested struct fields to generate
4007 // accessors for them also; struct_fields represents the stack of currently
4008 // active struct fields.
4009 void GenerateFieldAccessors(const Field& class_field,
4010 std::vector<const Field*>& struct_fields);
4011 void EmitLoadFieldStatement(std::ostream& stream, const Field& class_field,
4012 std::vector<const Field*>& struct_fields);
4013 void EmitStoreFieldStatement(std::ostream& stream, const Field& class_field,
4014 std::vector<const Field*>& struct_fields);
4015
4016 void GenerateClassCasts();
4017
4018 std::string GetFieldOffsetForAccessor(const Field& f);
4019
4020 // Gets the C++ type name that should be used in accessors for referring to
4021 // the value of a class field.
4022 std::string GetTypeNameForAccessor(const Field& f);
4023
4024 bool CanContainHeapObjects(const Type* t);
4025
4026 const ClassType* type_;
4027 const ClassType* super_;
4028 const std::string name_;
4029 const std::string gen_name_;
4030 const std::string gen_name_T_;
4031 const std::string gen_name_I_;
4032 std::ostream& hdr_;
4033 std::ostream& inl_;
4034 std::ostream& impl_;
4035};
4036
4037base::Optional<std::vector<Field>> GetOrderedUniqueIndexFields(
4038 const ClassType& type) {
4039 std::vector<Field> result;
4040 std::set<std::string> index_names;
4041 for (const Field& field : type.ComputeAllFields()) {
4042 if (field.index) {
4043 auto name_and_type = ExtractSimpleFieldArraySize(type, field.index->expr);
4044 if (!name_and_type) {
4045 return base::nullopt;
4046 }
4047 index_names.insert(name_and_type->name);
4048 }
4049 }
4050
4051 for (const Field& field : type.ComputeAllFields()) {
4052 if (index_names.count(field.name_and_type.name) != 0) {
4053 result.push_back(field);
4054 }
4055 }
4056
4057 return result;
4058}
4059
4060void CppClassGenerator::GenerateClass() {
4061 // Is<name>_NonInline(HeapObject)
4062 if (!type_->IsShape()) {
4063 cpp::Function f("Is"s + name_ + "_NonInline");
4064 f.SetDescription("Alias for HeapObject::Is"s + name_ +
4065 "() that avoids inlining.");
4066 f.SetExport(true);
4067 f.SetReturnType("bool");
4068 f.AddParameter("HeapObject", "o");
4069
4070 f.PrintDeclaration(hdr_);
4071 hdr_ << "\n";
4072 f.PrintDefinition(impl_, [&](std::ostream& stream) {
4073 stream << " return o.Is" << name_ << "();\n";
4074 });
4075 }
4076 hdr_ << "// Definition " << Position() << "\n";
4077 hdr_ << template_decl() << "\n";
4078 hdr_ << "class " << gen_name_ << " : public P {\n";
4079 hdr_ << " static_assert(\n"
4080 << " std::is_same<" << name_ << ", D>::value,\n"
4081 << " \"Use this class as direct base for " << name_ << ".\");\n";
4082 hdr_ << " static_assert(\n"
4083 << " std::is_same<" << super_->name() << ", P>::value,\n"
4084 << " \"Pass in " << super_->name()
4085 << " as second template parameter for " << gen_name_ << ".\");\n\n";
4086 hdr_ << " public: \n";
4087 hdr_ << " using Super = P;\n";
4088 hdr_ << " using TorqueGeneratedClass = " << gen_name_ << "<D,P>;\n\n";
4089 if (!type_->ShouldExport() && !type_->IsExtern()) {
4090 hdr_ << " protected: // not extern or @export\n";
4091 }
4092 for (const Field& f : type_->fields()) {
4093 CurrentSourcePosition::Scope scope(f.pos);
4094 std::vector<const Field*> struct_fields;
4095 GenerateFieldAccessors(f, struct_fields);
4096 }
4097 if (!type_->ShouldExport() && !type_->IsExtern()) {
4098 hdr_ << " public:\n";
4099 }
4100
4101 GenerateClassCasts();
4102
4103 std::vector<cpp::TemplateParameter> templateArgs = {
4104 cpp::TemplateParameter("D"), cpp::TemplateParameter("P")};
4105 cpp::Class c(std::move(templateArgs), gen_name_);
4106
4107 if (type_->ShouldGeneratePrint()) {
4108 hdr_ << " DECL_PRINTER(" << name_ << ")\n\n";
4109 }
4110
4111 if (type_->ShouldGenerateVerify()) {
4112 IfDefScope hdr_scope(hdr_, "VERIFY_HEAP");
4113 // V8_EXPORT_PRIVATE void Verify(Isolate*);
4114 cpp::Function f(&c, name_ + "Verify");
4115 f.SetExport();
4116 f.SetReturnType("void");
4117 f.AddParameter("Isolate*", "isolate");
4118 f.PrintDeclaration(hdr_);
4119
4120 IfDefScope impl_scope(impl_, "VERIFY_HEAP");
4121 impl_ << "\ntemplate <>\n";
4122 impl_ << "void " << gen_name_I_ << "::" << name_
4123 << "Verify(Isolate* isolate) {\n";
4124 impl_ << " TorqueGeneratedClassVerifiers::" << name_ << "Verify(" << name_
4125 << "::cast(*this), "
4126 "isolate);\n";
4127 impl_ << "}\n\n";
4128 impl_ << "\n";
4129 }
4130
4131 hdr_ << "\n";
4132 ClassFieldOffsetGenerator g(hdr_, inl_, type_, gen_name_,
4133 type_->GetSuperClass());
4134 for (auto f : type_->fields()) {
4135 CurrentSourcePosition::Scope scope(f.pos);
4136 g.RecordOffsetFor(f);
4137 }
4138 g.Finish();
4139 hdr_ << "\n";
4140
4141 auto index_fields = GetOrderedUniqueIndexFields(*type_);
4142
4143 if (!index_fields.has_value()) {
4144 hdr_ << " // SizeFor implementations not generated due to complex array "
4145 "lengths\n\n";
4146
4147 const Field& last_field = type_->LastField();
4148 std::string last_field_item_size =
4149 std::get<1>(*SizeOf(last_field.name_and_type.type));
4150
4151 // int AllocatedSize() const
4152 {
4153 cpp::Function f =
4154 cpp::Function::DefaultGetter("int", &c, "AllocatedSize");
4155 f.PrintDeclaration(hdr_);
4156
4157 f.PrintDefinition(inl_, [&](std::ostream& stream) {
4158 stream << " auto slice = "
4159 << Callable::PrefixNameForCCOutput(
4160 type_->GetSliceMacroName(last_field))
4161 << "(*static_cast<const D*>(this));\n";
4162 stream << " return static_cast<int>(std::get<1>(slice)) + "
4163 << last_field_item_size
4164 << " * static_cast<int>(std::get<2>(slice));\n";
4165 });
4166 }
4167 } else if (type_->ShouldGenerateBodyDescriptor() ||
4168 (!type_->IsAbstract() &&
4169 !type_->IsSubtypeOf(TypeOracle::GetJSObjectType()))) {
4170 cpp::Function f(&c, "SizeFor");
4171 f.SetReturnType("int32_t");
4172 f.SetFlags(cpp::Function::kStatic | cpp::Function::kConstexpr |
4173 cpp::Function::kV8Inline);
4174 for (const Field& field : *index_fields) {
4175 f.AddParameter("int", field.name_and_type.name);
4176 }
4177 f.PrintInlineDefinition(hdr_, [&](std::ostream& stream) {
4178 if (index_fields->empty()) {
4179 stream << " DCHECK(kHeaderSize == kSize && kHeaderSize == "
4180 << *type_->size().SingleValue() << ");\n";
4181 }
4182 stream << " int32_t size = kHeaderSize;\n";
4183 for (const Field& field : type_->ComputeAllFields()) {
4184 if (field.index) {
4185 auto index_name_and_type =
4186 *ExtractSimpleFieldArraySize(*type_, field.index->expr);
4187 stream << " size += " << index_name_and_type.name << " * "
4188 << std::get<0>(field.GetFieldSizeInformation()) << ";\n";
4189 }
4190 }
4191 if (type_->size().Alignment() < TargetArchitecture::TaggedSize()) {
4192 stream << " size = OBJECT_POINTER_ALIGN(size);\n";
4193 }
4194 stream << " return size;\n";
4195 });
4196
4197 // V8_INLINE int32_t AllocatedSize() const
4198 {
4199 cpp::Function allocated_size_f =
4200 cpp::Function::DefaultGetter("int32_t", &c, "AllocatedSize");
4201 allocated_size_f.SetFlag(cpp::Function::kV8Inline);
4202 allocated_size_f.PrintInlineDefinition(hdr_, [&](std::ostream& stream) {
4203 stream << " return SizeFor(";
4204 bool first = true;
4205 for (auto field : *index_fields) {
4206 if (!first) stream << ", ";
4207 stream << "this->" << field.name_and_type.name << "()";
4208 first = false;
4209 }
4210 stream << ");\n";
4211 });
4212 }
4213 }
4214
4215 hdr_ << " friend class Factory;\n\n";
4216
4217 GenerateClassConstructors();
4218
4219 hdr_ << "};\n\n";
4220
4221 if (type_->ShouldGenerateFullClassDefinition()) {
4222 // If this class extends from another class which is defined in the same tq
4223 // file, and that other class doesn't generate a full class definition, then
4224 // the resulting .inc file would be uncompilable due to ordering
4225 // requirements: the generated file must go before the hand-written
4226 // definition of the base class, but it must also go after that same
4227 // hand-written definition.
4228 base::Optional<const ClassType*> parent = type_->parent()->ClassSupertype();
4229 while (parent) {
4230 if ((*parent)->ShouldGenerateCppClassDefinitions() &&
4231 !(*parent)->ShouldGenerateFullClassDefinition() &&
4232 (*parent)->AttributedToFile() == type_->AttributedToFile()) {
4233 Error("Exported ", *type_,
4234 " cannot be in the same file as its parent extern ", **parent);
4235 }
4236 parent = (*parent)->parent()->ClassSupertype();
4237 }
4238
4239 GenerateClassExport(type_, hdr_, inl_);
4240 }
4241}
4242
4243void CppClassGenerator::GenerateCppObjectDefinitionAsserts() {
4244 hdr_ << "// Definition " << Position() << "\n"
4245 << template_decl() << "\n"
4246 << "class " << gen_name_ << "Asserts {\n";
4247
4248 ClassFieldOffsetGenerator g(hdr_, inl_, type_, gen_name_,
4249 type_->GetSuperClass());
4250 for (auto f : type_->fields()) {
4251 CurrentSourcePosition::Scope scope(f.pos);
4252 g.RecordOffsetFor(f);
4253 }
4254 g.Finish();
4255 hdr_ << "\n";
4256
4257 for (auto f : type_->fields()) {
4258 std::string field = "k" + CamelifyString(f.name_and_type.name) + "Offset";
4259 std::string type = f.name_and_type.type->SimpleName();
4260 hdr_ << " static_assert(" << field << " == D::" << field << ",\n"
4261 << " \"Values of " << name_ << "::" << field
4262 << " defined in Torque and C++ do not match\");\n"
4263 << " static_assert(StaticStringsEqual(\"" << type << "\", D::k"
4264 << CamelifyString(f.name_and_type.name) << "TqFieldType),\n"
4265 << " \"Types of " << name_ << "::" << field
4266 << " specified in Torque and C++ do not match\");\n";
4267 }
4268 hdr_ << " static_assert(kSize == D::kSize);\n";
4269
4270 hdr_ << "};\n\n";
4271}
4272
4273void CppClassGenerator::GenerateClassCasts() {
4274 cpp::Class owner({cpp::TemplateParameter("D"), cpp::TemplateParameter("P")},
4275 gen_name_);
4276 cpp::Function f(&owner, "cast");
4277 f.SetFlags(cpp::Function::kV8Inline | cpp::Function::kStatic);
4278 f.SetReturnType("D");
4279 f.AddParameter("Object", "object");
4280
4281 // V8_INLINE static D cast(Object)
4282 f.PrintDeclaration(hdr_);
4283 f.PrintDefinition(inl_, [](std::ostream& stream) {
4284 stream << " return D(object.ptr());\n";
4285 });
4286 // V8_INLINE static D unchecked_cast(Object)
4287 f.SetName("unchecked_cast");
4288 f.PrintInlineDefinition(hdr_, [](std::ostream& stream) {
4289 stream << " return bit_cast<D>(object);\n";
4290 });
4291}
4292
4293SourcePosition CppClassGenerator::Position() { return type_->GetPosition(); }
4294
4295void CppClassGenerator::GenerateClassConstructors() {
4296 const ClassType* typecheck_type = type_;
4297 while (typecheck_type->IsShape()) {
4298 typecheck_type = typecheck_type->GetSuperClass();
4299
4300 // Shapes have already been checked earlier to inherit from JSObject, so we
4301 // should have found an appropriate type.
4302 DCHECK(typecheck_type)((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
4339namespace {
4340std::string GenerateRuntimeTypeCheck(const Type* type,
4341 const std::string& value) {
4342 bool maybe_object = !type->IsSubtypeOf(TypeOracle::GetStrongTaggedType());
4343 std::stringstream type_check;
4344 bool at_start = true;
4345 // If weak pointers are allowed, then start by checking for a cleared value.
4346 if (maybe_object) {
4347 type_check << value << ".IsCleared()";
4348 at_start = false;
4349 }
4350 for (const TypeChecker& runtime_type : type->GetTypeCheckers()) {
4351 if (!at_start) type_check << " || ";
4352 at_start = false;
4353 if (maybe_object) {
4354 bool strong = runtime_type.weak_ref_to.empty();
4355 if (strong && runtime_type.type == WEAK_HEAP_OBJECT) {
4356 // Rather than a generic Weak<T>, this is the basic type WeakHeapObject.
4357 // We can't validate anything more about the type of the object pointed
4358 // to, so just check that it's weak.
4359 type_check << value << ".IsWeak()";
4360 } else {
4361 type_check << "(" << (strong ? "!" : "") << value << ".IsWeak() && "
4362 << value << ".GetHeapObjectOrSmi().Is"
4363 << (strong ? runtime_type.type : runtime_type.weak_ref_to)
4364 << "())";
4365 }
4366 } else {
4367 type_check << value << ".Is" << runtime_type.type << "()";
4368 }
4369 }
4370 return type_check.str();
4371}
4372
4373void GenerateBoundsDCheck(std::ostream& os, const std::string& index,
4374 const ClassType* type, const Field& f) {
4375 os << " DCHECK_GE(" << index << ", 0);\n";
4376 std::string length_expression;
4377 if (base::Optional<NameAndType> array_length =
4378 ExtractSimpleFieldArraySize(*type, f.index->expr)) {
4379 length_expression = "this ->" + array_length->name + "()";
4380 } else {
4381 // The length is element 2 in the flattened field slice.
4382 length_expression =
4383 "static_cast<int>(std::get<2>(" +
4384 Callable::PrefixNameForCCOutput(type->GetSliceMacroName(f)) +
4385 "(*static_cast<const D*>(this))))";
4386 }
4387 os << " DCHECK_LT(" << index << ", " << length_expression << ");\n";
4388}
4389
4390bool CanGenerateFieldAccessors(const Type* field_type) {
4391 // float64_or_hole should be treated like float64. For now, we don't need it.
4392 // TODO(v8:10391) Generate accessors for external pointers.
4393 return field_type != TypeOracle::GetVoidType() &&
4394 field_type != TypeOracle::GetFloat64OrHoleType() &&
4395 !field_type->IsSubtypeOf(TypeOracle::GetExternalPointerType());
4396}
4397} // namespace
4398
4399// TODO(sigurds): Keep in sync with DECL_ACCESSORS and ACCESSORS macro.
4400void CppClassGenerator::GenerateFieldAccessors(
4401 const Field& class_field, std::vector<const Field*>& struct_fields) {
4402 const Field& innermost_field =
4403 struct_fields.empty() ? class_field : *struct_fields.back();
4404 const Type* field_type = innermost_field.name_and_type.type;
4405 if (!CanGenerateFieldAccessors(field_type)) return;
4406
4407 if (const StructType* struct_type = StructType::DynamicCast(field_type)) {
4408 struct_fields.resize(struct_fields.size() + 1);
4409 for (const Field& struct_field : struct_type->fields()) {
4410 struct_fields[struct_fields.size() - 1] = &struct_field;
4411 GenerateFieldAccessors(class_field, struct_fields);
4412 }
4413 struct_fields.resize(struct_fields.size() - 1);
4414 return;
4415 }
4416
4417 bool indexed = class_field.index && !class_field.index->optional;
4418 std::string type_name = GetTypeNameForAccessor(innermost_field);
4419 bool can_contain_heap_objects = CanContainHeapObjects(field_type);
4420
4421 // Assemble an accessor name by accumulating together all of the nested field
4422 // names.
4423 std::string name = class_field.name_and_type.name;
4424 for (const Field* nested_struct_field : struct_fields) {
4425 name += "_" + nested_struct_field->name_and_type.name;
4426 }
4427
4428 // Generate declarations in header.
4429 if (can_contain_heap_objects && !field_type->IsClassType() &&
4430 !field_type->IsStructType() &&
4431 field_type != TypeOracle::GetObjectType()) {
4432 hdr_ << " // Torque type: " << field_type->ToString() << "\n";
4433 }
4434
4435 std::vector<cpp::TemplateParameter> templateParameters = {
4436 cpp::TemplateParameter("D"), cpp::TemplateParameter("P")};
4437 cpp::Class owner(std::move(templateParameters), gen_name_);
4438
4439 // getter
4440 {
4441 auto getter = cpp::Function::DefaultGetter(type_name, &owner, name);
4442 if (indexed) {
4443 getter.AddParameter("int", "i");
4444 }
4445 const char* tag_argument;
4446 switch (class_field.read_synchronization) {
4447 case FieldSynchronization::kNone:
4448 tag_argument = "";
4449 break;
4450 case FieldSynchronization::kRelaxed:
4451 getter.AddParameter("RelaxedLoadTag");
4452 tag_argument = ", kRelaxedLoad";
4453 break;
4454 case FieldSynchronization::kAcquireRelease:
4455 getter.AddParameter("AcquireLoadTag");
4456 tag_argument = ", kAcquireLoad";
4457 break;
4458 }
4459
4460 getter.PrintDeclaration(hdr_);
4461
4462 // For tagged data, generate the extra getter that derives an
4463 // PtrComprCageBase from the current object's pointer.
4464 if (can_contain_heap_objects) {
4465 getter.PrintDefinition(inl_, [&](auto& stream) {
4466 stream
4467 << " PtrComprCageBase cage_base = GetPtrComprCageBase(*this);\n";
4468 stream << " return " << gen_name_ << "::" << name << "(cage_base"
4469 << (indexed ? ", i" : "") << tag_argument << ");\n";
4470 });
4471
4472 getter.InsertParameter(0, "PtrComprCageBase", "cage_base");
4473 getter.PrintDeclaration(hdr_);
4474 }
4475
4476 getter.PrintDefinition(inl_, [&](auto& stream) {
4477 stream << " " << type_name << " value;\n";
4478 EmitLoadFieldStatement(stream, class_field, struct_fields);
4479 stream << " return value;\n";
4480 });
4481 }
4482
4483 // setter
4484 {
4485 auto setter = cpp::Function::DefaultSetter(
4486 &owner, std::string("set_") + name, type_name, "value");
4487 if (indexed) {
4488 setter.InsertParameter(0, "int", "i");
4489 }
4490 switch (class_field.write_synchronization) {
4491 case FieldSynchronization::kNone:
4492 break;
4493 case FieldSynchronization::kRelaxed:
4494 setter.AddParameter("RelaxedStoreTag");
4495 break;
4496 case FieldSynchronization::kAcquireRelease:
4497 setter.AddParameter("ReleaseStoreTag");
4498 break;
4499 }
4500 if (can_contain_heap_objects) {
4501 setter.AddParameter("WriteBarrierMode", "mode", "UPDATE_WRITE_BARRIER");
4502 }
4503 setter.PrintDeclaration(hdr_);
4504
4505 setter.PrintDefinition(inl_, [&](auto& stream) {
4506 EmitStoreFieldStatement(stream, class_field, struct_fields);
4507 });
4508 }
4509
4510 hdr_ << "\n";
4511}
4512
4513std::string CppClassGenerator::GetFieldOffsetForAccessor(const Field& f) {
4514 if (f.offset.has_value()) {
4515 return "k" + CamelifyString(f.name_and_type.name) + "Offset";
4516 }
4517 return CamelifyString(f.name_and_type.name) + "Offset()";
4518}
4519
4520std::string CppClassGenerator::GetTypeNameForAccessor(const Field& f) {
4521 const Type* field_type = f.name_and_type.type;
4522 if (!field_type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
4523 const Type* constexpr_version = field_type->ConstexprVersion();
4524 if (!constexpr_version) {
4525 Error("Field accessor for ", type_->name(), ":: ", f.name_and_type.name,
4526 " cannot be generated because its type ", *field_type,
4527 " is neither a subclass of Object nor does the type have a "
4528 "constexpr "
4529 "version.")
4530 .Position(f.pos)
4531 .Throw();
4532 }
4533 return constexpr_version->GetGeneratedTypeName();
4534 }
4535 if (field_type->IsSubtypeOf(TypeOracle::GetSmiType())) {
4536 // Follow the convention to create Smi accessors with type int.
4537 return "int";
4538 }
4539 return field_type->UnhandlifiedCppTypeName();
4540}
4541
4542bool CppClassGenerator::CanContainHeapObjects(const Type* t) {
4543 return t->IsSubtypeOf(TypeOracle::GetTaggedType()) &&
4544 !t->IsSubtypeOf(TypeOracle::GetSmiType());
4545}
4546
4547void CppClassGenerator::EmitLoadFieldStatement(
4548 std::ostream& stream, const Field& class_field,
4549 std::vector<const Field*>& struct_fields) {
4550 const Field& innermost_field =
4551 struct_fields.empty() ? class_field : *struct_fields.back();
4552 const Type* field_type = innermost_field.name_and_type.type;
4553 std::string type_name = GetTypeNameForAccessor(innermost_field);
4554 const std::string class_field_size =
4555 std::get<1>(class_field.GetFieldSizeInformation());
4556
4557 // field_offset contains both the offset from the beginning of the object to
4558 // the class field and the combined offsets of any nested struct fields
4559 // within, but not the index adjustment.
4560 std::string field_offset = GetFieldOffsetForAccessor(class_field);
4561 for (const Field* nested_struct_field : struct_fields) {
4562 field_offset += " + " + std::to_string(*nested_struct_field->offset);
4563 }
4564
4565 std::string offset = field_offset;
4566 if (class_field.index) {
4567 const char* index = class_field.index->optional ? "0" : "i";
4568 GenerateBoundsDCheck(stream, index, type_, class_field);
4569 stream << " int offset = " << field_offset << " + " << index << " * "
4570 << class_field_size << ";\n";
4571 offset = "offset";
4572 }
4573
4574 stream << " value = ";
4575
4576 if (!field_type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
4577 if (class_field.read_synchronization ==
4578 FieldSynchronization::kAcquireRelease) {
4579 ReportError("Torque doesn't support @cppAcquireRead on untagged data");
4580 } else if (class_field.read_synchronization ==
4581 FieldSynchronization::kRelaxed) {
4582 ReportError("Torque doesn't support @cppRelaxedRead on untagged data");
4583 }
4584 stream << "this->template ReadField<" << type_name << ">(" << offset
4585 << ");\n";
4586 } else {
4587 const char* load;
4588 switch (class_field.read_synchronization) {
4589 case FieldSynchronization::kNone:
4590 load = "load";
4591 break;
4592 case FieldSynchronization::kRelaxed:
4593 load = "Relaxed_Load";
4594 break;
4595 case FieldSynchronization::kAcquireRelease:
4596 load = "Acquire_Load";
4597 break;
4598 }
4599 bool is_smi = field_type->IsSubtypeOf(TypeOracle::GetSmiType());
4600 const std::string load_type = is_smi ? "Smi" : type_name;
4601 const char* postfix = is_smi ? ".value()" : "";
4602 const char* optional_cage_base = is_smi ? "" : "cage_base, ";
4603
4604 stream << "TaggedField<" << load_type << ">::" << load << "("
4605 << optional_cage_base << "*this, " << offset << ")" << postfix
4606 << ";\n";
4607 }
4608
4609 if (CanContainHeapObjects(field_type)) {
4610 stream << " DCHECK(" << GenerateRuntimeTypeCheck(field_type, "value")
4611 << ");\n";
4612 }
4613}
4614
4615void CppClassGenerator::EmitStoreFieldStatement(
4616 std::ostream& stream, const Field& class_field,
4617 std::vector<const Field*>& struct_fields) {
4618 const Field& innermost_field =
4619 struct_fields.empty() ? class_field : *struct_fields.back();
4620 const Type* field_type = innermost_field.name_and_type.type;
4621 std::string type_name = GetTypeNameForAccessor(innermost_field);
4622 const std::string class_field_size =
4623 std::get<1>(class_field.GetFieldSizeInformation());
4624
4625 // field_offset contains both the offset from the beginning of the object to
4626 // the class field and the combined offsets of any nested struct fields
4627 // within, but not the index adjustment.
4628 std::string field_offset = GetFieldOffsetForAccessor(class_field);
4629 for (const Field* nested_struct_field : struct_fields) {
4630 field_offset += " + " + std::to_string(*nested_struct_field->offset);
4631 }
4632
4633 std::string offset = field_offset;
4634 if (class_field.index) {
4635 const char* index = class_field.index->optional ? "0" : "i";
4636 GenerateBoundsDCheck(stream, index, type_, class_field);
4637 stream << " int offset = " << field_offset << " + " << index << " * "
4638 << class_field_size << ";\n";
4639 offset = "offset";
4640 }
4641
4642 if (!field_type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
4643 stream << " this->template WriteField<" << type_name << ">(" << offset
4644 << ", value);\n";
4645 } else {
4646 bool strong_pointer = field_type->IsSubtypeOf(TypeOracle::GetObjectType());
4647 bool is_smi = field_type->IsSubtypeOf(TypeOracle::GetSmiType());
4648 const char* write_macro;
4649 if (!strong_pointer) {
4650 if (class_field.write_synchronization ==
4651 FieldSynchronization::kAcquireRelease) {
4652 ReportError("Torque doesn't support @releaseWrite on weak fields");
4653 }
4654 write_macro = "RELAXED_WRITE_WEAK_FIELD";
4655 } else {
4656 switch (class_field.write_synchronization) {
4657 case FieldSynchronization::kNone:
4658 write_macro = "WRITE_FIELD";
4659 break;
4660 case FieldSynchronization::kRelaxed:
4661 write_macro = "RELAXED_WRITE_FIELD";
4662 break;
4663 case FieldSynchronization::kAcquireRelease:
4664 write_macro = "RELEASE_WRITE_FIELD";
4665 break;
4666 }
4667 }
4668 const std::string value_to_write = is_smi ? "Smi::FromInt(value)" : "value";
4669
4670 if (!is_smi) {
4671 stream << " SLOW_DCHECK("
4672 << GenerateRuntimeTypeCheck(field_type, "value") << ");\n";
4673 }
4674 stream << " " << write_macro << "(*this, " << offset << ", "
4675 << value_to_write << ");\n";
4676 if (!is_smi) {
4677 const char* write_barrier = strong_pointer
4678 ? "CONDITIONAL_WRITE_BARRIER"
4679 : "CONDITIONAL_WEAK_WRITE_BARRIER";
4680 stream << " " << write_barrier << "(*this, " << offset
4681 << ", value, mode);\n";
4682 }
4683 }
4684}
4685
4686void GenerateStructLayoutDescription(std::ostream& header,
4687 const StructType* type) {
4688 header << "struct TorqueGenerated" << CamelifyString(type->name())
4689 << "Offsets {\n";
4690 for (const Field& field : type->fields()) {
4691 header << " static constexpr int k"
4692 << CamelifyString(field.name_and_type.name)
4693 << "Offset = " << *field.offset << ";\n";
4694 }
4695 header << " static constexpr int kSize = " << type->PackedSize() << ";\n";
4696 header << "};\n\n";
4697}
4698
4699} // namespace
4700
4701void ImplementationVisitor::GenerateClassDefinitions(
4702 const std::string& output_directory) {
4703 std::stringstream factory_header;
4704 std::stringstream factory_impl;
4705 std::string factory_basename = "factory";
4706
4707 std::stringstream forward_declarations;
4708 std::string forward_declarations_filename = "class-forward-declarations.h";
4709
4710 {
4711 factory_impl << "#include \"src/heap/factory-base.h\"\n";
4712 factory_impl << "#include \"src/heap/factory-base-inl.h\"\n";
4713 factory_impl << "#include \"src/heap/heap.h\"\n";
4714 factory_impl << "#include \"src/heap/heap-inl.h\"\n";
4715 factory_impl << "#include \"src/execution/isolate.h\"\n";
4716 factory_impl << "#include "
4717 "\"src/objects/all-objects-inl.h\"\n\n";
4718 NamespaceScope factory_impl_namespaces(factory_impl, {"v8", "internal"});
4719 factory_impl << "\n";
4720
4721 IncludeGuardScope include_guard(forward_declarations,
4722 forward_declarations_filename);
4723 NamespaceScope forward_declarations_namespaces(forward_declarations,
4724 {"v8", "internal"});
4725
4726 std::set<const StructType*, TypeLess> structs_used_in_classes;
4727
4728 // Emit forward declarations.
4729 for (const ClassType* type : TypeOracle::GetClasses()) {
4730 CurrentSourcePosition::Scope position_activator(type->GetPosition());
4731 auto& streams = GlobalContext::GeneratedPerFile(type->AttributedToFile());
4732 std::ostream& header = streams.class_definition_headerfile;
4733 std::string name = type->ShouldGenerateCppClassDefinitions()
4734 ? type->name()
4735 : type->GetGeneratedTNodeTypeName();
4736 if (type->ShouldGenerateCppClassDefinitions()) {
4737 header << "class " << name << ";\n";
4738 }
4739 forward_declarations << "class " << name << ";\n";
4740 }
4741
4742 for (const ClassType* type : TypeOracle::GetClasses()) {
4743 CurrentSourcePosition::Scope position_activator(type->GetPosition());
4744 auto& streams = GlobalContext::GeneratedPerFile(type->AttributedToFile());
4745 std::ostream& header = streams.class_definition_headerfile;
4746 std::ostream& inline_header = streams.class_definition_inline_headerfile;
4747 std::ostream& implementation = streams.class_definition_ccfile;
4748
4749 if (type->ShouldGenerateCppClassDefinitions()) {
4750 CppClassGenerator g(type, header, inline_header, implementation);
4751 g.GenerateClass();
4752 } else if (type->ShouldGenerateCppObjectDefinitionAsserts()) {
4753 CppClassGenerator g(type, header, inline_header, implementation);
4754 g.GenerateCppObjectDefinitionAsserts();
4755 }
4756 for (const Field& f : type->fields()) {
4757 const Type* field_type = f.name_and_type.type;
4758 if (auto field_as_struct = field_type->StructSupertype()) {
4759 structs_used_in_classes.insert(*field_as_struct);
4760 }
4761 }
4762 if (type->ShouldGenerateFactoryFunction()) {
4763 std::string return_type = type->HandlifiedCppTypeName();
4764 std::string function_name = "New" + type->name();
4765 std::stringstream parameters;
4766 for (const Field& f : type->ComputeAllFields()) {
4767 if (f.name_and_type.name == "map") continue;
4768 if (!f.index) {
4769 std::string type_string =
4770 f.name_and_type.type->HandlifiedCppTypeName();
4771 parameters << type_string << " " << f.name_and_type.name << ", ";
4772 }
4773 }
4774 parameters << "AllocationType allocation_type";
4775
4776 factory_header << return_type << " " << function_name << "("
4777 << parameters.str() << ");\n";
4778 factory_impl << "template <typename Impl>\n";
4779 factory_impl << return_type
4780 << " TorqueGeneratedFactory<Impl>::" << function_name
4781 << "(" << parameters.str() << ") {\n";
4782
4783 factory_impl << " int size = ";
4784 const ClassType* super = type->GetSuperClass();
4785 std::string gen_name = "TorqueGenerated" + type->name();
4786 std::string gen_name_T =
4787 gen_name + "<" + type->name() + ", " + super->name() + ">";
4788 factory_impl << gen_name_T << "::SizeFor(";
4789
4790 bool first = true;
4791 auto index_fields = GetOrderedUniqueIndexFields(*type);
4792 CHECK(index_fields.has_value())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
4866namespace {
4867void GeneratePrintDefinitionsForClass(std::ostream& impl, const ClassType* type,
4868 const std::string& gen_name,
4869 const std::string& gen_name_T,
4870 const std::string template_params) {
4871 impl << template_params << "\n";
4872 impl << "void " << gen_name_T << "::" << type->name()
4873 << "Print(std::ostream& os) {\n";
4874 impl << " this->PrintHeader(os, \"" << type->name() << "\");\n";
4875 auto hierarchy = type->GetHierarchy();
4876 std::map<std::string, const AggregateType*> field_names;
4877 for (const AggregateType* aggregate_type : hierarchy) {
4878 for (const Field& f : aggregate_type->fields()) {
4879 if (f.name_and_type.name == "map" || f.index.has_value() ||
4880 !CanGenerateFieldAccessors(f.name_and_type.type)) {
4881 continue;
4882 }
4883 std::string getter = f.name_and_type.name;
4884 if (aggregate_type != type) {
4885 // We must call getters directly on the class that provided them,
4886 // because a subclass could have hidden them.
4887 getter = aggregate_type->name() + "::TorqueGeneratedClass::" + getter;
4888 }
4889 if (f.name_and_type.type->IsSubtypeOf(TypeOracle::GetSmiType()) ||
4890 !f.name_and_type.type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
4891 impl << " os << \"\\n - " << f.name_and_type.name << ": \" << ";
4892 if (f.name_and_type.type->StructSupertype()) {
4893 // TODO(turbofan): Print struct fields too.
4894 impl << "\" <struct field printing still unimplemented>\";\n";
4895 } else {
4896 impl << "this->" << getter;
4897 switch (f.read_synchronization) {
4898 case FieldSynchronization::kNone:
4899 impl << "();\n";
4900 break;
4901 case FieldSynchronization::kRelaxed:
4902 impl << "(kRelaxedLoad);\n";
4903 break;
4904 case FieldSynchronization::kAcquireRelease:
4905 impl << "(kAcquireLoad);\n";
4906 break;
4907 }
4908 }
4909 } else {
4910 impl << " os << \"\\n - " << f.name_and_type.name << ": \" << "
4911 << "Brief(this->" << getter;
4912 switch (f.read_synchronization) {
4913 case FieldSynchronization::kNone:
4914 impl << "());\n";
4915 break;
4916 case FieldSynchronization::kRelaxed:
4917 impl << "(kRelaxedLoad));\n";
4918 break;
4919 case FieldSynchronization::kAcquireRelease:
4920 impl << "(kAcquireLoad));\n";
4921 break;
4922 }
4923 }
4924 }
4925 }
4926 impl << " os << '\\n';\n";
4927 impl << "}\n\n";
4928}
4929} // namespace
4930
4931void ImplementationVisitor::GeneratePrintDefinitions(
4932 const std::string& output_directory) {
4933 std::stringstream impl;
4934 std::string file_name = "objects-printer.cc";
4935 {
4936 IfDefScope object_print(impl, "OBJECT_PRINT");
4937
4938 impl << "#include <iosfwd>\n\n";
4939 impl << "#include \"src/objects/all-objects-inl.h\"\n\n";
4940
4941 NamespaceScope impl_namespaces(impl, {"v8", "internal"});
4942
4943 for (const ClassType* type : TypeOracle::GetClasses()) {
4944 if (!type->ShouldGeneratePrint()) continue;
4945 DCHECK(type->ShouldGenerateCppClassDefinitions())((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
4960base::Optional<std::string> MatchSimpleBodyDescriptor(const ClassType* type) {
4961 std::vector<ObjectSlotKind> slots = type->ComputeHeaderSlotKinds();
4962 if (!type->HasStaticSize()) {
4963 slots.push_back(*type->ComputeArraySlotKind());
4964 }
4965
4966 // Skip the map slot.
4967 size_t i = 1;
4968 while (i < slots.size() && slots[i] == ObjectSlotKind::kNoPointer) ++i;
4969 if (i == slots.size()) return "DataOnlyBodyDescriptor";
4970 bool has_weak_pointers = false;
4971 size_t start_index = i;
4972 for (; i < slots.size(); ++i) {
4973 if (slots[i] == ObjectSlotKind::kStrongPointer) {
4974 continue;
4975 } else if (slots[i] == ObjectSlotKind::kMaybeObjectPointer) {
4976 has_weak_pointers = true;
4977 } else if (slots[i] == ObjectSlotKind::kNoPointer) {
4978 break;
4979 } else {
4980 return base::nullopt;
4981 }
4982 }
4983 size_t end_index = i;
4984 for (; i < slots.size(); ++i) {
4985 if (slots[i] != ObjectSlotKind::kNoPointer) return base::nullopt;
4986 }
4987 size_t start_offset = start_index * TargetArchitecture::TaggedSize();
4988 size_t end_offset = end_index * TargetArchitecture::TaggedSize();
4989 // We pick a suffix-range body descriptor even in cases where the object size
4990 // is fixed, to reduce the amount of code executed for object visitation.
4991 if (end_index == slots.size()) {
4992 return ToString("SuffixRange", has_weak_pointers ? "Weak" : "",
4993 "BodyDescriptor<", start_offset, ">");
4994 }
4995 if (!has_weak_pointers) {
4996 return ToString("FixedRangeBodyDescriptor<", start_offset, ", ", end_offset,
4997 ">");
4998 }
4999 return base::nullopt;
5000}
5001
5002void ImplementationVisitor::GenerateBodyDescriptors(
5003 const std::string& output_directory) {
5004 std::string file_name = "objects-body-descriptors-inl.inc";
5005 std::stringstream h_contents;
5006
5007 for (const ClassType* type : TypeOracle::GetClasses()) {
5008 std::string name = type->name();
5009 if (!type->ShouldGenerateBodyDescriptor()) continue;
5010
5011 bool has_array_fields = !type->HasStaticSize();
5012 std::vector<ObjectSlotKind> header_slot_kinds =
5013 type->ComputeHeaderSlotKinds();
5014 base::Optional<ObjectSlotKind> array_slot_kind =
5015 type->ComputeArraySlotKind();
5016 DCHECK_EQ(has_array_fields, array_slot_kind.has_value())((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
5132namespace {
5133
5134// Generate verification code for a single piece of class data, which might be
5135// nested within a struct or might be a single element in an indexed field (or
5136// both).
5137void GenerateFieldValueVerifier(const std::string& class_name, bool indexed,
5138 std::string offset, const Field& leaf_field,
5139 std::string indexed_field_size,
5140 std::ostream& cc_contents, bool is_map) {
5141 const Type* field_type = leaf_field.name_and_type.type;
5142
5143 bool maybe_object =
5144 !field_type->IsSubtypeOf(TypeOracle::GetStrongTaggedType());
5145 const char* object_type = maybe_object ? "MaybeObject" : "Object";
5146 const char* verify_fn =
5147 maybe_object ? "VerifyMaybeObjectPointer" : "VerifyPointer";
5148 if (indexed) {
5149 offset += " + i * " + indexed_field_size;
5150 }
5151 // Name the local var based on the field name for nicer CHECK output.
5152 const std::string value = leaf_field.name_and_type.name + "__value";
5153
5154 // Read the field.
5155 if (is_map) {
5156 cc_contents << " " << object_type << " " << value << " = o.map();\n";
5157 } else {
5158 cc_contents << " " << object_type << " " << value << " = TaggedField<"
5159 << object_type << ">::load(o, " << offset << ");\n";
5160 }
5161
5162 // Call VerifyPointer or VerifyMaybeObjectPointer on it.
5163 cc_contents << " " << object_type << "::" << verify_fn << "(isolate, "
5164 << value << ");\n";
5165
5166 // Check that the value is of an appropriate type. We can skip this part for
5167 // the Object type because it would not check anything beyond what we already
5168 // checked with VerifyPointer.
5169 if (field_type != TypeOracle::GetObjectType()) {
5170 cc_contents << " CHECK(" << GenerateRuntimeTypeCheck(field_type, value)
5171 << ");\n";
5172 }
5173}
5174
5175void GenerateClassFieldVerifier(const std::string& class_name,
5176 const ClassType& class_type, const Field& f,
5177 std::ostream& h_contents,
5178 std::ostream& cc_contents) {
5179 const Type* field_type = f.name_and_type.type;
5180
5181 // We only verify tagged types, not raw numbers or pointers. Structs
5182 // consisting of tagged types are also included.
5183 if (!field_type->IsSubtypeOf(TypeOracle::GetTaggedType()) &&
5184 !field_type->StructSupertype())
5185 return;
5186 if (field_type == TypeOracle::GetFloat64OrHoleType()) return;
5187 // Do not verify if the field may be uninitialized.
5188 if (TypeOracle::GetUninitializedType()->IsSubtypeOf(field_type)) return;
5189
5190 std::string field_start_offset;
5191 if (f.index) {
5192 field_start_offset = f.name_and_type.name + "__offset";
5193 std::string length = f.name_and_type.name + "__length";
5194 cc_contents << " intptr_t " << field_start_offset << ", " << length
5195 << ";\n";
5196 cc_contents << " std::tie(std::ignore, " << field_start_offset << ", "
5197 << length << ") = "
5198 << Callable::PrefixNameForCCOutput(
5199 class_type.GetSliceMacroName(f))
5200 << "(o);\n";
5201
5202 // Slices use intptr, but TaggedField<T>.load() uses int, so verify that
5203 // such a cast is valid.
5204 cc_contents << " CHECK_EQ(" << field_start_offset << ", static_cast<int>("
5205 << field_start_offset << "));\n";
5206 cc_contents << " CHECK_EQ(" << length << ", static_cast<int>(" << length
5207 << "));\n";
5208 field_start_offset = "static_cast<int>(" + field_start_offset + ")";
5209 length = "static_cast<int>(" + length + ")";
5210
5211 cc_contents << " for (int i = 0; i < " << length << "; ++i) {\n";
5212 } else {
5213 // Non-indexed fields have known offsets.
5214 field_start_offset = std::to_string(*f.offset);
5215 cc_contents << " {\n";
5216 }
5217
5218 if (auto struct_type = field_type->StructSupertype()) {
5219 for (const Field& struct_field : (*struct_type)->fields()) {
5220 if (struct_field.name_and_type.type->IsSubtypeOf(
5221 TypeOracle::GetTaggedType())) {
5222 GenerateFieldValueVerifier(
5223 class_name, f.index.has_value(),
5224 field_start_offset + " + " + std::to_string(*struct_field.offset),
5225 struct_field, std::to_string((*struct_type)->PackedSize()),
5226 cc_contents, f.name_and_type.name == "map");
5227 }
5228 }
5229 } else {
5230 GenerateFieldValueVerifier(class_name, f.index.has_value(),
5231 field_start_offset, f, "kTaggedSize",
5232 cc_contents, f.name_and_type.name == "map");
5233 }
5234
5235 cc_contents << " }\n";
5236}
5237
5238} // namespace
5239
5240void ImplementationVisitor::GenerateClassVerifiers(
5241 const std::string& output_directory) {
5242 std::string file_name = "class-verifiers";
5243 std::stringstream h_contents;
5244 std::stringstream cc_contents;
5245 {
5246 IncludeGuardScope include_guard(h_contents, file_name + ".h");
5247 IfDefScope verify_heap_h(h_contents, "VERIFY_HEAP");
5248 IfDefScope verify_heap_cc(cc_contents, "VERIFY_HEAP");
5249
5250 h_contents << "#include \"src/base/macros.h\"\n\n";
5251
5252 cc_contents << "#include \"torque-generated/" << file_name << ".h\"\n\n";
5253 cc_contents << "#include \"src/objects/all-objects-inl.h\"\n";
5254
5255 IncludeObjectMacrosScope object_macros(cc_contents);
5256
5257 NamespaceScope h_namespaces(h_contents, {"v8", "internal"});
5258 NamespaceScope cc_namespaces(cc_contents, {"v8", "internal"});
5259
5260 cc_contents
5261 << "#include \"torque-generated/test/torque/test-torque-tq-inl.inc\"\n";
5262
5263 // Generate forward declarations to avoid including any headers.
5264 h_contents << "class Isolate;\n";
5265 for (const ClassType* type : TypeOracle::GetClasses()) {
5266 if (!type->ShouldGenerateVerify()) continue;
5267 h_contents << "class " << type->name() << ";\n";
5268 }
5269
5270 const char* verifier_class = "TorqueGeneratedClassVerifiers";
5271
5272 h_contents << "class V8_EXPORT_PRIVATE " << verifier_class << "{\n";
5273 h_contents << " public:\n";
5274
5275 for (const ClassType* type : TypeOracle::GetClasses()) {
5276 std::string name = type->name();
5277 if (!type->ShouldGenerateVerify()) continue;
5278
5279 std::string method_name = name + "Verify";
5280
5281 h_contents << " static void " << method_name << "(" << name
5282 << " o, Isolate* isolate);\n";
5283
5284 cc_contents << "void " << verifier_class << "::" << method_name << "("
5285 << name << " o, Isolate* isolate) {\n";
5286
5287 // First, do any verification for the super class. Not all classes have
5288 // verifiers, so skip to the nearest super class that has one.
5289 const ClassType* super_type = type->GetSuperClass();
5290 while (super_type && !super_type->ShouldGenerateVerify()) {
5291 super_type = super_type->GetSuperClass();
5292 }
5293 if (super_type) {
5294 std::string super_name = super_type->name();
5295 cc_contents << " o." << super_name << "Verify(isolate);\n";
5296 }
5297
5298 // Second, verify that this object is what it claims to be.
5299 cc_contents << " CHECK(o.Is" << name << "(isolate));\n";
5300
5301 // Third, verify its properties.
5302 for (auto f : type->fields()) {
5303 GenerateClassFieldVerifier(name, *type, f, h_contents, cc_contents);
5304 }
5305
5306 cc_contents << "}\n";
5307 }
5308
5309 h_contents << "};\n";
5310 }
5311 WriteFile(output_directory + "/" + file_name + ".h", h_contents.str());
5312 WriteFile(output_directory + "/" + file_name + ".cc", cc_contents.str());
5313}
5314
5315void ImplementationVisitor::GenerateEnumVerifiers(
5316 const std::string& output_directory) {
5317 std::string file_name = "enum-verifiers";
5318 std::stringstream cc_contents;
5319 {
5320 cc_contents << "#include \"src/compiler/code-assembler.h\"\n";
5321 for (const std::string& include_path : GlobalContext::CppIncludes()) {
5322 cc_contents << "#include " << StringLiteralQuote(include_path) << "\n";
5323 }
5324 cc_contents << "\n";
5325
5326 NamespaceScope cc_namespaces(cc_contents, {"v8", "internal", ""});
5327
5328 cc_contents << "class EnumVerifier {\n";
5329 for (const auto& desc : GlobalContext::Get().ast()->EnumDescriptions()) {
5330 cc_contents << " // " << desc.name << " (" << desc.pos << ")\n";
5331 cc_contents << " void VerifyEnum_" << desc.name << "("
5332 << desc.constexpr_generates
5333 << " x) {\n"
5334 " switch(x) {\n";
5335 for (const auto& entry : desc.entries) {
5336 cc_contents << " case " << entry << ": break;\n";
5337 }
5338 if (desc.is_open) cc_contents << " default: break;\n";
5339 cc_contents << " }\n }\n\n";
5340 }
5341 cc_contents << "};\n";
5342 }
5343
5344 WriteFile(output_directory + "/" + file_name + ".cc", cc_contents.str());
5345}
5346
5347void ImplementationVisitor::GenerateExportedMacrosAssembler(
5348 const std::string& output_directory) {
5349 std::string file_name = "exported-macros-assembler";
5350 std::stringstream h_contents;
5351 std::stringstream cc_contents;
5352 {
5353 IncludeGuardScope include_guard(h_contents, file_name + ".h");
5354
5355 h_contents << "#include \"src/compiler/code-assembler.h\"\n";
5356 h_contents << "#include \"src/execution/frames.h\"\n";
5357 h_contents << "#include \"torque-generated/csa-types.h\"\n";
5358
5359 for (const std::string& include_path : GlobalContext::CppIncludes()) {
5360 cc_contents << "#include " << StringLiteralQuote(include_path) << "\n";
5361 }
5362 cc_contents << "#include \"torque-generated/" << file_name << ".h\"\n";
5363
5364 for (SourceId file : SourceFileMap::AllSources()) {
5365 cc_contents << "#include \"torque-generated/" +
5366 SourceFileMap::PathFromV8RootWithoutExtension(file) +
5367 "-tq-csa.h\"\n";
5368 }
5369
5370 NamespaceScope h_namespaces(h_contents, {"v8", "internal"});
5371 NamespaceScope cc_namespaces(cc_contents, {"v8", "internal"});
5372
5373 h_contents << "class V8_EXPORT_PRIVATE "
5374 "TorqueGeneratedExportedMacrosAssembler {\n"
5375 << " public:\n"
5376 << " explicit TorqueGeneratedExportedMacrosAssembler"
5377 "(compiler::CodeAssemblerState* state) : state_(state) {\n"
5378 << " USE(state_);\n"
5379 << " }\n";
5380
5381 for (auto& declarable : GlobalContext::AllDeclarables()) {
5382 TorqueMacro* macro = TorqueMacro::DynamicCast(declarable.get());
5383 if (!(macro && macro->IsExportedToCSA())) continue;
5384 CurrentSourcePosition::Scope position_activator(macro->Position());
5385
5386 cpp::Class assembler("TorqueGeneratedExportedMacrosAssembler");
5387 std::vector<std::string> generated_parameter_names;
5388 cpp::Function f = GenerateFunction(
5389 &assembler, macro->ReadableName(), macro->signature(),
5390 macro->parameter_names(), false, &generated_parameter_names);
5391
5392 f.PrintDeclaration(h_contents);
5393 f.PrintDefinition(cc_contents, [&](std::ostream& stream) {
5394 stream << "return " << macro->ExternalName() << "(state_";
5395 for (const auto& name : generated_parameter_names) {
5396 stream << ", " << name;
5397 }
5398 stream << ");";
5399 });
5400 }
5401
5402 h_contents << " private:\n"
5403 << " compiler::CodeAssemblerState* state_;\n"
5404 << "};\n";
5405 }
5406 WriteFile(output_directory + "/" + file_name + ".h", h_contents.str());
5407 WriteFile(output_directory + "/" + file_name + ".cc", cc_contents.str());
5408}
5409
5410namespace {
5411
5412void CollectAllFields(const std::string& path, const Field& field,
5413 std::vector<std::string>& result) {
5414 if (field.name_and_type.type->StructSupertype()) {
5415 std::string next_path = path + field.name_and_type.name + ".";
5416 const StructType* struct_type =
5417 StructType::DynamicCast(field.name_and_type.type);
5418 for (const auto& inner_field : struct_type->fields()) {
5419 CollectAllFields(next_path, inner_field, result);
5420 }
5421 } else {
5422 result.push_back(path + field.name_and_type.name);
5423 }
5424}
5425
5426} // namespace
5427
5428void ImplementationVisitor::GenerateCSATypes(
5429 const std::string& output_directory) {
5430 std::string file_name = "csa-types";
5431 std::stringstream h_contents;
5432 {
5433 IncludeGuardScope include_guard(h_contents, file_name + ".h");
5434 h_contents << "#include \"src/compiler/code-assembler.h\"\n\n";
5435
5436 NamespaceScope h_namespaces(h_contents, {"v8", "internal"});
5437
5438 // Generates headers for all structs in a topologically-sorted order, since
5439 // TypeOracle keeps them in the order of their resolution
5440 for (const auto& type : TypeOracle::GetAggregateTypes()) {
5441 const StructType* struct_type = StructType::DynamicCast(type.get());
5442 if (!struct_type) continue;
5443 h_contents << "struct " << struct_type->GetGeneratedTypeNameImpl()
5444 << " {\n";
5445 for (auto& field : struct_type->fields()) {
5446 h_contents << " " << field.name_and_type.type->GetGeneratedTypeName();
5447 h_contents << " " << field.name_and_type.name << ";\n";
5448 }
5449 h_contents << "\n std::tuple<";
5450 bool first = true;
5451 for (const Type* lowered_type : LowerType(struct_type)) {
5452 if (!first) {
5453 h_contents << ", ";
5454 }
5455 first = false;
5456 h_contents << lowered_type->GetGeneratedTypeName();
5457 }
5458 std::vector<std::string> all_fields;
5459 for (auto& field : struct_type->fields()) {
5460 CollectAllFields("", field, all_fields);
5461 }
5462 h_contents << "> Flatten() const {\n"
5463 " return std::make_tuple(";
5464 PrintCommaSeparatedList(h_contents, all_fields);
5465 h_contents << ");\n";
5466 h_contents << " }\n";
5467 h_contents << "};\n";
5468 }
5469 }
5470 WriteFile(output_directory + "/" + file_name + ".h", h_contents.str());
5471}
5472
5473void ReportAllUnusedMacros() {
5474 for (const auto& declarable : GlobalContext::AllDeclarables()) {
5475 if (!declarable->IsMacro() || declarable->IsExternMacro()) continue;
5476
5477 Macro* macro = Macro::cast(declarable.get());
5478 if (macro->IsUsed()) continue;
5479
5480 if (macro->IsTorqueMacro() && TorqueMacro::cast(macro)->IsExportedToCSA()) {
5481 continue;
5482 }
5483 // TODO(gsps): Mark methods of generic structs used if they are used in any
5484 // instantiation
5485 if (Method* method = Method::DynamicCast(macro)) {
5486 if (StructType* struct_type =
5487 StructType::DynamicCast(method->aggregate_type())) {
5488 if (struct_type->GetSpecializedFrom().has_value()) {
5489 continue;
5490 }
5491 }
5492 }
5493
5494 std::vector<std::string> ignored_prefixes = {"Convert<", "Cast<",
5495 "FromConstexpr<"};
5496 const std::string name = macro->ReadableName();
5497 const bool ignore =
5498 StartsWithSingleUnderscore(name) ||
5499 std::any_of(ignored_prefixes.begin(), ignored_prefixes.end(),
5500 [&name](const std::string& prefix) {
5501 return StringStartsWith(name, prefix);
5502 });
5503
5504 if (!ignore) {
5505 Lint("Macro '", macro->ReadableName(), "' is never used.")
5506 .Position(macro->IdentifierPosition());
5507 }
5508 }
5509}
5510
5511} // namespace torque
5512} // namespace internal
5513} // namespace v8