Line data Source code
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 <algorithm>
6 :
7 : #include "src/torque/csa-generator.h"
8 : #include "src/torque/declaration-visitor.h"
9 : #include "src/torque/implementation-visitor.h"
10 : #include "src/torque/parameter-difference.h"
11 :
12 : namespace v8 {
13 : namespace internal {
14 : namespace torque {
15 :
16 11766 : VisitResult ImplementationVisitor::Visit(Expression* expr) {
17 11766 : CurrentSourcePosition::Scope scope(expr->pos);
18 11766 : switch (expr->kind) {
19 : #define ENUM_ITEM(name) \
20 : case AstNode::Kind::k##name: \
21 : return Visit(name::cast(expr));
22 11612 : AST_EXPRESSION_NODE_KIND_LIST(ENUM_ITEM)
23 : #undef ENUM_ITEM
24 : default:
25 0 : UNREACHABLE();
26 : }
27 : }
28 :
29 5004 : const Type* ImplementationVisitor::Visit(Statement* stmt) {
30 5004 : CurrentSourcePosition::Scope scope(stmt->pos);
31 : StackScope stack_scope(this);
32 : const Type* result;
33 5004 : switch (stmt->kind) {
34 : #define ENUM_ITEM(name) \
35 : case AstNode::Kind::k##name: \
36 : result = Visit(name::cast(stmt)); \
37 : break;
38 5004 : AST_STATEMENT_NODE_KIND_LIST(ENUM_ITEM)
39 : #undef ENUM_ITEM
40 : default:
41 0 : UNREACHABLE();
42 : }
43 : DCHECK_EQ(result == TypeOracle::GetNeverType(),
44 : assembler().CurrentBlockIsComplete());
45 5004 : return result;
46 : }
47 :
48 23 : void ImplementationVisitor::BeginNamespaceFile(Namespace* nspace) {
49 23 : std::ostream& source = nspace->source_stream();
50 23 : std::ostream& header = nspace->header_stream();
51 :
52 391 : for (const std::string& include_path : GlobalContext::CppIncludes()) {
53 1035 : source << "#include " << StringLiteralQuote(include_path) << "\n";
54 : }
55 :
56 575 : for (Namespace* n : GlobalContext::Get().GetNamespaces()) {
57 1587 : source << "#include \"torque-generated/builtins-" +
58 529 : DashifyString(n->name()) + "-from-dsl-gen.h\"\n";
59 : }
60 23 : source << "\n";
61 :
62 23 : source << "namespace v8 {\n"
63 23 : << "namespace internal {\n"
64 23 : << "\n";
65 :
66 23 : std::string upper_name(nspace->name());
67 : transform(upper_name.begin(), upper_name.end(), upper_name.begin(),
68 23 : ::toupper);
69 : std::string headerDefine =
70 69 : std::string("V8_TORQUE_") + upper_name + "_FROM_DSL_BASE_H__";
71 46 : header << "#ifndef " << headerDefine << "\n";
72 46 : header << "#define " << headerDefine << "\n\n";
73 23 : header << "#include \"src/compiler/code-assembler.h\"\n";
74 23 : if (nspace != GlobalContext::GetDefaultNamespace()) {
75 22 : header << "#include \"src/code-stub-assembler.h\"\n";
76 : }
77 23 : header << "#include \"src/utils.h\"\n";
78 23 : header << "#include \"torque-generated/class-definitions-from-dsl.h\"\n";
79 23 : header << "\n";
80 :
81 23 : header << "namespace v8 {\n"
82 23 : << "namespace internal {\n"
83 23 : << "\n";
84 :
85 69 : header << "class " << nspace->ExternalName() << " {\n";
86 23 : header << " public:\n";
87 46 : header << " explicit " << nspace->ExternalName()
88 : << "(compiler::CodeAssemblerState* state) : state_(state), ca_(state) "
89 23 : "{ USE(state_, ca_); }\n";
90 23 : }
91 :
92 23 : void ImplementationVisitor::EndNamespaceFile(Namespace* nspace) {
93 23 : std::ostream& source = nspace->source_stream();
94 23 : std::ostream& header = nspace->header_stream();
95 :
96 23 : std::string upper_name(nspace->name());
97 : transform(upper_name.begin(), upper_name.end(), upper_name.begin(),
98 23 : ::toupper);
99 : std::string headerDefine =
100 69 : std::string("V8_TORQUE_") + upper_name + "_FROM_DSL_BASE_H__";
101 :
102 23 : source << "} // namespace internal\n"
103 23 : << "} // namespace v8\n"
104 23 : << "\n";
105 :
106 23 : header << " private:\n"
107 23 : << " compiler::CodeAssemblerState* const state_;\n"
108 23 : << " compiler::CodeAssembler ca_;"
109 23 : << "}; \n\n";
110 23 : header << "} // namespace internal\n"
111 23 : << "} // namespace v8\n"
112 23 : << "\n";
113 46 : header << "#endif // " << headerDefine << "\n";
114 23 : }
115 :
116 48 : void ImplementationVisitor::Visit(NamespaceConstant* decl) {
117 120 : Signature signature{{}, base::nullopt, {{}, false}, 0, decl->type(), {}};
118 24 : const std::string& name = decl->name();
119 :
120 : BindingsManagersScope bindings_managers_scope;
121 :
122 24 : header_out() << " ";
123 48 : GenerateFunctionDeclaration(header_out(), "", name, signature, {});
124 24 : header_out() << ";\n";
125 :
126 24 : GenerateFunctionDeclaration(source_out(),
127 72 : CurrentNamespace()->ExternalName() + "::", name,
128 48 : signature, {});
129 24 : source_out() << " {\n";
130 :
131 : DCHECK(!signature.return_type->IsVoidOrNever());
132 :
133 72 : assembler_ = CfgAssembler(Stack<const Type*>{});
134 :
135 24 : VisitResult expression_result = Visit(decl->body());
136 : VisitResult return_result =
137 48 : GenerateImplicitConvert(signature.return_type, expression_result);
138 :
139 24 : CSAGenerator csa_generator{assembler().Result(), source_out()};
140 72 : Stack<std::string> values = *csa_generator.EmitGraph(Stack<std::string>{});
141 :
142 : assembler_ = base::nullopt;
143 :
144 24 : source_out() << "return ";
145 48 : CSAGenerator::EmitCSAValue(return_result, values, source_out());
146 24 : source_out() << ";\n";
147 48 : source_out() << "}\n\n";
148 24 : }
149 :
150 5007 : void ImplementationVisitor::Visit(TypeAlias* alias) {
151 4833 : if (alias->IsRedeclaration()) return;
152 19 : const ClassType* class_type = ClassType::DynamicCast(alias->type());
153 193 : if (class_type && class_type->IsExtern()) {
154 : // Classes that are in the default namespace are defined in the C++
155 : // world and all of their fields and methods are declared explicitly.
156 : // Internal classes (e.g. ones used for testing that are not in the default
157 : // name space) need to be defined by Torque.
158 : // TODO(danno): This is a pretty cheesy hack for now. There should be a more
159 : // robust mechanism for this, e.g. declaring classes 'extern' or something.
160 33 : if (class_type->nspace()->IsTestNamespace()) {
161 : std::string class_name{
162 1 : class_type->GetSuperClass()->GetGeneratedTNodeTypeName()};
163 2 : header_out() << " class " << class_type->name() << " : public "
164 1 : << class_name << " {\n";
165 1 : header_out() << " public:\n";
166 1 : header_out() << " DEFINE_FIELD_OFFSET_CONSTANTS(" << class_name
167 1 : << "::kSize, "
168 2 : << CapifyStringWithUnderscores(class_type->name())
169 1 : << "_FIELDS)\n";
170 1 : header_out() << " };\n";
171 16 : } else if (!class_type->nspace()->IsDefaultNamespace()) {
172 : ReportError(
173 : "extern classes are currently only supported in the default and test "
174 0 : "namespaces");
175 : }
176 : return;
177 : }
178 : const StructType* struct_type = StructType::DynamicCast(alias->type());
179 157 : if (!struct_type) return;
180 : const std::string& name = struct_type->name();
181 64 : header_out() << " struct " << name << " {\n";
182 175 : for (auto& field : struct_type->fields()) {
183 222 : header_out() << " " << field.name_and_type.type->GetGeneratedTypeName();
184 222 : header_out() << " " << field.name_and_type.name << ";\n";
185 : }
186 32 : header_out() << "\n std::tuple<";
187 : bool first = true;
188 247 : for (const Type* type : LowerType(struct_type)) {
189 151 : if (!first) {
190 119 : header_out() << ", ";
191 : }
192 : first = false;
193 302 : header_out() << type->GetGeneratedTypeName();
194 : }
195 32 : header_out() << "> Flatten() const {\n"
196 32 : << " return std::tuple_cat(";
197 : first = true;
198 175 : for (auto& field : struct_type->fields()) {
199 111 : if (!first) {
200 79 : header_out() << ", ";
201 : }
202 : first = false;
203 222 : if (field.name_and_type.type->IsStructType()) {
204 40 : header_out() << field.name_and_type.name << ".Flatten()";
205 : } else {
206 182 : header_out() << "std::make_tuple(" << field.name_and_type.name << ")";
207 : }
208 : }
209 32 : header_out() << ");\n";
210 32 : header_out() << " }\n";
211 32 : header_out() << " };\n";
212 : }
213 :
214 755 : VisitResult ImplementationVisitor::InlineMacro(
215 2262 : Macro* macro, base::Optional<LocationReference> this_reference,
216 : const std::vector<VisitResult>& arguments,
217 153 : const std::vector<Block*> label_blocks) {
218 755 : CurrentScope::Scope current_scope(macro);
219 : BindingsManagersScope bindings_managers_scope;
220 : CurrentCallable::Scope current_callable(macro);
221 755 : CurrentReturnValue::Scope current_return_value;
222 1916 : const Signature& signature = macro->signature();
223 755 : const Type* return_type = macro->signature().return_type;
224 755 : bool can_return = return_type != TypeOracle::GetNeverType();
225 :
226 : CurrentConstructorInfo::Scope current_constructor;
227 1510 : if (macro->IsConstructor())
228 : CurrentConstructorInfo::Get() = ConstructorInfo{0};
229 :
230 : BlockBindings<LocalValue> parameter_bindings(&ValueBindingsManager::Get());
231 : BlockBindings<LocalLabel> label_bindings(&LabelBindingsManager::Get());
232 : DCHECK_EQ(macro->signature().parameter_names.size(),
233 : arguments.size() + (this_reference ? 1 : 0));
234 : DCHECK_EQ(this_reference.has_value(), macro->IsMethod());
235 :
236 : // Bind the this for methods. Methods that modify a struct-type "this" must
237 : // only be called if the this is in a variable, in which case the
238 : // LocalValue is non-const. Otherwise, the LocalValue used for the parameter
239 : // binding is const, and thus read-only, which will cause errors if
240 : // modified, e.g. when called by a struct method that sets the structs
241 : // fields. This prevents using temporary struct values for anything other
242 : // than read operations.
243 755 : if (this_reference) {
244 : DCHECK(macro->IsMethod());
245 227 : LocalValue this_value = LocalValue{!this_reference->IsVariableAccess(),
246 454 : this_reference->GetVisitResult()};
247 681 : parameter_bindings.Add(kThisParameterName, this_value);
248 : }
249 :
250 : size_t i = 0;
251 3017 : for (auto arg : arguments) {
252 1507 : if (this_reference && i == signature.implicit_count) i++;
253 1507 : const std::string& name = macro->parameter_names()[i++];
254 4521 : parameter_bindings.Add(name, LocalValue{true, arg});
255 : }
256 :
257 : DCHECK_EQ(label_blocks.size(), signature.labels.size());
258 1816 : for (size_t i = 0; i < signature.labels.size(); ++i) {
259 908 : const LabelDeclaration& label_info = signature.labels[i];
260 : label_bindings.Add(label_info.name,
261 918 : LocalLabel{label_blocks[i], label_info.types});
262 : }
263 :
264 : Block* macro_end;
265 755 : base::Optional<Binding<LocalLabel>> macro_end_binding;
266 755 : if (can_return) {
267 : Stack<const Type*> stack = assembler().CurrentStack();
268 731 : std::vector<const Type*> lowered_return_types = LowerType(return_type);
269 731 : stack.PushMany(lowered_return_types);
270 731 : if (!return_type->IsConstexpr()) {
271 : SetReturnValue(VisitResult(return_type,
272 1438 : stack.TopRange(lowered_return_types.size())));
273 : }
274 : // The stack copy used to initialize the _macro_end block is only used
275 : // as a template for the actual gotos generated by return statements. It
276 : // doesn't correspond to any real return values, and thus shouldn't contain
277 : // top types, because these would pollute actual return value types that get
278 : // unioned with them for return statements, erroneously forcing them to top.
279 13206 : for (auto i = stack.begin(); i != stack.end(); ++i) {
280 11744 : if ((*i)->IsTopType()) {
281 351 : *i = TopType::cast(*i)->source_type();
282 : }
283 : }
284 1462 : macro_end = assembler().NewBlock(std::move(stack));
285 : macro_end_binding.emplace(&LabelBindingsManager::Get(), "_macro_end",
286 2924 : LocalLabel{macro_end, {return_type}});
287 : } else {
288 48 : SetReturnValue(VisitResult::NeverResult());
289 : }
290 :
291 1510 : const Type* result = Visit(*macro->body());
292 :
293 755 : if (result->IsNever()) {
294 836 : if (!return_type->IsNever() && !macro->HasReturns()) {
295 0 : std::stringstream s;
296 0 : s << "macro " << macro->ReadableName()
297 0 : << " that never returns must have return type never";
298 0 : ReportError(s.str());
299 : }
300 : } else {
301 325 : if (return_type->IsNever()) {
302 0 : std::stringstream s;
303 0 : s << "macro " << macro->ReadableName()
304 : << " has implicit return at end of its declartion but return type "
305 0 : "never";
306 0 : ReportError(s.str());
307 325 : } else if (!macro->signature().return_type->IsVoid()) {
308 0 : std::stringstream s;
309 0 : s << "macro " << macro->ReadableName()
310 0 : << " expects to return a value but doesn't on all paths";
311 0 : ReportError(s.str());
312 : }
313 : }
314 755 : if (!result->IsNever()) {
315 325 : assembler().Goto(macro_end);
316 : }
317 :
318 1510 : if (macro->HasReturns() || !result->IsNever()) {
319 731 : assembler().Bind(macro_end);
320 : }
321 :
322 755 : return GetAndClearReturnValue();
323 : }
324 :
325 568 : void ImplementationVisitor::VisitMacroCommon(Macro* macro) {
326 : CurrentCallable::Scope current_callable(macro);
327 : const Signature& signature = macro->signature();
328 568 : const Type* return_type = macro->signature().return_type;
329 568 : bool can_return = return_type != TypeOracle::GetNeverType();
330 : bool has_return_value =
331 568 : can_return && return_type != TypeOracle::GetVoidType();
332 :
333 568 : header_out() << " ";
334 1136 : GenerateMacroFunctionDeclaration(header_out(), "", macro);
335 568 : header_out() << ";\n";
336 :
337 : GenerateMacroFunctionDeclaration(
338 2272 : source_out(), CurrentNamespace()->ExternalName() + "::", macro);
339 568 : source_out() << " {\n";
340 :
341 : Stack<std::string> lowered_parameters;
342 : Stack<const Type*> lowered_parameter_types;
343 :
344 568 : std::vector<VisitResult> arguments;
345 :
346 568 : base::Optional<LocationReference> this_reference;
347 568 : if (Method* method = Method::DynamicCast(macro)) {
348 : const Type* this_type = method->aggregate_type();
349 : LowerParameter(this_type, ExternalParameterName(kThisParameterName),
350 132 : &lowered_parameters);
351 88 : StackRange range = lowered_parameter_types.PushMany(LowerType(this_type));
352 : VisitResult this_result = VisitResult(this_type, range);
353 : // For classes, mark 'this' as a temporary to prevent assignment to it.
354 : // Note that using a VariableAccess for non-class types is technically
355 : // incorrect because changes to the 'this' variable do not get reflected
356 : // to the caller. Therefore struct methods should always be inlined and a
357 : // C++ version should never be generated, since it would be incorrect.
358 : // However, in order to be able to type- and semantics-check even unused
359 : // struct methods, set the this_reference to be the local variable copy of
360 : // the passed-in this, which allows the visitor to at least find and report
361 : // errors.
362 180 : this_reference =
363 : (this_type->IsClassType())
364 : ? LocationReference::Temporary(this_result, "this parameter")
365 40 : : LocationReference::VariableAccess(this_result);
366 : }
367 :
368 3104 : for (size_t i = 0; i < macro->signature().parameter_names.size(); ++i) {
369 1312 : if (this_reference && i == macro->signature().implicit_count) continue;
370 : const std::string& name = macro->parameter_names()[i];
371 1224 : std::string external_name = ExternalParameterName(name);
372 2448 : const Type* type = macro->signature().types()[i];
373 :
374 1224 : if (type->IsConstexpr()) {
375 65 : arguments.push_back(VisitResult(type, external_name));
376 : } else {
377 1159 : LowerParameter(type, external_name, &lowered_parameters);
378 2318 : StackRange range = lowered_parameter_types.PushMany(LowerType(type));
379 1159 : arguments.push_back(VisitResult(type, range));
380 : }
381 : }
382 :
383 : DCHECK_EQ(lowered_parameters.Size(), lowered_parameter_types.Size());
384 1704 : assembler_ = CfgAssembler(lowered_parameter_types);
385 :
386 : std::vector<Block*> label_blocks;
387 1270 : for (const LabelDeclaration& label_info : signature.labels) {
388 : Stack<const Type*> label_input_stack;
389 302 : for (const Type* type : label_info.types) {
390 68 : label_input_stack.PushMany(LowerType(type));
391 : }
392 268 : Block* block = assembler().NewBlock(std::move(label_input_stack));
393 134 : label_blocks.push_back(block);
394 : }
395 :
396 : VisitResult return_value =
397 2272 : InlineMacro(macro, this_reference, arguments, label_blocks);
398 1136 : Block* end = assembler().NewBlock();
399 568 : if (return_type != TypeOracle::GetNeverType()) {
400 545 : assembler().Goto(end);
401 : }
402 :
403 836 : for (size_t i = 0; i < label_blocks.size(); ++i) {
404 134 : Block* label_block = label_blocks[i];
405 134 : const LabelDeclaration& label_info = signature.labels[i];
406 134 : assembler().Bind(label_block);
407 : std::vector<std::string> label_parameter_variables;
408 336 : for (size_t i = 0; i < label_info.types.size(); ++i) {
409 202 : LowerLabelParameter(label_info.types[i],
410 : ExternalLabelParameterName(label_info.name, i),
411 102 : &label_parameter_variables);
412 : }
413 : assembler().Emit(GotoExternalInstruction{ExternalLabelName(label_info.name),
414 536 : label_parameter_variables});
415 134 : }
416 :
417 568 : if (return_type != TypeOracle::GetNeverType()) {
418 545 : assembler().Bind(end);
419 : }
420 :
421 568 : CSAGenerator csa_generator{assembler().Result(), source_out()};
422 : base::Optional<Stack<std::string>> values =
423 1136 : csa_generator.EmitGraph(lowered_parameters);
424 :
425 : assembler_ = base::nullopt;
426 :
427 568 : if (has_return_value) {
428 358 : source_out() << " return ";
429 716 : CSAGenerator::EmitCSAValue(return_value, *values, source_out());
430 358 : source_out() << ";\n";
431 : }
432 568 : source_out() << "}\n\n";
433 568 : }
434 :
435 0 : void ImplementationVisitor::Visit(Macro* macro) {
436 917 : if (macro->IsExternal()) return;
437 524 : VisitMacroCommon(macro);
438 : }
439 :
440 0 : void ImplementationVisitor::Visit(Method* method) {
441 : DCHECK(!method->IsExternal());
442 44 : VisitMacroCommon(method);
443 0 : }
444 :
445 : namespace {
446 :
447 460 : std::string AddParameter(size_t i, Builtin* builtin,
448 : Stack<std::string>* parameters,
449 : Stack<const Type*>* parameter_types,
450 : BlockBindings<LocalValue>* parameter_bindings) {
451 460 : const std::string& name = builtin->signature().parameter_names[i];
452 920 : const Type* type = builtin->signature().types()[i];
453 460 : std::string external_name = "parameter" + std::to_string(i);
454 920 : parameters->Push(external_name);
455 920 : StackRange range = parameter_types->PushMany(LowerType(type));
456 1840 : parameter_bindings->Add(name, LocalValue{true, VisitResult(type, range)});
457 460 : return external_name;
458 : }
459 :
460 : } // namespace
461 :
462 150 : void ImplementationVisitor::Visit(Builtin* builtin) {
463 582 : if (builtin->IsExternal()) return;
464 132 : CurrentScope::Scope current_scope(builtin);
465 132 : const std::string& name = builtin->ExternalName();
466 : const Signature& signature = builtin->signature();
467 264 : source_out() << "TF_BUILTIN(" << name << ", CodeStubAssembler) {\n"
468 132 : << " compiler::CodeAssemblerState* state_ = state();"
469 132 : << " compiler::CodeAssembler ca_(state());\n";
470 :
471 : CurrentCallable::Scope current_callable(builtin);
472 132 : CurrentReturnValue::Scope current_return_value;
473 :
474 : Stack<const Type*> parameter_types;
475 : Stack<std::string> parameters;
476 :
477 : BindingsManagersScope bindings_managers_scope;
478 :
479 : BlockBindings<LocalValue> parameter_bindings(&ValueBindingsManager::Get());
480 :
481 : // Context
482 : std::string parameter0 = AddParameter(0, builtin, ¶meters,
483 132 : ¶meter_types, ¶meter_bindings);
484 132 : source_out() << " TNode<Context> " << parameter0
485 132 : << " = UncheckedCast<Context>(Parameter("
486 132 : << "Descriptor::kContext));\n";
487 264 : source_out() << " USE(" << parameter0 << ");\n";
488 :
489 : size_t first = 1;
490 264 : if (builtin->IsVarArgsJavaScript()) {
491 : DCHECK(signature.parameter_types.var_args);
492 48 : source_out()
493 48 : << " Node* argc = Parameter(Descriptor::kJSActualArgumentsCount);\n";
494 48 : source_out() << " CodeStubArguments arguments_impl(this, "
495 48 : "ChangeInt32ToIntPtr(argc));\n";
496 : std::string parameter1 = AddParameter(
497 48 : 1, builtin, ¶meters, ¶meter_types, ¶meter_bindings);
498 :
499 48 : source_out() << " TNode<Object> " << parameter1
500 48 : << " = arguments_impl.GetReceiver();\n";
501 48 : source_out() << "auto " << CSAGenerator::ARGUMENTS_VARIABLE_STRING
502 48 : << " = &arguments_impl;\n";
503 48 : source_out() << "USE(arguments);\n";
504 96 : source_out() << "USE(" << parameter1 << ");\n";
505 : parameter_bindings.Add(
506 48 : *signature.arguments_variable,
507 : LocalValue{true,
508 240 : VisitResult(TypeOracle::GetArgumentsType(), "arguments")});
509 : first = 2;
510 : }
511 :
512 1184 : for (size_t i = 0; i < signature.parameter_names.size(); ++i) {
513 640 : if (i < first) continue;
514 592 : const std::string& parameter_name = signature.parameter_names[i];
515 560 : const Type* type = signature.types()[i];
516 : std::string var = AddParameter(i, builtin, ¶meters, ¶meter_types,
517 280 : ¶meter_bindings);
518 1120 : source_out() << " " << type->GetGeneratedTypeName() << " " << var << " = "
519 840 : << "UncheckedCast<" << type->GetGeneratedTNodeTypeName()
520 280 : << ">(Parameter(Descriptor::k"
521 840 : << CamelifyString(parameter_name) << "));\n";
522 560 : source_out() << " USE(" << var << ");\n";
523 : }
524 :
525 396 : assembler_ = CfgAssembler(parameter_types);
526 264 : const Type* body_result = Visit(*builtin->body());
527 132 : if (body_result != TypeOracle::GetNeverType()) {
528 0 : ReportError("control reaches end of builtin, expected return of a value");
529 : }
530 132 : CSAGenerator csa_generator{assembler().Result(), source_out(),
531 132 : builtin->kind()};
532 396 : csa_generator.EmitGraph(parameters);
533 : assembler_ = base::nullopt;
534 132 : source_out() << "}\n\n";
535 : }
536 :
537 0 : const Type* ImplementationVisitor::Visit(VarDeclarationStatement* stmt) {
538 : BlockBindings<LocalValue> block_bindings(&ValueBindingsManager::Get());
539 0 : return Visit(stmt, &block_bindings);
540 : }
541 :
542 1118 : const Type* ImplementationVisitor::Visit(
543 : VarDeclarationStatement* stmt, BlockBindings<LocalValue>* block_bindings) {
544 : // const qualified variables are required to be initialized properly.
545 2236 : if (stmt->const_qualified && !stmt->initializer) {
546 0 : ReportError("local constant \"", stmt->name, "\" is not initialized.");
547 : }
548 :
549 : base::Optional<const Type*> type;
550 1118 : if (stmt->type) {
551 936 : type = Declarations::GetType(*stmt->type);
552 936 : if ((*type)->IsConstexpr() && !stmt->const_qualified) {
553 : ReportError(
554 0 : "cannot declare variable with constexpr type. Use 'const' instead.");
555 : }
556 : }
557 1118 : base::Optional<VisitResult> init_result;
558 1118 : if (stmt->initializer) {
559 : StackScope scope(this);
560 2180 : init_result = Visit(*stmt->initializer);
561 1090 : if (type) {
562 2724 : init_result = GenerateImplicitConvert(*type, *init_result);
563 : }
564 3270 : init_result = scope.Yield(*init_result);
565 : } else {
566 : DCHECK(type.has_value());
567 28 : if ((*type)->IsConstexpr()) {
568 0 : ReportError("constexpr variables need an initializer");
569 : }
570 28 : TypeVector lowered_types = LowerType(*type);
571 87 : for (const Type* type : lowered_types) {
572 31 : assembler().Emit(PushUninitializedInstruction{TypeOracle::GetTopType(
573 186 : "unitialized variable '" + stmt->name + "' of type " +
574 155 : type->ToString() + " originally defined at " +
575 : PositionAsString(stmt->pos),
576 155 : type)});
577 : }
578 56 : init_result =
579 : VisitResult(*type, assembler().TopRange(lowered_types.size()));
580 : }
581 : block_bindings->Add(stmt->name,
582 3354 : LocalValue{stmt->const_qualified, *init_result});
583 2236 : return TypeOracle::GetVoidType();
584 : }
585 :
586 1 : const Type* ImplementationVisitor::Visit(TailCallStatement* stmt) {
587 2 : return Visit(stmt->call, true).type();
588 : }
589 :
590 192 : VisitResult ImplementationVisitor::Visit(ConditionalExpression* expr) {
591 192 : Block* true_block = assembler().NewBlock(assembler().CurrentStack());
592 192 : Block* false_block = assembler().NewBlock(assembler().CurrentStack());
593 192 : Block* done_block = assembler().NewBlock();
594 192 : Block* true_conversion_block = assembler().NewBlock();
595 96 : GenerateExpressionBranch(expr->condition, true_block, false_block);
596 :
597 96 : VisitResult left;
598 96 : VisitResult right;
599 :
600 : {
601 : // The code for both paths of the conditional need to be generated first
602 : // before evaluating the conditional expression because the common type of
603 : // the result of both the true and false of the condition needs to be known
604 : // to convert both branches to a common type.
605 96 : assembler().Bind(true_block);
606 : StackScope left_scope(this);
607 192 : left = Visit(expr->if_true);
608 96 : assembler().Goto(true_conversion_block);
609 :
610 : const Type* common_type;
611 : {
612 96 : assembler().Bind(false_block);
613 : StackScope right_scope(this);
614 192 : right = Visit(expr->if_false);
615 192 : common_type = GetCommonType(left.type(), right.type());
616 384 : right = right_scope.Yield(GenerateImplicitConvert(common_type, right));
617 96 : assembler().Goto(done_block);
618 : }
619 :
620 96 : assembler().Bind(true_conversion_block);
621 384 : left = left_scope.Yield(GenerateImplicitConvert(common_type, left));
622 96 : assembler().Goto(done_block);
623 : }
624 :
625 96 : assembler().Bind(done_block);
626 96 : CHECK_EQ(left, right);
627 96 : return left;
628 : }
629 :
630 29 : VisitResult ImplementationVisitor::Visit(LogicalOrExpression* expr) {
631 29 : VisitResult left_result;
632 : {
633 58 : Block* false_block = assembler().NewBlock(assembler().CurrentStack());
634 : Binding<LocalLabel> false_binding{&LabelBindingsManager::Get(),
635 116 : kFalseLabelName, LocalLabel{false_block}};
636 58 : left_result = Visit(expr->left);
637 29 : if (left_result.type()->IsBool()) {
638 30 : Block* true_block = LookupSimpleLabel(kTrueLabelName);
639 15 : assembler().Branch(true_block, false_block);
640 15 : assembler().Bind(false_block);
641 14 : } else if (left_result.type()->IsNever()) {
642 7 : assembler().Bind(false_block);
643 7 : } else if (!left_result.type()->IsConstexprBool()) {
644 : ReportError(
645 : "expected type bool, constexpr bool, or never on left-hand side of "
646 0 : "operator ||");
647 29 : }
648 : }
649 :
650 29 : if (left_result.type()->IsConstexprBool()) {
651 7 : VisitResult right_result = Visit(expr->right);
652 7 : if (!right_result.type()->IsConstexprBool()) {
653 : ReportError(
654 : "expected type constexpr bool on right-hand side of operator "
655 0 : "||");
656 : }
657 : return VisitResult(TypeOracle::GetConstexprBoolType(),
658 28 : std::string("(") + left_result.constexpr_value() +
659 21 : " || " + right_result.constexpr_value() + ")");
660 : }
661 :
662 22 : VisitResult right_result = Visit(expr->right);
663 22 : if (right_result.type()->IsBool()) {
664 34 : Block* true_block = LookupSimpleLabel(kTrueLabelName);
665 34 : Block* false_block = LookupSimpleLabel(kFalseLabelName);
666 17 : assembler().Branch(true_block, false_block);
667 17 : return VisitResult::NeverResult();
668 5 : } else if (!right_result.type()->IsNever()) {
669 : ReportError(
670 0 : "expected type bool or never on right-hand side of operator ||");
671 : }
672 5 : return right_result;
673 : }
674 :
675 28 : VisitResult ImplementationVisitor::Visit(LogicalAndExpression* expr) {
676 28 : VisitResult left_result;
677 : {
678 56 : Block* true_block = assembler().NewBlock(assembler().CurrentStack());
679 : Binding<LocalLabel> false_binding{&LabelBindingsManager::Get(),
680 112 : kTrueLabelName, LocalLabel{true_block}};
681 56 : left_result = Visit(expr->left);
682 28 : if (left_result.type()->IsBool()) {
683 40 : Block* false_block = LookupSimpleLabel(kFalseLabelName);
684 20 : assembler().Branch(true_block, false_block);
685 20 : assembler().Bind(true_block);
686 8 : } else if (left_result.type()->IsNever()) {
687 8 : assembler().Bind(true_block);
688 0 : } else if (!left_result.type()->IsConstexprBool()) {
689 : ReportError(
690 : "expected type bool, constexpr bool, or never on left-hand side of "
691 0 : "operator &&");
692 28 : }
693 : }
694 :
695 28 : if (left_result.type()->IsConstexprBool()) {
696 0 : VisitResult right_result = Visit(expr->right);
697 0 : if (!right_result.type()->IsConstexprBool()) {
698 : ReportError(
699 : "expected type constexpr bool on right-hand side of operator "
700 0 : "&&");
701 : }
702 : return VisitResult(TypeOracle::GetConstexprBoolType(),
703 0 : std::string("(") + left_result.constexpr_value() +
704 0 : " && " + right_result.constexpr_value() + ")");
705 : }
706 :
707 28 : VisitResult right_result = Visit(expr->right);
708 28 : if (right_result.type()->IsBool()) {
709 46 : Block* true_block = LookupSimpleLabel(kTrueLabelName);
710 46 : Block* false_block = LookupSimpleLabel(kFalseLabelName);
711 23 : assembler().Branch(true_block, false_block);
712 23 : return VisitResult::NeverResult();
713 5 : } else if (!right_result.type()->IsNever()) {
714 : ReportError(
715 0 : "expected type bool or never on right-hand side of operator &&");
716 : }
717 5 : return right_result;
718 : }
719 :
720 142 : VisitResult ImplementationVisitor::Visit(IncrementDecrementExpression* expr) {
721 : StackScope scope(this);
722 284 : LocationReference location_ref = GetLocationReference(expr->location);
723 142 : VisitResult current_value = GenerateFetchFromLocation(location_ref);
724 284 : VisitResult one = {TypeOracle::GetConstInt31Type(), "1"};
725 142 : Arguments args;
726 426 : args.parameters = {current_value, one};
727 : VisitResult assignment_value = GenerateCall(
728 426 : expr->op == IncrementDecrementOperator::kIncrement ? "+" : "-", args);
729 142 : GenerateAssignToLocation(location_ref, assignment_value);
730 426 : return scope.Yield(expr->postfix ? current_value : assignment_value);
731 : }
732 :
733 792 : VisitResult ImplementationVisitor::Visit(AssignmentExpression* expr) {
734 : StackScope scope(this);
735 1584 : LocationReference location_ref = GetLocationReference(expr->location);
736 792 : VisitResult assignment_value;
737 792 : if (expr->op) {
738 11 : VisitResult location_value = GenerateFetchFromLocation(location_ref);
739 22 : assignment_value = Visit(expr->value);
740 11 : Arguments args;
741 33 : args.parameters = {location_value, assignment_value};
742 44 : assignment_value = GenerateCall(*expr->op, args);
743 11 : GenerateAssignToLocation(location_ref, assignment_value);
744 : } else {
745 1562 : assignment_value = Visit(expr->value);
746 781 : GenerateAssignToLocation(location_ref, assignment_value);
747 : }
748 2376 : return scope.Yield(assignment_value);
749 : }
750 :
751 829 : VisitResult ImplementationVisitor::Visit(NumberLiteralExpression* expr) {
752 : // TODO(tebbi): Do not silently loose precision; support 64bit literals.
753 1658 : double d = std::stod(expr->number.c_str());
754 829 : int32_t i = static_cast<int32_t>(d);
755 1658 : const Type* result_type = Declarations::LookupType(CONST_FLOAT64_TYPE_STRING);
756 829 : if (i == d) {
757 824 : if ((i >> 30) == (i >> 31)) {
758 1644 : result_type = Declarations::LookupType(CONST_INT31_TYPE_STRING);
759 : } else {
760 4 : result_type = Declarations::LookupType(CONST_INT32_TYPE_STRING);
761 : }
762 : }
763 1658 : return VisitResult{result_type, expr->number};
764 : }
765 :
766 96 : VisitResult ImplementationVisitor::Visit(AssumeTypeImpossibleExpression* expr) {
767 48 : VisitResult result = Visit(expr->expression);
768 : const Type* result_type =
769 96 : SubtractType(result.type(), Declarations::GetType(expr->excluded_type));
770 48 : if (result_type->IsNever()) {
771 0 : ReportError("unreachable code");
772 : }
773 192 : CHECK_EQ(LowerType(result_type), TypeVector{result_type});
774 96 : assembler().Emit(UnsafeCastInstruction{result_type});
775 : result.SetType(result_type);
776 48 : return result;
777 : }
778 :
779 46 : VisitResult ImplementationVisitor::Visit(StringLiteralExpression* expr) {
780 : return VisitResult{
781 : TypeOracle::GetConstStringType(),
782 230 : "\"" + expr->literal.substr(1, expr->literal.size() - 2) + "\""};
783 : }
784 :
785 190 : VisitResult ImplementationVisitor::GetBuiltinCode(Builtin* builtin) {
786 190 : if (builtin->IsExternal() || builtin->kind() != Builtin::kStub) {
787 : ReportError(
788 : "creating function pointers is only allowed for internal builtins with "
789 0 : "stub linkage");
790 : }
791 : const Type* type = TypeOracle::GetBuiltinPointerType(
792 : builtin->signature().parameter_types.types,
793 190 : builtin->signature().return_type);
794 95 : assembler().Emit(
795 570 : PushBuiltinPointerInstruction{builtin->ExternalName(), type});
796 95 : return VisitResult(type, assembler().TopRange(1));
797 : }
798 :
799 5471 : VisitResult ImplementationVisitor::Visit(IdentifierExpression* expr) {
800 : StackScope scope(this);
801 16413 : return scope.Yield(GenerateFetchFromLocation(GetLocationReference(expr)));
802 : }
803 :
804 217 : const Type* ImplementationVisitor::Visit(GotoStatement* stmt) {
805 217 : LocalLabel* label = LookupLabel(stmt->label);
806 536 : size_t parameter_count = label->parameter_types.size();
807 434 : if (stmt->arguments.size() != parameter_count) {
808 : ReportError("goto to label has incorrect number of parameters (expected ",
809 0 : parameter_count, " found ", stmt->arguments.size(), ")");
810 : }
811 :
812 : size_t i = 0;
813 : StackRange arguments = assembler().TopRange(0);
814 536 : for (Expression* e : stmt->arguments) {
815 : StackScope scope(this);
816 102 : VisitResult result = Visit(e);
817 204 : const Type* parameter_type = label->parameter_types[i++];
818 306 : result = GenerateImplicitConvert(parameter_type, result);
819 306 : arguments.Extend(scope.Yield(result).stack_range());
820 : }
821 :
822 217 : assembler().Goto(label->block, arguments.Size());
823 217 : return TypeOracle::GetNeverType();
824 : }
825 :
826 510 : const Type* ImplementationVisitor::Visit(IfStatement* stmt) {
827 966 : bool has_else = stmt->if_false.has_value();
828 :
829 510 : if (stmt->is_constexpr) {
830 54 : VisitResult expression_result = Visit(stmt->condition);
831 :
832 54 : if (!(expression_result.type() == TypeOracle::GetConstexprBoolType())) {
833 0 : std::stringstream stream;
834 0 : stream << "expression should return type constexpr bool "
835 0 : << "but returns type " << *expression_result.type();
836 0 : ReportError(stream.str());
837 : }
838 :
839 108 : Block* true_block = assembler().NewBlock();
840 108 : Block* false_block = assembler().NewBlock();
841 108 : Block* done_block = assembler().NewBlock();
842 :
843 : assembler().Emit(ConstexprBranchInstruction{
844 270 : expression_result.constexpr_value(), true_block, false_block});
845 :
846 54 : assembler().Bind(true_block);
847 54 : const Type* left_result = Visit(stmt->if_true);
848 54 : if (left_result == TypeOracle::GetVoidType()) {
849 12 : assembler().Goto(done_block);
850 : }
851 :
852 54 : assembler().Bind(false_block);
853 54 : const Type* right_result = TypeOracle::GetVoidType();
854 54 : if (has_else) {
855 51 : right_result = Visit(*stmt->if_false);
856 : }
857 54 : if (right_result == TypeOracle::GetVoidType()) {
858 12 : assembler().Goto(done_block);
859 : }
860 :
861 54 : if (left_result->IsNever() != right_result->IsNever()) {
862 0 : std::stringstream stream;
863 : stream << "either both or neither branches in a constexpr if statement "
864 0 : "must reach their end at"
865 0 : << PositionAsString(stmt->pos);
866 0 : ReportError(stream.str());
867 : }
868 :
869 54 : if (left_result != TypeOracle::GetNeverType()) {
870 12 : assembler().Bind(done_block);
871 : }
872 : return left_result;
873 : } else {
874 456 : Block* true_block = assembler().NewBlock(assembler().CurrentStack(),
875 1368 : IsDeferred(stmt->if_true));
876 : Block* false_block =
877 : assembler().NewBlock(assembler().CurrentStack(),
878 1368 : stmt->if_false && IsDeferred(*stmt->if_false));
879 456 : GenerateExpressionBranch(stmt->condition, true_block, false_block);
880 :
881 : Block* done_block;
882 : bool live = false;
883 456 : if (has_else) {
884 228 : done_block = assembler().NewBlock();
885 : } else {
886 : done_block = false_block;
887 : live = true;
888 : }
889 :
890 456 : assembler().Bind(true_block);
891 : {
892 456 : const Type* result = Visit(stmt->if_true);
893 456 : if (result == TypeOracle::GetVoidType()) {
894 : live = true;
895 184 : assembler().Goto(done_block);
896 : }
897 : }
898 :
899 456 : if (has_else) {
900 114 : assembler().Bind(false_block);
901 114 : const Type* result = Visit(*stmt->if_false);
902 114 : if (result == TypeOracle::GetVoidType()) {
903 : live = true;
904 96 : assembler().Goto(done_block);
905 : }
906 : }
907 :
908 456 : if (live) {
909 448 : assembler().Bind(done_block);
910 : }
911 456 : return live ? TypeOracle::GetVoidType() : TypeOracle::GetNeverType();
912 : }
913 : }
914 :
915 40 : const Type* ImplementationVisitor::Visit(WhileStatement* stmt) {
916 80 : Block* body_block = assembler().NewBlock(assembler().CurrentStack());
917 80 : Block* exit_block = assembler().NewBlock(assembler().CurrentStack());
918 :
919 80 : Block* header_block = assembler().NewBlock();
920 40 : assembler().Goto(header_block);
921 :
922 40 : assembler().Bind(header_block);
923 40 : GenerateExpressionBranch(stmt->condition, body_block, exit_block);
924 :
925 40 : assembler().Bind(body_block);
926 : {
927 40 : BreakContinueActivator activator{exit_block, header_block};
928 40 : const Type* body_result = Visit(stmt->body);
929 40 : if (body_result != TypeOracle::GetNeverType()) {
930 40 : assembler().Goto(header_block);
931 : }
932 : }
933 :
934 40 : assembler().Bind(exit_block);
935 40 : return TypeOracle::GetVoidType();
936 : }
937 :
938 1652 : const Type* ImplementationVisitor::Visit(BlockStatement* block) {
939 : BlockBindings<LocalValue> block_bindings(&ValueBindingsManager::Get());
940 1652 : const Type* type = TypeOracle::GetVoidType();
941 7282 : for (Statement* s : block->statements) {
942 3978 : CurrentSourcePosition::Scope source_position(s->pos);
943 3978 : if (type->IsNever()) {
944 0 : ReportError("statement after non-returning statement");
945 : }
946 3978 : if (auto* var_declaration = VarDeclarationStatement::DynamicCast(s)) {
947 1090 : type = Visit(var_declaration, &block_bindings);
948 : } else {
949 2888 : type = Visit(s);
950 : }
951 : }
952 1652 : return type;
953 : }
954 :
955 77 : const Type* ImplementationVisitor::Visit(DebugStatement* stmt) {
956 : #if defined(DEBUG)
957 : assembler().Emit(PrintConstantStringInstruction{"halting because of '" +
958 : stmt->reason + "' at " +
959 : PositionAsString(stmt->pos)});
960 : #endif
961 77 : assembler().Emit(AbortInstruction{stmt->never_continues
962 : ? AbortInstruction::Kind::kUnreachable
963 462 : : AbortInstruction::Kind::kDebugBreak});
964 77 : if (stmt->never_continues) {
965 77 : return TypeOracle::GetNeverType();
966 : } else {
967 0 : return TypeOracle::GetVoidType();
968 : }
969 : }
970 :
971 : namespace {
972 :
973 109 : std::string FormatAssertSource(const std::string& str) {
974 : // Replace all whitespace characters with a space character.
975 109 : std::string str_no_newlines = str;
976 : std::replace_if(str_no_newlines.begin(), str_no_newlines.end(),
977 3445 : [](unsigned char c) { return isspace(c); }, ' ');
978 :
979 : // str might include indentation, squash multiple space characters into one.
980 : std::string result;
981 : std::unique_copy(str_no_newlines.begin(), str_no_newlines.end(),
982 : std::back_inserter(result),
983 3227 : [](char a, char b) { return a == ' ' && b == ' '; });
984 109 : return result;
985 : }
986 :
987 : } // namespace
988 :
989 259 : const Type* ImplementationVisitor::Visit(AssertStatement* stmt) {
990 259 : bool do_check = !stmt->debug_only;
991 : #if defined(DEBUG)
992 : do_check = true;
993 : #endif
994 259 : if (do_check) {
995 : // CSA_ASSERT & co. are not used here on purpose for two reasons. First,
996 : // Torque allows and handles two types of expressions in the if protocol
997 : // automagically, ones that return TNode<BoolT> and those that use the
998 : // BranchIf(..., Label* true, Label* false) idiom. Because the machinery to
999 : // handle this is embedded in the expression handling and to it's not
1000 : // possible to make the decision to use CSA_ASSERT or CSA_ASSERT_BRANCH
1001 : // isn't trivial up-front. Secondly, on failure, the assert text should be
1002 : // the corresponding Torque code, not the -gen.cc code, which would be the
1003 : // case when using CSA_ASSERT_XXX.
1004 218 : Block* true_block = assembler().NewBlock(assembler().CurrentStack());
1005 218 : Block* false_block = assembler().NewBlock(assembler().CurrentStack(), true);
1006 109 : GenerateExpressionBranch(stmt->expression, true_block, false_block);
1007 :
1008 109 : assembler().Bind(false_block);
1009 :
1010 : assembler().Emit(AbortInstruction{
1011 : AbortInstruction::Kind::kAssertionFailure,
1012 654 : "Torque assert '" + FormatAssertSource(stmt->source) + "' failed"});
1013 :
1014 109 : assembler().Bind(true_block);
1015 : }
1016 259 : return TypeOracle::GetVoidType();
1017 : }
1018 :
1019 1472 : const Type* ImplementationVisitor::Visit(ExpressionStatement* stmt) {
1020 2944 : const Type* type = Visit(stmt->expression).type();
1021 1472 : return type->IsNever() ? type : TypeOracle::GetVoidType();
1022 : }
1023 :
1024 691 : const Type* ImplementationVisitor::Visit(ReturnStatement* stmt) {
1025 1382 : Callable* current_callable = CurrentCallable::Get();
1026 691 : if (current_callable->signature().return_type->IsNever()) {
1027 0 : std::stringstream s;
1028 0 : s << "cannot return from a function with return type never";
1029 0 : ReportError(s.str());
1030 : }
1031 : LocalLabel* end =
1032 1199 : current_callable->IsMacro() ? LookupLabel("_macro_end") : nullptr;
1033 691 : if (current_callable->HasReturnValue()) {
1034 691 : if (!stmt->value) {
1035 0 : std::stringstream s;
1036 0 : s << "return expression needs to be specified for a return type of "
1037 0 : << *current_callable->signature().return_type;
1038 0 : ReportError(s.str());
1039 : }
1040 680 : VisitResult expression_result = Visit(*stmt->value);
1041 : VisitResult return_result = GenerateImplicitConvert(
1042 1360 : current_callable->signature().return_type, expression_result);
1043 680 : if (current_callable->IsMacro()) {
1044 497 : if (return_result.IsOnStack()) {
1045 : StackRange return_value_range =
1046 : GenerateLabelGoto(end, return_result.stack_range());
1047 970 : SetReturnValue(VisitResult(return_result.type(), return_value_range));
1048 : } else {
1049 : GenerateLabelGoto(end);
1050 24 : SetReturnValue(return_result);
1051 : }
1052 183 : } else if (current_callable->IsBuiltin()) {
1053 732 : assembler().Emit(ReturnInstruction{});
1054 : } else {
1055 0 : UNREACHABLE();
1056 : }
1057 : } else {
1058 11 : if (stmt->value) {
1059 0 : std::stringstream s;
1060 : s << "return expression can't be specified for a void or never return "
1061 0 : "type";
1062 0 : ReportError(s.str());
1063 : }
1064 : GenerateLabelGoto(end);
1065 : }
1066 : current_callable->IncrementReturns();
1067 691 : return TypeOracle::GetNeverType();
1068 : }
1069 :
1070 4 : const Type* ImplementationVisitor::Visit(ForOfLoopStatement* stmt) {
1071 4 : VisitResult expression_result = Visit(stmt->iterable);
1072 : VisitResult begin = stmt->begin
1073 : ? Visit(*stmt->begin)
1074 8 : : VisitResult(TypeOracle::GetConstInt31Type(), "0");
1075 :
1076 : VisitResult end = stmt->end
1077 : ? Visit(*stmt->end)
1078 24 : : GenerateCall(".length", {{expression_result}, {}});
1079 :
1080 4 : const Type* common_type = GetCommonType(begin.type(), end.type());
1081 8 : VisitResult index = GenerateImplicitConvert(common_type, begin);
1082 :
1083 8 : Block* body_block = assembler().NewBlock();
1084 8 : Block* increment_block = assembler().NewBlock(assembler().CurrentStack());
1085 8 : Block* exit_block = assembler().NewBlock(assembler().CurrentStack());
1086 :
1087 8 : Block* header_block = assembler().NewBlock();
1088 :
1089 4 : assembler().Goto(header_block);
1090 :
1091 4 : assembler().Bind(header_block);
1092 :
1093 4 : BreakContinueActivator activator(exit_block, increment_block);
1094 :
1095 : {
1096 : StackScope comparison_scope(this);
1097 20 : VisitResult result = GenerateCall("<", {{index, end}, {}});
1098 4 : if (result.type() != TypeOracle::GetBoolType()) {
1099 0 : ReportError("operator < with arguments(", *index.type(), ", ",
1100 0 : *end.type(),
1101 : ") used in for-of loop has to return type bool, but "
1102 : "returned type ",
1103 0 : *result.type());
1104 : }
1105 12 : comparison_scope.Yield(result);
1106 : }
1107 4 : assembler().Branch(body_block, exit_block);
1108 :
1109 4 : assembler().Bind(body_block);
1110 : {
1111 4 : VisitResult element_result;
1112 : {
1113 : StackScope element_scope(this);
1114 20 : VisitResult result = GenerateCall("[]", {{expression_result, index}, {}});
1115 4 : if (stmt->var_declaration->type) {
1116 : const Type* declared_type =
1117 4 : Declarations::GetType(*stmt->var_declaration->type);
1118 12 : result = GenerateImplicitConvert(declared_type, result);
1119 : }
1120 12 : element_result = element_scope.Yield(result);
1121 : }
1122 : Binding<LocalValue> element_var_binding{&ValueBindingsManager::Get(),
1123 : stmt->var_declaration->name,
1124 16 : LocalValue{true, element_result}};
1125 4 : Visit(stmt->body);
1126 : }
1127 4 : assembler().Goto(increment_block);
1128 :
1129 4 : assembler().Bind(increment_block);
1130 : {
1131 : Arguments increment_args;
1132 16 : increment_args.parameters = {index, {TypeOracle::GetConstInt31Type(), "1"}};
1133 12 : VisitResult increment_result = GenerateCall("+", increment_args);
1134 :
1135 : GenerateAssignToLocation(LocationReference::VariableAccess(index),
1136 16 : increment_result);
1137 : }
1138 :
1139 4 : assembler().Goto(header_block);
1140 :
1141 4 : assembler().Bind(exit_block);
1142 8 : return TypeOracle::GetVoidType();
1143 : }
1144 :
1145 72 : VisitResult ImplementationVisitor::TemporaryUninitializedStruct(
1146 : const StructType* struct_type, const std::string& reason) {
1147 : StackRange range = assembler().TopRange(0);
1148 337 : for (const Field& f : struct_type->fields()) {
1149 193 : if (const StructType* struct_type =
1150 193 : StructType::DynamicCast(f.name_and_type.type)) {
1151 : range.Extend(
1152 58 : TemporaryUninitializedStruct(struct_type, reason).stack_range());
1153 : } else {
1154 492 : std::string descriptor = "unitialized field '" + f.name_and_type.name +
1155 820 : "' declared at " + PositionAsString(f.pos) +
1156 164 : " (" + reason + ")";
1157 164 : TypeVector lowered_types = LowerType(f.name_and_type.type);
1158 492 : for (const Type* type : lowered_types) {
1159 164 : assembler().Emit(PushUninitializedInstruction{
1160 984 : TypeOracle::GetTopType(descriptor, type)});
1161 : }
1162 164 : range.Extend(assembler().TopRange(lowered_types.size()));
1163 : }
1164 : }
1165 72 : return VisitResult(struct_type, range);
1166 : }
1167 :
1168 1128 : VisitResult ImplementationVisitor::Visit(TryLabelExpression* expr) {
1169 292 : size_t parameter_count = expr->label_block->parameters.names.size();
1170 : std::vector<VisitResult> parameters;
1171 :
1172 : Block* label_block = nullptr;
1173 526 : Block* done_block = assembler().NewBlock();
1174 263 : VisitResult try_result;
1175 :
1176 : {
1177 263 : CurrentSourcePosition::Scope source_position(expr->label_block->pos);
1178 263 : if (expr->label_block->parameters.has_varargs) {
1179 0 : ReportError("cannot use ... for label parameters");
1180 : }
1181 : Stack<const Type*> label_input_stack = assembler().CurrentStack();
1182 : TypeVector parameter_types;
1183 292 : for (size_t i = 0; i < parameter_count; ++i) {
1184 : const Type* type =
1185 58 : Declarations::GetType(expr->label_block->parameters.types[i]);
1186 29 : parameter_types.push_back(type);
1187 29 : if (type->IsConstexpr()) {
1188 0 : ReportError("no constexpr type allowed for label arguments");
1189 : }
1190 58 : StackRange range = label_input_stack.PushMany(LowerType(type));
1191 58 : parameters.push_back(VisitResult(type, range));
1192 : }
1193 : label_block = assembler().NewBlock(label_input_stack,
1194 789 : IsDeferred(expr->label_block->body));
1195 :
1196 : Binding<LocalLabel> label_binding{&LabelBindingsManager::Get(),
1197 : expr->label_block->label,
1198 1578 : LocalLabel{label_block, parameter_types}};
1199 :
1200 : // Visit try
1201 : StackScope stack_scope(this);
1202 526 : try_result = Visit(expr->try_expression);
1203 263 : if (try_result.type() != TypeOracle::GetNeverType()) {
1204 537 : try_result = stack_scope.Yield(try_result);
1205 179 : assembler().Goto(done_block);
1206 : }
1207 : }
1208 :
1209 : // Visit and output the code for the label block. If the label block falls
1210 : // through, then the try must not return a value. Also, if the try doesn't
1211 : // fall through, but the label does, then overall the try-label block
1212 : // returns type void.
1213 263 : assembler().Bind(label_block);
1214 : const Type* label_result;
1215 : {
1216 : BlockBindings<LocalValue> parameter_bindings(&ValueBindingsManager::Get());
1217 292 : for (size_t i = 0; i < parameter_count; ++i) {
1218 29 : parameter_bindings.Add(expr->label_block->parameters.names[i],
1219 145 : LocalValue{true, parameters[i]});
1220 : }
1221 :
1222 263 : label_result = Visit(expr->label_block->body);
1223 : }
1224 263 : if (!try_result.type()->IsVoidOrNever() && label_result->IsVoid()) {
1225 : ReportError(
1226 0 : "otherwise clauses cannot fall through in a non-void expression");
1227 : }
1228 263 : if (label_result != TypeOracle::GetNeverType()) {
1229 76 : assembler().Goto(done_block);
1230 : }
1231 339 : if (label_result->IsVoid() && try_result.type()->IsNever()) {
1232 32 : try_result =
1233 16 : VisitResult(TypeOracle::GetVoidType(), try_result.stack_range());
1234 : }
1235 :
1236 263 : if (!try_result.type()->IsNever()) {
1237 195 : assembler().Bind(done_block);
1238 : }
1239 263 : return try_result;
1240 : }
1241 :
1242 0 : VisitResult ImplementationVisitor::Visit(StatementExpression* expr) {
1243 154 : return VisitResult{Visit(expr->statement), assembler().TopRange(0)};
1244 : }
1245 :
1246 12 : VisitResult ImplementationVisitor::Visit(NewExpression* expr) {
1247 : StackScope stack_scope(this);
1248 12 : const Type* type = Declarations::GetType(expr->type);
1249 : const ClassType* class_type = ClassType::DynamicCast(type);
1250 39 : if (class_type == nullptr) {
1251 : ReportError("type for new expression must be a class, \"", *type,
1252 0 : "\" is not");
1253 : }
1254 :
1255 12 : if (!class_type->AllowInstantiation()) {
1256 : // Classes that are only used for testing should never be instantiated.
1257 : ReportError(*class_type,
1258 0 : " cannot be allocated with new (it's used for testing)");
1259 : }
1260 :
1261 : // In order to ensure "atomicity" of object allocation, a class' constructors
1262 : // operate on a per-class internal struct rather than the class directly until
1263 : // the constructor has successfully completed and all class members are
1264 : // available. Create the appropriate unitialized struct and pass it to the
1265 : // matching class constructor with the arguments that were passed to new{}
1266 : StructType* class_this_struct = class_type->struct_type();
1267 : VisitResult unitialized_struct = TemporaryUninitializedStruct(
1268 : class_this_struct,
1269 24 : "it's not set in the constructor for class " + class_type->name());
1270 12 : Arguments constructor_arguments;
1271 47 : for (auto p : expr->parameters) {
1272 46 : constructor_arguments.parameters.push_back(Visit(p));
1273 : }
1274 : LocationReference unitialized_struct_ref =
1275 36 : LocationReference::VariableAccess(unitialized_struct);
1276 : Callable* callable =
1277 24 : LookupConstructor(unitialized_struct_ref, constructor_arguments, {});
1278 : GenerateCall(callable, unitialized_struct_ref, constructor_arguments,
1279 60 : {class_type}, false);
1280 12 : VisitResult new_struct_result = unitialized_struct;
1281 :
1282 : // Output the code to generate an unitialized object of the class size in the
1283 : // GC heap.
1284 12 : VisitResult allocate_result;
1285 12 : if (class_type->IsExtern()) {
1286 27 : VisitResult object_map = ProjectStructField(new_struct_result, "map");
1287 9 : Arguments size_arguments;
1288 9 : size_arguments.parameters.push_back(object_map);
1289 : VisitResult object_size = GenerateCall("%GetAllocationBaseSize",
1290 36 : size_arguments, {class_type}, false);
1291 9 : Arguments allocate_arguments;
1292 9 : allocate_arguments.parameters.push_back(object_size);
1293 45 : allocate_result =
1294 9 : GenerateCall("%Allocate", allocate_arguments, {class_type}, false);
1295 : DCHECK(allocate_result.IsOnStack());
1296 : } else {
1297 : Arguments allocate_arguments;
1298 : allocate_arguments.parameters.push_back(
1299 : VisitResult(TypeOracle::GetConstexprIntPtrType(),
1300 12 : std::to_string(class_type->size() / kTaggedSize)));
1301 15 : allocate_result = GenerateCall("%AllocateInternalClass", allocate_arguments,
1302 6 : {class_type}, false);
1303 : }
1304 :
1305 : // Fill in the fields of the newly allocated class by copying the values
1306 : // from the struct that was built by the constructor. So that the generaeted
1307 : // code is a bit more readable, assign the values from the first class
1308 : // member to the last, in order. To do this, first build a list of fields
1309 : // to assign to in reverse order by visiting the class heirarchy.
1310 12 : std::vector<std::pair<const Field*, VisitResult>> store_pairs;
1311 : const ClassType* current_class = class_type;
1312 61 : while (current_class != nullptr) {
1313 : auto& fields = current_class->fields();
1314 103 : for (auto i = fields.rbegin(); i != fields.rend(); ++i) {
1315 : store_pairs.push_back(std::make_pair(
1316 264 : &*i, ProjectStructField(new_struct_result, i->name_and_type.name)));
1317 : }
1318 : current_class = current_class->GetSuperClass();
1319 37 : if (current_class) {
1320 100 : new_struct_result = ProjectStructField(new_struct_result,
1321 25 : kConstructorStructSuperFieldName);
1322 : }
1323 : }
1324 :
1325 : // Now that the reversed list of fields and the assignment VisitResults are
1326 : // available, emit the copies in reverse order of the reversed list to
1327 : // produce the class field assignments in the expected order.
1328 78 : for (auto i = store_pairs.rbegin(); i != store_pairs.rend(); ++i) {
1329 66 : assembler().Emit(
1330 264 : PeekInstruction(allocate_result.stack_range().begin(), class_type));
1331 : assembler().Emit(PeekInstruction(i->second.stack_range().begin(),
1332 264 : i->first->name_and_type.type));
1333 : assembler().Emit(
1334 330 : StoreObjectFieldInstruction(class_type, i->first->name_and_type.name));
1335 : }
1336 :
1337 36 : return stack_scope.Yield(allocate_result);
1338 : }
1339 :
1340 17 : const Type* ImplementationVisitor::Visit(BreakStatement* stmt) {
1341 34 : base::Optional<Binding<LocalLabel>*> break_label = TryLookupLabel("_break");
1342 17 : if (!break_label) {
1343 0 : ReportError("break used outside of loop");
1344 : }
1345 17 : assembler().Goto((*break_label)->block);
1346 17 : return TypeOracle::GetNeverType();
1347 : }
1348 :
1349 15 : const Type* ImplementationVisitor::Visit(ContinueStatement* stmt) {
1350 : base::Optional<Binding<LocalLabel>*> continue_label =
1351 30 : TryLookupLabel("_continue");
1352 15 : if (!continue_label) {
1353 0 : ReportError("continue used outside of loop");
1354 : }
1355 15 : assembler().Goto((*continue_label)->block);
1356 15 : return TypeOracle::GetNeverType();
1357 : }
1358 :
1359 49 : const Type* ImplementationVisitor::Visit(ForLoopStatement* stmt) {
1360 : BlockBindings<LocalValue> loop_bindings(&ValueBindingsManager::Get());
1361 :
1362 49 : if (stmt->var_declaration) Visit(*stmt->var_declaration, &loop_bindings);
1363 :
1364 98 : Block* body_block = assembler().NewBlock(assembler().CurrentStack());
1365 98 : Block* exit_block = assembler().NewBlock(assembler().CurrentStack());
1366 :
1367 98 : Block* header_block = assembler().NewBlock();
1368 49 : assembler().Goto(header_block);
1369 49 : assembler().Bind(header_block);
1370 :
1371 : // The continue label is where "continue" statements jump to. If no action
1372 : // expression is provided, we jump directly to the header.
1373 : Block* continue_block = header_block;
1374 :
1375 : // The action label is only needed when an action expression was provided.
1376 : Block* action_block = nullptr;
1377 98 : if (stmt->action) {
1378 88 : action_block = assembler().NewBlock();
1379 :
1380 : // The action expression needs to be executed on a continue.
1381 : continue_block = action_block;
1382 : }
1383 :
1384 49 : if (stmt->test) {
1385 44 : GenerateExpressionBranch(*stmt->test, body_block, exit_block);
1386 : } else {
1387 5 : assembler().Goto(body_block);
1388 : }
1389 :
1390 49 : assembler().Bind(body_block);
1391 : {
1392 49 : BreakContinueActivator activator(exit_block, continue_block);
1393 49 : const Type* body_result = Visit(stmt->body);
1394 49 : if (body_result != TypeOracle::GetNeverType()) {
1395 49 : assembler().Goto(continue_block);
1396 : }
1397 : }
1398 :
1399 49 : if (stmt->action) {
1400 44 : assembler().Bind(action_block);
1401 44 : const Type* action_result = Visit(*stmt->action);
1402 44 : if (action_result != TypeOracle::GetNeverType()) {
1403 44 : assembler().Goto(header_block);
1404 : }
1405 : }
1406 :
1407 49 : assembler().Bind(exit_block);
1408 98 : return TypeOracle::GetVoidType();
1409 : }
1410 :
1411 23 : void ImplementationVisitor::GenerateImplementation(const std::string& dir,
1412 : Namespace* nspace) {
1413 : std::string new_source(nspace->source());
1414 : std::string base_file_name =
1415 69 : "builtins-" + DashifyString(nspace->name()) + "-from-dsl-gen";
1416 :
1417 46 : std::string source_file_name = dir + "/" + base_file_name + ".cc";
1418 23 : ReplaceFileContentsIfDifferent(source_file_name, new_source);
1419 : std::string new_header(nspace->header());
1420 46 : std::string header_file_name = dir + "/" + base_file_name + ".h";
1421 23 : ReplaceFileContentsIfDifferent(header_file_name, new_header);
1422 23 : }
1423 :
1424 0 : void ImplementationVisitor::GenerateMacroFunctionDeclaration(
1425 : std::ostream& o, const std::string& macro_prefix, Macro* macro) {
1426 568 : GenerateFunctionDeclaration(o, macro_prefix, macro->ExternalName(),
1427 1704 : macro->signature(), macro->parameter_names());
1428 0 : }
1429 :
1430 1184 : void ImplementationVisitor::GenerateFunctionDeclaration(
1431 : std::ostream& o, const std::string& macro_prefix, const std::string& name,
1432 : const Signature& signature, const NameVector& parameter_names) {
1433 1184 : if (GlobalContext::verbose()) {
1434 0 : std::cout << "generating source for declaration " << name << "\n";
1435 : }
1436 :
1437 1184 : if (signature.return_type->IsVoidOrNever()) {
1438 420 : o << "void";
1439 : } else {
1440 1528 : o << signature.return_type->GetGeneratedTypeName();
1441 : }
1442 2368 : o << " " << macro_prefix << name << "(";
1443 :
1444 : DCHECK_EQ(signature.types().size(), parameter_names.size());
1445 1184 : auto type_iterator = signature.types().begin();
1446 : bool first = true;
1447 4904 : for (const std::string& name : parameter_names) {
1448 2536 : if (!first) {
1449 1468 : o << ", ";
1450 : }
1451 2536 : const Type* parameter_type = *type_iterator;
1452 : const std::string& generated_type_name =
1453 2536 : parameter_type->GetGeneratedTypeName();
1454 7608 : o << generated_type_name << " " << ExternalParameterName(name);
1455 : type_iterator++;
1456 : first = false;
1457 : }
1458 :
1459 2636 : for (const LabelDeclaration& label_info : signature.labels) {
1460 268 : if (!first) {
1461 262 : o << ", ";
1462 : }
1463 804 : o << "compiler::CodeAssemblerLabel* " << ExternalLabelName(label_info.name);
1464 : size_t i = 0;
1465 604 : for (const Type* type : label_info.types) {
1466 : std::string generated_type_name;
1467 68 : if (type->IsStructType()) {
1468 : generated_type_name = "\n#error no structs allowed in labels\n";
1469 : } else {
1470 : generated_type_name = "compiler::TypedCodeAssemblerVariable<";
1471 124 : generated_type_name += type->GetGeneratedTNodeTypeName();
1472 : generated_type_name += ">*";
1473 : }
1474 68 : o << ", ";
1475 68 : o << generated_type_name << " "
1476 136 : << ExternalLabelParameterName(label_info.name, i);
1477 68 : ++i;
1478 : }
1479 : }
1480 :
1481 1184 : o << ")";
1482 1184 : }
1483 :
1484 : namespace {
1485 :
1486 0 : void FailCallableLookup(const std::string& reason, const QualifiedName& name,
1487 : const TypeVector& parameter_types,
1488 0 : const std::vector<Binding<LocalLabel>*>& labels,
1489 : const std::vector<Signature>& candidates) {
1490 0 : std::stringstream stream;
1491 0 : stream << "\n" << reason << ": \n " << name << "(" << parameter_types << ")";
1492 0 : if (labels.size() != 0) {
1493 0 : stream << " labels ";
1494 0 : for (size_t i = 0; i < labels.size(); ++i) {
1495 0 : stream << labels[i]->name() << "(" << labels[i]->parameter_types << ")";
1496 : }
1497 : }
1498 0 : stream << "\ncandidates are:";
1499 0 : for (const Signature& signature : candidates) {
1500 0 : stream << "\n " << name;
1501 0 : PrintSignature(stream, signature, false);
1502 : }
1503 0 : ReportError(stream.str());
1504 : }
1505 :
1506 2049 : Callable* GetOrCreateSpecialization(const SpecializationKey& key) {
1507 2049 : if (base::Optional<Callable*> specialization =
1508 2049 : key.generic->GetSpecialization(key.specialized_types)) {
1509 1861 : return *specialization;
1510 : }
1511 188 : return DeclarationVisitor().SpecializeImplicit(key);
1512 : }
1513 :
1514 : } // namespace
1515 :
1516 0 : base::Optional<Binding<LocalValue>*> ImplementationVisitor::TryLookupLocalValue(
1517 : const std::string& name) {
1518 10053 : return ValueBindingsManager::Get().TryLookup(name);
1519 : }
1520 :
1521 0 : base::Optional<Binding<LocalLabel>*> ImplementationVisitor::TryLookupLabel(
1522 : const std::string& name) {
1523 6205 : return LabelBindingsManager::Get().TryLookup(name);
1524 : }
1525 :
1526 1205 : Binding<LocalLabel>* ImplementationVisitor::LookupLabel(
1527 : const std::string& name) {
1528 : base::Optional<Binding<LocalLabel>*> label = TryLookupLabel(name);
1529 1205 : if (!label) ReportError("cannot find label ", name);
1530 1205 : return *label;
1531 : }
1532 :
1533 115 : Block* ImplementationVisitor::LookupSimpleLabel(const std::string& name) {
1534 115 : LocalLabel* label = LookupLabel(name);
1535 115 : if (!label->parameter_types.empty()) {
1536 : ReportError("label ", name,
1537 : "was expected to have no parameters, but has parameters (",
1538 0 : label->parameter_types, ")");
1539 : }
1540 115 : return label->block;
1541 : }
1542 :
1543 : template <class Container>
1544 5205 : Callable* ImplementationVisitor::LookupCallable(
1545 : const QualifiedName& name, const Container& declaration_container,
1546 5205 : const TypeVector& parameter_types,
1547 35152 : const std::vector<Binding<LocalLabel>*>& labels,
1548 : const TypeVector& specialization_types) {
1549 : Callable* result = nullptr;
1550 :
1551 : std::vector<Declarable*> overloads;
1552 5205 : std::vector<Signature> overload_signatures;
1553 27986 : for (auto* declarable : declaration_container) {
1554 17576 : if (Generic* generic = Generic::DynamicCast(declarable)) {
1555 : base::Optional<TypeVector> inferred_specialization_types =
1556 : generic->InferSpecializationTypes(specialization_types,
1557 2358 : parameter_types);
1558 2358 : if (!inferred_specialization_types) continue;
1559 4716 : overloads.push_back(generic);
1560 : overload_signatures.push_back(
1561 : DeclarationVisitor().MakeSpecializedSignature(
1562 7074 : SpecializationKey{generic, *inferred_specialization_types}));
1563 15218 : } else if (Callable* callable = Callable::DynamicCast(declarable)) {
1564 30436 : overloads.push_back(callable);
1565 15218 : overload_signatures.push_back(callable->signature());
1566 : }
1567 : }
1568 : // Indices of candidates in overloads/overload_signatures.
1569 : std::vector<size_t> candidates;
1570 45562 : for (size_t i = 0; i < overloads.size(); ++i) {
1571 17576 : const Signature& signature = overload_signatures[i];
1572 : bool try_bool_context = labels.size() == 0 &&
1573 17576 : signature.return_type == TypeOracle::GetNeverType();
1574 17968 : if (IsCompatibleSignature(signature, parameter_types, labels.size()) ||
1575 : (try_bool_context &&
1576 392 : IsCompatibleSignature(signature, parameter_types, 2))) {
1577 6009 : candidates.push_back(i);
1578 : }
1579 : }
1580 :
1581 5205 : if (overloads.empty()) {
1582 0 : std::stringstream stream;
1583 0 : stream << "no matching declaration found for " << name;
1584 0 : ReportError(stream.str());
1585 5205 : } else if (candidates.empty()) {
1586 0 : FailCallableLookup("cannot find suitable callable with name", name,
1587 0 : parameter_types, labels, overload_signatures);
1588 : }
1589 :
1590 1608 : auto is_better_candidate = [&](size_t a, size_t b) {
1591 1608 : return ParameterDifference(overload_signatures[a].GetExplicitTypes(),
1592 : parameter_types)
1593 : .StrictlyBetterThan(ParameterDifference(
1594 12864 : overload_signatures[b].GetExplicitTypes(), parameter_types));
1595 8421 : };
1596 :
1597 10410 : size_t best = *std::min_element(candidates.begin(), candidates.end(),
1598 5205 : is_better_candidate);
1599 : // This check is contained in libstdc++'s std::min_element.
1600 : DCHECK(!is_better_candidate(best, best));
1601 16419 : for (size_t candidate : candidates) {
1602 6009 : if (candidate != best && !is_better_candidate(best, candidate)) {
1603 : std::vector<Signature> candidate_signatures;
1604 0 : for (size_t i : candidates) {
1605 0 : candidate_signatures.push_back(overload_signatures[i]);
1606 : }
1607 0 : FailCallableLookup("ambiguous callable ", name, parameter_types, labels,
1608 0 : candidate_signatures);
1609 : }
1610 : }
1611 :
1612 15615 : if (Generic* generic = Generic::DynamicCast(overloads[best])) {
1613 1962 : result = GetOrCreateSpecialization(
1614 : SpecializationKey{generic, *generic->InferSpecializationTypes(
1615 5886 : specialization_types, parameter_types)});
1616 : } else {
1617 : result = Callable::cast(overloads[best]);
1618 : }
1619 :
1620 : size_t caller_size = parameter_types.size();
1621 : size_t callee_size =
1622 10410 : result->signature().types().size() - result->signature().implicit_count;
1623 5205 : if (caller_size != callee_size &&
1624 : !result->signature().parameter_types.var_args) {
1625 0 : std::stringstream stream;
1626 0 : stream << "parameter count mismatch calling " << *result << " - expected "
1627 0 : << std::to_string(callee_size) << ", found "
1628 0 : << std::to_string(caller_size);
1629 0 : ReportError(stream.str());
1630 : }
1631 :
1632 5205 : return result;
1633 : }
1634 :
1635 : template <class Container>
1636 5007 : Callable* ImplementationVisitor::LookupCallable(
1637 : const QualifiedName& name, const Container& declaration_container,
1638 : const Arguments& arguments, const TypeVector& specialization_types) {
1639 : return LookupCallable(name, declaration_container,
1640 : arguments.parameters.GetTypeVector(), arguments.labels,
1641 10014 : specialization_types);
1642 : }
1643 :
1644 198 : Method* ImplementationVisitor::LookupMethod(
1645 : const std::string& name, LocationReference this_reference,
1646 : const Arguments& arguments, const TypeVector& specialization_types) {
1647 198 : TypeVector types(arguments.parameters.GetTypeVector());
1648 396 : types.insert(types.begin(), this_reference.GetVisitResult().type());
1649 : return Method::cast(
1650 : LookupCallable({{}, name},
1651 198 : AggregateType::cast(this_reference.GetVisitResult().type())
1652 : ->Methods(name),
1653 1188 : types, arguments.labels, specialization_types));
1654 : }
1655 :
1656 100 : const Type* ImplementationVisitor::GetCommonType(const Type* left,
1657 : const Type* right) {
1658 : const Type* common_type;
1659 100 : if (IsAssignableFrom(left, right)) {
1660 : common_type = left;
1661 12 : } else if (IsAssignableFrom(right, left)) {
1662 : common_type = right;
1663 : } else {
1664 0 : common_type = TypeOracle::GetUnionType(left, right);
1665 : }
1666 100 : common_type = common_type->NonConstexprVersion();
1667 100 : return common_type;
1668 : }
1669 :
1670 47156 : VisitResult ImplementationVisitor::GenerateCopy(const VisitResult& to_copy) {
1671 17588 : if (to_copy.IsOnStack()) {
1672 : return VisitResult(to_copy.type(),
1673 29568 : assembler().Peek(to_copy.stack_range(), to_copy.type()));
1674 : }
1675 2804 : return to_copy;
1676 : }
1677 :
1678 31 : VisitResult ImplementationVisitor::Visit(StructExpression* decl) {
1679 : StackScope stack_scope(this);
1680 : const Type* raw_type = Declarations::LookupType(
1681 93 : QualifiedName(decl->namespace_qualification, decl->name));
1682 31 : if (!raw_type->IsStructType()) {
1683 0 : std::stringstream s;
1684 0 : s << decl->name << " is not a struct but used like one ";
1685 0 : ReportError(s.str());
1686 : }
1687 : const StructType* struct_type = StructType::cast(raw_type);
1688 : // Push unitialized 'this'
1689 : VisitResult uninitialized_struct = TemporaryUninitializedStruct(
1690 : struct_type,
1691 62 : "it's not set in the constructor for struct " + struct_type->name());
1692 31 : Arguments constructor_arguments;
1693 114 : for (auto p : decl->expressions) {
1694 104 : constructor_arguments.parameters.push_back(Visit(p));
1695 : }
1696 : LocationReference this_ref =
1697 93 : LocationReference::VariableAccess(uninitialized_struct);
1698 62 : Callable* callable = LookupConstructor(this_ref, constructor_arguments, {});
1699 124 : GenerateCall(callable, this_ref, constructor_arguments, {}, false);
1700 93 : return stack_scope.Yield(uninitialized_struct);
1701 : }
1702 :
1703 2493 : LocationReference ImplementationVisitor::GetLocationReference(
1704 : Expression* location) {
1705 2493 : switch (location->kind) {
1706 : case AstNode::Kind::kIdentifierExpression:
1707 1751 : return GetLocationReference(static_cast<IdentifierExpression*>(location));
1708 : case AstNode::Kind::kFieldAccessExpression:
1709 : return GetLocationReference(
1710 651 : static_cast<FieldAccessExpression*>(location));
1711 : case AstNode::Kind::kElementAccessExpression:
1712 : return GetLocationReference(
1713 68 : static_cast<ElementAccessExpression*>(location));
1714 : default:
1715 69 : return LocationReference::Temporary(Visit(location), "expression");
1716 : }
1717 : }
1718 :
1719 1171 : LocationReference ImplementationVisitor::GetLocationReference(
1720 : FieldAccessExpression* expr) {
1721 1171 : LocationReference reference = GetLocationReference(expr->object);
1722 1816 : if (reference.IsVariableAccess() &&
1723 645 : reference.variable().type()->IsStructType()) {
1724 : return LocationReference::VariableAccess(
1725 1749 : ProjectStructField(reference.variable(), expr->field));
1726 : }
1727 1104 : if (reference.IsTemporary() && reference.temporary().type()->IsStructType()) {
1728 : return LocationReference::Temporary(
1729 : ProjectStructField(reference.temporary(), expr->field),
1730 104 : reference.temporary_description());
1731 : }
1732 562 : VisitResult object_result = GenerateFetchFromLocation(reference);
1733 562 : if (const ClassType* class_type =
1734 562 : ClassType::DynamicCast(object_result.type())) {
1735 392 : if (class_type->HasField(expr->field)) {
1736 356 : const Field& field = (class_type->LookupField(expr->field));
1737 356 : if (field.index) {
1738 : return LocationReference::IndexedFieldAccess(object_result,
1739 402 : expr->field);
1740 : }
1741 : }
1742 : }
1743 2455 : return LocationReference::FieldAccess(object_result, expr->field);
1744 : }
1745 :
1746 233 : LocationReference ImplementationVisitor::GetLocationReference(
1747 : ElementAccessExpression* expr) {
1748 233 : LocationReference reference = GetLocationReference(expr->array);
1749 233 : VisitResult index = Visit(expr->index);
1750 233 : if (reference.IsIndexedFieldAccess()) {
1751 268 : return LocationReference::IndexedFieldIndexedAccess(reference, index);
1752 : } else {
1753 : return LocationReference::ArrayAccess(GenerateFetchFromLocation(reference),
1754 297 : index);
1755 233 : }
1756 : }
1757 :
1758 7222 : LocationReference ImplementationVisitor::GetLocationReference(
1759 : IdentifierExpression* expr) {
1760 7222 : if (expr->namespace_qualification.empty()) {
1761 7222 : if (base::Optional<Binding<LocalValue>*> value =
1762 14444 : TryLookupLocalValue(expr->name)) {
1763 13452 : if (expr->generic_arguments.size() != 0) {
1764 : ReportError("cannot have generic parameters on local name ",
1765 0 : expr->name);
1766 : }
1767 6238 : if ((*value)->is_const) {
1768 : return LocationReference::Temporary((*value)->value,
1769 11919 : "constant value " + expr->name);
1770 : }
1771 4530 : return LocationReference::VariableAccess((*value)->value);
1772 : }
1773 : }
1774 :
1775 984 : if (expr->IsThis()) {
1776 0 : ReportError("\"this\" cannot be qualified");
1777 : }
1778 2952 : QualifiedName name = QualifiedName(expr->namespace_qualification, expr->name);
1779 984 : if (base::Optional<Builtin*> builtin = Declarations::TryLookupBuiltin(name)) {
1780 : return LocationReference::Temporary(GetBuiltinCode(*builtin),
1781 24 : "builtin " + expr->name);
1782 : }
1783 976 : if (expr->generic_arguments.size() != 0) {
1784 87 : Generic* generic = Declarations::LookupUniqueGeneric(name);
1785 : Callable* specialization = GetOrCreateSpecialization(
1786 174 : SpecializationKey{generic, GetTypeVector(expr->generic_arguments)});
1787 87 : if (Builtin* builtin = Builtin::DynamicCast(specialization)) {
1788 : DCHECK(!builtin->IsExternal());
1789 : return LocationReference::Temporary(GetBuiltinCode(builtin),
1790 261 : "builtin " + expr->name);
1791 : } else {
1792 : ReportError("cannot create function pointer for non-builtin ",
1793 0 : generic->name());
1794 : }
1795 : }
1796 889 : Value* value = Declarations::LookupValue(name);
1797 889 : if (auto* constant = NamespaceConstant::DynamicCast(value)) {
1798 969 : if (constant->type()->IsConstexpr()) {
1799 : return LocationReference::Temporary(
1800 36 : VisitResult(constant->type(), constant->ExternalAssemblerName() +
1801 18 : "(state_)." +
1802 36 : constant->constant_name() + "()"),
1803 54 : "namespace constant " + expr->name);
1804 : }
1805 933 : assembler().Emit(NamespaceConstantInstruction{constant});
1806 : StackRange stack_range =
1807 311 : assembler().TopRange(LoweredSlotCount(constant->type()));
1808 : return LocationReference::Temporary(
1809 : VisitResult(constant->type(), stack_range),
1810 933 : "namespace constant " + expr->name);
1811 : }
1812 : ExternConstant* constant = ExternConstant::cast(value);
1813 : return LocationReference::Temporary(constant->value(),
1814 1680 : "extern value " + expr->name);
1815 : }
1816 :
1817 6985 : VisitResult ImplementationVisitor::GenerateFetchFromLocation(
1818 : const LocationReference& reference) {
1819 6985 : if (reference.IsTemporary()) {
1820 4980 : return GenerateCopy(reference.temporary());
1821 2005 : } else if (reference.IsVariableAccess()) {
1822 1454 : return GenerateCopy(reference.variable());
1823 : } else {
1824 551 : if (reference.IsIndexedFieldAccess()) {
1825 : ReportError(
1826 0 : "fetching a value directly from an indexed field isn't allowed");
1827 : }
1828 : DCHECK(reference.IsCallAccess());
1829 551 : return GenerateCall(reference.eval_function(),
1830 1653 : Arguments{reference.call_arguments(), {}});
1831 : }
1832 : }
1833 :
1834 938 : void ImplementationVisitor::GenerateAssignToLocation(
1835 : const LocationReference& reference, const VisitResult& assignment_value) {
1836 938 : if (reference.IsCallAccess()) {
1837 : Arguments arguments{reference.call_arguments(), {}};
1838 110 : arguments.parameters.push_back(assignment_value);
1839 440 : GenerateCall(reference.assign_function(), arguments);
1840 828 : } else if (reference.IsVariableAccess()) {
1841 828 : VisitResult variable = reference.variable();
1842 : VisitResult converted_value =
1843 1656 : GenerateImplicitConvert(variable.type(), assignment_value);
1844 828 : assembler().Poke(variable.stack_range(), converted_value.stack_range(),
1845 1656 : variable.type());
1846 0 : } else if (reference.IsIndexedFieldAccess()) {
1847 0 : ReportError("assigning a value directly to an indexed field isn't allowed");
1848 : } else {
1849 : DCHECK(reference.IsTemporary());
1850 : ReportError("cannot assign to temporary ",
1851 0 : reference.temporary_description());
1852 : }
1853 938 : }
1854 :
1855 14 : VisitResult ImplementationVisitor::GeneratePointerCall(
1856 : Expression* callee, const Arguments& arguments, bool is_tailcall) {
1857 : StackScope scope(this);
1858 14 : TypeVector parameter_types(arguments.parameters.GetTypeVector());
1859 14 : VisitResult callee_result = Visit(callee);
1860 28 : if (!callee_result.type()->IsBuiltinPointerType()) {
1861 0 : std::stringstream stream;
1862 0 : stream << "Expected a function pointer type but found "
1863 0 : << *callee_result.type();
1864 0 : ReportError(stream.str());
1865 : }
1866 14 : const BuiltinPointerType* type =
1867 : BuiltinPointerType::cast(callee_result.type());
1868 :
1869 42 : if (type->parameter_types().size() != parameter_types.size()) {
1870 0 : std::stringstream stream;
1871 0 : stream << "parameter count mismatch calling function pointer with Type: "
1872 0 : << *type << " - expected "
1873 0 : << std::to_string(type->parameter_types().size()) << ", found "
1874 0 : << std::to_string(parameter_types.size());
1875 0 : ReportError(stream.str());
1876 : }
1877 :
1878 14 : ParameterTypes types{type->parameter_types(), false};
1879 14 : Signature sig;
1880 : sig.parameter_types = types;
1881 14 : if (!IsCompatibleSignature(sig, parameter_types, 0)) {
1882 0 : std::stringstream stream;
1883 0 : stream << "parameters do not match function pointer signature. Expected: ("
1884 0 : << type->parameter_types() << ") but got: (" << parameter_types
1885 0 : << ")";
1886 0 : ReportError(stream.str());
1887 : }
1888 :
1889 28 : callee_result = GenerateCopy(callee_result);
1890 : StackRange arg_range = assembler().TopRange(0);
1891 108 : for (size_t current = 0; current < arguments.parameters.size(); ++current) {
1892 80 : const Type* to_type = type->parameter_types()[current];
1893 : arg_range.Extend(
1894 54 : GenerateImplicitConvert(to_type, arguments.parameters[current])
1895 120 : .stack_range());
1896 : }
1897 :
1898 14 : assembler().Emit(
1899 56 : CallBuiltinPointerInstruction{is_tailcall, type, arg_range.Size()});
1900 :
1901 14 : if (is_tailcall) {
1902 0 : return VisitResult::NeverResult();
1903 : }
1904 : DCHECK_EQ(1, LoweredSlotCount(type->return_type()));
1905 28 : return scope.Yield(VisitResult(type->return_type(), assembler().TopRange(1)));
1906 : }
1907 :
1908 9006 : void ImplementationVisitor::AddCallParameter(
1909 : Callable* callable, VisitResult parameter, const Type* parameter_type,
1910 : std::vector<VisitResult>* converted_arguments, StackRange* argument_range,
1911 : std::vector<std::string>* constexpr_arguments) {
1912 18012 : VisitResult converted = GenerateImplicitConvert(parameter_type, parameter);
1913 9006 : converted_arguments->push_back(converted);
1914 9006 : if (!callable->ShouldBeInlined()) {
1915 8723 : if (converted.IsOnStack()) {
1916 : argument_range->Extend(converted.stack_range());
1917 : } else {
1918 1910 : constexpr_arguments->push_back(converted.constexpr_value());
1919 : }
1920 : }
1921 9006 : }
1922 :
1923 5205 : VisitResult ImplementationVisitor::GenerateCall(
1924 10410 : Callable* callable, base::Optional<LocationReference> this_reference,
1925 : Arguments arguments, const TypeVector& specialization_types,
1926 : bool is_tailcall) {
1927 : // Operators used in a branching context can also be function calls that never
1928 : // return but have a True and False label
1929 20945 : if (arguments.labels.size() == 0 &&
1930 4848 : callable->signature().labels.size() == 2) {
1931 : base::Optional<Binding<LocalLabel>*> true_label =
1932 136 : TryLookupLabel(kTrueLabelName);
1933 : base::Optional<Binding<LocalLabel>*> false_label =
1934 136 : TryLookupLabel(kFalseLabelName);
1935 68 : if (!true_label || !false_label) {
1936 : ReportError(
1937 0 : callable->ReadableName(),
1938 : " does not return a value, but has to be called in a branching "
1939 : "context (e.g., conditional or if-condition). You can fix this by "
1940 0 : "adding \"? true : false\".");
1941 : }
1942 68 : arguments.labels.push_back(*true_label);
1943 68 : arguments.labels.push_back(*false_label);
1944 : }
1945 :
1946 5205 : const Type* return_type = callable->signature().return_type;
1947 :
1948 : std::vector<VisitResult> converted_arguments;
1949 5205 : StackRange argument_range = assembler().TopRange(0);
1950 5205 : std::vector<std::string> constexpr_arguments;
1951 :
1952 : size_t current = 0;
1953 5839 : for (; current < callable->signature().implicit_count; ++current) {
1954 1268 : std::string implicit_name = callable->signature().parameter_names[current];
1955 : base::Optional<Binding<LocalValue>*> val =
1956 : TryLookupLocalValue(implicit_name);
1957 634 : if (!val) {
1958 : ReportError("implicit parameter '", implicit_name,
1959 0 : "' required for call to '", callable->ReadableName(),
1960 0 : "' is not defined");
1961 : }
1962 : AddCallParameter(callable, (*val)->value,
1963 634 : callable->signature().parameter_types.types[current],
1964 : &converted_arguments, &argument_range,
1965 1268 : &constexpr_arguments);
1966 : }
1967 :
1968 5205 : if (this_reference) {
1969 : DCHECK(callable->IsMethod());
1970 198 : Method* method = Method::cast(callable);
1971 : // By now, the this reference should either be a variable or
1972 : // a temporary, in both cases the fetch of the VisitResult should succeed.
1973 198 : VisitResult this_value = this_reference->GetVisitResult();
1974 198 : if (method->ShouldBeInlined()) {
1975 366 : if (!this_value.type()->IsSubtypeOf(method->aggregate_type())) {
1976 : ReportError("this parameter must be a subtype of ",
1977 : *method->aggregate_type(), " but it is of type ",
1978 0 : this_value.type());
1979 : }
1980 : } else {
1981 : AddCallParameter(callable, this_value, method->aggregate_type(),
1982 : &converted_arguments, &argument_range,
1983 30 : &constexpr_arguments);
1984 : }
1985 198 : ++current;
1986 : }
1987 :
1988 18767 : for (auto arg : arguments.parameters) {
1989 8357 : const Type* to_type = (current >= callable->signature().types().size())
1990 : ? TypeOracle::GetObjectType()
1991 16714 : : callable->signature().types()[current++];
1992 : AddCallParameter(callable, arg, to_type, &converted_arguments,
1993 16714 : &argument_range, &constexpr_arguments);
1994 : }
1995 :
1996 5205 : if (GlobalContext::verbose()) {
1997 0 : std::cout << "generating code for call to " << callable->ReadableName()
1998 0 : << "\n";
1999 : }
2000 :
2001 5205 : size_t label_count = callable->signature().labels.size();
2002 5205 : if (label_count != arguments.labels.size()) {
2003 0 : std::stringstream s;
2004 0 : s << "unexpected number of otherwise labels for "
2005 0 : << callable->ReadableName() << " (expected "
2006 0 : << std::to_string(label_count) << " found "
2007 0 : << std::to_string(arguments.labels.size()) << ")";
2008 0 : ReportError(s.str());
2009 : }
2010 :
2011 5205 : if (callable->IsTransitioning()) {
2012 196 : if (!CurrentCallable::Get()->IsTransitioning()) {
2013 0 : std::stringstream s;
2014 0 : s << *CurrentCallable::Get()
2015 0 : << " isn't marked transitioning but calls the transitioning "
2016 0 : << *callable;
2017 0 : ReportError(s.str());
2018 : }
2019 : }
2020 :
2021 5205 : if (auto* builtin = Builtin::DynamicCast(callable)) {
2022 98 : base::Optional<Block*> catch_block = GetCatchBlock();
2023 98 : assembler().Emit(CallBuiltinInstruction{
2024 392 : is_tailcall, builtin, argument_range.Size(), catch_block});
2025 98 : GenerateCatchBlock(catch_block);
2026 98 : if (is_tailcall) {
2027 1 : return VisitResult::NeverResult();
2028 : } else {
2029 97 : size_t slot_count = LoweredSlotCount(return_type);
2030 : DCHECK_LE(slot_count, 1);
2031 : // TODO(tebbi): Actually, builtins have to return a value, so we should
2032 : // assert slot_count == 1 here.
2033 : return VisitResult(return_type, assembler().TopRange(slot_count));
2034 : }
2035 5107 : } else if (auto* macro = Macro::DynamicCast(callable)) {
2036 4977 : if (is_tailcall) {
2037 0 : ReportError("can't tail call a macro");
2038 : }
2039 4977 : if (return_type->IsConstexpr()) {
2040 : DCHECK_EQ(0, arguments.labels.size());
2041 100 : std::stringstream result;
2042 200 : result << "(" << macro->external_assembler_name() << "(state_)."
2043 100 : << macro->ExternalName() << "(";
2044 : bool first = true;
2045 320 : for (VisitResult arg : arguments.parameters) {
2046 : DCHECK(!arg.IsOnStack());
2047 120 : if (!first) {
2048 55 : result << ", ";
2049 : }
2050 : first = false;
2051 : result << arg.constexpr_value();
2052 : }
2053 100 : result << "))";
2054 100 : return VisitResult(return_type, result.str());
2055 4877 : } else if (macro->ShouldBeInlined()) {
2056 : std::vector<Block*> label_blocks;
2057 393 : for (Binding<LocalLabel>* label : arguments.labels) {
2058 19 : label_blocks.push_back(label->block);
2059 : }
2060 : return InlineMacro(macro, this_reference, converted_arguments,
2061 748 : label_blocks);
2062 8974 : } else if (arguments.labels.empty() &&
2063 4284 : return_type != TypeOracle::GetNeverType()) {
2064 4237 : base::Optional<Block*> catch_block = GetCatchBlock();
2065 4237 : assembler().Emit(
2066 16948 : CallCsaMacroInstruction{macro, constexpr_arguments, catch_block});
2067 4237 : GenerateCatchBlock(catch_block);
2068 4237 : size_t return_slot_count = LoweredSlotCount(return_type);
2069 : return VisitResult(return_type, assembler().TopRange(return_slot_count));
2070 : } else {
2071 : base::Optional<Block*> return_continuation;
2072 453 : if (return_type != TypeOracle::GetNeverType()) {
2073 652 : return_continuation = assembler().NewBlock();
2074 : }
2075 :
2076 : std::vector<Block*> label_blocks;
2077 :
2078 935 : for (size_t i = 0; i < label_count; ++i) {
2079 1446 : label_blocks.push_back(assembler().NewBlock());
2080 : }
2081 453 : base::Optional<Block*> catch_block = GetCatchBlock();
2082 453 : assembler().Emit(CallCsaMacroAndBranchInstruction{
2083 : macro, constexpr_arguments, return_continuation, label_blocks,
2084 2265 : catch_block});
2085 453 : GenerateCatchBlock(catch_block);
2086 :
2087 935 : for (size_t i = 0; i < label_count; ++i) {
2088 482 : Binding<LocalLabel>* label = arguments.labels[i];
2089 : size_t callee_label_parameters =
2090 964 : callable->signature().labels[i].types.size();
2091 995 : if (label->parameter_types.size() != callee_label_parameters) {
2092 0 : std::stringstream s;
2093 0 : s << "label " << label->name()
2094 0 : << " doesn't have the right number of parameters (found "
2095 0 : << std::to_string(label->parameter_types.size()) << " expected "
2096 0 : << std::to_string(callee_label_parameters) << ")";
2097 0 : ReportError(s.str());
2098 : }
2099 964 : assembler().Bind(label_blocks[i]);
2100 : assembler().Goto(
2101 : label->block,
2102 1928 : LowerParameterTypes(callable->signature().labels[i].types).size());
2103 :
2104 : size_t j = 0;
2105 1477 : for (auto t : callable->signature().labels[i].types) {
2106 31 : const Type* parameter_type = label->parameter_types[j];
2107 31 : if (parameter_type != t) {
2108 : ReportError("mismatch of label parameters (expected ", *t, " got ",
2109 0 : parameter_type, " for parameter ", i + 1, ")");
2110 : }
2111 31 : j++;
2112 : }
2113 : }
2114 :
2115 453 : if (return_continuation) {
2116 326 : assembler().Bind(*return_continuation);
2117 326 : size_t return_slot_count = LoweredSlotCount(return_type);
2118 : return VisitResult(return_type,
2119 : assembler().TopRange(return_slot_count));
2120 : } else {
2121 127 : return VisitResult::NeverResult();
2122 : }
2123 : }
2124 130 : } else if (auto* runtime_function = RuntimeFunction::DynamicCast(callable)) {
2125 31 : base::Optional<Block*> catch_block = GetCatchBlock();
2126 31 : assembler().Emit(CallRuntimeInstruction{
2127 124 : is_tailcall, runtime_function, argument_range.Size(), catch_block});
2128 31 : GenerateCatchBlock(catch_block);
2129 31 : if (is_tailcall || return_type == TypeOracle::GetNeverType()) {
2130 11 : return VisitResult::NeverResult();
2131 : } else {
2132 20 : size_t slot_count = LoweredSlotCount(return_type);
2133 : DCHECK_LE(slot_count, 1);
2134 : // TODO(tebbi): Actually, runtime functions have to return a value, so
2135 : // we should assert slot_count == 1 here.
2136 : return VisitResult(return_type, assembler().TopRange(slot_count));
2137 : }
2138 99 : } else if (auto* intrinsic = Intrinsic::DynamicCast(callable)) {
2139 198 : if (intrinsic->ExternalName() == "%RawConstexprCast") {
2140 3 : if (intrinsic->signature().parameter_types.types.size() != 1 ||
2141 1 : constexpr_arguments.size() != 1) {
2142 : ReportError(
2143 : "%RawConstexprCast must take a single parameter with constexpr "
2144 0 : "type");
2145 : }
2146 1 : if (!return_type->IsConstexpr()) {
2147 0 : std::stringstream s;
2148 0 : s << *return_type
2149 0 : << " return type for %RawConstexprCast is not constexpr";
2150 0 : ReportError(s.str());
2151 : }
2152 1 : std::stringstream result;
2153 3 : result << "static_cast<" << return_type->GetGeneratedTypeName() << ">(";
2154 1 : result << constexpr_arguments[0];
2155 1 : result << ")";
2156 1 : return VisitResult(return_type, result.str());
2157 : } else {
2158 98 : assembler().Emit(CallIntrinsicInstruction{intrinsic, specialization_types,
2159 392 : constexpr_arguments});
2160 : size_t return_slot_count =
2161 98 : LoweredSlotCount(intrinsic->signature().return_type);
2162 : return VisitResult(return_type, assembler().TopRange(return_slot_count));
2163 : }
2164 : } else {
2165 0 : UNREACHABLE();
2166 5205 : }
2167 : }
2168 :
2169 5007 : VisitResult ImplementationVisitor::GenerateCall(
2170 : const QualifiedName& callable_name, Arguments arguments,
2171 : const TypeVector& specialization_types, bool is_tailcall) {
2172 : Callable* callable =
2173 : LookupCallable(callable_name, Declarations::Lookup(callable_name),
2174 10014 : arguments, specialization_types);
2175 : return GenerateCall(callable, base::nullopt, arguments, specialization_types,
2176 15021 : is_tailcall);
2177 : }
2178 :
2179 2792 : VisitResult ImplementationVisitor::Visit(CallExpression* expr,
2180 : bool is_tailcall) {
2181 : StackScope scope(this);
2182 2792 : Arguments arguments;
2183 : QualifiedName name =
2184 11168 : QualifiedName(expr->callee->namespace_qualification, expr->callee->name);
2185 : TypeVector specialization_types =
2186 2792 : GetTypeVector(expr->callee->generic_arguments);
2187 : bool has_template_arguments = !specialization_types.empty();
2188 10980 : for (Expression* arg : expr->arguments)
2189 10792 : arguments.parameters.push_back(Visit(arg));
2190 5584 : arguments.labels = LabelsFromIdentifiers(expr->labels);
2191 10617 : if (!has_template_arguments && name.namespace_qualification.empty() &&
2192 7186 : TryLookupLocalValue(name.name)) {
2193 : return scope.Yield(
2194 28 : GeneratePointerCall(expr->callee, arguments, is_tailcall));
2195 : } else {
2196 : return scope.Yield(
2197 8334 : GenerateCall(name, arguments, specialization_types, is_tailcall));
2198 : }
2199 : }
2200 :
2201 155 : VisitResult ImplementationVisitor::Visit(CallMethodExpression* expr) {
2202 : StackScope scope(this);
2203 155 : Arguments arguments;
2204 155 : std::string method_name = expr->method->name;
2205 : TypeVector specialization_types =
2206 155 : GetTypeVector(expr->method->generic_arguments);
2207 310 : LocationReference target = GetLocationReference(expr->target);
2208 155 : if (!target.IsVariableAccess()) {
2209 15 : VisitResult result = GenerateFetchFromLocation(target);
2210 60 : target = LocationReference::Temporary(result, "method target result");
2211 : }
2212 : const AggregateType* target_type =
2213 155 : AggregateType::DynamicCast(target.GetVisitResult().type());
2214 155 : if (!target_type) {
2215 0 : ReportError("target of method call not a struct or class type");
2216 : }
2217 310 : if (method_name == kConstructMethodName || method_name == kSuperMethodName) {
2218 62 : if (CurrentConstructorInfo::Get()) {
2219 : ConstructorInfo& info = *CurrentConstructorInfo::Get();
2220 62 : if (method_name == kSuperMethodName) {
2221 62 : if (info.super_calls != 0) {
2222 0 : ReportError("\"super\" can only be called once from a constructor");
2223 : }
2224 62 : ++info.super_calls;
2225 : DCHECK(target_type->IsStructType());
2226 : base::Optional<const ClassType*> derived_from =
2227 : StructType::cast(target_type)->GetDerivedFrom();
2228 62 : if (!derived_from) {
2229 0 : ReportError("\"super\" can only be called from class constructors");
2230 : }
2231 124 : if ((*derived_from)->GetSuperClass() == nullptr) {
2232 : ReportError(
2233 : "\"super\" can only be called in constructors for derived "
2234 0 : "classes");
2235 : }
2236 : } else {
2237 0 : ReportError("cannot call a constructor from a constructor");
2238 : }
2239 : } else {
2240 : ReportError(
2241 0 : "cannot call a constructor or \"super\" from a non-constructor");
2242 : }
2243 : }
2244 499 : for (Expression* arg : expr->arguments) {
2245 378 : arguments.parameters.push_back(Visit(arg));
2246 : }
2247 310 : arguments.labels = LabelsFromIdentifiers(expr->labels);
2248 155 : TypeVector argument_types = arguments.parameters.GetTypeVector();
2249 : DCHECK_EQ(expr->method->namespace_qualification.size(), 0);
2250 465 : QualifiedName qualified_name = QualifiedName(method_name);
2251 : Callable* callable = nullptr;
2252 155 : if (method_name == kConstructMethodName) {
2253 0 : callable = LookupConstructor(target, arguments, {});
2254 155 : } else if (method_name == kSuperMethodName) {
2255 : LocationReference super_this =
2256 : LocationReference::VariableAccess(ProjectStructField(
2257 310 : target.GetVisitResult(), kConstructorStructSuperFieldName));
2258 124 : callable = LookupConstructor(super_this, arguments, {});
2259 : VisitResult super_result =
2260 186 : GenerateCall(callable, super_this, arguments, {}, false);
2261 186 : return scope.Yield(super_result);
2262 : } else {
2263 186 : callable = LookupMethod(method_name, target, arguments, {});
2264 : }
2265 372 : return scope.Yield(GenerateCall(callable, target, arguments, {}, false));
2266 : }
2267 :
2268 58 : VisitResult ImplementationVisitor::Visit(LoadObjectFieldExpression* expr) {
2269 58 : VisitResult base_result = Visit(expr->base);
2270 58 : auto class_type = ClassType::DynamicCast(base_result.type());
2271 58 : if (!class_type) {
2272 : ReportError(
2273 : "base expression for a LoadObjectFieldExpression is not a class type "
2274 : "but instead ",
2275 0 : *base_result.type());
2276 : }
2277 58 : VisitResult result = base_result;
2278 290 : assembler().Emit(LoadObjectFieldInstruction{class_type, expr->field_name});
2279 58 : const Field& field = class_type->LookupField(expr->field_name);
2280 58 : result.SetType(field.name_and_type.type);
2281 58 : return result;
2282 : }
2283 :
2284 58 : VisitResult ImplementationVisitor::Visit(StoreObjectFieldExpression* expr) {
2285 58 : VisitResult base_result = Visit(expr->base);
2286 58 : auto class_type = ClassType::DynamicCast(base_result.type());
2287 58 : if (!class_type) {
2288 : ReportError(
2289 : "base expression for a StoreObjectFieldExpression is not a class type "
2290 : "but instead ",
2291 0 : *base_result.type());
2292 : }
2293 58 : VisitResult value = Visit(expr->value);
2294 290 : assembler().Emit(StoreObjectFieldInstruction{class_type, expr->field_name});
2295 116 : return VisitResult(value.type(), assembler().TopRange(0));
2296 : }
2297 :
2298 78 : VisitResult ImplementationVisitor::Visit(IntrinsicCallExpression* expr) {
2299 : StackScope scope(this);
2300 78 : Arguments arguments;
2301 78 : TypeVector specialization_types = GetTypeVector(expr->generic_arguments);
2302 234 : for (Expression* arg : expr->arguments)
2303 156 : arguments.parameters.push_back(Visit(arg));
2304 : return scope.Yield(
2305 312 : GenerateCall(expr->name, arguments, specialization_types, false));
2306 : }
2307 :
2308 0 : void ImplementationVisitor::GenerateBranch(const VisitResult& condition,
2309 : Block* true_block,
2310 : Block* false_block) {
2311 : DCHECK_EQ(condition,
2312 : VisitResult(TypeOracle::GetBoolType(), assembler().TopRange(1)));
2313 652 : assembler().Branch(true_block, false_block);
2314 0 : }
2315 :
2316 745 : void ImplementationVisitor::GenerateExpressionBranch(Expression* expression,
2317 : Block* true_block,
2318 : Block* false_block) {
2319 : // Conditional expressions can either explicitly return a bit
2320 : // type, or they can be backed by macros that don't return but
2321 : // take a true and false label. By declaring the labels before
2322 : // visiting the conditional expression, those label-based
2323 : // macro conditionals will be able to find them through normal
2324 : // label lookups.
2325 : Binding<LocalLabel> true_binding{&LabelBindingsManager::Get(), kTrueLabelName,
2326 2980 : LocalLabel{true_block}};
2327 : Binding<LocalLabel> false_binding{&LabelBindingsManager::Get(),
2328 3725 : kFalseLabelName, LocalLabel{false_block}};
2329 : StackScope stack_scope(this);
2330 745 : VisitResult expression_result = Visit(expression);
2331 745 : if (!expression_result.type()->IsNever()) {
2332 2608 : expression_result = stack_scope.Yield(
2333 652 : GenerateImplicitConvert(TypeOracle::GetBoolType(), expression_result));
2334 : GenerateBranch(expression_result, true_block, false_block);
2335 745 : }
2336 745 : }
2337 :
2338 12440 : VisitResult ImplementationVisitor::GenerateImplicitConvert(
2339 27323 : const Type* destination_type, VisitResult source) {
2340 : StackScope scope(this);
2341 12440 : if (source.type() == TypeOracle::GetNeverType()) {
2342 0 : ReportError("it is not allowed to use a value of type never");
2343 : }
2344 :
2345 12440 : if (destination_type == source.type()) {
2346 19994 : return scope.Yield(GenerateCopy(source));
2347 : }
2348 :
2349 2443 : if (TypeOracle::IsImplicitlyConvertableFrom(destination_type,
2350 : source.type())) {
2351 : return scope.Yield(GenerateCall(kFromConstexprMacroName, {{source}, {}},
2352 10400 : {destination_type, source.type()}, false));
2353 1143 : } else if (IsAssignableFrom(destination_type, source.type())) {
2354 : source.SetType(destination_type);
2355 2286 : return scope.Yield(GenerateCopy(source));
2356 : } else {
2357 0 : std::stringstream s;
2358 0 : s << "cannot use expression of type " << *source.type()
2359 0 : << " as a value of type " << *destination_type;
2360 0 : ReportError(s.str());
2361 : }
2362 : }
2363 :
2364 0 : StackRange ImplementationVisitor::GenerateLabelGoto(
2365 0 : LocalLabel* label, base::Optional<StackRange> arguments) {
2366 508 : return assembler().Goto(label->block, arguments ? arguments->Size() : 0);
2367 : }
2368 :
2369 2947 : std::vector<Binding<LocalLabel>*> ImplementationVisitor::LabelsFromIdentifiers(
2370 2947 : const std::vector<std::string>& names) {
2371 : std::vector<Binding<LocalLabel>*> result;
2372 2947 : result.reserve(names.size());
2373 6259 : for (const auto& name : names) {
2374 730 : result.push_back(LookupLabel(name));
2375 : }
2376 2947 : return result;
2377 : }
2378 :
2379 1476 : StackRange ImplementationVisitor::LowerParameter(
2380 : const Type* type, const std::string& parameter_name,
2381 : Stack<std::string>* lowered_parameters) {
2382 1476 : if (const StructType* struct_type = StructType::DynamicCast(type)) {
2383 : StackRange range = lowered_parameters->TopRange(0);
2384 467 : for (auto& field : struct_type->fields()) {
2385 : StackRange parameter_range = LowerParameter(
2386 : field.name_and_type.type,
2387 1092 : parameter_name + "." + field.name_and_type.name, lowered_parameters);
2388 : range.Extend(parameter_range);
2389 : }
2390 97 : return range;
2391 : } else {
2392 2758 : lowered_parameters->Push(parameter_name);
2393 : return lowered_parameters->TopRange(1);
2394 : }
2395 : }
2396 :
2397 45 : void ImplementationVisitor::LowerLabelParameter(
2398 : const Type* type, const std::string& parameter_name,
2399 : std::vector<std::string>* lowered_parameters) {
2400 45 : if (const StructType* struct_type = StructType::DynamicCast(type)) {
2401 17 : for (auto& field : struct_type->fields()) {
2402 : LowerLabelParameter(
2403 : field.name_and_type.type,
2404 55 : "&((*" + parameter_name + ")." + field.name_and_type.name + ")",
2405 11 : lowered_parameters);
2406 : }
2407 : } else {
2408 42 : lowered_parameters->push_back(parameter_name);
2409 : }
2410 45 : }
2411 :
2412 0 : std::string ImplementationVisitor::ExternalLabelName(
2413 : const std::string& label_name) {
2414 402 : return "label_" + label_name;
2415 : }
2416 :
2417 102 : std::string ImplementationVisitor::ExternalLabelParameterName(
2418 : const std::string& label_name, size_t i) {
2419 510 : return "label_" + label_name + "_parameter_" + std::to_string(i);
2420 : }
2421 :
2422 3804 : std::string ImplementationVisitor::ExternalParameterName(
2423 : const std::string& name) {
2424 7608 : return std::string("p_") + name;
2425 : }
2426 :
2427 0 : DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::ValueBindingsManager)
2428 0 : DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::LabelBindingsManager)
2429 0 : DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::CurrentCallable)
2430 0 : DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::CurrentReturnValue)
2431 0 : DEFINE_CONTEXTUAL_VARIABLE(ImplementationVisitor::CurrentConstructorInfo)
2432 :
2433 35964 : bool IsCompatibleSignature(const Signature& sig, const TypeVector& types,
2434 : size_t label_count) {
2435 58525 : auto i = sig.parameter_types.types.begin() + sig.implicit_count;
2436 35964 : if ((sig.parameter_types.types.size() - sig.implicit_count) > types.size())
2437 : return false;
2438 35456 : if (sig.labels.size() != label_count) return false;
2439 51145 : for (auto current : types) {
2440 22561 : if (i == sig.parameter_types.types.end()) {
2441 131 : if (!sig.parameter_types.var_args) return false;
2442 0 : if (!IsAssignableFrom(TypeOracle::GetObjectType(), current)) return false;
2443 : } else {
2444 22430 : if (!IsAssignableFrom(*i++, current)) return false;
2445 : }
2446 : }
2447 : return true;
2448 : }
2449 :
2450 4819 : base::Optional<Block*> ImplementationVisitor::GetCatchBlock() {
2451 4819 : base::Optional<Block*> catch_block;
2452 4819 : if (base::Optional<Binding<LocalLabel>*> catch_handler =
2453 9638 : TryLookupLabel("_catch")) {
2454 26 : catch_block = assembler().NewBlock(base::nullopt, true);
2455 : }
2456 4819 : return catch_block;
2457 : }
2458 :
2459 4819 : void ImplementationVisitor::GenerateCatchBlock(
2460 4819 : base::Optional<Block*> catch_block) {
2461 4819 : if (catch_block) {
2462 : base::Optional<Binding<LocalLabel>*> catch_handler =
2463 26 : TryLookupLabel("_catch");
2464 26 : if (assembler().CurrentBlockIsComplete()) {
2465 4 : assembler().Bind(*catch_block);
2466 4 : assembler().Goto((*catch_handler)->block, 1);
2467 : } else {
2468 9 : CfgAssemblerScopedTemporaryBlock temp(&assembler(), *catch_block);
2469 9 : assembler().Goto((*catch_handler)->block, 1);
2470 : }
2471 : }
2472 4819 : }
2473 :
2474 1 : void ImplementationVisitor::VisitAllDeclarables() {
2475 : CurrentCallable::Scope current_callable(nullptr);
2476 6203 : const std::vector<std::unique_ptr<Declarable>>& all_declarables =
2477 : GlobalContext::AllDeclarables();
2478 : // This has to be an index-based loop because all_declarables can be extended
2479 : // during the loop.
2480 12406 : for (size_t i = 0; i < all_declarables.size(); ++i) {
2481 6202 : Visit(all_declarables[i].get());
2482 : }
2483 1 : }
2484 :
2485 18606 : void ImplementationVisitor::Visit(Declarable* declarable) {
2486 : CurrentConstructorInfo::Scope current_constructor(base::nullopt);
2487 6202 : CurrentScope::Scope current_scope(declarable->ParentScope());
2488 6202 : CurrentSourcePosition::Scope current_source_position(declarable->pos());
2489 6202 : switch (declarable->kind()) {
2490 : case Declarable::kMacro:
2491 : return Visit(Macro::cast(declarable));
2492 : case Declarable::kMethod:
2493 : return Visit(Method::cast(declarable));
2494 : case Declarable::kBuiltin:
2495 150 : return Visit(Builtin::cast(declarable));
2496 : case Declarable::kTypeAlias:
2497 4833 : return Visit(TypeAlias::cast(declarable));
2498 : case Declarable::kNamespaceConstant:
2499 24 : return Visit(NamespaceConstant::cast(declarable));
2500 : case Declarable::kRuntimeFunction:
2501 : case Declarable::kIntrinsic:
2502 : case Declarable::kExternConstant:
2503 : case Declarable::kNamespace:
2504 : case Declarable::kGeneric:
2505 : return;
2506 : }
2507 : }
2508 :
2509 1 : void ImplementationVisitor::GenerateBuiltinDefinitions(std::string& file_name) {
2510 1 : std::stringstream new_contents_stream;
2511 : new_contents_stream
2512 : << "#ifndef V8_BUILTINS_BUILTIN_DEFINITIONS_FROM_DSL_H_\n"
2513 : "#define V8_BUILTINS_BUILTIN_DEFINITIONS_FROM_DSL_H_\n"
2514 : "\n"
2515 : "#define BUILTIN_LIST_FROM_DSL(CPP, API, TFJ, TFC, TFS, TFH, ASM) "
2516 1 : "\\\n";
2517 6204 : for (auto& declarable : GlobalContext::AllDeclarables()) {
2518 190 : Builtin* builtin = Builtin::DynamicCast(declarable.get());
2519 6352 : if (!builtin || builtin->IsExternal()) continue;
2520 : int firstParameterIndex = 1;
2521 : bool declareParameters = true;
2522 132 : if (builtin->IsStub()) {
2523 74 : new_contents_stream << "TFS(" << builtin->ExternalName();
2524 : } else {
2525 58 : new_contents_stream << "TFJ(" << builtin->ExternalName();
2526 58 : if (builtin->IsVarArgsJavaScript()) {
2527 : new_contents_stream
2528 48 : << ", SharedFunctionInfo::kDontAdaptArgumentsSentinel";
2529 : declareParameters = false;
2530 : } else {
2531 : assert(builtin->IsFixedArgsJavaScript());
2532 : // FixedArg javascript builtins need to offer the parameter
2533 : // count.
2534 : assert(builtin->parameter_names().size() >= 2);
2535 20 : new_contents_stream << ", " << (builtin->parameter_names().size() - 2);
2536 : // And the receiver is explicitly declared.
2537 10 : new_contents_stream << ", kReceiver";
2538 : firstParameterIndex = 2;
2539 : }
2540 : }
2541 132 : if (declareParameters) {
2542 : int index = 0;
2543 532 : for (const auto& parameter : builtin->parameter_names()) {
2544 364 : if (index >= firstParameterIndex) {
2545 540 : new_contents_stream << ", k" << CamelifyString(parameter);
2546 : }
2547 364 : index++;
2548 : }
2549 : }
2550 132 : new_contents_stream << ") \\\n";
2551 : }
2552 1 : new_contents_stream << "\n";
2553 :
2554 : new_contents_stream
2555 1 : << "#define TORQUE_FUNCTION_POINTER_TYPE_TO_BUILTIN_MAP(V) \\\n";
2556 22 : for (const BuiltinPointerType* type : TypeOracle::AllBuiltinPointerTypes()) {
2557 : Builtin* example_builtin =
2558 10 : Declarations::FindSomeInternalBuiltinWithType(type);
2559 10 : if (!example_builtin) {
2560 : CurrentSourcePosition::Scope current_source_position(
2561 0 : SourcePosition{CurrentSourceFile::Get(), {-1, -1}, {-1, -1}});
2562 0 : ReportError("unable to find any builtin with type \"", *type, "\"");
2563 : }
2564 20 : new_contents_stream << " V(" << type->function_pointer_type_id() << ","
2565 10 : << example_builtin->ExternalName() << ")\\\n";
2566 : }
2567 1 : new_contents_stream << "\n";
2568 :
2569 : new_contents_stream
2570 1 : << "#endif // V8_BUILTINS_BUILTIN_DEFINITIONS_FROM_DSL_H_\n";
2571 :
2572 : std::string new_contents(new_contents_stream.str());
2573 2 : ReplaceFileContentsIfDifferent(file_name, new_contents);
2574 1 : }
2575 :
2576 : namespace {
2577 :
2578 : enum class FieldSectionType {
2579 : kNoSection = 0,
2580 : kWeakSection,
2581 : kStrongSection,
2582 : kScalarSection
2583 : };
2584 :
2585 38 : void PossiblyStartTagged(FieldSectionType* section,
2586 : std::set<FieldSectionType>* completed_sections,
2587 : std::stringstream* o) {
2588 107 : if (completed_sections->count(FieldSectionType::kWeakSection) == 0 &&
2589 88 : completed_sections->count(FieldSectionType::kStrongSection) == 0 &&
2590 76 : *section != FieldSectionType::kWeakSection &&
2591 : *section != FieldSectionType::kStrongSection) {
2592 19 : *o << "V(kStartOfPointerFieldsOffset, 0) \\\n";
2593 : }
2594 38 : }
2595 :
2596 38 : void PossiblyEndTagged(FieldSectionType* section,
2597 : std::set<FieldSectionType>* completed_sections,
2598 : std::stringstream* o) {
2599 140 : if (completed_sections->count(FieldSectionType::kWeakSection) != 0 &&
2600 64 : completed_sections->count(FieldSectionType::kStrongSection) != 0) {
2601 19 : *o << "V(kEndOfTaggedFieldsOffset, 0) \\\n";
2602 : }
2603 38 : }
2604 :
2605 125 : void ProcessFieldInSection(FieldSectionType* section,
2606 : std::set<FieldSectionType>* completed_sections,
2607 : FieldSectionType field_section,
2608 : std::stringstream* o) {
2609 125 : if (*section != FieldSectionType::kNoSection) {
2610 83 : if (*section != field_section) {
2611 44 : if (completed_sections->count(field_section) != 0) {
2612 0 : ReportError("reopening of weak, strong or scalar field section");
2613 : }
2614 : completed_sections->insert(*section);
2615 44 : if (*section == FieldSectionType::kWeakSection) {
2616 19 : *o << "V(kEndOfWeakFieldsOffset, 0) \\\n";
2617 19 : PossiblyEndTagged(section, completed_sections, o);
2618 25 : } else if (*section == FieldSectionType::kStrongSection) {
2619 19 : *o << "V(kEndOfStrongFieldsOffset, 0) \\\n";
2620 19 : PossiblyEndTagged(section, completed_sections, o);
2621 : }
2622 : }
2623 : }
2624 125 : if (*section != field_section) {
2625 84 : if (field_section == FieldSectionType::kWeakSection) {
2626 19 : PossiblyStartTagged(section, completed_sections, o);
2627 19 : *o << "V(kStartOfWeakFieldsOffset, 0) \\\n";
2628 65 : } else if (field_section == FieldSectionType::kStrongSection) {
2629 19 : PossiblyStartTagged(section, completed_sections, o);
2630 19 : *o << "V(kStartOfStrongFieldsOffset, 0) \\\n";
2631 : }
2632 : }
2633 125 : *section = field_section;
2634 125 : }
2635 :
2636 38 : void CompleteFieldSection(FieldSectionType* section,
2637 : std::set<FieldSectionType>* completed_sections,
2638 : FieldSectionType field_section,
2639 : std::stringstream* o) {
2640 38 : if (completed_sections->count(field_section) == 0) {
2641 23 : ProcessFieldInSection(section, completed_sections, field_section, o);
2642 : ProcessFieldInSection(section, completed_sections,
2643 23 : FieldSectionType::kNoSection, o);
2644 : }
2645 38 : }
2646 :
2647 : } // namespace
2648 :
2649 1 : void ImplementationVisitor::GenerateClassDefinitions(std::string& file_name) {
2650 1 : std::stringstream new_contents_stream;
2651 : new_contents_stream << "#ifndef V8_CLASS_BUILTIN_DEFINITIONS_FROM_DSL_H_\n"
2652 : "#define V8_CLASS_BUILTIN_DEFINITIONS_FROM_DSL_H_\n"
2653 1 : "\n\n";
2654 :
2655 21 : for (auto i : GlobalContext::GetClasses()) {
2656 : // TODO(danno): Ideally (and we've got several core V8 dev's feedback
2657 : // supporting this), Torque should generate the constants for the offsets
2658 : // directly and not go through the existing layer of macros, which actually
2659 : // currently just serves to additionally obfuscate where these values come
2660 : // from.
2661 19 : new_contents_stream << "#define ";
2662 38 : new_contents_stream << CapifyStringWithUnderscores(i.first)
2663 19 : << "_FIELDS(V) \\\n";
2664 19 : ClassType* type = i.second;
2665 38 : std::vector<Field> fields = type->fields();
2666 19 : FieldSectionType section = FieldSectionType::kNoSection;
2667 : std::set<FieldSectionType> completed_sections;
2668 98 : for (auto f : fields) {
2669 60 : CurrentSourcePosition::Scope scope(f.pos);
2670 60 : if (f.name_and_type.type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
2671 37 : if (f.is_weak) {
2672 : ProcessFieldInSection(§ion, &completed_sections,
2673 : FieldSectionType::kWeakSection,
2674 3 : &new_contents_stream);
2675 : } else {
2676 : ProcessFieldInSection(§ion, &completed_sections,
2677 : FieldSectionType::kStrongSection,
2678 34 : &new_contents_stream);
2679 : }
2680 : } else {
2681 : ProcessFieldInSection(§ion, &completed_sections,
2682 : FieldSectionType::kScalarSection,
2683 23 : &new_contents_stream);
2684 : }
2685 : size_t field_size;
2686 : std::string size_string;
2687 : std::string machine_type;
2688 180 : std::tie(field_size, size_string, machine_type) =
2689 : f.GetFieldSizeInformation();
2690 120 : new_contents_stream << "V(k" << CamelifyString(f.name_and_type.name)
2691 120 : << "Offset, " << size_string << ") \\\n";
2692 : }
2693 :
2694 : ProcessFieldInSection(§ion, &completed_sections,
2695 19 : FieldSectionType::kNoSection, &new_contents_stream);
2696 : CompleteFieldSection(§ion, &completed_sections,
2697 19 : FieldSectionType::kWeakSection, &new_contents_stream);
2698 : CompleteFieldSection(§ion, &completed_sections,
2699 : FieldSectionType::kStrongSection,
2700 19 : &new_contents_stream);
2701 :
2702 19 : new_contents_stream << "V(kSize, 0) \\\n";
2703 19 : new_contents_stream << "\n";
2704 : }
2705 :
2706 : new_contents_stream
2707 1 : << "\n#endif // V8_CLASS_BUILTIN_DEFINITIONS_FROM_DSL_H_\n";
2708 :
2709 : std::string new_contents(new_contents_stream.str());
2710 2 : ReplaceFileContentsIfDifferent(file_name, new_contents);
2711 1 : }
2712 :
2713 : } // namespace torque
2714 : } // namespace internal
2715 9114 : } // namespace v8
|