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