nutils.pas 19 KB

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