njvmflw.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  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. { not necessary on a garbage-collected platform }
  285. if implicitframe then
  286. internalerror(2011031803);
  287. location_reset(location,LOC_VOID,OS_NO);
  288. { check if child nodes do a break/continue/exit }
  289. oldflowcontrol:=flowcontrol;
  290. flowcontrol:=[fc_inflowcontrol];
  291. current_asmdata.getjumplabel(finallylabel);
  292. current_asmdata.getjumplabel(endfinallylabel);
  293. current_asmdata.getjumplabel(reraiselabel);
  294. { the finally block must catch break, continue and exit }
  295. { statements }
  296. oldCurrExitLabel:=current_procinfo.CurrExitLabel;
  297. current_asmdata.getjumplabel(exitfinallylabel);
  298. current_procinfo.CurrExitLabel:=exitfinallylabel;
  299. if assigned(current_procinfo.CurrBreakLabel) then
  300. begin
  301. oldContinueLabel:=current_procinfo.CurrContinueLabel;
  302. oldBreakLabel:=current_procinfo.CurrBreakLabel;
  303. current_asmdata.getjumplabel(breakfinallylabel);
  304. current_asmdata.getjumplabel(continuefinallylabel);
  305. current_procinfo.CurrContinueLabel:=continuefinallylabel;
  306. current_procinfo.CurrBreakLabel:=breakfinallylabel;
  307. end;
  308. { allocate reg to store the reason why the finally block was entered
  309. (no exception, break, continue, exit), so we can continue to the
  310. right label afterwards. In case of an exception, we use a separate
  311. (duplicate) finally block because otherwise the JVM's bytecode
  312. verification cannot statically prove that the exception reraise code
  313. will only execute in case an exception actually happened }
  314. reasonbuf:=hlcg.getaddressregister(current_asmdata.CurrAsmList,s32inttype);
  315. { try code }
  316. begintrylabel:=nil;
  317. endtrylabel:=nil;
  318. if assigned(left) then
  319. begin
  320. current_asmdata.getaddrlabel(begintrylabel);
  321. current_asmdata.getaddrlabel(endtrylabel);
  322. hlcg.a_label(current_asmdata.CurrAsmList,begintrylabel);
  323. secondpass(left);
  324. hlcg.a_label(current_asmdata.CurrAsmList,endtrylabel);
  325. tryflowcontrol:=flowcontrol;
  326. if codegenerror then
  327. exit;
  328. { reason: no exception occurred }
  329. hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,0,reasonbuf);
  330. end
  331. else
  332. tryflowcontrol:=[fc_inflowcontrol];
  333. { begin of the finally code }
  334. hlcg.a_label(current_asmdata.CurrAsmList,finallylabel);
  335. { finally code }
  336. flowcontrol:=[fc_inflowcontrol];
  337. { duplicate finally code for case when exception happened }
  338. if assigned(begintrylabel) then
  339. finallycodecopy:=right.getcopy;
  340. secondpass(right);
  341. { goto is allowed if it stays inside the finally block,
  342. this is checked using the exception block number }
  343. if (flowcontrol-[fc_gotolabel])<>[fc_inflowcontrol] then
  344. CGMessage(cg_e_control_flow_outside_finally);
  345. if codegenerror then
  346. begin
  347. if assigned(begintrylabel) then
  348. finallycodecopy.free;
  349. exit;
  350. end;
  351. { don't generate line info for internal cleanup }
  352. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoStart));
  353. { the reasonbuf holds the reason why this (non-exception) finally code
  354. was executed:
  355. 0 = try code simply finished
  356. 1 = (unused) exception raised
  357. 2 = exit called
  358. 3 = break called
  359. 4 = continue called }
  360. hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,0,reasonbuf,endfinallylabel);
  361. if fc_exit in tryflowcontrol then
  362. if ([fc_break,fc_continue]*tryflowcontrol)<>[] then
  363. hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,2,reasonbuf,oldCurrExitLabel)
  364. else
  365. hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel);
  366. if fc_break in tryflowcontrol then
  367. if fc_continue in tryflowcontrol then
  368. hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,s32inttype,OC_EQ,3,reasonbuf,oldBreakLabel)
  369. else
  370. hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel);
  371. if fc_continue in tryflowcontrol then
  372. hlcg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel);
  373. { now generate the trampolines for exit/break/continue to load the reasonbuf }
  374. if fc_exit in tryflowcontrol then
  375. begin
  376. hlcg.a_label(current_asmdata.CurrAsmList,exitfinallylabel);
  377. hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,2,reasonbuf);
  378. hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel);
  379. end;
  380. if fc_break in tryflowcontrol then
  381. begin
  382. hlcg.a_label(current_asmdata.CurrAsmList,breakfinallylabel);
  383. hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,3,reasonbuf);
  384. hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel);
  385. end;
  386. if fc_continue in tryflowcontrol then
  387. begin
  388. hlcg.a_label(current_asmdata.CurrAsmList,continuefinallylabel);
  389. hlcg.a_load_const_reg(current_asmdata.CurrAsmList,s32inttype,4,reasonbuf);
  390. hlcg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel);
  391. end;
  392. { jump over finally-code-in-case-an-exception-happened }
  393. hlcg.a_jmp_always(current_asmdata.CurrAsmList,endfinallylabel);
  394. { generate finally code in case an exception occurred }
  395. if assigned(begintrylabel) then
  396. begin
  397. current_asmdata.getaddrlabel(finallyexceptlabel);
  398. hlcg.a_label(current_asmdata.CurrAsmList,finallyexceptlabel);
  399. { catch the exceptions }
  400. current_asmdata.CurrAsmList.concat(tai_jcatch.create(
  401. 'all',begintrylabel,endtrylabel,finallyexceptlabel));
  402. { store the generated exception object to a temp }
  403. exceptreg:=hlcg.getaddressregister(current_asmdata.CurrAsmList,java_jlthrowable);
  404. thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1);
  405. thlcgjvm(hlcg).a_load_stack_reg(current_asmdata.CurrAsmList,java_jlthrowable,exceptreg);
  406. { generate the finally code again }
  407. secondpass(finallycodecopy);
  408. finallycodecopy.free;
  409. { reraise the exception }
  410. thlcgjvm(hlcg).a_load_reg_stack(current_asmdata.CurrAsmList,java_jlthrowable,exceptreg);
  411. current_asmdata.CurrAsmList.Concat(taicpu.op_none(a_athrow));
  412. thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  413. end;
  414. hlcg.a_label(current_asmdata.CurrAsmList,endfinallylabel);
  415. { end cleanup }
  416. current_asmdata.CurrAsmList.concat(tai_marker.create(mark_NoLineInfoEnd));
  417. current_procinfo.CurrExitLabel:=oldCurrExitLabel;
  418. if assigned(current_procinfo.CurrBreakLabel) then
  419. begin
  420. current_procinfo.CurrContinueLabel:=oldContinueLabel;
  421. current_procinfo.CurrBreakLabel:=oldBreakLabel;
  422. end;
  423. flowcontrol:=oldflowcontrol+(tryflowcontrol-[fc_inflowcontrol]);
  424. end;
  425. begin
  426. cfornode:=tjvmfornode;
  427. craisenode:=tjvmraisenode;
  428. ctryexceptnode:=tjvmtryexceptnode;
  429. ctryfinallynode:=tjvmtryfinallynode;
  430. connode:=tjvmonnode;
  431. end.