njvmflw.pas 20 KB

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