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 #ifndef V8_TORQUE_INSTRUCTIONS_H_
6 #define V8_TORQUE_INSTRUCTIONS_H_
7 
8 #include <memory>
9 
10 #include "src/torque/ast.h"
11 #include "src/torque/source-positions.h"
12 #include "src/torque/types.h"
13 #include "src/torque/utils.h"
14 
15 namespace v8 {
16 namespace internal {
17 namespace torque {
18 
19 class Block;
20 class Builtin;
21 class ControlFlowGraph;
22 class Intrinsic;
23 class Macro;
24 class NamespaceConstant;
25 class RuntimeFunction;
26 
27 // Instructions where all backends generate code the same way.
28 #define TORQUE_BACKEND_AGNOSTIC_INSTRUCTION_LIST(V) \
29   V(PeekInstruction)                                \
30   V(PokeInstruction)                                \
31   V(DeleteRangeInstruction)
32 
33 // Instructions where different backends may generate different code.
34 #define TORQUE_BACKEND_DEPENDENT_INSTRUCTION_LIST(V) \
35   V(PushUninitializedInstruction)                    \
36   V(PushBuiltinPointerInstruction)                   \
37   V(LoadReferenceInstruction)                        \
38   V(StoreReferenceInstruction)                       \
39   V(LoadBitFieldInstruction)                         \
40   V(StoreBitFieldInstruction)                        \
41   V(CallCsaMacroInstruction)                         \
42   V(CallIntrinsicInstruction)                        \
43   V(NamespaceConstantInstruction)                    \
44   V(CallCsaMacroAndBranchInstruction)                \
45   V(CallBuiltinInstruction)                          \
46   V(CallRuntimeInstruction)                          \
47   V(CallBuiltinPointerInstruction)                   \
48   V(BranchInstruction)                               \
49   V(ConstexprBranchInstruction)                      \
50   V(GotoInstruction)                                 \
51   V(GotoExternalInstruction)                         \
52   V(MakeLazyNodeInstruction)                         \
53   V(ReturnInstruction)                               \
54   V(PrintConstantStringInstruction)                  \
55   V(AbortInstruction)                                \
56   V(UnsafeCastInstruction)
57 
58 #define TORQUE_INSTRUCTION_LIST(V)            \
59   TORQUE_BACKEND_AGNOSTIC_INSTRUCTION_LIST(V) \
60   TORQUE_BACKEND_DEPENDENT_INSTRUCTION_LIST(V)
61 
62 #define TORQUE_INSTRUCTION_BOILERPLATE()                                  \
63   static const InstructionKind kKind;                                     \
64   std::unique_ptr<InstructionBase> Clone() const override;                \
65   void Assign(const InstructionBase& other) override;                     \
66   void TypeInstruction(Stack<const Type*>* stack, ControlFlowGraph* cfg)  \
67       const override;                                                     \
68   void RecomputeDefinitionLocations(Stack<DefinitionLocation>* locations, \
69                                     Worklist<Block*>* worklist)           \
70       const override;
71 
72 enum class InstructionKind {
73 #define ENUM_ITEM(name) k##name,
74   TORQUE_INSTRUCTION_LIST(ENUM_ITEM)
75 #undef ENUM_ITEM
76 };
77 
78 struct InstructionBase;
79 
80 class DefinitionLocation {
81  public:
82   enum class Kind {
83     kInvalid,
84     kParameter,
85     kPhi,
86     kInstruction,
87   };
88 
DefinitionLocation()89   DefinitionLocation() : kind_(Kind::kInvalid), location_(nullptr), index_(0) {}
90 
Parameter(std::size_t index)91   static DefinitionLocation Parameter(std::size_t index) {
92     return DefinitionLocation(Kind::kParameter, nullptr, index);
93   }
94 
Phi(const Block * block,std::size_t index)95   static DefinitionLocation Phi(const Block* block, std::size_t index) {
96     return DefinitionLocation(Kind::kPhi, block, index);
97   }
98 
99   static DefinitionLocation Instruction(const InstructionBase* instruction,
100                                         std::size_t index = 0) {
101     return DefinitionLocation(Kind::kInstruction, instruction, index);
102   }
103 
GetKind()104   Kind GetKind() const { return kind_; }
IsValid()105   bool IsValid() const { return kind_ != Kind::kInvalid; }
IsParameter()106   bool IsParameter() const { return kind_ == Kind::kParameter; }
IsPhi()107   bool IsPhi() const { return kind_ == Kind::kPhi; }
IsInstruction()108   bool IsInstruction() const { return kind_ == Kind::kInstruction; }
109 
GetParameterIndex()110   std::size_t GetParameterIndex() const {
111     DCHECK(IsParameter());
112     return index_;
113   }
114 
GetPhiBlock()115   const Block* GetPhiBlock() const {
116     DCHECK(IsPhi());
117     return reinterpret_cast<const Block*>(location_);
118   }
119 
IsPhiFromBlock(const Block * block)120   bool IsPhiFromBlock(const Block* block) const {
121     return IsPhi() && GetPhiBlock() == block;
122   }
123 
GetPhiIndex()124   std::size_t GetPhiIndex() const {
125     DCHECK(IsPhi());
126     return index_;
127   }
128 
GetInstruction()129   const InstructionBase* GetInstruction() const {
130     DCHECK(IsInstruction());
131     return reinterpret_cast<const InstructionBase*>(location_);
132   }
133 
GetInstructionIndex()134   std::size_t GetInstructionIndex() const {
135     DCHECK(IsInstruction());
136     return index_;
137   }
138 
139   bool operator==(const DefinitionLocation& other) const {
140     if (kind_ != other.kind_) return false;
141     if (location_ != other.location_) return false;
142     return index_ == other.index_;
143   }
144 
145   bool operator!=(const DefinitionLocation& other) const {
146     return !operator==(other);
147   }
148 
149   bool operator<(const DefinitionLocation& other) const {
150     if (kind_ != other.kind_) {
151       return static_cast<int>(kind_) < static_cast<int>(other.kind_);
152     }
153     if (location_ != other.location_) {
154       return location_ < other.location_;
155     }
156     return index_ < other.index_;
157   }
158 
159  private:
DefinitionLocation(Kind kind,const void * location,std::size_t index)160   DefinitionLocation(Kind kind, const void* location, std::size_t index)
161       : kind_(kind), location_(location), index_(index) {}
162 
163   Kind kind_;
164   const void* location_;
165   std::size_t index_;
166 };
167 
168 inline std::ostream& operator<<(std::ostream& stream,
169                                 const DefinitionLocation& loc) {
170   switch (loc.GetKind()) {
171     case DefinitionLocation::Kind::kInvalid:
172       return stream << "DefinitionLocation::Invalid()";
173     case DefinitionLocation::Kind::kParameter:
174       return stream << "DefinitionLocation::Parameter("
175                     << loc.GetParameterIndex() << ")";
176     case DefinitionLocation::Kind::kPhi:
177       return stream << "DefinitionLocation::Phi(" << std::hex
178                     << loc.GetPhiBlock() << std::dec << ", "
179                     << loc.GetPhiIndex() << ")";
180     case DefinitionLocation::Kind::kInstruction:
181       return stream << "DefinitionLocation::Instruction(" << std::hex
182                     << loc.GetInstruction() << std::dec << ", "
183                     << loc.GetInstructionIndex() << ")";
184   }
185 }
186 
187 struct InstructionBase {
InstructionBaseInstructionBase188   InstructionBase() : pos(CurrentSourcePosition::Get()) {}
189   virtual std::unique_ptr<InstructionBase> Clone() const = 0;
190   virtual void Assign(const InstructionBase& other) = 0;
191   virtual ~InstructionBase() = default;
192 
193   virtual void TypeInstruction(Stack<const Type*>* stack,
194                                ControlFlowGraph* cfg) const = 0;
195   virtual void RecomputeDefinitionLocations(
196       Stack<DefinitionLocation>* locations,
197       Worklist<Block*>* worklist) const = 0;
198   void InvalidateTransientTypes(Stack<const Type*>* stack) const;
IsBlockTerminatorInstructionBase199   virtual bool IsBlockTerminator() const { return false; }
AppendSuccessorBlocksInstructionBase200   virtual void AppendSuccessorBlocks(std::vector<Block*>* block_list) const {}
201 
202   SourcePosition pos;
203 };
204 
205 class Instruction {
206  public:
207   template <class T>
Instruction(T instr)208   Instruction(T instr)  // NOLINT(runtime/explicit)
209       : kind_(T::kKind), instruction_(new T(std::move(instr))) {}
210 
211   template <class T>
Cast()212   T& Cast() {
213     DCHECK(Is<T>());
214     return static_cast<T&>(*instruction_);
215   }
216 
217   template <class T>
Cast()218   const T& Cast() const {
219     DCHECK(Is<T>());
220     return static_cast<const T&>(*instruction_);
221   }
222 
223   template <class T>
Is()224   bool Is() const {
225     return kind_ == T::kKind;
226   }
227 
228   template <class T>
DynamicCast()229   T* DynamicCast() {
230     if (Is<T>()) return &Cast<T>();
231     return nullptr;
232   }
233 
234   template <class T>
DynamicCast()235   const T* DynamicCast() const {
236     if (Is<T>()) return &Cast<T>();
237     return nullptr;
238   }
239 
Instruction(const Instruction & other)240   Instruction(const Instruction& other) V8_NOEXCEPT
241       : kind_(other.kind_),
242         instruction_(other.instruction_->Clone()) {}
243   Instruction& operator=(const Instruction& other) V8_NOEXCEPT {
244     if (kind_ == other.kind_) {
245       instruction_->Assign(*other.instruction_);
246     } else {
247       kind_ = other.kind_;
248       instruction_ = other.instruction_->Clone();
249     }
250     return *this;
251   }
252 
kind()253   InstructionKind kind() const { return kind_; }
Mnemonic()254   const char* Mnemonic() const {
255     switch (kind()) {
256 #define ENUM_ITEM(name)          \
257   case InstructionKind::k##name: \
258     return #name;
259       TORQUE_INSTRUCTION_LIST(ENUM_ITEM)
260 #undef ENUM_ITEM
261       default:
262         UNREACHABLE();
263     }
264   }
TypeInstruction(Stack<const Type * > * stack,ControlFlowGraph * cfg)265   void TypeInstruction(Stack<const Type*>* stack, ControlFlowGraph* cfg) const {
266     return instruction_->TypeInstruction(stack, cfg);
267   }
RecomputeDefinitionLocations(Stack<DefinitionLocation> * locations,Worklist<Block * > * worklist)268   void RecomputeDefinitionLocations(Stack<DefinitionLocation>* locations,
269                                     Worklist<Block*>* worklist) const {
270     instruction_->RecomputeDefinitionLocations(locations, worklist);
271   }
272 
273   InstructionBase* operator->() { return instruction_.get(); }
274   const InstructionBase* operator->() const { return instruction_.get(); }
275 
276  private:
277   InstructionKind kind_;
278   std::unique_ptr<InstructionBase> instruction_;
279 };
280 
281 struct PeekInstruction : InstructionBase {
282   TORQUE_INSTRUCTION_BOILERPLATE()
283 
PeekInstructionPeekInstruction284   PeekInstruction(BottomOffset slot, base::Optional<const Type*> widened_type)
285       : slot(slot), widened_type(widened_type) {}
286 
287   BottomOffset slot;
288   base::Optional<const Type*> widened_type;
289 };
290 
291 inline std::ostream& operator<<(std::ostream& os,
292                                 const PeekInstruction& instruction) {
293   os << "Peek " << instruction.slot;
294   if (instruction.widened_type) {
295     os << ", " << **instruction.widened_type;
296   }
297   return os;
298 }
299 
300 struct PokeInstruction : InstructionBase {
301   TORQUE_INSTRUCTION_BOILERPLATE()
302 
PokeInstructionPokeInstruction303   PokeInstruction(BottomOffset slot, base::Optional<const Type*> widened_type)
304       : slot(slot), widened_type(widened_type) {}
305 
306   BottomOffset slot;
307   base::Optional<const Type*> widened_type;
308 };
309 
310 inline std::ostream& operator<<(std::ostream& os,
311                                 const PokeInstruction& instruction) {
312   os << "Poke " << instruction.slot;
313   if (instruction.widened_type) {
314     os << ", " << **instruction.widened_type;
315   }
316   return os;
317 }
318 
319 // Preserve the top {preserved_slots} number of slots, and delete
320 // {deleted_slots} number or slots below.
321 struct DeleteRangeInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATEDeleteRangeInstruction322   TORQUE_INSTRUCTION_BOILERPLATE()
323   explicit DeleteRangeInstruction(StackRange range) : range(range) {}
324 
325   StackRange range;
326 };
327 
328 inline std::ostream& operator<<(std::ostream& os,
329                                 const DeleteRangeInstruction& instruction) {
330   return os << "DeleteRange " << instruction.range;
331 }
332 
333 struct PushUninitializedInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATEPushUninitializedInstruction334   TORQUE_INSTRUCTION_BOILERPLATE()
335   explicit PushUninitializedInstruction(const Type* type) : type(type) {}
336 
337   DefinitionLocation GetValueDefinition() const;
338 
339   const Type* type;
340 };
341 
342 inline std::ostream& operator<<(
343     std::ostream& os, const PushUninitializedInstruction& instruction) {
344   return os << "PushUninitialized " << *instruction.type;
345 }
346 
347 struct PushBuiltinPointerInstruction : InstructionBase {
348   TORQUE_INSTRUCTION_BOILERPLATE()
PushBuiltinPointerInstructionPushBuiltinPointerInstruction349   PushBuiltinPointerInstruction(std::string external_name, const Type* type)
350       : external_name(std::move(external_name)), type(type) {
351     DCHECK(type->IsBuiltinPointerType());
352   }
353 
354   DefinitionLocation GetValueDefinition() const;
355 
356   std::string external_name;
357   const Type* type;
358 };
359 
360 inline std::ostream& operator<<(
361     std::ostream& os, const PushBuiltinPointerInstruction& instruction) {
362   return os << "PushBuiltinPointer "
363             << StringLiteralQuote(instruction.external_name) << ", "
364             << *instruction.type;
365 }
366 
367 struct NamespaceConstantInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATENamespaceConstantInstruction368   TORQUE_INSTRUCTION_BOILERPLATE()
369   explicit NamespaceConstantInstruction(NamespaceConstant* constant)
370       : constant(constant) {}
371 
372   std::size_t GetValueDefinitionCount() const;
373   DefinitionLocation GetValueDefinition(std::size_t index) const;
374 
375   NamespaceConstant* constant;
376 };
377 
378 std::ostream& operator<<(std::ostream& os,
379                          const NamespaceConstantInstruction& instruction);
380 
381 struct LoadReferenceInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATELoadReferenceInstruction382   TORQUE_INSTRUCTION_BOILERPLATE()
383   explicit LoadReferenceInstruction(const Type* type) : type(type) {}
384 
385   DefinitionLocation GetValueDefinition() const;
386 
387   const Type* type;
388 };
389 
390 inline std::ostream& operator<<(std::ostream& os,
391                                 const LoadReferenceInstruction& instruction) {
392   return os << "LoadReference " << *instruction.type;
393 }
394 
395 struct StoreReferenceInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATEStoreReferenceInstruction396   TORQUE_INSTRUCTION_BOILERPLATE()
397   explicit StoreReferenceInstruction(const Type* type) : type(type) {}
398   const Type* type;
399 };
400 
401 inline std::ostream& operator<<(std::ostream& os,
402                                 const StoreReferenceInstruction& instruction) {
403   return os << "StoreReference " << *instruction.type;
404 }
405 
406 // Pops a bitfield struct; pushes a bitfield value extracted from it.
407 struct LoadBitFieldInstruction : InstructionBase {
408   TORQUE_INSTRUCTION_BOILERPLATE()
LoadBitFieldInstructionLoadBitFieldInstruction409   LoadBitFieldInstruction(const Type* bit_field_struct_type, BitField bit_field)
410       : bit_field_struct_type(bit_field_struct_type),
411         bit_field(std::move(bit_field)) {}
412 
413   DefinitionLocation GetValueDefinition() const;
414 
415   const Type* bit_field_struct_type;
416   BitField bit_field;
417 };
418 
419 inline std::ostream& operator<<(std::ostream& os,
420                                 const LoadBitFieldInstruction& instruction) {
421   return os << "LoadBitField " << *instruction.bit_field_struct_type << ", "
422             << instruction.bit_field.name_and_type.name;
423 }
424 
425 // Pops a bitfield value and a bitfield struct; pushes a new bitfield struct
426 // containing the updated value.
427 struct StoreBitFieldInstruction : InstructionBase {
428   TORQUE_INSTRUCTION_BOILERPLATE()
StoreBitFieldInstructionStoreBitFieldInstruction429   StoreBitFieldInstruction(const Type* bit_field_struct_type,
430                            BitField bit_field, bool starts_as_zero)
431       : bit_field_struct_type(bit_field_struct_type),
432         bit_field(std::move(bit_field)),
433         starts_as_zero(starts_as_zero) {}
434 
435   DefinitionLocation GetValueDefinition() const;
436 
437   const Type* bit_field_struct_type;
438   BitField bit_field;
439   // Allows skipping the mask step if we know the starting value is zero.
440   bool starts_as_zero;
441 };
442 
443 inline std::ostream& operator<<(std::ostream& os,
444                                 const StoreBitFieldInstruction& instruction) {
445   os << "StoreBitField " << *instruction.bit_field_struct_type << ", "
446      << instruction.bit_field.name_and_type.name;
447   if (instruction.starts_as_zero) {
448     os << ", starts_as_zero";
449   }
450   return os;
451 }
452 
453 struct CallIntrinsicInstruction : InstructionBase {
454   TORQUE_INSTRUCTION_BOILERPLATE()
CallIntrinsicInstructionCallIntrinsicInstruction455   CallIntrinsicInstruction(Intrinsic* intrinsic,
456                            TypeVector specialization_types,
457                            std::vector<std::string> constexpr_arguments)
458       : intrinsic(intrinsic),
459         specialization_types(std::move(specialization_types)),
460         constexpr_arguments(constexpr_arguments) {}
461 
462   std::size_t GetValueDefinitionCount() const;
463   DefinitionLocation GetValueDefinition(std::size_t index) const;
464 
465   Intrinsic* intrinsic;
466   TypeVector specialization_types;
467   std::vector<std::string> constexpr_arguments;
468 };
469 
470 std::ostream& operator<<(std::ostream& os,
471                          const CallIntrinsicInstruction& instruction);
472 
473 struct CallCsaMacroInstruction : InstructionBase {
474   TORQUE_INSTRUCTION_BOILERPLATE()
CallCsaMacroInstructionCallCsaMacroInstruction475   CallCsaMacroInstruction(Macro* macro,
476                           std::vector<std::string> constexpr_arguments,
477                           base::Optional<Block*> catch_block)
478       : macro(macro),
479         constexpr_arguments(constexpr_arguments),
480         catch_block(catch_block) {}
AppendSuccessorBlocksCallCsaMacroInstruction481   void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override {
482     if (catch_block) block_list->push_back(*catch_block);
483   }
484 
485   base::Optional<DefinitionLocation> GetExceptionObjectDefinition() const;
486   std::size_t GetValueDefinitionCount() const;
487   DefinitionLocation GetValueDefinition(std::size_t index) const;
488 
489   Macro* macro;
490   std::vector<std::string> constexpr_arguments;
491   base::Optional<Block*> catch_block;
492 };
493 
494 std::ostream& operator<<(std::ostream& os,
495                          const CallCsaMacroInstruction& instruction);
496 
497 struct CallCsaMacroAndBranchInstruction : InstructionBase {
498   TORQUE_INSTRUCTION_BOILERPLATE()
CallCsaMacroAndBranchInstructionCallCsaMacroAndBranchInstruction499   CallCsaMacroAndBranchInstruction(Macro* macro,
500                                    std::vector<std::string> constexpr_arguments,
501                                    base::Optional<Block*> return_continuation,
502                                    std::vector<Block*> label_blocks,
503                                    base::Optional<Block*> catch_block)
504       : macro(macro),
505         constexpr_arguments(constexpr_arguments),
506         return_continuation(return_continuation),
507         label_blocks(label_blocks),
508         catch_block(catch_block) {}
IsBlockTerminatorCallCsaMacroAndBranchInstruction509   bool IsBlockTerminator() const override { return true; }
AppendSuccessorBlocksCallCsaMacroAndBranchInstruction510   void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override {
511     if (catch_block) block_list->push_back(*catch_block);
512     if (return_continuation) block_list->push_back(*return_continuation);
513     for (Block* block : label_blocks) block_list->push_back(block);
514   }
515 
516   std::size_t GetLabelCount() const;
517   std::size_t GetLabelValueDefinitionCount(std::size_t label) const;
518   DefinitionLocation GetLabelValueDefinition(std::size_t label,
519                                              std::size_t index) const;
520   std::size_t GetValueDefinitionCount() const;
521   DefinitionLocation GetValueDefinition(std::size_t index) const;
522   base::Optional<DefinitionLocation> GetExceptionObjectDefinition() const;
523 
524   Macro* macro;
525   std::vector<std::string> constexpr_arguments;
526   base::Optional<Block*> return_continuation;
527   std::vector<Block*> label_blocks;
528   base::Optional<Block*> catch_block;
529 };
530 
531 std::ostream& operator<<(std::ostream& os,
532                          const CallCsaMacroAndBranchInstruction& instruction);
533 
534 struct MakeLazyNodeInstruction : InstructionBase {
535   TORQUE_INSTRUCTION_BOILERPLATE()
MakeLazyNodeInstructionMakeLazyNodeInstruction536   MakeLazyNodeInstruction(Macro* macro, const Type* result_type,
537                           std::vector<std::string> constexpr_arguments)
538       : macro(macro),
539         result_type(result_type),
540         constexpr_arguments(std::move(constexpr_arguments)) {}
541 
542   DefinitionLocation GetValueDefinition() const;
543 
544   Macro* macro;
545   const Type* result_type;
546   std::vector<std::string> constexpr_arguments;
547 };
548 
549 std::ostream& operator<<(std::ostream& os,
550                          const MakeLazyNodeInstruction& instruction);
551 
552 struct CallBuiltinInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATECallBuiltinInstruction553   TORQUE_INSTRUCTION_BOILERPLATE()
554   bool IsBlockTerminator() const override { return is_tailcall; }
CallBuiltinInstructionCallBuiltinInstruction555   CallBuiltinInstruction(bool is_tailcall, Builtin* builtin, size_t argc,
556                          base::Optional<Block*> catch_block)
557       : is_tailcall(is_tailcall),
558         builtin(builtin),
559         argc(argc),
560         catch_block(catch_block) {}
AppendSuccessorBlocksCallBuiltinInstruction561   void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override {
562     if (catch_block) block_list->push_back(*catch_block);
563   }
564 
565   std::size_t GetValueDefinitionCount() const;
566   DefinitionLocation GetValueDefinition(std::size_t index) const;
567   base::Optional<DefinitionLocation> GetExceptionObjectDefinition() const;
568 
569   bool is_tailcall;
570   Builtin* builtin;
571   size_t argc;
572   base::Optional<Block*> catch_block;
573 };
574 
575 std::ostream& operator<<(std::ostream& os,
576                          const CallBuiltinInstruction& instruction);
577 
578 struct CallBuiltinPointerInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATECallBuiltinPointerInstruction579   TORQUE_INSTRUCTION_BOILERPLATE()
580   bool IsBlockTerminator() const override { return is_tailcall; }
CallBuiltinPointerInstructionCallBuiltinPointerInstruction581   CallBuiltinPointerInstruction(bool is_tailcall,
582                                 const BuiltinPointerType* type, size_t argc)
583       : is_tailcall(is_tailcall), type(type), argc(argc) {}
584 
585   std::size_t GetValueDefinitionCount() const;
586   DefinitionLocation GetValueDefinition(std::size_t index) const;
587 
588   bool is_tailcall;
589   const BuiltinPointerType* type;
590   size_t argc;
591 };
592 
593 inline std::ostream& operator<<(
594     std::ostream& os, const CallBuiltinPointerInstruction& instruction) {
595   os << "CallBuiltinPointer " << *instruction.type
596      << ", argc: " << instruction.argc;
597   if (instruction.is_tailcall) {
598     os << ", is_tailcall";
599   }
600   return os;
601 }
602 
603 struct CallRuntimeInstruction : InstructionBase {
604   TORQUE_INSTRUCTION_BOILERPLATE()
605   bool IsBlockTerminator() const override;
606 
CallRuntimeInstructionCallRuntimeInstruction607   CallRuntimeInstruction(bool is_tailcall, RuntimeFunction* runtime_function,
608                          size_t argc, base::Optional<Block*> catch_block)
609       : is_tailcall(is_tailcall),
610         runtime_function(runtime_function),
611         argc(argc),
612         catch_block(catch_block) {}
AppendSuccessorBlocksCallRuntimeInstruction613   void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override {
614     if (catch_block) block_list->push_back(*catch_block);
615   }
616 
617   std::size_t GetValueDefinitionCount() const;
618   DefinitionLocation GetValueDefinition(std::size_t index) const;
619   base::Optional<DefinitionLocation> GetExceptionObjectDefinition() const;
620 
621   bool is_tailcall;
622   RuntimeFunction* runtime_function;
623   size_t argc;
624   base::Optional<Block*> catch_block;
625 };
626 
627 std::ostream& operator<<(std::ostream& os,
628                          const CallRuntimeInstruction& instruction);
629 
630 struct BranchInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATEBranchInstruction631   TORQUE_INSTRUCTION_BOILERPLATE()
632   bool IsBlockTerminator() const override { return true; }
AppendSuccessorBlocksBranchInstruction633   void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override {
634     block_list->push_back(if_true);
635     block_list->push_back(if_false);
636   }
637 
BranchInstructionBranchInstruction638   BranchInstruction(Block* if_true, Block* if_false)
639       : if_true(if_true), if_false(if_false) {}
640 
641   Block* if_true;
642   Block* if_false;
643 };
644 
645 std::ostream& operator<<(std::ostream& os,
646                          const BranchInstruction& instruction);
647 
648 struct ConstexprBranchInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATEConstexprBranchInstruction649   TORQUE_INSTRUCTION_BOILERPLATE()
650   bool IsBlockTerminator() const override { return true; }
AppendSuccessorBlocksConstexprBranchInstruction651   void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override {
652     block_list->push_back(if_true);
653     block_list->push_back(if_false);
654   }
655 
ConstexprBranchInstructionConstexprBranchInstruction656   ConstexprBranchInstruction(std::string condition, Block* if_true,
657                              Block* if_false)
658       : condition(condition), if_true(if_true), if_false(if_false) {}
659 
660   std::string condition;
661   Block* if_true;
662   Block* if_false;
663 };
664 
665 std::ostream& operator<<(std::ostream& os,
666                          const ConstexprBranchInstruction& instruction);
667 
668 struct GotoInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATEGotoInstruction669   TORQUE_INSTRUCTION_BOILERPLATE()
670   bool IsBlockTerminator() const override { return true; }
AppendSuccessorBlocksGotoInstruction671   void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override {
672     block_list->push_back(destination);
673   }
674 
GotoInstructionGotoInstruction675   explicit GotoInstruction(Block* destination) : destination(destination) {}
676 
677   Block* destination;
678 };
679 
680 std::ostream& operator<<(std::ostream& os, const GotoInstruction& instruction);
681 
682 struct GotoExternalInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATEGotoExternalInstruction683   TORQUE_INSTRUCTION_BOILERPLATE()
684   bool IsBlockTerminator() const override { return true; }
685 
GotoExternalInstructionGotoExternalInstruction686   GotoExternalInstruction(std::string destination,
687                           std::vector<std::string> variable_names)
688       : destination(std::move(destination)),
689         variable_names(std::move(variable_names)) {}
690 
691   std::string destination;
692   std::vector<std::string> variable_names;
693 };
694 
695 inline std::ostream& operator<<(std::ostream& os,
696                                 const GotoExternalInstruction& instruction) {
697   os << "GotoExternal " << instruction.destination;
698   for (const std::string& name : instruction.variable_names) {
699     os << ", " << name;
700   }
701   return os;
702 }
703 
704 struct ReturnInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATEReturnInstruction705   TORQUE_INSTRUCTION_BOILERPLATE()
706   explicit ReturnInstruction(size_t count) : count(count) {}
IsBlockTerminatorReturnInstruction707   bool IsBlockTerminator() const override { return true; }
708 
709   size_t count;  // How many values to return.
710 };
711 
712 inline std::ostream& operator<<(std::ostream& os,
713                                 const ReturnInstruction& instruction) {
714   return os << "Return count: " << instruction.count;
715 }
716 
717 struct PrintConstantStringInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATEPrintConstantStringInstruction718   TORQUE_INSTRUCTION_BOILERPLATE()
719   explicit PrintConstantStringInstruction(std::string message)
720       : message(std::move(message)) {}
721 
722   std::string message;
723 };
724 
725 inline std::ostream& operator<<(
726     std::ostream& os, const PrintConstantStringInstruction& instruction) {
727   return os << "PrintConstantString "
728             << StringLiteralQuote(instruction.message);
729 }
730 
731 struct AbortInstruction : InstructionBase {
732   TORQUE_INSTRUCTION_BOILERPLATE()
733   enum class Kind { kDebugBreak, kUnreachable, kAssertionFailure };
IsBlockTerminatorAbortInstruction734   bool IsBlockTerminator() const override { return kind != Kind::kDebugBreak; }
735   explicit AbortInstruction(Kind kind, std::string message = "")
kindAbortInstruction736       : kind(kind), message(std::move(message)) {}
KindToStringAbortInstruction737   static const char* KindToString(Kind kind) {
738     switch (kind) {
739       case Kind::kDebugBreak:
740         return "kDebugBreak";
741       case Kind::kUnreachable:
742         return "kUnreachable";
743       case Kind::kAssertionFailure:
744         return "kAssertionFailure";
745     }
746   }
747 
748   Kind kind;
749   std::string message;
750 };
751 
752 inline std::ostream& operator<<(std::ostream& os,
753                                 const AbortInstruction& instruction) {
754   return os << "Abort " << AbortInstruction::KindToString(instruction.kind)
755             << ", " << StringLiteralQuote(instruction.message);
756 }
757 
758 struct UnsafeCastInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATEUnsafeCastInstruction759   TORQUE_INSTRUCTION_BOILERPLATE()
760   explicit UnsafeCastInstruction(const Type* destination_type)
761       : destination_type(destination_type) {}
762 
763   DefinitionLocation GetValueDefinition() const;
764 
765   const Type* destination_type;
766 };
767 
768 inline std::ostream& operator<<(std::ostream& os,
769                                 const UnsafeCastInstruction& instruction) {
770   return os << "UnsafeCast " << *instruction.destination_type;
771 }
772 
773 }  // namespace torque
774 }  // namespace internal
775 }  // namespace v8
776 
777 #endif  // V8_TORQUE_INSTRUCTIONS_H_
778