Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/jit/tile.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
#include "internal.h"
3
#include <math.h>
4
5
#if MVM_JIT_ARCH == MVM_JIT_ARCH_X64
6
#include "jit/x64/tile_decl.h"
7
#include "jit/x64/tile_pattern.h"
8
#endif
9
10
11
#if MVM_JIT_DEBUG
12
#define _ASSERT(x, f, ...) do { if (!(x)) { MVM_oops(tc, f, __VA_ARGS__); } } while(0)
13
#else
14
30.7M
#define _ASSERT(x, f, ...) do { } while (0)
15
#endif
16
17
struct TileState {
18
    /* state is junction of applicable tiles */
19
    MVMint32 state;
20
    /* rule is number of best applicable tile */
21
    MVMint32 rule;
22
    /* template is template of assigned tile */
23
    const MVMJitTileTemplate *template;
24
    /* block that ends at this node (or node ref) */
25
    MVMint32 block;
26
};
27
28
struct TreeTiler {
29
    MVM_VECTOR_DECL(struct TileState, states);
30
    MVMJitCompiler *compiler;
31
    MVMJitTileList *list;
32
};
33
34
/* Make complete tiles. Note that any argument passed is interpreted as an
35
 * int32. Used primarily for making 'synthetic' tiles introduced by the
36
 * compiler */
37
MVMJitTile* MVM_jit_tile_make(MVMThreadContext *tc, MVMJitCompiler *compiler,
38
2.54M
                              void *emit, MVMint32 num_args, MVMint32 num_values, ...) {
39
2.54M
    MVMJitTile *tile;
40
2.54M
    MVMint32 i;
41
2.54M
    va_list arglist;
42
2.54M
    va_start(arglist, num_values);
43
2.54M
    tile = MVM_spesh_alloc(tc, compiler->graph->sg, sizeof(MVMJitTile));
44
2.54M
    tile->emit = emit;
45
2.54M
    tile->num_refs = num_values;
46
5.06M
    for (i = 0; i < num_args; i++) {
47
2.51M
        tile->args[i] = va_arg(arglist, MVMint32);
48
2.51M
    }
49
5.27M
    for (i = 0; i < num_values; i++) {
50
2.72M
        tile->values[i] = (MVMint8)va_arg(arglist, MVMint32);
51
2.72M
    }
52
2.54M
    va_end(arglist);
53
2.54M
    return tile;
54
2.54M
}
55
56
/* Postorder collection of tile states (rulesets) */
57
static void tile_node(MVMThreadContext *tc, MVMJitTreeTraverser *traverser,
58
9.53M
                      MVMJitExprTree *tree, MVMint32 node) {
59
9.53M
    struct TreeTiler *tiler      = traverser->data;
60
9.53M
    MVMJitExprNode op            = tree->nodes[node];
61
9.53M
    const MVMJitExprOpInfo *info = tree->info[node].op_info;
62
9.53M
    MVMint32 first_child = node+1;
63
8.97M
    MVMint32 nchild      = info->nchild < 0 ? tree->nodes[first_child++] : info->nchild;
64
9.53M
    MVMint32 *state_info = NULL;
65
9.53M
66
9.53M
    switch (op) {
67
388k
    case MVM_JIT_ALL:
68
388k
    case MVM_JIT_ANY:
69
388k
    case MVM_JIT_ARGLIST:
70
388k
        {
71
388k
            /* Unary variadic nodes are exactly the same... */
72
388k
            MVMint32 i;
73
1.73M
            for (i = 0; i < nchild; i++) {
74
1.34M
                MVMint32 child = tree->nodes[first_child+i];
75
1.34M
                state_info = MVM_jit_tile_state_lookup(tc, op, tiler->states[child].state, -1);
76
1.34M
                _ASSERT(state_info != NULL, "OOPS, %s can't be tiled with a %s child at position %d",
77
1.34M
                        info->name, tree->info[child].op_info->name, i);
78
1.34M
            }
79
388k
            tiler->states[node].state    = state_info[3];
80
388k
            tiler->states[node].rule     = state_info[4];
81
388k
        }
82
388k
        break;
83
174k
    case MVM_JIT_DO:
84
174k
    case MVM_JIT_DOV:
85
174k
        {
86
174k
            MVMint32 last_child = first_child+nchild-1;
87
174k
            MVMint32 left_state = tiler->states[tree->nodes[first_child]].state;
88
174k
            MVMint32 right_state = tiler->states[tree->nodes[last_child]].state;
89
174k
            state_info = MVM_jit_tile_state_lookup(tc, op, left_state, right_state);
90
174k
            _ASSERT(state_info != NULL, "Can't tile this DO node %d", node);
91
174k
            tiler->states[node].state = state_info[3];
92
174k
            tiler->states[node].rule  = state_info[4];
93
174k
        }
94
174k
        break;
95
155k
    case MVM_JIT_IF:
96
155k
    case MVM_JIT_IFV:
97
155k
        {
98
155k
            MVMint32 cond = tree->nodes[node+1],
99
155k
                left = tree->nodes[node+2],
100
155k
                right = tree->nodes[node+3];
101
155k
            MVMint32 *left_state  = MVM_jit_tile_state_lookup(tc, op, tiler->states[cond].state,
102
155k
                                                              tiler->states[left].state);
103
155k
            MVMint32 *right_state = MVM_jit_tile_state_lookup(tc, op, tiler->states[cond].state,
104
155k
                                                              tiler->states[right].state);
105
155k
            _ASSERT(left_state != NULL && right_state != NULL,
106
155k
                    "Inconsistent %s tile state", info->name);
107
155k
            tiler->states[node].state = left_state[3];
108
155k
            tiler->states[node].rule  = left_state[4];
109
155k
        }
110
155k
        break;
111
8.82M
    default:
112
8.82M
        {
113
8.82M
            if (nchild == 0) {
114
2.21M
                state_info = MVM_jit_tile_state_lookup(tc, op, -1, -1);
115
6.60M
            } else if (nchild == 1) {
116
5.38M
                MVMint32 left = tree->nodes[first_child];
117
5.38M
                MVMint32 lstate = tiler->states[left].state;
118
5.38M
                state_info = MVM_jit_tile_state_lookup(tc, op, lstate, -1);
119
1.22M
            } else if (nchild == 2) {
120
1.22M
                MVMint32 left  = tree->nodes[first_child];
121
1.22M
                MVMint32 lstate = tiler->states[left].state;
122
1.22M
                MVMint32 right = tree->nodes[first_child+1];
123
1.22M
                MVMint32 rstate = tiler->states[right].state;
124
1.22M
                state_info = MVM_jit_tile_state_lookup(tc, op, lstate, rstate);
125
1.22M
            }
126
8.82M
            _ASSERT(state_info != NULL, "Tiler table could not find next state for %s\n", info->name);
127
8.82M
            tiler->states[node].state = state_info[3];
128
8.82M
            tiler->states[node].rule  = state_info[4];
129
8.82M
        }
130
9.53M
    }
131
9.53M
}
132
133
134
/* It may happen that a nodes which is used multiple times is tiled in
135
 * differrent ways, because it is the parent tile which determines which
136
 * 'symbol' the child node gets to implement, and hence different parents might
137
 * decide differently. That may mean the same value will be computed more than
138
 * once, which could be suboptimal. Still, it is necessary to resolve such
139
 * conflicts. We do so by generating a new node for the parent node to refer to,
140
 * leaving the old node as it was. That may cause the tree to grow, which is
141
 * implemented by realloc. As a result, it is unsafe to take references to tree
142
 * elements while it is being modified. */
