gravity_codegen.c 57 KB

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