csa-generator.cc 40.2 KB
Newer Older
1 2 3 4 5 6
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/torque/csa-generator.h"

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 12 13 14 15 16 17 18
#include "src/torque/utils.h"

namespace v8 {
namespace internal {
namespace torque {

base::Optional<Stack<std::string>> CSAGenerator::EmitGraph(
    Stack<std::string> parameters) {
19
  for (BottomOffset i = {0}; i < parameters.AboveTop(); ++i) {
20 21 22 23
    SetDefinitionVariable(DefinitionLocation::Parameter(i.offset),
                          parameters.Peek(i));
  }

24
  for (Block* block : cfg_.blocks()) {
25 26 27 28 29
    if (block->IsDead()) continue;

    out() << "  compiler::CodeAssemblerParameterizedLabel<";
    bool first = true;
    DCHECK_EQ(block->InputTypes().Size(), block->InputDefinitions().Size());
30
    for (BottomOffset i = {0}; i < block->InputTypes().AboveTop(); ++i) {
31 32 33 34 35 36 37 38
      if (block->InputDefinitions().Peek(i).IsPhiFromBlock(block)) {
        if (!first) out() << ", ";
        out() << block->InputTypes().Peek(i)->GetGeneratedTNodeTypeName();
        first = false;
      }
    }
    out() << "> " << BlockName(block) << "(&ca_, compiler::CodeAssemblerLabel::"
          << (block->IsDeferred() ? "kDeferred" : "kNonDeferred") << ");\n";
39 40 41 42 43
  }

  EmitInstruction(GotoInstruction{cfg_.start()}, &parameters);
  for (Block* block : cfg_.blocks()) {
    if (cfg_.end() && *cfg_.end() == block) continue;
44 45 46 47 48 49 50 51 52 53
    if (block->IsDead()) continue;
    out() << "\n";

    // Redirect the output of non-declarations into a buffer and only output
    // declarations right away.
    std::stringstream out_buffer;
    std::ostream* old_out = out_;
    out_ = &out_buffer;

    out() << "  if (" << BlockName(block) << ".is_used()) {\n";
54
    EmitBlock(block);
55 56 57 58 59 60
    out() << "  }\n";

    // All declarations have been printed now, so we can append the buffered
    // output and redirect back to the original output stream.
    out_ = old_out;
    out() << out_buffer.str();
61 62
  }
  if (cfg_.end()) {
63
    out() << "\n";
64 65 66 67 68 69 70
    return EmitBlock(*cfg_.end());
  }
  return base::nullopt;
}

Stack<std::string> CSAGenerator::EmitBlock(const Block* block) {
  Stack<std::string> stack;
71 72
  std::stringstream phi_names;

73
  for (BottomOffset i = {0}; i < block->InputTypes().AboveTop(); ++i) {
74 75 76 77 78 79 80 81
    const auto& def = block->InputDefinitions().Peek(i);
    stack.Push(DefinitionToVariable(def));
    if (def.IsPhiFromBlock(block)) {
      decls() << "  TNode<"
              << block->InputTypes().Peek(i)->GetGeneratedTNodeTypeName()
              << "> " << stack.Top() << ";\n";
      phi_names << ", &" << stack.Top();
    }
82
  }
83 84
  out() << "    ca_.Bind(&" << BlockName(block) << phi_names.str() << ");\n";

85
  for (const Instruction& instruction : block->instructions()) {
86
    TorqueCodeGenerator::EmitInstruction(instruction, &stack);
87 88 89 90
  }
  return stack;
}

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 95
    // Lines in Torque SourcePositions are zero-based, while the
    // CodeStubAssembler and downwind systems are one-based.
96 97
    out() << "    ca_.SetSourcePosition(\"" << file << "\", "
          << (pos.start.line + 1) << ");\n";
98 99 100 101
    previous_position_ = pos;
  }
}

102 103 104
void CSAGenerator::EmitInstruction(
    const PushUninitializedInstruction& instruction,
    Stack<std::string>* stack) {
105
  // TODO(turbofan): This can trigger an error in CSA if it is used. Instead, we
106 107
  // should prevent usage of uninitialized in the type system. This
  // requires "if constexpr" being evaluated at Torque time.
108 109 110 111
  const std::string str = "ca_.Uninitialized<" +
                          instruction.type->GetGeneratedTNodeTypeName() + ">()";
  stack->Push(str);
  SetDefinitionVariable(instruction.GetValueDefinition(), str);
112 113 114
}

void CSAGenerator::EmitInstruction(
115 116
    const PushBuiltinPointerInstruction& instruction,
    Stack<std::string>* stack) {
117
  const std::string str =
118
      "ca_.UncheckedCast<BuiltinPtr>(ca_.SmiConstant(Builtin::k" +
119 120 121
      instruction.external_name + "))";
  stack->Push(str);
  SetDefinitionVariable(instruction.GetValueDefinition(), str);
122 123
}

124 125 126
void CSAGenerator::EmitInstruction(
    const NamespaceConstantInstruction& instruction,
    Stack<std::string>* stack) {
127 128
  const Type* type = instruction.constant->type();
  std::vector<std::string> results;
129 130 131 132

  const auto lowered = LowerType(type);
  for (std::size_t i = 0; i < lowered.size(); ++i) {
    results.push_back(DefinitionToVariable(instruction.GetValueDefinition(i)));
133
    stack->Push(results.back());
134 135
    decls() << "  TNode<" << lowered[i]->GetGeneratedTNodeTypeName() << "> "
            << stack->Top() << ";\n";
136
  }
137 138

  out() << "    ";
139
  if (type->StructSupertype()) {
140 141 142
    out() << "std::tie(";
    PrintCommaSeparatedList(out(), results);
    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
  }
}

154 155 156 157
std::vector<std::string> CSAGenerator::ProcessArgumentsCommon(
    const TypeVector& parameter_types,
    std::vector<std::string> constexpr_arguments, Stack<std::string>* stack) {
  std::vector<std::string> args;
158 159 160
  for (auto it = parameter_types.rbegin(); it != parameter_types.rend(); ++it) {
    const Type* type = *it;
    if (type->IsConstexpr()) {
161 162
      args.push_back(std::move(constexpr_arguments.back()));
      constexpr_arguments.pop_back();
163 164 165 166 167
    } else {
      std::stringstream s;
      size_t slot_count = LoweredSlotCount(type);
      VisitResult arg = VisitResult(type, stack->TopRange(slot_count));
      EmitCSAValue(arg, *stack, s);
168
      args.push_back(s.str());
169 170 171
      stack->PopMany(slot_count);
    }
  }
172 173
  std::reverse(args.begin(), args.end());
  return args;
174 175 176 177 178 179
}

void CSAGenerator::EmitInstruction(const CallIntrinsicInstruction& instruction,
                                   Stack<std::string>* stack) {
  TypeVector parameter_types =
      instruction.intrinsic->signature().parameter_types.types;
180 181
  std::vector<std::string> args = ProcessArgumentsCommon(
      parameter_types, instruction.constexpr_arguments, stack);
182 183 184 185

  Stack<std::string> pre_call_stack = *stack;
  const Type* return_type = instruction.intrinsic->signature().return_type;
  std::vector<std::string> results;
186 187 188 189

  const auto lowered = LowerType(return_type);
  for (std::size_t i = 0; i < lowered.size(); ++i) {
    results.push_back(DefinitionToVariable(instruction.GetValueDefinition(i)));
190
    stack->Push(results.back());
191 192
    decls() << "  TNode<" << lowered[i]->GetGeneratedTNodeTypeName() << "> "
            << stack->Top() << ";\n";
193 194
  }

195
  out() << "    ";
196
  if (return_type->StructSupertype()) {
197 198 199
    out() << "std::tie(";
    PrintCommaSeparatedList(out(), results);
    out() << ") = ";
200 201
  } else {
    if (results.size() == 1) {
202
      out() << results[0] << " = ";
203 204 205
    }
  }

206
  if (instruction.intrinsic->ExternalName() == "%RawDownCast") {
207
    if (parameter_types.size() != 1) {
208 209
      ReportError("%RawDownCast must take a single parameter");
    }
210 211 212 213 214 215
    const Type* original_type = parameter_types[0];
    bool is_subtype =
        return_type->IsSubtypeOf(original_type) ||
        (original_type == TypeOracle::GetUninitializedHeapObjectType() &&
         return_type->IsSubtypeOf(TypeOracle::GetHeapObjectType()));
    if (!is_subtype) {
216
      ReportError("%RawDownCast error: ", *return_type, " is not a subtype of ",
217
                  *original_type);
218
    }
219 220 221
    if (!original_type->StructSupertype() &&
        return_type->GetGeneratedTNodeTypeName() !=
            original_type->GetGeneratedTNodeTypeName()) {
222
      if (return_type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
223
        out() << "TORQUE_CAST";
224
      } else {
225 226
        out() << "ca_.UncheckedCast<"
              << return_type->GetGeneratedTNodeTypeName() << ">";
227 228
      }
    }
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
  } else if (instruction.intrinsic->ExternalName() == "%GetClassMapConstant") {
    if (parameter_types.size() != 0) {
      ReportError("%GetClassMapConstant must not take parameters");
    }
    if (instruction.specialization_types.size() != 1) {
      ReportError(
          "%GetClassMapConstant must take a single class as specialization "
          "parameter");
    }
    const ClassType* class_type =
        ClassType::DynamicCast(instruction.specialization_types[0]);
    if (!class_type) {
      ReportError("%GetClassMapConstant must take a class type parameter");
    }
    // If the class isn't actually used as the parameter to a TNode,
    // then we can't rely on the class existing in C++ or being of the same
    // type (e.g. it could be a template), so don't use the template CSA
    // machinery for accessing the class' map.
    std::string class_name =
        class_type->name() != class_type->GetGeneratedTNodeTypeName()
            ? std::string("void")
            : class_type->name();

    out() << std::string("CodeStubAssembler(state_).GetClassMapConstant<") +
                 class_name + ">";
254 255 256 257 258 259 260 261 262 263
  } else if (instruction.intrinsic->ExternalName() == "%FromConstexpr") {
    if (parameter_types.size() != 1 || !parameter_types[0]->IsConstexpr()) {
      ReportError(
          "%FromConstexpr must take a single parameter with constexpr "
          "type");
    }
    if (return_type->IsConstexpr()) {
      ReportError("%FromConstexpr must return a non-constexpr type");
    }
    if (return_type->IsSubtypeOf(TypeOracle::GetSmiType())) {
264
      out() << "ca_.SmiConstant";
265
    } else if (return_type->IsSubtypeOf(TypeOracle::GetNumberType())) {
266
      out() << "ca_.NumberConstant";
267
    } else if (return_type->IsSubtypeOf(TypeOracle::GetStringType())) {
268
      out() << "ca_.StringConstant";
269 270 271 272 273
    } else if (return_type->IsSubtypeOf(TypeOracle::GetObjectType())) {
      ReportError(
          "%FromConstexpr cannot cast to subclass of HeapObject unless it's a "
          "String or Number");
    } else if (return_type->IsSubtypeOf(TypeOracle::GetIntPtrType())) {
274
      out() << "ca_.IntPtrConstant";
275
    } else if (return_type->IsSubtypeOf(TypeOracle::GetUIntPtrType())) {
276
      out() << "ca_.UintPtrConstant";
277
    } else if (return_type->IsSubtypeOf(TypeOracle::GetInt32Type())) {
278
      out() << "ca_.Int32Constant";
279
    } else if (return_type->IsSubtypeOf(TypeOracle::GetUint32Type())) {
280
      out() << "ca_.Uint32Constant";
281 282 283 284
    } else if (return_type->IsSubtypeOf(TypeOracle::GetInt64Type())) {
      out() << "ca_.Int64Constant";
    } else if (return_type->IsSubtypeOf(TypeOracle::GetUint64Type())) {
      out() << "ca_.Uint64Constant";
285
    } else if (return_type->IsSubtypeOf(TypeOracle::GetBoolType())) {
286
      out() << "ca_.BoolConstant";
287 288 289 290
    } else {
      std::stringstream s;
      s << "%FromConstexpr does not support return type " << *return_type;
      ReportError(s.str());
291
    }
292 293
    // Wrap the raw constexpr value in a static_cast to ensure that
    // enums get properly casted to their backing integral value.
294
    out() << "(CastToUnderlyingTypeIfEnum";
295 296 297 298 299
  } else {
    ReportError("no built in intrinsic with name " +
                instruction.intrinsic->ExternalName());
  }

300 301
  out() << "(";
  PrintCommaSeparatedList(out(), args);
302
  if (instruction.intrinsic->ExternalName() == "%FromConstexpr") {
303
    out() << ")";
304
  }
305
  if (return_type->StructSupertype()) {
306
    out() << ").Flatten();\n";
307
  } else {
308
    out() << ");\n";
309 310 311 312 313 314 315
  }
}

void CSAGenerator::EmitInstruction(const CallCsaMacroInstruction& instruction,
                                   Stack<std::string>* stack) {
  TypeVector parameter_types =
      instruction.macro->signature().parameter_types.types;
316 317
  std::vector<std::string> args = ProcessArgumentsCommon(
      parameter_types, instruction.constexpr_arguments, stack);
318

319
  Stack<std::string> pre_call_stack = *stack;
320 321
  const Type* return_type = instruction.macro->signature().return_type;
  std::vector<std::string> results;
322 323 324 325

  const auto lowered = LowerType(return_type);
  for (std::size_t i = 0; i < lowered.size(); ++i) {
    results.push_back(DefinitionToVariable(instruction.GetValueDefinition(i)));
326
    stack->Push(results.back());
327 328
    decls() << "  TNode<" << lowered[i]->GetGeneratedTNodeTypeName() << "> "
            << stack->Top() << ";\n";
329
  }
330

331 332
  std::string catch_name =
      PreCallableExceptionPreparation(instruction.catch_block);
333
  out() << "    ";
334
  bool needs_flattening = return_type->StructSupertype().has_value();
335
  if (needs_flattening) {
336 337 338
    out() << "std::tie(";
    PrintCommaSeparatedList(out(), results);
    out() << ") = ";
339 340
  } else {
    if (results.size() == 1) {
341
      out() << results[0] << " = ";
342 343
    } else {
      DCHECK_EQ(0, results.size());
344 345
    }
  }
346
  if (ExternMacro* extern_macro = ExternMacro::DynamicCast(instruction.macro)) {
347
    out() << extern_macro->external_assembler_name() << "(state_).";
348 349 350
  } else {
    args.insert(args.begin(), "state_");
  }
351 352
  out() << instruction.macro->ExternalName() << "(";
  PrintCommaSeparatedList(out(), args);
353
  if (needs_flattening) {
354
    out() << ").Flatten();\n";
355
  } else {
356
    out() << ");\n";
357
  }
358
  PostCallableExceptionPreparation(catch_name, return_type,
359 360
                                   instruction.catch_block, &pre_call_stack,
                                   instruction.GetExceptionObjectDefinition());
361 362 363 364 365 366 367
}

void CSAGenerator::EmitInstruction(
    const CallCsaMacroAndBranchInstruction& instruction,
    Stack<std::string>* stack) {
  TypeVector parameter_types =
      instruction.macro->signature().parameter_types.types;
368 369
  std::vector<std::string> args = ProcessArgumentsCommon(
      parameter_types, instruction.constexpr_arguments, stack);
370

371
  Stack<std::string> pre_call_stack = *stack;
372 373
  std::vector<std::string> results;
  const Type* return_type = instruction.macro->signature().return_type;
374

375
  if (return_type != TypeOracle::GetNeverType()) {
376 377 378 379 380 381
    const auto lowered = LowerType(return_type);
    for (std::size_t i = 0; i < lowered.size(); ++i) {
      results.push_back(
          DefinitionToVariable(instruction.GetValueDefinition(i)));
      decls() << "  TNode<" << lowered[i]->GetGeneratedTNodeTypeName() << "> "
              << results.back() << ";\n";
382 383 384 385 386 387 388 389 390
    }
  }

  std::vector<std::string> label_names;
  std::vector<std::vector<std::string>> var_names;
  const LabelDeclarationVector& labels = instruction.macro->signature().labels;
  DCHECK_EQ(labels.size(), instruction.label_blocks.size());
  for (size_t i = 0; i < labels.size(); ++i) {
    TypeVector label_parameters = labels[i].types;
391
    label_names.push_back(FreshLabelName());
392 393
    var_names.push_back({});
    for (size_t j = 0; j < label_parameters.size(); ++j) {
394 395 396 397 398 399
      var_names[i].push_back(FreshNodeName());
      const auto def = instruction.GetLabelValueDefinition(i, j);
      SetDefinitionVariable(def, var_names[i].back() + ".value()");
      decls() << "    compiler::TypedCodeAssemblerVariable<"
              << label_parameters[j]->GetGeneratedTNodeTypeName() << "> "
              << var_names[i][j] << "(&ca_);\n";
400
    }
401 402
    out() << "    compiler::CodeAssemblerLabel " << label_names[i]
          << "(&ca_);\n";
403 404
  }

405 406
  std::string catch_name =
      PreCallableExceptionPreparation(instruction.catch_block);
407
  out() << "    ";
408
  if (results.size() == 1) {
409
    out() << results[0] << " = ";
410
  } else if (results.size() > 1) {
411 412 413
    out() << "std::tie(";
    PrintCommaSeparatedList(out(), results);
    out() << ") = ";
414
  }
415
  if (ExternMacro* extern_macro = ExternMacro::DynamicCast(instruction.macro)) {
416
    out() << extern_macro->external_assembler_name() << "(state_).";
417 418 419
  } else {
    args.insert(args.begin(), "state_");
  }
420 421
  out() << instruction.macro->ExternalName() << "(";
  PrintCommaSeparatedList(out(), args);
422 423
  bool first = args.empty();
  for (size_t i = 0; i < label_names.size(); ++i) {
424 425
    if (!first) out() << ", ";
    out() << "&" << label_names[i];
426 427
    first = false;
    for (size_t j = 0; j < var_names[i].size(); ++j) {
428
      out() << ", &" << var_names[i][j];
429 430
    }
  }
431
  if (return_type->StructSupertype()) {
432
    out() << ").Flatten();\n";
433
  } else {
434
    out() << ");\n";
435
  }
436 437

  PostCallableExceptionPreparation(catch_name, return_type,
438 439
                                   instruction.catch_block, &pre_call_stack,
                                   instruction.GetExceptionObjectDefinition());
440

441
  if (instruction.return_continuation) {
442 443 444 445 446 447
    out() << "    ca_.Goto(&" << BlockName(*instruction.return_continuation);
    DCHECK_EQ(stack->Size() + results.size(),
              (*instruction.return_continuation)->InputDefinitions().Size());

    const auto& input_definitions =
        (*instruction.return_continuation)->InputDefinitions();
448
    for (BottomOffset i = {0}; i < input_definitions.AboveTop(); ++i) {
449 450 451 452 453
      if (input_definitions.Peek(i).IsPhiFromBlock(
              *instruction.return_continuation)) {
        out() << ", "
              << (i < stack->AboveTop() ? stack->Peek(i) : results[i.offset]);
      }
454
    }
455 456 457 458 459 460 461 462 463 464 465 466
    out() << ");\n";
  }
  for (size_t l = 0; l < label_names.size(); ++l) {
    out() << "    if (" << label_names[l] << ".is_used()) {\n";
    out() << "      ca_.Bind(&" << label_names[l] << ");\n";
    out() << "      ca_.Goto(&" << BlockName(instruction.label_blocks[l]);
    DCHECK_EQ(stack->Size() + var_names[l].size(),
              instruction.label_blocks[l]->InputDefinitions().Size());

    const auto& label_definitions =
        instruction.label_blocks[l]->InputDefinitions();

467
    BottomOffset i = {0};
468 469 470 471 472
    for (; i < stack->AboveTop(); ++i) {
      if (label_definitions.Peek(i).IsPhiFromBlock(
              instruction.label_blocks[l])) {
        out() << ", " << stack->Peek(i);
      }
473
    }
474 475 476 477 478
    for (std::size_t k = 0; k < var_names[l].size(); ++k, ++i) {
      if (label_definitions.Peek(i).IsPhiFromBlock(
              instruction.label_blocks[l])) {
        out() << ", " << var_names[l][k] << ".value()";
      }
479
    }
480 481
    out() << ");\n";
    out() << "    }\n";
482 483 484
  }
}

485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
void CSAGenerator::EmitInstruction(const MakeLazyNodeInstruction& instruction,
                                   Stack<std::string>* stack) {
  TypeVector parameter_types =
      instruction.macro->signature().parameter_types.types;
  std::vector<std::string> args = ProcessArgumentsCommon(
      parameter_types, instruction.constexpr_arguments, stack);

  std::string result_name =
      DefinitionToVariable(instruction.GetValueDefinition());

  stack->Push(result_name);

  decls() << "  " << instruction.result_type->GetGeneratedTypeName() << " "
          << result_name << ";\n";

  // We assume here that the CodeAssemblerState will outlive any usage of
  // the generated std::function that binds it. Likewise, copies of TNode values
  // are only valid during generation of the current builtin.
  out() << "    " << result_name << " = [=] () { return ";
  bool first = true;
  if (const ExternMacro* extern_macro =
          ExternMacro::DynamicCast(instruction.macro)) {
    out() << extern_macro->external_assembler_name() << "(state_)."
          << extern_macro->ExternalName() << "(";
  } else {
    out() << instruction.macro->ExternalName() << "(state_";
    first = false;
  }
  if (!args.empty()) {
    if (!first) out() << ", ";
    PrintCommaSeparatedList(out(), args);
  }
  out() << "); };\n";
}

520 521 522 523 524 525
void CSAGenerator::EmitInstruction(const CallBuiltinInstruction& instruction,
                                   Stack<std::string>* stack) {
  std::vector<std::string> arguments = stack->PopMany(instruction.argc);
  std::vector<const Type*> result_types =
      LowerType(instruction.builtin->signature().return_type);
  if (instruction.is_tailcall) {
526
    out() << "   CodeStubAssembler(state_).TailCallBuiltin(Builtin::k"
527 528 529 530 531 532 533 534
          << instruction.builtin->ExternalName();
    if (!instruction.builtin->signature().HasContextParameter()) {
      // Add dummy context parameter to satisfy the TailCallBuiltin signature.
      out() << ", TNode<Object>()";
    }
    for (const std::string& argument : arguments) {
      out() << ", " << argument;
    }
535
    out() << ");\n";
536
  } else {
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
    std::vector<std::string> result_names(result_types.size());
    for (size_t i = 0; i < result_types.size(); ++i) {
      result_names[i] = DefinitionToVariable(instruction.GetValueDefinition(i));
      decls() << "  TNode<" << result_types[i]->GetGeneratedTNodeTypeName()
              << "> " << result_names[i] << ";\n";
    }

    std::string lhs_name;
    std::string lhs_type;
    switch (result_types.size()) {
      case 1:
        lhs_name = result_names[0];
        lhs_type = result_types[0]->GetGeneratedTNodeTypeName();
        break;
      case 2:
        // If a builtin returns two values, the return type is represented as a
        // TNode containing a pair. We need a temporary place to store that
        // result so we can unpack it into separate TNodes.
        lhs_name = result_names[0] + "_and_" + result_names[1];
        lhs_type = "PairT<" + result_types[0]->GetGeneratedTNodeTypeName() +
                   ", " + result_types[1]->GetGeneratedTNodeTypeName() + ">";
        decls() << "  TNode<" << lhs_type << "> " << lhs_name << ";\n";
        break;
      default:
        ReportError(
            "Torque can only call builtins that return one or two values, not ",
            result_types.size());
564
    }
565

566 567 568
    std::string catch_name =
        PreCallableExceptionPreparation(instruction.catch_block);
    Stack<std::string> pre_call_stack = *stack;
569 570

    std::string generated_type = result_types[0]->GetGeneratedTNodeTypeName();
571 572 573 574 575
    for (const std::string& name : result_names) {
      stack->Push(name);
    }
    out() << "    " << lhs_name << " = ";
    out() << "ca_.CallStub<" << lhs_type
576
          << ">(Builtins::CallableFor(ca_.isolate(), Builtin::k"
577
          << instruction.builtin->ExternalName() << ")";
578 579 580 581 582 583
    if (!instruction.builtin->signature().HasContextParameter()) {
      // Add dummy context parameter to satisfy the CallBuiltin signature.
      out() << ", TNode<Object>()";
    }
    for (const std::string& argument : arguments) {
      out() << ", " << argument;
584
    }
585 586
    out() << ");\n";

587 588 589 590 591 592 593
    if (result_types.size() > 1) {
      for (size_t i = 0; i < result_types.size(); ++i) {
        out() << "    " << result_names[i] << " = ca_.Projection<" << i << ">("
              << lhs_name << ");\n";
      }
    }

594 595 596
    PostCallableExceptionPreparation(
        catch_name,
        result_types.size() == 0 ? TypeOracle::GetVoidType() : result_types[0],
597 598
        instruction.catch_block, &pre_call_stack,
        instruction.GetExceptionObjectDefinition());
599 600 601 602 603 604
  }
}

void CSAGenerator::EmitInstruction(
    const CallBuiltinPointerInstruction& instruction,
    Stack<std::string>* stack) {
605 606
  std::vector<std::string> arguments = stack->PopMany(instruction.argc);
  std::string function = stack->Pop();
607
  std::vector<const Type*> result_types =
608
      LowerType(instruction.type->return_type());
609 610 611 612
  if (result_types.size() != 1) {
    ReportError("builtins must have exactly one result");
  }
  if (instruction.is_tailcall) {
613
    ReportError("tail-calls to builtin pointers are not supported");
614
  }
615

616 617
  DCHECK_EQ(1, instruction.GetValueDefinitionCount());
  stack->Push(DefinitionToVariable(instruction.GetValueDefinition(0)));
618
  std::string generated_type = result_types[0]->GetGeneratedTNodeTypeName();
619 620 621 622 623 624 625
  decls() << "  TNode<" << generated_type << "> " << stack->Top() << ";\n";
  out() << stack->Top() << " = ";
  if (generated_type != "Object") out() << "TORQUE_CAST(";
  out() << "CodeStubAssembler(state_).CallBuiltinPointer(Builtins::"
           "CallableFor(ca_."
           "isolate(),"
           "ExampleBuiltinForTorqueFunctionPointerType("
626 627 628 629 630 631 632 633 634
        << instruction.type->function_pointer_type_id() << ")).descriptor(), "
        << function;
  if (!instruction.type->HasContextParameter()) {
    // Add dummy context parameter to satisfy the CallBuiltinPointer signature.
    out() << ", TNode<Object>()";
  }
  for (const std::string& argument : arguments) {
    out() << ", " << argument;
  }
635 636 637
  out() << ")";
  if (generated_type != "Object") out() << ")";
  out() << ";\n";
638 639
}

640 641 642 643 644
std::string CSAGenerator::PreCallableExceptionPreparation(
    base::Optional<Block*> catch_block) {
  std::string catch_name;
  if (catch_block) {
    catch_name = FreshCatchName();
645 646
    out() << "    compiler::CodeAssemblerExceptionHandlerLabel " << catch_name
          << "__label(&ca_, compiler::CodeAssemblerLabel::kDeferred);\n";
647 648
    out() << "    { compiler::ScopedExceptionHandler s(&ca_, &" << catch_name
          << "__label);\n";
649 650 651 652 653 654
  }
  return catch_name;
}

void CSAGenerator::PostCallableExceptionPreparation(
    const std::string& catch_name, const Type* return_type,
655 656
    base::Optional<Block*> catch_block, Stack<std::string>* stack,
    const base::Optional<DefinitionLocation>& exception_object_definition) {
657
  if (catch_block) {
658
    DCHECK(exception_object_definition);
659
    std::string block_name = BlockName(*catch_block);
660 661 662 663
    out() << "    }\n";
    out() << "    if (" << catch_name << "__label.is_used()) {\n";
    out() << "      compiler::CodeAssemblerLabel " << catch_name
          << "_skip(&ca_);\n";
664
    if (!return_type->IsNever()) {
665
      out() << "      ca_.Goto(&" << catch_name << "_skip);\n";
666
    }
667 668 669 670 671 672 673 674
    decls() << "      TNode<Object> "
            << DefinitionToVariable(*exception_object_definition) << ";\n";
    out() << "      ca_.Bind(&" << catch_name << "__label, &"
          << DefinitionToVariable(*exception_object_definition) << ");\n";
    out() << "      ca_.Goto(&" << block_name;

    DCHECK_EQ(stack->Size() + 1, (*catch_block)->InputDefinitions().Size());
    const auto& input_definitions = (*catch_block)->InputDefinitions();
675
    for (BottomOffset i = {0}; i < input_definitions.AboveTop(); ++i) {
676 677 678 679 680 681 682 683
      if (input_definitions.Peek(i).IsPhiFromBlock(*catch_block)) {
        if (i < stack->AboveTop()) {
          out() << ", " << stack->Peek(i);
        } else {
          DCHECK_EQ(i, stack->AboveTop());
          out() << ", " << DefinitionToVariable(*exception_object_definition);
        }
      }
684
    }
685 686
    out() << ");\n";

687
    if (!return_type->IsNever()) {
688
      out() << "      ca_.Bind(&" << catch_name << "_skip);\n";
689
    }
690
    out() << "    }\n";
691 692 693
  }
}

694 695 696
void CSAGenerator::EmitInstruction(const CallRuntimeInstruction& instruction,
                                   Stack<std::string>* stack) {
  std::vector<std::string> arguments = stack->PopMany(instruction.argc);
697 698 699 700 701 702
  const Type* return_type =
      instruction.runtime_function->signature().return_type;
  std::vector<const Type*> result_types;
  if (return_type != TypeOracle::GetNeverType()) {
    result_types = LowerType(return_type);
  }
703 704 705 706
  if (result_types.size() > 1) {
    ReportError("runtime function must have at most one result");
  }
  if (instruction.is_tailcall) {
707 708 709 710
    out() << "    CodeStubAssembler(state_).TailCallRuntime(Runtime::k"
          << instruction.runtime_function->ExternalName() << ", ";
    PrintCommaSeparatedList(out(), arguments);
    out() << ");\n";
711
  } else {
712
    std::string result_name;
713
    if (result_types.size() == 1) {
714 715 716
      result_name = DefinitionToVariable(instruction.GetValueDefinition(0));
      decls() << "  TNode<" << result_types[0]->GetGeneratedTNodeTypeName()
              << "> " << result_name << ";\n";
717 718 719 720 721
    }
    std::string catch_name =
        PreCallableExceptionPreparation(instruction.catch_block);
    Stack<std::string> pre_call_stack = *stack;
    if (result_types.size() == 1) {
722
      std::string generated_type = result_types[0]->GetGeneratedTNodeTypeName();
723
      stack->Push(result_name);
724 725 726 727 728 729 730 731
      out() << "    " << result_name << " = ";
      if (generated_type != "Object") out() << "TORQUE_CAST(";
      out() << "CodeStubAssembler(state_).CallRuntime(Runtime::k"
            << instruction.runtime_function->ExternalName() << ", ";
      PrintCommaSeparatedList(out(), arguments);
      out() << ")";
      if (generated_type != "Object") out() << ")";
      out() << "; \n";
732 733
    } else {
      DCHECK_EQ(0, result_types.size());
734 735 736 737
      out() << "    CodeStubAssembler(state_).CallRuntime(Runtime::k"
            << instruction.runtime_function->ExternalName() << ", ";
      PrintCommaSeparatedList(out(), arguments);
      out() << ");\n";
738
      if (return_type == TypeOracle::GetNeverType()) {
739
        out() << "    CodeStubAssembler(state_).Unreachable();\n";
740 741 742
      } else {
        DCHECK(return_type == TypeOracle::GetVoidType());
      }
743
    }
744 745 746
    PostCallableExceptionPreparation(
        catch_name, return_type, instruction.catch_block, &pre_call_stack,
        instruction.GetExceptionObjectDefinition());
747 748 749 750 751
  }
}

void CSAGenerator::EmitInstruction(const BranchInstruction& instruction,
                                   Stack<std::string>* stack) {
752
  out() << "    ca_.Branch(" << stack->Pop() << ", &"
753
        << BlockName(instruction.if_true) << ", std::vector<compiler::Node*>{";
754 755 756 757

  const auto& true_definitions = instruction.if_true->InputDefinitions();
  DCHECK_EQ(stack->Size(), true_definitions.Size());
  bool first = true;
758
  for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) {
759 760 761 762 763
    if (true_definitions.Peek(i).IsPhiFromBlock(instruction.if_true)) {
      if (!first) out() << ", ";
      out() << stack->Peek(i);
      first = false;
    }
764
  }
765

766 767
  out() << "}, &" << BlockName(instruction.if_false)
        << ", std::vector<compiler::Node*>{";
768 769 770 771

