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