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