Coverage Report

Created: 2018-07-03 15:31

/home/travis/build/MoarVM/MoarVM/src/spesh/codegen.c
Line
Count
Source (jump to first uncovered line)
1
#include "moar.h"
2
3
/* Here we turn a spesh tree back into MoarVM bytecode, after optimizations
4
 * have been applied to it. */
5
6
/* Writer state. */
7
typedef struct {
8
    /* Bytecode output buffer. */
9
    MVMuint8  *bytecode;
10
    MVMuint32  bytecode_pos;
11
    MVMuint32  bytecode_alloc;
12
13
    /* Offsets where basic blocks are. */
14
    MVMint32 *bb_offsets;
15
16
    /* Fixups we need to do by basic block. */
17
    MVMint32    *fixup_locations;
18
    MVMSpeshBB **fixup_bbs;
19
    MVMint32     num_fixups;
20
    MVMint32     alloc_fixups;
21
22
    /* Copied frame handlers (which we'll update offsets of). */
23
    MVMFrameHandler *handlers;
24
} SpeshWriterState;
25
26
/* Write functions; all native endian. */
27
3.59M
static void ensure_space(SpeshWriterState *ws, int bytes) {
28
3.59M
    if (ws->bytecode_pos + bytes >= ws->bytecode_alloc) {
29
3.41k
        ws->bytecode_alloc *= 2;
30
3.41k
        ws->bytecode = MVM_realloc(ws->bytecode, ws->bytecode_alloc);
31
3.41k
    }
32
3.59M
}
33
2.53k
static void write_int64(SpeshWriterState *ws, MVMuint64 value) {
34
2.53k
    ensure_space(ws, 8);
35
2.53k
    memcpy(ws->bytecode + ws->bytecode_pos, &value, 8);
36
2.53k
    ws->bytecode_pos += 8;
37
2.53k
}
38
255k
static void write_int32(SpeshWriterState *ws, MVMuint32 value) {
39
255k
    ensure_space(ws, 4);
40
255k
    memcpy(ws->bytecode + ws->bytecode_pos, &value, 4);
41
255k
    ws->bytecode_pos += 4;
42
255k
}
43
3.33M
static void write_int16(SpeshWriterState *ws, MVMuint16 value) {
44
3.33M
    ensure_space(ws, 2);
45
3.33M
    memcpy(ws->bytecode + ws->bytecode_pos, &value, 2);
46
3.33M
    ws->bytecode_pos += 2;
47
3.33M
}
48
0
static void write_int8(SpeshWriterState *ws, MVMuint8 value) {
49
0
    ensure_space(ws, 1);
50
0
    memcpy(ws->bytecode + ws->bytecode_pos, &value, 1);
51
0
    ws->bytecode_pos++;
52
0
}
53
0
static void write_num32(SpeshWriterState *ws, MVMnum32 value) {
54
0
    ensure_space(ws, 4);
55
0
    memcpy(ws->bytecode + ws->bytecode_pos, &value, 4);
56
0
    ws->bytecode_pos += 4;
57
0
}
58
1.51k
static void write_num64(SpeshWriterState *ws, MVMnum64 value) {
59
1.51k
    ensure_space(ws, 8);
60
1.51k
    memcpy(ws->bytecode + ws->bytecode_pos, &value, 8);
61
1.51k
    ws->bytecode_pos += 8;
62
1.51k
}
63
64
/* Writes instructions within a basic block boundary. */
65
310k
static void write_instructions(MVMThreadContext *tc, MVMSpeshGraph *g, SpeshWriterState *ws, MVMSpeshBB *bb) {
66
310k
    MVMSpeshIns *ins = bb->first_ins;
67
2.13M
    while (ins) {
68
1.82M
        MVMint32 i;
69
1.82M
70
1.82M
        /* Process any annotations. */
71
1.82M
        MVMSpeshAnn *ann              = ins->annotations;
72
1.82M
        MVMSpeshAnn *deopt_one_ann    = NULL;
73
1.82M
        MVMSpeshAnn *deopt_all_ann    = NULL;
74
1.82M
        MVMSpeshAnn *deopt_inline_ann = NULL;
75
2.27M
        while (ann) {
76
444k
            switch (ann->type) {
77
17.6k
            case MVM_SPESH_ANN_FH_START:
78
17.6k
                ws->handlers[ann->data.frame_handler_index].start_offset =
79
17.6k
                    ws->bytecode_pos;
80
17.6k
                break;
81
17.6k
            case MVM_SPESH_ANN_FH_END:
82
17.6k
                ws->handlers[ann->data.frame_handler_index].end_offset =
83
17.6k
                    ws->bytecode_pos;
84
17.6k
                break;
85
17.6k
            case MVM_SPESH_ANN_FH_GOTO:
86
17.6k
                ws->handlers[ann->data.frame_handler_index].goto_offset =
87
17.6k
                    ws->bytecode_pos;
88
17.6k
                break;
89
219k
            case MVM_SPESH_ANN_DEOPT_ONE_INS:
90
219k
                deopt_one_ann = ann;
91
219k
                break;
92
25.6k
            case MVM_SPESH_ANN_DEOPT_ALL_INS:
93
25.6k
                deopt_all_ann = ann;
94
25.6k
                break;
95
8.50k
            case MVM_SPESH_ANN_INLINE_START:
96
8.50k
                g->inlines[ann->data.inline_idx].start = ws->bytecode_pos;
97
8.50k
                break;
98
8.50k
            case MVM_SPESH_ANN_INLINE_END:
99
8.50k
                g->inlines[ann->data.inline_idx].end = ws->bytecode_pos;
100
8.50k
                break;
101
46.5k
            case MVM_SPESH_ANN_DEOPT_INLINE:
102
46.5k
                deopt_inline_ann = ann;
103
46.5k
                break;
104
4.98k
            case MVM_SPESH_ANN_DEOPT_OSR:
105
4.98k
                g->deopt_addrs[2 * ann->data.deopt_idx + 1] = ws->bytecode_pos;
106
4.98k
                break;
107
444k
            }
108
444k
            ann = ann->next;
109
444k
        }
110
1.82M
111
1.82M
        if (ins->info->opcode != MVM_SSA_PHI) {
112
1.13M
            /* Real instruction, not a phi. Emit opcode. */
113
1.13M
            if (ins->info->opcode == (MVMuint16)-1) {
114
0
                /* Ext op; resolve. */
115
0
                MVMExtOpRecord *extops     = g->sf->body.cu->body.extops;
116
0
                MVMuint16       num_extops = g->sf->body.cu->body.num_extops;
117
0
                MVMint32        found      = 0;
118
0
                for (i = 0; i < num_extops; i++) {
119
0
                    if (extops[i].info == ins->info) {
120
0
                        write_int16(ws, MVM_OP_EXT_BASE + i);
121
0
                        found = 1;
122
0
                        break;
123
0
                    }
124
0
                }
125
0
                if (!found)
126
0
                    MVM_oops(tc, "Spesh: failed to resolve extop in code-gen");
127
0
            }
128
1.13M
            else {
129
1.13M
                /* Core op. */
130
1.13M
                write_int16(ws, ins->info->opcode);
131
1.13M
            }
132
1.13M
133
1.13M
            /* Write out operands. */
134
3.58M
            for (i = 0; i < ins->info->num_operands; i++) {
135
2.44M
                MVMuint8 flags = ins->info->operands[i];
136
2.44M
                MVMuint8 rw    = flags & MVM_operand_rw_mask;
137
2.44M
                switch (rw) {
138
1.73M
                case MVM_operand_read_reg:
139
1.73M
                case MVM_operand_write_reg:
140
1.73M
                    write_int16(ws, ins->operands[i].reg.orig);
141
1.73M
                    break;
142
13.3k
                case MVM_operand_read_lex:
143
13.3k
                case MVM_operand_write_lex:
144
13.3k
                    write_int16(ws, ins->operands[i].lex.idx);
145
13.3k
                    write_int16(ws, ins->operands[i].lex.outers);
146
13.3k
                    break;
147
700k
                case MVM_operand_literal: {
148
700k
                    MVMuint8 type = flags & MVM_operand_type_mask;
149
700k
                    switch (type) {
150
0
                    case MVM_operand_int8:
151
0
                        write_int8(ws, ins->operands[i].lit_i8);
152
0
                        break;
153
288k
                    case MVM_operand_int16:
154
288k
                        write_int16(ws, ins->operands[i].lit_i16);
155
288k
                        break;
156
1.79k
                    case MVM_operand_int32:
157
1.79k
                        write_int32(ws, ins->operands[i].lit_i32);
158
1.79k
                        break;
159
37.8k
                    case MVM_operand_uint32:
160
37.8k
                        write_int32(ws, ins->operands[i].lit_ui32);
161
37.8k
                        break;
162
2.53k
                    case MVM_operand_int64:
163
2.53k
                        write_int64(ws, ins->operands[i].lit_i64);
164
2.53k
                        break;
165
0
                    case MVM_operand_num32:
166
0
                        write_num32(ws, ins->operands[i].lit_n32);
167
0
                        break;
168
1.51k
                    case MVM_operand_num64:
169
1.51k
                        write_num64(ws, ins->operands[i].lit_n64);
170
1.51k
                        break;
171
27.7k
                    case MVM_operand_callsite:
172
27.7k
                        write_int16(ws, ins->operands[i].callsite_idx);
173
27.7k
                        break;
174
496
                    case MVM_operand_coderef:
175
496
                        write_int16(ws, ins->operands[i].coderef_idx);
176
496
                        break;
177
72.8k
                    case MVM_operand_str:
178
72.8k
                        write_int32(ws, ins->operands[i].lit_str_idx);
179
72.8k
                        break;
180
143k
                    case MVM_operand_ins: {
181
143k
                        MVMint32 bb_idx = ins->operands[i].ins_bb->idx;
182
143k
                        MVMint32 offset;
183
143k
                        if (bb_idx >= g->num_bbs)
184
0
                            MVM_panic(1, "Spesh codegen: out of range BB index %d", bb_idx);
185
143k
                        offset = ws->bb_offsets[bb_idx];
186
143k
                        if (offset >= 0) {
187
14.2k
                            /* Already know where it is, so just write it. */
188
14.2k
                            write_int32(ws, offset);
189
14.2k
                        }
190
129k
                        else {
191
129k
                            /* Need to fix it up. */
192
129k
                            if (ws->num_fixups == ws->alloc_fixups) {
193
706
                                ws->alloc_fixups *= 2;
194
706
                                ws->fixup_locations = MVM_realloc(ws->fixup_locations,
195
706
                                    ws->alloc_fixups * sizeof(MVMint32));
196
706
                                ws->fixup_bbs = MVM_realloc(ws->fixup_bbs,
197
706
                                    ws->alloc_fixups * sizeof(MVMSpeshBB *));
198
706
                            }
199
129k
                            ws->fixup_locations[ws->num_fixups] = ws->bytecode_pos;
200
129k
                            ws->fixup_bbs[ws->num_fixups]       = ins->operands[i].ins_bb;
201
129k
                            write_int32(ws, 0);
202
129k
                            ws->num_fixups++;
203
129k
                        }
204
143k
                        break;
205
0
                    }
206
123k
                    case MVM_operand_spesh_slot:
207
123k
                        write_int16(ws, ins->operands[i].lit_i16);
208
123k
                        break;
209
0
                    default:
210
0
                        MVM_oops(tc,
211
0
                            "Spesh: unknown operand type %d in codegen (op %s)",
212
0
                            (int)type, ins->info->name);
213
700k
                    }
214
700k
                    }
215
700k
                    break;
216
0
                default:
217
0
                    MVM_oops(tc, "Spesh: unknown operand type in codegen");
218
2.44M
                }
219
2.44M
            }
220
1.13M
        }
221
1.82M
222
1.82M
        /* If there was a deopt point annotation, update table. */
223
1.82M
        if (deopt_one_ann)
224
213k
            g->deopt_addrs[2 * deopt_one_ann->data.deopt_idx + 1] = ws->bytecode_pos;
225
1.82M
        if (deopt_all_ann)
226
25.6k
            g->deopt_addrs[2 * deopt_all_ann->data.deopt_idx + 1] = ws->bytecode_pos;
227
1.82M
        if (deopt_inline_ann)
228
44.9k
            g->deopt_addrs[2 * deopt_inline_ann->data.deopt_idx + 1] = ws->bytecode_pos;
229
1.82M
230
1.82M
        ins = ins->next;
231
1.82M
    }
232
310k
}
233
234
/* Generate bytecode from a spesh graph. */
235
11.1k
MVMSpeshCode * MVM_spesh_codegen(MVMThreadContext *tc, MVMSpeshGraph *g) {
236
11.1k
    MVMSpeshCode *res;
237
11.1k
    MVMSpeshBB   *bb;
238
11.1k
    MVMint32      i, hanlen;
239
11.1k
240
11.1k
    /* Initialize writer state. */
241
11.1k
    SpeshWriterState *ws     = MVM_malloc(sizeof(SpeshWriterState));
242
11.1k
    ws->bytecode_pos    = 0;
243
11.1k
    ws->bytecode_alloc  = 1024;
244
11.1k
    ws->bytecode        = MVM_malloc(ws->bytecode_alloc);
245
11.1k
    ws->bb_offsets      = MVM_malloc(g->num_bbs * sizeof(MVMint32));
246
11.1k
    ws->num_fixups      = 0;
247
11.1k
    ws->alloc_fixups    = 64;
248
11.1k
    ws->fixup_locations = MVM_malloc(ws->alloc_fixups * sizeof(MVMint32));
249
11.1k
    ws->fixup_bbs       = MVM_malloc(ws->alloc_fixups * sizeof(MVMSpeshBB *));
250
332k
    for (i = 0; i < g->num_bbs; i++)
251
321k
        ws->bb_offsets[i] = -1;
252
11.1k
253
11.1k
    /* Create copy of handlers, and -1 all offsets so we can catch missing
254
11.1k
     * updates. */
255
11.1k
    hanlen = g->num_handlers * sizeof(MVMFrameHandler);
256
11.1k
    if (hanlen) {
257
4.13k
        ws->handlers = MVM_malloc(hanlen);
258
4.13k
        memcpy(ws->handlers, g->handlers, hanlen);
259
21.7k
        for (i = 0; i < g->num_handlers; i++) {
260
17.6k
            ws->handlers[i].start_offset = -1;
261
17.6k
            ws->handlers[i].end_offset   = -1;
262
17.6k
            ws->handlers[i].goto_offset  = -1;
263
17.6k
        }
264
4.13k
    }
265
6.96k
    else {
266
6.96k
        ws->handlers = NULL;
267
6.96k
    }
268
11.1k
269
11.1k
    /* -1 all the deopt targets, so we'll easily catch those that don't get
270
11.1k
     * mapped if we try to use them. Same for inlines. */
271
331k
    for (i = 0; i < g->num_deopt_addrs; i++)
272
319k
        g->deopt_addrs[i * 2 + 1] = -1;
273
19.6k
    for (i = 0; i < g->num_inlines; i++) {
274
8.51k
        g->inlines[i].start = -1;
275
8.51k
        g->inlines[i].end = -1;
276
8.51k
    }
277
11.1k
278
11.1k
    /* Write out each of the basic blocks, in linear order. Skip the first,
279
11.1k
     * dummy, block. */
280
11.1k
    bb = g->entry->linear_next;
281
321k
    while (bb) {
282
310k
        ws->bb_offsets[bb->idx] = ws->bytecode_pos;
283
310k
        write_instructions(tc, g, ws, bb);
284
310k
        bb = bb->linear_next;
285
310k
    }
286
11.1k
287
11.1k
    /* Fixup labels we were too early for. */
288
140k
    for (i = 0; i < ws->num_fixups; i++)
289
129k
        *((MVMuint32 *)(ws->bytecode + ws->fixup_locations[i])) =
290
129k
            ws->bb_offsets[ws->fixup_bbs[i]->idx];
291
11.1k
292
11.1k
    /* Ensure all handlers that are reachable got fixed up. */
293
28.7k
    for (i = 0; i < g->num_handlers; i++) {
294
17.6k
        if (g->unreachable_handlers && g->unreachable_handlers[i]) {
295
0
            ws->handlers[i].start_offset = -1;
296
0
            ws->handlers[i].end_offset = -1;
297
0
            ws->handlers[i].goto_offset = -1;
298
0
        }
299
17.6k
        else if (ws->handlers[i].start_offset == -1 ||
300
17.6k
                 ws->handlers[i].end_offset   == -1 ||
301
17.6k
                 ws->handlers[i].goto_offset  == -1) {
302
0
            MVM_oops(tc, "Spesh: failed to fix up handler %d in %s (%d, %d, %d)",
303
0
                i,
304
0
                MVM_string_utf8_maybe_encode_C_string(tc, g->sf->body.name),
305
0
                (int)ws->handlers[i].start_offset,
306
0
                (int)ws->handlers[i].end_offset,
307
0
                (int)ws->handlers[i].goto_offset);
308
0
        }
309
17.6k
    }
310
11.1k
311
11.1k
    /* Ensure all inlines got fixed up. */
312
19.6k
    for (i = 0; i < g->num_inlines; i++) {
313
8.50k
        if (g->inlines[i].unreachable) {
314
0
            g->inlines[i].start = -1;
315
0
            g->inlines[i].end = -1;
316
0
        }
317
8.50k
        else {
318
8.50k
            if (g->inlines[i].start == -1 || g->inlines[i].end == -1)
319
0
                MVM_oops(tc, "Spesh: failed to fix up inline %d %p (%s) %d %d",
320
0
                    i,
321
0
                    g->inlines[i].g,
322
0
                    MVM_string_utf8_maybe_encode_C_string(tc, g->inlines[i].sf->body.name),
323
0
                    g->inlines[i].start,
324
0
                    g->inlines[i].end
325
0
                );
326
8.50k
        }
327
8.50k
    }
328
11.1k
329
11.1k
    /* Produce result data structure. */
330
11.1k
    res                = MVM_malloc(sizeof(MVMSpeshCode));
331
11.1k
    res->bytecode      = ws->bytecode;
332
11.1k
    res->bytecode_size = ws->bytecode_pos;
333
11.1k
    res->handlers      = ws->handlers;
334
11.1k
335
11.1k
    /* Cleanup. */
336
11.1k
    MVM_free(ws->bb_offsets);
337
11.1k
    MVM_free(ws->fixup_locations);
338
11.1k
    MVM_free(ws->fixup_bbs);
339
11.1k
    MVM_free(ws);
340
11.1k
341
11.1k
    return res;
342
11.1k
}