summaryrefslogtreecommitdiff
path: root/compiler/x86/rgx86.pas
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/x86/rgx86.pas')
-rw-r--r--compiler/x86/rgx86.pas346
1 files changed, 346 insertions, 0 deletions
diff --git a/compiler/x86/rgx86.pas b/compiler/x86/rgx86.pas
new file mode 100644
index 0000000000..725ba50022
--- /dev/null
+++ b/compiler/x86/rgx86.pas
@@ -0,0 +1,346 @@
+{
+ 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
+ cclasses,globtype,
+ cpubase,cpuinfo,cgbase,cgutils,
+ aasmbase,aasmtai,aasmcpu,
+ rgobj;
+
+ type
+ trgx86 = class(trgobj)
+ function get_spill_subreg(r : tregister) : tsubregister;override;
+ function do_spill_replace(list:Taasmoutput;instr:taicpu;orgreg:tsuperregister;const spilltemp:treference):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;
+
+ 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.get_spill_subreg(r : tregister) : tsubregister;
+ begin
+ result:=getsubreg(r);
+ end;
+
+
+ function trgx86.do_spill_replace(list:Taasmoutput;instr:taicpu;orgreg:tsuperregister;const spilltemp:treference):boolean;
+ var
+ replaceoper : longint;
+ begin
+ result:=false;
+ with instr do
+ begin
+ replaceoper:=-1;
+ case ops of
+ 1 :
+ begin
+ if (oper[0]^.typ=top_reg) then
+ begin
+ if getsupreg(oper[0]^.reg)<>orgreg then
+ internalerror(200410101);
+ replaceoper:=0;
+ end;
+ end;
+ 2,3 :
+ begin
+ { We can handle opcodes with 2 and 3 operands the same way. The opcodes
+ with 3 registers are shrd/shld, where the 3rd operand is const or CL,
+ that doesn't need spilling }
+ if (oper[0]^.typ=top_reg) and
+ (oper[1]^.typ=top_reg) and
+ (getsupreg(oper[0]^.reg)<>getsupreg(oper[1]^.reg)) then
+ begin
+ { One of the arguments shall be able to be replaced }
+ if (getregtype(oper[0]^.reg)=regtype) and
+ (getsupreg(oper[0]^.reg)=orgreg) then
+ replaceoper:=0
+ else
+ if (getregtype(oper[1]^.reg)=regtype) and
+ (getsupreg(oper[1]^.reg)=orgreg) then
+ replaceoper:=1
+ else
+ internalerror(200410106);
+ case replaceoper of
+ 0 :
+ begin
+ { Some instructions don't allow memory references
+ for source }
+ case instr.opcode of
+ A_BT,
+ A_BTS,
+ A_BTC,
+ A_BTR :
+ replaceoper:=-1;
+ end;
+ end;
+ 1 :
+ begin
+ { Some instructions don't allow memory references
+ for destination }
+ case instr.opcode of
+ A_MOVZX,
+ A_MOVSX,
+ A_MULSS,
+ A_MULSD,
+ A_SUBSS,
+ A_SUBSD,
+ A_ADDSD,
+ A_ADDSS,
+ A_DIVSD,
+ A_DIVSS,
+ A_SHLD,
+ A_SHRD,
+ A_CVTDQ2PD,
+ A_CVTDQ2PS,
+ A_CVTPD2DQ,
+ A_CVTPD2PI,
+ A_CVTPD2PS,
+ A_CVTPI2PD,
+ A_CVTPS2DQ,
+ A_CVTPS2PD,
+ A_CVTSD2SI,
+ A_CVTSD2SS,
+ A_CVTSI2SD,
+ A_CVTSS2SD,
+ A_CVTTPD2PI,
+ A_CVTTPD2DQ,
+ A_CVTTPS2DQ,
+ A_CVTTSD2SI,
+ A_CVTPI2PS,
+ A_CVTPS2PI,
+ A_CVTSI2SS,
+ A_CVTSS2SI,
+ A_CVTTPS2PI,
+ A_CVTTSS2SI,
+ A_IMUL :
+ replaceoper:=-1;
+ end;
+ end;
+ end;
+ end;
+ end;
+ end;
+
+ { Replace register with spill reference }
+ if replaceoper<>-1 then
+ begin
+ oper[replaceoper]^.typ:=top_ref;
+ new(oper[replaceoper]^.ref);
+ oper[replaceoper]^.ref^:=spilltemp;
+ result:=true;
+ end;
+ end;
+ end;
+
+
+{******************************************************************************
+ Trgx86fpu
+******************************************************************************}
+
+ constructor Trgx86fpu.create;
+ 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.