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 | #define TORQUE_INSTRUCTION_LIST(V) \ |
28 | V(PeekInstruction) \ |
29 | V(PokeInstruction) \ |
30 | V(DeleteRangeInstruction) \ |
31 | V(PushUninitializedInstruction) \ |
32 | V(PushBuiltinPointerInstruction) \ |
33 | V(CreateFieldReferenceInstruction) \ |
34 | V(LoadReferenceInstruction) \ |
35 | V(StoreReferenceInstruction) \ |
36 | V(CallCsaMacroInstruction) \ |
37 | V(CallIntrinsicInstruction) \ |
38 | V(NamespaceConstantInstruction) \ |
39 | V(CallCsaMacroAndBranchInstruction) \ |
40 | V(CallBuiltinInstruction) \ |
41 | V(CallRuntimeInstruction) \ |
42 | V(CallBuiltinPointerInstruction) \ |
43 | V(BranchInstruction) \ |
44 | V(ConstexprBranchInstruction) \ |
45 | V(GotoInstruction) \ |
46 | V(GotoExternalInstruction) \ |
47 | V(ReturnInstruction) \ |
48 | V(PrintConstantStringInstruction) \ |
49 | V(AbortInstruction) \ |
50 | V(UnsafeCastInstruction) |
51 | |
52 | #define TORQUE_INSTRUCTION_BOILERPLATE() \ |
53 | static const InstructionKind kKind; \ |
54 | std::unique_ptr<InstructionBase> Clone() const override; \ |
55 | void Assign(const InstructionBase& other) override; \ |
56 | void TypeInstruction(Stack<const Type*>* stack, ControlFlowGraph* cfg) \ |
57 | const override; |
58 | |
59 | enum class InstructionKind { |
60 | #define ENUM_ITEM(name) k##name, |
61 | TORQUE_INSTRUCTION_LIST(ENUM_ITEM) |
62 | #undef ENUM_ITEM |
63 | }; |
64 | |
65 | struct InstructionBase { |
66 | InstructionBase() : pos(CurrentSourcePosition::Get()) {} |
67 | virtual std::unique_ptr<InstructionBase> Clone() const = 0; |
68 | virtual void Assign(const InstructionBase& other) = 0; |
69 | virtual ~InstructionBase() = default; |
70 | |
71 | virtual void TypeInstruction(Stack<const Type*>* stack, |
72 | ControlFlowGraph* cfg) const = 0; |
73 | void InvalidateTransientTypes(Stack<const Type*>* stack) const; |
74 | virtual bool IsBlockTerminator() const { return false; } |
75 | virtual void AppendSuccessorBlocks(std::vector<Block*>* block_list) const {} |
76 | |
77 | SourcePosition pos; |
78 | }; |
79 | |
80 | class Instruction { |
81 | public: |
82 | template <class T> |
83 | Instruction(T instr) // NOLINT(runtime/explicit) |
84 | : kind_(T::kKind), instruction_(new T(std::move(instr))) {} |
85 | |
86 | template <class T> |
87 | T& Cast() { |
88 | DCHECK(Is<T>()); |
89 | return static_cast<T&>(*instruction_); |
90 | } |
91 | |
92 | template <class T> |
93 | const T& Cast() const { |
94 | DCHECK(Is<T>()); |
95 | return static_cast<const T&>(*instruction_); |
96 | } |
97 | |
98 | template <class T> |
99 | bool Is() const { |
100 | return kind_ == T::kKind; |
101 | } |
102 | |
103 | template <class T> |
104 | T* DynamicCast() { |
105 | if (Is<T>()) return &Cast<T>(); |
106 | return nullptr; |
107 | } |
108 | |
109 | template <class T> |
110 | const T* DynamicCast() const { |
111 | if (Is<T>()) return &Cast<T>(); |
112 | return nullptr; |
113 | } |
114 | |
115 | Instruction(const Instruction& other) V8_NOEXCEPT |
116 | : kind_(other.kind_), |
117 | instruction_(other.instruction_->Clone()) {} |
118 | Instruction& operator=(const Instruction& other) V8_NOEXCEPT { |
119 | if (kind_ == other.kind_) { |
120 | instruction_->Assign(*other.instruction_); |
121 | } else { |
122 | kind_ = other.kind_; |
123 | instruction_ = other.instruction_->Clone(); |
124 | } |
125 | return *this; |
126 | } |
127 | |
128 | InstructionKind kind() const { return kind_; } |
129 | const char* Mnemonic() const { |
130 | switch (kind()) { |
131 | #define ENUM_ITEM(name) \ |
132 | case InstructionKind::k##name: \ |
133 | return #name; |
134 | TORQUE_INSTRUCTION_LIST(ENUM_ITEM) |
135 | #undef ENUM_ITEM |
136 | default: |
137 | UNREACHABLE(); |
138 | } |
139 | } |
140 | void TypeInstruction(Stack<const Type*>* stack, ControlFlowGraph* cfg) const { |
141 | return instruction_->TypeInstruction(stack, cfg); |
142 | } |
143 | |
144 | InstructionBase* operator->() { return instruction_.get(); } |
145 | const InstructionBase* operator->() const { return instruction_.get(); } |
146 | |
147 | private: |
148 | InstructionKind kind_; |
149 | std::unique_ptr<InstructionBase> instruction_; |
150 | }; |
151 | |
152 | struct PeekInstruction : InstructionBase { |
153 | TORQUE_INSTRUCTION_BOILERPLATE() |
154 | |
155 | PeekInstruction(BottomOffset slot, base::Optional<const Type*> widened_type) |
156 | : slot(slot), widened_type(widened_type) {} |
157 | |
158 | BottomOffset slot; |
159 | base::Optional<const Type*> widened_type; |
160 | }; |
161 | |
162 | struct PokeInstruction : InstructionBase { |
163 | TORQUE_INSTRUCTION_BOILERPLATE() |
164 | |
165 | PokeInstruction(BottomOffset slot, base::Optional<const Type*> widened_type) |
166 | : slot(slot), widened_type(widened_type) {} |
167 | |
168 | BottomOffset slot; |
169 | base::Optional<const Type*> widened_type; |
170 | }; |
171 | |
172 | // Preserve the top {preserved_slots} number of slots, and delete |
173 | // {deleted_slots} number or slots below. |
174 | struct DeleteRangeInstruction : InstructionBase { |
175 | TORQUE_INSTRUCTION_BOILERPLATE() |
176 | explicit DeleteRangeInstruction(StackRange range) : range(range) {} |
177 | |
178 | StackRange range; |
179 | }; |
180 | |
181 | struct PushUninitializedInstruction : InstructionBase { |
182 | TORQUE_INSTRUCTION_BOILERPLATE() |
183 | explicit PushUninitializedInstruction(const Type* type) : type(type) {} |
184 | |
185 | const Type* type; |
186 | }; |
187 | |
188 | struct PushBuiltinPointerInstruction : InstructionBase { |
189 | TORQUE_INSTRUCTION_BOILERPLATE() |
190 | PushBuiltinPointerInstruction(std::string external_name, const Type* type) |
191 | : external_name(std::move(external_name)), type(type) { |
192 | DCHECK(type->IsBuiltinPointerType()); |
193 | } |
194 | |
195 | std::string external_name; |
196 | const Type* type; |
197 | }; |
198 | |
199 | struct NamespaceConstantInstruction : InstructionBase { |
200 | TORQUE_INSTRUCTION_BOILERPLATE() |
201 | explicit NamespaceConstantInstruction(NamespaceConstant* constant) |
202 | : constant(constant) {} |
203 | |
204 | NamespaceConstant* constant; |
205 | }; |
206 | |
207 | struct CreateFieldReferenceInstruction : InstructionBase { |
208 | TORQUE_INSTRUCTION_BOILERPLATE() |
209 | CreateFieldReferenceInstruction(const ClassType* class_type, |
210 | std::string field_name) |
211 | : class_type(class_type), field_name(std::move(field_name)) {} |
212 | const ClassType* class_type; |
213 | std::string field_name; |
214 | }; |
215 | |
216 | struct LoadReferenceInstruction : InstructionBase { |
217 | TORQUE_INSTRUCTION_BOILERPLATE() |
218 | explicit LoadReferenceInstruction(const Type* type) : type(type) {} |
219 | const Type* type; |
220 | }; |
221 | |
222 | struct StoreReferenceInstruction : InstructionBase { |
223 | TORQUE_INSTRUCTION_BOILERPLATE() |
224 | explicit StoreReferenceInstruction(const Type* type) : type(type) {} |
225 | const Type* type; |
226 | }; |
227 | |
228 | struct CallIntrinsicInstruction : InstructionBase { |
229 | TORQUE_INSTRUCTION_BOILERPLATE() |
230 | CallIntrinsicInstruction(Intrinsic* intrinsic, |
231 | TypeVector specialization_types, |
232 | std::vector<std::string> constexpr_arguments) |
233 | : intrinsic(intrinsic), |
234 | specialization_types(std::move(specialization_types)), |
235 | constexpr_arguments(constexpr_arguments) {} |
236 | |
237 | Intrinsic* intrinsic; |
238 | TypeVector specialization_types; |
239 | std::vector<std::string> constexpr_arguments; |
240 | }; |
241 | |
242 | struct CallCsaMacroInstruction : InstructionBase { |
243 | TORQUE_INSTRUCTION_BOILERPLATE() |
244 | CallCsaMacroInstruction(Macro* macro, |
245 | std::vector<std::string> constexpr_arguments, |
246 | base::Optional<Block*> catch_block) |
247 | : macro(macro), |
248 | constexpr_arguments(constexpr_arguments), |
249 | catch_block(catch_block) {} |
250 | void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override { |
251 | if (catch_block) block_list->push_back(*catch_block); |
252 | } |
253 | |
254 | Macro* macro; |
255 | std::vector<std::string> constexpr_arguments; |
256 | base::Optional<Block*> catch_block; |
257 | }; |
258 | |
259 | struct CallCsaMacroAndBranchInstruction : InstructionBase { |
260 | TORQUE_INSTRUCTION_BOILERPLATE() |
261 | CallCsaMacroAndBranchInstruction(Macro* macro, |
262 | std::vector<std::string> constexpr_arguments, |
263 | base::Optional<Block*> return_continuation, |
264 | std::vector<Block*> label_blocks, |
265 | base::Optional<Block*> catch_block) |
266 | : macro(macro), |
267 | constexpr_arguments(constexpr_arguments), |
268 | return_continuation(return_continuation), |
269 | label_blocks(label_blocks), |
270 | catch_block(catch_block) {} |
271 | bool IsBlockTerminator() const override { return true; } |
272 | void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override { |
273 | if (catch_block) block_list->push_back(*catch_block); |
274 | if (return_continuation) block_list->push_back(*return_continuation); |
275 | for (Block* block : label_blocks) block_list->push_back(block); |
276 | } |
277 | |
278 | Macro* macro; |
279 | std::vector<std::string> constexpr_arguments; |
280 | base::Optional<Block*> return_continuation; |
281 | std::vector<Block*> label_blocks; |
282 | base::Optional<Block*> catch_block; |
283 | }; |
284 | |
285 | struct CallBuiltinInstruction : InstructionBase { |
286 | TORQUE_INSTRUCTION_BOILERPLATE() |
287 | bool IsBlockTerminator() const override { return is_tailcall; } |
288 | CallBuiltinInstruction(bool is_tailcall, Builtin* builtin, size_t argc, |
289 | base::Optional<Block*> catch_block) |
290 | : is_tailcall(is_tailcall), |
291 | builtin(builtin), |
292 | argc(argc), |
293 | catch_block(catch_block) {} |
294 | void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override { |
295 | if (catch_block) block_list->push_back(*catch_block); |
296 | } |
297 | |
298 | bool is_tailcall; |
299 | Builtin* builtin; |
300 | size_t argc; |
301 | base::Optional<Block*> catch_block; |
302 | }; |
303 | |
304 | struct CallBuiltinPointerInstruction : InstructionBase { |
305 | TORQUE_INSTRUCTION_BOILERPLATE() |
306 | bool IsBlockTerminator() const override { return is_tailcall; } |
307 | CallBuiltinPointerInstruction(bool is_tailcall, |
308 | const BuiltinPointerType* type, size_t argc) |
309 | : is_tailcall(is_tailcall), type(type), argc(argc) {} |
310 | |
311 | bool is_tailcall; |
312 | const BuiltinPointerType* type; |
313 | size_t argc; |
314 | }; |
315 | |
316 | struct CallRuntimeInstruction : InstructionBase { |
317 | TORQUE_INSTRUCTION_BOILERPLATE() |
318 | bool IsBlockTerminator() const override; |
319 | |
320 | CallRuntimeInstruction(bool is_tailcall, RuntimeFunction* runtime_function, |
321 | size_t argc, base::Optional<Block*> catch_block) |
322 | : is_tailcall(is_tailcall), |
323 | runtime_function(runtime_function), |
324 | argc(argc), |
325 | catch_block(catch_block) {} |
326 | void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override { |
327 | if (catch_block) block_list->push_back(*catch_block); |
328 | } |
329 | |
330 | bool is_tailcall; |
331 | RuntimeFunction* runtime_function; |
332 | size_t argc; |
333 | base::Optional<Block*> catch_block; |
334 | }; |
335 | |
336 | struct BranchInstruction : InstructionBase { |
337 | TORQUE_INSTRUCTION_BOILERPLATE() |
338 | bool IsBlockTerminator() const override { return true; } |
339 | void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override { |
340 | block_list->push_back(if_true); |
341 | block_list->push_back(if_false); |
342 | } |
343 | |
344 | BranchInstruction(Block* if_true, Block* if_false) |
345 | : if_true(if_true), if_false(if_false) {} |
346 | |
347 | Block* if_true; |
348 | Block* if_false; |
349 | }; |
350 | |
351 | struct ConstexprBranchInstruction : InstructionBase { |
352 | TORQUE_INSTRUCTION_BOILERPLATE() |
353 | bool IsBlockTerminator() const override { return true; } |
354 | void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override { |
355 | block_list->push_back(if_true); |
356 | block_list->push_back(if_false); |
357 | } |
358 | |
359 | ConstexprBranchInstruction(std::string condition, Block* if_true, |
360 | Block* if_false) |
361 | : condition(condition), if_true(if_true), if_false(if_false) {} |
362 | |
363 | std::string condition; |
364 | Block* if_true; |
365 | Block* if_false; |
366 | }; |
367 | |
368 | struct GotoInstruction : InstructionBase { |
369 | TORQUE_INSTRUCTION_BOILERPLATE() |
370 | bool IsBlockTerminator() const override { return true; } |
371 | void AppendSuccessorBlocks(std::vector<Block*>* block_list) const override { |
372 | block_list->push_back(destination); |
373 | } |
374 | |
375 | explicit GotoInstruction(Block* destination) : destination(destination) {} |
376 | |
377 | Block* destination; |
378 | }; |
379 | |
380 | struct GotoExternalInstruction : InstructionBase { |
381 | TORQUE_INSTRUCTION_BOILERPLATE() |
382 | bool IsBlockTerminator() const override { return true; } |
383 | |
384 | GotoExternalInstruction(std::string destination, |
385 | std::vector<std::string> variable_names) |
386 | : destination(std::move(destination)), |
387 | variable_names(std::move(variable_names)) {} |
388 | |
389 | std::string destination; |
390 | std::vector<std::string> variable_names; |
391 | }; |
392 | |
393 | struct ReturnInstruction : InstructionBase { |
394 | TORQUE_INSTRUCTION_BOILERPLATE() |
395 | bool IsBlockTerminator() const override { return true; } |
396 | }; |
397 | |
398 | struct PrintConstantStringInstruction : InstructionBase { |
399 | TORQUE_INSTRUCTION_BOILERPLATE() |
400 | explicit PrintConstantStringInstruction(std::string message) |
401 | : message(std::move(message)) {} |
402 | |
403 | std::string message; |
404 | }; |
405 | |
406 | struct AbortInstruction : InstructionBase { |
407 | TORQUE_INSTRUCTION_BOILERPLATE() |
408 | enum class Kind { kDebugBreak, kUnreachable, kAssertionFailure }; |
409 | bool IsBlockTerminator() const override { return kind != Kind::kDebugBreak; } |
410 | explicit AbortInstruction(Kind kind, std::string message = "" ) |
411 | : kind(kind), message(std::move(message)) {} |
412 | |
413 | Kind kind; |
414 | std::string message; |
415 | }; |
416 | |
417 | struct UnsafeCastInstruction : InstructionBase { |
418 | TORQUE_INSTRUCTION_BOILERPLATE() |
419 | explicit UnsafeCastInstruction(const Type* destination_type) |
420 | : destination_type(destination_type) {} |
421 | |
422 | const Type* destination_type; |
423 | }; |
424 | |
425 | } // namespace torque |
426 | } // namespace internal |
427 | } // namespace v8 |
428 | |
429 | #endif // V8_TORQUE_INSTRUCTIONS_H_ |
430 | |