fpc/compiler/x86/rgx86.pas
2004-01-12 16:37:59 +00:00

840 lines
34 KiB
ObjectPascal

{
$Id$
Copyright (c) 1998-2002 by Florian Klaempfl
This unit implements the x86 specific class for the register
allocator
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 rgx86;
{$i fpcdefs.inc}
interface
uses
cpubase,
cpuinfo,
aasmbase,aasmtai,
cclasses,globtype,cgbase,rgobj;
type
trgx86 = class(trgobj)
function instr_spill_register(list:Taasmoutput;
instr:taicpu_abstract;
const r:Tsuperregisterset;
const spilltemplist:Tspill_temp_list): boolean;override;
end;
tpushedsavedloc = record
case byte of
0: (pushed: boolean);
1: (ofs: longint);
end;
tpushedsavedfpu = array[tsuperregister] of tpushedsavedloc;
trgx86fpu = class
{ The "usableregsxxx" contain all registers of type "xxx" that }
{ aren't currently allocated to a regvar. The "unusedregsxxx" }
{ contain all registers of type "xxx" that aren't currently }
{ allocated }
unusedregsfpu,usableregsfpu : Tsuperregisterset;
{ these counters contain the number of elements in the }
{ unusedregsxxx/usableregsxxx sets }
countunusedregsfpu : byte;
{ Contains the registers which are really used by the proc itself.
It doesn't take care of registers used by called procedures
}
used_in_proc : tcpuregisterset;
{reg_pushes_other : regvarother_longintarray;
is_reg_var_other : regvarother_booleanarray;
regvar_loaded_other : regvarother_booleanarray;}
{ tries to hold the amount of times which the current tree is processed }
t_times: longint;
fpuvaroffset : byte;
constructor create;
function getregisterfpu(list: taasmoutput) : tregister;
procedure ungetregisterfpu(list: taasmoutput; r : tregister);
{ pushes and restores registers }
procedure saveusedfpuregisters(list:Taasmoutput;
var saved:Tpushedsavedfpu;
const s:Tcpuregisterset);
procedure restoreusedfpuregisters(list:Taasmoutput;
const saved:Tpushedsavedfpu);
{ corrects the fpu stack register by ofs }
function correct_fpuregister(r : tregister;ofs : byte) : tregister;
end;
implementation
uses
systems,
verbose,
aasmcpu;
const
{ This value is used in tsaved. If the array value is equal
to this, then this means that this register is not used.}
reg_not_saved = $7fffffff;
{******************************************************************************
Trgcpu
******************************************************************************}
function trgx86.instr_spill_register(list:Taasmoutput;
instr:taicpu_abstract;
const r:Tsuperregisterset;
const spilltemplist:Tspill_temp_list): boolean;
{
Spill the registers in r in this instruction. Returns true if any help
registers are used. This procedure has become one big hack party, because
of the huge amount of situations you can have. The irregularity of the i386
instruction set doesn't help either. (DM)
}
var i:byte;
supreg:Tsuperregister;
subreg:Tsubregister;
helpreg:Tregister;
helpins:Taicpu;
op:Tasmop;
hopsize:Topsize;
pos:Tai;
begin
{Situation examples are in intel notation, so operand order:
mov eax , ebx
^^^ ^^^
oper[1] oper[0]
(DM)}
result:=false;
with taicpu(instr) do
begin
case ops of
1:
begin
if (oper[0]^.typ=top_reg) and
(getregtype(oper[0]^.reg)=regtype) then
begin
supreg:=getsupreg(oper[0]^.reg);
if supregset_in(r,supreg) then
begin
{Situation example:
push r20d ; r20d must be spilled into [ebp-12]
Change into:
push [ebp-12] ; Replace register by reference }
{ hopsize:=reg2opsize(oper[0].reg);}
oper[0]^.typ:=top_ref;
new(oper[0]^.ref);
oper[0]^.ref^:=spilltemplist[supreg];
{ oper[0]^.ref^.size:=hopsize;}
end;
end;
if oper[0]^.typ=top_ref then
begin
supreg:=getsupreg(oper[0]^.ref^.base);
if supregset_in(r,supreg) then
begin
{Situation example:
push [r21d+4*r22d] ; r21d must be spilled into [ebp-12]
Change into:
mov r23d,[ebp-12] ; Use a help register
push [r23d+4*r22d] ; Replace register by helpregister }
subreg:=getsubreg(oper[0]^.ref^.base);
if oper[0]^.ref^.index=NR_NO then
pos:=Tai(previous)
else
pos:=get_insert_pos(Tai(previous),getsupreg(oper[0]^.ref^.index),RS_INVALID,RS_INVALID);
getregisterinline(list,pos,subreg,helpreg);
result:=true;
helpins:=Taicpu.op_ref_reg(A_MOV,reg2opsize(oper[0]^.ref^.base),spilltemplist[supreg],helpreg);
if pos=nil then
list.insertafter(helpins,list.first)
else
list.insertafter(helpins,pos.next);
ungetregisterinline(list,helpins,helpreg);
forward_allocation(Tai(helpins.next),instr);
oper[0]^.ref^.base:=helpreg;
end;
supreg:=getsupreg(oper[0]^.ref^.index);
if supregset_in(r,supreg) then
begin
{Situation example:
push [r21d+4*r22d] ; r22d must be spilled into [ebp-12]
Change into:
mov r23d,[ebp-12] ; Use a help register
push [r21d+4*r23d] ; Replace register by helpregister }
subreg:=getsubreg(oper[0]^.ref^.index);
if oper[0]^.ref^.base=NR_NO then
pos:=Tai(instr.previous)
else
pos:=get_insert_pos(Tai(instr.previous),getsupreg(oper[0]^.ref^.base),RS_INVALID,RS_INVALID);
getregisterinline(list,pos,subreg,helpreg);
result:=true;
helpins:=Taicpu.op_ref_reg(A_MOV,reg2opsize(oper[0]^.ref^.index),spilltemplist[supreg],helpreg);
if pos=nil then
list.insertafter(helpins,list.first)
else
list.insertafter(helpins,pos.next);
ungetregisterinline(list,helpins,helpreg);
forward_allocation(Tai(helpins.next),instr);
oper[0]^.ref^.index:=helpreg;
end;
end;
end;
2:
begin
{ First spill the registers from the references. This is
required because the reference can be moved from this instruction
to a MOV instruction when spilling of the register operand is done }
for i:=0 to 1 do
if oper[i]^.typ=top_ref then
begin
supreg:=getsupreg(oper[i]^.ref^.base);
if supregset_in(r,supreg) then
begin
{Situation example:
add r20d,[r21d+4*r22d] ; r21d must be spilled into [ebp-12]
Change into:
mov r23d,[ebp-12] ; Use a help register
add r20d,[r23d+4*r22d] ; Replace register by helpregister }
subreg:=getsubreg(oper[i]^.ref^.base);
if i=1 then
pos:=get_insert_pos(Tai(instr.previous),getsupreg(oper[i]^.ref^.index),getsupreg(oper[0]^.reg),RS_INVALID)
else
pos:=get_insert_pos(Tai(instr.previous),getsupreg(oper[i]^.ref^.index),RS_INVALID,RS_INVALID);
getregisterinline(list,pos,subreg,helpreg);
result:=true;
helpins:=Taicpu.op_ref_reg(A_MOV,reg2opsize(oper[i]^.ref^.base),spilltemplist[supreg],helpreg);
if pos=nil then
list.insertafter(helpins,list.first)
else
list.insertafter(helpins,pos.next);
oper[i]^.ref^.base:=helpreg;
ungetregisterinline(list,helpins,helpreg);
forward_allocation(Tai(helpins.next),instr);
end;
supreg:=getsupreg(oper[i]^.ref^.index);
if supregset_in(r,supreg) then
begin
{Situation example:
add r20d,[r21d+4*r22d] ; r22d must be spilled into [ebp-12]
Change into:
mov r23d,[ebp-12] ; Use a help register
add r20d,[r21d+4*r23d] ; Replace register by helpregister }
subreg:=getsubreg(oper[i]^.ref^.index);
if i=1 then
pos:=get_insert_pos(Tai(instr.previous),getsupreg(oper[i]^.ref^.base),
getsupreg(oper[0]^.reg),RS_INVALID)
else
pos:=get_insert_pos(Tai(instr.previous),getsupreg(oper[i]^.ref^.base),RS_INVALID,RS_INVALID);
getregisterinline(list,pos,subreg,helpreg);
result:=true;
helpins:=Taicpu.op_ref_reg(A_MOV,reg2opsize(oper[i]^.ref^.index),spilltemplist[supreg],helpreg);
if pos=nil then
list.insertafter(helpins,list.first)
else
list.insertafter(helpins,pos.next);
oper[i]^.ref^.index:=helpreg;
ungetregisterinline(list,helpins,helpreg);
forward_allocation(Tai(helpins.next),instr);
end;
end;
if (oper[0]^.typ=top_reg) and
(getregtype(oper[0]^.reg)=regtype) then
begin
supreg:=getsupreg(oper[0]^.reg);
subreg:=getsubreg(oper[0]^.reg);
if supregset_in(r,supreg) then
if oper[1]^.typ=top_ref then
begin
{Situation example:
add [r20d],r21d ; r21d must be spilled into [ebp-12]
Change into:
mov r22d,[ebp-12] ; Use a help register
add [r20d],r22d ; Replace register by helpregister }
pos:=get_insert_pos(Tai(instr.previous),getsupreg(oper[0]^.reg),
getsupreg(oper[1]^.ref^.base),getsupreg(oper[1]^.ref^.index));
getregisterinline(list,pos,subreg,helpreg);
result:=true;
helpins:=Taicpu.op_ref_reg(A_MOV,reg2opsize(oper[0]^.reg),spilltemplist[supreg],helpreg);
if pos=nil then
list.insertafter(helpins,list.first)
else
list.insertafter(helpins,pos.next);
oper[0]^.reg:=helpreg;
ungetregisterinline(list,helpins,helpreg);
forward_allocation(Tai(helpins.next),instr);
end
else
begin
{Situation example:
add r20d,r21d ; r21d must be spilled into [ebp-12]
Change into:
add r20d,[ebp-12] ; Replace register by reference }
oper[0]^.typ:=top_ref;
new(oper[0]^.ref);
oper[0]^.ref^:=spilltemplist[supreg];
end;
end;
if (oper[1]^.typ=top_reg) and
(getregtype(oper[1]^.reg)=regtype) then
begin
supreg:=getsupreg(oper[1]^.reg);
subreg:=getsubreg(oper[1]^.reg);
if supregset_in(r,supreg) then
begin
if oper[0]^.typ=top_ref then
begin
{Situation example:
add r20d,[r21d] ; r20d must be spilled into [ebp-12]
Change into:
mov r22d,[r21d] ; Use a help register
add [ebp-12],r22d ; Replace register by helpregister }
pos:=get_insert_pos(Tai(instr.previous),getsupreg(oper[0]^.ref^.base),
getsupreg(oper[0]^.ref^.index),RS_INVALID);
getregisterinline(list,pos,subreg,helpreg);
result:=true;
op:=A_MOV;
hopsize:=opsize; {Save old value...}
if (opcode=A_MOVZX) or (opcode=A_MOVSX) or (opcode=A_LEA) then
begin
{Because 'movzx memory,register' does not exist...}
op:=opcode;
opcode:=A_MOV;
opsize:=reg2opsize(oper[1]^.reg);
end;
helpins:=Taicpu.op_ref_reg(op,hopsize,oper[0]^.ref^,helpreg);
if pos=nil then
list.insertafter(helpins,list.first)
else
list.insertafter(helpins,pos.next);
dispose(oper[0]^.ref);
oper[0]^.typ:=top_reg;
oper[0]^.reg:=helpreg;
oper[1]^.typ:=top_ref;
new(oper[1]^.ref);
oper[1]^.ref^:=spilltemplist[supreg];
ungetregisterinline(list,helpins,helpreg);
forward_allocation(Tai(helpins.next),instr);
end
else
begin
{Situation example:
add r20d,r21d ; r20d must be spilled into [ebp-12]
Change into:
add [ebp-12],r21d ; Replace register by reference }
if (opcode=A_MOVZX) or (opcode=A_MOVSX) then
begin
{Because 'movzx memory,register' does not exist...}
result:=true;
op:=opcode;
hopsize:=opsize;
opcode:=A_MOV;
opsize:=reg2opsize(oper[1]^.reg);
pos:=get_insert_pos(Tai(instr.previous),getsupreg(oper[0]^.reg),RS_INVALID,RS_INVALID);
getregisterinline(list,pos,subreg,helpreg);
helpins:=Taicpu.op_reg_reg(op,hopsize,oper[0]^.reg,helpreg);
if pos=nil then
list.insertafter(helpins,list.first)
else
list.insertafter(helpins,pos.next);
oper[0]^.reg:=helpreg;
ungetregisterinline(list,helpins,helpreg);
forward_allocation(Tai(helpins.next),instr);
end;
oper[1]^.typ:=top_ref;
new(oper[1]^.ref);
oper[1]^.ref^:=spilltemplist[supreg];
end;
end;
end;
{ The i386 instruction set never gets boring...
some opcodes do not support a memory location as destination }
if (oper[1]^.typ=top_ref) and
(
(oper[0]^.typ=top_const) or
((oper[0]^.typ=top_reg) and
(getregtype(oper[0]^.reg)=regtype))
) then
begin
case opcode of
A_IMUL :
begin
{Yikes! We just changed the destination register into
a memory location above here.
Situation examples:
imul [ebp-12],r21d ; We need a help register
imul [ebp-12],<const> ; We need a help register
Change into:
mov r22d,[ebp-12] ; Use a help instruction (only for IMUL)
imul r22d,r21d ; Replace reference by helpregister
mov [ebp-12],r22d ; Use another help instruction}
getregisterinline(list,Tai(previous),subreg,helpreg);
result:=true;
{First help instruction.}
helpins:=Taicpu.op_ref_reg(A_MOV,opsize,oper[1]^.ref^,helpreg);
if previous=nil then
list.insert(helpins)
else
list.insertafter(helpins,previous);
{Second help instruction.}
helpins:=Taicpu.op_reg_ref(A_MOV,opsize,helpreg,oper[1]^.ref^);
dispose(oper[1]^.ref);
oper[1]^.typ:=top_reg;
oper[1]^.reg:=helpreg;
list.insertafter(helpins,instr);
ungetregisterinline(list,instr,helpreg);
end;
end;
end;
{ The i386 instruction set never gets boring...
some opcodes do not support a memory location as source }
if (oper[0]^.typ=top_ref) and
(oper[1]^.typ=top_reg) and
(getregtype(oper[1]^.reg)=regtype) then
begin
case opcode of
A_BT,A_BTS,
A_BTC,A_BTR :
begin
{Yikes! We just changed the source register into
a memory location above here.
Situation example:
bt r21d,[ebp-12] ; We need a help register
Change into:
mov r22d,[ebp-12] ; Use a help instruction (only for IMUL)
bt r21d,r22d ; Replace reference by helpregister}
getregisterinline(list,Tai(previous),subreg,helpreg);
result:=true;
{First help instruction.}
helpins:=Taicpu.op_ref_reg(A_MOV,opsize,oper[0]^.ref^,helpreg);
if previous=nil then
list.insert(helpins)
else
list.insertafter(helpins,previous);
dispose(oper[0]^.ref);
oper[0]^.typ:=top_reg;
oper[0]^.reg:=helpreg;
ungetregisterinline(list,helpins,helpreg);
end;
end;
end;
end;
3:
begin
{$warning todo!!}
end;
end;
end;
end;
{******************************************************************************
Trgx86fpu
******************************************************************************}
constructor Trgx86fpu.create;
var i:Tsuperregister;
begin
used_in_proc:=[];
t_times := 0;
unusedregsfpu:=usableregsfpu;
end;
function trgx86fpu.getregisterfpu(list: taasmoutput) : tregister;
begin
{ note: don't return R_ST0, see comments above implementation of }
{ a_loadfpu_* methods in cgcpu (JM) }
result:=NR_ST;
end;
procedure trgx86fpu.ungetregisterfpu(list : taasmoutput; r : tregister);
begin
{ nothing to do, fpu stack management is handled by the load/ }
{ store operations in cgcpu (JM) }
end;
function trgx86fpu.correct_fpuregister(r : tregister;ofs : byte) : tregister;
begin
correct_fpuregister:=r;
setsupreg(correct_fpuregister,ofs);
end;
procedure trgx86fpu.saveusedfpuregisters(list: taasmoutput;
var saved : tpushedsavedfpu;
const s: tcpuregisterset);
var
r : tregister;
hr : treference;
begin
used_in_proc:=used_in_proc+s;
{$warning TODO firstsavefpureg}
(*
{ don't try to save the fpu registers if not desired (e.g. for }
{ the 80x86) }
if firstsavefpureg <> R_NO then
for r.enum:=firstsavefpureg to lastsavefpureg do
begin
saved[r.enum].ofs:=reg_not_saved;
{ if the register is used by the calling subroutine and if }
{ it's not a regvar (those are handled separately) }
if not is_reg_var_other[r.enum] and
(r.enum in s) and
{ and is present in use }
not(r.enum in unusedregsfpu) then
begin
{ then save it }
tg.GetTemp(list,extended_size,tt_persistent,hr);
saved[r.enum].ofs:=hr.offset;
cg.a_loadfpu_reg_ref(list,OS_FLOAT,r,hr);
cg.a_reg_dealloc(list,r);
include(unusedregsfpu,r.enum);
inc(countunusedregsfpu);
end;
end;
*)
end;
procedure trgx86fpu.restoreusedfpuregisters(list : taasmoutput;
const saved : tpushedsavedfpu);
var
r,r2 : tregister;
hr : treference;
begin
{$warning TODO firstsavefpureg}
(*
if firstsavefpureg <> R_NO then
for r.enum:=lastsavefpureg downto firstsavefpureg do
begin
if saved[r.enum].ofs <> reg_not_saved then
begin
r2.enum:=R_INTREGISTER;
r2.number:=NR_FRAME_POINTER_REG;
reference_reset_base(hr,r2,saved[r.enum].ofs);
cg.a_reg_alloc(list,r);
cg.a_loadfpu_ref_reg(list,OS_FLOAT,hr,r);
if not (r.enum in unusedregsfpu) then
{ internalerror(10)
in n386cal we always save/restore the reg *state*
using save/restoreunusedstate -> the current state
may not be real (JM) }
else
begin
dec(countunusedregsfpu);
exclude(unusedregsfpu,r.enum);
end;
tg.UnGetTemp(list,hr);
end;
end;
*)
end;
(*
procedure Trgx86fpu.saveotherregvars(list: taasmoutput; const s: totherregisterset);
var
r: Tregister;
begin
if not(cs_regvars in aktglobalswitches) then
exit;
if firstsavefpureg <> NR_NO then
for r.enum := firstsavefpureg to lastsavefpureg do
if is_reg_var_other[r.enum] and
(r.enum in s) then
store_regvar(list,r);
end;
*)
end.
{
$Log$
Revision 1.2 2004-01-12 16:37:59 peter
* moved spilling code from taicpu to rg
Revision 1.1 2003/12/24 00:12:57 florian
* rg unified for i386/x86-64
Revision 1.40 2003/10/17 15:08:34 peter
* commented out more obsolete constants
Revision 1.39 2003/10/17 14:38:32 peter
* 64k registers supported
* fixed some memory leaks
Revision 1.38 2003/10/10 17:48:14 peter
* old trgobj moved to x86/rgcpu and renamed to trgx86fpu
* tregisteralloctor renamed to trgobj
* removed rgobj from a lot of units
* moved location_* and reference_* to cgobj
* first things for mmx register allocation
Revision 1.37 2003/10/09 21:31:37 daniel
* Register allocator splitted, ans abstract now
Revision 1.36 2003/10/01 20:34:49 peter
* procinfo unit contains tprocinfo
* cginfo renamed to cgbase
* moved cgmessage to verbose
* fixed ppc and sparc compiles
Revision 1.35 2003/09/11 11:55:00 florian
* improved arm code generation
* move some protected and private field around
* the temp. register for register parameters/arguments are now released
before the move to the parameter register is done. This improves
the code in a lot of cases.
Revision 1.34 2003/09/09 20:59:27 daniel
* Adding register allocation order
Revision 1.33 2003/09/07 22:09:35 peter
* preparations for different default calling conventions
* various RA fixes
Revision 1.32 2003/09/03 15:55:01 peter
* NEWRA branch merged
Revision 1.31.2.3 2003/08/31 13:50:16 daniel
* Remove sorting and use pregenerated indexes
* Some work on making things compile
Revision 1.31.2.2 2003/08/28 18:35:08 peter
* tregister changed to cardinal
Revision 1.31.2.1 2003/08/27 19:55:54 peter
* first tregister patch
Revision 1.31 2003/08/20 09:07:00 daniel
* New register coding now mandatory, some more convert_registers calls
removed.
Revision 1.30 2003/08/17 08:48:02 daniel
* Another register allocator bug fixed.
* cpu_registers set to 6 for i386
Revision 1.29 2003/06/17 16:51:30 peter
* cycle fixes
Revision 1.28 2003/06/17 16:34:44 jonas
* lots of newra fixes (need getfuncretparaloc implementation for i386)!
* renamed all_intregisters to volatile_intregisters and made it
processor dependent
Revision 1.27 2003/06/13 21:19:31 peter
* current_procdef removed, use current_procinfo.procdef instead
Revision 1.26 2003/06/12 21:12:20 peter
* size para for ungetregisterfpu
Revision 1.25 2003/06/03 21:11:09 peter
* cg.a_load_* get a from and to size specifier
* makeregsize only accepts newregister
* i386 uses generic tcgnotnode,tcgunaryminus
Revision 1.24 2003/06/03 13:01:59 daniel
* Register allocator finished
Revision 1.23 2003/06/01 21:38:06 peter
* getregisterfpu size parameter added
* op_const_reg size parameter added
* sparc updates
Revision 1.22 2003/05/16 14:33:31 peter
* regvar fixes
Revision 1.21 2003/04/25 08:25:26 daniel
* Ifdefs around a lot of calls to cleartempgen
* Fixed registers that are allocated but not freed in several nodes
* Tweak to register allocator to cause less spills
* 8-bit registers now interfere with esi,edi and ebp
Compiler can now compile rtl successfully when using new register
allocator
Revision 1.20 2003/04/23 14:42:08 daniel
* Further register allocator work. Compiler now smaller with new
allocator than without.
* Somebody forgot to adjust ppu version number
Revision 1.19 2003/04/22 10:09:35 daniel
+ Implemented the actual register allocator
+ Scratch registers unavailable when new register allocator used
+ maybe_save/maybe_restore unavailable when new register allocator used
Revision 1.18 2003/04/21 19:16:50 peter
* count address regs separate
Revision 1.17 2003/03/28 19:16:57 peter
* generic constructor working for i386
* remove fixed self register
* esi added as address register for i386
Revision 1.16 2003/03/17 15:52:57 peter
* SUPPORT_MMX define compile fix
Revision 1.15 2003/03/08 13:59:17 daniel
* Work to handle new register notation in ag386nsm
+ Added newra version of Ti386moddivnode
Revision 1.14 2003/03/08 08:59:07 daniel
+ $define newra will enable new register allocator
+ getregisterint will return imaginary registers with $newra
+ -sr switch added, will skip register allocation so you can see
the direct output of the code generator before register allocation
Revision 1.13 2003/03/07 21:57:53 daniel
* Improved getregisterint
Revision 1.12 2003/02/19 22:00:16 daniel
* Code generator converted to new register notation
- Horribily outdated todo.txt removed
Revision 1.11 2003/01/08 18:43:57 daniel
* Tregister changed into a record
Revision 1.10 2002/10/05 12:43:29 carl
* fixes for Delphi 6 compilation
(warning : Some features do not work under Delphi)
Revision 1.9 2002/08/17 09:23:48 florian
* first part of procinfo rewrite
Revision 1.8 2002/07/01 18:46:34 peter
* internal linker
* reorganized aasm layer
Revision 1.7 2002/05/16 19:46:52 carl
+ defines.inc -> fpcdefs.inc to avoid conflicts if compiling by hand
+ try to fix temp allocation (still in ifdef)
+ generic constructor calls
+ start of tassembler / tmodulebase class cleanup
Revision 1.6 2002/05/12 16:53:18 peter
* moved entry and exitcode to ncgutil and cgobj
* foreach gets extra argument for passing local data to the
iterator function
* -CR checks also class typecasts at runtime by changing them
into as
* fixed compiler to cycle with the -CR option
* fixed stabs with elf writer, finally the global variables can
be watched
* removed a lot of routines from cga unit and replaced them by
calls to cgobj
* u32bit-s32bit updates for and,or,xor nodes. When one element is
u32bit then the other is typecasted also to u32bit without giving
a rangecheck warning/error.
* fixed pascal calling method with reversing also the high tree in
the parast, detected by tcalcst3 test
Revision 1.5 2002/04/21 15:43:32 carl
* changeregsize -> rg.makeregsize
* changeregsize moved from cpubase to here
Revision 1.4 2002/04/15 19:44:22 peter
* fixed stackcheck that would be called recursively when a stack
error was found
* generic changeregsize(reg,size) for i386 register resizing
* removed some more routines from cga unit
* fixed returnvalue handling
* fixed default stacksize of linux and go32v2, 8kb was a bit small :-)
Revision 1.3 2002/04/04 19:06:13 peter
* removed unused units
* use tlocation.size in cg.a_*loc*() routines
Revision 1.2 2002/04/02 17:11:39 peter
* tlocation,treference update
* LOC_CONSTANT added for better constant handling
* secondadd splitted in multiple routines
* location_force_reg added for loading a location to a register
of a specified size
* secondassignment parses now first the right and then the left node
(this is compatible with Kylix). This saves a lot of push/pop especially
with string operations
* adapted some routines to use the new cg methods
Revision 1.1 2002/03/31 20:26:40 jonas
+ a_loadfpu_* and a_loadmm_* methods in tcg
* register allocation is now handled by a class and is mostly processor
independent (+rgobj.pas and i386/rgcpu.pas)
* temp allocation is now handled by a class (+tgobj.pas, -i386\tgcpu.pas)
* some small improvements and fixes to the optimizer
* some register allocation fixes
* some fpuvaroffset fixes in the unary minus node
* push/popusedregisters is now called rg.save/restoreusedregisters and
(for i386) uses temps instead of push/pop's when using -Op3 (that code is
also better optimizable)
* fixed and optimized register saving/restoring for new/dispose nodes
* LOC_FPU locations now also require their "register" field to be set to
R_ST, not R_ST0 (the latter is used for LOC_CFPUREGISTER locations only)
- list field removed of the tnode class because it's not used currently
and can cause hard-to-find bugs
}