optdfa.pas 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998
  1. {
  2. DFA
  3. Copyright (c) 2007 by Florian Klaempfl
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. { $define DEBUG_DFA}
  18. { $define EXTDEBUG_DFA}
  19. { this unit implements routines to perform dfa }
  20. unit optdfa;
  21. {$i fpcdefs.inc}
  22. interface
  23. uses
  24. node,optutils;
  25. type
  26. TDFABuilder = class
  27. protected
  28. procedure CreateLifeInfo(node : tnode;map : TIndexedNodeSet);
  29. public
  30. resultnode : tnode;
  31. nodemap : TIndexedNodeSet;
  32. { reset all dfa info, this is required before creating dfa info
  33. if the tree has been changed without updating dfa }
  34. procedure resetdfainfo(node : tnode);
  35. procedure createdfainfo(node : tnode);
  36. destructor destroy;override;
  37. end;
  38. procedure CheckAndWarn(code : tnode;nodetosearch : tnode);
  39. implementation
  40. uses
  41. globtype,constexp,
  42. verbose,
  43. symconst,symdef,symsym,
  44. defutil,
  45. procinfo,
  46. nutils,htypechk,
  47. nbas,nflw,ncal,nset,nld,nadd,
  48. optbase;
  49. (*
  50. function initnodes(var n:tnode; arg: pointer) : foreachnoderesult;
  51. begin
  52. { node worth to add? }
  53. if (node_complexity(n)>1) and (tstoreddef(n.resultdef).is_intregable or tstoreddef(n.resultdef).is_fpuregable) then
  54. begin
  55. plists(arg)^.nodelist.Add(n);
  56. plists(arg)^.locationlist.Add(@n);
  57. result:=fen_false;
  58. end
  59. else
  60. result:=fen_norecurse_false;
  61. end;
  62. *)
  63. {
  64. x:=f; read: [f]
  65. while x do read: []
  66. a:=b; read: [a,b,d] def: [a] life: read*def=[a]
  67. c:=d; read: [a,d] def: [a,c] life: read*def=[a]
  68. e:=a; read: [a] def: [a,c,e] life: read*def=[a]
  69. function f(b,d,x : type) : type;
  70. begin
  71. while x do alive: b,d,x
  72. begin
  73. a:=b; alive: b,d,x
  74. c:=d; alive: a,d,x
  75. e:=a+c; alive: a,c,x
  76. dec(x); alive: c,e,x
  77. end;
  78. result:=c+e; alive: c,e
  79. end; alive: result
  80. }
  81. type
  82. tdfainfo = record
  83. use : PDFASet;
  84. def : PDFASet;
  85. map : TIndexedNodeSet
  86. end;
  87. pdfainfo = ^tdfainfo;
  88. function AddDefUse(var n: tnode; arg: pointer): foreachnoderesult;
  89. begin
  90. case n.nodetype of
  91. tempcreaten:
  92. begin
  93. if assigned(ttempcreatenode(n).tempinfo^.tempinitcode) then
  94. begin
  95. pdfainfo(arg)^.map.Add(n);
  96. DFASetInclude(pdfainfo(arg)^.def^,n.optinfo^.index);
  97. end;
  98. end;
  99. temprefn,
  100. loadn:
  101. begin
  102. pdfainfo(arg)^.map.Add(n);
  103. if nf_modify in n.flags then
  104. begin
  105. DFASetInclude(pdfainfo(arg)^.use^,n.optinfo^.index);
  106. DFASetInclude(pdfainfo(arg)^.def^,n.optinfo^.index)
  107. end
  108. else if nf_write in n.flags then
  109. DFASetInclude(pdfainfo(arg)^.def^,n.optinfo^.index)
  110. else
  111. DFASetInclude(pdfainfo(arg)^.use^,n.optinfo^.index);
  112. end;
  113. else
  114. ;
  115. end;
  116. result:=fen_false;
  117. end;
  118. function ResetProcessing(var n: tnode; arg: pointer): foreachnoderesult;
  119. begin
  120. exclude(n.flags,nf_processing);
  121. { dfa works only on normalized trees, so do not recurse into expressions, because
  122. ResetProcessing eats a signififcant amount of time of CheckAndWarn
  123. the following set contains (hopefully) most of the expression nodes }
  124. if n.nodetype in [calln,inlinen,assignn,callparan,andn,addn,orn,subn,muln,divn,slashn,notn,equaln,unequaln,gtn,ltn,lten,gten,loadn,
  125. typeconvn,vecn,subscriptn,addrn,derefn] then
  126. result:=fen_norecurse_false
  127. else
  128. result:=fen_false;
  129. end;
  130. function ResetDFA(var n: tnode; arg: pointer): foreachnoderesult;
  131. begin
  132. if assigned(n.optinfo) then
  133. begin
  134. with n.optinfo^ do
  135. begin
  136. life:=nil;
  137. def:=nil;
  138. use:=nil;
  139. defsum:=nil;
  140. end;
  141. end;
  142. result:=fen_false;
  143. end;
  144. procedure TDFABuilder.CreateLifeInfo(node : tnode;map : TIndexedNodeSet);
  145. var
  146. changed : boolean;
  147. procedure CreateInfo(node : tnode);
  148. { update life entry of a node with l, set changed if this changes
  149. life info for the node
  150. }
  151. procedure updatelifeinfo(n : tnode;l : TDFASet);
  152. var
  153. b : boolean;
  154. begin
  155. b:=DFASetNotEqual(l,n.optinfo^.life);
  156. {$ifdef DEBUG_DFA}
  157. if not(changed) and b then
  158. begin
  159. writeln('Another DFA pass caused by: ',nodetype2str[n.nodetype],'(',n.fileinfo.line,',',n.fileinfo.column,')');
  160. write(' Life info set was: ');PrintDFASet(Output,n.optinfo^.life);writeln;
  161. write(' Life info set will be: ');PrintDFASet(Output,l);writeln;
  162. end;
  163. {$endif DEBUG_DFA}
  164. changed:=changed or b;
  165. n.optinfo^.life:=l;
  166. end;
  167. procedure calclife(n : tnode);
  168. var
  169. l : TDFASet;
  170. begin
  171. if assigned(n.successor) then
  172. begin
  173. { ensure we can access optinfo }
  174. DFASetDiff(l,n.successor.optinfo^.life,n.optinfo^.def);
  175. DFASetIncludeSet(l,n.optinfo^.use);
  176. DFASetIncludeSet(l,n.optinfo^.life);
  177. end
  178. else
  179. begin
  180. l:=n.optinfo^.use;
  181. DFASetIncludeSet(l,n.optinfo^.life);
  182. end;
  183. updatelifeinfo(n,l);
  184. end;
  185. var
  186. dfainfo : tdfainfo;
  187. l : TDFASet;
  188. save: TDFASet;
  189. lv, hv: TConstExprInt;
  190. i : longint;
  191. counteruse_after_loop : boolean;
  192. begin
  193. if node=nil then
  194. exit;
  195. { ensure we've already optinfo set }
  196. node.allocoptinfo;
  197. if nf_processing in node.flags then
  198. exit;
  199. include(node.flags,nf_processing);
  200. if assigned(node.successor) then
  201. CreateInfo(node.successor);
  202. {$ifdef EXTDEBUG_DFA}
  203. writeln('Handling: ',nodetype2str[node.nodetype],'(',node.fileinfo.line,',',node.fileinfo.column,')');
  204. {$endif EXTDEBUG_DFA}
  205. { life:=succesorlive-definition+use }
  206. case node.nodetype of
  207. whilerepeatn:
  208. begin
  209. { analyze the loop condition }
  210. if not(assigned(node.optinfo^.def)) and
  211. not(assigned(node.optinfo^.use)) then
  212. begin
  213. dfainfo.use:[email protected]^.use;
  214. dfainfo.def:[email protected]^.def;
  215. dfainfo.map:=map;
  216. foreachnodestatic(pm_postprocess,twhilerepeatnode(node).left,@AddDefUse,@dfainfo);
  217. end;
  218. { NB: this node should typically have empty def set }
  219. if assigned(node.successor) then
  220. DFASetDiff(l,node.successor.optinfo^.life,node.optinfo^.def)
  221. else if assigned(resultnode) then
  222. DFASetDiff(l,resultnode.optinfo^.life,node.optinfo^.def)
  223. else
  224. l:=nil;
  225. { for repeat..until, node use set in included at the end of loop }
  226. if not (lnf_testatbegin in twhilerepeatnode(node).loopflags) then
  227. DFASetIncludeSet(l,node.optinfo^.use);
  228. DFASetIncludeSet(l,node.optinfo^.life);
  229. save:=node.optinfo^.life;
  230. { to process body correctly, we need life info in place (because
  231. whilerepeatnode is successor of its body). }
  232. node.optinfo^.life:=l;
  233. { now process the body }
  234. CreateInfo(twhilerepeatnode(node).right);
  235. { restore, to prevent infinite recursion via changed flag }
  236. node.optinfo^.life:=save;
  237. { for while loops, node use set is included at the beginning of loop }
  238. l:=twhilerepeatnode(node).right.optinfo^.life;
  239. if lnf_testatbegin in twhilerepeatnode(node).loopflags then
  240. DFASetIncludeSet(l,node.optinfo^.use);
  241. UpdateLifeInfo(node,l);
  242. { ... and a second iteration for fast convergence }
  243. CreateInfo(twhilerepeatnode(node).right);
  244. end;
  245. forn:
  246. begin
  247. {
  248. left: loopvar
  249. right: from
  250. t1: to
  251. t2: body
  252. }
  253. node.allocoptinfo;
  254. tfornode(node).loopiteration.allocoptinfo;
  255. if not(assigned(node.optinfo^.def)) and
  256. not(assigned(node.optinfo^.use)) then
  257. begin
  258. dfainfo.use:[email protected]^.use;
  259. dfainfo.def:[email protected]^.def;
  260. dfainfo.map:=map;
  261. foreachnodestatic(pm_postprocess,tfornode(node).left,@AddDefUse,@dfainfo);
  262. foreachnodestatic(pm_postprocess,tfornode(node).right,@AddDefUse,@dfainfo);
  263. foreachnodestatic(pm_postprocess,tfornode(node).t1,@AddDefUse,@dfainfo);
  264. end;
  265. { create life for the body }
  266. CreateInfo(tfornode(node).t2);
  267. { is the counter living after the loop?
  268. if left is a record element, it might not be tracked by dfa, so
  269. optinfo might not be assigned
  270. }
  271. counteruse_after_loop:=assigned(tfornode(node).left.optinfo) and assigned(node.successor) and
  272. DFASetIn(node.successor.optinfo^.life,tfornode(node).left.optinfo^.index);
  273. if counteruse_after_loop then
  274. begin
  275. { if yes, then we should warn }
  276. { !!!!!! }
  277. end
  278. else
  279. Include(tfornode(node).loopflags,lnf_dont_mind_loopvar_on_exit);
  280. { first update the dummy node }
  281. { get the life of the loop block }
  282. l:=copy(tfornode(node).t2.optinfo^.life);
  283. { take care of the sucessor }
  284. if assigned(node.successor) then
  285. DFASetIncludeSet(l,node.successor.optinfo^.life);
  286. { the counter variable is living as well inside the for loop
  287. if left is a record element, it might not be tracked by dfa, so
  288. optinfo might not be assigned
  289. }
  290. if assigned(tfornode(node).left.optinfo) then
  291. DFASetInclude(l,tfornode(node).left.optinfo^.index);
  292. { force block node life info }
  293. UpdateLifeInfo(tfornode(node).loopiteration,l);
  294. { now update the for node itself }
  295. { get the life of the loop block }
  296. l:=copy(tfornode(node).t2.optinfo^.life);
  297. { take care of the sucessor as it's possible that we don't have one execution of the body }
  298. if (not(tfornode(node).right.nodetype=ordconstn) or not(tfornode(node).t1.nodetype=ordconstn)) and
  299. assigned(node.successor) then
  300. DFASetIncludeSet(l,node.successor.optinfo^.life);
  301. {
  302. the counter variable is not living at the entry of the for node
  303. if left is a record element, it might not be tracked by dfa, so
  304. optinfo might not be assigned
  305. }
  306. if assigned(tfornode(node).left.optinfo) then
  307. DFASetExclude(l,tfornode(node).left.optinfo^.index);
  308. { ... but it could be that left/right use it, so do this after
  309. removing the def of the counter variable }
  310. DFASetIncludeSet(l,node.optinfo^.use);
  311. UpdateLifeInfo(node,l);
  312. { ... and a second iteration for fast convergence }
  313. CreateInfo(tfornode(node).t2);
  314. end;
  315. temprefn,
  316. loadn,
  317. typeconvn,
  318. derefn,
  319. assignn:
  320. begin
  321. if not(assigned(node.optinfo^.def)) and
  322. not(assigned(node.optinfo^.use)) then
  323. begin
  324. dfainfo.use:[email protected]^.use;
  325. dfainfo.def:[email protected]^.def;
  326. dfainfo.map:=map;
  327. foreachnodestatic(pm_postprocess,node,@AddDefUse,@dfainfo);
  328. end;
  329. calclife(node);
  330. end;
  331. statementn:
  332. begin
  333. { nested statement }
  334. CreateInfo(tstatementnode(node).statement);
  335. { inherit info }
  336. node.optinfo^.life:=tstatementnode(node).statement.optinfo^.life;
  337. end;
  338. blockn:
  339. begin
  340. CreateInfo(tblocknode(node).statements);
  341. { ensure that we don't remove life info }
  342. l:=node.optinfo^.life;
  343. if assigned(node.successor) then
  344. DFASetIncludeSet(l,node.successor.optinfo^.life);
  345. UpdateLifeInfo(node,l);
  346. end;
  347. ifn:
  348. begin
  349. { get information from cond. expression }
  350. if not(assigned(node.optinfo^.def)) and
  351. not(assigned(node.optinfo^.use)) then
  352. begin
  353. dfainfo.use:[email protected]^.use;
  354. dfainfo.def:[email protected]^.def;
  355. dfainfo.map:=map;
  356. foreachnodestatic(pm_postprocess,tifnode(node).left,@AddDefUse,@dfainfo);
  357. end;
  358. { create life info for then and else node }
  359. CreateInfo(tifnode(node).right);
  360. CreateInfo(tifnode(node).t1);
  361. { ensure that we don't remove life info }
  362. l:=node.optinfo^.life;
  363. { get life info from then branch }
  364. if assigned(tifnode(node).right) then
  365. DFASetIncludeSet(l,tifnode(node).right.optinfo^.life);
  366. { get life info from else branch }
  367. if assigned(tifnode(node).t1) then
  368. DFASetIncludeSet(l,tifnode(node).t1.optinfo^.life)
  369. else if assigned(node.successor) then
  370. DFASetIncludeSet(l,node.successor.optinfo^.life);
  371. { remove def info from the cond. expression }
  372. DFASetExcludeSet(l,tifnode(node).optinfo^.def);
  373. { add use info from the cond. expression }
  374. DFASetIncludeSet(l,tifnode(node).optinfo^.use);
  375. { finally, update the life info of the node }
  376. UpdateLifeInfo(node,l);
  377. end;
  378. casen:
  379. begin
  380. { get information from "case" expression }
  381. if not(assigned(node.optinfo^.def)) and
  382. not(assigned(node.optinfo^.use)) then
  383. begin
  384. dfainfo.use:[email protected]^.use;
  385. dfainfo.def:[email protected]^.def;
  386. dfainfo.map:=map;
  387. foreachnodestatic(pm_postprocess,tcasenode(node).left,@AddDefUse,@dfainfo);
  388. end;
  389. { create life info for block and else nodes }
  390. for i:=0 to tcasenode(node).blocks.count-1 do
  391. CreateInfo(pcaseblock(tcasenode(node).blocks[i])^.statement);
  392. CreateInfo(tcasenode(node).elseblock);
  393. { ensure that we don't remove life info }
  394. l:=node.optinfo^.life;
  395. { get life info from case branches }
  396. for i:=0 to tcasenode(node).blocks.count-1 do
  397. DFASetIncludeSet(l,pcaseblock(tcasenode(node).blocks[i])^.statement.optinfo^.life);
  398. { get life info from else branch or the succesor }
  399. if assigned(tcasenode(node).elseblock) then
  400. DFASetIncludeSet(l,tcasenode(node).elseblock.optinfo^.life)
  401. else if assigned(node.successor) then
  402. begin
  403. if is_ordinal(tcasenode(node).left.resultdef) then
  404. begin
  405. getrange(tcasenode(node).left.resultdef,lv,hv);
  406. if tcasenode(node).labelcoverage<(hv-lv) then
  407. DFASetIncludeSet(l,node.successor.optinfo^.life);
  408. end
  409. else
  410. DFASetIncludeSet(l,node.successor.optinfo^.life);
  411. end;
  412. { add use info from the "case" expression }
  413. DFASetIncludeSet(l,tcasenode(node).optinfo^.use);
  414. { finally, update the life info of the node }
  415. UpdateLifeInfo(node,l);
  416. end;
  417. exitn:
  418. begin
  419. { in case of inlining, an exit node can have a successor, in this case, we do not have to
  420. use the faked resultnode }
  421. if assigned(node.successor) then
  422. begin
  423. l:=node.optinfo^.life;
  424. DFASetIncludeSet(l,node.successor.optinfo^.life);
  425. UpdateLifeInfo(node,l);
  426. end
  427. else if assigned(resultnode) and (resultnode.nodetype<>nothingn) then
  428. begin
  429. if not(assigned(node.optinfo^.def)) and
  430. not(assigned(node.optinfo^.use)) then
  431. begin
  432. if assigned(texitnode(node).left) then
  433. begin
  434. { this should never happen as
  435. texitnode.pass_typecheck converts the left node into a separate node already
  436. node.optinfo^.def:=resultnode.optinfo^.def;
  437. dfainfo.use:[email protected]^.use;
  438. dfainfo.def:[email protected]^.def;
  439. dfainfo.map:=map;
  440. foreachnodestatic(pm_postprocess,texitnode(node).left,@AddDefUse,@dfainfo);
  441. calclife(node); }
  442. Internalerror(2020122901);
  443. end
  444. else
  445. begin
  446. { get info from faked resultnode }
  447. node.optinfo^.use:=resultnode.optinfo^.use;
  448. node.optinfo^.life:=node.optinfo^.use;
  449. changed:=true;
  450. end;
  451. end;
  452. end;
  453. end;
  454. {$ifdef JVM}
  455. { all other platforms except jvm translate raise nodes into call nodes during pass_1 }
  456. raisen,
  457. {$endif JVM}
  458. asn,
  459. inlinen,
  460. calln:
  461. begin
  462. if not(assigned(node.optinfo^.def)) and
  463. not(assigned(node.optinfo^.use)) then
  464. begin
  465. dfainfo.use:[email protected]^.use;
  466. dfainfo.def:[email protected]^.def;
  467. dfainfo.map:=map;
  468. foreachnodestatic(pm_postprocess,node,@AddDefUse,@dfainfo);
  469. end;
  470. calclife(node);
  471. end;
  472. labeln:
  473. begin
  474. calclife(node);
  475. if assigned(tlabelnode(node).left) then
  476. begin
  477. l:=node.optinfo^.life;
  478. DFASetIncludeSet(l,tlabelnode(node).optinfo^.life);
  479. UpdateLifeInfo(node,l);
  480. end;
  481. end;
  482. tempcreaten,
  483. tempdeleten,
  484. nothingn,
  485. continuen,
  486. goton,
  487. breakn:
  488. begin
  489. calclife(node);
  490. end;
  491. else
  492. internalerror(2007050502);
  493. end;
  494. end;
  495. var
  496. runs : integer;
  497. begin
  498. runs:=0;
  499. repeat
  500. inc(runs);
  501. changed:=false;
  502. CreateInfo(node);
  503. foreachnodestatic(pm_postprocess,node,@ResetProcessing,nil);
  504. { the result node is not reached by foreachnodestatic }
  505. exclude(resultnode.flags,nf_processing);
  506. {$ifdef DEBUG_DFA}
  507. PrintIndexedNodeSet(output,map);
  508. PrintDFAInfo(output,node);
  509. {$endif DEBUG_DFA}
  510. until not(changed);
  511. {$ifdef DEBUG_DFA}
  512. writeln('DFA solver iterations: ',runs);
  513. {$endif DEBUG_DFA}
  514. end;
  515. { reset all dfa info, this is required before creating dfa info
  516. if the tree has been changed without updating dfa }
  517. procedure TDFABuilder.resetdfainfo(node : tnode);
  518. begin
  519. nodemap.Free;
  520. nodemap:=nil;
  521. resultnode.Free;
  522. resultnode:=nil;
  523. foreachnodestatic(pm_postprocess,node,@ResetDFA,nil);
  524. end;
  525. procedure TDFABuilder.createdfainfo(node : tnode);
  526. var
  527. dfarec : tdfainfo;
  528. begin
  529. if not(assigned(nodemap)) then
  530. nodemap:=TIndexedNodeSet.Create;
  531. { create a fake node using the result which will be the last node }
  532. if not(is_void(current_procinfo.procdef.returndef)) then
  533. begin
  534. if current_procinfo.procdef.proctypeoption=potype_constructor then
  535. resultnode:=load_self_node
  536. else
  537. resultnode:=load_result_node;
  538. resultnode.allocoptinfo;
  539. dfarec.use:[email protected]^.use;
  540. dfarec.def:[email protected]^.def;
  541. dfarec.map:=nodemap;
  542. AddDefUse(resultnode,@dfarec);
  543. resultnode.optinfo^.life:=resultnode.optinfo^.use;
  544. end
  545. else
  546. begin
  547. resultnode:=cnothingnode.create;
  548. resultnode.allocoptinfo;
  549. end;
  550. { add controll flow information }
  551. SetNodeSucessors(node,resultnode);
  552. { now, collect life information }
  553. CreateLifeInfo(node,nodemap);
  554. end;
  555. destructor TDFABuilder.Destroy;
  556. begin
  557. Resultnode.free;
  558. nodemap.free;
  559. inherited destroy;
  560. end;
  561. var
  562. { we have to pass the address of SearchNode in a call inside of SearchNode:
  563. @SearchNode does not work because the compiler thinks we take the address of the result
  564. so store the address from outside }
  565. SearchNodeProcPointer : function(var n: tnode; arg: pointer): foreachnoderesult;
  566. type
  567. { helper structure to be able to pass more than one variable to the iterator function }
  568. TSearchNodeInfo = record
  569. nodetosearch : tnode;
  570. { this contains a list of all file locations where a warning was thrown already,
  571. the same location might appear multiple times because nodes might have been copied }
  572. warnedfilelocs : array of tfileposinfo;
  573. end;
  574. PSearchNodeInfo = ^TSearchNodeInfo;
  575. { searches for a given node n and warns if the node is found as being uninitialized. If a node is
  576. found, searching is stopped so each call issues only one warning/hint }
  577. function SearchNode(var n: tnode; arg: pointer): foreachnoderesult;
  578. function WarnedForLocation(f : tfileposinfo) : boolean;
  579. var
  580. i : longint;
  581. begin
  582. result:=true;
  583. for i:=0 to high(PSearchNodeInfo(arg)^.warnedfilelocs) do
  584. with PSearchNodeInfo(arg)^.warnedfilelocs[i] do
  585. begin
  586. if (f.column=column) and (f.fileindex=fileindex) and (f.line=line) and (f.moduleindex=moduleindex) then
  587. exit;
  588. end;
  589. result:=false;
  590. end;
  591. procedure AddFilepos(const f : tfileposinfo);
  592. begin
  593. Setlength(PSearchNodeInfo(arg)^.warnedfilelocs,length(PSearchNodeInfo(arg)^.warnedfilelocs)+1);
  594. PSearchNodeInfo(arg)^.warnedfilelocs[high(PSearchNodeInfo(arg)^.warnedfilelocs)]:=f;
  595. end;
  596. { Checks if the symbol is a candidate for a warning.
  597. Emit warning/note for living locals, result and parameters, but only about the current
  598. symtables }
  599. function SymbolCandidateForWarningOrHint(sym : tabstractnormalvarsym) : Boolean;
  600. begin
  601. Result:=(((sym.owner=current_procinfo.procdef.localst) and
  602. (current_procinfo.procdef.localst.symtablelevel=sym.owner.symtablelevel)
  603. ) or
  604. ((sym.owner=current_procinfo.procdef.parast) and
  605. (sym.typ=paravarsym) and
  606. (current_procinfo.procdef.parast.symtablelevel=sym.owner.symtablelevel) and
  607. { all parameters except out parameters are initialized by the caller }
  608. (tparavarsym(sym).varspez=vs_out)
  609. ) or
  610. ((vo_is_funcret in sym.varoptions) and
  611. (current_procinfo.procdef.parast.symtablelevel=sym.owner.symtablelevel)
  612. )
  613. ) and not(vo_is_external in sym.varoptions) and
  614. not sym.inparentfpstruct;
  615. end;
  616. var
  617. varsym : tabstractnormalvarsym;
  618. methodpointer,
  619. hpt : tnode;
  620. begin
  621. result:=fen_false;
  622. case n.nodetype of
  623. callparan:
  624. begin
  625. { do not warn about variables passed by var, just issue a hint, this
  626. is a workaround for old code e.g. using fillchar }
  627. if assigned(tcallparanode(n).parasym) and (tcallparanode(n).parasym.varspez in [vs_var,vs_out]) then
  628. begin
  629. hpt:=tcallparanode(n).left;
  630. while assigned(hpt) and (hpt.nodetype in [subscriptn,vecn,typeconvn]) do
  631. hpt:=tunarynode(hpt).left;
  632. if assigned(hpt) and (hpt.nodetype=loadn) and not(WarnedForLocation(hpt.fileinfo)) and
  633. SymbolCandidateForWarningOrHint(tabstractnormalvarsym(tloadnode(hpt).symtableentry)) and
  634. PSearchNodeInfo(arg)^.nodetosearch.isequal(hpt) then
  635. begin
  636. { issue only a hint for var, when encountering the node passed as out, we need only to stop searching }
  637. if tcallparanode(n).parasym.varspez=vs_var then
  638. UninitializedVariableMessage(hpt.fileinfo,false,
  639. tloadnode(hpt).symtable.symtabletype=localsymtable,
  640. is_managed_type(tloadnode(hpt).resultdef),
  641. tloadnode(hpt).symtableentry.RealName);
  642. AddFilepos(hpt.fileinfo);
  643. result:=fen_norecurse_true;
  644. end
  645. end;
  646. end;
  647. orn,
  648. andn:
  649. begin
  650. { take care of short boolean evaluation: if the expression to be search is found in left,
  651. we do not need to search right }
  652. if foreachnodestatic(pm_postprocess,taddnode(n).left,SearchNodeProcPointer,arg) or
  653. foreachnodestatic(pm_postprocess,taddnode(n).right,SearchNodeProcPointer,arg) then
  654. result:=fen_norecurse_true
  655. else
  656. result:=fen_norecurse_false;
  657. end;
  658. calln:
  659. begin
  660. methodpointer:=tcallnode(n).methodpointer;
  661. if assigned(methodpointer) and (methodpointer.nodetype<>typen) then
  662. begin
  663. { Remove all postfix operators }
  664. hpt:=methodpointer;
  665. while assigned(hpt) and (hpt.nodetype in [subscriptn,vecn]) do
  666. hpt:=tunarynode(hpt).left;
  667. { skip (absolute and other simple) type conversions -- only now,
  668. because the checks above have to take type conversions into
  669. e.g. class reference types account }
  670. hpt:=actualtargetnode(@hpt)^;
  671. { R.Init then R will be initialized by the constructor,
  672. Also allow it for simple loads }
  673. if (tcallnode(n).procdefinition.proctypeoption=potype_constructor) or
  674. (PSearchNodeInfo(arg)^.nodetosearch.isequal(hpt) and
  675. (((methodpointer.resultdef.typ=objectdef) and
  676. not(oo_has_virtual in tobjectdef(methodpointer.resultdef).objectoptions)) or
  677. (methodpointer.resultdef.typ=recorddef)
  678. )
  679. ) then
  680. begin
  681. { don't warn about the method pointer }
  682. AddFilepos(hpt.fileinfo);
  683. if not(foreachnodestatic(pm_postprocess,tcallnode(n).left,SearchNodeProcPointer,arg)) then
  684. foreachnodestatic(pm_postprocess,tcallnode(n).right,SearchNodeProcPointer,arg);
  685. result:=fen_norecurse_true
  686. end;
  687. end;
  688. end;
  689. loadn:
  690. begin
  691. if (tloadnode(n).symtableentry.typ in [localvarsym,paravarsym,staticvarsym]) and
  692. PSearchNodeInfo(arg)^.nodetosearch.isequal(n) and ((nf_modify in n.flags) or not(nf_write in n.flags)) then
  693. begin
  694. varsym:=tabstractnormalvarsym(tloadnode(n).symtableentry);
  695. if assigned(varsym.owner) and SymbolCandidateForWarningOrHint(varsym) then
  696. begin
  697. if (vo_is_funcret in varsym.varoptions) and not(WarnedForLocation(n.fileinfo)) then
  698. begin
  699. if is_managed_type(varsym.vardef) then
  700. MessagePos(n.fileinfo,sym_w_managed_function_result_uninitialized)
  701. else
  702. MessagePos(n.fileinfo,sym_w_function_result_uninitialized);
  703. AddFilepos(n.fileinfo);
  704. result:=fen_norecurse_true;
  705. end
  706. else
  707. begin
  708. { typed consts are initialized, further, warn only once per location }
  709. if not (vo_is_typed_const in varsym.varoptions) and not(WarnedForLocation(n.fileinfo)) then
  710. begin
  711. UninitializedVariableMessage(n.fileinfo,true,varsym.typ=localvarsym,is_managed_type(varsym.vardef),varsym.realname);
  712. AddFilepos(n.fileinfo);
  713. result:=fen_norecurse_true;
  714. end;
  715. end;
  716. end
  717. {$ifdef dummy}
  718. { if a the variable we are looking for is passed as a var parameter, we stop searching }
  719. else if assigned(varsym.owner) and
  720. (varsym.owner=current_procinfo.procdef.parast) and
  721. (varsym.typ=paravarsym) and
  722. (current_procinfo.procdef.parast.symtablelevel=varsym.owner.symtablelevel) and
  723. (tparavarsym(varsym).varspez=vs_var) then
  724. result:=fen_norecurse_true;
  725. {$endif dummy}
  726. end;
  727. end;
  728. else
  729. ;
  730. end;
  731. end;
  732. procedure CheckAndWarn(code : tnode;nodetosearch : tnode);
  733. var
  734. SearchNodeInfo : TSearchNodeInfo;
  735. function DoCheck(node : tnode) : boolean;
  736. var
  737. i : longint;
  738. touchesnode : Boolean;
  739. procedure MaybeDoCheck(n : tnode);inline;
  740. begin
  741. Result:=Result or DoCheck(n);
  742. end;
  743. procedure MaybeSearchIn(n : tnode);
  744. begin
  745. if touchesnode then
  746. Result:=Result or foreachnodestatic(pm_postprocess,n,@SearchNode,@SearchNodeInfo);
  747. end;
  748. begin
  749. result:=false;
  750. if node=nil then
  751. exit;
  752. if nf_processing in node.flags then
  753. exit;
  754. include(node.flags,nf_processing);
  755. if not(DFASetIn(node.optinfo^.life,nodetosearch.optinfo^.index)) then
  756. exit;
  757. { we do not need this info always, so try to safe some time here, CheckAndWarn
  758. takes a lot of time anyways }
  759. if not(node.nodetype in [statementn,blockn]) then
  760. touchesnode:=DFASetIn(node.optinfo^.use,nodetosearch.optinfo^.index) or
  761. DFASetIn(node.optinfo^.def,nodetosearch.optinfo^.index)
  762. else
  763. touchesnode:=false;
  764. case node.nodetype of
  765. whilerepeatn:
  766. begin
  767. MaybeSearchIn(twhilerepeatnode(node).left);
  768. MaybeDoCheck(twhilerepeatnode(node).right);
  769. end;
  770. forn:
  771. begin
  772. MaybeSearchIn(tfornode(node).right);
  773. MaybeSearchIn(tfornode(node).t1);
  774. MaybeDoCheck(tfornode(node).t2);
  775. end;
  776. statementn:
  777. MaybeDoCheck(tstatementnode(node).statement);
  778. blockn:
  779. MaybeDoCheck(tblocknode(node).statements);
  780. ifn:
  781. begin
  782. MaybeSearchIn(tifnode(node).left);
  783. MaybeDoCheck(tifnode(node).right);
  784. MaybeDoCheck(tifnode(node).t1);
  785. end;
  786. casen:
  787. begin
  788. MaybeSearchIn(tcasenode(node).left);
  789. for i:=0 to tcasenode(node).blocks.count-1 do
  790. MaybeDoCheck(pcaseblock(tcasenode(node).blocks[i])^.statement);
  791. MaybeDoCheck(tcasenode(node).elseblock);
  792. end;
  793. labeln:
  794. MaybeDoCheck(tlabelnode(node).left);
  795. { we are aware of the following nodes so if new node types are added to the compiler
  796. and pop up in the search, the ie below kicks in as a reminder }
  797. exitn:
  798. begin
  799. MaybeSearchIn(texitnode(node).left);
  800. { exit uses the resultnode implicitly, so searching for a matching node is
  801. useless, if we reach the exit node and found the living node not in left, then
  802. it can be only the resultnode }
  803. if not(Result) and not(is_void(current_procinfo.procdef.returndef)) and
  804. not(assigned(texitnode(node).resultexpr)) and
  805. { don't warn about constructors }
  806. not(current_procinfo.procdef.proctypeoption in [potype_class_constructor,potype_constructor]) then
  807. begin
  808. if is_managed_type(current_procinfo.procdef.returndef) then
  809. MessagePos(node.fileinfo,sym_w_managed_function_result_uninitialized)
  810. else
  811. MessagePos(node.fileinfo,sym_w_function_result_uninitialized);
  812. Setlength(SearchNodeInfo.warnedfilelocs,length(SearchNodeInfo.warnedfilelocs)+1);
  813. SearchNodeInfo.warnedfilelocs[high(SearchNodeInfo.warnedfilelocs)]:=node.fileinfo;
  814. end
  815. end;
  816. { could be the implicitly generated load node for the result }
  817. {$ifdef JVM}
  818. { all other platforms except jvm translate raise nodes into call nodes during pass_1 }
  819. raisen,
  820. {$endif JVM}
  821. loadn,
  822. assignn,
  823. calln,
  824. temprefn,
  825. typeconvn,
  826. inlinen,
  827. tempcreaten,
  828. tempdeleten:
  829. MaybeSearchIn(node);
  830. nothingn,
  831. continuen,
  832. goton,
  833. breakn:
  834. ;
  835. else
  836. internalerror(2013111301);
  837. end;
  838. { if already a warning has been issued, then stop }
  839. if Result then
  840. exit;
  841. if assigned(node.successor) then
  842. MaybeDoCheck(node.successor);
  843. end;
  844. begin
  845. SearchNodeInfo.nodetosearch:=nodetosearch;
  846. DoCheck(code);
  847. foreachnodestatic(pm_postprocess,code,@ResetProcessing,nil);
  848. end;
  849. begin
  850. SearchNodeProcPointer:=@SearchNode;
  851. end.