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