143
144
static MVMint32 assign_tile(MVMThreadContext *tc, MVMJitExprTree *tree,
145
                            MVMJitTreeTraverser *traverser,
146
11.1M
                            MVMJitExprNode node, MVMint32 rule_nr) {
147
11.1M
    const MVMJitTileTemplate *template = &MVM_jit_tile_templates[rule_nr];
148
11.1M
    struct TreeTiler *tiler = traverser->data;
149
11.1M
150
11.1M
    _ASSERT(rule_nr <= (sizeof(MVM_jit_tile_templates)/sizeof(MVM_jit_tile_templates[0])),
151
11.1M
            "Attempt to assign invalid tile rule %d\n", rule_nr);
152
11.1M
153
11.1M
    if (tiler->states[node].template == NULL || tiler->states[node].template == template ||
154
10.7M
        memcmp(template, tiler->states[node].template, sizeof(MVMJitTileTemplate)) == 0) {
155
10.7M
        /* happy case, no conflict */
156
10.7M
        tiler->states[node].rule     = rule_nr;
157
10.7M
        tiler->states[node].template = template;
158
10.7M
        return node;
159
341k
    } else {
160
341k
        /* resolve conflict by copying this node */
161
341k
        const MVMJitExprOpInfo *info = tree->info[node].op_info;
162
341k
        MVMint32 space = (info->nchild < 0 ?
163
0
                          2 + tree->nodes[node+1] + info->nargs :
164
341k
                          1 + info->nchild + info->nargs);
165
341k
        MVMint32 num   = tree->nodes_num;
166
341k
167
341k
        /* NB - we should have an append_during_traversal function
168
341k
         * because the following is quite a common pattern */
169
341k
170
341k
        /* Internal copy; hence no realloc may happen during append, ensure the
171
341k
         * space is available before the copy */
172
341k
        MVM_VECTOR_ENSURE_SPACE(tree->nodes, space);
173
341k
        MVM_VECTOR_APPEND(tree->nodes, tree->nodes + node, space);
174
341k
175
341k
        /* Copy the information node as well */
176
341k
        MVM_VECTOR_ENSURE_SIZE(tree->info, num);
177
341k
        memcpy(tree->info + num, tree->info + node, sizeof(MVMJitExprNodeInfo));
178
341k
179
341k
        /* Also ensure the visits and tiles array are of correct size */
180
341k
        MVM_VECTOR_ENSURE_SIZE(tiler->states, num);
181
341k
        MVM_VECTOR_ENSURE_SIZE(traverser->visits, num);
182
341k
183
341k
        /* Assign the new tile */
184
341k
        tiler->states[num].rule     = rule_nr;
185
341k
        tiler->states[num].template = template;
186
341k
187
341k
        /* Return reference to new node */
188
341k
        return num;
189
341k
    }
190
11.1M
}
191
192
193
194
/* Preorder propagation of rules downward */
195
static void select_tiles(MVMThreadContext *tc, MVMJitTreeTraverser *traverser,
196
9.87M
                         MVMJitExprTree *tree, MVMint32 node) {
197
9.87M
198
9.87M
    MVMJitExprNode op    = tree->nodes[node];
199
9.87M
    MVMint32 first_child = node+1;
200
9.87M
    MVMint32 nchild      = (tree->info[node].op_info->nchild < 0 ?
201
563k
                            tree->nodes[first_child++] :
202
9.31M
                            tree->info[node].op_info->nchild);
203
9.87M
    struct TreeTiler *tiler = traverser->data;
204
9.87M
205
9.87M
    const MVMJitTileTemplate *tile = tiler->states[node].template;
206
9.87M
    MVMint32 left_sym = tile->left_sym, right_sym = tile->right_sym;
207
9.87M
208
9.87M
/* Tile assignment is somewhat precarious due to (among other things), possible
209
9.87M
 * reallocation. So let's provide a single macro to do it correctly. */
210
10.3M
#define DO_ASSIGN_CHILD(NUM, SYM) do { \
211
10.3M
        MVMint32 child     = tree->nodes[first_child+(NUM)]; \
212
10.3M
        MVMint32 state     = tiler->states[child].state; \
213
10.3M
        MVMint32 rule      = MVM_jit_tile_select_lookup(tc, state, (SYM)); \
214
10.3M
        MVMint32 assigned  = assign_tile(tc, tree, traverser, child, rule); \
215
10.3M
        tree->nodes[first_child+(NUM)] = assigned; \
216
10.3M
    } while(0)
