optdfa.pas 37 KB

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