/home/travis/build/MoarVM/MoarVM/src/jit/expr.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | /* Mathematical min and max macro's */ |
4 | | #ifndef MAX |
5 | 316k | #define MAX(a,b) ((a) > (b) ? (a) : (b)); |
6 | | #endif |
7 | | |
8 | | #ifndef MIN |
9 | | #define MIN(a,b) ((a) < (b) ? (a) : (b)); |
10 | | #endif |
11 | | |
12 | | |
13 | | /* macros used in the expression list templates, defined here so they |
14 | | don't overwrite other definitions */ |
15 | | #define CONST_PTR(x) ((uintptr_t)(x)) |
16 | | #define QUOTE(x) (x) |
17 | | #define MSG(...) CONST_PTR(#__VA_ARGS__) |
18 | | #define SIZEOF_MEMBER(type, member) sizeof(((type*)0)->member) |
19 | | |
20 | | #include "jit/core_templates.h" |
21 | | |
22 | | |
23 | | static const MVMJitExprOpInfo expr_op_info[] = { |
24 | | #define OP_INFO(name, nchild, nargs, vtype, cast) { #name, nchild, nargs, MVM_JIT_ ## vtype, MVM_JIT_ ## cast } |
25 | | MVM_JIT_EXPR_OPS(OP_INFO) |
26 | | #undef OP_INFO |
27 | | }; |
28 | | |
29 | | |
30 | 42.7M | const MVMJitExprOpInfo * MVM_jit_expr_op_info(MVMThreadContext *tc, MVMint32 op) { |
31 | 42.7M | if (op < 0 || op >= MVM_JIT_MAX_NODES) { |
32 | 0 | MVM_oops(tc, "JIT: Expr op index out of bounds: %d", op); |
33 | 0 | } |
34 | 42.7M | return &expr_op_info[op]; |
35 | 42.7M | } |
36 | | |
37 | | /* Record the node that defines a value */ |
38 | | struct ValueDefinition { |
39 | | MVMint32 node; |
40 | | MVMint32 root; |
41 | | MVMint32 addr; |
42 | | }; |
43 | | |
44 | | |
45 | | |
46 | | /* Logical negation of MVMJitExprOp flags. */ |
47 | 392k | MVMint32 MVM_jit_expr_op_negate_flag(MVMThreadContext *tc, MVMint32 op) { |
48 | 392k | switch(op) { |
49 | 0 | case MVM_JIT_LT: |
50 | 0 | return MVM_JIT_GE; |
51 | 0 | case MVM_JIT_LE: |
52 | 0 | return MVM_JIT_GT; |
53 | 634 | case MVM_JIT_EQ: |
54 | 634 | return MVM_JIT_NE; |
55 | 18.3k | case MVM_JIT_NE: |
56 | 18.3k | return MVM_JIT_EQ; |
57 | 0 | case MVM_JIT_GE: |
58 | 0 | return MVM_JIT_LT; |
59 | 0 | case MVM_JIT_GT: |
60 | 0 | return MVM_JIT_LE; |
61 | 282k | case MVM_JIT_NZ: |
62 | 282k | return MVM_JIT_ZR; |
63 | 90.6k | case MVM_JIT_ZR: |
64 | 90.6k | return MVM_JIT_NZ; |
65 | 0 | default: |
66 | 0 | break; |
67 | 392k | } |
68 | 0 | return -1; /* not a flag */ |
69 | 392k | } |
70 | | |
71 | 0 | MVMint32 MVM_jit_expr_op_is_binary_noncommutative(MVMThreadContext *tc, MVMint32 op) { |
72 | 0 | switch (op) { |
73 | 0 | case MVM_JIT_SUB: |
74 | 0 | case MVM_JIT_XOR: |
75 | 0 | /* and DIV, SHIFT, etc */ |
76 | 0 | return 1; |
77 | 0 | default: |
78 | 0 | /* ADD, MUL, AND, OR, etc. are commutative */ |
79 | 0 | return 0; |
80 | 0 | } |
81 | 0 | } |
82 | | |
83 | | |
84 | | static MVMint32 MVM_jit_expr_add_regaddr(MVMThreadContext *tc, MVMJitExprTree *tree, |
85 | 909k | MVMuint16 reg) { |
86 | 909k | MVMint32 num = tree->nodes_num; |
87 | 909k | MVMJitExprNode template[] = { MVM_JIT_LOCAL, |
88 | 909k | MVM_JIT_ADDR, num, reg * MVM_JIT_REG_SZ }; |
89 | 909k | MVM_VECTOR_APPEND(tree->nodes, template, sizeof(template)/sizeof(MVMJitExprNode)); |
90 | 909k | return num + 1; |
91 | 909k | } |
92 | | |
93 | 30 | static MVMint32 MVM_jit_expr_add_loadframe(MVMThreadContext *tc, MVMJitExprTree *tree) { |
94 | 30 | MVMint32 num = tree->nodes_num; |
95 | 30 | MVMJitExprNode template[] = { MVM_JIT_TC, |
96 | 30 | MVM_JIT_ADDR, num, offsetof(MVMThreadContext, cur_frame), |
97 | 30 | MVM_JIT_LOAD, num + 1, sizeof(MVMFrame*) }; |
98 | 30 | MVM_VECTOR_APPEND(tree->nodes, template, sizeof(template)/sizeof(MVMJitExprNode)); |
99 | 30 | return num + 4; |
100 | 30 | } |
101 | | |
102 | | |
103 | | |
104 | | static MVMint32 MVM_jit_expr_add_load(MVMThreadContext *tc, MVMJitExprTree *tree, |
105 | 339k | MVMint32 addr) { |
106 | 339k | MVMint32 num = tree->nodes_num; |
107 | 339k | MVMJitExprNode template[] = { MVM_JIT_LOAD, addr, MVM_JIT_REG_SZ }; |
108 | 339k | MVM_VECTOR_APPEND(tree->nodes, template, sizeof(template)/sizeof(MVMJitExprNode)); |
109 | 339k | return num; |
110 | 339k | } |
111 | | |
112 | | |
113 | | static MVMint32 MVM_jit_expr_add_store(MVMThreadContext *tc, MVMJitExprTree *tree, |
114 | 412k | MVMint32 addr, MVMint32 val, MVMint32 sz) { |
115 | 412k | MVMint32 num = tree->nodes_num; |
116 | 412k | MVMJitExprNode template[] = { MVM_JIT_STORE, addr, val, sz }; |
117 | 412k | MVM_VECTOR_APPEND(tree->nodes, template, sizeof(template)/sizeof(MVMJitExprNode)); |
118 | 412k | return num; |
119 | 412k | } |
120 | | |
121 | | |
122 | 5.30k | static MVMint32 MVM_jit_expr_add_cast(MVMThreadContext *tc, MVMJitExprTree *tree, MVMint32 node, MVMint32 to_size, MVMint32 from_size, MVMint32 is_signed) { |
123 | 5.30k | MVMint32 num = tree->nodes_num; |
124 | 5.30k | MVMJitExprNode template[] = { MVM_JIT_CAST, node, to_size, from_size, is_signed }; |
125 | 5.30k | MVM_VECTOR_APPEND(tree->nodes, template, sizeof(template)/sizeof(MVMJitExprNode)); |
126 | 5.30k | return num; |
127 | 5.30k | } |
128 | | |
129 | 5.69k | static MVMint32 MVM_jit_expr_add_label(MVMThreadContext *tc, MVMJitExprTree *tree, MVMint32 label) { |
130 | 5.69k | MVMint32 num = tree->nodes_num; |
131 | 5.69k | MVMJitExprNode template[] = { MVM_JIT_MARK, label }; |
132 | 5.69k | MVM_VECTOR_APPEND(tree->nodes, template, sizeof(template)/sizeof(template[0])); |
133 | 5.69k | return num; |
134 | 5.69k | } |
135 | | |
136 | | static MVMint32 MVM_jit_expr_add_lexaddr(MVMThreadContext *tc, MVMJitExprTree *tree, |
137 | 30 | MVMuint16 outers, MVMuint16 idx) { |
138 | 30 | MVMint32 i; |
139 | 30 | /* (frame) as the root */ |
140 | 30 | MVMint32 num = MVM_jit_expr_add_loadframe(tc, tree); |
141 | 30 | for (i = 0; i < outers; i++) { |
142 | 0 | /* (load (addr $val (&offsetof MVMFrame outer)) (&sizeof MVMFrame*)) */ |
143 | 0 | MVMJitExprNode template[] = { MVM_JIT_ADDR, num, offsetof(MVMFrame, outer), |
144 | 0 | MVM_JIT_LOAD, tree->nodes_num, sizeof(MVMFrame*) }; |
145 | 0 | MVM_VECTOR_APPEND(tree->nodes, template, sizeof(template)/sizeof(MVMJitExprNode)); |
146 | 0 | num = tree->nodes_num - 3; |
147 | 0 | } |
148 | 30 | /* (addr (load (addr $frame (&offsetof MVMFrame env)) ptr_sz) ptr_sz*idx) */ |
149 | 30 | { |
150 | 30 | MVMJitExprNode template[] = { |
151 | 30 | MVM_JIT_ADDR, num, offsetof(MVMFrame, env), /* (addr $frame (&offsetof MVMFrame env)) */ |
152 | 30 | MVM_JIT_LOAD, tree->nodes_num, MVM_JIT_PTR_SZ, /* (load $addr ptr_sz) */ |
153 | 30 | MVM_JIT_ADDR, tree->nodes_num + 3, idx * MVM_JIT_REG_SZ /* (addr $frame_env idx*reg_sz) */ |
154 | 30 | }; |
155 | 30 | MVM_VECTOR_APPEND(tree->nodes, template, sizeof(template)/sizeof(MVMJitExprNode)); |
156 | 30 | num = tree->nodes_num - 3; |
157 | 30 | } |
158 | 30 | return num; |
159 | 30 | } |
160 | | |
161 | | |
162 | | static MVMint32 MVM_jit_expr_add_const(MVMThreadContext *tc, MVMJitExprTree *tree, |
163 | 371k | MVMSpeshOperand opr, MVMuint8 info) { |
164 | 371k | |
165 | 371k | MVMJitExprNode template[] = { MVM_JIT_CONST, 0, 0 }; |
166 | 371k | MVMint32 num = tree->nodes_num; |
167 | 371k | MVMint32 size = 3; |
168 | 371k | switch(info & MVM_operand_type_mask) { |
169 | 0 | case MVM_operand_int8: |
170 | 0 | template[1] = opr.lit_i8; |
171 | 0 | template[2] = sizeof(MVMint8); |
172 | 0 | break; |
173 | 163k | case MVM_operand_int16: |
174 | 163k | template[1] = opr.lit_i16; |
175 | 163k | template[2] = sizeof(MVMint16); |
176 | 163k | break; |
177 | 496 | case MVM_operand_coderef: |
178 | 496 | template[1] = opr.coderef_idx; |
179 | 496 | template[2] = sizeof(MVMuint16); |
180 | 496 | break; |
181 | 1.62k | case MVM_operand_int32: |
182 | 1.62k | template[1] = opr.lit_i32; |
183 | 1.62k | template[2] = sizeof(MVMint32); |
184 | 1.62k | break; |
185 | 1 | case MVM_operand_int64: |
186 | 1 | template[1] = opr.lit_i64; |
187 | 1 | template[2] = sizeof(MVMint64); |
188 | 1 | break; |
189 | 0 | case MVM_operand_num32: |
190 | 0 | /* possible endianess issue here */ |
191 | 0 | template[1] = opr.lit_i32; |
192 | 0 | template[2] = sizeof(MVMnum32); |
193 | 0 | break; |
194 | 1.02k | case MVM_operand_num64: |
195 | 1.02k | /* use i64 to get the bits */ |
196 | 1.02k | template[1] = opr.lit_i64; |
197 | 1.02k | template[2] = sizeof(MVMnum64); |
198 | 1.02k | break; |
199 | 37.8k | case MVM_operand_str: |
200 | 37.8k | /* string index really */ |
201 | 37.8k | template[1] = opr.lit_str_idx; |
202 | 37.8k | template[2] = sizeof(MVMuint32); |
203 | 37.8k | break; |
204 | 111k | case MVM_operand_ins: |
205 | 111k | template[0] = MVM_JIT_LABEL; |
206 | 111k | template[1] = MVM_jit_label_before_bb(tc, tree->graph, opr.ins_bb); |
207 | 111k | size = 2; |
208 | 111k | break; |
209 | 0 | case MVM_operand_callsite: |
210 | 0 | template[1] = opr.callsite_idx; |
211 | 0 | template[2] = sizeof(MVMuint16); |
212 | 0 | break; |
213 | 55.5k | case MVM_operand_spesh_slot: |
214 | 55.5k | template[1] = opr.lit_i16; |
215 | 55.5k | template[2] = sizeof(MVMuint16); |
216 | 55.5k | break; |
217 | 0 | default: |
218 | 0 | MVM_oops(tc, "Can't add constant for operand type %d\n", (info & MVM_operand_type_mask) >> 3); |
219 | 371k | } |
220 | 371k | MVM_VECTOR_APPEND(tree->nodes, template, size); |
221 | 371k | return num; |
222 | 371k | } |
223 | | |
224 | 0 | static MVMint32 getlex_needs_autoviv(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIns *ins) { |
225 | 0 | MVMSpeshOperand opr = ins->operands[1]; |
226 | 0 | MVMuint16 lexical_type = MVM_spesh_get_lex_type(tc, jg->sg, opr.lex.outers, opr.lex.idx); |
227 | 0 | return lexical_type == MVM_reg_obj; |
228 | 0 | } |
229 | | |
230 | 816 | static MVMint32 bindlex_needs_write_barrier(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIns *ins) { |
231 | 816 | MVMSpeshOperand opr = ins->operands[0]; |
232 | 816 | MVMuint16 lexical_type = MVM_spesh_get_lex_type(tc, jg->sg, opr.lex.outers, opr.lex.idx); |
233 | 816 | /* need to hit a write barrier if we bindlex to a string */ |
234 | 816 | return lexical_type == MVM_reg_obj || lexical_type == MVM_reg_str; |
235 | 816 | } |
236 | | |
237 | | |
238 | 748k | static MVMint32 ins_has_single_input_output_operand(MVMSpeshIns *ins) { |
239 | 748k | switch (ins->info->opcode) { |
240 | 20.1k | case MVM_OP_inc_i: |
241 | 20.1k | case MVM_OP_inc_u: |
242 | 20.1k | case MVM_OP_dec_i: |
243 | 20.1k | case MVM_OP_dec_u: |
244 | 20.1k | return 1; |
245 | 728k | default: |
246 | 728k | break; |
247 | 748k | } |
248 | 728k | return 0; |
249 | 748k | } |
250 | | |
251 | | void MVM_jit_expr_load_operands(MVMThreadContext *tc, MVMJitExprTree *tree, MVMSpeshIns *ins, |
252 | 738k | struct ValueDefinition *values, MVMint32 *operands) { |
253 | 738k | MVMint32 i; |
254 | 2.27M | for (i = 0; i < ins->info->num_operands; i++) { |
255 | 1.53M | MVMSpeshOperand opr = ins->operands[i]; |
256 | 1.53M | MVMint8 opr_kind = ins->info->operands[i]; |
257 | 1.53M | switch(opr_kind & MVM_operand_rw_mask) { |
258 | 588k | case MVM_operand_read_reg: |
259 | 588k | if (values[opr.reg.orig].node >= 0) { |
260 | 257k | operands[i] = values[opr.reg.orig].node; |
261 | 331k | } else { |
262 | 331k | MVMint32 addr = MVM_jit_expr_add_regaddr(tc, tree, opr.reg.orig); |
263 | 331k | operands[i] = MVM_jit_expr_add_load(tc, tree, addr); |
264 | 331k | values[opr.reg.orig].node = operands[i]; |
265 | 331k | values[opr.reg.orig].addr = addr; |
266 | 331k | values[opr.reg.orig].root = -1; /* load is not part of a root */ |
267 | 331k | } |
268 | 588k | break; |
269 | 578k | case MVM_operand_write_reg: |
270 | 578k | /* get address of register to write */ |
271 | 578k | operands[i] = MVM_jit_expr_add_regaddr(tc, tree, opr.reg.orig); |
272 | 578k | break; |
273 | 371k | case MVM_operand_literal: |
274 | 371k | operands[i] = MVM_jit_expr_add_const(tc, tree, opr, ins->info->operands[i]); |
275 | 371k | break; |
276 | 0 | case MVM_operand_read_lex: |
277 | 0 | { |
278 | 0 | MVMint32 addr = MVM_jit_expr_add_lexaddr(tc, tree, opr.lex.outers, opr.lex.idx); |
279 | 0 | operands[i] = MVM_jit_expr_add_load(tc, tree, addr); |
280 | 0 | break; |
281 | 588k | } |
282 | 30 | case MVM_operand_write_lex: |
283 | 30 | operands[i] = MVM_jit_expr_add_lexaddr(tc, tree, opr.lex.outers, opr.lex.idx); |
284 | 30 | break; |
285 | 0 | default: |
286 | 0 | continue; |
287 | 1.53M | } |
288 | 1.53M | if (operands[i] >= tree->nodes_num || operands[i] < 0) { |
289 | 0 | MVM_oops(tc, "JIT: something is wrong with operand loading"); |
290 | 0 | } |
291 | 1.53M | } |
292 | 738k | |
293 | 738k | /* A HACK. |
294 | 738k | * |
295 | 738k | * dec_i and inc_i have a single operand that acts both as input and output. |
296 | 738k | * This is marked only as an output operand, though. Thus, we load the |
297 | 738k | * address here, and define the value later. However, if we have multiple of |
298 | 738k | * these in sequence, each will load the old value from memory, disregarding |
299 | 738k | * the value that an earlier operator has defined, i.e. losing the update. |
300 | 738k | * That's a bug, and this tries to fix it, by forcing a 'split' between the |
301 | 738k | * input and the output operand. |
302 | 738k | */ |
303 | 738k | if (ins_has_single_input_output_operand(ins)) { |
304 | 10.0k | MVMuint16 reg = ins->operands[0].reg.orig; |
305 | 10.0k | if (values[reg].node >= 0) { |
306 | 2.28k | operands[1] = values[reg].node; |
307 | 7.81k | } else { |
308 | 7.81k | /* operands[0] has the address */ |
309 | 7.81k | operands[1] = MVM_jit_expr_add_load(tc, tree, operands[0]); |
310 | 7.81k | /* no need to insert it in the table since it will be directly |
311 | 7.81k | * overwritten */ |
312 | 7.81k | } |
313 | 10.0k | } |
314 | 738k | } |
315 | | |
316 | | /* This function is to check the internal consistency of a template |
317 | | * before I apply it. I need this because I make a lot of mistakes in |
318 | | * writing templates, and debugging is hard. */ |
319 | 738k | static void check_template(MVMThreadContext *tc, const MVMJitExprTemplate *template, MVMSpeshIns *ins) { |
320 | 738k | MVMint32 i; |
321 | 21.2M | for (i = 0; i < template->len; i++) { |
322 | 20.5M | switch(template->info[i]) { |
323 | 0 | case 0: |
324 | 0 | MVM_oops(tc, "JIT: Template info shorter than template length (instruction %s)", ins->info->name); |
325 | 0 | break; |
326 | 6.54M | case 'l': |
327 | 6.54M | if (template->code[i] >= i || template->code[i] < 0) |
328 | 0 | MVM_oops(tc, "JIT: Template link out of bounds (instruction: %s)", ins->info->name); |
329 | 6.54M | break; |
330 | 1.76M | case 'f': |
331 | 1.76M | if (template->code[i] < 0 || |
332 | 1.76M | (template->code[i] >= ins->info->num_operands && |
333 | 10.0k | !ins_has_single_input_output_operand(ins))) |
334 | 0 | MVM_oops(tc, "JIT: Operand access out of bounds (instruction: %s)", ins->info->name); |
335 | 1.76M | break; |
336 | 12.2M | default: |
337 | 12.2M | continue; |
338 | 20.5M | } |
339 | 20.5M | } |
340 | 738k | if (template->info[i]) |
341 | 0 | MVM_oops(tc, "JIT: Template info longer than template length (instruction: %s)", |
342 | 0 | ins->info->name); |
343 | 738k | } |
344 | | |
345 | | /* Add template to nodes, filling in operands and linking tree nodes. Return template root */ |
346 | | MVMint32 MVM_jit_expr_apply_template(MVMThreadContext *tc, MVMJitExprTree *tree, |
347 | 738k | const MVMJitExprTemplate *template, MVMint32 *operands) { |
348 | 738k | MVMint32 i, num; |
349 | 738k | num = tree->nodes_num; |
350 | 738k | MVM_VECTOR_ENSURE_SPACE(tree->nodes, template->len); |
351 | 738k | /* Loop over string until the end */ |
352 | 21.2M | for (i = 0; template->info[i]; i++) { |
353 | 20.5M | switch (template->info[i]) { |
354 | 6.54M | case 'l': |
355 | 6.54M | /* link template-relative to nodes-relative */ |
356 | 6.54M | tree->nodes[num+i] = template->code[i] + num; |
357 | 6.54M | break; |
358 | 1.76M | case 'f': |
359 | 1.76M | /* add operand node into the nodes */ |
360 | 1.76M | tree->nodes[num+i] = operands[template->code[i]]; |
361 | 1.76M | break; |
362 | 12.2M | default: |
363 | 12.2M | /* copy from template to nodes */ |
364 | 12.2M | tree->nodes[num+i] = template->code[i]; |
365 | 12.2M | break; |
366 | 20.5M | } |
367 | 20.5M | } |
368 | 738k | tree->nodes_num = num + template->len; |
369 | 738k | return num + template->root; /* root relative to nodes */ |
370 | 738k | } |
371 | | |
372 | | |
373 | | |
374 | | /* Collect tree analysis information, add stores of computed values */ |
375 | | static void analyze_node(MVMThreadContext *tc, MVMJitTreeTraverser *traverser, |
376 | 9.82M | MVMJitExprTree *tree, MVMint32 node) { |
377 | 9.82M | |
378 | 9.82M | const MVMJitExprOpInfo *op_info = MVM_jit_expr_op_info(tc, tree->nodes[node]); |
379 | 9.82M | MVMint32 first_child = node + 1; |
380 | 9.24M | MVMint32 nchild = op_info->nchild < 0 ? tree->nodes[first_child++] : op_info->nchild; |
381 | 9.82M | MVMJitExprNode *args = tree->nodes + first_child + nchild; |
382 | 9.82M | MVMJitExprNodeInfo *node_info = tree->info + node; |
383 | 9.82M | MVMint32 i; |
384 | 9.82M | |
385 | 9.82M | node_info->op_info = op_info; |
386 | 9.82M | /* propagate node sizes and assign labels */ |
387 | 9.82M | switch (tree->nodes[node]) { |
388 | 683k | case MVM_JIT_CONST: |
389 | 683k | /* node size is given */ |
390 | 683k | node_info->size = args[1]; |
391 | 683k | break; |
392 | 193k | case MVM_JIT_COPY: |
393 | 193k | node_info->size = tree->info[tree->nodes[first_child]].size; |
394 | 193k | break; |
395 | 1.51M | case MVM_JIT_LOAD: |
396 | 1.51M | node_info->size = args[0]; |
397 | 1.51M | break; |
398 | 5.30k | case MVM_JIT_CAST: |
399 | 5.30k | node_info->size = args[0]; |
400 | 5.30k | break; |
401 | 3.71M | case MVM_JIT_ADDR: |
402 | 3.71M | case MVM_JIT_IDX: |
403 | 3.71M | case MVM_JIT_LABEL: |
404 | 3.71M | case MVM_JIT_TC: |
405 | 3.71M | case MVM_JIT_CU: |
406 | 3.71M | case MVM_JIT_LOCAL: |
407 | 3.71M | case MVM_JIT_STACK: |
408 | 3.71M | /* addresses result in pointers */ |
409 | 3.71M | node_info->size = MVM_JIT_PTR_SZ; |
410 | 3.71M | break; |
411 | 3.71M | /* binary operations */ |
412 | 213k | case MVM_JIT_ADD: |
413 | 213k | case MVM_JIT_SUB: |
414 | 213k | case MVM_JIT_AND: |
415 | 213k | case MVM_JIT_OR: |
416 | 213k | case MVM_JIT_XOR: |
417 | 213k | case MVM_JIT_NOT: |
418 | 213k | /* comparisons */ |
419 | 213k | case MVM_JIT_NE: |
420 | 213k | case MVM_JIT_LT: |
421 | 213k | case MVM_JIT_LE: |
422 | 213k | case MVM_JIT_EQ: |
423 | 213k | case MVM_JIT_GE: |
424 | 213k | case MVM_JIT_GT: |
425 | 213k | { |
426 | 213k | /* arithmetic nodes use their largest operand */ |
427 | 213k | MVMint32 left = tree->nodes[first_child]; |
428 | 213k | MVMint32 right = tree->nodes[first_child+1]; |
429 | 213k | node_info->size = MAX(tree->info[left].size, |
430 | 213k | tree->info[right].size); |
431 | 213k | break; |
432 | 213k | } |
433 | 33.1k | case MVM_JIT_FLAGVAL: |
434 | 33.1k | /* XXX THIS IS A HACK |
435 | 33.1k | * |
436 | 33.1k | * The true size of 'flagval' is a single byte. But that would mean it |
437 | 33.1k | * had to be upcast to be used as a 64-bit word, and that subtly |
438 | 33.1k | * doesn't work if the value is STORE-d to memory. */ |
439 | 33.1k | node_info->size = 4; |
440 | 33.1k | break; |
441 | 113k | case MVM_JIT_DO: |
442 | 113k | /* node size of last child */ |
443 | 113k | { |
444 | 113k | MVMint32 last_child = tree->nodes[first_child + nchild - 1]; |
445 | 113k | node_info->size = tree->info[last_child].size; |
446 | 113k | break; |
447 | 213k | } |
448 | 102k | case MVM_JIT_IF: |
449 | 102k | { |
450 | 102k | MVMint32 left = tree->nodes[first_child+1]; |
451 | 102k | MVMint32 right = tree->nodes[first_child+2]; |
452 | 102k | node_info->size = MAX(tree->info[left].size, |
453 | 102k | tree->info[right].size); |
454 | 102k | break; |
455 | 213k | } |
456 | 113k | case MVM_JIT_CALL: |
457 | 113k | node_info->size = args[0]; |
458 | 113k | break; |
459 | 391k | case MVM_JIT_NZ: |
460 | 391k | case MVM_JIT_ZR: |
461 | 391k | node_info->size = tree->info[tree->nodes[first_child]].size; |
462 | 391k | break; |
463 | 2.74M | default: |
464 | 2.74M | /* all other things, branches, labels, when, arglist, carg, |
465 | 2.74M | * comparisons, etc, have no value size */ |
466 | 2.74M | node_info->size = 0; |
467 | 2.74M | break; |
468 | 9.82M | } |
469 | 9.82M | |
470 | 9.82M | /* Insert casts as necessary */ |
471 | 9.82M | if (op_info->cast != MVM_JIT_NO_CAST) { |
472 | 5.80M | for (i = 0; i < nchild; i++) { |
473 | 3.06M | MVMint32 child = tree->nodes[first_child+i]; |
474 | 3.06M | if (tree->nodes[child] == MVM_JIT_CONST) { |
475 | 275k | /* CONST nodes can always take over their target size, so they never need to be cast */ |
476 | 275k | tree->info[child].size = tree->info[node].size; |
477 | 2.79M | } else if (tree->info[child].size < node_info->size) { |
478 | 5.30k | /* Widening casts need to be handled explicitly, shrinking casts do not */ |
479 | 5.30k | MVMint32 cast = MVM_jit_expr_add_cast(tc, tree, child, node_info->size, tree->info[child].size, op_info->cast); |
480 | 5.30k | /* Because the cast may have grown the backing nodes array, the info array needs to grow as well */ |
481 | 5.30k | MVM_VECTOR_ENSURE_SIZE(tree->info, cast); |
482 | 5.30k | /* And because analyze_node is called in postorder, |
483 | 5.30k | the newly added cast node would be neglected by the |
484 | 5.30k | traverser. So we traverse it explicitly.. */ |
485 | 5.30k | MVM_VECTOR_ENSURE_SIZE(traverser->visits, cast); |
486 | 5.30k | traverser->visits[cast] = 1; |
487 | 5.30k | analyze_node(tc, traverser, tree, cast); |
488 | 5.30k | /* Finally we replace the child with its cast */ |
489 | 5.30k | tree->nodes[first_child+i] = cast; |
490 | 5.30k | } |
491 | 3.06M | } |
492 | 2.73M | } |
493 | 9.82M | } |
494 | | |
495 | | static void assign_labels(MVMThreadContext *tc, MVMJitTreeTraverser *traverser, |
496 | 9.82M | MVMJitExprTree *tree, MVMint32 node) { |
497 | 9.82M | /* IF has two blocks, the first I call left, the second I call right. |
498 | 9.82M | Regular IF is implemented by the following sequence: |
499 | 9.82M | |
500 | 9.82M | * test |
501 | 9.82M | * negated conditional jump to label 1 |
502 | 9.82M | * left block |
503 | 9.82M | * unconditional jump to label 2 |
504 | 9.82M | * label 1 |
505 | 9.82M | * right block |
506 | 9.82M | * label 2 |
507 | 9.82M | |
508 | 9.82M | The 'short-circuiting' cases of IF ALL and IF ANY require special |
509 | 9.82M | treatment. IF ALL simply repeats the test+negated branch for each of the |
510 | 9.82M | ALL's children. IF ANY on the other hand must short circuit not into the |
511 | 9.82M | default (right) but into the (left) conditional block. So IF ANY must be |
512 | 9.82M | implemented as: |
513 | 9.82M | |
514 | 9.82M | (* test |
515 | 9.82M | * conditional jump to label 3) - repeated n times |
516 | 9.82M | * unconditional jump to label 1 |
517 | 9.82M | * label 3 |
518 | 9.82M | * left block |
519 | 9.82M | * unconditional jump to label 2 |
520 | 9.82M | * label 1 |
521 | 9.82M | * right block |
522 | 9.82M | * label 2 |
523 | 9.82M | |
524 | 9.82M | NB - the label before the left block has been given the number |
525 | 9.82M | 3 for consistency with the regular case. |
526 | 9.82M | |
527 | 9.82M | Simpilar observations are applicable to WHEN and WHEN ANY/WHEN |
528 | 9.82M | ALL. Different altogether are the cases of ANY ALL and ALL |
529 | 9.82M | ANY. |
530 | 9.82M | |
531 | 9.82M | ANY ALL can be implemented as: |
532 | 9.82M | ( test |
533 | 9.82M | negated conditional jump to label 4) - repeated for all in ALL |
534 | 9.82M | * unconditional jump to label 3 |
535 | 9.82M | * label 4 (continuing the ANY) |
536 | 9.82M | |
537 | 9.82M | This way the 'short-circuit' jump of the ALL sequence implies |
538 | 9.82M | the continuation of the ANY sequence, whereas the finishing of |
539 | 9.82M | the ALL sequence implies it succeeded and hence the ANY needs |
540 | 9.82M | to short-circuit. |
541 | 9.82M | |
542 | 9.82M | ALL ANY can be implemented analogously as: |
543 | 9.82M | ( test |
544 | 9.82M | conditional jump to label 4) repeated for all children of ANY |
545 | 9.82M | * unconditional short-circuit jump to label 1 |
546 | 9.82M | * label 4 |
547 | 9.82M | |
548 | 9.82M | Nested ALL in ALL and ANY in ANY all have the same |
549 | 9.82M | short-circuiting behaviour (i.e. a nested ALL in ALL is |
550 | 9.82M | indistinguishable from inserting all the children of the nested |
551 | 9.82M | ALL into the nesting ALL), so they don't require special |
552 | 9.82M | treatment. |
553 | 9.82M | |
554 | 9.82M | All this goes to say in that the number of labels required and |
555 | 9.82M | the actual labels assigned to different children depends on the |
556 | 9.82M | structure of the tree, which is why labels are 'pushed down' |
557 | 9.82M | from parents to children, at least when those children are ANY |
558 | 9.82M | and ALL. */ |
559 | 9.82M | |
560 | 9.82M | switch (tree->nodes[node]) { |
561 | 91.7k | case MVM_JIT_WHEN: |
562 | 91.7k | { |
563 | 91.7k | /* WHEN just requires one label in the default case */ |
564 | 91.7k | MVMint32 test = tree->nodes[node+1]; |
565 | 91.7k | tree->info[node].label = tree->num_labels++; |
566 | 91.7k | if (tree->nodes[test] == MVM_JIT_ANY) { |
567 | 0 | /* ANY requires a pre-left-block label */ |
568 | 0 | tree->info[test].label = tree->num_labels++; |
569 | 91.7k | } else if (tree->nodes[test] == MVM_JIT_ALL) { |
570 | 44.0k | /* ALL takes over the label of its parent */ |
571 | 44.0k | tree->info[test].label = tree->info[node].label; |
572 | 44.0k | } |
573 | 91.7k | } |
574 | 91.7k | break; |
575 | 160k | case MVM_JIT_IF: |
576 | 160k | case MVM_JIT_IFV: |
577 | 160k | { |
578 | 160k | MVMint32 test = tree->nodes[node+1]; |
579 | 160k | /* take two labels, one for the left block and one for the right block */ |
580 | 160k | tree->info[node].label = tree->num_labels; |
581 | 160k | tree->num_labels += 2; |
582 | 160k | if (tree->nodes[test] == MVM_JIT_ANY) { |
583 | 22 | /* assign 'label 3' to the ANY */ |
584 | 22 | tree->info[test].label = tree->num_labels++; |
585 | 160k | } else if (tree->nodes[test] == MVM_JIT_ALL) { |
586 | 43.8k | /* assign 'label 1' to the ALL */ |
587 | 43.8k | tree->info[test].label = tree->info[node].label; |
588 | 43.8k | } |
589 | 160k | } |
590 | 160k | break; |
591 | 87.9k | case MVM_JIT_ALL: |
592 | 87.9k | { |
593 | 87.9k | MVMint32 nchild = tree->nodes[node+1]; |
594 | 87.9k | MVMint32 i; |
595 | 328k | for (i = 0; i < nchild; i++) { |
596 | 240k | MVMint32 test = tree->nodes[node+2+i]; |
597 | 240k | if (tree->nodes[test] == MVM_JIT_ALL) { |
598 | 0 | /* use same label for child as parent */ |
599 | 0 | tree->info[test].label = tree->info[node].label; |
600 | 240k | } else if (tree->nodes[test] == MVM_JIT_ANY) { |
601 | 0 | /* assign an extra label for ANY to short-circuit into */ |
602 | 0 | tree->info[test].label = tree->num_labels++; |
603 | 0 | } |
604 | 240k | } |
605 | 87.9k | } |
606 | 87.9k | break; |
607 | 22 | case MVM_JIT_ANY: |
608 | 22 | { |
609 | 22 | MVMint32 nchild = tree->nodes[node+1]; |
610 | 22 | MVMint32 i; |
611 | 66 | for (i = 0; i < nchild; i++) { |
612 | 44 | MVMint32 test = tree->nodes[node+2+i]; |
613 | 44 | if (tree->nodes[test] == MVM_JIT_ANY) { |
614 | 0 | tree->info[test].label = tree->info[node].label; |
615 | 44 | } else if (tree->nodes[test] == MVM_JIT_ALL) { |
616 | 0 | tree->info[test].label = tree->num_labels++; |
617 | 0 | } |
618 | 44 | } |
619 | 22 | } |
620 | 22 | break; |
621 | 9.48M | default: |
622 | 9.48M | break; |
623 | 9.82M | } |
624 | 9.82M | } |
625 | | |
626 | | |
627 | 262k | void MVM_jit_expr_tree_analyze(MVMThreadContext *tc, MVMJitExprTree *tree) { |
628 | 262k | /* analyse the tree, calculate usage and destination information */ |
629 | 262k | MVMJitTreeTraverser traverser; |
630 | 262k | MVM_VECTOR_ENSURE_SIZE(tree->info, tree->nodes_num); |
631 | 262k | traverser.policy = MVM_JIT_TRAVERSER_ONCE; |
632 | 262k | traverser.data = NULL; |
633 | 262k | traverser.preorder = &assign_labels; |
634 | 262k | traverser.inorder = NULL; |
635 | 262k | traverser.postorder = &analyze_node; |
636 | 262k | MVM_jit_expr_tree_traverse(tc, tree, &traverser); |
637 | 262k | } |
638 | | |
639 | | |
640 | | |
641 | | /* insert stores for all the active unstored values */ |
642 | | static void active_values_flush(MVMThreadContext *tc, MVMJitExprTree *tree, |
643 | 353k | struct ValueDefinition *values, MVMint32 num_values) { |
644 | 353k | MVMint32 i; |
645 | 28.1M | for (i = 0; i < num_values; i++) { |
646 | 27.7M | if (values[i].root >= 0) { |
647 | 412k | tree->roots[values[i].root] = MVM_jit_expr_add_store( |
648 | 412k | tc, tree, values[i].addr, |
649 | 412k | values[i].node, MVM_JIT_REG_SZ |
650 | 412k | ); |
651 | 412k | } |
652 | 27.7M | if (values[i].node >= 0) { |
653 | 715k | memset(values + i, -1, sizeof(struct ValueDefinition)); |
654 | 715k | } |
655 | 27.7M | } |
656 | 353k | } |
657 | | |
658 | | |
659 | | /* TODO add labels to the expression tree */ |
660 | 296k | MVMJitExprTree * MVM_jit_expr_tree_build(MVMThreadContext *tc, MVMJitGraph *jg, MVMSpeshIterator *iter) { |
661 | 296k | MVMSpeshGraph *sg = jg->sg; |
662 | 296k | MVMSpeshIns *entry = iter->ins; |
663 | 296k | MVMSpeshIns *ins; |
664 | 296k | MVMJitExprTree *tree; |
665 | 296k | MVMint32 operands[MVM_MAX_OPERANDS]; |
666 | 296k | struct ValueDefinition *values; |
667 | 296k | MVMint32 root, node; |
668 | 296k | MVMuint16 i; |
669 | 296k | /* No instructions, just skip */ |
670 | 296k | if (!iter->ins) |
671 | 0 | return NULL; |
672 | 296k | |
673 | 296k | /* Make the tree */ |
674 | 296k | tree = MVM_malloc(sizeof(MVMJitExprTree)); |
675 | 296k | MVM_VECTOR_INIT(tree->nodes, 256); |
676 | 296k | MVM_VECTOR_INIT(tree->info, 256); |
677 | 296k | MVM_VECTOR_INIT(tree->roots, 16); |
678 | 296k | |
679 | 296k | /* ensure that all references are nonzero */ |
680 | 296k | MVM_VECTOR_PUSH(tree->nodes, MVM_JIT_NOOP); |
681 | 296k | |
682 | 296k | tree->graph = jg; |
683 | 296k | tree->num_labels = 0; |
684 | 296k | /* Hold indices to the node that last computed a value belonging |
685 | 296k | * to a register. Initialized as -1 to indicate that these |
686 | 296k | * values are empty. */ |
687 | 296k | values = MVM_malloc(sizeof(struct ValueDefinition)*sg->num_locals); |
688 | 296k | memset(values, -1, sizeof(struct ValueDefinition)*sg->num_locals); |
689 | 296k | |
690 | 4.94M | #define BAIL(x, ...) do { if (x) { MVM_jit_log(tc, __VA_ARGS__); goto done; } } while (0) |
691 | 296k | |
692 | 296k | /* Generate a tree based on templates. The basic idea is to keep a |
693 | 296k | index to the node that last computed the value of a local. |
694 | 296k | Each opcode is translated to the expression using a template, |
695 | 296k | which is a): filled with nodes coming from operands and b): |
696 | 296k | internally linked together (relative to absolute indexes). |
697 | 296k | Afterwards stores are inserted for computed values. */ |
698 | 296k | |
699 | 1.71M | for (ins = iter->ins; ins != NULL; ins = MVM_spesh_iterator_next_ins(tc, iter)) { |
700 | 1.49M | /* NB - we probably will want to involve the spesh info in selecting a |
701 | 1.49M | template. And for optimisation, I'd like to copy spesh facts (if any) |
702 | 1.49M | to the tree info */ |
703 | 1.49M | MVMuint16 opcode = ins->info->opcode; |
704 | 1.49M | MVMSpeshAnn *ann; |
705 | 1.49M | const MVMJitExprTemplate *template; |
706 | 1.49M | MVMint32 before_label = -1, after_label = -1, store_directly = 0; |
707 | 1.49M | |
708 | 1.49M | struct ValueDefinition *defined_value = NULL; |
709 | 1.49M | |
710 | 1.49M | /* check if this is a getlex and if we can handle it */ |
711 | 1.49M | BAIL(opcode == MVM_OP_getlex && getlex_needs_autoviv(tc, jg, ins), "Can't compile object getlex"); |
712 | 1.49M | BAIL(opcode == MVM_OP_bindlex && bindlex_needs_write_barrier(tc, jg, ins), "Can't compile write-barrier bindlex"); |
713 | 1.49M | |
714 | 1.49M | /* Check annotations that may require handling or wrapping the expression */ |
715 | 1.74M | for (ann = ins->annotations; ann != NULL; ann = ann->next) { |
716 | 268k | MVMint32 idx; |
717 | 268k | switch (ann->type) { |
718 | 17.3k | case MVM_SPESH_ANN_FH_START: |
719 | 17.3k | /* start of a frame handler (inclusive). We need to mark this |
720 | 17.3k | * instruction with a label so that we know the handler covers |
721 | 17.3k | * this code */ |
722 | 17.3k | before_label = MVM_jit_label_before_ins(tc, jg, iter->bb, ins); |
723 | 17.3k | jg->handlers[ann->data.frame_handler_index].start_label = before_label; |
724 | 17.3k | break; |
725 | 16.0k | case MVM_SPESH_ANN_FH_END: |
726 | 16.0k | /* end of the frame handler (exclusive), funnily enough not |
727 | 16.0k | * necessarily the end of a basic block. */ |
728 | 16.0k | before_label = MVM_jit_label_before_ins(tc, jg, iter->bb, ins); |
729 | 16.0k | jg->handlers[ann->data.frame_handler_index].end_label = before_label; |
730 | 16.0k | break; |
731 | 17.4k | case MVM_SPESH_ANN_FH_GOTO: |
732 | 17.4k | /* A label to jump to for when a handler catches an |
733 | 17.4k | * exception. Thus, this can be a control flow entry point (and |
734 | 17.4k | * should be the start of a basic block, but I'm not sure if it |
735 | 17.4k | * always is). */ |
736 | 17.4k | before_label = MVM_jit_label_before_ins(tc, jg, iter->bb, ins); |
737 | 17.4k | jg->handlers[ann->data.frame_handler_index].goto_label = before_label; |
738 | 17.4k | active_values_flush(tc, tree, values, sg->num_locals); |
739 | 17.4k | break; |
740 | 4.94k | case MVM_SPESH_ANN_DEOPT_OSR: |
741 | 4.94k | /* A label the OSR can jump into to 'start running', so to |
742 | 4.94k | * speak. As it breaks the basic-block assumption, arguably, |
743 | 4.94k | * this should only ever be at the start of a basic block. But |
744 | 4.94k | * it's not. So we have to insert the label and compute it. */ |
745 | 4.94k | before_label = MVM_jit_label_before_ins(tc, jg, iter->bb, ins); |
746 | 4.94k | /* OSR reuses the deopt label mechanism */ |
747 | 4.94k | MVM_VECTOR_ENSURE_SIZE(jg->deopts, idx = jg->deopts_num++); |
748 | 4.94k | jg->deopts[idx].label = before_label; |
749 | 4.94k | jg->deopts[idx].idx = ann->data.deopt_idx; |
750 | 4.94k | /* possible entrypoint, so flush intermediates */ |
751 | 4.94k | active_values_flush(tc, tree, values, sg->num_locals); |
752 | 4.94k | break; |
753 | 8.45k | case MVM_SPESH_ANN_INLINE_START: |
754 | 8.45k | /* start of an inline, used for reconstructing state when deoptimizing */ |
755 | 8.45k | before_label = MVM_jit_label_before_ins(tc, jg, iter->bb, ins); |
756 | 8.45k | jg->inlines[ann->data.inline_idx].start_label = before_label; |
757 | 8.45k | break; |
758 | 7.04k | case MVM_SPESH_ANN_INLINE_END: |
759 | 7.04k | /* end of the inline (inclusive), so we need to add a label, |
760 | 7.04k | * which should be the end of the basic block. */ |
761 | 7.04k | after_label = MVM_jit_label_after_ins(tc, jg, iter->bb, ins); |
762 | 7.04k | jg->inlines[ann->data.inline_idx].end_label = after_label; |
763 | 7.04k | break; |
764 | 34.3k | case MVM_SPESH_ANN_DEOPT_INLINE: |
765 | 34.3k | MVM_jit_log(tc, "Not sure if we can handle DEOPT_INLINE on instruction %s\n", ins->info->name); |
766 | 34.3k | break; |
767 | 124k | case MVM_SPESH_ANN_DEOPT_ONE_INS: |
768 | 124k | /* we should only see this in guards, which we don't do just |
769 | 124k | * yet, although we will. At the very least, this implies a flush. */ |
770 | 124k | switch (opcode) { |
771 | 12.6k | case MVM_OP_sp_guard: |
772 | 12.6k | case MVM_OP_sp_guardconc: |
773 | 12.6k | case MVM_OP_sp_guardtype: |
774 | 12.6k | case MVM_OP_sp_guardsf: |
775 | 12.6k | BAIL(1, "Cannot handle DEOPT_ONE (ins=%s)\n", ins->info->name); |
776 | 0 | break; |
777 | 124k | } |
778 | 111k | break; |
779 | 0 | case MVM_SPESH_ANN_DEOPT_ALL_INS: |
780 | 0 | /* don't expect to be handling these, either, but these also |
781 | 0 | * might need a label-after-the-fact */ |
782 | 0 | after_label = MVM_jit_label_after_ins(tc, jg, iter->bb, ins); |
783 | 0 | /* ensure a consistent state for deoptimization */ |
784 | 0 | active_values_flush(tc, tree, values, sg->num_locals); |
785 | 0 | /* add deopt idx */ |
786 | 0 | MVM_VECTOR_ENSURE_SIZE(jg->deopts, idx = jg->deopts_num++); |
787 | 0 | jg->deopts[idx].label = after_label; |
788 | 0 | jg->deopts[idx].idx = ann->data.deopt_idx; |
789 | 0 | break; |
790 | 268k | } |
791 | 268k | } |
792 | 1.49M | |
793 | 1.49M | |
794 | 1.47M | if (opcode == MVM_SSA_PHI || opcode == MVM_OP_no_op) { |
795 | 681k | /* By definition, a PHI node can only occur at the start of a basic |
796 | 681k | * block. (A no_op instruction only seems to happen as the very |
797 | 681k | * first instruction of a frame, and I'm not sure why). |
798 | 681k | * |
799 | 681k | * Thus, if it happens that we've processed annotations on those |
800 | 681k | * instructions (which probably means they migrated there from |
801 | 681k | * somewhere else), they always refer to the start of the basic |
802 | 681k | * block, which is already assigned a label and |
803 | 681k | * dynamic-control-handler. |
804 | 681k | * |
805 | 681k | * So we never need to do anything with this label and wrapper, but |
806 | 681k | * we do need to process the annotation to setup the frame handler |
807 | 681k | * correctly. |
808 | 681k | */ |
809 | 681k | BAIL(after_label >= 0, "A PHI node should not have an after label"); |
810 | 681k | continue; |
811 | 681k | } |
812 | 1.47M | |
813 | 796k | template = MVM_jit_get_template_for_opcode(opcode); |
814 | 796k | BAIL(template == NULL, "Cannot get template for: %s\n", ins->info->name); |
815 | 796k | |
816 | 738k | check_template(tc, template, ins); |
817 | 738k | |
818 | 738k | MVM_jit_expr_load_operands(tc, tree, ins, values, operands); |
819 | 738k | root = MVM_jit_expr_apply_template(tc, tree, template, operands); |
820 | 738k | |
821 | 738k | /* root is highest node by construction, so we don't have to check the size of info later */ |
822 | 738k | MVM_VECTOR_ENSURE_SIZE(tree->info, root); |
823 | 738k | tree->info[root].spesh_ins = ins; |
824 | 738k | |
825 | 738k | |
826 | 738k | /* mark operand types */ |
827 | 2.27M | for (i = 0; i < ins->info->num_operands; i++) { |
828 | 1.53M | MVMint8 opr_kind = ins->info->operands[i]; |
829 | 1.53M | MVMint8 opr_type = opr_kind & MVM_operand_type_mask; |
830 | 1.53M | MVMSpeshOperand opr = ins->operands[i]; |
831 | 1.53M | if (opr_type == MVM_operand_type_var) { |
832 | 300k | switch (opr_kind & MVM_operand_rw_mask) { |
833 | 300k | case MVM_operand_read_reg: |
834 | 300k | case MVM_operand_write_reg: |
835 | 300k | opr_type = MVM_spesh_get_reg_type(tc, sg, opr.reg.orig) << 3; /* shift up 3 to match operand type */ |
836 | 300k | break; |
837 | 30 | case MVM_operand_read_lex: |
838 | 30 | case MVM_operand_write_lex: |
839 | 30 | opr_type = MVM_spesh_get_lex_type(tc, sg, opr.lex.outers, opr.lex.idx) << 3; |
840 | 30 | break; |
841 | 300k | } |
842 | 300k | } |
843 | 1.53M | switch(opr_kind & MVM_operand_rw_mask) { |
844 | 588k | case MVM_operand_read_reg: |
845 | 588k | case MVM_operand_read_lex: |
846 | 588k | tree->info[operands[i]].opr_type = opr_type; |
847 | 588k | break; |
848 | 578k | case MVM_operand_write_reg: |
849 | 578k | /* for write_reg and write_lex, operands[i] is the *address*, |
850 | 578k | * the *value* is the root, but this is only valid if the |
851 | 578k | * operand index is 0 */ |
852 | 578k | if (template->flags & MVM_JIT_EXPR_TEMPLATE_DESTRUCTIVE) { |
853 | 113k | /* overrides any earlier definition of this local variable */ |
854 | 113k | memset(values + opr.reg.orig, -1, sizeof(struct ValueDefinition)); |
855 | 465k | } else { |
856 | 465k | /* record this value, should be only one for the root */ |
857 | 465k | BAIL(i != 0, "Write reg operand %d", i); |
858 | 465k | tree->info[root].opr_type = opr_type; |
859 | 465k | defined_value = values + opr.reg.orig; |
860 | 465k | defined_value->addr = operands[i]; |
861 | 465k | defined_value->node = root; |
862 | 465k | /* this overwrites any previous definition */ |
863 | 465k | defined_value->root = -1; |
864 | 465k | } |
865 | 578k | break; |
866 | 30 | case MVM_operand_write_lex: |
867 | 30 | /* does not define a value we can look up, but we may need to |
868 | 30 | * insert a store */ |
869 | 30 | if (!(template->flags & MVM_JIT_EXPR_TEMPLATE_DESTRUCTIVE)) { |
870 | 0 | BAIL(i != 0, "Write lex operand %d", i); |
871 | 0 | tree->info[root].opr_type = opr_type; |
872 | 0 | /* insert the store to lexicals directly, do not record as value */ |
873 | 0 | root = MVM_jit_expr_add_store(tc, tree, operands[i], root, MVM_JIT_REG_SZ); |
874 | 0 | } |
875 | 30 | break; |
876 | 1.53M | } |
877 | 1.53M | } |
878 | 738k | |
879 | 738k | |
880 | 738k | |
881 | 738k | |
882 | 738k | if (ins->info->jittivity & (MVM_JIT_INFO_THROWISH | MVM_JIT_INFO_INVOKISH)) { |
883 | 67.7k | /* NB: we should make this a template-level flag; should be possible |
884 | 67.7k | * to replace an invokish version with a non-invokish version (but |
885 | 67.7k | * perhaps best if that is opt-in so people don't accidentally |
886 | 67.7k | * forget to set it). */ |
887 | 67.7k | MVM_jit_log(tc, "EXPR: adding throwish guard to op (%s)\n", ins->info->name); |
888 | 67.7k | active_values_flush(tc, tree, values, sg->num_locals); |
889 | 67.7k | store_directly = 1; |
890 | 67.7k | } |
891 | 738k | |
892 | 738k | |
893 | 738k | /* Add root to tree to ensure source evaluation order, wrapped with |
894 | 738k | * labels if necessary. */ |
895 | 738k | if (before_label >= 0 && MVM_jit_label_is_for_ins(tc, jg, before_label)) { |
896 | 5.69k | MVM_VECTOR_PUSH(tree->roots, MVM_jit_expr_add_label(tc, tree, before_label)); |
897 | 5.69k | } |
898 | 738k | |
899 | 738k | /* NB: GUARD only wraps void nodes. Currently, we replace any |
900 | 738k | * value-yielding node with it's STORE (and thereby make sure it is |
901 | 738k | * flushed directly) */ |
902 | 738k | if (store_directly && defined_value != NULL) { |
903 | 0 | /* If we're wrapping this template and it defines a value, we |
904 | 0 | * had maybe better flush it directly */ |
905 | 0 | root = MVM_jit_expr_add_store(tc, tree, defined_value->addr, root, MVM_JIT_REG_SZ); |
906 | 0 | defined_value = NULL; |
907 | 0 | } |
908 | 738k | |
909 | 738k | if (defined_value != NULL) { |
910 | 465k | defined_value->root = tree->roots_num; |
911 | 465k | } |
912 | 738k | |
913 | 738k | MVM_VECTOR_PUSH(tree->roots, root); |
914 | 738k | if (after_label >= 0 && MVM_jit_label_is_for_ins(tc, jg, after_label)) { |
915 | 0 | MVM_VECTOR_PUSH(tree->roots, MVM_jit_expr_add_label(tc, tree, after_label)); |
916 | 0 | } |
917 | 738k | } |
918 | 296k | |
919 | 296k | done: |
920 | 296k | if (tree->roots_num > 0) { |
921 | 262k | active_values_flush(tc, tree, values, sg->num_locals); |
922 | 262k | MVM_jit_expr_tree_analyze(tc, tree); |
923 | 262k | MVM_jit_log(tc, "Build tree out of: ["); |
924 | 1.65M | for (ins = entry; ins != iter->ins; ins = ins->next) { |
925 | 1.38M | if (ins->info->opcode == MVM_SSA_PHI) |
926 | 650k | continue; |
927 | 738k | MVM_jit_log(tc, "%s, ", ins->info->name); |
928 | 738k | } |
929 | 262k | MVM_jit_log(tc, "]\n"); |
930 | 33.7k | } else { |
931 | 33.7k | /* Don't return empty trees, nobody wants that */ |
932 | 33.7k | MVM_jit_expr_tree_destroy(tc, tree); |
933 | 33.7k | tree = NULL; |
934 | 33.7k | } |
935 | 296k | MVM_free(values); |
936 | 296k | return tree; |
937 | 296k | } |
938 | | |
939 | 284k | void MVM_jit_expr_tree_destroy(MVMThreadContext *tc, MVMJitExprTree *tree) { |
940 | 284k | if (tree->info) |
941 | 284k | MVM_free(tree->info); |
942 | 284k | MVM_free(tree->nodes); |
943 | 284k | MVM_free(tree->roots); |
944 | 284k | MVM_free(tree); |
945 | 284k | } |
946 | | |
947 | | static void walk_tree(MVMThreadContext *tc, MVMJitExprTree *tree, |
948 | 32.9M | MVMJitTreeTraverser *traverser, MVMint32 node) { |
949 | 32.9M | const MVMJitExprOpInfo *info = MVM_jit_expr_op_info(tc, tree->nodes[node]); |
950 | 32.9M | MVMint32 nchild = info->nchild; |
951 | 32.9M | MVMint32 first_child = node + 1; |
952 | 32.9M | MVMint32 i; |
953 | 32.9M | if (traverser->policy == MVM_JIT_TRAVERSER_ONCE && |
954 | 32.9M | traverser->visits[node] >= 1) |
955 | 3.72M | return; |
956 | 32.9M | |
957 | 29.2M | traverser->visits[node]++; |
958 | 29.2M | /* visiting on the way down - NB want to add visitation information */ |
959 | 29.2M | if (traverser->preorder) |
960 | 19.7M | traverser->preorder(tc, traverser, tree, node); |
961 | 29.2M | if (nchild < 0) { |
962 | 1.70M | /* Variadic case: take first child as constant signifying the |
963 | 1.70M | * number of children. Increment because the 'real' children now |
964 | 1.70M | * start node later */ |
965 | 1.70M | nchild = tree->nodes[first_child++]; |
966 | 1.70M | } |
967 | 60.0M | for (i = 0; i < nchild; i++) { |
968 | 30.7M | /* Enter child node */ |
969 | 30.7M | walk_tree(tc, tree, traverser, tree->nodes[first_child+i]); |
970 | 30.7M | if (traverser->inorder) { |
971 | 10.3M | traverser->inorder(tc, traverser, tree, node, i); |
972 | 10.3M | } |
973 | 30.7M | } |
974 | 29.2M | if (traverser->postorder) { |
975 | 29.2M | traverser->postorder(tc, traverser, tree, node); |
976 | 29.2M | } |
977 | 29.2M | } |
978 | | |
979 | | /* TODO specify revisiting policy */ |
980 | | void MVM_jit_expr_tree_traverse(MVMThreadContext *tc, MVMJitExprTree *tree, |
981 | 770k | MVMJitTreeTraverser *traverser) { |
982 | 770k | MVMint32 i; |
983 | 770k | MVM_VECTOR_INIT(traverser->visits, tree->nodes_num); |
984 | 2.96M | for (i = 0; i < tree->roots_num; i++) { |
985 | 2.19M | /* TODO deal with nodes with multiple entries */ |
986 | 2.19M | walk_tree(tc, tree, traverser, tree->roots[i]); |
987 | 2.19M | } |
988 | 770k | MVM_free(traverser->visits); |
989 | 770k | } |
990 | | |
991 | | |
992 | 18.0M | #define FIRST_CHILD(t,x) (t->info[x].op_info->nchild < 0 ? x + 2 : x + 1) |
993 | | /* Walk tree to get nodes along a path */ |
994 | | MVMint32 MVM_jit_expr_tree_get_nodes(MVMThreadContext *tc, MVMJitExprTree *tree, |
995 | | MVMint32 node, const char *path, |
996 | 5.34M | MVMJitExprNode *buffer) { |
997 | 5.34M | MVMJitExprNode *ptr = buffer; |
998 | 16.1M | while (*path) { |
999 | 10.7M | MVMJitExprNode cur_node = node; |
1000 | 18.0M | do { |
1001 | 18.0M | MVMint32 first_child = FIRST_CHILD(tree, cur_node) - 1; |
1002 | 18.0M | MVMint32 child_nr = *path++ - '0'; |
1003 | 18.0M | cur_node = tree->nodes[first_child+child_nr]; |
1004 | 18.0M | } while (*path != '.'); |
1005 | 10.7M | /* regs nodes go to values, others to args */ |
1006 | 10.7M | *ptr++ = cur_node; |
1007 | 10.7M | path++; |
1008 | 10.7M | } |
1009 | 5.34M | return ptr - buffer; |
1010 | 5.34M | } |
1011 | | #undef FIRST_CHILD |