njvmflw.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. {
  2. Copyright (c) 1998-2011 by Florian Klaempfl and Jonas Maebe
  3. Generate assembler for nodes that influence the flow for the JVM
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. unit njvmflw;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. aasmbase,node,nflw,ncgflw;
  22. type
  23. tjvmfornode = class(tcgfornode)
  24. function pass_1: tnode; override;
  25. end;
  26. tjvmraisenode = class(traisenode)
  27. function pass_typecheck: tnode; override;
  28. function pass_1: tnode; override;
  29. procedure pass_generate_code;override;
  30. end;
  31. tjvmtryexceptnode = class(ttryexceptnode)
  32. procedure pass_generate_code;override;
  33. end;
  34. tjvmtryfinallynode = class(ttryfinallynode)
  35. procedure pass_generate_code;override;
  36. end;
  37. tjvmonnode = class(tonnode)
  38. procedure pass_generate_code;override;
  39. end;
  40. implementation
  41. uses
  42. verbose,globals,systems,globtype,constexp,
  43. symconst,symdef,symsym,aasmtai,aasmdata,aasmcpu,defutil,jvmdef,defcmp,
  44. procinfo,cgbase,pass_1,pass_2,parabase,
  45. cpubase,cpuinfo,
  46. nbas,nld,ncon,ncnv,
  47. tgobj,paramgr,
  48. cgutils,hlcgobj,hlcgcpu
  49. ;
  50. {*****************************************************************************
  51. TFJVMFORNODE
  52. *****************************************************************************}
  53. function tjvmfornode.pass_1: tnode;
  54. var
  55. iteratortmp: ttempcreatenode;
  56. olditerator: tnode;
  57. block,
  58. newbody: tblocknode;
  59. stat,
  60. newbodystat: tstatementnode;
  61. begin
  62. { transform for-loops with enums to:
  63. for tempint:=ord(lowval) to ord(upperval) do
  64. begin
  65. originalctr:=tenum(tempint);
  66. <original loop body>
  67. end;
  68. enums are class instances in Java and hence can't be increased or so.
  69. The type conversion consists of an array lookup in a final method,
  70. so it shouldn't be too expensive.
  71. }
  72. if left.resultdef.typ=enumdef then
  73. begin
  74. block:=internalstatements(stat);
  75. iteratortmp:=ctempcreatenode.create(s32inttype,left.resultdef.size,tt_persistent,true);
  76. addstatement(stat,iteratortmp);
  77. olditerator:=left;
  78. left:=ctemprefnode.create(iteratortmp);
  79. inserttypeconv_explicit(right,s32inttype);
  80. inserttypeconv_explicit(t1,s32inttype);
  81. newbody:=internalstatements(newbodystat);
  82. addstatement(newbodystat,cassignmentnode.create(olditerator,
  83. ctypeconvnode.create_explicit(ctemprefnode.create(iteratortmp),
  84. olditerator.resultdef)));
  85. addstatement(newbodystat,t2);
  86. addstatement(stat,cfornode.create(left,right,t1,newbody,lnf_backward in loopflags));
  87. addstatement(stat,ctempdeletenode.create(iteratortmp));
  88. left:=nil;
  89. right:=nil;
  90. t1:=nil;
  91. t2:=nil;
  92. result:=block
  93. end
  94. else
  95. result:=inherited pass_1;
  96. end;
  97. {*****************************************************************************
  98. SecondRaise
  99. *****************************************************************************}
  100. var
  101. current_except_loc: tlocation;
  102. function tjvmraisenode.pass_typecheck: tnode;
  103. begin
  104. Result:=inherited pass_typecheck;
  105. if codegenerror then
  106. exit;
  107. { Java exceptions must descend from java.lang.Throwable }
  108. if assigned(left) and
  109. not def_is_related(left.resultdef,java_jlthrowable) then
  110. MessagePos2(left.fileinfo,type_e_incompatible_types,left.resultdef.typename,'class(JLThrowable)');
  111. { Java exceptions cannot be raised "at" a specific location }
  112. if assigned(right) then
  113. MessagePos(right.fileinfo,parser_e_illegal_expression);
  114. end;
  115. function tjvmraisenode.pass_1: tnode;
  116. begin
  117. result:=nil;
  118. expectloc:=LOC_VOID;
  119. if assigned(left) then
  120. firstpass(left);
  121. end;
  122. procedure tjvmraisenode.pass_generate_code;
  123. begin
  124. if assigned(left) then
  125. begin
  126. secondpass(left);
  127. thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,left.resultdef,left.location);
  128. end
  129. else
  130. thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,java_jlthrowable,current_except_loc);
  131. current_asmdata.CurrAsmList.Concat(taicpu.op_none(a_athrow));
  132. thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  133. end;
  134. {*****************************************************************************
  135. SecondTryExcept
  136. *****************************************************************************}
  137. var
  138. begintrylabel,
  139. endtrylabel: tasmlabel;
  140. endexceptlabel : tasmlabel;
  141. procedure tjvmtryexceptnode.pass_generate_code;
  142. var
  143. oldendexceptlabel,
  144. oldbegintrylabel,
  145. oldendtrylabel,
  146. defaultcatchlabel: tasmlabel;
  147. oldflowcontrol,tryflowcontrol,
  148. exceptflowcontrol : tflowcontrol;
  149. prev_except_loc: tlocation;
  150. begin
  151. location_reset(location,LOC_VOID,OS_NO);
  152. oldflowcontrol:=flowcontrol;
  153. flowcontrol:=[fc_inflowcontrol];
  154. { this can be called recursivly }
  155. oldbegintrylabel:=begintrylabel;
  156. oldendtrylabel:=endtrylabel;
  157. oldendexceptlabel:=endexceptlabel;
  158. { get new labels for the control flow statements }
  159. current_asmdata.getaddrlabel(begintrylabel);
  160. current_asmdata.getaddrlabel(endtrylabel);
  161. current_asmdata.getjumplabel(endexceptlabel);
  162. { try block }
  163. { set control flow labels for the try block }
  164. hlcg.a_label(current_asmdata.CurrAsmList,begintrylabel);
  165. secondpass(left);
  166. hlcg.a_label(current_asmdata.CurrAsmList,endtrylabel);
  167. tryflowcontrol:=flowcontrol;
  168. { jump over exception handling blocks }
  169. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  170. hlcg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
  171. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  172. { set control flow labels for the except block }
  173. { and the on statements }
  174. flowcontrol:=[fc_inflowcontrol];
  175. { on-statements }
  176. if assigned(right) then
  177. secondpass(right);
  178. { default handling except handling }
  179. if assigned(t1) then
  180. begin
  181. current_asmdata.getaddrlabel(defaultcatchlabel);
  182. current_asmdata.CurrAsmList.concat(tai_jcatch.create(
  183. 'all',begintrylabel,endtrylabel,defaultcatchlabel));
  184. hlcg.a_label(current_asmdata.CurrAsmList,defaultcatchlabel);
  185. { here we don't have to reset flowcontrol }
  186. { the default and on flowcontrols are handled equal }
  187. { get the exception object from the stack and store it for use by
  188. the exception code (in case of an anonymous "raise") }
  189. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  190. prev_except_loc:=current_except_loc;
  191. location_reset_ref(current_except_loc,LOC_REFERENCE,OS_ADDR,4);
  192. tg.GetLocal(current_asmdata.CurrAsmList,sizeof(pint),java_jlthrowable,current_except_loc.reference);
  193. thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1);
  194. thlcgjvm(hlcg).a_load_stack_loc(current_asmdata.CurrAsmList,java_jlthrowable,current_except_loc);
  195. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  196. { and generate the exception handling code }
  197. secondpass(t1);
  198. { free the temp containing the exception and invalidate }
  199. tg.UngetLocal(current_asmdata.CurrAsmList,current_except_loc.reference);
  200. current_except_loc:=prev_except_loc;
  201. exceptflowcontrol:=flowcontrol;
  202. end
  203. else
  204. exceptflowcontrol:=flowcontrol;
  205. hlcg.a_label(current_asmdata.CurrAsmList,endexceptlabel);
  206. { restore all saved labels }
  207. begintrylabel:=oldbegintrylabel;
  208. endtrylabel:=oldendtrylabel;
  209. endexceptlabel:=oldendexceptlabel;
  210. { return all used control flow statements }
  211. flowcontrol:=oldflowcontrol+(exceptflowcontrol +
  212. tryflowcontrol - [fc_inflowcontrol]);
  213. end;
  214. {*****************************************************************************
  215. SecondOn
  216. *****************************************************************************}
  217. procedure tjvmonnode.pass_generate_code;
  218. var
  219. thisonlabel : tasmlabel;
  220. oldflowcontrol : tflowcontrol;
  221. exceptvarsym : tlocalvarsym;
  222. prev_except_loc : tlocation;
  223. begin
  224. location_reset(location,LOC_VOID,OS_NO);
  225. oldflowcontrol:=flowcontrol;
  226. flowcontrol:=[fc_inflowcontrol];
  227. current_asmdata.getjumplabel(thisonlabel);
  228. hlcg.a_label(current_asmdata.CurrAsmList,thisonlabel);
  229. if assigned(excepTSymtable) then
  230. exceptvarsym:=tlocalvarsym(excepTSymtable.SymList[0])
  231. else
  232. internalerror(2011020402);
  233. { add exception catching information for the JVM: exception type
  234. (will have to be adjusted if/when support for catching class
  235. reference types is added), begin/end of code in which the exception
  236. can be raised, and start of this exception handling code }
  237. current_asmdata.CurrAsmList.concat(tai_jcatch.create(
  238. tobjectdef(exceptvarsym.vardef).jvm_full_typename(true),
  239. begintrylabel,endtrylabel,thisonlabel));
  240. { Retrieve exception variable }
  241. { 1) prepare the location where we'll store it }
  242. location_reset_ref(exceptvarsym.localloc,LOC_REFERENCE,OS_ADDR,sizeof(pint));
  243. tg.GetLocal(current_asmdata.CurrAsmList,sizeof(pint),exceptvarsym.vardef,exceptvarsym.localloc.reference);
  244. prev_except_loc:=current_except_loc;
  245. current_except_loc:=exceptvarsym.localloc;
  246. { 2) the exception variable is at the top of the evaluation stack
  247. (placed there by the JVM) -> adjust stack count, then store it }
  248. thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1);
  249. thlcgjvm(hlcg).a_load_stack_loc(current_asmdata.CurrAsmList,exceptvarsym.vardef,current_except_loc);
  250. if assigned(right) then
  251. secondpass(right);
  252. { clear some stuff }
  253. tg.UngetLocal(current_asmdata.CurrAsmList,exceptvarsym.localloc.reference);
  254. exceptvarsym.localloc.loc:=LOC_INVALID;
  255. current_except_loc:=prev_except_loc;
  256. hlcg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
  257. flowcontrol:=oldflowcontrol+(flowcontrol-[fc_inflowcontrol]);
  258. { next on node }
  259. if assigned(left) then
  260. secondpass(left);
  261. end;
  262. {*****************************************************************************
  263. SecondTryFinally
  264. *****************************************************************************}
  265. procedure tjvmtryfinallynode.pass_generate_code;
  266. var
  267. begintrylabel,
  268. endtrylabel,
  269. reraiselabel,
  270. finallylabel,
  271. finallyexceptlabel,
  272. endfinallylabel,
  273. exitfinallylabel,
  274. continuefinallylabel,
  275. breakfinallylabel,
  276. oldCurrExitLabel,
  277. oldContinueLabel,
  278. oldBreakLabel : tasmlabel;
  279. oldflowcontrol,tryflowcontrol : tflowcontrol;
  280. finallycodecopy: tnode;
  281. reasonbuf,
  282. exceptreg: tregister;
  283. begin
  284. oldBreakLabel:=nil;
  285. oldContinueLabel:=nil;
  286. finallycodecopy:=nil;
  287. continuefinallylabel:=nil;
  288. breakfinallylabel:=nil;
  289. { not necessary on a garbage-collected platform }
  290. if implicitframe then
  291. internalerror(2011031803);
  292. location_reset(location,LOC_VOID,OS_NO);
  293. { check if child nodes do a break/continue/exit }
  294. oldflowcontrol:=flowcontrol;
  295. flowcontrol:=[fc_inflowcontrol];
  296. current_asmdata.getjumplabel(finallylabel);
  297. current_asmdata.getjumplabel(endfinallylabel);
  298. current_asmdata.getjumplabel(reraiselabel);
  299. { the finally block must catch break, continue and exit }
  300. { statements }
  301. oldCurrExitLabel:=current_procinfo.CurrExitLabel;
  302. current_asmdata.getjumplabel(exitfinallylabel);
  303. current_procinfo.CurrExitLabel:=exitfinallylabel;
  304. if assigned(current_procinfo.CurrBreakLabel) then
  305. begin
  306. oldContinueLabel:=current_procinfo.CurrContinueLabel;
  307. oldBreakLabel:=current_procinfo.CurrBreakLabel;
  308. current_asmdata.getjumplabel(breakfinallylabel);
  309. current_asmdata.getjumplabel(continuefinallylabel);
  310. current_procinfo.CurrContinueLabel:=continuefinallylabel;
  311. current_procinfo.CurrBreakLabel:=breakfinallylabel;
  312. end;
  313. { allocate reg to store the reason why the finally block was entered
  314. (no exception, break, continue, exit), so we can continue to the
  315. right label afterwards. In case of an exception, we use a separate
  316. (duplicate) finally block because otherwise the JVM's bytecode
  317. verification cannot statically prove that the exception reraise code
  318. will only execute in case an exception actually happened }
  319. reasonbuf:=hlcg.getaddressregister(current_asmdata.CurrAsmList,s32inttype);
  320. { try code }
  321. begintrylabel:=nil;
  322. endtrylabel:=nil;
  323. if assigned(left) then
  324. begin
  325. current_asmdata.getaddrlabel(begintrylabel);
  326. current_asmdata.getaddrlabel(endtrylabel);
  327. hlcg.a_label(current_asmdata.CurrAsmList,begintrylabel);
  328. secondpass(left);
  329. hlcg.a_label(current_asmdata.CurrAsmList,endtrylabel);
  330. tryflowcontrol:=flowcontrol;
  331. if codegenerror then
  332. exit;
  333. { reason: no exception occurred }
  334. hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,0,reasonbuf);
  335. end
  336. else
  337. tryflowcontrol:=[fc_inflowcontrol];
  338. { begin of the finally code }
  339. hlcg.a_label(current_asmdata.CurrAsmList,finallylabel);
  340. { finally code }
  341. flowcontrol:=[fc_inflowcontrol];
  342. { duplicate finally code for case when exception happened }
  343. if assigned(begintrylabel) then
  344. finallycodecopy:=right.getcopy;
  345. secondpass(right);
  346. { goto is allowed if it stays inside the finally block,
  347. this is checked using the exception block number }
  348. if (flowcontrol-[fc_gotolabel])<>[fc_inflowcontrol] then
  349. CGMessage(cg_e_control_flow_outside_finally);
  350. if codegenerror then
  351. begin
  352. if assigned(begintrylabel) then
  353. finallycodecopy.free;
  354. exit;
  355. end;
  356. { don't generate line info for internal cleanup }
  357. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  358. { the reasonbuf holds the reason why this (non-exception) finally code
  359. was executed:
  360. 0 = try code simply finished
  361. 1 = (unused) exception raised
  362. 2 = exit called
  363. 3 = break called
  364. 4 = continue called }
  365. hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,0,reasonbuf,endfinallylabel);
  366. if fc_exit in tryflowcontrol then
  367. if ([fc_break,fc_continue]*tryflowcontrol)<>[] then
  368. hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,2,reasonbuf,oldCurrExitLabel)
  369. else
  370. hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel);
  371. if fc_break in tryflowcontrol then
  372. if fc_continue in tryflowcontrol then
  373. hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,3,reasonbuf,oldBreakLabel)
  374. else
  375. hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel);
  376. if fc_continue in tryflowcontrol then
  377. hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel);
  378. { now generate the trampolines for exit/break/continue to load the reasonbuf }
  379. if fc_exit in tryflowcontrol then
  380. begin
  381. hlcg.a_label(current_asmdata.CurrAsmList,exitfinallylabel);
  382. hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,2,reasonbuf);
  383. hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel);
  384. end;
  385. if fc_break in tryflowcontrol then
  386. begin
  387. hlcg.a_label(current_asmdata.CurrAsmList,breakfinallylabel);
  388. hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,3,reasonbuf);
  389. hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel);
  390. end;
  391. if fc_continue in tryflowcontrol then
  392. begin
  393. hlcg.a_label(current_asmdata.CurrAsmList,continuefinallylabel);
  394. hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,4,reasonbuf);
  395. hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel);
  396. end;
  397. { jump over finally-code-in-case-an-exception-happened }
  398. hlcg.a_jmp_always(current_asmdata.CurrAsmList,endfinallylabel);
  399. { generate finally code in case an exception occurred }
  400. if assigned(begintrylabel) then
  401. begin
  402. current_asmdata.getaddrlabel(finallyexceptlabel);
  403. hlcg.a_label(current_asmdata.CurrAsmList,finallyexceptlabel);
  404. { catch the exceptions }
  405. current_asmdata.CurrAsmList.concat(tai_jcatch.create(
  406. 'all',begintrylabel,endtrylabel,finallyexceptlabel));
  407. { store the generated exception object to a temp }
  408. exceptreg:=hlcg.getaddressregister(current_asmdata.CurrAsmList,java_jlthrowable);
  409. thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1);
  410. thlcgjvm(hlcg).a_load_stack_reg(current_asmdata.CurrAsmList,java_jlthrowable,exceptreg);
  411. { generate the finally code again }
  412. secondpass(finallycodecopy);
  413. finallycodecopy.free;
  414. { reraise the exception }
  415. thlcgjvm(hlcg).a_load_reg_stack(current_asmdata.CurrAsmList,java_jlthrowable,exceptreg);
  416. current_asmdata.CurrAsmList.Concat(taicpu.op_none(a_athrow));
  417. thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  418. end;
  419. hlcg.a_label(current_asmdata.CurrAsmList,endfinallylabel);
  420. { end cleanup }
  421. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  422. current_procinfo.CurrExitLabel:=oldCurrExitLabel;
  423. if assigned(current_procinfo.CurrBreakLabel) then
  424. begin
  425. current_procinfo.CurrContinueLabel:=oldContinueLabel;
  426. current_procinfo.CurrBreakLabel:=oldBreakLabel;
  427. end;
  428. flowcontrol:=oldflowcontrol+(tryflowcontrol-[fc_inflowcontrol]);
  429. end;
  430. begin
  431. cfornode:=tjvmfornode;
  432. craisenode:=tjvmraisenode;
  433. ctryexceptnode:=tjvmtryexceptnode;
  434. ctryfinallynode:=tjvmtryfinallynode;
  435. connode:=tjvmonnode;
  436. end.