  const auto& false_definitions = instruction.if_false->InputDefinitions();
  DCHECK_EQ(stack->Size(), false_definitions.Size());
  first = true;
772
  for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) {
773 774 775 776 777 778 779 780
    if (false_definitions.Peek(i).IsPhiFromBlock(instruction.if_false)) {
      if (!first) out() << ", ";
      out() << stack->Peek(i);
      first = false;
    }
  }

  out() << "});\n";
781 782 783 784
}

void CSAGenerator::EmitInstruction(
    const ConstexprBranchInstruction& instruction, Stack<std::string>* stack) {
785 786 787 788 789
  out() << "    if ((" << instruction.condition << ")) {\n";
  out() << "      ca_.Goto(&" << BlockName(instruction.if_true);

  const auto& true_definitions = instruction.if_true->InputDefinitions();
  DCHECK_EQ(stack->Size(), true_definitions.Size());
790
  for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) {
791 792 793
    if (true_definitions.Peek(i).IsPhiFromBlock(instruction.if_true)) {
      out() << ", " << stack->Peek(i);
    }
794
  }
795 796 797 798 799 800 801

  out() << ");\n";
  out() << "    } else {\n";
  out() << "      ca_.Goto(&" << BlockName(instruction.if_false);

  const auto& false_definitions = instruction.if_false->InputDefinitions();
  DCHECK_EQ(stack->Size(), false_definitions.Size());
