1 // Copyright 2018 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/torque/csa-generator.h"
6 
7 #include "src/common/globals.h"
8 #include "src/torque/global-context.h"
9 #include "src/torque/type-oracle.h"
10 #include "src/torque/types.h"
11 #include "src/torque/utils.h"
12 
13 namespace v8 {
14 namespace internal {
15 namespace torque {
16 
EmitGraph(Stack<std::string> parameters)17 base::Optional<Stack<std::string>> CSAGenerator::EmitGraph(
18     Stack<std::string> parameters) {
19   for (BottomOffset i = {0}; i < parameters.AboveTop(); ++i) {
20     SetDefinitionVariable(DefinitionLocation::Parameter(i.offset),
21                           parameters.Peek(i));
22   }
23 
24   for (Block* block : cfg_.blocks()) {
25     if (block->IsDead()) continue;
26 
27     out() << "  compiler::CodeAssemblerParameterizedLabel<";
28     bool first = true;
29     DCHECK_EQ(block->InputTypes().Size(), block->InputDefinitions().Size());
30     for (BottomOffset i = {0}; i < block->InputTypes().AboveTop(); ++i) {
31       if (block->InputDefinitions().Peek(i).IsPhiFromBlock(block)) {
32         if (!first) out() << ", ";
33         out() << block->InputTypes().Peek(i)->GetGeneratedTNodeTypeName();
34         first = false;
35       }
36     }
37     out() << "> " << BlockName(block) << "(&ca_, compiler::CodeAssemblerLabel::"
38           << (block->IsDeferred() ? "kDeferred" : "kNonDeferred") << ");\n";
39   }
40 
41   EmitInstruction(GotoInstruction{cfg_.start()}, &parameters);
42   for (Block* block : cfg_.blocks()) {
43     if (cfg_.end() && *cfg_.end() == block) continue;
44     if (block->IsDead()) continue;
45     out() << "\n";
46 
47     // Redirect the output of non-declarations into a buffer and only output
48     // declarations right away.
49     std::stringstream out_buffer;
50     std::ostream* old_out = out_;
51     out_ = &out_buffer;
52 
53     out() << "  if (" << BlockName(block) << ".is_used()) {\n";
54     EmitBlock(block);
55     out() << "  }\n";
56 
57     // All declarations have been printed now, so we can append the buffered
58     // output and redirect back to the original output stream.
59     out_ = old_out;
60     out() << out_buffer.str();
61   }
62   if (cfg_.end()) {
63     out() << "\n";
64     return EmitBlock(*cfg_.end());
65   }
66   return base::nullopt;
67 }
68 
EmitBlock(const Block * block)69 Stack<std::string> CSAGenerator::EmitBlock(const Block* block) {
70   Stack<std::string> stack;
71   std::stringstream phi_names;
72 
73   for (BottomOffset i = {0}; i < block->InputTypes().AboveTop(); ++i) {
74     const auto& def = block->InputDefinitions().Peek(i);
75     stack.Push(DefinitionToVariable(def));
76     if (def.IsPhiFromBlock(block)) {
77       decls() << "  TNode<"
78               << block->InputTypes().Peek(i)->GetGeneratedTNodeTypeName()
79               << "> " << stack.Top() << ";\n";
80       phi_names << ", &" << stack.Top();
81     }
82   }
83   out() << "    ca_.Bind(&" << BlockName(block) << phi_names.str() << ");\n";
84 
85   for (const Instruction& instruction : block->instructions()) {
86     TorqueCodeGenerator::EmitInstruction(instruction, &stack);
87   }
88   return stack;
89 }
90 
EmitSourcePosition(SourcePosition pos,bool always_emit)91 void CSAGenerator::EmitSourcePosition(SourcePosition pos, bool always_emit) {
92   const std::string& file = SourceFileMap::AbsolutePath(pos.source);
93   if (always_emit || !previous_position_.CompareStartIgnoreColumn(pos)) {
94     // Lines in Torque SourcePositions are zero-based, while the
95     // CodeStubAssembler and downwind systems are one-based.
96     out() << "    ca_.SetSourcePosition(\"" << file << "\", "
97           << (pos.start.line + 1) << ");\n";
98     previous_position_ = pos;
99   }
100 }
101 
EmitInstruction(const PushUninitializedInstruction & instruction,Stack<std::string> * stack)102 void CSAGenerator::EmitInstruction(
103     const PushUninitializedInstruction& instruction,
104     Stack<std::string>* stack) {
105   // TODO(tebbi): This can trigger an error in CSA if it is used. Instead, we
106   // should prevent usage of uninitialized in the type system. This
107   // requires "if constexpr" being evaluated at Torque time.
108   const std::string str = "ca_.Uninitialized<" +
109                           instruction.type->GetGeneratedTNodeTypeName() + ">()";
110   stack->Push(str);
111   SetDefinitionVariable(instruction.GetValueDefinition(), str);
112 }
113 
EmitInstruction(const PushBuiltinPointerInstruction & instruction,Stack<std::string> * stack)114 void CSAGenerator::EmitInstruction(
115     const PushBuiltinPointerInstruction& instruction,
116     Stack<std::string>* stack) {
117   const std::string str =
118       "ca_.UncheckedCast<BuiltinPtr>(ca_.SmiConstant(Builtins::k" +
119       instruction.external_name + "))";
120   stack->Push(str);
121   SetDefinitionVariable(instruction.GetValueDefinition(), str);
122 }
123 
EmitInstruction(const NamespaceConstantInstruction & instruction,Stack<std::string> * stack)124 void CSAGenerator::EmitInstruction(
125     const NamespaceConstantInstruction& instruction,
126     Stack<std::string>* stack) {
127   const Type* type = instruction.constant->type();
128   std::vector<std::string> results;
129 
130   const auto lowered = LowerType(type);
131   for (std::size_t i = 0; i < lowered.size(); ++i) {
132     results.push_back(DefinitionToVariable(instruction.GetValueDefinition(i)));
133     stack->Push(results.back());
134     decls() << "  TNode<" << lowered[i]->GetGeneratedTNodeTypeName() << "> "
135             << stack->Top() << ";\n";
136   }
137 
138   out() << "    ";
139   if (type->StructSupertype()) {
140     out() << "std::tie(";
141     PrintCommaSeparatedList(out(), results);
142     out() << ") = ";
143   } else if (results.size() == 1) {
144     out() << results[0] << " = ";
145   }
146   out() << instruction.constant->external_name() << "(state_)";
147   if (type->StructSupertype()) {
148     out() << ".Flatten();\n";
149   } else {
150     out() << ";\n";
151   }
152 }
153 
ProcessArgumentsCommon(const TypeVector & parameter_types,std::vector<std::string> constexpr_arguments,Stack<std::string> * stack)154 std::vector<std::string> CSAGenerator::ProcessArgumentsCommon(
155     const TypeVector& parameter_types,
156     std::vector<std::string> constexpr_arguments, Stack<std::string>* stack) {
157   std::vector<std::string> args;
158   for (auto it = parameter_types.rbegin(); it != parameter_types.rend(); ++it) {
159     const Type* type = *it;
160     VisitResult arg;
161     if (type->IsConstexpr()) {
162       args.push_back(std::move(constexpr_arguments.back()));
163       constexpr_arguments.pop_back();
164     } else {
165       std::stringstream s;
166       size_t slot_count = LoweredSlotCount(type);
167       VisitResult arg = VisitResult(type, stack->TopRange(slot_count));
168       EmitCSAValue(arg, *stack, s);
169       args.push_back(s.str());
170       stack->PopMany(slot_count);
171     }
172   }
173   std::reverse(args.begin(), args.end());
174   return args;
175 }
176 
EmitInstruction(const CallIntrinsicInstruction & instruction,Stack<std::string> * stack)177 void CSAGenerator::EmitInstruction(const CallIntrinsicInstruction& instruction,
178                                    Stack<std::string>* stack) {
179   TypeVector parameter_types =
180       instruction.intrinsic->signature().parameter_types.types;
181   std::vector<std::string> args = ProcessArgumentsCommon(
182       parameter_types, instruction.constexpr_arguments, stack);
183 
184   Stack<std::string> pre_call_stack = *stack;
185   const Type* return_type = instruction.intrinsic->signature().return_type;
186   std::vector<std::string> results;
187 
188   const auto lowered = LowerType(return_type);
189   for (std::size_t i = 0; i < lowered.size(); ++i) {
190     results.push_back(DefinitionToVariable(instruction.GetValueDefinition(i)));
191     stack->Push(results.back());
192     decls() << "  TNode<" << lowered[i]->GetGeneratedTNodeTypeName() << "> "
193             << stack->Top() << ";\n";
194   }
195 
196   out() << "    ";
197   if (return_type->StructSupertype()) {
198     out() << "std::tie(";
199     PrintCommaSeparatedList(out(), results);
200     out() << ") = ";
201   } else {
202     if (results.size() == 1) {
203       out() << results[0] << " = ";
204     }
205   }
206 
207   if (instruction.intrinsic->ExternalName() == "%RawDownCast") {
208     if (parameter_types.size() != 1) {
209       ReportError("%RawDownCast must take a single parameter");
210     }
211     const Type* original_type = parameter_types[0];
212     bool is_subtype =
213         return_type->IsSubtypeOf(original_type) ||
214         (original_type == TypeOracle::GetUninitializedHeapObjectType() &&
215          return_type->IsSubtypeOf(TypeOracle::GetHeapObjectType()));
216     if (!is_subtype) {
217       ReportError("%RawDownCast error: ", *return_type, " is not a subtype of ",
218                   *original_type);
219     }
220     if (!original_type->StructSupertype() &&
221         return_type->GetGeneratedTNodeTypeName() !=
222             original_type->GetGeneratedTNodeTypeName()) {
223       if (return_type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
224         out() << "TORQUE_CAST";
225       } else {
226         out() << "ca_.UncheckedCast<"
227               << return_type->GetGeneratedTNodeTypeName() << ">";
228       }
229     }
230   } else if (instruction.intrinsic->ExternalName() == "%GetClassMapConstant") {
231     if (parameter_types.size() != 0) {
232       ReportError("%GetClassMapConstant must not take parameters");
233     }
234     if (instruction.specialization_types.size() != 1) {
235       ReportError(
236           "%GetClassMapConstant must take a single class as specialization "
237           "parameter");
238     }
239     const ClassType* class_type =
240         ClassType::DynamicCast(instruction.specialization_types[0]);
241     if (!class_type) {
242       ReportError("%GetClassMapConstant must take a class type parameter");
243     }
244     // If the class isn't actually used as the parameter to a TNode,
245     // then we can't rely on the class existing in C++ or being of the same
246     // type (e.g. it could be a template), so don't use the template CSA
247     // machinery for accessing the class' map.
248     std::string class_name =
249         class_type->name() != class_type->GetGeneratedTNodeTypeName()
250             ? std::string("void")
251             : class_type->name();
252 
253     out() << std::string("CodeStubAssembler(state_).GetClassMapConstant<") +
254                  class_name + ">";
255   } else if (instruction.intrinsic->ExternalName() == "%FromConstexpr") {
256     if (parameter_types.size() != 1 || !parameter_types[0]->IsConstexpr()) {
257       ReportError(
258           "%FromConstexpr must take a single parameter with constexpr "
259           "type");
260     }
261     if (return_type->IsConstexpr()) {
262       ReportError("%FromConstexpr must return a non-constexpr type");
263     }
264     if (return_type->IsSubtypeOf(TypeOracle::GetSmiType())) {
265       out() << "ca_.SmiConstant";
266     } else if (return_type->IsSubtypeOf(TypeOracle::GetNumberType())) {
267       out() << "ca_.NumberConstant";
268     } else if (return_type->IsSubtypeOf(TypeOracle::GetStringType())) {
269       out() << "ca_.StringConstant";
270     } else if (return_type->IsSubtypeOf(TypeOracle::GetObjectType())) {
271       ReportError(
272           "%FromConstexpr cannot cast to subclass of HeapObject unless it's a "
273           "String or Number");
274     } else if (return_type->IsSubtypeOf(TypeOracle::GetIntPtrType())) {
275       out() << "ca_.IntPtrConstant";
276     } else if (return_type->IsSubtypeOf(TypeOracle::GetUIntPtrType())) {
277       out() << "ca_.UintPtrConstant";
278     } else if (return_type->IsSubtypeOf(TypeOracle::GetInt32Type())) {
279       out() << "ca_.Int32Constant";
280     } else if (return_type->IsSubtypeOf(TypeOracle::GetUint32Type())) {
281       out() << "ca_.Uint32Constant";
282     } else if (return_type->IsSubtypeOf(TypeOracle::GetBoolType())) {
283       out() << "ca_.BoolConstant";
284     } else {
285       std::stringstream s;
286       s << "%FromConstexpr does not support return type " << *return_type;
287       ReportError(s.str());
288     }
289     // Wrap the raw constexpr value in a static_cast to ensure that
290     // enums get properly casted to their backing integral value.
291     out() << "(CastToUnderlyingTypeIfEnum";
292   } else {
293     ReportError("no built in intrinsic with name " +
294                 instruction.intrinsic->ExternalName());
295   }
296 
297   out() << "(";
298   PrintCommaSeparatedList(out(), args);
299   if (instruction.intrinsic->ExternalName() == "%FromConstexpr") {
300     out() << ")";
301   }
302   if (return_type->StructSupertype()) {
303     out() << ").Flatten();\n";
304   } else {
305     out() << ");\n";
306   }
307 }
308 
EmitInstruction(const CallCsaMacroInstruction & instruction,Stack<std::string> * stack)309 void CSAGenerator::EmitInstruction(const CallCsaMacroInstruction& instruction,
310                                    Stack<std::string>* stack) {
311   TypeVector parameter_types =
312       instruction.macro->signature().parameter_types.types;
313   std::vector<std::string> args = ProcessArgumentsCommon(
314       parameter_types, instruction.constexpr_arguments, stack);
315 
316   Stack<std::string> pre_call_stack = *stack;
317   const Type* return_type = instruction.macro->signature().return_type;
318   std::vector<std::string> results;
319 
320   const auto lowered = LowerType(return_type);
321   for (std::size_t i = 0; i < lowered.size(); ++i) {
322     results.push_back(DefinitionToVariable(instruction.GetValueDefinition(i)));
323     stack->Push(results.back());
324     decls() << "  TNode<" << lowered[i]->GetGeneratedTNodeTypeName() << "> "
325             << stack->Top() << ";\n";
326   }
327 
328   std::string catch_name =
329       PreCallableExceptionPreparation(instruction.catch_block);
330   out() << "    ";
331   bool needs_flattening = return_type->StructSupertype().has_value();
332   if (needs_flattening) {
333     out() << "std::tie(";
334     PrintCommaSeparatedList(out(), results);
335     out() << ") = ";
336   } else {
337     if (results.size() == 1) {
338       out() << results[0] << " = ";
339     } else {
340       DCHECK_EQ(0, results.size());
341     }
342   }
343   if (ExternMacro* extern_macro = ExternMacro::DynamicCast(instruction.macro)) {
344     out() << extern_macro->external_assembler_name() << "(state_).";
345   } else {
346     args.insert(args.begin(), "state_");
347   }
348   out() << instruction.macro->ExternalName() << "(";
349   PrintCommaSeparatedList(out(), args);
350   if (needs_flattening) {
351     out() << ").Flatten();\n";
352   } else {
353     out() << ");\n";
354   }
355   PostCallableExceptionPreparation(catch_name, return_type,
356                                    instruction.catch_block, &pre_call_stack,
357                                    instruction.GetExceptionObjectDefinition());
358 }
359 
EmitInstruction(const CallCsaMacroAndBranchInstruction & instruction,Stack<std::string> * stack)360 void CSAGenerator::EmitInstruction(
361     const CallCsaMacroAndBranchInstruction& instruction,
362     Stack<std::string>* stack) {
363   TypeVector parameter_types =
364       instruction.macro->signature().parameter_types.types;
365   std::vector<std::string> args = ProcessArgumentsCommon(
366       parameter_types, instruction.constexpr_arguments, stack);
367 
368   Stack<std::string> pre_call_stack = *stack;
369   std::vector<std::string> results;
370   const Type* return_type = instruction.macro->signature().return_type;
371 
372   if (return_type != TypeOracle::GetNeverType()) {
373     const auto lowered = LowerType(return_type);
374     for (std::size_t i = 0; i < lowered.size(); ++i) {
375       results.push_back(
376           DefinitionToVariable(instruction.GetValueDefinition(i)));
377       decls() << "  TNode<" << lowered[i]->GetGeneratedTNodeTypeName() << "> "
378               << results.back() << ";\n";
379     }
380   }
381 
382   std::vector<std::string> label_names;
383   std::vector<std::vector<std::string>> var_names;
384   const LabelDeclarationVector& labels = instruction.macro->signature().labels;
385   DCHECK_EQ(labels.size(), instruction.label_blocks.size());
386   for (size_t i = 0; i < labels.size(); ++i) {
387     TypeVector label_parameters = labels[i].types;
388     label_names.push_back(FreshLabelName());
389     var_names.push_back({});
390     for (size_t j = 0; j < label_parameters.size(); ++j) {
391       var_names[i].push_back(FreshNodeName());
392       const auto def = instruction.GetLabelValueDefinition(i, j);
393       SetDefinitionVariable(def, var_names[i].back() + ".value()");
394       decls() << "    compiler::TypedCodeAssemblerVariable<"
395               << label_parameters[j]->GetGeneratedTNodeTypeName() << "> "
396               << var_names[i][j] << "(&ca_);\n";
397     }
398     out() << "    compiler::CodeAssemblerLabel " << label_names[i]
399           << "(&ca_);\n";
400   }
401 
402   std::string catch_name =
403       PreCallableExceptionPreparation(instruction.catch_block);
404   out() << "    ";
405   if (results.size() == 1) {
406     out() << results[0] << " = ";
407   } else if (results.size() > 1) {
408     out() << "std::tie(";
409     PrintCommaSeparatedList(out(), results);
410     out() << ") = ";
411   }
412   if (ExternMacro* extern_macro = ExternMacro::DynamicCast(instruction.macro)) {
413     out() << extern_macro->external_assembler_name() << "(state_).";
414   } else {
415     args.insert(args.begin(), "state_");
416   }
417   out() << instruction.macro->ExternalName() << "(";
418   PrintCommaSeparatedList(out(), args);
419   bool first = args.empty();
420   for (size_t i = 0; i < label_names.size(); ++i) {
421     if (!first) out() << ", ";
422     out() << "&" << label_names[i];
423     first = false;
424     for (size_t j = 0; j < var_names[i].size(); ++j) {
425       out() << ", &" << var_names[i][j];
426     }
427   }
428   if (return_type->StructSupertype()) {
429     out() << ").Flatten();\n";
430   } else {
431     out() << ");\n";
432   }
433 
434   PostCallableExceptionPreparation(catch_name, return_type,
435                                    instruction.catch_block, &pre_call_stack,
436                                    instruction.GetExceptionObjectDefinition());
437 
438   if (instruction.return_continuation) {
439     out() << "    ca_.Goto(&" << BlockName(*instruction.return_continuation);
440     DCHECK_EQ(stack->Size() + results.size(),
441               (*instruction.return_continuation)->InputDefinitions().Size());
442 
443     const auto& input_definitions =
444         (*instruction.return_continuation)->InputDefinitions();
445     for (BottomOffset i = {0}; i < input_definitions.AboveTop(); ++i) {
446       if (input_definitions.Peek(i).IsPhiFromBlock(
447               *instruction.return_continuation)) {
448         out() << ", "
449               << (i < stack->AboveTop() ? stack->Peek(i) : results[i.offset]);
450       }
451     }
452     out() << ");\n";
453   }
454   for (size_t l = 0; l < label_names.size(); ++l) {
455     out() << "    if (" << label_names[l] << ".is_used()) {\n";
456     out() << "      ca_.Bind(&" << label_names[l] << ");\n";
457     out() << "      ca_.Goto(&" << BlockName(instruction.label_blocks[l]);
458     DCHECK_EQ(stack->Size() + var_names[l].size(),
459               instruction.label_blocks[l]->InputDefinitions().Size());
460 
461     const auto& label_definitions =
462         instruction.label_blocks[l]->InputDefinitions();
463 
464     BottomOffset i = {0};
465     for (; i < stack->AboveTop(); ++i) {
466       if (label_definitions.Peek(i).IsPhiFromBlock(
467               instruction.label_blocks[l])) {
468         out() << ", " << stack->Peek(i);
469       }
470     }
471     for (std::size_t k = 0; k < var_names[l].size(); ++k, ++i) {
472       if (label_definitions.Peek(i).IsPhiFromBlock(
473               instruction.label_blocks[l])) {
474         out() << ", " << var_names[l][k] << ".value()";
475       }
476     }
477     out() << ");\n";
478     out() << "    }\n";
479   }
480 }
481 
EmitInstruction(const CallBuiltinInstruction & instruction,Stack<std::string> * stack)482 void CSAGenerator::EmitInstruction(const CallBuiltinInstruction& instruction,
483                                    Stack<std::string>* stack) {
484   std::vector<std::string> arguments = stack->PopMany(instruction.argc);
485   std::vector<const Type*> result_types =
486       LowerType(instruction.builtin->signature().return_type);
487   if (instruction.is_tailcall) {
488     out() << "   CodeStubAssembler(state_).TailCallBuiltin(Builtins::k"
489           << instruction.builtin->ExternalName();
490     if (!instruction.builtin->signature().HasContextParameter()) {
491       // Add dummy context parameter to satisfy the TailCallBuiltin signature.
492       out() << ", TNode<Object>()";
493     }
494     for (const std::string& argument : arguments) {
495       out() << ", " << argument;
496     }
497     out() << ");\n";
498   } else {
499     std::string result_name;
500     if (result_types.size() == 1) {
501       result_name = DefinitionToVariable(instruction.GetValueDefinition(0));
502       decls() << "  TNode<" << result_types[0]->GetGeneratedTNodeTypeName()
503               << "> " << result_name << ";\n";
504     }
505     std::string catch_name =
506         PreCallableExceptionPreparation(instruction.catch_block);
507     Stack<std::string> pre_call_stack = *stack;
508 
509     DCHECK_EQ(1, result_types.size());
510     std::string generated_type = result_types[0]->GetGeneratedTNodeTypeName();
511     stack->Push(result_name);
512     out() << "    " << result_name << " = ";
513     if (generated_type != "Object") out() << "TORQUE_CAST(";
514     out() << "CodeStubAssembler(state_).CallBuiltin(Builtins::k"
515           << instruction.builtin->ExternalName();
516     if (!instruction.builtin->signature().HasContextParameter()) {
517       // Add dummy context parameter to satisfy the CallBuiltin signature.
518       out() << ", TNode<Object>()";
519     }
520     for (const std::string& argument : arguments) {
521       out() << ", " << argument;
522     }
523     if (generated_type != "Object") out() << ")";
524     out() << ");\n";
525 
526     PostCallableExceptionPreparation(
527         catch_name,
528         result_types.size() == 0 ? TypeOracle::GetVoidType() : result_types[0],
529         instruction.catch_block, &pre_call_stack,
530         instruction.GetExceptionObjectDefinition());
531   }
532 }
533 
EmitInstruction(const CallBuiltinPointerInstruction & instruction,Stack<std::string> * stack)534 void CSAGenerator::EmitInstruction(
535     const CallBuiltinPointerInstruction& instruction,
536     Stack<std::string>* stack) {
537   std::vector<std::string> arguments = stack->PopMany(instruction.argc);
538   std::string function = stack->Pop();
539   std::vector<const Type*> result_types =
540       LowerType(instruction.type->return_type());
541   if (result_types.size() != 1) {
542     ReportError("builtins must have exactly one result");
543   }
544   if (instruction.is_tailcall) {
545     ReportError("tail-calls to builtin pointers are not supported");
546   }
547 
548   DCHECK_EQ(1, instruction.GetValueDefinitionCount());
549   stack->Push(DefinitionToVariable(instruction.GetValueDefinition(0)));
550   std::string generated_type = result_types[0]->GetGeneratedTNodeTypeName();
551   decls() << "  TNode<" << generated_type << "> " << stack->Top() << ";\n";
552   out() << stack->Top() << " = ";
553   if (generated_type != "Object") out() << "TORQUE_CAST(";
554   out() << "CodeStubAssembler(state_).CallBuiltinPointer(Builtins::"
555            "CallableFor(ca_."
556            "isolate(),"
557            "ExampleBuiltinForTorqueFunctionPointerType("
558         << instruction.type->function_pointer_type_id() << ")).descriptor(), "
559         << function;
560   if (!instruction.type->HasContextParameter()) {
561     // Add dummy context parameter to satisfy the CallBuiltinPointer signature.
562     out() << ", TNode<Object>()";
563   }
564   for (const std::string& argument : arguments) {
565     out() << ", " << argument;
566   }
567   out() << ")";
568   if (generated_type != "Object") out() << ")";
569   out() << ";\n";
570 }
571 
PreCallableExceptionPreparation(base::Optional<Block * > catch_block)572 std::string CSAGenerator::PreCallableExceptionPreparation(
573     base::Optional<Block*> catch_block) {
574   std::string catch_name;
575   if (catch_block) {
576     catch_name = FreshCatchName();
577     out() << "    compiler::CodeAssemblerExceptionHandlerLabel " << catch_name
578           << "__label(&ca_, compiler::CodeAssemblerLabel::kDeferred);\n";
579     out() << "    { compiler::ScopedExceptionHandler s(&ca_, &" << catch_name
580           << "__label);\n";
581   }
582   return catch_name;
583 }
584 
PostCallableExceptionPreparation(const std::string & catch_name,const Type * return_type,base::Optional<Block * > catch_block,Stack<std::string> * stack,const base::Optional<DefinitionLocation> & exception_object_definition)585 void CSAGenerator::PostCallableExceptionPreparation(
586     const std::string& catch_name, const Type* return_type,
587     base::Optional<Block*> catch_block, Stack<std::string>* stack,
588     const base::Optional<DefinitionLocation>& exception_object_definition) {
589   if (catch_block) {
590     DCHECK(exception_object_definition);
591     std::string block_name = BlockName(*catch_block);
592     out() << "    }\n";
593     out() << "    if (" << catch_name << "__label.is_used()) {\n";
594     out() << "      compiler::CodeAssemblerLabel " << catch_name
595           << "_skip(&ca_);\n";
596     if (!return_type->IsNever()) {
597       out() << "      ca_.Goto(&" << catch_name << "_skip);\n";
598     }
599     decls() << "      TNode<Object> "
600             << DefinitionToVariable(*exception_object_definition) << ";\n";
601     out() << "      ca_.Bind(&" << catch_name << "__label, &"
602           << DefinitionToVariable(*exception_object_definition) << ");\n";
603     out() << "      ca_.Goto(&" << block_name;
604 
605     DCHECK_EQ(stack->Size() + 1, (*catch_block)->InputDefinitions().Size());
606     const auto& input_definitions = (*catch_block)->InputDefinitions();
607     for (BottomOffset i = {0}; i < input_definitions.AboveTop(); ++i) {
608       if (input_definitions.Peek(i).IsPhiFromBlock(*catch_block)) {
609         if (i < stack->AboveTop()) {
610           out() << ", " << stack->Peek(i);
611         } else {
612           DCHECK_EQ(i, stack->AboveTop());
613           out() << ", " << DefinitionToVariable(*exception_object_definition);
614         }
615       }
616     }
617     out() << ");\n";
618 
619     if (!return_type->IsNever()) {
620       out() << "      ca_.Bind(&" << catch_name << "_skip);\n";
621     }
622     out() << "    }\n";
623   }
624 }
625 
EmitInstruction(const CallRuntimeInstruction & instruction,Stack<std::string> * stack)626 void CSAGenerator::EmitInstruction(const CallRuntimeInstruction& instruction,
627                                    Stack<std::string>* stack) {
628   std::vector<std::string> arguments = stack->PopMany(instruction.argc);
629   const Type* return_type =
630       instruction.runtime_function->signature().return_type;
631   std::vector<const Type*> result_types;
632   if (return_type != TypeOracle::GetNeverType()) {
633     result_types = LowerType(return_type);
634   }
635   if (result_types.size() > 1) {
636     ReportError("runtime function must have at most one result");
637   }
638   if (instruction.is_tailcall) {
639     out() << "    CodeStubAssembler(state_).TailCallRuntime(Runtime::k"
640           << instruction.runtime_function->ExternalName() << ", ";
641     PrintCommaSeparatedList(out(), arguments);
642     out() << ");\n";
643   } else {
644     std::string result_name;
645     if (result_types.size() == 1) {
646       result_name = DefinitionToVariable(instruction.GetValueDefinition(0));
647       decls() << "  TNode<" << result_types[0]->GetGeneratedTNodeTypeName()
648               << "> " << result_name << ";\n";
649     }
650     std::string catch_name =
651         PreCallableExceptionPreparation(instruction.catch_block);
652     Stack<std::string> pre_call_stack = *stack;
653     if (result_types.size() == 1) {
654       std::string generated_type = result_types[0]->GetGeneratedTNodeTypeName();
655       stack->Push(result_name);
656       out() << "    " << result_name << " = ";
657       if (generated_type != "Object") out() << "TORQUE_CAST(";
658       out() << "CodeStubAssembler(state_).CallRuntime(Runtime::k"
659             << instruction.runtime_function->ExternalName() << ", ";
660       PrintCommaSeparatedList(out(), arguments);
661       out() << ")";
662       if (generated_type != "Object") out() << ")";
663       out() << "; \n";
664     } else {
665       DCHECK_EQ(0, result_types.size());
666       out() << "    CodeStubAssembler(state_).CallRuntime(Runtime::k"
667             << instruction.runtime_function->ExternalName() << ", ";
668       PrintCommaSeparatedList(out(), arguments);
669       out() << ");\n";
670       if (return_type == TypeOracle::GetNeverType()) {
671         out() << "    CodeStubAssembler(state_).Unreachable();\n";
672       } else {
673         DCHECK(return_type == TypeOracle::GetVoidType());
674       }
675     }
676     PostCallableExceptionPreparation(
677         catch_name, return_type, instruction.catch_block, &pre_call_stack,
678         instruction.GetExceptionObjectDefinition());
679   }
680 }
681 
EmitInstruction(const BranchInstruction & instruction,Stack<std::string> * stack)682 void CSAGenerator::EmitInstruction(const BranchInstruction& instruction,
683                                    Stack<std::string>* stack) {
684   out() << "    ca_.Branch(" << stack->Pop() << ", &"
685         << BlockName(instruction.if_true) << ", std::vector<Node*>{";
686 
687   const auto& true_definitions = instruction.if_true->InputDefinitions();
688   DCHECK_EQ(stack->Size(), true_definitions.Size());
689   bool first = true;
690   for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) {
691     if (true_definitions.Peek(i).IsPhiFromBlock(instruction.if_true)) {
692       if (!first) out() << ", ";
693       out() << stack->Peek(i);
694       first = false;
695     }
696   }
697 
698   out() << "}, &" << BlockName(instruction.if_false) << ", std::vector<Node*>{";
699 
700   const auto& false_definitions = instruction.if_false->InputDefinitions();
701   DCHECK_EQ(stack->Size(), false_definitions.Size());
702   first = true;
703   for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) {
704     if (false_definitions.Peek(i).IsPhiFromBlock(instruction.if_false)) {
705       if (!first) out() << ", ";
706       out() << stack->Peek(i);
707       first = false;
708     }
709   }
710 
711   out() << "});\n";
712 }
713 
EmitInstruction(const ConstexprBranchInstruction & instruction,Stack<std::string> * stack)714 void CSAGenerator::EmitInstruction(
715     const ConstexprBranchInstruction& instruction, Stack<std::string>* stack) {
716   out() << "    if ((" << instruction.condition << ")) {\n";
717   out() << "      ca_.Goto(&" << BlockName(instruction.if_true);
718 
719   const auto& true_definitions = instruction.if_true->InputDefinitions();
720   DCHECK_EQ(stack->Size(), true_definitions.Size());
721   for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) {
722     if (true_definitions.Peek(i).IsPhiFromBlock(instruction.if_true)) {
723       out() << ", " << stack->Peek(i);
724     }
725   }
726 
727   out() << ");\n";
728   out() << "    } else {\n";
729   out() << "      ca_.Goto(&" << BlockName(instruction.if_false);
730 
731   const auto& false_definitions = instruction.if_false->InputDefinitions();
732   DCHECK_EQ(stack->Size(), false_definitions.Size());
733   for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) {
734     if (false_definitions.Peek(i).IsPhiFromBlock(instruction.if_false)) {
735       out() << ", " << stack->Peek(i);
736     }
737   }
738 
739   out() << ");\n";
740   out() << "    }\n";
741 }
742 
EmitInstruction(const GotoInstruction & instruction,Stack<std::string> * stack)743 void CSAGenerator::EmitInstruction(const GotoInstruction& instruction,
744                                    Stack<std::string>* stack) {
745   out() << "    ca_.Goto(&" << BlockName(instruction.destination);
746   const auto& destination_definitions =
747       instruction.destination->InputDefinitions();
748   DCHECK_EQ(stack->Size(), destination_definitions.Size());
749   for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) {
750     if (destination_definitions.Peek(i).IsPhiFromBlock(
751             instruction.destination)) {
752       out() << ", " << stack->Peek(i);
753     }
754   }
755   out() << ");\n";
756 }
757 
EmitInstruction(const GotoExternalInstruction & instruction,Stack<std::string> * stack)758 void CSAGenerator::EmitInstruction(const GotoExternalInstruction& instruction,
759                                    Stack<std::string>* stack) {
760   for (auto it = instruction.variable_names.rbegin();
761        it != instruction.variable_names.rend(); ++it) {
762     out() << "    *" << *it << " = " << stack->Pop() << ";\n";
763   }
764   out() << "    ca_.Goto(" << instruction.destination << ");\n";
765 }
766 
EmitInstruction(const ReturnInstruction & instruction,Stack<std::string> * stack)767 void CSAGenerator::EmitInstruction(const ReturnInstruction& instruction,
768                                    Stack<std::string>* stack) {
769   if (*linkage_ == Builtin::kVarArgsJavaScript) {
770     out() << "    " << ARGUMENTS_VARIABLE_STRING << ".PopAndReturn(";
771   } else {
772     out() << "    CodeStubAssembler(state_).Return(";
773   }
774   out() << stack->Pop() << ");\n";
775 }
776 
EmitInstruction(const PrintConstantStringInstruction & instruction,Stack<std::string> * stack)777 void CSAGenerator::EmitInstruction(
778     const PrintConstantStringInstruction& instruction,
779     Stack<std::string>* stack) {
780   out() << "    CodeStubAssembler(state_).Print("
781         << StringLiteralQuote(instruction.message) << ");\n";
782 }
783 
EmitInstruction(const AbortInstruction & instruction,Stack<std::string> * stack)784 void CSAGenerator::EmitInstruction(const AbortInstruction& instruction,
785                                    Stack<std::string>* stack) {
786   switch (instruction.kind) {
787     case AbortInstruction::Kind::kUnreachable:
788       DCHECK(instruction.message.empty());
789       out() << "    CodeStubAssembler(state_).Unreachable();\n";
790       break;
791     case AbortInstruction::Kind::kDebugBreak:
792       DCHECK(instruction.message.empty());
793       out() << "    CodeStubAssembler(state_).DebugBreak();\n";
794       break;
795     case AbortInstruction::Kind::kAssertionFailure: {
796       std::string file = StringLiteralQuote(
797           SourceFileMap::PathFromV8Root(instruction.pos.source));
798       out() << "    {\n";
799       out() << "      auto pos_stack = ca_.GetMacroSourcePositionStack();\n";
800       out() << "      pos_stack.push_back({" << file << ", "
801             << instruction.pos.start.line + 1 << "});\n";
802       out() << "      CodeStubAssembler(state_).FailAssert("
803             << StringLiteralQuote(instruction.message) << ", pos_stack);\n";
804       out() << "    }\n";
805       break;
806     }
807   }
808 }
809 
EmitInstruction(const UnsafeCastInstruction & instruction,Stack<std::string> * stack)810 void CSAGenerator::EmitInstruction(const UnsafeCastInstruction& instruction,
811                                    Stack<std::string>* stack) {
812   const std::string str =
813       "ca_.UncheckedCast<" +
814       instruction.destination_type->GetGeneratedTNodeTypeName() + ">(" +
815       stack->Top() + ")";
816   stack->Poke(stack->AboveTop() - 1, str);
817   SetDefinitionVariable(instruction.GetValueDefinition(), str);
818 }
819 
EmitInstruction(const LoadReferenceInstruction & instruction,Stack<std::string> * stack)820 void CSAGenerator::EmitInstruction(const LoadReferenceInstruction& instruction,
821                                    Stack<std::string>* stack) {
822   std::string result_name =
823       DefinitionToVariable(instruction.GetValueDefinition());
824 
825   std::string offset = stack->Pop();
826   std::string object = stack->Pop();
827   stack->Push(result_name);
828 
829   decls() << "  " << instruction.type->GetGeneratedTypeName() << " "
830           << result_name << ";\n";
831   out() << "    " << result_name
832         << " = CodeStubAssembler(state_).LoadReference<"
833         << instruction.type->GetGeneratedTNodeTypeName()
834         << ">(CodeStubAssembler::Reference{" << object << ", " << offset
835         << "});\n";
836 }
837 
EmitInstruction(const StoreReferenceInstruction & instruction,Stack<std::string> * stack)838 void CSAGenerator::EmitInstruction(const StoreReferenceInstruction& instruction,
839                                    Stack<std::string>* stack) {
840   std::string value = stack->Pop();
841   std::string offset = stack->Pop();
842   std::string object = stack->Pop();
843 
844   out() << "    CodeStubAssembler(state_).StoreReference<"
845         << instruction.type->GetGeneratedTNodeTypeName()
846         << ">(CodeStubAssembler::"
847            "Reference{"
848         << object << ", " << offset << "}, " << value << ");\n";
849 }
850 
851 namespace {
GetBitFieldSpecialization(const Type * container,const BitField & field)852 std::string GetBitFieldSpecialization(const Type* container,
853                                       const BitField& field) {
854   auto smi_tagged_type =
855       Type::MatchUnaryGeneric(container, TypeOracle::GetSmiTaggedGeneric());
856   std::string container_type = smi_tagged_type
857                                    ? "uintptr_t"
858                                    : container->GetConstexprGeneratedTypeName();
859   int offset = smi_tagged_type
860                    ? field.offset + TargetArchitecture::SmiTagAndShiftSize()
861                    : field.offset;
862   std::stringstream stream;
863   stream << "base::BitField<"
864          << field.name_and_type.type->GetConstexprGeneratedTypeName() << ", "
865          << offset << ", " << field.num_bits << ", " << container_type << ">";
866   return stream.str();
867 }
868 }  // namespace
869 
EmitInstruction(const LoadBitFieldInstruction & instruction,Stack<std::string> * stack)870 void CSAGenerator::EmitInstruction(const LoadBitFieldInstruction& instruction,
871                                    Stack<std::string>* stack) {
872   std::string result_name =
873       DefinitionToVariable(instruction.GetValueDefinition());
874 
875   std::string bit_field_struct = stack->Pop();
876   stack->Push(result_name);
877 
878   const Type* struct_type = instruction.bit_field_struct_type;
879   const Type* field_type = instruction.bit_field.name_and_type.type;
880   auto smi_tagged_type =
881       Type::MatchUnaryGeneric(struct_type, TypeOracle::GetSmiTaggedGeneric());
882   bool struct_is_pointer_size =
883       IsPointerSizeIntegralType(struct_type) || smi_tagged_type;
884   DCHECK_IMPLIES(!struct_is_pointer_size, Is32BitIntegralType(struct_type));
885   bool field_is_pointer_size = IsPointerSizeIntegralType(field_type);
886   DCHECK_IMPLIES(!field_is_pointer_size, Is32BitIntegralType(field_type));
887   std::string struct_word_type = struct_is_pointer_size ? "WordT" : "Word32T";
888   std::string decoder =
889       struct_is_pointer_size
890           ? (field_is_pointer_size ? "DecodeWord" : "DecodeWord32FromWord")
891           : (field_is_pointer_size ? "DecodeWordFromWord32" : "DecodeWord32");
892 
893   decls() << "  " << field_type->GetGeneratedTypeName() << " " << result_name
894           << ";\n";
895 
896   if (smi_tagged_type) {
897     // If the container is a SMI, then UncheckedCast is insufficient and we must
898     // use a bit cast.
899     bit_field_struct =
900         "ca_.BitcastTaggedToWordForTagAndSmiBits(" + bit_field_struct + ")";
901   }
902 
903   out() << "    " << result_name << " = ca_.UncheckedCast<"
904         << field_type->GetGeneratedTNodeTypeName()
905         << ">(CodeStubAssembler(state_)." << decoder << "<"
906         << GetBitFieldSpecialization(struct_type, instruction.bit_field)
907         << ">(ca_.UncheckedCast<" << struct_word_type << ">("
908         << bit_field_struct << ")));\n";
909 }
910 
EmitInstruction(const StoreBitFieldInstruction & instruction,Stack<std::string> * stack)911 void CSAGenerator::EmitInstruction(const StoreBitFieldInstruction& instruction,
912                                    Stack<std::string>* stack) {
913   std::string result_name =
914       DefinitionToVariable(instruction.GetValueDefinition());
915 
916   std::string value = stack->Pop();
917   std::string bit_field_struct = stack->Pop();
918   stack->Push(result_name);
919 
920   const Type* struct_type = instruction.bit_field_struct_type;
921   const Type* field_type = instruction.bit_field.name_and_type.type;
922   auto smi_tagged_type =
923       Type::MatchUnaryGeneric(struct_type, TypeOracle::GetSmiTaggedGeneric());
924   bool struct_is_pointer_size =
925       IsPointerSizeIntegralType(struct_type) || smi_tagged_type;
926   DCHECK_IMPLIES(!struct_is_pointer_size, Is32BitIntegralType(struct_type));
927   bool field_is_pointer_size = IsPointerSizeIntegralType(field_type);
928   DCHECK_IMPLIES(!field_is_pointer_size, Is32BitIntegralType(field_type));
929   std::string struct_word_type = struct_is_pointer_size ? "WordT" : "Word32T";
930   std::string field_word_type = field_is_pointer_size ? "UintPtrT" : "Uint32T";
931   std::string encoder =
932       struct_is_pointer_size
933           ? (field_is_pointer_size ? "UpdateWord" : "UpdateWord32InWord")
934           : (field_is_pointer_size ? "UpdateWordInWord32" : "UpdateWord32");
935 
936   decls() << "  " << struct_type->GetGeneratedTypeName() << " " << result_name
937           << ";\n";
938 
939   if (smi_tagged_type) {
940     // If the container is a SMI, then UncheckedCast is insufficient and we must
941     // use a bit cast.
942     bit_field_struct =
943         "ca_.BitcastTaggedToWordForTagAndSmiBits(" + bit_field_struct + ")";
944   }
945 
946   std::string result_expression =
947       "CodeStubAssembler(state_)." + encoder + "<" +
948       GetBitFieldSpecialization(struct_type, instruction.bit_field) +
949       ">(ca_.UncheckedCast<" + struct_word_type + ">(" + bit_field_struct +
950       "), ca_.UncheckedCast<" + field_word_type + ">(" + value + ")" +
951       (instruction.starts_as_zero ? ", true" : "") + ")";
952 
953   if (smi_tagged_type) {
954     result_expression =
955         "ca_.BitcastWordToTaggedSigned(" + result_expression + ")";
956   }
957 
958   out() << "    " << result_name << " = ca_.UncheckedCast<"
959         << struct_type->GetGeneratedTNodeTypeName() << ">(" << result_expression
960         << ");\n";
961 }
962 
963 // static
EmitCSAValue(VisitResult result,const Stack<std::string> & values,std::ostream & out)964 void CSAGenerator::EmitCSAValue(VisitResult result,
965                                 const Stack<std::string>& values,
966                                 std::ostream& out) {
967   if (!result.IsOnStack()) {
968     out << result.constexpr_value();
969   } else if (auto struct_type = result.type()->StructSupertype()) {
970     out << (*struct_type)->GetGeneratedTypeName() << "{";
971     bool first = true;
972     for (auto& field : (*struct_type)->fields()) {
973       if (!first) {
974         out << ", ";
975       }
976       first = false;
977       EmitCSAValue(ProjectStructField(result, field.name_and_type.name), values,
978                    out);
979     }
980     out << "}";
981   } else {
982     DCHECK_EQ(1, result.stack_range().Size());
983     out << "TNode<" << result.type()->GetGeneratedTNodeTypeName() << ">{"
984         << values.Peek(result.stack_range().begin()) << "}";
985   }
986 }
987 
988 }  // namespace torque
989 }  // namespace internal
990 }  // namespace v8
991