nutils.pas 20 KB

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