1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
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
|
import {Source} from ../sources;
import {PatioLocalRef, PatioOperand} from ./operands;
import {PatioDataLayout} from ./types;
sum PatioInstruction {
variant PatioBlock;
variant PatioBoolToInt;
variant PatioCall;
variant PatioCallPointer;
variant PatioDataPtrToInt;
variant PatioIf;
variant PatioIntAdd;
variant PatioIntEquals;
variant PatioIntNotEqual;
variant PatioIntSub;
variant PatioIntToBool;
variant PatioIntToDataPtr;
variant PatioLocalStore;
variant PatioLoop;
variant PatioLoopBreak;
variant PatioLoopContinue;
variant PatioMemoryLoad;
variant PatioMemoryStore;
variant PatioReturn;
variant PatioSwitch;
variant PatioThrow;
variant PatioTry;
variant PatioUnreachable;
variant PatioVarLoad;
variant PatioVarStore;
}
/// Execute the instructions in the body of the block. The results may be used in
/// the parent scope, but any other variables (as distinct from values) are no
/// longer in use.
struct PatioBlock {
field body: List[PatioInstruction];
field results: List[PatioOperand];
field source: Source;
}
struct PatioBoolToInt {
field destination: PatioLocalRef;
field value: PatioOperand;
field source: Source;
}
/// Call the function with the given name, and store the returned values in the
/// destination locals.
struct PatioCall {
field destinations: List[PatioLocalRef];
field functionName: String;
field args: List[PatioOperand];
field source: Source;
}
/// Call the function pointed to by the function pointer with the given
/// arguments, and store the returned values in the destination locals.
struct PatioCallPointer {
field destinations: List[PatioLocalRef];
field functionPointer: PatioOperand;
field args: List[PatioOperand];
field source: Source;
}
struct PatioDataPtrToInt {
field destination: PatioLocalRef;
field value: PatioOperand;
field source: Source;
}
/// If the condition is true, execute the instructions in the "true" body,
/// otherwise execute the instructions in the "false" body.
///
/// Each block must have the same number of results as destinations. Each result is
/// assigned to the destination with the same index.
struct PatioIf {
field destinations: List[PatioLocalRef];
field condition: PatioOperand;
field trueBody: PatioBlock;
field falseBody: PatioBlock;
field source: Source;
}
struct PatioIntAdd {
field destination: PatioLocalRef;
field left: PatioOperand;
field right: PatioOperand;
field source: Source;
}
struct PatioIntEquals {
field destination: PatioLocalRef;
field left: PatioOperand;
field right: PatioOperand;
field source: Source;
}
/// Compare the left and right operands. If they are equal, store false in the
/// destination, otherwise store true in the destination.
struct PatioIntNotEqual {
field destination: PatioLocalRef;
field left: PatioOperand;
field right: PatioOperand;
field source: Source;
}
/// Calculate the left operand minus the right operand, and store the result in
/// the destination.
struct PatioIntSub {
field destination: PatioLocalRef;
field left: PatioOperand;
field right: PatioOperand;
field source: Source;
}
struct PatioIntToBool {
field destination: PatioLocalRef;
field value: PatioOperand;
field source: Source;
}
struct PatioIntToDataPtr {
field destination: PatioLocalRef;
field value: PatioOperand;
field source: Source;
}
/// Store the given value in the destination local.
struct PatioLocalStore {
field destination: PatioLocalRef;
field value: PatioOperand;
field source: Source;
}
/// Execute the body. The `continue` instruction can jump back to the
/// start of the body. The `break` instruction can jump to the end of
/// the body.
struct PatioLoop {
field body: List[PatioInstruction];
field source: Source;
}
/// Jump to the end of the enclosing `loop` instruction's body.
struct PatioLoopBreak {
field source: Source;
}
/// Jump to the start of the enclosing `loop` instruction's body.
struct PatioLoopContinue {
field source: Source;
}
/// Load the value at the given offset from the given pointer and store it in the
/// destination local.
struct PatioMemoryLoad {
field destination: PatioLocalRef;
field pointer: PatioOperand;
field layout: PatioDataLayout;
field elementOffsets: List[Int32];
field source: Source;
}
/// Store the given value at the given offset from the given pointer.
struct PatioMemoryStore {
field pointer: PatioOperand;
field layout: PatioDataLayout;
field elementOffsets: List[Int32];
field value: PatioOperand;
field source: Source;
}
/// Return with the given values from the current function.
struct PatioReturn {
field values: List[PatioOperand];
field source: Source;
}
/// Find a case with a value matching that of the instruction's value. If such a
/// case is found, execute the body of that case, otherwise execute the default
/// body.
///
/// Each block must have the same number of results as destinations. Each result
/// is assigned to the destination with the same index.
struct PatioSwitch {
field destinations: List[PatioLocalRef];
field value: PatioOperand;
field cases: List[PatioSwitchCase];
field defaultBody: PatioBlock;
field source: Source;
}
struct PatioSwitchCase {
field value: Int32;
field body: PatioBlock;
field source: Source;
}
/// Search the stack for an exception handler with a catch clause matching the
/// thrown exception type. Unwind the stack up to and including this exception
/// handler, and run the body of the catch clause. If no such exception handler
/// is found, the process should abort.
struct PatioThrow {
field exceptionTypeName: String;
field value: PatioOperand;
field source: Source;
}
/// Push an exception handler with the given catch clauses onto the control flow
/// stack, and then execute the instructions in its body.
///
/// If the exception handler catches an exception, the body of the corresponding
/// is executed instead.
///
/// Each block (that is, the body of the try and the body of each catch clause)
/// must have the same number of results as destinations. Each result is assigned to
/// the destination with the same index.
struct PatioTry {
field destinations: List[PatioLocalRef];
field catchClauses: List[PatioCatchClause];
field body: PatioBlock;
field source: Source;
}
/// A catch clause can accept an exception of a given type. If executed, it will
/// stored the value of the exception in the given destination, and then execute the
/// body.
struct PatioCatchClause {
field exceptionTypeName: String;
field destination: PatioLocalRef;
field body: PatioBlock;
field source: Source;
}
/// Unreachable instructions have undefined behaviour, and are used to indicate
/// that an instruction sequence is unreachable.
struct PatioUnreachable {
field source: Source;
}
/// Load the value from the given var and store it in the destination local.
struct PatioVarLoad {
field destination: PatioLocalRef;
field var: PatioLocalRef;
field source: Source;
}
/// Store the given value in the destination var.
struct PatioVarStore {
field var: PatioLocalRef;
field value: PatioOperand;
field source: Source;
}
|