802
  for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) {
803 804 805
    if (false_definitions.Peek(i).IsPhiFromBlock(instruction.if_false)) {
      out() << ", " << stack->Peek(i);
    }
806 807
  }

808 809
  out() << ");\n";
  out() << "    }\n";
810 811 812 813
}

void CSAGenerator::EmitInstruction(const GotoInstruction& instruction,
                                   Stack<std::string>* stack) {
814 815 816 817
  out() << "    ca_.Goto(&" << BlockName(instruction.destination);
  const auto& destination_definitions =
      instruction.destination->InputDefinitions();
  DCHECK_EQ(stack->Size(), destination_definitions.Size());
818
  for (BottomOffset i = {0}; i < stack->AboveTop(); ++i) {
819 820 821 822
    if (destination_definitions.Peek(i).IsPhiFromBlock(
            instruction.destination)) {
      out() << ", " << stack->Peek(i);
    }
823
  }
824
  out() << ");\n";
825 826 827 828 829 830
}

void CSAGenerator::EmitInstruction(const GotoExternalInstruction& instruction,
                                   Stack<std::string>* stack) {
  for (auto it = instruction.variable_names.rbegin();
       it != instruction.variable_names.rend(); ++it) {
831
    out() << "    *" << *it << " = " << stack->Pop() << ";\n";
832
  }
833
  out() << "    ca_.Goto(" << instruction.destination << ");\n";
834 835 836 837 838
}

