PageRenderTime 24ms CodeModel.GetById 15ms app.highlight 4ms RepoModel.GetById 1ms app.codeStats 0ms

/compiler/x86_64/nx64flw.pas

https://github.com/slibre/freepascal
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