aoptcpu.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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. function MaybeRealConstOperSimplify(var p: tai): boolean;
  31. { outputs a debug message into the assembler file }
  32. procedure DebugMsg(const s: string; p: tai);
  33. End;
  34. Implementation
  35. uses
  36. cutils, aasmcpu, cgutils, globtype, globals, verbose, cpuinfo, itcpugas;
  37. { Range check must be disabled explicitly as conversions between signed and unsigned
  38. 32-bit values are done without explicit typecasts }
  39. {$R-}
  40. function opname(var p: tai): string;
  41. begin
  42. result:=upcase(gas_op2str[taicpu(p).opcode]);
  43. end;
  44. function RefsEqual(const r1, r2: treference): boolean;
  45. begin
  46. RefsEqual :=
  47. (r1.offset = r2.offset) and
  48. (r1.base = r2.base) and
  49. (r1.index = r2.index) and (r1.scalefactor = r2.scalefactor) and
  50. (r1.symbol=r2.symbol) and (r1.refaddr = r2.refaddr) and
  51. (r1.relsymbol = r2.relsymbol) and
  52. (r1.volatility=[]) and
  53. (r2.volatility=[]);
  54. end;
  55. function MatchOperand(const oper1: TOper; const oper2: TOper): boolean;
  56. begin
  57. result := oper1.typ = oper2.typ;
  58. if result then
  59. case oper1.typ of
  60. top_const:
  61. Result:=oper1.val = oper2.val;
  62. top_reg:
  63. Result:=oper1.reg = oper2.reg;
  64. top_ref:
  65. Result:=RefsEqual(oper1.ref^, oper2.ref^);
  66. else
  67. internalerror(2016112401);
  68. end
  69. end;
  70. function MatchInstruction(const instr: tai; const op: TAsmOp; const opsize: topsizes): boolean;
  71. begin
  72. result :=
  73. (instr.typ = ait_instruction) and
  74. (taicpu(instr).opcode = op) and
  75. ((opsize = []) or (taicpu(instr).opsize in opsize));
  76. end;
  77. function TCpuAsmOptimizer.MaybeRealConstOperSimplify(var p: tai): boolean;
  78. var
  79. tmpint64: int64;
  80. tmpsingle: single;
  81. begin
  82. result:=false;
  83. if (taicpu(p).oper[0]^.typ = top_realconst) then
  84. begin
  85. { if we work with actual integers, turn the operand into one }
  86. if frac(taicpu(p).oper[0]^.val_real) = 0 then
  87. begin
  88. tmpint64:=trunc(taicpu(p).oper[0]^.val_real);
  89. if (high(shortint) >= tmpint64) and (low(shortint) <= tmpint64) then
  90. begin
  91. taicpu(p).opsize := S_B;
  92. taicpu(p).oper[0]^.typ:=top_const;
  93. end
  94. else
  95. if (high(smallint) >= tmpint64) and (low(smallint) <= tmpint64) then
  96. begin
  97. taicpu(p).opsize := S_W;
  98. taicpu(p).oper[0]^.typ:=top_const;
  99. end
  100. else
  101. if (high(longint) >= tmpint64) and (low(longint) <= tmpint64) then
  102. begin
  103. taicpu(p).opsize := S_L;
  104. taicpu(p).oper[0]^.typ:=top_const;
  105. end;
  106. if (taicpu(p).oper[0]^.typ) = top_const then
  107. begin
  108. DebugMsg('Optimizer: FPU real const to integer',p);
  109. taicpu(p).oper[0]^.val:=tmpint64;
  110. result:=true;
  111. end;
  112. end
  113. else
  114. begin
  115. tmpsingle:=taicpu(p).oper[0]^.val_real;
  116. if (taicpu(p).opsize = S_FD) and
  117. ((taicpu(p).oper[0]^.val_real - tmpsingle) = 0.0) then
  118. begin
  119. DebugMsg('Optimizer: FPU real const to lesser precision',p);
  120. taicpu(p).opsize:=S_FS;
  121. result:=true;
  122. end;
  123. end;
  124. end;
  125. end;
  126. function TCpuAsmOptimizer.RegLoadedWithNewValue(reg: tregister; hp: tai): boolean;
  127. var
  128. p: taicpu;
  129. begin
  130. if not assigned(hp) or
  131. (hp.typ <> ait_instruction) then
  132. begin
  133. Result := false;
  134. exit;
  135. end;
  136. p := taicpu(hp);
  137. Result :=
  138. (((p.opcode=A_MOVE) or (p.opcode=A_MOVEA) or (p.opcode=A_MVS) or
  139. (p.opcode=A_MVZ) or (p.opcode=A_MOVEQ) or (p.opcode=A_LEA)) and
  140. (p.oper[1]^.typ = top_reg) and
  141. (SuperRegistersEqual(p.oper[1]^.reg,reg)) and
  142. ((p.oper[0]^.typ = top_const) or
  143. ((p.oper[0]^.typ = top_reg) and
  144. not(SuperRegistersEqual(p.oper[0]^.reg,reg))) or
  145. ((p.oper[0]^.typ = top_ref) and
  146. not RegInRef(reg,p.oper[0]^.ref^)))) or
  147. ((p.opcode = A_FMOVE) and
  148. (p.oper[1]^.typ = top_reg) and
  149. (SuperRegistersEqual(p.oper[1]^.reg,reg)) and
  150. ((p.oper[0]^.typ = top_realconst) or
  151. ((p.oper[0]^.typ = top_reg) and
  152. not(SuperRegistersEqual(p.oper[0]^.reg,reg))))) or
  153. ((p.opcode = A_MOVEM) and
  154. (p.oper[1]^.typ = top_regset) and
  155. ((getsupreg(reg) in p.oper[1]^.dataregset) or
  156. (getsupreg(reg) in p.oper[1]^.addrregset))) or
  157. ((p.opcode = A_FMOVEM) and
  158. (p.oper[1]^.typ = top_regset) and
  159. (getsupreg(reg) in p.oper[1]^.fpuregset));
  160. end;
  161. {$ifdef DEBUG_AOPTCPU}
  162. procedure TCpuAsmOptimizer.DebugMsg(const s: string; p : tai);
  163. begin
  164. asml.insertbefore(tai_comment.Create(strpnew(s)), p);
  165. end;
  166. {$else DEBUG_AOPTCPU}
  167. procedure TCpuAsmOptimizer.DebugMsg(const s: string; p : tai);inline;
  168. begin
  169. end;
  170. {$endif DEBUG_AOPTCPU}
  171. function TCpuAsmOptimizer.TryToOptimizeMove(var p: tai): boolean;
  172. var
  173. next, next2: tai;
  174. opstr: string[15];
  175. begin
  176. result:=false;
  177. if GetNextInstruction(p,next) and
  178. (next.typ = ait_instruction) and
  179. (taicpu(next).opcode = taicpu(p).opcode) and
  180. (taicpu(p).opsize = taicpu(next).opsize) and
  181. (taicpu(p).oper[1]^.typ = top_reg) and
  182. MatchOperand(taicpu(p).oper[1]^,taicpu(next).oper[0]^) then
  183. begin
  184. if not(RegInOp(taicpu(p).oper[1]^.reg,taicpu(next).oper[1]^)) and
  185. RegEndOfLife(taicpu(next).oper[0]^.reg, taicpu(next)) then
  186. begin
  187. opstr:=opname(p);
  188. case taicpu(p).oper[0]^.typ of
  189. top_reg:
  190. begin
  191. { move %reg0, %tmpreg; move %tmpreg, <ea> -> move %reg0, <ea> }
  192. taicpu(p).loadOper(1,taicpu(next).oper[1]^);
  193. asml.remove(next);
  194. next.free;
  195. result:=true;
  196. { also remove leftover move %reg0, %reg0, which can occur as the result
  197. of the previous optimization, if %reg0 and %tmpreg was different types
  198. (addr vs. data), so these moves were left in by the cg }
  199. if MatchOperand(taicpu(p).oper[0]^,taicpu(p).oper[1]^) then
  200. begin
  201. DebugMsg('Optimizer: '+opstr+' + '+opstr+' removed',p);
  202. GetNextInstruction(p,next);
  203. asml.remove(p);
  204. p.free;
  205. p:=next;
  206. end
  207. else
  208. DebugMsg('Optimizer: '+opstr+' + '+opstr+' to '+opstr+' #1',p)
  209. end;
  210. top_const:
  211. begin
  212. // DebugMsg('Optimizer: '+opstr+' + '+opstr+' to '+opstr+' #2',p);
  213. end;
  214. top_ref:
  215. begin
  216. { move ref, %tmpreg; move %tmpreg, <ea> -> move ref, <ea> }
  217. { we only want to do this when <ea> is a reg or a simple reference }
  218. with taicpu(next).oper[1]^ do
  219. if (taicpu(next).opcode <> A_FMOVE) and
  220. ((typ = top_reg) or
  221. ((typ = top_ref) and
  222. ((ref^.index = NR_NO) or
  223. (ref^.base = NR_NO)) and
  224. (ref^.symbol = nil) and
  225. (ref^.offset = 0))) then
  226. begin
  227. DebugMsg('Optimizer: '+opstr+' + '+opstr+' to '+opstr+' #3',p);
  228. taicpu(p).loadOper(1,taicpu(next).oper[1]^);
  229. asml.remove(next);
  230. next.free;
  231. result:=true;
  232. end;
  233. end;
  234. else
  235. ;
  236. end;
  237. end;
  238. exit;
  239. end;
  240. if GetNextInstruction(p,next) and
  241. (next.typ = ait_instruction) and
  242. GetNextInstruction(next,next2) and
  243. (next2.typ = ait_instruction) and
  244. (taicpu(next).opcode <> taicpu(p).opcode) and
  245. (taicpu(next2).opcode = taicpu(p).opcode) and
  246. (taicpu(p).oper[0]^.typ = top_reg) and
  247. (taicpu(p).oper[1]^.typ = top_reg) and
  248. (getregtype(taicpu(p).oper[0]^.reg) = getregtype(taicpu(p).oper[1]^.reg)) and
  249. MatchOperand(taicpu(p).oper[1]^,taicpu(next2).oper[0]^) and
  250. MatchOperand(taicpu(next2).oper[1]^,taicpu(p).oper[0]^) and
  251. (taicpu(p).opsize = taicpu(next2).opsize) and
  252. ((taicpu(p).opcode = A_FMOVE) or
  253. (taicpu(p).opsize = taicpu(next).opsize)) then
  254. begin
  255. opstr:=opname(p);
  256. if not(RegInOp(taicpu(p).oper[1]^.reg,taicpu(next2).oper[1]^)) and
  257. not(RegInOp(taicpu(p).oper[1]^.reg,taicpu(next).oper[0]^)) and
  258. RegEndOfLife(taicpu(p).oper[0]^.reg, taicpu(next2)) then
  259. begin
  260. { move %reg0, %tmpreg
  261. op ???, %tmpreg
  262. move %tmpreg, %reg0
  263. to:
  264. op ???, %reg0 }
  265. if MatchOperand(taicpu(p).oper[1]^,taicpu(next).oper[taicpu(next).ops-1]^) then
  266. begin
  267. {
  268. Disabled, because it breaks some tests... :( (KB)
  269. DebugMsg('Optimizer: '+opstr+' + OP + '+opstr+' to OP #1',next);
  270. taicpu(next).loadOper(taicpu(next).ops-1,taicpu(p).oper[0]^);
  271. asml.remove(p);
  272. asml.remove(next2);
  273. p.free;
  274. next2.free;
  275. result:=true;
  276. }
  277. end;
  278. end;
  279. end;
  280. end;
  281. function TCpuAsmOptimizer.PeepHoleOptPass1Cpu(var p: tai): boolean;
  282. var
  283. next: tai;
  284. tmpref: treference;
  285. begin
  286. result:=false;
  287. case p.typ of
  288. ait_instruction:
  289. begin
  290. //asml.insertbefore(tai_comment.Create(strpnew('pass1 called for instr')), p);
  291. case taicpu(p).opcode of
  292. A_MOVE:
  293. result:=TryToOptimizeMove(p);
  294. { LEA (Ax),Ax is a NOP if src and dest reg is equal, so remove it. }
  295. A_LEA:
  296. if not assigned(taicpu(p).oper[0]^.ref^.symbol) and
  297. (((taicpu(p).oper[0]^.ref^.base = taicpu(p).oper[1]^.reg) and
  298. (taicpu(p).oper[0]^.ref^.index = NR_NO)) or
  299. ((taicpu(p).oper[0]^.ref^.index = taicpu(p).oper[1]^.reg) and
  300. (taicpu(p).oper[0]^.ref^.base = NR_NO))) and
  301. (taicpu(p).oper[0]^.ref^.offset = 0) then
  302. begin
  303. DebugMsg('Optimizer: LEA 0(Ax),Ax removed',p);
  304. GetNextInstruction(p,next);
  305. asml.remove(p);
  306. p.free;
  307. p:=next;
  308. result:=true;
  309. end;
  310. { Address register sub/add can be replaced with ADDQ/SUBQ or LEA if the value is in the
  311. SmallInt range, which is shorter to encode and faster to execute on most 68k }
  312. A_SUB,A_SUBA,A_ADD,A_ADDA:
  313. if (taicpu(p).oper[1]^.typ = top_reg) and isaddressregister(taicpu(p).oper[1]^.reg) and
  314. (taicpu(p).oper[0]^.typ = top_const) then
  315. begin
  316. if isvalueforaddqsubq(taicpu(p).oper[0]^.val) then
  317. begin
  318. DebugMsg('Optimizer: SUB/ADD #val,Ax to SUBQ/ADDQ',p);
  319. taicpu(p).opsize:=S_L; // this is safe, because we're targetting an address reg
  320. if taicpu(p).opcode in [A_ADD,A_ADDA] then
  321. taicpu(p).opcode:=A_ADDQ
  322. else
  323. taicpu(p).opcode:=A_SUBQ;
  324. result:=true;
  325. end
  326. else
  327. if isvalue16bit(abs(taicpu(p).oper[0]^.val)) then
  328. begin
  329. DebugMsg('Optimizer: SUB/ADD #val,Ax to LEA val(Ax),Ax',p);
  330. if (taicpu(p).opcode=A_SUB) or (taicpu(p).opcode=A_SUBA) then
  331. reference_reset_base(tmpref,taicpu(p).oper[1]^.reg,-taicpu(p).oper[0]^.val,ctempposinvalid,0,[])
  332. else
  333. reference_reset_base(tmpref,taicpu(p).oper[1]^.reg,taicpu(p).oper[0]^.val,ctempposinvalid,0,[]);
  334. taicpu(p).opcode:=A_LEA;
  335. taicpu(p).loadref(0,tmpref);
  336. result:=true;
  337. end;
  338. end;
  339. { MOVEA #0,Ax to SUBA Ax,Ax, because it's shorter }
  340. A_MOVEA:
  341. if (taicpu(p).oper[0]^.typ = top_const) and
  342. (taicpu(p).oper[0]^.val = 0) then
  343. begin
  344. DebugMsg('Optimizer: MOVEA #0,Ax to SUBA Ax,Ax',p);
  345. taicpu(p).opcode:=A_SUBA;
  346. taicpu(p).opsize:=S_L; { otherwise it will be .W -> BOOM }
  347. taicpu(p).loadoper(0,taicpu(p).oper[1]^);
  348. result:=true;
  349. end;
  350. { CLR.L Dx on a 68000 is slower than MOVEQ #0,Dx }
  351. A_CLR:
  352. if (current_settings.cputype in [cpu_mc68000]) and
  353. (taicpu(p).oper[0]^.typ = top_reg) and
  354. (taicpu(p).opsize = S_L) and
  355. isintregister(taicpu(p).oper[0]^.reg) then
  356. begin
  357. //DebugMsg('Optimizer: CLR.L Dx to MOVEQ #0,Dx',p);
  358. taicpu(p).opcode:=A_MOVEQ;
  359. taicpu(p).loadoper(1,taicpu(p).oper[0]^);
  360. taicpu(p).loadconst(0,0);
  361. taicpu(p).ops:=2;
  362. result:=true;
  363. end;
  364. A_JSR:
  365. begin
  366. if (cs_opt_level4 in current_settings.optimizerswitches) and
  367. GetNextInstruction(p,next) and
  368. MatchInstruction(next,A_RTS,[S_NO]) then
  369. begin
  370. DebugMsg('Optimizer: JSR, RTS to JMP',p);
  371. taicpu(p).opcode:=A_JMP;
  372. asml.remove(next);
  373. next.free;
  374. result:=true;
  375. end;
  376. end;
  377. { CMP #0,<ea> equals to TST <ea>, just shorter and TST is more flexible anyway }
  378. A_CMP,A_CMPI:
  379. if (taicpu(p).oper[0]^.typ = top_const) and
  380. (taicpu(p).oper[0]^.val = 0) then
  381. begin
  382. DebugMsg('Optimizer: CMP #0 to TST',p);
  383. taicpu(p).opcode:=A_TST;
  384. taicpu(p).loadoper(0,taicpu(p).oper[1]^);
  385. taicpu(p).clearop(1);
  386. taicpu(p).ops:=1;
  387. result:=true;
  388. end;
  389. A_FCMP:
  390. if (taicpu(p).oper[0]^.typ = top_realconst) then
  391. begin
  392. if (taicpu(p).oper[0]^.val_real = 0.0) then
  393. begin
  394. DebugMsg('Optimizer: FCMP #0.0 to FTST',p);
  395. taicpu(p).opcode:=A_FTST;
  396. taicpu(p).opsize:=S_FX;
  397. taicpu(p).loadoper(0,taicpu(p).oper[1]^);
  398. taicpu(p).clearop(1);
  399. taicpu(p).ops:=1;
  400. result:=true;
  401. end
  402. else
  403. result:=result or MaybeRealConstOperSimplify(p);
  404. end;
  405. A_FMOVE,A_FSMOVE,A_FDMOVE,
  406. A_FADD,A_FSADD,A_FDADD,A_FSUB,A_FSSUB,A_FDSUB,
  407. A_FMUL,A_FSMUL,A_FDMUL,A_FDIV,A_FSDIV,A_FDDIV,
  408. A_FSGLMUL,A_FSGLDIV:
  409. begin
  410. if (taicpu(p).opcode = A_FMOVE) and TryToOptimizeMove(p) then
  411. begin
  412. result:=true;
  413. exit;
  414. end;
  415. result:=result or MaybeRealConstOperSimplify(p);
  416. end;
  417. else
  418. ;
  419. end;
  420. end;
  421. else
  422. ;
  423. end;
  424. end;
  425. begin
  426. casmoptimizer:=TCpuAsmOptimizer;
  427. end.