njvmflw.pas 20 KB

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