mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-05 05:58:02 +02:00
3106 lines
115 KiB
ObjectPascal
3106 lines
115 KiB
ObjectPascal
{
|
|
|
|
Copyright (c) 2008 by Florian Klaempfl
|
|
Member of the Free Pascal development team
|
|
|
|
This unit implements the code generator for the AVR
|
|
|
|
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 cgcpu;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
globtype,symtype,symdef,
|
|
cgbase,cgutils,cgobj,
|
|
aasmbase,aasmcpu,aasmtai,aasmdata,
|
|
parabase,
|
|
cpubase,cpuinfo,node,cg64f32,rgcpu;
|
|
|
|
type
|
|
|
|
{ tcgavr }
|
|
|
|
tcgavr = class(tcg)
|
|
{ true, if the next arithmetic operation should modify the flags }
|
|
cgsetflags : boolean;
|
|
procedure init_register_allocators;override;
|
|
procedure done_register_allocators;override;
|
|
|
|
function getaddressregister(list:TAsmList):TRegister;override;
|
|
|
|
function GetHigh(const r : TRegister) : TRegister;inline;
|
|
function GetOffsetReg(const r: TRegister;ofs : shortint): TRegister;override;
|
|
function GetOffsetReg64(const r,rhi: TRegister;ofs : shortint): TRegister;override;
|
|
|
|
procedure a_load_const_cgpara(list : TAsmList;size : tcgsize;a : tcgint;const paraloc : TCGPara);override;
|
|
procedure a_load_ref_cgpara(list : TAsmList;size : tcgsize;const r : treference;const paraloc : TCGPara);override;
|
|
procedure a_loadaddr_ref_cgpara(list : TAsmList;const r : treference;const paraloc : TCGPara);override;
|
|
procedure a_load_reg_cgpara(list : TAsmList; size : tcgsize;r : tregister; const cgpara : tcgpara);override;
|
|
|
|
procedure a_call_name(list : TAsmList;const s : string; weak: boolean);override;
|
|
procedure a_call_reg(list : TAsmList;reg: tregister);override;
|
|
|
|
procedure a_op_const_reg(list : TAsmList; Op: TOpCG; size: TCGSize; a: tcgint; reg: TRegister); override;
|
|
procedure a_op_reg_reg(list: TAsmList; Op: TOpCG; size: TCGSize; src, dst : TRegister); override;
|
|
procedure a_op_const_reg_reg(list : TAsmList;op : TOpCg;size : tcgsize; a : tcgint;src,dst : tregister); override;
|
|
|
|
procedure a_op_const_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tcgsize; a: tcgint; src, dst: tregister; setflags: boolean; var ovloc: tlocation); override;
|
|
procedure a_op_reg_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tcgsize; src1, src2, dst: tregister; setflags: boolean; var ovloc: tlocation); override;
|
|
|
|
{ move instructions }
|
|
procedure a_load_const_reg(list : TAsmList; size: tcgsize; a : tcgint;reg : tregister);override;
|
|
procedure a_load_reg_ref(list : TAsmList; fromsize, tosize: tcgsize; reg : tregister;const ref : treference);override;
|
|
procedure a_load_ref_reg(list : TAsmList; fromsize, tosize : tcgsize;const Ref : treference;reg : tregister);override;
|
|
procedure a_load_reg_reg(list : TAsmList; fromsize, tosize : tcgsize;reg1,reg2 : tregister);override;
|
|
|
|
{ fpu move instructions }
|
|
procedure a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tcgsize; reg1, reg2: tregister); override;
|
|
procedure a_loadfpu_ref_reg(list: TAsmList; fromsize, tosize: tcgsize; const ref: treference; reg: tregister); override;
|
|
procedure a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tcgsize; reg: tregister; const ref: treference); override;
|
|
|
|
{ comparison operations }
|
|
procedure a_cmp_const_reg_label(list : TAsmList;size : tcgsize;cmp_op : topcmp;a : tcgint;reg : tregister;
|
|
l : tasmlabel);override;
|
|
procedure a_cmp_reg_reg_label(list : TAsmList;size : tcgsize;cmp_op : topcmp;reg1,reg2 : tregister;l : tasmlabel); override;
|
|
|
|
procedure a_jmp_name(list : TAsmList;const s : string); override;
|
|
procedure a_jmp_always(list : TAsmList;l: tasmlabel); override;
|
|
procedure a_jmp_flags(list : TAsmList;const f : TResFlags;l: tasmlabel); override;
|
|
|
|
procedure g_flags2reg(list: TAsmList; size: TCgSize; const f: TResFlags; reg: TRegister); override;
|
|
|
|
procedure g_proc_entry(list : TAsmList;localsize : longint;nostackframe:boolean);override;
|
|
procedure g_proc_exit(list : TAsmList;parasize : longint;nostackframe:boolean); override;
|
|
|
|
procedure a_loadaddr_ref_reg(list : TAsmList;const ref : treference;r : tregister);override;
|
|
|
|
procedure g_concatcopy(list : TAsmList;const source,dest : treference;len : tcgint);override;
|
|
|
|
procedure g_overflowcheck(list: TAsmList; const l: tlocation; def: tdef); override;
|
|
procedure g_overflowCheck_loc(List: TAsmList; const Loc: TLocation; def: TDef; ovloc: tlocation); override;
|
|
|
|
procedure g_save_registers(list : TAsmList);override;
|
|
procedure g_restore_registers(list : TAsmList);override;
|
|
|
|
procedure a_jmp_cond(list : TAsmList;cond : TOpCmp;l: tasmlabel);
|
|
procedure fixref(list : TAsmList;var ref : treference);
|
|
function normalize_ref(list : TAsmList;ref : treference;
|
|
tmpreg : tregister) : treference;
|
|
|
|
procedure emit_mov(list: TAsmList;reg2: tregister; reg1: tregister);
|
|
|
|
procedure a_adjust_sp(list: TAsmList; value: longint);
|
|
function GetLoad(const ref : treference) : tasmop;
|
|
function GetStore(const ref: treference): tasmop;
|
|
|
|
procedure gen_multiply(list: TAsmList; op: topcg; size: TCgSize; src2, src1, dst: tregister; check_overflow: boolean; var ovloc: tlocation);
|
|
|
|
private
|
|
procedure a_op_const_reg_reg_internal(list: TAsmList; op: TOpCg; size: tcgsize; a: tcgint; src, srchi, dst, dsthi: tregister);
|
|
protected
|
|
procedure a_op_reg_reg_internal(list: TAsmList; Op: TOpCG; size: TCGSize; src, srchi, dst, dsthi: TRegister);
|
|
procedure a_op_const_reg_internal(list : TAsmList; Op: TOpCG; size: TCGSize; a: tcgint; reg, reghi: TRegister);
|
|
procedure maybegetcpuregister(list : tasmlist; reg : tregister);
|
|
function addr_is_io_register(const addr: integer): boolean;
|
|
end;
|
|
|
|
tcg64favr = class(tcg64f32)
|
|
procedure a_op64_reg_reg(list : TAsmList;op:TOpCG;size : tcgsize;regsrc,regdst : tregister64);override;
|
|
procedure a_op64_const_reg(list : TAsmList;op:TOpCG;size : tcgsize;value : int64;reg : tregister64);override;
|
|
procedure a_op64_const_reg_reg(list: TAsmList; op: TOpCg; size: tcgsize; value: int64;src,dst: tregister64);override;
|
|
end;
|
|
|
|
procedure create_codegen;
|
|
|
|
const
|
|
TOpCG2AsmOp: Array[topcg] of TAsmOp = (A_NONE,A_MOV,A_ADD,A_AND,A_NONE,
|
|
A_NONE,A_MULS,A_MUL,A_NEG,A_COM,A_OR,
|
|
A_ASR,A_LSL,A_LSR,A_SUB,A_EOR,A_ROL,A_ROR);
|
|
implementation
|
|
|
|
uses
|
|
globals,verbose,systems,cutils,
|
|
fmodule,
|
|
symconst,symsym,symtable,
|
|
tgobj,rgobj,
|
|
procinfo,cpupi,
|
|
paramgr;
|
|
|
|
|
|
procedure tcgavr.init_register_allocators;
|
|
begin
|
|
inherited init_register_allocators;
|
|
if CPUAVR_16_REGS in cpu_capabilities[current_settings.cputype] then
|
|
rg[R_INTREGISTER]:=trgintcpu.create(R_INTREGISTER,R_SUBWHOLE,
|
|
[RS_R18,RS_R19,RS_R20,RS_R21,RS_R22,RS_R23,RS_R24,RS_R25],first_int_imreg,[])
|
|
else
|
|
rg[R_INTREGISTER]:=trgintcpu.create(R_INTREGISTER,R_SUBWHOLE,
|
|
[RS_R18,RS_R19,RS_R20,RS_R21,RS_R22,RS_R23,RS_R24,RS_R25,
|
|
RS_R2,RS_R3,RS_R4,RS_R5,RS_R6,RS_R7,RS_R8,RS_R9,
|
|
RS_R10,RS_R11,RS_R12,RS_R13,RS_R14,RS_R15,RS_R16,RS_R17],first_int_imreg,[]);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.done_register_allocators;
|
|
begin
|
|
rg[R_INTREGISTER].free;
|
|
// rg[R_ADDRESSREGISTER].free;
|
|
inherited done_register_allocators;
|
|
end;
|
|
|
|
|
|
function tcgavr.getaddressregister(list: TAsmList): TRegister;
|
|
begin
|
|
Result:=getintregister(list,OS_ADDR);
|
|
end;
|
|
|
|
|
|
function tcgavr.GetHigh(const r : TRegister) : TRegister;
|
|
begin
|
|
result:=GetNextReg(r);
|
|
end;
|
|
|
|
|
|
function tcgavr.GetOffsetReg(const r: TRegister;ofs : shortint): TRegister;
|
|
begin
|
|
result:=TRegister(longint(r)+ofs);
|
|
end;
|
|
|
|
|
|
function tcgavr.GetOffsetReg64(const r,rhi: TRegister;ofs : shortint): TRegister;
|
|
begin
|
|
if ofs>3 then
|
|
result:=TRegister(longint(rhi)+ofs-4)
|
|
else
|
|
result:=TRegister(longint(r)+ofs);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_load_reg_cgpara(list : TAsmList;size : tcgsize;r : tregister;const cgpara : tcgpara);
|
|
|
|
procedure load_para_loc(r : TRegister;paraloc : PCGParaLocation);
|
|
var
|
|
ref : treference;
|
|
begin
|
|
paramanager.allocparaloc(list,paraloc);
|
|
case paraloc^.loc of
|
|
LOC_REGISTER,LOC_CREGISTER:
|
|
a_load_reg_reg(list,paraloc^.size,paraloc^.size,r,paraloc^.register);
|
|
LOC_REFERENCE,LOC_CREFERENCE:
|
|
begin
|
|
reference_reset_base(ref,paraloc^.reference.index,paraloc^.reference.offset,ctempposinvalid,2,[]);
|
|
if ref.base<>NR_STACK_POINTER_REG then
|
|
Internalerror(2020011801);
|
|
|
|
{ as AVR allows no stack indirect addressing, everything else than a push makes no sense }
|
|
list.concat(taicpu.op_reg(A_PUSH,r));
|
|
end;
|
|
else
|
|
internalerror(2002071007);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
i, i2 : longint;
|
|
hp : PCGParaLocation;
|
|
|
|
begin
|
|
if not(tcgsize2size[cgpara.Size] in [1..4]) then
|
|
internalerror(2014011106);
|
|
|
|
hp:=cgpara.location;
|
|
|
|
i:=0;
|
|
while i<tcgsize2size[cgpara.Size] do
|
|
begin
|
|
if not(assigned(hp)) then
|
|
internalerror(2014011102);
|
|
|
|
inc(i, tcgsize2size[hp^.Size]);
|
|
|
|
if hp^.Loc=LOC_REGISTER then
|
|
begin
|
|
load_para_loc(r,hp);
|
|
hp:=hp^.Next;
|
|
{ check if we are not in the last iteration to avoid an internalerror in GetNextReg }
|
|
if i<tcgsize2size[cgpara.Size] then
|
|
r:=GetNextReg(r);
|
|
end
|
|
else
|
|
begin
|
|
load_para_loc(r,hp);
|
|
|
|
if i<tcgsize2size[cgpara.Size] then
|
|
for i2:=1 to tcgsize2size[hp^.Size] do
|
|
r:=GetNextReg(r);
|
|
|
|
hp:=hp^.Next;
|
|
end;
|
|
end;
|
|
if assigned(hp) then
|
|
internalerror(2014011103);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_load_const_cgpara(list : TAsmList;size : tcgsize;a : tcgint;const paraloc : TCGPara);
|
|
var
|
|
i,j : longint;
|
|
hp : PCGParaLocation;
|
|
tmpreg: TRegister;
|
|
begin
|
|
if not(tcgsize2size[paraloc.Size] in [1..4]) then
|
|
internalerror(2014011107);
|
|
|
|
hp:=paraloc.location;
|
|
|
|
i:=1;
|
|
while i<=tcgsize2size[paraloc.Size] do
|
|
begin
|
|
if not(assigned(hp)) then
|
|
internalerror(2014011105);
|
|
paramanager.allocparaloc(list,hp);
|
|
case hp^.loc of
|
|
LOC_REGISTER,LOC_CREGISTER:
|
|
begin
|
|
if (tcgsize2size[hp^.size]<>1) or
|
|
(hp^.shiftval<>0) then
|
|
internalerror(2015041101);
|
|
a_load_const_reg(list,hp^.size,(a shr (8*(i-1))) and $ff,hp^.register);
|
|
|
|
inc(i,tcgsize2size[hp^.size]);
|
|
hp:=hp^.Next;
|
|
end;
|
|
LOC_REFERENCE,LOC_CREFERENCE:
|
|
begin
|
|
for j:=1 to tcgsize2size[hp^.size] do
|
|
begin
|
|
tmpreg:=getintregister(list,OS_8);
|
|
a_load_const_reg(list,OS_8,(a shr (8*(i-1+j-1))) and $ff,tmpreg);
|
|
{ as AVR allows no stack indirect addressing, everything else than a push makes no sense }
|
|
list.concat(taicpu.op_reg(A_PUSH,tmpreg));
|
|
end;
|
|
inc(i,tcgsize2size[hp^.size]);
|
|
hp:=hp^.Next;
|
|
end;
|
|
else
|
|
internalerror(2002071008);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_load_ref_cgpara(list : TAsmList;size : tcgsize;const r : treference;const paraloc : TCGPara);
|
|
var
|
|
tmpref: treference;
|
|
location: pcgparalocation;
|
|
sizeleft: tcgint;
|
|
i: Integer;
|
|
tmpreg: TRegister;
|
|
begin
|
|
location := paraloc.location;
|
|
tmpref := r;
|
|
sizeleft := paraloc.intsize;
|
|
while assigned(location) do
|
|
begin
|
|
paramanager.allocparaloc(list,location);
|
|
case location^.loc of
|
|
LOC_REGISTER,LOC_CREGISTER:
|
|
a_load_ref_reg(list,location^.size,location^.size,tmpref,location^.register);
|
|
LOC_REFERENCE:
|
|
begin
|
|
for i:=1 to sizeleft do
|
|
begin
|
|
tmpreg:=getintregister(list,OS_8);
|
|
a_load_ref_reg(list,OS_8,OS_8,tmpref,tmpreg);
|
|
{ as AVR allows no stack indirect addressing, everything else than a push makes no sense }
|
|
list.concat(taicpu.op_reg(A_PUSH,tmpreg));
|
|
inc(tmpref.offset);
|
|
end;
|
|
end;
|
|
LOC_VOID:
|
|
begin
|
|
// nothing to do
|
|
end;
|
|
else
|
|
internalerror(2002081103);
|
|
end;
|
|
inc(tmpref.offset,tcgsize2size[location^.size]);
|
|
dec(sizeleft,tcgsize2size[location^.size]);
|
|
location := location^.next;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_loadaddr_ref_cgpara(list : TAsmList;const r : treference;const paraloc : TCGPara);
|
|
var
|
|
tmpreg: tregister;
|
|
begin
|
|
tmpreg:=getaddressregister(list);
|
|
a_loadaddr_ref_reg(list,r,tmpreg);
|
|
a_load_reg_cgpara(list,OS_ADDR,tmpreg,paraloc);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_call_name(list : TAsmList;const s : string; weak: boolean);
|
|
var
|
|
sym: TAsmSymbol;
|
|
begin
|
|
if weak then
|
|
sym:=current_asmdata.WeakRefAsmSymbol(s,AT_FUNCTION)
|
|
else
|
|
sym:=current_asmdata.RefAsmSymbol(s,AT_FUNCTION);
|
|
|
|
if CPUAVR_HAS_JMP_CALL in cpu_capabilities[current_settings.cputype] then
|
|
list.concat(taicpu.op_sym(A_CALL,sym))
|
|
else
|
|
list.concat(taicpu.op_sym(A_RCALL,sym));
|
|
|
|
include(current_procinfo.flags,pi_do_call);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_call_reg(list : TAsmList;reg: tregister);
|
|
begin
|
|
a_reg_alloc(list,NR_ZLO);
|
|
emit_mov(list,NR_ZLO,reg);
|
|
a_reg_alloc(list,NR_ZHI);
|
|
emit_mov(list,NR_ZHI,GetHigh(reg));
|
|
list.concat(taicpu.op_none(A_ICALL));
|
|
a_reg_dealloc(list,NR_ZHI);
|
|
a_reg_dealloc(list,NR_ZLO);
|
|
|
|
include(current_procinfo.flags,pi_do_call);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_op_const_reg(list : TAsmList; Op: TOpCG; size: TCGSize; a: tcgint; reg: TRegister);
|
|
begin
|
|
if not(size in [OS_S8,OS_8,OS_S16,OS_16,OS_S32,OS_32]) then
|
|
internalerror(2012102403);
|
|
a_op_const_reg_internal(list,Op,size,a,reg,NR_NO);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_op_reg_reg(list: TAsmList; Op: TOpCG; size: TCGSize; src, dst : TRegister);
|
|
begin
|
|
if not(size in [OS_S8,OS_8,OS_S16,OS_16,OS_S32,OS_32]) then
|
|
internalerror(2012102401);
|
|
a_op_reg_reg_internal(list,Op,size,src,NR_NO,dst,NR_NO);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_op_const_reg_reg(list: TAsmList; op: TOpCg; size: tcgsize; a: tcgint; src, dst: tregister);
|
|
begin
|
|
a_op_const_reg_reg_internal(list,op,size,a,src,NR_NO,dst,NR_NO);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_op_const_reg_reg_internal(list: TAsmList; op: TOpCg; size: tcgsize; a: tcgint; src,srchi,dst,dsthi: tregister);
|
|
|
|
procedure shiftLoop(const bitstoshift, bytestart: integer);
|
|
var
|
|
l1: TAsmLabel;
|
|
i: integer;
|
|
countreg: TRegister;
|
|
oldexecutionweight: LongInt;
|
|
begin
|
|
current_asmdata.getjumplabel(l1);
|
|
countreg:=getintregister(list,OS_8);
|
|
a_load_const_reg(list,OS_8,bitstoshift,countreg);
|
|
cg.a_label(list,l1);
|
|
oldexecutionweight:=executionweight;
|
|
executionweight:=executionweight*bitstoshift;
|
|
if op=OP_SHL then
|
|
list.concat(taicpu.op_reg(A_LSL,GetOffsetReg64(dst,dsthi,bytestart)))
|
|
else
|
|
list.concat(taicpu.op_reg(A_LSR,GetOffsetReg64(dst,dsthi,tcgsize2size[size]-1-bytestart)));
|
|
|
|
if size in [OS_S16,OS_16,OS_S32,OS_32,OS_S64,OS_64] then
|
|
begin
|
|
for i:=2+bytestart to tcgsize2size[size] do
|
|
if op=OP_SHL then
|
|
list.concat(taicpu.op_reg(A_ROL,GetOffsetReg64(dst,dsthi,i-1)))
|
|
else
|
|
list.concat(taicpu.op_reg(A_ROR,GetOffsetReg64(dst,dsthi,tcgsize2size[size]-i)));
|
|
end;
|
|
cg.a_reg_alloc(list,NR_DEFAULTFLAGS);
|
|
list.concat(taicpu.op_reg(A_DEC,countreg));
|
|
a_jmp_flags(list,F_NE,l1);
|
|
executionweight:=oldexecutionweight;
|
|
{ keep registers alive }
|
|
a_reg_sync(list,countreg);
|
|
end;
|
|
|
|
procedure shiftSequence(const bitstoshift, bytestart: integer);
|
|
var
|
|
i, j: integer;
|
|
begin
|
|
{ Unroll shift loop over non-moved bytes }
|
|
for j:=1 to bitstoshift do
|
|
begin
|
|
if op=OP_SHL then
|
|
list.concat(taicpu.op_reg(A_LSL,
|
|
GetOffsetReg64(dst,dsthi,bytestart)))
|
|
else
|
|
list.concat(taicpu.op_reg(A_LSR,
|
|
GetOffsetReg64(dst,dsthi,tcgsize2size[size]-bytestart-1)));
|
|
|
|
if tcgsize2size[size]-bytestart > 1 then
|
|
for i:=2 to tcgsize2size[size]-bytestart do
|
|
if op=OP_SHL then
|
|
list.concat(taicpu.op_reg(A_ROL,
|
|
GetOffsetReg64(dst,dsthi,bytestart+i-1)))
|
|
else
|
|
list.concat(taicpu.op_reg(A_ROR,
|
|
GetOffsetReg64(dst,dsthi,tcgsize2size[size]-bytestart-i)));
|
|
end;
|
|
end;
|
|
|
|
procedure fastshift4(const bytestart: integer);
|
|
var
|
|
j, mask, sign, stopindex: integer;
|
|
begin
|
|
if op=OP_SHL then
|
|
begin
|
|
mask:=$F0;
|
|
j:=tcgsize2size[size]-1;
|
|
sign:=-1; // Start at MSB
|
|
stopindex:=-bytestart;
|
|
end
|
|
else
|
|
begin
|
|
mask:=$0F;
|
|
j:=0;
|
|
sign:=1; // Start at LSB
|
|
stopindex:=tcgsize2size[size]-bytestart-1;
|
|
end;
|
|
|
|
list.concat(taicpu.op_reg(A_SWAP,
|
|
GetOffsetReg64(dst,dsthi,j)));
|
|
list.concat(taicpu.op_reg_const(A_ANDI,
|
|
GetOffsetReg64(dst,dsthi,j),
|
|
mask));
|
|
|
|
while sign*j<stopindex do
|
|
begin
|
|
list.concat(taicpu.op_reg(A_SWAP,
|
|
GetOffsetReg64(dst,dsthi,j+sign)));
|
|
|
|
list.concat(taicpu.op_reg_reg(A_EOR,
|
|
GetOffsetReg64(dst,dsthi,j),
|
|
GetOffsetReg64(dst,dsthi,j+sign)));
|
|
|
|
list.concat(taicpu.op_reg_const(A_ANDI,
|
|
GetOffsetReg64(dst,dsthi,j+sign),
|
|
mask));
|
|
|
|
list.concat(taicpu.op_reg_reg(A_EOR,
|
|
GetOffsetReg64(dst,dsthi,j),
|
|
GetOffsetReg64(dst,dsthi,j+sign)));
|
|
j:=j+sign;
|
|
end
|
|
end;
|
|
|
|
procedure fastshift6(const bytestart: integer);
|
|
var
|
|
i, j, k, mask, sign: integer;
|
|
asmop: TAsmOp;
|
|
begin
|
|
{ 8 bit value can just wrap around through carry flag. }
|
|
if tcgsize2size[size]-bytestart = 1 then
|
|
begin
|
|
if op=OP_SHL then
|
|
begin
|
|
mask:=$C0;
|
|
asmop:=A_ROR;
|
|
i:=bytestart;
|
|
end
|
|
else
|
|
begin
|
|
mask:=$03;
|
|
asmop:=A_ROL;
|
|
i:=tcgsize2size[size]-bytestart-1;
|
|
end;
|
|
|
|
list.concat(taicpu.op_reg(asmop,
|
|
GetOffsetReg64(dst,dsthi,i)));
|
|
list.concat(taicpu.op_reg(asmop,
|
|
GetOffsetReg64(dst,dsthi,i)));
|
|
list.concat(taicpu.op_reg(asmop,
|
|
GetOffsetReg64(dst,dsthi,i)));
|
|
list.concat(taicpu.op_reg_const(A_ANDI,
|
|
GetOffsetReg64(dst,dsthi,i),
|
|
mask));
|
|
end
|
|
else { > 8 bit }
|
|
begin
|
|
if op=OP_SHL then
|
|
begin
|
|
asmop:=A_ROR;
|
|
k:=tcgsize2size[size]-1;
|
|
sign:=-1;
|
|
end
|
|
else
|
|
begin
|
|
asmop:=A_ROL;
|
|
k:=0;
|
|
sign:=1;
|
|
end;
|
|
list.concat(taicpu.op_reg(A_CLR,GetDefaultTmpReg));
|
|
|
|
for j:=0 to 1 do
|
|
begin
|
|
for i:=0 to tcgsize2size[size]-bytestart-1 do
|
|
list.concat(taicpu.op_reg(asmop,
|
|
GetOffsetReg64(dst,dsthi,k+sign*i)));
|
|
|
|
list.concat(taicpu.op_reg(asmop,GetDefaultTmpReg));
|
|
end;
|
|
|
|
{ Sort bytes into correct order. }
|
|
for i:=0 to tcgsize2size[size]-bytestart-2 do
|
|
list.concat(taicpu.op_reg_reg(A_MOV,
|
|
GetOffsetReg64(dst,dsthi,k+sign*i),
|
|
GetOffsetReg64(dst,dsthi,k+sign*(i+1))));
|
|
|
|
list.concat(taicpu.op_reg_reg(A_MOV,
|
|
GetOffsetReg64(dst,dsthi,k+sign*(tcgsize2size[size]-bytestart-1)),
|
|
GetDefaultTmpReg));
|
|
end;
|
|
end;
|
|
|
|
procedure fastshift7(const bytestart: integer);
|
|
var
|
|
i, j: integer;
|
|
begin
|
|
if op=OP_SHL then
|
|
list.concat(taicpu.op_reg(A_ROR,
|
|
GetOffsetReg64(dst,dsthi,tcgsize2size[size]-1)))
|
|
else
|
|
list.concat(taicpu.op_reg(A_ROL,
|
|
GetOffsetReg64(dst,dsthi,0)));
|
|
|
|
if (tcgsize2size[size]-bytestart > 1) then
|
|
for j:=0 to tcgsize2size[size]-bytestart-2 do
|
|
begin
|
|
if op=OP_SHL then
|
|
begin
|
|
list.concat(taicpu.op_reg_reg(A_MOV,
|
|
GetOffsetReg64(dst,dsthi,tcgsize2size[size]-1-j),
|
|
GetOffsetReg64(dst,dsthi,tcgsize2size[size]-2-j)));
|
|
list.concat(taicpu.op_reg(A_ROR,
|
|
GetOffsetReg64(dst,dsthi,tcgsize2size[size]-1-j)));
|
|
end
|
|
else
|
|
begin
|
|
list.concat(taicpu.op_reg_reg(A_MOV,
|
|
GetOffsetReg64(dst,dsthi,j),
|
|
GetOffsetReg64(dst,dsthi,1+j)));
|
|
list.concat(taicpu.op_reg(A_ROL,
|
|
GetOffsetReg64(dst,dsthi,j)));
|
|
end;
|
|
end;
|
|
|
|
if op=OP_SHL then
|
|
begin
|
|
list.concat(taicpu.op_reg(A_CLR,
|
|
GetOffsetReg64(dst,dsthi,bytestart)));
|
|
list.concat(taicpu.op_reg(A_ROR,
|
|
GetOffsetReg64(dst,dsthi,bytestart)));
|
|
end
|
|
else
|
|
begin
|
|
list.concat(taicpu.op_reg(A_CLR,
|
|
GetOffsetReg64(dst,dsthi,tcgsize2size[size]-bytestart-1)));
|
|
list.concat(taicpu.op_reg(A_ROL,
|
|
GetOffsetReg64(dst,dsthi,tcgsize2size[size]-bytestart-1)));
|
|
end;
|
|
end;
|
|
|
|
var
|
|
b, b2, i: byte;
|
|
begin
|
|
if (op in [OP_MUL,OP_IMUL]) and (size in [OS_16,OS_S16]) and (a in [2,4,8]) then
|
|
begin
|
|
emit_mov(list,dst,src);
|
|
emit_mov(list,GetNextReg(dst),GetNextReg(src));
|
|
a:=a shr 1;
|
|
while a>0 do
|
|
begin
|
|
list.concat(taicpu.op_reg(A_LSL,dst));
|
|
list.concat(taicpu.op_reg(A_ROL,GetNextReg(dst)));
|
|
a:=a shr 1;
|
|
end;
|
|
end
|
|
else if (op in [OP_SHL,OP_SHR]) and
|
|
{ a=0 get eliminated later by tcg.optimize_op_const }
|
|
(a>0) then
|
|
begin
|
|
{ number of bytes to shift }
|
|
b:=a div 8;
|
|
|
|
{ Ensure that b is never larger than base type }
|
|
if b>tcgsize2size[size] then
|
|
begin
|
|
b:=tcgsize2size[size];
|
|
b2:=0;
|
|
end
|
|
else
|
|
b2:=a mod 8;
|
|
|
|
if b < tcgsize2size[size] then
|
|
{ copy from src to dst accounting for shift offset }
|
|
for i:=0 to (tcgsize2size[size]-b-1) do
|
|
if op=OP_SHL then
|
|
a_load_reg_reg(list,OS_8,OS_8,
|
|
GetOffsetReg64(src,srchi,i),
|
|
GetOffsetReg64(dst,dsthi,i+b))
|
|
else
|
|
a_load_reg_reg(list,OS_8,OS_8,
|
|
GetOffsetReg64(src,srchi,i+b),
|
|
GetOffsetReg64(dst,dsthi,i));
|
|
|
|
{ remaining bit shifts }
|
|
if b2 > 0 then
|
|
begin
|
|
{ A shift loop construct will always involve more instructions
|
|
and/or take more cycles than the alternatives
|
|
in the following cases. }
|
|
if not(cs_opt_size in current_settings.optimizerswitches) or
|
|
(tcgsize2size[size]-b=1) then
|
|
begin
|
|
case b2 of
|
|
1,2,3: shiftSequence(b2, b);
|
|
4: fastshift4(b);
|
|
5:
|
|
begin
|
|
fastshift4(b);
|
|
shiftSequence(1,b);
|
|
end;
|
|
6: fastshift6(b);
|
|
7: fastshift7(b);
|
|
else
|
|
Internalerror(2022031101);
|
|
end;
|
|
end
|
|
else { cs_opt_size or multi-byte shift }
|
|
begin
|
|
{ Check if shift loop size is smaller than
|
|
a straight shift sequence. }
|
|
if (tcgsize2size[size]-b)*(b2-1) > 3 then
|
|
shiftLoop(b2,b)
|
|
else
|
|
shiftSequence(b2,b);
|
|
end;
|
|
end;
|
|
|
|
{ fill skipped destination registers with 0
|
|
Do last,then optimizer can optimize register moves }
|
|
for i:=1 to b do
|
|
if op=OP_SHL then
|
|
emit_mov(list,GetOffsetReg64(dst,dsthi,i-1),GetDefaultZeroReg)
|
|
else
|
|
emit_mov(list,GetOffsetReg64(dst,dsthi,tcgsize2size[size]-i),GetDefaultZeroReg);
|
|
end
|
|
else
|
|
inherited a_op_const_reg_reg(list,op,size,a,src,dst);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_op_const_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tcgsize; a: tcgint; src, dst: tregister; setflags: boolean; var ovloc: tlocation);
|
|
var
|
|
tmpreg: TRegister;
|
|
begin
|
|
if (op in [OP_MUL,OP_IMUL]) and
|
|
setflags then
|
|
begin
|
|
tmpreg:=getintregister(list,size);
|
|
a_load_const_reg(list,size,a,tmpreg);
|
|
a_op_reg_reg_reg_checkoverflow(list,op,size,tmpreg,src,dst,setflags,ovloc);
|
|
end
|
|
else
|
|
begin
|
|
inherited a_op_const_reg_reg_checkoverflow(list, op, size, a, src, dst, setflags, ovloc);
|
|
ovloc.loc:=LOC_FLAGS;
|
|
end;
|
|
end;
|
|
|
|
procedure tcgavr.a_op_reg_reg_reg_checkoverflow(list: TAsmList; op: TOpCg; size: tcgsize; src1, src2, dst: tregister; setflags: boolean; var ovloc: tlocation);
|
|
begin
|
|
if (op in [OP_MUL,OP_IMUL]) and
|
|
setflags then
|
|
gen_multiply(list,op,size,src1,src2,dst,setflags,ovloc)
|
|
else
|
|
begin
|
|
inherited a_op_reg_reg_reg_checkoverflow(list, op, size, src1, src2, dst, setflags, ovloc);
|
|
ovloc.loc:=LOC_FLAGS;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_op_reg_reg_internal(list : TAsmList; Op: TOpCG; size: TCGSize; src, srchi, dst, dsthi: TRegister);
|
|
var
|
|
countreg,
|
|
tmpreg: tregister;
|
|
i : integer;
|
|
l1,l2 : tasmlabel;
|
|
hovloc: tlocation;
|
|
|
|
{ NextRegDst* is sometimes called before the register usage and sometimes afterwards }
|
|
procedure NextSrcDstPreInc;
|
|
begin
|
|
if i=5 then
|
|
begin
|
|
dst:=dsthi;
|
|
src:=srchi;
|
|
end
|
|
else
|
|
begin
|
|
dst:=GetNextReg(dst);
|
|
src:=GetNextReg(src);
|
|
end;
|
|
end;
|
|
|
|
procedure NextSrcDstPostInc;
|
|
begin
|
|
if i=4 then
|
|
begin
|
|
dst:=dsthi;
|
|
src:=srchi;
|
|
end
|
|
else
|
|
begin
|
|
dst:=GetNextReg(dst);
|
|
src:=GetNextReg(src);
|
|
end;
|
|
end;
|
|
|
|
{ iterates TmpReg through all registers of dst }
|
|
procedure NextTmp;
|
|
begin
|
|
if i=4 then
|
|
tmpreg:=dsthi
|
|
else
|
|
tmpreg:=GetNextReg(tmpreg);
|
|
end;
|
|
|
|
begin
|
|
case op of
|
|
OP_ADD:
|
|
begin
|
|
if tcgsize2size[size]>1 then
|
|
cg.a_reg_alloc(list, NR_DEFAULTFLAGS);
|
|
|
|
list.concat(taicpu.op_reg_reg(A_ADD,dst,src));
|
|
for i:=2 to tcgsize2size[size] do
|
|
begin
|
|
NextSrcDstPreInc;
|
|
list.concat(taicpu.op_reg_reg(A_ADC,dst,src));
|
|
end;
|
|
|
|
if tcgsize2size[size]>1 then
|
|
cg.a_reg_dealloc(list, NR_DEFAULTFLAGS);
|
|
end;
|
|
|
|
OP_SUB:
|
|
begin
|
|
if tcgsize2size[size]>1 then
|
|
cg.a_reg_alloc(list, NR_DEFAULTFLAGS);
|
|
|
|
list.concat(taicpu.op_reg_reg(A_SUB,dst,src));
|
|
for i:=2 to tcgsize2size[size] do
|
|
begin
|
|
NextSrcDstPreInc;
|
|
list.concat(taicpu.op_reg_reg(A_SBC,dst,src));
|
|
end;
|
|
|
|
if tcgsize2size[size]>1 then
|
|
cg.a_reg_dealloc(list, NR_DEFAULTFLAGS);
|
|
end;
|
|
|
|
OP_NEG:
|
|
begin
|
|
if src<>dst then
|
|
begin
|
|
if size in [OS_S64,OS_64] then
|
|
begin
|
|
a_load_reg_reg(list,OS_32,OS_32,src,dst);
|
|
a_load_reg_reg(list,OS_32,OS_32,srchi,dsthi);
|
|
end
|
|
else
|
|
a_load_reg_reg(list,size,size,src,dst);
|
|
end;
|
|
|
|
if size in [OS_S16,OS_16,OS_S32,OS_32,OS_S64,OS_64] then
|
|
begin
|
|
tmpreg:=GetNextReg(dst);
|
|
for i:=2 to tcgsize2size[size] do
|
|
begin
|
|
list.concat(taicpu.op_reg(A_COM,tmpreg));
|
|
{ check if we are not in the last iteration to avoid an internalerror in GetNextReg }
|
|
if i<tcgsize2size[size] then
|
|
NextTmp;
|
|
end;
|
|
if tcgsize2size[size]>1 then
|
|
cg.a_reg_alloc(list, NR_DEFAULTFLAGS);
|
|
|
|
list.concat(taicpu.op_reg(A_NEG,dst));
|
|
tmpreg:=GetNextReg(dst);
|
|
for i:=2 to tcgsize2size[size] do
|
|
begin
|
|
list.concat(taicpu.op_reg_const(A_SBCI,tmpreg,-1));
|
|
{ check if we are not in the last iteration to avoid an internalerror in GetNextReg }
|
|
if i<tcgsize2size[size] then
|
|
NextTmp;
|
|
end;
|
|
|
|
if tcgsize2size[size]>1 then
|
|
cg.a_reg_dealloc(list, NR_DEFAULTFLAGS);
|
|
end
|
|
else if size in [OS_S8,OS_8] then
|
|
list.concat(taicpu.op_reg(A_NEG,dst))
|
|
else
|
|
Internalerror(2018030401);
|
|
end;
|
|
|
|
OP_NOT:
|
|
begin
|
|
for i:=1 to tcgsize2size[size] do
|
|
begin
|
|
if src<>dst then
|
|
a_load_reg_reg(list,OS_8,OS_8,src,dst);
|
|
list.concat(taicpu.op_reg(A_COM,dst));
|
|
{ check if we are not in the last iteration to avoid an internalerror in GetNextReg }
|
|
if i<tcgsize2size[size] then
|
|
NextSrcDstPostInc;
|
|
end;
|
|
end;
|
|
|
|
OP_MUL,OP_IMUL:
|
|
begin
|
|
tmpreg:=dst;
|
|
if size in [OS_16,OS_S16] then
|
|
begin
|
|
tmpreg:=getintregister(list,size);
|
|
a_load_reg_reg(list,size,size,dst,tmpreg);
|
|
end;
|
|
gen_multiply(list,op,size,src,tmpreg,dst,false,hovloc);
|
|
end;
|
|
|
|
OP_DIV,OP_IDIV:
|
|
{ special stuff, needs separate handling inside code }
|
|
{ generator }
|
|
internalerror(2011022001);
|
|
|
|
OP_SHR,OP_SHL,OP_SAR,OP_ROL,OP_ROR:
|
|
begin
|
|
current_asmdata.getjumplabel(l1);
|
|
current_asmdata.getjumplabel(l2);
|
|
countreg:=getintregister(list,OS_8);
|
|
a_load_reg_reg(list,size,OS_8,src,countreg);
|
|
cg.a_reg_alloc(list,NR_DEFAULTFLAGS);
|
|
list.concat(taicpu.op_reg(A_TST,countreg));
|
|
a_jmp_flags(list,F_EQ,l2);
|
|
cg.a_label(list,l1);
|
|
case op of
|
|
OP_SHR:
|
|
list.concat(taicpu.op_reg(A_LSR,GetOffsetReg64(dst,dsthi,tcgsize2size[size]-1)));
|
|
OP_SHL:
|
|
list.concat(taicpu.op_reg(A_LSL,dst));
|
|
OP_SAR:
|
|
list.concat(taicpu.op_reg(A_ASR,GetOffsetReg64(dst,dsthi,tcgsize2size[size]-1)));
|
|
OP_ROR:
|
|
begin
|
|
{ load carry? }
|
|
if not(size in [OS_8,OS_S8]) then
|
|
begin
|
|
list.concat(taicpu.op_none(A_CLC));
|
|
list.concat(taicpu.op_reg_const(A_SBRC,dst,0));
|
|
list.concat(taicpu.op_none(A_SEC));
|
|
end;
|
|
list.concat(taicpu.op_reg(A_ROR,GetOffsetReg64(dst,dsthi,tcgsize2size[size]-1)));
|
|
end;
|
|
OP_ROL:
|
|
begin
|
|
{ load carry? }
|
|
if not(size in [OS_8,OS_S8]) then
|
|
begin
|
|
list.concat(taicpu.op_none(A_CLC));
|
|
list.concat(taicpu.op_reg_const(A_SBRC,GetOffsetReg64(dst,dsthi,tcgsize2size[size]-1),7));
|
|
list.concat(taicpu.op_none(A_SEC));
|
|
end;
|
|
list.concat(taicpu.op_reg(A_ROL,dst))
|
|
end;
|
|
else
|
|
internalerror(2011030901);
|
|
end;
|
|
if size in [OS_S16,OS_16,OS_S32,OS_32,OS_S64,OS_64] then
|
|
begin
|
|
for i:=2 to tcgsize2size[size] do
|
|
begin
|
|
case op of
|
|
OP_ROR,
|
|
OP_SHR:
|
|
list.concat(taicpu.op_reg(A_ROR,GetOffsetReg64(dst,dsthi,tcgsize2size[size]-i)));
|
|
OP_ROL,
|
|
OP_SHL:
|
|
list.concat(taicpu.op_reg(A_ROL,GetOffsetReg64(dst,dsthi,i-1)));
|
|
OP_SAR:
|
|
list.concat(taicpu.op_reg(A_ROR,GetOffsetReg64(dst,dsthi,tcgsize2size[size]-i)));
|
|
else
|
|
internalerror(2011030902);
|
|
end;
|
|
end;
|
|
end;
|
|
cg.a_reg_alloc(list,NR_DEFAULTFLAGS);
|
|
list.concat(taicpu.op_reg(A_DEC,countreg));
|
|
a_jmp_flags(list,F_NE,l1);
|
|
{ keep registers alive }
|
|
a_reg_sync(list,countreg);
|
|
cg.a_label(list,l2);
|
|
end;
|
|
|
|
OP_AND,OP_OR,OP_XOR:
|
|
begin
|
|
for i:=1 to tcgsize2size[size] do
|
|
begin
|
|
list.concat(taicpu.op_reg_reg(topcg2asmop[op],dst,src));
|
|
{ check if we are not in the last iteration to avoid an internalerror in GetNextReg }
|
|
if i<tcgsize2size[size] then
|
|
NextSrcDstPostInc;
|
|
end;
|
|
end;
|
|
else
|
|
internalerror(2011022004);
|
|
end;
|
|
end;
|
|
|
|
procedure tcgavr.a_op_const_reg_internal(list: TAsmList; Op: TOpCG;
|
|
size: TCGSize; a: tcgint; reg, reghi: TRegister);
|
|
|
|
var
|
|
mask : qword;
|
|
shift : byte;
|
|
i,j : byte;
|
|
tmpreg : tregister;
|
|
tmpreg64 : tregister64;
|
|
|
|
{ NextReg* is sometimes called before the register usage and sometimes afterwards }
|
|
procedure NextRegPreInc;
|
|
begin
|
|
if i=5 then
|
|
reg:=reghi
|
|
else
|
|
reg:=GetNextReg(reg);
|
|
end;
|
|
|
|
|
|
procedure NextRegPostInc;
|
|
begin
|
|
if i=4 then
|
|
reg:=reghi
|
|
else
|
|
reg:=GetNextReg(reg);
|
|
end;
|
|
|
|
var
|
|
curvalue : byte;
|
|
l1: TAsmLabel;
|
|
|
|
begin
|
|
optimize_op_const(size,op,a);
|
|
mask:=$ff;
|
|
shift:=0;
|
|
case op of
|
|
OP_NONE:
|
|
begin
|
|
{ Opcode is optimized away }
|
|
end;
|
|
OP_MOVE:
|
|
begin
|
|
{ Optimized, replaced with a simple load }
|
|
a_load_const_reg(list,size,a,reg);
|
|
end;
|
|
OP_OR:
|
|
begin
|
|
for i:=1 to tcgsize2size[size] do
|
|
begin
|
|
if ((qword(a) and mask) shr shift)<>0 then
|
|
list.concat(taicpu.op_reg_const(A_ORI,reg,(qword(a) and mask) shr shift));
|
|
{ check if we are not in the last iteration to avoid an internalerror in GetNextReg }
|
|
if i<tcgsize2size[size] then
|
|
NextRegPostInc;
|
|
mask:=mask shl 8;
|
|
inc(shift,8);
|
|
end;
|
|
end;
|
|
OP_AND:
|
|
begin
|
|
for i:=1 to tcgsize2size[size] do
|
|
begin
|
|
if ((qword(a) and mask) shr shift)=0 then
|
|
list.concat(taicpu.op_reg_reg(A_MOV,reg,GetDefaultZeroReg))
|
|
else if ((qword(a) and mask) shr shift)<>$ff then
|
|
begin
|
|
getcpuregister(list,NR_R26);
|
|
list.concat(taicpu.op_reg_const(A_LDI,NR_R26,(qword(a) and mask) shr shift));
|
|
list.concat(taicpu.op_reg_reg(A_AND,reg,NR_R26));
|
|
ungetcpuregister(list,NR_R26);
|
|
end;
|
|
{ check if we are not in the last iteration to avoid an internalerror in GetNextReg }
|
|
if i<tcgsize2size[size] then
|
|
NextRegPostInc;
|
|
mask:=mask shl 8;
|
|
inc(shift,8);
|
|
end;
|
|
end;
|
|
OP_SUB:
|
|
begin
|
|
if ((a and mask)=1) and (tcgsize2size[size]=1) then
|
|
list.concat(taicpu.op_reg(A_DEC,reg))
|
|
else
|
|
begin
|
|
getcpuregister(list,NR_R26);
|
|
list.concat(taicpu.op_reg_const(A_LDI,NR_R26,a and mask));
|
|
list.concat(taicpu.op_reg_reg(A_SUB,reg,NR_R26));
|
|
ungetcpuregister(list,NR_R26);
|
|
end;
|
|
if size in [OS_S16,OS_16,OS_S32,OS_32,OS_S64,OS_64] then
|
|
begin
|
|
for i:=2 to tcgsize2size[size] do
|
|
begin
|
|
NextRegPreInc;
|
|
mask:=mask shl 8;
|
|
inc(shift,8);
|
|
curvalue:=(qword(a) and mask) shr shift;
|
|
{ decrease pressure on upper half of registers by using SBC ...,R1 instead
|
|
of SBCI ...,0 }
|
|
if curvalue=0 then
|
|
list.concat(taicpu.op_reg_reg(A_SBC,reg,GetDefaultZeroReg))
|
|
else
|
|
list.concat(taicpu.op_reg_const(A_SBCI,reg,curvalue));
|
|
end;
|
|
end;
|
|
end;
|
|
OP_SHR,OP_SHL,OP_SAR,OP_ROL,OP_ROR:
|
|
begin
|
|
if (op=OP_SAR) and (a>=(tcgsize2size[size]*8-1)) then
|
|
begin
|
|
current_asmdata.getjumplabel(l1);
|
|
cg.a_reg_alloc(list,NR_DEFAULTFLAGS);
|
|
list.concat(taicpu.op_reg(A_TST,GetOffsetReg64(reg,reghi,tcgsize2size[size]-1)));
|
|
a_load_const_reg(list,OS_8,0,GetOffsetReg64(reg,reghi,tcgsize2size[size]-1));
|
|
a_jmp_flags(list,F_PL,l1);
|
|
list.concat(taicpu.op_reg(A_DEC,GetOffsetReg64(reg,reghi,tcgsize2size[size]-1)));
|
|
cg.a_label(list,l1);
|
|
for i:=2 to tcgsize2size[size] do
|
|
a_load_reg_reg(list,OS_8,OS_8,GetOffsetReg64(reg,reghi,tcgsize2size[size]-1),GetOffsetReg64(reg,reghi,tcgsize2size[size]-i));
|
|
end
|
|
else if (op=OP_SHR) and (a=(tcgsize2size[size]*8-1)) then
|
|
begin
|
|
current_asmdata.getjumplabel(l1);
|
|
cg.a_reg_alloc(list,NR_DEFAULTFLAGS);
|
|
list.concat(taicpu.op_reg(A_TST,GetOffsetReg64(reg,reghi,tcgsize2size[size]-1)));
|
|
a_load_const_reg(list,OS_8,0,GetOffsetReg64(reg,reghi,0));
|
|
a_jmp_flags(list,F_PL,l1);
|
|
list.concat(taicpu.op_reg(A_INC,GetOffsetReg64(reg,reghi,0)));
|
|
cg.a_label(list,l1);
|
|
for i:=1 to tcgsize2size[size]-1 do
|
|
a_load_const_reg(list,OS_8,0,GetOffsetReg64(reg,reghi,i));
|
|
end
|
|
else if a*tcgsize2size[size]<=8 then
|
|
begin
|
|
for j:=1 to a do
|
|
begin
|
|
case op of
|
|
OP_SHR:
|
|
list.concat(taicpu.op_reg(A_LSR,GetOffsetReg64(reg,reghi,tcgsize2size[size]-1)));
|
|
OP_SHL:
|
|
list.concat(taicpu.op_reg(A_LSL,reg));
|
|
OP_SAR:
|
|
list.concat(taicpu.op_reg(A_ASR,GetOffsetReg64(reg,reghi,tcgsize2size[size]-1)));
|
|
OP_ROR:
|
|
begin
|
|
{ load carry? }
|
|
if not(size in [OS_8,OS_S8]) then
|
|
begin
|
|
list.concat(taicpu.op_none(A_CLC));
|
|
list.concat(taicpu.op_reg_const(A_SBRC,reg,0));
|
|
list.concat(taicpu.op_none(A_SEC));
|
|
end;
|
|
list.concat(taicpu.op_reg(A_ROR,GetOffsetReg64(reg,reghi,tcgsize2size[size]-1)));
|
|
end;
|
|
OP_ROL:
|
|
begin
|
|
{ load carry? }
|
|
if not(size in [OS_8,OS_S8]) then
|
|
begin
|
|
list.concat(taicpu.op_none(A_CLC));
|
|
list.concat(taicpu.op_reg_const(A_SBRC,GetOffsetReg64(reg,reghi,tcgsize2size[size]-1),7));
|
|
list.concat(taicpu.op_none(A_SEC));
|
|
end;
|
|
list.concat(taicpu.op_reg(A_ROL,reg))
|
|
end;
|
|
else
|
|
internalerror(2011030903);
|
|
end;
|
|
if size in [OS_S16,OS_16,OS_S32,OS_32,OS_S64,OS_64] then
|
|
begin
|
|
for i:=2 to tcgsize2size[size] do
|
|
begin
|
|
case op of
|
|
OP_ROR,
|
|
OP_SHR:
|
|
list.concat(taicpu.op_reg(A_ROR,GetOffsetReg64(reg,reghi,tcgsize2size[size]-i)));
|
|
OP_ROL,
|
|
OP_SHL:
|
|
list.concat(taicpu.op_reg(A_ROL,GetOffsetReg64(reg,reghi,i-1)));
|
|
OP_SAR:
|
|
list.concat(taicpu.op_reg(A_ROR,GetOffsetReg64(reg,reghi,tcgsize2size[size]-i)));
|
|
else
|
|
internalerror(2011030904);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
tmpreg:=getintregister(list,size);
|
|
a_load_const_reg(list,size,a,tmpreg);
|
|
a_op_reg_reg(list,op,size,tmpreg,reg);
|
|
end;
|
|
end;
|
|
OP_ADD:
|
|
begin
|
|
curvalue:=a and mask;
|
|
|
|
if tcgsize2size[size]>1 then
|
|
cg.a_reg_alloc(list, NR_DEFAULTFLAGS);
|
|
|
|
if curvalue=0 then
|
|
list.concat(taicpu.op_reg_reg(A_ADD,reg,GetDefaultZeroReg))
|
|
else if (curvalue=1) and (tcgsize2size[size]=1) then
|
|
list.concat(taicpu.op_reg(A_INC,reg))
|
|
else
|
|
begin
|
|
tmpreg:=getintregister(list,OS_8);
|
|
a_load_const_reg(list,OS_8,curvalue,tmpreg);
|
|
list.concat(taicpu.op_reg_reg(A_ADD,reg,tmpreg));
|
|
end;
|
|
if size in [OS_S16,OS_16,OS_S32,OS_32,OS_S64,OS_64] then
|
|
begin
|
|
for i:=2 to tcgsize2size[size] do
|
|
begin
|
|
NextRegPreInc;
|
|
mask:=mask shl 8;
|
|
inc(shift,8);
|
|
curvalue:=(qword(a) and mask) shr shift;
|
|
{ decrease pressure on upper half of registers by using ADC ...,R1 instead
|
|
of ADD ...,0 }
|
|
if curvalue=0 then
|
|
list.concat(taicpu.op_reg_reg(A_ADC,reg,GetDefaultZeroReg))
|
|
else
|
|
begin
|
|
tmpreg:=getintregister(list,OS_8);
|
|
a_load_const_reg(list,OS_8,curvalue,tmpreg);
|
|
list.concat(taicpu.op_reg_reg(A_ADC,reg,tmpreg));
|
|
end;
|
|
end;
|
|
end;
|
|
if tcgsize2size[size]>1 then
|
|
cg.a_reg_dealloc(list, NR_DEFAULTFLAGS);
|
|
end;
|
|
else
|
|
begin
|
|
if size in [OS_64,OS_S64] then
|
|
begin
|
|
tmpreg64.reglo:=getintregister(list,OS_32);
|
|
tmpreg64.reghi:=getintregister(list,OS_32);
|
|
cg64.a_load64_const_reg(list,a,tmpreg64);
|
|
cg64.a_op64_reg_reg(list,op,size,tmpreg64,joinreg64(reg,reghi));
|
|
end
|
|
else
|
|
begin
|
|
{$if 0}
|
|
{ code not working yet }
|
|
if (op=OP_SAR) and (a=31) and (size in [OS_32,OS_S32]) then
|
|
begin
|
|
tmpreg:=reg;
|
|
for i:=1 to 4 do
|
|
begin
|
|
list.concat(taicpu.op_reg_reg(A_MOV,tmpreg,GetDefaultZeroReg));
|
|
tmpreg:=GetNextReg(tmpreg);
|
|
end;
|
|
end
|
|
else
|
|
{$endif}
|
|
begin
|
|
tmpreg:=getintregister(list,size);
|
|
a_load_const_reg(list,size,a,tmpreg);
|
|
a_op_reg_reg(list,op,size,tmpreg,reg);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_load_const_reg(list : TAsmList; size: tcgsize; a : tcgint;reg : tregister);
|
|
var
|
|
mask : qword;
|
|
shift : byte;
|
|
i : byte;
|
|
begin
|
|
mask:=$ff;
|
|
shift:=0;
|
|
for i:=1 to tcgsize2size[size] do
|
|
begin
|
|
if ((qword(a) and mask) shr shift)=0 then
|
|
emit_mov(list,reg,GetDefaultZeroReg)
|
|
else
|
|
begin
|
|
getcpuregister(list,NR_R26);
|
|
list.concat(taicpu.op_reg_const(A_LDI,NR_R26,(qword(a) and mask) shr shift));
|
|
a_load_reg_reg(list,OS_8,OS_8,NR_R26,reg);
|
|
ungetcpuregister(list,NR_R26);
|
|
end;
|
|
|
|
mask:=mask shl 8;
|
|
inc(shift,8);
|
|
{ check if we are not in the last iteration to avoid an internalerror in GetNextReg }
|
|
if i<tcgsize2size[size] then
|
|
reg:=GetNextReg(reg);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tcgavr.maybegetcpuregister(list:tasmlist;reg : tregister);
|
|
begin
|
|
{ allocate the register only, if a cpu register is passed }
|
|
if getsupreg(reg)<first_int_imreg then
|
|
getcpuregister(list,reg);
|
|
end;
|
|
|
|
{ Returns true if dataspace address falls in I/O register range }
|
|
function tcgavr.addr_is_io_register(const addr: integer): boolean;
|
|
begin
|
|
result := (not(current_settings.cputype in [cpu_avrxmega3,cpu_avrtiny]) and (addr>31)) or
|
|
((current_settings.cputype in [cpu_avrxmega3,cpu_avrtiny]) and (addr>=0)) and
|
|
(addr<cpuinfo.embedded_controllers[current_settings.controllertype].srambase);
|
|
end;
|
|
|
|
|
|
function tcgavr.normalize_ref(list:TAsmList;ref: treference;tmpreg : tregister) : treference;
|
|
var
|
|
tmpref : treference;
|
|
begin
|
|
Result:=ref;
|
|
|
|
if ref.addressmode<>AM_UNCHANGED then
|
|
internalerror(2011021705);
|
|
|
|
{ Be sure to have a base register }
|
|
if (ref.base=NR_NO) then
|
|
begin
|
|
ref.base:=ref.index;
|
|
ref.index:=NR_NO;
|
|
end;
|
|
|
|
{ can we take advantage of adiw/sbiw? }
|
|
if (CPUAVR_HAS_ADIW in cpu_capabilities[current_settings.cputype]) and not(assigned(ref.symbol)) and (ref.offset<>0) and (ref.offset>=-63) and (ref.offset<=63) and
|
|
((tmpreg=NR_R24) or (tmpreg=NR_R26) or (tmpreg=NR_R28) or (tmpreg=NR_R30)) and (ref.base<>NR_NO) then
|
|
begin
|
|
maybegetcpuregister(list,tmpreg);
|
|
emit_mov(list,tmpreg,ref.base);
|
|
maybegetcpuregister(list,GetNextReg(tmpreg));
|
|
emit_mov(list,GetNextReg(tmpreg),GetNextReg(ref.base));
|
|
if ref.index<>NR_NO then
|
|
begin
|
|
cg.a_reg_alloc(list, NR_DEFAULTFLAGS);
|
|
list.concat(taicpu.op_reg_reg(A_ADD,tmpreg,ref.index));
|
|
list.concat(taicpu.op_reg_reg(A_ADC,GetNextReg(tmpreg),GetNextReg(ref.index)));
|
|
cg.a_reg_dealloc(list, NR_DEFAULTFLAGS);
|
|
end;
|
|
if ref.offset>0 then
|
|
list.concat(taicpu.op_reg_const(A_ADIW,tmpreg,ref.offset))
|
|
else
|
|
list.concat(taicpu.op_reg_const(A_SBIW,tmpreg,-ref.offset));
|
|
ref.offset:=0;
|
|
ref.base:=tmpreg;
|
|
ref.index:=NR_NO;
|
|
end
|
|
else if assigned(ref.symbol) or (ref.offset<>0) then
|
|
begin
|
|
reference_reset(tmpref,0,[]);
|
|
tmpref.symbol:=ref.symbol;
|
|
tmpref.offset:=ref.offset;
|
|
if assigned(ref.symbol) and (ref.symbol.typ in [AT_FUNCTION,AT_LABEL]) then
|
|
tmpref.refaddr:=addr_lo8_gs
|
|
else
|
|
tmpref.refaddr:=addr_lo8;
|
|
maybegetcpuregister(list,tmpreg);
|
|
list.concat(taicpu.op_reg_ref(A_LDI,tmpreg,tmpref));
|
|
|
|
if assigned(ref.symbol) and (ref.symbol.typ in [AT_FUNCTION,AT_LABEL]) then
|
|
tmpref.refaddr:=addr_hi8_gs
|
|
else
|
|
tmpref.refaddr:=addr_hi8;
|
|
maybegetcpuregister(list,GetNextReg(tmpreg));
|
|
list.concat(taicpu.op_reg_ref(A_LDI,GetNextReg(tmpreg),tmpref));
|
|
|
|
if (ref.base<>NR_NO) then
|
|
begin
|
|
cg.a_reg_alloc(list, NR_DEFAULTFLAGS);
|
|
list.concat(taicpu.op_reg_reg(A_ADD,tmpreg,ref.base));
|
|
list.concat(taicpu.op_reg_reg(A_ADC,GetNextReg(tmpreg),GetNextReg(ref.base)));
|
|
cg.a_reg_dealloc(list, NR_DEFAULTFLAGS);
|
|
end;
|
|
if (ref.index<>NR_NO) then
|
|
begin
|
|
cg.a_reg_alloc(list, NR_DEFAULTFLAGS);
|
|
list.concat(taicpu.op_reg_reg(A_ADD,tmpreg,ref.index));
|
|
list.concat(taicpu.op_reg_reg(A_ADC,GetNextReg(tmpreg),GetNextReg(ref.index)));
|
|
cg.a_reg_dealloc(list, NR_DEFAULTFLAGS);
|
|
end;
|
|
ref.symbol:=nil;
|
|
ref.offset:=0;
|
|
ref.base:=tmpreg;
|
|
ref.index:=NR_NO;
|
|
end
|
|
else if (ref.base<>NR_NO) and (ref.index<>NR_NO) then
|
|
begin
|
|
maybegetcpuregister(list,tmpreg);
|
|
emit_mov(list,tmpreg,ref.base);
|
|
maybegetcpuregister(list,GetNextReg(tmpreg));
|
|
emit_mov(list,GetNextReg(tmpreg),GetNextReg(ref.base));
|
|
cg.a_reg_alloc(list, NR_DEFAULTFLAGS);
|
|
list.concat(taicpu.op_reg_reg(A_ADD,tmpreg,ref.index));
|
|
list.concat(taicpu.op_reg_reg(A_ADC,GetNextReg(tmpreg),GetNextReg(ref.index)));
|
|
cg.a_reg_dealloc(list, NR_DEFAULTFLAGS);
|
|
ref.base:=tmpreg;
|
|
ref.index:=NR_NO;
|
|
end
|
|
else if (ref.base<>NR_NO) then
|
|
begin
|
|
maybegetcpuregister(list,tmpreg);
|
|
emit_mov(list,tmpreg,ref.base);
|
|
maybegetcpuregister(list,GetNextReg(tmpreg));
|
|
emit_mov(list,GetNextReg(tmpreg),GetNextReg(ref.base));
|
|
ref.base:=tmpreg;
|
|
ref.index:=NR_NO;
|
|
end
|
|
else if (ref.index<>NR_NO) then
|
|
begin
|
|
maybegetcpuregister(list,tmpreg);
|
|
emit_mov(list,tmpreg,ref.index);
|
|
maybegetcpuregister(list,GetNextReg(tmpreg));
|
|
emit_mov(list,GetNextReg(tmpreg),GetNextReg(ref.index));
|
|
ref.base:=tmpreg;
|
|
ref.index:=NR_NO;
|
|
end
|
|
else
|
|
Internalerror(2020011901);
|
|
Result:=ref;
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_load_reg_ref(list : TAsmList; fromsize, tosize: tcgsize; reg : tregister;const ref : treference);
|
|
var
|
|
href : treference;
|
|
conv_done: boolean;
|
|
tmpreg : tregister;
|
|
i : integer;
|
|
QuickRef,ungetcpuregister_z: Boolean;
|
|
begin
|
|
QuickRef:=false;
|
|
ungetcpuregister_z:=false;
|
|
|
|
href:=Ref;
|
|
{ ensure, href.base contains a valid register if there is any register used }
|
|
if href.base=NR_NO then
|
|
begin
|
|
href.base:=href.index;
|
|
href.index:=NR_NO;
|
|
end;
|
|
|
|
{ try to use std/sts }
|
|
if not((href.Base=NR_NO) and (href.Index=NR_NO)) then
|
|
begin
|
|
if not((href.addressmode=AM_UNCHANGED) and
|
|
(href.symbol=nil) and
|
|
(href.Index=NR_NO) and
|
|
(href.Offset in [0..64-tcgsize2size[fromsize]])) or
|
|
(CPUAVR_16_REGS in cpu_capabilities[current_settings.cputype]) then
|
|
begin
|
|
href:=normalize_ref(list,href,NR_R30);
|
|
getcpuregister(list,NR_R30);
|
|
getcpuregister(list,NR_R31);
|
|
ungetcpuregister_z:=true;
|
|
end
|
|
else
|
|
begin
|
|
if (href.base<>NR_R28) and (href.base<>NR_R30) then
|
|
begin
|
|
getcpuregister(list,NR_R30);
|
|
emit_mov(list,NR_R30,href.base);
|
|
getcpuregister(list,NR_R31);
|
|
emit_mov(list,NR_R31,GetNextReg(href.base));
|
|
href.base:=NR_R30;
|
|
ungetcpuregister_z:=true;
|
|
end;
|
|
QuickRef:=true;
|
|
end;
|
|
end
|
|
else
|
|
QuickRef:=true;
|
|
|
|
if (tcgsize2size[fromsize]>32) or (tcgsize2size[tosize]>32) or (fromsize=OS_NO) or (tosize=OS_NO) then
|
|
internalerror(2011021303);
|
|
|
|
conv_done:=false;
|
|
if tosize<>fromsize then
|
|
begin
|
|
conv_done:=true;
|
|
if tcgsize2size[tosize]<=tcgsize2size[fromsize] then
|
|
fromsize:=tosize;
|
|
case fromsize of
|
|
OS_8:
|
|
begin
|
|
if not(QuickRef) and (tcgsize2size[tosize]>1) then
|
|
href.addressmode:=AM_POSTINCREMENT;
|
|
|
|
list.concat(taicpu.op_ref_reg(GetStore(href),href,reg));
|
|
for i:=2 to tcgsize2size[tosize] do
|
|
begin
|
|
if QuickRef then
|
|
inc(href.offset);
|
|
|
|
if not(QuickRef) and (i<tcgsize2size[fromsize]) then
|
|
href.addressmode:=AM_POSTINCREMENT
|
|
else
|
|
href.addressmode:=AM_UNCHANGED;
|
|
|
|
list.concat(taicpu.op_ref_reg(GetStore(href),href,GetDefaultZeroReg));
|
|
end;
|
|
end;
|
|
OS_S8:
|
|
begin
|
|
if not(QuickRef) and (tcgsize2size[tosize]>1) then
|
|
href.addressmode:=AM_POSTINCREMENT;
|
|
list.concat(taicpu.op_ref_reg(GetStore(href),href,reg));
|
|
|
|
if tcgsize2size[tosize]>1 then
|
|
begin
|
|
tmpreg:=getintregister(list,OS_8);
|
|
emit_mov(list,tmpreg,GetDefaultZeroReg);
|
|
list.concat(taicpu.op_reg_const(A_SBRC,reg,7));
|
|
list.concat(taicpu.op_reg(A_COM,tmpreg));
|
|
for i:=2 to tcgsize2size[tosize] do
|
|
begin
|
|
if QuickRef then
|
|
inc(href.offset);
|
|
|
|
if not(QuickRef) and (i<tcgsize2size[fromsize]) then
|
|
href.addressmode:=AM_POSTINCREMENT
|
|
else
|
|
href.addressmode:=AM_UNCHANGED;
|
|
list.concat(taicpu.op_ref_reg(GetStore(href),href,tmpreg));
|
|
end;
|
|
end;
|
|
end;
|
|
OS_16:
|
|
begin
|
|
if not(QuickRef) and (tcgsize2size[tosize]>1) then
|
|
href.addressmode:=AM_POSTINCREMENT;
|
|
|
|
list.concat(taicpu.op_ref_reg(GetStore(href),href,reg));
|
|
if QuickRef then
|
|
inc(href.offset)
|
|
else if not(QuickRef) and (tcgsize2size[fromsize]>2) then
|
|
href.addressmode:=AM_POSTINCREMENT
|
|
else
|
|
href.addressmode:=AM_UNCHANGED;
|
|
|
|
reg:=GetNextReg(reg);
|
|
list.concat(taicpu.op_ref_reg(GetStore(href),href,reg));
|
|
|
|
for i:=3 to tcgsize2size[tosize] do
|
|
begin
|
|
if QuickRef then
|
|
inc(href.offset);
|
|
|
|
if not(QuickRef) and (i<tcgsize2size[fromsize]) then
|
|
href.addressmode:=AM_POSTINCREMENT
|
|
else
|
|
href.addressmode:=AM_UNCHANGED;
|
|
|
|
list.concat(taicpu.op_ref_reg(GetStore(href),href,GetDefaultZeroReg));
|
|
end;
|
|
end;
|
|
OS_S16:
|
|
begin
|
|
if not(QuickRef) and (tcgsize2size[tosize]>1) then
|
|
href.addressmode:=AM_POSTINCREMENT;
|
|
|
|
list.concat(taicpu.op_ref_reg(GetStore(href),href,reg));
|
|
if QuickRef then
|
|
inc(href.offset)
|
|
else if not(QuickRef) and (tcgsize2size[fromsize]>2) then
|
|
href.addressmode:=AM_POSTINCREMENT
|
|
else
|
|
href.addressmode:=AM_UNCHANGED;
|
|
|
|
reg:=GetNextReg(reg);
|
|
list.concat(taicpu.op_ref_reg(GetStore(href),href,reg));
|
|
|
|
if tcgsize2size[tosize]>2 then
|
|
begin
|
|
tmpreg:=getintregister(list,OS_8);
|
|
emit_mov(list,tmpreg,GetDefaultZeroReg);
|
|
list.concat(taicpu.op_reg_const(A_SBRC,reg,7));
|
|
list.concat(taicpu.op_reg(A_COM,tmpreg));
|
|
for i:=3 to tcgsize2size[tosize] do
|
|
begin
|
|
if QuickRef then
|
|
inc(href.offset);
|
|
|
|
if not(QuickRef) and (i<tcgsize2size[fromsize]) then
|
|
href.addressmode:=AM_POSTINCREMENT
|
|
else
|
|
href.addressmode:=AM_UNCHANGED;
|
|
list.concat(taicpu.op_ref_reg(GetStore(href),href,tmpreg));
|
|
end;
|
|
end;
|
|
end;
|
|
else
|
|
conv_done:=false;
|
|
end;
|
|
end;
|
|
if not conv_done then
|
|
begin
|
|
{ Write to 16 bit ioreg, first high byte then low byte
|
|
sequence required for 16 bit timer registers
|
|
See e.g. atmega328p manual para 15.3 Accessing 16 bit registers
|
|
Avrxmega3: write low byte first then high byte
|
|
See e.g. megaAVR-0 family data sheet 7.5.6 Accessing 16-bit registers }
|
|
if (current_settings.cputype <> cpu_avrxmega3) and
|
|
(fromsize in [OS_16, OS_S16]) and QuickRef and addr_is_io_register(href.offset) then
|
|
begin
|
|
tmpreg:=GetNextReg(reg);
|
|
href.addressmode:=AM_UNCHANGED;
|
|
inc(href.offset);
|
|
list.concat(taicpu.op_ref_reg(GetStore(href),href,tmpreg));
|
|
dec(href.offset);
|
|
list.concat(taicpu.op_ref_reg(GetStore(href),href,reg));
|
|
end
|
|
else
|
|
begin
|
|
for i:=1 to tcgsize2size[fromsize] do
|
|
begin
|
|
if not(QuickRef) and (i<tcgsize2size[fromsize]) then
|
|
href.addressmode:=AM_POSTINCREMENT
|
|
else
|
|
href.addressmode:=AM_UNCHANGED;
|
|
|
|
list.concat(taicpu.op_ref_reg(GetStore(href),href,reg));
|
|
|
|
if QuickRef then
|
|
inc(href.offset);
|
|
|
|
{ check if we are not in the last iteration to avoid an internalerror in GetNextReg }
|
|
if i<tcgsize2size[fromsize] then
|
|
reg:=GetNextReg(reg);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if not(QuickRef) or ungetcpuregister_z then
|
|
begin
|
|
ungetcpuregister(list,href.base);
|
|
ungetcpuregister(list,GetNextReg(href.base));
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_load_ref_reg(list : TAsmList; fromsize, tosize : tcgsize;
|
|
const Ref : treference;reg : tregister);
|
|
var
|
|
href : treference;
|
|
conv_done: boolean;
|
|
tmpreg : tregister;
|
|
i : integer;
|
|
QuickRef,ungetcpuregister_z: boolean;
|
|
begin
|
|
QuickRef:=false;
|
|
ungetcpuregister_z:=false;
|
|
|
|
href:=Ref;
|
|
{ ensure, href.base contains a valid register if there is any register used }
|
|
if href.base=NR_NO then
|
|
begin
|
|
href.base:=href.index;
|
|
href.index:=NR_NO;
|
|
end;
|
|
|
|
{ try to use ldd/lds }
|
|
if not((href.Base=NR_NO) and (href.Index=NR_NO)) then
|
|
begin
|
|
if not((href.addressmode=AM_UNCHANGED) and
|
|
(href.symbol=nil) and
|
|
(href.Index=NR_NO) and
|
|
(href.Offset in [0..64-tcgsize2size[fromsize]])) or
|
|
(CPUAVR_16_REGS in cpu_capabilities[current_settings.cputype]) then
|
|
begin
|
|
href:=normalize_ref(list,href,NR_R30);
|
|
getcpuregister(list,NR_R30);
|
|
getcpuregister(list,NR_R31);
|
|
ungetcpuregister_z:=true;
|
|
end
|
|
else
|
|
begin
|
|
if (href.base<>NR_R28) and (href.base<>NR_R30) then
|
|
begin
|
|
getcpuregister(list,NR_R30);
|
|
emit_mov(list,NR_R30,href.base);
|
|
getcpuregister(list,NR_R31);
|
|
emit_mov(list,NR_R31,GetNextReg(href.base));
|
|
href.base:=NR_R30;
|
|
ungetcpuregister_z:=true;
|
|
end;
|
|
QuickRef:=true;
|
|
end;
|
|
end
|
|
else
|
|
QuickRef:=true;
|
|
|
|
if (tcgsize2size[fromsize]>32) or (tcgsize2size[tosize]>32) or (fromsize=OS_NO) or (tosize=OS_NO) then
|
|
internalerror(2011021304);
|
|
|
|
conv_done:=false;
|
|
if tosize<>fromsize then
|
|
begin
|
|
conv_done:=true;
|
|
if tcgsize2size[tosize]<=tcgsize2size[fromsize] then
|
|
fromsize:=tosize;
|
|
case fromsize of
|
|
OS_8:
|
|
begin
|
|
list.concat(taicpu.op_reg_ref(GetLoad(href),reg,href));
|
|
for i:=2 to tcgsize2size[tosize] do
|
|
begin
|
|
reg:=GetNextReg(reg);
|
|
emit_mov(list,reg,GetDefaultZeroReg);
|
|
end;
|
|
end;
|
|
OS_S8:
|
|
begin
|
|
list.concat(taicpu.op_reg_ref(GetLoad(href),reg,href));
|
|
tmpreg:=reg;
|
|
|
|
if tcgsize2size[tosize]>1 then
|
|
begin
|
|
reg:=GetNextReg(reg);
|
|
emit_mov(list,reg,GetDefaultZeroReg);
|
|
list.concat(taicpu.op_reg_const(A_SBRC,tmpreg,7));
|
|
list.concat(taicpu.op_reg(A_COM,reg));
|
|
tmpreg:=reg;
|
|
for i:=3 to tcgsize2size[tosize] do
|
|
begin
|
|
reg:=GetNextReg(reg);
|
|
emit_mov(list,reg,tmpreg);
|
|
end;
|
|
end;
|
|
end;
|
|
OS_16:
|
|
begin
|
|
if not(QuickRef) then
|
|
href.addressmode:=AM_POSTINCREMENT;
|
|
list.concat(taicpu.op_reg_ref(GetLoad(href),reg,href));
|
|
|
|
if QuickRef then
|
|
inc(href.offset);
|
|
href.addressmode:=AM_UNCHANGED;
|
|
|
|
reg:=GetNextReg(reg);
|
|
list.concat(taicpu.op_reg_ref(GetLoad(href),reg,href));
|
|
|
|
for i:=3 to tcgsize2size[tosize] do
|
|
begin
|
|
reg:=GetNextReg(reg);
|
|
emit_mov(list,reg,GetDefaultZeroReg);
|
|
end;
|
|
end;
|
|
OS_S16:
|
|
begin
|
|
if not(QuickRef) then
|
|
href.addressmode:=AM_POSTINCREMENT;
|
|
list.concat(taicpu.op_reg_ref(GetLoad(href),reg,href));
|
|
if QuickRef then
|
|
inc(href.offset);
|
|
href.addressmode:=AM_UNCHANGED;
|
|
|
|
reg:=GetNextReg(reg);
|
|
list.concat(taicpu.op_reg_ref(GetLoad(href),reg,href));
|
|
tmpreg:=reg;
|
|
|
|
reg:=GetNextReg(reg);
|
|
emit_mov(list,reg,GetDefaultZeroReg);
|
|
list.concat(taicpu.op_reg_const(A_SBRC,tmpreg,7));
|
|
list.concat(taicpu.op_reg(A_COM,reg));
|
|
tmpreg:=reg;
|
|
for i:=4 to tcgsize2size[tosize] do
|
|
begin
|
|
reg:=GetNextReg(reg);
|
|
emit_mov(list,reg,tmpreg);
|
|
end;
|
|
end;
|
|
else
|
|
conv_done:=false;
|
|
end;
|
|
end;
|
|
if not conv_done then
|
|
begin
|
|
for i:=1 to tcgsize2size[fromsize] do
|
|
begin
|
|
if not(QuickRef) and (i<tcgsize2size[fromsize]) then
|
|
href.addressmode:=AM_POSTINCREMENT
|
|
else
|
|
href.addressmode:=AM_UNCHANGED;
|
|
|
|
list.concat(taicpu.op_reg_ref(GetLoad(href),reg,href));
|
|
|
|
if QuickRef then
|
|
inc(href.offset);
|
|
|
|
{ check if we are not in the last iteration to avoid an internalerror in GetNextReg }
|
|
if i<tcgsize2size[fromsize] then
|
|
reg:=GetNextReg(reg);
|
|
end;
|
|
end;
|
|
|
|
if ungetcpuregister_z then
|
|
begin
|
|
ungetcpuregister(list,href.base);
|
|
ungetcpuregister(list,GetNextReg(href.base));
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_load_reg_reg(list : TAsmList; fromsize, tosize : tcgsize;reg1,reg2 : tregister);
|
|
var
|
|
conv_done: boolean;
|
|
tmpreg : tregister;
|
|
i : integer;
|
|
begin
|
|
if (tcgsize2size[fromsize]>32) or (tcgsize2size[tosize]>32) or (fromsize=OS_NO) or (tosize=OS_NO) then
|
|
internalerror(2011021310);
|
|
|
|
conv_done:=false;
|
|
if tosize<>fromsize then
|
|
begin
|
|
conv_done:=true;
|
|
if tcgsize2size[tosize]<=tcgsize2size[fromsize] then
|
|
fromsize:=tosize;
|
|
case fromsize of
|
|
OS_8:
|
|
begin
|
|
emit_mov(list,reg2,reg1);
|
|
for i:=2 to tcgsize2size[tosize] do
|
|
begin
|
|
reg2:=GetNextReg(reg2);
|
|
emit_mov(list,reg2,GetDefaultZeroReg);
|
|
end;
|
|
end;
|
|
OS_S8:
|
|
begin
|
|
emit_mov(list,reg2,reg1);
|
|
|
|
if tcgsize2size[tosize]>1 then
|
|
begin
|
|
reg2:=GetNextReg(reg2);
|
|
emit_mov(list,reg2,GetDefaultZeroReg);
|
|
list.concat(taicpu.op_reg_const(A_SBRC,reg1,7));
|
|
list.concat(taicpu.op_reg(A_COM,reg2));
|
|
tmpreg:=reg2;
|
|
for i:=3 to tcgsize2size[tosize] do
|
|
begin
|
|
reg2:=GetNextReg(reg2);
|
|
emit_mov(list,reg2,tmpreg);
|
|
end;
|
|
end;
|
|
end;
|
|
OS_16:
|
|
begin
|
|
emit_mov(list,reg2,reg1);
|
|
|
|
reg1:=GetNextReg(reg1);
|
|
reg2:=GetNextReg(reg2);
|
|
emit_mov(list,reg2,reg1);
|
|
|
|
for i:=3 to tcgsize2size[tosize] do
|
|
begin
|
|
reg2:=GetNextReg(reg2);
|
|
emit_mov(list,reg2,GetDefaultZeroReg);
|
|
end;
|
|
end;
|
|
OS_S16:
|
|
begin
|
|
emit_mov(list,reg2,reg1);
|
|
|
|
reg1:=GetNextReg(reg1);
|
|
reg2:=GetNextReg(reg2);
|
|
emit_mov(list,reg2,reg1);
|
|
|
|
if tcgsize2size[tosize]>2 then
|
|
begin
|
|
reg2:=GetNextReg(reg2);
|
|
emit_mov(list,reg2,GetDefaultZeroReg);
|
|
list.concat(taicpu.op_reg_const(A_SBRC,reg1,7));
|
|
list.concat(taicpu.op_reg(A_COM,reg2));
|
|
tmpreg:=reg2;
|
|
for i:=4 to tcgsize2size[tosize] do
|
|
begin
|
|
reg2:=GetNextReg(reg2);
|
|
emit_mov(list,reg2,tmpreg);
|
|
end;
|
|
end;
|
|
end;
|
|
else
|
|
conv_done:=false;
|
|
end;
|
|
end;
|
|
if not conv_done and (reg1<>reg2) then
|
|
begin
|
|
for i:=1 to tcgsize2size[fromsize] do
|
|
begin
|
|
emit_mov(list,reg2,reg1);
|
|
{ check if we are not in the last iteration to avoid an internalerror in GetNextReg }
|
|
if i<tcgsize2size[fromsize] then
|
|
begin
|
|
reg1:=GetNextReg(reg1);
|
|
reg2:=GetNextReg(reg2);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_loadfpu_reg_reg(list: TAsmList; fromsize,tosize: tcgsize; reg1, reg2: tregister);
|
|
begin
|
|
internalerror(2012010702);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_loadfpu_ref_reg(list: TAsmList; fromsize,tosize: tcgsize; const ref: treference; reg: tregister);
|
|
begin
|
|
internalerror(2012010703);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tcgsize; reg: tregister; const ref: treference);
|
|
begin
|
|
internalerror(2012010704);
|
|
end;
|
|
|
|
|
|
{ comparison operations }
|
|
procedure tcgavr.a_cmp_const_reg_label(list : TAsmList;size : tcgsize;
|
|
cmp_op : topcmp;a : tcgint;reg : tregister;l : tasmlabel);
|
|
var
|
|
swapped : boolean;
|
|
i : byte;
|
|
begin
|
|
if a=0 then
|
|
begin
|
|
swapped:=false;
|
|
{ swap parameters? }
|
|
case cmp_op of
|
|
OC_GT:
|
|
begin
|
|
swapped:=true;
|
|
cmp_op:=OC_LT;
|
|
end;
|
|
OC_LTE:
|
|
begin
|
|
swapped:=true;
|
|
cmp_op:=OC_GTE;
|
|
end;
|
|
OC_BE:
|
|
begin
|
|
swapped:=true;
|
|
cmp_op:=OC_AE;
|
|
end;
|
|
OC_A:
|
|
begin
|
|
swapped:=true;
|
|
cmp_op:=OC_B;
|
|
end;
|
|
end;
|
|
|
|
cg.a_reg_alloc(list,NR_DEFAULTFLAGS);
|
|
|
|
{ If doing a signed test for x<0, we can simply test the sign bit
|
|
of the most significant byte }
|
|
if (cmp_op in [OC_LT,OC_GTE]) and
|
|
(not swapped) then
|
|
begin
|
|
for i:=2 to tcgsize2size[size] do
|
|
reg:=GetNextReg(reg);
|
|
|
|
list.concat(taicpu.op_reg_reg(A_CP,reg,GetDefaultZeroReg));
|
|
end
|
|
else
|
|
begin
|
|
if swapped then
|
|
list.concat(taicpu.op_reg_reg(A_CP,GetDefaultZeroReg,reg))
|
|
else
|
|
list.concat(taicpu.op_reg_reg(A_CP,reg,GetDefaultZeroReg));
|
|
|
|
for i:=2 to tcgsize2size[size] do
|
|
begin
|
|
reg:=GetNextReg(reg);
|
|
if swapped then
|
|
list.concat(taicpu.op_reg_reg(A_CPC,GetDefaultZeroReg,reg))
|
|
else
|
|
list.concat(taicpu.op_reg_reg(A_CPC,reg,GetDefaultZeroReg));
|
|
end;
|
|
end;
|
|
|
|
a_jmp_cond(list,cmp_op,l);
|
|
|
|
cg.a_reg_dealloc(list,NR_DEFAULTFLAGS);
|
|
end
|
|
else
|
|
inherited a_cmp_const_reg_label(list,size,cmp_op,a,reg,l);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_cmp_reg_reg_label(list : TAsmList;size : tcgsize;
|
|
cmp_op : topcmp;reg1,reg2 : tregister;l : tasmlabel);
|
|
var
|
|
swapped : boolean;
|
|
tmpreg : tregister;
|
|
i : byte;
|
|
begin
|
|
swapped:=false;
|
|
{ swap parameters? }
|
|
case cmp_op of
|
|
OC_GT:
|
|
begin
|
|
swapped:=true;
|
|
cmp_op:=OC_LT;
|
|
end;
|
|
OC_LTE:
|
|
begin
|
|
swapped:=true;
|
|
cmp_op:=OC_GTE;
|
|
end;
|
|
OC_BE:
|
|
begin
|
|
swapped:=true;
|
|
cmp_op:=OC_AE;
|
|
end;
|
|
OC_A:
|
|
begin
|
|
swapped:=true;
|
|
cmp_op:=OC_B;
|
|
end;
|
|
end;
|
|
if swapped then
|
|
begin
|
|
tmpreg:=reg1;
|
|
reg1:=reg2;
|
|
reg2:=tmpreg;
|
|
end;
|
|
|
|
cg.a_reg_alloc(list,NR_DEFAULTFLAGS);
|
|
|
|
list.concat(taicpu.op_reg_reg(A_CP,reg2,reg1));
|
|
|
|
for i:=2 to tcgsize2size[size] do
|
|
begin
|
|
reg1:=GetNextReg(reg1);
|
|
reg2:=GetNextReg(reg2);
|
|
list.concat(taicpu.op_reg_reg(A_CPC,reg2,reg1));
|
|
end;
|
|
|
|
a_jmp_cond(list,cmp_op,l);
|
|
cg.a_reg_dealloc(list,NR_DEFAULTFLAGS);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_jmp_name(list : TAsmList;const s : string);
|
|
var
|
|
ai : taicpu;
|
|
begin
|
|
if CPUAVR_HAS_JMP_CALL in cpu_capabilities[current_settings.cputype] then
|
|
ai:=taicpu.op_sym(A_JMP,current_asmdata.RefAsmSymbol(s,AT_FUNCTION))
|
|
else
|
|
ai:=taicpu.op_sym(A_RJMP,current_asmdata.RefAsmSymbol(s,AT_FUNCTION));
|
|
ai.is_jmp:=true;
|
|
list.concat(ai);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_jmp_always(list : TAsmList;l: tasmlabel);
|
|
var
|
|
ai : taicpu;
|
|
begin
|
|
if CPUAVR_HAS_JMP_CALL in cpu_capabilities[current_settings.cputype] then
|
|
ai:=taicpu.op_sym(A_JMP,l)
|
|
else
|
|
ai:=taicpu.op_sym(A_RJMP,l);
|
|
ai.is_jmp:=true;
|
|
list.concat(ai);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_jmp_flags(list : TAsmList;const f : TResFlags;l: tasmlabel);
|
|
var
|
|
ai : taicpu;
|
|
begin
|
|
ai:=setcondition(taicpu.op_sym(A_BRxx,l),flags_to_cond(f));
|
|
ai.is_jmp:=true;
|
|
list.concat(ai);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.g_flags2reg(list: TAsmList; size: TCgSize; const f: TResFlags; reg: TRegister);
|
|
var
|
|
l : TAsmLabel;
|
|
//tmpflags : TResFlags;
|
|
i: Integer;
|
|
hreg: TRegister;
|
|
begin
|
|
current_asmdata.getjumplabel(l);
|
|
{
|
|
if flags_to_cond(f) then
|
|
begin
|
|
tmpflags:=f;
|
|
inverse_flags(tmpflags);
|
|
emit_mov(reg,GetDefaultZeroReg);
|
|
a_jmp_flags(list,tmpflags,l);
|
|
list.concat(taicpu.op_reg_const(A_LDI,reg,1));
|
|
end
|
|
else
|
|
}
|
|
begin
|
|
list.concat(taicpu.op_reg_const(A_LDI,reg,1));
|
|
hreg:=reg;
|
|
for i:=2 to tcgsize2size[size] do
|
|
begin
|
|
hreg:=GetNextReg(hreg);
|
|
emit_mov(list,hreg,GetDefaultZeroReg);
|
|
end;
|
|
|
|
a_jmp_flags(list,f,l);
|
|
emit_mov(list,reg,GetDefaultZeroReg);
|
|
end;
|
|
cg.a_label(list,l);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_adjust_sp(list : TAsmList; value : longint);
|
|
{var
|
|
i : integer; }
|
|
begin
|
|
case value of
|
|
0:
|
|
;
|
|
{-14..-1:
|
|
begin
|
|
if ((-value) mod 2)<>0 then
|
|
list.concat(taicpu.op_reg(A_PUSH,GetDefaultTmpReg));
|
|
for i:=1 to (-value) div 2 do
|
|
list.concat(taicpu.op_const(A_RCALL,0));
|
|
end;
|
|
1..7:
|
|
begin
|
|
for i:=1 to value do
|
|
list.concat(taicpu.op_reg(A_POP,GetDefaultTmpReg));
|
|
end;}
|
|
else
|
|
begin
|
|
list.concat(taicpu.op_reg_const(A_SUBI,NR_R28,lo(word(-value))));
|
|
list.concat(taicpu.op_reg_const(A_SBCI,NR_R29,hi(word(-value))));
|
|
// get SREG
|
|
list.concat(taicpu.op_reg_const(A_IN,GetDefaultTmpReg,NIO_SREG));
|
|
|
|
// block interrupts
|
|
list.concat(taicpu.op_none(A_CLI));
|
|
|
|
// write high SP
|
|
list.concat(taicpu.op_const_reg(A_OUT,NIO_SP_HI,NR_R29));
|
|
|
|
// release interrupts
|
|
list.concat(taicpu.op_const_reg(A_OUT,NIO_SREG,GetDefaultTmpReg));
|
|
|
|
// write low SP
|
|
list.concat(taicpu.op_const_reg(A_OUT,NIO_SP_LO,NR_R28));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function tcgavr.GetLoad(const ref: treference) : tasmop;
|
|
begin
|
|
if (ref.base=NR_NO) and (ref.index=NR_NO) then
|
|
result:=A_LDS
|
|
else if (ref.base<>NR_NO) and (ref.offset<>0) then
|
|
result:=A_LDD
|
|
else
|
|
result:=A_LD;
|
|
end;
|
|
|
|
|
|
function tcgavr.GetStore(const ref: treference) : tasmop;
|
|
begin
|
|
if (ref.base=NR_NO) and (ref.index=NR_NO) then
|
|
result:=A_STS
|
|
else if (ref.base<>NR_NO) and (ref.offset<>0) then
|
|
result:=A_STD
|
|
else
|
|
result:=A_ST;
|
|
end;
|
|
|
|
procedure tcgavr.gen_multiply(list: TAsmList; op: topcg; size: TCgSize; src2, src1, dst: tregister; check_overflow: boolean; var ovloc: tlocation);
|
|
|
|
procedure perform_r1_check(overflow_label: TAsmLabel; other_reg: TRegister=NR_R1);
|
|
var
|
|
ai: taicpu;
|
|
begin
|
|
if check_overflow then
|
|
begin
|
|
list.concat(taicpu.op_reg_reg(A_OR,NR_R1,other_reg));
|
|
|
|
ai:=Taicpu.Op_Sym(A_BRxx,overflow_label);
|
|
ai.SetCondition(C_NE);
|
|
ai.is_jmp:=true;
|
|
list.concat(ai);
|
|
end;
|
|
end;
|
|
|
|
procedure perform_ovf_check(overflow_label: TAsmLabel);
|
|
var
|
|
ai: taicpu;
|
|
begin
|
|
if check_overflow then
|
|
begin
|
|
ai:=Taicpu.Op_Sym(A_BRxx,overflow_label);
|
|
ai.SetCondition(C_CS);
|
|
ai.is_jmp:=true;
|
|
list.concat(ai);
|
|
end;
|
|
end;
|
|
|
|
var
|
|
pd: tprocdef;
|
|
paraloc1, paraloc2: tcgpara;
|
|
ai: taicpu;
|
|
hl, no_overflow: TAsmLabel;
|
|
name: String;
|
|
begin
|
|
ovloc.loc:=LOC_VOID;
|
|
if size in [OS_8,OS_S8] then
|
|
begin
|
|
if (CPUAVR_HAS_MUL in cpu_capabilities[current_settings.cputype]) and
|
|
(op=OP_MUL) then
|
|
begin
|
|
cg.a_reg_alloc(list,NR_R0);
|
|
cg.a_reg_alloc(list,NR_R1);
|
|
list.concat(taicpu.op_reg_reg(topcg2asmop[op],src1,src2));
|
|
// Check overflow
|
|
if check_overflow then
|
|
begin
|
|
current_asmdata.getjumplabel(hl);
|
|
list.concat(taicpu.op_reg_reg(A_AND,NR_R1,NR_R1));
|
|
|
|
{ Clear carry as it's not affected by any of the instructions }
|
|
list.concat(taicpu.op_none(A_CLC));
|
|
|
|
ai:=Taicpu.Op_Sym(A_BRxx,hl);
|
|
ai.SetCondition(C_EQ);
|
|
ai.is_jmp:=true;
|
|
list.concat(ai);
|
|
|
|
list.concat(taicpu.op_reg(A_CLR,NR_R1));
|
|
list.concat(taicpu.op_none(A_SEC));
|
|
|
|
a_label(list,hl);
|
|
|
|
ovloc.loc:=LOC_FLAGS;
|
|
end
|
|
else
|
|
list.concat(taicpu.op_reg(A_CLR,NR_R1));
|
|
cg.a_reg_dealloc(list,NR_R1);
|
|
|
|
list.concat(taicpu.op_reg_reg(A_MOV,dst,NR_R0));
|
|
cg.a_reg_dealloc(list,NR_R0);
|
|
end
|
|
else if (CPUAVR_HAS_MUL in cpu_capabilities[current_settings.cputype]) and
|
|
(op=OP_IMUL) then
|
|
begin
|
|
cg.a_reg_alloc(list,NR_R0);
|
|
cg.a_reg_alloc(list,NR_R1);
|
|
list.concat(taicpu.op_reg_reg(A_MULS,src1,src2));
|
|
list.concat(taicpu.op_reg_reg(A_MOV,dst,NR_R0));
|
|
|
|
// Check overflow
|
|
if check_overflow then
|
|
begin
|
|
current_asmdata.getjumplabel(no_overflow);
|
|
|
|
list.concat(taicpu.op_reg_const(A_SBRC,NR_R0,7));
|
|
list.concat(taicpu.op_reg(A_INC,NR_R1));
|
|
list.concat(taicpu.op_reg(A_TST,NR_R1));
|
|
|
|
ai:=Taicpu.Op_Sym(A_BRxx,no_overflow);
|
|
ai.SetCondition(C_EQ);
|
|
ai.is_jmp:=true;
|
|
list.concat(ai);
|
|
|
|
list.concat(taicpu.op_reg(A_CLR,NR_R1));
|
|
|
|
a_call_name(list,'FPC_OVERFLOW',false);
|
|
|
|
a_label(list,no_overflow);
|
|
|
|
ovloc.loc:=LOC_VOID;
|
|
end
|
|
else
|
|
list.concat(taicpu.op_reg(A_CLR,NR_R1));
|
|
cg.a_reg_dealloc(list,NR_R1);
|
|
cg.a_reg_dealloc(list,NR_R0);
|
|
end
|
|
else
|
|
begin
|
|
if size=OS_8 then
|
|
name:='fpc_mul_byte'
|
|
else
|
|
name:='fpc_mul_shortint';
|
|
|
|
if check_overflow then
|
|
name:=name+'_checkoverflow';
|
|
|
|
pd:=search_system_proc(name);
|
|
paraloc1.init;
|
|
paraloc2.init;
|
|
paramanager.getcgtempparaloc(list,pd,1,paraloc1);
|
|
paramanager.getcgtempparaloc(list,pd,2,paraloc2);
|
|
a_load_reg_cgpara(list,OS_8,src1,paraloc2);
|
|
a_load_reg_cgpara(list,OS_8,src2,paraloc1);
|
|
paramanager.freecgpara(list,paraloc2);
|
|
paramanager.freecgpara(list,paraloc1);
|
|
alloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
|
|
a_call_name(list,upper(name),false);
|
|
dealloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
|
|
cg.a_reg_alloc(list,NR_R24);
|
|
cg.a_load_reg_reg(list,OS_8,OS_8,NR_R24,dst);
|
|
cg.a_reg_dealloc(list,NR_R24);
|
|
paraloc2.done;
|
|
paraloc1.done;
|
|
end;
|
|
end
|
|
else if size in [OS_16,OS_S16] then
|
|
begin
|
|
if (CPUAVR_HAS_MUL in cpu_capabilities[current_settings.cputype]) and
|
|
((not check_overflow) or
|
|
(size=OS_16)) then
|
|
begin
|
|
if check_overflow then
|
|
begin
|
|
current_asmdata.getjumplabel(hl);
|
|
current_asmdata.getjumplabel(no_overflow);
|
|
end;
|
|
cg.a_reg_alloc(list,NR_R0);
|
|
cg.a_reg_alloc(list,NR_R1);
|
|
list.concat(taicpu.op_reg_reg(A_MUL,src2,src1));
|
|
emit_mov(list,dst,NR_R0);
|
|
emit_mov(list,GetNextReg(dst),NR_R1);
|
|
list.concat(taicpu.op_reg_reg(A_MUL,GetNextReg(src1),src2));
|
|
perform_r1_check(hl);
|
|
list.concat(taicpu.op_reg_reg(A_ADD,GetNextReg(dst),NR_R0));
|
|
perform_ovf_check(hl);
|
|
list.concat(taicpu.op_reg_reg(A_MUL,src1,GetNextReg(src2)));
|
|
perform_r1_check(hl);
|
|
list.concat(taicpu.op_reg_reg(A_ADD,GetNextReg(dst),NR_R0));
|
|
perform_ovf_check(hl);
|
|
|
|
if check_overflow then
|
|
begin
|
|
list.concat(taicpu.op_reg_reg(A_MUL,GetNextReg(src1),GetNextReg(src2)));
|
|
perform_r1_check(hl,NR_R0);
|
|
end;
|
|
|
|
cg.a_reg_dealloc(list,NR_R0);
|
|
list.concat(taicpu.op_reg(A_CLR,NR_R1));
|
|
|
|
if check_overflow then
|
|
begin
|
|
{
|
|
CLV/CLC
|
|
JMP no_overflow
|
|
.hl:
|
|
CLR R1
|
|
SEV/SEC
|
|
.no_overflow:
|
|
}
|
|
|
|
if op=OP_MUL then
|
|
list.concat(taicpu.op_none(A_CLC))
|
|
else
|
|
list.concat(taicpu.op_none(A_CLV));
|
|
|
|
a_jmp_always(list,no_overflow);
|
|
|
|
a_label(list,hl);
|
|
|
|
list.concat(taicpu.op_reg(A_CLR,NR_R1));
|
|
|
|
if op=OP_MUL then
|
|
list.concat(taicpu.op_none(A_SEC))
|
|
else
|
|
list.concat(taicpu.op_none(A_SEV));
|
|
|
|
a_label(list,no_overflow);
|
|
|
|
ovloc.loc:=LOC_FLAGS;
|
|
end;
|
|
|
|
cg.a_reg_dealloc(list,NR_R1);
|
|
end
|
|
else
|
|
begin
|
|
if size=OS_16 then
|
|
name:='fpc_mul_word'
|
|
else
|
|
name:='fpc_mul_integer';
|
|
|
|
if check_overflow then
|
|
name:=name+'_checkoverflow';
|
|
|
|
pd:=search_system_proc(name);
|
|
paraloc1.init;
|
|
paraloc2.init;
|
|
paramanager.getcgtempparaloc(list,pd,1,paraloc1);
|
|
paramanager.getcgtempparaloc(list,pd,2,paraloc2);
|
|
a_load_reg_cgpara(list,OS_16,src1,paraloc2);
|
|
a_load_reg_cgpara(list,OS_16,src2,paraloc1);
|
|
paramanager.freecgpara(list,paraloc2);
|
|
paramanager.freecgpara(list,paraloc1);
|
|
alloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
|
|
a_call_name(list,upper(name),false);
|
|
dealloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
|
|
cg.a_reg_alloc(list,NR_R24);
|
|
cg.a_reg_alloc(list,NR_R25);
|
|
cg.a_load_reg_reg(list,OS_8,OS_8,NR_R24,dst);
|
|
cg.a_reg_dealloc(list,NR_R24);
|
|
cg.a_load_reg_reg(list,OS_8,OS_8,NR_R25,GetNextReg(dst));
|
|
cg.a_reg_dealloc(list,NR_R25);
|
|
paraloc2.done;
|
|
paraloc1.done;
|
|
end;
|
|
end
|
|
else
|
|
internalerror(2011022002);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.g_proc_entry(list : TAsmList;localsize : longint;nostackframe:boolean);
|
|
var
|
|
regs : tcpuregisterset;
|
|
reg : tsuperregister;
|
|
begin
|
|
if current_procinfo.procdef.isempty then
|
|
exit;
|
|
if (po_interrupt in current_procinfo.procdef.procoptions) and
|
|
(not nostackframe) then
|
|
begin
|
|
{ check if the framepointer is actually used, this is done here because
|
|
we have to know the size of the locals (must be 0), avr does not know
|
|
an sp based stack }
|
|
|
|
if not(current_procinfo.procdef.stack_tainting_parameter(calleeside)) and
|
|
(localsize=0) then
|
|
current_procinfo.framepointer:=NR_NO;
|
|
|
|
{ save int registers,
|
|
but only if the procedure returns }
|
|
if not(po_noreturn in current_procinfo.procdef.procoptions) then
|
|
regs:=rg[R_INTREGISTER].used_in_proc
|
|
else
|
|
regs:=[];
|
|
{ if the framepointer is potentially used, save it always because we need a proper stack frame,
|
|
even if the procedure never returns, the procedure could be e.g. a nested one accessing
|
|
an outer stackframe }
|
|
if current_procinfo.framepointer<>NR_NO then
|
|
regs:=regs+[RS_R28,RS_R29];
|
|
|
|
{ we clear r1 }
|
|
include(regs,getsupreg(GetDefaultZeroReg));
|
|
|
|
regs:=regs+[getsupreg(GetDefaultTmpReg)];
|
|
|
|
if current_settings.cputype=cpu_avr1 then
|
|
message1(cg_w_interrupt_does_not_save_registers,current_procinfo.procdef.fullprocname(false))
|
|
else
|
|
begin
|
|
for reg:=RS_R31 downto RS_R0 do
|
|
if reg in regs then
|
|
list.concat(taicpu.op_reg(A_PUSH,newreg(R_INTREGISTER,reg,R_SUBWHOLE)));
|
|
{ Save SREG }
|
|
cg.getcpuregister(list,GetDefaultTmpReg);
|
|
list.concat(taicpu.op_reg_const(A_IN, GetDefaultTmpReg, $3F));
|
|
list.concat(taicpu.op_reg(A_PUSH, GetDefaultTmpReg));
|
|
cg.ungetcpuregister(list,GetDefaultTmpReg);
|
|
end;
|
|
|
|
|
|
list.concat(taicpu.op_reg(A_CLR,GetDefaultZeroReg));
|
|
|
|
if current_procinfo.framepointer<>NR_NO then
|
|
begin
|
|
cg.getcpuregister(list,NR_R28);
|
|
list.concat(taicpu.op_reg_const(A_IN,NR_R28,NIO_SP_LO));
|
|
cg.getcpuregister(list,NR_R29);
|
|
list.concat(taicpu.op_reg_const(A_IN,NR_R29,NIO_SP_HI));
|
|
a_adjust_sp(list,-localsize);
|
|
end;
|
|
end
|
|
else if not(nostackframe) then
|
|
begin
|
|
{ check if the framepointer is actually used, this is done here because
|
|
we have to know the size of the locals (must be 0), avr does not know
|
|
an sp based stack }
|
|
|
|
if not(current_procinfo.procdef.stack_tainting_parameter(calleeside)) and
|
|
(localsize=0) then
|
|
current_procinfo.framepointer:=NR_NO;
|
|
|
|
{ save int registers,
|
|
but only if the procedure returns }
|
|
if not(po_noreturn in current_procinfo.procdef.procoptions) then
|
|
regs:=rg[R_INTREGISTER].used_in_proc-paramanager.get_volatile_registers_int(pocall_stdcall)
|
|
else
|
|
regs:=[];
|
|
{ if the framepointer is potentially used, save it always because we need a proper stack frame,
|
|
even if the procedure never returns, the procedure could be e.g. a nested one accessing
|
|
an outer stackframe }
|
|
if current_procinfo.framepointer<>NR_NO then
|
|
regs:=regs+[RS_R28,RS_R29];
|
|
|
|
for reg:=RS_R31 downto RS_R0 do
|
|
if reg in regs then
|
|
list.concat(taicpu.op_reg(A_PUSH,newreg(R_INTREGISTER,reg,R_SUBWHOLE)));
|
|
|
|
if current_procinfo.framepointer<>NR_NO then
|
|
begin
|
|
cg.getcpuregister(list,NR_R28);
|
|
list.concat(taicpu.op_reg_const(A_IN,NR_R28,NIO_SP_LO));
|
|
cg.getcpuregister(list,NR_R29);
|
|
list.concat(taicpu.op_reg_const(A_IN,NR_R29,NIO_SP_HI));
|
|
a_adjust_sp(list,-localsize);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tcgavr.g_proc_exit(list : TAsmList;parasize : longint;nostackframe:boolean);
|
|
var
|
|
regs : tcpuregisterset;
|
|
reg : TSuperRegister;
|
|
LocalSize : longint;
|
|
begin
|
|
{ every byte counts for avr, so if a subroutine is marked as non-returning, we do
|
|
not generate any exit code, so we really trust the noreturn directive
|
|
}
|
|
if po_noreturn in current_procinfo.procdef.procoptions then
|
|
exit;
|
|
if po_interrupt in current_procinfo.procdef.procoptions then
|
|
begin
|
|
if not(current_procinfo.procdef.isempty) and
|
|
(not nostackframe) then
|
|
begin
|
|
regs:=rg[R_INTREGISTER].used_in_proc;
|
|
if current_procinfo.framepointer<>NR_NO then
|
|
begin
|
|
regs:=regs+[RS_R28,RS_R29];
|
|
LocalSize:=current_procinfo.calc_stackframe_size;
|
|
a_adjust_sp(list,LocalSize);
|
|
end;
|
|
|
|
{ we clear r1 }
|
|
include(regs,getsupreg(GetDefaultZeroReg));
|
|
|
|
if current_settings.cputype<>cpu_avr1 then
|
|
begin
|
|
{ Reload SREG }
|
|
regs:=regs+[getsupreg(GetDefaultTmpReg)];
|
|
|
|
|
|
cg.getcpuregister(list,GetDefaultTmpReg);
|
|
list.concat(taicpu.op_reg(A_POP, GetDefaultTmpReg));
|
|
list.concat(taicpu.op_const_reg(A_OUT, $3F, GetDefaultTmpReg));
|
|
cg.ungetcpuregister(list,GetDefaultTmpReg);
|
|
|
|
for reg:=RS_R0 to RS_R31 do
|
|
if reg in regs then
|
|
list.concat(taicpu.op_reg(A_POP,newreg(R_INTREGISTER,reg,R_SUBWHOLE)));
|
|
end;
|
|
end;
|
|
list.concat(taicpu.op_none(A_RETI));
|
|
end
|
|
else if not(nostackframe) and not(current_procinfo.procdef.isempty) then
|
|
begin
|
|
regs:=rg[R_INTREGISTER].used_in_proc-paramanager.get_volatile_registers_int(pocall_stdcall);
|
|
if current_procinfo.framepointer<>NR_NO then
|
|
begin
|
|
regs:=regs+[RS_R28,RS_R29];
|
|
LocalSize:=current_procinfo.calc_stackframe_size;
|
|
a_adjust_sp(list,LocalSize);
|
|
end;
|
|
for reg:=RS_R0 to RS_R31 do
|
|
if reg in regs then
|
|
list.concat(taicpu.op_reg(A_POP,newreg(R_INTREGISTER,reg,R_SUBWHOLE)));
|
|
list.concat(taicpu.op_none(A_RET));
|
|
end
|
|
else
|
|
list.concat(taicpu.op_none(A_RET));
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_loadaddr_ref_reg(list : TAsmList;const ref : treference;r : tregister);
|
|
var
|
|
tmpref : treference;
|
|
begin
|
|
if ref.addressmode<>AM_UNCHANGED then
|
|
internalerror(2011021706);
|
|
|
|
if assigned(ref.symbol) or (ref.offset<>0) or
|
|
{ If no other reference information it must imply an absolute reference to address 0 }
|
|
((ref.index=NR_NO) and (ref.base=NR_NO)) then
|
|
begin
|
|
reference_reset(tmpref,0,[]);
|
|
tmpref.symbol:=ref.symbol;
|
|
tmpref.offset:=ref.offset;
|
|
|
|
if assigned(ref.symbol) and (ref.symbol.typ in [AT_FUNCTION,AT_LABEL]) then
|
|
tmpref.refaddr:=addr_lo8_gs
|
|
else
|
|
tmpref.refaddr:=addr_lo8;
|
|
list.concat(taicpu.op_reg_ref(A_LDI,r,tmpref));
|
|
|
|
if assigned(ref.symbol) and (ref.symbol.typ in [AT_FUNCTION,AT_LABEL]) then
|
|
tmpref.refaddr:=addr_hi8_gs
|
|
else
|
|
tmpref.refaddr:=addr_hi8;
|
|
list.concat(taicpu.op_reg_ref(A_LDI,GetNextReg(r),tmpref));
|
|
|
|
if (ref.base<>NR_NO) then
|
|
begin
|
|
cg.a_reg_alloc(list, NR_DEFAULTFLAGS);
|
|
list.concat(taicpu.op_reg_reg(A_ADD,r,ref.base));
|
|
list.concat(taicpu.op_reg_reg(A_ADC,GetNextReg(r),GetNextReg(ref.base)));
|
|
cg.a_reg_dealloc(list, NR_DEFAULTFLAGS);
|
|
end;
|
|
if (ref.index<>NR_NO) then
|
|
begin
|
|
cg.a_reg_alloc(list, NR_DEFAULTFLAGS);
|
|
list.concat(taicpu.op_reg_reg(A_ADD,r,ref.index));
|
|
list.concat(taicpu.op_reg_reg(A_ADC,GetNextReg(r),GetNextReg(ref.index)));
|
|
cg.a_reg_dealloc(list, NR_DEFAULTFLAGS);
|
|
end;
|
|
end
|
|
else if (ref.base<>NR_NO)then
|
|
begin
|
|
emit_mov(list,r,ref.base);
|
|
emit_mov(list,GetNextReg(r),GetNextReg(ref.base));
|
|
if (ref.index<>NR_NO) then
|
|
begin
|
|
cg.a_reg_alloc(list, NR_DEFAULTFLAGS);
|
|
list.concat(taicpu.op_reg_reg(A_ADD,r,ref.index));
|
|
list.concat(taicpu.op_reg_reg(A_ADC,GetNextReg(r),GetNextReg(ref.index)));
|
|
cg.a_reg_dealloc(list, NR_DEFAULTFLAGS);
|
|
end;
|
|
end
|
|
else if (ref.index<>NR_NO) then
|
|
begin
|
|
emit_mov(list,r,ref.index);
|
|
emit_mov(list,GetNextReg(r),GetNextReg(ref.index));
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tcgavr.fixref(list : TAsmList;var ref : treference);
|
|
begin
|
|
internalerror(2011021320);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.g_concatcopy(list : TAsmList;const source,dest : treference;len : tcgint);
|
|
var
|
|
countreg,tmpreg,tmpreg2: tregister;
|
|
srcref,dstref : treference;
|
|
countregsize : tcgsize;
|
|
l : TAsmLabel;
|
|
i : longint;
|
|
SrcQuickRef, DestQuickRef : Boolean;
|
|
begin
|
|
if len>16 then
|
|
begin
|
|
current_asmdata.getjumplabel(l);
|
|
|
|
reference_reset(srcref,source.alignment,source.volatility);
|
|
reference_reset(dstref,dest.alignment,source.volatility);
|
|
srcref.base:=NR_R30;
|
|
srcref.addressmode:=AM_POSTINCREMENT;
|
|
dstref.base:=NR_R26;
|
|
dstref.addressmode:=AM_POSTINCREMENT;
|
|
|
|
if len<256 then
|
|
countregsize:=OS_8
|
|
else if len<65536 then
|
|
countregsize:=OS_16
|
|
else
|
|
internalerror(2011022007);
|
|
countreg:=getintregister(list,countregsize);
|
|
a_load_const_reg(list,countregsize,len,countreg);
|
|
cg.getcpuregister(list,NR_R30);
|
|
cg.getcpuregister(list,NR_R31);
|
|
a_loadaddr_ref_reg(list,source,NR_R30);
|
|
|
|
{ only base or index register in dest? }
|
|
if ((dest.addressmode=AM_UNCHANGED) and (dest.offset=0) and not(assigned(dest.symbol))) and
|
|
((dest.base<>NR_NO) xor (dest.index<>NR_NO)) then
|
|
begin
|
|
if dest.base<>NR_NO then
|
|
tmpreg:=dest.base
|
|
else if dest.index<>NR_NO then
|
|
tmpreg:=dest.index
|
|
else
|
|
internalerror(2016112001);
|
|
end
|
|
else
|
|
begin
|
|
tmpreg:=getaddressregister(list);
|
|
a_loadaddr_ref_reg(list,dest,tmpreg);
|
|
end;
|
|
|
|
{ X is used for spilling code so we can load it
|
|
only by a push/pop sequence, this can be
|
|
optimized later on by the peephole optimizer
|
|
}
|
|
list.concat(taicpu.op_reg(A_PUSH,tmpreg));
|
|
list.concat(taicpu.op_reg(A_PUSH,GetNextReg(tmpreg)));
|
|
cg.getcpuregister(list,NR_R27);
|
|
list.concat(taicpu.op_reg(A_POP,NR_R27));
|
|
cg.getcpuregister(list,NR_R26);
|
|
list.concat(taicpu.op_reg(A_POP,NR_R26));
|
|
cg.a_label(list,l);
|
|
cg.getcpuregister(list,GetDefaultTmpReg);
|
|
list.concat(taicpu.op_reg_ref(GetLoad(srcref),GetDefaultTmpReg,srcref));
|
|
list.concat(taicpu.op_ref_reg(GetStore(dstref),dstref,GetDefaultTmpReg));
|
|
cg.ungetcpuregister(list,GetDefaultTmpReg);
|
|
cg.a_reg_alloc(list,NR_DEFAULTFLAGS);
|
|
if tcgsize2size[countregsize] = 1 then
|
|
list.concat(taicpu.op_reg(A_DEC,countreg))
|
|
else
|
|
begin
|
|
list.concat(taicpu.op_reg_const(A_SUBI,countreg,1));
|
|
list.concat(taicpu.op_reg_reg(A_SBC,GetNextReg(countreg),GetDefaultZeroReg));
|
|
end;
|
|
|
|
a_jmp_flags(list,F_NE,l);
|
|
cg.ungetcpuregister(list,NR_R26);
|
|
cg.ungetcpuregister(list,NR_R27);
|
|
cg.ungetcpuregister(list,NR_R30);
|
|
cg.ungetcpuregister(list,NR_R31);
|
|
{ keep registers alive }
|
|
a_reg_sync(list,countreg);
|
|
end
|
|
else
|
|
begin
|
|
SrcQuickRef:=false;
|
|
DestQuickRef:=false;
|
|
if ((CPUAVR_16_REGS in cpu_capabilities[current_settings.cputype]) and
|
|
not((source.Base=NR_NO) and (source.Index=NR_NO) and (source.Offset in [0..192-len]))) or
|
|
(
|
|
not((source.addressmode=AM_UNCHANGED) and
|
|
(source.symbol=nil) and
|
|
((source.base=NR_R28) or
|
|
(source.base=NR_R30)) and
|
|
(source.Index=NR_NO) and
|
|
(source.Offset in [0..64-len])) and
|
|
not((source.Base=NR_NO) and (source.Index=NR_NO))
|
|
) then
|
|
begin
|
|
cg.getcpuregister(list,NR_R30);
|
|
cg.getcpuregister(list,NR_R31);
|
|
srcref:=normalize_ref(list,source,NR_R30);
|
|
end
|
|
else
|
|
begin
|
|
SrcQuickRef:=true;
|
|
srcref:=source;
|
|
end;
|
|
|
|
if ((CPUAVR_16_REGS in cpu_capabilities[current_settings.cputype]) and
|
|
not((dest.Base=NR_NO) and (dest.Index=NR_NO) and (dest.Offset in [0..192-len]))) or
|
|
(
|
|
not((dest.addressmode=AM_UNCHANGED) and
|
|
(dest.symbol=nil) and
|
|
((dest.base=NR_R28) or
|
|
(dest.base=NR_R30)) and
|
|
(dest.Index=NR_No) and
|
|
(dest.Offset in [0..64-len])) and
|
|
not((dest.Base=NR_NO) and (dest.Index=NR_NO))
|
|
) then
|
|
begin
|
|
if not(SrcQuickRef) then
|
|
begin
|
|
{ only base or index register in dest? }
|
|
if ((dest.addressmode=AM_UNCHANGED) and (dest.offset=0) and not(assigned(dest.symbol))) and
|
|
((dest.base<>NR_NO) xor (dest.index<>NR_NO)) then
|
|
begin
|
|
if dest.base<>NR_NO then
|
|
tmpreg:=dest.base
|
|
else if dest.index<>NR_NO then
|
|
tmpreg:=dest.index
|
|
else
|
|
internalerror(2016112002);
|
|
end
|
|
else
|
|
tmpreg:=getaddressregister(list);
|
|
|
|
dstref:=normalize_ref(list,dest,tmpreg);
|
|
|
|
{ X is used for spilling code so we can load it
|
|
only by a push/pop sequence, this can be
|
|
optimized later on by the peephole optimizer
|
|
}
|
|
list.concat(taicpu.op_reg(A_PUSH,tmpreg));
|
|
list.concat(taicpu.op_reg(A_PUSH,GetNextReg(tmpreg)));
|
|
cg.getcpuregister(list,NR_R27);
|
|
list.concat(taicpu.op_reg(A_POP,NR_R27));
|
|
cg.getcpuregister(list,NR_R26);
|
|
list.concat(taicpu.op_reg(A_POP,NR_R26));
|
|
dstref.base:=NR_R26;
|
|
end
|
|
else
|
|
begin
|
|
cg.getcpuregister(list,NR_R30);
|
|
cg.getcpuregister(list,NR_R31);
|
|
dstref:=normalize_ref(list,dest,NR_R30);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
DestQuickRef:=true;
|
|
dstref:=dest;
|
|
end;
|
|
|
|
{ If dest is an ioreg and size = 16 bit then
|
|
write high byte first, then low byte
|
|
but not for avrxmega3 }
|
|
if (len = 2) and DestQuickRef and (current_settings.cputype <> cpu_avrxmega3) and
|
|
addr_is_io_register(dest.offset) then
|
|
begin
|
|
// If src is also a 16 bit ioreg then read low byte then high byte
|
|
if SrcQuickRef and addr_is_io_register(srcref.offset) then
|
|
begin
|
|
// First read source into temp registers
|
|
tmpreg:=getintregister(list, OS_16);
|
|
list.concat(taicpu.op_reg_ref(GetLoad(srcref),tmpreg,srcref));
|
|
inc(srcref.offset);
|
|
tmpreg2:=GetNextReg(tmpreg);
|
|
list.concat(taicpu.op_reg_ref(GetLoad(srcref),tmpreg2,srcref));
|
|
|
|
// then move temp registers to dest in reverse order
|
|
inc(dstref.offset);
|
|
list.concat(taicpu.op_ref_reg(GetStore(dstref),dstref,tmpreg2));
|
|
dec(dstref.offset);
|
|
list.concat(taicpu.op_ref_reg(GetStore(dstref),dstref,tmpreg));
|
|
end
|
|
else
|
|
begin
|
|
{ avrtiny doesn't have LDD instruction, so use
|
|
predecrement version of LD with pre-incremented pointer }
|
|
if current_settings.cputype = cpu_avrtiny then
|
|
begin
|
|
srcref.addressmode:=AM_PREDECREMENT;
|
|
list.concat(taicpu.op_reg_const(A_SUBI,srcref.base,-2));
|
|
list.concat(taicpu.op_reg_const(A_SBCI,GetNextReg(srcref.base),$FF));
|
|
end
|
|
else
|
|
begin
|
|
srcref.addressmode:=AM_UNCHANGED;
|
|
inc(srcref.offset);
|
|
end;
|
|
|
|
dstref.addressmode:=AM_UNCHANGED;
|
|
inc(dstref.offset);
|
|
|
|
cg.getcpuregister(list,GetDefaultTmpReg);
|
|
list.concat(taicpu.op_reg_ref(GetLoad(srcref),GetDefaultTmpReg,srcref));
|
|
list.concat(taicpu.op_ref_reg(GetStore(dstref),dstref,GetDefaultTmpReg));
|
|
cg.ungetcpuregister(list,GetDefaultTmpReg);
|
|
|
|
if not(SrcQuickRef) and (current_settings.cputype <> cpu_avrtiny) then
|
|
srcref.addressmode:=AM_POSTINCREMENT
|
|
else if current_settings.cputype = cpu_avrtiny then
|
|
srcref.addressmode:=AM_PREDECREMENT
|
|
else
|
|
srcref.addressmode:=AM_UNCHANGED;
|
|
|
|
if current_settings.cputype <> cpu_avrtiny then
|
|
dec(srcref.offset);
|
|
dec(dstref.offset);
|
|
|
|
cg.getcpuregister(list,GetDefaultTmpReg);
|
|
list.concat(taicpu.op_reg_ref(GetLoad(srcref),GetDefaultTmpReg,srcref));
|
|
list.concat(taicpu.op_ref_reg(GetStore(dstref),dstref,GetDefaultTmpReg));
|
|
cg.ungetcpuregister(list,GetDefaultTmpReg);
|
|
end;
|
|
end
|
|
else
|
|
for i:=1 to len do
|
|
begin
|
|
if not(SrcQuickRef) and (i<len) then
|
|
srcref.addressmode:=AM_POSTINCREMENT
|
|
else
|
|
srcref.addressmode:=AM_UNCHANGED;
|
|
|
|
if not(DestQuickRef) and (i<len) then
|
|
dstref.addressmode:=AM_POSTINCREMENT
|
|
else
|
|
dstref.addressmode:=AM_UNCHANGED;
|
|
|
|
cg.getcpuregister(list,GetDefaultTmpReg);
|
|
list.concat(taicpu.op_reg_ref(GetLoad(srcref),GetDefaultTmpReg,srcref));
|
|
list.concat(taicpu.op_ref_reg(GetStore(dstref),dstref,GetDefaultTmpReg));
|
|
cg.ungetcpuregister(list,GetDefaultTmpReg);
|
|
|
|
if SrcQuickRef then
|
|
inc(srcref.offset);
|
|
if DestQuickRef then
|
|
inc(dstref.offset);
|
|
end;
|
|
|
|
if not(SrcQuickRef) then
|
|
begin
|
|
ungetcpuregister(list,srcref.base);
|
|
ungetcpuregister(list,TRegister(ord(srcref.base)+1));
|
|
end;
|
|
if not(DestQuickRef) then
|
|
begin
|
|
ungetcpuregister(list,dstref.base);
|
|
ungetcpuregister(list,TRegister(ord(dstref.base)+1));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tcgavr.g_overflowcheck(list: TAsmList; const l: tlocation; def: tdef);
|
|
var
|
|
hl : tasmlabel;
|
|
ai : taicpu;
|
|
cond : TAsmCond;
|
|
begin
|
|
if not(cs_check_overflow in current_settings.localswitches) then
|
|
exit;
|
|
current_asmdata.getjumplabel(hl);
|
|
if not ((def.typ=pointerdef) or
|
|
((def.typ=orddef) and
|
|
(torddef(def).ordtype in [u64bit,u16bit,u32bit,u8bit,uchar,
|
|
pasbool1,pasbool8,pasbool16,pasbool32,pasbool64]))) then
|
|
cond:=C_VC
|
|
else
|
|
cond:=C_CC;
|
|
ai:=Taicpu.Op_Sym(A_BRxx,hl);
|
|
ai.SetCondition(cond);
|
|
ai.is_jmp:=true;
|
|
list.concat(ai);
|
|
|
|
a_call_name(list,'FPC_OVERFLOW',false);
|
|
a_label(list,hl);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.g_overflowCheck_loc(List: TAsmList; const Loc: TLocation; def: TDef; ovloc: tlocation);
|
|
var
|
|
hl : tasmlabel;
|
|
ai : taicpu;
|
|
cond : TAsmCond;
|
|
begin
|
|
if not(cs_check_overflow in current_settings.localswitches) then
|
|
exit;
|
|
|
|
case ovloc.loc of
|
|
LOC_FLAGS:
|
|
begin
|
|
current_asmdata.getjumplabel(hl);
|
|
if not ((def.typ=pointerdef) or
|
|
((def.typ=orddef) and
|
|
(torddef(def).ordtype in [u64bit,u16bit,u32bit,u8bit,uchar,
|
|
pasbool1,pasbool8,pasbool16,pasbool32,pasbool64]))) then
|
|
cond:=C_VC
|
|
else
|
|
cond:=C_CC;
|
|
ai:=Taicpu.Op_Sym(A_BRxx,hl);
|
|
ai.SetCondition(cond);
|
|
ai.is_jmp:=true;
|
|
list.concat(ai);
|
|
|
|
a_call_name(list,'FPC_OVERFLOW',false);
|
|
a_label(list,hl);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tcgavr.g_save_registers(list: TAsmList);
|
|
begin
|
|
{ this is done by the entry code }
|
|
end;
|
|
|
|
|
|
procedure tcgavr.g_restore_registers(list: TAsmList);
|
|
begin
|
|
{ this is done by the exit code }
|
|
end;
|
|
|
|
|
|
procedure tcgavr.a_jmp_cond(list : TAsmList;cond : TOpCmp;l: tasmlabel);
|
|
var
|
|
ai1,ai2 : taicpu;
|
|
hl : TAsmLabel;
|
|
begin
|
|
ai1:=Taicpu.Op_sym(A_BRxx,l);
|
|
ai1.is_jmp:=true;
|
|
hl:=nil;
|
|
case cond of
|
|
OC_EQ:
|
|
ai1.SetCondition(C_EQ);
|
|
OC_GT:
|
|
begin
|
|
{ emulate GT }
|
|
current_asmdata.getjumplabel(hl);
|
|
ai2:=Taicpu.Op_Sym(A_BRxx,hl);
|
|
ai2.SetCondition(C_EQ);
|
|
ai2.is_jmp:=true;
|
|
list.concat(ai2);
|
|
|
|
ai1.SetCondition(C_GE);
|
|
end;
|
|
OC_LT:
|
|
ai1.SetCondition(C_LT);
|
|
OC_GTE:
|
|
ai1.SetCondition(C_GE);
|
|
OC_LTE:
|
|
begin
|
|
{ emulate LTE }
|
|
ai2:=Taicpu.Op_Sym(A_BRxx,l);
|
|
ai2.SetCondition(C_EQ);
|
|
ai2.is_jmp:=true;
|
|
list.concat(ai2);
|
|
|
|
ai1.SetCondition(C_LT);
|
|
end;
|
|
OC_NE:
|
|
ai1.SetCondition(C_NE);
|
|
OC_BE:
|
|
begin
|
|
{ emulate BE }
|
|
ai2:=Taicpu.Op_Sym(A_BRxx,l);
|
|
ai2.SetCondition(C_EQ);
|
|
ai2.is_jmp:=true;
|
|
list.concat(ai2);
|
|
|
|
ai1.SetCondition(C_LO);
|
|
end;
|
|
OC_B:
|
|
ai1.SetCondition(C_LO);
|
|
OC_AE:
|
|
ai1.SetCondition(C_SH);
|
|
OC_A:
|
|
begin
|
|
{ emulate A (unsigned GT) }
|
|
current_asmdata.getjumplabel(hl);
|
|
ai2:=Taicpu.Op_Sym(A_BRxx,hl);
|
|
ai2.SetCondition(C_EQ);
|
|
ai2.is_jmp:=true;
|
|
list.concat(ai2);
|
|
|
|
ai1.SetCondition(C_SH);
|
|
end;
|
|
else
|
|
internalerror(2011082501);
|
|
end;
|
|
list.concat(ai1);
|
|
if assigned(hl) then
|
|
a_label(list,hl);
|
|
end;
|
|
|
|
|
|
procedure tcgavr.emit_mov(list: TAsmList;reg2: tregister; reg1: tregister);
|
|
var
|
|
instr: taicpu;
|
|
begin
|
|
instr:=taicpu.op_reg_reg(A_MOV,reg2,reg1);
|
|
list.Concat(instr);
|
|
{ Notify the register allocator that we have written a move instruction so
|
|
it can try to eliminate it. }
|
|
add_move_instruction(instr);
|
|
end;
|
|
|
|
|
|
procedure tcg64favr.a_op64_reg_reg(list : TAsmList;op:TOpCG;size : tcgsize;regsrc,regdst : tregister64);
|
|
begin
|
|
if not(size in [OS_S64,OS_64]) then
|
|
internalerror(2012102402);
|
|
tcgavr(cg).a_op_reg_reg_internal(list,Op,size,regsrc.reglo,regsrc.reghi,regdst.reglo,regdst.reghi);
|
|
end;
|
|
|
|
|
|
procedure tcg64favr.a_op64_const_reg(list : TAsmList;op:TOpCG;size : tcgsize;value : int64;reg : tregister64);
|
|
begin
|
|
tcgavr(cg).a_op_const_reg_internal(list,Op,size,value,reg.reglo,reg.reghi);
|
|
end;
|
|
|
|
|
|
procedure tcg64favr.a_op64_const_reg_reg(list: TAsmList; op: TOpCg;size: tcgsize;value: int64;src,dst : tregister64);
|
|
begin
|
|
if op in [OP_SHL,OP_SHR] then
|
|
tcgavr(cg).a_op_const_reg_reg_internal(list,Op,size,value,src.reglo,src.reghi,dst.reglo,dst.reghi)
|
|
else
|
|
Inherited a_op64_const_reg_reg(list,op,size,value,src,dst);
|
|
end;
|
|
|
|
|
|
procedure create_codegen;
|
|
begin
|
|
cg:=tcgavr.create;
|
|
cg64:=tcg64favr.create;
|
|
end;
|
|
|
|
end.
|