217
9.87M
218
9.87M
    switch (op) {
219
388k
    case MVM_JIT_ALL:
220
388k
    case MVM_JIT_ANY:
221
388k
    case MVM_JIT_ARGLIST:
222
388k
        {
223
388k
            MVMint32 i;
224
1.73M
            for (i = 0; i < nchild; i++) {
225
1.34M
                DO_ASSIGN_CHILD(i, left_sym);
226
1.34M
            }
227
388k
        }
228
388k
        break;
229
174k
    case MVM_JIT_DO:
230
174k
    case MVM_JIT_DOV:
231
174k
        {
232
174k
            MVMint32 i, last_child, last_rule;
233
403k
            for (i = 0; i < nchild - 1; i++) {
234
229k
                DO_ASSIGN_CHILD(i, left_sym);
235
229k
            }
236
174k
            DO_ASSIGN_CHILD(i, right_sym);
237
174k
        }
238
174k
        break;
239
155k
    case MVM_JIT_IF:
240
155k
    case MVM_JIT_IFV:
241
155k
        {
242
155k
            DO_ASSIGN_CHILD(0, left_sym);
243
155k
            DO_ASSIGN_CHILD(1, right_sym);
244
155k
            DO_ASSIGN_CHILD(2, right_sym);
245
155k
        }
246
155k
        break;
247
0
    case MVM_JIT_GUARD:
248
0
        {
249
0
            /* tree->nodes[node+2] = the first guard of the before/after pair */
250
0
            if (tree->nodes[node+2] != 0) {
251
0
                MVMJitTile *tile = MVM_jit_tile_make(tc, tiler->compiler,
252
0
                                                     MVM_jit_compile_guard, 1, 0,
253
0
                                                     tree->nodes[node+2]);
254
0
                /* XXX - request a spare register (necessary for DYMAMIC LABEL
255
0
                 * etc). This should be generalized */
256
0
                tile->register_spec = MVM_JIT_REGISTER_ANY;
257
0
                tile->debug_name    = "(guard :pre)";
258
0
                MVM_VECTOR_PUSH(tiler->list->items, tile);
259
0
            }
260
0
            DO_ASSIGN_CHILD(0, left_sym);
261
0
        }
262
9.16M
    default:
263
9.16M
        {
264
9.16M
            _ASSERT(nchild <= 2, "Can't tile %d children of %s", nchild,
265
9.16M
                    tree->info[node].op_info->name);
266
9.16M
            if (nchild > 0) {
267
6.95M
                DO_ASSIGN_CHILD(0, left_sym);
268
6.95M
            }
269
9.16M
            if (nchild > 1) {
270
1.22M
                DO_ASSIGN_CHILD(1, right_sym);
271
1.22M
            }
272
9.16M
        }
273
9.87M
    }
274
9.87M
#undef DO_ASSIGN_CHILD
275
9.87M
    /* (Currently) we never insert into the tile list here */
