nutils.pas 18 KB

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