nutils.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  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. if (hp.nodetype=loadn) then
  204. begin
  205. hp:=ccallnode.create_procvar(nil,p1);
  206. resulttypepass(hp);
  207. p1:=hp;
  208. result:=true;
  209. end;
  210. end;
  211. function load_high_value_node(vs:tvarsym):tnode;
  212. var
  213. srsym : tsym;
  214. srsymtable : tsymtable;
  215. begin
  216. result:=nil;
  217. srsymtable:=vs.owner;
  218. srsym:=searchsymonlyin(srsymtable,'high'+vs.name);
  219. if assigned(srsym) then
  220. begin
  221. result:=cloadnode.create(srsym,srsymtable);
  222. resulttypepass(result);
  223. end
  224. else
  225. CGMessage(parser_e_illegal_expression);
  226. end;
  227. function load_self_node:tnode;
  228. var
  229. srsym : tsym;
  230. srsymtable : tsymtable;
  231. begin
  232. result:=nil;
  233. searchsym('self',srsym,srsymtable);
  234. if assigned(srsym) then
  235. begin
  236. result:=cloadnode.create(srsym,srsymtable);
  237. resulttypepass(result);
  238. end
  239. else
  240. CGMessage(parser_e_illegal_expression);
  241. end;
  242. function load_result_node:tnode;
  243. var
  244. srsym : tsym;
  245. srsymtable : tsymtable;
  246. begin
  247. result:=nil;
  248. searchsym('result',srsym,srsymtable);
  249. if assigned(srsym) then
  250. begin
  251. result:=cloadnode.create(srsym,srsymtable);
  252. resulttypepass(result);
  253. end
  254. else
  255. CGMessage(parser_e_illegal_expression);
  256. end;
  257. function load_self_pointer_node:tnode;
  258. var
  259. srsym : tsym;
  260. srsymtable : tsymtable;
  261. begin
  262. result:=nil;
  263. searchsym('self',srsym,srsymtable);
  264. if assigned(srsym) then
  265. begin
  266. result:=cloadnode.create(srsym,srsymtable);
  267. include(result.flags,nf_load_self_pointer);
  268. resulttypepass(result);
  269. end
  270. else
  271. CGMessage(parser_e_illegal_expression);
  272. end;
  273. function load_vmt_pointer_node:tnode;
  274. var
  275. srsym : tsym;
  276. srsymtable : tsymtable;
  277. begin
  278. result:=nil;
  279. searchsym('vmt',srsym,srsymtable);
  280. if assigned(srsym) then
  281. begin
  282. result:=cloadnode.create(srsym,srsymtable);
  283. resulttypepass(result);
  284. end
  285. else
  286. CGMessage(parser_e_illegal_expression);
  287. end;
  288. function is_self_node(p:tnode):boolean;
  289. begin
  290. is_self_node:=(p.nodetype=loadn) and
  291. (tloadnode(p).symtableentry.typ=varsym) and
  292. (vo_is_self in tvarsym(tloadnode(p).symtableentry).varoptions);
  293. end;
  294. function call_fail_node:tnode;
  295. var
  296. para : tcallparanode;
  297. newstatement : tstatementnode;
  298. srsym : tsym;
  299. begin
  300. result:=internalstatements(newstatement);
  301. { call fail helper and exit normal }
  302. if is_class(current_procinfo.procdef._class) then
  303. begin
  304. srsym:=search_class_member(current_procinfo.procdef._class,'FREEINSTANCE');
  305. if assigned(srsym) and
  306. (srsym.typ=procsym) then
  307. begin
  308. { if self<>0 and vmt=1 then freeinstance }
  309. addstatement(newstatement,cifnode.create(
  310. caddnode.create(andn,
  311. caddnode.create(unequaln,
  312. load_self_pointer_node,
  313. cnilnode.create),
  314. caddnode.create(equaln,
  315. ctypeconvnode.create(
  316. load_vmt_pointer_node,
  317. voidpointertype),
  318. cpointerconstnode.create(1,voidpointertype))),
  319. ccallnode.create(nil,tprocsym(srsym),srsym.owner,load_self_node,[]),
  320. nil));
  321. end
  322. else
  323. internalerror(200305108);
  324. end
  325. else
  326. if is_object(current_procinfo.procdef._class) then
  327. begin
  328. { parameter 3 : vmt_offset }
  329. { parameter 2 : pointer to vmt }
  330. { parameter 1 : self pointer }
  331. para:=ccallparanode.create(
  332. cordconstnode.create(current_procinfo.procdef._class.vmt_offset,s32inttype,false),
  333. ccallparanode.create(
  334. ctypeconvnode.create_explicit(
  335. load_vmt_pointer_node,
  336. voidpointertype),
  337. ccallparanode.create(
  338. ctypeconvnode.create_explicit(
  339. load_self_pointer_node,
  340. voidpointertype),
  341. nil)));
  342. addstatement(newstatement,
  343. ccallnode.createintern('fpc_help_fail',para));
  344. end
  345. else
  346. internalerror(200305132);
  347. { self:=nil }
  348. addstatement(newstatement,cassignmentnode.create(
  349. load_self_pointer_node,
  350. cnilnode.create));
  351. { exit }
  352. addstatement(newstatement,cexitnode.create(nil));
  353. end;
  354. function initialize_data_node(p:tnode):tnode;
  355. begin
  356. if not assigned(p.resulttype.def) then
  357. resulttypepass(p);
  358. if is_ansistring(p.resulttype.def) or
  359. is_widestring(p.resulttype.def) or
  360. is_interfacecom(p.resulttype.def) or
  361. is_dynamic_array(p.resulttype.def) then
  362. begin
  363. result:=cassignmentnode.create(
  364. ctypeconvnode.create_explicit(p,voidpointertype),
  365. cnilnode.create
  366. );
  367. end
  368. else
  369. begin
  370. result:=ccallnode.createintern('fpc_initialize',
  371. ccallparanode.create(
  372. caddrnode.create(
  373. crttinode.create(
  374. tstoreddef(p.resulttype.def),initrtti)),
  375. ccallparanode.create(
  376. caddrnode.create(p),
  377. nil)));
  378. end;
  379. end;
  380. function finalize_data_node(p:tnode):tnode;
  381. begin
  382. if not assigned(p.resulttype.def) then
  383. resulttypepass(p);
  384. result:=ccallnode.createintern('fpc_finalize',
  385. ccallparanode.create(
  386. caddrnode.create(
  387. crttinode.create(
  388. tstoreddef(p.resulttype.def),initrtti)),
  389. ccallparanode.create(
  390. caddrnode.create(p),
  391. nil)));
  392. end;
  393. { this function must return a very high value ("infinity") for }
  394. { trees containing a call, the rest can be balanced more or less }
  395. { at will, probably best mainly in terms of required memory }
  396. { accesses }
  397. function node_complexity(p: tnode): cardinal;
  398. begin
  399. result := 0;
  400. while true do
  401. begin
  402. case p.nodetype of
  403. temprefn:
  404. begin
  405. result := 1;
  406. exit;
  407. end;
  408. loadn:
  409. begin
  410. if not(vo_is_thread_var in tvarsym(tloadnode(p).symtableentry).varoptions) then
  411. inc(result)
  412. else
  413. inc(result,5);
  414. if (result >= NODE_COMPLEXITY_INF) then
  415. result := NODE_COMPLEXITY_INF;
  416. exit;
  417. end;
  418. subscriptn,
  419. blockn:
  420. p := tunarynode(p).left;
  421. derefn,
  422. { may be more complex in some cases }
  423. typeconvn:
  424. begin
  425. inc(result);
  426. if (result = NODE_COMPLEXITY_INF) then
  427. exit;
  428. p := tunarynode(p).left;
  429. end;
  430. vecn,
  431. statementn:
  432. begin
  433. inc(result,node_complexity(tbinarynode(p).left));
  434. if (result >= NODE_COMPLEXITY_INF) then
  435. begin
  436. result := NODE_COMPLEXITY_INF;
  437. exit;
  438. end;
  439. p := tbinarynode(p).right;
  440. end;
  441. { better: make muln/divn/modn more expensive }
  442. addn,subn,orn,andn,xorn,muln,divn,modn,symdifn,
  443. assignn:
  444. begin
  445. inc(result,node_complexity(tbinarynode(p).left)+1);
  446. if (result >= NODE_COMPLEXITY_INF) then
  447. begin
  448. result := NODE_COMPLEXITY_INF;
  449. exit;
  450. end;
  451. p := tbinarynode(p).right;
  452. end;
  453. tempcreaten,
  454. tempdeleten,
  455. ordconstn,
  456. pointerconstn:
  457. exit;
  458. else
  459. begin
  460. result := NODE_COMPLEXITY_INF;
  461. exit;
  462. end;
  463. end;
  464. end;
  465. end;
  466. end.
  467. {
  468. $Log$
  469. Revision 1.18 2004-08-04 08:35:59 jonas
  470. * some improvements to node complexity calculations
  471. Revision 1.17 2004/07/15 20:59:58 jonas
  472. * fixed complexity function so it doesn't always return infinity when a
  473. load node is encountered
  474. Revision 1.16 2004/07/15 19:55:40 jonas
  475. + (incomplete) node_complexity function to assess the complexity of a
  476. tree
  477. + support for inlining value and const parameters at the node level
  478. (all procedures without local variables and without formal parameters
  479. can now be inlined at the node level)
  480. Revision 1.15 2004/07/12 09:14:04 jonas
  481. * inline procedures at the node tree level, but only under some very
  482. limited circumstances for now (only procedures, and only if they have
  483. no or only vs_out/vs_var parameters).
  484. * fixed ppudump for inline procedures
  485. * fixed ppudump for ppc
  486. Revision 1.14 2004/06/20 08:55:29 florian
  487. * logs truncated
  488. Revision 1.13 2004/06/16 20:07:09 florian
  489. * dwarf branch merged
  490. Revision 1.12 2004/05/23 18:28:41 peter
  491. * methodpointer is loaded into a temp when it was a calln
  492. Revision 1.11 2004/05/23 15:04:49 peter
  493. * generate better code for ansistring initialization
  494. Revision 1.10.2.1 2004/04/28 19:55:52 peter
  495. * new warning for ordinal-pointer when size is different
  496. * fixed some cg_e_ messages to the correct section type_e_ or parser_e_
  497. Revision 1.10 2004/02/20 21:55:59 peter
  498. * procvar cleanup
  499. }