mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-07-03 09:28:17 +02:00
1085 lines
44 KiB
ObjectPascal
1085 lines
44 KiB
ObjectPascal
{
|
|
$Id$
|
|
Copyright (c) 1998-2000 by Florian Klaempfl
|
|
|
|
Helper routines for the i386 code generator
|
|
|
|
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 n386util;
|
|
|
|
{$i defines.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
symtable,node;
|
|
|
|
function maybe_push(needed : byte;p : tnode;isint64 : boolean) : boolean;
|
|
{$ifdef TEMPS_NOT_PUSH}
|
|
function maybe_savetotemp(needed : byte;p : tnode;isint64 : boolean) : boolean;
|
|
{$endif TEMPS_NOT_PUSH}
|
|
procedure restore(p : tnode;isint64 : boolean);
|
|
procedure pushsetelement(p : tnode);
|
|
procedure push_value_para(p:tnode;inlined,is_cdecl:boolean;
|
|
para_offset:longint;alignment : longint);
|
|
|
|
procedure maketojumpbool(p : tnode);
|
|
procedure emitoverflowcheck(p:tnode);
|
|
procedure emitrangecheck(p:tnode;todef:pdef);
|
|
procedure firstcomplex(p : tnode);
|
|
|
|
implementation
|
|
|
|
uses
|
|
ncon;
|
|
|
|
{*****************************************************************************
|
|
Emit Push Functions
|
|
*****************************************************************************}
|
|
|
|
function maybe_push(needed : byte;p : tnode;isint64 : boolean) : boolean;
|
|
var
|
|
pushed : boolean;
|
|
{hregister : tregister; }
|
|
{$ifdef TEMPS_NOT_PUSH}
|
|
href : treference;
|
|
{$endif TEMPS_NOT_PUSH}
|
|
begin
|
|
if needed>usablereg32 then
|
|
begin
|
|
if (p.location.loc=LOC_REGISTER) then
|
|
begin
|
|
if isint64 then
|
|
begin
|
|
{$ifdef TEMPS_NOT_PUSH}
|
|
gettempofsizereference(href,8);
|
|
p.temp_offset:=href.offset;
|
|
href.offset:=href.offset+4;
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_MOV,S_L,p.location.registerhigh,href)));
|
|
href.offset:=href.offset-4;
|
|
{$else TEMPS_NOT_PUSH}
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,p.location.registerhigh)));
|
|
{$endif TEMPS_NOT_PUSH}
|
|
ungetregister32(p.location.registerhigh);
|
|
end
|
|
{$ifdef TEMPS_NOT_PUSH}
|
|
else
|
|
begin
|
|
gettempofsizereference(href,4);
|
|
p.temp_offset:=href.offset;
|
|
end
|
|
{$endif TEMPS_NOT_PUSH}
|
|
;
|
|
pushed:=true;
|
|
{$ifdef TEMPS_NOT_PUSH}
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,p.location.register,href)));
|
|
{$else TEMPS_NOT_PUSH}
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,p.location.register)));
|
|
{$endif TEMPS_NOT_PUSH}
|
|
ungetregister32(p.location.register);
|
|
end
|
|
else if (p.location.loc in [LOC_MEM,LOC_REFERENCE]) and
|
|
((p.location.reference.base<>R_NO) or
|
|
(p.location.reference.index<>R_NO)
|
|
) then
|
|
begin
|
|
del_reference(p.location.reference);
|
|
getexplicitregister32(R_EDI);
|
|
emit_ref_reg(A_LEA,S_L,newreference(p.location.reference),R_EDI);
|
|
{$ifdef TEMPS_NOT_PUSH}
|
|
gettempofsizereference(href,4);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,href)));
|
|
p.temp_offset:=href.offset;
|
|
{$else TEMPS_NOT_PUSH}
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,R_EDI)));
|
|
{$endif TEMPS_NOT_PUSH}
|
|
ungetregister32(R_EDI);
|
|
pushed:=true;
|
|
end
|
|
else pushed:=false;
|
|
end
|
|
else pushed:=false;
|
|
maybe_push:=pushed;
|
|
end;
|
|
|
|
{$ifdef TEMPS_NOT_PUSH}
|
|
function maybe_savetotemp(needed : byte;p : tnode;isint64 : boolean) : boolean;
|
|
|
|
var
|
|
pushed : boolean;
|
|
href : treference;
|
|
|
|
begin
|
|
if needed>usablereg32 then
|
|
begin
|
|
if (p^.location.loc=LOC_REGISTER) then
|
|
begin
|
|
if isint64(p^.resulttype) then
|
|
begin
|
|
gettempofsizereference(href,8);
|
|
p^.temp_offset:=href.offset;
|
|
href.offset:=href.offset+4;
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_MOV,S_L,p^.location.registerhigh,href)));
|
|
href.offset:=href.offset-4;
|
|
ungetregister32(p^.location.registerhigh);
|
|
end
|
|
else
|
|
begin
|
|
gettempofsizereference(href,4);
|
|
p^.temp_offset:=href.offset;
|
|
end;
|
|
pushed:=true;
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,p^.location.register,href)));
|
|
ungetregister32(p^.location.register);
|
|
end
|
|
else if (p^.location.loc in [LOC_MEM,LOC_REFERENCE]) and
|
|
((p^.location.reference.base<>R_NO) or
|
|
(p^.location.reference.index<>R_NO)
|
|
) then
|
|
begin
|
|
del_reference(p^.location.reference);
|
|
getexplicitregister32(R_EDI);
|
|
emit_ref_reg(A_LEA,S_L,newreference(p^.location.reference),
|
|
R_EDI);
|
|
gettempofsizereference(href,4);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,href)));
|
|
ungetregister32(R_EDI);
|
|
p^.temp_offset:=href.offset;
|
|
pushed:=true;
|
|
end
|
|
else pushed:=false;
|
|
end
|
|
else pushed:=false;
|
|
maybe_push:=pushed;
|
|
end;
|
|
{$endif TEMPS_NOT_PUSH}
|
|
|
|
|
|
procedure restore(p : tnode;isint64 : boolean);
|
|
var
|
|
hregister : tregister;
|
|
{$ifdef TEMPS_NOT_PUSH}
|
|
href : treference;
|
|
{$endif TEMPS_NOT_PUSH}
|
|
begin
|
|
hregister:=getregister32;
|
|
{$ifdef TEMPS_NOT_PUSH}
|
|
reset_reference(href);
|
|
href.base:=procinfo^.frame_pointer;
|
|
href.offset:=p.temp_offset;
|
|
emit_ref_reg(A_MOV,S_L,href,hregister);
|
|
{$else TEMPS_NOT_PUSH}
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_POP,S_L,hregister)));
|
|
{$endif TEMPS_NOT_PUSH}
|
|
if (p.location.loc in [LOC_REGISTER,LOC_CREGISTER]) then
|
|
begin
|
|
p.location.register:=hregister;
|
|
if isint64 then
|
|
begin
|
|
p.location.registerhigh:=getregister32;
|
|
{$ifdef TEMPS_NOT_PUSH}
|
|
href.offset:=p.temp_offset+4;
|
|
emit_ref_reg(A_MOV,S_L,p.location.registerhigh);
|
|
{ set correctly for release ! }
|
|
href.offset:=p.temp_offset;
|
|
{$else TEMPS_NOT_PUSH}
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_POP,S_L,p.location.registerhigh)));
|
|
{$endif TEMPS_NOT_PUSH}
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
reset_reference(p.location.reference);
|
|
{ any reasons why this was moved into the index register ? }
|
|
{ normally usage of base register is much better (FK) }
|
|
p.location.reference.base:=hregister;
|
|
{ Why is this done? We can never be sure about p.left
|
|
because otherwise secondload fails !!!
|
|
set_location(p.left^.location,p.location);}
|
|
end;
|
|
{$ifdef TEMPS_NOT_PUSH}
|
|
ungetiftemp(href);
|
|
{$endif TEMPS_NOT_PUSH}
|
|
end;
|
|
|
|
|
|
procedure pushsetelement(p : tnode);
|
|
var
|
|
hr,hr16,hr32 : tregister;
|
|
begin
|
|
{ copy the element on the stack, slightly complicated }
|
|
if p.nodetype=ordconstn then
|
|
begin
|
|
if target_os.stackalignment=4 then
|
|
exprasmlist^.concat(new(paicpu,op_const(A_PUSH,S_L,tordconstnode(p).value)))
|
|
else
|
|
exprasmlist^.concat(new(paicpu,op_const(A_PUSH,S_W,tordconstnode(p).value)));
|
|
end
|
|
else
|
|
begin
|
|
case p.location.loc of
|
|
LOC_REGISTER,
|
|
LOC_CREGISTER :
|
|
begin
|
|
hr:=p.location.register;
|
|
case hr of
|
|
R_EAX,R_EBX,R_ECX,R_EDX,R_EDI,R_ESI,R_ESP :
|
|
begin
|
|
hr16:=reg32toreg16(hr);
|
|
hr32:=hr;
|
|
end;
|
|
R_AX,R_BX,R_CX,R_DX,R_DI,R_SI,R_SP :
|
|
begin
|
|
hr16:=hr;
|
|
hr32:=reg16toreg32(hr);
|
|
end;
|
|
R_AL,R_BL,R_CL,R_DL :
|
|
begin
|
|
hr16:=reg8toreg16(hr);
|
|
hr32:=reg8toreg32(hr);
|
|
end;
|
|
end;
|
|
if target_os.stackalignment=4 then
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,hr32)))
|
|
else
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_W,hr16)));
|
|
ungetregister32(hr32);
|
|
end;
|
|
else
|
|
begin
|
|
{ you can't push more bytes than the size of the element, }
|
|
{ because this may cross a page boundary and you'll get a }
|
|
{ sigsegv (JM) }
|
|
emit_push_mem_size(p.location.reference,1);
|
|
del_reference(p.location.reference);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure push_value_para(p:tnode;inlined,is_cdecl:boolean;
|
|
para_offset:longint;alignment : longint);
|
|
var
|
|
tempreference : treference;
|
|
r : preference;
|
|
opsize : topsize;
|
|
op : tasmop;
|
|
hreg : tregister;
|
|
size : longint;
|
|
hlabel : pasmlabel;
|
|
begin
|
|
case p.location.loc of
|
|
LOC_REGISTER,
|
|
LOC_CREGISTER:
|
|
begin
|
|
case p.location.register of
|
|
R_EAX,R_EBX,R_ECX,R_EDX,R_ESI,
|
|
R_EDI,R_ESP,R_EBP :
|
|
begin
|
|
if p.resulttype^.size=8 then
|
|
begin
|
|
inc(pushedparasize,8);
|
|
if inlined then
|
|
begin
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,
|
|
p.location.registerlow,r)));
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize+4);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,
|
|
p.location.registerhigh,r)));
|
|
end
|
|
else
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,p.location.registerhigh)));
|
|
ungetregister32(p.location.registerhigh);
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,p.location.registerlow)));
|
|
ungetregister32(p.location.registerlow);
|
|
end
|
|
else
|
|
begin
|
|
inc(pushedparasize,4);
|
|
if inlined then
|
|
begin
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,
|
|
p.location.register,r)));
|
|
end
|
|
else
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,p.location.register)));
|
|
ungetregister32(p.location.register);
|
|
end;
|
|
end;
|
|
R_AX,R_BX,R_CX,R_DX,R_SI,R_DI:
|
|
begin
|
|
if alignment=4 then
|
|
begin
|
|
opsize:=S_L;
|
|
hreg:=reg16toreg32(p.location.register);
|
|
inc(pushedparasize,4);
|
|
end
|
|
else
|
|
begin
|
|
opsize:=S_W;
|
|
hreg:=p.location.register;
|
|
inc(pushedparasize,2);
|
|
end;
|
|
if inlined then
|
|
begin
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,opsize,hreg,r)));
|
|
end
|
|
else
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,opsize,hreg)));
|
|
ungetregister32(reg16toreg32(p.location.register));
|
|
end;
|
|
R_AL,R_BL,R_CL,R_DL:
|
|
begin
|
|
if alignment=4 then
|
|
begin
|
|
opsize:=S_L;
|
|
hreg:=reg8toreg32(p.location.register);
|
|
inc(pushedparasize,4);
|
|
end
|
|
else
|
|
begin
|
|
opsize:=S_W;
|
|
hreg:=reg8toreg16(p.location.register);
|
|
inc(pushedparasize,2);
|
|
end;
|
|
{ we must push always 16 bit }
|
|
if inlined then
|
|
begin
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,opsize,hreg,r)));
|
|
end
|
|
else
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,opsize,hreg)));
|
|
ungetregister32(reg8toreg32(p.location.register));
|
|
end;
|
|
else internalerror(1899);
|
|
end;
|
|
end;
|
|
LOC_FPU:
|
|
begin
|
|
size:=align(pfloatdef(p.resulttype)^.size,alignment);
|
|
inc(pushedparasize,size);
|
|
if not inlined then
|
|
emit_const_reg(A_SUB,S_L,size,R_ESP);
|
|
{$ifdef GDB}
|
|
if (cs_debuginfo in aktmoduleswitches) and
|
|
(exprasmlist^.first=exprasmlist^.last) then
|
|
exprasmlist^.concat(new(pai_force_line,init));
|
|
{$endif GDB}
|
|
r:=new_reference(R_ESP,0);
|
|
floatstoreops(pfloatdef(p.resulttype)^.typ,op,opsize);
|
|
{ this is the easiest case for inlined !! }
|
|
if inlined then
|
|
begin
|
|
r^.base:=procinfo^.framepointer;
|
|
r^.offset:=para_offset-pushedparasize;
|
|
end;
|
|
exprasmlist^.concat(new(paicpu,op_ref(op,opsize,r)));
|
|
dec(fpuvaroffset);
|
|
end;
|
|
LOC_CFPUREGISTER:
|
|
begin
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_FLD,S_NO,
|
|
correct_fpuregister(p.location.register,fpuvaroffset))));
|
|
size:=align(pfloatdef(p.resulttype)^.size,alignment);
|
|
inc(pushedparasize,size);
|
|
if not inlined then
|
|
emit_const_reg(A_SUB,S_L,size,R_ESP);
|
|
{$ifdef GDB}
|
|
if (cs_debuginfo in aktmoduleswitches) and
|
|
(exprasmlist^.first=exprasmlist^.last) then
|
|
exprasmlist^.concat(new(pai_force_line,init));
|
|
{$endif GDB}
|
|
r:=new_reference(R_ESP,0);
|
|
floatstoreops(pfloatdef(p.resulttype)^.typ,op,opsize);
|
|
{ this is the easiest case for inlined !! }
|
|
if inlined then
|
|
begin
|
|
r^.base:=procinfo^.framepointer;
|
|
r^.offset:=para_offset-pushedparasize;
|
|
end;
|
|
exprasmlist^.concat(new(paicpu,op_ref(op,opsize,r)));
|
|
end;
|
|
LOC_REFERENCE,LOC_MEM:
|
|
begin
|
|
tempreference:=p.location.reference;
|
|
del_reference(p.location.reference);
|
|
case p.resulttype^.deftype of
|
|
enumdef,
|
|
orddef :
|
|
begin
|
|
case p.resulttype^.size of
|
|
8 : begin
|
|
inc(pushedparasize,8);
|
|
if inlined then
|
|
begin
|
|
getexplicitregister32(R_EDI);
|
|
emit_ref_reg(A_MOV,S_L,
|
|
newreference(tempreference),R_EDI);
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r)));
|
|
ungetregister32(R_EDI);
|
|
getexplicitregister32(R_EDI);
|
|
inc(tempreference.offset,4);
|
|
emit_ref_reg(A_MOV,S_L,
|
|
newreference(tempreference),R_EDI);
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize+4);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r)));
|
|
ungetregister32(R_EDI);
|
|
end
|
|
else
|
|
begin
|
|
inc(tempreference.offset,4);
|
|
emit_push_mem(tempreference);
|
|
dec(tempreference.offset,4);
|
|
emit_push_mem(tempreference);
|
|
end;
|
|
end;
|
|
4 : begin
|
|
inc(pushedparasize,4);
|
|
if inlined then
|
|
begin
|
|
getexplicitregister32(R_EDI);
|
|
emit_ref_reg(A_MOV,S_L,
|
|
newreference(tempreference),R_EDI);
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r)));
|
|
ungetregister32(R_EDI);
|
|
end
|
|
else
|
|
emit_push_mem(tempreference);
|
|
end;
|
|
1,2 : begin
|
|
if alignment=4 then
|
|
begin
|
|
opsize:=S_L;
|
|
hreg:=R_EDI;
|
|
inc(pushedparasize,4);
|
|
end
|
|
else
|
|
begin
|
|
opsize:=S_W;
|
|
hreg:=R_DI;
|
|
inc(pushedparasize,2);
|
|
end;
|
|
if inlined then
|
|
begin
|
|
getexplicitregister32(R_EDI);
|
|
emit_ref_reg(A_MOV,opsize,
|
|
newreference(tempreference),hreg);
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,opsize,hreg,r)));
|
|
ungetregister32(R_EDI);
|
|
end
|
|
else
|
|
emit_push_mem_size(tempreference,p.resulttype^.size);
|
|
end;
|
|
else
|
|
internalerror(234231);
|
|
end;
|
|
end;
|
|
floatdef :
|
|
begin
|
|
case pfloatdef(p.resulttype)^.typ of
|
|
f32bit,
|
|
s32real :
|
|
begin
|
|
inc(pushedparasize,4);
|
|
if inlined then
|
|
begin
|
|
getexplicitregister32(R_EDI);
|
|
emit_ref_reg(A_MOV,S_L,
|
|
newreference(tempreference),R_EDI);
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r)));
|
|
ungetregister32(R_EDI);
|
|
end
|
|
else
|
|
emit_push_mem(tempreference);
|
|
end;
|
|
s64real,
|
|
s64comp :
|
|
begin
|
|
inc(pushedparasize,4);
|
|
inc(tempreference.offset,4);
|
|
if inlined then
|
|
begin
|
|
getexplicitregister32(R_EDI);
|
|
emit_ref_reg(A_MOV,S_L,
|
|
newreference(tempreference),R_EDI);
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r)));
|
|
ungetregister32(R_EDI);
|
|
end
|
|
else
|
|
emit_push_mem(tempreference);
|
|
inc(pushedparasize,4);
|
|
dec(tempreference.offset,4);
|
|
if inlined then
|
|
begin
|
|
getexplicitregister32(R_EDI);
|
|
emit_ref_reg(A_MOV,S_L,
|
|
newreference(tempreference),R_EDI);
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r)));
|
|
ungetregister32(R_EDI);
|
|
end
|
|
else
|
|
emit_push_mem(tempreference);
|
|
end;
|
|
s80real :
|
|
begin
|
|
inc(pushedparasize,4);
|
|
if alignment=4 then
|
|
inc(tempreference.offset,8)
|
|
else
|
|
inc(tempreference.offset,6);
|
|
if inlined then
|
|
begin
|
|
getexplicitregister32(R_EDI);
|
|
emit_ref_reg(A_MOV,S_L,
|
|
newreference(tempreference),R_EDI);
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r)));
|
|
ungetregister32(R_EDI);
|
|
end
|
|
else
|
|
emit_push_mem(tempreference);
|
|
dec(tempreference.offset,4);
|
|
inc(pushedparasize,4);
|
|
if inlined then
|
|
begin
|
|
getexplicitregister32(R_EDI);
|
|
emit_ref_reg(A_MOV,S_L,
|
|
newreference(tempreference),R_EDI);
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r)));
|
|
ungetregister32(R_EDI);
|
|
end
|
|
else
|
|
emit_push_mem(tempreference);
|
|
if alignment=4 then
|
|
begin
|
|
opsize:=S_L;
|
|
hreg:=R_EDI;
|
|
inc(pushedparasize,4);
|
|
dec(tempreference.offset,4);
|
|
end
|
|
else
|
|
begin
|
|
opsize:=S_W;
|
|
hreg:=R_DI;
|
|
inc(pushedparasize,2);
|
|
dec(tempreference.offset,2);
|
|
end;
|
|
if inlined then
|
|
begin
|
|
getexplicitregister32(R_EDI);
|
|
emit_ref_reg(A_MOV,opsize,
|
|
newreference(tempreference),hreg);
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,opsize,hreg,r)));
|
|
ungetregister32(R_EDI);
|
|
end
|
|
else
|
|
exprasmlist^.concat(new(paicpu,op_ref(A_PUSH,opsize,
|
|
newreference(tempreference))));
|
|
end;
|
|
end;
|
|
end;
|
|
pointerdef,
|
|
procvardef,
|
|
classrefdef:
|
|
begin
|
|
inc(pushedparasize,4);
|
|
if inlined then
|
|
begin
|
|
getexplicitregister32(R_EDI);
|
|
emit_ref_reg(A_MOV,S_L,
|
|
newreference(tempreference),R_EDI);
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r)));
|
|
ungetregister32(R_EDI);
|
|
end
|
|
else
|
|
emit_push_mem(tempreference);
|
|
end;
|
|
arraydef,
|
|
recorddef,
|
|
stringdef,
|
|
setdef,
|
|
objectdef :
|
|
begin
|
|
{ even some structured types are 32 bit }
|
|
if is_widestring(p.resulttype) or
|
|
is_ansistring(p.resulttype) or
|
|
is_smallset(p.resulttype) or
|
|
((p.resulttype^.deftype in [recorddef,arraydef]) and
|
|
(
|
|
(p.resulttype^.deftype<>arraydef) or not
|
|
(parraydef(p.resulttype)^.IsConstructor or
|
|
parraydef(p.resulttype)^.isArrayOfConst or
|
|
is_open_array(p.resulttype))
|
|
) and
|
|
(p.resulttype^.size<=4)
|
|
) or
|
|
((p.resulttype^.deftype=objectdef) and
|
|
pobjectdef(p.resulttype)^.is_class) then
|
|
begin
|
|
if (p.resulttype^.size>2) or
|
|
((alignment=4) and (p.resulttype^.size>0)) then
|
|
begin
|
|
inc(pushedparasize,4);
|
|
if inlined then
|
|
begin
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
concatcopy(tempreference,r^,4,false,false);
|
|
end
|
|
else
|
|
emit_push_mem(tempreference);
|
|
end
|
|
else
|
|
begin
|
|
if p.resulttype^.size>0 then
|
|
begin
|
|
inc(pushedparasize,2);
|
|
if inlined then
|
|
begin
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
concatcopy(tempreference,r^,2,false,false);
|
|
end
|
|
else
|
|
exprasmlist^.concat(new(paicpu,op_ref(A_PUSH,S_W,newreference(tempreference))));
|
|
end;
|
|
end;
|
|
end
|
|
{ call by value open array ? }
|
|
else if is_cdecl then
|
|
begin
|
|
{ push on stack }
|
|
size:=align(p.resulttype^.size,alignment);
|
|
inc(pushedparasize,size);
|
|
emit_const_reg(A_SUB,S_L,size,R_ESP);
|
|
r:=new_reference(R_ESP,0);
|
|
concatcopy(tempreference,r^,size,false,false);
|
|
end
|
|
else
|
|
internalerror(8954);
|
|
end;
|
|
else
|
|
CGMessage(cg_e_illegal_expression);
|
|
end;
|
|
end;
|
|
LOC_JUMP:
|
|
begin
|
|
getlabel(hlabel);
|
|
if alignment=4 then
|
|
begin
|
|
opsize:=S_L;
|
|
inc(pushedparasize,4);
|
|
end
|
|
else
|
|
begin
|
|
opsize:=S_W;
|
|
inc(pushedparasize,2);
|
|
end;
|
|
emitlab(truelabel);
|
|
if inlined then
|
|
begin
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
emit_const_ref(A_MOV,opsize,1,r);
|
|
end
|
|
else
|
|
exprasmlist^.concat(new(paicpu,op_const(A_PUSH,opsize,1)));
|
|
emitjmp(C_None,hlabel);
|
|
emitlab(falselabel);
|
|
if inlined then
|
|
begin
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
emit_const_ref(A_MOV,opsize,0,r);
|
|
end
|
|
else
|
|
exprasmlist^.concat(new(paicpu,op_const(A_PUSH,opsize,0)));
|
|
emitlab(hlabel);
|
|
end;
|
|
LOC_FLAGS:
|
|
begin
|
|
if not(R_EAX in unused) then
|
|
begin
|
|
getexplicitregister32(R_EDI);
|
|
emit_reg_reg(A_MOV,S_L,R_EAX,R_EDI);
|
|
end;
|
|
emit_flag2reg(p.location.resflags,R_AL);
|
|
emit_reg_reg(A_MOVZX,S_BW,R_AL,R_AX);
|
|
if alignment=4 then
|
|
begin
|
|
opsize:=S_L;
|
|
hreg:=R_EAX;
|
|
inc(pushedparasize,4);
|
|
end
|
|
else
|
|
begin
|
|
opsize:=S_W;
|
|
hreg:=R_AX;
|
|
inc(pushedparasize,2);
|
|
end;
|
|
if inlined then
|
|
begin
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,opsize,hreg,r)));
|
|
end
|
|
else
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,opsize,hreg)));
|
|
if not(R_EAX in unused) then
|
|
begin
|
|
emit_reg_reg(A_MOV,S_L,R_EDI,R_EAX);
|
|
ungetregister32(R_EDI);
|
|
end;
|
|
end;
|
|
{$ifdef SUPPORT_MMX}
|
|
LOC_MMXREGISTER,
|
|
LOC_CMMXREGISTER:
|
|
begin
|
|
inc(pushedparasize,8); { was missing !!! (PM) }
|
|
emit_const_reg(
|
|
A_SUB,S_L,8,R_ESP);
|
|
{$ifdef GDB}
|
|
if (cs_debuginfo in aktmoduleswitches) and
|
|
(exprasmlist^.first=exprasmlist^.last) then
|
|
exprasmlist^.concat(new(pai_force_line,init));
|
|
{$endif GDB}
|
|
if inlined then
|
|
begin
|
|
r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOVQ,S_NO,
|
|
p.location.register,r)));
|
|
end
|
|
else
|
|
begin
|
|
r:=new_reference(R_ESP,0);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(
|
|
A_MOVQ,S_NO,p.location.register,r)));
|
|
end;
|
|
end;
|
|
{$endif SUPPORT_MMX}
|
|
end;
|
|
end;
|
|
|
|
{*****************************************************************************
|
|
Emit Functions
|
|
*****************************************************************************}
|
|
|
|
procedure maketojumpbool(p : tnode);
|
|
{
|
|
produces jumps to true respectively false labels using boolean expressions
|
|
}
|
|
var
|
|
opsize : topsize;
|
|
storepos : tfileposinfo;
|
|
begin
|
|
if p.error then
|
|
exit;
|
|
storepos:=aktfilepos;
|
|
aktfilepos:=p.fileinfo;
|
|
if is_boolean(p.resulttype) then
|
|
begin
|
|
if is_constboolnode(p) then
|
|
begin
|
|
if p.value<>0 then
|
|
emitjmp(C_None,truelabel)
|
|
else
|
|
emitjmp(C_None,falselabel);
|
|
end
|
|
else
|
|
begin
|
|
opsize:=def_opsize(p.resulttype);
|
|
case p.location.loc of
|
|
LOC_CREGISTER,LOC_REGISTER : begin
|
|
emit_reg_reg(A_OR,opsize,p.location.register,
|
|
p.location.register);
|
|
ungetregister(p.location.register);
|
|
emitjmp(C_NZ,truelabel);
|
|
emitjmp(C_None,falselabel);
|
|
end;
|
|
LOC_MEM,LOC_REFERENCE : begin
|
|
emit_const_ref(
|
|
A_CMP,opsize,0,newreference(p.location.reference));
|
|
del_reference(p.location.reference);
|
|
emitjmp(C_NZ,truelabel);
|
|
emitjmp(C_None,falselabel);
|
|
end;
|
|
LOC_FLAGS : begin
|
|
emitjmp(flag_2_cond[p.location.resflags],truelabel);
|
|
emitjmp(C_None,falselabel);
|
|
end;
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
CGMessage(type_e_mismatch);
|
|
aktfilepos:=storepos;
|
|
end;
|
|
|
|
|
|
{ produces if necessary overflowcode }
|
|
procedure emitoverflowcheck(p:tnode);
|
|
var
|
|
hl : pasmlabel;
|
|
begin
|
|
if not(cs_check_overflow in aktlocalswitches) then
|
|
exit;
|
|
getlabel(hl);
|
|
if not ((p.resulttype^.deftype=pointerdef) or
|
|
((p.resulttype^.deftype=orddef) and
|
|
(porddef(p.resulttype)^.typ in [u64bit,u16bit,u32bit,u8bit,uchar,
|
|
bool8bit,bool16bit,bool32bit]))) then
|
|
emitjmp(C_NO,hl)
|
|
else
|
|
emitjmp(C_NB,hl);
|
|
emitcall('FPC_OVERFLOW');
|
|
emitlab(hl);
|
|
end;
|
|
|
|
{ produces range check code, while one of the operands is a 64 bit
|
|
integer }
|
|
procedure emitrangecheck64(p : tnode;todef : pdef);
|
|
|
|
begin
|
|
|
|
CGMessage(cg_w_64bit_range_check_not_supported);
|
|
{internalerror(28699);}
|
|
end;
|
|
|
|
{ produces if necessary rangecheckcode }
|
|
procedure emitrangecheck(p:tnode;todef:pdef);
|
|
{
|
|
generate range checking code for the value at location t. The
|
|
type used is the checked against todefs ranges. fromdef (p.resulttype)
|
|
is the original type used at that location, when both defs are
|
|
equal the check is also insert (needed for succ,pref,inc,dec)
|
|
}
|
|
var
|
|
neglabel,
|
|
poslabel : pasmlabel;
|
|
href : treference;
|
|
rstr : string;
|
|
hreg : tregister;
|
|
opsize : topsize;
|
|
op : tasmop;
|
|
fromdef : pdef;
|
|
lto,hto,
|
|
lfrom,hfrom : longint;
|
|
doublebound,
|
|
is_reg,
|
|
popecx : boolean;
|
|
begin
|
|
{ range checking on and range checkable value? }
|
|
if not(cs_check_range in aktlocalswitches) or
|
|
not(todef^.deftype in [orddef,enumdef,arraydef]) then
|
|
exit;
|
|
{ only check when assigning to scalar, subranges are different,
|
|
when todef=fromdef then the check is always generated }
|
|
fromdef:=p.resulttype;
|
|
if is_64bitint(fromdef) or is_64bitint(todef) then
|
|
begin
|
|
emitrangecheck64(p,todef);
|
|
exit;
|
|
end;
|
|
{we also need lto and hto when checking if we need to use doublebound!
|
|
(JM)}
|
|
getrange(todef,lto,hto);
|
|
if todef<>fromdef then
|
|
begin
|
|
getrange(p.resulttype,lfrom,hfrom);
|
|
{ first check for not being u32bit, then if the to is bigger than
|
|
from }
|
|
if (lto<hto) and (lfrom<hfrom) and
|
|
(lto<=lfrom) and (hto>=hfrom) then
|
|
exit;
|
|
end;
|
|
{ generate the rangecheck code for the def where we are going to
|
|
store the result }
|
|
doublebound:=false;
|
|
case todef^.deftype of
|
|
orddef :
|
|
begin
|
|
porddef(todef)^.genrangecheck;
|
|
rstr:=porddef(todef)^.getrangecheckstring;
|
|
doublebound:=(porddef(todef)^.typ=u32bit) and (lto>hto);
|
|
end;
|
|
enumdef :
|
|
begin
|
|
penumdef(todef)^.genrangecheck;
|
|
rstr:=penumdef(todef)^.getrangecheckstring;
|
|
end;
|
|
arraydef :
|
|
begin
|
|
parraydef(todef)^.genrangecheck;
|
|
rstr:=parraydef(todef)^.getrangecheckstring;
|
|
doublebound:=(lto>hto);
|
|
end;
|
|
end;
|
|
{ get op and opsize }
|
|
opsize:=def2def_opsize(fromdef,u32bitdef);
|
|
if opsize in [S_B,S_W,S_L] then
|
|
op:=A_MOV
|
|
else
|
|
if is_signed(fromdef) then
|
|
op:=A_MOVSX
|
|
else
|
|
op:=A_MOVZX;
|
|
is_reg:=(p.location.loc in [LOC_REGISTER,LOC_CREGISTER]);
|
|
if is_reg then
|
|
hreg:=p.location.register;
|
|
if not target_os.use_bound_instruction then
|
|
begin
|
|
{ FPC_BOUNDCHECK needs to be called with
|
|
%ecx - value
|
|
%edi - pointer to the ranges }
|
|
popecx:=false;
|
|
if not(is_reg) or
|
|
(p.location.register<>R_ECX) then
|
|
begin
|
|
if not(R_ECX in unused) then
|
|
begin
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,R_ECX)));
|
|
popecx:=true;
|
|
end
|
|
else exprasmlist^.concat(new(pairegalloc,alloc(R_ECX)));
|
|
if is_reg then
|
|
emit_reg_reg(op,opsize,p.location.register,R_ECX)
|
|
else
|
|
emit_ref_reg(op,opsize,newreference(p.location.reference),R_ECX);
|
|
end;
|
|
if doublebound then
|
|
begin
|
|
getlabel(neglabel);
|
|
getlabel(poslabel);
|
|
emit_reg_reg(A_OR,S_L,R_ECX,R_ECX);
|
|
emitjmp(C_L,neglabel);
|
|
end;
|
|
{ insert bound instruction only }
|
|
getexplicitregister32(R_EDI);
|
|
exprasmlist^.concat(new(paicpu,op_sym_ofs_reg(A_MOV,S_L,newasmsymbol(rstr),0,R_EDI)));
|
|
emitcall('FPC_BOUNDCHECK');
|
|
ungetregister32(R_EDI);
|
|
{ u32bit needs 2 checks }
|
|
if doublebound then
|
|
begin
|
|
emitjmp(C_None,poslabel);
|
|
emitlab(neglabel);
|
|
getexplicitregister32(R_EDI);
|
|
exprasmlist^.concat(new(paicpu,op_sym_ofs_reg(A_MOV,S_L,newasmsymbol(rstr),8,R_EDI)));
|
|
emitcall('FPC_BOUNDCHECK');
|
|
ungetregister32(R_EDI);
|
|
emitlab(poslabel);
|
|
end;
|
|
if popecx then
|
|
exprasmlist^.concat(new(paicpu,op_reg(A_POP,S_L,R_ECX)))
|
|
else exprasmlist^.concat(new(pairegalloc,dealloc(R_ECX)));
|
|
end
|
|
else
|
|
begin
|
|
reset_reference(href);
|
|
href.symbol:=newasmsymbol(rstr);
|
|
{ load the value in a register }
|
|
if is_reg then
|
|
begin
|
|
{ be sure that hreg is a 32 bit reg, if not load it in %edi }
|
|
if p.location.register in [R_EAX..R_EDI] then
|
|
hreg:=p.location.register
|
|
else
|
|
begin
|
|
getexplicitregister32(R_EDI);
|
|
emit_reg_reg(op,opsize,p.location.register,R_EDI);
|
|
hreg:=R_EDI;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
getexplicitregister32(R_EDI);
|
|
emit_ref_reg(op,opsize,newreference(p.location.reference),R_EDI);
|
|
hreg:=R_EDI;
|
|
end;
|
|
if doublebound then
|
|
begin
|
|
getlabel(neglabel);
|
|
getlabel(poslabel);
|
|
emit_reg_reg(A_TEST,S_L,hreg,hreg);
|
|
emitjmp(C_L,neglabel);
|
|
end;
|
|
{ insert bound instruction only }
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_BOUND,S_L,hreg,newreference(href))));
|
|
{ u32bit needs 2 checks }
|
|
if doublebound then
|
|
begin
|
|
href.offset:=8;
|
|
emitjmp(C_None,poslabel);
|
|
emitlab(neglabel);
|
|
exprasmlist^.concat(new(paicpu,op_reg_ref(A_BOUND,S_L,hreg,newreference(href))));
|
|
emitlab(poslabel);
|
|
end;
|
|
if hreg = R_EDI then
|
|
ungetregister32(R_EDI);
|
|
end;
|
|
end;
|
|
|
|
|
|
{ DO NOT RELY on the fact that the tnode is not yet swaped
|
|
because of inlining code PM }
|
|
procedure firstcomplex(p : tnode);
|
|
var
|
|
hp : tnode;
|
|
begin
|
|
{ always calculate boolean AND and OR from left to right }
|
|
if (p.treetype in [orn,andn]) and
|
|
(p.left^.resulttype^.deftype=orddef) and
|
|
(porddef(p.left^.resulttype)^.typ in [bool8bit,bool16bit,bool32bit]) then
|
|
begin
|
|
{ p.swaped:=false}
|
|
if p.swaped then
|
|
internalerror(234234);
|
|
end
|
|
else
|
|
if (p.left^.registers32<p.right^.registers32) and
|
|
{ the following check is appropriate, because all }
|
|
{ 4 registers are rarely used and it is thereby }
|
|
{ achieved that the extra code is being dropped }
|
|
{ by exchanging not commutative operators }
|
|
(p.right^.registers32<=4) then
|
|
begin
|
|
hp:=p.left;
|
|
p.left:=p.right;
|
|
p.right:=hp;
|
|
p.swaped:=not p.swaped;
|
|
end;
|
|
{else
|
|
p.swaped:=false; do not modify }
|
|
end;
|
|
{$endif}
|
|
|
|
end.
|
|
{
|
|
$Log$
|
|
Revision 1.1 2000-10-01 19:58:40 peter
|
|
* new file
|
|
|
|
} |