nutils.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  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. include(result.flags,nf_is_self);
  259. resulttypepass(result);
  260. end
  261. else
  262. CGMessage(parser_e_illegal_expression);
  263. end;
  264. function load_result_node:tnode;
  265. var
  266. srsym : tsym;
  267. srsymtable : tsymtable;
  268. begin
  269. result:=nil;
  270. searchsym('result',srsym,srsymtable);
  271. if assigned(srsym) then
  272. begin
  273. result:=cloadnode.create(srsym,srsymtable);
  274. resulttypepass(result);
  275. end
  276. else
  277. CGMessage(parser_e_illegal_expression);
  278. end;
  279. function load_self_pointer_node:tnode;
  280. var
  281. srsym : tsym;
  282. srsymtable : tsymtable;
  283. begin
  284. result:=nil;
  285. searchsym('self',srsym,srsymtable);
  286. if assigned(srsym) then
  287. begin
  288. result:=cloadnode.create(srsym,srsymtable);
  289. include(result.flags,nf_load_self_pointer);
  290. resulttypepass(result);
  291. end
  292. else
  293. CGMessage(parser_e_illegal_expression);
  294. end;
  295. function load_vmt_pointer_node:tnode;
  296. var
  297. srsym : tsym;
  298. srsymtable : tsymtable;
  299. begin
  300. result:=nil;
  301. searchsym('vmt',srsym,srsymtable);
  302. if assigned(srsym) then
  303. begin
  304. result:=cloadnode.create(srsym,srsymtable);
  305. resulttypepass(result);
  306. end
  307. else
  308. CGMessage(parser_e_illegal_expression);
  309. end;
  310. function is_self_node(p:tnode):boolean;
  311. begin
  312. is_self_node:=(p.nodetype=loadn) and
  313. (tloadnode(p).symtableentry.typ=paravarsym) and
  314. (vo_is_self in tparavarsym(tloadnode(p).symtableentry).varoptions);
  315. end;
  316. function call_fail_node:tnode;
  317. var
  318. para : tcallparanode;
  319. newstatement : tstatementnode;
  320. srsym : tsym;
  321. begin
  322. result:=internalstatements(newstatement);
  323. { call fail helper and exit normal }
  324. if is_class(current_procinfo.procdef._class) then
  325. begin
  326. srsym:=search_class_member(current_procinfo.procdef._class,'FREEINSTANCE');
  327. if assigned(srsym) and
  328. (srsym.typ=procsym) then
  329. begin
  330. { if self<>0 and vmt=1 then freeinstance }
  331. addstatement(newstatement,cifnode.create(
  332. caddnode.create(andn,
  333. caddnode.create(unequaln,
  334. load_self_pointer_node,
  335. cnilnode.create),
  336. caddnode.create(equaln,
  337. ctypeconvnode.create(
  338. load_vmt_pointer_node,
  339. voidpointertype),
  340. cpointerconstnode.create(1,voidpointertype))),
  341. ccallnode.create(nil,tprocsym(srsym),srsym.owner,load_self_node,[]),
  342. nil));
  343. end
  344. else
  345. internalerror(200305108);
  346. end
  347. else
  348. if is_object(current_procinfo.procdef._class) then
  349. begin
  350. { parameter 3 : vmt_offset }
  351. { parameter 2 : pointer to vmt }
  352. { parameter 1 : self pointer }
  353. para:=ccallparanode.create(
  354. cordconstnode.create(current_procinfo.procdef._class.vmt_offset,s32inttype,false),
  355. ccallparanode.create(
  356. ctypeconvnode.create_internal(
  357. load_vmt_pointer_node,
  358. voidpointertype),
  359. ccallparanode.create(
  360. ctypeconvnode.create_internal(
  361. load_self_pointer_node,
  362. voidpointertype),
  363. nil)));
  364. addstatement(newstatement,
  365. ccallnode.createintern('fpc_help_fail',para));
  366. end
  367. else
  368. internalerror(200305132);
  369. { self:=nil }
  370. addstatement(newstatement,cassignmentnode.create(
  371. load_self_pointer_node,
  372. cnilnode.create));
  373. { exit }
  374. addstatement(newstatement,cexitnode.create(nil));
  375. end;
  376. function initialize_data_node(p:tnode):tnode;
  377. begin
  378. if not assigned(p.resulttype.def) then
  379. resulttypepass(p);
  380. if is_ansistring(p.resulttype.def) or
  381. is_widestring(p.resulttype.def) or
  382. is_interfacecom(p.resulttype.def) or
  383. is_dynamic_array(p.resulttype.def) then
  384. begin
  385. result:=cassignmentnode.create(
  386. ctypeconvnode.create_internal(p,voidpointertype),
  387. cnilnode.create
  388. );
  389. end
  390. else
  391. begin
  392. result:=ccallnode.createintern('fpc_initialize',
  393. ccallparanode.create(
  394. caddrnode.create_internal(
  395. crttinode.create(
  396. tstoreddef(p.resulttype.def),initrtti)),
  397. ccallparanode.create(
  398. caddrnode.create_internal(p),
  399. nil)));
  400. end;
  401. end;
  402. function finalize_data_node(p:tnode):tnode;
  403. begin
  404. if not assigned(p.resulttype.def) then
  405. resulttypepass(p);
  406. result:=ccallnode.createintern('fpc_finalize',
  407. ccallparanode.create(
  408. caddrnode.create_internal(
  409. crttinode.create(
  410. tstoreddef(p.resulttype.def),initrtti)),
  411. ccallparanode.create(
  412. caddrnode.create_internal(p),
  413. nil)));
  414. end;
  415. { this function must return a very high value ("infinity") for }
  416. { trees containing a call, the rest can be balanced more or less }
  417. { at will, probably best mainly in terms of required memory }
  418. { accesses }
  419. function node_complexity(p: tnode): cardinal;
  420. begin
  421. result := 0;
  422. while true do
  423. begin
  424. case p.nodetype of
  425. temprefn,
  426. loadvmtaddrn,
  427. { main reason for the next one: we can't take the address of }
  428. { loadparentfpnode, so replacing it by a temp which is the }
  429. { address of this node's location and then dereferencing }
  430. { doesn't work. If changed, check whether webtbs/tw0935 }
  431. { still works with nodeinlining (JM) }
  432. loadparentfpn:
  433. begin
  434. result := 1;
  435. exit;
  436. end;
  437. loadn:
  438. begin
  439. { threadvars need a helper call }
  440. if (tloadnode(p).symtableentry.typ=globalvarsym) and
  441. (vo_is_thread_var in tglobalvarsym(tloadnode(p).symtableentry).varoptions) then
  442. inc(result,5)
  443. else
  444. inc(result);
  445. if (result >= NODE_COMPLEXITY_INF) then
  446. result := NODE_COMPLEXITY_INF;
  447. exit;
  448. end;
  449. subscriptn,
  450. blockn:
  451. p := tunarynode(p).left;
  452. derefn :
  453. begin
  454. inc(result);
  455. if (result = NODE_COMPLEXITY_INF) then
  456. exit;
  457. p := tunarynode(p).left;
  458. end;
  459. typeconvn:
  460. begin
  461. { may be more complex in some cases }
  462. 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
  463. inc(result);
  464. if (result = NODE_COMPLEXITY_INF) then
  465. exit;
  466. p := tunarynode(p).left;
  467. end;
  468. vecn,
  469. statementn:
  470. begin
  471. inc(result,node_complexity(tbinarynode(p).left));
  472. if (result >= NODE_COMPLEXITY_INF) then
  473. begin
  474. result := NODE_COMPLEXITY_INF;
  475. exit;
  476. end;
  477. p := tbinarynode(p).right;
  478. end;
  479. { better: make muln/divn/modn more expensive }
  480. addn,subn,orn,andn,xorn,muln,divn,modn,symdifn,
  481. assignn:
  482. begin
  483. inc(result,node_complexity(tbinarynode(p).left)+1);
  484. if (result >= NODE_COMPLEXITY_INF) then
  485. begin
  486. result := NODE_COMPLEXITY_INF;
  487. exit;
  488. end;
  489. p := tbinarynode(p).right;
  490. end;
  491. tempcreaten,
  492. tempdeleten,
  493. ordconstn,
  494. pointerconstn:
  495. exit;
  496. else
  497. begin
  498. result := NODE_COMPLEXITY_INF;
  499. exit;
  500. end;
  501. end;
  502. end;
  503. end;
  504. function setnodefilepos(var n: tnode; arg: pointer): foreachnoderesult;
  505. begin
  506. result:=fen_true;
  507. n.fileinfo:=pfileposinfo(arg)^;
  508. end;
  509. procedure node_tree_set_filepos(var n:tnode;const filepos:tfileposinfo);
  510. begin
  511. foreachnodestatic(n,@setnodefilepos,@filepos);
  512. end;
  513. end.
  514. {
  515. $Log$
  516. Revision 1.29 2005-01-04 16:39:46 peter
  517. * set nf_is_self node flag when self is loaded
  518. Revision 1.28 2004/12/26 16:22:01 peter
  519. * fix lineinfo for with blocks
  520. Revision 1.27 2004/12/15 16:00:16 peter
  521. * external is again allowed in implementation
  522. Revision 1.26 2004/12/15 15:27:03 jonas
  523. * fixed foreachnode(static) for case nodes (fixes inlining of case
  524. statements)
  525. Revision 1.25 2004/12/10 13:16:31 jonas
  526. * certain type conversions have no cost (also fixes problem of
  527. inc(int64) with regvars turned on on non-64bit platforms)
  528. Revision 1.24 2004/12/05 12:28:11 peter
  529. * procvar handling for tp procvar mode fixed
  530. * proc to procvar moved from addrnode to typeconvnode
  531. * inlininginfo is now allocated only for inline routines that
  532. can be inlined, introduced a new flag po_has_inlining_info
  533. Revision 1.23 2004/12/02 19:26:15 peter
  534. * disable pass2inline
  535. Revision 1.22 2004/11/28 19:29:45 jonas
  536. * loadvmtaddrn and loadparentfpn both have complexity 1 (the latter
  537. fixes compilation of tw0935 with nodeinlining)
  538. Revision 1.21 2004/11/08 22:09:59 peter
  539. * tvarsym splitted
  540. Revision 1.20 2004/11/02 12:55:16 peter
  541. * nf_internal flag for internal inserted typeconvs. This will
  542. supress the generation of warning/hints
  543. Revision 1.19 2004/08/25 15:54:46 peter
  544. * fix possible wrong typecast
  545. Revision 1.18 2004/08/04 08:35:59 jonas
  546. * some improvements to node complexity calculations
  547. Revision 1.17 2004/07/15 20:59:58 jonas
  548. * fixed complexity function so it doesn't always return infinity when a
  549. load node is encountered
  550. Revision 1.16 2004/07/15 19:55:40 jonas
  551. + (incomplete) node_complexity function to assess the complexity of a
  552. tree
  553. + support for inlining value and const parameters at the node level
  554. (all procedures without local variables and without formal parameters
  555. can now be inlined at the node level)
  556. Revision 1.15 2004/07/12 09:14:04 jonas
  557. * inline procedures at the node tree level, but only under some very
  558. limited circumstances for now (only procedures, and only if they have
  559. no or only vs_out/vs_var parameters).
  560. * fixed ppudump for inline procedures
  561. * fixed ppudump for ppc
  562. Revision 1.14 2004/06/20 08:55:29 florian
  563. * logs truncated
  564. Revision 1.13 2004/06/16 20:07:09 florian
  565. * dwarf branch merged
  566. Revision 1.12 2004/05/23 18:28:41 peter
  567. * methodpointer is loaded into a temp when it was a calln
  568. Revision 1.11 2004/05/23 15:04:49 peter
  569. * generate better code for ansistring initialization
  570. Revision 1.10.2.1 2004/04/28 19:55:52 peter
  571. * new warning for ordinal-pointer when size is different
  572. * fixed some cg_e_ messages to the correct section type_e_ or parser_e_
  573. Revision 1.10 2004/02/20 21:55:59 peter
  574. * procvar cleanup
  575. }