njvmflw.pas 20 KB

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