nutils.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. {
  2. $Id$
  3. Copyright (c) 1998-2002 by Florian Klaempfl
  4. Type checking and register allocation for inline nodes
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. ****************************************************************************
  17. }
  18. unit nutils;
  19. {$i fpcdefs.inc}
  20. interface
  21. uses
  22. globals,
  23. symsym,node;
  24. const
  25. NODE_COMPLEXITY_INF = 255;
  26. type
  27. { resulttype of functions that process on all nodes in a (sub)tree }
  28. foreachnoderesult = (
  29. { false, continue recursion }
  30. fen_false,
  31. { false, stop recursion }
  32. fen_norecurse_false,
  33. { true, continue recursion }
  34. fen_true,
  35. { true, stop recursion }
  36. fen_norecurse_true
  37. );
  38. foreachnodefunction = function(var n: tnode; arg: pointer): foreachnoderesult of object;
  39. staticforeachnodefunction = function(var n: tnode; arg: pointer): foreachnoderesult;
  40. function foreachnode(var n: tnode; f: foreachnodefunction; arg: pointer): boolean;
  41. function foreachnodestatic(var n: tnode; f: staticforeachnodefunction; arg: pointer): boolean;
  42. procedure load_procvar_from_calln(var p1:tnode);
  43. function maybe_call_procvar(var p1:tnode;tponly:boolean):boolean;
  44. function load_high_value_node(vs:tparavarsym):tnode;
  45. function load_self_node:tnode;
  46. function load_result_node:tnode;
  47. function load_self_pointer_node:tnode;
  48. function load_vmt_pointer_node:tnode;
  49. function is_self_node(p:tnode):boolean;
  50. function call_fail_node:tnode;
  51. function initialize_data_node(p:tnode):tnode;
  52. function finalize_data_node(p:tnode):tnode;
  53. function node_complexity(p: tnode): cardinal;
  54. procedure node_tree_set_filepos(var n:tnode;const filepos:tfileposinfo);
  55. implementation
  56. uses
  57. globtype,verbose,
  58. symconst,symbase,symtype,symdef,symtable,
  59. defutil,defcmp,
  60. nbas,ncon,ncnv,nld,nflw,nset,ncal,nadd,nmem,
  61. cgbase,procinfo,
  62. pass_1;
  63. function foreachnode(var n: tnode; f: foreachnodefunction; arg: pointer): boolean;
  64. var
  65. i: longint;
  66. begin
  67. result := false;
  68. if not assigned(n) then
  69. exit;
  70. case f(n,arg) of
  71. fen_norecurse_false:
  72. exit;
  73. fen_norecurse_true:
  74. begin
  75. result := true;
  76. exit;
  77. end;
  78. fen_true:
  79. result := true;
  80. { result is already false
  81. fen_false:
  82. result := false; }
  83. end;
  84. case n.nodetype of
  85. calln:
  86. begin
  87. { not in one statement, won't work because of b- }
  88. result := foreachnode(tcallnode(n).methodpointer,f,arg) or result;
  89. {$ifdef PASS2INLINE}
  90. result := foreachnode(tcallnode(n).inlinecode,f,arg) or result;
  91. {$endif PASS2INLINE}
  92. end;
  93. ifn, whilerepeatn, forn:
  94. begin
  95. { not in one statement, won't work because of b- }
  96. result := foreachnode(tloopnode(n).t1,f,arg) or result;
  97. result := foreachnode(tloopnode(n).t2,f,arg) or result;
  98. end;
  99. raisen:
  100. result := foreachnode(traisenode(n).frametree,f,arg) or result;
  101. casen:
  102. begin
  103. for i := 0 to tcasenode(n).blocks.count-1 do
  104. if assigned(tcasenode(n).blocks[i]) then
  105. result := foreachnode(pcaseblock(tcasenode(n).blocks[i])^.statement,f,arg) or result;
  106. result := foreachnode(tcasenode(n).elseblock,f,arg) or result;
  107. end;
  108. end;
  109. if n.inheritsfrom(tbinarynode) then
  110. begin
  111. result := foreachnode(tbinarynode(n).right,f,arg) or result;
  112. result := foreachnode(tbinarynode(n).left,f,arg) or result;
  113. end
  114. else if n.inheritsfrom(tunarynode) then
  115. result := foreachnode(tunarynode(n).left,f,arg) or result;
  116. end;
  117. function foreachnodestatic(var n: tnode; f: staticforeachnodefunction; arg: pointer): boolean;
  118. var
  119. i: longint;
  120. begin
  121. result := false;
  122. if not assigned(n) then
  123. exit;
  124. case f(n,arg) of
  125. fen_norecurse_false:
  126. exit;
  127. fen_norecurse_true:
  128. begin
  129. result := true;
  130. exit;
  131. end;
  132. fen_true:
  133. result := true;
  134. { result is already false
  135. fen_false:
  136. result := false; }
  137. end;
  138. case n.nodetype of
  139. calln:
  140. begin
  141. result := foreachnodestatic(tcallnode(n).methodpointer,f,arg) or result;
  142. {$ifdef PASS2INLINE}
  143. result := foreachnodestatic(tcallnode(n).inlinecode,f,arg) or result;
  144. {$endif PASS2INLINE}
  145. end;
  146. ifn, whilerepeatn, forn:
  147. begin
  148. { not in one statement, won't work because of b- }
  149. result := foreachnodestatic(tloopnode(n).t1,f,arg) or result;
  150. result := foreachnodestatic(tloopnode(n).t2,f,arg) or result;
  151. end;
  152. raisen:
  153. result := foreachnodestatic(traisenode(n).frametree,f,arg) or result;
  154. casen:
  155. begin
  156. for i := 0 to tcasenode(n).blocks.count-1 do
  157. if assigned(tcasenode(n).blocks[i]) then
  158. result := foreachnodestatic(pcaseblock(tcasenode(n).blocks[i])^.statement,f,arg) or result;
  159. result := foreachnodestatic(tcasenode(n).elseblock,f,arg) or result;
  160. end;
  161. end;
  162. if n.inheritsfrom(tbinarynode) then
  163. begin
  164. result := foreachnodestatic(tbinarynode(n).right,f,arg) or result;
  165. result := foreachnodestatic(tbinarynode(n).left,f,arg) or result;
  166. end
  167. else if n.inheritsfrom(tunarynode) then
  168. result := foreachnodestatic(tunarynode(n).left,f,arg) or result;
  169. end;
  170. procedure load_procvar_from_calln(var p1:tnode);
  171. var
  172. p2 : tnode;
  173. begin
  174. if p1.nodetype<>calln then
  175. internalerror(200212251);
  176. { was it a procvar, then we simply remove the calln and
  177. reuse the right }
  178. if assigned(tcallnode(p1).right) then
  179. begin
  180. p2:=tcallnode(p1).right;
  181. tcallnode(p1).right:=nil;
  182. end
  183. else
  184. begin
  185. p2:=cloadnode.create_procvar(tcallnode(p1).symtableprocentry,
  186. tprocdef(tcallnode(p1).procdefinition),tcallnode(p1).symtableproc);
  187. { when the methodpointer is typen we've something like:
  188. tobject.create. Then only the address is needed of the
  189. method without a self pointer }
  190. if assigned(tcallnode(p1).methodpointer) and
  191. (tcallnode(p1).methodpointer.nodetype<>typen) then
  192. begin
  193. tloadnode(p2).set_mp(tcallnode(p1).methodpointer);
  194. tcallnode(p1).methodpointer:=nil;
  195. end;
  196. end;
  197. resulttypepass(p2);
  198. p1.free;
  199. p1:=p2;
  200. end;
  201. function maybe_call_procvar(var p1:tnode;tponly:boolean):boolean;
  202. var
  203. hp : tnode;
  204. begin
  205. result:=false;
  206. if (p1.resulttype.def.deftype<>procvardef) or
  207. (tponly and
  208. not(m_tp_procvar in aktmodeswitches)) then
  209. exit;
  210. { ignore vecn,subscriptn }
  211. hp:=p1;
  212. repeat
  213. case hp.nodetype of
  214. vecn,
  215. derefn,
  216. typeconvn,
  217. subscriptn :
  218. hp:=tunarynode(hp).left;
  219. else
  220. break;
  221. end;
  222. until false;
  223. { a tempref is used when it is loaded from a withsymtable }
  224. if (hp.nodetype in [loadn,temprefn]) then
  225. begin
  226. hp:=ccallnode.create_procvar(nil,p1);
  227. resulttypepass(hp);
  228. p1:=hp;
  229. result:=true;
  230. end;
  231. end;
  232. function load_high_value_node(vs:tparavarsym):tnode;
  233. var
  234. srsym : tsym;
  235. srsymtable : tsymtable;
  236. begin
  237. result:=nil;
  238. srsymtable:=vs.owner;
  239. srsym:=searchsymonlyin(srsymtable,'high'+vs.name);
  240. if assigned(srsym) then
  241. begin
  242. result:=cloadnode.create(srsym,srsymtable);
  243. resulttypepass(result);
  244. end
  245. else
  246. CGMessage(parser_e_illegal_expression);
  247. end;
  248. function load_self_node:tnode;
  249. var
  250. srsym : tsym;
  251. srsymtable : tsymtable;
  252. begin
  253. result:=nil;
  254. searchsym('self',srsym,srsymtable);
  255. if assigned(srsym) then
  256. begin
  257. result:=cloadnode.create(srsym,srsymtable);
  258. resulttypepass(result);
  259. end
  260. else
  261. CGMessage(parser_e_illegal_expression);
  262. end;
  263. function load_result_node:tnode;
  264. var
  265. srsym : tsym;
  266. srsymtable : tsymtable;
  267. begin
  268. result:=nil;
  269. searchsym('result',srsym,srsymtable);
  270. if assigned(srsym) then
  271. begin
  272. result:=cloadnode.create(srsym,srsymtable);
  273. resulttypepass(result);
  274. end
  275. else
  276. CGMessage(parser_e_illegal_expression);
  277. end;
  278. function load_self_pointer_node:tnode;
  279. var
  280. srsym : tsym;
  281. srsymtable : tsymtable;
  282. begin
  283. result:=nil;
  284. searchsym('self',srsym,srsymtable);
  285. if assigned(srsym) then
  286. begin
  287. result:=cloadnode.create(srsym,srsymtable);
  288. include(result.flags,nf_load_self_pointer);
  289. resulttypepass(result);
  290. end
  291. else
  292. CGMessage(parser_e_illegal_expression);
  293. end;
  294. function load_vmt_pointer_node:tnode;
  295. var
  296. srsym : tsym;
  297. srsymtable : tsymtable;
  298. begin
  299. result:=nil;
  300. searchsym('vmt',srsym,srsymtable);
  301. if assigned(srsym) then
  302. begin
  303. result:=cloadnode.create(srsym,srsymtable);
  304. resulttypepass(result);
  305. end
  306. else
  307. CGMessage(parser_e_illegal_expression);
  308. end;
  309. function is_self_node(p:tnode):boolean;
  310. begin
  311. is_self_node:=(p.nodetype=loadn) and
  312. (tloadnode(p).symtableentry.typ=paravarsym) and
  313. (vo_is_self in tparavarsym(tloadnode(p).symtableentry).varoptions);
  314. end;
  315. function call_fail_node:tnode;
  316. var
  317. para : tcallparanode;
  318. newstatement : tstatementnode;
  319. srsym : tsym;
  320. begin
  321. result:=internalstatements(newstatement);
  322. { call fail helper and exit normal }
  323. if is_class(current_procinfo.procdef._class) then
  324. begin
  325. srsym:=search_class_member(current_procinfo.procdef._class,'FREEINSTANCE');
  326. if assigned(srsym) and
  327. (srsym.typ=procsym) then
  328. begin
  329. { if self<>0 and vmt=1 then freeinstance }
  330. addstatement(newstatement,cifnode.create(
  331. caddnode.create(andn,
  332. caddnode.create(unequaln,
  333. load_self_pointer_node,
  334. cnilnode.create),
  335. caddnode.create(equaln,
  336. ctypeconvnode.create(
  337. load_vmt_pointer_node,
  338. voidpointertype),
  339. cpointerconstnode.create(1,voidpointertype))),
  340. ccallnode.create(nil,tprocsym(srsym),srsym.owner,load_self_node,[]),
  341. nil));
  342. end
  343. else
  344. internalerror(200305108);
  345. end
  346. else
  347. if is_object(current_procinfo.procdef._class) then
  348. begin
  349. { parameter 3 : vmt_offset }
  350. { parameter 2 : pointer to vmt }
  351. { parameter 1 : self pointer }
  352. para:=ccallparanode.create(
  353. cordconstnode.create(current_procinfo.procdef._class.vmt_offset,s32inttype,false),
  354. ccallparanode.create(
  355. ctypeconvnode.create_internal(
  356. load_vmt_pointer_node,
  357. voidpointertype),
  358. ccallparanode.create(
  359. ctypeconvnode.create_internal(
  360. load_self_pointer_node,
  361. voidpointertype),
  362. nil)));
  363. addstatement(newstatement,
  364. ccallnode.createintern('fpc_help_fail',para));
  365. end
  366. else
  367. internalerror(200305132);
  368. { self:=nil }
  369. addstatement(newstatement,cassignmentnode.create(
  370. load_self_pointer_node,
  371. cnilnode.create));
  372. { exit }
  373. addstatement(newstatement,cexitnode.create(nil));
  374. end;
  375. function initialize_data_node(p:tnode):tnode;
  376. begin
  377. if not assigned(p.resulttype.def) then
  378. resulttypepass(p);
  379. if is_ansistring(p.resulttype.def) or
  380. is_widestring(p.resulttype.def) or
  381. is_interfacecom(p.resulttype.def) or
  382. is_dynamic_array(p.resulttype.def) then
  383. begin
  384. result:=cassignmentnode.create(
  385. ctypeconvnode.create_internal(p,voidpointertype),
  386. cnilnode.create
  387. );
  388. end
  389. else
  390. begin
  391. result:=ccallnode.createintern('fpc_initialize',
  392. ccallparanode.create(
  393. caddrnode.create_internal(
  394. crttinode.create(
  395. tstoreddef(p.resulttype.def),initrtti)),
  396. ccallparanode.create(
  397. caddrnode.create_internal(p),
  398. nil)));
  399. end;
  400. end;
  401. function finalize_data_node(p:tnode):tnode;
  402. begin
  403. if not assigned(p.resulttype.def) then
  404. resulttypepass(p);
  405. result:=ccallnode.createintern('fpc_finalize',
  406. ccallparanode.create(
  407. caddrnode.create_internal(
  408. crttinode.create(
  409. tstoreddef(p.resulttype.def),initrtti)),
  410. ccallparanode.create(
  411. caddrnode.create_internal(p),
  412. nil)));
  413. end;
  414. { this function must return a very high value ("infinity") for }
  415. { trees containing a call, the rest can be balanced more or less }
  416. { at will, probably best mainly in terms of required memory }
  417. { accesses }
  418. function node_complexity(p: tnode): cardinal;
  419. begin
  420. result := 0;
  421. while true do
  422. begin
  423. case p.nodetype of
  424. temprefn,
  425. loadvmtaddrn,
  426. { main reason for the next one: we can't take the address of }
  427. { loadparentfpnode, so replacing it by a temp which is the }
  428. { address of this node's location and then dereferencing }
  429. { doesn't work. If changed, check whether webtbs/tw0935 }
  430. { still works with nodeinlining (JM) }
  431. loadparentfpn:
  432. begin
  433. result := 1;
  434. exit;
  435. end;
  436. loadn:
  437. begin
  438. { threadvars need a helper call }
  439. if (tloadnode(p).symtableentry.typ=globalvarsym) and
  440. (vo_is_thread_var in tglobalvarsym(tloadnode(p).symtableentry).varoptions) then
  441. inc(result,5)
  442. else
  443. inc(result);
  444. if (result >= NODE_COMPLEXITY_INF) then
  445. result := NODE_COMPLEXITY_INF;
  446. exit;
  447. end;
  448. subscriptn,
  449. blockn:
  450. p := tunarynode(p).left;
  451. derefn :
  452. begin
  453. inc(result);
  454. if (result = NODE_COMPLEXITY_INF) then
  455. exit;
  456. p := tunarynode(p).left;
  457. end;
  458. typeconvn:
  459. begin
  460. { may be more complex in some cases }
  461. if not(ttypeconvnode(p).convtype in [tc_equal,tc_int_2_int,tc_bool_2_bool,tc_real_2_real,tc_cord_2_pointer]) then
  462. inc(result);
  463. if (result = NODE_COMPLEXITY_INF) then
  464. exit;
  465. p := tunarynode(p).left;
  466. end;
  467. vecn,
  468. statementn:
  469. begin
  470. inc(result,node_complexity(tbinarynode(p).left));
  471. if (result >= NODE_COMPLEXITY_INF) then
  472. begin
  473. result := NODE_COMPLEXITY_INF;
  474. exit;
  475. end;
  476. p := tbinarynode(p).right;
  477. end;
  478. { better: make muln/divn/modn more expensive }
  479. addn,subn,orn,andn,xorn,muln,divn,modn,symdifn,
  480. assignn:
  481. begin
  482. inc(result,node_complexity(tbinarynode(p).left)+1);
  483. if (result >= NODE_COMPLEXITY_INF) then
  484. begin
  485. result := NODE_COMPLEXITY_INF;
  486. exit;
  487. end;
  488. p := tbinarynode(p).right;
  489. end;
  490. tempcreaten,
  491. tempdeleten,
  492. ordconstn,
  493. pointerconstn:
  494. exit;
  495. else
  496. begin
  497. result := NODE_COMPLEXITY_INF;
  498. exit;
  499. end;
  500. end;
  501. end;
  502. end;
  503. function setnodefilepos(var n: tnode; arg: pointer): foreachnoderesult;
  504. begin
  505. result:=fen_true;
  506. n.fileinfo:=pfileposinfo(arg)^;
  507. end;
  508. procedure node_tree_set_filepos(var n:tnode;const filepos:tfileposinfo);
  509. begin
  510. foreachnodestatic(n,@setnodefilepos,@filepos);
  511. end;
  512. end.
  513. {
  514. $Log$
  515. Revision 1.28 2004-12-26 16:22:01 peter
  516. * fix lineinfo for with blocks
  517. Revision 1.27 2004/12/15 16:00:16 peter
  518. * external is again allowed in implementation
  519. Revision 1.26 2004/12/15 15:27:03 jonas
  520. * fixed foreachnode(static) for case nodes (fixes inlining of case
  521. statements)
  522. Revision 1.25 2004/12/10 13:16:31 jonas
  523. * certain type conversions have no cost (also fixes problem of
  524. inc(int64) with regvars turned on on non-64bit platforms)
  525. Revision 1.24 2004/12/05 12:28:11 peter
  526. * procvar handling for tp procvar mode fixed
  527. * proc to procvar moved from addrnode to typeconvnode
  528. * inlininginfo is now allocated only for inline routines that
  529. can be inlined, introduced a new flag po_has_inlining_info
  530. Revision 1.23 2004/12/02 19:26:15 peter
  531. * disable pass2inline
  532. Revision 1.22 2004/11/28 19:29:45 jonas
  533. * loadvmtaddrn and loadparentfpn both have complexity 1 (the latter
  534. fixes compilation of tw0935 with nodeinlining)
  535. Revision 1.21 2004/11/08 22:09:59 peter
  536. * tvarsym splitted
  537. Revision 1.20 2004/11/02 12:55:16 peter
  538. * nf_internal flag for internal inserted typeconvs. This will
  539. supress the generation of warning/hints
  540. Revision 1.19 2004/08/25 15:54:46 peter
  541. * fix possible wrong typecast
  542. Revision 1.18 2004/08/04 08:35:59 jonas
  543. * some improvements to node complexity calculations
  544. Revision 1.17 2004/07/15 20:59:58 jonas
  545. * fixed complexity function so it doesn't always return infinity when a
  546. load node is encountered
  547. Revision 1.16 2004/07/15 19:55:40 jonas
  548. + (incomplete) node_complexity function to assess the complexity of a
  549. tree
  550. + support for inlining value and const parameters at the node level
  551. (all procedures without local variables and without formal parameters
  552. can now be inlined at the node level)
  553. Revision 1.15 2004/07/12 09:14:04 jonas
  554. * inline procedures at the node tree level, but only under some very
  555. limited circumstances for now (only procedures, and only if they have
  556. no or only vs_out/vs_var parameters).
  557. * fixed ppudump for inline procedures
  558. * fixed ppudump for ppc
  559. Revision 1.14 2004/06/20 08:55:29 florian
  560. * logs truncated
  561. Revision 1.13 2004/06/16 20:07:09 florian
  562. * dwarf branch merged
  563. Revision 1.12 2004/05/23 18:28:41 peter
  564. * methodpointer is loaded into a temp when it was a calln
  565. Revision 1.11 2004/05/23 15:04:49 peter
  566. * generate better code for ansistring initialization
  567. Revision 1.10.2.1 2004/04/28 19:55:52 peter
  568. * new warning for ordinal-pointer when size is different
  569. * fixed some cg_e_ messages to the correct section type_e_ or parser_e_
  570. Revision 1.10 2004/02/20 21:55:59 peter
  571. * procvar cleanup
  572. }