aoptcpu.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. {
  2. Copyright (c) 1998-2014 by the Free Pascal development team
  3. This unit calls the optimization procedures to optimize the assembler
  4. code for m68k
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. ****************************************************************************
  17. }
  18. unit aoptcpu;
  19. {$i fpcdefs.inc}
  20. {$define DEBUG_AOPTCPU}
  21. Interface
  22. uses
  23. cpubase, aoptobj, aoptcpub, aopt, aasmtai,
  24. cgbase;
  25. Type
  26. TCpuAsmOptimizer = class(TAsmOptimizer)
  27. function RegLoadedWithNewValue(reg: tregister; hp: tai): boolean; override;
  28. function PeepHoleOptPass1Cpu(var p: tai): boolean; override;
  29. function TryToOptimizeMove(var p: tai): boolean;
  30. { outputs a debug message into the assembler file }
  31. procedure DebugMsg(const s: string; p: tai);
  32. End;
  33. Implementation
  34. uses
  35. cutils, aasmcpu, cgutils, globals, verbose, cpuinfo, itcpugas;
  36. { Range check must be disabled explicitly as conversions between signed and unsigned
  37. 32-bit values are done without explicit typecasts }
  38. {$R-}
  39. function opname(var p: tai): string;
  40. begin
  41. result:=upcase(gas_op2str[taicpu(p).opcode]);
  42. end;
  43. function RefsEqual(const r1, r2: treference): boolean;
  44. begin
  45. RefsEqual :=
  46. (r1.offset = r2.offset) and
  47. (r1.base = r2.base) and
  48. (r1.index = r2.index) and (r1.scalefactor = r2.scalefactor) and
  49. (r1.symbol=r2.symbol) and (r1.refaddr = r2.refaddr) and
  50. (r1.relsymbol = r2.relsymbol);
  51. end;
  52. function MatchOperand(const oper1: TOper; const oper2: TOper): boolean;
  53. begin
  54. result := oper1.typ = oper2.typ;
  55. if result then
  56. case oper1.typ of
  57. top_const:
  58. Result:=oper1.val = oper2.val;
  59. top_reg:
  60. Result:=oper1.reg = oper2.reg;
  61. top_ref:
  62. Result:=RefsEqual(oper1.ref^, oper2.ref^);
  63. else
  64. internalerror(2016112401);
  65. end
  66. end;
  67. function TCpuAsmOptimizer.RegLoadedWithNewValue(reg: tregister; hp: tai): boolean;
  68. var
  69. p: taicpu;
  70. begin
  71. if not assigned(hp) or
  72. (hp.typ <> ait_instruction) then
  73. begin
  74. Result := false;
  75. exit;
  76. end;
  77. p := taicpu(hp);
  78. Result :=
  79. ((p.opcode in [A_MOVE,A_MOVEA,A_MVS,A_MVZ,A_MOVEQ,A_LEA]) and
  80. (p.oper[1]^.typ = top_reg) and
  81. (SuperRegistersEqual(p.oper[1]^.reg,reg)) and
  82. ((p.oper[0]^.typ = top_const) or
  83. ((p.oper[0]^.typ = top_reg) and
  84. not(SuperRegistersEqual(p.oper[0]^.reg,reg))) or
  85. ((p.oper[0]^.typ = top_ref) and
  86. not RegInRef(reg,p.oper[0]^.ref^)))) or
  87. ((p.opcode = A_FMOVE) and
  88. (p.oper[1]^.typ = top_reg) and
  89. (SuperRegistersEqual(p.oper[1]^.reg,reg)) and
  90. ((p.oper[0]^.typ = top_realconst) or
  91. ((p.oper[0]^.typ = top_reg) and
  92. not(SuperRegistersEqual(p.oper[0]^.reg,reg))))) or
  93. ((p.opcode = A_MOVEM) and
  94. (p.oper[1]^.typ = top_regset) and
  95. ((getsupreg(reg) in p.oper[1]^.dataregset) or
  96. (getsupreg(reg) in p.oper[1]^.addrregset))) or
  97. ((p.opcode = A_FMOVEM) and
  98. (p.oper[1]^.typ = top_regset) and
  99. (getsupreg(reg) in p.oper[1]^.fpuregset));
  100. end;
  101. {$ifdef DEBUG_AOPTCPU}
  102. procedure TCpuAsmOptimizer.DebugMsg(const s: string; p : tai);
  103. begin
  104. asml.insertbefore(tai_comment.Create(strpnew(s)), p);
  105. end;
  106. {$else DEBUG_AOPTCPU}
  107. procedure TCpuAsmOptimizer.DebugMsg(const s: string; p : tai);inline;
  108. begin
  109. end;
  110. {$endif DEBUG_AOPTCPU}
  111. function TCpuAsmOptimizer.TryToOptimizeMove(var p: tai): boolean;
  112. var
  113. next, next2: tai;
  114. opstr: string[15];
  115. begin
  116. result:=false;
  117. if GetNextInstruction(p,next) and
  118. (next.typ = ait_instruction) and
  119. (taicpu(next).opcode = taicpu(p).opcode) and
  120. (taicpu(p).opsize = taicpu(next).opsize) and
  121. (taicpu(p).oper[1]^.typ = top_reg) and
  122. MatchOperand(taicpu(p).oper[1]^,taicpu(next).oper[0]^) then
  123. begin
  124. if not(RegInOp(taicpu(p).oper[1]^.reg,taicpu(next).oper[1]^)) and
  125. RegEndOfLife(taicpu(next).oper[0]^.reg, taicpu(next)) then
  126. begin
  127. opstr:=opname(p);
  128. case taicpu(p).oper[0]^.typ of
  129. top_reg:
  130. { do not optimize away FPU to INT to FPU reg moves. These are used for
  131. to-single-rounding on FPUs which have no FSMOVE/FDMOVE. (KB) }
  132. if not ((taicpu(p).opcode = A_FMOVE) and
  133. (getregtype(taicpu(p).oper[0]^.reg) <> getregtype(taicpu(p).oper[1]^.reg))) then
  134. begin
  135. { move %reg0, %tmpreg; move %tmpreg, <ea> -> move %reg0, <ea> }
  136. taicpu(p).loadOper(1,taicpu(next).oper[1]^);
  137. asml.remove(next);
  138. next.free;
  139. result:=true;
  140. { also remove leftover move %reg0, %reg0, which can occur as the result
  141. of the previous optimization, if %reg0 and %tmpreg was different types
  142. (addr vs. data), so these moves were left in by the cg }
  143. if MatchOperand(taicpu(p).oper[0]^,taicpu(p).oper[1]^) then
  144. begin
  145. DebugMsg('Optimizer: '+opstr+' + '+opstr+' removed',p);
  146. GetNextInstruction(p,next);
  147. asml.remove(p);
  148. p.free;
  149. p:=next;
  150. end
  151. else
  152. DebugMsg('Optimizer: '+opstr+' + '+opstr+' to '+opstr+' #1',p)
  153. end;
  154. top_const:
  155. begin
  156. // DebugMsg('Optimizer: '+opstr+' + '+opstr+' to '+opstr+' #2',p);
  157. end;
  158. top_ref:
  159. begin
  160. { move ref, %tmpreg; move %tmpreg, <ea> -> move ref, <ea> }
  161. { we only want to do this when <ea> is a reg or a simple reference }
  162. with taicpu(next).oper[1]^ do
  163. if (taicpu(next).opcode <> A_FMOVE) and
  164. ((typ = top_reg) or
  165. ((typ = top_ref) and
  166. ((ref^.index = NR_NO) or
  167. (ref^.base = NR_NO)) and
  168. (ref^.symbol = nil) and
  169. (ref^.offset = 0))) then
  170. begin
  171. DebugMsg('Optimizer: '+opstr+' + '+opstr+' to '+opstr+' #3',p);
  172. taicpu(p).loadOper(1,taicpu(next).oper[1]^);
  173. asml.remove(next);
  174. next.free;
  175. result:=true;
  176. end;
  177. end;
  178. end;
  179. end;
  180. exit;
  181. end;
  182. if GetNextInstruction(p,next) and
  183. (next.typ = ait_instruction) and
  184. GetNextInstruction(next,next2) and
  185. (next2.typ = ait_instruction) and
  186. (taicpu(next).opcode <> taicpu(p).opcode) and
  187. (taicpu(next2).opcode = taicpu(p).opcode) and
  188. (taicpu(p).oper[0]^.typ = top_reg) and
  189. (taicpu(p).oper[1]^.typ = top_reg) and
  190. (getregtype(taicpu(p).oper[0]^.reg) = getregtype(taicpu(p).oper[1]^.reg)) and
  191. MatchOperand(taicpu(p).oper[1]^,taicpu(next2).oper[0]^) and
  192. MatchOperand(taicpu(next2).oper[1]^,taicpu(p).oper[0]^) and
  193. (taicpu(p).opsize = taicpu(next2).opsize) and
  194. ((taicpu(p).opcode = A_FMOVE) or
  195. (taicpu(p).opsize = taicpu(next).opsize)) then
  196. begin
  197. opstr:=opname(p);
  198. if not(RegInOp(taicpu(p).oper[1]^.reg,taicpu(next2).oper[1]^)) and
  199. not(RegInOp(taicpu(p).oper[1]^.reg,taicpu(next).oper[0]^)) and
  200. RegEndOfLife(taicpu(p).oper[0]^.reg, taicpu(next2)) then
  201. begin
  202. { move %reg0, %tmpreg
  203. op ???, %tmpreg
  204. move %tmpreg, %reg0
  205. to:
  206. op ???, %reg0 }
  207. if MatchOperand(taicpu(p).oper[1]^,taicpu(next).oper[taicpu(next).ops-1]^) then
  208. begin
  209. {
  210. Disabled, because it breaks some tests... :( (KB)
  211. DebugMsg('Optimizer: '+opstr+' + OP + '+opstr+' to OP #1',next);
  212. taicpu(next).loadOper(taicpu(next).ops-1,taicpu(p).oper[0]^);
  213. asml.remove(p);
  214. asml.remove(next2);
  215. p.free;
  216. next2.free;
  217. result:=true;
  218. }
  219. end;
  220. end;
  221. end;
  222. end;
  223. function TCpuAsmOptimizer.PeepHoleOptPass1Cpu(var p: tai): boolean;
  224. var
  225. next: tai;
  226. tmpref: treference;
  227. tmpsingle: single;
  228. begin
  229. result:=false;
  230. case p.typ of
  231. ait_instruction:
  232. begin
  233. //asml.insertbefore(tai_comment.Create(strpnew('pass1 called for instr')), p);
  234. case taicpu(p).opcode of
  235. A_MOVE:
  236. result:=TryToOptimizeMove(p);
  237. { LEA (Ax),Ax is a NOP if src and dest reg is equal, so remove it. }
  238. A_LEA:
  239. if not assigned(taicpu(p).oper[0]^.ref^.symbol) and
  240. (((taicpu(p).oper[0]^.ref^.base = taicpu(p).oper[1]^.reg) and
  241. (taicpu(p).oper[0]^.ref^.index = NR_NO)) or
  242. ((taicpu(p).oper[0]^.ref^.index = taicpu(p).oper[1]^.reg) and
  243. (taicpu(p).oper[0]^.ref^.base = NR_NO))) and
  244. (taicpu(p).oper[0]^.ref^.offset = 0) then
  245. begin
  246. DebugMsg('Optimizer: LEA 0(Ax),Ax removed',p);
  247. GetNextInstruction(p,next);
  248. asml.remove(p);
  249. p.free;
  250. p:=next;
  251. result:=true;
  252. end;
  253. { Address register sub/add can be replaced with ADDQ/SUBQ or LEA if the value is in the
  254. SmallInt range, which is shorter to encode and faster to execute on most 68k }
  255. A_SUB,A_SUBA,A_ADD,A_ADDA:
  256. if (taicpu(p).oper[1]^.typ = top_reg) and isaddressregister(taicpu(p).oper[1]^.reg) and
  257. (taicpu(p).oper[0]^.typ = top_const) then
  258. begin
  259. if isvalueforaddqsubq(taicpu(p).oper[0]^.val) then
  260. begin
  261. DebugMsg('Optimizer: SUB/ADD #val,Ax to SUBQ/ADDQ',p);
  262. taicpu(p).opsize:=S_L; // this is safe, because we're targetting an address reg
  263. if taicpu(p).opcode in [A_ADD,A_ADDA] then
  264. taicpu(p).opcode:=A_ADDQ
  265. else
  266. taicpu(p).opcode:=A_SUBQ;
  267. result:=true;
  268. end
  269. else
  270. if isvalue16bit(abs(taicpu(p).oper[0]^.val)) then
  271. begin
  272. DebugMsg('Optimizer: SUB/ADD #val,Ax to LEA val(Ax),Ax',p);
  273. if taicpu(p).opcode in [A_SUB,A_SUBA] then
  274. reference_reset_base(tmpref,taicpu(p).oper[1]^.reg,-taicpu(p).oper[0]^.val,ctempposinvalid,0,[])
  275. else
  276. reference_reset_base(tmpref,taicpu(p).oper[1]^.reg,taicpu(p).oper[0]^.val,ctempposinvalid,0,[]);
  277. taicpu(p).opcode:=A_LEA;
  278. taicpu(p).loadref(0,tmpref);
  279. result:=true;
  280. end;
  281. end;
  282. { MOVEA #0,Ax to SUBA Ax,Ax, because it's shorter }
  283. A_MOVEA:
  284. if (taicpu(p).oper[0]^.typ = top_const) and
  285. (taicpu(p).oper[0]^.val = 0) then
  286. begin
  287. DebugMsg('Optimizer: MOVEA #0,Ax to SUBA Ax,Ax',p);
  288. taicpu(p).opcode:=A_SUBA;
  289. taicpu(p).opsize:=S_L; { otherwise it will be .W -> BOOM }
  290. taicpu(p).loadoper(0,taicpu(p).oper[1]^);
  291. result:=true;
  292. end;
  293. { CLR.L Dx on a 68000 is slower than MOVEQ #0,Dx }
  294. A_CLR:
  295. if (current_settings.cputype in [cpu_mc68000]) and
  296. (taicpu(p).oper[0]^.typ = top_reg) and
  297. (taicpu(p).opsize = S_L) and
  298. isintregister(taicpu(p).oper[0]^.reg) then
  299. begin
  300. //DebugMsg('Optimizer: CLR.L Dx to MOVEQ #0,Dx',p);
  301. taicpu(p).opcode:=A_MOVEQ;
  302. taicpu(p).loadoper(1,taicpu(p).oper[0]^);
  303. taicpu(p).loadconst(0,0);
  304. taicpu(p).ops:=2;
  305. result:=true;
  306. end;
  307. { CMP #0,<ea> equals to TST <ea>, just shorter and TST is more flexible anyway }
  308. A_CMP,A_CMPI:
  309. if (taicpu(p).oper[0]^.typ = top_const) and
  310. (taicpu(p).oper[0]^.val = 0) then
  311. begin
  312. DebugMsg('Optimizer: CMP #0 to TST',p);
  313. taicpu(p).opcode:=A_TST;
  314. taicpu(p).loadoper(0,taicpu(p).oper[1]^);
  315. taicpu(p).clearop(1);
  316. taicpu(p).ops:=1;
  317. result:=true;
  318. end;
  319. A_FCMP:
  320. if (taicpu(p).oper[0]^.typ = top_realconst) then
  321. begin
  322. if (taicpu(p).oper[0]^.val_real = 0.0) then
  323. begin
  324. DebugMsg('Optimizer: FCMP #0.0 to FTST',p);
  325. taicpu(p).opcode:=A_FTST;
  326. taicpu(p).opsize:=S_FX;
  327. taicpu(p).loadoper(0,taicpu(p).oper[1]^);
  328. taicpu(p).clearop(1);
  329. taicpu(p).ops:=1;
  330. result:=true;
  331. end
  332. else
  333. begin
  334. tmpsingle:=taicpu(p).oper[0]^.val_real;
  335. if (taicpu(p).opsize = S_FD) and
  336. ((taicpu(p).oper[0]^.val_real - tmpsingle) = 0.0) then
  337. begin
  338. DebugMsg('Optimizer: FCMP const to lesser precision',p);
  339. taicpu(p).opsize:=S_FS;
  340. result:=true;
  341. end;
  342. end;
  343. end;
  344. A_FMOVE,A_FMUL,A_FADD,A_FSUB,A_FDIV:
  345. begin
  346. if (taicpu(p).opcode = A_FMOVE) and TryToOptimizeMove(p) then
  347. begin
  348. result:=true;
  349. exit;
  350. end;
  351. if (taicpu(p).oper[0]^.typ = top_realconst) then
  352. begin
  353. tmpsingle:=taicpu(p).oper[0]^.val_real;
  354. if (taicpu(p).opsize = S_FD) and
  355. ((taicpu(p).oper[0]^.val_real - tmpsingle) = 0.0) then
  356. begin
  357. DebugMsg('Optimizer: FMOVE/FMUL/FADD/FSUB/FDIV const to lesser precision',p);
  358. taicpu(p).opsize:=S_FS;
  359. result:=true;
  360. end;
  361. end;
  362. end;
  363. end;
  364. end;
  365. end;
  366. end;
  367. begin
  368. casmoptimizer:=TCpuAsmOptimizer;
  369. end.