ncpuflw.pas 19 KB

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