1 //
2 // gravity_ircode.c
3 // gravity
4 //
5 // Created by Marco Bambini on 06/11/14.
6 // Copyright (c) 2014 CreoLabs. All rights reserved.
7 //
8
9 #include "gravity_ircode.h"
10 #include "gravity_value.h"
11 #include "gravity_debug.h"
12 #include <inttypes.h>
13
14 typedef marray_t(inst_t *) code_r;
15 typedef marray_t(bool *) context_r;
16
17 struct ircode_t {
18 code_r *list; // array of ircode instructions
19
20 uint32_r label_true; // labels used in loops
21 uint32_r label_false;
22 uint32_r label_check;
23 uint32_t label_counter;
24
25 uint32_t maxtemp; // maximum number of temp registers used in this ircode
26 uint32_t ntemps; // current number of temp registers in use
27 uint16_t nlocals; // number of local registers (params + local variables)
28 bool error; // error flag set when no more registers are availables
29
30 bool state[MAX_REGISTERS]; // registers mask
31 bool skipclear[MAX_REGISTERS]; // registers protection for temps used in for loop
32 uint32_r registers; // registers stack
33 context_r context; // context array
34 };
35
ircode_create(uint16_t nlocals)36 ircode_t *ircode_create (uint16_t nlocals) {
37 ircode_t *code = (ircode_t *)mem_alloc(NULL, sizeof(ircode_t));
38 if (!code) return NULL;
39 code->label_counter = 0;
40 code->nlocals = nlocals;
41 code->ntemps = 0;
42 code->maxtemp = 0;
43 code->error = false;
44
45 code->list = mem_alloc(NULL, sizeof(code_r));
46 if (!code->list) return NULL;
47 marray_init(*code->list);
48 marray_init(code->label_true);
49 marray_init(code->label_false);
50 marray_init(code->label_check);
51 marray_init(code->registers);
52 marray_init(code->context);
53
54 // init state array (register 0 is reserved)
55 bzero(code->state, MAX_REGISTERS * sizeof(bool));
56 code->state[0] = true;
57 for (uint32_t i=0; i<nlocals; ++i) {
58 code->state[i] = true;
59 }
60 return code;
61 }
62
ircode_free(ircode_t * code)63 void ircode_free (ircode_t *code) {
64 uint32_t count = ircode_count(code);
65 for (uint32_t i=0; i<count; ++i) {
66 inst_t *inst = marray_get(*code->list, i);
67 mem_free(inst);
68 }
69
70 marray_destroy(*code->list);
71 marray_destroy(code->context);
72 marray_destroy(code->registers);
73 marray_destroy(code->label_true);
74 marray_destroy(code->label_false);
75 marray_destroy(code->label_check);
76 mem_free(code->list);
77 mem_free(code);
78 }
79
ircode_ntemps(ircode_t * code)80 uint32_t ircode_ntemps (ircode_t *code) {
81 return code->ntemps;
82 }
83
ircode_count(ircode_t * code)84 uint32_t ircode_count (ircode_t *code) {
85 return (uint32_t)marray_size(*code->list);
86 }
87
ircode_get(ircode_t * code,uint32_t index)88 inst_t *ircode_get (ircode_t *code, uint32_t index) {
89 uint32_t n = (uint32_t)marray_size(*code->list);
90 return (index >= n) ? NULL : marray_get(*code->list, index);
91 }
92
ircode_iserror(ircode_t * code)93 bool ircode_iserror (ircode_t *code) {
94 return code->error;
95 }
96 // MARK: -
97
inst_new(opcode_t op,uint32_t p1,uint32_t p2,uint32_t p3,optag_t tag,int64_t n,double d,uint32_t lineno)98 static inst_t *inst_new (opcode_t op, uint32_t p1, uint32_t p2, uint32_t p3, optag_t tag, int64_t n, double d, uint32_t lineno) {
99
100 // debug code
101 #if GRAVITY_OPCODE_DEBUG
102 if (tag == LABEL_TAG) {
103 DEBUG_OPCODE("LABEL %d", p1);
104 } else if (tag != PRAGMA_MOVE_OPTIMIZATION){
105 const char *op_name = opcode_name(op);
106
107 if (op == LOADI) {
108 if (tag == DOUBLE_TAG)
109 printf("%s %d %.2f\n", op_name, p1, d);
110 else
111 printf("%s %d %lld\n", op_name, p1, n);
112 } else {
113 int nop = opcode_numop(op);
114 if (nop == 0) {
115 DEBUG_OPCODE("%s", op_name);
116 } else if (nop == 1) {
117 DEBUG_OPCODE("%s %d", op_name, p1);
118 } else if (nop == 2) {
119 DEBUG_OPCODE("%s %d %d", op_name, p1, p2);
120 } else if (nop == 3) {
121 DEBUG_OPCODE("%s %d %d %d", opcode_name(op), p1, p2, p3);
122 }
123 }
124 }
125 #endif
126
127 inst_t *inst = (inst_t *)mem_alloc(NULL, sizeof(inst_t));
128 inst->op = op;
129 inst->tag = tag;
130 inst->p1 = p1;
131 inst->p2 = p2;
132 inst->p3 = p3;
133 inst->lineno = lineno;
134
135 if (tag == DOUBLE_TAG) inst->d = d;
136 else if (tag == INT_TAG) inst->n = n;
137
138 assert(inst);
139 return inst;
140 }
141
inst_setskip(inst_t * inst)142 void inst_setskip (inst_t *inst) {
143 inst->tag = SKIP_TAG;
144 }
145
ircode_patch_init(ircode_t * code,uint16_t index)146 void ircode_patch_init (ircode_t *code, uint16_t index) {
147 // prepend call instructions to code
148 // LOADK temp index
149 // LOAD temp 0 temp
150 // MOVE temp+1 0
151 // CALL temp temp 1
152
153 // load constant
154 uint32_t dest = ircode_register_push_temp(code);
155 inst_t *inst1 = inst_new(LOADK, dest, index, 0, NO_TAG, 0, 0.0, 0);
156
157 // load from lookup
158 inst_t *inst2 = inst_new(LOAD, dest, 0, dest, NO_TAG, 0, 0.0, 0);
159
160 // prepare parameter
161 uint32_t dest2 = ircode_register_push_temp(code);
162 inst_t *inst3 = inst_new(MOVE, dest2, 0, 0, NO_TAG, 0, 0.0, 0);
163 ircode_register_pop(code);
164
165 // execute call
166 inst_t *inst4 = inst_new(CALL, dest, dest, 1, NO_TAG, 0, 0.0, 0);
167
168 // pop temps used
169 ircode_register_pop(code);
170
171 // create new instruction list
172 code_r *list = mem_alloc(NULL, sizeof(code_r));
173 marray_init(*list);
174
175 // add newly create instructions
176 marray_push(inst_t*, *list, inst1);
177 marray_push(inst_t*, *list, inst2);
178 marray_push(inst_t*, *list, inst3);
179 marray_push(inst_t*, *list, inst4);
180
181 // then copy original instructions
182 code_r *orig_list = code->list;
183 uint32_t count = ircode_count(code);
184 for (uint32_t i=0; i<count; ++i) {
185 inst_t *inst = marray_get(*orig_list, i);
186 marray_push(inst_t*, *list, inst);
187 }
188
189 // free dest list
190 marray_destroy(*orig_list);
191 mem_free(code->list);
192
193 // replace dest list with the newly created list
194 code->list = list;
195 }
196
opcode_numop(opcode_t op)197 uint8_t opcode_numop (opcode_t op) {
198 switch (op) {
199 case HALT: return 0;
200 case NOP: return 0;
201 case RET0: return 0;
202 case RET: return 1;
203 case CALL: return 3;
204 case SETLIST: return 3;
205 case LOADK: return 2;
206 case LOADG: return 2;
207 case LOADI: return 2;
208 case LOADAT: return 3;
209 case LOADS: return 3;
210 case LOAD: return 3;
211 case LOADU: return 2;
212 case MOVE: return 2;
213 case STOREG: return 2;
214 case STOREAT: return 3;
215 case STORE: return 3;
216 case STOREU: return 2;
217 case JUMP: return 1;
218 case JUMPF: return 2;
219 case SWITCH: return 1;
220 case ADD: return 3;
221 case SUB: return 3;
222 case DIV: return 3;
223 case MUL: return 3;
224 case REM: return 3;
225 case AND: return 3;
226 case OR: return 3;
227 case LT: return 3;
228 case GT: return 3;
229 case EQ: return 3;
230 case ISA: return 3;
231 case MATCH: return 3;
232 case EQQ: return 3;
233 case LEQ: return 3;
234 case GEQ: return 3;
235 case NEQ: return 3;
236 case NEQQ: return 3;
237 case NEG: return 2;
238 case NOT: return 2;
239 case LSHIFT: return 3;
240 case RSHIFT: return 3;
241 case BAND: return 3;
242 case BOR: return 3;
243 case BXOR: return 3;
244 case BNOT: return 2;
245 case MAPNEW: return 2;
246 case LISTNEW: return 2;
247 case RANGENEW: return 3;
248 case CLOSURE: return 2;
249 case CLOSE: return 1;
250 case CHECK: return 1;
251 case RESERVED2:
252 case RESERVED3:
253 case RESERVED4:
254 case RESERVED5:
255 case RESERVED6: return 0;
256 }
257
258 assert(0);
259 return 0;
260 }
261
ircode_dump(void * _code)262 void ircode_dump (void *_code) {
263 ircode_t *code = (ircode_t *)_code;
264 code_r *list = code->list;
265 uint32_t count = ircode_count(code);
266
267 if (count == 0) {
268 printf("NONE\n");
269 return;
270 }
271
272 for (uint32_t i=0, line=0; i<count; ++i) {
273 inst_t *inst = marray_get(*list, i);
274 opcode_t op = inst->op;
275 int32_t p1 = inst->p1;
276 int32_t p2 = inst->p2;
277 int32_t p3 = inst->p3;
278 if (inst->tag == SKIP_TAG) continue;
279 if (inst->tag == PRAGMA_MOVE_OPTIMIZATION) continue;
280 if (inst->tag == LABEL_TAG) {printf("LABEL %d:\n", p1); continue;}
281
282 uint8_t n = opcode_numop(op);
283 if ((op == SETLIST) && (p2 == 0)) n = 2;
284
285 // set to 1 to debug line number for each instruction
286 #if 0
287 printf("(%d)\t\t", inst->lineno);
288 #endif
289
290 switch (n) {
291 case 0: {
292 printf("%05d\t%s\n", line, opcode_name(op));
293 }
294
295 case 1: {
296 printf("%05d\t%s %d\n", line, opcode_name(op), p1);
297 } break;
298
299 case 2: {
300 if (op == LOADI) {
301 if (inst->tag == DOUBLE_TAG) printf("%05d\t%s %d %.2f\n", line, opcode_name(op), p1, inst->d);
302 #if defined(_WIN32)
303 else printf("%05d\t%s %d %I64d\n", line, opcode_name(op), p1, inst->n);
304 #else
305 else printf("%05d\t%s %d %"PRId64"\n", line, opcode_name(op), p1, inst->n);
306 #endif
307 } else if (op == LOADK) {
308 if (p2 < CPOOL_INDEX_MAX) printf("%05d\t%s %d %d\n", line, opcode_name(op), p1, p2);
309 else printf("%05d\t%s %d %s\n", line, opcode_name(op), p1, opcode_constname(p2));
310 } else {
311 printf("%05d\t%s %d %d\n", line, opcode_name(op), p1, p2);
312 }
313 } break;
314
315 case 3: {
316 printf("%05d\t%s %d %d %d\n", line, opcode_name(op), p1, p2, p3);
317 } break;
318
319 default: assert(0);
320 }
321 ++line;
322 }
323 }
324
325 // MARK: -
326
ircode_newlabel(ircode_t * code)327 uint32_t ircode_newlabel (ircode_t *code) {
328 return ++code->label_counter;
329 }
330
ircode_setlabel_true(ircode_t * code,uint32_t nlabel)331 void ircode_setlabel_true (ircode_t *code, uint32_t nlabel) {
332 marray_push(uint32_t, code->label_true, nlabel);
333 }
334
ircode_setlabel_false(ircode_t * code,uint32_t nlabel)335 void ircode_setlabel_false (ircode_t *code, uint32_t nlabel) {
336 marray_push(uint32_t, code->label_false, nlabel);
337 }
338
ircode_setlabel_check(ircode_t * code,uint32_t nlabel)339 void ircode_setlabel_check (ircode_t *code, uint32_t nlabel) {
340 marray_push(uint32_t, code->label_check, nlabel);
341 }
342
ircode_unsetlabel_true(ircode_t * code)343 void ircode_unsetlabel_true (ircode_t *code) {
344 marray_pop(code->label_true);
345 }
346
ircode_unsetlabel_false(ircode_t * code)347 void ircode_unsetlabel_false (ircode_t *code) {
348 marray_pop(code->label_false);
349 }
350
ircode_unsetlabel_check(ircode_t * code)351 void ircode_unsetlabel_check (ircode_t *code) {
352 marray_pop(code->label_check);
353 }
354
ircode_getlabel_true(ircode_t * code)355 uint32_t ircode_getlabel_true (ircode_t *code) {
356 size_t n = marray_size(code->label_true);
357 uint32_t v = marray_get(code->label_true, n-1);
358 return v;
359 }
360
ircode_getlabel_false(ircode_t * code)361 uint32_t ircode_getlabel_false (ircode_t *code) {
362 size_t n = marray_size(code->label_false);
363 uint32_t v = marray_get(code->label_false, n-1);
364 return v;
365 }
366
ircode_getlabel_check(ircode_t * code)367 uint32_t ircode_getlabel_check (ircode_t *code) {
368 size_t n = marray_size(code->label_check);
369 uint32_t v = marray_get(code->label_check, n-1);
370 return v;
371 }
372
ircode_marklabel(ircode_t * code,uint32_t nlabel,uint32_t lineno)373 void ircode_marklabel (ircode_t *code, uint32_t nlabel, uint32_t lineno) {
374 inst_t *inst = inst_new(0, nlabel, 0, 0, LABEL_TAG, 0, 0.0, lineno);
375 marray_push(inst_t*, *code->list, inst);
376 }
377
378 // MARK: -
ircode_pragma(ircode_t * code,optag_t tag,uint32_t value,uint32_t lineno)379 void ircode_pragma (ircode_t *code, optag_t tag, uint32_t value, uint32_t lineno) {
380 ircode_add_tag(code, 0, value, 0, 0, tag, lineno);
381 }
382
383 // MARK: -
384
ircode_set_index(uint32_t index,ircode_t * code,opcode_t op,uint32_t p1,uint32_t p2,uint32_t p3)385 void ircode_set_index (uint32_t index, ircode_t *code, opcode_t op, uint32_t p1, uint32_t p2, uint32_t p3) {
386 inst_t *inst = marray_get(*code->list, index);
387 inst->op = op;
388 inst->p1 = p1;
389 inst->p2 = p2;
390 inst->p3 = p3;
391 inst->tag = NO_TAG;
392 }
393
ircode_add(ircode_t * code,opcode_t op,uint32_t p1,uint32_t p2,uint32_t p3,uint32_t lineno)394 void ircode_add (ircode_t *code, opcode_t op, uint32_t p1, uint32_t p2, uint32_t p3, uint32_t lineno) {
395 ircode_add_tag(code, op, p1, p2, p3, 0, lineno);
396 }
397
ircode_add_tag(ircode_t * code,opcode_t op,uint32_t p1,uint32_t p2,uint32_t p3,optag_t tag,uint32_t lineno)398 void ircode_add_tag (ircode_t *code, opcode_t op, uint32_t p1, uint32_t p2, uint32_t p3, optag_t tag, uint32_t lineno) {
399 inst_t *inst = inst_new(op, p1, p2, p3, tag, 0, 0.0, lineno);
400 marray_push(inst_t*, *code->list, inst);
401 }
402
ircode_add_double(ircode_t * code,double d,uint32_t lineno)403 void ircode_add_double (ircode_t *code, double d, uint32_t lineno) {
404 uint32_t regnum = ircode_register_push_temp(code);
405 inst_t *inst = inst_new(LOADI, regnum, 0, 0, DOUBLE_TAG, 0, d, lineno);
406 marray_push(inst_t*, *code->list, inst);
407 }
408
ircode_add_constant(ircode_t * code,uint32_t index,uint32_t lineno)409 void ircode_add_constant (ircode_t *code, uint32_t index, uint32_t lineno) {
410 uint32_t regnum = ircode_register_push_temp(code);
411 inst_t *inst = inst_new(LOADK, regnum, index, 0, NO_TAG, 0, 0, lineno);
412 marray_push(inst_t*, *code->list, inst);
413 }
414
ircode_add_int(ircode_t * code,int64_t n,uint32_t lineno)415 void ircode_add_int (ircode_t *code, int64_t n, uint32_t lineno) {
416 uint32_t regnum = ircode_register_push_temp(code);
417 inst_t *inst = inst_new(LOADI, regnum, 0, 0, INT_TAG, n, 0, lineno);
418 marray_push(inst_t*, *code->list, inst);
419 }
420
ircode_add_skip(ircode_t * code,uint32_t lineno)421 void ircode_add_skip (ircode_t *code, uint32_t lineno) {
422 inst_t *inst = inst_new(0, 0, 0, 0, NO_TAG, 0, 0, lineno);
423 inst_setskip(inst);
424 marray_push(inst_t*, *code->list, inst);
425 }
426
ircode_add_check(ircode_t * code)427 void ircode_add_check (ircode_t *code) {
428 inst_t *inst = marray_last(*code->list);
429 if ((inst) && (inst->op == MOVE)) {
430 inst_t *newinst = inst_new(CHECK, inst->p1, 0, 0, NO_TAG, 0, 0, inst->lineno);
431 marray_push(inst_t*, *code->list, newinst);
432 }
433 }
434
435 // MARK: - Context based functions -
436
437 #if 0
438 static void dump_context(bool *context) {
439 for (uint32_t i=0; i<MAX_REGISTERS; ++i) {
440 printf("%d ", context[i]);
441 }
442 printf("\n");
443 }
444 #endif
445
ircode_push_context(ircode_t * code)446 void ircode_push_context (ircode_t *code) {
447 bool *context = mem_alloc(NULL, sizeof(bool) * MAX_REGISTERS);
448 marray_push(bool *, code->context, context);
449 }
450
ircode_pop_context(ircode_t * code)451 void ircode_pop_context (ircode_t *code) {
452 bool *context = marray_pop(code->context);
453 // apply context mask
454 for (uint32_t i=0; i<MAX_REGISTERS; ++i) {
455 if (context[i]) code->state[i] = false;
456 }
457 mem_free(context);
458 }
459
ircode_register_pop_context_protect(ircode_t * code,bool protect)460 uint32_t ircode_register_pop_context_protect (ircode_t *code, bool protect) {
461 if (marray_size(code->registers) == 0) return REGISTER_ERROR;
462 uint32_t value = (uint32_t)marray_pop(code->registers);
463
464 if (protect) code->state[value] = true;
465 else if (value >= code->nlocals) code->state[value] = false;
466
467 if (protect && value >= code->nlocals) {
468 bool *context = marray_last(code->context);
469 context[value] = true;
470 }
471
472 DEBUG_REGISTER("POP REGISTER %d", value);
473 return value;
474 }
475
ircode_register_protect_outside_context(ircode_t * code,uint32_t nreg)476 bool ircode_register_protect_outside_context (ircode_t *code, uint32_t nreg) {
477 if (nreg < code->nlocals) return true;
478 if (!code->state[nreg]) return false;
479 bool *context = marray_last(code->context);
480 context[nreg] = false;
481 return true;
482 }
483
ircode_register_protect_in_context(ircode_t * code,uint32_t nreg)484 void ircode_register_protect_in_context (ircode_t *code, uint32_t nreg) {
485 assert(code->state[nreg]);
486 bool *context = marray_last(code->context);
487 context[nreg] = true;
488 }
489
490 // MARK: -
491
ircode_register_new(ircode_t * code)492 static uint32_t ircode_register_new (ircode_t *code) {
493 for (uint32_t i=0; i<MAX_REGISTERS; ++i) {
494 if (code->state[i] == false) {
495 code->state[i] = true;
496 return i;
497 }
498 }
499 // 0 means no registers available
500 code->error = true;
501 return 0;
502 }
503
ircode_register_push(ircode_t * code,uint32_t nreg)504 uint32_t ircode_register_push (ircode_t *code, uint32_t nreg) {
505 marray_push(uint32_t, code->registers, nreg);
506 if (ircode_register_istemp(code, nreg)) ++code->ntemps;
507
508 DEBUG_REGISTER("PUSH REGISTER %d", nreg);
509 return nreg;
510 }
511
ircode_register_first_temp_available(ircode_t * code)512 uint32_t ircode_register_first_temp_available (ircode_t *code) {
513 for (uint32_t i=0; i<MAX_REGISTERS; ++i) {
514 if (code->state[i] == false) {
515 return i;
516 }
517 }
518 // 0 means no registers available
519 code->error = true;
520 return 0;
521 }
522
ircode_register_push_temp_protected(ircode_t * code)523 uint32_t ircode_register_push_temp_protected (ircode_t *code) {
524 uint32_t value = ircode_register_push_temp(code);
525 ircode_register_temp_protect(code, value);
526 return value;
527 }
528
ircode_register_push_temp(ircode_t * code)529 uint32_t ircode_register_push_temp (ircode_t *code) {
530 uint32_t value = ircode_register_new(code);
531 marray_push(uint32_t, code->registers, value);
532 if (value > code->maxtemp) {code->maxtemp = value; ++code->ntemps;}
533
534 DEBUG_REGISTER("PUSH TEMP REGISTER %d", value);
535 return value;
536 }
537
ircode_register_pop(ircode_t * code)538 uint32_t ircode_register_pop (ircode_t *code) {
539 return ircode_register_pop_context_protect(code, false);
540 }
541
ircode_register_clear(ircode_t * code,uint32_t nreg)542 void ircode_register_clear (ircode_t *code, uint32_t nreg) {
543 if (nreg == REGISTER_ERROR) return;
544 // cleanup busy mask only if it is a temp register
545 if (nreg >= code->nlocals) code->state[nreg] = false;
546 }
547
ircode_register_set(ircode_t * code,uint32_t nreg)548 void ircode_register_set (ircode_t *code, uint32_t nreg) {
549 if (nreg == REGISTER_ERROR) return;
550 // set busy mask only if it is a temp register
551 if (nreg >= code->nlocals) code->state[nreg] = true;
552 }
553
ircode_register_last(ircode_t * code)554 uint32_t ircode_register_last (ircode_t *code) {
555 if (marray_size(code->registers) == 0) return REGISTER_ERROR;
556 return (uint32_t)marray_last(code->registers);
557 }
558
ircode_register_istemp(ircode_t * code,uint32_t nreg)559 bool ircode_register_istemp (ircode_t *code, uint32_t nreg) {
560 return (nreg >= (uint32_t)code->nlocals);
561 }
562
ircode_register_dump(ircode_t * code)563 void ircode_register_dump (ircode_t *code) {
564 uint32_t n = (uint32_t)marray_size(code->registers);
565 if (n == 0) printf("EMPTY\n");
566 for (uint32_t i=0; i<n; ++i) {
567 uint32_t value = marray_get(code->registers, i);
568 printf("[%d]\t%d\n", i, value);
569 }
570 }
571
ircode_register_count(ircode_t * code)572 uint32_t ircode_register_count (ircode_t *code) {
573 return (uint32_t)marray_size(code->registers);
574 }
575
576 // MARK: -
577
ircode_register_temp_protect(ircode_t * code,uint32_t nreg)578 void ircode_register_temp_protect (ircode_t *code, uint32_t nreg) {
579 code->skipclear[nreg] = true;
580 DEBUG_REGISTER("SET SKIP REGISTER %d", nreg);
581 }
582
ircode_register_temp_unprotect(ircode_t * code,uint32_t nreg)583 void ircode_register_temp_unprotect (ircode_t *code, uint32_t nreg) {
584 code->skipclear[nreg] = false;
585 DEBUG_REGISTER("UNSET SKIP REGISTER %d", nreg);
586 }
587
ircode_register_temps_clear(ircode_t * code)588 void ircode_register_temps_clear (ircode_t *code) {
589 // clear all temporary registers (if not protected)
590 for (uint32_t i=code->nlocals; i<=code->maxtemp; ++i) {
591 if (!code->skipclear[i]) {
592 code->state[i] = false;
593 DEBUG_REGISTER("CLEAR TEMP REGISTER %d", i);
594 }
595 }
596 }
597