Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epidemian
GitHub Repository: epidemian/gravity
Path: blob/master/src/compiler/gravity_codegen.c
1214 views
1
//
2
// gravity_codegen.c
3
// gravity
4
//
5
// Created by Marco Bambini on 09/10/14.
6
// Copyright (c) 2014 CreoLabs. All rights reserved.
7
//
8
9
#include "gravity_codegen.h"
10
#include "gravity_symboltable.h"
11
#include "gravity_optimizer.h"
12
#include "gravity_visitor.h"
13
#include "gravity_ircode.h"
14
#include "gravity_utils.h"
15
#include "gravity_array.h"
16
#include "gravity_hash.h"
17
18
typedef marray_t(gnode_class_decl_t *) gnode_class_r;
19
struct codegen_t {
20
gravity_object_r context;
21
gnode_class_r superfix;
22
gravity_vm *vm;
23
};
24
typedef struct codegen_t codegen_t;
25
26
#define CONTEXT_PUSH(x) marray_push(gravity_object_t*, ((codegen_t *)self->data)->context, (gravity_object_t*)x)
27
#define CONTEXT_POP() marray_pop(((codegen_t *)self->data)->context)
28
#define CONTEXT_GET() marray_last(((codegen_t *)self->data)->context)
29
#define CONTEXT_IS_MODULE(x) ((OBJECT_ISA_FUNCTION(x)) && (string_cmp(((gravity_function_t *)x)->identifier, INITMODULE_NAME) == 0))
30
31
#define DECLARE_CONTEXT() gravity_object_t *context_object = CONTEXT_GET();
32
#define DECLARE_FUNCTION_CONTEXT() DECLARE_CONTEXT(); \
33
assert(OBJECT_ISA_FUNCTION(context_object)); \
34
gravity_function_t *context_function = (gravity_function_t *)context_object;
35
#define DECLARE_CLASS_CONTEXT() DECLARE_CONTEXT(); \
36
assert(OBJECT_ISA_CLASS(context_object)); \
37
gravity_class_t *context_class = (gravity_class_t *)context_object;
38
#define DECLARE_CODE() DECLARE_FUNCTION_CONTEXT(); \
39
ircode_t *code = (ircode_t *)context_function->bytecode;
40
41
#define IS_IMPLICIT_SELF(_expr) (NODE_ISA(_expr, NODE_IDENTIFIER_EXPR) && \
42
(((gnode_identifier_expr_t *)_expr)->location.type == LOCATION_CLASS_IVAR_SAME) && \
43
(((gnode_identifier_expr_t *)_expr)->location.nup == 0) && \
44
(((gnode_identifier_expr_t *)_expr)->location.index == UINT16_MAX))
45
46
#define IS_SUPER(_expr) (NODE_ISA(_expr, NODE_KEYWORD_EXPR) && (((gnode_keyword_expr_t *)_expr)->base.token.type == TOK_KEY_SUPER))
47
48
#define GET_VM() ((codegen_t *)self->data)->vm
49
50
#define IS_LAST_LOOP(n1,n2) (n1+1==n2)
51
52
#if 1
53
#define CODEGEN_COUNT_REGISTERS(_n) uint32_t _n = ircode_register_count(code)
54
#define CODEGEN_ASSERT_REGISTERS(_n1,_n2,_v) assert(_n2 -_n1 == (_v))
55
#else
56
#define CODEGEN_COUNT_REGISTERS(_n)
57
#define CODEGEN_ASSERT_REGISTERS(_n1,_n2,_v)
58
#endif
59
60
// MARK: -
61
62
static void report_error (gvisitor_t *self, gnode_t *node, const char *format, ...) {
63
// increment internal error counter
64
++self->nerr;
65
66
// get error callback (if any)
67
void *data = (self->delegate) ? ((gravity_delegate_t *)self->delegate)->xdata : NULL;
68
gravity_error_callback error_fn = (self->delegate) ? ((gravity_delegate_t *)self->delegate)->error_callback : NULL;
69
70
// build error message
71
char buffer[1024];
72
va_list arg;
73
if (format) {
74
va_start (arg, format);
75
vsnprintf(buffer, sizeof(buffer), format, arg);
76
va_end (arg);
77
}
78
79
// setup error struct
80
error_desc_t error_desc = {
81
.code = 0,
82
.lineno = node->token.lineno,
83
.colno = node->token.colno,
84
.fileid = node->token.fileid,
85
.offset = node->token.position
86
};
87
88
// finally call error callback
89
if (error_fn) error_fn(GRAVITY_ERROR_SEMANTIC, buffer, error_desc, data);
90
else printf("%s\n", buffer);
91
}
92
93
// MARK: -
94
static opcode_t token2opcode(gtoken_t op) {
95
switch (op) {
96
// BIT
97
case TOK_OP_SHIFT_LEFT: return LSHIFT;
98
case TOK_OP_SHIFT_RIGHT: return RSHIFT;
99
case TOK_OP_BIT_NOT: return BNOT;
100
case TOK_OP_BIT_AND: return BAND;
101
case TOK_OP_BIT_OR: return BOR;
102
case TOK_OP_BIT_XOR: return BXOR;
103
104
// MATH
105
case TOK_OP_ADD: return ADD;
106
case TOK_OP_SUB: return SUB;
107
case TOK_OP_DIV: return DIV;
108
case TOK_OP_MUL: return MUL;
109
case TOK_OP_REM: return REM;
110
// NEG not handled here
111
112
// COMPARISON
113
case TOK_KEY_ISA: return ISA;
114
case TOK_OP_LESS: return LT;
115
case TOK_OP_GREATER: return GT;
116
case TOK_OP_LESS_EQUAL: return LEQ;
117
case TOK_OP_GREATER_EQUAL: return GEQ;
118
case TOK_OP_ISEQUAL: return EQ;
119
case TOK_OP_ISNOTEQUAL: return NEQ;
120
case TOK_OP_ISIDENTICAL: return EQQ;
121
case TOK_OP_ISNOTIDENTICAL: return NEQQ;
122
case TOK_OP_PATTERN_MATCH: return MATCH;
123
124
// LOGICAL
125
case TOK_OP_AND: return AND;
126
case TOK_OP_NOT: return NOT;
127
case TOK_OP_OR: return OR;
128
129
default: assert(0); break; // should never reach this point
130
}
131
}
132
133
#if 0
134
static gravity_value_t literal2value (gnode_literal_expr_t *node) {
135
if (node->type == LITERAL_STRING) return VALUE_FROM_STRING(NULL, node->value.str, node->len);
136
if (node->type == LITERAL_FLOAT) return VALUE_FROM_FLOAT(node->value.d);
137
if (node->type == LITERAL_INT) return VALUE_FROM_INT(node->value.n64);
138
return VALUE_FROM_INT(node->value.n64); // default BOOLEAN case
139
}
140
141
static gravity_list_t *literals2list (gvisitor_t *self, gnode_r *r, uint32_t start, uint32_t stop) {
142
gravity_list_t *list = gravity_list_new(GET_VM(), stop-start);
143
144
for (uint32_t i=start; i<stop; ++i) {
145
gnode_literal_expr_t *node = (gnode_literal_expr_t *)marray_get(*r, i);
146
gravity_value_t value = literal2value(node);
147
marray_push(gravity_value_t, list->array, value);
148
}
149
150
return list;
151
}
152
153
static gravity_map_t *literals2map (gvisitor_t *self, gnode_r *r1, gnode_r *r2, uint32_t start, uint32_t stop) {
154
gravity_map_t *map = gravity_map_new(GET_VM(), stop-start);
155
156
for (uint32_t i=start; i<stop; ++i) {
157
gnode_literal_expr_t *_key = (gnode_literal_expr_t *)marray_get(*r1, i);
158
gnode_literal_expr_t *_value = (gnode_literal_expr_t *)marray_get(*r2, i);
159
160
// when here I am sure that both key and value are literals
161
// so they can be LITERAL_STRING, LITERAL_FLOAT, LITERAL_INT, LITERAL_BOOL
162
gravity_value_t key = literal2value(_key);
163
gravity_value_t value = literal2value(_value);
164
gravity_map_insert(NULL, map, key, value);
165
}
166
167
return map;
168
}
169
170
static gravity_map_t *enum2map (gvisitor_t *self, gnode_enum_decl_t *node) {
171
uint32_t count = symboltable_count(node->symtable, 0);
172
gravity_map_t *map = gravity_map_new(GET_VM(), count);
173
174
// FixMe
175
176
return map;
177
}
178
179
static bool check_literals_list (gvisitor_t *self, gnode_list_expr_t *node, bool ismap, size_t idxstart, size_t idxend, uint32_t dest) {
180
DEBUG_CODEGEN("check_literal_list_expr");
181
DECLARE_CODE();
182
183
// first check if all nodes inside this chuck are all literals
184
// and in this case apply a more efficient SETLIST variant
185
for (size_t j=idxstart; j < idxend; ++j) {
186
gnode_t *e = gnode_array_get(node->list1, j);
187
if (!gnode_is_literal(e)) return false;
188
if (ismap) {
189
// additional check on key that must be a string literal in case of a map
190
if (!gnode_is_literal_string(e)) return false;
191
e = gnode_array_get(node->list2, j);
192
if (!gnode_is_literal(e)) return false;
193
}
194
}
195
196
// emit optimized (cpoll based) version
197
gravity_value_t v;
198
199
if (ismap) {
200
gravity_map_t *map = literals2map(self, node->list1, node->list2, (uint32_t)idxstart, (uint32_t)idxend);
201
v = VALUE_FROM_OBJECT(map);
202
} else {
203
gravity_list_t *list = literals2list(self, node->list1, (uint32_t)idxstart, (uint32_t)idxend);
204
v = VALUE_FROM_OBJECT(list);
205
}
206
uint16_t index = gravity_function_cpool_add(GET_VM(), context_function, v);
207
ircode_add(code, SETLIST, dest, 0, index);
208
209
return true;
210
}
211
#endif
212
213
static uint32_t node2index(gnode_t * node) {
214
// node can be a VARIABLE declaration or a local IDENTIFIER
215
216
if (NODE_ISA(node, NODE_VARIABLE_DECL)) {
217
gnode_variable_decl_t *expr = (gnode_variable_decl_t *)node;
218
assert(gnode_array_size(expr->decls) == 1);
219
220
gnode_var_t *var = (gnode_var_t *)gnode_array_get(expr->decls, 0);
221
return var->index;
222
}
223
224
if (NODE_ISA(node, NODE_IDENTIFIER_EXPR)) {
225
gnode_identifier_expr_t *expr = (gnode_identifier_expr_t *)node;
226
assert(expr->location.type == LOCATION_LOCAL);
227
return expr->location.index;
228
}
229
230
// should never reach this point because semacheck2 should take care of the check
231
assert(0);
232
return UINT32_MAX;
233
}
234
235
static void fix_superclasses(gvisitor_t *self) {
236
// this function cannot fail because superclasses was already checked in samecheck2 so I am sure that they exist somewhere
237
codegen_t *data = (codegen_t *)self->data;
238
gnode_class_r *superfix = &data->superfix;
239
240
size_t count = gnode_array_size(superfix);
241
for (size_t i=0; i<count; ++i) {
242
gnode_class_decl_t *node = (gnode_class_decl_t *)gnode_array_get(superfix, i);
243
gnode_class_decl_t *super = (gnode_class_decl_t *)node->superclass;
244
245
gravity_class_t *c = (gravity_class_t *)node->data;
246
gravity_class_setsuper(c, (gravity_class_t *)super->data);
247
}
248
}
249
250
// MARK: - Statements -
251
252
static void visit_list_stmt (gvisitor_t *self, gnode_compound_stmt_t *node) {
253
DEBUG_CODEGEN("visit_list_stmt");
254
gnode_array_each(node->stmts, {visit(val);});
255
}
256
257
static void visit_compound_stmt (gvisitor_t *self, gnode_compound_stmt_t *node) {
258
DEBUG_CODEGEN("visit_compound_stmt");
259
gnode_array_each(node->stmts, {visit(val);});
260
if (node->nclose != UINT32_MAX) {
261
DECLARE_CODE();
262
ircode_add(code, CLOSE, node->nclose, 0, 0);
263
}
264
}
265
266
static void visit_label_stmt (gvisitor_t *self, gnode_label_stmt_t *node) {
267
DEBUG_CODEGEN("visit_label_stmt");
268
269
gtoken_t type = NODE_TOKEN_TYPE(node);
270
assert((type == TOK_KEY_DEFAULT) || (type == TOK_KEY_CASE));
271
272
if (type == TOK_KEY_DEFAULT) {visit(node->stmt);}
273
else if (type == TOK_KEY_CASE) {visit(node->expr); visit(node->stmt);}
274
}
275
276
static void visit_flow_if_stmt (gvisitor_t *self, gnode_flow_stmt_t *node) {
277
DEBUG_CODEGEN("visit_flow_if_stmt");
278
DECLARE_CODE();
279
280
/*
281
<condition>
282
if-false: goto $end
283
<then-part>
284
goto $true
285
$end:
286
<else-part>
287
$true:
288
289
TRUE := getLabel
290
FALSE := getLabel
291
compile condition
292
emit ifeq FALSE
293
compile stm1
294
emit goto TRUE
295
emit FALSE
296
compile stm2
297
emit TRUE
298
*/
299
300
uint32_t labelTrue = ircode_newlabel(code);
301
uint32_t labelFalse = ircode_newlabel(code);
302
303
visit(node->cond);
304
ircode_add(code, JUMPF, ircode_register_pop(code), labelFalse, 0);
305
306
visit(node->stmt);
307
if (node->elsestmt) ircode_add(code, JUMP, labelTrue, 0, 0);
308
309
ircode_marklabel(code, labelFalse);
310
if (node->elsestmt) {
311
visit(node->elsestmt);
312
ircode_marklabel(code, labelTrue);
313
}
314
}
315
316
static void visit_flow_switch_stmt (gvisitor_t *self, gnode_flow_stmt_t *node) {
317
DEBUG_CODEGEN("visit_flow_switch_stmt");
318
319
visit(node->cond);
320
visit(node->stmt);
321
}
322
323
static void visit_flow_stmt (gvisitor_t *self, gnode_flow_stmt_t *node) {
324
DEBUG_CODEGEN("visit_flow_stmt");
325
326
gtoken_t type = NODE_TOKEN_TYPE(node);
327
assert((type == TOK_KEY_IF) || (type == TOK_KEY_SWITCH));
328
329
if (type == TOK_KEY_IF) {
330
visit_flow_if_stmt(self, node);
331
} else if (type == TOK_KEY_SWITCH) {
332
visit_flow_switch_stmt(self, node);
333
}
334
}
335
336
static void visit_loop_while_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
337
DEBUG_CODEGEN("visit_loop_while_stmt");
338
DECLARE_CODE();
339
340
/*
341
$start: <condition>
342
if-false: goto $end
343
<body>
344
goto $start
345
$end:
346
347
START := getLabel
348
END := getLabel
349
emit START
350
compile exp
351
emit ifeq END
352
compile stm
353
emit goto START
354
emit END
355
*/
356
357
uint32_t labelTrue = ircode_newlabel(code);
358
uint32_t labelFalse = ircode_newlabel(code);
359
ircode_setlabel_true(code, labelTrue);
360
ircode_setlabel_false(code, labelFalse);
361
362
ircode_marklabel(code, labelTrue);
363
visit(node->cond);
364
ircode_add(code, JUMPF, ircode_register_pop(code), labelFalse, 0);
365
366
visit(node->stmt);
367
ircode_add(code, JUMP, labelTrue, 0, 0);
368
369
ircode_marklabel(code, labelFalse);
370
371
ircode_unsetlabel_true(code);
372
ircode_unsetlabel_false(code);
373
}
374
375
static void visit_loop_repeat_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
376
DEBUG_CODEGEN("visit_loop_repeat_stmt");
377
DECLARE_CODE();
378
379
/*
380
$start: <body>
381
<expression>
382
if-false: goto $start
383
$end:
384
*/
385
386
uint32_t labelTrue = ircode_newlabel(code);
387
uint32_t labelFalse = ircode_newlabel(code); // end label is necessary to handle optional break statement
388
ircode_setlabel_true(code, labelTrue);
389
ircode_setlabel_false(code, labelFalse);
390
391
ircode_marklabel(code, labelTrue);
392
visit(node->stmt);
393
visit(node->expr);
394
ircode_add(code, JUMPF, ircode_register_pop(code), labelFalse, 0);
395
ircode_add(code, JUMP, labelTrue, 0, 0);
396
397
ircode_marklabel(code, labelFalse);
398
399
ircode_unsetlabel_true(code);
400
ircode_unsetlabel_false(code);
401
}
402
403
static void visit_loop_for_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
404
// https://www.natashatherobot.com/swift-alternatives-to-c-style-for-loops/
405
406
DEBUG_CODEGEN("visit_loop_for_stmt");
407
DECLARE_CODE();
408
409
// FOR loop is transformed to a WHILE loop
410
//
411
// from:
412
// for (cond in expr) {
413
// stmp;
414
// }
415
//
416
// to:
417
// {
418
// var $expr = expr;
419
// var $value = $expr.iterate(null);
420
// while ($value) {
421
// cond = $expr.next($value);
422
// stmp;
423
// $value = $expr.iterate($value);
424
// }
425
// }
426
427
uint32_t $expr = ircode_register_push_temp(code); // ++TEMP => 1
428
uint32_t $value = ircode_register_push_temp(code); // ++TEMP => 2
429
430
uint16_t iterate_idx = gravity_function_cpool_add(GET_VM(), context_function, VALUE_FROM_CSTRING(NULL, ITERATOR_INIT_FUNCTION));
431
uint16_t next_idx = gravity_function_cpool_add(GET_VM(), context_function, VALUE_FROM_CSTRING(NULL, ITERATOR_NEXT_FUNCTION));
432
uint32_t cond_idx = node2index(node->cond);
433
434
// generate code for $expr = expr (so expr is only evaluated once)
435
visit(node->expr);
436
uint32_t once_expr = ircode_register_pop(code);
437
ircode_add(code, MOVE, $expr, once_expr, 0);
438
439
// generate code for $value = $expr.iterate(null);
440
uint32_t iterate_fn = ircode_register_push_temp(code); // ++TEMP => 3
441
ircode_add(code, LOADK, iterate_fn, iterate_idx, 0);
442
ircode_add(code, LOAD, iterate_fn, $expr, iterate_fn);
443
444
uint32_t next_fn = ircode_register_push_temp(code); // ++TEMP => 4
445
ircode_add(code, LOADK, next_fn, next_idx, 0);
446
ircode_add(code, LOAD, next_fn, $expr, next_fn);
447
448
uint32_t temp1 = ircode_register_push_temp(code); // ++TEMP => 5
449
ircode_add(code, MOVE, temp1, iterate_fn, 0);
450
uint32_t temp2 = ircode_register_push_temp(code); // ++TEMP => 6
451
ircode_add(code, MOVE, temp2, $expr, 0);
452
temp2 = ircode_register_push_temp(code); // ++TEMP => 7
453
ircode_add(code, LOADK, temp2, CPOOL_VALUE_NULL, 0);
454
ircode_add(code, CALL, $value, temp1, 2);
455
ircode_register_pop(code); // --TEMP => 6
456
ircode_register_pop(code); // --TEMP => 5
457
ircode_register_pop(code); // --TEMP => 4
458
459
// while code
460
uint32_t labelTrue = ircode_newlabel(code);
461
uint32_t labelFalse = ircode_newlabel(code);
462
ircode_setlabel_true(code, labelTrue);
463
ircode_setlabel_false(code, labelFalse);
464
465
ircode_marklabel(code, labelTrue);
466
ircode_add(code, JUMPF, $value, labelFalse, 1); // flag JUMPF instruction to check ONLY BOOL values
467
468
// cond = $expr.next($value);
469
// cond is a local variable
470
temp1 = ircode_register_push_temp(code); // ++TEMP => 5
471
ircode_add(code, MOVE, temp1, next_fn, 0);
472
temp2 = ircode_register_push_temp(code); // ++TEMP => 6
473
ircode_add(code, MOVE, temp2, $expr, 0);
474
temp2 = ircode_register_push_temp(code); // ++TEMP => 7
475
ircode_add(code, MOVE, temp2, $value, 0);
476
ircode_add(code, CALL, cond_idx, temp1, 2);
477
478
// process statement
479
visit(node->stmt);
480
481
// pop next_fn temp register AFTER user code because function ptr must be protected inside loop
482
ircode_register_pop(code); // --TEMP => 6
483
ircode_register_pop(code); // --TEMP => 5
484
ircode_register_pop(code); // --TEMP => 4
485
486
// update $value for the next check
487
// $value = $expr.iterate($value);
488
temp1 = ircode_register_push_temp(code); // ++TEMP => 5
489
ircode_add(code, MOVE, temp1, iterate_fn, 0);
490
temp2 = ircode_register_push_temp(code); // ++TEMP => 6
491
ircode_add(code, MOVE, temp2, $expr, 0);
492
temp2 = ircode_register_push_temp(code); // ++TEMP => 7
493
ircode_add(code, MOVE, temp2, $value, 0);
494
ircode_add(code, CALL, $value, temp1, 2);
495
ircode_register_pop(code); // --TEMP => 6
496
ircode_register_pop(code); // --TEMP => 5
497
ircode_register_pop(code); // --TEMP => 4
498
499
ircode_add(code, JUMP, labelTrue, 0, 0);
500
501
ircode_marklabel(code, labelFalse);
502
503
ircode_unsetlabel_true(code);
504
ircode_unsetlabel_false(code);
505
506
ircode_register_pop(code); // --TEMP => 3
507
ircode_register_pop(code); // --TEMP => 2
508
ircode_register_pop(code); // --TEMP => 1
509
ircode_register_pop(code); // --TEMP => 0
510
511
if (node->nclose != UINT32_MAX) {
512
ircode_add(code, CLOSE, node->nclose, 0, 0);
513
}
514
}
515
516
static void visit_loop_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
517
DEBUG_CODEGEN("visit_loop_stmt");
518
519
gtoken_t type = NODE_TOKEN_TYPE(node);
520
assert((type == TOK_KEY_WHILE) || (type == TOK_KEY_REPEAT) || (type == TOK_KEY_FOR));
521
522
if (type == TOK_KEY_WHILE) {
523
visit_loop_while_stmt(self, node);
524
} else if (type == TOK_KEY_REPEAT) {
525
visit_loop_repeat_stmt(self, node);
526
} else if (type == TOK_KEY_FOR) {
527
visit_loop_for_stmt(self, node);
528
}
529
}
530
531
static void visit_jump_stmt (gvisitor_t *self, gnode_jump_stmt_t *node) {
532
DEBUG_CODEGEN("visit_jump_stmt");
533
DECLARE_CODE();
534
535
gtoken_t type = NODE_TOKEN_TYPE(node);
536
assert((type == TOK_KEY_BREAK) || (type == TOK_KEY_CONTINUE) || (type == TOK_KEY_RETURN));
537
538
if (type == TOK_KEY_BREAK) {
539
uint32_t label = ircode_getlabel_false(code);
540
ircode_add(code, JUMP, label, 0, 0); // goto $end;
541
} else if (type == TOK_KEY_CONTINUE) {
542
uint32_t label = ircode_getlabel_true(code);
543
ircode_add(code, JUMP, label, 0, 0); // goto $start;
544
} else if (type == TOK_KEY_RETURN) {
545
if (node->expr) {
546
visit(node->expr);
547
ircode_add(code, RET, ircode_register_pop(code), 0, 0);
548
} else {
549
ircode_add(code, RET0, 0, 0, 0);
550
}
551
}
552
}
553
554
static void visit_empty_stmt (gvisitor_t *self, gnode_empty_stmt_t *node) {
555
#pragma unused(self, node)
556
DEBUG_CODEGEN("visit_empty_stmt");
557
558
DECLARE_CODE();
559
ircode_add(code, NOP, 0, 0, 0);
560
}
561
562
// MARK: - Declarations -
563
564
static void store_declaration (gvisitor_t *self, gravity_object_t *obj, bool is_static, gnode_function_decl_t *node) {
565
DEBUG_CODEGEN("store_object_declaration");
566
assert(obj);
567
568
DECLARE_CONTEXT();
569
bool is_module = CONTEXT_IS_MODULE(context_object);
570
bool is_class = OBJECT_ISA_CLASS(context_object);
571
bool is_local = ((is_module == false) && (is_class == false));
572
if (is_static) assert(is_class); // static makes sense only for class objects
573
574
if (is_local || is_module) {
575
gravity_function_t *context_function = (gravity_function_t *)context_object;
576
ircode_t *code = (ircode_t *)context_function->bytecode;
577
578
// add object to cpool and get its index
579
uint16_t index = gravity_function_cpool_add(NULL, context_function, VALUE_FROM_OBJECT(obj));
580
581
// if it is a function then generate a CLOSURE opcode instead of LOADK
582
if (OBJECT_ISA_FUNCTION(obj)) {
583
gravity_function_t *f = (gravity_function_t *)obj;
584
uint32_t regnum = ircode_register_push_temp(code);
585
ircode_add(code, CLOSURE, regnum, index, 0);
586
uint32_t upindex = 0;
587
for (uint16_t i=0; i<f->nupvalues; ++i) {
588
gupvalue_t *upvalue = (gupvalue_t *)gnode_array_get(node->uplist, i);
589
uint32_t opindex = (upvalue->is_direct) ? upvalue->index : upindex++;
590
ircode_add(code, MOVE, opindex, (upvalue->is_direct) ? 1 : 0, 0);
591
}
592
} else {
593
ircode_add_constant(code, index);
594
}
595
596
if (is_module && obj->identifier) {
597
index = gravity_function_cpool_add(GET_VM(), context_function, VALUE_FROM_CSTRING(NULL, obj->identifier));
598
ircode_add(code, STOREG, ircode_register_pop(code), index, 0);
599
}
600
601
return;
602
}
603
604
if (is_class) {
605
gravity_class_t *context_class = (gravity_class_t *)context_object;
606
context_class = (is_static) ? context_class->objclass : context_class;
607
gravity_class_bind(context_class, obj->identifier, VALUE_FROM_OBJECT(obj));
608
return;
609
}
610
611
// should never reach this point
612
assert(0);
613
}
614
615
// used ONLY by CODEGEN
616
static gravity_function_t *class_lookup_nosuper (gravity_class_t *c, const char *name) {
617
STATICVALUE_FROM_STRING(key, name, strlen(name));
618
gravity_value_t *v = gravity_hash_lookup(c->htable, key);
619
return (v) ? (gravity_function_t*)v->p : NULL;
620
}
621
622
static void process_constructor (gvisitor_t *self, gravity_class_t *c) {
623
DEBUG_CODEGEN("process_constructor");
624
625
// $init is an internal function used to initialize instance variables to a default value
626
// in case of subclasses USER is RESPONSIBLE to call super.init();
627
// in case of subclasses COMPILER is RESPONSIBLE to create the appropriate $init call chain
628
629
// check internal $init function
630
gravity_function_t *internal_init_function = class_lookup_nosuper(c, CLASS_INTERNAL_INIT_NAME);
631
632
// check for user constructor function
633
gravity_function_t *constructor_function = class_lookup_nosuper(c, CLASS_CONSTRUCTOR_NAME);
634
635
// build appropriate $init function
636
gravity_class_t *super = c->superclass;
637
uint32_t ninit = 2;
638
while (super) {
639
gravity_function_t *super_init = class_lookup_nosuper(super, CLASS_INTERNAL_INIT_NAME);
640
if (super_init) {
641
// copy super init code to internal_init code
642
if (!internal_init_function) internal_init_function = gravity_function_new(NULL, CLASS_INTERNAL_INIT_NAME, 1, 0, 0, ircode_create(1));
643
644
// build unique internal init name ($init2, $init3 and so on)
645
char name[256];
646
snprintf(name, sizeof(name), "%s%d", CLASS_INTERNAL_INIT_NAME, ninit++);
647
648
// add new internal init to class and call it from main $init function
649
// super_init should not be duplicated here because class hash table values are not freed (only keys are freed)
650
gravity_class_bind(c, name, VALUE_FROM_OBJECT(super_init));
651
uint16_t index = gravity_function_cpool_add(NULL, internal_init_function, VALUE_FROM_CSTRING(GET_VM(), name));
652
ircode_patch_init((ircode_t *)internal_init_function->bytecode, index);
653
}
654
super = super->superclass;
655
}
656
657
// 4 cases to handle:
658
659
// 1. internal_init and constuctor are not present, so nothing to do here
660
if ((!internal_init_function) && (!constructor_function)) goto check_meta;
661
662
// // 2. internal init is present and constructor is not used
663
// if ((internal_init_function) && (!constructor_function)) {
664
// // add a RET0 command
665
// ircode_t *code = (ircode_t *)internal_init_function->bytecode;
666
// ircode_add(code, RET0, 0, 0, 0);
667
//
668
// // bind internal init as constructor
669
// gravity_class_bind(c, CLASS_CONSTRUCTOR_NAME, VALUE_FROM_OBJECT(internal_init_function));
670
// gravity_object_setenclosing((gravity_object_t *)internal_init_function, (gravity_object_t *)c);
671
// goto process_funcs;
672
// }
673
674
// 3. internal init is present so constructor is mandatory
675
if (internal_init_function) {
676
// convert ircode to bytecode for $init special function and add a RET0 command
677
ircode_t *code = (ircode_t *)internal_init_function->bytecode;
678
ircode_add(code, RET0, 0, 0, 0);
679
680
if (constructor_function == NULL) {
681
constructor_function = gravity_function_new(NULL, CLASS_CONSTRUCTOR_NAME, 1, 0, 2, ircode_create(1));
682
ircode_t *code2 = (ircode_t *)constructor_function->bytecode;
683
ircode_add_skip(code2); // LOADK
684
ircode_add_skip(code2); // LOAD
685
ircode_add_skip(code2); // MOVE
686
ircode_add_skip(code2); // CALL
687
gravity_class_bind(c, CLASS_CONSTRUCTOR_NAME, VALUE_FROM_OBJECT(constructor_function));
688
}
689
}
690
691
// 4. constructor is present so internal init is optional
692
if (constructor_function) {
693
// add an implicit RET 0 (RET self) to the end of the constructor
694
ircode_t *code = (ircode_t *)constructor_function->bytecode;
695
ircode_add(code, RET, 0, 0, 0);
696
697
if (internal_init_function) {
698
// if an internal init function is present ($init) then add a call to it as a first instruction
699
uint16_t index = gravity_function_cpool_add(GET_VM(), constructor_function, VALUE_FROM_CSTRING(NULL, CLASS_INTERNAL_INIT_NAME));
700
701
// load constant
702
uint32_t dest = ircode_register_push_temp(code);
703
ircode_set_index(0, code, LOADK, dest, index, 0);
704
705
// load from lookup
706
ircode_set_index(1, code, LOAD, dest, 0, dest);
707
708
// prepare parameters
709
uint32_t dest2 = ircode_register_push_temp(code);
710
ircode_set_index(2, code, MOVE, dest2, 0, 0);
711
ircode_register_pop(code);
712
713
// execute call
714
ircode_set_index(3, code, CALL, dest, dest, 1);
715
}
716
}
717
718
//process_funcs:
719
if (internal_init_function) gravity_optimizer(internal_init_function);
720
if (constructor_function) gravity_optimizer(constructor_function);
721
722
check_meta:
723
// recursively process constructor but stop when object or class class is found, otherwise an infinite loop is triggered
724
if ((c->objclass) && (c->objclass->isa) && (c->objclass->isa != c->objclass->objclass)) process_constructor(self, c->objclass);
725
}
726
727
static void process_getter_setter (gvisitor_t *self, gnode_var_t *p, gravity_class_t *c) {
728
gnode_compound_stmt_t *expr = (gnode_compound_stmt_t *)p->expr;
729
gnode_function_decl_t *getter = (gnode_function_decl_t *)gnode_array_get(expr->stmts, 0);
730
gnode_function_decl_t *setter = (gnode_function_decl_t *)gnode_array_get(expr->stmts, 1);
731
732
gnode_function_decl_t *f1[2] = {getter, setter};
733
gravity_function_t *f2[2] = {NULL, NULL};
734
735
for (uint16_t i=0; i<2; ++i) {
736
gnode_function_decl_t *node = f1[i];
737
if (!node) continue;
738
739
// create gravity function
740
uint16_t nparams = (uint16_t)(node->params) ? marray_size(*node->params) : 0;
741
f2[i] = gravity_function_new(NULL, NULL, nparams, node->nlocals, 0, ircode_create(node->nlocals+nparams));
742
743
// process inner block
744
CONTEXT_PUSH(f2[i]);
745
gnode_compound_stmt_t *block = node->block;
746
if (block) {gnode_array_each(block->stmts, {visit(val);});}
747
CONTEXT_POP();
748
749
gravity_optimizer(f2[i]);
750
}
751
752
// getter and setter NULL means default
753
// since getter and setter are methods and not simple functions, do not transfer to VM
754
gravity_function_t *f = gravity_function_new_special(NULL, NULL, GRAVITY_COMPUTED_INDEX, f2[0], f2[1]);
755
gravity_class_bind(c, p->identifier, VALUE_FROM_OBJECT(f));
756
}
757
758
static void visit_function_decl (gvisitor_t *self, gnode_function_decl_t *node) {
759
DEBUG_CODEGEN("visit_function_decl %s", node->identifier);
760
761
// extern means it will be provided at runtime by the delegate
762
if (node->storage == TOK_KEY_EXTERN) return;
763
764
DECLARE_CONTEXT();
765
bool is_class_ctx = OBJECT_ISA_CLASS(context_object);
766
767
// create new function object
768
uint16_t nparams = (uint16_t)(node->params) ? marray_size(*node->params) : 0;
769
gravity_function_t *f = gravity_function_new((is_class_ctx) ? NULL : GET_VM(), node->identifier,
770
nparams, node->nlocals, 0, (void *)ircode_create(node->nlocals+nparams));
771
772
// check if f is a special constructor function (init)
773
// name must be CLASS_CONSTRUCTOR_NAME and context_object must be a class
774
bool is_constructor = (string_cmp(node->identifier, CLASS_CONSTRUCTOR_NAME) == 0) && (is_class_ctx);
775
776
CONTEXT_PUSH(f);
777
778
if (is_constructor) {
779
// reserve first four instructions that could be later filled with a CALL to $init
780
// see process_constructor for more information
781
ircode_t *code = (ircode_t *)f->bytecode;
782
ircode_add_skip(code);
783
ircode_add_skip(code);
784
ircode_add_skip(code);
785
ircode_add_skip(code);
786
}
787
788
// process inner block
789
if (node->block) {gnode_array_each(node->block->stmts, {visit(val);});}
790
791
// check for upvalues
792
if (node->uplist) f->nupvalues = gnode_array_size(node->uplist);
793
794
// remove current function
795
CONTEXT_POP();
796
797
// check for ircode errors
798
if (ircode_iserror((ircode_t *)f->bytecode))
799
report_error(self, (gnode_t *)node, "Maximum number of available registers used in function %s.", f->identifier);
800
801
// store function in current context
802
store_declaration(self, (gravity_object_t *)f, (node->storage == TOK_KEY_STATIC), node);
803
804
// convert ircode to bytecode (postpone optimization of the constructor)
805
if (!is_constructor) gravity_optimizer(f);
806
}
807
808
static void visit_variable_decl (gvisitor_t *self, gnode_variable_decl_t *node) {
809
DEBUG_CODEGEN("visit_variable_decl");
810
DECLARE_CONTEXT();
811
812
// no initialization for extern variables since the real value will be provided at runtime
813
if (node->storage == TOK_KEY_EXTERN) return;
814
815
bool is_module = CONTEXT_IS_MODULE(context_object);
816
bool is_class = OBJECT_ISA_CLASS(context_object);
817
bool is_local = ((is_module == false) && (is_class == false));
818
819
// loop through declarations
820
size_t count = gnode_array_size(node->decls);
821
for (size_t i=0; i<count; ++i) {
822
gnode_var_t *p = (gnode_var_t *)gnode_array_get(node->decls, i);
823
DEBUG_CODEGEN("visit_variable_decl %s", p->identifier);
824
825
// variable declarations can be specified in:
826
// FUNCTION (local variable)
827
// MODULE (module variable)
828
// CLASS (property)
829
830
if (is_local) {
831
// it is a local variable declaration (default initialized to NULL)
832
// code is generate ONLY if an init expression is specified
833
//
834
// example:
835
//
836
// func foo () {
837
// var a = 10;
838
// }
839
//
840
// LOADI 1 10 ; move 10 into register 1
841
// MOVE 0 1 ; move register 1 into register 0
842
//
843
844
// generate expression code
845
if (p->expr) visit(p->expr); // context is a function
846
847
gravity_function_t *context_function = (gravity_function_t *)context_object;
848
ircode_t *code = (ircode_t *)context_function->bytecode;
849
if (p->expr) {
850
// assign to variable result of the expression
851
ircode_add(code, MOVE, p->index, ircode_register_pop(code), 0);
852
} else {
853
// no default assignment expression found so initialize to NULL
854
ircode_add(code, LOADK, p->index, CPOOL_VALUE_NULL, 0);
855
}
856
continue;
857
}
858
859
if (is_module) {
860
// it is a module variable (default initialized to null)
861
// code must ALWAYS be generated
862
//
863
// example 1:
864
// var a;
865
//
866
// LOADK 0 NULL ; move null into register 0
867
// STOREG 0 0 ; move register 0 into hash(constant_pool(0))
868
//
869
// example 2:
870
// var a = 10;
871
//
872
// LOADI 0 10 ; move 10 into register 0
873
// STOREG 0 0 ; move register 0 into hash(constant_pool(0))
874
//
875
876
gravity_function_t *context_function = (gravity_function_t *)context_object;
877
ircode_t *code = (ircode_t *)context_function->bytecode;
878
uint16_t index = gravity_function_cpool_add(GET_VM(), context_function, VALUE_FROM_CSTRING(NULL, p->identifier));
879
880
if (p->expr) {
881
visit(p->expr); // context is a function
882
} else {
883
ircode_add_constant(code, CPOOL_VALUE_NULL);
884
}
885
ircode_add(code, STOREG, ircode_register_pop(code), index, 0);
886
continue;
887
}
888
889
if (is_class) {
890
bool is_static = (node->storage == TOK_KEY_STATIC);
891
gravity_class_t *context_class = (is_static) ? ((gravity_class_t *)context_object)->objclass : (gravity_class_t *)context_object;
892
893
// check computed property case first
894
if ((p->expr) && (p->expr->tag == NODE_COMPOUND_STAT)) {
895
process_getter_setter(self, p, context_class);
896
continue;
897
}
898
899
// create ivar first
900
uint16_t ivar_index = (p->index >= 0) ? p->index : gravity_class_add_ivar(context_class, NULL);
901
902
// add default getter and setter ONLY if property is public
903
if (node->access == TOK_KEY_PUBLIC) {
904
// since getter and setter are methods and not simple functions, do not transfer to VM
905
gravity_function_t *f = gravity_function_new_special(NULL, NULL, ivar_index, NULL, NULL); // getter and setter NULL means default
906
gravity_class_bind(context_class, p->identifier, VALUE_FROM_OBJECT(f));
907
}
908
DEBUG_CODEGEN("Class: %s (static: %d) property: %s index: %d", context_class->identifier, is_static, p->identifier, ivar_index);
909
910
// it is a property (default initialized to null)
911
// code must be generated ONLY if an init expression is specified
912
913
//
914
// example:
915
// class foo {
916
// var a = 10;
917
// }
918
//
919
// get $init function
920
// depending if variable has been created static
921
// and push it as current declaration then:
922
//
923
// LOADI 0 10 ; move 10 into register 0
924
// STOREF 0 0 ; move register 0 into property 0
925
926
if (p->expr) {
927
// was gravity_class_lookup but that means than $init or init will be recursively searched also in super classes
928
gravity_function_t *init_function = class_lookup_nosuper(context_class, CLASS_INTERNAL_INIT_NAME);
929
if (init_function == NULL) {
930
// no $init method found so create a new one
931
init_function = gravity_function_new (NULL, CLASS_INTERNAL_INIT_NAME, 1, 0, 0, ircode_create(1));
932
gravity_class_bind(context_class, CLASS_INTERNAL_INIT_NAME, VALUE_FROM_OBJECT(init_function));
933
}
934
935
CONTEXT_PUSH(init_function);
936
ircode_t *code = (ircode_t *)init_function->bytecode;
937
visit(p->expr);
938
uint32_t dest = ircode_register_pop(code);
939
ircode_add(code, STORE, dest, 0, p->index + MAX_REGISTERS);
940
CONTEXT_POP();
941
}
942
943
continue;
944
}
945
946
// should never reach this point
947
assert(0);
948
}
949
}
950
951
static void visit_enum_decl (gvisitor_t *self, gnode_enum_decl_t *node) {
952
#pragma unused(self,node)
953
DEBUG_CODEGEN("visit_enum_decl %s", node->identifier);
954
955
// enum is a map at runtime
956
// enum foo {a=1,b=2,...}
957
// is translated to
958
// var foo = [a:1,b:2,...]
959
}
960
961
static void visit_class_decl (gvisitor_t *self, gnode_class_decl_t *node) {
962
DEBUG_CODEGEN("visit_class_decl %s", node->identifier);
963
964
// extern means it will be provided at runtime by the delegate
965
if (node->storage == TOK_KEY_EXTERN) return;
966
967
// create a new pair of classes (class itself and its meta)
968
gravity_class_t *c = gravity_class_new_pair(GET_VM(), node->identifier, NULL, node->nivar, node->nsvar);
969
970
// mark the class as a struct
971
c->is_struct = node->is_struct;
972
973
// check if class has a declared superclass
974
if (node->superclass) {
975
// node->superclass should be a gnode_class_decl_t at this point
976
assert(NODE_ISA_CLASS(node->superclass));
977
978
gnode_class_decl_t *super = (gnode_class_decl_t *)node->superclass;
979
if (super->data) {
980
// means that superclass has already been processed and its runtime representation is available
981
gravity_class_setsuper(c, (gravity_class_t *)super->data);
982
} else {
983
// superclass has not yet processed so we need recheck the node at the end of the visit
984
// add node to superfix for later processing
985
codegen_t *data = (codegen_t *)self->data;
986
marray_push(gnode_class_decl_t *, data->superfix, node);
987
}
988
}
989
990
CONTEXT_PUSH(c);
991
992
// process inner declarations
993
gnode_array_each(node->decls, {visit(val);});
994
995
// adjust declaration stack
996
CONTEXT_POP();
997
998
// fix constructor chain
999
process_constructor(self, c);
1000
1001
// store class declaration in current context
1002
store_declaration(self, (gravity_object_t *)c, (node->storage == TOK_KEY_STATIC), NULL);
1003
1004
// save runtime representation
1005
// since this class could be a superclass of another class
1006
// then save opaque gravity_class_t pointer to node->data
1007
node->data = (void *)c;
1008
}
1009
1010
static void visit_module_decl (gvisitor_t *self, gnode_module_decl_t *node) {
1011
#pragma unused(self, node)
1012
DEBUG_CODEGEN("visit_module_decl %s", node->identifier);
1013
1014
// a module should be like a class with static entries
1015
// instantiated with import
1016
1017
// gravity_module_t *module = gravity_module_new(GET_VM(), node->identifier);
1018
// CONTEXT_PUSH(module);
1019
//
1020
// // process inner declarations
1021
// gnode_array_each(node->decls, {visit(val);});
1022
//
1023
// // adjust declaration stack
1024
// CONTEXT_POP();
1025
}
1026
1027
// MARK: - Expressions -
1028
1029
//static void process_special_enum (gvisitor_t *self, gnode_enum_decl_t *node, gnode_identifier_expr_t *identifier) {
1030
// symboltable_t *symtable = node->symtable;
1031
// gnode_t *v = symboltable_lookup(symtable, identifier->value);
1032
// assert(v);
1033
// assert(NODE_ISA(v, NODE_LITERAL_EXPR));
1034
// visit(v);
1035
//}
1036
1037
static void visit_binary_expr (gvisitor_t *self, gnode_binary_expr_t *node) {
1038
DEBUG_CODEGEN("visit_binary_expr %s", token_name(node->op));
1039
DECLARE_CODE();
1040
1041
// assignment is right associative
1042
if (node->op == TOK_OP_ASSIGN) {
1043
CODEGEN_COUNT_REGISTERS(n1);
1044
visit(node->right);
1045
visit(node->left); // left expression can be: IDENTIFIER, FILE, POSTIFIX (not a call)
1046
CODEGEN_COUNT_REGISTERS(n2);
1047
CODEGEN_ASSERT_REGISTERS(n1, n2, 0);
1048
return;
1049
}
1050
1051
CODEGEN_COUNT_REGISTERS(n1);
1052
1053
// visiting binary operation from left to right
1054
visit(node->left);
1055
visit(node->right);
1056
1057
uint32_t r3 = ircode_register_pop(code);
1058
uint32_t r2 = ircode_register_pop(code);
1059
uint32_t r1 = ircode_register_push_temp(code);
1060
1061
// a special instruction needs to be generated for a binary expression of type RANGE
1062
if ((node->op == TOK_OP_RANGE_INCLUDED) || (node->op == TOK_OP_RANGE_EXCLUDED)) {
1063
ircode_add_tag(code, RANGENEW, r1, r2, r3, (node->op == TOK_OP_RANGE_INCLUDED) ? RANGE_INCLUDE_TAG : RANGE_EXCLUDE_TAG);
1064
return;
1065
}
1066
1067
// generate code for binary OP
1068
opcode_t op = token2opcode(node->op);
1069
ircode_add(code, op, r1, r2, r3);
1070
1071
CODEGEN_COUNT_REGISTERS(n2);
1072
CODEGEN_ASSERT_REGISTERS(n1, n2, 1);
1073
}
1074
1075
static void visit_unary_expr (gvisitor_t *self, gnode_unary_expr_t *node) {
1076
DEBUG_CODEGEN("visit_unary_expr %s", token_name(node->op));
1077
DECLARE_CODE();
1078
1079
CODEGEN_COUNT_REGISTERS(n1);
1080
1081
// unary expression can be:
1082
// + Unary PLUS
1083
// - Unary MINUS
1084
// ! Logical NOT
1085
// ~ Bitwise NOT
1086
1087
visit(node->expr);
1088
if (node->op == TOK_OP_ADD) {
1089
// +1 is just 1 and more generally +expr is just expr so ignore + and proceed
1090
return;
1091
}
1092
1093
uint32_t r2 = ircode_register_pop(code);
1094
uint32_t r1 = ircode_register_push_temp(code);
1095
1096
opcode_t op = (node->op == TOK_OP_SUB) ? NEG : token2opcode(node->op);
1097
ircode_add(code, op, r1, r2, 0);
1098
1099
CODEGEN_COUNT_REGISTERS(n2);
1100
CODEGEN_ASSERT_REGISTERS(n1, n2, 1);
1101
}
1102
1103
static void visit_postfix_expr (gvisitor_t *self, gnode_postfix_expr_t *node) {
1104
DEBUG_CODEGEN("visit_call_expr");
1105
1106
// node->list usually cannot be NULL, it is NULL only as a result of a static enum transformation (see semacheck2.c)
1107
if (node->list == NULL) {
1108
visit(node->id);
1109
return;
1110
}
1111
1112
DECLARE_CODE();
1113
CODEGEN_COUNT_REGISTERS(n1);
1114
ircode_push_context(code);
1115
1116
// generate code for the common id node
1117
visit(node->id);
1118
1119
bool is_super = IS_SUPER(node->id);
1120
1121
// register that contains callable object
1122
uint32_t target_register = ircode_register_pop_protect(code, true);
1123
1124
// register where to store final result
1125
uint32_t dest_register = target_register;
1126
1127
// mandatory self register (initialized to 0 in case of implicit self or explicit super)
1128
uint32_r self_list; marray_init(self_list);
1129
marray_push(uint32_t, self_list, ((IS_IMPLICIT_SELF(node->id)) || (is_super)) ? 0 : target_register);
1130
1131
// process each subnode and set is_assignment flag
1132
bool is_assignment = node->base.is_assignment;
1133
size_t count = gnode_array_size(node->list);
1134
1135
for (size_t i=0; i<count; ++i) {
1136
// a subnode can be a CALL_EXPR => id.()
1137
// a NODE_ACCESS_EXPR => id.id2
1138
// a NODE_SUBSCRIPT_EXPR => id[]
1139
// or ANY combination of the them! => id.id2.id3()[24]().id5()
1140
gnode_postfix_subexpr_t *subnode = (gnode_postfix_subexpr_t *)gnode_array_get(node->list, i);
1141
1142
// identify postfix type: NODE_CALL_EXPR, NODE_ACCESS_EXPR, NODE_SUBSCRIPT_EXPR
1143
gnode_n tag = subnode->base.tag;
1144
1145
// check assignment flag
1146
bool is_real_assigment = (is_assignment && IS_LAST_LOOP(i, count));
1147
1148
if (tag == NODE_CALL_EXPR) {
1149
// a CALL instruction needs to properly prepare stack before execution
1150
// format is CALL A B C
1151
1152
// where A is the destination register
1153
// B is the callable object
1154
// and C is the number of arguments passed to the CALL
1155
// arguments must be on the stack (so gravity VM can use a register window in order to speed up instruction)
1156
// and are expected to be starting from B+1
1157
1158
// check dest register
1159
bool dest_is_temp = ircode_register_istemp(code, dest_register);
1160
if (!dest_is_temp) dest_register = ircode_register_push_temp(code);
1161
1162
// add target register (must be temp)
1163
uint32_t temp_target_register = ircode_register_push_temp(code);
1164
ircode_add(code, MOVE, temp_target_register, target_register, 0);
1165
ircode_register_pop_protect(code, true);
1166
1167
// always add SELF parameter (must be temp+1)
1168
uint32_t self_register = marray_pop(self_list);
1169
uint32_t temp_self_register = ircode_register_push_temp(code);
1170
ircode_add(code, MOVE, temp_self_register, self_register, 0);
1171
ircode_register_pop_protect(code, true);
1172
1173
// process each parameter (each must be temp+2 ... temp+n)
1174
marray_decl_init(uint32_r, args);
1175
size_t n = gnode_array_size(subnode->args);
1176
for (size_t j=0; j<n; ++j) {
1177
// process each argument
1178
gnode_t *arg = (gnode_t *)gnode_array_get(subnode->args, j);
1179
visit(arg);
1180
uint32_t nreg = ircode_register_pop_protect(code, true);
1181
// make sure args are in consecutive register locations (from temp_target_register + 1 to temp_target_register + n)
1182
if (nreg != temp_target_register + j + 2) {
1183
uint32_t temp = ircode_register_push_temp(code);
1184
if (temp == 0) return; // temp value == 0 means codegen error (error will be automatically reported later in visit_function_decl
1185
ircode_add(code, MOVE, temp, nreg, 0);
1186
nreg = ircode_register_pop_protect(code, true);
1187
}
1188
assert(nreg == temp_target_register + j + 2);
1189
marray_push(uint32_t, args, nreg);
1190
}
1191
1192
// generate instruction CALL with count parameters (taking in account self)
1193
ircode_add(code, CALL, dest_register, temp_target_register, (uint32_t)n+1);
1194
1195
// cleanup temp registers
1196
ircode_register_clean(code, temp_target_register);
1197
ircode_register_clean(code, temp_self_register);
1198
n = gnode_array_size(subnode->args);
1199
for (size_t j=0; j<n; ++j) {
1200
uint32_t reg = marray_get(args, j);
1201
ircode_register_clean(code, reg);
1202
}
1203
1204
// update self list
1205
marray_push(uint32_t, self_list, dest_register);
1206
1207
// a call returns a value
1208
if (IS_LAST_LOOP(i, count)) {
1209
if (ircode_register_count(code)) {
1210
// code added in order to protect the extra register pushed in case
1211
// of code like: f(20)(30)
1212
uint32_t last_register = ircode_register_last(code);
1213
if (dest_is_temp && last_register == dest_register) dest_is_temp = false;
1214
}
1215
if (dest_is_temp) ircode_register_push(code, dest_register);
1216
ircode_register_protect(code, dest_register);
1217
}
1218
1219
// free temp args array
1220
marray_destroy(args);
1221
1222
} else if (tag == NODE_ACCESS_EXPR) {
1223
// process identifier node (semantic check assures that each node is an identifier)
1224
gnode_identifier_expr_t *expr = (gnode_identifier_expr_t *)subnode->expr;
1225
uint32_t index = gravity_function_cpool_add(GET_VM(), context_function, VALUE_FROM_CSTRING(NULL, expr->value));
1226
uint32_t index_register = ircode_register_push_temp(code);
1227
ircode_add(code, LOADK, index_register, index, 0);
1228
ircode_register_pop(code);
1229
1230
// generate LOAD/STORE instruction
1231
dest_register = (is_real_assigment) ? ircode_register_pop(code) : ircode_register_push_temp(code);
1232
if (is_super) ircode_add(code, LOADS, dest_register, target_register, index_register);
1233
else ircode_add(code, (is_real_assigment) ? STORE : LOAD, dest_register, target_register, index_register);
1234
if ((!is_real_assigment) && (i+1<count)) ircode_register_pop_protect(code, true);
1235
1236
// update self list (if latest instruction)
1237
// this was added in order to properly emit instructions for nested_class.gravity unit test
1238
if (!IS_LAST_LOOP(i, count)) {
1239
gnode_postfix_subexpr_t *nextnode = (gnode_postfix_subexpr_t *)gnode_array_get(node->list, i+1);
1240
if (nextnode->base.tag != NODE_CALL_EXPR) marray_push(uint32_t, self_list, dest_register);
1241
}
1242
1243
} else if (tag == NODE_SUBSCRIPT_EXPR) {
1244
// process index
1245
visit(subnode->expr);
1246
uint32_t index = ircode_register_pop(code);
1247
1248
// generate LOADAT/STOREAT instruction
1249
dest_register = (is_real_assigment) ? ircode_register_pop(code) : ircode_register_push_temp(code);
1250
ircode_add(code, (is_assignment) ? STOREAT : LOADAT, dest_register, target_register, index);
1251
if ((!is_real_assigment) && (i+1<count)) ircode_register_pop_protect(code, true);
1252
}
1253
1254
// reset is_super flag
1255
is_super = false;
1256
1257
// update target
1258
target_register = dest_register;
1259
}
1260
1261
marray_destroy(self_list);
1262
ircode_pop_context(code);
1263
1264
CODEGEN_COUNT_REGISTERS(n2);
1265
CODEGEN_ASSERT_REGISTERS(n1, n2, (is_assignment) ? -1 : 1);
1266
}
1267
1268
static void visit_file_expr (gvisitor_t *self, gnode_file_expr_t *node) {
1269
DEBUG_CODEGEN("visit_file_expr");
1270
DECLARE_CODE();
1271
1272
CODEGEN_COUNT_REGISTERS(n1);
1273
1274
// check if the node is a left expression of an assignment
1275
bool is_assignment = node->base.is_assignment;
1276
1277
size_t count = gnode_array_size(node->identifiers);
1278
for (size_t i=0; i<count; ++i) {
1279
const char *identifier = gnode_array_get(node->identifiers, i);
1280
uint16_t kindex = gravity_function_cpool_add(GET_VM(), context_function, VALUE_FROM_CSTRING(NULL, identifier));
1281
1282
if ((is_assignment) && (IS_LAST_LOOP(i, count)))
1283
ircode_add(code, STOREG, ircode_register_pop(code), kindex, 0);
1284
else
1285
ircode_add(code, LOADG, ircode_register_push_temp(code), kindex, 0);
1286
}
1287
1288
CODEGEN_COUNT_REGISTERS(n2);
1289
CODEGEN_ASSERT_REGISTERS(n1, n2, (is_assignment) ? -1 : 1);
1290
}
1291
1292
static void visit_literal_expr (gvisitor_t *self, gnode_literal_expr_t *node) {
1293
/*
1294
1295
NOTE:
1296
1297
doubles and int64 should be added to the constant pool but I avoid
1298
adding them here so the optimizer has a way to perform better constant folding:
1299
http://en.wikipedia.org/wiki/Constant_folding
1300
http://www.compileroptimizations.com/category/constant_folding.htm
1301
1302
*/
1303
1304
DEBUG_CODEGEN("visit_literal_expr");
1305
DECLARE_CODE();
1306
1307
CODEGEN_COUNT_REGISTERS(n1);
1308
1309
switch (node->type) {
1310
case LITERAL_STRING: {
1311
// LOADK temp, s
1312
uint16_t index = gravity_function_cpool_add(GET_VM(), context_function, VALUE_FROM_STRING(NULL, node->value.str, node->len));
1313
ircode_add_constant(code, index);
1314
DEBUG_CODEGEN("visit_literal_expr (string) %s", node->value.str);
1315
} break;
1316
1317
case LITERAL_FLOAT:
1318
// LOADI temp, d
1319
ircode_add_double(code, node->value.d);
1320
DEBUG_CODEGEN("visit_literal_expr (float) %.2f", node->value.d);
1321
break;
1322
1323
case LITERAL_INT:
1324
// LOADI temp, n
1325
ircode_add_int(code, node->value.n64);
1326
DEBUG_CODEGEN("visit_literal_expr (int) %lld", node->value.n64);
1327
break;
1328
1329
case LITERAL_BOOL: {
1330
uint32_t value = (node->value.n64 == 0) ? CPOOL_VALUE_FALSE : CPOOL_VALUE_TRUE;
1331
ircode_add_constant(code, value);
1332
DEBUG_CODEGEN("visit_literal_expr (bool) %lld", node->value.n64);
1333
} break;
1334
1335
default: assert(0);
1336
}
1337
1338
CODEGEN_COUNT_REGISTERS(n2);
1339
CODEGEN_ASSERT_REGISTERS(n1, n2, 1);
1340
}
1341
1342
static void visit_identifier_expr (gvisitor_t *self, gnode_identifier_expr_t *node) {
1343
DEBUG_CODEGEN("visit_identifier_expr %s", node->value);
1344
DECLARE_CODE();
1345
1346
CODEGEN_COUNT_REGISTERS(n1);
1347
1348
// check if the node is a left expression of an assignment
1349
bool is_assignment = node->base.is_assignment;
1350
1351
const char *identifier = node->value; // identifier as c string
1352
gnode_location_type type = node->location.type; // location type
1353
uint16_t index = node->location.index; // symbol index
1354
uint16_t nup = node->location.nup; // upvalue index or outer index
1355
1356
switch (type) {
1357
1358
// local variable
1359
case LOCATION_LOCAL: {
1360
if (is_assignment)
1361
ircode_add(code, MOVE, index, ircode_register_pop(code), 0);
1362
else
1363
ircode_register_push(code, index);
1364
} break;
1365
1366
// module (global) variable
1367
case LOCATION_GLOBAL: {
1368
uint16_t kindex = gravity_function_cpool_add(GET_VM(), context_function, VALUE_FROM_CSTRING(NULL, identifier));
1369
if (is_assignment)
1370
ircode_add(code, STOREG, ircode_register_pop(code), kindex, 0);
1371
else
1372
ircode_add(code, LOADG, ircode_register_push_temp(code), kindex, 0);
1373
} break;
1374
1375
// upvalue access
1376
case LOCATION_UPVALUE: {
1377
gupvalue_t *upvalue = node->upvalue;
1378
if (is_assignment)
1379
ircode_add(code, STOREU, ircode_register_pop(code), upvalue->selfindex, 0);
1380
else
1381
ircode_add(code, LOADU, ircode_register_push_temp(code), upvalue->selfindex, 0);
1382
} break;
1383
1384
// class ivar case (outer case just need a loop lookup before)
1385
case LOCATION_CLASS_IVAR_OUTER:
1386
case LOCATION_CLASS_IVAR_SAME: {
1387
// needs to differentiate ivar (indexed by an integer) from other cases (indexed by a string)
1388
bool is_ivar = (index != UINT16_MAX);
1389
uint32_t dest = 0;
1390
uint32_t target = 0;
1391
1392
if (type == LOCATION_CLASS_IVAR_OUTER) {
1393
dest = ircode_register_push_temp(code);
1394
for (uint16_t i=0; i<nup; ++i) {
1395
ircode_add(code, LOAD, dest, target, 0 + MAX_REGISTERS);
1396
target = dest;
1397
}
1398
if (is_assignment) ircode_register_pop(code);
1399
}
1400
1401
uint32_t index_register;
1402
if (is_ivar) {
1403
// ivar case, use an index to load/retrieve it
1404
index_register = index + MAX_REGISTERS;
1405
} else {
1406
// not an ivar so it could be another class declaration like a func, a class or an enum
1407
// use lookup in order to retrieve it (assignment is handled here so you can change a
1408
// first class citizen at runtime too)
1409
uint16_t kindex = gravity_function_cpool_add(GET_VM(), context_function, VALUE_FROM_CSTRING(NULL, identifier));
1410
index_register = ircode_register_push_temp(code);
1411
ircode_add(code, LOADK, index_register, kindex, 0);
1412
ircode_register_pop(code);
1413
}
1414
1415
if (is_assignment) {
1416
// should be prohibited by semantic to store something into a non ivar slot?
1417
dest = ircode_register_pop(code); // consume temp register
1418
ircode_add(code, STORE, dest, target, index_register);
1419
} else {
1420
dest = (type == LOCATION_CLASS_IVAR_OUTER) ? target : ircode_register_push_temp(code);
1421
ircode_add(code, LOAD, dest , target, index_register);
1422
}
1423
} break;
1424
}
1425
1426
CODEGEN_COUNT_REGISTERS(n2);
1427
CODEGEN_ASSERT_REGISTERS(n1, n2, (is_assignment) ? -1 : 1);
1428
}
1429
1430
static void visit_keyword_expr (gvisitor_t *self, gnode_keyword_expr_t *node) {
1431
DEBUG_CODEGEN("visit_keyword_expr %s", token_name(node->base.token.type));
1432
DECLARE_CODE();
1433
1434
CODEGEN_COUNT_REGISTERS(n1);
1435
1436
gtoken_t type = NODE_TOKEN_TYPE(node);
1437
switch (type) {
1438
case TOK_KEY_CURRFUNC:
1439
ircode_add_constant(code, CPOOL_VALUE_FUNC);
1440
break;
1441
1442
case TOK_KEY_NULL:
1443
ircode_add_constant(code, CPOOL_VALUE_NULL);
1444
break;
1445
1446
case TOK_KEY_SUPER:
1447
ircode_add_constant(code, CPOOL_VALUE_SUPER);
1448
break;
1449
1450
case TOK_KEY_CURRARGS:
1451
// compiler can know in advance if arguments special array is used
1452
context_function->useargs = true;
1453
ircode_add_constant(code, CPOOL_VALUE_ARGUMENTS);
1454
break;
1455
1456
case TOK_KEY_UNDEFINED:
1457
ircode_add_constant(code, CPOOL_VALUE_UNDEFINED);
1458
break;
1459
1460
case TOK_KEY_TRUE:
1461
ircode_add_constant(code, CPOOL_VALUE_TRUE);
1462
break;
1463
1464
case TOK_KEY_FALSE:
1465
ircode_add_constant(code, CPOOL_VALUE_FALSE);
1466
break;
1467
1468
default:
1469
// should never reach this point
1470
assert(0);
1471
break;
1472
}
1473
1474
CODEGEN_COUNT_REGISTERS(n2);
1475
CODEGEN_ASSERT_REGISTERS(n1, n2, 1);
1476
}
1477
1478
static void visit_list_expr (gvisitor_t *self, gnode_list_expr_t *node) {
1479
DEBUG_CODEGEN("visit_list_expr");
1480
DECLARE_CODE();
1481
1482
CODEGEN_COUNT_REGISTERS(n1);
1483
1484
bool ismap = node->ismap;
1485
uint32_t n = (uint32_t)gnode_array_size(node->list1);
1486
1487
// a map requires twice registers than a list
1488
uint32_t max_fields = (ismap) ? MAX_FIELDSxFLUSH : MAX_FIELDSxFLUSH*2;
1489
1490
// destination register of a new instruction is ALWAYS a temp register
1491
// then the optimizer could decide to optimize and merge the step
1492
uint32_t dest = ircode_register_push_temp(code);
1493
ircode_add(code, (ismap) ? MAPNEW : LISTNEW, dest, n, 0);
1494
if (n == 0) return;
1495
1496
// this is just like Lua "fields per flush"
1497
// basically nodes are processed in a finite chunk
1498
// and then added to the list/map
1499
uint32_t nloops = n / max_fields;
1500
if (n % max_fields != 0) ++nloops;
1501
uint32_t nprocessed = 0;
1502
1503
while (nprocessed < n) {
1504
size_t k = (n - nprocessed > max_fields) ? max_fields : (n - nprocessed);
1505
size_t idxstart = nprocessed;
1506
size_t idxend = nprocessed + k;
1507
nprocessed += k;
1508
1509
// check if this chunk can be optimized
1510
// if (check_literals_list(self, node, ismap, idxstart, idxend, dest)) continue;
1511
1512
// save register context
1513
ircode_push_context(code);
1514
1515
// process each node
1516
for (size_t j=idxstart; j<idxend; ++j) {
1517
gnode_t *e = gnode_array_get(node->list1, j);
1518
visit(e);
1519
uint32_t nreg = ircode_register_pop_protect(code, true);
1520
if (!ircode_register_istemp(code, nreg)) {
1521
uint32_t temp_register = ircode_register_push_temp(code);
1522
ircode_add(code, MOVE, temp_register, nreg, 0);
1523
ircode_register_pop_protect(code, true);
1524
}
1525
if (ismap) {
1526
e = gnode_array_get(node->list2, j);
1527
visit(e);
1528
nreg = ircode_register_pop_protect(code, true);
1529
if (!ircode_register_istemp(code, nreg)) {
1530
uint32_t temp_register = ircode_register_push_temp(code);
1531
ircode_add(code, MOVE, temp_register, nreg, 0);
1532
ircode_register_pop_protect(code, true);
1533
}
1534
}
1535
}
1536
1537
// emit proper SETLIST instruction
1538
// since in a map registers are always used in pairs (key, value) it is
1539
// extremely easier to just set reg1 to be always 0 and use r in a loop
1540
ircode_add(code, SETLIST, dest, (uint32_t)(idxend-idxstart), 0);
1541
1542
// restore register context
1543
ircode_pop_context(code);
1544
}
1545
1546
CODEGEN_COUNT_REGISTERS(n2);
1547
CODEGEN_ASSERT_REGISTERS(n1, n2, 1);
1548
}
1549
1550
// MARK: -
1551
1552
gravity_function_t *gravity_codegen(gnode_t *node, gravity_delegate_t *delegate, gravity_vm *vm) {
1553
codegen_t data;
1554
data.vm = vm;
1555
marray_init(data.context);
1556
marray_init(data.superfix);
1557
1558
ircode_t *code = ircode_create(0);
1559
gravity_function_t *f = gravity_function_new(vm, INITMODULE_NAME, 0, 0, 0, code);
1560
marray_push(gravity_object_t*, data.context, (gravity_object_t *)f);
1561
1562
gvisitor_t visitor = {
1563
.nerr = 0, // used for internal codegen errors
1564
.data = &data, // used to store a pointer to codegen struct
1565
.delegate = (void *)delegate, // compiler delegate to report errors
1566
1567
// STATEMENTS: 7
1568
.visit_list_stmt = visit_list_stmt,
1569
.visit_compound_stmt = visit_compound_stmt,
1570
.visit_label_stmt = visit_label_stmt,
1571
.visit_flow_stmt = visit_flow_stmt,
1572
.visit_loop_stmt = visit_loop_stmt,
1573
.visit_jump_stmt = visit_jump_stmt,
1574
.visit_empty_stmt = visit_empty_stmt,
1575
1576
// DECLARATIONS: 5
1577
.visit_function_decl = visit_function_decl,
1578
.visit_variable_decl = visit_variable_decl,
1579
.visit_enum_decl = visit_enum_decl,
1580
.visit_class_decl = visit_class_decl,
1581
.visit_module_decl = visit_module_decl,
1582
1583
// EXPRESSIONS: 8
1584
.visit_binary_expr = visit_binary_expr,
1585
.visit_unary_expr = visit_unary_expr,
1586
.visit_file_expr = visit_file_expr,
1587
.visit_literal_expr = visit_literal_expr,
1588
.visit_identifier_expr = visit_identifier_expr,
1589
.visit_keyword_expr = visit_keyword_expr,
1590
.visit_list_expr = visit_list_expr,
1591
.visit_postfix_expr = visit_postfix_expr,
1592
};
1593
1594
DEBUG_CODEGEN("=== BEGIN CODEGEN ===");
1595
gvisit(&visitor, node);
1596
DEBUG_CODEGEN("\n");
1597
1598
// check for special superfix
1599
if (marray_size(data.superfix)) {
1600
fix_superclasses(&visitor);
1601
}
1602
1603
// pop globals instance init special function
1604
marray_pop(data.context);
1605
assert(marray_size(data.context) == 0);
1606
marray_destroy(data.context);
1607
1608
// in case of codegen errors explicity free code and return NULL
1609
if (visitor.nerr != 0) {ircode_free(code); f->bytecode = NULL;}
1610
return (visitor.nerr == 0) ? f : NULL;
1611
}
1612
1613