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()}, ¶meters);
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