mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-05 10:58:06 +02:00
302 lines
8.5 KiB
ObjectPascal
302 lines
8.5 KiB
ObjectPascal
{
|
|
Copyright (c) 1998-2002 by Jonas Maebe, member of the Free Pascal
|
|
Development Team
|
|
|
|
This unit implements the ARM64 optimizer object
|
|
|
|
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}
|
|
|
|
{$ifdef EXTDEBUG}
|
|
{$define DEBUG_AOPTCPU}
|
|
{$endif EXTDEBUG}
|
|
|
|
Interface
|
|
|
|
uses
|
|
globtype, globals,
|
|
cutils,
|
|
cgbase, cpubase, aasmtai, aasmcpu, aopt, aoptcpub;
|
|
|
|
Type
|
|
TCpuAsmOptimizer = class(TAsmOptimizer)
|
|
function CanDoJumpOpts: Boolean; override;
|
|
|
|
{ uses the same constructor as TAopObj }
|
|
function RegLoadedWithNewValue(reg: tregister; hp: tai): boolean;override;
|
|
function InstructionLoadsFromReg(const reg: TRegister; const hp: tai): boolean;override;
|
|
function GetNextInstructionUsingReg(Current : tai; out Next : tai; reg : TRegister) : Boolean;
|
|
procedure DebugMsg(const s : string; p : tai);
|
|
|
|
function PeepHoleOptPass1Cpu(var p: tai): boolean; override;
|
|
private
|
|
function RemoveSuperfluousMove(const p: tai; movp: tai; const optimizer: string): boolean;
|
|
End;
|
|
|
|
Implementation
|
|
|
|
uses
|
|
aasmbase,
|
|
aoptutils,
|
|
cgutils,
|
|
verbose;
|
|
|
|
{$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 CanBeCond(p : tai) : boolean;
|
|
begin
|
|
result:=(p.typ=ait_instruction) and (taicpu(p).condition=C_None);
|
|
end;
|
|
|
|
|
|
function TCpuAsmOptimizer.CanDoJumpOpts: Boolean;
|
|
begin
|
|
Result := true;
|
|
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) and
|
|
(r1.volatility=[]) and
|
|
(r2.volatility=[]);
|
|
end;
|
|
|
|
|
|
function MatchInstruction(const instr: tai; const op: TAsmOp; const postfix: TOpPostfixes): boolean;
|
|
begin
|
|
result :=
|
|
(instr.typ = ait_instruction) and
|
|
(taicpu(instr).opcode = op) and
|
|
((postfix = []) or (taicpu(instr).oppostfix in postfix));
|
|
end;
|
|
|
|
|
|
function MatchOperand(const oper: TOper; const reg: TRegister): boolean; inline;
|
|
begin
|
|
result := (oper.typ = top_reg) and (oper.reg = reg);
|
|
end;
|
|
|
|
|
|
function MatchOperand(const oper1: TOper; const oper2: TOper): boolean; inline;
|
|
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 Result:=false;
|
|
end
|
|
end;
|
|
|
|
|
|
function TCpuAsmOptimizer.GetNextInstructionUsingReg(Current: tai;
|
|
Out Next: tai; reg: TRegister): Boolean;
|
|
begin
|
|
Next:=Current;
|
|
repeat
|
|
Result:=GetNextInstruction(Next,Next);
|
|
until not (Result) or
|
|
not(cs_opt_level3 in current_settings.optimizerswitches) or
|
|
(Next.typ<>ait_instruction) or
|
|
RegInInstruction(reg,Next) or
|
|
is_calljmp(taicpu(Next).opcode);
|
|
end;
|
|
|
|
|
|
function TCpuAsmOptimizer.RegLoadedWithNewValue(reg: tregister; hp: tai): boolean;
|
|
var
|
|
p: taicpu;
|
|
begin
|
|
Result := false;
|
|
if not(assigned(hp) and (hp.typ = ait_instruction)) then
|
|
exit;
|
|
|
|
p := taicpu(hp);
|
|
if not (p.ops >0) then
|
|
exit;
|
|
case p.opcode of
|
|
A_B,
|
|
A_SSI,A_SSIU,A_SSX,A_SSXU,
|
|
A_S16I,A_S32C1I,A_S32E,A_S32I,A_S32RI,A_S8I:
|
|
exit;
|
|
else
|
|
;
|
|
end;
|
|
case p.oper[0]^.typ of
|
|
top_reg:
|
|
Result := (p.oper[0]^.reg = reg) ;
|
|
else
|
|
;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCpuAsmOptimizer.InstructionLoadsFromReg(const reg: TRegister; const hp: tai): boolean;
|
|
var
|
|
p: taicpu;
|
|
i: longint;
|
|
begin
|
|
instructionLoadsFromReg := false;
|
|
if not (assigned(hp) and (hp.typ = ait_instruction)) then
|
|
exit;
|
|
p:=taicpu(hp);
|
|
|
|
i:=1;
|
|
|
|
{ Start on oper[0]? }
|
|
if taicpu(hp).spilling_get_operation_type(0) in [operand_read, operand_readwrite] then
|
|
i:=0;
|
|
|
|
while(i<p.ops) do
|
|
begin
|
|
case p.oper[I]^.typ of
|
|
top_reg:
|
|
Result := (p.oper[I]^.reg = reg);
|
|
top_ref:
|
|
Result :=
|
|
(p.oper[I]^.ref^.base = reg) or
|
|
(p.oper[I]^.ref^.index = reg);
|
|
else
|
|
;
|
|
end;
|
|
{ Bailout if we found something }
|
|
if Result then
|
|
exit;
|
|
Inc(I);
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCpuAsmOptimizer.RemoveSuperfluousMove(const p: tai; movp: tai; const optimizer: string):boolean;
|
|
var
|
|
alloc,
|
|
dealloc : tai_regalloc;
|
|
hp1 : tai;
|
|
begin
|
|
Result:=false;
|
|
if MatchInstruction(movp, A_MOV, [PF_None,PF_N]) and
|
|
{ We can't optimize if there is a shiftop }
|
|
(taicpu(movp).ops=2) and
|
|
MatchOperand(taicpu(movp).oper[1]^, taicpu(p).oper[0]^.reg) and
|
|
{ the destination register of the mov might not be used beween p and movp }
|
|
not(RegUsedBetween(taicpu(movp).oper[0]^.reg,p,movp)) and
|
|
{ Take care to only do this for instructions which REALLY load to the first register.
|
|
Otherwise
|
|
s* reg0, [reg1]
|
|
mov reg2, reg0
|
|
will be optimized to
|
|
s* reg2, [reg1]
|
|
}
|
|
RegLoadedWithNewValue(taicpu(p).oper[0]^.reg, p) then
|
|
begin
|
|
dealloc:=FindRegDeAlloc(taicpu(p).oper[0]^.reg,tai(movp.Next));
|
|
if assigned(dealloc) then
|
|
begin
|
|
DebugMsg('Peephole '+optimizer+' removed superfluous mov', movp);
|
|
result:=true;
|
|
|
|
{ taicpu(p).oper[0]^.reg is not used anymore, try to find its allocation
|
|
and remove it if possible }
|
|
asml.Remove(dealloc);
|
|
alloc:=FindRegAllocBackward(taicpu(p).oper[0]^.reg,tai(p.previous));
|
|
if assigned(alloc) then
|
|
begin
|
|
asml.Remove(alloc);
|
|
alloc.free;
|
|
dealloc.free;
|
|
end
|
|
else
|
|
asml.InsertAfter(dealloc,p);
|
|
|
|
{ try to move the allocation of the target register }
|
|
GetLastInstruction(movp,hp1);
|
|
alloc:=FindRegAlloc(taicpu(movp).oper[0]^.reg,tai(hp1.Next));
|
|
if assigned(alloc) then
|
|
begin
|
|
asml.Remove(alloc);
|
|
asml.InsertBefore(alloc,p);
|
|
{ adjust used regs }
|
|
IncludeRegInUsedRegs(taicpu(movp).oper[0]^.reg,UsedRegs);
|
|
end;
|
|
|
|
{ finally get rid of the mov }
|
|
taicpu(p).loadreg(0,taicpu(movp).oper[0]^.reg);
|
|
asml.remove(movp);
|
|
movp.free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TCpuAsmOptimizer.PeepHoleOptPass1Cpu(var p: tai): boolean;
|
|
var
|
|
hp1: tai;
|
|
begin
|
|
result := false;
|
|
case p.typ of
|
|
ait_instruction:
|
|
begin
|
|
case taicpu(p).opcode of
|
|
A_ADD,
|
|
A_ADDI,
|
|
A_L32I,
|
|
A_SRLI,
|
|
A_SUB:
|
|
begin
|
|
if GetNextInstructionUsingReg(p, hp1, taicpu(p).oper[0]^.reg) and
|
|
RemoveSuperfluousMove(p, hp1, 'DataMov2Data') then
|
|
Result:=true;
|
|
end;
|
|
else
|
|
;
|
|
end;
|
|
end
|
|
else
|
|
;
|
|
end
|
|
end;
|
|
|
|
|
|
begin
|
|
casmoptimizer:=TCpuAsmOptimizer;
|
|
End.
|
|
|