1 // Copyright 2020 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/cc-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>> CCGenerator::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   // C++ doesn't have parameterized labels like CSA, so we must pre-declare all
25   // phi values so they're in scope for both the blocks that define them and the
26   // blocks that read them.
27   for (Block* block : cfg_.blocks()) {
28     if (block->IsDead()) continue;
29 
30     DCHECK_EQ(block->InputTypes().Size(), block->InputDefinitions().Size());
31     for (BottomOffset i = {0}; i < block->InputTypes().AboveTop(); ++i) {
32       DefinitionLocation input_def = block->InputDefinitions().Peek(i);
33       if (block->InputDefinitions().Peek(i).IsPhiFromBlock(block)) {
34         out() << "  " << block->InputTypes().Peek(i)->GetRuntimeType() << " "
35               << DefinitionToVariable(input_def) << ";\n";
36       }
37     }
38   }
39 
40   // Redirect the output of non-declarations into a buffer and only output
41   // declarations right away.
42   std::stringstream out_buffer;
43   std::ostream* old_out = out_;
44   out_ = &out_buffer;
45 
46   EmitInstruction(GotoInstruction{cfg_.start()}, &parameters);
47 
48   for (Block* block : cfg_.blocks()) {
49     if (cfg_.end() && *cfg_.end() == block) continue;
50     if (block->IsDead()) continue;
51     EmitBlock(block);
52   }
53 
54   base::Optional<Stack<std::string>> result;
55   if (cfg_.end()) {
56     result = EmitBlock(*cfg_.end());
57   }
58 
59   // All declarations have been printed now, so we can append the buffered
60   // output and redirect back to the original output stream.
61   out_ = old_out;
62   out() << out_buffer.str();
63 
64   return result;
65 }
66 
EmitBlock(const Block * block)67 Stack<std::string> CCGenerator::EmitBlock(const Block* block) {
68   out() << "\n";
69   out() << "  " << BlockName(block) << ":\n";
70 
71   Stack<std::string> stack;
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() << "  " << block->InputTypes().Peek(i)->GetRuntimeType() << " "
78               << stack.Top() << "{}; USE(" << stack.Top() << ");\n";
79     }
80   }
81 
82   for (const Instruction& instruction : block->instructions()) {
83     TorqueCodeGenerator::EmitInstruction(instruction, &stack);
84   }
85   return stack;
86 }
87 
EmitSourcePosition(SourcePosition pos,bool always_emit)88 void CCGenerator::EmitSourcePosition(SourcePosition pos, bool always_emit) {
89   const std::string& file = SourceFileMap::AbsolutePath(pos.source);
90   if (always_emit || !previous_position_.CompareStartIgnoreColumn(pos)) {
91     // Lines in Torque SourcePositions are zero-based, while the
92     // CodeStubAssembler and downwind systems are one-based.
93     out() << "  // " << file << ":" << (pos.start.line + 1) << "\n";
94     previous_position_ = pos;
95   }
96 }
97 
EmitInstruction(const PushUninitializedInstruction & instruction,Stack<std::string> * stack)98 void CCGenerator::EmitInstruction(
99     const PushUninitializedInstruction& instruction,
100     Stack<std::string>* stack) {
101   ReportError("Not supported in C++ output: PushUninitialized");
102 }
103 
EmitInstruction(const PushBuiltinPointerInstruction & instruction,Stack<std::string> * stack)104 void CCGenerator::EmitInstruction(
105     const PushBuiltinPointerInstruction& instruction,
106     Stack<std::string>* stack) {
107   ReportError("Not supported in C++ output: PushBuiltinPointer");
108 }
109 
EmitInstruction(const NamespaceConstantInstruction & instruction,Stack<std::string> * stack)110 void CCGenerator::EmitInstruction(
111     const NamespaceConstantInstruction& instruction,
112     Stack<std::string>* stack) {
113   ReportError("Not supported in C++ output: NamespaceConstantInstruction");
114 }
115 
ProcessArgumentsCommon(const TypeVector & parameter_types,std::vector<std::string> constexpr_arguments,Stack<std::string> * stack)116 std::vector<std::string> CCGenerator::ProcessArgumentsCommon(
117     const TypeVector& parameter_types,
118     std::vector<std::string> constexpr_arguments, Stack<std::string>* stack) {
119   std::vector<std::string> args;
120   for (auto it = parameter_types.rbegin(); it != parameter_types.rend(); ++it) {
121     const Type* type = *it;
122     VisitResult arg;
123     if (type->IsConstexpr()) {
124       args.push_back(std::move(constexpr_arguments.back()));
125       constexpr_arguments.pop_back();
126     } else {
127       std::stringstream s;
128       size_t slot_count = LoweredSlotCount(type);
129       VisitResult arg = VisitResult(type, stack->TopRange(slot_count));
130       EmitCCValue(arg, *stack, s);
131       args.push_back(s.str());
132       stack->PopMany(slot_count);
133     }
134   }
135   std::reverse(args.begin(), args.end());
136   return args;
137 }
138 
EmitInstruction(const CallIntrinsicInstruction & instruction,Stack<std::string> * stack)139 void CCGenerator::EmitInstruction(const CallIntrinsicInstruction& instruction,
140                                   Stack<std::string>* stack) {
141   TypeVector parameter_types =
142       instruction.intrinsic->signature().parameter_types.types;
143   std::vector<std::string> args = ProcessArgumentsCommon(
144       parameter_types, instruction.constexpr_arguments, stack);
145 
146   Stack<std::string> pre_call_stack = *stack;
147   const Type* return_type = instruction.intrinsic->signature().return_type;
148   std::vector<std::string> results;
149 
150   const auto lowered = LowerType(return_type);
151   for (std::size_t i = 0; i < lowered.size(); ++i) {
152     results.push_back(DefinitionToVariable(instruction.GetValueDefinition(i)));
153     stack->Push(results.back());
154     decls() << "  " << lowered[i]->GetRuntimeType() << " " << stack->Top()
155             << "{}; USE(" << stack->Top() << ");\n";
156   }
157 
158   out() << "  ";
159   if (return_type->StructSupertype()) {
160     out() << "std::tie(";
161     PrintCommaSeparatedList(out(), results);
162     out() << ") = ";
163   } else {
164     if (results.size() == 1) {
165       out() << results[0] << " = ";
166     }
167   }
168 
169   if (instruction.intrinsic->ExternalName() == "%RawDownCast") {
170     if (parameter_types.size() != 1) {
171       ReportError("%RawDownCast must take a single parameter");
172     }
173     const Type* original_type = parameter_types[0];
174     bool is_subtype =
175         return_type->IsSubtypeOf(original_type) ||
176         (original_type == TypeOracle::GetUninitializedHeapObjectType() &&
177          return_type->IsSubtypeOf(TypeOracle::GetHeapObjectType()));
178     if (!is_subtype) {
179       ReportError("%RawDownCast error: ", *return_type, " is not a subtype of ",
180                   *original_type);
181     }
182     if (!original_type->StructSupertype() &&
183         return_type->GetRuntimeType() != original_type->GetRuntimeType()) {
184       out() << "static_cast<" << return_type->GetRuntimeType() << ">";
185     }
186   } else if (instruction.intrinsic->ExternalName() == "%GetClassMapConstant") {
187     ReportError("C++ generator doesn't yet support %GetClassMapConstant");
188   } else if (instruction.intrinsic->ExternalName() == "%FromConstexpr") {
189     if (parameter_types.size() != 1 || !parameter_types[0]->IsConstexpr()) {
190       ReportError(
191           "%FromConstexpr must take a single parameter with constexpr "
192           "type");
193     }
194     if (return_type->IsConstexpr()) {
195       ReportError("%FromConstexpr must return a non-constexpr type");
196     }
197     // Nothing to do here; constexpr expressions are already valid C++.
198   } else {
199     ReportError("no built in intrinsic with name " +
200                 instruction.intrinsic->ExternalName());
201   }
202 
203   out() << "(";
204   PrintCommaSeparatedList(out(), args);
205   out() << ");\n";
206 }
207 
EmitInstruction(const CallCsaMacroInstruction & instruction,Stack<std::string> * stack)208 void CCGenerator::EmitInstruction(const CallCsaMacroInstruction& instruction,
209                                   Stack<std::string>* stack) {
210   TypeVector parameter_types =
211       instruction.macro->signature().parameter_types.types;
212   std::vector<std::string> args = ProcessArgumentsCommon(
213       parameter_types, instruction.constexpr_arguments, stack);
214 
215   Stack<std::string> pre_call_stack = *stack;
216   const Type* return_type = instruction.macro->signature().return_type;
217   std::vector<std::string> results;
218 
219   const auto lowered = LowerType(return_type);
220   for (std::size_t i = 0; i < lowered.size(); ++i) {
221     results.push_back(DefinitionToVariable(instruction.GetValueDefinition(i)));
222     stack->Push(results.back());
223     decls() << "  " << lowered[i]->GetRuntimeType() << " " << stack->Top()
224             << "{}; USE(" << stack->Top() << ");\n";
225   }
226 
227   // We should have inlined any calls requiring complex control flow.
228   CHECK(!instruction.catch_block);
229   out() << "  ";
230   if (return_type->StructSupertype().has_value()) {
231     out() << "std::tie(";
232     PrintCommaSeparatedList(out(), results);
233     out() << ") = ";
234   } else {
235     if (results.size() == 1) {
236       out() << results[0] << " = ";
237     } else {
238       DCHECK_EQ(0, results.size());
239     }
240   }
241 
242   out() << instruction.macro->CCName() << "(isolate";
243   if (!args.empty()) out() << ", ";
244   PrintCommaSeparatedList(out(), args);
245   out() << ");\n";
246 }
247 
EmitInstruction(const CallCsaMacroAndBranchInstruction & instruction,Stack<std::string> * stack)248 void CCGenerator::EmitInstruction(
249     const CallCsaMacroAndBranchInstruction& instruction,
250     Stack<std::string>* stack) {
251   ReportError("Not supported in C++ output: CallCsaMacroAndBranch");
252 }
253 
EmitInstruction(const CallBuiltinInstruction & instruction,Stack<std::string> * stack)254 void CCGenerator::EmitInstruction(const CallBuiltinInstruction& instruction,
255                                   Stack<std::string>* stack) {
256   ReportError("Not supported in C++ output: CallBuiltin");
257 }
258 
EmitInstruction(const CallBuiltinPointerInstruction & instruction,Stack<std::string> * stack)259 void CCGenerator::EmitInstruction(
260     const CallBuiltinPointerInstruction& instruction,
261     Stack<std::string>* stack) {
262   ReportError("Not supported in C++ output: CallBuiltinPointer");
263 }
264 
EmitInstruction(const CallRuntimeInstruction & instruction,Stack<std::string> * stack)265 void CCGenerator::EmitInstruction(const CallRuntimeInstruction& instruction,
266                                   Stack<std::string>* stack) {
267   ReportError("Not supported in C++ output: CallRuntime");
268 }
269 
EmitInstruction(const BranchInstruction & instruction,Stack<std::string> * stack)270 void CCGenerator::EmitInstruction(const BranchInstruction& instruction,
271                                   Stack<std::string>* stack) {
272   out() << "  if (" << stack->Pop() << ") {\n";
273   EmitGoto(instruction.if_true, stack, "    ");
274   out() << "  } else {\n";
275   EmitGoto(instruction.if_false, stack, "    ");
276   out() << "  }\n";
277 }
278 
EmitInstruction(const ConstexprBranchInstruction & instruction,Stack<std::string> * stack)279 void CCGenerator::EmitInstruction(const ConstexprBranchInstruction& instruction,
280                                   Stack<std::string>* stack) {
281   out() << "  if ((" << instruction.condition << ")) {\n";
282   EmitGoto(instruction.if_true, stack, "    ");
283   out() << "  } else {\n";
284   EmitGoto(instruction.if_false, stack, "    ");
285   out() << "  }\n";
286 }
287 
EmitGoto(const Block * destination,Stack<std::string> * stack,std::string indentation)288 void CCGenerator::EmitGoto(const Block* destination, Stack<std::string>* stack,
289                            std::string indentation) {
290   const auto& destination_definitions = destination->InputDefinitions();
291   DCHECK_EQ(stack->Size(), destination_definitions.Size());
292   for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) {
293     DefinitionLocation def = destination_definitions.Peek(i);
294     if (def.IsPhiFromBlock(destination)) {
295       out() << indentation << DefinitionToVariable(def) << " = "
296             << stack->Peek(i) << ";\n";
297     }
298   }
299   out() << indentation << "goto " << BlockName(destination) << ";\n";
300 }
301 
EmitInstruction(const GotoInstruction & instruction,Stack<std::string> * stack)302 void CCGenerator::EmitInstruction(const GotoInstruction& instruction,
303                                   Stack<std::string>* stack) {
304   EmitGoto(instruction.destination, stack, "  ");
305 }
306 
EmitInstruction(const GotoExternalInstruction & instruction,Stack<std::string> * stack)307 void CCGenerator::EmitInstruction(const GotoExternalInstruction& instruction,
308                                   Stack<std::string>* stack) {
309   ReportError("Not supported in C++ output: GotoExternal");
310 }
311 
EmitInstruction(const ReturnInstruction & instruction,Stack<std::string> * stack)312 void CCGenerator::EmitInstruction(const ReturnInstruction& instruction,
313                                   Stack<std::string>* stack) {
314   ReportError("Not supported in C++ output: Return");
315 }
316 
EmitInstruction(const PrintConstantStringInstruction & instruction,Stack<std::string> * stack)317 void CCGenerator::EmitInstruction(
318     const PrintConstantStringInstruction& instruction,
319     Stack<std::string>* stack) {
320   out() << "  std::cout << " << StringLiteralQuote(instruction.message)
321         << ";\n";
322 }
323 
EmitInstruction(const AbortInstruction & instruction,Stack<std::string> * stack)324 void CCGenerator::EmitInstruction(const AbortInstruction& instruction,
325                                   Stack<std::string>* stack) {
326   switch (instruction.kind) {
327     case AbortInstruction::Kind::kUnreachable:
328       DCHECK(instruction.message.empty());
329       out() << "  UNREACHABLE();\n";
330       break;
331     case AbortInstruction::Kind::kDebugBreak:
332       DCHECK(instruction.message.empty());
333       out() << "  base::OS::DebugBreak();\n";
334       break;
335     case AbortInstruction::Kind::kAssertionFailure: {
336       std::string file = StringLiteralQuote(
337           SourceFileMap::PathFromV8Root(instruction.pos.source));
338       out() << "  CHECK(false, \"Failed Torque assertion: '\""
339             << StringLiteralQuote(instruction.message) << "\"' at \"" << file
340             << "\":\""
341             << StringLiteralQuote(
342                    std::to_string(instruction.pos.start.line + 1))
343             << ");\n";
344       break;
345     }
346   }
347 }
348 
EmitInstruction(const UnsafeCastInstruction & instruction,Stack<std::string> * stack)349 void CCGenerator::EmitInstruction(const UnsafeCastInstruction& instruction,
350                                   Stack<std::string>* stack) {
351   const std::string str = "static_cast<" +
352                           instruction.destination_type->GetRuntimeType() +
353                           ">(" + stack->Top() + ")";
354   stack->Poke(stack->AboveTop() - 1, str);
355   SetDefinitionVariable(instruction.GetValueDefinition(), str);
356 }
357 
EmitInstruction(const LoadReferenceInstruction & instruction,Stack<std::string> * stack)358 void CCGenerator::EmitInstruction(const LoadReferenceInstruction& instruction,
359                                   Stack<std::string>* stack) {
360   std::string result_name =
361       DefinitionToVariable(instruction.GetValueDefinition());
362 
363   std::string offset = stack->Pop();
364   std::string object = stack->Pop();
365   stack->Push(result_name);
366 
367   std::string result_type = instruction.type->GetRuntimeType();
368   decls() << "  " << result_type << " " << result_name << "{}; USE("
369           << result_name << ");\n";
370   out() << "  " << result_name << " = ";
371   if (instruction.type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
372     out() << "TaggedField<" << result_type << ">::load(isolate, " << object
373           << ", static_cast<int>(" << offset << "));\n";
374   } else {
375     out() << "(" << object << ").ReadField<" << result_type << ">(" << offset
376           << ");\n";
377   }
378 }
379 
EmitInstruction(const StoreReferenceInstruction & instruction,Stack<std::string> * stack)380 void CCGenerator::EmitInstruction(const StoreReferenceInstruction& instruction,
381                                   Stack<std::string>* stack) {
382   ReportError("Not supported in C++ output: StoreReference");
383 }
384 
385 namespace {
GetBitFieldSpecialization(const Type * container,const BitField & field)386 std::string GetBitFieldSpecialization(const Type* container,
387                                       const BitField& field) {
388   std::stringstream stream;
389   stream << "base::BitField<"
390          << field.name_and_type.type->GetConstexprGeneratedTypeName() << ", "
391          << field.offset << ", " << field.num_bits << ", "
392          << container->GetConstexprGeneratedTypeName() << ">";
393   return stream.str();
394 }
395 }  // namespace
396 
EmitInstruction(const LoadBitFieldInstruction & instruction,Stack<std::string> * stack)397 void CCGenerator::EmitInstruction(const LoadBitFieldInstruction& instruction,
398                                   Stack<std::string>* stack) {
399   std::string result_name =
400       DefinitionToVariable(instruction.GetValueDefinition());
401 
402   std::string bit_field_struct = stack->Pop();
403   stack->Push(result_name);
404 
405   const Type* struct_type = instruction.bit_field_struct_type;
406 
407   decls() << "  " << instruction.bit_field.name_and_type.type->GetRuntimeType()
408           << " " << result_name << "{}; USE(" << result_name << ");\n";
409 
410   base::Optional<const Type*> smi_tagged_type =
411       Type::MatchUnaryGeneric(struct_type, TypeOracle::GetSmiTaggedGeneric());
412   if (smi_tagged_type) {
413     // Get the untagged value and its type.
414     bit_field_struct = bit_field_struct + ".value()";
415     struct_type = *smi_tagged_type;
416   }
417 
418   out() << "  " << result_name << " = "
419         << GetBitFieldSpecialization(struct_type, instruction.bit_field)
420         << "::decode(" << bit_field_struct << ");\n";
421 }
422 
EmitInstruction(const StoreBitFieldInstruction & instruction,Stack<std::string> * stack)423 void CCGenerator::EmitInstruction(const StoreBitFieldInstruction& instruction,
424                                   Stack<std::string>* stack) {
425   ReportError("Not supported in C++ output: StoreBitField");
426 }
427 
428 // static
EmitCCValue(VisitResult result,const Stack<std::string> & values,std::ostream & out)429 void CCGenerator::EmitCCValue(VisitResult result,
430                               const Stack<std::string>& values,
431                               std::ostream& out) {
432   if (!result.IsOnStack()) {
433     out << result.constexpr_value();
434   } else if (auto struct_type = result.type()->StructSupertype()) {
435     out << "std::tuple_cat(";
436     bool first = true;
437     for (auto& field : (*struct_type)->fields()) {
438       if (!first) {
439         out << ", ";
440       }
441       first = false;
442       if (!field.name_and_type.type->IsStructType()) {
443         out << "std::make_tuple(";
444       }
445       EmitCCValue(ProjectStructField(result, field.name_and_type.name), values,
446                   out);
447       if (!field.name_and_type.type->IsStructType()) {
448         out << ")";
449       }
450     }
451     out << ")";
452   } else {
453     DCHECK_EQ(1, result.stack_range().Size());
454     out << values.Peek(result.stack_range().begin());
455   }
456 }
457 
458 }  // namespace torque
459 }  // namespace internal
460 }  // namespace v8
461