aopt.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. {
  2. Copyright (c) 1998-2004 by Jonas Maebe, member of the Free Pascal
  3. Development Team
  4. This unit contains the interface routines between the code generator
  5. and the optimizer.
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program; if not, write to the Free Software
  16. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17. ****************************************************************************
  18. }
  19. Unit aopt;
  20. {$i fpcdefs.inc}
  21. { $define DEBUG_OPTALLOC}
  22. { $define DEBUG_INSTRUCTIONREGISTERDEPENDENCIES}
  23. Interface
  24. Uses
  25. aasmbase,aasmtai,aasmdata,aasmcpu,
  26. aoptobj;
  27. Type
  28. TAsmOptimizer = class(TAoptObj)
  29. { Pooled object that can be used by optimisation procedures to evaluate
  30. future register usage without upsetting the current state. }
  31. TmpUsedRegs: TAllUsedRegs;
  32. { _AsmL is the PAasmOutpout list that has to be optimized }
  33. Constructor create(_AsmL: TAsmList); virtual; reintroduce;
  34. { call the necessary optimizer procedures }
  35. Procedure Optimize;virtual;
  36. Destructor destroy;override;
  37. private
  38. procedure FindLoHiLabels;
  39. { Builds a table with the locations of the labels in the TAsmList.
  40. Also fixes some RegDeallocs like "# %eax released; push (%eax)" }
  41. Procedure BuildLabelTableAndFixRegAlloc;
  42. procedure clear;
  43. protected
  44. procedure pass_1;
  45. End;
  46. TAsmOptimizerClass = class of TAsmOptimizer;
  47. TAsmScheduler = class(TAoptObj)
  48. { _AsmL is the PAasmOutpout list that has to be re-scheduled }
  49. Constructor Create(_AsmL: TAsmList); virtual; reintroduce;
  50. Procedure Optimize;
  51. function SchedulerPass1Cpu(var p: tai): boolean; virtual; abstract;
  52. procedure SchedulerPass1;
  53. end;
  54. TAsmSchedulerClass = class of TAsmScheduler;
  55. var
  56. casmoptimizer : TAsmOptimizerClass;
  57. cpreregallocscheduler : TAsmSchedulerClass;
  58. procedure Optimize(AsmL:TAsmList);
  59. procedure PreRegallocSchedule(AsmL:TAsmList);
  60. Implementation
  61. uses
  62. cutils,
  63. globtype, globals,
  64. verbose,
  65. cpubase,
  66. cgbase,
  67. aoptcpu;
  68. Constructor TAsmOptimizer.create(_AsmL: TAsmList);
  69. Begin
  70. inherited create(_asml,nil,nil,nil);
  71. { setup labeltable, always necessary }
  72. New(LabelInfo);
  73. CreateUsedRegs(TmpUsedRegs);
  74. End;
  75. procedure TAsmOptimizer.FindLoHiLabels;
  76. { Walks through the paasmlist to find the lowest and highest label number. }
  77. { Returns the last Pai object of the current block }
  78. Var LabelFound: Boolean;
  79. p: tai;
  80. Begin
  81. LabelInfo^.LowLabel := High(longint);
  82. LabelInfo^.HighLabel := 0;
  83. LabelInfo^.LabelDif := 0;
  84. LabelInfo^.LabelTable:=nil;
  85. LabelFound := False;
  86. P := BlockStart;
  87. With LabelInfo^ Do
  88. Begin
  89. While Assigned(P) And
  90. ((P.typ <> Ait_Marker) Or
  91. (tai_Marker(P).Kind <> mark_AsmBlockStart)) Do
  92. Begin
  93. If (p.typ = ait_label) and
  94. (tai_Label(p).labsym.labeltype=alt_jump) and
  95. (tai_Label(p).labsym.is_used) Then
  96. Begin
  97. LabelFound := True;
  98. If (tai_Label(p).labsym.labelnr < LowLabel) Then
  99. LowLabel := tai_Label(p).labsym.labelnr;
  100. If (tai_Label(p).labsym.labelnr > HighLabel) Then
  101. HighLabel := tai_Label(p).labsym.labelnr
  102. End;
  103. GetNextInstruction(p, p)
  104. End;
  105. blockend:=p;
  106. If LabelFound
  107. Then LabelDif := HighLabel-LowLabel+1
  108. Else LabelDif := 0
  109. End
  110. End;
  111. Procedure TAsmOptimizer.BuildLabelTableAndFixRegAlloc;
  112. Var p,hp1, hp2: tai;
  113. Regs: TAllUsedRegs;
  114. LabelIdx : longint;
  115. Begin
  116. CreateUsedRegs(Regs);
  117. With LabelInfo^ Do
  118. begin
  119. If (LabelDif <> 0) Then
  120. Begin
  121. GetMem(LabelTable, LabelDif*SizeOf(TLabelTableItem));
  122. FillChar(LabelTable^, LabelDif*SizeOf(TLabelTableItem), 0);
  123. end;
  124. p := BlockStart;
  125. While (P <> BlockEnd) Do
  126. Begin
  127. Case p.typ Of
  128. ait_Label:
  129. begin
  130. If tai_label(p).labsym.is_used and
  131. (tai_Label(p).labsym.labeltype=alt_jump) then
  132. begin
  133. LabelIdx:=tai_label(p).labsym.labelnr-LowLabel;
  134. if LabelIdx>int64(LabelDif) then
  135. internalerror(200604202);
  136. LabelTable^[LabelIdx].PaiObj := p;
  137. end;
  138. end;
  139. ait_regAlloc:
  140. begin
  141. if tai_regalloc(p).ratype=ra_alloc then
  142. Begin
  143. If Not(RegInUsedRegs(tai_regalloc(p).Reg,Regs)) Then
  144. IncludeRegInUsedRegs(tai_regalloc(p).Reg,Regs)
  145. Else
  146. Begin
  147. hp1 := tai(p.previous);
  148. {$ifdef DEBUG_OPTALLOC}
  149. AsmL.InsertAfter(tai_comment.Create(strpnew('Removed allocation of '+std_regname(tai_regalloc(p).Reg))),p);
  150. {$endif DEBUG_OPTALLOC}
  151. AsmL.remove(p);
  152. p.free;
  153. p := hp1;
  154. { not sure if this is useful, it even skips previous deallocs of the register (FK)
  155. hp1 := p;
  156. hp2 := nil;
  157. While GetLastInstruction(hp1, hp1) And
  158. Not(RegInInstruction(tai_regalloc(p).Reg, hp1)) Do
  159. hp2:=hp1;
  160. If hp2<>nil Then
  161. Begin
  162. hp1:=tai_regalloc.DeAlloc(tai_regalloc(p).Reg,hp2);
  163. InsertLLItem(tai(hp2.previous), hp2, hp1);
  164. End;
  165. }
  166. End;
  167. End
  168. else if tai_regalloc(p).ratype=ra_dealloc then
  169. Begin
  170. ExcludeRegFromUsedRegs(tai_regalloc(p).Reg,Regs);
  171. hp1 := p;
  172. hp2 := nil;
  173. While Not(assigned(FindRegAlloc(tai_regalloc(p).Reg, tai(hp1.Next)))) And
  174. GetNextInstruction(hp1, hp1) And
  175. InstructionLoadsFromReg(tai_regalloc(p).Reg, hp1) Do
  176. hp2 := hp1;
  177. { move deallocations }
  178. If hp2 <> nil Then
  179. Begin
  180. hp1 := tai(p.previous);
  181. {$ifdef DEBUG_OPTALLOC}
  182. AsmL.InsertAfter(tai_comment.Create(strpnew('Moved deallocation of '+std_regname(tai_regalloc(p).Reg))),p);
  183. {$endif DEBUG_OPTALLOC}
  184. AsmL.Remove(p);
  185. InsertLLItem(hp2, tai(hp2.Next), p);
  186. { don't remove this deallocation later on when merging dealloc/alloc pairs because
  187. it marks indenpendent use of a register
  188. This could be also achieved by a separate passes for merging first and then later
  189. moving but I did not choose this solution because it takes more time and code (FK) }
  190. tai_regalloc(p).keep:=true;
  191. {$ifdef DEBUG_OPTALLOC}
  192. AsmL.InsertAfter(tai_comment.Create(strpnew('Moved deallocation of '+std_regname(tai_regalloc(p).Reg)+' here')),hp2);
  193. {$endif DEBUG_OPTALLOC}
  194. p := hp1;
  195. End
  196. { merge allocations/deallocations }
  197. else if assigned(findregalloc(tai_regalloc(p).reg, tai(p.next)))
  198. and getnextinstruction(p,hp1) and
  199. { don't merge deallocations/allocation which mark a new use of register, this
  200. enables more possibilities for the peephole optimizer }
  201. not(tai_regalloc(p).keep) then
  202. begin
  203. hp1 := tai(p.previous);
  204. {$ifdef DEBUG_OPTALLOC}
  205. AsmL.InsertAfter(tai_comment.Create(strpnew('Removed deallocation of '+std_regname(tai_regalloc(p).Reg))),p);
  206. {$endif DEBUG_OPTALLOC}
  207. AsmL.remove(p);
  208. p.free;
  209. p := hp1;
  210. end;
  211. End
  212. End
  213. else
  214. ;
  215. End;
  216. P := tai(p.Next);
  217. While Assigned(p) and
  218. (p <> blockend) and
  219. (p.typ in (SkipInstr - [ait_regalloc])) Do
  220. P := tai(P.Next)
  221. End;
  222. end;
  223. ReleaseUsedRegs(Regs);
  224. End;
  225. procedure tasmoptimizer.clear;
  226. begin
  227. if assigned(LabelInfo^.labeltable) then
  228. begin
  229. freemem(LabelInfo^.labeltable);
  230. LabelInfo^.labeltable := nil;
  231. end;
  232. LabelInfo^.labeldif:=0;
  233. LabelInfo^.lowlabel:=high(longint);
  234. LabelInfo^.highlabel:=0;
  235. end;
  236. procedure tasmoptimizer.pass_1;
  237. begin
  238. findlohilabels;
  239. BuildLabelTableAndFixRegAlloc;
  240. end;
  241. Procedure TAsmOptimizer.Optimize;
  242. Var
  243. HP: tai;
  244. pass: longint;
  245. Begin
  246. pass:=0;
  247. BlockStart := tai(AsmL.First);
  248. pass_1;
  249. While Assigned(BlockStart) Do
  250. Begin
  251. if (cs_opt_peephole in current_settings.optimizerswitches) then
  252. begin
  253. if pass = 0 then
  254. PrePeepHoleOpts;
  255. { Peephole optimizations }
  256. PeepHoleOptPass1;
  257. { Only perform them twice in the first pass }
  258. if pass = 0 then
  259. PeepHoleOptPass1;
  260. end;
  261. { more peephole optimizations }
  262. if (cs_opt_peephole in current_settings.optimizerswitches) then
  263. begin
  264. PeepHoleOptPass2;
  265. { if pass = last_pass then }
  266. PostPeepHoleOpts;
  267. end;
  268. { free memory }
  269. clear;
  270. { continue where we left off, BlockEnd is either the start of an }
  271. { assembler block or nil}
  272. BlockStart := BlockEnd;
  273. While Assigned(BlockStart) And
  274. (BlockStart.typ = ait_Marker) And
  275. (tai_Marker(BlockStart).Kind = mark_AsmBlockStart) Do
  276. Begin
  277. { we stopped at an assembler block, so skip it }
  278. While GetNextInstruction(BlockStart, BlockStart) And
  279. ((BlockStart.Typ <> Ait_Marker) Or
  280. (tai_Marker(Blockstart).Kind <> mark_AsmBlockEnd)) Do;
  281. { blockstart now contains a tai_marker(mark_AsmBlockEnd) }
  282. If GetNextInstruction(BlockStart, HP) And
  283. ((HP.typ <> ait_Marker) Or
  284. (Tai_Marker(HP).Kind <> mark_AsmBlockStart)) Then
  285. { There is no assembler block anymore after the current one, so }
  286. { optimize the next block of "normal" instructions }
  287. pass_1
  288. { Otherwise, skip the next assembler block }
  289. else
  290. blockStart := hp;
  291. End
  292. End;
  293. End;
  294. Destructor TAsmOptimizer.Destroy;
  295. Begin
  296. ReleaseUsedRegs(TmpUsedRegs);
  297. if assigned(LabelInfo^.LabelTable) then
  298. Freemem(LabelInfo^.LabelTable);
  299. Dispose(LabelInfo);
  300. inherited Destroy;
  301. End;
  302. constructor TAsmScheduler.Create(_AsmL: TAsmList);
  303. begin
  304. inherited create(_asml,nil,nil,nil);
  305. end;
  306. procedure TAsmScheduler.SchedulerPass1;
  307. var
  308. p : tai;
  309. begin
  310. p:=BlockStart;
  311. while p<>BlockEnd Do
  312. begin
  313. if SchedulerPass1Cpu(p) then
  314. continue;
  315. p:=tai(p.next);
  316. end;
  317. end;
  318. procedure TAsmScheduler.Optimize;
  319. Var
  320. HP: tai;
  321. Begin
  322. BlockStart := tai(AsmL.First);
  323. While Assigned(BlockStart) Do
  324. Begin
  325. { Peephole optimizations }
  326. SchedulerPass1;
  327. { continue where we left off, BlockEnd is either the start of an }
  328. { assembler block or nil}
  329. BlockStart:=BlockEnd;
  330. While Assigned(BlockStart) And
  331. (BlockStart.typ = ait_Marker) And
  332. (tai_Marker(BlockStart).Kind = mark_AsmBlockStart) Do
  333. Begin
  334. { we stopped at an assembler block, so skip it }
  335. While GetNextInstruction(BlockStart, BlockStart) And
  336. ((BlockStart.Typ <> Ait_Marker) Or
  337. (tai_Marker(Blockstart).Kind <> mark_AsmBlockEnd)) Do;
  338. { blockstart now contains a tai_marker(mark_AsmBlockEnd) }
  339. If not(GetNextInstruction(BlockStart, HP) And
  340. ((HP.typ <> ait_Marker) Or
  341. (Tai_Marker(HP).Kind <> mark_AsmBlockStart))) Then
  342. { skip the next assembler block }
  343. blockStart := hp;
  344. End
  345. End;
  346. End;
  347. procedure Optimize(AsmL:TAsmList);
  348. var
  349. p : TAsmOptimizer;
  350. begin
  351. p:=casmoptimizer.Create(AsmL);
  352. p.Optimize;
  353. {$ifdef DEBUG_INSTRUCTIONREGISTERDEPENDENCIES}
  354. p.Debug_InsertInstrRegisterDependencyInfo;
  355. {$endif DEBUG_INSTRUCTIONREGISTERDEPENDENCIES}
  356. p.free
  357. end;
  358. procedure PreRegallocSchedule(AsmL:TAsmList);
  359. var
  360. p : TAsmScheduler;
  361. begin
  362. p:=cpreregallocscheduler.Create(AsmL);
  363. p.Optimize;
  364. p.free
  365. end;
  366. end.