CE Compiler.cpp 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************
  5. Esenthel Script functions use following memory diagram: (engine codes which rely on this diagram have "RESULT PARENT PARAMS" near in the comments, use the string to search the engine)
  6. Stack:
  7. 0 - Memory for local variables and temporaries
  8. Param Stack: (all parameters must be stored continuously)
  9. 0 - Continuous memory for function parameters
  10. Nvidia Tegra CPU (Android) requires that FPU operations (on Flt and Dbl) use memories aligned to 4 bytes (non-float types alignment is not required)
  11. Performing FPU operation on unaligned memory results in instant crash.
  12. /******************************************************************************
  13. Each "map local in stack" should check if that variable requires calling destructor.
  14. That destructor should be called in one of following scenario:
  15. -1 instruction after usage of the variable (only for temporaries), however remember that in C++ they can be held longer "C Str &var=Str("123")" (until end of 'var' life)
  16. -during goto which relocates code position outside of variable scope
  17. -at the end of variable scope
  18. Example:
  19. {
  20. start:
  21. C Str &s=Str("123"); // Str("123") is held by reference so make its life as long until next '}'
  22. goto start; // here call dtor for Str("123") since it jumps outside the scope of the var
  23. Str("abc");
  24. // here call dtor for Str("abc") since it's a temporary not held by any reference
  25. C Str &s2=(cond ? Str("def") : s); // Str("def") is created conditionally, so upon its creation add it to 'destructor_list' (with scope of 1 block or 1 instruction depending if value is set into reference)
  26. goto end; // here call dtor for Str("123"), since there was one conditional with block scope then check for 'destructor_list'
  27. } // here call dtor for Str("123"), since there was one conditional with block scope then check for 'destructor_list'
  28. end:
  29. There can be following types of destructors:
  30. Bool block_scope; (false for instruction scope)
  31. Bool conditional; (false for permanent)
  32. Goto's in some cases can't jump from outside the scope of a variable to inside the scope of a variable (don't do any special checks for references as they are treated the same as non-references)
  33. C++ behavior for:
  34. {
  35. TYPE var;
  36. label:
  37. }
  38. goto label;
  39. TYPE = basic type or pointer (initialization means "var=..")
  40. initialization | compile result
  41. 0 success
  42. 1 success
  43. TYPE = class object/array (initialization means if class has anykind of ctor, even empty or default)
  44. initialization | has dtor | compile result
  45. 0 0 success
  46. 1 0 warning
  47. 0 1 success
  48. 1 1 error
  49. /******************************************************************************/
  50. Int Compare(C Edit::Message &a, C Edit::Message &b)
  51. {
  52. if(a.source && b.source)
  53. {
  54. if(a.source!=b.source)if(Int c=ComparePath(a.source->loc.asText(), b.source->loc.asText()))return c;
  55. if(a.token && b.token && a.token->line && b.token->line)
  56. {
  57. return Compare(a.token->lineIndex(), b.token->lineIndex());
  58. }
  59. return Compare(a.token!=null, b.token!=null);
  60. }
  61. return Compare(a.source!=null, b.source!=null);
  62. }
  63. /******************************************************************************/
  64. namespace Edit{
  65. /******************************************************************************/
  66. static Int CompareConstantData(Byte *a, Int size_a, Byte *b, Int size_b)
  67. {
  68. REP(Min(size_a, size_b))if(Int c=Compare(*a++, *b++))return c;
  69. return Compare(size_a, size_b);
  70. }
  71. Int CompilerContext::heapConstant(CPtr data, Int size, Int align)
  72. {
  73. if(data && size>0)
  74. {
  75. Int l=0, r=constants.elms(); for(; l<r; )
  76. {
  77. Int mid =UInt(l+r)/2; Constant &c=constants[mid];
  78. Int compare=CompareConstantData(const_data.data()+c.const_offset, c.size, (Byte*)data, size);
  79. if( compare<0)l=mid+1;else
  80. if( compare>0)r=mid ;else return c.heap_offset; // found
  81. }
  82. Constant &c=constants.NewAt(l);
  83. c.size=size;
  84. c.heap_offset=AlignAddress(env.heap_size, align);
  85. env.heap_size=c.heap_offset+size;
  86. c.const_offset=const_data.addNum(size);
  87. Copy(const_data.data()+c.const_offset, data, size);
  88. return c.heap_offset;
  89. }
  90. return 0;
  91. }
  92. void CompilerContext::varHeapConstant(Int heap_offset, CPtr data, Int size)
  93. {
  94. if(data && size>0)
  95. {
  96. Constant &c=var_constants.New();
  97. c.size=size;
  98. c. heap_offset=heap_offset;
  99. c.const_offset=const_data.addNum(size);
  100. Copy(const_data.data()+c.const_offset, data, size);
  101. }
  102. }
  103. Int Compiler:: heapConstant( CPtr data, Int size, Int align) {if(!ctx)msgs.New().error("CompilerContext not set", source, -1); return ctx ? ctx-> heapConstant( data, size, align) : 0;}
  104. void Compiler::varHeapConstant(Int heap_offset, CPtr data, Int size ) {if(!ctx)msgs.New().error("CompilerContext not set", source, -1); if(ctx) ctx->varHeapConstant(heap_offset, data, size ) ;}
  105. Int Compiler::newLocal(Symbol::Modif &type, Bool block_scope, Token *token)
  106. {
  107. Int index=locals.elms(); // get index of newly created element
  108. Local &local=locals.New (); // create the new local
  109. local.type =type ; // set its type
  110. local.block_scope=block_scope; // set its scope
  111. local.token =token ; // set its token
  112. return index; // return index of the local variable
  113. }
  114. void Compiler::mapLocalInStack(Expr::Memory &mem, Bool error_on_fail)
  115. {
  116. if(mem.type!=Expr::Memory::LOCAL)msgs.New().error("Can't map variable in the stack because it's not a local variable");else
  117. if(!InRange(mem.index, locals) )msgs.New().error("Specified local variable index is invalid");else
  118. if(!ctx )msgs.New().error("null compiler context");else
  119. {
  120. Local &local=locals[mem.index];
  121. if(local.stack_offset>=0)
  122. {
  123. if(error_on_fail)msgs.New().error("Can't map local variable to stack because it's already mapped");
  124. }else
  125. {
  126. Int size =local.type.rawSize(true),
  127. align=local.type.firstMemberSize();
  128. if(local.force_heap) // if temporary is forced to exist on the heap
  129. {
  130. local.stack_offset=AlignAddress(ctx->env.heap_size, align); // ALIGN
  131. ctx->env.heap_size=local.stack_offset+size; // adjust new heap size
  132. }else
  133. {
  134. local.stack_offset=AlignAddress(stack_size, align); // ALIGN
  135. stack_size =local.stack_offset+size; // adjust new stack size
  136. }
  137. if(local.type.hasDestructor())live_locals.add(mem.index); // add to just mapped container
  138. local.scope_start=cmd_index; // set creation position to the currently compiled command
  139. local.scope_label=scope_label; // set scope label index at which variable should be destroyed
  140. }
  141. }
  142. }
  143. void Compiler::mapVar(Expr::Memory &mem)
  144. {
  145. if(mem.type==Expr::Memory::LOCAL)mapLocalInStack(mem, false);
  146. }
  147. Bool Compiler::unmappedLocal(Expr::Memory &mem)
  148. {
  149. if(mem.type==Expr::Memory::LOCAL && InRange(mem.index, locals))return locals[mem.index].stack_offset<0;
  150. return false;
  151. }
  152. /******************************************************************************/
  153. void Compiler::expand(Memc<Command> &src, Memc<Command> &dest, Int label_break, Int label_continue, Int label_return, Int label_scope)
  154. {
  155. FREPA(src)
  156. {
  157. Command &cmd=src[i]; cmd.scope_label=label_scope;
  158. switch( cmd.type)
  159. {
  160. default: dest.New()=cmd; break;
  161. case CMD_RETURN : {Command &d=dest.New(); d=cmd; d.label_index=label_return ;} break;
  162. case CMD_BREAK : {Command &d=dest.New(); d=cmd; d.label_index=label_break ;} break;
  163. case CMD_CONTINUE: {Command &d=dest.New(); d=cmd; d.label_index=label_continue;} break;
  164. case CMD_GOTO :
  165. case CMD_LABEL: {Command &d=dest.New(); d=cmd; if(InRange(cmd.raw_range.x, tokens)){Token &token=*tokens[cmd.raw_range.x]; if(token.symbol && token.symbol->type==Symbol::LABEL)d.label_index=token.symbol->raw_offset;}} break;
  166. case CMD_GROUP:
  167. {
  168. /*{
  169. cmds;
  170. }
  171. cmds; #0
  172. scope: #1 - end of the group scope*/
  173. Int label_scope=labels++;
  174. expand(cmd.cmds, dest, label_break, label_continue, label_return, label_scope); // 0
  175. {Command &d=dest.New(); d.type=CMD_LABEL; d.label_index=label_scope;} // 1
  176. }break;
  177. case CMD_IF:
  178. {
  179. /*if(cond)true;else false;
  180. if(!cond)goto false; #0
  181. true; #1
  182. true_scope: #2
  183. goto end; #3
  184. false: #4
  185. false; #5
  186. false_scope: #6
  187. end: #7*/
  188. Bool has_else =(cmd.cmds_false.elms()!=0);
  189. Int label_true_scope =labels++,
  190. label_false =labels++,
  191. label_false_scope=labels++,
  192. label_end =labels++;
  193. {Command &d=dest.New(); d.type=CMD_GOTO_COND_N; d._for.cond_range=cmd._for.cond_range; d.label_index=label_false; d.scope_label=label_end;} // 0
  194. expand(cmd.cmds, dest, label_break, label_continue, label_return, label_true_scope); // 1
  195. {Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_true_scope ;} // 2
  196. if(has_else){Command &d=dest.New(); d.type=CMD_GOTO ; d.label_index=label_end ;} // 3
  197. {Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_false ;} // 4
  198. if(has_else)expand(cmd.cmds_false, dest, label_break, label_continue, label_return, label_false_scope); // 5
  199. if(has_else){Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_false_scope;} // 6
  200. {Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_end ;} // 7
  201. }break;
  202. case CMD_FOR:
  203. {
  204. /*for(init; cond; step)cmds;
  205. init; #0 (this can be empty if 'init_range' is empty)
  206. loop: #1
  207. if(!cond)goto break; #2 (this can be empty if 'cond_range' is empty, in that case always proceed to step 3)
  208. cmds; #3
  209. cmds_scope: #4 (this label destroys all vars from 'cmds' scope)
  210. continue: // any "continue;" command will jump here #5
  211. step; #6 (this can be empty if 'step_range' is empty)
  212. step_scope: #7
  213. goto loop; #8
  214. break: // any "break;" command will jump here #9*/
  215. Int label_loop =labels++,
  216. label_cmds_scope=labels++,
  217. label_continue =labels++,
  218. label_step_scope=labels++,
  219. label_break =labels++;
  220. if(cmd._for.init_range.x <= cmd._for.init_range.y){Command &d=dest.New(); d.type=CMD_INSTRUCT ; d.raw_range=cmd._for.init_range; d.scope_label=label_break;} // 0 (add step only if 'init_range' is specified)
  221. {Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_loop;} // 1
  222. if(cmd._for.cond_range.x <= cmd._for.cond_range.y){Command &d=dest.New(); d.type=CMD_GOTO_COND_N; d._for.cond_range=cmd._for.cond_range; d.label_index=label_break; d.scope_label=label_break;} // 2 (add step only if 'cond_range' is specified) (this should point to 'label_break' and not 'step_scope' because there is no destroying of block scoped variables in conditional goto)
  223. expand(cmd.cmds, dest, label_break, label_continue, label_return, label_cmds_scope); // 3
  224. {Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_cmds_scope;} // 4
  225. {Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_continue;} // 5
  226. if(cmd._for.step_range.x <= cmd._for.step_range.y){Command &d=dest.New(); d.type=CMD_INSTRUCT ; d.raw_range=cmd._for.step_range; d.scope_label=label_step_scope;} // 6 (add step only if 'step_range' is specified)
  227. if(cmd._for.step_range.x <= cmd._for.step_range.y){Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_step_scope;} // 7 (add step only if 'step_range' is specified)
  228. {Command &d=dest.New(); d.type=CMD_GOTO ; d.label_index=label_loop;} // 8
  229. {Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_break;} // 9
  230. }break;
  231. case CMD_WHILE:
  232. {
  233. /*while(cond)cmds;
  234. continue: // any "continue;" command will jump here #0
  235. if(!cond)goto break; #1
  236. cmds; #2
  237. cmds_scope: #3
  238. goto continue; #4
  239. break: // any "break;" command will jump here #5*/
  240. Int label_continue =labels++,
  241. label_cmds_scope=labels++,
  242. label_break =labels++;
  243. {Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_continue;} // 0
  244. {Command &d=dest.New(); d.type=CMD_GOTO_COND_N; d._for.cond_range=cmd._for.cond_range; d.label_index=label_break; d.scope_label=label_break;} // 1
  245. expand(cmd.cmds, dest, label_break, label_continue, label_return, label_cmds_scope); // 2
  246. {Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_cmds_scope;} // 3
  247. {Command &d=dest.New(); d.type=CMD_GOTO ; d.label_index=label_continue;} // 4
  248. {Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_break;} // 5
  249. }break;
  250. case CMD_DO:
  251. {
  252. /*do cmds while(cond);
  253. loop: #0
  254. cmds; #1
  255. cmds_scope: #2
  256. continue: // any "continue;" command will jump here #3
  257. if(cond)goto loop; #4
  258. break: // any "break;" command will jump here #5*/
  259. Int label_loop =labels++,
  260. label_cmds_scope=labels++,
  261. label_continue =labels++,
  262. label_break =labels++;
  263. {Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_loop;} // 0
  264. expand(cmd.cmds, dest, label_break, label_continue, label_return, label_cmds_scope); // 1
  265. {Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_cmds_scope;} // 2
  266. {Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_continue;} // 3
  267. {Command &d=dest.New(); d.type=CMD_GOTO_COND; d._for.cond_range=cmd._for.cond_range; d.label_index=label_loop; d.scope_label=label_break;} // 4
  268. {Command &d=dest.New(); d.type=CMD_LABEL ; d.label_index=label_break;} // 5
  269. }break;
  270. /*case CMD_SWITCH:
  271. {
  272. /*switch(expr)
  273. {
  274. case 0: break;
  275. case 1:
  276. case 2: break;
  277. default: break;
  278. }
  279. if(expr==0)goto case 0;
  280. if(expr==1)goto case 1;
  281. if(expr==2)goto case 2;
  282. if(default_exists)goto default;else goto break;
  283. case 0:
  284. goto break;
  285. case 1:
  286. case 2:
  287. goto break;
  288. default:
  289. goto break;
  290. break;*/
  291. /*}break;*/
  292. }
  293. }
  294. }
  295. /******************************************************************************/
  296. static void CompileSetConst(Expr &expr, Compiler &compiler, Memc<Byte> &code)
  297. {
  298. Int size=expr.symbol.rawSize(false);
  299. if( size>0) // size can be 0 for empty classes
  300. {
  301. Int params=2, // stack_offset + raw value
  302. pos =code.addNum(Call::Size(params)),
  303. align =expr.symbol.firstMemberSize(),
  304. offset=AlignAddress(compiler.stack_size, align); compiler.stack_size=offset+size; // ALIGN
  305. U64 raw =expr.asRaw(compiler);
  306. Bool heap =!(ValueFitsInInstruction(size) || expr.symbol.isPtr() && !raw); // store in the instruction only if it fits, or allow special case of 'null' ptr value because in both 32/64 it will be just 0
  307. Call &call =*(Call*)(&code[pos]);
  308. call.params =params;
  309. call.func =GetSetConstCall(size, heap); if(!call.func){compiler.msgs.New().error("CompileSetConst invalid expression size"); return;}
  310. call.param(0).setInstructI(offset); // set as offset from stack to the value
  311. if(heap)call.param(1).setHeap (compiler.heapConstant(&raw, size, align), 0, false); // create constant on the heap
  312. else call.param(1).setInstruct (expr.asRaw(compiler));
  313. }
  314. }
  315. static void CompileSetAddrStackHeap(Expr &expr, Compiler &compiler, Bool stack, Int offset, Memc<Byte> &code, Bool auto_map=true) // this function puts address of 'expr' variable into specified position offset at the stack/heap
  316. {
  317. Int params=2, // target offset + src variable
  318. pos =code.addNum(Call::Size(params));
  319. Call &call =*(Call*)(&code[pos]);
  320. call.params=params;
  321. call.func =(stack ? GetSetAddrStackCall() : GetSetAddrHeapCall()); if(!call.func){compiler.msgs.New().error("CompileSetAddrStackHeap unknown func call"); return;}
  322. call.param(0).setInstructI(offset); // set as offset from stack/heap to the value
  323. call.param(1).set (expr, compiler, auto_map);
  324. }
  325. static void CompileSetAddrResult(Expr &expr, Compiler &compiler, Memc<Byte> &code) // this function puts address of 'expr' variable into the result stack
  326. {
  327. Int params=1, // src variable
  328. pos =code.addNum(Call::Size(params));
  329. Call &call =*(Call*)(&code[pos]);
  330. call.params=params;
  331. call.func =GetSetAddrResultCall(); if(!call.func){compiler.msgs.New().error("CompileSetAddrResult unknown func call"); return;}
  332. call.param(0).set(expr, compiler);
  333. }
  334. enum PASS_TYPE // how the parameter is passed
  335. {
  336. PASS_NONE ,
  337. PASS_MAP , // map local in stack
  338. PASS_CONST, // set constant
  339. PASS_ADDR , // set address
  340. };
  341. static void CompileExpr(Expr &expr, Compiler &compiler, Memc<Byte> &code)
  342. {
  343. // always compile parents of this expression as first
  344. FREPA(expr.parent)CompileExpr(expr.parent[i], compiler, code);
  345. if(!expr.known())
  346. {
  347. if(expr.func.elms()==1)
  348. {
  349. Expr &func=expr.func[0];
  350. if(func._operator && func==',') // for "a,b" operator process all parameters in their order
  351. {
  352. FREPA(func.params)CompileExpr(func.params[i], compiler, code);
  353. }else
  354. if(func.func_call)
  355. {
  356. // compile func parent and params first
  357. FREPA(func.parent)CompileExpr(func.parent[i], compiler, code);
  358. FREPA(func.params)CompileExpr(func.params[i], compiler, code);
  359. if(func.func_call!=GetIgnoreCall())
  360. {
  361. Int params=(FlagTest(func.func_call_mask, Expr::FC_RESULT) + FlagTest(func.func_call_mask, Expr::FC_PARENT) + func.params.elms()),
  362. pos =code.addNum(Call::Size(params));
  363. Call &call =*(Call*)(&code[pos]);
  364. call.params=params;
  365. call.func =func.func_call;
  366. params=0;
  367. if(func.func_call_mask&Expr::FC_RESULT) { call.param(params++).set(expr , compiler);}
  368. if(func.func_call_mask&Expr::FC_PARENT) {if(func.parent.elms()!=1)goto error; call.param(params++).set(func.parent[0], compiler);}
  369. FREPA(func.params){ call.param(params++).set(func.params[i], compiler);}
  370. }
  371. return; // success
  372. }else
  373. if(func.symbol && func.symbol->type==Symbol::FUNC && func.symbol->source && func.symbol->source->active) // Esenthel Script function, RESULT PARENT PARAMS
  374. {
  375. if(func.params.elms()!=func.symbol->realParams())compiler.msgs.New().error("Invalid number of function parameters", expr.origin);else
  376. {
  377. // compile func parent first
  378. FREPA(func.parent)CompileExpr(func.parent[i], compiler, code);
  379. // get func info
  380. Bool has_result =func.symbol->hasResult(null),
  381. has_this =func.symbol->isClassNonStaticFunc(),
  382. is_virtual=func.symbol->isVirtualFunc(null);
  383. if(is_virtual)
  384. {
  385. // TODO: remember to checkup virtual disabling when specyfing class "obj.Class::method()" (this is done in "Expr.cpp")
  386. compiler.msgs.New().error("Calling virtual script methods is not yet supported", expr.origin);
  387. }
  388. // TODO: array_dims
  389. Mems<PASS_TYPE> param_pass; param_pass.setNumZero(func.params.elms());
  390. FREPA(func.params) // order is important as parameters must be in continuous memory
  391. {
  392. Expr &src=func.params[i];
  393. Symbol::Modif dest=func.symbol->params[i]->value; dest.proceedToFinal(&expr.symbol.templates, true);
  394. /* func.params | func.symbol->params
  395. SRC DEST
  396. int -> int GOOD (can use SRC, if can't then use COPY CTOR)
  397. int -> int& GOOD ( must use REF CTOR)
  398. int& -> int GOOD ( must use COPY CTOR)
  399. int& -> int& GOOD (can use SRC, if can't then use REF CTOR)
  400. int -> const int GOOD (can use SRC, if can't then use COPY CTOR)
  401. int -> const int& GOOD ( must use REF CTOR)
  402. int& -> const int GOOD ( must use COPY CTOR)
  403. int& -> const int& GOOD (can use SRC, if can't then use REF CTOR)
  404. const int -> int GOOD ( must use COPY CTOR) (SRC may be allowed for basicType, read below)
  405. const int -> int& BAD
  406. const int& -> int GOOD ( must use COPY CTOR)
  407. const int& -> int& BAD
  408. const int -> const int GOOD (can use SRC, if can't then use COPY CTOR)
  409. const int -> const int& GOOD ( must use REF CTOR)
  410. const int& -> const int GOOD ( must use COPY CTOR)
  411. const int& -> const int& GOOD (can use SRC, if can't then use REF CTOR)*/
  412. Bool is_null=(src=="null"), good_null=(is_null && dest.isPtr());
  413. UInt src_const_level=src.symbol.const_level; FlagDisable( src_const_level, 1<<(src.symbol.ptr_level+src.symbol.array_dims.elms())); // disable top const
  414. UInt dest_const_level=dest .const_level; FlagDisable(dest_const_level, 1<<(dest .ptr_level+dest .array_dims.elms())); // disable top const
  415. if((!src.symbol.same(dest, false, false) || LostConst(src_const_level, dest_const_level)) && !good_null) // if symbol is not the same (test underlying consts but not the main ones)
  416. {
  417. compiler.msgs.New().error(S+"Can't convert parameter #"+(i+1)+" from '"+src.symbol.modifName(true, true, false)+"' to '"+dest.modifName(true, true, false)+"'", src.origin);
  418. }else
  419. if((is_null || LostConst(src.symbol.const_level, dest.const_level)) && FlagTest(dest.modifiers, Symbol::MODIF_REF)) // const int/int& -> int& BAD
  420. {
  421. compiler.msgs.New().error("Can't convert const parameter to non-const reference", src.origin);
  422. }else
  423. if(
  424. (!LostConst(src.symbol.const_level, dest.const_level) // only if not losing const
  425. || (src.basicType() && !(src.symbol.modifiers&Symbol::MODIF_REF)) // if losing const then allow optimization only if we're operating on non-ref temporary basic type like "const int" (because if it's a LOCAL variable then we can modify it and don't care, if it's a KNOWN constant then it will be copied using CompileSetConst to the stack either way and we will be operating on the copy)
  426. || good_null // when passing null the const doesn't matter
  427. )
  428. && src.fullTemporary() && (src.symbol.modifiers&Symbol::MODIF_REF)==(dest.modifiers&Symbol::MODIF_REF) && src.mem.offset==0) // use SRC
  429. {
  430. if(src.mem.type==Expr::Memory::LOCAL)param_pass[i]=PASS_MAP ;else
  431. if(src.mem.type==Expr::Memory::KNOWN)param_pass[i]=PASS_CONST;else // since we're copying the constant to the stack, then this allows above optimization when losing const but operating on basic type
  432. compiler.msgs.New().error("Invalid expression memory type for src_temporary + dest_parameter merging optimization", src.origin);
  433. }else
  434. if(dest.modifiers&Symbol::MODIF_REF) // use REF CTOR, compile code which sets Ptr address at specified location of a custom variable
  435. {
  436. param_pass[i]=PASS_ADDR;
  437. compiler.mapVar(src.mem); // make sure that param is mapped before func param stack
  438. }else
  439. {
  440. src.copyCtor(compiler); // perform copy constructor
  441. param_pass[i]=PASS_MAP; // created temporary should be mapped directly to the param stack
  442. }
  443. }
  444. // ALIGN
  445. // set memory map for params in local stack (must be CONTINUOUS) do this before compiling params so the result is already set, RESULT PARENT PARAMS
  446. if( param_pass.elms() )compiler.stack_size=Ceil8(compiler.stack_size); // must be aligned to max possible var size, because when compiling functions, each of their parameter's offset is calculated from zero
  447. Int param_stack_offset=compiler.stack_size;
  448. FREPA(param_pass)
  449. {
  450. Expr &src=func.params[i];
  451. switch(param_pass[i])
  452. {
  453. case PASS_MAP : compiler.mapLocalInStack(src.mem); break;
  454. case PASS_CONST: CompileSetConst (src, compiler, code); break;
  455. case PASS_ADDR : {Int offset=AlignAddress(compiler.stack_size, PtrSize); compiler.stack_size=offset+PtrSize; CompileSetAddrStackHeap(src, compiler, true, offset, code, false);} break;
  456. }
  457. }
  458. // compile params
  459. FREPA(func.params)CompileExpr(func.params[i], compiler, code);
  460. Int params=1+has_this+has_result, // (func_body + param_stack)(2in1) + this + result
  461. pos =code.addNum(Call::Size(params));
  462. Call &call =*(Call*)(&code[pos]);
  463. call.params=params;
  464. call.func =GetCallFunc(has_this, has_result);
  465. params=0;
  466. call.param(params++).setInstructPI2(func.symbol(), // set pointer to func symbol, because that function may not yet be compiled into FuncBody, REMAP needed
  467. param_stack_offset); // set as offset from stack to first element in param stack
  468. if(has_this)
  469. {
  470. Call::Param &call_parent=call.param(params++);
  471. if(func.parent.elms()==1)
  472. {
  473. Expr &expr_parent=func.parent[0]; if(!expr_parent.instance || !expr_parent.symbol.isObj())compiler.msgs.New().error("Invalid object for method call", expr.origin);
  474. call_parent.set(expr_parent, compiler);
  475. }else
  476. {
  477. compiler.msgs.New().error("No object specified for method call", expr.origin);
  478. }
  479. }
  480. if(has_result)call.param(params++).setRef(expr, compiler); // if result is a reference then we want to get the address of the reference and not the value that it points to
  481. }
  482. return; // don't process any more messages
  483. }
  484. error:
  485. compiler.msgs.New().error("Can't compile expression", expr.origin);
  486. }
  487. }
  488. }
  489. static void DestroyVar(Symbol::Modif &type, Token *token, Compiler &compiler, Memc<Byte> &code, Bool local, Int local_index, Symbol *global_symbol)
  490. {
  491. if(type.isArray())
  492. {
  493. compiler.msgs.New().error(S+"Destructor for arrays is not yet supported", token);
  494. }else
  495. {
  496. Expr result, &func=result.func.New(), &parent=func.parent.New();
  497. func.instance=true;
  498. func.symbol =FindChild(S+'~'+type, type(), null, false); // find destructor function
  499. if(!func.symbol)
  500. {
  501. func.func_call =FindFuncCall(DtorName(*type));
  502. func.func_call_mask=Expr::FC_PARENT;
  503. }
  504. if(!func.symbol && !func.func_call)compiler.msgs.New().error(S+"Destructor not found for "+type.modifName(false, false, false), token);else
  505. {
  506. parent.instance=true;
  507. parent.symbol =type;
  508. if(local)parent.mem.setLocal(local_index);else parent.mem.setGlobal(*global_symbol);
  509. if(func.func_call || result.calculate(compiler)>CAST_NONE)CompileExpr(result, compiler, code); // there is no need to calculate if we already know the 'func_call'
  510. }
  511. }
  512. }
  513. static void DestroyLiveLocal(Int i, Bool remove, Compiler &compiler, Memc<Byte> &code)
  514. {
  515. if(InRange(i, compiler.live_locals))
  516. {
  517. Int local_index=compiler.live_locals[i]; if(remove)compiler.live_locals.remove(i, true); // keep order
  518. Local &local =compiler.locals[local_index];
  519. DestroyVar(local.type, local.token, compiler, code, true, local_index, null);
  520. }
  521. }
  522. static void DestroyInstructScopes(Compiler &compiler, Memc<Byte> &code)
  523. {
  524. REPA(compiler.live_locals) // process in reversed order (ORDER IS IMPORTANT)
  525. {
  526. Local &local=compiler.locals[compiler.live_locals[i]];
  527. if(!local.block_scope)DestroyLiveLocal(i, true, compiler, code); // only instruction scope
  528. }
  529. }
  530. static void DestroyBlockScopes(Int label_index, Compiler &compiler, Memc<Byte> &code)
  531. {
  532. REPA(compiler.live_locals) // process in reversed order (ORDER IS IMPORTANT)
  533. {
  534. Local &local=compiler.locals[compiler.live_locals[i]];
  535. if(local.block_scope && local.scope_label==label_index)DestroyLiveLocal(i, true, compiler, code);
  536. }
  537. }
  538. static Int LabelCommandIndex(Int label, Memc<Command> &cmds, Compiler &compiler, Int token_index) // get command index of specified label (always calculate in case commands container will get changed dynamically during compilation)
  539. {
  540. if(InRange(label, compiler.labels))REPA(cmds)if(cmds[i].type==CMD_LABEL && cmds[i].label_index==label)return i;
  541. compiler.msgs.New().error("Invalid label index", compiler.source, token_index);
  542. return -1;
  543. }
  544. static void DestroyGoto(Int start_command, Int goto_label_index, Memc<Command> &cmds, Compiler &compiler, Memc<Byte> &code, Int token_index)
  545. {
  546. if(compiler.live_locals.elms()) // only if there are live locals
  547. {
  548. Int goto_command_index=LabelCommandIndex(goto_label_index, cmds, compiler, token_index);
  549. REPA(compiler.live_locals) // process in reversed order (ORDER IS IMPORTANT)
  550. {
  551. Local &local=compiler.locals[compiler.live_locals[i]];
  552. if(local.block_scope) // only block scope
  553. {
  554. Int var_start= local.scope_start, // command index of variable creation
  555. var_end =LabelCommandIndex(local.scope_label, cmds, compiler, token_index); // command index of variable destruction
  556. if(goto_command_index<=var_start // include start, so in case of jumping we will delete the variable, because it will be created in that command again , example: "0) Str s="123"; 1) goto 0;"
  557. || goto_command_index> var_end ) // don't include end , because at end, the variable gets destroyed, and we don't want to destroy it twice (here and at end), example: "0) Str s; 1) goto 2; 2) s.Str::~Str();"
  558. DestroyLiveLocal(i, false, compiler, code); // don't remove from live locals, as goto may be optional, example: "{Str s; if(x)goto end; s.clear();} end:"
  559. }
  560. }
  561. }
  562. }
  563. static void CreateVar(Expr &var, Token &token, Symbol &symbol, Compiler &compiler, Memc<Byte> &code)
  564. {
  565. var.create(token, compiler); // create the variable 'x' expression from its token (don't map it in the stack here, so it will be mapped after any parameters/default values)
  566. if(var.symbol.modifiers&Symbol::MODIF_REF) // reference - "TYPE &x="
  567. {
  568. if(var.symbol.isArray() )compiler.msgs.New().error("Arrays of references are not supported", &token);else
  569. if(!(symbol.modifiers&Symbol::MODIF_DEF_VALUE))compiler.msgs.New().error("References must be initialized" , &token);else
  570. {
  571. Expr expr;
  572. if(!symbol.source || compiler.compileTokens(symbol.def_val_range.x, symbol.def_val_range.y, expr, &symbol.source->tokens)!=COMPILE_FULL)compiler.msgs.New().error("Can't compile tokens", symbol.source, symbol.def_val_range.x);else
  573. if(!expr.castTo(var.symbol, compiler))compiler.msgs.New().error(S+"Can't initialize '"+var.symbol.modifName(true, true, true)+"' from '"+expr.symbol.modifName(true, true, false), &token);else
  574. {
  575. expr.setBlockScope(compiler, var.mem.type==Expr::Memory::GLOBAL); // the value will be held by reference so set it as block scope (do this before code compilation), if we're initializing global reference then it means we're compiling "global var init" and block scope temporaries should be held in heap memory
  576. CompileExpr(expr, compiler, code); // compile the source expression so later we can get its address
  577. compiler.mapVar(var.mem); // make sure that var is mapped
  578. if(var.mem.type==Expr::Memory::LOCAL)
  579. {
  580. if(!InRange(var.mem.index, compiler.locals))compiler.msgs.New().error("Local variable index is invalid", &token);else
  581. {
  582. Local &local=compiler.locals[var.mem.index];
  583. if(local.stack_offset<0)compiler.msgs.New().error("Local variable stack offset is invalid", &token);
  584. else CompileSetAddrStackHeap(expr, compiler, !local.force_heap, local.stack_offset, code); // add instruction that sets the 'expr' address into the 'var' reference
  585. }
  586. }else
  587. if(var.mem.type==Expr::Memory::GLOBAL)
  588. {
  589. if(var.mem.index<0)compiler.msgs.New().error("Global variable heap offset is invalid", &token);
  590. else CompileSetAddrStackHeap(expr, compiler, false, var.mem.index, code); // add instruction that sets the 'expr' address into the 'var' reference
  591. }else
  592. {
  593. compiler.msgs.New().error("Invalid variable memory type for CreateVar", &token);
  594. }
  595. }
  596. }
  597. }else
  598. if(var.symbol.anyPtr() || (var.symbol && var.symbol->var_type!=VAR_NONE)) // basic type obj or pointer - "int x", "Obj *x"
  599. {
  600. if(symbol.modifiers&Symbol::MODIF_DEF_VALUE)
  601. {
  602. if(var.symbol.isArray())compiler.msgs.New().error("Variable initialization as array is not yet supported", &token);else
  603. {
  604. var.symbol.setConst(false); // disable const to allow setting the value
  605. Memc<Expr> expr; Swap(expr.New(), var); expr.New().setOperator(u"=").origin=&token; expr.New().setOperator(u"("); Bool ok=(symbol.source && compiler.appendTokens(expr, symbol.def_val_range.x, symbol.def_val_range.y, &symbol.source->tokens)); expr.New().setOperator(u")"); // create the "var=(..)" expressions
  606. Expr result;
  607. if(!ok || compiler.compileExpr(expr, token.parent, result)!=COMPILE_FULL)compiler.msgs.New().error("Can't compile tokens", symbol.source, symbol.def_val_range.x);else
  608. {
  609. if(compiler.ctx && compiler.ctx->store_known_global_var_on_heap
  610. && result.mem.type==Expr::Memory::GLOBAL && result.mem.index>=0 // if we're initializing global var
  611. && result.func.elms()==1 && result.func[0]=='=' && result.func[0].params.elms()==2 && result.func[0].params[1].known()) // and the result is known then store that value on the heap instead of as codes (check known for the 'b' in "a=b" because 'a' known may be disabled)
  612. {
  613. // we must first convert to target type in case of "int=flt"
  614. Expr temp, &func=result.func[0];
  615. temp.mem.setKnown();
  616. temp.symbol=result.symbol;
  617. temp.calculateKnown(func, func.params[0], func.params[1], compiler);
  618. U64 raw=temp.asRaw(compiler); compiler.varHeapConstant(result.mem.index+result.mem.offset, &raw, result.symbol.rawSize(true));
  619. }else
  620. {
  621. CompileExpr(result, compiler, code);
  622. }
  623. Swap(result, var);
  624. }
  625. }
  626. }
  627. }else // class object or array - "Vec x", "Vec x(..)", "Str x(..)", "Vec v[2];", "Vec v[]={..}"
  628. {
  629. if(var.symbol.isArray())compiler.msgs.New().error("Variable initialization as array is not yet supported", &token);else
  630. {
  631. var.instance=false; // disable instance to allow constructor call
  632. Memc<Expr> expr; Swap(expr.New(), var); expr.New().setOperator(u"("); // create the "var(..)" expressions
  633. Bool ok=((symbol.modifiers&Symbol::MODIF_DEF_VALUE) ? symbol.source && compiler.appendTokens(expr, symbol.def_val_range.x, symbol.def_val_range.y, &symbol.source->tokens) : true);
  634. expr.New().setOperator(u")");
  635. Expr result;
  636. if(!ok || compiler.compileExpr(expr, token.parent, result)!=COMPILE_FULL)compiler.msgs.New().error("Can't compile tokens", symbol.source, symbol.def_val_range.x);else
  637. {
  638. CompileExpr(result, compiler, code); Swap(result, var);
  639. }
  640. }
  641. }
  642. DestroyInstructScopes(compiler, code);
  643. }
  644. /******************************************************************************/
  645. // TODO: optimizations:
  646. // replace "goto label; ..; label: goto label2;" with "goto label2; ..; label: goto label2;" (replace jump->jump with jump)
  647. // skip "goto label; label:" jumps to labels right after instruction
  648. #if 1
  649. static Bool FollowedByLabel(Memc<Command> &cmds, Int i, Int label_index) // this checks following command if it's a label (version below checks all labels, however this is no longer used since from now, labels can also generate codes - calling destructors)
  650. {
  651. if(InRange(i+1, cmds)){Command &cmd=cmds[i+1]; return cmd.type==CMD_LABEL && cmd.label_index==label_index;}
  652. return false;
  653. }
  654. #else
  655. static Bool FollowedByLabel(Memc<Command> &cmds, Int i, Int label_index) // check all following labels if one of them is 'label_index' (break on any other command)
  656. {
  657. for(; ++i<cmds.elms(); ) // start from 1 after 'i'
  658. {
  659. Command &cmd=cmds[i];
  660. if(cmd.type!=CMD_LABEL)break;
  661. if(cmd.label_index==label_index)return true;
  662. }
  663. return false;
  664. }
  665. #endif
  666. void Compiler::compile(Memc<Command> &cmds, Mems<Byte> &code, Symbol &func, Symbol::Modif *result_value)
  667. {
  668. Memc<Int > label_code_pos; label_code_pos.setNum(labels); // code position for labels
  669. Memc<Int > gotos; // code position for goto commands
  670. Memc<Byte> temp ; // use temporary Memc for faster creation of the codes
  671. Bool has_return=false;
  672. FREPA(cmds)
  673. {
  674. Command &cmd=cmds[i];
  675. cmd.code_pos=temp.elms(); // set code position of current command
  676. cmd_index =i; // set index of currently compiled command
  677. scope_label =cmd.scope_label; // set index of scope label
  678. switch(cmd.type)
  679. {
  680. default: msgs.New().error("Unsupported CMD_TYPE", source, cmd.startTokenIndex()); break;
  681. case CMD_LABEL:
  682. {
  683. if(!InRange(cmd.label_index, labels))msgs.New().error("Unknown label", source, cmd.startTokenIndex());else
  684. {
  685. label_code_pos(cmd.label_index)=cmd.code_pos;
  686. DestroyBlockScopes(cmd.label_index, T, temp);
  687. }
  688. }break;
  689. case CMD_INSTRUCT:
  690. {
  691. Bool has_vars=false;
  692. for(Int i=cmd.raw_range.x; i<=cmd.raw_range.y && InRange(i, tokens); i++) // check if in the token range there is a variable definition "int x;", "int a=0, b=a;", "Vec v(0,0,0);" (order is important)
  693. {
  694. Token &token=*tokens[i]; if(token.def_decl)if(Symbol *symbol=token.symbol()) // TYPE x; TYPE x=..; TYPE x(..);
  695. {
  696. has_vars=true;
  697. if(symbol->isVar()) // ignore functions, classes or some other declarations
  698. {
  699. Expr var; CreateVar(var, token, *symbol, T, temp);
  700. }
  701. }
  702. }
  703. if(!has_vars) // if there is no local variable definition then just process the commands
  704. {
  705. Expr expr;
  706. if(compileTokens(cmd.raw_range.x, cmd.raw_range.y, expr)!=COMPILE_FULL)msgs.New().error("Can't compile tokens", source, cmd.startTokenIndex());else
  707. {
  708. CompileExpr(expr, T, temp);
  709. DestroyInstructScopes(T, temp);
  710. }
  711. }
  712. }break;
  713. case CMD_RETURN: // RESULT PARENT PARAMS
  714. {
  715. has_return=true;
  716. if(cmd.raw_range.x <= cmd.raw_range.y) // "return x;"
  717. {
  718. if(!result_value)msgs.New().error("Function can't return a value", source, cmd.startTokenIndex());else
  719. {
  720. Expr result, value;
  721. result.instance=true;
  722. result.symbol =*result_value;
  723. result.mem.setResult();
  724. if(result.symbol.modifiers&Symbol::MODIF_REF)
  725. {
  726. if(compileTokens(cmd.raw_range.x, cmd.raw_range.y, value)!=COMPILE_FULL)msgs.New().error("Can't compile tokens", source, cmd.raw_range.x);else
  727. if(!value.castTo(result.symbol, T))msgs.New().error(S+"Can't initialize '"+result.symbol.modifName(true, true, true)+"' from '"+value.symbol.modifName(true, true, false), source, cmd.raw_range.x);else
  728. {
  729. CompileExpr (value, T, temp); // compile the source expression so later we can get its address
  730. CompileSetAddrResult(value, T, temp); // add instruction that sets the 'value' address into the 'result' reference
  731. }
  732. }else
  733. if(result.basicType() && !result.symbol.isArray()) // basic type obj or pointer - "int x", "Obj *x"
  734. {
  735. result.symbol.setConst(false); // disable const to allow setting the value
  736. Memc<Expr> expr; Swap(expr.New(), result); expr.New().setOperator(u"="); expr.New().setOperator(u"("); Bool ok=appendTokens(expr, cmd.raw_range.x, cmd.raw_range.y); expr.New().setOperator(u")"); // create the "result=(..)" expressions
  737. if(!ok || compileExpr(expr, InRange(cmd.raw_range.x, tokens) ? tokens[cmd.raw_range.x]->parent : null, value)!=COMPILE_FULL)msgs.New().error("Can't compile tokens", source, cmd.raw_range.x);
  738. else CompileExpr(value, T, temp);
  739. }else
  740. if(!result.basicType() && result.symbol.isObj()) // class object - "Vec x", "Vec x(..)", "Str x(..)"
  741. {
  742. if(compileTokens(cmd.raw_range.x, cmd.raw_range.y, value)!=COMPILE_FULL)msgs.New().error("Can't compile tokens", source, cmd.raw_range.x);else
  743. {
  744. // check if we're returning temporary object of the same type, in that case we can map it to the return value already
  745. if(value.fullTemporary() && value.symbol.same(result.symbol, false, true) && !LostConst(value.symbol.const_level, result.symbol.const_level) && unmappedLocal(value.mem))
  746. {
  747. value.mem=result.mem;
  748. if(value.func.elms()) // ctors need to have parent changed too
  749. {
  750. Expr &func=value.func[0];
  751. if(func.symbol && (func.symbol->modifiers&Symbol::MODIF_CTOR))
  752. {
  753. DEBUG_ASSERT(func.parent.elms(), "no parent for ctor");
  754. if(func.parent.elms())func.parent[0].mem=value.mem;
  755. }
  756. }
  757. CompileExpr(value, T, temp);
  758. }else // convert result to class constructor, and convert "return x" -> "result(x)" (as in ctor call)
  759. {
  760. result.instance=false; // disable instance to allow constructor call
  761. Expr ctor, &func=ctor.func.New(); Swap(func, result); Swap(func.params.New(), value);
  762. if(ctor.calculate(T)>CAST_NONE)CompileExpr(ctor, T, temp);
  763. }
  764. }
  765. }else
  766. {
  767. // TODO:
  768. msgs.New().error("Returning value not yet supported", source, cmd.startTokenIndex());
  769. }
  770. }
  771. }else // "return;"
  772. {
  773. if(result_value)msgs.New().error("Function must return a value", source, cmd.startTokenIndex());
  774. }
  775. DestroyInstructScopes(T, temp);
  776. goto write_goto; // jump to end of the function ('label_index' was set earlier)
  777. }break;
  778. case CMD_BREAK : if(InRange(cmd.label_index, labels))goto write_goto; msgs.New().error("Invalid 'break'" ); break;
  779. case CMD_CONTINUE: if(InRange(cmd.label_index, labels))goto write_goto; msgs.New().error("Invalid 'continue'"); break;
  780. case CMD_GOTO:
  781. write_goto:
  782. {
  783. if(!InRange(cmd.label_index, labels))msgs.New().error("Unknown label for 'goto' command", source, cmd.startTokenIndex());else
  784. if(!FollowedByLabel(cmds, i, cmd.label_index)) // check if we're not using goto to jump to command after us
  785. {
  786. // destroy all block scopes that are out of range
  787. DestroyGoto(cmd_index, cmd.label_index, cmds, T, temp, cmd.startTokenIndex());
  788. // write jump code
  789. Int pos =temp.addNum(Call::Size(1)); // 1 param (label index)
  790. Call &call=*(Call*)(&temp[pos]);
  791. call.params=1;
  792. call.func =GetGotoCall(); if(!call.func)msgs.New().error("Unknown 'goto' func", source, cmd.startTokenIndex());
  793. call.param(0).setInstructI(cmd.label_index); // REMAP needed
  794. gotos.add(pos); // add to goto container
  795. }
  796. }break;
  797. case CMD_GOTO_COND : // if( cond)goto label; -> if(cond!=0)goto label;
  798. case CMD_GOTO_COND_N: // if(!cond)goto label; -> if(cond==0)goto label;
  799. {
  800. Expr cond;
  801. if(!InRange(cmd.label_index, labels))msgs.New().error("Unknown label for 'goto conditional' command", source, cmd.startTokenIndex());else
  802. {
  803. Bool has_var=false;
  804. for(Int i=cmd._for.cond_range.x; i<=cmd._for.cond_range.y && InRange(i, tokens); i++) // check if in the token range there is a variable definition "int x;", "int a=0, b=a;", "Vec v(0,0,0);" (order is important)
  805. {
  806. Token &token=*tokens[i]; if(token.def_decl)if(Symbol *symbol=token.symbol()) // TYPE x; TYPE x=..; TYPE x(..);
  807. {
  808. if(has_var){msgs.New().error("Multiple variable definition inside condition is invalid", source, cmd.startTokenIndex()); break;}
  809. has_var=true;
  810. if(symbol->isVar())CreateVar(cond, token, *symbol, T, temp);else msgs.New().error("Invalid symbol declaration inside condition", source, cmd.startTokenIndex());
  811. }
  812. }
  813. if(!has_var)
  814. {
  815. if(compileTokens(cmd._for.cond_range.x, cmd._for.cond_range.y, cond)!=COMPILE_FULL){msgs.New().error("Can't compile tokens", source, cmd.startTokenIndex()); break;}
  816. }
  817. if(cond.known())
  818. {
  819. if(cond.asBool(T)==(cmd.type==CMD_GOTO_COND))goto write_goto; // for GOTO_COND type add goto only if "cond!=0", for GOTO_COND_N type add goto only if "cond==0" (in other case don't do anything)
  820. }else
  821. {
  822. if(has_var) // compilation of calculation of 'cond' was already performed in 'CreateLocal', now we need to make sure that it can be casted to bool
  823. {
  824. // leave only raw data (memory layout) without calculation
  825. cond.parent.del(); // delete parent calculation
  826. cond.func .del(); // delete func calculation
  827. cond.params.del(); // delete params calculation
  828. }
  829. if(!cond.castToConditional())msgs.New().error("Can't convert to bool", source, cmd.startTokenIndex());else
  830. {
  831. // check if the condition result needs to be copied into temporary conditional before destroying it
  832. if(!has_var) // don't need to test if it's a named variable
  833. {
  834. if(cond.symbol.modifiers&Symbol::MODIF_REF) // returned value is a reference to something (it may point to some member of the object about to be destroyed) "class X {int x=1; ~X(){x=0;} int& ref(){return x;}} void test() {if(X().ref());}"
  835. {
  836. REPA(live_locals)if(!locals[live_locals[i]].block_scope) // if there is at least one variable that needs to be destroyed
  837. {
  838. cond.copyCtor(T);
  839. break;
  840. }
  841. }else
  842. if(cond.mem.type==Expr::Memory::LOCAL && InRange(cond.mem.index, locals)) // check if the condition result is a local variable
  843. REPA(live_locals)if(live_locals[i]==cond.mem.index) // check if that local variable is live and needs to be destroyed
  844. {
  845. if(!locals[cond.mem.index].block_scope)cond.copyCtor(T); // if not block scope then copy it to temporary
  846. break;
  847. }
  848. }
  849. // first compile expression
  850. CompileExpr(cond, T, temp);
  851. DestroyInstructScopes(T, temp); // call destructors before doing goto
  852. if(!FollowedByLabel(cmds, i, cmd.label_index)) // check if we're not using goto to jump to command after us
  853. {
  854. // this should NOT perform destroying block scopes that are out of range
  855. // write conditional jump code
  856. Int pos =temp.addNum(Call::Size(2)); // 2 params (label index, condition)
  857. Call &call=*(Call*)(&temp[pos]);
  858. call.params=2;
  859. call.func =((cmd.type==CMD_GOTO_COND) ? GetGotoCondCall(cond.symbol) : GetGotoCondNCall(cond.symbol)); if(!call.func)msgs.New().error("Unknown 'goto conditional' func", source, cmd.startTokenIndex());
  860. call.param(0).setInstructI(cmd.label_index); // REMAP needed
  861. call.param(1).set (cond, T);
  862. gotos.add(pos); // add to goto container
  863. }
  864. }
  865. }
  866. }
  867. }break;
  868. }
  869. }
  870. // copy into final memory
  871. code=temp;
  872. // adjust goto's (replace 'label_index' with 'label_code_pos[label_index]') (do this after having final memory of codes)
  873. REPA(gotos)
  874. {
  875. Call &call =*(Call*)(&code[gotos[i]]);
  876. Int label_index=call.param(0).getInstructI();
  877. call.param(0).addr=code.data()+label_code_pos[label_index];
  878. }
  879. // check if function does not return anything
  880. if(result_value && !has_return)msgs.New().error("Function does not return a value", &func);
  881. }
  882. void CodeEnvironment::FuncBody::del()
  883. {
  884. zero();
  885. code.del();
  886. name.del();
  887. }
  888. void CodeEnvironment::FuncBody::create(Symbol &func, Memc<Message> &msgs, CompilerContext &ctx)
  889. {
  890. del();
  891. // RESULT PARENT PARAMS
  892. Symbol::Modif result;
  893. Bool has_result =func.hasResult(null, &result);
  894. Int param_stack_size=0;
  895. REPAO(func.children )->raw_offset=-1; // make sure that each local doesn't have 'raw_offset' set
  896. REPAO(func.params )->raw_offset=-1;
  897. FREP (func.realParams()) // order important
  898. {
  899. Symbol::Modif value=func.params[i]->value; value.proceedToFinal(null);
  900. if(!value || value->type==Symbol::TYPENAME)msgs.New().error("Uknown parameter type", func.params[i]());
  901. Int size =value.rawSize(true),
  902. align=value.firstMemberSize();
  903. func.params[i]->raw_offset=AlignAddress(param_stack_size, align); // ALIGN
  904. param_stack_size=func.params[i]->raw_offset+size;
  905. }
  906. if(Source *source=func.source)
  907. {
  908. Memc<Command> cmds, exp; source->parseFunc(func, cmds, msgs);
  909. //FileText ft; ft.append("c:/funcs.txt"); ListFunction(ft, func, cmds);
  910. Compiler compiler(msgs, source->tokens, source, &ctx);
  911. // set label indexes
  912. REPA(func.children)if(func.children[i]->type==Symbol::LABEL)func.children[i]->raw_offset=compiler.labels++;
  913. Int label_body_scope=compiler.labels++,
  914. label_return =compiler.labels++;
  915. compiler.expand(cmds, exp, -1, -1, label_return, label_body_scope);
  916. {Command &d=exp.New(); d.type=CMD_LABEL; d.label_index=label_body_scope;} // place body scope label at the end of the codes (this deletes all variables)
  917. {Command &d=exp.New(); d.type=CMD_LABEL; d.label_index=label_return ;} // place return label at the end of the codes
  918. compiler.compile(exp, code, func, has_result ? &result : null);
  919. name=FuncName(func);
  920. stack_size=Ceil8(compiler.stack_size); // ALIGN, use Ceil8 to make sure that newer variables will have 8 byte alignment
  921. }
  922. }
  923. // TODO: statics in functions "void func() {Str s; static Str ss=s;}"
  924. // TODO: vars as known, setup into heap constants instead of instructions
  925. struct GlobalVarInit
  926. {
  927. };
  928. void CodeEnvironment::createGlobalVarSetup(Int func_index, Memc<Message> &msgs, CompilerContext &ctx)
  929. {
  930. if(InRange(func_index , func_bodies)
  931. && InRange(func_index+1, func_bodies))
  932. {
  933. Str temp;
  934. Memc<Symbol* > vars, sorted;
  935. Memc<Symbol::Modif> templates;
  936. // get global vars and clear dependencies for all symbols
  937. FREPA(Symbols)
  938. {
  939. Symbol &symbol=Symbols.lockedData(i);
  940. symbol.dependencies.del();
  941. FlagSet(symbol.helper, Symbol::HELPER_PROCESSED, true);
  942. if(symbol.source && symbol.source->active && symbol.valid && symbol.isVar() && symbol.isGlobalOrStatic())
  943. {
  944. FlagSet(symbol.helper, Symbol::HELPER_PROCESSED, false);
  945. vars.add(&symbol);
  946. }
  947. }
  948. // setup dependencies
  949. REPA(vars)
  950. {
  951. Symbol &symbol=*vars[i];
  952. if(symbol.modifiers&Symbol::MODIF_DEF_VALUE)
  953. {
  954. for(Int i=symbol.def_val_range.x; i<=symbol.def_val_range.y; )
  955. {
  956. Int start=i;
  957. if(Symbol *s=GetFullSymbol(symbol.source->tokens, i, temp, symbol.parent(), templates))
  958. {
  959. if(!(s->helper&Symbol::HELPER_PROCESSED))symbol.dependencies.include(s);
  960. }
  961. MAX(i, start+1);
  962. }
  963. }
  964. }
  965. // process all that haven't got left any dependencies "int a=1, b=a+1, c=func(), d=func()+a" (process 'a', and then 'b', but not 'c' 'd')
  966. REPA(vars)
  967. {
  968. Symbol &symbol=*vars[i];
  969. REPA(symbol.dependencies) // check if any dependency got processed
  970. {
  971. Symbol &dep=*symbol.dependencies[i];
  972. if(dep.helper&Symbol::HELPER_PROCESSED)symbol.dependencies.remove(i); // if dependency was processed then remove it
  973. }
  974. if(!symbol.dependencies.elms()) // if no dependencies got left
  975. {
  976. FlagEnable(symbol.helper, Symbol::HELPER_PROCESSED); // set as processed
  977. sorted.add(&symbol); // add to processed
  978. vars.remove(i); // remove from to process
  979. }
  980. }
  981. // process all that haven't got left any dependencies on vars "int a, c=func(), d=func()+a" (process 'c' but not 'd')
  982. REPA(vars)
  983. {
  984. Symbol &symbol=*vars[i];
  985. REPA(symbol.dependencies) // check if any dependency got processed
  986. {
  987. Symbol &dep=*symbol.dependencies[i];
  988. if((dep.helper&Symbol::HELPER_PROCESSED) // if processed
  989. || !(dep.source && dep.source->active && dep.valid && dep.isVar() && dep.isGlobalOrStatic())) // or not a global var
  990. symbol.dependencies.remove(i); // remove dependency
  991. }
  992. if(!symbol.dependencies.elms()) // if no dependencies got left
  993. {
  994. FlagEnable(symbol.helper, Symbol::HELPER_PROCESSED); // set as processed
  995. sorted.add(&symbol); // add to processed
  996. vars.remove(i); // remove from to process
  997. }
  998. }
  999. // add all remaining vars
  1000. FREPA(vars)sorted.add(vars[i]);
  1001. // process variables
  1002. Memc<Token*> tokens; // use temporary tokens so we can pass it as dummy reference to compiler construct to avoid changing 'tokens' to pointer with null checks
  1003. Compiler compiler(msgs, tokens, null, &ctx);
  1004. Int label_body_scope=compiler.labels++;
  1005. Memc<Byte > temp_init, temp_shut;
  1006. Memc<Symbol*> dtors;
  1007. // initialize variables
  1008. compiler.scope_label=label_body_scope;
  1009. FREPA(sorted) // in forward order
  1010. {
  1011. Symbol &symbol=*sorted[i];
  1012. if(Token *token=symbol.getToken())
  1013. {
  1014. Expr var; CreateVar(var, *token, symbol, compiler, temp_init);
  1015. if(var.symbol.hasDestructor())dtors.add(&symbol); // upon creation add in same order to destructor list
  1016. }else
  1017. {
  1018. msgs.New().error("Unknown token for variable", &symbol);
  1019. }
  1020. }
  1021. // destroy variables
  1022. REPA(dtors) // first destroy named variables (in reversed order)
  1023. {
  1024. Symbol &symbol=*dtors[i];
  1025. Symbol::Modif final; final=&symbol; final.proceedToFinal(null);
  1026. DestroyVar(final, symbol.getToken(), compiler, temp_shut, false, -1, &symbol);
  1027. }
  1028. DestroyBlockScopes(label_body_scope, compiler, temp_shut); // then destroy temporaries
  1029. // setup function bodies
  1030. FuncBody &init=func_bodies[func_index ],
  1031. &shut=func_bodies[func_index+1];
  1032. init.del(); init.name="global var init"; init.code=temp_init; init.stack_size=Ceil8(compiler.stack_size); // ALIGN, use Ceil8 to make sure that newer variables will have 8 byte alignment
  1033. shut.del(); shut.name="global var shut"; shut.code=temp_shut; shut.stack_size=Ceil8(compiler.stack_size);
  1034. }
  1035. }
  1036. void CodeEnvironment::FuncBody::link(Memc<Message> &msgs, CodeEnvironment &ce)
  1037. {
  1038. Call::Func c[]={GetCallFunc(false, false), GetCallFunc(false, true), GetCallFunc(true, false), GetCallFunc(true, true)};
  1039. for(Int pos=0; pos<code.elms(); )
  1040. {
  1041. Call &call=*(Call*)(&code[pos]);
  1042. REPA(c)if(call.func==c[i]) // if any of the 'call' functions
  1043. {
  1044. if(Symbol *func=(Symbol*)call.param(0).getInstructP())
  1045. {
  1046. if(InRange(func->raw_offset, ce.func_bodies))call.param(0).addr=&ce.func_bodies[func->raw_offset]; // REMAP from Symbol* to FuncBody*
  1047. else msgs.New().error("Invalid func call 'func body'");
  1048. }else msgs.New().error("Invalid func call 'func' pointer");
  1049. break;
  1050. }
  1051. pos+=Call::Size(call.params);
  1052. }
  1053. }
  1054. void CodeEnvironment::FuncBody::optimize(Memc<Message> &msgs, CodeEnvironment &ce)
  1055. {
  1056. for(Int pos=0; pos<code.elms(); )
  1057. {
  1058. Call &call=*(Call*)(&code[pos]);
  1059. REP(call.params)
  1060. {
  1061. Call::Param &param=call.param(i);
  1062. if(param.type==Call::ADDR_HEAP ){param.type=Call::ADDR_GLOBAL ; param.addr=ce.heap.data()+param.offset;}else
  1063. if(param.type==Call::ADDR_HEAP_REF){param.type=Call::ADDR_GLOBAL_REF; param.addr=ce.heap.data()+param.offset;}
  1064. }
  1065. pos+=Call::Size(call.params);
  1066. }
  1067. }
  1068. /******************************************************************************/
  1069. }}
  1070. /******************************************************************************/