2
0

optdfa.pas 37 KB

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