void CSAGenerator::EmitInstruction(const ReturnInstruction& instruction,
                                   Stack<std::string>* stack) {
  if (*linkage_ == Builtin::kVarArgsJavaScript) {
839
    out() << "    " << ARGUMENTS_VARIABLE_STRING << ".PopAndReturn(";
840
  } else {
841
    out() << "    CodeStubAssembler(state_).Return(";
842
  }
843 844 845
  std::vector<std::string> values = stack->PopMany(instruction.count);
  PrintCommaSeparatedList(out(), values);
  out() << ");\n";
846 847 848 849 850
}

void CSAGenerator::EmitInstruction(
    const PrintConstantStringInstruction& instruction,
    Stack<std::string>* stack) {
851 852
  out() << "    CodeStubAssembler(state_).Print("
        << StringLiteralQuote(instruction.message) << ");\n";
853 854
}

855
void CSAGenerator::EmitInstruction(const AbortInstruction& instruction,
856
                                   Stack<std::string>* stack) {
857 858 859
  switch (instruction.kind) {
    case AbortInstruction::Kind::kUnreachable:
      DCHECK(instruction.message.empty());
860
      out() << "    CodeStubAssembler(state_).Unreachable();\n";
861 862 863
      break;
    case AbortInstruction::Kind::kDebugBreak:
      DCHECK(instruction.message.empty());
864
      out() << "    CodeStubAssembler(state_).DebugBreak();\n";
865 866
      break;
    case AbortInstruction::Kind::kAssertionFailure: {
867 868
      std::string file = StringLiteralQuote(
          SourceFileMap::PathFromV8Root(instruction.pos.source));
869 870 871 872 873 874 875
      out() << "    {\n";
      out() << "      auto pos_stack = ca_.GetMacroSourcePositionStack();\n";
      out() << "      pos_stack.push_back({" << file << ", "
            << instruction.pos.start.line + 1 << "});\n";
      out() << "      CodeStubAssembler(state_).FailAssert("
            << StringLiteralQuote(instruction.message) << ", pos_stack);\n";
      out() << "    }\n";
876 877
      break;
    }
878 879 880 881 882
  }
}

