/home/travis/build/MoarVM/MoarVM/src/jit/compile.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | #include "internal.h" |
3 | | #include "platform/mmap.h" |
4 | | |
5 | | |
6 | | void MVM_jit_compiler_init(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *jg); |
7 | | void MVM_jit_compiler_deinit(MVMThreadContext *tc, MVMJitCompiler *compiler); |
8 | | MVMJitCode * MVM_jit_compiler_assemble(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *jg); |
9 | | void MVM_jit_compile_expr_tree(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *graph, MVMJitExprTree *tree); |
10 | | |
11 | | |
12 | 32.1k | #define COPY_ARRAY(a, n) ((n) > 0) ? memcpy(MVM_malloc((n) * sizeof(a[0])), a, (n) * sizeof(a[0])) : NULL; |
13 | | |
14 | | static const MVMuint16 MAGIC_BYTECODE[] = { MVM_OP_sp_jit_enter, 0 }; |
15 | | |
16 | 10.7k | void MVM_jit_compiler_init(MVMThreadContext *tc, MVMJitCompiler *cl, MVMJitGraph *jg) { |
17 | 10.7k | /* Create dasm state */ |
18 | 10.7k | dasm_init(cl, 2); |
19 | 10.7k | dasm_setupglobal(cl, cl->dasm_globals, MVM_JIT_MAX_GLOBALS); |
20 | 10.7k | dasm_setup(cl, MVM_jit_actions()); |
21 | 10.7k | |
22 | 10.7k | /* Store graph we're compiling */ |
23 | 10.7k | cl->graph = jg; |
24 | 10.7k | /* next (internal) label to assign */ |
25 | 10.7k | cl->label_offset = jg->num_labels; |
26 | 10.7k | /* space for dynamic labels */ |
27 | 10.7k | dasm_growpc(cl, jg->num_labels); |
28 | 10.7k | |
29 | 10.7k | /* Spill offset and free list */ |
30 | 10.7k | cl->spills_base = jg->sg->num_locals * sizeof(MVMRegister); |
31 | 10.7k | memset(cl->spills_free, -1, sizeof(cl->spills_free)); |
32 | 10.7k | MVM_VECTOR_INIT(cl->spills, 4); |
33 | 10.7k | } |
34 | | |
35 | | |
36 | 10.7k | void MVM_jit_compiler_deinit(MVMThreadContext *tc, MVMJitCompiler *cl) { |
37 | 10.7k | dasm_free(cl); |
38 | 10.7k | MVM_VECTOR_DESTROY(cl->spills); |
39 | 10.7k | } |
40 | | |
41 | 10.7k | MVMJitCode * MVM_jit_compile_graph(MVMThreadContext *tc, MVMJitGraph *jg) { |
42 | 10.7k | MVMJitCompiler cl; |
43 | 10.7k | MVMJitCode *code; |
44 | 10.7k | MVMJitNode *node = jg->first_node; |
45 | 10.7k | |
46 | 10.7k | MVM_jit_log(tc, "Starting compilation\n"); |
47 | 10.7k | /* initialation */ |
48 | 10.7k | MVM_jit_compiler_init(tc, &cl, jg); |
49 | 10.7k | /* generate code */ |
50 | 10.7k | MVM_jit_emit_prologue(tc, &cl, jg); |
51 | 875k | while (node) { |
52 | 865k | switch(node->type) { |
53 | 322k | case MVM_JIT_NODE_LABEL: |
54 | 322k | MVM_jit_emit_label(tc, &cl, jg, node->u.label.name); |
55 | 322k | break; |
56 | 135k | case MVM_JIT_NODE_PRIMITIVE: |
57 | 135k | MVM_jit_emit_primitive(tc, &cl, jg, &node->u.prim); |
58 | 135k | break; |
59 | 22.9k | case MVM_JIT_NODE_BRANCH: |
60 | 22.9k | MVM_jit_emit_block_branch(tc, &cl, jg, &node->u.branch); |
61 | 22.9k | break; |
62 | 67.2k | case MVM_JIT_NODE_CALL_C: |
63 | 67.2k | MVM_jit_emit_call_c(tc, &cl, jg, &node->u.call); |
64 | 67.2k | break; |
65 | 36.2k | case MVM_JIT_NODE_GUARD: |
66 | 36.2k | MVM_jit_emit_guard(tc, &cl, jg, &node->u.guard); |
67 | 36.2k | break; |
68 | 26.1k | case MVM_JIT_NODE_INVOKE: |
69 | 26.1k | MVM_jit_emit_invoke(tc, &cl, jg, &node->u.invoke); |
70 | 26.1k | break; |
71 | 713 | case MVM_JIT_NODE_JUMPLIST: |
72 | 713 | MVM_jit_emit_jumplist(tc, &cl, jg, &node->u.jumplist); |
73 | 713 | break; |
74 | 0 | case MVM_JIT_NODE_CONTROL: |
75 | 0 | MVM_jit_emit_control(tc, &cl, &node->u.control, NULL); |
76 | 0 | break; |
77 | 253k | case MVM_JIT_NODE_EXPR_TREE: |
78 | 253k | MVM_jit_compile_expr_tree(tc, &cl, jg, node->u.tree); |
79 | 253k | break; |
80 | 0 | case MVM_JIT_NODE_DATA: |
81 | 0 | MVM_jit_emit_data(tc, &cl, &node->u.data); |
82 | 0 | break; |
83 | 865k | } |
84 | 865k | node = node->next; |
85 | 865k | } |
86 | 10.7k | MVM_jit_emit_epilogue(tc, &cl, jg); |
87 | 10.7k | |
88 | 10.7k | /* Generate code */ |
89 | 10.7k | code = MVM_jit_compiler_assemble(tc, &cl, jg); |
90 | 10.7k | |
91 | 10.7k | /* Clear up the compiler */ |
92 | 10.7k | MVM_jit_compiler_deinit(tc, &cl); |
93 | 10.7k | |
94 | 10.7k | /* Logging for insight */ |
95 | 10.7k | if (tc->instance->jit_bytecode_dir) { |
96 | 0 | MVM_jit_log_bytecode(tc, code); |
97 | 0 | } |
98 | 10.7k | if (tc->instance->jit_log_fh) |
99 | 0 | fflush(tc->instance->jit_log_fh); |
100 | 10.7k | return code; |
101 | 10.7k | } |
102 | | |
103 | 10.7k | MVMJitCode * MVM_jit_compiler_assemble(MVMThreadContext *tc, MVMJitCompiler *cl, MVMJitGraph *jg) { |
104 | 10.7k | MVMJitCode * code; |
105 | 10.7k | MVMint32 i; |
106 | 10.7k | char * memory; |
107 | 10.7k | size_t codesize; |
108 | 10.7k | |
109 | 10.7k | MVMint32 dasm_error = 0; |
110 | 10.7k | |
111 | 10.7k | /* compile the function */ |
112 | 10.7k | if ((dasm_error = dasm_link(cl, &codesize)) != 0) { |
113 | 0 | MVM_jit_log(tc, "DynASM could not link, error: %d\n", dasm_error); |
114 | 0 | return NULL; |
115 | 0 | } |
116 | 10.7k | |
117 | 10.7k | memory = MVM_platform_alloc_pages(codesize, MVM_PAGE_READ|MVM_PAGE_WRITE); |
118 | 10.7k | if ((dasm_error = dasm_encode(cl, memory)) != 0) { |
119 | 0 | MVM_jit_log(tc, "DynASM could not encode, error: %d\n", dasm_error); |
120 | 0 | return NULL; |
121 | 0 | } |
122 | 10.7k | |
123 | 10.7k | /* set memory readable + executable */ |
124 | 10.7k | if (!MVM_platform_set_page_mode(memory, codesize, MVM_PAGE_READ|MVM_PAGE_EXEC)) { |
125 | 0 | MVM_jit_log(tc, "Setting jit page executable failed or was denied. deactivating jit.\n"); |
126 | 0 | /* our caller allocated the compiler and our caller must clean it up */ |
127 | 0 | tc->instance->jit_enabled = 0; |
128 | 0 | return NULL; |
129 | 0 | } |
130 | 10.7k | |
131 | 10.7k | MVM_jit_log(tc, "Bytecode size: %"MVM_PRSz"\n", codesize); |
132 | 10.7k | |
133 | 10.7k | /* Create code segment */ |
134 | 10.7k | code = MVM_malloc(sizeof(MVMJitCode)); |
135 | 10.7k | code->func_ptr = (void (*)(MVMThreadContext*,MVMCompUnit*,void*)) memory; |
136 | 10.7k | code->size = codesize; |
137 | 10.7k | code->bytecode = (MVMuint8*)MAGIC_BYTECODE; |
138 | 10.7k | code->sf = jg->sg->sf; |
139 | 10.7k | code->spill_size = cl->spills_num; |
140 | 10.7k | if (cl->spills_num > 0) { |
141 | 9.59k | MVMint32 sg_num_locals = jg->sg->num_locals; |
142 | 9.59k | code->num_locals = sg_num_locals + cl->spills_num; |
143 | 9.59k | code->local_types = MVM_malloc(code->num_locals * sizeof(MVMuint16)); |
144 | 9.59k | if (jg->sg->local_types != NULL) { |
145 | 4.75k | memcpy(code->local_types, jg->sg->local_types, sizeof(MVMuint16)*sg_num_locals); |
146 | 4.84k | } else { |
147 | 4.84k | memcpy(code->local_types, code->sf->body.local_types, sizeof(MVMuint16)*sg_num_locals); |
148 | 4.84k | } |
149 | 28.6k | for (i = 0; i < cl->spills_num; i++) { |
150 | 19.0k | code->local_types[sg_num_locals + i] = cl->spills[i].reg_type; |
151 | 19.0k | } |
152 | 1.11k | } else { |
153 | 1.11k | code->local_types = NULL; |
154 | 1.11k | code->num_locals = 0; |
155 | 1.11k | } |
156 | 10.7k | |
157 | 10.7k | /* Get the basic block labels */ |
158 | 10.7k | code->num_labels = jg->num_labels; |
159 | 10.7k | code->labels = MVM_calloc(code->num_labels, sizeof(void*)); |
160 | 10.7k | |
161 | 338k | for (i = 0; i < code->num_labels; i++) { |
162 | 327k | MVMint32 offset = dasm_getpclabel(cl, i); |
163 | 327k | if (offset < 0) |
164 | 0 | MVM_jit_log(tc, "Got negative offset for dynamic label %d\n", i); |
165 | 327k | code->labels[i] = memory + offset; |
166 | 327k | } |
167 | 10.7k | /* We only ever use one global label, which is the exit label */ |
168 | 10.7k | code->exit_label = cl->dasm_globals[0]; |
169 | 10.7k | |
170 | 10.7k | /* Copy the deopts, inlines, and handlers. Because these use the |
171 | 10.7k | * label index rather than the direct pointer, no fixup is |
172 | 10.7k | * necessary */ |
173 | 10.7k | code->num_deopts = jg->deopts_num; |
174 | 10.7k | code->deopts = COPY_ARRAY(jg->deopts, jg->deopts_num); |
175 | 10.7k | code->num_handlers = jg->handlers_num; |
176 | 10.7k | code->handlers = COPY_ARRAY(jg->handlers, jg->handlers_alloc); |
177 | 10.7k | code->num_inlines = jg->inlines_num; |
178 | 10.7k | code->inlines = COPY_ARRAY(jg->inlines, jg->inlines_alloc); |
179 | 10.7k | |
180 | 10.7k | /* add sequence number */ |
181 | 10.7k | code->seq_nr = tc->instance->jit_seq_nr++; |
182 | 10.7k | |
183 | 10.7k | return code; |
184 | 10.7k | } |
185 | | |
186 | 0 | void MVM_jit_code_destroy(MVMThreadContext *tc, MVMJitCode *code) { |
187 | 0 | MVM_platform_free_pages(code->func_ptr, code->size); |
188 | 0 | MVM_free(code->labels); |
189 | 0 | MVM_free(code->deopts); |
190 | 0 | MVM_free(code->handlers); |
191 | 0 | MVM_free(code->inlines); |
192 | 0 | MVM_free(code->local_types); |
193 | 0 | MVM_free(code); |
194 | 0 | } |
195 | | |
196 | | |
197 | | |
198 | | #define NYI(x) MVM_oops(tc, #x " NYI") |
199 | | |
200 | | |
201 | | /* pseudotile emit functions */ |
202 | | void MVM_jit_compile_branch(MVMThreadContext *tc, MVMJitCompiler *compiler, |
203 | 155k | MVMJitTile *tile, MVMJitExprTree *tree) { |
204 | 155k | MVM_jit_emit_branch(tc, compiler, tile->args[0] + compiler->label_offset); |
205 | 155k | } |
206 | | |
207 | | void MVM_jit_compile_conditional_branch(MVMThreadContext *tc, MVMJitCompiler *compiler, |
208 | 392k | MVMJitTile *tile, MVMJitExprTree *tree) { |
209 | 392k | MVM_jit_emit_conditional_branch(tc, compiler, tile->args[0], tile->args[1] + compiler->label_offset); |
210 | 392k | } |
211 | | |
212 | | void MVM_jit_compile_label(MVMThreadContext *tc, MVMJitCompiler *compiler, |
213 | 400k | MVMJitTile *tile, MVMJitExprTree *tree) { |
214 | 400k | MVM_jit_emit_label(tc, compiler, tree->graph, tile->args[0] + compiler->label_offset); |
215 | 400k | } |
216 | | |
217 | | void MVM_jit_compile_store(MVMThreadContext *tc, MVMJitCompiler *compiler, |
218 | 114k | MVMJitTile *tile, MVMJitExprTree *tree) { |
219 | 114k | MVM_jit_emit_store(tc, compiler, tile->args[0], tile->args[1], |
220 | 114k | MVM_JIT_STORAGE_GPR, tile->values[1], sizeof(MVMRegister)); |
221 | 114k | } |
222 | | |
223 | | void MVM_jit_compile_memory_copy(MVMThreadContext *tc, MVMJitCompiler *compiler, |
224 | 0 | MVMJitTile *tile, MVMJitExprTree *tree) { |
225 | 0 | MVM_jit_emit_load(tc, compiler, MVM_JIT_STORAGE_GPR, tile->values[1], |
226 | 0 | tile->args[2], tile->args[3], sizeof(MVMRegister)); |
227 | 0 | MVM_jit_emit_store(tc, compiler, tile->args[0], tile->args[1], |
228 | 0 | MVM_JIT_STORAGE_GPR, tile->values[1], sizeof(MVMRegister)); |
229 | 0 | } |
230 | | |
231 | | void MVM_jit_compile_move(MVMThreadContext *tc, MVMJitCompiler *compiler, |
232 | 1.01M | MVMJitTile *tile, MVMJitExprTree *tree) { |
233 | 1.01M | MVM_jit_emit_copy(tc, compiler, MVM_JIT_STORAGE_GPR, tile->values[0], |
234 | 1.01M | MVM_JIT_STORAGE_GPR, tile->values[1]); |
235 | 1.01M | } |
236 | | |
237 | | void MVM_jit_compile_load(MVMThreadContext *tc, MVMJitCompiler *compiler, |
238 | 470k | MVMJitTile *tile, MVMJitExprTree *tree) { |
239 | 470k | MVM_jit_emit_load(tc, compiler, |
240 | 470k | MVM_JIT_STORAGE_GPR, tile->values[0], |
241 | 470k | tile->args[0], tile->args[1], |
242 | 470k | sizeof(MVMRegister)); |
243 | 470k | } |
244 | | |
245 | | void MVM_jit_compile_guard(MVMThreadContext *tc, MVMJitCompiler *compiler, |
246 | 0 | MVMJitTile *tile, MVMJitExprTree *tree) { |
247 | 0 | MVM_jit_emit_control(tc, compiler, NULL, tile); |
248 | 0 | } |
249 | | |
250 | 253k | void MVM_jit_compile_expr_tree(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *jg, MVMJitExprTree *tree) { |
251 | 253k | MVMJitTileList *list; |
252 | 253k | MVMJitTile *tile; |
253 | 253k | MVMint32 i; |
254 | 253k | /* First stage, tile the tree */ |
255 | 253k | list = MVM_jit_tile_expr_tree(tc, compiler, tree); |
256 | 253k | MVM_jit_log_tile_list(tc, list); |
257 | 253k | |
258 | 253k | /* Second stage, allocate registers */ |
259 | 253k | MVM_jit_linear_scan_allocate(tc, compiler, list); |
260 | 253k | |
261 | 253k | /* Allocate sufficient space for the internal labels */ |
262 | 253k | dasm_growpc(compiler, compiler->label_offset + tree->num_labels); |
263 | 253k | |
264 | 253k | /* Third stage, emit the code */ |
265 | 8.65M | for (i = 0; i < list->items_num; i++) { |
266 | 8.40M | tile = list->items[i]; |
267 | 8.40M | /* definition tiles etc. have NULL emit rules */ |
268 | 8.40M | if (tile->emit != NULL) { |
269 | 5.84M | tile->emit(tc, compiler, tile, tree); |
270 | 5.84M | } |
271 | 8.40M | } |
272 | 253k | /* Cleanup tile lits */ |
273 | 253k | MVM_jit_tile_list_destroy(tc, list); |
274 | 253k | |
275 | 253k | /* Make sure no other tree reuses the same labels */ |
276 | 253k | compiler->label_offset += tree->num_labels; |
277 | 253k | } |
278 | | |
279 | 133k | MVM_STATIC_INLINE MVMint32 reg_type_bucket(MVMint8 reg_type) { |
280 | 133k | switch (reg_type) { |
281 | 0 | case MVM_reg_num32: |
282 | 0 | case MVM_reg_num64: |
283 | 0 | return 1; |
284 | 0 | break; |
285 | 3.59k | case MVM_reg_str: |
286 | 3.59k | return 2; |
287 | 0 | break; |
288 | 108k | case MVM_reg_obj: |
289 | 108k | return 3; |
290 | 0 | break; |
291 | 21.2k | default: |
292 | 21.2k | break; |
293 | 133k | } |
294 | 21.2k | return 0; |
295 | 133k | } |
296 | | |
297 | | |
298 | 66.6k | MVMint32 MVM_jit_spill_memory_select(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMint8 reg_type) { |
299 | 66.6k | MVMint32 idx; |
300 | 66.6k | MVMint8 bucket = reg_type_bucket(reg_type); |
301 | 66.6k | |
302 | 66.6k | if (compiler->spills_free[bucket] >= 0) { |
303 | 47.4k | idx = compiler->spills_free[bucket]; |
304 | 47.4k | compiler->spills_free[bucket] = compiler->spills[idx].next; |
305 | 19.2k | } else { |
306 | 19.2k | MVM_VECTOR_ENSURE_SPACE(compiler->spills, idx = compiler->spills_num++); |
307 | 19.2k | compiler->spills[idx].reg_type = reg_type; |
308 | 19.2k | } |
309 | 66.6k | return compiler->spills_base + idx * sizeof(MVMRegister); |
310 | 66.6k | } |
311 | | |
312 | 66.6k | void MVM_jit_spill_memory_release(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMint32 pos, MVMint8 reg_type) { |
313 | 66.6k | MVMint32 idx = (pos - compiler->spills_base) / sizeof(MVMRegister); |
314 | 66.6k | MVMint8 bucket = reg_type_bucket(reg_type); |
315 | 66.6k | compiler->spills[idx].next = compiler->spills_free[bucket]; |
316 | 66.6k | compiler->spills_free[bucket] = idx; |
317 | 66.6k | } |