fpc/compiler/cg386inl.pas

1573 lines
62 KiB
ObjectPascal

{
$Id$
Copyright (c) 1993-98 by Florian Klaempfl
Generate i386 inline nodes
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 cg386inl;
interface
uses
tree;
procedure secondinline(var p : ptree);
implementation
uses
globtype,systems,
cobjects,verbose,globals,files,
symtable,aasm,types,
hcodegen,temp_gen,pass_1,pass_2,
i386base,i386asm,
cgai386,tgeni386,cg386cal;
{*****************************************************************************
Helpers
*****************************************************************************}
{ reverts the parameter list }
var nb_para : integer;
function reversparameter(p : ptree) : ptree;
var
hp1,hp2 : ptree;
begin
hp1:=nil;
nb_para := 0;
while assigned(p) do
begin
{ pull out }
hp2:=p;
p:=p^.right;
inc(nb_para);
{ pull in }
hp2^.right:=hp1;
hp1:=hp2;
end;
reversparameter:=hp1;
end;
{*****************************************************************************
SecondInLine
*****************************************************************************}
procedure StoreDirectFuncResult(dest:ptree);
var
hp : ptree;
hdef : porddef;
hreg : tregister;
hregister : tregister;
oldregisterdef : boolean;
begin
{ Get the accumulator first so it can't be used in the dest }
hregister:=getexplicitregister32(accumulator);
{ process dest }
SecondPass(dest);
if Codegenerror then
exit;
{ store the value }
Case dest^.resulttype^.deftype of
floatdef:
floatstore(PFloatDef(dest^.resulttype)^.typ,dest^.location.reference);
orddef:
begin
if porddef(dest^.resulttype)^.typ in [u64bit,s64bitint] then
begin
emit_movq_reg_loc(R_EDX,R_EAX,dest^.location);
end
else
begin
Case dest^.resulttype^.size of
1 : hreg:=regtoreg8(hregister);
2 : hreg:=regtoreg16(hregister);
4 : hreg:=hregister;
End;
emit_mov_reg_loc(hreg,dest^.location);
If (cs_check_range in aktlocalswitches) and
{no need to rangecheck longints or cardinals on 32bit processors}
not((porddef(dest^.resulttype)^.typ = s32bit) and
(porddef(dest^.resulttype)^.low = $80000000) and
(porddef(dest^.resulttype)^.high = $7fffffff)) and
not((porddef(dest^.resulttype)^.typ = u32bit) and
(porddef(dest^.resulttype)^.low = 0) and
(porddef(dest^.resulttype)^.high = $ffffffff)) then
Begin
{do not register this temporary def}
OldRegisterDef := RegisterDef;
RegisterDef := False;
hdef:=nil;
Case PordDef(dest^.resulttype)^.typ of
u8bit,u16bit,u32bit:
begin
new(hdef,init(u32bit,0,$ffffffff));
hreg:=hregister;
end;
s8bit,s16bit,s32bit:
begin
new(hdef,init(s32bit,$80000000,$7fffffff));
hreg:=hregister;
end;
end;
{ create a fake node }
hp := genzeronode(nothingn);
hp^.location.loc := LOC_REGISTER;
hp^.location.register := hreg;
if assigned(hdef) then
hp^.resulttype:=hdef
else
hp^.resulttype:=dest^.resulttype;
{ emit the range check }
emitrangecheck(hp,dest^.resulttype);
hp^.right := nil;
if assigned(hdef) then
Dispose(hdef, Done);
RegisterDef := OldRegisterDef;
disposetree(hp);
End;
end;
End;
else
internalerror(66766766);
end;
{ free used registers }
del_locref(dest^.location);
ungetregister(hregister);
end;
procedure secondinline(var p : ptree);
const
{tfloattype = (s32real,s64real,s80real,s64bit,f16bit,f32bit);}
{ float_name: array[tfloattype] of string[8]=
('S32REAL','S64REAL','S80REAL','S64BIT','F16BIT','F32BIT'); }
incdecop:array[in_inc_x..in_dec_x] of tasmop=(A_INC,A_DEC);
addsubop:array[in_inc_x..in_dec_x] of tasmop=(A_ADD,A_SUB);
var
aktfile : treference;
ft : tfiletype;
opsize : topsize;
op,
asmop : tasmop;
pushed : tpushed;
{inc/dec}
addconstant : boolean;
addvalue : longint;
procedure handlereadwrite(doread,doln : boolean);
{ produces code for READ(LN) and WRITE(LN) }
procedure loadstream;
const
io:array[boolean] of string[7]=('_OUTPUT','_INPUT');
var
r : preference;
begin
new(r);
reset_reference(r^);
r^.symbol:=newasmsymbol('U_'+upper(target_info.system_unit)+io[doread]);
exprasmlist^.concat(new(pai386,op_ref_reg(A_LEA,S_L,r,R_EDI)))
end;
const
rdwrprefix:array[boolean] of string[15]=('FPC_WRITE_TEXT_','FPC_READ_TEXT_');
var
destpara,
node,hp : ptree;
typedtyp,
pararesult : pdef;
orgfloattype : tfloattype;
has_length : boolean;
dummycoll : tdefcoll;
iolabel : pasmlabel;
npara : longint;
begin
{ I/O check }
if (cs_check_io in aktlocalswitches) and
((aktprocsym^.definition^.options and poiocheck)=0) then
begin
getlabel(iolabel);
emitlab(iolabel);
end
else
iolabel:=nil;
{ for write of real with the length specified }
has_length:=false;
hp:=nil;
{ reserve temporary pointer to data variable }
aktfile.symbol:=nil;
gettempofsizereference(4,aktfile);
{ first state text data }
ft:=ft_text;
{ and state a parameter ? }
if p^.left=nil then
begin
{ the following instructions are for "writeln;" }
loadstream;
{ save @aktfile in temporary variable }
exprasmlist^.concat(new(pai386,op_reg_ref(A_MOV,S_L,R_EDI,newreference(aktfile))));
end
else
begin
{ revers paramters }
node:=reversparameter(p^.left);
p^.left := node;
npara := nb_para;
{ calculate data variable }
{ is first parameter a file type ? }
if node^.left^.resulttype^.deftype=filedef then
begin
ft:=pfiledef(node^.left^.resulttype)^.filetype;
if ft=ft_typed then
typedtyp:=pfiledef(node^.left^.resulttype)^.typed_as;
secondpass(node^.left);
if codegenerror then
exit;
{ save reference in temporary variables }
if node^.left^.location.loc<>LOC_REFERENCE then
begin
CGMessage(cg_e_illegal_expression);
exit;
end;
exprasmlist^.concat(new(pai386,op_ref_reg(A_LEA,S_L,newreference(node^.left^.location.reference),R_EDI)));
{ skip to the next parameter }
node:=node^.right;
end
else
begin
{ load stdin/stdout stream }
loadstream;
end;
{ save @aktfile in temporary variable }
exprasmlist^.concat(new(pai386,op_reg_ref(A_MOV,S_L,R_EDI,newreference(aktfile))));
if doread then
{ parameter by READ gives call by reference }
dummycoll.paratyp:=vs_var
{ an WRITE Call by "Const" }
else
dummycoll.paratyp:=vs_const;
{ because of secondcallparan, which otherwise attaches }
if ft=ft_typed then
{ this is to avoid copy of simple const parameters }
{dummycoll.data:=new(pformaldef,init)}
dummycoll.data:=cformaldef
else
{ I think, this isn't a good solution (FK) }
dummycoll.data:=nil;
while assigned(node) do
begin
pushusedregisters(pushed,$ff);
hp:=node;
node:=node^.right;
hp^.right:=nil;
if hp^.is_colon_para then
CGMessage(parser_e_illegal_colon_qualifier);
{ when float is written then we need bestreal to be pushed
convert here else we loose the old flaot type }
if (not doread) and
(ft<>ft_typed) and
(hp^.left^.resulttype^.deftype=floatdef) then
begin
orgfloattype:=pfloatdef(hp^.left^.resulttype)^.typ;
hp^.left:=gentypeconvnode(hp^.left,bestrealdef^);
firstpass(hp^.left);
end;
{ when read ord,floats are functions, so they need this
parameter as their destination instead of being pushed }
if doread and
(ft<>ft_typed) and
(hp^.resulttype^.deftype in [orddef,floatdef]) then
destpara:=hp^.left
else
begin
if ft=ft_typed then
never_copy_const_param:=true;
{ reset data type }
dummycoll.data:=nil;
{ create temporary defs for high tree generation }
if doread and (is_shortstring(hp^.resulttype)) then
dummycoll.data:=openshortstringdef
else
if (is_chararray(hp^.resulttype)) then
dummycoll.data:=openchararraydef;
secondcallparan(hp,@dummycoll,false,false,false,0);
if ft=ft_typed then
never_copy_const_param:=false;
end;
hp^.right:=node;
if codegenerror then
exit;
emit_push_mem(aktfile);
if (ft=ft_typed) then
begin
{ OK let's try this }
{ first we must only allow the right type }
{ we have to call blockread or blockwrite }
{ but the real problem is that }
{ reset and rewrite should have set }
{ the type size }
{ as recordsize for that file !!!! }
{ how can we make that }
{ I think that is only possible by adding }
{ reset and rewrite to the inline list a call }
{ allways read only one record by element }
push_int(typedtyp^.size);
if doread then
emitcall('FPC_TYPED_READ')
else
emitcall('FPC_TYPED_WRITE');
end
else
begin
{ save current position }
pararesult:=hp^.left^.resulttype;
{ handle possible field width }
{ of course only for write(ln) }
if not doread then
begin
{ handle total width parameter }
if assigned(node) and node^.is_colon_para then
begin
hp:=node;
node:=node^.right;
hp^.right:=nil;
secondcallparan(hp,@dummycoll,false,false,false,0);
hp^.right:=node;
if codegenerror then
exit;
has_length:=true;
end
else
if pararesult^.deftype<>floatdef then
push_int(0)
else
push_int(-32767);
{ a second colon para for a float ? }
if assigned(node) and node^.is_colon_para then
begin
hp:=node;
node:=node^.right;
hp^.right:=nil;
secondcallparan(hp,@dummycoll,false,false,false,0);
hp^.right:=node;
if pararesult^.deftype<>floatdef then
CGMessage(parser_e_illegal_colon_qualifier);
if codegenerror then
exit;
end
else
begin
if pararesult^.deftype=floatdef then
push_int(-1);
end;
{ push also the real type for floats }
if pararesult^.deftype=floatdef then
push_int(ord(orgfloattype));
end;
case pararesult^.deftype of
stringdef :
begin
emitcall(rdwrprefix[doread]+pstringdef(pararesult)^.stringtypname);
end;
pointerdef :
begin
if is_pchar(pararesult) then
emitcall(rdwrprefix[doread]+'PCHAR_AS_POINTER')
end;
arraydef :
begin
if is_chararray(pararesult) then
emitcall(rdwrprefix[doread]+'PCHAR_AS_ARRAY')
end;
floatdef :
begin
emitcall(rdwrprefix[doread]+'FLOAT');
if doread then
StoreDirectFuncResult(destpara);
end;
orddef :
begin
case porddef(pararesult)^.typ of
s8bit,s16bit,s32bit :
emitcall(rdwrprefix[doread]+'SINT');
u8bit,u16bit,u32bit :
emitcall(rdwrprefix[doread]+'UINT');
uchar :
emitcall(rdwrprefix[doread]+'CHAR');
s64bitint:
emitcall(rdwrprefix[doread]+'INT64');
u64bit :
emitcall(rdwrprefix[doread]+'QWORD');
bool8bit,
bool16bit,
bool32bit :
emitcall(rdwrprefix[doread]+'BOOLEAN');
end; if doread then
StoreDirectFuncResult(destpara);
end;
end;
end;
{ load ESI in methods again }
popusedregisters(pushed);
maybe_loadesi;
end;
end;
{ Insert end of writing for textfiles }
if ft=ft_text then
begin
pushusedregisters(pushed,$ff);
emit_push_mem(aktfile);
if doread then
begin
if doln then
emitcall('FPC_READLN_END')
else
emitcall('FPC_READ_END');
end
else
begin
if doln then
emitcall('FPC_WRITELN_END')
else
emitcall('FPC_WRITE_END');
end;
popusedregisters(pushed);
maybe_loadesi;
end;
{ Insert IOCheck if set }
if assigned(iolabel) then
begin
{ registers are saved in the procedure }
exprasmlist^.concat(new(pai386,op_sym(A_PUSH,S_L,iolabel)));
emitcall('FPC_IOCHECK');
end;
{ Freeup all used temps }
ungetiftemp(aktfile);
if assigned(p^.left) then
begin
p^.left:=reversparameter(p^.left);
if npara<>nb_para then
CGMessage(cg_f_internal_error_in_secondinline);
hp:=p^.left;
while assigned(hp) do
begin
if assigned(hp^.left) then
if (hp^.left^.location.loc in [LOC_MEM,LOC_REFERENCE]) then
ungetiftemp(hp^.left^.location.reference);
hp:=hp^.right;
end;
end;
end;
procedure handle_str;
var
hp,node : ptree;
dummycoll : tdefcoll;
is_real,has_length : boolean;
realtype : tfloattype;
procedureprefix : string;
begin
pushusedregisters(pushed,$ff);
node:=p^.left;
is_real:=false;
has_length:=false;
while assigned(node^.right) do node:=node^.right;
{ if a real parameter somewhere then call REALSTR }
if (node^.left^.resulttype^.deftype=floatdef) then
begin
is_real:=true;
realtype:=pfloatdef(node^.left^.resulttype)^.typ;
end;
node:=p^.left;
{ we have at least two args }
{ with at max 2 colon_para in between }
{ string arg }
hp:=node;
node:=node^.right;
hp^.right:=nil;
dummycoll.paratyp:=vs_var;
if is_shortstring(hp^.resulttype) then
dummycoll.data:=openshortstringdef
else
dummycoll.data:=hp^.resulttype;
procedureprefix:='FPC_'+pstringdef(hp^.resulttype)^.stringtypname+'_';
secondcallparan(hp,@dummycoll,false,false,false,0);
if codegenerror then
exit;
dummycoll.paratyp:=vs_const;
disposetree(p^.left);
p^.left:=nil;
{ second arg }
hp:=node;
node:=node^.right;
hp^.right:=nil;
{ if real push real type }
if is_real then
push_int(ord(realtype));
{ frac para }
if hp^.is_colon_para and assigned(node) and
node^.is_colon_para then
begin
dummycoll.data:=hp^.resulttype;
secondcallparan(hp,@dummycoll,false
,false,false,0
);
if codegenerror then
exit;
disposetree(hp);
hp:=node;
node:=node^.right;
hp^.right:=nil;
has_length:=true;
end
else
if is_real then
push_int(-1);
{ third arg, length only if is_real }
if hp^.is_colon_para then
begin
dummycoll.data:=hp^.resulttype;
secondcallparan(hp,@dummycoll,false
,false,false,0
);
if codegenerror then
exit;
disposetree(hp);
hp:=node;
node:=node^.right;
hp^.right:=nil;
end
else
if is_real then
push_int(-32767)
else
push_int(-1);
{ Convert float to bestreal }
if is_real then
begin
hp^.left:=gentypeconvnode(hp^.left,bestrealdef^);
firstpass(hp^.left);
end;
{ last arg longint or real }
secondcallparan(hp,@dummycoll,false
,false,false,0
);
if codegenerror then
exit;
if is_real then
emitcall(procedureprefix+'FLOAT')
else
case porddef(hp^.resulttype)^.typ of
u32bit:
emitcall(procedureprefix+'CARDINAL');
u64bit:
emitcall(procedureprefix+'QWORD');
s64bitint:
emitcall(procedureprefix+'INT64');
else
emitcall(procedureprefix+'LONGINT');
end;
disposetree(hp);
popusedregisters(pushed);
end;
{$IfnDef OLDVAL}
Procedure Handle_Val;
var
hp,node, code_para, dest_para : ptree;
hreg: TRegister;
hdef: POrdDef;
procedureprefix : string;
hr, hr2: TReference;
dummycoll : tdefcoll;
has_code, has_32bit_code, oldregisterdef: boolean;
begin
node:=p^.left;
hp:=node;
node:=node^.right;
hp^.right:=nil;
{if we have 3 parameters, we have a code parameter}
has_code := Assigned(node^.right);
has_32bit_code := false;
reset_reference(hr);
hreg := R_NO;
If has_code then
Begin
{code is an orddef, that's checked in tcinl}
code_para := hp;
hp := node;
node := node^.right;
hp^.right := nil;
has_32bit_code := (porddef(code_para^.left^.resulttype)^.typ in [u32bit,s32bit]);
End;
{hp = destination now, save for later use}
dest_para := hp;
{if EAX is already in use, it's a register variable. Since we don't
need another register besides EAX, release the one we got}
If hreg <> R_EAX Then ungetregister32(hreg);
{load and push the address of the destination}
dummycoll.paratyp:=vs_var;
dummycoll.data:=dest_para^.resulttype;
secondcallparan(dest_para,@dummycoll,false,false,false,0);
if codegenerror then
exit;
{save the regvars}
pushusedregisters(pushed,$ff);
{now that we've already pushed the addres of dest_para^.left on the
stack, we can put the real parameters on the stack}
If has_32bit_code Then
Begin
dummycoll.paratyp:=vs_var;
dummycoll.data:=code_para^.resulttype;
secondcallparan(code_para,@dummycoll,false,false,false,0);
if codegenerror then
exit;
Disposetree(code_para);
End
Else
Begin
{only 32bit code parameter is supported, so fake one}
GetTempOfSizeReference(4,hr);
emitpushreferenceaddr(hr);
End;
{node = first parameter = string}
dummycoll.paratyp:=vs_const;
dummycoll.data:=node^.resulttype;
secondcallparan(node,@dummycoll,false,false,false,0);
if codegenerror then
exit;
Case dest_para^.resulttype^.deftype of
floatdef:
procedureprefix := 'FPC_VAL_REAL_';
orddef:
if is_signed(dest_para^.resulttype) then
begin
{if we are converting to a signed number, we have to include the
size of the destination, so the Val function can extend the sign
of the result to allow proper range checking}
exprasmlist^.concat(new(pai386,op_const(A_PUSH,S_L,dest_para^.resulttype^.size)));
procedureprefix := 'FPC_VAL_SINT_'
end
else
procedureprefix := 'FPC_VAL_UINT_';
End;
emitcall(procedureprefix+pstringdef(node^.resulttype)^.stringtypname);
{ before disposing node we need to ungettemp !! PM }
if node^.left^.location.loc in [LOC_REFERENCE,LOC_MEM] then
ungetiftemp(node^.left^.location.reference);
disposetree(node);
p^.left := nil;
{reload esi in case the dest_para/code_para is a class variable or so}
maybe_loadesi;
If (dest_para^.resulttype^.deftype = orddef) Then
Begin
{store the result in a safe place, because EAX may be used by a
register variable}
hreg := getexplicitregister32(R_EAX);
emit_reg_reg(A_MOV,S_L,R_EAX,hreg);
{as of now, hreg now holds the location of the result, if it was
integer}
End;
{ restore the register vars}
popusedregisters(pushed);
If has_code and Not(has_32bit_code) Then
{only 16bit code is possible}
Begin
{load the address of the code parameter}
secondpass(code_para^.left);
{move the code to its destination}
exprasmlist^.concat(new(pai386,op_ref_reg(A_MOV,S_L,NewReference(hr),R_EDI)));
emit_mov_reg_loc(R_DI,code_para^.left^.location);
Disposetree(code_para);
End;
{restore the address of the result}
exprasmlist^.concat(new(pai386,op_reg(A_POP,S_L,R_EDI)));
{set up hr2 to a refernce with EDI as base register}
reset_reference(hr2);
hr2.base := R_EDI;
{save the function result in the destination variable}
Case dest_para^.left^.resulttype^.deftype of
floatdef:
floatstore(PFloatDef(dest_para^.left^.resulttype)^.typ, hr2);
orddef:
Case PordDef(dest_para^.left^.resulttype)^.typ of
u8bit,s8bit:
exprasmlist^.concat(new(pai386,op_reg_ref(A_MOV, S_B,
RegToReg8(hreg),newreference(hr2))));
u16bit,s16bit:
exprasmlist^.concat(new(pai386,op_reg_ref(A_MOV, S_W,
RegToReg16(hreg),newreference(hr2))));
u32bit,s32bit:
exprasmlist^.concat(new(pai386,op_reg_ref(A_MOV, S_L,
hreg,newreference(hr2))));
{u64bit,s64bitint: ???}
End;
End;
If (cs_check_range in aktlocalswitches) and
(dest_para^.left^.resulttype^.deftype = orddef) and
{the following has to be changed to 64bit checking, once Val
returns 64 bit values (unless a special Val function is created
for that)}
{no need to rangecheck longints or cardinals on 32bit processors}
not((porddef(dest_para^.left^.resulttype)^.typ = s32bit) and
(porddef(dest_para^.left^.resulttype)^.low = $80000000) and
(porddef(dest_para^.left^.resulttype)^.high = $7fffffff)) and
not((porddef(dest_para^.left^.resulttype)^.typ = u32bit) and
(porddef(dest_para^.left^.resulttype)^.low = 0) and
(porddef(dest_para^.left^.resulttype)^.high = $ffffffff)) then
Begin
hp := getcopy(dest_para^.left);
hp^.location.loc := LOC_REGISTER;
hp^.location.register := hreg;
{do not register this temporary def}
OldRegisterDef := RegisterDef;
RegisterDef := False;
Case PordDef(dest_para^.left^.resulttype)^.typ of
u8bit,u16bit,u32bit: new(hdef,init(u32bit,0,$ffffffff));
s8bit,s16bit,s32bit: new(hdef,init(s32bit,$80000000,$7fffffff));
end;
hp^.resulttype := hdef;
emitrangecheck(hp,dest_para^.left^.resulttype);
hp^.right := nil;
Dispose(hp^.resulttype, Done);
RegisterDef := OldRegisterDef;
disposetree(hp);
End;
{dest_para^.right is already nil}
disposetree(dest_para);
UnGetIfTemp(hr);
end;
{$EndIf OLDVAL}
var
r : preference;
hp : ptree;
l : longint;
ispushed : boolean;
hregister : tregister;
otlabel,oflabel : pasmlabel;
oldpushedparasize : longint;
begin
{ save & reset pushedparasize }
oldpushedparasize:=pushedparasize;
pushedparasize:=0;
case p^.inlinenumber of
in_assert_x_y:
begin
otlabel:=truelabel;
oflabel:=falselabel;
getlabel(truelabel);
getlabel(falselabel);
secondpass(p^.left^.left);
if cs_do_assertion in aktlocalswitches then
begin
maketojumpbool(p^.left^.left);
emitlab(falselabel);
{ erroraddr }
exprasmlist^.concat(new(pai386,op_reg(A_PUSH,S_L,R_EBP)));
{ lineno }
exprasmlist^.concat(new(pai386,op_const(A_PUSH,S_L,aktfilepos.line)));
{ filename string }
hp:=genstringconstnode(current_module^.sourcefiles^.get_file_name(aktfilepos.fileindex));
secondpass(hp);
if codegenerror then
exit;
emitpushreferenceaddr(hp^.location.reference);
disposetree(hp);
{ push msg }
secondpass(p^.left^.right^.left);
emitpushreferenceaddr(p^.left^.right^.left^.location.reference);
{ call }
emitcall('FPC_ASSERT');
emitlab(truelabel);
end;
freelabel(truelabel);
freelabel(falselabel);
truelabel:=otlabel;
falselabel:=oflabel;
end;
in_lo_word,
in_hi_word :
begin
secondpass(p^.left);
p^.location.loc:=LOC_REGISTER;
if p^.left^.location.loc<>LOC_REGISTER then
begin
if p^.left^.location.loc=LOC_CREGISTER then
begin
p^.location.register:=reg32toreg16(getregister32);
emit_reg_reg(A_MOV,S_W,p^.left^.location.register,
p^.location.register);
end
else
begin
del_reference(p^.left^.location.reference);
p^.location.register:=reg32toreg16(getregister32);
exprasmlist^.concat(new(pai386,op_ref_reg(A_MOV,S_W,newreference(p^.left^.location.reference),
p^.location.register)));
end;
end
else p^.location.register:=p^.left^.location.register;
if p^.inlinenumber=in_hi_word then
exprasmlist^.concat(new(pai386,op_const_reg(A_SHR,S_W,8,p^.location.register)));
p^.location.register:=reg16toreg8(p^.location.register);
end;
in_sizeof_x,
in_typeof_x :
begin
{ for both cases load vmt }
if p^.left^.treetype=typen then
begin
p^.location.register:=getregister32;
exprasmlist^.concat(new(pai386,op_sym_ofs_reg(A_MOV,
S_L,newasmsymbol(pobjectdef(p^.left^.resulttype)^.vmt_mangledname),0,
p^.location.register)));
end
else
begin
secondpass(p^.left);
del_reference(p^.left^.location.reference);
p^.location.loc:=LOC_REGISTER;
p^.location.register:=getregister32;
{ load VMT pointer }
inc(p^.left^.location.reference.offset,
pobjectdef(p^.left^.resulttype)^.vmt_offset);
exprasmlist^.concat(new(pai386,op_ref_reg(A_MOV,S_L,
newreference(p^.left^.location.reference),
p^.location.register)));
end;
{ in sizeof load size }
if p^.inlinenumber=in_sizeof_x then
begin
new(r);
reset_reference(r^);
r^.base:=p^.location.register;
exprasmlist^.concat(new(pai386,op_ref_reg(A_MOV,S_L,r,
p^.location.register)));
end;
end;
in_lo_long,
in_hi_long :
begin
secondpass(p^.left);
p^.location.loc:=LOC_REGISTER;
if p^.left^.location.loc<>LOC_REGISTER then
begin
if p^.left^.location.loc=LOC_CREGISTER then
begin
p^.location.register:=getregister32;
emit_reg_reg(A_MOV,S_L,p^.left^.location.register,
p^.location.register);
end
else
begin
del_reference(p^.left^.location.reference);
p^.location.register:=getregister32;
exprasmlist^.concat(new(pai386,op_ref_reg(A_MOV,S_L,newreference(p^.left^.location.reference),
p^.location.register)));
end;
end
else p^.location.register:=p^.left^.location.register;
if p^.inlinenumber=in_hi_long then
exprasmlist^.concat(new(pai386,op_const_reg(A_SHR,S_L,16,p^.location.register)));
p^.location.register:=reg32toreg16(p^.location.register);
end;
in_length_string :
begin
secondpass(p^.left);
set_location(p^.location,p^.left^.location);
{ length in ansi strings is at offset -8 }
if is_ansistring(p^.left^.resulttype) then
dec(p^.location.reference.offset,8)
{ char is always 1, so make it a constant value }
else if is_char(p^.left^.resulttype) then
begin
clear_location(p^.location);
p^.location.loc:=LOC_MEM;
p^.location.reference.is_immediate:=true;
p^.location.reference.offset:=1;
end;
end;
in_pred_x,
in_succ_x:
begin
secondpass(p^.left);
if not (cs_check_overflow in aktlocalswitches) then
if p^.inlinenumber=in_pred_x then
asmop:=A_DEC
else
asmop:=A_INC
else
if p^.inlinenumber=in_pred_x then
asmop:=A_SUB
else
asmop:=A_ADD;
case p^.resulttype^.size of
4 : opsize:=S_L;
2 : opsize:=S_W;
1 : opsize:=S_B;
else
internalerror(10080);
end;
p^.location.loc:=LOC_REGISTER;
if p^.left^.location.loc<>LOC_REGISTER then
begin
p^.location.register:=getregister32;
if (p^.resulttype^.size=2) then
p^.location.register:=reg32toreg16(p^.location.register);
if (p^.resulttype^.size=1) then
p^.location.register:=reg32toreg8(p^.location.register);
if p^.left^.location.loc=LOC_CREGISTER then
emit_reg_reg(A_MOV,opsize,p^.left^.location.register,
p^.location.register)
else
if p^.left^.location.loc=LOC_FLAGS then
emit_flag2reg(p^.left^.location.resflags,p^.location.register)
else
begin
del_reference(p^.left^.location.reference);
exprasmlist^.concat(new(pai386,op_ref_reg(A_MOV,opsize,newreference(p^.left^.location.reference),
p^.location.register)));
end;
end
else p^.location.register:=p^.left^.location.register;
if not (cs_check_overflow in aktlocalswitches) then
exprasmlist^.concat(new(pai386,op_reg(asmop,opsize,
p^.location.register)))
else
exprasmlist^.concat(new(pai386,op_const_reg(asmop,opsize,1,
p^.location.register)));
emitoverflowcheck(p);
emitrangecheck(p,p^.resulttype);
end;
in_dec_x,
in_inc_x :
begin
{ set defaults }
addvalue:=1;
addconstant:=true;
{ load first parameter, must be a reference }
secondpass(p^.left^.left);
case p^.left^.left^.resulttype^.deftype of
orddef,
enumdef : begin
case p^.left^.left^.resulttype^.size of
1 : opsize:=S_B;
2 : opsize:=S_W;
4 : opsize:=S_L;
end;
end;
pointerdef : begin
opsize:=S_L;
if porddef(ppointerdef(p^.left^.left^.resulttype)^.definition)=voiddef then
addvalue:=1
else
addvalue:=ppointerdef(p^.left^.left^.resulttype)^.definition^.savesize;
end;
else
internalerror(10081);
end;
{ second argument specified?, must be a s32bit in register }
if assigned(p^.left^.right) then
begin
secondpass(p^.left^.right^.left);
{ when constant, just multiply the addvalue }
if is_constintnode(p^.left^.right^.left) then
addvalue:=addvalue*get_ordinal_value(p^.left^.right^.left)
else
begin
case p^.left^.right^.left^.location.loc of
LOC_REGISTER,
LOC_CREGISTER : hregister:=p^.left^.right^.left^.location.register;
LOC_MEM,
LOC_REFERENCE : begin
del_reference(p^.left^.right^.left^.location.reference);
hregister:=getregister32;
exprasmlist^.concat(new(pai386,op_ref_reg(A_MOV,S_L,
newreference(p^.left^.right^.left^.location.reference),hregister)));
end;
else
internalerror(10082);
end;
{ insert multiply with addvalue if its >1 }
if addvalue>1 then
exprasmlist^.concat(new(pai386,op_const_reg(A_IMUL,opsize,
addvalue,hregister)));
addconstant:=false;
end;
end;
{ write the add instruction }
if addconstant then
begin
if (addvalue=1) and not(cs_check_overflow in aktlocalswitches) then
begin
if p^.left^.left^.location.loc=LOC_CREGISTER then
exprasmlist^.concat(new(pai386,op_reg(incdecop[p^.inlinenumber],opsize,
p^.left^.left^.location.register)))
else
exprasmlist^.concat(new(pai386,op_ref(incdecop[p^.inlinenumber],opsize,
newreference(p^.left^.left^.location.reference))))
end
else
begin
if p^.left^.left^.location.loc=LOC_CREGISTER then
exprasmlist^.concat(new(pai386,op_const_reg(addsubop[p^.inlinenumber],opsize,
addvalue,p^.left^.left^.location.register)))
else
exprasmlist^.concat(new(pai386,op_const_ref(addsubop[p^.inlinenumber],opsize,
addvalue,newreference(p^.left^.left^.location.reference))));
end
end
else
begin
{ BUG HERE : detected with nasm :
hregister is allways 32 bit
it should be converted to 16 or 8 bit depending on op_size PM }
{ still not perfect :
if hregister is already a 16 bit reg ?? PM }
case opsize of
S_B : hregister:=reg32toreg8(hregister);
S_W : hregister:=reg32toreg16(hregister);
end;
if p^.left^.left^.location.loc=LOC_CREGISTER then
exprasmlist^.concat(new(pai386,op_reg_reg(addsubop[p^.inlinenumber],opsize,
hregister,p^.left^.left^.location.register)))
else
exprasmlist^.concat(new(pai386,op_reg_ref(addsubop[p^.inlinenumber],opsize,
hregister,newreference(p^.left^.left^.location.reference))));
case opsize of
S_B : hregister:=reg8toreg32(hregister);
S_W : hregister:=reg16toreg32(hregister);
end;
ungetregister32(hregister);
end;
emitoverflowcheck(p^.left^.left);
emitrangecheck(p^.left^.left,p^.left^.left^.resulttype);
end;
in_assigned_x :
begin
secondpass(p^.left^.left);
p^.location.loc:=LOC_FLAGS;
if (p^.left^.left^.location.loc in [LOC_REGISTER,LOC_CREGISTER]) then
begin
exprasmlist^.concat(new(pai386,op_reg_reg(A_OR,S_L,
p^.left^.left^.location.register,
p^.left^.left^.location.register)));
ungetregister32(p^.left^.left^.location.register);
end
else
begin
exprasmlist^.concat(new(pai386,op_const_ref(A_CMP,S_L,0,
newreference(p^.left^.left^.location.reference))));
del_reference(p^.left^.left^.location.reference);
end;
p^.location.resflags:=F_NE;
end;
in_reset_typedfile,in_rewrite_typedfile :
begin
pushusedregisters(pushed,$ff);
exprasmlist^.concat(new(pai386,op_const(A_PUSH,S_L,pfiledef(p^.left^.resulttype)^.typed_as^.size)));
secondpass(p^.left);
emitpushreferenceaddr(p^.left^.location.reference);
if p^.inlinenumber=in_reset_typedfile then
emitcall('FPC_RESET_TYPED')
else
emitcall('FPC_REWRITE_TYPED');
popusedregisters(pushed);
end;
in_write_x :
handlereadwrite(false,false);
in_writeln_x :
handlereadwrite(false,true);
in_read_x :
handlereadwrite(true,false);
in_readln_x :
handlereadwrite(true,true);
in_str_x_string :
begin
handle_str;
maybe_loadesi;
end;
{$IfnDef OLDVAL}
in_val_x :
Begin
handle_val;
End;
{$EndIf OLDVAL}
in_include_x_y,
in_exclude_x_y:
begin
secondpass(p^.left^.left);
if p^.left^.right^.left^.treetype=ordconstn then
begin
{ calculate bit position }
l:=1 shl (p^.left^.right^.left^.value mod 32);
{ determine operator }
if p^.inlinenumber=in_include_x_y then
asmop:=A_OR
else
begin
asmop:=A_AND;
l:=not(l);
end;
if (p^.left^.left^.location.loc=LOC_REFERENCE) then
begin
inc(p^.left^.left^.location.reference.offset,(p^.left^.right^.left^.value div 32)*4);
exprasmlist^.concat(new(pai386,op_const_ref(asmop,S_L,
l,newreference(p^.left^.left^.location.reference))));
del_reference(p^.left^.left^.location.reference);
end
else
{ LOC_CREGISTER }
exprasmlist^.concat(new(pai386,op_const_reg(asmop,S_L,
l,p^.left^.left^.location.register)));
end
else
begin
{ generate code for the element to set }
ispushed:=maybe_push(p^.left^.right^.left^.registers32,p^.left^.left);
secondpass(p^.left^.right^.left);
if ispushed then
restore(p^.left^.left);
{ determine asm operator }
if p^.inlinenumber=in_include_x_y then
asmop:=A_BTS
else
asmop:=A_BTR;
if psetdef(p^.left^.resulttype)^.settype=smallset then
begin
if p^.left^.right^.left^.location.loc in [LOC_CREGISTER,LOC_REGISTER] then
hregister:=p^.left^.right^.left^.location.register
else
begin
hregister:=R_EDI;
opsize:=def2def_opsize(p^.left^.right^.left^.resulttype,u32bitdef);
if opsize in [S_B,S_W,S_L] then
op:=A_MOV
else
op:=A_MOVZX;
exprasmlist^.concat(new(pai386,op_ref_reg(op,opsize,
newreference(p^.left^.right^.left^.location.reference),R_EDI)));
end;
if (p^.left^.left^.location.loc=LOC_REFERENCE) then
exprasmlist^.concat(new(pai386,op_reg_ref(asmop,S_L,hregister,
newreference(p^.left^.right^.left^.location.reference))))
else
exprasmlist^.concat(new(pai386,op_reg_reg(asmop,S_L,hregister,
p^.left^.right^.left^.location.register)));
end
else
begin
pushsetelement(p^.left^.right^.left);
{ normset is allways a ref }
emitpushreferenceaddr(p^.left^.left^.location.reference);
if p^.inlinenumber=in_include_x_y then
emitcall('FPC_SET_SET_BYTE')
else
emitcall('FPC_SET_UNSET_BYTE');
{CGMessage(cg_e_include_not_implemented);}
end;
end;
end;
else internalerror(9);
end;
{ reset pushedparasize }
pushedparasize:=oldpushedparasize;
end;
end.
{
$Log$
Revision 1.56 1999-05-31 12:43:32 peter
* fixed register allocation for storefuncresult
Revision 1.55 1999/05/27 19:44:13 peter
* removed oldasm
* plabel -> pasmlabel
* -a switches to source writing automaticly
* assembler readers OOPed
* asmsymbol automaticly external
* jumptables and other label fixes for asm readers
Revision 1.54 1999/05/23 19:55:11 florian
* qword/int64 multiplication fixed
+ qword/int64 subtraction
Revision 1.53 1999/05/23 18:42:01 florian
* better error recovering in typed constants
* some problems with arrays of const fixed, some problems
due my previous
- the location type of array constructor is now LOC_MEM
- the pushing of high fixed
- parameter copying fixed
- zero temp. allocation removed
* small problem in the assembler writers fixed:
ref to nil wasn't written correctly
Revision 1.52 1999/05/21 13:54:50 peter
* NEWLAB for label as symbol
Revision 1.51 1999/05/18 21:58:27 florian
* fixed some bugs related to temp. ansistrings and functions results
which return records/objects/arrays which need init/final.
Revision 1.50 1999/05/17 21:57:03 florian
* new temporary ansistring handling
Revision 1.49 1999/05/12 15:46:26 pierre
* handle_str disposetree was badly placed
Revision 1.48 1999/05/12 00:19:42 peter
* removed R_DEFAULT_SEG
* uniform float names
Revision 1.47 1999/05/06 09:05:13 peter
* generic write_float and str_float
* fixed constant float conversions
Revision 1.46 1999/05/05 16:18:20 jonas
* changes to handle_val so register vars are pushed/poped only once
Revision 1.45 1999/05/01 13:24:08 peter
* merged nasm compiler
* old asm moved to oldasm/
Revision 1.44 1999/04/26 18:28:13 peter
* better read/write array
Revision 1.43 1999/04/19 09:45:48 pierre
+ cdecl or stdcall push all args with longint size
* tempansi stuff cleaned up
Revision 1.42 1999/04/14 09:11:59 peter
* fixed include
Revision 1.41 1999/04/08 23:59:49 pierre
* temp string for val code freed
Revision 1.40 1999/04/08 15:57:46 peter
+ subrange checking for readln()
Revision 1.39 1999/04/07 15:31:16 pierre
* all formaldefs are now a sinlge definition
cformaldef (this was necessary for double_checksum)
+ small part of double_checksum code
Revision 1.38 1999/04/05 11:07:26 jonas
* fixed some typos in the constants of the range checking for Val
Revision 1.37 1999/04/01 22:07:51 peter
* universal string names (ansistr instead of stransi) for val/str
Revision 1.36 1999/04/01 06:21:04 jonas
* added initialization for has_32bit_code (caused problems with Val statement
without code parameter)
Revision 1.35 1999/03/31 20:30:49 michael
* fixed typo: odlval to oldval
Revision 1.34 1999/03/31 17:13:09 jonas
* bugfix for -Ox with internal val code
* internal val code now requires less free registers
* internal val code no longer needs a temp var for range checking
Revision 1.33 1999/03/26 00:24:15 peter
* last para changed to long for easier pushing with 4 byte aligns
Revision 1.32 1999/03/26 00:05:26 peter
* released valintern
+ deffile is now removed when compiling is finished
* ^( compiles now correct
+ static directive
* shrd fixed
Revision 1.31 1999/03/24 23:16:49 peter
* fixed bugs 212,222,225,227,229,231,233
Revision 1.30 1999/03/16 17:52:56 jonas
* changes for internal Val code (do a "make cycle OPT=-dvalintern" to test)
* in cgi386inl: also range checking for subrange types (compile with "-dreadrangecheck")
* in cgai386: also small fixes to emitrangecheck
Revision 1.29 1999/02/25 21:02:27 peter
* ag386bin updates
+ coff writer
Revision 1.28 1999/02/22 02:15:11 peter
* updates for ag386bin
Revision 1.27 1999/02/17 14:21:40 pierre
* unused local removed
Revision 1.26 1999/02/15 11:40:21 pierre
* pred/succ with overflow check must use ADD DEC !!
Revision 1.25 1999/02/05 10:56:19 florian
* in some cases a writeln of temp. ansistrings cause a memory leak, fixed
Revision 1.24 1999/01/21 22:10:39 peter
* fixed array of const
* generic platform independent high() support
Revision 1.23 1999/01/06 12:23:29 florian
* str(...) for ansi/long and widestrings fixed
Revision 1.22 1998/12/11 23:36:07 florian
+ again more stuff for int64/qword:
- comparision operators
- code generation for: str, read(ln), write(ln)
Revision 1.21 1998/12/11 00:02:50 peter
+ globtype,tokens,version unit splitted from globals
Revision 1.20 1998/11/27 14:50:32 peter
+ open strings, $P switch support
Revision 1.19 1998/11/26 13:10:40 peter
* new int - int conversion -dNEWCNV
* some function renamings
Revision 1.18 1998/11/24 17:04:27 peter
* fixed length(char) when char is a variable
Revision 1.17 1998/11/05 12:02:33 peter
* released useansistring
* removed -Sv, its now available in fpc modes
Revision 1.16 1998/10/22 17:11:13 pierre
+ terminated the include exclude implementation for i386
* enums inside records fixed
Revision 1.15 1998/10/21 15:12:50 pierre
* bug fix for IOCHECK inside a procedure with iocheck modifier
* removed the GPF for unexistant overloading
(firstcall was called with procedinition=nil !)
* changed typen to what Florian proposed
gentypenode(p : pdef) sets the typenodetype field
and resulttype is only set if inside bt_type block !
Revision 1.14 1998/10/20 08:06:40 pierre
* several memory corruptions due to double freemem solved
=> never use p^.loc.location:=p^.left^.loc.location;
+ finally I added now by default
that ra386dir translates global and unit symbols
+ added a first field in tsymtable and
a nextsym field in tsym
(this allows to obtain ordered type info for
records and objects in gdb !)
Revision 1.13 1998/10/13 16:50:02 pierre
* undid some changes of Peter that made the compiler wrong
for m68k (I had to reinsert some ifdefs)
* removed several memory leaks under m68k
* removed the meory leaks for assembler readers
* cross compiling shoud work again better
( crosscompiling sysamiga works
but as68k still complain about some code !)
Revision 1.12 1998/10/08 17:17:12 pierre
* current_module old scanner tagged as invalid if unit is recompiled
+ added ppheap for better info on tracegetmem of heaptrc
(adds line column and file index)
* several memory leaks removed ith help of heaptrc !!
Revision 1.11 1998/10/05 21:33:15 peter
* fixed 161,165,166,167,168
Revision 1.10 1998/10/05 12:32:44 peter
+ assert() support
Revision 1.8 1998/10/02 10:35:09 peter
* support for inc(pointer,value) which now increases with value instead
of 0*value :)
Revision 1.7 1998/09/21 08:45:07 pierre
+ added vmt_offset in tobjectdef.write for fututre use
(first steps to have objects without vmt if no virtual !!)
+ added fpu_used field for tabstractprocdef :
sets this level to 2 if the functions return with value in FPU
(is then set to correct value at parsing of implementation)
THIS MIGHT refuse some code with FPU expression too complex
that were accepted before and even in some cases
that don't overflow in fact
( like if f : float; is a forward that finally in implementation
only uses one fpu register !!)
Nevertheless I think that it will improve security on
FPU operations !!
* most other changes only for UseBrowser code
(added symtable references for record and objects)
local switch for refs to args and local of each function
(static symtable still missing)
UseBrowser still not stable and probably broken by
the definition hash array !!
Revision 1.6 1998/09/20 12:26:37 peter
* merged fixes
Revision 1.5 1998/09/17 09:42:15 peter
+ pass_2 for cg386
* Message() -> CGMessage() for pass_1/pass_2
Revision 1.4 1998/09/14 10:43:49 peter
* all internal RTL functions start with FPC_
Revision 1.3.2.1 1998/09/20 12:20:07 peter
* Fixed stack not on 4 byte boundary when doing a call
Revision 1.3 1998/09/05 23:03:57 florian
* some fixes to get -Or work:
- inc/dec didn't take care of CREGISTER
- register calculcation of inc/dec was wrong
- var/const parameters get now assigned 32 bit register, but
const parameters only if they are passed by reference !
Revision 1.2 1998/09/04 08:41:40 peter
* updated some error CGMessages
Revision 1.1 1998/08/31 12:22:14 peter
* secondinline moved to cg386inl
Revision 1.19 1998/08/31 08:52:03 peter
* fixed error 10 with succ() and pref()
Revision 1.18 1998/08/20 21:36:38 peter
* fixed 'with object do' bug
Revision 1.17 1998/08/19 16:07:36 jonas
* changed optimizer switches + cleanup of DestroyRefs in daopt386.pas
Revision 1.16 1998/08/18 09:24:36 pierre
* small warning position bug fixed
* support_mmx switches splitting was missing
* rhide error and warning output corrected
Revision 1.15 1998/08/13 11:00:09 peter
* fixed procedure<>procedure construct
Revision 1.14 1998/08/11 14:05:33 peter
* fixed sizeof(array of char)
Revision 1.13 1998/08/10 14:49:45 peter
+ localswitches, moduleswitches, globalswitches splitting
Revision 1.12 1998/07/30 13:30:31 florian
* final implemenation of exception support, maybe it needs
some fixes :)
Revision 1.11 1998/07/24 22:16:52 florian
* internal error 10 together with array access fixed. I hope
that's the final fix.
Revision 1.10 1998/07/18 22:54:23 florian
* some ansi/wide/longstring support fixed:
o parameter passing
o returning as result from functions
Revision 1.9 1998/07/07 17:40:37 peter
* packrecords 4 works
* word aligning of parameters
Revision 1.8 1998/07/06 15:51:15 michael
Added length checking for string reading
Revision 1.7 1998/07/06 14:19:51 michael
+ Added calls for reading/writing ansistrings
Revision 1.6 1998/07/01 15:28:48 peter
+ better writeln/readln handling, now 100% like tp7
Revision 1.5 1998/06/25 14:04:17 peter
+ internal inc/dec
Revision 1.4 1998/06/25 08:48:06 florian
* first version of rtti support
Revision 1.3 1998/06/09 16:01:33 pierre
+ added procedure directive parsing for procvars
(accepted are popstack cdecl and pascal)
+ added C vars with the following syntax
var C calias 'true_c_name';(can be followed by external)
reason is that you must add the Cprefix
which is target dependent
Revision 1.2 1998/06/08 13:13:29 pierre
+ temporary variables now in temp_gen.pas unit
because it is processor independent
* mppc68k.bat modified to undefine i386 and support_mmx
(which are defaults for i386)
Revision 1.1 1998/06/05 17:44:10 peter
* splitted cgi386
}