void CSAGenerator::EmitInstruction(const UnsafeCastInstruction& instruction,
                                   Stack<std::string>* stack) {
883 884 885 886 887 888
  const std::string str =
      "ca_.UncheckedCast<" +
      instruction.destination_type->GetGeneratedTNodeTypeName() + ">(" +
      stack->Top() + ")";
  stack->Poke(stack->AboveTop() - 1, str);
  SetDefinitionVariable(instruction.GetValueDefinition(), str);
889 890
}

891 892
void CSAGenerator::EmitInstruction(const LoadReferenceInstruction& instruction,
                                   Stack<std::string>* stack) {
893 894
  std::string result_name =
      DefinitionToVariable(instruction.GetValueDefinition());
895 896 897 898 899

  std::string offset = stack->Pop();
  std::string object = stack->Pop();
  stack->Push(result_name);

900 901 902 903 904 905 906
  decls() << "  " << instruction.type->GetGeneratedTypeName() << " "
          << result_name << ";\n";
  out() << "    " << result_name
        << " = CodeStubAssembler(state_).LoadReference<"
        << instruction.type->GetGeneratedTNodeTypeName()
        << ">(CodeStubAssembler::Reference{" << object << ", " << offset
        << "});\n";
907 908 909 910 911 912 913 914
}

