{ Copyright (c) 1998-2014 by the Free Pascal development team This unit calls the optimization procedures to optimize the assembler code for m68k This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. **************************************************************************** } unit aoptcpu; {$i fpcdefs.inc} {$define DEBUG_AOPTCPU} Interface uses cpubase, aoptobj, aoptcpub, aopt, aasmtai, cgbase; Type TCpuAsmOptimizer = class(TAsmOptimizer) function RegLoadedWithNewValue(reg: tregister; hp: tai): boolean; override; function PeepHoleOptPass1Cpu(var p: tai): boolean; override; function TryToOptimizeMove(var p: tai): boolean; { outputs a debug message into the assembler file } procedure DebugMsg(const s: string; p: tai); End; Implementation uses cutils, aasmcpu, cgutils, globals, verbose, cpuinfo, itcpugas; function opname(var p: tai): string; begin result:=upcase(gas_op2str[taicpu(p).opcode]); end; function RefsEqual(const r1, r2: treference): boolean; begin RefsEqual := (r1.offset = r2.offset) and (r1.base = r2.base) and (r1.index = r2.index) and (r1.scalefactor = r2.scalefactor) and (r1.symbol=r2.symbol) and (r1.refaddr = r2.refaddr) and (r1.relsymbol = r2.relsymbol); end; function MatchOperand(const oper1: TOper; const oper2: TOper): boolean; begin result := oper1.typ = oper2.typ; if result then case oper1.typ of top_const: Result:=oper1.val = oper2.val; top_reg: Result:=oper1.reg = oper2.reg; top_ref: Result:=RefsEqual(oper1.ref^, oper2.ref^); else internalerror(2016112401); end end; function TCpuAsmOptimizer.RegLoadedWithNewValue(reg: tregister; hp: tai): boolean; var p: taicpu; begin if not assigned(hp) or (hp.typ <> ait_instruction) then begin Result := false; exit; end; p := taicpu(hp); Result := ((p.opcode in [A_MOVE,A_MOVEA,A_MVS,A_MVZ,A_MOVEQ,A_LEA]) and (p.oper[1]^.typ = top_reg) and (SuperRegistersEqual(p.oper[1]^.reg,reg)) and ((p.oper[0]^.typ = top_const) or ((p.oper[0]^.typ = top_reg) and not(SuperRegistersEqual(p.oper[0]^.reg,reg))) or ((p.oper[0]^.typ = top_ref) and not RegInRef(reg,p.oper[0]^.ref^)))) or ((p.opcode = A_FMOVE) and (p.oper[1]^.typ = top_reg) and (SuperRegistersEqual(p.oper[1]^.reg,reg)) and ((p.oper[0]^.typ = top_realconst) or ((p.oper[0]^.typ = top_reg) and not(SuperRegistersEqual(p.oper[0]^.reg,reg))))) or ((p.opcode = A_MOVEM) and (p.oper[1]^.typ = top_regset) and ((getsupreg(reg) in p.oper[1]^.dataregset) or (getsupreg(reg) in p.oper[1]^.addrregset))) or ((p.opcode = A_FMOVEM) and (p.oper[1]^.typ = top_regset) and (getsupreg(reg) in p.oper[1]^.fpuregset)); end; {$ifdef DEBUG_AOPTCPU} procedure TCpuAsmOptimizer.DebugMsg(const s: string; p : tai); begin asml.insertbefore(tai_comment.Create(strpnew(s)), p); end; {$else DEBUG_AOPTCPU} procedure TCpuAsmOptimizer.DebugMsg(const s: string; p : tai);inline; begin end; {$endif DEBUG_AOPTCPU} function TCpuAsmOptimizer.TryToOptimizeMove(var p: tai): boolean; var next, next2: tai; opstr: string[15]; begin result:=false; if GetNextInstruction(p,next) and (taicpu(next).typ = ait_instruction) and (taicpu(next).opcode = taicpu(p).opcode) and (taicpu(p).opsize = taicpu(next).opsize) and (taicpu(p).oper[1]^.typ = top_reg) and MatchOperand(taicpu(p).oper[1]^,taicpu(next).oper[0]^) then begin if not(RegInOp(taicpu(p).oper[1]^.reg,taicpu(next).oper[1]^)) and RegEndOfLife(taicpu(next).oper[0]^.reg, taicpu(next)) then begin opstr:=opname(p); case taicpu(p).oper[0]^.typ of top_reg: begin { move %reg0, %tmpreg; move %tmpreg, -> move %reg0, } taicpu(p).loadOper(1,taicpu(next).oper[1]^); asml.remove(next); next.free; result:=true; { also remove leftover move %reg0, %reg0, which can occur as the result of the previous optimization, if %reg0 and %tmpreg was different types (addr vs. data), so these moves were left in by the cg } if MatchOperand(taicpu(p).oper[0]^,taicpu(p).oper[1]^) then begin DebugMsg('Optimizer: '+opstr+' + '+opstr+' removed',p); asml.remove(p); p.free; end else DebugMsg('Optimizer: '+opstr+' + '+opstr+' to '+opstr+' #1',p) end; top_const: begin // DebugMsg('Optimizer: '+opstr+' + '+opstr+' to '+opstr+' #2',p); end; top_ref: begin { move ref, %tmpreg; move %tmpreg, -> move ref, } { we only want to do this when is a reg or a simple reference } with taicpu(next).oper[1]^ do if (taicpu(next).opcode <> A_FMOVE) and ((typ = top_reg) or ((typ = top_ref) and ((ref^.index = NR_NO) or (ref^.base = NR_NO)) and (ref^.symbol = nil) and (ref^.offset = 0))) then begin DebugMsg('Optimizer: '+opstr+' + '+opstr+' to '+opstr+' #3',p); taicpu(p).loadOper(1,taicpu(next).oper[1]^); asml.remove(next); next.free; result:=true; end; end; end; end; exit; end; if GetNextInstruction(p,next) and (taicpu(next).typ = ait_instruction) and GetNextInstruction(next,next2) and (taicpu(next2).typ = ait_instruction) and (taicpu(next).opcode <> taicpu(p).opcode) and (taicpu(next2).opcode = taicpu(p).opcode) and (taicpu(p).oper[0]^.typ = top_reg) and (taicpu(p).oper[1]^.typ = top_reg) and (getregtype(taicpu(p).oper[0]^.reg) = getregtype(taicpu(p).oper[1]^.reg)) and MatchOperand(taicpu(p).oper[1]^,taicpu(next2).oper[0]^) and MatchOperand(taicpu(next2).oper[1]^,taicpu(p).oper[0]^) and (taicpu(p).opsize = taicpu(next2).opsize) and ((taicpu(p).opcode = A_FMOVE) or (taicpu(p).opsize = taicpu(next).opsize)) then begin opstr:=opname(p); if not(RegInOp(taicpu(p).oper[1]^.reg,taicpu(next2).oper[1]^)) and not(RegInOp(taicpu(p).oper[1]^.reg,taicpu(next).oper[0]^)) and RegEndOfLife(taicpu(p).oper[0]^.reg, taicpu(next2)) then begin { move %reg0, %tmpreg op ???, %tmpreg move %tmpreg, %reg0 to: op ???, %reg0 } if MatchOperand(taicpu(p).oper[1]^,taicpu(next).oper[taicpu(next).ops-1]^) then begin DebugMsg('Optimizer: '+opstr+' + OP + '+opstr+' to OP #1',next); taicpu(next).loadOper(taicpu(next).ops-1,taicpu(p).oper[0]^); asml.remove(p); asml.remove(next2); p.free; next2.free; result:=true; end; end; end; end; function TCpuAsmOptimizer.PeepHoleOptPass1Cpu(var p: tai): boolean; var next: tai; tmpref: treference; tmpsingle: single; begin result:=false; case p.typ of ait_instruction: begin //asml.insertbefore(tai_comment.Create(strpnew('pass1 called for instr')), p); case taicpu(p).opcode of A_MOVE: result:=TryToOptimizeMove(p); { LEA (Ax),Ax is a NOP if src and dest reg is equal, so remove it. } A_LEA: if not assigned(taicpu(p).oper[0]^.ref^.symbol) and (((taicpu(p).oper[0]^.ref^.base = taicpu(p).oper[1]^.reg) and (taicpu(p).oper[0]^.ref^.index = NR_NO)) or ((taicpu(p).oper[0]^.ref^.index = taicpu(p).oper[1]^.reg) and (taicpu(p).oper[0]^.ref^.base = NR_NO))) and (taicpu(p).oper[0]^.ref^.offset = 0) then begin DebugMsg('Optimizer: LEA 0(Ax),Ax removed',p); asml.remove(p); p.free; result:=true; end; { Address register sub/add can be replaced with ADDQ/SUBQ or LEA if the value is in the SmallInt range, which is shorter to encode and faster to execute on most 68k } A_SUB,A_SUBA,A_ADD,A_ADDA: if (taicpu(p).oper[1]^.typ = top_reg) and isaddressregister(taicpu(p).oper[1]^.reg) and (taicpu(p).oper[0]^.typ = top_const) then begin if isvalueforaddqsubq(taicpu(p).oper[0]^.val) then begin DebugMsg('Optimizer: SUB/ADD #val,Ax to SUBQ/ADDQ',p); taicpu(p).opsize:=S_L; // this is safe, because we're targetting an address reg if taicpu(p).opcode in [A_ADD,A_ADDA] then taicpu(p).opcode:=A_ADDQ else taicpu(p).opcode:=A_SUBQ; result:=true; end else if isvalue16bit(abs(taicpu(p).oper[0]^.val)) then begin DebugMsg('Optimizer: SUB/ADD #val,Ax to LEA val(Ax),Ax',p); if taicpu(p).opcode in [A_SUB,A_SUBA] then reference_reset_base(tmpref,taicpu(p).oper[1]^.reg,-taicpu(p).oper[0]^.val,0,[]) else reference_reset_base(tmpref,taicpu(p).oper[1]^.reg,taicpu(p).oper[0]^.val,0,[]); taicpu(p).opcode:=A_LEA; taicpu(p).loadref(0,tmpref); result:=true; end; end; { MOVEA #0,Ax to SUBA Ax,Ax, because it's shorter } A_MOVEA: if (taicpu(p).oper[0]^.typ = top_const) and (taicpu(p).oper[0]^.val = 0) then begin DebugMsg('Optimizer: MOVEA #0,Ax to SUBA Ax,Ax',p); taicpu(p).opcode:=A_SUBA; taicpu(p).opsize:=S_L; { otherwise it will be .W -> BOOM } taicpu(p).loadoper(0,taicpu(p).oper[1]^); result:=true; end; { CLR.L Dx on a 68000 is slower than MOVEQ #0,Dx } A_CLR: if (current_settings.cputype in [cpu_mc68000]) and (taicpu(p).oper[0]^.typ = top_reg) and (taicpu(p).opsize = S_L) and isintregister(taicpu(p).oper[0]^.reg) then begin //DebugMsg('Optimizer: CLR.L Dx to MOVEQ #0,Dx',p); taicpu(p).opcode:=A_MOVEQ; taicpu(p).loadoper(1,taicpu(p).oper[0]^); taicpu(p).loadconst(0,0); taicpu(p).ops:=2; result:=true; end; { CMP #0, equals to TST , just shorter and TST is more flexible anyway } A_CMP,A_CMPI: if (taicpu(p).oper[0]^.typ = top_const) and (taicpu(p).oper[0]^.val = 0) then begin DebugMsg('Optimizer: CMP #0 to TST',p); taicpu(p).opcode:=A_TST; taicpu(p).loadoper(0,taicpu(p).oper[1]^); taicpu(p).clearop(1); taicpu(p).ops:=1; result:=true; end; A_FCMP: if (taicpu(p).oper[0]^.typ = top_realconst) then begin if (taicpu(p).oper[0]^.val_real = 0.0) then begin DebugMsg('Optimizer: FCMP #0.0 to FTST',p); taicpu(p).opcode:=A_FTST; taicpu(p).opsize:=S_FX; taicpu(p).loadoper(0,taicpu(p).oper[1]^); taicpu(p).clearop(1); taicpu(p).ops:=1; result:=true; end else begin tmpsingle:=taicpu(p).oper[0]^.val_real; if (taicpu(p).opsize = S_FD) and ((taicpu(p).oper[0]^.val_real - tmpsingle) = 0.0) then begin DebugMsg('Optimizer: FCMP const to lesser precision',p); taicpu(p).opsize:=S_FS; result:=true; end; end; end; A_FMOVE,A_FMUL,A_FADD,A_FSUB,A_FDIV: begin if (taicpu(p).opcode = A_FMOVE) and TryToOptimizeMove(p) then begin result:=true; exit; end; if (taicpu(p).oper[0]^.typ = top_realconst) then begin tmpsingle:=taicpu(p).oper[0]^.val_real; if (taicpu(p).opsize = S_FD) and ((taicpu(p).oper[0]^.val_real - tmpsingle) = 0.0) then begin DebugMsg('Optimizer: FMOVE/FMUL/FADD/FSUB/FDIV const to lesser precision',p); taicpu(p).opsize:=S_FS; result:=true; end; end; end; end; end; end; end; begin casmoptimizer:=TCpuAsmOptimizer; end.