Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epidemian
GitHub Repository: epidemian/gravity
Path: blob/master/src/compiler/gravity_optimizer.c
1214 views
1
//
2
// gravity_optimizer.c
3
// gravity
4
//
5
// Created by Marco Bambini on 24/09/14.
6
// Copyright (c) 2014 CreoLabs. All rights reserved.
7
//
8
9
// Some optimizations taken from: http://www.compileroptimizations.com/
10
11
#include "gravity_hash.h"
12
#include "gravity_optimizer.h"
13
#include "gravity_opcodes.h"
14
#include "gravity_ircode.h"
15
#include "gravity_utils.h"
16
#include "gravity_value.h"
17
18
#define IS_MOVE(inst) ((inst) && (inst->op == MOVE))
19
#define IS_RET(inst) ((inst) && (inst->op == RET))
20
#define IS_NEG(inst) ((inst) && (inst->op == NEG))
21
#define IS_NUM(inst) ((inst) && (inst->op == LOADI))
22
#define IS_MATH(inst) ((inst) && (inst->op >= ADD) && (inst->op <= REM))
23
#define IS_SKIP(inst) (inst->tag == SKIP_TAG)
24
#define IS_LABEL(inst) (inst->tag == LABEL_TAG)
25
#define IS_NOTNULL(inst) (inst)
26
27
// http://www.mathsisfun.com/binary-decimal-hexadecimal-converter.html
28
#define OPCODE_SET(op,code) op = (code & 0x3F) << 26
29
#define OPCODE_SET_TWO8bit_ONE10bit(op,code,a,b,c) op = (code & 0x3F) << 26; op += (a & 0xFF) << 18; op += (b & 0xFF) << 10; op += (c & 0x3FF)
30
#define OPCODE_SET_FOUR8bit(op,a,b,c,d) op = (a & 0xFF) << 24; op += (b & 0xFF) << 16; op += (c & 0xFF) << 8; op += (d & 0xFF)
31
#define OPCODE_SET_ONE8bit_SIGN_ONE17bit(op,code,a,s,n) op = (code & 0x3F) << 26; op += (a & 0xFF) << 18; op += (s & 0x01) << 17; op += (n & 0x1FFFF)
32
#define OPCODE_SET_SIGN_ONE25bit(op,code,s,a) op = (code & 0x3F) << 26; op += (s & 0x01) << 25; op += (a & 0x1FFFFFF)
33
#define OPCODE_SET_ONE8bit_ONE18bit(op,code,a,n) op = (code & 0x3F) << 26; op += (a & 0xFF) << 18; op += (n & 0x3FFFF)
34
#define OPCODE_SET_ONE26bit(op,code,a) op = (code & 0x3F) << 26; op += (a & 0x3FFFFFF)
35
#define OPCODE_SET_THREE8bit(op,code,a,b,c) OPCODE_SET_TWO8bit_ONE10bit(op,code,a,b,c)
36
#define OPCODE_SET_ONE8bit_ONE10bit(op,code,a,b) OPCODE_SET_TWO8bit_ONE10bit(op,code,a,0,b)
37
#define OPCODE_SET_ONE8bit(op,code,a) OPCODE_SET_TWO8bit_ONE10bit(op,code,a,0,0)
38
#define OPCODE_SET_THREE8bit_ONE2bit(op,code,a,b,c,f) op =(code & 0x3F)<<26; op+=(a & 0xFF)<<18; op+=(b & 0xFF)<<10; op+=(c & 0xFF)<<2; op+=(f & 0x03)
39
40
// MARK: -
41
42
static bool hash_isequal (gravity_value_t v1, gravity_value_t v2) {
43
return (v1.n == v2.n);
44
}
45
46
static uint32_t hash_compute (gravity_value_t v) {
47
return gravity_hash_compute_int(v.n);
48
}
49
50
static void finalize_function (gravity_function_t *f) {
51
ircode_t *code = (ircode_t *)f->bytecode;
52
uint32_t ninst = 0, count = ircode_count(code);
53
uint32_t notpure = 0;
54
uint32_t *bytecode = NULL;
55
gravity_hash_t *labels = gravity_hash_create(0, hash_compute, hash_isequal, NULL, NULL);
56
57
// determine how big bytecode buffer must be
58
// and collect all LABEL instructions
59
for (uint32_t i=0; i<count; ++i) {
60
inst_t *inst = ircode_get(code, i);
61
if (IS_SKIP(inst)) continue;
62
if (IS_LABEL(inst)) {
63
// insert key inst->p1 into hash table labels with value ninst (next instruction)
64
gravity_hash_insert(labels, VALUE_FROM_INT(inst->p1), VALUE_FROM_INT(ninst));
65
continue;
66
}
67
++ninst;
68
}
69
70
// +1 is just a trick so the VM switch loop terminates with an implicit RET0 instruction (RET0 has opcode 0)
71
f->ninsts = ninst;
72
bytecode = (uint32_t *)mem_alloc((ninst+1) * sizeof(uint32_t));
73
assert(bytecode);
74
75
uint32_t j=0;
76
for (uint32_t i=0; i<count; ++i) {
77
inst_t *inst = ircode_get(code, i);
78
if (IS_SKIP(inst)) continue;
79
if (IS_LABEL(inst)) continue;
80
81
uint32_t op = 0x0;
82
switch (inst->op) {
83
case HALT:
84
case RET0:
85
case NOP:
86
OPCODE_SET(op, inst->op);
87
break;
88
89
case LOAD:
90
case STORE:
91
++notpure; // not sure here
92
case LOADS:
93
case LOADAT:
94
case STOREAT:
95
case EQQ:
96
case NEQQ:
97
case ISA:
98
case MATCH:
99
case LSHIFT:
100
case RSHIFT:
101
case BOR:
102
case BAND:
103
case BNOT:
104
case BXOR:
105
case ADD:
106
case SUB:
107
case DIV:
108
case MUL:
109
case REM:
110
case AND:
111
case OR:
112
case LT:
113
case GT:
114
case EQ:
115
case LEQ:
116
case GEQ:
117
case NEQ:
118
case NEG:
119
case NOT:
120
OPCODE_SET_TWO8bit_ONE10bit(op, inst->op, inst->p1, inst->p2, inst->p3);
121
break;
122
123
case LOADI:
124
OPCODE_SET_ONE8bit_SIGN_ONE17bit(op, inst->op, inst->p1, (inst->n < 0) ? 1 : 0, inst->n);
125
break;
126
127
case JUMPF: {
128
gravity_value_t *v = gravity_hash_lookup(labels, VALUE_FROM_INT(inst->p2));
129
assert(v); // key MUST exists!
130
uint32_t njump = (uint32_t)v->n;
131
uint32_t bflag = inst->p3;
132
OPCODE_SET_ONE8bit_SIGN_ONE17bit(op, inst->op, inst->p1, bflag, njump);
133
//OPCODE_SET_ONE8bit_ONE18bit(op, inst->op, inst->p1, njump);
134
break;
135
}
136
137
case RET:
138
OPCODE_SET_ONE8bit(op, inst->op, inst->p1);
139
break;
140
141
case JUMP: {
142
gravity_value_t *v = gravity_hash_lookup(labels, VALUE_FROM_INT(inst->p1));
143
assert(v); // key MUST exists!
144
uint32_t njump = (uint32_t)v->n;
145
OPCODE_SET_ONE26bit(op, inst->op, njump);
146
break;
147
}
148
149
case LOADG:
150
case STOREG:
151
++notpure;
152
case MOVE:
153
case LOADK:
154
OPCODE_SET_ONE8bit_ONE18bit(op, inst->op, inst->p1, inst->p2);
155
break;
156
157
case CALL:
158
OPCODE_SET_TWO8bit_ONE10bit(op, inst->op, inst->p1, inst->p2, inst->p3);
159
break;
160
161
case SETLIST:
162
OPCODE_SET_TWO8bit_ONE10bit(op, inst->op, inst->p1, inst->p2, inst->p3);
163
break;
164
165
case LOADU:
166
case STOREU:
167
++notpure;
168
OPCODE_SET_ONE8bit_ONE18bit(op, inst->op, inst->p1, inst->p2);
169
break;
170
171
case RANGENEW: {
172
uint8_t flag = (inst->tag == RANGE_INCLUDE_TAG) ? 0 : 1;
173
OPCODE_SET_THREE8bit_ONE2bit(op, inst->op, inst->p1, inst->p2, inst->p3, flag);
174
break;
175
}
176
case MAPNEW:
177
case LISTNEW:
178
OPCODE_SET_ONE8bit_ONE18bit(op, inst->op, inst->p1, inst->p2);
179
break;
180
181
case SWITCH:
182
assert(0);
183
break;
184
185
case CLOSURE:
186
case CLOSE:
187
OPCODE_SET_ONE8bit_ONE18bit(op, inst->op, inst->p1, inst->p2);
188
break;
189
190
case RESERVED1:
191
case RESERVED2:
192
case RESERVED3:
193
case RESERVED4:
194
case RESERVED5:
195
case RESERVED6:
196
assert(0);
197
break;
198
}
199
200
// store encoded instruction
201
bytecode[j++] = op;
202
}
203
204
ircode_free(code);
205
gravity_hash_free(labels);
206
207
f->bytecode = bytecode;
208
f->purity = (notpure == 0) ? 1.0 : ((float)(notpure * 100) / (float)ninst) / 100.0f;
209
}
210
211
// MARK: -
212
213
inline static bool pop1_instruction (ircode_t *code, uint32_t index, inst_t **inst1) {
214
*inst1 = NULL;
215
216
for (int32_t i=index-1; i>=0; --i) {
217
inst_t *inst = ircode_get(code, i);
218
if ((inst != NULL) && (inst->tag != SKIP_TAG)) {
219
*inst1 = inst;
220
return true;
221
}
222
}
223
224
return false;
225
}
226
227
inline static bool pop2_instructions (ircode_t *code, uint32_t index, inst_t **inst1, inst_t **inst2) {
228
*inst1 = NULL;
229
*inst2 = NULL;
230
231
for (int32_t i=index-1; i>=0; --i) {
232
inst_t *inst = ircode_get(code, i);
233
if ((inst != NULL) && (inst->tag != SKIP_TAG)) {
234
if (*inst1 == NULL) *inst1 = inst;
235
else if (*inst2 == NULL) {
236
*inst2 = inst;
237
return true;
238
}
239
}
240
}
241
242
return false;
243
}
244
245
inline static inst_t *current_instruction (ircode_t *code, uint32_t i) {
246
while (1) {
247
inst_t *inst = ircode_get(code, i);
248
if (inst == NULL) return NULL;
249
if (inst->tag != SKIP_TAG) return inst;
250
++i;
251
}
252
253
return NULL;
254
}
255
256
// MARK: -
257
258
static bool optimize_const_instruction (inst_t *inst, inst_t *inst1, inst_t *inst2) {
259
// select type algorithm:
260
// two numeric types are supported here, int64 or double
261
// if types are equals then set the first one
262
// if types are not equals then set to double
263
optag_t type;
264
double d = 0.0, d1 = 0.0, d2 = 0.0;
265
int64_t n = 0, n1 = 0, n2 = 0;
266
267
// compute types
268
if (inst1->tag == inst2->tag) type = inst1->tag;
269
else type = DOUBLE_TAG;
270
271
// compute operands
272
if (type == DOUBLE_TAG) {
273
d1 = (inst1->tag == INT_TAG) ? (double)inst1->n : inst1->d;
274
d2 = (inst2->tag == INT_TAG) ? (double)inst2->n : inst2->d;
275
} else {
276
n1 = (inst1->tag == INT_TAG) ? inst1->n : (int64_t)inst1->d;
277
n2 = (inst2->tag == INT_TAG) ? inst2->n : (int64_t)inst2->d;
278
}
279
280
// perform operation
281
switch (inst->op) {
282
case ADD:
283
if (type == DOUBLE_TAG) d = d1 + d2;
284
else n = n1 + n2;
285
break;
286
287
case SUB:
288
if (type == DOUBLE_TAG) d = d1 - d2;
289
else n = n1 - n2;
290
break;
291
292
case MUL:
293
if (type == DOUBLE_TAG) d = d1 * d2;
294
else n = n1 * n2;
295
break;
296
297
case DIV:
298
// don't optimize in case of division by 0
299
if (d2 == 0) return false;
300
if (type == DOUBLE_TAG) d = d1 / d2;
301
else n = n1 / n2;
302
break;
303
304
case REM:
305
if (type == DOUBLE_TAG) d = (int64_t)d1 % (int64_t)d2;
306
else n = n1 % n2;
307
break;
308
309
default:
310
assert(0);
311
}
312
313
// adjust IRCODE
314
inst_setskip(inst1);
315
inst_setskip(inst2);
316
317
// convert an ADD instruction to a LOADI instruction
318
// ADD A B C => R(A) = R(B) + R(C)
319
// LOADI A B => R(A) = N
320
inst->op = LOADI;
321
inst->tag = type;
322
inst->p2 = inst->p3 = 0;
323
if (type == DOUBLE_TAG) inst->d = d;
324
else inst->n = n;
325
326
return true;
327
}
328
329
static bool optimize_neg_instruction (ircode_t *code, inst_t *inst, uint32_t i) {
330
inst_t *inst1 = NULL;
331
pop1_instruction(code, i, &inst1);
332
if (inst1 == NULL) return false;
333
if (inst1->op != LOADI) return false;
334
if (inst1->p1 != inst->p2) return false;
335
if (!ircode_register_istemp(code, inst1->p1)) return false;
336
337
uint64_t n = inst1->n;
338
if (n>131072) return false;
339
inst1->p1 = inst->p2;
340
inst1->n = -n;
341
inst_setskip(inst);
342
return true;
343
}
344
345
static bool optimize_math_instruction (ircode_t *code, inst_t *inst, uint32_t i) {
346
uint8_t count = opcode_numop(inst->op) - 1;
347
inst_t *inst1 = NULL, *inst2 = NULL;
348
bool flag = false;
349
350
if (count == 2) {
351
pop2_instructions(code, i, &inst2, &inst1);
352
if (IS_NUM(inst1) && IS_NUM(inst2)) flag = optimize_const_instruction(inst, inst1, inst2);
353
354
// process inst2
355
if (IS_MOVE(inst2)) {
356
bool b1 = ircode_register_istemp(code, inst->p3);
357
bool b2 = ((inst2) && ircode_register_istemp(code, inst2->p1));
358
if ((b1) && (b2) && (inst->p3 == inst2->p1)) {
359
inst->p3 = inst2->p2;
360
inst_setskip(inst2);
361
flag = true;
362
}
363
}
364
365
// process inst1
366
if (IS_MOVE(inst1)) {
367
bool b1 = ircode_register_istemp(code, inst->p2);
368
bool b2 = ((inst1) && ircode_register_istemp(code, inst1->p1));
369
if ((b1) && (b2) && (inst->p2 == inst1->p1)) {
370
inst->p2 = inst1->p2;
371
inst_setskip(inst1);
372
flag = true;
373
}
374
}
375
376
}
377
else {
378
pop1_instruction(code, i, &inst1);
379
if (IS_NUM(inst1)) flag = optimize_const_instruction(inst, inst1, NULL);
380
}
381
382
return flag;
383
}
384
385
static bool optimize_move_instruction (ircode_t *code, inst_t *inst, uint32_t i) {
386
return false;
387
inst_t *inst1 = NULL;
388
pop1_instruction(code, i, &inst1);
389
if (inst1 == NULL) return false;
390
if ((inst1->op != LOADI) && (inst1->op != LOADG) && (inst1->op != LOADK)) return false;
391
392
bool b1 = ircode_register_istemp(code, inst->p2);
393
bool b2 = ((inst1) && ircode_register_istemp(code, inst1->p1));
394
395
if ((b1) && (b2) && (inst->p2 == inst1->p1)) {
396
inst1->p1 = inst->p1;
397
inst_setskip(inst);
398
return true;
399
}
400
401
return false;
402
}
403
404
static bool optimize_return_instruction (ircode_t *code, inst_t *inst, uint32_t i) {
405
inst_t *inst1 = NULL;
406
pop1_instruction(code, i, &inst1);
407
408
if (!ircode_register_istemp(code, inst->p1)) return false;
409
if ((IS_MOVE(inst1)) && (inst->p1 == inst1->p1)) {
410
inst->p1 = inst1->p2;
411
inst_setskip(inst1);
412
return true;
413
}
414
415
return false;
416
}
417
418
static bool optimize_num_instruction (inst_t *inst, gravity_function_t *f) {
419
420
// double values always added to constant pool
421
bool add_cpool = (inst->tag == DOUBLE_TAG);
422
423
// LOADI is a 32bit instruction
424
// 32 - 6 (OPCODE) - 8 (register) - 1 bit sign = 17
425
// range is from MAX_INLINE_INT-1 to MAX_INLINE_INT
426
// so max/min values are in the range -(2^17)-1/+2^17
427
// 2^17 = 131072 (MAX_INLINE_INT)
428
if (inst->tag == INT_TAG) {
429
int64_t n = inst->n;
430
add_cpool = ((n < -MAX_INLINE_INT + 1) || (n > MAX_INLINE_INT));
431
}
432
433
if (add_cpool) {
434
uint16_t index = 0;
435
if (inst->tag == INT_TAG) {
436
int64_t n = inst->n;
437
index = gravity_function_cpool_add(NULL, f, VALUE_FROM_INT(n));
438
} else {
439
// always add floating point values as double in constant pool (then VM will be configured to interpret it as float or double)
440
index = gravity_function_cpool_add(NULL, f, VALUE_FROM_FLOAT(inst->d));
441
}
442
443
// replace LOADI with a LOADK instruction
444
inst->op = LOADK;
445
inst->p2 = index;
446
inst->tag = NO_TAG;
447
}
448
449
return true;
450
}
451
452
// MARK: -
453
454
gravity_function_t *gravity_optimizer(gravity_function_t *f) {
455
if (f->bytecode == NULL) return f;
456
457
ircode_t *code = (ircode_t *)f->bytecode;
458
uint32_t count = ircode_count(code);
459
460
f->ntemps = ircode_ntemps(code);
461
462
loop_neg:
463
for (uint32_t i=0; i<count; ++i) {
464
inst_t *inst = current_instruction(code, i);
465
if (IS_NEG(inst)) {
466
bool b = optimize_neg_instruction (code, inst, i);
467
if (b) goto loop_neg;
468
}
469
}
470
471
loop_math:
472
for (uint32_t i=0; i<count; ++i) {
473
inst_t *inst = current_instruction(code, i);
474
if (IS_MATH(inst)) {
475
bool b = optimize_math_instruction (code, inst, i);
476
if (b) goto loop_math;
477
}
478
}
479
480
loop_move:
481
for (uint32_t i=0; i<count; ++i) {
482
inst_t *inst = current_instruction(code, i);
483
if (IS_MOVE(inst)) {
484
bool b = optimize_move_instruction (code, inst, i);
485
if (b) goto loop_move;
486
}
487
}
488
489
loop_ret:
490
for (uint32_t i=0; i<count; ++i) {
491
inst_t *inst = current_instruction(code, i);
492
if (IS_RET(inst)) {
493
bool b = optimize_return_instruction (code, inst, i);
494
if (b) goto loop_ret;
495
}
496
}
497
498
for (uint32_t i=0; i<count; ++i) {
499
inst_t *inst = current_instruction(code, i);
500
if (IS_NUM(inst)) optimize_num_instruction (inst, f);
501
}
502
503
// dump optimized version
504
#if GRAVITY_BYTECODE_DEBUG
505
gravity_function_dump(f, ircode_dump);
506
#endif
507
508
// finalize function
509
finalize_function(f);
510
511
return f;
512
}
513
514