void CSAGenerator::EmitInstruction(const StoreReferenceInstruction& instruction,
                                   Stack<std::string>* stack) {
  std::string value = stack->Pop();
  std::string offset = stack->Pop();
  std::string object = stack->Pop();

915 916 917 918 919
  out() << "    CodeStubAssembler(state_).StoreReference<"
        << instruction.type->GetGeneratedTNodeTypeName()
        << ">(CodeStubAssembler::"
           "Reference{"
        << object << ", " << offset << "}, " << value << ");\n";
920 921
}

922
namespace {
923
std::string GetBitFieldSpecialization(const Type* container,
924
                                      const BitField& field) {
925 926 927 928 929 930 931 932
  auto smi_tagged_type =
      Type::MatchUnaryGeneric(container, TypeOracle::GetSmiTaggedGeneric());
  std::string container_type = smi_tagged_type
                                   ? "uintptr_t"
                                   : container->GetConstexprGeneratedTypeName();
  int offset = smi_tagged_type
                   ? field.offset + TargetArchitecture::SmiTagAndShiftSize()
                   : field.offset;
933 934 935
  std::stringstream stream;
  stream << "base::BitField<"
         << field.name_and_type.type->GetConstexprGeneratedTypeName() << ", "
936
         << offset << ", " << field.num_bits << ", " << container_type << ">";
937
  return stream.str();
938 939 940 941 942
}
}  // namespace

