nx64flw.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. {
  2. Copyright (c) 2011 by Free Pascal development team
  3. Generate Win64-specific exception handling code
  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 nx64flw;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. node,nflw,ncgflw,psub;
  22. type
  23. tx64raisenode=class(tcgraisenode)
  24. function pass_1 : tnode;override;
  25. end;
  26. tx64onnode=class(tcgonnode)
  27. procedure pass_generate_code;override;
  28. end;
  29. tx64tryexceptnode=class(tcgtryexceptnode)
  30. procedure pass_generate_code;override;
  31. end;
  32. tx64tryfinallynode=class(tcgtryfinallynode)
  33. finalizepi: tcgprocinfo;
  34. constructor create(l,r:TNode);override;
  35. constructor create_implicit(l,r:TNode);override;
  36. function simplify(forinline: boolean): tnode;override;
  37. procedure pass_generate_code;override;
  38. end;
  39. implementation
  40. uses
  41. globtype,globals,verbose,systems,fmodule,
  42. nbas,ncal,nutils,
  43. symconst,symsym,symdef,
  44. cgbase,cgobj,cgutils,tgobj,
  45. cpubase,htypechk,
  46. pass_1,pass_2,
  47. aasmbase,aasmtai,aasmdata,aasmcpu,procinfo,cpupi;
  48. var
  49. endexceptlabel: tasmlabel;
  50. { tx64raisenode }
  51. function tx64raisenode.pass_1 : tnode;
  52. var
  53. statements : tstatementnode;
  54. raisenode : tcallnode;
  55. begin
  56. { difference from generic code is that address stack is not popped on reraise }
  57. if (target_info.system<>system_x86_64_win64) or assigned(left) then
  58. result:=inherited pass_1
  59. else
  60. begin
  61. result:=internalstatements(statements);
  62. raisenode:=ccallnode.createintern('fpc_reraise',nil);
  63. include(raisenode.callnodeflags,cnf_call_never_returns);
  64. addstatement(statements,raisenode);
  65. end;
  66. end;
  67. { tx64onnode }
  68. procedure tx64onnode.pass_generate_code;
  69. var
  70. exceptvarsym : tlocalvarsym;
  71. begin
  72. if (target_info.system<>system_x86_64_win64) then
  73. begin
  74. inherited pass_generate_code;
  75. exit;
  76. end;
  77. location_reset(location,LOC_VOID,OS_NO);
  78. { RTL will put exceptobject into RAX when jumping here }
  79. cg.a_reg_alloc(current_asmdata.CurrAsmList,NR_FUNCTION_RESULT_REG);
  80. { Retrieve exception variable }
  81. if assigned(excepTSymtable) then
  82. exceptvarsym:=tlocalvarsym(excepTSymtable.SymList[0])
  83. else
  84. exceptvarsym:=nil;
  85. if assigned(exceptvarsym) then
  86. begin
  87. exceptvarsym.localloc.loc:=LOC_REFERENCE;
  88. exceptvarsym.localloc.size:=OS_ADDR;
  89. tg.GetLocal(current_asmdata.CurrAsmList,sizeof(pint),voidpointertype,exceptvarsym.localloc.reference);
  90. cg.a_load_reg_ref(current_asmdata.CurrAsmList,OS_ADDR,OS_ADDR,NR_FUNCTION_RESULT_REG,exceptvarsym.localloc.reference);
  91. end;
  92. cg.a_reg_dealloc(current_asmdata.CurrAsmList,NR_FUNCTION_RESULT_REG);
  93. if assigned(right) then
  94. secondpass(right);
  95. { deallocate exception symbol }
  96. if assigned(exceptvarsym) then
  97. begin
  98. tg.UngetLocal(current_asmdata.CurrAsmList,exceptvarsym.localloc.reference);
  99. exceptvarsym.localloc.loc:=LOC_INVALID;
  100. end;
  101. cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION');
  102. cg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
  103. end;
  104. { tx64tryfinallynode }
  105. function reset_regvars(var n: tnode; arg: pointer): foreachnoderesult;
  106. begin
  107. case n.nodetype of
  108. temprefn:
  109. make_not_regable(n,[]);
  110. calln:
  111. include(tprocinfo(arg).flags,pi_do_call);
  112. else
  113. ;
  114. end;
  115. result:=fen_true;
  116. end;
  117. function copy_parasize(var n: tnode; arg: pointer): foreachnoderesult;
  118. begin
  119. case n.nodetype of
  120. calln:
  121. tcgprocinfo(arg).allocate_push_parasize(tcallnode(n).pushed_parasize);
  122. else
  123. ;
  124. end;
  125. result:=fen_true;
  126. end;
  127. constructor tx64tryfinallynode.create(l, r: TNode);
  128. begin
  129. inherited create(l,r);
  130. if (target_info.system=system_x86_64_win64) and
  131. { Don't create child procedures for generic methods, their nested-like
  132. behavior causes compilation errors because real nested procedures
  133. aren't allowed for generics. Not creating them doesn't harm because
  134. generic node tree is discarded without generating code. }
  135. not (df_generic in current_procinfo.procdef.defoptions) then
  136. begin
  137. finalizepi:=tcgprocinfo(current_procinfo.create_for_outlining('$fin$',current_procinfo.procdef.struct,potype_exceptfilter,voidtype,r));
  138. { the init/final code is messing with asm nodes, so inform the compiler about this }
  139. include(finalizepi.flags,pi_has_assembler_block);
  140. { Regvar optimization for symbols is suppressed when using exceptions, but
  141. temps may be still placed into registers. This must be fixed. }
  142. foreachnodestatic(r,@reset_regvars,finalizepi);
  143. end;
  144. end;
  145. constructor tx64tryfinallynode.create_implicit(l, r: TNode);
  146. begin
  147. inherited create_implicit(l, r);
  148. if (target_info.system=system_x86_64_win64) then
  149. begin
  150. if df_generic in current_procinfo.procdef.defoptions then
  151. InternalError(2013012501);
  152. finalizepi:=tcgprocinfo(current_procinfo.create_for_outlining('$fin$',current_procinfo.procdef.struct,potype_exceptfilter,voidtype,r));
  153. include(finalizepi.flags,pi_do_call);
  154. { the init/final code is messing with asm nodes, so inform the compiler about this }
  155. include(finalizepi.flags,pi_has_assembler_block);
  156. finalizepi.allocate_push_parasize(32);
  157. end;
  158. end;
  159. function tx64tryfinallynode.simplify(forinline: boolean): tnode;
  160. begin
  161. result:=inherited simplify(forinline);
  162. if (target_info.system<>system_x86_64_win64) then
  163. exit;
  164. if (result=nil) then
  165. begin
  166. finalizepi.code:=right;
  167. foreachnodestatic(right,@copy_parasize,finalizepi);
  168. right:=ccallnode.create(nil,tprocsym(finalizepi.procdef.procsym),nil,nil,[],nil);
  169. firstpass(right);
  170. { For implicit frames, no actual code is available at this time,
  171. it is added later in assembler form. So store the nested procinfo
  172. for later use. }
  173. if implicitframe then
  174. begin
  175. current_procinfo.finalize_procinfo:=finalizepi;
  176. end;
  177. end;
  178. end;
  179. procedure emit_nop;
  180. var
  181. dummy: TAsmLabel;
  182. begin
  183. { To avoid optimizing away the whole thing, prepend a jumplabel with increased refcount }
  184. current_asmdata.getjumplabel(dummy);
  185. dummy.increfs;
  186. cg.a_label(current_asmdata.CurrAsmList,dummy);
  187. current_asmdata.CurrAsmList.concat(Taicpu.op_none(A_NOP,S_NO));
  188. end;
  189. procedure tx64tryfinallynode.pass_generate_code;
  190. var
  191. trylabel,
  192. endtrylabel,
  193. finallylabel,
  194. endfinallylabel,
  195. templabel,
  196. oldexitlabel: tasmlabel;
  197. oldflowcontrol: tflowcontrol;
  198. catch_frame: boolean;
  199. begin
  200. if (target_info.system<>system_x86_64_win64) then
  201. begin
  202. inherited pass_generate_code;
  203. exit;
  204. end;
  205. location_reset(location,LOC_VOID,OS_NO);
  206. { Do not generate a frame that catches exceptions if the only action
  207. would be reraising it. Doing so is extremely inefficient with SEH
  208. (in contrast with setjmp/longjmp exception handling) }
  209. catch_frame:=implicitframe and
  210. (current_procinfo.procdef.proccalloption=pocall_safecall);
  211. oldflowcontrol:=flowcontrol;
  212. flowcontrol:=[fc_inflowcontrol];
  213. templabel:=nil;
  214. current_asmdata.getjumplabel(trylabel);
  215. current_asmdata.getjumplabel(endtrylabel);
  216. current_asmdata.getjumplabel(finallylabel);
  217. current_asmdata.getjumplabel(endfinallylabel);
  218. oldexitlabel:=current_procinfo.CurrExitLabel;
  219. if implicitframe then
  220. current_procinfo.CurrExitLabel:=finallylabel;
  221. { Start of scope }
  222. { Padding with NOP is necessary here because exceptions in called
  223. procedures are seen at the next instruction, while CPU/OS exceptions
  224. like AV are seen at the current instruction.
  225. So in the following code
  226. raise_some_exception; //(a)
  227. try
  228. pchar(nil)^:='0'; //(b)
  229. ...
  230. without NOP, exceptions (a) and (b) will be seen at the same address
  231. and fall into the same scope. However they should be seen in different scopes.
  232. }
  233. emit_nop;
  234. cg.a_label(current_asmdata.CurrAsmList,trylabel);
  235. { try code }
  236. if assigned(left) then
  237. begin
  238. { fc_unwind_xx tells exit/continue/break statements to emit special
  239. unwind code instead of just JMP }
  240. if not implicitframe then
  241. flowcontrol:=flowcontrol+[fc_catching_exceptions,fc_unwind_exit,fc_unwind_loop];
  242. secondpass(left);
  243. flowcontrol:=flowcontrol-[fc_catching_exceptions,fc_unwind_exit,fc_unwind_loop];
  244. if codegenerror then
  245. exit;
  246. end;
  247. { finallylabel is only used in implicit frames as an exit point from nested try..finally
  248. statements, if any. To prevent finalizer from being executed twice, it must come before
  249. endtrylabel (bug #34772) }
  250. if catch_frame then
  251. begin
  252. current_asmdata.getjumplabel(templabel);
  253. cg.a_label(current_asmdata.CurrAsmList, finallylabel);
  254. { jump over exception handler }
  255. cg.a_jmp_always(current_asmdata.CurrAsmList,templabel);
  256. { Handle the except block first, so endtrylabel serves both
  257. as end of scope and as unwind target. This way it is possible to
  258. encode everything into a single scope record. }
  259. cg.a_label(current_asmdata.CurrAsmList,endtrylabel);
  260. if (current_procinfo.procdef.proccalloption=pocall_safecall) then
  261. begin
  262. handle_safecall_exception;
  263. cg.a_jmp_always(current_asmdata.CurrAsmList,endfinallylabel);
  264. end
  265. else
  266. InternalError(2014031601);
  267. cg.a_label(current_asmdata.CurrAsmList,templabel);
  268. end
  269. else
  270. begin
  271. { same as emit_nop but using finallylabel instead of dummy }
  272. cg.a_label(current_asmdata.CurrAsmList,finallylabel);
  273. finallylabel.increfs;
  274. current_asmdata.CurrAsmList.concat(Taicpu.op_none(A_NOP,S_NO));
  275. cg.a_label(current_asmdata.CurrAsmList,endtrylabel);
  276. end;
  277. { i32913 - if the try..finally block is also inside a try..finally or
  278. try..except block, make a note of any Exit calls so all necessary labels
  279. are generated. [Kit] }
  280. if ((flowcontrol*[fc_exit,fc_break,fc_continue])<>[]) and (fc_inflowcontrol in oldflowcontrol) then
  281. oldflowcontrol:=oldflowcontrol+(flowcontrol*[fc_exit,fc_break,fc_continue]);
  282. flowcontrol:=[fc_inflowcontrol];
  283. { generate finally code as a separate procedure }
  284. if not implicitframe then
  285. tcgprocinfo(current_procinfo).generate_exceptfilter(finalizepi);
  286. { right is a call to finalizer procedure }
  287. secondpass(right);
  288. if codegenerror then
  289. exit;
  290. { normal exit from safecall proc must zero the result register }
  291. if implicitframe and (current_procinfo.procdef.proccalloption=pocall_safecall) then
  292. cg.a_load_const_reg(current_asmdata.CurrAsmList,OS_INT,0,NR_FUNCTION_RESULT_REG);
  293. cg.a_label(current_asmdata.CurrAsmList,endfinallylabel);
  294. { generate the scope record in .xdata }
  295. tcpuprocinfo(current_procinfo).add_finally_scope(trylabel,endtrylabel,
  296. current_asmdata.RefAsmSymbol(finalizepi.procdef.mangledname,AT_FUNCTION),catch_frame);
  297. if implicitframe then
  298. current_procinfo.CurrExitLabel:=oldexitlabel;
  299. flowcontrol:=oldflowcontrol;
  300. end;
  301. { tx64tryexceptnode }
  302. procedure tx64tryexceptnode.pass_generate_code;
  303. var
  304. trylabel,
  305. exceptlabel,oldendexceptlabel,
  306. lastonlabel,
  307. exitexceptlabel,
  308. continueexceptlabel,
  309. breakexceptlabel,
  310. oldCurrExitLabel,
  311. oldContinueLabel,
  312. oldBreakLabel : tasmlabel;
  313. onlabel,
  314. filterlabel: tasmlabel;
  315. oldflowcontrol,tryflowcontrol,
  316. exceptflowcontrol : tflowcontrol;
  317. hnode : tnode;
  318. hlist : tasmlist;
  319. onnodecount : tai_const;
  320. sym : tasmsymbol;
  321. label
  322. errorexit;
  323. begin
  324. if (target_info.system<>system_x86_64_win64) then
  325. begin
  326. inherited pass_generate_code;
  327. exit;
  328. end;
  329. location_reset(location,LOC_VOID,OS_NO);
  330. oldflowcontrol:=flowcontrol;
  331. exceptflowcontrol:=[];
  332. continueexceptlabel:=nil;
  333. breakexceptlabel:=nil;
  334. include(flowcontrol,fc_inflowcontrol);
  335. { this can be called recursivly }
  336. oldBreakLabel:=nil;
  337. oldContinueLabel:=nil;
  338. oldendexceptlabel:=endexceptlabel;
  339. { save the old labels for control flow statements }
  340. oldCurrExitLabel:=current_procinfo.CurrExitLabel;
  341. current_asmdata.getjumplabel(exitexceptlabel);
  342. if assigned(current_procinfo.CurrBreakLabel) then
  343. begin
  344. oldContinueLabel:=current_procinfo.CurrContinueLabel;
  345. oldBreakLabel:=current_procinfo.CurrBreakLabel;
  346. current_asmdata.getjumplabel(breakexceptlabel);
  347. current_asmdata.getjumplabel(continueexceptlabel);
  348. end;
  349. current_asmdata.getjumplabel(exceptlabel);
  350. current_asmdata.getjumplabel(endexceptlabel);
  351. current_asmdata.getjumplabel(lastonlabel);
  352. filterlabel:=nil;
  353. { start of scope }
  354. current_asmdata.getjumplabel(trylabel);
  355. emit_nop;
  356. cg.a_label(current_asmdata.CurrAsmList,trylabel);
  357. { control flow in try block needs no special handling,
  358. just make sure that target labels are outside the scope }
  359. secondpass(left);
  360. tryflowcontrol:=flowcontrol;
  361. if codegenerror then
  362. goto errorexit;
  363. { jump over except handlers }
  364. cg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
  365. { end of scope }
  366. cg.a_label(current_asmdata.CurrAsmList,exceptlabel);
  367. { set control flow labels for the except block }
  368. { and the on statements }
  369. current_procinfo.CurrExitLabel:=exitexceptlabel;
  370. if assigned(oldBreakLabel) then
  371. begin
  372. current_procinfo.CurrContinueLabel:=continueexceptlabel;
  373. current_procinfo.CurrBreakLabel:=breakexceptlabel;
  374. end;
  375. { i32913 - if the try..finally block is also inside a try..finally or
  376. try..except block, make a note of any Exit calls so all necessary labels
  377. are generated. [Kit] }
  378. if ((flowcontrol*[fc_exit,fc_break,fc_continue])<>[]) and (fc_inflowcontrol in oldflowcontrol) then
  379. oldflowcontrol:=oldflowcontrol+(flowcontrol*[fc_exit,fc_break,fc_continue]);
  380. flowcontrol:=[fc_inflowcontrol];
  381. { on statements }
  382. if assigned(right) then
  383. begin
  384. { emit filter table to a temporary asmlist }
  385. hlist:=TAsmList.Create;
  386. current_asmdata.getaddrlabel(filterlabel);
  387. new_section(hlist,sec_rodata_norel,filterlabel.name,4);
  388. cg.a_label(hlist,filterlabel);
  389. onnodecount:=tai_const.create_32bit(0);
  390. hlist.concat(onnodecount);
  391. hnode:=right;
  392. while assigned(hnode) do
  393. begin
  394. if hnode.nodetype<>onn then
  395. InternalError(2011103101);
  396. current_asmdata.getjumplabel(onlabel);
  397. sym:=current_asmdata.RefAsmSymbol(tonnode(hnode).excepttype.vmt_mangledname,AT_DATA,true);
  398. hlist.concat(tai_const.create_rva_sym(sym));
  399. hlist.concat(tai_const.create_rva_sym(onlabel));
  400. current_module.add_extern_asmsym(sym);
  401. cg.a_label(current_asmdata.CurrAsmList,onlabel);
  402. secondpass(hnode);
  403. inc(onnodecount.value);
  404. hnode:=tonnode(hnode).left;
  405. end;
  406. { add 'else' node to the filter list, too }
  407. if assigned(t1) then
  408. begin
  409. hlist.concat(tai_const.create_32bit(-1));
  410. hlist.concat(tai_const.create_rva_sym(lastonlabel));
  411. inc(onnodecount.value);
  412. end;
  413. { now move filter table to permanent list all at once }
  414. current_procinfo.aktlocaldata.concatlist(hlist);
  415. hlist.free;
  416. end;
  417. cg.a_label(current_asmdata.CurrAsmList,lastonlabel);
  418. if assigned(t1) then
  419. begin
  420. { here we don't have to reset flowcontrol }
  421. { the default and on flowcontrols are handled equal }
  422. secondpass(t1);
  423. cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION');
  424. if (flowcontrol*[fc_exit,fc_break,fc_continue]<>[]) then
  425. cg.a_jmp_always(current_asmdata.CurrAsmList,endexceptlabel);
  426. end;
  427. exceptflowcontrol:=flowcontrol;
  428. if fc_exit in exceptflowcontrol then
  429. begin
  430. { do some magic for exit in the try block }
  431. cg.a_label(current_asmdata.CurrAsmList,exitexceptlabel);
  432. cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION');
  433. if (fc_unwind_exit in oldflowcontrol) then
  434. cg.g_local_unwind(current_asmdata.CurrAsmList,oldCurrExitLabel)
  435. else
  436. cg.a_jmp_always(current_asmdata.CurrAsmList,oldCurrExitLabel);
  437. end;
  438. if fc_break in exceptflowcontrol then
  439. begin
  440. cg.a_label(current_asmdata.CurrAsmList,breakexceptlabel);
  441. cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION');
  442. if (fc_unwind_loop in oldflowcontrol) then
  443. cg.g_local_unwind(current_asmdata.CurrAsmList,oldBreakLabel)
  444. else
  445. cg.a_jmp_always(current_asmdata.CurrAsmList,oldBreakLabel);
  446. end;
  447. if fc_continue in exceptflowcontrol then
  448. begin
  449. cg.a_label(current_asmdata.CurrAsmList,continueexceptlabel);
  450. cg.g_call(current_asmdata.CurrAsmList,'FPC_DONEEXCEPTION');
  451. if (fc_unwind_loop in oldflowcontrol) then
  452. cg.g_local_unwind(current_asmdata.CurrAsmList,oldContinueLabel)
  453. else
  454. cg.a_jmp_always(current_asmdata.CurrAsmList,oldContinueLabel);
  455. end;
  456. emit_nop;
  457. cg.a_label(current_asmdata.CurrAsmList,endexceptlabel);
  458. tcpuprocinfo(current_procinfo).add_except_scope(trylabel,exceptlabel,endexceptlabel,filterlabel);
  459. errorexit:
  460. { restore all saved labels }
  461. endexceptlabel:=oldendexceptlabel;
  462. { i32913 - if the try..finally block is also inside a try..finally or
  463. try..except block, make a note of any Exit calls so all necessary labels
  464. are generated. [Kit] }
  465. if ((flowcontrol*[fc_exit,fc_break,fc_continue])<>[]) and (fc_inflowcontrol in oldflowcontrol) then
  466. oldflowcontrol:=oldflowcontrol+(flowcontrol*[fc_exit,fc_break,fc_continue]);
  467. { restore the control flow labels }
  468. current_procinfo.CurrExitLabel:=oldCurrExitLabel;
  469. if assigned(oldBreakLabel) then
  470. begin
  471. current_procinfo.CurrContinueLabel:=oldContinueLabel;
  472. current_procinfo.CurrBreakLabel:=oldBreakLabel;
  473. end;
  474. { return all used control flow statements }
  475. flowcontrol:=oldflowcontrol+(exceptflowcontrol +
  476. tryflowcontrol - [fc_inflowcontrol]);
  477. end;
  478. initialization
  479. craisenode:=tx64raisenode;
  480. connode:=tx64onnode;
  481. ctryexceptnode:=tx64tryexceptnode;
  482. ctryfinallynode:=tx64tryfinallynode;
  483. end.