mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-12-11 10:30:40 +01:00
+ support for nested procedural variables:
o activate using {$modeswitch nestedprocvars} (compatible with all
regular syntax modes, enabled by default for MacPas mode)
o activating this mode switch changes the way the frame pointer is
passed to nested routines into the same way that Delphi uses (always
passed via the stack, and if necessary removed from the stack by
the caller) -- Todo: possibly also allow using this parameter
passing convention without enabling nested procvars, maybe even
by default in Delphi mode, see mantis #9432
o both global and nested routines can be passed to/assigned to a
nested procvar (and called via them). Note that converting global
*procvars* to nested procvars is intentionally not supported, so
that this functionality can also be implemented via compile-time
generated trampolines if necessary (e.g. for LLVM or CIL backends
as long as they don't support the aforementioned parameter passing
convention)
o a nested procvar can both be declared using a Mac/ISO Pascal style
"inline" type declaration as a parameter type, or as a stand-alone
type (in the latter case, add "is nested" at the end in analogy to
"of object" for method pointers -- note that using variables of
such a type is dangerous, because if you call them once the enclosing
stack frame no longer exists on the stack, the results are
undefined; this is however allowed for Metaware Pascal compatibility)
git-svn-id: trunk@15694 -
372 lines
14 KiB
ObjectPascal
372 lines
14 KiB
ObjectPascal
{
|
|
Copyright (c) 1998-2002 by Florian Klaempfl and David Zhang
|
|
|
|
Calling conventions for the MIPSEL
|
|
|
|
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 cpupara;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
globtype,
|
|
cclasses,
|
|
aasmtai,
|
|
cpubase,cpuinfo,
|
|
symconst,symbase,symsym,symtype,symdef,paramgr,parabase,cgbase;
|
|
|
|
type
|
|
TMIPSELParaManager=class(TParaManager)
|
|
function push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;override;
|
|
function get_volatile_registers_int(calloption : tproccalloption):TCpuRegisterSet;override;
|
|
function get_volatile_registers_fpu(calloption : tproccalloption):TCpuRegisterSet;override;
|
|
{Returns a structure giving the information on the storage of the parameter
|
|
(which must be an integer parameter)
|
|
@param(nr Parameter number of routine, starting from 1)}
|
|
procedure getintparaloc(calloption : tproccalloption; nr : longint;var cgpara : TCGPara);override;
|
|
function create_paraloc_info(p : TAbstractProcDef; side: tcallercallee):longint;override;
|
|
function create_varargs_paraloc_info(p : TAbstractProcDef; varargspara:tvarargsparalist):longint;override;
|
|
function get_funcretloc(p : tabstractprocdef; side: tcallercallee; def: tdef): tcgpara;override;
|
|
private
|
|
procedure create_funcretloc_info(p : tabstractprocdef; side: tcallercallee);
|
|
procedure create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee; paras: tparalist;
|
|
var intparareg,parasize:longint);
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
cutils,verbose,systems,
|
|
defutil,
|
|
cgutils,cgobj;
|
|
|
|
type
|
|
tparasupregs = array[0..5] of tsuperregister;
|
|
pparasupregs = ^tparasupregs;
|
|
const
|
|
paraoutsupregs : tparasupregs = (RS_R4, RS_R5, RS_R6, RS_R7, RS_R8, RS_R9);
|
|
parainsupregs : tparasupregs = (RS_R4, RS_R5, RS_R6, RS_R7, RS_R8, RS_R9);
|
|
|
|
|
|
function TMIPSELParaManager.get_volatile_registers_int(calloption : tproccalloption):TCpuRegisterSet;
|
|
begin
|
|
result:=[RS_R16..RS_R23];
|
|
end;
|
|
|
|
|
|
function tMIPSELparamanager.get_volatile_registers_fpu(calloption : tproccalloption):TCpuRegisterSet;
|
|
begin
|
|
result:=[RS_F0..RS_F31];
|
|
end;
|
|
|
|
|
|
procedure TMIPSELParaManager.GetIntParaLoc(calloption : tproccalloption; nr : longint;var cgpara : tcgpara);
|
|
var
|
|
paraloc : pcgparalocation;
|
|
begin
|
|
if nr<1 then
|
|
InternalError(2002100806);
|
|
cgpara.reset;
|
|
cgpara.size:=OS_INT;
|
|
cgpara.intsize:=tcgsize2size[OS_INT];
|
|
cgpara.alignment:=std_param_align;
|
|
paraloc:=cgpara.add_location;
|
|
with paraloc^ do
|
|
begin
|
|
{ The six first parameters are passed into registers } {MIPS first four}
|
|
dec(nr);
|
|
if nr<6 then //MIPSEL nr<6
|
|
begin
|
|
loc:=LOC_REGISTER;
|
|
register:=newreg(R_INTREGISTER,(RS_R4+nr),R_SUBWHOLE);
|
|
end
|
|
else
|
|
begin
|
|
{ The other parameters are passed on the stack }
|
|
loc:=LOC_REFERENCE;
|
|
reference.index:=NR_STACK_POINTER_REG;
|
|
reference.offset:=92+(nr-6)*4;
|
|
end;
|
|
size:=OS_INT;
|
|
end;
|
|
end;
|
|
|
|
{ true if a parameter is too large to copy and only the address is pushed }
|
|
function tMIPSELparamanager.push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;
|
|
begin
|
|
result:=false;
|
|
{ var,out always require address }
|
|
if varspez in [vs_var,vs_out] then
|
|
begin
|
|
result:=true;
|
|
exit;
|
|
end;
|
|
case def.typ of
|
|
recorddef,
|
|
arraydef,
|
|
variantdef,
|
|
formaldef :
|
|
push_addr_param:=true;
|
|
objectdef :
|
|
result:=is_object(def);
|
|
stringdef :
|
|
result:=(tstringdef(def).stringtype in [st_shortstring,st_longstring]);
|
|
procvardef :
|
|
result:=not tprocvardef(def).is_addressonly;
|
|
setdef :
|
|
result:=not(is_smallset(def));
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure tMIPSELparamanager.create_funcretloc_info(p : tabstractprocdef; side: tcallercallee);
|
|
begin
|
|
p.funcretloc[side]:=get_funcretloc(p,side,p.returndef);
|
|
end;
|
|
|
|
|
|
function tMIPSELparamanager.get_funcretloc(p : tabstractprocdef; side: tcallercallee; def: tdef): tcgpara;
|
|
var
|
|
paraloc : pcgparalocation;
|
|
retcgsize : tcgsize;
|
|
begin
|
|
result.init;
|
|
result.alignment:=get_para_align(p.proccalloption);
|
|
{ void has no location }
|
|
if is_void(def) then
|
|
begin
|
|
paraloc:=result.add_location;
|
|
result.size:=OS_NO;
|
|
result.intsize:=0;
|
|
paraloc^.size:=OS_NO;
|
|
paraloc^.loc:=LOC_VOID;
|
|
exit;
|
|
end;
|
|
{ Constructors return self instead of a boolean }
|
|
if (p.proctypeoption=potype_constructor) then
|
|
begin
|
|
retcgsize:=OS_ADDR;
|
|
result.intsize:=sizeof(pint);
|
|
end
|
|
else
|
|
begin
|
|
retcgsize:=def_cgsize(def);
|
|
result.intsize:=def.size;
|
|
end;
|
|
result.size:=retcgsize;
|
|
{ Return is passed as var parameter }
|
|
if ret_in_param(def,p.proccalloption) then
|
|
begin
|
|
paraloc:=result.add_location;
|
|
paraloc^.loc:=LOC_REFERENCE;
|
|
paraloc^.size:=retcgsize;
|
|
exit;
|
|
end;
|
|
|
|
paraloc:=result.add_location;
|
|
{ Return in FPU register? }
|
|
if p.returndef.typ=floatdef then
|
|
begin
|
|
paraloc^.loc:=LOC_FPUREGISTER;
|
|
paraloc^.register:=NR_FPU_RESULT_REG;
|
|
if retcgsize=OS_F64 then
|
|
setsubreg(paraloc^.register,R_SUBFD);
|
|
paraloc^.size:=retcgsize;
|
|
end
|
|
else
|
|
{ Return in register }
|
|
begin
|
|
{$ifndef cpu64bit}
|
|
if retcgsize in [OS_64,OS_S64] then
|
|
begin
|
|
{ high }
|
|
paraloc^.loc:=LOC_REGISTER;
|
|
if side=callerside then
|
|
paraloc^.register:=NR_FUNCTION_RESULT64_HIGH_REG
|
|
else
|
|
paraloc^.register:=NR_FUNCTION_RETURN64_HIGH_REG;
|
|
paraloc^.size:=OS_32;
|
|
{ low }
|
|
paraloc:=result.add_location;
|
|
paraloc^.loc:=LOC_REGISTER;
|
|
if side=callerside then
|
|
paraloc^.register:=NR_FUNCTION_RESULT64_LOW_REG
|
|
else
|
|
paraloc^.register:=NR_FUNCTION_RETURN64_LOW_REG;
|
|
paraloc^.size:=OS_32;
|
|
end
|
|
else
|
|
{$endif cpu64bit}
|
|
begin
|
|
paraloc^.loc:=LOC_REGISTER;
|
|
paraloc^.size:=retcgsize;
|
|
if side=callerside then
|
|
paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RESULT_REG,cgsize2subreg(R_INTREGISTER,retcgsize))
|
|
else
|
|
paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RETURN_REG,cgsize2subreg(R_INTREGISTER,retcgsize));
|
|
end;
|
|
end
|
|
end;
|
|
|
|
var
|
|
param_offset:array[0..20] of ^Aint;
|
|
|
|
procedure tMIPSELparamanager.create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee;paras:tparalist;
|
|
var intparareg,parasize:longint);
|
|
var
|
|
paraloc : pcgparalocation;
|
|
i : integer;
|
|
hp : tparavarsym;
|
|
paracgsize : tcgsize;
|
|
hparasupregs : pparasupregs;
|
|
paralen : longint;
|
|
begin
|
|
if side=callerside then
|
|
hparasupregs:=@paraoutsupregs
|
|
else
|
|
hparasupregs:=@parainsupregs;
|
|
for i:=0 to paras.count-1 do
|
|
begin
|
|
|
|
param_offset[i] := Nil;
|
|
hp:=tparavarsym(paras[i]);
|
|
{ currently only support C-style array of const,
|
|
there should be no location assigned to the vararg array itself }
|
|
if (p.proccalloption in [pocall_cdecl,pocall_cppdecl]) and
|
|
is_array_of_const(hp.vardef) then
|
|
begin
|
|
paraloc:=hp.paraloc[side].add_location;
|
|
{ hack: the paraloc must be valid, but is not actually used }
|
|
paraloc^.loc:=LOC_REGISTER;
|
|
paraloc^.register:=NR_R0;
|
|
paraloc^.size:=OS_ADDR;
|
|
break;
|
|
end;
|
|
|
|
if push_addr_param(hp.varspez,hp.vardef,p.proccalloption) then
|
|
paracgsize:=OS_ADDR
|
|
else
|
|
begin
|
|
paracgsize:=def_cgSize(hp.vardef);
|
|
if paracgsize=OS_NO then
|
|
paracgsize:=OS_ADDR;
|
|
end;
|
|
hp.paraloc[side].reset;
|
|
hp.paraloc[side].size:=paracgsize;
|
|
hp.paraloc[side].Alignment:=std_param_align;
|
|
paralen:=tcgsize2size[paracgsize];
|
|
hp.paraloc[side].intsize:=paralen;
|
|
while paralen>0 do
|
|
begin
|
|
paraloc:=hp.paraloc[side].add_location;
|
|
{ Floats are passed in int registers,
|
|
We can allocate at maximum 32 bits per register }
|
|
if paracgsize in [OS_64,OS_S64,OS_F32,OS_F64] then
|
|
paraloc^.size:=OS_32
|
|
else
|
|
paraloc^.size:=paracgsize;
|
|
{ ret in param? }
|
|
if vo_is_funcret in hp.varoptions then
|
|
begin
|
|
paraloc^.loc:=LOC_REFERENCE;
|
|
if side=callerside then
|
|
begin
|
|
paraloc^.reference.index := NR_STACK_POINTER_REG;
|
|
paraloc^.reference.offset:=target_info.first_parm_offset{1000}-12 - parasize;
|
|
end
|
|
else
|
|
begin
|
|
paraloc^.reference.index := NR_FRAME_POINTER_REG;
|
|
paraloc^.reference.offset:=target_info.first_parm_offset{1000}-4 - parasize;
|
|
param_offset[i] := @paraloc^.reference.offset;
|
|
end;
|
|
inc(parasize,align(tcgsize2size[paraloc^.size],sizeof(aint)));
|
|
end
|
|
{ In case of po_delphi_nested_cc, the parent frame pointer
|
|
is always passed on the stack. }
|
|
else if (intparareg<=high(tparasupregs)) and
|
|
(not(vo_is_parentfp in hp.varoptions) or
|
|
not(po_delphi_nested_cc in p.procoptions)) then
|
|
begin
|
|
paraloc^.loc:=LOC_REGISTER;
|
|
paraloc^.register:=newreg(R_INTREGISTER,hparasupregs^[intparareg],R_SUBWHOLE);
|
|
inc(intparareg);
|
|
end
|
|
else
|
|
begin
|
|
paraloc^.loc:=LOC_REFERENCE;
|
|
if side=callerside then
|
|
begin
|
|
paraloc^.reference.index := {NR_R17;//}NR_STACK_POINTER_REG;
|
|
paraloc^.reference.offset:=target_info.first_parm_offset{1000}-12 - parasize;
|
|
end
|
|
else
|
|
begin
|
|
paraloc^.reference.index := {NR_R18;//}NR_FRAME_POINTER_REG;
|
|
paraloc^.reference.offset:=target_info.first_parm_offset{1000}-4 - parasize;
|
|
param_offset[i] := @paraloc^.reference.offset;
|
|
end;
|
|
{ Parameters are aligned at 4 bytes }
|
|
inc(parasize,align(tcgsize2size[paraloc^.size],sizeof(aint)));
|
|
end;
|
|
dec(paralen,tcgsize2size[paraloc^.size]);
|
|
end;
|
|
end;
|
|
for i:=0 to paras.count-1 do
|
|
begin
|
|
if (side = calleeside) and (param_offset[i] <> nil) then
|
|
param_offset[i]^ := param_offset[i]^ + parasize - 8;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TMIPSELParaManager.create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargsparalist):longint;
|
|
var
|
|
intparareg,
|
|
parasize : longint;
|
|
begin
|
|
intparareg:=0;
|
|
parasize:=0;
|
|
{ calculate the registers for the normal parameters }
|
|
create_paraloc_info_intern(p,callerside,p.paras,intparareg,parasize);
|
|
{ append the varargs }
|
|
create_paraloc_info_intern(p,callerside,varargspara,intparareg,parasize);
|
|
result:=parasize;
|
|
end;
|
|
|
|
|
|
|
|
function tMIPSELparamanager.create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;
|
|
var
|
|
intparareg,
|
|
parasize : longint;
|
|
begin
|
|
intparareg:=0;
|
|
parasize:=0;
|
|
create_paraloc_info_intern(p,side,p.paras,intparareg,parasize);
|
|
{ Create Function result paraloc }
|
|
create_funcretloc_info(p,side);
|
|
{ We need to return the size allocated on the stack }
|
|
result:=parasize;
|
|
end;
|
|
|
|
|
|
begin
|
|
ParaManager:=TMIPSELParaManager.create;
|
|
end.
|