fpc/compiler/riscv/cgrv.pas
2025-03-25 22:40:26 +01:00

1095 lines
37 KiB
ObjectPascal

{
Copyright (c) 2006 by Florian Klaempfl
This unit implements the common part of the code generator for the Risc-V
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 cgrv;
{$i fpcdefs.inc}
interface
uses
globtype,symtype,symdef,
cgbase,cgobj,
aasmbase,aasmcpu,aasmtai,aasmdata,
cpubase,cpuinfo,cgutils,rgcpu,
parabase;
type
tcgrv = class(tcg)
procedure a_loadaddr_ref_cgpara(list : TAsmList;const r : treference;const paraloc : tcgpara); override;
procedure a_bit_scan_reg_reg(list: TAsmList; reverse,not_zero: boolean; srcsize, dstsize: tcgsize; src, dst: TRegister); override;
procedure a_call_reg(list : TAsmList;reg: tregister); override;
procedure a_call_name(list : TAsmList;const s : string; weak: boolean); override;
procedure a_load_const_ref(list: TAsmList; size: tcgsize; a: tcgint; const ref: treference); 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_const_reg(list: TAsmList; size: tcgsize; a: tcgint; register: 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_reg_reg_reg(list: TAsmList; op: TOpCg; size: tcgsize; src1, src2, dst: tregister); override;
procedure a_loadaddr_ref_reg(list : TAsmList;const ref : treference;r : tregister);override;
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 g_proc_entry(list : TAsmList;localsize : longint;nostackframe:boolean);override;
procedure g_proc_exit(list : TAsmList;parasize : longint;nostackframe:boolean); override;
procedure g_save_registers(list: TAsmList); override;
procedure g_restore_registers(list: TAsmList); override;
procedure g_profilecode(list: TAsmList); 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;
procedure g_check_for_fpu_exception(list: TAsmList;force,clear : boolean); override;
protected
function fixref(list: TAsmList; var ref: treference): boolean;
procedure maybeadjustresult(list: TAsmList; op: topcg; size: tcgsize; dst: tregister);
end;
const
TOpCmp2AsmCond: Array[topcmp] of TAsmCond = (C_NONE,C_EQ,C_NONE,
C_LT,C_GE,C_None,C_NE,C_NONE,C_LTU,C_GEU,C_NONE);
const
TOpCG2AsmConstOp: Array[topcg] of TAsmOp = (A_NONE,
A_NONE,A_ADDI,A_ANDI,A_NONE,A_NONE,A_NONE,A_NONE,
A_None,A_None,A_ORI,A_SRAI,A_SLLI,A_SRLI,A_NONE,A_XORI,A_None,A_RORI);
TOpCG2AsmOp: Array[topcg] of TAsmOp = (A_NONE,
A_NONE,A_ADD,A_AND,A_DIVU,A_DIV,A_MUL,A_MUL,
A_None,A_None,A_OR,A_SRA,A_SLL,A_SRL,A_SUB,A_XOR,A_ROL,A_ROR);
{$ifdef extdebug}
function ref2string(const ref : treference) : string;
function cgop2string(const op : TOpCg) : String;
{$endif extdebug}
implementation
uses
{$ifdef extdebug}sysutils,{$endif}
globals,verbose,systems,cutils,
symconst,symsym,symtable,fmodule,
rgobj,tgobj,cpupi,procinfo,paramgr;
{$ifdef extdebug}
function ref2string(const ref : treference) : string;
begin
result := 'base : ' + inttostr(ord(ref.base)) + ' index : ' + inttostr(ord(ref.index)) + ' refaddr : ' + inttostr(ord(ref.refaddr)) + ' offset : ' + inttostr(ref.offset) + ' symbol : ';
if (assigned(ref.symbol)) then
result := result + ref.symbol.name;
end;
function cgop2string(const op : TOpCg) : String;
const
opcg_strings : array[TOpCg] of string[6] = (
'None', 'Move', 'Add', 'And', 'Div', 'IDiv', 'IMul', 'Mul',
'Neg', 'Not', 'Or', 'Sar', 'Shl', 'Shr', 'Sub', 'Xor', 'Rol', 'Ror'
);
begin
result := opcg_strings[op];
end;
{$endif extdebug}
procedure tcgrv.a_call_name(list : TAsmList;const s : string; weak: boolean);
var
href: treference;
l: TAsmLabel;
ai: taicpu;
begin
if not(weak) then
reference_reset_symbol(href,current_asmdata.RefAsmSymbol(s,AT_FUNCTION),0,0,[])
else
reference_reset_symbol(href,current_asmdata.WeakRefAsmSymbol(s,AT_FUNCTION),0,0,[]);
if cs_create_pic in current_settings.moduleswitches then
begin
href.refaddr:=addr_plt;
list.concat(taicpu.op_ref(A_CALL,href));
end
else
begin
current_asmdata.getjumplabel(l);
a_label(list,l);
href.refaddr:=addr_pcrel_hi20;
list.concat(taicpu.op_reg_ref(A_AUIPC,NR_RETURN_ADDRESS_REG,href));
reference_reset_symbol(href,l,0,0,[]);
href.refaddr:=addr_pcrel_lo12;
ai:=taicpu.op_reg_reg_ref(A_JALR,NR_RETURN_ADDRESS_REG,NR_RETURN_ADDRESS_REG,href);
ai.is_jmp:=true;
list.concat(ai);
end;
{ not assigned while generating external wrappers }
if assigned(current_procinfo) then
include(current_procinfo.flags,pi_do_call);
end;
procedure tcgrv.a_load_const_ref(list: TAsmList; size: tcgsize; a: tcgint; const ref: treference);
begin
if a=0 then
a_load_reg_ref(list,size,size,NR_X0,ref)
else
inherited a_load_const_ref(list, size, a, ref);
end;
procedure tcgrv.a_loadaddr_ref_cgpara(list : TAsmList;const r : treference;const paraloc : tcgpara);
var
ref: treference;
tmpreg: tregister;
begin
paraloc.check_simple_location;
paramanager.allocparaloc(list,paraloc.location);
case paraloc.location^.loc of
LOC_REGISTER,LOC_CREGISTER:
a_loadaddr_ref_reg(list,r,paraloc.location^.register);
LOC_REFERENCE:
begin
reference_reset(ref,paraloc.alignment,[]);
ref.base := paraloc.location^.reference.index;
ref.offset := paraloc.location^.reference.offset;
tmpreg := rg[R_INTREGISTER].getregister(list,R_SUBWHOLE);
a_loadaddr_ref_reg(list,r,tmpreg);
a_load_reg_ref(list,OS_ADDR,OS_ADDR,tmpreg,ref);
end;
else
internalerror(2002080701);
end;
end;
procedure tcgrv.a_bit_scan_reg_reg(list: TAsmList; reverse,not_zero: boolean; srcsize, dstsize: tcgsize; src, dst: TRegister);
begin
internalerror(2016060401);
end;
procedure tcgrv.a_op_const_reg(list : TAsmList; Op: TOpCG; size: TCGSize; a: tcgint; reg: TRegister);
begin
a_op_const_reg_reg(list,op,size,a,reg,reg);
end;
procedure tcgrv.a_op_reg_reg(list : TAsmList; Op: TOpCG; size: TCGSize; src, dst: TRegister);
begin
a_op_reg_reg_reg(list,op,size,src,dst,dst);
end;
procedure tcgrv.a_op_const_reg_reg(list: TAsmList; op: TOpCg; size: tcgsize; a: tcgint; src, dst: tregister);
var
tmpreg: TRegister;
begin
optimize_op_const(size,op,a);
if op=OP_NONE then
begin
a_load_reg_reg(list,size,size,src,dst);
exit;
end;
if op=OP_SUB then
begin
op:=OP_ADD;
a:=-a;
end;
{$ifdef RISCV64}
if (op=OP_SHL) and
(size=OS_S32) then
begin
list.concat(taicpu.op_reg_reg_const(A_SLLIW,dst,src,a));
maybeadjustresult(list,op,size,dst);
end
else if (op=OP_SHR) and
(size=OS_S32) then
begin
list.concat(taicpu.op_reg_reg_const(A_SRLIW,dst,src,a));
maybeadjustresult(list,op,size,dst);
end
else if (op=OP_SAR) and
(size=OS_S32) then
begin
list.concat(taicpu.op_reg_reg_const(A_SRAIW,dst,src,a));
maybeadjustresult(list,op,size,dst);
end
else
{$endif RISCV64}
if (TOpCG2AsmConstOp[op]<>A_None) and
is_imm12(a) then
begin
list.concat(taicpu.op_reg_reg_const(TOpCG2AsmConstOp[op],dst,src,a));
maybeadjustresult(list,op,size,dst);
end
else
begin
tmpreg:=getintregister(list,size);
a_load_const_reg(list,size,a,tmpreg);
a_op_reg_reg_reg(list,op,size,tmpreg,src,dst);
end;
end;
procedure tcgrv.a_op_reg_reg_reg(list: TAsmList; op: TOpCg; size: tcgsize; src1, src2, dst: tregister);
var
name: String;
pd: tprocdef;
paraloc1, paraloc2: tcgpara;
begin
if op=OP_NOT then
begin
list.concat(taicpu.op_reg_reg_const(A_XORI,dst,src1,-1));
maybeadjustresult(list,op,size,dst);
end
else if op=OP_NEG then
begin
list.concat(taicpu.op_reg_reg_reg(A_SUB,dst,NR_X0,src1));
maybeadjustresult(list,op,size,dst);
end
else
case op of
OP_MOVE:
a_load_reg_reg(list,size,size,src1,dst);
else
{$ifdef RISCV64}
if (op=OP_SHL) and
(size=OS_S32) then
begin
list.concat(taicpu.op_reg_reg_reg(A_SLLW,dst,src2,src1));
maybeadjustresult(list,op,size,dst);
end
else if (op=OP_SHR) and
(size=OS_S32) then
begin
list.concat(taicpu.op_reg_reg_reg(A_SRLW,dst,src2,src1));
maybeadjustresult(list,op,size,dst);
end
else if (op=OP_SAR) and
(size=OS_S32) then
begin
list.concat(taicpu.op_reg_reg_reg(A_SRAW,dst,src2,src1));
maybeadjustresult(list,op,size,dst);
end
else if (op=OP_SUB) and
(size in [OS_32,OS_S32]) then
begin
list.concat(taicpu.op_reg_reg_reg(A_SUBW,dst,src2,src1));
maybeadjustresult(list,op,size,dst);
end
else
{$endif RISCV64}
if (op in [OP_IMUL,OP_MUL]) and ([CPURV_HAS_MUL,CPURV_HAS_ZMMUL]*cpu_capabilities[current_settings.cputype]=[]) then
begin
case size of
OS_8:
name:='fpc_mul_byte';
OS_S8:
name:='fpc_mul_shortint';
OS_16:
name:='fpc_mul_word';
OS_S16:
name:='fpc_mul_integer';
OS_32:
name:='fpc_mul_dword';
OS_S32:
name:='fpc_mul_longint';
else
Internalerror(2021030601);
end;
// 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_FUNCTION_RESULT_REG);
cg.a_load_reg_reg(list,size,size,NR_FUNCTION_RESULT_REG,dst);
cg.a_reg_dealloc(list,NR_FUNCTION_RESULT_REG);
paraloc2.done;
paraloc1.done;
end
else
begin
list.concat(taicpu.op_reg_reg_reg(TOpCG2AsmOp[op],dst,src2,src1));
maybeadjustresult(list,op,size,dst);
end;
end;
end;
procedure tcgrv.a_loadaddr_ref_reg(list : TAsmList;const ref : treference;r : tregister);
var
href: treference;
b, tmpreg: TRegister;
l: TAsmLabel;
begin
href:=ref;
fixref(list,href);
if (not assigned(href.symbol)) and
(href.offset=0) then
a_load_reg_reg(list,OS_ADDR,OS_ADDR,href.base,r)
else if (assigned(href.symbol) or
(not is_imm12(href.offset))) and
(href.base<>NR_NO) then
begin
b:= href.base;
current_asmdata.getjumplabel(l);
a_label(list,l);
href.base:=NR_NO;
href.refaddr:=addr_pcrel_hi20;
list.concat(taicpu.op_reg_ref(A_AUIPC,r,href));
reference_reset_symbol(href,l,0,0,ref.volatility);
href.refaddr:=addr_pcrel_lo12;
list.concat(taicpu.op_reg_reg_ref(A_ADDI,r,r,href));
list.concat(taicpu.op_reg_reg_reg(A_ADD,r,r,b));
end
else if is_imm12(href.offset) and
(href.base<>NR_NO) then
begin
list.concat(taicpu.op_reg_reg_const(A_ADDI,r,href.base,href.offset));
end
else if (href.refaddr=addr_pcrel) then
begin
tmpreg:=getintregister(list,OS_ADDR);
b:=href.base;
href.base:=NR_NO;
current_asmdata.getjumplabel(l);
a_label(list,l);
href.refaddr:=addr_pcrel_hi20;
list.concat(taicpu.op_reg_ref(A_AUIPC,tmpreg,href));
reference_reset_symbol(href,l,0,0,ref.volatility);
href.refaddr:=addr_pcrel_lo12;
list.concat(taicpu.op_reg_reg_ref(A_ADDI,r,tmpreg,href));
if b<>NR_NO then
list.concat(taicpu.op_reg_reg_reg(A_ADD,r,r,b));
end
else
internalerror(2016060504);
end;
procedure tcgrv.a_cmp_const_reg_label(list: TAsmList; size: tcgsize; cmp_op: topcmp; a: tcgint; reg: tregister; l: tasmlabel);
begin
if a=0 then
a_cmp_reg_reg_label(list,size,cmp_op,NR_X0,reg,l)
else
inherited;
end;
procedure tcgrv.a_cmp_reg_reg_label(list : TAsmList;size : tcgsize;cmp_op : topcmp; reg1,reg2 : tregister;l : tasmlabel);
var
tmpreg: TRegister;
ai: taicpu;
begin
if TOpCmp2AsmCond[cmp_op]=C_None then
begin
cmp_op:=swap_opcmp(cmp_op);
tmpreg:=reg1;
reg1:=reg2;
reg2:=tmpreg;
end;
ai:=taicpu.op_reg_reg_sym_ofs(A_Bxx,reg2,reg1,l,0);
ai.is_jmp:=true;
ai.condition:=TOpCmp2AsmCond[cmp_op];
list.concat(ai);
end;
procedure tcgrv.a_jmp_name(list : TAsmList;const s : string);
var
ai: taicpu;
href: treference;
tmpreg: TRegister;
l: TAsmLabel;
begin
reference_reset_symbol(href,current_asmdata.RefAsmSymbol(s,AT_FUNCTION),0,0,[]);
tmpreg:=getintregister(list,OS_ADDR);
current_asmdata.getjumplabel(l);
a_label(list,l);
href.refaddr:=addr_pcrel_hi20;
list.concat(taicpu.op_reg_ref(A_AUIPC,tmpreg,href));
reference_reset_symbol(href,l,0,0,[]);
href.refaddr:=addr_pcrel_lo12;
ai:=taicpu.op_reg_reg_ref(A_JALR,NR_X0,tmpreg,href);
ai.is_jmp:=true;
list.concat(ai);
//ai:=taicpu.op_reg_sym(A_JAL,NR_X0,current_asmdata.RefAsmSymbol(s));
//ai.is_jmp:=true;
end;
procedure tcgrv.a_jmp_always(list : TAsmList;l: tasmlabel);
var
ai: taicpu;
{href: treference;
tmpreg: TRegister;}
begin
{reference_reset_symbol(href,l,0,0);
tmpreg:=getintregister(list,OS_ADDR);
current_asmdata.getjumplabel(l);
a_label(list,l);
href.refaddr:=addr_pcrel_hi20;
list.concat(taicpu.op_reg_ref(A_AUIPC,tmpreg,href));
reference_reset_symbol(href,l,0,0);
href.refaddr:=addr_pcrel_lo12;
ai:=taicpu.op_reg_reg_ref(A_JALR,NR_X0,tmpreg,href);
ai.is_jmp:=true;
list.concat(ai);}
ai:=taicpu.op_reg_sym(A_JAL,NR_X0,l);
ai.is_jmp:=true;
list.concat(ai);
end;
procedure tcgrv.g_proc_entry(list: TAsmList; localsize: longint; nostackframe: boolean);
const
{$ifdef cpu64bitalu}
store_int_op = A_SD;
{$else cpu64bitalu}
store_int_op = A_SW;
{$endif cpu64bitalu}
var
regs, fregs: tcpuregisterset;
r: TSuperRegister;
href: treference;
stackcount, stackAdjust: longint;
begin
if not(nostackframe) then
begin
a_reg_alloc(list,NR_STACK_POINTER_REG);
if current_procinfo.framepointer<>NR_STACK_POINTER_REG then
a_reg_alloc(list,NR_FRAME_POINTER_REG);
{ Int registers }
regs:=rg[R_INTREGISTER].used_in_proc-paramanager.get_volatile_registers_int(pocall_stdcall);
if current_procinfo.framepointer<>NR_STACK_POINTER_REG then
regs:=regs+[RS_FRAME_POINTER_REG,RS_RETURN_ADDRESS_REG];
if (pi_do_call in current_procinfo.flags) then
regs:=regs+[RS_RETURN_ADDRESS_REG];
stackcount:=0;
for r:=RS_X0 to RS_X31 do
if r in regs then
inc(stackcount,sizeof(pint));
{ Float registers }
fregs:=rg[R_FPUREGISTER].used_in_proc-paramanager.get_volatile_registers_fpu(pocall_stdcall);
for r:=RS_F0 to RS_F31 do
if r in fregs then
inc(stackcount,8);
inc(localsize,stackcount);
if not is_imm12(-localsize) then
begin
if not (RS_RETURN_ADDRESS_REG in regs) then
begin
include(regs,RS_RETURN_ADDRESS_REG);
inc(localsize,sizeof(pint));
end;
end;
reference_reset_base(href,NR_STACK_POINTER_REG,stackcount,ctempposinvalid,0,[]);
stackAdjust:=0;
if stackcount>0 then
begin
list.concat(taicpu.op_reg_reg_const(A_ADDI,NR_STACK_POINTER_REG,NR_STACK_POINTER_REG,-stackcount));
stackAdjust:=stackcount;
dec(localsize,stackcount);
end;
for r:=RS_X0 to RS_X31 do
if r in regs then
begin
dec(href.offset,sizeof(pint));
list.concat(taicpu.op_reg_ref(store_int_op,newreg(R_INTREGISTER,r,R_SUBWHOLE),href));
end;
{ Float registers }
for r:=RS_F0 to RS_F31 do
if r in fregs then
begin
dec(href.offset,8);
list.concat(taicpu.op_reg_ref(A_FSD,newreg(R_FPUREGISTER,r,R_SUBWHOLE),href));
end;
if current_procinfo.framepointer<>NR_STACK_POINTER_REG then
list.concat(taicpu.op_reg_reg_const(A_ADDI,NR_FRAME_POINTER_REG,NR_STACK_POINTER_REG,stackAdjust));
if localsize>0 then
begin
localsize:=align(localsize,sizeof(pint));
if is_imm12(-localsize) then
list.concat(taicpu.op_reg_reg_const(A_ADDI,NR_STACK_POINTER_REG,NR_STACK_POINTER_REG,-localsize))
else
begin
a_load_const_reg(list,OS_INT,localsize,NR_RETURN_ADDRESS_REG);
list.concat(taicpu.op_reg_reg_reg(A_SUB,NR_STACK_POINTER_REG,NR_STACK_POINTER_REG,NR_RETURN_ADDRESS_REG));
end;
end;
end;
end;
procedure tcgrv.g_proc_exit(list: TAsmList; parasize: longint; nostackframe: boolean);
const
{$ifdef cpu64bitalu}
load_op = A_LD;
{$else cpu64bitalu}
load_op = A_LW;
{$endif cpu64bitalu}
var
r: tsuperregister;
regs, fregs: tcpuregisterset;
stacksize, localsize, precompensation, postcompensation: longint;
href: treference;
begin
if not(nostackframe) then
begin
regs:=rg[R_INTREGISTER].used_in_proc-paramanager.get_volatile_registers_int(pocall_stdcall);
if current_procinfo.framepointer<>NR_STACK_POINTER_REG then
regs:=regs+[RS_FRAME_POINTER_REG,RS_RETURN_ADDRESS_REG];
if (pi_do_call in current_procinfo.flags) then
regs:=regs+[RS_RETURN_ADDRESS_REG];
stacksize:=0;
for r:=RS_X31 downto RS_X0 do
if r in regs then
inc(stacksize,sizeof(pint));
{ Float registers }
fregs:=rg[R_FPUREGISTER].used_in_proc-paramanager.get_volatile_registers_fpu(pocall_stdcall);
for r:=RS_F0 to RS_F31 do
if r in fregs then
inc(stacksize,8);
localsize:=current_procinfo.calc_stackframe_size+stacksize;
if localsize>0 then
begin
localsize:=align(localsize,sizeof(pint));
if not is_imm12(-localsize) then
begin
if not (RS_RETURN_ADDRESS_REG in regs) then
begin
include(regs,RS_RETURN_ADDRESS_REG);
inc(localsize,sizeof(pint));
inc(stacksize,sizeof(pint));
end;
end;
end;
if not is_imm12(localsize) then
begin
precompensation:=localsize-2032;
postcompensation:=localsize-precompensation;
end
else
begin
precompensation:=0;
postcompensation:=localsize;
end;
reference_reset_base(href,NR_STACK_POINTER_REG,postcompensation-stacksize,ctempposinvalid,0,[]);
if precompensation>0 then
begin
if is_imm12(precompensation) then
list.concat(taicpu.op_reg_reg_const(A_ADDI,NR_STACK_POINTER_REG,NR_STACK_POINTER_REG,precompensation))
else
begin
{ use X12 as temporary register as it is not callee-saved }
a_load_const_reg(list,OS_INT,precompensation,NR_X12);
list.concat(taicpu.op_reg_reg_reg(A_ADD,NR_STACK_POINTER_REG,NR_STACK_POINTER_REG,NR_X12));
end;
end;
{ Float registers }
for r:=RS_F31 downto RS_F0 do
if r in fregs then
begin
list.concat(taicpu.op_reg_ref(A_FLD,newreg(R_FPUREGISTER,r,R_SUBWHOLE),href));
inc(href.offset,8);
end;
for r:=RS_X31 downto RS_X0 do
if r in regs then
begin
list.concat(taicpu.op_reg_ref(load_op,newreg(R_INTREGISTER,r,R_SUBWHOLE),href));
inc(href.offset,sizeof(pint));
end;
if postcompensation>0 then
list.concat(taicpu.op_reg_reg_const(A_ADDI,NR_STACK_POINTER_REG,NR_STACK_POINTER_REG,postcompensation));
end;
if (target_info.system in (systems_freertos+systems_embedded)) and (po_interrupt in current_procinfo.procdef.procoptions) then
begin
list.concat(Taicpu.Op_none(A_MRET));
end
else
list.concat(taicpu.op_reg_reg(A_JALR,NR_X0,NR_RETURN_ADDRESS_REG));
end;
procedure tcgrv.g_save_registers(list: TAsmList);
begin
end;
procedure tcgrv.g_restore_registers(list: TAsmList);
begin
end;
procedure tcgrv.g_profilecode(list: TAsmList);
begin
if target_info.system in [system_riscv32_linux,system_riscv64_linux] then
begin
list.concat(taicpu.op_reg_reg_const(A_ADDI,NR_X10,NR_RETURN_ADDRESS_REG,0));
a_call_name(list,'_mcount',false);
end
else
internalerror(2018092201);
end;
procedure tcgrv.a_call_reg(list : TAsmList;reg: tregister);
begin
list.concat(taicpu.op_reg_reg(A_JALR,NR_RETURN_ADDRESS_REG,reg));
include(current_procinfo.flags,pi_do_call);
end;
procedure tcgrv.a_load_reg_ref(list: TAsmList; fromsize, tosize: TCGSize;
reg: tregister; const ref: treference);
const
StoreInstr: array[OS_8..OS_INT] of TAsmOp =
(A_SB,A_SH,A_SW
{$ifdef cpu64bitalu}
,
A_SD
{$endif cpu64bitalu}
);
var
ref2: TReference;
tmpreg: tregister;
op: TAsmOp;
begin
if not (fromsize in [OS_8..OS_INT,OS_S8..OS_SINT]) then
internalerror(2002090904);
if not (tosize in [OS_8..OS_INT,OS_S8..OS_SINT]) then
internalerror(2002090905);
tosize:=tcgsize2unsigned[tosize];
ref2 := ref;
fixref(list, ref2);
op := storeinstr[tcgsize2unsigned[tosize]];
list.concat(taicpu.op_reg_ref(op, reg,ref2));
end;
procedure tcgrv.a_load_ref_reg(list: TAsmList; fromsize, tosize: tcgsize; const ref: treference; reg: tregister);
var
href: treference;
op: TAsmOp;
tmpreg: TRegister;
begin
href:=ref;
fixref(list,href);
if href.refaddr=addr_pcrel then
begin
tmpreg:=getintregister(list,OS_ADDR);
a_loadaddr_ref_reg(list,href,tmpreg);
reference_reset_base(href,tmpreg,0,ctempposinvalid,0,ref.volatility);
end;
case fromsize of
OS_8: op:=A_LBU;
OS_16: op:=A_LHU;
OS_S8: op:=A_LB;
OS_S16: op:=A_LH;
{$ifdef RISCV64}
OS_32: op:=A_LWU;
OS_S32: op:=A_LW;
OS_64,
OS_S64: op:=A_LD;
{$else}
OS_64,OS_S64, { This only happens if tosize is smaller than fromsize }
{ We can therefore only consider the low 32-bit of the 64bit value }
OS_32,
OS_S32: op:=A_LW;
{$endif}
else
internalerror(2016060502);
end;
list.concat(taicpu.op_reg_ref(op,reg,href));
if (fromsize<>tosize) and (not (tosize in [OS_SINT,OS_INT])) then
a_load_reg_reg(list,fromsize,tosize,reg,reg);
end;
procedure tcgrv.a_load_const_reg(list: TAsmList; size: tcgsize; a: tcgint; register: tregister);
begin
if a=0 then
a_load_reg_reg(list,size,size,NR_X0,register)
else
begin
if is_imm12(a) then
list.concat(taicpu.op_reg_reg_const(A_ADDI,register,NR_X0,a))
else if is_lui_imm(a) then
list.concat(taicpu.op_reg_const(A_LUI,register,(a shr 12) and $FFFFF))
else
begin
if (a and $800)<>0 then
list.concat(taicpu.op_reg_const(A_LUI,register,((a shr 12)+1) and $FFFFF))
else
list.concat(taicpu.op_reg_const(A_LUI,register,(a shr 12) and $FFFFF));
list.concat(taicpu.op_reg_reg_const(A_ADDI,register,register,SarSmallint(smallint(a shl 4),4)));
end;
end;
end;
procedure tcgrv.a_loadfpu_reg_reg(list: TAsmList; fromsize, tosize: tcgsize; reg1, reg2: tregister);
var
op: TAsmOp;
ai: taicpu;
const
convOp: array[OS_F32..OS_F64,OS_F32..OS_F64] of TAsmOp =
((A_None,A_FCVT_D_S),
(A_FCVT_S_D,A_None));
begin
if fromsize<>tosize then
begin
list.concat(taicpu.op_reg_reg(convOp[fromsize,tosize],reg2,reg1));
maybe_check_for_fpu_exception(list);
end
else
begin
if tosize=OS_F32 then
op:=A_FSGNJ_S
else
op:=A_FSGNJ_D;
ai:=taicpu.op_reg_reg_reg(op,reg2,reg1,reg1);
list.concat(ai);
rg[R_FPUREGISTER].add_move_instruction(ai);
end;
end;
procedure tcgrv.a_loadfpu_ref_reg(list: TAsmList; fromsize, tosize: tcgsize; const ref: treference; reg: tregister);
var
href: treference;
op: TAsmOp;
tmpreg: TRegister;
l: TAsmLabel;
begin
href:=ref;
{ can we use the fl* rd,symbol,rd pseudoinstruction? }
if (assigned(href.symbol) or (href.offset<>0)) then
begin
if (href.base<>NR_NO) or (href.index<>NR_NO) then
fixref(list,href)
else
href.refaddr:=addr_full;
end
else
fixref(list,href);
if fromsize=OS_F32 then
op:=A_FLW
else if fromsize=OS_F64 then
op:=A_FLD
else if fromsize=OS_F128 then
op:=A_FLQ
else
Internalerror(2025011101);
if href.refaddr in [addr_pcrel,addr_full] then
begin
tmpreg:=getintregister(list,OS_ADDR);
list.concat(taicpu.op_reg_ref_reg(op,reg,href,tmpreg));
end
else
list.concat(taicpu.op_reg_ref(op,reg,href));
if fromsize<>tosize then
a_loadfpu_reg_reg(list,fromsize,tosize,reg,reg);
end;
procedure tcgrv.a_loadfpu_reg_ref(list: TAsmList; fromsize, tosize: tcgsize; reg: tregister; const ref: treference);
var
href: treference;
op: TAsmOp;
tmpreg: TRegister;
begin
href:=ref;
fixref(list,href);
if href.refaddr=addr_pcrel then
begin
tmpreg:=getintregister(list,OS_ADDR);
a_loadaddr_ref_reg(list,href,tmpreg);
reference_reset_base(href,tmpreg,0,ctempposinvalid,0,ref.volatility);
end;
if fromsize<>tosize then
begin
tmpreg:=getfpuregister(list,tosize);
a_loadfpu_reg_reg(list,fromsize,tosize,reg,tmpreg);
reg:=tmpreg;
end;
if tosize=OS_F32 then
op:=A_FSW
else
op:=A_FSD;
list.concat(taicpu.op_reg_ref(op,reg,href));
end;
function tcgrv.fixref(list: TAsmList; var ref: treference): boolean;
var
tmpreg: TRegister;
href: treference;
l: TAsmLabel;
begin
result:=true;
if ref.refaddr=addr_pcrel then
exit;
if assigned(ref.symbol) then
begin
{$ifdef unsed}
{ keeping the code for reference
we use the pseudo instruction LA below which is expanded by the assembler, doing
so results in more readable assembler and easier optimization of the assembler code
}
if cs_create_pic in current_settings.moduleswitches then
begin
reference_reset_symbol(href,ref.symbol,0,0,[]);
ref.symbol:=nil;
tmpreg:=getintregister(list,OS_INT);
current_asmdata.getaddrlabel(l);
a_label(list,l);
href.refaddr:=addr_got_pcrel_hi;
list.concat(taicpu.op_reg_ref(A_AUIPC,tmpreg,href));
reference_reset_symbol(href,l,0,0,[]);
href.refaddr:=addr_pcrel_lo12;
href.base:=tmpreg;
{$ifdef RISCV64}
list.concat(taicpu.op_reg_ref(A_LD,tmpreg,href));
{$else}
list.concat(taicpu.op_reg_ref(A_LW,tmpreg,href));
{$endif}
end
else
begin
reference_reset_symbol(href,ref.symbol,ref.offset,ref.alignment,ref.volatility);
ref.symbol:=nil;
ref.offset:=0;
tmpreg:=getintregister(list,OS_INT);
current_asmdata.getaddrlabel(l);
a_label(list,l);
href.refaddr:=addr_pcrel_hi20;
list.concat(taicpu.op_reg_ref(A_AUIPC,tmpreg,href));
reference_reset_symbol(href,l,0,0,ref.volatility);
href.refaddr:=addr_pcrel_lo12;
list.concat(taicpu.op_reg_reg_ref(A_ADDI,tmpreg,tmpreg,href));
end;
{$endif unsed}
reference_reset_symbol(href,ref.symbol,0,0,[]);
href.refaddr:=addr_full;
ref.symbol:=nil;
tmpreg:=getintregister(list,OS_ADDR);
list.concat(taicpu.op_reg_ref(A_LA,tmpreg,href));
if (ref.index<>NR_NO) and
(ref.base<>NR_NO) then
begin
a_op_reg_reg(list,OP_ADD,OS_INT,ref.base,tmpreg);
ref.base:=tmpreg;
end
else if (ref.index=NR_NO) and
(ref.base<>NR_NO) then
ref.index:=tmpreg
else
ref.base:=tmpreg;
end
else if (ref.index=NR_NO) and
(ref.base=NR_NO) then
begin
tmpreg:=getintregister(list,OS_INT);
a_load_const_reg(list, OS_ADDR,ref.offset,tmpreg);
reference_reset_base(ref,tmpreg,0,ctempposinvalid,ref.alignment,ref.volatility);
end;
if (ref.index<>NR_NO) and
(ref.base=NR_NO) then
begin
ref.base:=ref.index;
ref.index:=NR_NO;
end;
if not is_imm12(ref.offset) then
begin
tmpreg:=getintregister(list,OS_INT);
a_load_const_reg(list,OS_INT,ref.offset,tmpreg);
ref.offset:=0;
if (ref.index<>NR_NO) and
(ref.base<>NR_NO) then
begin
a_op_reg_reg(list,OP_ADD,OS_INT,ref.index,tmpreg);
ref.index:=tmpreg;
end
else
ref.index:=tmpreg;
end;
if (ref.index<>NR_NO) and
(ref.base<>NR_NO) then
begin
tmpreg:=getaddressregister(list);
list.concat(taicpu.op_reg_reg_reg(A_ADD,tmpreg,ref.base,ref.index));
ref.base:=tmpreg;
ref.index:=NR_NO;
end;
end;
procedure tcgrv.maybeadjustresult(list: TAsmList; op: topcg; size: tcgsize; dst: tregister);
const
overflowops = [OP_MUL,OP_IMUL,OP_SHL,OP_ADD,OP_SUB,OP_NOT,OP_NEG];
begin
if (op in overflowops) and
(size in [OS_8,OS_S8,OS_16,OS_S16{$ifdef RISCV64},OS_32,OS_S32{$endif RISCV64}]) then
a_load_reg_reg(list,OS_INT,size,dst,dst)
end;
procedure tcgrv.g_check_for_fpu_exception(list: TAsmList;force,clear : boolean);
var
r : TRegister;
ai: taicpu;
l: TAsmLabel;
begin
if (CPURV_HAS_F in cpu_capabilities[current_settings.cputype]) and
needs_check_for_fpu_exceptions then
begin
r:=getintregister(list,OS_INT);
list.concat(taicpu.op_reg(A_FRFLAGS,r));
current_asmdata.getjumplabel(l);
ai:=taicpu.op_reg_reg_sym_ofs(A_Bxx,r,NR_X0,l,0);
ai.is_jmp:=true;
ai.condition:=C_EQ;
list.concat(ai);
alloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
cg.a_call_name(current_asmdata.CurrAsmList,'FPC_THROWFPUEXCEPTION',false);
dealloccpuregisters(list,R_INTREGISTER,paramanager.get_volatile_registers_int(pocall_default));
a_label(list,l);
end;
end;
end.