void CSAGenerator::EmitInstruction(const LoadBitFieldInstruction& instruction,
                                   Stack<std::string>* stack) {
943 944
  std::string result_name =
      DefinitionToVariable(instruction.GetValueDefinition());
945 946 947 948

  std::string bit_field_struct = stack->Pop();
  stack->Push(result_name);

949 950 951 952 953 954 955 956 957 958
  const Type* struct_type = instruction.bit_field_struct_type;
  const Type* field_type = instruction.bit_field.name_and_type.type;
  auto smi_tagged_type =
      Type::MatchUnaryGeneric(struct_type, TypeOracle::GetSmiTaggedGeneric());
  bool struct_is_pointer_size =
      IsPointerSizeIntegralType(struct_type) || smi_tagged_type;
  DCHECK_IMPLIES(!struct_is_pointer_size, Is32BitIntegralType(struct_type));
  bool field_is_pointer_size = IsPointerSizeIntegralType(field_type);
  DCHECK_IMPLIES(!field_is_pointer_size, Is32BitIntegralType(field_type));
  std::string struct_word_type = struct_is_pointer_size ? "WordT" : "Word32T";
959
  std::string decoder =
960 961 962
      struct_is_pointer_size
          ? (field_is_pointer_size ? "DecodeWord" : "DecodeWord32FromWord")
          : (field_is_pointer_size ? "DecodeWordFromWord32" : "DecodeWord32");
963

964
  decls() << "  " << field_type->GetGeneratedTypeName() << " " << result_name
965
          << ";\n";
966 967 968 969 970 971 972 973

  if (smi_tagged_type) {
    // If the container is a SMI, then UncheckedCast is insufficient and we must
    // use a bit cast.
    bit_field_struct =
        "ca_.BitcastTaggedToWordForTagAndSmiBits(" + bit_field_struct + ")";
  }

974
  out() << "    " << result_name << " = ca_.UncheckedCast<"
975
        << field_type->GetGeneratedTNodeTypeName()
976
        << ">(CodeStubAssembler(state_)." << decoder << "<"
977 978
        << GetBitFieldSpecialization(struct_type, instruction.bit_field)
        << ">(ca_.UncheckedCast<" << struct_word_type << ">("
979
        << bit_field_struct << ")));\n";
980 981 982 983
}