276
9.87M
}
277
278
279
793k
static void start_basic_block(MVMThreadContext *tc, struct TreeTiler *tiler, MVMint32 node) {
280
793k
    /* After the last tile of a basic block (e.g. after a branch) or before the
281
793k
     * first tile of a new basic block (before a label), 'split' off a new basic
282
793k
     * block from the old one; tag the node with this basic block, so the
283
793k
     * patchup process can work */
284
793k
    MVMJitTileList *list = tiler->list;
285
793k
    MVMint32 tile_idx = list->items_num, block_idx = list->blocks_num;
286
793k
287
793k
    MVM_VECTOR_ENSURE_SPACE(list->blocks, 1);
288
793k
    list->blocks[block_idx].end     = tile_idx;
289
793k
    list->blocks[block_idx+1].start = tile_idx;
290
793k
    list->blocks_num++;
291
793k
    /* associate block with node */
292
793k
    tiler->states[node].block = block_idx;
293
793k
}
294
295
21
static void extend_last_block(MVMThreadContext *tc, struct TreeTiler *tiler, MVMint32 node) {
296
21
    /* In some cases (ANY in WHEN, ALL in ANY, ANY in ALL) the last basic block
297
21
     * of the inner block has functionally the same successors as the outer node
298
21
     * block; in this case we can 'extend' this block to include the
299
21
     * (unconditional) branch and tag the outer block withthe inner block. This
300
21
     * works mostly because the call to extend_last_block follows directly after
301
21
     * the start_basic_block by the inner block. */
302
21
    MVMJitTileList *list = tiler->list;
303
21
    MVMint32 tile_idx = list->items_num, block_idx = list->blocks_num;
304
21
    list->blocks[block_idx - 1].end = tile_idx;
305
21
    list->blocks[block_idx].start   = tile_idx;
306
21
    tiler->states[node].block = block_idx - 1;
307
21
}
308
309
84.9k
static void patch_shortcircuit_blocks(MVMThreadContext *tc, struct TreeTiler *tiler, MVMJitExprTree *tree, MVMint32 node, MVMint32 alt) {
310
84.9k
    /* Shortcircuit operators (ALL/ANY), are series of tests and conditional
311
84.9k
     * jumps to a common label (i.e. basic block). Hence every block associated
312
84.9k
     * with a child has two successors, namely the following block (block + 1)
313
84.9k
     * or the shortcircuited block (alt). */
314
84.9k
    MVMJitTileList *list = tiler->list;
315
84.9k
    MVMint32 i, nchild = tree->nodes[node+1];
316
317k
    for (i = 0; i < nchild; i++) {
317
232k
        MVMint32 child = tree->nodes[node + 2 + i];
318
232k
        MVMint32 block = tiler->states[node + 2 + i].block;
319
232k
        if (tree->nodes[child] == tree->nodes[node]) {
320
0
            /* in the case of nested shortcircuit operators, if they are equal
321
0
             * they shortcircuit identically, and so all children need to be
322
0
             * patched up in the same way */
323
0
            patch_shortcircuit_blocks(tc, tiler, tree, child, alt);
324
232k
        } else if (tree->nodes[child] == MVM_JIT_ALL || tree->nodes[child] == MVM_JIT_ANY) {
325
0
            /* unequal nested shortcircuit operators (ALL in ANY or ANY in ALL)
326
0
             * have the behaviour that shortcircuit to the next block or at the
327
0
             * end shortcircuit to the alternative block. E.g. ANY nested in ALL
328
0
             * must jump to the next block (continue tests) or continue testing;
329
0
             * after the last block, however, if we reach it we can shortcircuit
330
0
             * (in ANY, we only reach it, if nothing was true, in ALL, only if
331
0
             * everything was true, and hence one of the ANY is true) */
332
0
            patch_shortcircuit_blocks(tc, tiler, tree, child, block + 1);
333
0
        }
334
232k
        list->blocks[block].num_succ = 2;
335
232k
        list->blocks[block].succ[0] = block + 1;
336
232k
        list->blocks[block].succ[1] = alt;
337
232k
    }
338
84.9k
}
339
340
245k
static void patch_basic_blocks(MVMThreadContext *tc, struct TreeTiler *tiler, MVMJitExprTree *tree, MVMint32 node) {
341
245k
    /* Postorder assign the successors to blocks associated with nodes */
342
245k
    MVMJitTileList *list = tiler->list;
343
245k
    MVMint32 test = tree->nodes[node+1];
344
245k
    if (tree->nodes[node] == MVM_JIT_WHEN) {
345
89.6k
        MVMint32 pre  = tiler->states[node + 1].block;
346
89.6k
        MVMint32 post = tiler->states[node + 2].block;
347
89.6k
        if (tree->nodes[test] == MVM_JIT_ALL) {
348
43.0k
            patch_shortcircuit_blocks(tc, tiler, tree, test, post + 1);
349
46.6k
        } else if (tree->nodes[test] == MVM_JIT_ANY) {
350
0
            /* ANY will start numbering the blocks and assigning (n+1, pre+1) to
351
0
             * each of them; pre+1 is the alternative successor.  */
352
0
            patch_shortcircuit_blocks(tc, tiler, tree, test, pre + 1);
353
0
        }
354
89.6k
        list->blocks[pre].num_succ = 2;
355
89.6k
        list->blocks[pre].succ[0] = pre + 1;
356
89.6k
        list->blocks[pre].succ[1] = post + 1;
357
89.6k
        list->blocks[post].num_succ = 1;
358
89.6k
        list->blocks[post].succ[0] = post + 1;
359
155k
    } else if (tree->nodes[node] == MVM_JIT_IF || tree->nodes[node] == MVM_JIT_IFV) {
360
155k
        MVMint32 pre  = tiler->states[node + 1].block;
361
155k
        MVMint32 cond = tiler->states[node + 2].block;
362
155k
        MVMint32 post = tiler->states[node + 3].block;
363
155k
        if (tree->nodes[test] == MVM_JIT_ALL) {
364
41.9k
            patch_shortcircuit_blocks(tc, tiler, tree, test, cond + 1);
365
113k
        } else if (tree->nodes[test] == MVM_JIT_ANY) {
366
21
            patch_shortcircuit_blocks(tc, tiler, tree, test, pre + 1);
367
21
        }
368
155k
        list->blocks[pre].num_succ = 2;
369
155k
        list->blocks[pre].succ[0] = pre + 1;
370
155k
        list->blocks[pre].succ[1] = cond + 1;
371
155k
        list->blocks[cond].num_succ = 1;
372
155k
        list->blocks[cond].succ[0] = post + 1;
373
155k
        list->blocks[post].num_succ = 1;
374
155k
        list->blocks[post].succ[0] = post + 1;
375
155k
    }
376
245k
}
377
378
/* Insert labels, compute basic block extents (eventually) */
379
static void build_blocks(MVMThreadContext *tc, MVMJitTreeTraverser *traverser,
380
10.3M
                         MVMJitExprTree *tree, MVMint32 node, MVMint32 i) {
381
10.3M
    struct TreeTiler *tiler = traverser->data;
382
10.3M
    MVMJitTileList *list    = tiler->list;
383
10.3M
    switch (tree->nodes[node]) {
384
179k
    case MVM_JIT_WHEN:
385
179k
    {
386
179k
        MVMint32 when_label = tree->info[node].label;
387
179k
        if (i == 0) {
388
89.6k
            MVMint32 test  = tree->nodes[node+1];
389
89.6k
            MVMint32 flag  = tree->nodes[test];
390
89.6k
            /* First child is the test */
391
89.6k
            if (flag == MVM_JIT_ALL) {
392
43.0k
                /* Do nothing, shortcircuit of ALL has skipped the conditional
393
43.0k
                   block if necessary */
394
43.0k
                MVMint32 last_child = test + tree->nodes[test+1] + 1;
395
43.0k
                tiler->states[node+1].block = tiler->states[last_child].block;
396
46.6k
            } else if (flag == MVM_JIT_ANY) {
397
0
                /* If ANY hasn't short-circuited into the conditional block,
398
0
                 * it has failed, so insert an unconditional jump past it */
399
0
                MVMint32 any_label = tree->info[test].label;
400
0
                MVMJitTile *branch = MVM_jit_tile_make(tc, tiler->compiler, MVM_jit_compile_branch,
401
0
                                                       1, 0, when_label);
402
0
                MVMJitTile *label  = MVM_jit_tile_make(tc, tiler->compiler, MVM_jit_compile_label,
403
0
                                                       1, 0, any_label);
404
0
                branch->debug_name = "(branch :fail)";
405
0
                label->debug_name  = "(label :success)";
406
0
                MVM_VECTOR_PUSH(list->items, branch);
407
0
                /* extends last block of ANY to include the unconditional branch */
408
0
                extend_last_block(tc, tiler, node + 2 + i);
409
0
                MVM_VECTOR_PUSH(list->items, label);
410
46.6k
            } else {
411
46.6k
                /* Other tests require a conditional branch, but no label */
412
46.6k
                MVMJitTile *branch = MVM_jit_tile_make(tc, tiler->compiler, MVM_jit_compile_conditional_branch,
413
46.6k
                                                       2, 0, MVM_jit_expr_op_negate_flag(tc, flag), when_label);
414
46.6k
                branch->debug_name = "(branch :fail)";
415
46.6k
                MVM_VECTOR_PUSH(list->items, branch);
416
46.6k
                start_basic_block(tc, tiler, node + 1);
417
46.6k
            }
418
89.6k
        } else {
419
89.6k
            /* after child of WHEN, insert the label */
420
89.6k
            MVMJitTile *label = MVM_jit_tile_make(tc, tiler->compiler, MVM_jit_compile_label,
421
89.6k
                                                  1, 0, when_label);
422
89.6k
            label->debug_name = "(label :fail)";
423
89.6k
            start_basic_block(tc, tiler, node + 2);
424
89.6k
            MVM_VECTOR_PUSH(list->items, label);
425
89.6k
        }
426
179k
        break;
427
179k
    }
428
232k
    case MVM_JIT_ALL:
429
232k
    {
430
232k
        MVMint32 test = tree->nodes[node+2+i];
431
232k
        MVMint32 flag = tree->nodes[test];
432
232k
        MVMint32 all_label = tree->info[node].label;
433
232k
434
232k
        if (flag == MVM_JIT_ALL) {
435
0
            /* Nested ALL short-circuits identically */
436
0
            MVMint32 last_child = test + 1 + tree->nodes[test + 1];
437
0
            tiler->states[node + 2 + i].block = tiler->states[last_child].block;
438
232k
        } else if (flag == MVM_JIT_ANY) {
439
0
            /* If ANY reached it's end, that means it's false. So short-circuit out */
440
0
            MVMint32 any_label = tree->info[test].label;
441
0
            MVMJitTile *branch = MVM_jit_tile_make(tc, tiler->compiler, MVM_jit_compile_branch, 1, 0, all_label);
442
0
            MVMJitTile *label  = MVM_jit_tile_make(tc, tiler->compiler, MVM_jit_compile_label, 1, 0, any_label);
443
0
            branch->debug_name = "(branch :fail)   # ALL";
444
0
            label->debug_name  = "(label :success) # ANY";
445
0
            MVM_VECTOR_PUSH(list->items, branch);
446
0
            /* extends last block of ANY to include the unconditional branch */
447
0
            extend_last_block(tc, tiler, node + 2 + i);
448
0
            /* And if ANY short-circuits we should continue the evaluation of ALL */
449
0
            MVM_VECTOR_PUSH(list->items, label);
450
232k
        } else {
451
232k
            /* Flag should be negated (ALL = short-circiut unless condition)) */
452
232k
            MVMJitTile *branch = MVM_jit_tile_make(tc, tiler->compiler,
453
232k
                                                   MVM_jit_compile_conditional_branch, 2, 0,
454
232k
                                                   MVM_jit_expr_op_negate_flag(tc, flag), all_label);
455
232k
            branch->debug_name = "(conditional-branch :fail)";
456
232k
            MVM_VECTOR_PUSH(list->items, branch);
457
232k
            start_basic_block(tc, tiler, node + 2 + i);
458
232k
        }
459
232k
        break;
460
179k
    }
461
42
    case MVM_JIT_ANY:
462
42
    {
463
42
        MVMint32 test  = tree->nodes[node+2+i];
464
42
        MVMint32 flag  = tree->nodes[test];
465
42
        MVMint32 any_label = tree->info[node].label;
466
42
        if (flag == MVM_JIT_ALL) {
467
0
            /* If ALL reached the end, it must have been succesful, and ANY's
468
0
               short-circuit behaviour implies we should branch out */
469
0
            MVMint32 all_label = tree->info[test].label;
470
0
            MVMJitTile *branch = MVM_jit_tile_make(tc, tiler->compiler, MVM_jit_compile_branch,
471
0
                                                   1, 0, any_label);
472
0
            MVMJitTile *label  = MVM_jit_tile_make(tc, tiler->compiler, MVM_jit_compile_label,
473
0
                                                   1, 0, all_label);
474
0
            branch->debug_name = "(branch :success) # ALL";
475
0
            label->debug_name  = "(label  :fail) # ANY";
476
0
            MVM_VECTOR_PUSH(list->items, branch);
477
0
            extend_last_block(tc, tiler, node + 2 + i);
478
0
            /* If not succesful, testing should continue (thus ALL must branch
479
0
             * into our ANY) */
480
0
            MVM_VECTOR_PUSH(list->items, label);
481
42
        } else if (flag == MVM_JIT_ANY) {
482
0
            /* Nothing to do here, since nested ANY already short-circuits to
483
0
               our label */
484
0
            MVMint32 last_child = test + 1 + tree->nodes[test + 1];
485
0
            tiler->states[node + 2 + i].block = tiler->states[last_child].block;
486
42
        } else {
487
42
            /* Normal evaluation (ANY = short-circuit if condition) */
488
42
            MVMJitTile *branch = MVM_jit_tile_make(tc, tiler->compiler,
489
42
                                                   MVM_jit_compile_conditional_branch,
490
42
                                                   2, 0, flag, any_label);
491
42
            branch->debug_name  = "(branch :success)";
492
42
            MVM_VECTOR_PUSH(list->items, branch);
493
42
            start_basic_block(tc, tiler, node + 2 + i);
494
42
        }
495
42
        break;
496
179k
    }
497
466k
    case MVM_JIT_IF:
498
466k
    case MVM_JIT_IFV:
499
466k
    {
500
466k
        MVMint32 left_label = tree->info[node].label;
501
466k
        MVMint32 right_label = left_label + 1;
502
466k
        if (i == 0) {
503
155k
            /* after flag child */
504
155k
            MVMint32 test = tree->nodes[node+1];
505
155k
            MVMint32 flag = tree->nodes[test];
506
155k
            if (flag == MVM_JIT_ALL) {
507
41.9k
                /* If we reach this code then ALL was true, hence we should
508
41.9k
                 * enter the left block, and do nothing */
509
41.9k
                MVMint32 last_child = test + 1 + tree->nodes[test + 1];
510
41.9k
                tiler->states[node + 1].block = tiler->states[last_child].block;
511
113k
            } else if (flag == MVM_JIT_ANY) {
512
21
                /* We need the branch to the right block and the label for ANY
513
21
                 * to jump to enter the left block */
514
21
                MVMint32 any_label = tree->info[test].label;
515
21
                MVMJitTile *branch = MVM_jit_tile_make(tc, tiler->compiler, MVM_jit_compile_branch,
516
21
                                                       1, 0, left_label);
517
21
                MVMJitTile *label  = MVM_jit_tile_make(tc, tiler->compiler, MVM_jit_compile_label,
518
21
                                                       1, 0, any_label);
519
21
                branch->debug_name = "(branch: fail)";
520
21
                label->debug_name = "(label :success)";
521
21
                MVM_VECTOR_PUSH(list->items, branch);
522
21
                extend_last_block(tc, tiler, node + 1);
523
21
                MVM_VECTOR_PUSH(list->items, label);
524
113k
            } else {
525
113k
                MVMJitTile *branch = MVM_jit_tile_make(tc, tiler->compiler,
526
113k
                                                       MVM_jit_compile_conditional_branch, 2, 0,
527
113k
                                                       MVM_jit_expr_op_negate_flag(tc, flag), left_label);
528
113k
                branch->debug_name = "(conditional-branch: fail)";
529
113k
                MVM_VECTOR_PUSH(list->items, branch);
530
113k
                start_basic_block(tc, tiler, node + 1);
531
113k
            }
532
310k
        } else if (i == 1) {
533
155k
            /* between left and right conditional block */
534
155k
            MVMJitTile *branch = MVM_jit_tile_make(tc, tiler->compiler, MVM_jit_compile_branch,
535
155k
                                                   1, 0, right_label);
536
155k
            MVMJitTile *label  = MVM_jit_tile_make(tc, tiler->compiler, MVM_jit_compile_label,
537
155k
                                                   1, 0, left_label);
538
155k
            branch->debug_name = "(branch :after)";
539
155k
            label->debug_name  = "(label :fail)";
540
155k
            MVM_VECTOR_PUSH(list->items, branch);
541
155k
            start_basic_block(tc, tiler, node + 2);
542
155k
            MVM_VECTOR_PUSH(list->items, label);
543
155k
        } else {
544
155k
            /* after 'right' conditional block */
545
155k
            MVMJitTile *label = MVM_jit_tile_make(tc, tiler->compiler, MVM_jit_compile_label,
546
155k
                                                   1, 0, right_label);
547
155k
            label->debug_name = "(branch :after)";
548
155k
            start_basic_block(tc, tiler, node + 3);
549
155k
            MVM_VECTOR_PUSH(list->items, label);
550
155k
        }
551
466k
    }
552
9.97M
    default:
553
9.97M
        break;
554
10.3M
    }
555
10.3M
}
556
557
558
static void build_tilelist(MVMThreadContext *tc, MVMJitTreeTraverser *traverser,
559
9.87M
                           MVMJitExprTree *tree, MVMint32 node) {
560
9.87M
    struct TreeTiler *tiler = traverser->data;
561
9.87M
    const MVMJitTileTemplate *template = tiler->states[node].template;
562
9.87M
    MVMJitTile *tile;
563
9.87M
564
9.87M
    /* only need to add 'real' tiles; emit may be null for a definition */
565
9.87M
    if (template->expr == NULL)
566
4.02M
        return;
567
9.87M
568
5.85M
    tile = MVM_jit_tile_make_from_template(tc, tiler->compiler, template, tree, node);
569
5.85M
570
5.85M
    MVM_VECTOR_PUSH(tiler->list->items, tile);
571
5.85M
    /* count tne number of refs for ARGLIST */
572
5.85M
    if (tile->op == MVM_JIT_ARGLIST) {
573
303k
        tiler->list->num_arglist_refs += tile->num_refs;
574
5.55M
    } else if (tile->op == MVM_JIT_WHEN || tile->op == MVM_JIT_IF ||
575
5.36M
               tile->op == MVM_JIT_IFV) {
576
245k
        /* NB: ALL and ANY also generate basic blocks, but their successors can
577
245k
         * only be resolved after the conditional construct */
578
245k
        patch_basic_blocks(tc, tiler, tree, node);
579
5.30M
    } else if (tile->op == MVM_JIT_GUARD && tile->args[1] != 0) {
580
0
        /* second arg is wrap after (and nonzero if required). Because guard is
581
0
         * a 'definition' tile, it's emit is usually NULL, so we can overwrite
582
0
         * it to make it a 'real' tile. */
583
0
        tile->args[0] = tile->args[1];
584
0
        tile->emit    = MVM_jit_compile_guard;
585
0
    }
586
5.85M
}
587
588
/* Create a tile from a template */
589
MVMJitTile * MVM_jit_tile_make_from_template(MVMThreadContext *tc, MVMJitCompiler *compiler,
590
                                             const MVMJitTileTemplate *template,
591
5.85M
                                             MVMJitExprTree *tree, MVMint32 node) {
592
5.85M
    MVMJitTile *tile;
593
5.85M
    tile = MVM_spesh_alloc(tc, compiler->graph->sg, sizeof(MVMJitTile));
594
5.85M
    tile->emit          = template->emit;
595
5.85M
    tile->register_spec = template->register_spec;
596
5.85M
    tile->node          = node;
597
5.85M
    tile->op            = tree->nodes[node];
598
5.85M
    tile->size          = tree->info[node].size;
599
5.85M
600
5.85M
    /* Assign tile arguments and compute the refering nodes */
601
5.85M
    switch (tile->op) {
602
100k
    case MVM_JIT_IF:
603
100k
    {
604
100k
        tile->refs[0] = tree->nodes[node+2];
605
100k
        tile->refs[1] = tree->nodes[node+3];
606
100k
        tile->num_refs = 2;
607
100k
        break;
608
100k
    }
609
303k
    case MVM_JIT_ARGLIST:
610
303k
    {
611
303k
        /* because arglist needs special-casing and because it will use more
612
303k
         * than 8 (never mind 4) values, it won't fit into refs, so we're not
613
303k
         * reading them here */
614
303k
        tile->num_refs = tree->nodes[node+1];
615
303k
        break;
616
100k
    }
617
111k
    case MVM_JIT_DO:
618
111k
    {
619
111k
        MVMint32 nchild  = tree->nodes[node+1];
620
111k
        tile->refs[0]    = tree->nodes[node+1+nchild];
621
111k
        tile->num_refs = 1;
622
111k
        break;
623
100k
    }
624
5.34M
    default:
625
5.34M
    {
626
5.34M
        MVMint32 i, j, k, num_nodes, value_bitmap;
627
5.34M
        MVMJitExprNode buffer[8];
628
5.34M
        num_nodes        = MVM_jit_expr_tree_get_nodes(tc, tree, node, template->path, buffer);
629
5.34M
        value_bitmap     = template->value_bitmap;
630
5.34M
        tile->num_refs   = template->num_refs;
631
5.34M
        j = 0;
632
5.34M
        k = 0;
633
5.34M
        /* splice out args from node refs */
634
16.1M
        for (i = 0; i < num_nodes; i++) {
635
10.7M
            if (value_bitmap & 1) {
636
4.52M
                tile->refs[j++] = buffer[i];
637
6.25M
            } else {
638
6.25M
                tile->args[k++] = buffer[i];
639
6.25M
            }
640
10.7M
            value_bitmap >>= 1;
641
10.7M
        }
642
5.34M
        break;
643
100k
    }
644
5.85M
    }
645
5.85M
    tile->debug_name = template->expr;
646
5.85M
    return tile;
647
5.85M
}
648
649
253k
MVMJitTileList * MVM_jit_tile_expr_tree(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitExprTree *tree) {
650
253k
    MVMJitTreeTraverser traverser;
651
253k
    MVMint32 i;
652
253k
    struct TreeTiler tiler;
653
253k
654
253k
    MVM_VECTOR_INIT(tiler.states, tree->nodes_num);
655
253k
    traverser.policy = MVM_JIT_TRAVERSER_ONCE;
656
253k
    traverser.inorder = NULL;
657
253k
    traverser.preorder = NULL;
658
253k
    traverser.postorder = &tile_node;
659
253k
    traverser.data = &tiler;
660
253k
661
253k
    MVM_jit_expr_tree_traverse(tc, tree, &traverser);
662
253k
    /* 'pushdown' of tiles to roots */
663
976k
    for (i = 0; i < tree->roots_num; i++) {
664
722k
        MVMint32 node = tree->roots[i];
665
722k
        assign_tile(tc, tree, &traverser, tree->roots[i], tiler.states[node].rule);
666
722k
    }
667
253k
668
253k
    /* Create serial list of actual tiles which represent the final low-level code */
669
253k
    tiler.compiler      = compiler;
670
253k
    tiler.list          = MVM_spesh_alloc(tc, compiler->graph->sg, sizeof(MVMJitTileList));
671
253k
    tiler.list->tree    = tree;
672
253k
    tiler.list->num_arglist_refs = 0;
673
253k
674
253k
    MVM_VECTOR_INIT(tiler.list->items, tree->nodes_num / 2);
675
253k
    MVM_VECTOR_INIT(tiler.list->inserts, 0);
676
253k
    MVM_VECTOR_INIT(tiler.list->blocks, 8);
677
253k
678
253k
    traverser.preorder  = &select_tiles;
679
253k
    traverser.inorder   = &build_blocks;
680
253k
    traverser.postorder = &build_tilelist;
681
253k
682
253k
    MVM_jit_expr_tree_traverse(tc, tree, &traverser);
683
253k
684
253k
    MVM_free(tiler.states);
685
253k
686
253k
    /* finish last list block */
687
253k
    {
688
253k
        MVMint32 last_block = tiler.list->blocks_num++;
689
253k
        tiler.list->blocks[last_block].end = tiler.list->items_num;
690
253k
        tiler.list->blocks[last_block].num_succ = 0;
691
253k
    }
692
253k
693
253k
    return tiler.list;
694
253k
}
695
696
697
3.54M
static int cmp_tile_insert(const void *p1, const void *p2) {
698
3.54M
    const struct MVMJitTileInsert *a = p1, *b = p2;
699
3.54M
    return a->position == b->position ?
700
1.16M
        a->order - b->order :
701
2.38M
        a->position - b->position;
702
3.54M
}
703
704
705
1.59M
void MVM_jit_tile_list_insert(MVMThreadContext *tc, MVMJitTileList *list, MVMJitTile *tile, MVMint32 position, MVMint32 order) {
706
1.59M
    struct MVMJitTileInsert i = { position, order, tile };
707
1.59M
    MVM_VECTOR_PUSH(list->inserts, i);
708
1.59M
}
709
710
253k
void MVM_jit_tile_list_edit(MVMThreadContext *tc, MVMJitTileList *list) {
711
253k
    MVMJitTile **worklist;
712
253k
    MVMint32 i, j, k, n;
713
253k
    if (list->inserts_num == 0)
714
91.9k
        return;
715
253k
716
253k
    /* sort inserted tiles in ascending order */
717
161k
    qsort(list->inserts, list->inserts_num,
718
161k
          sizeof(struct MVMJitTileInsert), cmp_tile_insert);
719
161k
720
161k
    /* create a new array for the tiles */
721
161k
    worklist = MVM_malloc((list->items_num + list->inserts_num) * sizeof(MVMJitTile*));
722
161k
723
161k
    i = 0; /* items */
724
161k
    j = 0; /* inserts */
725
161k
    k = 0; /* output */
726
161k
    n = 0; /* block */
727
161k
728
6.09M
    while (i < list->items_num) {
729
7.53M
        while (j < list->inserts_num &&
730
6.42M
               list->inserts[j].position < i) {
731
1.59M
            worklist[k++] = list->inserts[j++].tile;
732
1.59M
        }
733
5.93M
        if (list->blocks[n].end == i) {
734
724k
            list->blocks[n++].end = k;
735
724k
            list->blocks[n].start = k;
736
724k
        }
737
5.93M
        worklist[k++] = list->items[i++];
738
5.93M
    }
739
161k
    /* insert all tiles after the last one, if any */
740
161k
    while (j < list->inserts_num) {
741
0
        worklist[k++] = list->inserts[j++].tile;
742
0
    }
743
161k
    list->blocks[n].end = k;
744
161k
745
161k
    /* swap old and new list */
746
161k
    MVM_free(list->items);
747
161k
    list->items = worklist;
748
161k
    list->items_num = k;
749
161k
    list->items_alloc = k;
750
161k
751
161k
    /* Cleanup edits buffer */
752
161k
    MVM_free(list->inserts);
753
161k
    MVM_VECTOR_INIT(list->inserts, 0);
754
161k
}
755
756
253k
void MVM_jit_tile_list_destroy(MVMThreadContext *tc, MVMJitTileList *list) {
757
253k
    MVM_free(list->items);
758
253k
    MVM_free(list->inserts);
759
253k
    MVM_free(list->blocks);
760
253k
}