{ $Id$ Copyright (c) 1993-98 by Florian Klaempfl This unit generates i386 (or better) assembler from the parse tree 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. **************************************************************************** } {$ifdef TP} {$E+,F+,N+,D+,L+,Y+} {$endif} unit cgi386; interface uses tree; { produces assembler for the expression in variable p } { and produces an assembler node at the end } procedure generatecode(var p : ptree); { produces the actual code } function do_secondpass(var p : ptree) : boolean; procedure secondpass(var p : ptree); {$ifdef test_dest_loc} const { used to avoid temporary assignments } dest_loc_known : boolean = false; in_dest_loc : boolean = false; dest_loc_tree : ptree = nil; var dest_loc : tlocation; procedure mov_reg_to_dest(p : ptree; s : topsize; reg : tregister); {$endif test_dest_loc} implementation uses verbose,cobjects,systems,globals,files, symtable,types,aasm,i386, pass_1,hcodegen,tgeni386,cgai386 {$ifdef GDB} ,gdb {$endif} {$ifdef TP} ,cgi3862 {$endif} ,cg386con,cg386mat,cg386cnv ; const never_copy_const_param : boolean = false; {$ifdef test_dest_loc} procedure mov_reg_to_dest(p : ptree; s : topsize; reg : tregister); begin if (dest_loc.loc=LOC_CREGISTER) or (dest_loc.loc=LOC_REGISTER) then begin emit_reg_reg(A_MOV,s,reg,dest_loc.register); p^.location:=dest_loc; in_dest_loc:=true; end else if (dest_loc.loc=LOC_REFERENCE) or (dest_loc.loc=LOC_MEM) then begin exprasmlist^.concat(new(pai386,op_reg_ref(A_MOV,s,reg,newreference(dest_loc.reference)))); p^.location:=dest_loc; in_dest_loc:=true; end else internalerror(20080); end; {$endif test_dest_loc} const bytes2Sxx:array[1..4] of Topsize=(S_B,S_W,S_NO,S_L); procedure message(const t : tmsgconst); var olderrorcount : longint; begin if not(codegenerror) then begin olderrorcount:=status.errorcount; verbose.Message(t); codegenerror:=olderrorcount<>status.errorcount; end; end; procedure message1(const t : tmsgconst;const s : string); var olderrorcount : longint; begin if not(codegenerror) then begin olderrorcount:=status.errorcount; verbose.Message1(t,s); codegenerror:=olderrorcount<>status.errorcount; end; end; procedure message2(const t : tmsgconst;const s1,s2 : string); var olderrorcount : longint; begin if not(codegenerror) then begin olderrorcount:=status.errorcount; verbose.Message2(t,s1,s2); codegenerror:=olderrorcount<>status.errorcount; end; end; procedure message3(const t : tmsgconst;const s1,s2,s3 : string); var olderrorcount : longint; begin if not(codegenerror) then begin olderrorcount:=status.errorcount; verbose.Message3(t,s1,s2,s3); codegenerror:=olderrorcount<>status.errorcount; end; end; type secondpassproc = procedure(var p : ptree); procedure seconderror(var p : ptree); begin p^.error:=true; codegenerror:=true; end; var { this is for open arrays and strings } { but be careful, this data is in the } { generated code destroyed quick, and also } { the next call of secondload destroys this } { data } { So be careful using the informations } { provided by this variables } highframepointer : tregister; highoffset : longint; {$ifndef TP} {$I cgi386ad.inc} {$endif TP} procedure secondload(var p : ptree); var hregister : tregister; symtabletype : tsymtabletype; i : longint; hp : preference; begin simple_loadn:=true; reset_reference(p^.location.reference); case p^.symtableentry^.typ of { this is only for toasm and toaddr } absolutesym : begin stringdispose(p^.location.reference.symbol); if (pabsolutesym(p^.symtableentry)^.abstyp=toaddr) then begin if pabsolutesym(p^.symtableentry)^.absseg then p^.location.reference.segment:=R_FS; p^.location.reference.offset:=pabsolutesym(p^.symtableentry)^.address; end else p^.location.reference.symbol:=stringdup(p^.symtableentry^.mangledname); maybe_concat_external(p^.symtableentry^.owner,p^.symtableentry^.mangledname); end; varsym : begin hregister:=R_NO; symtabletype:=p^.symtable^.symtabletype; { in case it is a register variable: } if pvarsym(p^.symtableentry)^.reg<>R_NO then begin p^.location.loc:=LOC_CREGISTER; p^.location.register:=pvarsym(p^.symtableentry)^.reg; unused:=unused-[pvarsym(p^.symtableentry)^.reg]; end else begin { first handle local and temporary variables } if (symtabletype in [parasymtable,inlinelocalsymtable, inlineparasymtable,localsymtable]) then begin p^.location.reference.base:=procinfo.framepointer; p^.location.reference.offset:=pvarsym(p^.symtableentry)^.address; if (symtabletype=localsymtable) or (symtabletype=inlinelocalsymtable) then p^.location.reference.offset:=-p^.location.reference.offset; if (symtabletype=parasymtable) or (symtabletype=inlineparasymtable) then inc(p^.location.reference.offset,p^.symtable^.call_offset); if (lexlevel>(p^.symtable^.symtablelevel)) then begin hregister:=getregister32; { make a reference } hp:=new_reference(procinfo.framepointer, procinfo.framepointer_offset); exprasmlist^.concat(new(pai386,op_ref_reg(A_MOV,S_L,hp,hregister))); simple_loadn:=false; i:=lexlevel-1; while i>(p^.symtable^.symtablelevel) do begin { make a reference } hp:=new_reference(hregister,8); exprasmlist^.concat(new(pai386,op_ref_reg(A_MOV,S_L,hp,hregister))); dec(i); end; p^.location.reference.base:=hregister; end; end else case symtabletype of unitsymtable,globalsymtable, staticsymtable : begin stringdispose(p^.location.reference.symbol); p^.location.reference.symbol:=stringdup(p^.symtableentry^.mangledname); if symtabletype=unitsymtable then concat_external(p^.symtableentry^.mangledname,EXT_NEAR); end; objectsymtable : begin if (pvarsym(p^.symtableentry)^.properties and sp_static)<>0 then begin stringdispose(p^.location.reference.symbol); p^.location.reference.symbol:=stringdup(p^.symtableentry^.mangledname); if p^.symtable^.defowner^.owner^.symtabletype=unitsymtable then concat_external(p^.symtableentry^.mangledname,EXT_NEAR); end else begin p^.location.reference.base:=R_ESI; p^.location.reference.offset:=pvarsym(p^.symtableentry)^.address; end; end; withsymtable: begin hregister:=getregister32; p^.location.reference.base:=hregister; { make a reference } { symtable datasize field contains the offset of the temp stored } hp:=new_reference(procinfo.framepointer, p^.symtable^.datasize); exprasmlist^.concat(new(pai386,op_ref_reg(A_MOV,S_L,hp,hregister))); p^.location.reference.offset:= pvarsym(p^.symtableentry)^.address; end; end; { in case call by reference, then calculate: } if (pvarsym(p^.symtableentry)^.varspez=vs_var) or ((pvarsym(p^.symtableentry)^.varspez=vs_const) and dont_copy_const_param(pvarsym(p^.symtableentry)^.definition)) or { call by value open arrays are also indirect addressed } is_open_array(pvarsym(p^.symtableentry)^.definition) then begin simple_loadn:=false; if hregister=R_NO then hregister:=getregister32; if (p^.location.reference.base=procinfo.framepointer) then begin highframepointer:=p^.location.reference.base; highoffset:=p^.location.reference.offset; end else begin highframepointer:=R_EDI; highoffset:=p^.location.reference.offset; exprasmlist^.concat(new(pai386,op_reg_reg(A_MOV,S_L, p^.location.reference.base,R_EDI))); end; exprasmlist^.concat(new(pai386,op_ref_reg(A_MOV,S_L,newreference(p^.location.reference), hregister))); clear_reference(p^.location.reference); p^.location.reference.base:=hregister; end; { if (pvarsym(p^.symtableentry)^.definition^.deftype=objectdef) and ((pobjectdef(pvarsym(p^.symtableentry)^.definition)^.options and oois_class)<>0) then begin simple_loadn:=false; if hregister=R_NO then hregister:=getregister32; exprasmlist^.concat(new(pai386,op_ref_reg(A_MOV,S_L,newreference(p^.location.reference), hregister))); clear_reference(p^.location.reference); p^.location.reference.base:=hregister; end; } end; end; procsym: begin {!!!!! Be aware, work on virtual methods too } stringdispose(p^.location.reference.symbol); p^.location.reference.symbol:= stringdup(pprocsym(p^.symtableentry)^.definition^.mangledname); maybe_concat_external(p^.symtable,p^.symtableentry^.mangledname); end; typedconstsym : begin stringdispose(p^.location.reference.symbol); p^.location.reference.symbol:=stringdup(p^.symtableentry^.mangledname); maybe_concat_external(p^.symtable,p^.symtableentry^.mangledname); end; else internalerror(4); end; end; procedure secondassignment(var p : ptree); var opsize : topsize; otlabel,hlabel,oflabel : plabel; hregister : tregister; loc : tloc; begin otlabel:=truelabel; oflabel:=falselabel; getlabel(truelabel); getlabel(falselabel); { calculate left sides } if not(p^.concat_string) then secondpass(p^.left); if codegenerror then exit; case p^.left^.location.loc of LOC_REFERENCE : begin { in case left operator uses to register } { but to few are free then LEA } if (p^.left^.location.reference.base<>R_NO) and (p^.left^.location.reference.index<>R_NO) and (usablereg32
LOC_REFERENCE then
internalerror(10010)
else
floatstore(pfloatdef(p^.left^.resulttype)^.typ,
p^.left^.location.reference);
end;
LOC_JUMP : begin
getlabel(hlabel);
emitl(A_LABEL,truelabel);
if loc=LOC_CREGISTER then
exprasmlist^.concat(new(pai386,op_const_reg(A_MOV,S_B,
1,p^.left^.location.register)))
else
exprasmlist^.concat(new(pai386,op_const_ref(A_MOV,S_B,
1,newreference(p^.left^.location.reference))));
{exprasmlist^.concat(new(pai386,op_const_loc(A_MOV,S_B,
1,p^.left^.location)));}
emitl(A_JMP,hlabel);
emitl(A_LABEL,falselabel);
if loc=LOC_CREGISTER then
exprasmlist^.concat(new(pai386,op_reg_reg(A_XOR,S_B,
p^.left^.location.register,
p^.left^.location.register)))
else
exprasmlist^.concat(new(pai386,op_const_ref(A_MOV,S_B,
0,newreference(p^.left^.location.reference))));
emitl(A_LABEL,hlabel);
end;
LOC_FLAGS : begin
if loc=LOC_CREGISTER then
exprasmlist^.concat(new(pai386,op_reg(flag_2_set[p^.right^.location.resflags],S_B,
p^.left^.location.register)))
else
exprasmlist^.concat(new(pai386,op_ref(flag_2_set[p^.right^.location.resflags],S_B,
newreference(p^.left^.location.reference))));
end;
end;
truelabel:=otlabel;
falselabel:=oflabel;
end;
procedure secondaddr(var p : ptree);
begin
secondpass(p^.left);
p^.location.loc:=LOC_REGISTER;
del_reference(p^.left^.location.reference);
p^.location.register:=getregister32;
{@ on a procvar means returning an address to the procedure that
is stored in it.}
{ yes but p^.left^.symtableentry can be nil
for example on @self !! }
{ symtableentry can be also invalid, if left is no tree node }
if (p^.left^.treetype=loadn) and
assigned(p^.left^.symtableentry) and
(p^.left^.symtableentry^.typ=varsym) and
(pvarsym(p^.left^.symtableentry)^.definition^.deftype=procvardef) then
exprasmlist^.concat(new(pai386,op_ref_reg(A_MOV,S_L,
newreference(p^.left^.location.reference),
p^.location.register)))
else
exprasmlist^.concat(new(pai386,op_ref_reg(A_LEA,S_L,
newreference(p^.left^.location.reference),
p^.location.register)));
{ for use of other segments }
if p^.left^.location.reference.segment<>R_DEFAULT_SEG then
p^.location.segment:=p^.left^.location.reference.segment;
end;
procedure seconddoubleaddr(var p : ptree);
begin
secondpass(p^.left);
p^.location.loc:=LOC_REGISTER;
del_reference(p^.left^.location.reference);
p^.location.register:=getregister32;
exprasmlist^.concat(new(pai386,op_ref_reg(A_LEA,S_L,
newreference(p^.left^.location.reference),
p^.location.register)));
end;
procedure secondnot(var p : ptree);
const
flagsinvers : array[F_E..F_BE] of tresflags =
(F_NE,F_E,F_LE,F_GE,F_L,F_G,F_NC,F_C,
F_A,F_AE,F_B,F_BE);
var
hl : plabel;
opsize : topsize;
begin
if (p^.resulttype^.deftype=orddef) and
(porddef(p^.resulttype)^.typ in [bool8bit,bool16bit,bool32bit]) then
begin
case porddef(p^.resulttype)^.typ of
bool8bit : opsize:=S_B;
bool16bit : opsize:=S_W;
bool32bit : opsize:=S_L;
end;
case p^.location.loc of
LOC_JUMP : begin
hl:=truelabel;
truelabel:=falselabel;
falselabel:=hl;
secondpass(p^.left);
maketojumpbool(p^.left);
hl:=truelabel;
truelabel:=falselabel;
falselabel:=hl;
end;
LOC_FLAGS : begin
secondpass(p^.left);
p^.location.resflags:=flagsinvers[p^.left^.location.resflags];
end;
LOC_REGISTER : begin
secondpass(p^.left);
p^.location.register:=p^.left^.location.register;
exprasmlist^.concat(new(pai386,op_const_reg(A_XOR,opsize,1,p^.location.register)));
end;
LOC_CREGISTER : begin
secondpass(p^.left);
p^.location.loc:=LOC_REGISTER;
case porddef(p^.resulttype)^.typ of
bool8bit : p^.location.register:=reg32toreg8(getregister32);
bool16bit : p^.location.register:=reg32toreg16(getregister32);
bool32bit : p^.location.register:=getregister32;
end;
emit_reg_reg(A_MOV,opsize,p^.left^.location.register,p^.location.register);
exprasmlist^.concat(new(pai386,op_const_reg(A_XOR,opsize,1,p^.location.register)));
end;
LOC_REFERENCE,
LOC_MEM : begin
secondpass(p^.left);
del_reference(p^.left^.location.reference);
p^.location.loc:=LOC_REGISTER;
case porddef(p^.resulttype)^.typ of
bool8bit : p^.location.register:=reg32toreg8(getregister32);
bool16bit : p^.location.register:=reg32toreg16(getregister32);
bool32bit : p^.location.register:=getregister32;
end;
if p^.left^.location.loc=LOC_CREGISTER then
emit_reg_reg(A_MOV,opsize,p^.left^.location.register,p^.location.register)
else
exprasmlist^.concat(new(pai386,op_ref_reg(A_MOV,opsize,
newreference(p^.left^.location.reference),p^.location.register)));
exprasmlist^.concat(new(pai386,op_const_reg(A_XOR,opsize,1,p^.location.register)));
end;
end;
end
{$ifdef SUPPORT_MMX}
else if (cs_mmx in aktswitches) and is_mmx_able_array(p^.left^.resulttype) then
begin
secondpass(p^.left);
p^.location.loc:=LOC_MMXREGISTER;
{ prepare EDI }
exprasmlist^.concat(new(pai386,op_const_reg(A_MOV,S_L,$ffffffff,R_EDI)));
{ load operand }
case p^.left^.location.loc of
LOC_MMXREGISTER:
p^.location:=p^.left^.location;
LOC_CMMXREGISTER:
begin
p^.location.register:=getregistermmx;
emit_reg_reg(A_MOVQ,S_NO,p^.left^.location.register,
p^.location.register);
end;
LOC_REFERENCE,LOC_MEM:
begin
del_reference(p^.left^.location.reference);
p^.location.register:=getregistermmx;
exprasmlist^.concat(new(pai386,op_ref_reg(A_MOVQ,S_NO,
newreference(p^.left^.location.reference),
p^.location.register)));
end;
end;
{ load mask }
emit_reg_reg(A_MOV,S_D,R_EDI,R_MM7);
{ lower 32 bit }
emit_reg_reg(A_PXOR,S_D,R_MM7,p^.location.register);
{ shift mask }
exprasmlist^.concat(new(pai386,op_const_reg(A_PSLLQ,S_NO,
32,R_MM7)));
{ higher 32 bit }
emit_reg_reg(A_PXOR,S_D,R_MM7,p^.location.register);
end
{$endif SUPPORT_MMX}
else
begin
secondpass(p^.left);
p^.location.loc:=LOC_REGISTER;
case p^.left^.location.loc of
LOC_REGISTER : begin
p^.location.register:=p^.left^.location.register;
exprasmlist^.concat(new(pai386,op_reg(A_NOT,S_L,p^.location.register)));
end;
LOC_CREGISTER : begin
p^.location.register:=getregister32;
emit_reg_reg(A_MOV,S_L,p^.left^.location.register,
p^.location.register);
exprasmlist^.concat(new(pai386,op_reg(A_NOT,S_L,p^.location.register)));
end;
LOC_REFERENCE,LOC_MEM :
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)));
exprasmlist^.concat(new(pai386,op_reg(A_NOT,S_L,p^.location.register)));
end;
end;
{if p^.left^.location.loc=loc_register then
p^.location.register:=p^.left^.location.register
else
begin
del_locref(p^.left^.location);
p^.location.register:=getregister32;
exprasmlist^.concat(new(pai386,op_loc_reg(A_MOV,S_L,
p^.left^.location,
p^.location.register)));
end;
exprasmlist^.concat(new(pai386,op_reg(A_NOT,S_L,p^.location.register)));}
end;
end;
procedure secondnothing(var p : ptree);
begin
end;
procedure secondderef(var p : ptree);
var
hr : tregister;
begin
secondpass(p^.left);
clear_reference(p^.location.reference);
case p^.left^.location.loc of
LOC_REGISTER:
p^.location.reference.base:=p^.left^.location.register;
LOC_CREGISTER:
begin
{ ... and reserve one for the pointer }
hr:=getregister32;
emit_reg_reg(A_MOV,S_L,p^.left^.location.register,hr);
p^.location.reference.base:=hr;
end;
else
begin
{ free register }
del_reference(p^.left^.location.reference);
{ ...and reserve one for the pointer }
hr:=getregister32;
exprasmlist^.concat(new(pai386,op_ref_reg(
A_MOV,S_L,newreference(p^.left^.location.reference),
hr)));
p^.location.reference.base:=hr;
end;
end;
end;
procedure secondvecn(var p : ptree);
var
pushed : boolean;
ind,hr : tregister;
_p : ptree;
function get_mul_size:longint;
begin
if p^.memindex then
get_mul_size:=1
else
get_mul_size:=p^.resulttype^.size;
end;
procedure calc_emit_mul;
var
l1,l2 : longint;
begin
l1:=get_mul_size;
case l1 of
1,2,4,8 : p^.location.reference.scalefactor:=l1;
else
begin
if ispowerof2(l1,l2) then
exprasmlist^.concat(new(pai386,op_const_reg(A_SHL,S_L,l2,ind)))
else
exprasmlist^.concat(new(pai386,op_const_reg(A_IMUL,S_L,l1,ind)));
end;
end;
end;
var
extraoffset : longint;
t : ptree;
hp : preference;
tai:Pai386;
begin
secondpass(p^.left);
set_location(p^.location,p^.left^.location);
{ in ansistrings S[1] is pchar(S)[0] !! }
if is_ansistring(p^.left^.resulttype) then
dec(p^.location.reference.offset);
{ offset can only differ from 0 if arraydef }
if p^.left^.resulttype^.deftype=arraydef then
dec(p^.location.reference.offset,
get_mul_size*parraydef(p^.left^.resulttype)^.lowrange);
if p^.right^.treetype=ordconstn then
begin
{ offset can only differ from 0 if arraydef }
if (p^.left^.resulttype^.deftype=arraydef) then
begin
if not(is_open_array(p^.left^.resulttype)) then
begin
if (p^.right^.value>parraydef(p^.left^.resulttype)^.highrange) or
(p^.right^.value