/compiler/x86_64/nx64flw.pas
Pascal | 571 lines | 400 code | 76 blank | 95 comment | 42 complexity | 879c79ef2ccc5aea7ffdd956e08719b4 MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-2.1, LGPL-3.0
1{ 2 Copyright (c) 2011 by Free Pascal development team 3 4 Generate Win64-specific exception handling code 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 nx64flw; 23 24{$i fpcdefs.inc} 25 26interface 27 28 uses 29 node,nflw,ncgflw,psub; 30 31 type 32 tx64raisenode=class(tcgraisenode) 33 procedure pass_generate_code;override; 34 end; 35 36 tx64onnode=class(tcgonnode) 37 procedure pass_generate_code;override; 38 end; 39 40 tx64tryexceptnode=class(tcgtryexceptnode) 41 procedure pass_generate_code;override; 42 end; 43 44 tx64tryfinallynode=class(tcgtryfinallynode) 45 finalizepi: tcgprocinfo; 46 constructor create(l,r:TNode);override; 47 constructor create_implicit(l,r,_t1:TNode);override; 48 function simplify(forinline: boolean): tnode;override; 49 procedure pass_generate_code;override; 50 end; 51 52implementation 53 54 uses 55 cutils,globtype,globals,verbose,systems, 56 nbas,ncal,nmem,nutils, 57 symconst,symbase,symtable,symsym,symdef, 58 cgbase,cgobj,cgcpu,cgutils,tgobj, 59 cpubase,htypechk, 60 parabase,paramgr,pdecsub,pass_1,pass_2,ncgutil,cga, 61 aasmbase,aasmtai,aasmdata,aasmcpu,procinfo,cpupi; 62 63 var 64 endexceptlabel: tasmlabel; 65 66 67{ tx64raisenode } 68 69procedure tx64raisenode.pass_generate_code; 70 begin 71 { difference from generic code is that address stack is not popped on reraise } 72 if (target_info.system<>system_x86_64_win64) or assigned(left) then 73 inherited pass_generate_code 74 else 75 cg.g_call(current_asmdata.CurrAsmList,'FPC_RERAISE'); 76 end; 77 78{ tx64onnode } 79 80procedure tx64onnode.pass_generate_code; 81 var 82 oldflowcontrol : tflowcontrol; 83 exceptvarsym : tlocalvarsym; 84 begin 85 if (target_info.system<>system_x86_64_win64) then 86 begin 87 inherited pass_generate_code; 88 exit; 89 end; 90 91 location_reset(location,LOC_VOID,OS_NO); 92 93 oldflowcontrol:=flowcontrol; 94 flowcontrol:=[fc_inflowcontrol]; 95 96 { RTL will put exceptobject into RAX when jumping here } 97 cg.a_reg_alloc(current_asmdata.CurrAsmList,NR_FUNCTION_RESULT_REG); 98 99 { Retrieve exception variable } 100 if assigned(excepTSymtable) then 101 exceptvarsym:=tlocalvarsym(excepTSymtable.SymList[0]) 102 else 103 exceptvarsym:=nil; 104 105 if assigned(exceptvarsym) then 106 begin 107 exceptvarsym.localloc.loc:=LOC_REFERENCE; 108 exceptvarsym.localloc.size:=OS_ADDR; 109 tg.GetLocal(current_asmdata.CurrAsmList,sizeof(pint),voidpointertype,exceptvarsym.localloc.reference); 110 cg.a_load_reg_ref(current_asmdata.CurrAsmList,OS_ADDR,OS_ADDR,NR_FUNCTION_RESULT_REG,exceptvarsym.localloc.reference); 111 end; 112 cg.a_reg_dealloc(current_asmdata.CurrAsmList,NR_FUNCTION_RESULT_REG); 113 114 if assigned(right) then 115 secondpass(right); 116 117 { deallocate exception symbol } 118 if assigned(exceptvarsym) then 119 begin 120 tg.UngetLocal(current_asmdata.CurrAsmList,exceptvarsym.localloc.reference); 121 exceptvarsym.localloc.loc:=LOC_INVALID; 122 end; 123 cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION'); 124 cg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel); 125 126 flowcontrol:=oldflowcontrol+(flowcontrol-[fc_inflowcontrol]); 127 end; 128 129{ tx64tryfinallynode } 130var 131 seq: longint=0; 132 133 134function create_pd: tprocdef; 135 var 136 st:TSymTable; 137 checkstack: psymtablestackitem; 138 sym:tprocsym; 139 begin 140 { get actual procedure symtable (skip withsymtables, etc.) } 141 st:=nil; 142 checkstack:=symtablestack.stack; 143 while assigned(checkstack) do 144 begin 145 st:=checkstack^.symtable; 146 if st.symtabletype in [staticsymtable,globalsymtable,localsymtable] then 147 break; 148 checkstack:=checkstack^.next; 149 end; 150 { Create a nested procedure, even from main_program_level. } 151 result:=tprocdef.create(max(normal_function_level,st.symtablelevel)+1); 152 result.struct:=current_procinfo.procdef.struct; 153 result.proctypeoption:=potype_exceptfilter; 154 handle_calling_convention(result); 155 sym:=tprocsym.create('$fin$'+tostr(seq)); 156 st.insert(sym); 157 inc(seq); 158 159 result.procsym:=sym; 160 proc_add_definition(result); 161 result.forwarddef:=false; 162 result.aliasnames.insert(result.mangledname); 163 alloc_proc_symbol(result); 164 end; 165 166function reset_regvars(var n: tnode; arg: pointer): foreachnoderesult; 167 begin 168 case n.nodetype of 169 temprefn: 170 make_not_regable(n,[]); 171 calln: 172 include(tprocinfo(arg).flags,pi_do_call); 173 end; 174 result:=fen_true; 175 end; 176 177function copy_parasize(var n: tnode; arg: pointer): foreachnoderesult; 178 begin 179 case n.nodetype of 180 calln: 181 tcgprocinfo(arg).allocate_push_parasize(tcallnode(n).pushed_parasize); 182 end; 183 result:=fen_true; 184 end; 185 186constructor tx64tryfinallynode.create(l, r: TNode); 187 begin 188 inherited create(l,r); 189 if (target_info.system<>system_x86_64_win64) or ( 190 { Don't create child procedures for generic methods, their nested-like 191 behavior causes compilation errors because real nested procedures 192 aren't allowed for generics. Not creating them doesn't harm because 193 generic node tree is discarded without generating code. } 194 assigned(current_procinfo.procdef.struct) and 195 (df_generic in current_procinfo.procdef.struct.defoptions) 196 ) then 197 exit; 198 finalizepi:=tcgprocinfo(cprocinfo.create(current_procinfo)); 199 finalizepi.force_nested; 200 finalizepi.procdef:=create_pd; 201 finalizepi.entrypos:=r.fileinfo; 202 finalizepi.entryswitches:=r.localswitches; 203 finalizepi.exitpos:=current_filepos; // last_endtoken_pos? 204 finalizepi.exitswitches:=current_settings.localswitches; 205 { Regvar optimization for symbols is suppressed when using exceptions, but 206 temps may be still placed into registers. This must be fixed. } 207 foreachnodestatic(r,@reset_regvars,finalizepi); 208 end; 209 210constructor tx64tryfinallynode.create_implicit(l, r, _t1: TNode); 211 begin 212 inherited create_implicit(l, r, _t1); 213 if (target_info.system<>system_x86_64_win64) then 214 exit; 215 216 if assigned(current_procinfo.procdef.struct) and 217 (df_generic in current_procinfo.procdef.struct.defoptions) then 218 InternalError(2013012501); 219 220 finalizepi:=tcgprocinfo(cprocinfo.create(current_procinfo)); 221 finalizepi.force_nested; 222 finalizepi.procdef:=create_pd; 223 224 finalizepi.entrypos:=current_filepos; 225 finalizepi.exitpos:=current_filepos; // last_endtoken_pos? 226 finalizepi.entryswitches:=r.localswitches; 227 finalizepi.exitswitches:=current_settings.localswitches; 228 include(finalizepi.flags,pi_do_call); 229 finalizepi.allocate_push_parasize(32); 230 end; 231 232function tx64tryfinallynode.simplify(forinline: boolean): tnode; 233 begin 234 result:=inherited simplify(forinline); 235 if (target_info.system<>system_x86_64_win64) then 236 exit; 237 if (result=nil) then 238 begin 239 finalizepi.code:=right; 240 foreachnodestatic(right,@copy_parasize,finalizepi); 241 right:=ccallnode.create(nil,tprocsym(finalizepi.procdef.procsym),nil,nil,[]); 242 firstpass(right); 243 { For implicit frames, no actual code is available at this time, 244 it is added later in assembler form. So store the nested procinfo 245 for later use. } 246 if implicitframe then 247 begin 248 current_procinfo.finalize_procinfo:=finalizepi; 249 { don't leave dangling pointer } 250 tcgprocinfo(current_procinfo).final_asmnode:=nil; 251 end; 252 end; 253 end; 254 255procedure emit_nop; 256 var 257 dummy: TAsmLabel; 258 begin 259 { To avoid optimizing away the whole thing, prepend a jumplabel with increased refcount } 260 current_asmdata.getjumplabel(dummy); 261 dummy.increfs; 262 cg.a_label(current_asmdata.CurrAsmList,dummy); 263 current_asmdata.CurrAsmList.concat(Taicpu.op_none(A_NOP,S_NO)); 264 end; 265 266procedure tx64tryfinallynode.pass_generate_code; 267 var 268 trylabel, 269 endtrylabel, 270 finallylabel, 271 endfinallylabel, 272 oldexitlabel: tasmlabel; 273 oldflowcontrol: tflowcontrol; 274 catch_frame: boolean; 275 begin 276 if (target_info.system<>system_x86_64_win64) then 277 begin 278 inherited pass_generate_code; 279 exit; 280 end; 281 282 location_reset(location,LOC_VOID,OS_NO); 283 284 { Do not generate a frame that catches exceptions if the only action 285 would be reraising it. Doing so is extremely inefficient with SEH 286 (in contrast with setjmp/longjmp exception handling) } 287 catch_frame:=implicitframe and ((not has_no_code(t1)) or 288 (current_procinfo.procdef.proccalloption=pocall_safecall)); 289 290 oldflowcontrol:=flowcontrol; 291 flowcontrol:=[fc_inflowcontrol]; 292 293 current_asmdata.getjumplabel(trylabel); 294 current_asmdata.getjumplabel(endtrylabel); 295 current_asmdata.getjumplabel(finallylabel); 296 current_asmdata.getjumplabel(endfinallylabel); 297 oldexitlabel:=current_procinfo.CurrExitLabel; 298 if implicitframe then 299 current_procinfo.CurrExitLabel:=finallylabel; 300 301 { Start of scope } 302 { Padding with NOP is necessary here because exceptions in called 303 procedures are seen at the next instruction, while CPU/OS exceptions 304 like AV are seen at the current instruction. 305 306 So in the following code 307 308 raise_some_exception; //(a) 309 try 310 pchar(nil)^:='0'; //(b) 311 ... 312 313 without NOP, exceptions (a) and (b) will be seen at the same address 314 and fall into the same scope. However they should be seen in different scopes. 315 } 316 317 emit_nop; 318 cg.a_label(current_asmdata.CurrAsmList,trylabel); 319 320 { try code } 321 if assigned(left) then 322 begin 323 { fc_unwind tells exit/continue/break statements to emit special 324 unwind code instead of just JMP } 325 if not implicitframe then 326 include(flowcontrol,fc_unwind); 327 secondpass(left); 328 exclude(flowcontrol,fc_unwind); 329 if codegenerror then 330 exit; 331 end; 332 333 { If the immediately preceding instruction is CALL, 334 its return address must not end up outside the scope, so pad with NOP. } 335 if catch_frame then 336 cg.a_jmp_always(current_asmdata.CurrAsmList,finallylabel) 337 else 338 emit_nop; 339 340 cg.a_label(current_asmdata.CurrAsmList,endtrylabel); 341 342 { Handle the except block first, so endtrylabel serves both 343 as end of scope and as unwind target. This way it is possible to 344 encode everything into a single scope record. } 345 if catch_frame then 346 begin 347 flowcontrol:=[fc_inflowcontrol]; 348 secondpass(t1); 349 { note 1: this is not a 'finally' block, no flow restrictions apply 350 note 2: it contains autogenerated sequential code, flow away is impossible } 351 if flowcontrol<>[fc_inflowcontrol] then 352 CGMessage(cg_e_control_flow_outside_finally); 353 if codegenerror then 354 exit; 355 356 if (current_procinfo.procdef.proccalloption=pocall_safecall) then 357 begin 358 handle_safecall_exception; 359 cg.a_jmp_always(current_asmdata.CurrAsmList,endfinallylabel); 360 end 361 else 362 cg.a_call_name(current_asmdata.CurrAsmList,'FPC_RERAISE_IMPLICIT',false); 363 end; 364 365 flowcontrol:=[fc_inflowcontrol]; 366 cg.a_label(current_asmdata.CurrAsmList,finallylabel); 367 { generate finally code as a separate procedure } 368 if not implicitframe then 369 tcgprocinfo(current_procinfo).generate_exceptfilter(finalizepi); 370 { right is a call to finalizer procedure } 371 secondpass(right); 372 373 if codegenerror then 374 exit; 375 376 { normal exit from safecall proc must zero the result register } 377 if implicitframe and (current_procinfo.procdef.proccalloption=pocall_safecall) then 378 cg.a_load_const_reg(current_asmdata.CurrAsmList,OS_INT,0,NR_FUNCTION_RESULT_REG); 379 380 cg.a_label(current_asmdata.CurrAsmList,endfinallylabel); 381 382 { generate the scope record in .xdata } 383 tx86_64procinfo(current_procinfo).add_finally_scope(trylabel,endtrylabel, 384 current_asmdata.RefAsmSymbol(finalizepi.procdef.mangledname),catch_frame); 385 386 if implicitframe then 387 current_procinfo.CurrExitLabel:=oldexitlabel; 388 flowcontrol:=oldflowcontrol; 389 end; 390 391{ tx64tryexceptnode } 392 393procedure tx64tryexceptnode.pass_generate_code; 394 var 395 trylabel, 396 exceptlabel,oldendexceptlabel, 397 lastonlabel, 398 exitexceptlabel, 399 continueexceptlabel, 400 breakexceptlabel, 401 oldCurrExitLabel, 402 oldContinueLabel, 403 oldBreakLabel : tasmlabel; 404 onlabel, 405 filterlabel: tasmlabel; 406 oldflowcontrol,tryflowcontrol, 407 exceptflowcontrol : tflowcontrol; 408 hnode : tnode; 409 hlist : tasmlist; 410 onnodecount : tai_const; 411 label 412 errorexit; 413 begin 414 if (target_info.system<>system_x86_64_win64) then 415 begin 416 inherited pass_generate_code; 417 exit; 418 end; 419 location_reset(location,LOC_VOID,OS_NO); 420 421 oldflowcontrol:=flowcontrol; 422 flowcontrol:=[fc_inflowcontrol]; 423 { this can be called recursivly } 424 oldBreakLabel:=nil; 425 oldContinueLabel:=nil; 426 oldendexceptlabel:=endexceptlabel; 427 428 { save the old labels for control flow statements } 429 oldCurrExitLabel:=current_procinfo.CurrExitLabel; 430 current_asmdata.getjumplabel(exitexceptlabel); 431 if assigned(current_procinfo.CurrBreakLabel) then 432 begin 433 oldContinueLabel:=current_procinfo.CurrContinueLabel; 434 oldBreakLabel:=current_procinfo.CurrBreakLabel; 435 current_asmdata.getjumplabel(breakexceptlabel); 436 current_asmdata.getjumplabel(continueexceptlabel); 437 end; 438 439 current_asmdata.getjumplabel(exceptlabel); 440 current_asmdata.getjumplabel(endexceptlabel); 441 current_asmdata.getjumplabel(lastonlabel); 442 filterlabel:=nil; 443 444 { start of scope } 445 current_asmdata.getjumplabel(trylabel); 446 emit_nop; 447 cg.a_label(current_asmdata.CurrAsmList,trylabel); 448 449 { control flow in try block needs no special handling, 450 just make sure that target labels are outside the scope } 451 secondpass(left); 452 tryflowcontrol:=flowcontrol; 453 if codegenerror then 454 goto errorexit; 455 456 { jump over except handlers } 457 cg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel); 458 459 { end of scope } 460 cg.a_label(current_asmdata.CurrAsmList,exceptlabel); 461 462 { set control flow labels for the except block } 463 { and the on statements } 464 current_procinfo.CurrExitLabel:=exitexceptlabel; 465 if assigned(oldBreakLabel) then 466 begin 467 current_procinfo.CurrContinueLabel:=continueexceptlabel; 468 current_procinfo.CurrBreakLabel:=breakexceptlabel; 469 end; 470 471 flowcontrol:=[fc_inflowcontrol]; 472 { on statements } 473 if assigned(right) then 474 begin 475 { emit filter table to a temporary asmlist } 476 hlist:=TAsmList.Create; 477 current_asmdata.getdatalabel(filterlabel); 478 new_section(hlist,sec_rodata_norel,filterlabel.name,4); 479 cg.a_label(hlist,filterlabel); 480 onnodecount:=tai_const.create_32bit(0); 481 hlist.concat(onnodecount); 482 483 hnode:=right; 484 while assigned(hnode) do 485 begin 486 if hnode.nodetype<>onn then 487 InternalError(2011103101); 488 { TODO: make it done without using global label } 489 current_asmdata.getglobaljumplabel(onlabel); 490 hlist.concat(tai_const.create_rva_sym(current_asmdata.RefAsmSymbol(tonnode(hnode).excepttype.vmt_mangledname))); 491 hlist.concat(tai_const.create_rva_sym(onlabel)); 492 cg.a_label(current_asmdata.CurrAsmList,onlabel); 493 secondpass(hnode); 494 inc(onnodecount.value); 495 hnode:=tonnode(hnode).left; 496 end; 497 { add 'else' node to the filter list, too } 498 if assigned(t1) then 499 begin 500 hlist.concat(tai_const.create_32bit(-1)); 501 hlist.concat(tai_const.create_rva_sym(lastonlabel)); 502 inc(onnodecount.value); 503 end; 504 { now move filter table to permanent list all at once } 505 maybe_new_object_file(current_asmdata.asmlists[al_typedconsts]); 506 current_asmdata.asmlists[al_typedconsts].concatlist(hlist); 507 hlist.free; 508 end; 509 510 cg.a_label(current_asmdata.CurrAsmList,lastonlabel); 511 if assigned(t1) then 512 begin 513 { here we don't have to reset flowcontrol } 514 { the default and on flowcontrols are handled equal } 515 secondpass(t1); 516 cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION'); 517 if (flowcontrol*[fc_exit,fc_break,fc_continue]<>[]) then 518 cg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel); 519 end; 520 exceptflowcontrol:=flowcontrol; 521 522 if fc_exit in exceptflowcontrol then 523 begin 524 { do some magic for exit in the try block } 525 cg.a_label(current_asmdata.CurrAsmList,exitexceptlabel); 526 cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION'); 527 cg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel); 528 end; 529 530 if fc_break in exceptflowcontrol then 531 begin 532 cg.a_label(current_asmdata.CurrAsmList,breakexceptlabel); 533 cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION'); 534 cg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel); 535 end; 536 537 if fc_continue in exceptflowcontrol then 538 begin 539 cg.a_label(current_asmdata.CurrAsmList,continueexceptlabel); 540 cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION'); 541 cg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel); 542 end; 543 544 emit_nop; 545 cg.a_label(current_asmdata.CurrAsmList,endexceptlabel); 546 tx86_64procinfo(current_procinfo).add_except_scope(trylabel,exceptlabel,endexceptlabel,filterlabel); 547 548errorexit: 549 { restore all saved labels } 550 endexceptlabel:=oldendexceptlabel; 551 552 { restore the control flow labels } 553 current_procinfo.CurrExitLabel:=oldCurrExitLabel; 554 if assigned(oldBreakLabel) then 555 begin 556 current_procinfo.CurrContinueLabel:=oldContinueLabel; 557 current_procinfo.CurrBreakLabel:=oldBreakLabel; 558 end; 559 560 { return all used control flow statements } 561 flowcontrol:=oldflowcontrol+(exceptflowcontrol + 562 tryflowcontrol - [fc_inflowcontrol]); 563 end; 564 565initialization 566 craisenode:=tx64raisenode; 567 connode:=tx64onnode; 568 ctryexceptnode:=tx64tryexceptnode; 569 ctryfinallynode:=tx64tryfinallynode; 570end. 571