1 /* This file is part of the dynarmic project.
2  * Copyright (c) 2016 MerryMage
3  * SPDX-License-Identifier: 0BSD
4  */
5 
6 #pragma once
7 
8 #include <boost/variant.hpp>
9 
10 #include "common/common_types.h"
11 #include "frontend/ir/cond.h"
12 #include "frontend/ir/location_descriptor.h"
13 
14 namespace Dynarmic::IR {
15 namespace Term {
16 
17 struct Invalid {};
18 
19 /**
20  * This terminal instruction calls the interpreter, starting at `next`.
21  * The interpreter must interpret exactly `num_instructions` instructions.
22  */
23 struct Interpret {
InterpretInterpret24     explicit Interpret(const LocationDescriptor& next_) : next(next_) {}
25     LocationDescriptor next; ///< Location at which interpretation starts.
26     size_t num_instructions = 1;
27 };
28 
29 /**
30  * This terminal instruction returns control to the dispatcher.
31  * The dispatcher will use the current cpu state to determine what comes next.
32  */
33 struct ReturnToDispatch {};
34 
35 /**
36  * This terminal instruction jumps to the basic block described by `next` if we have enough
37  * cycles remaining. If we do not have enough cycles remaining, we return to the
38  * dispatcher, which will return control to the host.
39  */
40 struct LinkBlock {
LinkBlockLinkBlock41     explicit LinkBlock(const LocationDescriptor& next_) : next(next_) {}
42     LocationDescriptor next; ///< Location descriptor for next block.
43 };
44 
45 /**
46  * This terminal instruction jumps to the basic block described by `next` unconditionally.
47  * This is an optimization and MUST only be emitted when this is guaranteed not to result
48  * in hanging, even in the face of other optimizations. (In practice, this means that only
49  * forward jumps to short-ish blocks would use this instruction.)
50  * A backend that doesn't support this optimization may choose to implement this exactly
51  * as LinkBlock.
52  */
53 struct LinkBlockFast {
LinkBlockFastLinkBlockFast54     explicit LinkBlockFast(const LocationDescriptor& next_) : next(next_) {}
55     LocationDescriptor next; ///< Location descriptor for next block.
56 };
57 
58 /**
59  * This terminal instruction checks the top of the Return Stack Buffer against the current
60  * location descriptor. If RSB lookup fails, control is returned to the dispatcher.
61  * This is an optimization for faster function calls. A backend that doesn't support
62  * this optimization or doesn't have a RSB may choose to implement this exactly as
63  * ReturnToDispatch.
64  */
65 struct PopRSBHint {};
66 
67 /**
68  * This terminal instruction performs a lookup of the current location descriptor in the
69  * fast dispatch lookup table. A backend that doesn't support this optimization may choose
70  * to implement this exactly as ReturnToDispatch.
71  */
72 struct FastDispatchHint {};
73 
74 struct If;
75 struct CheckBit;
76 struct CheckHalt;
77 /// A Terminal is the terminal instruction in a MicroBlock.
78 using Terminal = boost::variant<
79         Invalid,
80         Interpret,
81         ReturnToDispatch,
82         LinkBlock,
83         LinkBlockFast,
84         PopRSBHint,
85         FastDispatchHint,
86         boost::recursive_wrapper<If>,
87         boost::recursive_wrapper<CheckBit>,
88         boost::recursive_wrapper<CheckHalt>
89 >;
90 
91 /**
92  * This terminal instruction conditionally executes one terminal or another depending
93  * on the run-time state of the ARM flags.
94  */
95 struct If {
IfIf96     If(Cond if_, Terminal then_, Terminal else_) : if_(if_), then_(std::move(then_)), else_(std::move(else_)) {}
97     Cond if_;
98     Terminal then_;
99     Terminal else_;
100 };
101 
102 /**
103  * This terminal instruction conditionally executes one terminal or another depending
104  * on the run-time state of the check bit.
105  * then_ is executed if the check bit is non-zero, otherwise else_ is executed.
106  */
107 struct CheckBit {
CheckBitCheckBit108     CheckBit(Terminal then_, Terminal else_) : then_(std::move(then_)), else_(std::move(else_)) {}
109     Terminal then_;
110     Terminal else_;
111 };
112 
113 /**
114  * This terminal instruction checks if a halt was requested. If it wasn't, else_ is
115  * executed.
116  */
117 struct CheckHalt {
CheckHaltCheckHalt118     explicit CheckHalt(Terminal else_) : else_(std::move(else_)) {}
119     Terminal else_;
120 };
121 
122 } // namespace Term
123 
124 using Term::Terminal;
125 
126 } // namespace Dynarmic::IR
127