fpc/compiler/avr/cgcpu.pas
2024-09-10 22:53:35 +02:00

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.