void CSAGenerator::EmitInstruction(const StoreBitFieldInstruction& instruction,
                                   Stack<std::string>* stack) {
984 985
  std::string result_name =
      DefinitionToVariable(instruction.GetValueDefinition());
986 987 988 989 990

  std::string value = stack->Pop();
  std::string bit_field_struct = stack->Pop();
  stack->Push(result_name);

991
  const Type* struct_type = instruction.bit_field_struct_type;
992
  const Type* field_type = instruction.bit_field.name_and_type.type;
993 994 995 996 997 998 999 1000 1001
  auto smi_tagged_type =
      Type::MatchUnaryGeneric(struct_type, TypeOracle::GetSmiTaggedGeneric());
  bool struct_is_pointer_size =
      IsPointerSizeIntegralType(struct_type) || smi_tagged_type;
  DCHECK_IMPLIES(!struct_is_pointer_size, Is32BitIntegralType(struct_type));
  bool field_is_pointer_size = IsPointerSizeIntegralType(field_type);
  DCHECK_IMPLIES(!field_is_pointer_size, Is32BitIntegralType(field_type));
  std::string struct_word_type = struct_is_pointer_size ? "WordT" : "Word32T";
  std::string field_word_type = field_is_pointer_size ? "UintPtrT" : "Uint32T";
1002
  std::string encoder =
1003 1004 1005
      struct_is_pointer_size
          ? (field_is_pointer_size ? "UpdateWord" : "UpdateWord32InWord")
          : (field_is_pointer_size ? "UpdateWordInWord32" : "UpdateWord32");
1006

1007 1008
  decls() << "  " << struct_type->GetGeneratedTypeName() << " " << result_name
          << ";\n";
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020

  if (smi_tagged_type) {
    // If the container is a SMI, then UncheckedCast is insufficient and we must
    // use a bit cast.
    bit_field_struct =
        "ca_.BitcastTaggedToWordForTagAndSmiBits(" + bit_field_struct + ")";
  }

  std::string result_expression =
      "CodeStubAssembler(state_)." + encoder + "<" +
      GetBitFieldSpecialization(struct_type, instruction.bit_field) +
      ">(ca_.UncheckedCast<" + struct_word_type + ">(" + bit_field_struct +
1021 1022
      "), ca_.UncheckedCast<" + field_word_type + ">(" + value + ")" +
      (instruction.starts_as_zero ? ", true" : "") + ")";
1023 1024 1025 1026 1027 1028

  if (smi_tagged_type) {
    result_expression =
        "ca_.BitcastWordToTaggedSigned(" + result_expression + ")";
  }

1029
  out() << "    " << result_name << " = ca_.UncheckedCast<"
1030 1031
        << struct_type->GetGeneratedTNodeTypeName() << ">(" << result_expression
        << ");\n";
1032 1033
}

1034 1035 1036 1037 1038 1039
// static
void CSAGenerator::EmitCSAValue(VisitResult result,
                                const Stack<std::string>& values,
                                std::ostream& out) {
  if (!result.IsOnStack()) {
    out << result.constexpr_value();
1040 1041
  } else if (auto struct_type = result.type()->StructSupertype()) {
    out << (*struct_type)->GetGeneratedTypeName() << "{";
1042
    bool first = true;
1043
    for (auto& field : (*struct_type)->fields()) {
1044 1045 1046 1047
      if (!first) {
        out << ", ";
      }
      first = false;
1048 1049
      EmitCSAValue(ProjectStructField(result, field.name_and_type.name), values,
                   out);
1050 1051 1052 1053
    }
    out << "}";
  } else {
    DCHECK_EQ(1, result.stack_range().Size());
1054
    out << result.type()->GetGeneratedTypeName() << "{"
1055
        << values.Peek(result.stack_range().begin()) << "}";
1056 1057 1058 1059 1060 1061
  }
}

}  // namespace torque
}  // namespace internal
}  // namespace v8