optdfa.pas 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001
  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. successor might be assigned in case of an inlined exit node, in this case we do not warn about an unassigned
  804. result as this had happened already when the routine has been compiled }
  805. if not(assigned(node.successor)) and not(Result) and not(is_void(current_procinfo.procdef.returndef)) and
  806. not(assigned(texitnode(node).resultexpr)) and
  807. { don't warn about constructors }
  808. not(current_procinfo.procdef.proctypeoption in [potype_class_constructor,potype_constructor]) then
  809. begin
  810. if is_managed_type(current_procinfo.procdef.returndef) then
  811. MessagePos(node.fileinfo,sym_w_managed_function_result_uninitialized)
  812. else
  813. MessagePos(node.fileinfo,sym_w_function_result_uninitialized);
  814. Setlength(SearchNodeInfo.warnedfilelocs,length(SearchNodeInfo.warnedfilelocs)+1);
  815. SearchNodeInfo.warnedfilelocs[high(SearchNodeInfo.warnedfilelocs)]:=node.fileinfo;
  816. end
  817. end;
  818. { could be the implicitly generated load node for the result }
  819. {$ifdef JVM}
  820. { all other platforms except jvm translate raise nodes into call nodes during pass_1 }
  821. raisen,
  822. {$endif JVM}
  823. loadn,
  824. assignn,
  825. calln,
  826. temprefn,
  827. typeconvn,
  828. inlinen,
  829. tempcreaten,
  830. tempdeleten:
  831. MaybeSearchIn(node);
  832. nothingn,
  833. continuen,
  834. goton,
  835. breakn:
  836. ;
  837. else
  838. internalerror(2013111301);
  839. end;
  840. { if already a warning has been issued, then stop }
  841. if Result then
  842. exit;
  843. if assigned(node.successor) then
  844. MaybeDoCheck(node.successor);
  845. end;
  846. begin
  847. SearchNodeInfo.nodetosearch:=nodetosearch;
  848. DoCheck(code);
  849. foreachnodestatic(pm_postprocess,code,@ResetProcessing,nil);
  850. end;
  851. begin
  852. SearchNodeProcPointer:=@SearchNode;
  853. end.