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