mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-26 16:33:41 +02:00

This is needed to be able to call simplify after pass_1, because otherwise the inserttypeconv's inserted new type conversions which where never firstpassed (if you want to do something like that, you have to return a new node as well so it will be firstpassed properly -- but in this case it seems this is not needed, since inserting those typeconversions are part of typechecking and not of simplifying, and typechecking must be done before simplifying anyway) git-svn-id: trunk@6836 -
2708 lines
109 KiB
ObjectPascal
2708 lines
109 KiB
ObjectPascal
{
|
|
Copyright (c) 1998-2002 by Florian Klaempfl
|
|
|
|
Type checking and register allocation for add 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 nadd;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
{ define addstringopt}
|
|
|
|
interface
|
|
|
|
uses
|
|
node,symtype;
|
|
|
|
type
|
|
taddnode = class(tbinopnode)
|
|
resultrealdef : tdef;
|
|
constructor create(tt : tnodetype;l,r : tnode);override;
|
|
function pass_1 : tnode;override;
|
|
function pass_typecheck:tnode;override;
|
|
function simplify : tnode;override;
|
|
{$ifdef state_tracking}
|
|
function track_state_pass(exec_known:boolean):boolean;override;
|
|
{$endif}
|
|
protected
|
|
{ override the following if you want to implement }
|
|
{ parts explicitely in the code generator (JM) }
|
|
function first_addstring: tnode; virtual;
|
|
function first_addset: tnode; virtual;
|
|
{ only implements "muln" nodes, the rest always has to be done in }
|
|
{ the code generator for performance reasons (JM) }
|
|
function first_add64bitint: tnode; virtual;
|
|
|
|
{ override and return false if you can handle 32x32->64 }
|
|
{ bit multiplies directly in your code generator. If }
|
|
{ this function is overridden to return false, you can }
|
|
{ get multiplies with left/right both s32bit or u32bit, }
|
|
{ and resultdef of the muln s64bit or u64bit }
|
|
function use_generic_mul32to64: boolean; virtual;
|
|
|
|
{ This routine calls internal runtime library helpers
|
|
for all floating point arithmetic in the case
|
|
where the emulation switches is on. Otherwise
|
|
returns nil, and everything must be done in
|
|
the code generation phase.
|
|
}
|
|
function first_addfloat : tnode; virtual;
|
|
private
|
|
{ checks whether a muln can be calculated as a 32bit }
|
|
{ * 32bit -> 64 bit }
|
|
function try_make_mul32to64: boolean;
|
|
end;
|
|
taddnodeclass = class of taddnode;
|
|
|
|
var
|
|
{ caddnode is used to create nodes of the add type }
|
|
{ the virtual constructor allows to assign }
|
|
{ another class type to caddnode => processor }
|
|
{ specific node types can be created }
|
|
caddnode : taddnodeclass;
|
|
|
|
implementation
|
|
|
|
uses
|
|
{$IFNDEF USE_FAKE_SYSUTILS}
|
|
sysutils,
|
|
{$ELSE}
|
|
fksysutl,
|
|
{$ENDIF}
|
|
globtype,systems,
|
|
cutils,verbose,globals,widestr,
|
|
symconst,symdef,symsym,symtable,defutil,defcmp,
|
|
cgbase,
|
|
htypechk,pass_1,
|
|
nld,nbas,nmat,ncnv,ncon,nset,nopt,ncal,ninl,nmem,nutils,
|
|
{$ifdef state_tracking}
|
|
nstate,
|
|
{$endif}
|
|
cpuinfo,procinfo;
|
|
|
|
|
|
{*****************************************************************************
|
|
TADDNODE
|
|
*****************************************************************************}
|
|
|
|
{$maxfpuregisters 0}
|
|
|
|
function getbestreal(t1,t2 : tdef) : tdef;
|
|
const
|
|
floatweight : array[tfloattype] of byte =
|
|
(2,3,4,0,1,5);
|
|
begin
|
|
if t1.typ=floatdef then
|
|
begin
|
|
result:=t1;
|
|
if t2.typ=floatdef then
|
|
begin
|
|
{ when a comp or currency is used, use always the
|
|
best float type to calculate the result }
|
|
if (tfloatdef(t2).floattype in [s64comp,s64currency]) or
|
|
(tfloatdef(t2).floattype in [s64comp,s64currency]) then
|
|
result:=pbestrealtype^
|
|
else
|
|
if floatweight[tfloatdef(t2).floattype]>floatweight[tfloatdef(t1).floattype] then
|
|
result:=t2;
|
|
end;
|
|
end
|
|
else if t2.typ=floatdef then
|
|
result:=t2
|
|
else internalerror(200508061);
|
|
end;
|
|
|
|
|
|
constructor taddnode.create(tt : tnodetype;l,r : tnode);
|
|
begin
|
|
inherited create(tt,l,r);
|
|
end;
|
|
|
|
|
|
function taddnode.simplify : tnode;
|
|
var
|
|
t : tnode;
|
|
lt,rt : tnodetype;
|
|
rd,ld : tdef;
|
|
rv,lv : tconstexprint;
|
|
rvd,lvd : bestreal;
|
|
ws1,ws2 : pcompilerwidestring;
|
|
concatstrings : boolean;
|
|
c1,c2 : array[0..1] of char;
|
|
s1,s2 : pchar;
|
|
l1,l2 : longint;
|
|
resultset : Tconstset;
|
|
b : boolean;
|
|
begin
|
|
result:=nil;
|
|
|
|
{ load easier access variables }
|
|
rd:=right.resultdef;
|
|
ld:=left.resultdef;
|
|
rt:=right.nodetype;
|
|
lt:=left.nodetype;
|
|
|
|
if (nodetype = slashn) and
|
|
(((rt = ordconstn) and
|
|
(tordconstnode(right).value = 0)) or
|
|
((rt = realconstn) and
|
|
(trealconstnode(right).value_real = 0.0))) then
|
|
begin
|
|
if (cs_check_range in current_settings.localswitches) or
|
|
(cs_check_overflow in current_settings.localswitches) then
|
|
begin
|
|
result:=crealconstnode.create(1,pbestrealtype^);
|
|
Message(parser_e_division_by_zero);
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
|
|
{ both are int constants }
|
|
if (
|
|
(
|
|
is_constintnode(left) and
|
|
is_constintnode(right)
|
|
) or
|
|
(
|
|
is_constboolnode(left) and
|
|
is_constboolnode(right) and
|
|
(nodetype in [slashn,ltn,lten,gtn,gten,equaln,unequaln,andn,xorn,orn])
|
|
) or
|
|
(
|
|
is_constenumnode(left) and
|
|
is_constenumnode(right) and
|
|
allowenumop(nodetype))
|
|
) or
|
|
(
|
|
(lt = pointerconstn) and
|
|
is_constintnode(right) and
|
|
(nodetype in [addn,subn])
|
|
) or
|
|
(
|
|
(lt in [pointerconstn,niln]) and
|
|
(rt in [pointerconstn,niln]) and
|
|
(nodetype in [ltn,lten,gtn,gten,equaln,unequaln,subn])
|
|
) then
|
|
begin
|
|
t:=nil;
|
|
{ when comparing/substracting pointers, make sure they are }
|
|
{ of the same type (JM) }
|
|
if (lt = pointerconstn) and (rt = pointerconstn) then
|
|
begin
|
|
if not(cs_extsyntax in current_settings.moduleswitches) and
|
|
not(nodetype in [equaln,unequaln]) then
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename)
|
|
else
|
|
if (nodetype <> subn) and
|
|
is_voidpointer(rd) then
|
|
inserttypeconv(right,left.resultdef)
|
|
else if (nodetype <> subn) and
|
|
is_voidpointer(ld) then
|
|
inserttypeconv(left,right.resultdef)
|
|
else if not(equal_defs(ld,rd)) then
|
|
IncompatibleTypes(ld,rd);
|
|
end
|
|
else if (ld.typ=enumdef) and (rd.typ=enumdef) then
|
|
begin
|
|
if not(equal_defs(ld,rd)) then
|
|
inserttypeconv(right,left.resultdef);
|
|
end;
|
|
|
|
{ load values }
|
|
case lt of
|
|
ordconstn:
|
|
lv:=tordconstnode(left).value;
|
|
pointerconstn:
|
|
lv:=tpointerconstnode(left).value;
|
|
niln:
|
|
lv:=0;
|
|
else
|
|
internalerror(2002080202);
|
|
end;
|
|
case rt of
|
|
ordconstn:
|
|
rv:=tordconstnode(right).value;
|
|
pointerconstn:
|
|
rv:=tpointerconstnode(right).value;
|
|
niln:
|
|
rv:=0;
|
|
else
|
|
internalerror(2002080203);
|
|
end;
|
|
if (lt = pointerconstn) and
|
|
(rt <> pointerconstn) then
|
|
rv := rv * tpointerdef(left.resultdef).pointeddef.size;
|
|
if (rt = pointerconstn) and
|
|
(lt <> pointerconstn) then
|
|
lv := lv * tpointerdef(right.resultdef).pointeddef.size;
|
|
case nodetype of
|
|
addn :
|
|
begin
|
|
{$ifopt Q-}
|
|
{$define OVERFLOW_OFF}
|
|
{$Q+}
|
|
{$endif}
|
|
try
|
|
if (lt=pointerconstn) then
|
|
t := cpointerconstnode.create(lv+rv,left.resultdef)
|
|
else
|
|
if is_integer(ld) then
|
|
t := genintconstnode(lv+rv)
|
|
else
|
|
t := cordconstnode.create(lv+rv,left.resultdef,(ld.typ<>enumdef));
|
|
except
|
|
on E:EIntOverflow do
|
|
begin
|
|
Message(parser_e_arithmetic_operation_overflow);
|
|
{ Recover }
|
|
t:=genintconstnode(0)
|
|
end;
|
|
end;
|
|
{$ifdef OVERFLOW_OFF}
|
|
{$Q-}
|
|
{$undef OVERFLOW_OFF}
|
|
{$endif}
|
|
end;
|
|
subn :
|
|
begin
|
|
{$ifopt Q-}
|
|
{$define OVERFLOW_OFF}
|
|
{$Q+}
|
|
{$endif}
|
|
try
|
|
if (lt=pointerconstn) then
|
|
begin
|
|
{ pointer-pointer results in an integer }
|
|
if (rt=pointerconstn) then
|
|
t := genintconstnode((lv-rv) div tpointerdef(ld).pointeddef.size)
|
|
else
|
|
t := cpointerconstnode.create(lv-rv,left.resultdef);
|
|
end
|
|
else
|
|
begin
|
|
if is_integer(ld) then
|
|
t:=genintconstnode(lv-rv)
|
|
else
|
|
t:=cordconstnode.create(lv-rv,left.resultdef,(ld.typ<>enumdef));
|
|
end;
|
|
except
|
|
on E:EIntOverflow do
|
|
begin
|
|
Message(parser_e_arithmetic_operation_overflow);
|
|
{ Recover }
|
|
t:=genintconstnode(0)
|
|
end;
|
|
end;
|
|
{$ifdef OVERFLOW_OFF}
|
|
{$Q-}
|
|
{$undef OVERFLOW_OFF}
|
|
{$endif}
|
|
end;
|
|
muln :
|
|
begin
|
|
{$ifopt Q-}
|
|
{$define OVERFLOW_OFF}
|
|
{$Q+}
|
|
{$endif}
|
|
try
|
|
if (torddef(ld).ordtype <> u64bit) or
|
|
(torddef(rd).ordtype <> u64bit) then
|
|
t:=genintconstnode(lv*rv)
|
|
else
|
|
t:=genintconstnode(int64(qword(lv)*qword(rv)));
|
|
except
|
|
on E:EIntOverflow do
|
|
begin
|
|
Message(parser_e_arithmetic_operation_overflow);
|
|
{ Recover }
|
|
t:=genintconstnode(0)
|
|
end;
|
|
end;
|
|
{$ifdef OVERFLOW_OFF}
|
|
{$Q-}
|
|
{$undef OVERFLOW_OFF}
|
|
{$endif}
|
|
end;
|
|
xorn :
|
|
if is_integer(ld) then
|
|
t:=genintconstnode(lv xor rv)
|
|
else
|
|
t:=cordconstnode.create(lv xor rv,left.resultdef,true);
|
|
orn :
|
|
if is_integer(ld) then
|
|
t:=genintconstnode(lv or rv)
|
|
else
|
|
t:=cordconstnode.create(lv or rv,left.resultdef,true);
|
|
andn :
|
|
if is_integer(ld) then
|
|
t:=genintconstnode(lv and rv)
|
|
else
|
|
t:=cordconstnode.create(lv and rv,left.resultdef,true);
|
|
ltn :
|
|
t:=cordconstnode.create(ord(lv<rv),booltype,true);
|
|
lten :
|
|
t:=cordconstnode.create(ord(lv<=rv),booltype,true);
|
|
gtn :
|
|
t:=cordconstnode.create(ord(lv>rv),booltype,true);
|
|
gten :
|
|
t:=cordconstnode.create(ord(lv>=rv),booltype,true);
|
|
equaln :
|
|
t:=cordconstnode.create(ord(lv=rv),booltype,true);
|
|
unequaln :
|
|
t:=cordconstnode.create(ord(lv<>rv),booltype,true);
|
|
slashn :
|
|
begin
|
|
{ int/int becomes a real }
|
|
rvd:=rv;
|
|
lvd:=lv;
|
|
t:=crealconstnode.create(lvd/rvd,resultrealdef);
|
|
end;
|
|
else
|
|
begin
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
t:=cnothingnode.create;
|
|
end;
|
|
end;
|
|
result:=t;
|
|
exit;
|
|
end;
|
|
|
|
{ both real constants ? }
|
|
if (lt=realconstn) and (rt=realconstn) then
|
|
begin
|
|
lvd:=trealconstnode(left).value_real;
|
|
rvd:=trealconstnode(right).value_real;
|
|
case nodetype of
|
|
addn :
|
|
t:=crealconstnode.create(lvd+rvd,resultrealdef);
|
|
subn :
|
|
t:=crealconstnode.create(lvd-rvd,resultrealdef);
|
|
muln :
|
|
t:=crealconstnode.create(lvd*rvd,resultrealdef);
|
|
starstarn,
|
|
caretn :
|
|
begin
|
|
if lvd<0 then
|
|
begin
|
|
Message(parser_e_invalid_float_operation);
|
|
t:=crealconstnode.create(0,resultrealdef);
|
|
end
|
|
else if lvd=0 then
|
|
t:=crealconstnode.create(1.0,resultrealdef)
|
|
else
|
|
t:=crealconstnode.create(exp(ln(lvd)*rvd),resultrealdef);
|
|
end;
|
|
slashn :
|
|
t:=crealconstnode.create(lvd/rvd,resultrealdef);
|
|
ltn :
|
|
t:=cordconstnode.create(ord(lvd<rvd),booltype,true);
|
|
lten :
|
|
t:=cordconstnode.create(ord(lvd<=rvd),booltype,true);
|
|
gtn :
|
|
t:=cordconstnode.create(ord(lvd>rvd),booltype,true);
|
|
gten :
|
|
t:=cordconstnode.create(ord(lvd>=rvd),booltype,true);
|
|
equaln :
|
|
t:=cordconstnode.create(ord(lvd=rvd),booltype,true);
|
|
unequaln :
|
|
t:=cordconstnode.create(ord(lvd<>rvd),booltype,true);
|
|
else
|
|
begin
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
t:=cnothingnode.create;
|
|
end;
|
|
end;
|
|
result:=t;
|
|
exit;
|
|
end;
|
|
|
|
{ first, we handle widestrings, so we can check later for }
|
|
{ stringconstn only }
|
|
|
|
{ widechars are converted above to widestrings too }
|
|
{ this isn't veryy efficient, but I don't think }
|
|
{ that it does matter that much (FK) }
|
|
if (lt=stringconstn) and (rt=stringconstn) and
|
|
(tstringconstnode(left).cst_type=cst_widestring) and
|
|
(tstringconstnode(right).cst_type=cst_widestring) then
|
|
begin
|
|
initwidestring(ws1);
|
|
initwidestring(ws2);
|
|
copywidestring(pcompilerwidestring(tstringconstnode(left).value_str),ws1);
|
|
copywidestring(pcompilerwidestring(tstringconstnode(right).value_str),ws2);
|
|
case nodetype of
|
|
addn :
|
|
begin
|
|
concatwidestrings(ws1,ws2);
|
|
t:=cstringconstnode.createwstr(ws1);
|
|
end;
|
|
ltn :
|
|
t:=cordconstnode.create(byte(comparewidestrings(ws1,ws2)<0),booltype,true);
|
|
lten :
|
|
t:=cordconstnode.create(byte(comparewidestrings(ws1,ws2)<=0),booltype,true);
|
|
gtn :
|
|
t:=cordconstnode.create(byte(comparewidestrings(ws1,ws2)>0),booltype,true);
|
|
gten :
|
|
t:=cordconstnode.create(byte(comparewidestrings(ws1,ws2)>=0),booltype,true);
|
|
equaln :
|
|
t:=cordconstnode.create(byte(comparewidestrings(ws1,ws2)=0),booltype,true);
|
|
unequaln :
|
|
t:=cordconstnode.create(byte(comparewidestrings(ws1,ws2)<>0),booltype,true);
|
|
else
|
|
begin
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
t:=cnothingnode.create;
|
|
end;
|
|
end;
|
|
donewidestring(ws1);
|
|
donewidestring(ws2);
|
|
result:=t;
|
|
exit;
|
|
end;
|
|
|
|
{ concating strings ? }
|
|
concatstrings:=false;
|
|
|
|
if (lt=ordconstn) and (rt=ordconstn) and
|
|
is_char(ld) and is_char(rd) then
|
|
begin
|
|
c1[0]:=char(byte(tordconstnode(left).value));
|
|
c1[1]:=#0;
|
|
l1:=1;
|
|
c2[0]:=char(byte(tordconstnode(right).value));
|
|
c2[1]:=#0;
|
|
l2:=1;
|
|
s1:=@c1[0];
|
|
s2:=@c2[0];
|
|
concatstrings:=true;
|
|
end
|
|
else if (lt=stringconstn) and (rt=ordconstn) and is_char(rd) then
|
|
begin
|
|
s1:=tstringconstnode(left).value_str;
|
|
l1:=tstringconstnode(left).len;
|
|
c2[0]:=char(byte(tordconstnode(right).value));
|
|
c2[1]:=#0;
|
|
s2:=@c2[0];
|
|
l2:=1;
|
|
concatstrings:=true;
|
|
end
|
|
else if (lt=ordconstn) and (rt=stringconstn) and is_char(ld) then
|
|
begin
|
|
c1[0]:=char(byte(tordconstnode(left).value));
|
|
c1[1]:=#0;
|
|
l1:=1;
|
|
s1:=@c1[0];
|
|
s2:=tstringconstnode(right).value_str;
|
|
l2:=tstringconstnode(right).len;
|
|
concatstrings:=true;
|
|
end
|
|
else if (lt=stringconstn) and (rt=stringconstn) then
|
|
begin
|
|
s1:=tstringconstnode(left).value_str;
|
|
l1:=tstringconstnode(left).len;
|
|
s2:=tstringconstnode(right).value_str;
|
|
l2:=tstringconstnode(right).len;
|
|
concatstrings:=true;
|
|
end;
|
|
if concatstrings then
|
|
begin
|
|
case nodetype of
|
|
addn :
|
|
t:=cstringconstnode.createpchar(concatansistrings(s1,s2,l1,l2),l1+l2);
|
|
ltn :
|
|
t:=cordconstnode.create(byte(compareansistrings(s1,s2,l1,l2)<0),booltype,true);
|
|
lten :
|
|
t:=cordconstnode.create(byte(compareansistrings(s1,s2,l1,l2)<=0),booltype,true);
|
|
gtn :
|
|
t:=cordconstnode.create(byte(compareansistrings(s1,s2,l1,l2)>0),booltype,true);
|
|
gten :
|
|
t:=cordconstnode.create(byte(compareansistrings(s1,s2,l1,l2)>=0),booltype,true);
|
|
equaln :
|
|
t:=cordconstnode.create(byte(compareansistrings(s1,s2,l1,l2)=0),booltype,true);
|
|
unequaln :
|
|
t:=cordconstnode.create(byte(compareansistrings(s1,s2,l1,l2)<>0),booltype,true);
|
|
else
|
|
begin
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
t:=cnothingnode.create;
|
|
end;
|
|
end;
|
|
result:=t;
|
|
exit;
|
|
end;
|
|
|
|
{ set constant evaluation }
|
|
if (right.nodetype=setconstn) and
|
|
not assigned(tsetconstnode(right).left) and
|
|
(left.nodetype=setconstn) and
|
|
not assigned(tsetconstnode(left).left) then
|
|
begin
|
|
{ check if size adjusting is needed, only for left
|
|
to right as the other way is checked in the typeconv }
|
|
if (tsetdef(right.resultdef).settype=smallset) and
|
|
(tsetdef(left.resultdef).settype<>smallset) then
|
|
right.resultdef:=tsetdef.create(tsetdef(right.resultdef).elementdef,255);
|
|
{ check base types }
|
|
inserttypeconv(left,right.resultdef);
|
|
|
|
if codegenerror then
|
|
begin
|
|
{ recover by only returning the left part }
|
|
result:=left;
|
|
left:=nil;
|
|
exit;
|
|
end;
|
|
case nodetype of
|
|
addn :
|
|
begin
|
|
resultset:=tsetconstnode(right).value_set^ + tsetconstnode(left).value_set^;
|
|
t:=csetconstnode.create(@resultset,left.resultdef);
|
|
end;
|
|
muln :
|
|
begin
|
|
resultset:=tsetconstnode(right).value_set^ * tsetconstnode(left).value_set^;
|
|
t:=csetconstnode.create(@resultset,left.resultdef);
|
|
end;
|
|
subn :
|
|
begin
|
|
resultset:=tsetconstnode(left).value_set^ - tsetconstnode(right).value_set^;
|
|
t:=csetconstnode.create(@resultset,left.resultdef);
|
|
end;
|
|
symdifn :
|
|
begin
|
|
resultset:=tsetconstnode(right).value_set^ >< tsetconstnode(left).value_set^;
|
|
t:=csetconstnode.create(@resultset,left.resultdef);
|
|
end;
|
|
unequaln :
|
|
begin
|
|
b:=tsetconstnode(right).value_set^ <> tsetconstnode(left).value_set^;
|
|
t:=cordconstnode.create(byte(b),booltype,true);
|
|
end;
|
|
equaln :
|
|
begin
|
|
b:=tsetconstnode(right).value_set^ = tsetconstnode(left).value_set^;
|
|
t:=cordconstnode.create(byte(b),booltype,true);
|
|
end;
|
|
lten :
|
|
begin
|
|
b:=tsetconstnode(left).value_set^ <= tsetconstnode(right).value_set^;
|
|
t:=cordconstnode.create(byte(b),booltype,true);
|
|
end;
|
|
gten :
|
|
begin
|
|
b:=tsetconstnode(left).value_set^ >= tsetconstnode(right).value_set^;
|
|
t:=cordconstnode.create(byte(b),booltype,true);
|
|
end;
|
|
else
|
|
begin
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
t:=cnothingnode.create;
|
|
end;
|
|
end;
|
|
result:=t;
|
|
exit;
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
function taddnode.pass_typecheck:tnode;
|
|
var
|
|
hp : tnode;
|
|
lt,rt : tnodetype;
|
|
rd,ld : tdef;
|
|
ot : tnodetype;
|
|
hsym : tfieldvarsym;
|
|
i : longint;
|
|
strtype : tstringtype;
|
|
b : boolean;
|
|
{$ifdef state_tracking}
|
|
factval : Tnode;
|
|
change : boolean;
|
|
{$endif}
|
|
|
|
begin
|
|
result:=nil;
|
|
{ first do the two subtrees }
|
|
typecheckpass(left);
|
|
typecheckpass(right);
|
|
{ both left and right need to be valid }
|
|
set_varstate(left,vs_read,[vsf_must_be_valid]);
|
|
set_varstate(right,vs_read,[vsf_must_be_valid]);
|
|
if codegenerror then
|
|
exit;
|
|
|
|
{ tp procvar support }
|
|
maybe_call_procvar(left,true);
|
|
maybe_call_procvar(right,true);
|
|
|
|
{ convert array constructors to sets, because there is no other operator
|
|
possible for array constructors }
|
|
if is_array_constructor(left.resultdef) then
|
|
begin
|
|
arrayconstructor_to_set(left);
|
|
typecheckpass(left);
|
|
end;
|
|
if is_array_constructor(right.resultdef) then
|
|
begin
|
|
arrayconstructor_to_set(right);
|
|
typecheckpass(right);
|
|
end;
|
|
|
|
{ allow operator overloading }
|
|
hp:=self;
|
|
if isbinaryoverloaded(hp) then
|
|
begin
|
|
result:=hp;
|
|
exit;
|
|
end;
|
|
{ Stop checking when an error was found in the operator checking }
|
|
if codegenerror then
|
|
begin
|
|
result:=cerrornode.create;
|
|
exit;
|
|
end;
|
|
|
|
|
|
{ Kylix allows enum+ordconstn in an enum declaration (blocktype
|
|
is bt_type), we need to do the conversion here before the
|
|
constant folding }
|
|
if (m_delphi in current_settings.modeswitches) and
|
|
(blocktype=bt_type) then
|
|
begin
|
|
if (left.resultdef.typ=enumdef) and
|
|
(right.resultdef.typ=orddef) then
|
|
begin
|
|
{ insert explicit typecast to default signed int }
|
|
left:=ctypeconvnode.create_internal(left,sinttype);
|
|
typecheckpass(left);
|
|
end
|
|
else
|
|
if (left.resultdef.typ=orddef) and
|
|
(right.resultdef.typ=enumdef) then
|
|
begin
|
|
{ insert explicit typecast to default signed int }
|
|
right:=ctypeconvnode.create_internal(right,sinttype);
|
|
typecheckpass(right);
|
|
end;
|
|
end;
|
|
|
|
{ is one a real float, then both need to be floats, this
|
|
need to be done before the constant folding so constant
|
|
operation on a float and int are also handled }
|
|
resultrealdef:=pbestrealtype^;
|
|
if (right.resultdef.typ=floatdef) or (left.resultdef.typ=floatdef) then
|
|
begin
|
|
{ when both floattypes are already equal then use that
|
|
floattype for results }
|
|
if (right.resultdef.typ=floatdef) and
|
|
(left.resultdef.typ=floatdef) and
|
|
(tfloatdef(left.resultdef).floattype=tfloatdef(right.resultdef).floattype) then
|
|
resultrealdef:=left.resultdef
|
|
{ when there is a currency type then use currency, but
|
|
only when currency is defined as float }
|
|
else
|
|
if (is_currency(right.resultdef) or
|
|
is_currency(left.resultdef)) and
|
|
((s64currencytype.typ = floatdef) or
|
|
(nodetype <> slashn)) then
|
|
begin
|
|
resultrealdef:=s64currencytype;
|
|
inserttypeconv(right,resultrealdef);
|
|
inserttypeconv(left,resultrealdef);
|
|
end
|
|
else
|
|
begin
|
|
resultrealdef:=getbestreal(left.resultdef,right.resultdef);
|
|
inserttypeconv(right,resultrealdef);
|
|
inserttypeconv(left,resultrealdef);
|
|
end;
|
|
end;
|
|
|
|
{ If both operands are constant and there is a widechar
|
|
or widestring then convert everything to widestring. This
|
|
allows constant folding like char+widechar }
|
|
if is_constnode(right) and is_constnode(left) and
|
|
(is_widestring(right.resultdef) or
|
|
is_widestring(left.resultdef) or
|
|
is_widechar(right.resultdef) or
|
|
is_widechar(left.resultdef)) then
|
|
begin
|
|
inserttypeconv(right,cwidestringtype);
|
|
inserttypeconv(left,cwidestringtype);
|
|
end;
|
|
|
|
result:=simplify;
|
|
if assigned(result) then
|
|
exit;
|
|
|
|
{ load easier access variables }
|
|
rd:=right.resultdef;
|
|
ld:=left.resultdef;
|
|
rt:=right.nodetype;
|
|
lt:=left.nodetype;
|
|
|
|
{ but an int/int gives real/real! }
|
|
if (nodetype=slashn) and not(is_vector(left.resultdef)) and not(is_vector(right.resultdef)) then
|
|
begin
|
|
if is_currency(left.resultdef) and
|
|
is_currency(right.resultdef) then
|
|
{ In case of currency, converting to float means dividing by 10000 }
|
|
{ However, since this is already a division, both divisions by }
|
|
{ 10000 are eliminated when we divide the results -> we can skip }
|
|
{ them. }
|
|
if s64currencytype.typ = floatdef then
|
|
begin
|
|
{ there's no s64comptype or so, how do we avoid the type conversion?
|
|
left.resultdef := s64comptype;
|
|
right.resultdef := s64comptype; }
|
|
end
|
|
else
|
|
begin
|
|
left.resultdef := s64inttype;
|
|
right.resultdef := s64inttype;
|
|
end
|
|
else if (left.resultdef.typ <> floatdef) and
|
|
(right.resultdef.typ <> floatdef) then
|
|
CGMessage(type_h_use_div_for_int);
|
|
inserttypeconv(right,resultrealdef);
|
|
inserttypeconv(left,resultrealdef);
|
|
end
|
|
|
|
{ if both are orddefs then check sub types }
|
|
else if (ld.typ=orddef) and (rd.typ=orddef) then
|
|
begin
|
|
{ optimize multiplacation by a power of 2 }
|
|
if not(cs_check_overflow in current_settings.localswitches) and
|
|
(nodetype = muln) and
|
|
(((left.nodetype = ordconstn) and
|
|
ispowerof2(tordconstnode(left).value,i)) or
|
|
((right.nodetype = ordconstn) and
|
|
ispowerof2(tordconstnode(right).value,i))) then
|
|
begin
|
|
if left.nodetype = ordconstn then
|
|
begin
|
|
tordconstnode(left).value := i;
|
|
result := cshlshrnode.create(shln,right,left);
|
|
end
|
|
else
|
|
begin
|
|
tordconstnode(right).value := i;
|
|
result := cshlshrnode.create(shln,left,right);
|
|
end;
|
|
left := nil;
|
|
right := nil;
|
|
exit;
|
|
end;
|
|
|
|
{ set for & and | operations in macpas mode: they only work on }
|
|
{ booleans, and always short circuit evaluation }
|
|
if (nf_short_bool in flags) then
|
|
begin
|
|
if not is_boolean(ld) then
|
|
begin
|
|
inserttypeconv(left,booltype);
|
|
ld := left.resultdef;
|
|
end;
|
|
if not is_boolean(rd) then
|
|
begin
|
|
inserttypeconv(right,booltype);
|
|
rd := right.resultdef;
|
|
end;
|
|
end;
|
|
|
|
{ 2 booleans? Make them equal to the largest boolean }
|
|
if (is_boolean(ld) and is_boolean(rd)) or
|
|
(nf_short_bool in flags) then
|
|
begin
|
|
if torddef(left.resultdef).size>torddef(right.resultdef).size then
|
|
begin
|
|
right:=ctypeconvnode.create_internal(right,left.resultdef);
|
|
ttypeconvnode(right).convtype:=tc_bool_2_bool;
|
|
typecheckpass(right);
|
|
end
|
|
else if torddef(left.resultdef).size<torddef(right.resultdef).size then
|
|
begin
|
|
left:=ctypeconvnode.create_internal(left,right.resultdef);
|
|
ttypeconvnode(left).convtype:=tc_bool_2_bool;
|
|
typecheckpass(left);
|
|
end;
|
|
case nodetype of
|
|
xorn,
|
|
ltn,
|
|
lten,
|
|
gtn,
|
|
gten,
|
|
andn,
|
|
orn:
|
|
begin
|
|
end;
|
|
unequaln,
|
|
equaln:
|
|
begin
|
|
if not(cs_full_boolean_eval in current_settings.localswitches) or
|
|
(nf_short_bool in flags) then
|
|
begin
|
|
{ Remove any compares with constants }
|
|
if (left.nodetype=ordconstn) then
|
|
begin
|
|
hp:=right;
|
|
b:=(tordconstnode(left).value<>0);
|
|
ot:=nodetype;
|
|
left.free;
|
|
left:=nil;
|
|
right:=nil;
|
|
if (not(b) and (ot=equaln)) or
|
|
(b and (ot=unequaln)) then
|
|
begin
|
|
hp:=cnotnode.create(hp);
|
|
end;
|
|
result:=hp;
|
|
exit;
|
|
end;
|
|
if (right.nodetype=ordconstn) then
|
|
begin
|
|
hp:=left;
|
|
b:=(tordconstnode(right).value<>0);
|
|
ot:=nodetype;
|
|
right.free;
|
|
right:=nil;
|
|
left:=nil;
|
|
if (not(b) and (ot=equaln)) or
|
|
(b and (ot=unequaln)) then
|
|
begin
|
|
hp:=cnotnode.create(hp);
|
|
end;
|
|
result:=hp;
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
else
|
|
begin
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
result:=cnothingnode.create;
|
|
exit;
|
|
end;
|
|
end;
|
|
end
|
|
{ Both are chars? }
|
|
else if is_char(rd) and is_char(ld) then
|
|
begin
|
|
if nodetype=addn then
|
|
begin
|
|
resultdef:=cshortstringtype;
|
|
if not(is_constcharnode(left) and is_constcharnode(right)) then
|
|
begin
|
|
inserttypeconv(left,cshortstringtype);
|
|
{$ifdef addstringopt}
|
|
hp := genaddsstringcharoptnode(self);
|
|
result := hp;
|
|
exit;
|
|
{$endif addstringopt}
|
|
end;
|
|
end;
|
|
end
|
|
{ There is a widechar? }
|
|
else if is_widechar(rd) or is_widechar(ld) then
|
|
begin
|
|
{ widechar+widechar gives widestring }
|
|
if nodetype=addn then
|
|
begin
|
|
inserttypeconv(left,cwidestringtype);
|
|
if (torddef(rd).ordtype<>uwidechar) then
|
|
inserttypeconv(right,cwidechartype);
|
|
resultdef:=cwidestringtype;
|
|
end
|
|
else
|
|
begin
|
|
if (torddef(ld).ordtype<>uwidechar) then
|
|
inserttypeconv(left,cwidechartype);
|
|
if (torddef(rd).ordtype<>uwidechar) then
|
|
inserttypeconv(right,cwidechartype);
|
|
end;
|
|
end
|
|
{ is there a currency type ? }
|
|
else if ((torddef(rd).ordtype=scurrency) or (torddef(ld).ordtype=scurrency)) then
|
|
begin
|
|
if (torddef(ld).ordtype<>scurrency) then
|
|
inserttypeconv(left,s64currencytype);
|
|
if (torddef(rd).ordtype<>scurrency) then
|
|
inserttypeconv(right,s64currencytype);
|
|
end
|
|
{ and,or,xor work on bit patterns and don't care
|
|
about the sign of integers }
|
|
{ compares don't need extension to native int size either }
|
|
{ as long as both values are signed or unsigned }
|
|
else if is_integer(ld) and is_integer(rd) and
|
|
((nodetype in [andn,orn,xorn]) or
|
|
((nodetype in [equaln,unequaln,gtn,gten,ltn,lten]) and
|
|
not(is_signed(ld) xor is_signed(rd)))) then
|
|
begin
|
|
if rd.size>ld.size then
|
|
inserttypeconv_internal(left,right.resultdef)
|
|
else
|
|
inserttypeconv_internal(right,left.resultdef);
|
|
end
|
|
{ is there a signed 64 bit type ? }
|
|
else if ((torddef(rd).ordtype=s64bit) or (torddef(ld).ordtype=s64bit)) then
|
|
begin
|
|
if (torddef(ld).ordtype<>s64bit) then
|
|
inserttypeconv(left,s64inttype);
|
|
if (torddef(rd).ordtype<>s64bit) then
|
|
inserttypeconv(right,s64inttype);
|
|
end
|
|
{ is there a unsigned 64 bit type ? }
|
|
else if ((torddef(rd).ordtype=u64bit) or (torddef(ld).ordtype=u64bit)) then
|
|
begin
|
|
if (torddef(ld).ordtype<>u64bit) then
|
|
inserttypeconv(left,u64inttype);
|
|
if (torddef(rd).ordtype<>u64bit) then
|
|
inserttypeconv(right,u64inttype);
|
|
end
|
|
{ 64 bit cpus do calculations always in 64 bit }
|
|
{$ifndef cpu64bit}
|
|
{ is there a cardinal? }
|
|
else if ((torddef(rd).ordtype=u32bit) or (torddef(ld).ordtype=u32bit)) then
|
|
begin
|
|
{ convert positive constants to u32bit }
|
|
if (torddef(ld).ordtype<>u32bit) and
|
|
is_constintnode(left) and
|
|
(tordconstnode(left).value >= 0) then
|
|
inserttypeconv(left,u32inttype);
|
|
if (torddef(rd).ordtype<>u32bit) and
|
|
is_constintnode(right) and
|
|
(tordconstnode(right).value >= 0) then
|
|
inserttypeconv(right,u32inttype);
|
|
{ when one of the operand is signed or the operation is subn then perform
|
|
the operation in 64bit, can't use rd/ld here because there
|
|
could be already typeconvs inserted.
|
|
This is compatible with the code below for other unsigned types (PFV) }
|
|
if is_signed(left.resultdef) or
|
|
is_signed(right.resultdef) or
|
|
(nodetype=subn) then
|
|
begin
|
|
if nodetype<>subn then
|
|
CGMessage(type_w_mixed_signed_unsigned);
|
|
inserttypeconv(left,s64inttype);
|
|
inserttypeconv(right,s64inttype);
|
|
end
|
|
else
|
|
begin
|
|
if (torddef(left.resultdef).ordtype<>u32bit) then
|
|
inserttypeconv(left,u32inttype);
|
|
if (torddef(right.resultdef).ordtype<>u32bit) then
|
|
inserttypeconv(right,u32inttype);
|
|
end;
|
|
end
|
|
{$endif cpu64bit}
|
|
{ generic ord conversion is sinttype }
|
|
else
|
|
begin
|
|
{ if the left or right value is smaller than the normal
|
|
type sinttype and is unsigned, and the other value
|
|
is a constant < 0, the result will always be false/true
|
|
for equal / unequal nodes.
|
|
}
|
|
if (
|
|
{ left : unsigned ordinal var, right : < 0 constant }
|
|
(
|
|
((is_signed(ld)=false) and (is_constintnode(left) =false)) and
|
|
((is_constintnode(right)) and (tordconstnode(right).value < 0))
|
|
) or
|
|
{ right : unsigned ordinal var, left : < 0 constant }
|
|
(
|
|
((is_signed(rd)=false) and (is_constintnode(right) =false)) and
|
|
((is_constintnode(left)) and (tordconstnode(left).value < 0))
|
|
)
|
|
) then
|
|
begin
|
|
if nodetype = equaln then
|
|
CGMessage(type_w_signed_unsigned_always_false)
|
|
else
|
|
if nodetype = unequaln then
|
|
CGMessage(type_w_signed_unsigned_always_true)
|
|
else
|
|
if (is_constintnode(left) and (nodetype in [ltn,lten])) or
|
|
(is_constintnode(right) and (nodetype in [gtn,gten])) then
|
|
CGMessage(type_w_signed_unsigned_always_true)
|
|
else
|
|
if (is_constintnode(right) and (nodetype in [ltn,lten])) or
|
|
(is_constintnode(left) and (nodetype in [gtn,gten])) then
|
|
CGMessage(type_w_signed_unsigned_always_false);
|
|
end;
|
|
|
|
{ When there is a signed type or there is a minus operation
|
|
we convert to signed int. Otherwise (both are unsigned) we keep
|
|
the result also unsigned. This is compatible with Delphi (PFV) }
|
|
if is_signed(ld) or
|
|
is_signed(rd) or
|
|
(nodetype=subn) then
|
|
begin
|
|
inserttypeconv(right,sinttype);
|
|
inserttypeconv(left,sinttype);
|
|
end
|
|
else
|
|
begin
|
|
inserttypeconv(right,uinttype);
|
|
inserttypeconv(left,uinttype);
|
|
end;
|
|
end;
|
|
end
|
|
|
|
{ if both are floatdefs, conversion is already done before constant folding }
|
|
else if (ld.typ=floatdef) then
|
|
begin
|
|
if not(nodetype in [addn,subn,muln,slashn,equaln,unequaln,ltn,lten,gtn,gten]) then
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
end
|
|
|
|
{ left side a setdef, must be before string processing,
|
|
else array constructor can be seen as array of char (PFV) }
|
|
else if (ld.typ=setdef) then
|
|
begin
|
|
{ trying to add a set element? }
|
|
if (nodetype=addn) and (rd.typ<>setdef) then
|
|
begin
|
|
if (rt=setelementn) then
|
|
begin
|
|
if not(equal_defs(tsetdef(ld).elementdef,rd)) then
|
|
inserttypeconv(right,tsetdef(ld).elementdef);
|
|
end
|
|
else
|
|
CGMessage(type_e_mismatch);
|
|
{ ranges require normsets on big endian system }
|
|
if (target_info.endian=endian_big) and
|
|
(tsetdef(ld).size<>32) and
|
|
(rt=setelementn) and
|
|
assigned(tsetelementnode(right).right) then
|
|
begin
|
|
{ generate a temporary normset def, it'll be destroyed
|
|
when the symtable is unloaded }
|
|
inserttypeconv(left,tsetdef.create(tsetdef(ld).elementdef,255));
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if not(nodetype in [addn,subn,symdifn,muln,equaln,unequaln,lten,gten]) then
|
|
CGMessage(type_e_set_operation_unknown);
|
|
{ if the right side is also a setdef then the settype must
|
|
be the same as the left setdef }
|
|
if (rd.typ=setdef) and
|
|
not(equal_defs(ld,rd)) then
|
|
begin
|
|
if is_varset(rd) or is_normalset(rd) then
|
|
inserttypeconv(left,right.resultdef)
|
|
else
|
|
inserttypeconv(right,left.resultdef);
|
|
end;
|
|
end;
|
|
end
|
|
{ pointer comparision and subtraction }
|
|
else if (
|
|
(rd.typ=pointerdef) and (ld.typ=pointerdef)
|
|
) or
|
|
{ compare/add pchar to variable (not stringconst) char arrays
|
|
by addresses like BP/Delphi }
|
|
(
|
|
(nodetype in [equaln,unequaln,subn,addn]) and
|
|
(
|
|
((is_pchar(ld) or (lt=niln)) and is_chararray(rd) and (rt<>stringconstn)) or
|
|
((is_pchar(rd) or (rt=niln)) and is_chararray(ld) and (lt<>stringconstn))
|
|
)
|
|
) then
|
|
begin
|
|
{ convert char array to pointer }
|
|
if is_chararray(rd) then
|
|
begin
|
|
inserttypeconv(right,charpointertype);
|
|
rd:=right.resultdef;
|
|
end
|
|
else if is_chararray(ld) then
|
|
begin
|
|
inserttypeconv(left,charpointertype);
|
|
ld:=left.resultdef;
|
|
end;
|
|
|
|
case nodetype of
|
|
equaln,unequaln :
|
|
begin
|
|
if is_voidpointer(right.resultdef) then
|
|
inserttypeconv(right,left.resultdef)
|
|
else if is_voidpointer(left.resultdef) then
|
|
inserttypeconv(left,right.resultdef)
|
|
else if not(equal_defs(ld,rd)) then
|
|
IncompatibleTypes(ld,rd);
|
|
{ now that the type checking is done, convert both to charpointer, }
|
|
{ because methodpointers are 8 bytes even though only the first 4 }
|
|
{ bytes must be compared. This can happen here if we are in }
|
|
{ TP/Delphi mode, because there @methodpointer = voidpointer (but }
|
|
{ a voidpointer of 8 bytes). A conversion to voidpointer would be }
|
|
{ optimized away, since the result already was a voidpointer, so }
|
|
{ use a charpointer instead (JM) }
|
|
inserttypeconv_internal(left,charpointertype);
|
|
inserttypeconv_internal(right,charpointertype);
|
|
end;
|
|
ltn,lten,gtn,gten:
|
|
begin
|
|
if (cs_extsyntax in current_settings.moduleswitches) then
|
|
begin
|
|
if is_voidpointer(right.resultdef) then
|
|
inserttypeconv(right,left.resultdef)
|
|
else if is_voidpointer(left.resultdef) then
|
|
inserttypeconv(left,right.resultdef)
|
|
else if not(equal_defs(ld,rd)) then
|
|
IncompatibleTypes(ld,rd);
|
|
end
|
|
else
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
end;
|
|
subn:
|
|
begin
|
|
if (cs_extsyntax in current_settings.moduleswitches) then
|
|
begin
|
|
if is_voidpointer(right.resultdef) then
|
|
begin
|
|
if is_big_untyped_addrnode(right) then
|
|
CGMessage1(type_w_untyped_arithmetic_unportable,node2opstr(nodetype));
|
|
inserttypeconv(right,left.resultdef)
|
|
end
|
|
else if is_voidpointer(left.resultdef) then
|
|
inserttypeconv(left,right.resultdef)
|
|
else if not(equal_defs(ld,rd)) then
|
|
IncompatibleTypes(ld,rd);
|
|
end
|
|
else
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
|
|
if not(nf_has_pointerdiv in flags) and
|
|
(tpointerdef(rd).pointeddef.size>1) then
|
|
begin
|
|
hp:=getcopy;
|
|
include(hp.flags,nf_has_pointerdiv);
|
|
result:=cmoddivnode.create(divn,hp,cordconstnode.create(tpointerdef(rd).pointeddef.size,sinttype,false));
|
|
end;
|
|
resultdef:=sinttype;
|
|
exit;
|
|
end;
|
|
else
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
end;
|
|
end
|
|
|
|
{ is one of the operands a string?,
|
|
chararrays are also handled as strings (after conversion), also take
|
|
care of chararray+chararray and chararray+char.
|
|
Note: Must be done after pointerdef+pointerdef has been checked, else
|
|
pchar is converted to string }
|
|
else if (rd.typ=stringdef) or
|
|
(ld.typ=stringdef) or
|
|
((is_pchar(rd) or is_chararray(rd) or is_char(rd) or is_open_chararray(rd) or
|
|
is_pwidechar(rd) or is_widechararray(rd) or is_widechar(rd) or is_open_widechararray(rd)) and
|
|
(is_pchar(ld) or is_chararray(ld) or is_char(ld) or is_open_chararray(ld) or
|
|
is_pwidechar(ld) or is_widechararray(ld) or is_widechar(ld) or is_open_widechararray(ld))) then
|
|
begin
|
|
if (nodetype in [addn,equaln,unequaln,lten,gten,ltn,gtn]) then
|
|
begin
|
|
{ Is there a widestring? }
|
|
if is_widestring(rd) or is_widestring(ld) or
|
|
is_pwidechar(rd) or is_widechararray(rd) or is_widechar(rd) or is_open_widechararray(rd) or
|
|
is_pwidechar(ld) or is_widechararray(ld) or is_widechar(ld) or is_open_widechararray(ld) then
|
|
strtype:= st_widestring
|
|
else
|
|
if is_ansistring(rd) or is_ansistring(ld) or
|
|
((cs_ansistrings in current_settings.localswitches) and
|
|
//todo: Move some of this to longstring's then they are implemented?
|
|
(
|
|
is_pchar(rd) or (is_chararray(rd) and (rd.size > 255)) or is_open_chararray(rd) or
|
|
is_pchar(ld) or (is_chararray(ld) and (ld.size > 255)) or is_open_chararray(ld)
|
|
)
|
|
) then
|
|
strtype:= st_ansistring
|
|
else
|
|
if is_longstring(rd) or is_longstring(ld) then
|
|
strtype:= st_longstring
|
|
else
|
|
begin
|
|
{$warning todo: add a warning/hint here if one converting a too large array}
|
|
{ nodes is PChar, array [with size > 255] or OpenArrayOfChar.
|
|
Note: Delphi halts with error if "array [0..xx] of char"
|
|
is assigned to ShortString and string length is less
|
|
then array size }
|
|
strtype:= st_shortstring;
|
|
end;
|
|
|
|
// Now convert nodes to common string type
|
|
case strtype of
|
|
st_widestring :
|
|
begin
|
|
if not(is_widestring(rd)) then
|
|
inserttypeconv(right,cwidestringtype);
|
|
if not(is_widestring(ld)) then
|
|
inserttypeconv(left,cwidestringtype);
|
|
end;
|
|
st_ansistring :
|
|
begin
|
|
if not(is_ansistring(rd)) then
|
|
inserttypeconv(right,cansistringtype);
|
|
if not(is_ansistring(ld)) then
|
|
inserttypeconv(left,cansistringtype);
|
|
end;
|
|
st_longstring :
|
|
begin
|
|
if not(is_longstring(rd)) then
|
|
inserttypeconv(right,clongstringtype);
|
|
if not(is_longstring(ld)) then
|
|
inserttypeconv(left,clongstringtype);
|
|
end;
|
|
st_shortstring :
|
|
begin
|
|
if not(is_shortstring(ld)) then
|
|
inserttypeconv(left,cshortstringtype);
|
|
{ don't convert char, that can be handled by the optimized node }
|
|
if not(is_shortstring(rd) or is_char(rd)) then
|
|
inserttypeconv(right,cshortstringtype);
|
|
end;
|
|
else
|
|
internalerror(2005101);
|
|
end;
|
|
end
|
|
else
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
end
|
|
|
|
{ class or interface equation }
|
|
else if is_class_or_interface(rd) or is_class_or_interface(ld) then
|
|
begin
|
|
if (nodetype in [equaln,unequaln]) then
|
|
begin
|
|
if is_class_or_interface(rd) and is_class_or_interface(ld) then
|
|
begin
|
|
if tobjectdef(rd).is_related(tobjectdef(ld)) then
|
|
inserttypeconv(right,left.resultdef)
|
|
else
|
|
inserttypeconv(left,right.resultdef);
|
|
end
|
|
else if is_class_or_interface(rd) then
|
|
inserttypeconv(left,right.resultdef)
|
|
else
|
|
inserttypeconv(right,left.resultdef);
|
|
end
|
|
else
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
end
|
|
|
|
else if (rd.typ=classrefdef) and (ld.typ=classrefdef) then
|
|
begin
|
|
if (nodetype in [equaln,unequaln]) then
|
|
begin
|
|
if tobjectdef(tclassrefdef(rd).pointeddef).is_related(
|
|
tobjectdef(tclassrefdef(ld).pointeddef)) then
|
|
inserttypeconv(right,left.resultdef)
|
|
else
|
|
inserttypeconv(left,right.resultdef);
|
|
end
|
|
else
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
end
|
|
|
|
{ allows comperasion with nil pointer }
|
|
else if is_class_or_interface(rd) or (rd.typ=classrefdef) then
|
|
begin
|
|
if (nodetype in [equaln,unequaln]) then
|
|
inserttypeconv(left,right.resultdef)
|
|
else
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
end
|
|
|
|
else if is_class_or_interface(ld) or (ld.typ=classrefdef) then
|
|
begin
|
|
if (nodetype in [equaln,unequaln]) then
|
|
inserttypeconv(right,left.resultdef)
|
|
else
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
end
|
|
|
|
{ support procvar=nil,procvar<>nil }
|
|
else if ((ld.typ=procvardef) and (rt=niln)) or
|
|
((rd.typ=procvardef) and (lt=niln)) then
|
|
begin
|
|
if not(nodetype in [equaln,unequaln]) then
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
{ find proc field in methodpointer record }
|
|
hsym:=tfieldvarsym(trecorddef(methodpointertype).symtable.Find('proc'));
|
|
if not assigned(hsym) then
|
|
internalerror(200412043);
|
|
{ For methodpointers compare only tmethodpointer.proc }
|
|
if (rd.typ=procvardef) and
|
|
(not tprocvardef(rd).is_addressonly) then
|
|
begin
|
|
right:=csubscriptnode.create(
|
|
hsym,
|
|
ctypeconvnode.create_internal(right,methodpointertype));
|
|
typecheckpass(right);
|
|
end;
|
|
if (ld.typ=procvardef) and
|
|
(not tprocvardef(ld).is_addressonly) then
|
|
begin
|
|
left:=csubscriptnode.create(
|
|
hsym,
|
|
ctypeconvnode.create_internal(left,methodpointertype));
|
|
typecheckpass(left);
|
|
end;
|
|
end
|
|
|
|
{ support dynamicarray=nil,dynamicarray<>nil }
|
|
else if (is_dynamic_array(ld) and (rt=niln)) or
|
|
(is_dynamic_array(rd) and (lt=niln)) or
|
|
(is_dynamic_array(ld) and is_dynamic_array(rd)) then
|
|
begin
|
|
if not(nodetype in [equaln,unequaln]) then
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
end
|
|
|
|
{$ifdef SUPPORT_MMX}
|
|
{ mmx support, this must be before the zero based array
|
|
check }
|
|
else if (cs_mmx in current_settings.localswitches) and
|
|
is_mmx_able_array(ld) and
|
|
is_mmx_able_array(rd) and
|
|
equal_defs(ld,rd) then
|
|
begin
|
|
case nodetype of
|
|
addn,subn,xorn,orn,andn:
|
|
;
|
|
{ mul is a little bit restricted }
|
|
muln:
|
|
if not(mmx_type(ld) in [mmxu16bit,mmxs16bit,mmxfixed16]) then
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
else
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
end;
|
|
end
|
|
{$endif SUPPORT_MMX}
|
|
{ vector support, this must be before the zero based array
|
|
check }
|
|
else if (cs_support_vectors in current_settings.globalswitches) and
|
|
is_vector(ld) and
|
|
is_vector(rd) and
|
|
equal_defs(ld,rd) then
|
|
begin
|
|
if not(nodetype in [addn,subn,xorn,orn,andn,muln,slashn]) then
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
{ both defs must be equal, so taking left or right as resultdef doesn't matter }
|
|
resultdef:=left.resultdef;
|
|
end
|
|
|
|
{ this is a little bit dangerous, also the left type }
|
|
{ pointer to should be checked! This broke the mmx support }
|
|
else if (rd.typ=pointerdef) or
|
|
(is_zero_based_array(rd) and (rt<>stringconstn)) then
|
|
begin
|
|
if is_zero_based_array(rd) then
|
|
begin
|
|
resultdef:=tpointerdef.create(tarraydef(rd).elementdef);
|
|
inserttypeconv(right,resultdef);
|
|
end
|
|
else
|
|
resultdef:=right.resultdef;
|
|
inserttypeconv(left,sinttype);
|
|
if nodetype=addn then
|
|
begin
|
|
if not(cs_extsyntax in current_settings.moduleswitches) or
|
|
(not(is_pchar(ld)) and not(m_add_pointer in current_settings.modeswitches)) then
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
if (rd.typ=pointerdef) and
|
|
(tpointerdef(rd).pointeddef.size>1) then
|
|
begin
|
|
left:=caddnode.create(muln,left,
|
|
cordconstnode.create(tpointerdef(rd).pointeddef.size,sinttype,true));
|
|
typecheckpass(left);
|
|
end;
|
|
end
|
|
else
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
end
|
|
|
|
else if (ld.typ=pointerdef) or
|
|
(is_zero_based_array(ld) and (lt<>stringconstn)) then
|
|
begin
|
|
if is_zero_based_array(ld) then
|
|
begin
|
|
resultdef:=tpointerdef.create(tarraydef(ld).elementdef);
|
|
inserttypeconv(left,resultdef);
|
|
end
|
|
else
|
|
resultdef:=left.resultdef;
|
|
|
|
inserttypeconv(right,sinttype);
|
|
if nodetype in [addn,subn] then
|
|
begin
|
|
if not(cs_extsyntax in current_settings.moduleswitches) or
|
|
(not(is_pchar(ld)) and not(m_add_pointer in current_settings.modeswitches)) then
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
if (ld.typ=pointerdef) then
|
|
begin
|
|
if is_big_untyped_addrnode(left) then
|
|
CGMessage1(type_w_untyped_arithmetic_unportable,node2opstr(nodetype));
|
|
if (tpointerdef(ld).pointeddef.size>1) then
|
|
begin
|
|
right:=caddnode.create(muln,right,
|
|
cordconstnode.create(tpointerdef(ld).pointeddef.size,sinttype,true));
|
|
typecheckpass(right);
|
|
end
|
|
end else
|
|
if is_zero_based_array(ld) and
|
|
(tarraydef(ld).elementdef.size>1) then
|
|
begin
|
|
right:=caddnode.create(muln,right,
|
|
cordconstnode.create(tarraydef(ld).elementdef.size,sinttype,true));
|
|
typecheckpass(right);
|
|
end;
|
|
end
|
|
else
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
end
|
|
|
|
else if (rd.typ=procvardef) and
|
|
(ld.typ=procvardef) and
|
|
equal_defs(rd,ld) then
|
|
begin
|
|
if (nodetype in [equaln,unequaln]) then
|
|
begin
|
|
if tprocvardef(rd).is_addressonly then
|
|
begin
|
|
inserttypeconv_internal(right,voidpointertype);
|
|
inserttypeconv_internal(left,voidpointertype);
|
|
end
|
|
else
|
|
begin
|
|
{ find proc field in methodpointer record }
|
|
hsym:=tfieldvarsym(trecorddef(methodpointertype).symtable.Find('proc'));
|
|
if not assigned(hsym) then
|
|
internalerror(200412043);
|
|
{ Compare tmehodpointer(left).proc }
|
|
right:=csubscriptnode.create(
|
|
hsym,
|
|
ctypeconvnode.create_internal(right,methodpointertype));
|
|
typecheckpass(right);
|
|
left:=csubscriptnode.create(
|
|
hsym,
|
|
ctypeconvnode.create_internal(left,methodpointertype));
|
|
typecheckpass(left);
|
|
end;
|
|
end
|
|
else
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
end
|
|
|
|
{ enums }
|
|
else if (ld.typ=enumdef) and (rd.typ=enumdef) then
|
|
begin
|
|
if allowenumop(nodetype) then
|
|
inserttypeconv(right,left.resultdef)
|
|
else
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),ld.typename,rd.typename);
|
|
end
|
|
|
|
{ generic conversion, this is for error recovery }
|
|
else
|
|
begin
|
|
inserttypeconv(left,sinttype);
|
|
inserttypeconv(right,sinttype);
|
|
end;
|
|
|
|
{ set resultdef if not already done }
|
|
if not assigned(resultdef) then
|
|
begin
|
|
case nodetype of
|
|
ltn,lten,gtn,gten,equaln,unequaln :
|
|
resultdef:=booltype;
|
|
slashn :
|
|
resultdef:=resultrealdef;
|
|
addn:
|
|
begin
|
|
{ for strings, return is always a 255 char string }
|
|
if is_shortstring(left.resultdef) then
|
|
resultdef:=cshortstringtype
|
|
else
|
|
resultdef:=left.resultdef;
|
|
end;
|
|
else
|
|
resultdef:=left.resultdef;
|
|
end;
|
|
end;
|
|
|
|
{ when the result is currency we need some extra code for
|
|
multiplication and division. this should not be done when
|
|
the muln or slashn node is created internally }
|
|
if not(nf_is_currency in flags) and
|
|
is_currency(resultdef) then
|
|
begin
|
|
case nodetype of
|
|
slashn :
|
|
begin
|
|
{ slashn will only work with floats }
|
|
hp:=caddnode.create(muln,getcopy,crealconstnode.create(10000.0,s64currencytype));
|
|
include(hp.flags,nf_is_currency);
|
|
result:=hp;
|
|
end;
|
|
muln :
|
|
begin
|
|
if s64currencytype.typ=floatdef then
|
|
hp:=caddnode.create(slashn,getcopy,crealconstnode.create(10000.0,s64currencytype))
|
|
else
|
|
hp:=cmoddivnode.create(divn,getcopy,cordconstnode.create(10000,s64currencytype,false));
|
|
include(hp.flags,nf_is_currency);
|
|
result:=hp
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function taddnode.first_addstring: tnode;
|
|
const
|
|
swap_relation: array [ltn..unequaln] of Tnodetype=(gtn, gten, ltn, lten, equaln, unequaln);
|
|
var
|
|
p: tnode;
|
|
newstatement : tstatementnode;
|
|
tempnode,tempnode2 : ttempcreatenode;
|
|
cmpfuncname: string;
|
|
begin
|
|
{ when we get here, we are sure that both the left and the right }
|
|
{ node are both strings of the same stringtype (JM) }
|
|
case nodetype of
|
|
addn:
|
|
begin
|
|
if (left.nodetype=stringconstn) and (tstringconstnode(left).len=0) then
|
|
begin
|
|
result:=right;
|
|
left.free;
|
|
left:=nil;
|
|
right:=nil;
|
|
exit;
|
|
end;
|
|
if (right.nodetype=stringconstn) and (tstringconstnode(right).len=0) then
|
|
begin
|
|
result:=left;
|
|
left:=nil;
|
|
right.free;
|
|
right:=nil;
|
|
exit;
|
|
end;
|
|
{ create the call to the concat routine both strings as arguments }
|
|
if assigned(aktassignmentnode) and
|
|
(aktassignmentnode.right=self) and
|
|
(aktassignmentnode.left.resultdef=resultdef) and
|
|
valid_for_var(aktassignmentnode.left,false) then
|
|
begin
|
|
result:=ccallnode.createintern('fpc_'+
|
|
tstringdef(resultdef).stringtypname+'_concat',
|
|
ccallparanode.create(right,
|
|
ccallparanode.create(left,
|
|
ccallparanode.create(aktassignmentnode.left.getcopy,nil))));
|
|
include(aktassignmentnode.flags,nf_assign_done_in_right);
|
|
firstpass(result);
|
|
end
|
|
else
|
|
begin
|
|
result:=internalstatements(newstatement);
|
|
tempnode:=ctempcreatenode.create(resultdef,resultdef.size,tt_persistent,true);
|
|
addstatement(newstatement,tempnode);
|
|
addstatement(newstatement,ccallnode.createintern('fpc_'+
|
|
tstringdef(resultdef).stringtypname+'_concat',
|
|
ccallparanode.create(right,
|
|
ccallparanode.create(left,
|
|
ccallparanode.create(ctemprefnode.create(tempnode),nil)))));
|
|
addstatement(newstatement,ctempdeletenode.create_normal_temp(tempnode));
|
|
addstatement(newstatement,ctemprefnode.create(tempnode));
|
|
end;
|
|
{ we reused the arguments }
|
|
left := nil;
|
|
right := nil;
|
|
end;
|
|
ltn,lten,gtn,gten,equaln,unequaln :
|
|
begin
|
|
{ generate better code for comparison with empty string, we
|
|
only need to compare the length with 0 }
|
|
if (nodetype in [equaln,unequaln,gtn,gten,ltn,lten]) and
|
|
{ windows widestrings are too complicated to be handled optimized }
|
|
not(is_widestring(left.resultdef) and (target_info.system in system_windows)) and
|
|
(((left.nodetype=stringconstn) and (tstringconstnode(left).len=0)) or
|
|
((right.nodetype=stringconstn) and (tstringconstnode(right).len=0))) then
|
|
begin
|
|
{ switch so that the constant is always on the right }
|
|
if left.nodetype = stringconstn then
|
|
begin
|
|
p := left;
|
|
left := right;
|
|
right := p;
|
|
nodetype:=swap_relation[nodetype];
|
|
end;
|
|
if is_shortstring(left.resultdef) or
|
|
(nodetype in [gtn,gten,ltn,lten]) then
|
|
{ compare the length with 0 }
|
|
result := caddnode.create(nodetype,
|
|
cinlinenode.create(in_length_x,false,left),
|
|
cordconstnode.create(0,s32inttype,false))
|
|
else
|
|
begin
|
|
(*
|
|
if is_widestring(left.resultdef) and
|
|
(target_info.system in system_windows) then
|
|
begin
|
|
{ windows like widestrings requires that we also check the length }
|
|
result:=internalstatements(newstatement);
|
|
tempnode:=ctempcreatenode.create(voidpointertype,voidpointertype.size,tt_persistent,true);
|
|
tempnode2:=ctempcreatenode.create(resultdef,resultdef.size,tt_persistent,true);
|
|
addstatement(newstatement,tempnode);
|
|
addstatement(newstatement,tempnode2);
|
|
{ poor man's cse }
|
|
addstatement(newstatement,cassignmentnode.create(ctemprefnode.create(tempnode),
|
|
ctypeconvnode.create_internal(left,voidpointertype))
|
|
);
|
|
addstatement(newstatement,cassignmentnode.create(ctemprefnode.create(tempnode2),
|
|
caddnode.create(orn,
|
|
caddnode.create(nodetype,
|
|
ctemprefnode.create(tempnode),
|
|
cpointerconstnode.create(0,voidpointertype)
|
|
),
|
|
caddnode.create(nodetype,
|
|
ctypeconvnode.create_internal(cderefnode.create(ctemprefnode.create(tempnode)),s32inttype),
|
|
cordconstnode.create(0,s32inttype,false)
|
|
)
|
|
)
|
|
));
|
|
addstatement(newstatement,ctempdeletenode.create_normal_temp(tempnode));
|
|
addstatement(newstatement,ctempdeletenode.create_normal_temp(tempnode2));
|
|
addstatement(newstatement,ctemprefnode.create(tempnode2));
|
|
end
|
|
else
|
|
*)
|
|
begin
|
|
{ compare the pointer with nil (for ansistrings etc), }
|
|
{ faster than getting the length (JM) }
|
|
result:= caddnode.create(nodetype,
|
|
ctypeconvnode.create_internal(left,voidpointertype),
|
|
cpointerconstnode.create(0,voidpointertype));
|
|
end;
|
|
end;
|
|
{ left is reused }
|
|
left := nil;
|
|
{ right isn't }
|
|
right.free;
|
|
right := nil;
|
|
exit;
|
|
end;
|
|
{ no string constant -> call compare routine }
|
|
cmpfuncname := 'fpc_'+tstringdef(left.resultdef).stringtypname+'_compare';
|
|
{ for equality checks use optimized version }
|
|
if nodetype in [equaln,unequaln] then
|
|
cmpfuncname := cmpfuncname + '_equal';
|
|
|
|
result := ccallnode.createintern(cmpfuncname,
|
|
ccallparanode.create(right,ccallparanode.create(left,nil)));
|
|
{ and compare its result with 0 according to the original operator }
|
|
result := caddnode.create(nodetype,result,
|
|
cordconstnode.create(0,s32inttype,false));
|
|
left := nil;
|
|
right := nil;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function taddnode.first_addset : tnode;
|
|
|
|
procedure call_varset_helper(const n : string);
|
|
var
|
|
newstatement : tstatementnode;
|
|
temp : ttempcreatenode;
|
|
begin
|
|
{ add two var sets }
|
|
result:=internalstatements(newstatement);
|
|
|
|
{ create temp for result }
|
|
temp:=ctempcreatenode.create(resultdef,resultdef.size,tt_persistent,true);
|
|
addstatement(newstatement,temp);
|
|
|
|
addstatement(newstatement,ccallnode.createintern(n,
|
|
ccallparanode.create(cordconstnode.create(resultdef.size,sinttype,false),
|
|
ccallparanode.create(ctemprefnode.create(temp),
|
|
ccallparanode.create(right,
|
|
ccallparanode.create(left,nil)))))
|
|
);
|
|
|
|
{ remove reused parts from original node }
|
|
left:=nil;
|
|
right:=nil;
|
|
{ the last statement should return the value as
|
|
location and type, this is done be referencing the
|
|
temp and converting it first from a persistent temp to
|
|
normal temp }
|
|
addstatement(newstatement,ctempdeletenode.create_normal_temp(temp));
|
|
addstatement(newstatement,ctemprefnode.create(temp));
|
|
end;
|
|
|
|
var
|
|
procname: string[31];
|
|
tempn: tnode;
|
|
paras: tcallparanode;
|
|
srsym: ttypesym;
|
|
newstatement : tstatementnode;
|
|
temp : ttempcreatenode;
|
|
begin
|
|
if (is_varset(left.resultdef) or is_varset(right.resultdef)) and
|
|
not(is_normalset(left.resultdef)) and
|
|
not(is_normalset(right.resultdef)) then
|
|
begin
|
|
case nodetype of
|
|
equaln,unequaln,lten,gten:
|
|
begin
|
|
case nodetype of
|
|
equaln,unequaln:
|
|
procname := 'fpc_varset_comp_sets';
|
|
lten,gten:
|
|
begin
|
|
procname := 'fpc_varset_contains_sets';
|
|
{ (left >= right) = (right <= left) }
|
|
if nodetype = gten then
|
|
begin
|
|
tempn := left;
|
|
left := right;
|
|
right := tempn;
|
|
end;
|
|
end;
|
|
end;
|
|
result := ccallnode.createinternres(procname,
|
|
ccallparanode.create(cordconstnode.create(left.resultdef.size,sinttype,false),
|
|
ccallparanode.create(right,
|
|
ccallparanode.create(left,nil))),resultdef);
|
|
{ left and right are reused as parameters }
|
|
left := nil;
|
|
right := nil;
|
|
{ for an unequaln, we have to negate the result of comp_sets }
|
|
if nodetype = unequaln then
|
|
result := cnotnode.create(result);
|
|
end;
|
|
addn:
|
|
begin
|
|
{ optimize first loading of a set }
|
|
if (right.nodetype=setelementn) and
|
|
not(assigned(tsetelementnode(right).right)) and
|
|
is_emptyset(left) then
|
|
begin
|
|
result:=internalstatements(newstatement);
|
|
|
|
{ create temp for result }
|
|
temp:=ctempcreatenode.create(resultdef,resultdef.size,tt_persistent,true);
|
|
addstatement(newstatement,temp);
|
|
|
|
addstatement(newstatement,ccallnode.createintern('fpc_varset_create_element',
|
|
ccallparanode.create(ctemprefnode.create(temp),
|
|
ccallparanode.create(cordconstnode.create(resultdef.size,sinttype,false),
|
|
ccallparanode.create(ctypeconvnode.create_internal(tsetelementnode(right).left,sinttype),nil))))
|
|
);
|
|
|
|
{ the last statement should return the value as
|
|
location and type, this is done be referencing the
|
|
temp and converting it first from a persistent temp to
|
|
normal temp }
|
|
addstatement(newstatement,ctempdeletenode.create_normal_temp(temp));
|
|
addstatement(newstatement,ctemprefnode.create(temp));
|
|
|
|
tsetelementnode(right).left := nil;
|
|
end
|
|
else
|
|
begin
|
|
if right.nodetype=setelementn then
|
|
begin
|
|
result:=internalstatements(newstatement);
|
|
|
|
{ create temp for result }
|
|
temp:=ctempcreatenode.create(resultdef,resultdef.size,tt_persistent,true);
|
|
addstatement(newstatement,temp);
|
|
|
|
{ add a range or a single element? }
|
|
if assigned(tsetelementnode(right).right) then
|
|
addstatement(newstatement,ccallnode.createintern('fpc_varset_set_range',
|
|
ccallparanode.create(cordconstnode.create(resultdef.size,sinttype,false),
|
|
ccallparanode.create(ctypeconvnode.create_internal(tsetelementnode(right).right,sinttype),
|
|
ccallparanode.create(ctypeconvnode.create_internal(tsetelementnode(right).left,sinttype),
|
|
ccallparanode.create(ctemprefnode.create(temp),
|
|
ccallparanode.create(left,nil))))))
|
|
)
|
|
else
|
|
addstatement(newstatement,ccallnode.createintern('fpc_varset_set',
|
|
ccallparanode.create(cordconstnode.create(resultdef.size,sinttype,false),
|
|
ccallparanode.create(ctypeconvnode.create_internal(tsetelementnode(right).left,sinttype),
|
|
ccallparanode.create(ctemprefnode.create(temp),
|
|
ccallparanode.create(left,nil)))))
|
|
);
|
|
{ remove reused parts from original node }
|
|
tsetelementnode(right).right:=nil;
|
|
tsetelementnode(right).left:=nil;
|
|
left:=nil;
|
|
{ the last statement should return the value as
|
|
location and type, this is done be referencing the
|
|
temp and converting it first from a persistent temp to
|
|
normal temp }
|
|
addstatement(newstatement,ctempdeletenode.create_normal_temp(temp));
|
|
addstatement(newstatement,ctemprefnode.create(temp));
|
|
end
|
|
else
|
|
call_varset_helper('fpc_varset_add_sets');
|
|
end
|
|
end;
|
|
subn:
|
|
call_varset_helper('fpc_varset_sub_sets');
|
|
symdifn:
|
|
call_varset_helper('fpc_varset_symdif_sets');
|
|
muln:
|
|
call_varset_helper('fpc_varset_mul_sets');
|
|
else
|
|
internalerror(200609241);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
{ get the sym that represents the fpc_normal_set type }
|
|
srsym:=search_system_type('FPC_NORMAL_SET');
|
|
case nodetype of
|
|
equaln,unequaln,lten,gten:
|
|
begin
|
|
case nodetype of
|
|
equaln,unequaln:
|
|
procname := 'fpc_set_comp_sets';
|
|
lten,gten:
|
|
begin
|
|
procname := 'fpc_set_contains_sets';
|
|
{ (left >= right) = (right <= left) }
|
|
if nodetype = gten then
|
|
begin
|
|
tempn := left;
|
|
left := right;
|
|
right := tempn;
|
|
end;
|
|
end;
|
|
end;
|
|
{ convert the arguments (explicitely) to fpc_normal_set's }
|
|
left := ctypeconvnode.create_internal(left,srsym.typedef);
|
|
right := ctypeconvnode.create_internal(right,srsym.typedef);
|
|
result := ccallnode.createintern(procname,ccallparanode.create(right,
|
|
ccallparanode.create(left,nil)));
|
|
{ left and right are reused as parameters }
|
|
left := nil;
|
|
right := nil;
|
|
{ for an unequaln, we have to negate the result of comp_sets }
|
|
if nodetype = unequaln then
|
|
result := cnotnode.create(result);
|
|
end;
|
|
addn:
|
|
begin
|
|
{ optimize first loading of a set }
|
|
if (right.nodetype=setelementn) and
|
|
not(assigned(tsetelementnode(right).right)) and
|
|
is_emptyset(left) then
|
|
begin
|
|
{ type cast the value to pass as argument to a byte, }
|
|
{ since that's what the helper expects }
|
|
tsetelementnode(right).left :=
|
|
ctypeconvnode.create_internal(tsetelementnode(right).left,u8inttype);
|
|
{ set the resultdef to the actual one (otherwise it's }
|
|
{ "fpc_normal_set") }
|
|
result := ccallnode.createinternres('fpc_set_create_element',
|
|
ccallparanode.create(tsetelementnode(right).left,nil),
|
|
resultdef);
|
|
{ reused }
|
|
tsetelementnode(right).left := nil;
|
|
end
|
|
else
|
|
begin
|
|
if right.nodetype=setelementn then
|
|
begin
|
|
{ convert the arguments to bytes, since that's what }
|
|
{ the helper expects }
|
|
tsetelementnode(right).left :=
|
|
ctypeconvnode.create_internal(tsetelementnode(right).left,
|
|
u8inttype);
|
|
|
|
{ convert the original set (explicitely) to an }
|
|
{ fpc_normal_set so we can pass it to the helper }
|
|
left := ctypeconvnode.create_internal(left,srsym.typedef);
|
|
|
|
{ add a range or a single element? }
|
|
if assigned(tsetelementnode(right).right) then
|
|
begin
|
|
tsetelementnode(right).right :=
|
|
ctypeconvnode.create_internal(tsetelementnode(right).right,
|
|
u8inttype);
|
|
|
|
{ create the call }
|
|
result := ccallnode.createinternres('fpc_set_set_range',
|
|
ccallparanode.create(tsetelementnode(right).right,
|
|
ccallparanode.create(tsetelementnode(right).left,
|
|
ccallparanode.create(left,nil))),resultdef);
|
|
end
|
|
else
|
|
begin
|
|
result := ccallnode.createinternres('fpc_set_set_byte',
|
|
ccallparanode.create(tsetelementnode(right).left,
|
|
ccallparanode.create(left,nil)),resultdef);
|
|
end;
|
|
{ remove reused parts from original node }
|
|
tsetelementnode(right).right := nil;
|
|
tsetelementnode(right).left := nil;
|
|
left := nil;
|
|
end
|
|
else
|
|
begin
|
|
{ add two sets }
|
|
|
|
{ convert the sets to fpc_normal_set's }
|
|
result := ccallnode.createinternres('fpc_set_add_sets',
|
|
ccallparanode.create(
|
|
ctypeconvnode.create_explicit(right,srsym.typedef),
|
|
ccallparanode.create(
|
|
ctypeconvnode.create_internal(left,srsym.typedef),nil)),resultdef);
|
|
{ remove reused parts from original node }
|
|
left := nil;
|
|
right := nil;
|
|
end;
|
|
end
|
|
end;
|
|
subn,symdifn,muln:
|
|
begin
|
|
{ convert the sets to fpc_normal_set's }
|
|
paras := ccallparanode.create(ctypeconvnode.create_internal(right,srsym.typedef),
|
|
ccallparanode.create(ctypeconvnode.create_internal(left,srsym.typedef),nil));
|
|
case nodetype of
|
|
subn:
|
|
result := ccallnode.createinternres('fpc_set_sub_sets',
|
|
paras,resultdef);
|
|
symdifn:
|
|
result := ccallnode.createinternres('fpc_set_symdif_sets',
|
|
paras,resultdef);
|
|
muln:
|
|
result := ccallnode.createinternres('fpc_set_mul_sets',
|
|
paras,resultdef);
|
|
end;
|
|
{ remove reused parts from original node }
|
|
left := nil;
|
|
right := nil;
|
|
end;
|
|
else
|
|
internalerror(200108311);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
function taddnode.use_generic_mul32to64: boolean;
|
|
begin
|
|
result := true;
|
|
end;
|
|
|
|
|
|
function taddnode.try_make_mul32to64: boolean;
|
|
|
|
function canbe32bitint(v: tconstexprint; fromdef: torddef; todefsigned: boolean): boolean;
|
|
begin
|
|
if (fromdef.ordtype <> u64bit) then
|
|
result :=
|
|
((v >= 0) or
|
|
todefsigned) and
|
|
(v >= low(longint)) and
|
|
(v <= high(longint))
|
|
else
|
|
result :=
|
|
(qword(v) >= low(cardinal)) and
|
|
(qword(v) <= high(cardinal))
|
|
end;
|
|
|
|
var
|
|
temp: tnode;
|
|
begin
|
|
result := false;
|
|
if ((left.nodetype = typeconvn) and
|
|
is_integer(ttypeconvnode(left).left.resultdef) and
|
|
(not(torddef(ttypeconvnode(left).left.resultdef).ordtype in [u64bit,s64bit])) and
|
|
(((right.nodetype = ordconstn) and
|
|
canbe32bitint(tordconstnode(right).value,torddef(right.resultdef),is_signed(left.resultdef))) or
|
|
((right.nodetype = typeconvn) and
|
|
is_integer(ttypeconvnode(right).left.resultdef) and
|
|
not(torddef(ttypeconvnode(right).left.resultdef).ordtype in [u64bit,s64bit])) and
|
|
(is_signed(ttypeconvnode(left).left.resultdef) =
|
|
is_signed(ttypeconvnode(right).left.resultdef)))) then
|
|
begin
|
|
temp := ttypeconvnode(left).left;
|
|
ttypeconvnode(left).left := nil;
|
|
left.free;
|
|
left := temp;
|
|
if (right.nodetype = typeconvn) then
|
|
begin
|
|
temp := ttypeconvnode(right).left;
|
|
ttypeconvnode(right).left := nil;
|
|
right.free;
|
|
right := temp;
|
|
end;
|
|
if (is_signed(left.resultdef)) then
|
|
begin
|
|
inserttypeconv(left,s32inttype);
|
|
inserttypeconv(right,s32inttype);
|
|
end
|
|
else
|
|
begin
|
|
inserttypeconv(left,u32inttype);
|
|
inserttypeconv(right,u32inttype);
|
|
end;
|
|
firstpass(left);
|
|
firstpass(right);
|
|
result := true;
|
|
end;
|
|
end;
|
|
|
|
|
|
function taddnode.first_add64bitint: tnode;
|
|
var
|
|
procname: string[31];
|
|
temp: tnode;
|
|
power: longint;
|
|
begin
|
|
result := nil;
|
|
{ create helper calls mul }
|
|
if nodetype <> muln then
|
|
exit;
|
|
|
|
{ make sure that if there is a constant, that it's on the right }
|
|
if left.nodetype = ordconstn then
|
|
begin
|
|
temp := right;
|
|
right := left;
|
|
left := temp;
|
|
end;
|
|
|
|
{ can we use a shift instead of a mul? }
|
|
if not (cs_check_overflow in current_settings.localswitches) and
|
|
(right.nodetype = ordconstn) and
|
|
ispowerof2(tordconstnode(right).value,power) then
|
|
begin
|
|
tordconstnode(right).value := power;
|
|
result := cshlshrnode.create(shln,left,right);
|
|
{ left and right are reused }
|
|
left := nil;
|
|
right := nil;
|
|
{ return firstpassed new node }
|
|
exit;
|
|
end;
|
|
|
|
if not(use_generic_mul32to64) and
|
|
try_make_mul32to64 then
|
|
exit;
|
|
|
|
{ when currency is used set the result of the
|
|
parameters to s64bit, so they are not converted }
|
|
if is_currency(resultdef) then
|
|
begin
|
|
left.resultdef:=s64inttype;
|
|
right.resultdef:=s64inttype;
|
|
end;
|
|
|
|
{ otherwise, create the parameters for the helper }
|
|
right := ccallparanode.create(
|
|
cordconstnode.create(ord(cs_check_overflow in current_settings.localswitches),booltype,true),
|
|
ccallparanode.create(right,ccallparanode.create(left,nil)));
|
|
left := nil;
|
|
{ only qword needs the unsigned code, the
|
|
signed code is also used for currency }
|
|
if is_signed(resultdef) then
|
|
procname := 'fpc_mul_int64'
|
|
else
|
|
procname := 'fpc_mul_qword';
|
|
result := ccallnode.createintern(procname,right);
|
|
right := nil;
|
|
end;
|
|
|
|
|
|
function taddnode.first_addfloat : tnode;
|
|
var
|
|
procname: string[31];
|
|
{ do we need to reverse the result ? }
|
|
notnode : boolean;
|
|
fdef : tdef;
|
|
begin
|
|
result := nil;
|
|
notnode := false;
|
|
{ In non-emulation mode, real opcodes are
|
|
emitted for floating point values.
|
|
}
|
|
if not (cs_fp_emulation in current_settings.moduleswitches) then
|
|
exit;
|
|
|
|
if not(target_info.system in system_wince) then
|
|
begin
|
|
case tfloatdef(left.resultdef).floattype of
|
|
s32real:
|
|
begin
|
|
fdef:=search_system_type('FLOAT32REC').typedef;
|
|
procname:='float32';
|
|
end;
|
|
s64real:
|
|
begin
|
|
fdef:=search_system_type('FLOAT64').typedef;
|
|
procname:='float64';
|
|
end;
|
|
{!!! not yet implemented
|
|
s128real:
|
|
}
|
|
else
|
|
internalerror(2005082601);
|
|
end;
|
|
|
|
case nodetype of
|
|
addn:
|
|
procname:=procname+'_add';
|
|
muln:
|
|
procname:=procname+'_mul';
|
|
subn:
|
|
procname:=procname+'_sub';
|
|
slashn:
|
|
procname:=procname+'_div';
|
|
ltn:
|
|
procname:=procname+'_lt';
|
|
lten:
|
|
procname:=procname+'_le';
|
|
gtn:
|
|
begin
|
|
procname:=procname+'_le';
|
|
notnode:=true;
|
|
end;
|
|
gten:
|
|
begin
|
|
procname:=procname+'_lt';
|
|
notnode:=true;
|
|
end;
|
|
equaln:
|
|
procname:=procname+'_eq';
|
|
unequaln:
|
|
begin
|
|
procname:=procname+'_eq';
|
|
notnode:=true;
|
|
end;
|
|
else
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),left.resultdef.typename,right.resultdef.typename);
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
case nodetype of
|
|
addn:
|
|
procname:='ADD';
|
|
muln:
|
|
procname:='MUL';
|
|
subn:
|
|
procname:='SUB';
|
|
slashn:
|
|
procname:='DIV';
|
|
ltn:
|
|
procname:='LT';
|
|
lten:
|
|
procname:='LE';
|
|
gtn:
|
|
procname:='GT';
|
|
gten:
|
|
procname:='GE';
|
|
equaln:
|
|
procname:='EQ';
|
|
unequaln:
|
|
procname:='NE';
|
|
else
|
|
CGMessage3(type_e_operator_not_supported_for_types,node2opstr(nodetype),left.resultdef.typename,right.resultdef.typename);
|
|
end;
|
|
case tfloatdef(left.resultdef).floattype of
|
|
s32real:
|
|
begin
|
|
procname:=procname+'S';
|
|
if nodetype in [addn,muln,subn,slashn] then
|
|
procname:=lower(procname);
|
|
end;
|
|
s64real:
|
|
procname:=procname+'D';
|
|
{!!! not yet implemented
|
|
s128real:
|
|
}
|
|
else
|
|
internalerror(2005082602);
|
|
end;
|
|
|
|
end;
|
|
{ cast softfpu result? }
|
|
if not(target_info.system in system_wince) then
|
|
begin
|
|
if nodetype in [ltn,lten,gtn,gten,equaln,unequaln] then
|
|
resultdef:=booltype;
|
|
result:=ctypeconvnode.create_internal(ccallnode.createintern(procname,ccallparanode.create(
|
|
ctypeconvnode.create_internal(right,fdef),
|
|
ccallparanode.create(
|
|
ctypeconvnode.create_internal(left,fdef),nil))),resultdef);
|
|
end
|
|
else
|
|
result:=ccallnode.createintern(procname,ccallparanode.create(right,
|
|
ccallparanode.create(left,nil)));
|
|
left:=nil;
|
|
right:=nil;
|
|
|
|
{ do we need to reverse the result }
|
|
if notnode then
|
|
result:=cnotnode.create(result);
|
|
end;
|
|
|
|
|
|
function taddnode.pass_1 : tnode;
|
|
var
|
|
{$ifdef addstringopt}
|
|
hp : tnode;
|
|
{$endif addstringopt}
|
|
lt,rt : tnodetype;
|
|
rd,ld : tdef;
|
|
newstatement : tstatementnode;
|
|
temp : ttempcreatenode;
|
|
begin
|
|
result:=nil;
|
|
|
|
{ Can we optimize multiple string additions into a single call?
|
|
This need to be done on a complete tree to detect the multiple
|
|
add nodes and is therefor done before the subtrees are processed }
|
|
if canbemultistringadd(self) then
|
|
begin
|
|
result := genmultistringadd(self);
|
|
exit;
|
|
end;
|
|
|
|
{ first do the two subtrees }
|
|
firstpass(left);
|
|
firstpass(right);
|
|
|
|
if codegenerror then
|
|
exit;
|
|
|
|
{ load easier access variables }
|
|
rd:=right.resultdef;
|
|
ld:=left.resultdef;
|
|
rt:=right.nodetype;
|
|
lt:=left.nodetype;
|
|
|
|
{ int/int gives real/real! }
|
|
if nodetype=slashn then
|
|
begin
|
|
{$ifdef cpufpemu}
|
|
if (current_settings.fputype=fpu_soft) or (cs_fp_emulation in current_settings.moduleswitches) then
|
|
begin
|
|
result:=first_addfloat;
|
|
if assigned(result) then
|
|
exit;
|
|
end;
|
|
{$endif cpufpemu}
|
|
expectloc:=LOC_FPUREGISTER;
|
|
{ maybe we need an integer register to save }
|
|
{ a reference }
|
|
if ((left.expectloc<>LOC_FPUREGISTER) or
|
|
(right.expectloc<>LOC_FPUREGISTER)) and
|
|
(left.registersint=right.registersint) then
|
|
calcregisters(self,1,1,0)
|
|
else
|
|
calcregisters(self,0,1,0);
|
|
{ an add node always first loads both the left and the }
|
|
{ right in the fpu before doing the calculation. However, }
|
|
{ calcregisters(0,2,0) will overestimate the number of }
|
|
{ necessary registers (it will make it 3 in case one of }
|
|
{ the operands is already in the fpu) (JM) }
|
|
if ((left.expectloc<>LOC_FPUREGISTER) or
|
|
(right.expectloc<>LOC_FPUREGISTER)) and
|
|
(registersfpu < 2) then
|
|
inc(registersfpu);
|
|
end
|
|
|
|
{ if both are orddefs then check sub types }
|
|
else if (ld.typ=orddef) and (rd.typ=orddef) then
|
|
begin
|
|
{ 2 booleans ? }
|
|
if is_boolean(ld) and is_boolean(rd) then
|
|
begin
|
|
if (not(cs_full_boolean_eval in current_settings.localswitches) or
|
|
(nf_short_bool in flags)) and
|
|
(nodetype in [andn,orn]) then
|
|
begin
|
|
expectloc:=LOC_JUMP;
|
|
calcregisters(self,0,0,0);
|
|
end
|
|
else
|
|
begin
|
|
if nodetype in [ltn,lten,gtn,gten,equaln,unequaln] then
|
|
begin
|
|
expectloc:=LOC_FLAGS;
|
|
if (left.expectloc in [LOC_JUMP,LOC_FLAGS]) and
|
|
(left.expectloc in [LOC_JUMP,LOC_FLAGS]) then
|
|
calcregisters(self,2,0,0)
|
|
else
|
|
calcregisters(self,1,0,0);
|
|
end
|
|
else
|
|
begin
|
|
expectloc:=LOC_REGISTER;
|
|
calcregisters(self,0,0,0);
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
{ Both are chars? only convert to shortstrings for addn }
|
|
if is_char(ld) then
|
|
begin
|
|
if nodetype=addn then
|
|
internalerror(200103291);
|
|
expectloc:=LOC_FLAGS;
|
|
calcregisters(self,1,0,0);
|
|
end
|
|
{$ifndef cpu64bit}
|
|
{ is there a 64 bit type ? }
|
|
else if (torddef(ld).ordtype in [s64bit,u64bit,scurrency]) then
|
|
begin
|
|
result := first_add64bitint;
|
|
if assigned(result) then
|
|
exit;
|
|
if nodetype in [addn,subn,muln,andn,orn,xorn] then
|
|
expectloc:=LOC_REGISTER
|
|
else
|
|
expectloc:=LOC_JUMP;
|
|
calcregisters(self,2,0,0)
|
|
end
|
|
{$endif cpu64bit}
|
|
{ is there a cardinal? }
|
|
else if (torddef(ld).ordtype=u32bit) then
|
|
begin
|
|
if nodetype in [addn,subn,muln,andn,orn,xorn] then
|
|
expectloc:=LOC_REGISTER
|
|
else
|
|
expectloc:=LOC_FLAGS;
|
|
calcregisters(self,1,0,0);
|
|
{ for unsigned mul we need an extra register }
|
|
if nodetype=muln then
|
|
inc(registersint);
|
|
end
|
|
{ generic s32bit conversion }
|
|
else
|
|
begin
|
|
if nodetype in [addn,subn,muln,andn,orn,xorn] then
|
|
expectloc:=LOC_REGISTER
|
|
else
|
|
expectloc:=LOC_FLAGS;
|
|
calcregisters(self,1,0,0);
|
|
end;
|
|
end
|
|
|
|
{ left side a setdef, must be before string processing,
|
|
else array constructor can be seen as array of char (PFV) }
|
|
else if (ld.typ=setdef) then
|
|
begin
|
|
if not(is_varset(ld)) and not(is_normalset(ld)) then
|
|
begin
|
|
if nodetype in [ltn,lten,gtn,gten,equaln,unequaln] then
|
|
expectloc:=LOC_FLAGS
|
|
else
|
|
expectloc:=LOC_REGISTER;
|
|
{ are we adding set elements ? }
|
|
if right.nodetype=setelementn then
|
|
begin
|
|
{ add range?
|
|
the smallset code can't handle set ranges }
|
|
if assigned(tsetelementnode(right).right) then
|
|
begin
|
|
result:=internalstatements(newstatement);
|
|
|
|
{ create temp for result }
|
|
temp:=ctempcreatenode.create(resultdef,resultdef.size,tt_persistent,true);
|
|
addstatement(newstatement,temp);
|
|
|
|
{ add a range or a single element? }
|
|
if assigned(tsetelementnode(right).right) then
|
|
addstatement(newstatement,ccallnode.createintern('fpc_varset_set_range',
|
|
ccallparanode.create(cordconstnode.create(resultdef.size,sinttype,false),
|
|
ccallparanode.create(ctypeconvnode.create_internal(tsetelementnode(right).right,sinttype),
|
|
ccallparanode.create(ctypeconvnode.create_internal(tsetelementnode(right).left,sinttype),
|
|
ccallparanode.create(ctemprefnode.create(temp),
|
|
ccallparanode.create(left,nil))))))
|
|
)
|
|
else
|
|
addstatement(newstatement,ccallnode.createintern('fpc_varset_set',
|
|
ccallparanode.create(cordconstnode.create(resultdef.size,sinttype,false),
|
|
ccallparanode.create(ctypeconvnode.create_internal(tsetelementnode(right).left,sinttype),
|
|
ccallparanode.create(ctemprefnode.create(temp),
|
|
ccallparanode.create(left,nil)))))
|
|
);
|
|
|
|
{ remove reused parts from original node }
|
|
tsetelementnode(right).right:=nil;
|
|
tsetelementnode(right).left:=nil;
|
|
left:=nil;
|
|
{ the last statement should return the value as
|
|
location and type, this is done be referencing the
|
|
temp and converting it first from a persistent temp to
|
|
normal temp }
|
|
addstatement(newstatement,ctempdeletenode.create_normal_temp(temp));
|
|
addstatement(newstatement,ctemprefnode.create(temp));
|
|
end
|
|
else
|
|
calcregisters(self,2,0,0)
|
|
end
|
|
else
|
|
calcregisters(self,1,0,0);
|
|
end
|
|
else
|
|
{$ifdef MMXSET}
|
|
{$ifdef i386}
|
|
if cs_mmx in current_settings.localswitches then
|
|
begin
|
|
expectloc:=LOC_MMXREGISTER;
|
|
calcregisters(self,0,0,4);
|
|
end
|
|
else
|
|
{$endif}
|
|
{$endif MMXSET}
|
|
begin
|
|
result := first_addset;
|
|
if assigned(result) then
|
|
exit;
|
|
expectloc:=LOC_CREFERENCE;
|
|
calcregisters(self,0,0,0);
|
|
{ here we call SET... }
|
|
include(current_procinfo.flags,pi_do_call);
|
|
end;
|
|
end
|
|
|
|
{ compare pchar by addresses like BP/Delphi }
|
|
else if is_pchar(ld) then
|
|
begin
|
|
if nodetype in [addn,subn,muln,andn,orn,xorn] then
|
|
expectloc:=LOC_REGISTER
|
|
else
|
|
expectloc:=LOC_FLAGS;
|
|
calcregisters(self,1,0,0);
|
|
end
|
|
|
|
{ is one of the operands a string }
|
|
else if (ld.typ=stringdef) then
|
|
begin
|
|
if is_widestring(ld) then
|
|
begin
|
|
{ this is only for add, the comparisaion is handled later }
|
|
expectloc:=LOC_REGISTER;
|
|
end
|
|
else if is_ansistring(ld) then
|
|
begin
|
|
{ this is only for add, the comparisaion is handled later }
|
|
expectloc:=LOC_REGISTER;
|
|
end
|
|
else if is_longstring(ld) then
|
|
begin
|
|
{ this is only for add, the comparisaion is handled later }
|
|
expectloc:=LOC_REFERENCE;
|
|
end
|
|
else
|
|
begin
|
|
{$ifdef addstringopt}
|
|
{ can create a call which isn't handled by callparatemp }
|
|
if canbeaddsstringcharoptnode(self) then
|
|
begin
|
|
hp := genaddsstringcharoptnode(self);
|
|
pass_1 := hp;
|
|
exit;
|
|
end
|
|
else
|
|
{$endif addstringopt}
|
|
begin
|
|
{ Fix right to be shortstring }
|
|
if is_char(right.resultdef) then
|
|
begin
|
|
inserttypeconv(right,cshortstringtype);
|
|
firstpass(right);
|
|
end;
|
|
end;
|
|
{$ifdef addstringopt}
|
|
{ can create a call which isn't handled by callparatemp }
|
|
if canbeaddsstringcsstringoptnode(self) then
|
|
begin
|
|
hp := genaddsstringcsstringoptnode(self);
|
|
pass_1 := hp;
|
|
exit;
|
|
end;
|
|
{$endif addstringopt}
|
|
end;
|
|
{ otherwise, let addstring convert everything }
|
|
result := first_addstring;
|
|
exit;
|
|
end
|
|
|
|
{ is one a real float ? }
|
|
else if (rd.typ=floatdef) or (ld.typ=floatdef) then
|
|
begin
|
|
{$ifdef cpufpemu}
|
|
if (current_settings.fputype=fpu_soft) or (cs_fp_emulation in current_settings.moduleswitches) then
|
|
begin
|
|
result:=first_addfloat;
|
|
if assigned(result) then
|
|
exit;
|
|
end;
|
|
{$endif cpufpemu}
|
|
if nodetype in [addn,subn,muln,andn,orn,xorn] then
|
|
expectloc:=LOC_FPUREGISTER
|
|
else
|
|
expectloc:=LOC_FLAGS;
|
|
calcregisters(self,0,1,0);
|
|
{ an add node always first loads both the left and the }
|
|
{ right in the fpu before doing the calculation. However, }
|
|
{ calcregisters(0,2,0) will overestimate the number of }
|
|
{ necessary registers (it will make it 3 in case one of }
|
|
{ the operands is already in the fpu) (JM) }
|
|
if ((left.expectloc<>LOC_FPUREGISTER) or
|
|
(right.expectloc<>LOC_FPUREGISTER)) and
|
|
(registersfpu < 2) then
|
|
inc(registersfpu);
|
|
end
|
|
|
|
{ pointer comperation and subtraction }
|
|
else if (ld.typ=pointerdef) then
|
|
begin
|
|
if nodetype in [addn,subn,muln,andn,orn,xorn] then
|
|
expectloc:=LOC_REGISTER
|
|
else
|
|
expectloc:=LOC_FLAGS;
|
|
calcregisters(self,1,0,0);
|
|
end
|
|
|
|
else if is_class_or_interface(ld) then
|
|
begin
|
|
expectloc:=LOC_FLAGS;
|
|
calcregisters(self,1,0,0);
|
|
end
|
|
|
|
else if (ld.typ=classrefdef) then
|
|
begin
|
|
expectloc:=LOC_FLAGS;
|
|
calcregisters(self,1,0,0);
|
|
end
|
|
|
|
{ support procvar=nil,procvar<>nil }
|
|
else if ((ld.typ=procvardef) and (rt=niln)) or
|
|
((rd.typ=procvardef) and (lt=niln)) then
|
|
begin
|
|
expectloc:=LOC_FLAGS;
|
|
calcregisters(self,1,0,0);
|
|
end
|
|
|
|
{$ifdef SUPPORT_MMX}
|
|
{ mmx support, this must be before the zero based array
|
|
check }
|
|
else if (cs_mmx in current_settings.localswitches) and is_mmx_able_array(ld) and
|
|
is_mmx_able_array(rd) then
|
|
begin
|
|
expectloc:=LOC_MMXREGISTER;
|
|
calcregisters(self,0,0,1);
|
|
end
|
|
{$endif SUPPORT_MMX}
|
|
|
|
else if (rd.typ=pointerdef) or (ld.typ=pointerdef) then
|
|
begin
|
|
expectloc:=LOC_REGISTER;
|
|
calcregisters(self,1,0,0);
|
|
end
|
|
|
|
else if (rd.typ=procvardef) and
|
|
(ld.typ=procvardef) and
|
|
equal_defs(rd,ld) then
|
|
begin
|
|
expectloc:=LOC_FLAGS;
|
|
calcregisters(self,1,0,0);
|
|
end
|
|
|
|
else if (ld.typ=enumdef) then
|
|
begin
|
|
expectloc:=LOC_FLAGS;
|
|
calcregisters(self,1,0,0);
|
|
end
|
|
|
|
{$ifdef SUPPORT_MMX}
|
|
else if (cs_mmx in current_settings.localswitches) and
|
|
is_mmx_able_array(ld) and
|
|
is_mmx_able_array(rd) then
|
|
begin
|
|
expectloc:=LOC_MMXREGISTER;
|
|
calcregisters(self,0,0,1);
|
|
end
|
|
{$endif SUPPORT_MMX}
|
|
|
|
{ the general solution is to convert to 32 bit int }
|
|
else
|
|
begin
|
|
expectloc:=LOC_REGISTER;
|
|
calcregisters(self,1,0,0);
|
|
end;
|
|
end;
|
|
|
|
{$ifdef state_tracking}
|
|
function Taddnode.track_state_pass(exec_known:boolean):boolean;
|
|
|
|
var factval:Tnode;
|
|
|
|
begin
|
|
track_state_pass:=false;
|
|
if left.track_state_pass(exec_known) then
|
|
begin
|
|
track_state_pass:=true;
|
|
left.resultdef:=nil;
|
|
do_typecheckpass(left);
|
|
end;
|
|
factval:=aktstate.find_fact(left);
|
|
if factval<>nil then
|
|
begin
|
|
track_state_pass:=true;
|
|
left.destroy;
|
|
left:=factval.getcopy;
|
|
end;
|
|
if right.track_state_pass(exec_known) then
|
|
begin
|
|
track_state_pass:=true;
|
|
right.resultdef:=nil;
|
|
do_typecheckpass(right);
|
|
end;
|
|
factval:=aktstate.find_fact(right);
|
|
if factval<>nil then
|
|
begin
|
|
track_state_pass:=true;
|
|
right.destroy;
|
|
right:=factval.getcopy;
|
|
end;
|
|
end;
|
|
{$endif}
|
|
|
|
begin
|
|
caddnode:=taddnode;
|
|
end.
|