Nouvelle architecture pour les fichiers dependant du processeur

git-svn-id: http://caml.inria.fr/svn/ocaml/trunk@1655 f963ae5c-01c2-4b8c-9fe0-0dff7051ff02
master
Xavier Leroy 1997-07-24 11:49:12 +00:00
parent 4029d102d8
commit 119c8eeb67
59 changed files with 2698 additions and 7776 deletions

87
.depend
View File

@ -350,20 +350,17 @@ asmcomp/mach.cmi: asmcomp/arch.cmo asmcomp/cmm.cmi utils/nativeint.cmi \
asmcomp/printcmm.cmi: asmcomp/cmm.cmi
asmcomp/printlinear.cmi: asmcomp/linearize.cmi
asmcomp/printmach.cmi: asmcomp/mach.cmi asmcomp/reg.cmi
asmcomp/proc.cmi: asmcomp/arch.cmo asmcomp/cmm.cmi asmcomp/mach.cmi \
asmcomp/reg.cmi
asmcomp/proc.cmi: asmcomp/mach.cmi asmcomp/reg.cmi
asmcomp/reg.cmi: asmcomp/cmm.cmi
asmcomp/reload.cmi: asmcomp/mach.cmi asmcomp/reg.cmi
asmcomp/reload.cmi: asmcomp/mach.cmi
asmcomp/reloadgen.cmi: asmcomp/mach.cmi asmcomp/reg.cmi
asmcomp/schedgen.cmi: asmcomp/linearize.cmi asmcomp/mach.cmi
asmcomp/scheduling.cmi: asmcomp/linearize.cmi
asmcomp/selectgen.cmi: asmcomp/arch.cmo asmcomp/cmm.cmi typing/ident.cmi \
asmcomp/mach.cmi asmcomp/reg.cmi utils/tbl.cmi
asmcomp/selection.cmi: asmcomp/cmm.cmi asmcomp/mach.cmi
asmcomp/spill.cmi: asmcomp/mach.cmi
asmcomp/split.cmi: asmcomp/mach.cmi
asmcomp/arch_mips.cmo: utils/config.cmi utils/misc.cmi
asmcomp/arch_mips.cmx: utils/config.cmx utils/misc.cmx
asmcomp/arch_power.cmo: utils/misc.cmi
asmcomp/arch_power.cmx: utils/misc.cmx
asmcomp/arch_sparc.cmo: utils/misc.cmi
asmcomp/arch_sparc.cmx: utils/misc.cmx
asmcomp/asmgen.cmo: utils/clflags.cmo asmcomp/closure.cmi asmcomp/cmm.cmi \
asmcomp/cmmgen.cmi asmcomp/coloring.cmi utils/config.cmi asmcomp/emit.cmi \
asmcomp/emitaux.cmi asmcomp/interf.cmi asmcomp/linearize.cmi \
@ -435,11 +432,11 @@ asmcomp/compilenv.cmo: asmcomp/clambda.cmi utils/config.cmi typing/env.cmi \
asmcomp/compilenv.cmx: asmcomp/clambda.cmx utils/config.cmx typing/env.cmx \
typing/ident.cmx utils/misc.cmx asmcomp/compilenv.cmi
asmcomp/emit.cmo: asmcomp/arch.cmo asmcomp/cmm.cmi asmcomp/compilenv.cmi \
utils/config.cmi asmcomp/emitaux.cmi asmcomp/linearize.cmi \
asmcomp/emitaux.cmi asmcomp/linearize.cmi parsing/location.cmi \
asmcomp/mach.cmi utils/misc.cmi utils/nativeint.cmi asmcomp/proc.cmi \
asmcomp/reg.cmi asmcomp/emit.cmi
asmcomp/emit.cmx: asmcomp/arch.cmx asmcomp/cmm.cmx asmcomp/compilenv.cmx \
utils/config.cmx asmcomp/emitaux.cmx asmcomp/linearize.cmx \
asmcomp/emitaux.cmx asmcomp/linearize.cmx parsing/location.cmx \
asmcomp/mach.cmx utils/misc.cmx utils/nativeint.cmx asmcomp/proc.cmx \
asmcomp/reg.cmx asmcomp/emit.cmi
asmcomp/emitaux.cmo: utils/nativeint.cmi asmcomp/emitaux.cmi
@ -478,56 +475,32 @@ asmcomp/proc.cmo: asmcomp/arch.cmo utils/ccomp.cmi asmcomp/cmm.cmi \
asmcomp/mach.cmi utils/misc.cmi asmcomp/reg.cmi asmcomp/proc.cmi
asmcomp/proc.cmx: asmcomp/arch.cmx utils/ccomp.cmx asmcomp/cmm.cmx \
asmcomp/mach.cmx utils/misc.cmx asmcomp/reg.cmx asmcomp/proc.cmi
asmcomp/proc_alpha.cmo: asmcomp/arch.cmo utils/ccomp.cmi asmcomp/cmm.cmi \
asmcomp/mach.cmi utils/misc.cmi asmcomp/reg.cmi
asmcomp/proc_alpha.cmx: asmcomp/arch.cmx utils/ccomp.cmx asmcomp/cmm.cmx \
asmcomp/mach.cmx utils/misc.cmx asmcomp/reg.cmx
asmcomp/proc_hppa.cmo: asmcomp/arch.cmo utils/ccomp.cmi asmcomp/cmm.cmi \
utils/config.cmi asmcomp/mach.cmi utils/misc.cmi asmcomp/reg.cmi
asmcomp/proc_hppa.cmx: asmcomp/arch.cmx utils/ccomp.cmx asmcomp/cmm.cmx \
utils/config.cmx asmcomp/mach.cmx utils/misc.cmx asmcomp/reg.cmx
asmcomp/proc_i386.cmo: asmcomp/arch.cmo utils/ccomp.cmi asmcomp/cmm.cmi \
asmcomp/mach.cmi utils/misc.cmi asmcomp/reg.cmi
asmcomp/proc_i386.cmx: asmcomp/arch.cmx utils/ccomp.cmx asmcomp/cmm.cmx \
asmcomp/mach.cmx utils/misc.cmx asmcomp/reg.cmx
asmcomp/proc_i386nt.cmo: asmcomp/arch.cmo utils/ccomp.cmi asmcomp/cmm.cmi \
asmcomp/mach.cmi utils/misc.cmi asmcomp/reg.cmi
asmcomp/proc_i386nt.cmx: asmcomp/arch.cmx utils/ccomp.cmx asmcomp/cmm.cmx \
asmcomp/mach.cmx utils/misc.cmx asmcomp/reg.cmx
asmcomp/proc_m68k.cmo: asmcomp/arch.cmo utils/ccomp.cmi asmcomp/cmm.cmi \
asmcomp/mach.cmi utils/misc.cmi asmcomp/reg.cmi
asmcomp/proc_m68k.cmx: asmcomp/arch.cmx utils/ccomp.cmx asmcomp/cmm.cmx \
asmcomp/mach.cmx utils/misc.cmx asmcomp/reg.cmx
asmcomp/proc_mips.cmo: asmcomp/arch.cmo utils/ccomp.cmi asmcomp/cmm.cmi \
utils/config.cmi asmcomp/mach.cmi utils/misc.cmi asmcomp/reg.cmi
asmcomp/proc_mips.cmx: asmcomp/arch.cmx utils/ccomp.cmx asmcomp/cmm.cmx \
utils/config.cmx asmcomp/mach.cmx utils/misc.cmx asmcomp/reg.cmx
asmcomp/proc_power.cmo: asmcomp/arch.cmo utils/ccomp.cmi asmcomp/cmm.cmi \
utils/config.cmi asmcomp/mach.cmi utils/misc.cmi asmcomp/reg.cmi
asmcomp/proc_power.cmx: asmcomp/arch.cmx utils/ccomp.cmx asmcomp/cmm.cmx \
utils/config.cmx asmcomp/mach.cmx utils/misc.cmx asmcomp/reg.cmx
asmcomp/proc_sparc.cmo: asmcomp/arch.cmo utils/ccomp.cmi asmcomp/cmm.cmi \
asmcomp/mach.cmi utils/misc.cmi asmcomp/reg.cmi
asmcomp/proc_sparc.cmx: asmcomp/arch.cmx utils/ccomp.cmx asmcomp/cmm.cmx \
asmcomp/mach.cmx utils/misc.cmx asmcomp/reg.cmx
asmcomp/reg.cmo: asmcomp/cmm.cmi asmcomp/reg.cmi
asmcomp/reg.cmx: asmcomp/cmm.cmx asmcomp/reg.cmi
asmcomp/reload.cmo: asmcomp/mach.cmi utils/misc.cmi asmcomp/proc.cmi \
asmcomp/reg.cmi asmcomp/reload.cmi
asmcomp/reload.cmx: asmcomp/mach.cmx utils/misc.cmx asmcomp/proc.cmx \
asmcomp/reg.cmx asmcomp/reload.cmi
asmcomp/scheduling.cmo: asmcomp/arch.cmo asmcomp/cmm.cmi \
asmcomp/linearize.cmi asmcomp/mach.cmi utils/misc.cmi asmcomp/proc.cmi \
asmcomp/reg.cmi asmcomp/scheduling.cmi
asmcomp/scheduling.cmx: asmcomp/arch.cmx asmcomp/cmm.cmx \
asmcomp/linearize.cmx asmcomp/mach.cmx utils/misc.cmx asmcomp/proc.cmx \
asmcomp/reg.cmx asmcomp/scheduling.cmi
asmcomp/selection.cmo: asmcomp/arch.cmo asmcomp/cmm.cmi typing/ident.cmi \
asmcomp/reload.cmo: asmcomp/reloadgen.cmi asmcomp/reload.cmi
asmcomp/reload.cmx: asmcomp/reloadgen.cmx asmcomp/reload.cmi
asmcomp/reloadgen.cmo: asmcomp/mach.cmi utils/misc.cmi asmcomp/reg.cmi \
asmcomp/reloadgen.cmi
asmcomp/reloadgen.cmx: asmcomp/mach.cmx utils/misc.cmx asmcomp/reg.cmx \
asmcomp/reloadgen.cmi
asmcomp/schedgen.cmo: asmcomp/arch.cmo asmcomp/cmm.cmi asmcomp/linearize.cmi \
asmcomp/mach.cmi utils/misc.cmi asmcomp/reg.cmi asmcomp/schedgen.cmi
asmcomp/schedgen.cmx: asmcomp/arch.cmx asmcomp/cmm.cmx asmcomp/linearize.cmx \
asmcomp/mach.cmx utils/misc.cmx asmcomp/reg.cmx asmcomp/schedgen.cmi
asmcomp/scheduling.cmo: asmcomp/schedgen.cmi asmcomp/scheduling.cmi
asmcomp/scheduling.cmx: asmcomp/schedgen.cmx asmcomp/scheduling.cmi
asmcomp/selectgen.cmo: asmcomp/arch.cmo asmcomp/cmm.cmi typing/ident.cmi \
asmcomp/mach.cmi utils/misc.cmi utils/nativeint.cmi asmcomp/proc.cmi \
asmcomp/reg.cmi utils/tbl.cmi asmcomp/selection.cmi
asmcomp/selection.cmx: asmcomp/arch.cmx asmcomp/cmm.cmx typing/ident.cmx \
asmcomp/reg.cmi utils/tbl.cmi asmcomp/selectgen.cmi
asmcomp/selectgen.cmx: asmcomp/arch.cmx asmcomp/cmm.cmx typing/ident.cmx \
asmcomp/mach.cmx utils/misc.cmx utils/nativeint.cmx asmcomp/proc.cmx \
asmcomp/reg.cmx utils/tbl.cmx asmcomp/selection.cmi
asmcomp/reg.cmx utils/tbl.cmx asmcomp/selectgen.cmi
asmcomp/selection.cmo: asmcomp/arch.cmo asmcomp/cmm.cmi asmcomp/mach.cmi \
utils/misc.cmi asmcomp/reg.cmi asmcomp/selectgen.cmi \
asmcomp/selection.cmi
asmcomp/selection.cmx: asmcomp/arch.cmx asmcomp/cmm.cmx asmcomp/mach.cmx \
utils/misc.cmx asmcomp/reg.cmx asmcomp/selectgen.cmx \
asmcomp/selection.cmi
asmcomp/spill.cmo: asmcomp/mach.cmi asmcomp/proc.cmi asmcomp/reg.cmi \
asmcomp/spill.cmi
asmcomp/spill.cmx: asmcomp/mach.cmx asmcomp/proc.cmx asmcomp/reg.cmx \

View File

@ -51,10 +51,12 @@ ASMCOMP=asmcomp/arch.cmo asmcomp/cmm.cmo asmcomp/printcmm.cmo \
asmcomp/reg.cmo asmcomp/mach.cmo asmcomp/proc.cmo \
asmcomp/clambda.cmo asmcomp/compilenv.cmo \
asmcomp/closure.cmo asmcomp/cmmgen.cmo \
asmcomp/printmach.cmo asmcomp/selection.cmo asmcomp/liveness.cmo \
asmcomp/spill.cmo asmcomp/split.cmo \
asmcomp/interf.cmo asmcomp/coloring.cmo asmcomp/reload.cmo \
asmcomp/printlinear.cmo asmcomp/linearize.cmo asmcomp/scheduling.cmo \
asmcomp/printmach.cmo asmcomp/selectgen.cmo asmcomp/selection.cmo \
asmcomp/liveness.cmo asmcomp/spill.cmo asmcomp/split.cmo \
asmcomp/interf.cmo asmcomp/coloring.cmo \
asmcomp/reloadgen.cmo asmcomp/reload.cmo \
asmcomp/printlinear.cmo asmcomp/linearize.cmo \
asmcomp/schedgen.cmo asmcomp/scheduling.cmo \
asmcomp/emitaux.cmo asmcomp/emit.cmo asmcomp/asmgen.cmo \
asmcomp/asmlink.cmo asmcomp/asmlibrarian.cmo
@ -132,8 +134,10 @@ coldstart:
ln -s ../byterun/libcamlrun.a boot/libcamlrun.a; fi
# Save the current bootstrap compiler
MAXSAVED=boot/Saved/Saved.prev/Saved.prev/Saved.prev/Saved.prev/Saved.prev
backup:
if test -d boot/Saved; then : ; else mkdir boot/Saved; fi
if test -d $(MAXSAVED); then rm -r $(MAXSAVED); else : ; fi
mv boot/Saved boot/Saved.prev
mkdir boot/Saved
mv boot/Saved.prev boot/Saved/Saved.prev
@ -334,28 +338,52 @@ partialclean::
beforedepend:: bytecomp/runtimedef.ml
# Choose the right arch, emit and proc files
# Choose the right machine-dependent files
asmcomp/arch.ml: asmcomp/arch_$(ARCH).ml
ln -s arch_$(ARCH).ml asmcomp/arch.ml
asmcomp/arch.ml: asmcomp/$(ARCH)/arch.ml
ln -s $(ARCH)/arch.ml asmcomp/arch.ml
partialclean::
rm -f asmcomp/arch.ml
beforedepend:: asmcomp/arch.ml
asmcomp/proc.ml: asmcomp/proc_$(ARCH).ml
ln -s proc_$(ARCH).ml asmcomp/proc.ml
asmcomp/proc.ml: asmcomp/$(ARCH)/proc.ml
ln -s $(ARCH)/proc.ml asmcomp/proc.ml
partialclean::
rm -f asmcomp/proc.ml
beforedepend:: asmcomp/proc.ml
asmcomp/selection.ml: asmcomp/$(ARCH)/selection.ml
ln -s $(ARCH)/selection.ml asmcomp/selection.ml
partialclean::
rm -f asmcomp/selection.ml
beforedepend:: asmcomp/selection.ml
asmcomp/reload.ml: asmcomp/$(ARCH)/reload.ml
ln -s $(ARCH)/reload.ml asmcomp/reload.ml
partialclean::
rm -f asmcomp/reload.ml
beforedepend:: asmcomp/reload.ml
asmcomp/scheduling.ml: asmcomp/$(ARCH)/scheduling.ml
ln -s $(ARCH)/scheduling.ml asmcomp/scheduling.ml
partialclean::
rm -f asmcomp/scheduling.ml
beforedepend:: asmcomp/scheduling.ml
# Preprocess the code emitters
asmcomp/emit.ml: asmcomp/emit_$(ARCH).mlp tools/cvt_emit
boot/ocamlrun tools/cvt_emit < asmcomp/emit_$(ARCH).mlp > asmcomp/emit.ml \
asmcomp/emit.ml: asmcomp/$(ARCH)/emit.mlp tools/cvt_emit
boot/ocamlrun tools/cvt_emit < asmcomp/$(ARCH)/emit.mlp > asmcomp/emit.ml \
|| { rm -f asmcomp/emit.ml; exit 2; }
partialclean::

View File

@ -48,10 +48,12 @@ ASMCOMP=asmcomp\arch.cmo asmcomp\cmm.cmo asmcomp\printcmm.cmo \
asmcomp\reg.cmo asmcomp\mach.cmo asmcomp\proc.cmo \
asmcomp\clambda.cmo asmcomp\compilenv.cmo \
asmcomp\closure.cmo asmcomp\cmmgen.cmo \
asmcomp\printmach.cmo asmcomp\selection.cmo asmcomp\liveness.cmo \
asmcomp\spill.cmo asmcomp\split.cmo \
asmcomp\interf.cmo asmcomp\coloring.cmo asmcomp\reload.cmo \
asmcomp\printlinear.cmo asmcomp\linearize.cmo asmcomp\scheduling.cmo \
asmcomp\printmach.cmo asmcomp\selectgen.cmo asmcomp\selection.cmo \
asmcomp\liveness.cmo asmcomp\spill.cmo asmcomp\split.cmo \
asmcomp\interf.cmo asmcomp\coloring.cmo \
asmcomp\reloadgen.cmo asmcomp\reload.cmo \
asmcomp\printlinear.cmo asmcomp\linearize.cmo \
asmcomp\schedgen.cmo asmcomp\scheduling.cmo \
asmcomp\emitaux.cmo asmcomp\emit.cmo asmcomp\asmgen.cmo \
asmcomp\asmlink.cmo asmcomp\asmlibrarian.cmo
@ -130,8 +132,10 @@ coldstart:
cd stdlib & cp $(LIBFILES) ..\boot
# Save the current bootstrap compiler
MAXSAVED=boot\Saved\Saved.prev\Saved.prev\Saved.prev\Saved.prev\Saved.prev
backup:
if not exist boot\Saved mkdir boot\Saved
if exist $(MAXSAVED) rm -r $(MAXSAVED)
mv boot\Saved boot\Saved.prev
mkdir boot\Saved
mv boot\Saved.prev boot\Saved\Saved.prev
@ -332,28 +336,52 @@ partialclean::
beforedepend:: bytecomp\runtimedef.ml
# Choose the right arch, emit and proc files
# Choose the right machine-dependent files
asmcomp\arch.ml: asmcomp\arch_$(ARCH).ml
cp asmcomp\arch_$(ARCH).ml asmcomp\arch.ml
asmcomp\arch.ml: asmcomp\$(ARCH)\arch.ml
cp $(ARCH)\arch.ml asmcomp\arch.ml
partialclean::
rm -f asmcomp\arch.ml
beforedepend:: asmcomp\arch.ml
asmcomp\proc.ml: asmcomp\proc_$(ARCH)nt.ml
cp asmcomp\proc_$(ARCH)nt.ml asmcomp\proc.ml
asmcomp\proc.ml: asmcomp\$(ARCH)\proc_nt.ml
cp $(ARCH)\proc_nt.ml asmcomp\proc.ml
partialclean::
rm -f asmcomp\proc.ml
beforedepend:: asmcomp\proc.ml
asmcomp\selection.ml: asmcomp\$(ARCH)\selection.ml
cp $(ARCH)\selection.ml asmcomp\selection.ml
partialclean::
rm -f asmcomp\selection.ml
beforedepend:: asmcomp\selection.ml
asmcomp\reload.ml: asmcomp\$(ARCH)\reload.ml
cp $(ARCH)\reload.ml asmcomp\reload.ml
partialclean::
rm -f asmcomp\reload.ml
beforedepend:: asmcomp\reload.ml
asmcomp\scheduling.ml: asmcomp\$(ARCH)\scheduling.ml
cp $(ARCH)\scheduling.ml asmcomp\scheduling.ml
partialclean::
rm -f asmcomp\scheduling.ml
beforedepend:: asmcomp\scheduling.ml
# Preprocess the code emitters
asmcomp\emit.ml: asmcomp\emit_$(ARCH)nt.mlp tools\cvt_emit
boot\ocamlrun tools\cvt_emit < asmcomp\emit_$(ARCH)nt.mlp > asmcomp\emit.ml
asmcomp\emit.ml: asmcomp\$(ARCH)\emit_nt.mlp tools\cvt_emit
boot\ocamlrun tools\cvt_emit < asmcomp\$(ARCH)\emit_nt.mlp > asmcomp\emit.ml
partialclean::
rm -f asmcomp\emit.ml

View File

@ -19,56 +19,8 @@ open Reg
open Arch
open Mach
(* Exceptions raised to signal cases not handled here *)
exception Use_default
(* Instruction selection *)
let select_addressing = function
Cconst_symbol s ->
(Ibased(s, 0), Ctuple [])
| Cop(Cadda, [Cconst_symbol s; Cconst_int n]) ->
(Ibased(s, n), Ctuple [])
| Cop(Cadda, [arg; Cconst_int n]) ->
(Iindexed n, arg)
| Cop(Cadda, [arg1; Cop(Caddi, [arg2; Cconst_int n])]) ->
(Iindexed n, Cop(Cadda, [arg1; arg2]))
| arg ->
(Iindexed 0, arg)
let select_oper op args =
match (op, args) with
((Caddi|Cadda),
[arg2; Cop(Clsl, [arg1; Cconst_int(2|3 as shift)])]) ->
(Ispecific(if shift = 2 then Iadd4 else Iadd8), [arg1; arg2])
| ((Caddi|Cadda),
[arg2; Cop(Cmuli, [arg1; Cconst_int(4|8 as mult)])]) ->
(Ispecific(if mult = 4 then Iadd4 else Iadd8), [arg1; arg2])
| ((Caddi|Cadda),
[arg2; Cop(Cmuli, [Cconst_int(4|8 as mult); arg1])]) ->
(Ispecific(if mult = 4 then Iadd4 else Iadd8), [arg1; arg2])
| (Caddi, [Cop(Clsl, [arg1; Cconst_int(2|3 as shift)]); arg2]) ->
(Ispecific(if shift = 2 then Iadd4 else Iadd8), [arg1; arg2])
| (Caddi, [Cop(Cmuli, [arg1; Cconst_int(4|8 as mult)]); arg2]) ->
(Ispecific(if mult = 4 then Iadd4 else Iadd8), [arg1; arg2])
| (Caddi, [Cop(Cmuli, [Cconst_int(4|8 as mult); arg1]); arg2]) ->
(Ispecific(if mult = 4 then Iadd4 else Iadd8), [arg1; arg2])
| (Csubi, [Cop(Clsl, [arg1; Cconst_int(2|3 as shift)]); arg2]) ->
(Ispecific(if shift = 2 then Isub4 else Isub8), [arg1; arg2])
| (Csubi, [Cop(Cmuli, [Cconst_int(4|8 as mult); arg1]); arg2]) ->
(Ispecific(if mult = 4 then Isub4 else Isub8), [arg1; arg2])
| _ ->
raise Use_default
let select_store addr exp = raise Use_default
let select_push exp = fatal_error "Proc: select_push"
let pseudoregs_for_operation op arg res = raise Use_default
let is_immediate (n:int) = true
let word_addressed = true
(* Registers available for register allocation *)
@ -245,17 +197,6 @@ let max_register_pressure = function
Iextcall(_, _) -> [| 4; 8 |]
| _ -> [| 19; 29 |]
(* Reloading *)
let reload_test makereg round tst args = raise Use_default
let reload_operation makereg round op args res = raise Use_default
(* No scheduling is needed, the assembler does it better than us. *)
let need_scheduling = false
let oper_latency _ = 1
(* Layout of the stack *)
let num_stack_slots = [| 0; 0 |]

17
asmcomp/alpha/reload.ml Normal file
View File

@ -0,0 +1,17 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Reloading for the Alpha *)
let fundecl f =
(new Reloadgen.reload_generic ())#fundecl f

View File

@ -0,0 +1,20 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
open Schedgen (* to create a dependency *)
(* No scheduling is needed for the Alpha, the Digital Unix assembler
does it better than us. Problem: the assembler for Linux-Alpha
does not do scheduling... *)
let fundecl f = f

View File

@ -0,0 +1,66 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1997 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Instruction selection for the Alpha processor *)
open Misc
open Cmm
open Reg
open Arch
open Mach
class selector () as self =
inherit Selectgen.selector_generic() as super
method is_immediate (n : int) = true
method select_addressing = function
Cconst_symbol s ->
(Ibased(s, 0), Ctuple [])
| Cop(Cadda, [Cconst_symbol s; Cconst_int n]) ->
(Ibased(s, n), Ctuple [])
| Cop(Cadda, [arg; Cconst_int n]) ->
(Iindexed n, arg)
| Cop(Cadda, [arg1; Cop(Caddi, [arg2; Cconst_int n])]) ->
(Iindexed n, Cop(Cadda, [arg1; arg2]))
| arg ->
(Iindexed 0, arg)
method select_operation op args =
match (op, args) with
((Caddi|Cadda),
[arg2; Cop(Clsl, [arg1; Cconst_int(2|3 as shift)])]) ->
(Ispecific(if shift = 2 then Iadd4 else Iadd8), [arg1; arg2])
| ((Caddi|Cadda),
[arg2; Cop(Cmuli, [arg1; Cconst_int(4|8 as mult)])]) ->
(Ispecific(if mult = 4 then Iadd4 else Iadd8), [arg1; arg2])
| ((Caddi|Cadda),
[arg2; Cop(Cmuli, [Cconst_int(4|8 as mult); arg1])]) ->
(Ispecific(if mult = 4 then Iadd4 else Iadd8), [arg1; arg2])
| (Caddi, [Cop(Clsl, [arg1; Cconst_int(2|3 as shift)]); arg2]) ->
(Ispecific(if shift = 2 then Iadd4 else Iadd8), [arg1; arg2])
| (Caddi, [Cop(Cmuli, [arg1; Cconst_int(4|8 as mult)]); arg2]) ->
(Ispecific(if mult = 4 then Iadd4 else Iadd8), [arg1; arg2])
| (Caddi, [Cop(Cmuli, [Cconst_int(4|8 as mult); arg1]); arg2]) ->
(Ispecific(if mult = 4 then Iadd4 else Iadd8), [arg1; arg2])
| (Csubi, [Cop(Clsl, [arg1; Cconst_int(2|3 as shift)]); arg2]) ->
(Ispecific(if shift = 2 then Isub4 else Isub8), [arg1; arg2])
| (Csubi, [Cop(Cmuli, [Cconst_int(4|8 as mult); arg1]); arg2]) ->
(Ispecific(if mult = 4 then Isub4 else Isub8), [arg1; arg2])
| _ ->
super#select_operation op args
end
let fundecl f = (new selector ())#emit_fundecl f

View File

@ -47,7 +47,7 @@ let rec regalloc round fd =
if !dump_prefer then Printmach.preferences();
Coloring.allocate_registers();
dump_if dump_regalloc "After register allocation" fd;
let (newfd, redo_regalloc) = Reload.fundecl round fd in
let (newfd, redo_regalloc) = Reload.fundecl fd in
dump_if dump_reload "After insertion of reloading code" newfd;
if redo_regalloc
then begin Reg.reinit(); Liveness.fundecl newfd; regalloc (round+1) newfd end

View File

@ -1,661 +0,0 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Emission of Alpha assembly code *)
open Misc
open Cmm
open Arch
open Proc
open Reg
open Mach
open Linearize
open Emitaux
(* Tradeoff between code size and code speed *)
let fastcode_flag = ref true
(* Output a label *)
let emit_label lbl =
emit_string "$"; emit_int lbl
(* Output a symbol *)
let emit_symbol s =
Emitaux.emit_symbol '$' s
(* Output a pseudo-register *)
let emit_reg r =
match r.loc with
Reg r -> emit_string (register_name r)
| _ -> fatal_error "Emit_alpha.emit_reg"
(* Layout of the stack frame *)
let stack_offset = ref 0
let uses_gp = ref false
let frame_size () =
let size =
!stack_offset +
8 * (num_stack_slots.(0) + num_stack_slots.(1)) +
(if !contains_calls then 8 else 0) +
(if !uses_gp then 8 else 0) in
Misc.align size 16
let slot_offset loc cl =
match loc with
Incoming n -> frame_size() + n
| Local n ->
if cl = 0
then !stack_offset + n * 8
else !stack_offset + (num_stack_slots.(0) + n) * 8
| Outgoing n -> n
(* Output a stack reference *)
let emit_stack r =
match r.loc with
Stack s ->
let ofs = slot_offset s (register_class r) in `{emit_int ofs}($sp)`
| _ -> fatal_error "Emit_alpha.emit_stack"
(* Output an addressing mode *)
let emit_addressing addr r n =
match addr with
Iindexed ofs ->
`{emit_int ofs}({emit_reg r.(n)})`
| Ibased(s, ofs) ->
`{emit_symbol s}`;
if ofs > 0 then ` + {emit_int ofs}`;
if ofs < 0 then ` - {emit_int(-ofs)}`
(* Communicate live registers at call points to the assembler *)
let int_reg_number = [|
0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12;
16; 17; 18; 19; 20; 21; 22
|]
let float_reg_number = [|
0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15;
16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27; 28; 29
|]
let liveregs instr extra_msk =
(* $13, $14, $15, $26 always live *)
let int_mask = ref(0x00070020 lor extra_msk)
and float_mask = ref 0 in
let add_register = function
{loc = Reg r; typ = (Int | Addr)} ->
int_mask := !int_mask lor (1 lsl (31 - int_reg_number.(r)))
| {loc = Reg r; typ = Float} ->
float_mask := !float_mask lor (1 lsl (31 - float_reg_number.(r - 100)))
| _ -> () in
Reg.Set.iter add_register instr.live;
Array.iter add_register instr.arg;
emit_printf " .livereg 0x%08x, 0x%08x\n" !int_mask !float_mask
let live_24 = 1 lsl (31 - 24)
let live_25 = 1 lsl (31 - 25)
let live_26 = 1 lsl (31 - 26)
let live_27 = 1 lsl (31 - 27)
(* Record live pointers at call points *)
type frame_descr =
{ fd_lbl: int; (* Return address *)
fd_frame_size: int; (* Size of stack frame *)
fd_live_offset: int list } (* Offsets/regs of live addresses *)
let frame_descriptors = ref([] : frame_descr list)
let record_frame_label live =
let lbl = new_label() in
let live_offset = ref [] in
Reg.Set.iter
(function
{typ = Addr; loc = Reg r} ->
live_offset := ((int_reg_number.(r) lsl 1) + 1) :: !live_offset
| {typ = Addr; loc = Stack s} as reg ->
live_offset := slot_offset s (register_class reg) :: !live_offset
| _ -> ())
live;
frame_descriptors :=
{ fd_lbl = lbl;
fd_frame_size = frame_size();
fd_live_offset = !live_offset } :: !frame_descriptors;
lbl
let record_frame live =
let lbl = record_frame_label live in `{emit_label lbl}:`
let emit_frame fd =
` .quad {emit_label fd.fd_lbl} + 4\n`;
` .word {emit_int fd.fd_frame_size}\n`;
` .word {emit_int (List.length fd.fd_live_offset)}\n`;
List.iter
(fun n ->
` .word {emit_int n}\n`)
fd.fd_live_offset;
` .align 3\n`
(* Record calls to the GC -- we've moved them out of the way *)
type gc_call =
{ gc_lbl: label; (* Entry label *)
gc_return_lbl: label; (* Where to branch after GC *)
gc_frame: label; (* Label of frame descriptor *)
gc_instr: instruction } (* Record live registers *)
let call_gc_sites = ref ([] : gc_call list)
let emit_call_gc gc =
`{emit_label gc.gc_lbl}:`;
liveregs gc.gc_instr 0;
`{emit_label gc.gc_frame}: bsr caml_call_gc\n`;
` br {emit_label gc.gc_return_lbl}\n`
(* Determine if $gp is used in the function *)
let rec instr_uses_gp i =
match i.desc with
Lend -> false
| Lop(Iconst_int n) ->
if Nativeint.cmp n (-0x8000000) < 0 || Nativeint.cmp n 0x7FFFFFFF > 0
then true else instr_uses_gp i.next
| Lop(Iconst_float s) -> true
| Lop(Iconst_symbol s) -> true
| Lop(Iextcall(_, _)) -> true
| Lop(Iload(_, Ibased(_, _))) -> true
| Lop(Istore(_, Ibased(_, _))) -> true
| Lop(Iintop(Idiv | Imod)) -> true (* divq and remq can be turned into *)
| Lop(Iintop_imm((Idiv | Imod), _)) -> true (* a function call *)
| Lop(Iintop_imm(_, n)) ->
if n < -0x8000000 || n > 0x7FFFFFFF then true else instr_uses_gp i.next
| Lsetuptrap lbl -> true
| _ ->
instr_uses_gp i.next
(* Names of various instructions *)
let name_for_int_operation = function
Iadd -> "addq"
| Isub -> "subq"
| Imul -> "mulq"
| Idiv -> "divq"
| Imod -> "remq"
| Iand -> "and"
| Ior -> "or"
| Ixor -> "xor"
| Ilsl -> "sll"
| Ilsr -> "srl"
| Iasr -> "sra"
| _ -> Misc.fatal_error "Emit.name_for_int_operation"
let name_for_float_operation = function
Inegf -> "fneg"
| Iabsf -> "fabs"
| Iaddf -> "addt"
| Isubf -> "subt"
| Imulf -> "mult"
| Idivf -> "divt"
| _ -> Misc.fatal_error "Emit.name_for_float_operation"
let name_for_specific_operation = function
Iadd4 -> "s4addq"
| Iadd8 -> "s8addq"
| Isub4 -> "s4subq"
| Isub8 -> "s8subq"
let name_for_int_comparison = function
Isigned Ceq -> "cmpeq", true | Isigned Cne -> "cmpeq", false
| Isigned Cle -> "cmple", true | Isigned Cgt -> "cmple", false
| Isigned Clt -> "cmplt", true | Isigned Cge -> "cmplt", false
| Iunsigned Ceq -> "cmpeq", true | Iunsigned Cne -> "cmpeq", false
| Iunsigned Cle -> "cmpule", true | Iunsigned Cgt -> "cmpule", false
| Iunsigned Clt -> "cmpult", true | Iunsigned Cge -> "cmpult", false
(* Used for comparisons against 0 *)
let name_for_int_cond_branch = function
Isigned Ceq -> "beq" | Isigned Cne -> "bne"
| Isigned Cle -> "ble" | Isigned Cgt -> "bgt"
| Isigned Clt -> "blt" | Isigned Cge -> "bge"
| Iunsigned Ceq -> "beq" | Iunsigned Cne -> "bne"
| Iunsigned Cle -> "beq" | Iunsigned Cgt -> "bne"
| Iunsigned Clt -> "#" | Iunsigned Cge -> "br"
(* Always false *) (* Always true *)
let name_for_float_comparison cmp neg =
match cmp with
Ceq -> ("cmpteq", false, neg) | Cne -> ("cmpteq", false, not neg)
| Cle -> ("cmptle", false, neg) | Cgt -> ("cmptlt", true, neg)
| Clt -> ("cmptlt", false, neg) | Cge -> ("cmptle", true, neg)
(* Output the assembly code for an instruction *)
(* Name of current function *)
let function_name = ref ""
(* Entry point for tail recursive calls *)
let tailrec_entry_point = ref 0
(* Label of trap for out-of-range accesses *)
let range_check_trap = ref 0
let emit_instr i =
match i.desc with
Lend -> ()
| Lop(Imove | Ispill | Ireload) ->
let src = i.arg.(0) and dst = i.res.(0) in
if src.loc <> dst.loc then begin
match (src.loc, dst.loc) with
(Reg rs, Reg rd) ->
if src.typ = Float then
` fmov {emit_reg src}, {emit_reg dst}\n`
else
` mov {emit_reg src}, {emit_reg dst}\n`
| (Reg rs, Stack sd) ->
if src.typ = Float then
` stt {emit_reg src}, {emit_stack dst}\n`
else
` stq {emit_reg src}, {emit_stack dst}\n`
| (Stack ss, Reg rd) ->
if src.typ = Float then
` ldt {emit_reg dst}, {emit_stack src}\n`
else
` ldq {emit_reg dst}, {emit_stack src}\n`
| _ ->
fatal_error "Emit_alpha: Imove"
end
| Lop(Iconst_int n) ->
if Nativeint.sign n = 0 then
` clr {emit_reg i.res.(0)}\n`
else
` ldiq {emit_reg i.res.(0)}, {emit_nativeint n}\n`
| Lop(Iconst_float s) ->
` ldit {emit_reg i.res.(0)}, {emit_string s}\n`
| Lop(Iconst_symbol s) ->
` lda {emit_reg i.res.(0)}, {emit_symbol s}\n`
| Lop(Icall_ind) ->
liveregs i 0;
`{record_frame i.live} jsr ({emit_reg i.arg.(0)})\n`
| Lop(Icall_imm s) ->
liveregs i 0;
`{record_frame i.live} bsr {emit_symbol s}\n`
| Lop(Itailcall_ind) ->
let n = frame_size() in
if !contains_calls then
` ldq $26, {emit_int(n - 8)}($sp)\n`;
if !uses_gp then
` ldq $gp, {emit_int(n - 16)}($sp)\n`;
if n > 0 then
` lda $sp, {emit_int n}($sp)\n`;
liveregs i live_26;
` jmp ({emit_reg i.arg.(0)})\n`
| Lop(Itailcall_imm s) ->
if s = !function_name then begin
` br {emit_label !tailrec_entry_point}\n`
end else begin
let n = frame_size() in
if !contains_calls then
` ldq $26, {emit_int(n - 8)}($sp)\n`;
if !uses_gp then
` ldq $gp, {emit_int(n - 16)}($sp)\n`;
if n > 0 then
` lda $sp, {emit_int n}($sp)\n`;
liveregs i live_26;
` br {emit_symbol s}\n`
end
| Lop(Iextcall(s, alloc)) ->
if alloc then begin
` lda $27, {emit_symbol s}\n`;
liveregs i live_27;
`{record_frame i.live} bsr caml_c_call\n`
(* caml_c_call preserves $gp *)
end else begin
` jsr {emit_symbol s}\n`;
if !uses_gp then
` ldgp $gp, 0($26)\n`
end
| Lop(Istackoffset n) ->
` lda $sp, {emit_int (-n)}($sp)\n`;
stack_offset := !stack_offset + n
| Lop(Iload(chunk, addr)) ->
let load_instr =
match chunk with
Word -> if i.res.(0).typ = Float then "ldt" else "ldq"
| Byte_unsigned -> "ldbu"
| Byte_signed -> "ldb"
| Sixteen_unsigned -> "ldwu"
| Sixteen_signed -> "ldw" in
` {emit_string load_instr} {emit_reg i.res.(0)}, {emit_addressing addr i.arg 0}\n`
| Lop(Istore(chunk, addr)) ->
let store_instr =
match chunk with
Word -> if i.arg.(0).typ = Float then "stt" else "stq"
| Byte_unsigned | Byte_signed -> "stb"
| Sixteen_unsigned | Sixteen_signed -> "stw" in
` {emit_string store_instr} {emit_reg i.arg.(0)}, {emit_addressing addr i.arg 1}\n`
| Lop(Ialloc n) ->
if !fastcode_flag then begin
let lbl_redo = new_label() in
`{emit_label lbl_redo}: subq $13, {emit_int n}, $13\n`;
` cmpult $13, $14, $25\n`;
let lbl_call_gc = new_label() in
let lbl_frame = record_frame_label i.live in
` bne $25, {emit_label lbl_call_gc}\n`;
call_gc_sites :=
{ gc_lbl = lbl_call_gc;
gc_return_lbl = lbl_redo;
gc_frame = lbl_frame;
gc_instr = i } :: !call_gc_sites;
` addq $13, 8, {emit_reg i.res.(0)}\n`
end else begin
begin match n with
16 -> liveregs i 0;
`{record_frame i.live} bsr caml_alloc1\n`
| 24 -> liveregs i 0;
`{record_frame i.live} bsr caml_alloc2\n`
| 32 -> liveregs i 0;
`{record_frame i.live} bsr caml_alloc3\n`
| _ -> ` ldiq $25, {emit_int n}\n`;
liveregs i live_25;
`{record_frame i.live} bsr caml_alloc\n`
end;
` addq $13, 8, {emit_reg i.res.(0)}\n`
end
| Lop(Iintop(Icomp cmp)) ->
let (comp, test) = name_for_int_comparison cmp in
` {emit_string comp} {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, {emit_reg i.res.(0)}\n`;
if not test then
` xor {emit_reg i.res.(0)}, 1, {emit_reg i.res.(0)}\n`
| Lop(Iintop(Icheckbound)) ->
if !range_check_trap = 0 then range_check_trap := new_label();
` cmpule {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, $25\n`;
` bne $25, {emit_label !range_check_trap}\n`
| Lop(Iintop op) ->
let instr = name_for_int_operation op in
` {emit_string instr} {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, {emit_reg i.res.(0)}\n`
| Lop(Iintop_imm(Idiv, n)) when n = 1 lsl (Misc.log2 n) ->
let l = Misc.log2 n in
` addq {emit_reg i.arg.(0)}, {emit_int(n-1)}, $25\n`;
` cmovge {emit_reg i.arg.(0)}, {emit_reg i.arg.(0)}, $25\n`;
` sra $25, {emit_int l}, {emit_reg i.res.(0)}\n`
| Lop(Iintop_imm(Imod, n)) when n = 1 lsl (Misc.log2 n) ->
let l = Misc.log2 n in
` and {emit_reg i.arg.(0)}, {emit_int(n-1)}, $25\n`;
` subq $25, {emit_int n}, $24\n`;
` cmovge {emit_reg i.arg.(0)}, $25, $24\n`;
` cmoveq $25, $25, $24\n`;
` mov $24, {emit_reg i.res.(0)}\n`
| Lop(Iintop_imm(Icomp cmp, n)) ->
let (comp, test) = name_for_int_comparison cmp in
` {emit_string comp} {emit_reg i.arg.(0)}, {emit_int n}, {emit_reg i.res.(0)}\n`;
if not test then
` xor {emit_reg i.res.(0)}, 1, {emit_reg i.res.(0)}\n`
| Lop(Iintop_imm(Icheckbound, n)) ->
if !range_check_trap = 0 then range_check_trap := new_label();
` cmpule {emit_reg i.arg.(0)}, {emit_int n}, $25\n`;
` bne $25, {emit_label !range_check_trap}\n`
| Lop(Iintop_imm(op, n)) ->
let instr = name_for_int_operation op in
` {emit_string instr} {emit_reg i.arg.(0)}, {emit_int n}, {emit_reg i.res.(0)}\n`
| Lop(Inegf | Iabsf as op) ->
let instr = name_for_float_operation op in
` {emit_string instr} {emit_reg i.arg.(0)}, {emit_reg i.res.(0)}\n`
| Lop(Iaddf | Isubf | Imulf | Idivf as op) ->
let instr = name_for_float_operation op in
` {emit_string instr} {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, {emit_reg i.res.(0)}\n`
| Lop(Ifloatofint) ->
` lda $sp, -8($sp)\n`;
` stq {emit_reg i.arg.(0)}, 0($sp)\n`;
` ldt $f30, 0($sp)\n`;
` cvtqt $f30, {emit_reg i.res.(0)}\n`;
` lda $sp, 8($sp)\n`
| Lop(Iintoffloat) ->
` lda $sp, -8($sp)\n`;
` cvttqc {emit_reg i.arg.(0)}, $f30\n`;
` stt $f30, 0($sp)\n`;
` ldq {emit_reg i.res.(0)}, 0($sp)\n`;
` lda $sp, 8($sp)\n`
| Lop(Ispecific sop) ->
let instr = name_for_specific_operation sop in
` {emit_string instr} {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, {emit_reg i.res.(0)}\n`
| Lreloadretaddr ->
let n = frame_size() in
` ldq $26, {emit_int(n - 8)}($sp)\n`
| Lreturn ->
let n = frame_size() in
if !uses_gp then
` ldq $gp, {emit_int(n - 16)}($sp)\n`;
if n > 0 then
` lda $sp, {emit_int n}($sp)\n`;
liveregs i 0;
` ret ($26)\n`
| Llabel lbl ->
`{emit_label lbl}:\n`
| Lbranch lbl ->
` br {emit_label lbl}\n`
| Lcondbranch(tst, lbl) ->
begin match tst with
Itruetest ->
` bne {emit_reg i.arg.(0)}, {emit_label lbl}\n`
| Ifalsetest ->
` beq {emit_reg i.arg.(0)}, {emit_label lbl}\n`
| Iinttest cmp ->
let (comp, test) = name_for_int_comparison cmp in
` {emit_string comp} {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, $25\n`;
if test then
` bne $25, {emit_label lbl}\n`
else
` beq $25, {emit_label lbl}\n`
| Iinttest_imm(cmp, 0) ->
let branch = name_for_int_cond_branch cmp in
` {emit_string branch} {emit_reg i.arg.(0)}, {emit_label lbl}\n`
| Iinttest_imm(cmp, n) ->
let (comp, test) = name_for_int_comparison cmp in
` {emit_string comp} {emit_reg i.arg.(0)}, {emit_int n}, $25\n`;
if test then
` bne $25, {emit_label lbl}\n`
else
` beq $25, {emit_label lbl}\n`
| Ifloattest(cmp, neg) ->
let (comp, swap, test) = name_for_float_comparison cmp neg in
` {emit_string comp} `;
if swap
then `{emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}, $f30\n`
else `{emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, $f30\n`;
if test
then ` fbeq $f30, {emit_label lbl}\n`
else ` fbne $f30, {emit_label lbl}\n`
| Ioddtest ->
` blbs {emit_reg i.arg.(0)}, {emit_label lbl}\n`
| Ieventest ->
` blbc {emit_reg i.arg.(0)}, {emit_label lbl}\n`
end
| Lcondbranch3(lbl0, lbl1, lbl2) ->
begin match lbl0 with
None -> ()
| Some lbl -> ` beq {emit_reg i.arg.(0)}, {emit_label lbl}\n`
end;
begin match lbl1 with
None -> ()
| Some lbl -> ` blbs {emit_reg i.arg.(0)}, {emit_label lbl}\n`
end;
begin match lbl2 with
None -> ()
| Some lbl ->
if lbl0 <> None then
` blbc {emit_reg i.arg.(0)}, {emit_label lbl}\n`
else if lbl1 <> None then
` bne {emit_reg i.arg.(0)}, {emit_label lbl}\n`
else begin
` subq {emit_reg i.arg.(0)}, 2, $25\n`;
` beq $25, {emit_label lbl}\n`
end
end
| Lswitch jumptbl ->
let lbl_jump = new_label() in
` br $25, {emit_label lbl_jump}\n`;
for i = 0 to Array.length jumptbl - 1 do
` br {emit_label jumptbl.(i)}\n`
done;
`{emit_label lbl_jump}: s4addq {emit_reg i.arg.(0)}, $25, $25\n`;
` jmp ($25)\n`
| Lsetuptrap lbl ->
` br $25, {emit_label lbl}\n`;
if !uses_gp then
` ldgp $gp, 0($27)\n`
| Lpushtrap ->
stack_offset := !stack_offset + 16;
` lda $sp, -16($sp)\n`;
` stq $15, 0($sp)\n`;
` stq $25, 8($sp)\n`;
` mov $sp, $15\n`
| Lpoptrap ->
` ldq $15, 0($sp)\n`;
` lda $sp, 16($sp)\n`;
stack_offset := !stack_offset - 16
| Lraise ->
` mov $15, $sp\n`;
` ldq $15, 0($sp)\n`;
` ldq $27, 8($sp)\n`;
` lda $sp, 16($sp)\n`;
liveregs i 0;
` jmp $25, ($27)\n` (* Keep retaddr in $25 for debugging *)
let rec emit_all i =
match i.desc with Lend -> () | _ -> emit_instr i; emit_all i.next
(* Emission of a function declaration *)
let fundecl fundecl =
function_name := fundecl.fun_name;
fastcode_flag := fundecl.fun_fast;
tailrec_entry_point := new_label();
stack_offset := 0;
call_gc_sites := [];
uses_gp := instr_uses_gp fundecl.fun_body;
if !uses_gp then contains_calls := true;
range_check_trap := 0;
` .text\n`;
` .align 4\n`;
` .globl {emit_symbol fundecl.fun_name}\n`;
` .ent {emit_symbol fundecl.fun_name}\n`;
`{emit_symbol fundecl.fun_name}:\n`;
let n = frame_size() in
if n > 0 then
` lda $sp, -{emit_int n}($sp)\n`;
if !uses_gp then begin
let lbl = new_label() in
` br $27, {emit_label lbl}\n`;
`{emit_label lbl}: stq $gp, {emit_int(n - 16)}($sp)\n`;
` ldgp $gp, 4($27)\n`
end;
if !contains_calls then begin
` stq $26, {emit_int(n - 8)}($sp)\n`;
` .mask 0x04000000, -8\n`;
` .fmask 0x0, 0\n`
end;
` .frame $sp, {emit_int n}, $26\n`;
` .prologue {emit_int(if !uses_gp then 1 else 0)}\n`;
`{emit_label !tailrec_entry_point}:\n`;
emit_all fundecl.fun_body;
List.iter emit_call_gc !call_gc_sites;
if !range_check_trap > 0 then
`{emit_label !range_check_trap}: br $25, call_array_bound_error\n`;
` .end {emit_symbol fundecl.fun_name}\n`
(* Emission of data *)
let emit_item = function
Cdefine_symbol s ->
` .globl {emit_symbol s}\n`;
`{emit_symbol s}:\n`
| Cdefine_label lbl ->
`{emit_label (10000 + lbl)}:\n`
| Cint8 n ->
` .byte {emit_int n}\n`
| Cint16 n ->
` .word {emit_int n}\n`
| Cint n ->
` .quad {emit_nativeint n}\n`
| Cfloat f ->
` .double {emit_string f}\n`
| Csymbol_address s ->
` .quad {emit_symbol s}\n`
| Clabel_address lbl ->
` .quad {emit_label (10000 + lbl)}\n`
| Cstring s ->
emit_string_directive " .ascii " s
| Cskip n ->
if n > 0 then ` .space {emit_int n}\n`
| Calign n ->
` .align {emit_int(Misc.log2 n)}\n`
let data l =
` .data\n`;
List.iter emit_item l
(* Beginning / end of an assembly file *)
let begin_assembly() =
(* There are really two groups of registers:
$sp and $15 always point to stack locations
$0 - $14, $16-$23 never point to stack locations. *)
` .noalias $0,$sp; .noalias $0,$15; .noalias $1,$sp; .noalias $1,$15\n`;
` .noalias $2,$sp; .noalias $2,$15; .noalias $3,$sp; .noalias $3,$15\n`;
` .noalias $4,$sp; .noalias $4,$15; .noalias $5,$sp; .noalias $5,$15\n`;
` .noalias $6,$sp; .noalias $6,$15; .noalias $7,$sp; .noalias $7,$15\n`;
` .noalias $8,$sp; .noalias $8,$15; .noalias $9,$sp; .noalias $9,$15\n`;
` .noalias $10,$sp; .noalias $10,$15; .noalias $11,$sp; .noalias $11,$15\n`;
` .noalias $12,$sp; .noalias $12,$15; .noalias $13,$sp; .noalias $13,$15\n`;
` .noalias $14,$sp; .noalias $14,$15; .noalias $16,$sp; .noalias $16,$15\n`;
` .noalias $17,$sp; .noalias $17,$15; .noalias $18,$sp; .noalias $18,$15\n`;
` .noalias $19,$sp; .noalias $19,$15; .noalias $20,$sp; .noalias $20,$15\n`;
` .noalias $21,$sp; .noalias $21,$15; .noalias $22,$sp; .noalias $22,$15\n`;
` .noalias $23,$sp; .noalias $23,$15\n\n`;
(* The following .file directive is intended to prevent the generation
of line numbers for the debugger, 'cos they make .o files larger
and slow down linking. *)
` .file 1 \"{emit_string !Location.input_name}\"\n\n`;
let lbl_begin = Compilenv.current_unit_name() ^ "_data_begin" in
` .data\n`;
` .globl {emit_symbol lbl_begin}\n`;
`{emit_symbol lbl_begin}:\n`;
let lbl_begin = Compilenv.current_unit_name() ^ "_code_begin" in
` .text\n`;
` .globl {emit_symbol lbl_begin}\n`;
`{emit_symbol lbl_begin}:\n`
let end_assembly () =
let lbl_end = Compilenv.current_unit_name() ^ "_code_end" in
` .text\n`;
` .globl {emit_symbol lbl_end}\n`;
`{emit_symbol lbl_end}:\n`;
let lbl_end = Compilenv.current_unit_name() ^ "_data_end" in
` .data\n`;
` .globl {emit_symbol lbl_end}\n`;
`{emit_symbol lbl_end}:\n`;
` .quad 0\n`;
let lbl_frame = Compilenv.current_unit_name() ^ "_frametable" in
` .rdata\n`;
` .globl {emit_symbol lbl_frame}\n`;
`{emit_symbol lbl_frame}:\n`;
` .quad {emit_int (List.length !frame_descriptors)}\n`;
List.iter emit_frame !frame_descriptors;
frame_descriptors := []

File diff suppressed because it is too large Load Diff

View File

@ -1,799 +0,0 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Emission of Intel 386 assembly code *)
open Misc
open Cmm
open Arch
open Proc
open Reg
open Mach
open Linearize
open Emitaux
(* Tradeoff between code size and code speed *)
let fastcode_flag = ref true
let stack_offset = ref 0
(* Layout of the stack frame *)
let frame_size () = (* includes return address *)
!stack_offset + 4 * num_stack_slots.(0) + 8 * num_stack_slots.(1) + 4
let slot_offset loc cl =
match loc with
Incoming n -> frame_size() + n
| Local n ->
if cl = 0
then !stack_offset + n * 4
else !stack_offset + num_stack_slots.(0) * 4 + n * 8
| Outgoing n -> n
(* Symbols are prefixed with _, except under Linux with ELF binaries *)
let symbol_prefix =
match Config.system with
"linux_elf" -> ""
| "solaris" -> ""
| _ -> "_"
let emit_symbol s =
emit_string symbol_prefix; Emitaux.emit_symbol '$' s
(* Output a label *)
let label_prefix =
match Config.system with
"linux_elf" -> ".L"
| "solaris" -> ".L"
| _ -> "L"
let emit_label lbl =
emit_string label_prefix; emit_int lbl
(* Some data directives have different names under Solaris *)
let word_dir =
match Config.system with
"solaris" -> ".value"
| _ -> ".word"
let skip_dir =
match Config.system with
"solaris" -> ".zero"
| _ -> ".space"
let use_ascii_dir =
match Config.system with
"solaris" -> false
| _ -> true
(* Output a .align directive.
The numerical argument to .align is log2 of alignment size, except
under ELF, where it is the alignment size... *)
let emit_align =
match Config.system with
"linux_elf" | "solaris" ->
(fun n -> ` .align {emit_int n}\n`)
| _ ->
(fun n -> ` .align {emit_int(Misc.log2 n)}\n`)
(* Output a pseudo-register *)
let emit_reg = function
{ loc = Reg r } ->
emit_string (register_name r)
| { loc = Stack s } as r ->
let ofs = slot_offset s (register_class r) in
`{emit_int ofs}(%esp)`
| { loc = Unknown } ->
fatal_error "Emit_i386.emit_reg"
(* Output a reference to the lower 8 bits or lower 16 bits of a register *)
let reg_low_byte_name = [| "%al"; "%bl"; "%cl"; "%dl" |]
let reg_low_half_name = [| "%ax"; "%bx"; "%cx"; "%dx"; "%si"; "%di"; "%bp" |]
let emit_reg8 r =
match r.loc with
Reg r when r < 4 -> emit_string (reg_low_byte_name.(r))
| _ -> fatal_error "Emit_i386.emit_reg8"
let emit_reg16 r =
match r.loc with
Reg r when r < 7 -> emit_string (reg_low_half_name.(r))
| _ -> fatal_error "Emit_i386.emit_reg16"
(* Check if the given register overlaps (same location) with the given
array of registers *)
let register_overlap reg arr =
try
for i = 0 to Array.length arr - 1 do
if reg.loc = arr.(i).loc then raise Exit
done;
false
with Exit ->
true
(* Output an addressing mode *)
let emit_addressing addr r n =
match addr with
Ibased(s, d) ->
`{emit_symbol s}`;
if d <> 0 then ` + {emit_int d}`
| Iindexed d ->
if d <> 0 then emit_int d;
`({emit_reg r.(n)})`
| Iindexed2 d ->
if d <> 0 then emit_int d;
`({emit_reg r.(n)}, {emit_reg r.(n+1)})`
| Iscaled(scale, d) ->
if d <> 0 then emit_int d;
`(, {emit_reg r.(n)}, {emit_int scale})`
| Iindexed2scaled(scale, d) ->
if d <> 0 then emit_int d;
`({emit_reg r.(n)}, {emit_reg r.(n+1)}, {emit_int scale})`
(* Record live pointers at call points *)
type frame_descr =
{ fd_lbl: int; (* Return address *)
fd_frame_size: int; (* Size of stack frame *)
fd_live_offset: int list } (* Offsets/regs of live addresses *)
let frame_descriptors = ref([] : frame_descr list)
let record_frame_label live =
let lbl = new_label() in
let live_offset = ref [] in
Reg.Set.iter
(function
{typ = Addr; loc = Reg r} ->
live_offset := ((r lsl 1) + 1) :: !live_offset
| {typ = Addr; loc = Stack s} as reg ->
live_offset := slot_offset s (register_class reg) :: !live_offset
| _ -> ())
live;
frame_descriptors :=
{ fd_lbl = lbl;
fd_frame_size = frame_size();
fd_live_offset = !live_offset } :: !frame_descriptors;
lbl
let record_frame live =
let lbl = record_frame_label live in `{emit_label lbl}:\n`
let emit_frame fd =
` .long {emit_label fd.fd_lbl}\n`;
` {emit_string word_dir} {emit_int fd.fd_frame_size}\n`;
` {emit_string word_dir} {emit_int (List.length fd.fd_live_offset)}\n`;
List.iter
(fun n ->
` {emit_string word_dir} {emit_int n}\n`)
fd.fd_live_offset;
emit_align 4
(* Record calls to the GC -- we've moved them out of the way *)
type gc_call =
{ gc_lbl: label; (* Entry label *)
gc_return_lbl: label; (* Where to branch after GC *)
gc_frame: label } (* Label of frame descriptor *)
let call_gc_sites = ref ([] : gc_call list)
let emit_call_gc gc =
`{emit_label gc.gc_lbl}: call {emit_symbol "caml_call_gc"}\n`;
`{emit_label gc.gc_frame}: jmp {emit_label gc.gc_return_lbl}\n`
(* Names for instructions *)
let instr_for_intop = function
Iadd -> "addl"
| Isub -> "subl"
| Imul -> "imull"
| Iand -> "andl"
| Ior -> "orl"
| Ixor -> "xorl"
| Ilsl -> "sall"
| Ilsr -> "shrl"
| Iasr -> "sarl"
| _ -> fatal_error "Emit_i386: instr_for_intop"
let instr_for_floatop = function
Inegf -> "fchs"
| Iabsf -> "fabs"
| Iaddf -> "faddl"
| Isubf -> "fsubl"
| Imulf -> "fmull"
| Idivf -> "fdivl"
| Ispecific Isubfrev -> "fsubrl"
| Ispecific Idivfrev -> "fdivrl"
| _ -> fatal_error "Emit_i386: instr_for_floatop"
let instr_for_floatop_reversed = function
Iaddf -> "faddl"
| Isubf -> "fsubrl"
| Imulf -> "fmull"
| Idivf -> "fdivrl"
| Ispecific Isubfrev -> "fsubl"
| Ispecific Idivfrev -> "fdivl"
| _ -> fatal_error "Emit_i386: instr_for_floatop_reversed"
let instr_for_floatop_pop = function
Iaddf -> "faddp"
| Isubf -> "fsubp"
| Imulf -> "fmulp"
| Idivf -> "fdivp"
| Ispecific Isubfrev -> "fsubrp"
| Ispecific Idivfrev -> "fdivrp"
| _ -> fatal_error "Emit_i386: instr_for_floatop_pop"
let instr_for_floatarithmem = function
Ifloatadd -> "faddl"
| Ifloatsub -> "fsubl"
| Ifloatsubrev -> "fsubrl"
| Ifloatmul -> "fmull"
| Ifloatdiv -> "fdivl"
| Ifloatdivrev -> "fdivrl"
let name_for_cond_branch = function
Isigned Ceq -> "e" | Isigned Cne -> "ne"
| Isigned Cle -> "le" | Isigned Cgt -> "g"
| Isigned Clt -> "l" | Isigned Cge -> "ge"
| Iunsigned Ceq -> "e" | Iunsigned Cne -> "ne"
| Iunsigned Cle -> "be" | Iunsigned Cgt -> "a"
| Iunsigned Clt -> "b" | Iunsigned Cge -> "ae"
(* Output an = 0 or <> 0 test. *)
let output_test_zero arg =
match arg.loc with
Reg r -> ` testl {emit_reg arg}, {emit_reg arg}\n`
| _ -> ` cmpl $0, {emit_reg arg}\n`
(* Deallocate the stack frame before a return or tail call *)
let output_epilogue () =
let n = frame_size() - 4 in
if n > 0 then ` addl ${emit_int n}, %esp\n`
(* Output the assembly code for an instruction *)
(* Name of current function *)
let function_name = ref ""
(* Entry point for tail recursive calls *)
let tailrec_entry_point = ref 0
(* Label of trap for out-of-range accesses *)
let range_check_trap = ref 0
let float_constants = ref ([] : (int * string) list)
let tos = phys_reg 100
let emit_instr i =
match i.desc with
Lend -> ()
| Lop(Imove | Ispill | Ireload) ->
let src = i.arg.(0) and dst = i.res.(0) in
if src.loc <> dst.loc then begin
if src.typ = Float then
if src = tos then
` fstpl {emit_reg dst}\n`
else begin
` fldl {emit_reg src}\n`;
` fstpl {emit_reg dst}\n`
end
else
` movl {emit_reg src}, {emit_reg dst}\n`
end
| Lop(Iconst_int n) ->
if Nativeint.sign n = 0 then begin
match i.res.(0).loc with
Reg n -> ` xorl {emit_reg i.res.(0)}, {emit_reg i.res.(0)}\n`
| _ -> ` movl $0, {emit_reg i.res.(0)}\n`
end else
` movl ${emit_nativeint n}, {emit_reg i.res.(0)}\n`
| Lop(Iconst_float s) ->
let f = float_of_string s in
if f = 0.0 then
` fldz\n`
else if f = 1.0 then
` fld1\n`
else begin
let lbl = new_label() in
float_constants := (lbl, s) :: !float_constants;
` fldl {emit_label lbl}\n`
end
| Lop(Iconst_symbol s) ->
` movl ${emit_symbol s}, {emit_reg i.res.(0)}\n`
| Lop(Icall_ind) ->
` call *{emit_reg i.arg.(0)}\n`;
record_frame i.live
| Lop(Icall_imm s) ->
` call {emit_symbol s}\n`;
record_frame i.live
| Lop(Itailcall_ind) ->
output_epilogue();
` jmp *{emit_reg i.arg.(0)}\n`
| Lop(Itailcall_imm s) ->
if s = !function_name then
` jmp {emit_label !tailrec_entry_point}\n`
else begin
output_epilogue();
` jmp {emit_symbol s}\n`
end
| Lop(Iextcall(s, alloc)) ->
if alloc then begin
` movl ${emit_symbol s}, %eax\n`;
` call {emit_symbol "caml_c_call"}\n`;
record_frame i.live
end else begin
` call {emit_symbol s}\n`
end
| Lop(Istackoffset n) ->
if n < 0
then ` addl ${emit_int(-n)}, %esp\n`
else ` subl ${emit_int(n)}, %esp\n`;
stack_offset := !stack_offset + n
| Lop(Iload(chunk, addr)) ->
let dest = i.res.(0) in
begin match dest.typ with
Int | Addr ->
begin match (chunk, dest.loc) with
(Word, _) ->
` movl {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
| (Byte_unsigned, Reg r) when r < 4 & not (register_overlap dest i.arg) ->
` xorl {emit_reg dest}, {emit_reg dest}\n`;
` movb {emit_addressing addr i.arg 0}, {emit_reg8 dest}\n`
| (Byte_unsigned, _) ->
` movzbl {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
| (Byte_signed, _) ->
` movsbl {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
| (Sixteen_unsigned, Reg r) when not (register_overlap dest i.arg) ->
` xorl {emit_reg dest}, {emit_reg dest}\n`;
` movw {emit_addressing addr i.arg 0}, {emit_reg16 dest}\n`
| (Sixteen_unsigned, _) ->
` movzwl {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
| (Sixteen_signed, _) ->
` movswl {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
end
| Float ->
` fldl {emit_addressing addr i.arg 0}\n`
end
| Lop(Istore(Word, addr)) ->
begin match i.arg.(0).typ with
Int | Addr ->
` movl {emit_reg i.arg.(0)}, {emit_addressing addr i.arg 1}\n`
| Float ->
if i.arg.(0) = tos then
` fstpl {emit_addressing addr i.arg 1}\n`
else begin
` fldl {emit_reg i.arg.(0)}\n`;
` fstpl {emit_addressing addr i.arg 1}\n`
end
end
| Lop(Istore(chunk, addr)) ->
(* i.arg.(0) is guaranteed to be in %edx, actually *)
begin match chunk with
Word -> fatal_error "Emit_i386: store word"
| Byte_unsigned | Byte_signed ->
` movb {emit_reg8 i.arg.(0)}, {emit_addressing addr i.arg 1}\n`
| Sixteen_unsigned | Sixteen_signed ->
` movw {emit_reg16 i.arg.(0)}, {emit_addressing addr i.arg 1}\n`
end
| Lop(Ialloc n) ->
if !fastcode_flag then begin
let lbl_redo = new_label() in
`{emit_label lbl_redo}: movl {emit_symbol "young_ptr"}, %eax\n`;
` subl ${emit_int n}, %eax\n`;
` movl %eax, {emit_symbol "young_ptr"}\n`;
` cmpl {emit_symbol "young_limit"}, %eax\n`;
let lbl_call_gc = new_label() in
let lbl_frame = record_frame_label i.live in
` jb {emit_label lbl_call_gc}\n`;
` leal 4(%eax), {emit_reg i.res.(0)}\n`;
call_gc_sites :=
{ gc_lbl = lbl_call_gc;
gc_return_lbl = lbl_redo;
gc_frame = lbl_frame } :: !call_gc_sites
end else begin
begin match n with
8 -> ` call {emit_symbol "caml_alloc1"}\n`
| 12 -> ` call {emit_symbol "caml_alloc2"}\n`
| 16 -> ` call {emit_symbol "caml_alloc3"}\n`
| _ -> ` movl ${emit_int n}, %eax\n`;
` call {emit_symbol "caml_alloc"}\n`
end;
`{record_frame i.live} leal 4(%eax), {emit_reg i.res.(0)}\n`
end
| Lop(Iintop(Icomp cmp)) ->
` cmpl {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`;
let b = name_for_cond_branch cmp in
` set{emit_string b} %al\n`;
` movzbl %al, {emit_reg i.res.(0)}\n`
| Lop(Iintop_imm(Icomp cmp, n)) ->
` cmpl ${emit_int n}, {emit_reg i.arg.(0)}\n`;
let b = name_for_cond_branch cmp in
` set{emit_string b} %al\n`;
` movzbl %al, {emit_reg i.res.(0)}\n`
| Lop(Iintop Icheckbound) ->
if !range_check_trap = 0 then range_check_trap := new_label();
` cmpl {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`;
` jbe {emit_label !range_check_trap}\n`
| Lop(Iintop_imm(Icheckbound, n)) ->
if !range_check_trap = 0 then range_check_trap := new_label();
` cmpl ${emit_int n}, {emit_reg i.arg.(0)}\n`;
` jbe {emit_label !range_check_trap}\n`
| Lop(Iintop(Idiv | Imod)) ->
` cltd\n`;
` idivl {emit_reg i.arg.(1)}\n`
| Lop(Iintop(Ilsl | Ilsr | Iasr as op)) ->
(* We have i.arg.(0) = i.res.(0) and i.arg.(1) = %ecx *)
` {emit_string(instr_for_intop op)} %cl, {emit_reg i.res.(0)}\n`
| Lop(Iintop op) ->
(* We have i.arg.(0) = i.res.(0) *)
` {emit_string(instr_for_intop op)} {emit_reg i.arg.(1)}, {emit_reg i.res.(0)}\n`
| Lop(Iintop_imm(Iadd, 1) | Iintop_imm(Isub, -1)) ->
` incl {emit_reg i.res.(0)}\n`
| Lop(Iintop_imm(Iadd, -1) | Iintop_imm(Isub, 1)) ->
` decl {emit_reg i.res.(0)}\n`
| Lop(Iintop_imm(Idiv, n)) ->
let l = Misc.log2 n in
let lbl = new_label() in
output_test_zero i.arg.(0);
` jge {emit_label lbl}\n`;
` addl ${emit_int(n-1)}, {emit_reg i.arg.(0)}\n`;
`{emit_label lbl}: sarl ${emit_int l}, {emit_reg i.arg.(0)}\n`
| Lop(Iintop_imm(Imod, n)) ->
let l = Misc.log2 n in
let lbl = new_label() in
` movl {emit_reg i.arg.(0)}, %eax\n`;
` testl %eax, %eax\n`;
` jge {emit_label lbl}\n`;
` addl ${emit_int(n-1)}, %eax\n`;
`{emit_label lbl}: andl ${emit_int(-n)}, %eax\n`;
` subl %eax, {emit_reg i.arg.(0)}\n`
| Lop(Iintop_imm(op, n)) ->
(* We have i.arg.(0) = i.res.(0) *)
` {emit_string(instr_for_intop op)} ${emit_int n}, {emit_reg i.res.(0)}\n`
| Lop(Inegf | Iabsf as floatop) ->
if i.arg.(0) <> tos then
` fldl {emit_reg i.arg.(0)}\n`;
` {emit_string(instr_for_floatop floatop)}\n`
| Lop(Iaddf | Isubf | Imulf | Idivf | Ispecific(Isubfrev | Idivfrev)
as floatop) ->
if i.arg.(0) = tos && i.arg.(1) = tos then
(* both operands on top of FP stack *)
` {emit_string(instr_for_floatop_pop floatop)} %st, %st(1)\n`
else if i.arg.(0) = tos then
(* first operand on stack *)
` {emit_string(instr_for_floatop floatop)} {emit_reg i.arg.(1)}\n`
else if i.arg.(1) = tos then
(* second operand on stack *)
` {emit_string(instr_for_floatop_reversed floatop)} {emit_reg i.arg.(0)}\n`
else begin
(* both operands in memory *)
` fldl {emit_reg i.arg.(0)}\n`;
` {emit_string(instr_for_floatop floatop)} {emit_reg i.arg.(1)}\n`
end
| Lop(Ifloatofint) ->
begin match i.arg.(0).loc with
Stack s ->
` fildl {emit_reg i.arg.(0)}\n`
| _ ->
` pushl {emit_reg i.arg.(0)}\n`;
` fildl (%esp)\n`;
` addl $4, %esp\n`
end
| Lop(Iintoffloat) ->
if i.arg.(0) <> tos then
` fldl {emit_reg i.arg.(0)}\n`;
stack_offset := !stack_offset - 8;
` subl $8, %esp\n`;
` fnstcw 4(%esp)\n`;
` movl 4(%esp), %eax\n`;
` movb $12, %ah\n`;
` movl %eax, (%esp)\n`;
` fldcw (%esp)\n`;
begin match i.res.(0).loc with
Stack s ->
` fistpl {emit_reg i.res.(0)}\n`
| _ ->
` fistpl (%esp)\n`;
` movl (%esp), {emit_reg i.res.(0)}\n`
end;
` fldcw 4(%esp)\n`;
` addl $8, %esp\n`;
stack_offset := !stack_offset + 8
| Lop(Ispecific(Ilea addr)) ->
` lea {emit_addressing addr i.arg 0}, {emit_reg i.res.(0)}\n`
| Lop(Ispecific(Istore_int(n, addr))) ->
` movl ${emit_int n}, {emit_addressing addr i.arg 0}\n`
| Lop(Ispecific(Istore_symbol(s, addr))) ->
` movl ${emit_symbol s}, {emit_addressing addr i.arg 0}\n`
| Lop(Ispecific(Ioffset_loc(n, addr))) ->
` addl ${emit_int n}, {emit_addressing addr i.arg 0}\n`
| Lop(Ispecific(Ipush)) ->
(* Push arguments in reverse order *)
for n = Array.length i.arg - 1 downto 0 do
let r = i.arg.(n) in
match r with
{loc = Reg _; typ = Float} ->
` subl $8, %esp\n`;
` fstpl 0(%esp)\n`;
stack_offset := !stack_offset + 8
| {loc = Stack sl; typ = Float} ->
let ofs = slot_offset sl 1 in
` pushl {emit_int(ofs + 4)}(%esp)\n`;
` pushl {emit_int(ofs + 4)}(%esp)\n`;
stack_offset := !stack_offset + 8
| _ ->
` pushl {emit_reg r}\n`;
stack_offset := !stack_offset + 4
done
| Lop(Ispecific(Ipush_int n)) ->
` pushl ${emit_int n}\n`;
stack_offset := !stack_offset + 4
| Lop(Ispecific(Ipush_symbol s)) ->
` pushl ${emit_symbol s}\n`;
stack_offset := !stack_offset + 4
| Lop(Ispecific(Ipush_load addr)) ->
` pushl {emit_addressing addr i.arg 0}\n`;
stack_offset := !stack_offset + 4
| Lop(Ispecific(Ipush_load_float addr)) ->
` pushl {emit_addressing (offset_addressing addr 4) i.arg 0}\n`;
` pushl {emit_addressing addr i.arg 0}\n`;
stack_offset := !stack_offset + 8
| Lop(Ispecific(Ifloatarithmem(op, addr))) ->
if i.arg.(0) <> tos then
` fldl {emit_reg i.arg.(0)}\n`;
` {emit_string(instr_for_floatarithmem op)} {emit_addressing addr i.arg 1}\n`
| Lreloadretaddr ->
()
| Lreturn ->
output_epilogue();
` ret\n`
| Llabel lbl ->
`{emit_label lbl}:\n`
| Lbranch lbl ->
` jmp {emit_label lbl}\n`
| Lcondbranch(tst, lbl) ->
begin match tst with
Itruetest ->
output_test_zero i.arg.(0);
` jne {emit_label lbl}\n`
| Ifalsetest ->
output_test_zero i.arg.(0);
` je {emit_label lbl}\n`
| Iinttest cmp ->
` cmpl {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`;
let b = name_for_cond_branch cmp in
` j{emit_string b} {emit_label lbl}\n`
| Iinttest_imm((Isigned Ceq | Isigned Cne |
Iunsigned Ceq | Iunsigned Cne) as cmp, 0) ->
output_test_zero i.arg.(0);
let b = name_for_cond_branch cmp in
` j{emit_string b} {emit_label lbl}\n`
| Iinttest_imm(cmp, n) ->
` cmpl ${emit_int n}, {emit_reg i.arg.(0)}\n`;
let b = name_for_cond_branch cmp in
` j{emit_string b} {emit_label lbl}\n`
| Ifloattest((Ceq | Cne as cmp), neg) ->
if i.arg.(1) <> tos then
` fldl {emit_reg i.arg.(1)}\n`;
if i.arg.(0) <> tos then
` fldl {emit_reg i.arg.(0)}\n`;
` fucompp\n`;
` fnstsw %ax\n`;
let neg1 = if cmp = Ceq then neg else not neg in
if neg1 then begin (* branch if different *)
` andb $68, %ah\n`;
` xorb $64, %ah\n`;
` jne {emit_label lbl}\n`
end else begin (* branch if equal *)
` andb $69, %ah\n`;
` cmpb $64, %ah\n`;
` je {emit_label lbl}\n`
end
| Ifloattest(cmp, neg) ->
let actual_cmp =
if i.arg.(0) = tos && i.arg.(1) = tos then begin
(* both args on top of FP stack *)
` fcompp\n`;
cmp
end else if i.arg.(0) = tos then begin
(* first arg on top of FP stack *)
` fcompl {emit_reg i.arg.(1)}\n`;
cmp
end else if i.arg.(1) = tos then begin
(* second arg on top of FP stack *)
` fcompl {emit_reg i.arg.(0)}\n`;
Cmm.swap_comparison cmp
end else begin
` fldl {emit_reg i.arg.(0)}\n`;
` fcompl {emit_reg i.arg.(1)}\n`;
cmp
end in
` fnstsw %ax\n`;
begin match actual_cmp with
Cle ->
` andb $69, %ah\n`;
` decb %ah\n`;
` cmpb $64, %ah\n`;
if neg
then ` jae `
else ` jb `
| Cge ->
` andb $5, %ah\n`;
if neg
then ` jne `
else ` je `
| Clt ->
` andb $69, %ah\n`;
` cmpb $1, %ah\n`;
if neg
then ` jne `
else ` je `
| Cgt ->
` andb $69, %ah\n`;
if neg
then ` jne `
else ` je `
| _ -> fatal_error "Emit_i386: floattest"
end;
`{emit_label lbl}\n`
| Ioddtest ->
` testl $1, {emit_reg i.arg.(0)}\n`;
` jne {emit_label lbl}\n`
| Ieventest ->
` testl $1, {emit_reg i.arg.(0)}\n`;
` je {emit_label lbl}\n`
end
| Lcondbranch3(lbl0, lbl1, lbl2) ->
` cmpl $1, {emit_reg i.arg.(0)}\n`;
begin match lbl0 with
None -> ()
| Some lbl -> ` jb {emit_label lbl}\n`
end;
begin match lbl1 with
None -> ()
| Some lbl -> ` je {emit_label lbl}\n`
end;
begin match lbl2 with
None -> ()
| Some lbl -> ` jg {emit_label lbl}\n`
end
| Lswitch jumptbl ->
let lbl = new_label() in
` jmp *{emit_label lbl}(, {emit_reg i.arg.(0)}, 4)\n`;
` .data\n`;
`{emit_label lbl}:`;
for i = 0 to Array.length jumptbl - 1 do
` .long {emit_label jumptbl.(i)}\n`
done;
` .text\n`
| Lsetuptrap lbl ->
` call {emit_label lbl}\n`
| Lpushtrap ->
` pushl {emit_symbol "caml_exception_pointer"}\n`;
` movl %esp, {emit_symbol "caml_exception_pointer"}\n`;
stack_offset := !stack_offset + 8
| Lpoptrap ->
` popl {emit_symbol "caml_exception_pointer"}\n`;
` addl $4, %esp\n`;
stack_offset := !stack_offset - 8
| Lraise ->
` movl {emit_symbol "caml_exception_pointer"}, %esp\n`;
` popl {emit_symbol "caml_exception_pointer"}\n`;
` ret\n`
let rec emit_all i =
match i.desc with Lend -> () | _ -> emit_instr i; emit_all i.next
(* Emission of the floating-point constants *)
let emit_float_constant (lbl, cst) =
` .data\n`;
`{emit_label lbl}: .double {emit_string cst}\n`
(* Emission of a function declaration *)
let fundecl fundecl =
function_name := fundecl.fun_name;
fastcode_flag := fundecl.fun_fast;
tailrec_entry_point := new_label();
stack_offset := 0;
float_constants := [];
call_gc_sites := [];
range_check_trap := 0;
` .text\n`;
emit_align 4;
` .globl {emit_symbol fundecl.fun_name}\n`;
`{emit_symbol fundecl.fun_name}:\n`;
let n = frame_size() - 4 in
if n > 0 then
` subl ${emit_int n}, %esp\n`;
`{emit_label !tailrec_entry_point}:\n`;
emit_all fundecl.fun_body;
List.iter emit_call_gc !call_gc_sites;
if !range_check_trap > 0 then
`{emit_label !range_check_trap}: call {emit_symbol "array_bound_error"}\n`;
(* Never returns, but useful to have retaddr on stack for debugging *)
List.iter emit_float_constant !float_constants
(* Emission of data *)
let emit_item = function
Cdefine_symbol s ->
` .globl {emit_symbol s}\n`;
`{emit_symbol s}:\n`
| Cdefine_label lbl ->
`{emit_label (10000 + lbl)}:\n`
| Cint8 n ->
` .byte {emit_int n}\n`
| Cint16 n ->
` {emit_string word_dir} {emit_int n}\n`
| Cint n ->
` .long {emit_nativeint n}\n`
| Cfloat f ->
` .double {emit_string f}\n`
| Csymbol_address s ->
` .long {emit_symbol s}\n`
| Clabel_address lbl ->
` .long {emit_label (10000 + lbl)}\n`
| Cstring s ->
if use_ascii_dir
then emit_string_directive " .ascii " s
else emit_bytes_directive " .byte " s
| Cskip n ->
if n > 0 then ` {emit_string skip_dir} {emit_int n}\n`
| Calign n ->
emit_align n
let data l =
` .data\n`;
List.iter emit_item l
(* Beginning / end of an assembly file *)
let begin_assembly() =
let lbl_begin = Compilenv.current_unit_name() ^ "_data_begin" in
` .data\n`;
` .globl {emit_symbol lbl_begin}\n`;
`{emit_symbol lbl_begin}:\n`;
let lbl_begin = Compilenv.current_unit_name() ^ "_code_begin" in
` .text\n`;
` .globl {emit_symbol lbl_begin}\n`;
`{emit_symbol lbl_begin}:\n`
let end_assembly() =
let lbl_end = Compilenv.current_unit_name() ^ "_code_end" in
` .text\n`;
` .globl {emit_symbol lbl_end}\n`;
`{emit_symbol lbl_end}:\n`;
` .data\n`;
let lbl_end = Compilenv.current_unit_name() ^ "_data_end" in
` .globl {emit_symbol lbl_end}\n`;
`{emit_symbol lbl_end}:\n`;
` .long 0\n`;
let lbl = Compilenv.current_unit_name() ^ "_frametable" in
` .globl {emit_symbol lbl}\n`;
`{emit_symbol lbl}:\n`;
` .long {emit_int (List.length !frame_descriptors)}\n`;
List.iter emit_frame !frame_descriptors;
frame_descriptors := []

View File

@ -1,730 +0,0 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Emission of Motorola 68020 assembly code (MIT syntax) *)
open Misc
open Cmm
open Arch
open Proc
open Reg
open Mach
open Linearize
open Emitaux
(* Tradeoff between code size and code speed *)
let fastcode_flag = ref true
let stack_offset = ref 0
(* Layout of the stack frame *)
let frame_size () = (* includes return address *)
!stack_offset +
4 * (num_stack_slots.(0) + num_stack_slots.(1)) +
8 * num_stack_slots.(2) +
4 (* return address *)
let slot_offset loc cl =
match loc with
Incoming n -> frame_size() + n
| Local n ->
if cl = 0
then !stack_offset + n * 4
else if cl = 1
then !stack_offset + num_stack_slots.(0) * 4 + n * 4
else !stack_offset +
(num_stack_slots.(0) + num_stack_slots.(1)) * 4 + n * 8
| Outgoing n -> n
(* Output a symbol *)
let emit_symbol s =
emit_char '_'; Emitaux.emit_symbol '$' s
(* Output a label *)
let emit_label lbl =
emit_char 'L'; emit_int lbl
(* Output an align directive *)
let emit_align n =
` .align {emit_int n}\n`
(* Output a pseudo-register *)
let emit_reg = function
{ loc = Reg r } ->
emit_string (register_name r)
| { loc = Stack s } as r ->
let ofs = slot_offset s (register_class r) in
if ofs = 0
then `a7@`
else `a7@({emit_int ofs})`
| { loc = Unknown } ->
fatal_error "Emit_m68k.emit_reg"
(* Check if the given register is an address register *)
let is_address_reg = function { loc = Reg _; typ = Addr } -> true | _ -> false
(* Check if the given register overlaps (same location) with the given
array of registers *)
let register_overlap reg arr =
try
for i = 0 to Array.length arr - 1 do
if reg.loc = arr.(i).loc then raise Exit
done;
false
with Exit ->
true
(* Output a suffix for a floating-point instruction -- either .x if
the argument is a register or .d if it's in memory. *)
let emit_float_size r =
match r.loc with
Reg _ -> `x`
| _ -> `d`
let emit_float_size2 r1 r2 =
match (r1.loc, r2.loc) with
(Reg _, Reg _) -> `x`
| _ -> `d`
(* Output an addressing mode *)
let emit_displacement d =
if d <> 0 then `{emit_int d}, `
let emit_addressing addr r n =
match addr with
Ibased(s, d) ->
`{emit_symbol s}`;
if d <> 0 then ` + {emit_int d}`
| Iindexed d ->
`{emit_reg r.(n)}@`;
if d <> 0 then `({emit_int d})`
| Iindexed2 d ->
`{emit_reg r.(n)}@({emit_int d}, {emit_reg r.(n+1)}:l)`
| Iscaled(scale, d) ->
`@({emit_int d}, {emit_reg r.(n)}:l:{emit_int scale})`
| Iindexed2scaled(scale, d) ->
`{emit_reg r.(n)}@({emit_int d}, {emit_reg r.(n+1)}:l:{emit_int scale})`
(* Record live pointers at call points *)
type frame_descr =
{ fd_lbl: int; (* Return address *)
fd_frame_size: int; (* Size of stack frame *)
fd_live_offset: int list } (* Offsets/regs of live addresses *)
let frame_descriptors = ref([] : frame_descr list)
let record_frame_label live =
let lbl = new_label() in
let live_offset = ref [] in
Reg.Set.iter
(function
{typ = Addr; loc = Reg r} ->
live_offset := ((r lsl 1) + 1) :: !live_offset
| {typ = Addr; loc = Stack s} as reg ->
live_offset := slot_offset s (register_class reg) :: !live_offset
| _ -> ())
live;
frame_descriptors :=
{ fd_lbl = lbl;
fd_frame_size = frame_size();
fd_live_offset = !live_offset } :: !frame_descriptors;
lbl
let record_frame live =
let lbl = record_frame_label live in `{emit_label lbl}:\n`
let emit_frame fd =
` .long {emit_label fd.fd_lbl}\n`;
` .word {emit_int fd.fd_frame_size}\n`;
` .word {emit_int (List.length fd.fd_live_offset)}\n`;
List.iter
(fun n ->
` .word {emit_int n}\n`)
fd.fd_live_offset;
emit_align 4
(* Names for instructions *)
let instr_for_intop = function
Iadd -> "addl"
| Isub -> "subl"
| Imul -> "mulsl"
| Idiv -> "divsl"
| Iand -> "andl"
| Ior -> "orl"
| Ixor -> "eorl"
| Ilsl -> "lsll"
| Ilsr -> "lsrl"
| Iasr -> "asrl"
| _ -> fatal_error "Emit_m68k: instr_for_intop"
let instr_for_floatop = function
Inegf -> "fneg"
| Iabsf -> "fabs"
| Iaddf -> "fadd"
| Isubf -> "fsub"
| Imulf -> "fmul"
| Idivf -> "fdiv"
| _ -> fatal_error "Emit_m68k: instr_for_floatop"
let name_for_cond_branch = function
Isigned Ceq -> "eq" | Isigned Cne -> "ne"
| Isigned Cle -> "le" | Isigned Cgt -> "gt"
| Isigned Clt -> "lt" | Isigned Cge -> "ge"
| Iunsigned Ceq -> "eq" | Iunsigned Cne -> "ne"
| Iunsigned Cle -> "ls" | Iunsigned Cgt -> "hi"
| Iunsigned Clt -> "cs" | Iunsigned Cge -> "cc"
let name_for_float_cond_branch cond neg =
match cond with
Ceq -> if neg then "ne" else "eq"
| Cne -> if neg then "eq" else "ne"
| Cle -> if neg then "ugt" else "ole"
| Cgt -> if neg then "ule" else "ogt"
| Clt -> if neg then "uge" else "olt"
| Cge -> if neg then "ult" else "oge"
(* Emit an immediate move in the given data register *)
let emit_move_immediate n dreg =
if n >= -128 && n < 128
then ` moveq #{emit_int n}, {emit_string dreg}\n`
else ` movel #{emit_int n}, {emit_string dreg}\n`
(* Offset the stack by the given amount of bytes *)
let output_stack_offset n =
if n > 0 && n <= 8 then
` subql #{emit_int(n)}, a7\n`
else if n < 0 && n >= -8 then
` addql #{emit_int(-n)}, a7\n`
else
` addw #{emit_int(-n)}, a7\n`
(* Deallocate the stack frame before a return or tail call *)
let output_epilogue () =
let n = frame_size() - 4 in
if n > 0 then output_stack_offset (-n)
(* Record the state of the condition codes *)
type condition_code = CCundefined | CCreflect of Reg.t
let cc_state = ref CCundefined
let undef_cc () =
cc_state := CCundefined
let set_cc reg =
cc_state := CCreflect reg
let output_test reg =
match !cc_state with
CCreflect r when r.loc = reg.loc -> ()
| _ ->
` tstl {emit_reg reg}\n`;
cc_state := CCreflect reg
(* Output the assembly code for an instruction *)
(* Name of current function *)
let function_name = ref ""
(* Entry point for tail recursive calls *)
let tailrec_entry_point = ref 0
(* Label of trap for out-of-range accesses *)
let range_check_trap = ref 0
let emit_instr i =
match i.desc with
Lend -> ()
| Lop(Imove | Ispill | Ireload) ->
let src = i.arg.(0) and dst = i.res.(0) in
if src.loc <> dst.loc then begin
match (src, dst) with
({typ = Float; loc = Stack ss}, {loc = Stack sd}) ->
let os = slot_offset ss 2 in
let od = slot_offset sd 2 in
` movel ({emit_int os}, a7), ({emit_int od}, a7)\n`;
` movel ({emit_int (os+4)}, a7), ({emit_int (od+4)}, a7)\n`;
undef_cc()
| ({typ = Float}, _) ->
` fmove{emit_float_size2 src dst} {emit_reg src}, {emit_reg dst}\n`
| (_, _) ->
` movel {emit_reg src}, {emit_reg dst}\n`;
set_cc dst
end
| Lop(Iconst_int n) ->
begin match i.res.(0) with
{typ = Addr; loc = Reg _} ->
if Nativeint.cmp n (-32768) >= 0 && Nativeint.cmp n 32768 < 0 then
` movew #{emit_nativeint n}, {emit_reg i.res.(0)}\n`
else
` movel #{emit_nativeint n}, {emit_reg i.res.(0)}\n`
| _ when Nativeint.sign n = 0 ->
` clrl {emit_reg i.res.(0)}\n`;
set_cc i.res.(0)
| {typ = Int; loc = Reg _}
when Nativeint.cmp n (-128) >= 0 && Nativeint.cmp n 128 < 0 ->
` moveq #{emit_nativeint n}, {emit_reg i.res.(0)}\n`;
set_cc i.res.(0)
| _ ->
` movel #{emit_nativeint n}, {emit_reg i.res.(0)}\n`;
set_cc i.res.(0)
end
| Lop(Iconst_float s) ->
let f = float_of_string s in
if f = 0.0 then
` fmovecr #0x0F, {emit_reg i.res.(0)}\n`
else if f = 1.0 then
` fmovecr #0x32, {emit_reg i.res.(0)}\n`
else
` fmoved #0r{emit_string s}, {emit_reg i.res.(0)}\n`
| Lop(Iconst_symbol s) ->
` lea {emit_symbol s}, {emit_reg i.res.(0)}\n`
| Lop(Icall_ind) ->
` jbsr {emit_reg i.arg.(0)}@\n`;
record_frame i.live;
undef_cc()
| Lop(Icall_imm s) ->
` jbsr {emit_symbol s}\n`;
record_frame i.live;
undef_cc()
| Lop(Itailcall_ind) ->
output_epilogue();
` jmp {emit_reg i.arg.(0)}@\n`;
undef_cc()
| Lop(Itailcall_imm s) ->
if s = !function_name then
` bra {emit_label !tailrec_entry_point}\n`
else begin
output_epilogue();
` jmp {emit_symbol s}\n`
end;
undef_cc()
| Lop(Iextcall(s, alloc)) ->
if alloc then begin
` lea {emit_symbol s}, a0\n`;
` jbsr {emit_symbol "caml_c_call"}\n`;
record_frame i.live
end else begin
` jbsr {emit_symbol s}\n`
end;
if Array.length i.res > 0 && i.res.(0).typ = Float then begin
` movel d1, a7@-\n`;
` movel d0, a7@-\n`;
` fmoved a7@+, {emit_reg i.res.(0)}\n`
end;
undef_cc()
| Lop(Istackoffset n) ->
output_stack_offset n;
stack_offset := !stack_offset + n;
undef_cc()
| Lop(Iload(chunk, addr)) ->
let dest = i.res.(0) in
begin match dest.typ with
Int | Addr ->
begin match chunk with
Word ->
` movel {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
| Byte_unsigned when not (register_overlap dest i.arg) ->
` clrl {emit_reg dest}\n`;
` moveb {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
| Byte_unsigned ->
` moveb {emit_addressing addr i.arg 0}, {emit_reg dest}\n`;
` andl #0xFF, {emit_reg dest}\n`
| Byte_signed ->
` moveb {emit_addressing addr i.arg 0}, {emit_reg dest}\n`;
` extbl {emit_reg dest}\n`
| Sixteen_unsigned when not (register_overlap dest i.arg) ->
` clrl {emit_reg dest}\n`;
` movew {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
| Sixteen_unsigned ->
` movew {emit_addressing addr i.arg 0}, {emit_reg dest}\n`;
` andl #0xFFFF, {emit_reg dest}\n`
| Sixteen_signed ->
` movew {emit_addressing addr i.arg 0}, {emit_reg dest}\n`;
` extl {emit_reg dest}\n`
end;
set_cc dest
| Float ->
` fmoved {emit_addressing addr i.arg 0}, {emit_reg dest}\n`
end
| Lop(Istore(chunk, addr)) ->
let src = i.arg.(0) in
let instr =
match src.typ with
Int ->
begin match chunk with
Word -> "movel"
| Byte_unsigned | Byte_signed -> "moveb"
| Sixteen_unsigned | Sixteen_signed -> "movew"
end
| Addr -> "movel"
| Float -> "fmoved" in
` {emit_string instr} {emit_reg src}, {emit_addressing addr i.arg 1}\n`;
undef_cc()
| Lop(Ialloc n) ->
if !fastcode_flag then begin
let lbl_frame = record_frame_label i.live in
` subl #{emit_int n}, d6\n`;
` cmpl {emit_symbol "young_limit"}, d6\n`;
` bcc {emit_label lbl_frame}\n`;
emit_move_immediate n "d5";
` jbsr {emit_symbol "caml_call_gc"}\n`;
`{emit_label lbl_frame}: movel d6, {emit_reg i.res.(0)}\n`;
` addql #4, {emit_reg i.res.(0)}\n`
end else begin
begin match n with
8 -> ` jbsr {emit_symbol "caml_alloc1"}\n`
| 12 -> ` jbsr {emit_symbol "caml_alloc2"}\n`
| 16 -> ` jbsr {emit_symbol "caml_alloc3"}\n`
| _ -> emit_move_immediate n "d5";
` jbsr {emit_symbol "caml_alloc"}\n`
end;
`{record_frame i.live} movel d6, {emit_reg i.res.(0)}\n`;
` addql #4, {emit_reg i.res.(0)}\n`
end;
undef_cc()
| Lop(Iintop(Icomp cmp)) ->
` cmpl {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`;
let b = name_for_cond_branch cmp in
` s{emit_string b} {emit_reg i.res.(0)}\n`;
` negb {emit_reg i.res.(0)}\n`;
` extbl {emit_reg i.res.(0)}\n`;
set_cc i.res.(0)
| Lop(Iintop_imm(Icomp cmp, n)) ->
` cmpl #{emit_int n}, {emit_reg i.arg.(0)}\n`;
let b = name_for_cond_branch cmp in
` s{emit_string b} {emit_reg i.res.(0)}\n`;
` negb {emit_reg i.res.(0)}\n`;
` extbl {emit_reg i.res.(0)}\n`;
set_cc i.res.(0)
| Lop(Iintop Icheckbound) ->
if !range_check_trap = 0 then range_check_trap := new_label();
` cmpl {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`;
` bls {emit_label !range_check_trap}\n`
| Lop(Iintop_imm(Icheckbound, n)) ->
if !range_check_trap = 0 then range_check_trap := new_label();
` cmpl #{emit_int n}, {emit_reg i.arg.(0)}\n`;
` bls {emit_label !range_check_trap}\n`
| Lop(Iintop_imm(Iadd, n)) ->
let dest = i.res.(0) in
begin match dest with
{loc = Reg _} when n > 0 && n <= 8 ->
` addql #{emit_int n}, {emit_reg dest}\n`;
set_cc i.res.(0)
| {loc = Reg _} when n < 0 && n >= -8 ->
` subql #{emit_int(-n)}, {emit_reg dest}\n`;
set_cc i.res.(0)
| _ ->
` addl #{emit_int n}, {emit_reg dest}\n`;
set_cc i.res.(0)
end
| Lop(Iintop_imm(Isub, n)) ->
let dest = i.res.(0) in
begin match dest with
{loc = Reg _} when n > 0 && n <= 8 ->
` subql #{emit_int n}, {emit_reg dest}\n`;
set_cc i.res.(0)
| {loc = Reg _} when n < 0 && n >= -8 ->
` addql #{emit_int(-n)}, {emit_reg dest}\n`;
set_cc i.res.(0)
| _ ->
` subl #{emit_int n}, {emit_reg dest}\n`;
set_cc i.res.(0)
end
| Lop(Iintop_imm(Idiv, n)) when n = 1 lsl (Misc.log2 n) ->
let l = Misc.log2 n in
let lbl = new_label() in
output_test i.arg.(0);
` bge {emit_label lbl}\n`;
` addl #{emit_int(n-1)}, {emit_reg i.arg.(0)}\n`;
`{emit_label lbl}:`;
if l <= 8 then
` asrl #{emit_int l}, {emit_reg i.arg.(0)}\n`
else begin
` moveq #{emit_int l}, d5\n`;
` asrl d0, {emit_reg i.arg.(0)}\n`
end;
set_cc i.res.(0)
| Lop(Iintop Imod) ->
` movel {emit_reg i.arg.(0)}, d5\n`;
` divsll {emit_reg i.arg.(1)}, {emit_reg i.res.(0)}:d5\n`;
undef_cc()
| Lop(Iintop_imm(Imod, n)) when n = 1 lsl (Misc.log2 n) ->
let l = Misc.log2 n in
let lbl = new_label() in
` movel {emit_reg i.arg.(0)}, d5\n`;
` bge {emit_label lbl}\n`;
` addl #{emit_int(n-1)}, d5\n`;
`{emit_label lbl}: andl #{emit_int(-n)}, d5\n`;
` subl d5, {emit_reg i.arg.(0)}\n`;
set_cc i.res.(0)
| Lop(Iintop_imm(Imod, n)) ->
` movel {emit_reg i.arg.(0)}, d5\n`;
` divsll #{emit_int n}, {emit_reg i.res.(0)}:d5\n`;
undef_cc()
| Lop(Iintop op) ->
(* We have i.arg.(0) = i.res.(0) *)
` {emit_string(instr_for_intop op)} {emit_reg i.arg.(1)}, {emit_reg i.res.(0)}\n`;
set_cc i.res.(0)
| Lop(Iintop_imm(op, n)) ->
(* We have i.arg.(0) = i.res.(0) *)
` {emit_string(instr_for_intop op)} #{emit_int n}, {emit_reg i.res.(0)}\n`;
set_cc i.res.(0)
| Lop(Inegf | Iabsf as floatop) ->
` {emit_string(instr_for_floatop floatop)}{emit_float_size i.arg.(0)} {emit_reg i.arg.(0)}, {emit_reg i.res.(0)}\n`
| Lop(Iaddf | Isubf | Imulf | Idivf as floatop) ->
` {emit_string(instr_for_floatop floatop)}{emit_float_size i.arg.(1)} {emit_reg i.arg.(1)}, {emit_reg i.res.(0)}\n`
| Lop(Ifloatofint) ->
` fmovel {emit_reg i.arg.(0)}, {emit_reg i.res.(0)}\n`
| Lop(Iintoffloat) ->
` fintrz{emit_float_size i.arg.(0)} {emit_reg i.arg.(0)}, fp0\n`;
` fmovel fp0, {emit_reg i.res.(0)}\n`;
undef_cc()
| Lop(Ispecific(Ilea addr)) ->
` lea {emit_addressing addr i.arg 0}, {emit_reg i.res.(0)}\n`
| Lop(Ispecific(Istore_int(n, addr))) ->
if n = 0 then
` clrl {emit_addressing addr i.arg 0}\n`
else
` movel #{emit_int n}, {emit_addressing addr i.arg 0}\n`;
undef_cc()
| Lop(Ispecific(Istore_symbol(s, addr))) ->
` movel #{emit_symbol s}, {emit_addressing addr i.arg 0}\n`;
undef_cc()
| Lop(Ispecific(Ipush)) ->
(* Push arguments in reverse order *)
for n = Array.length i.arg - 1 downto 0 do
let r = i.arg.(n) in
match r with
{loc = Reg _; typ = Float} ->
` fmoved {emit_reg r}, a7@-\n`;
stack_offset := !stack_offset + 8
| {loc = Stack sl; typ = Float} ->
let ofs = slot_offset sl 2 in
` movel ({emit_int(ofs + 4)}, a7), a7@-\n`;
` movel ({emit_int(ofs + 4)}, a7), a7@-\n`;
stack_offset := !stack_offset + 8
| _ ->
` movel {emit_reg r}, a7@-\n`;
stack_offset := !stack_offset + 4
done;
undef_cc()
| Lop(Ispecific(Ipush_int n)) ->
` movel #{emit_int n}, a7@-\n`;
stack_offset := !stack_offset + 4;
undef_cc()
| Lop(Ispecific(Ipush_symbol s)) ->
` pea {emit_symbol s}\n`;
stack_offset := !stack_offset + 4;
undef_cc()
| Lop(Ispecific(Ipush_load addr)) ->
` movel {emit_addressing addr i.arg 0}, a7@-\n`;
stack_offset := !stack_offset + 4;
undef_cc()
| Lop(Ispecific(Ipush_load_float addr)) ->
` movel {emit_addressing (offset_addressing addr 4) i.arg 0}, a7@-\n`;
` movel {emit_addressing addr i.arg 0}, a7@-\n`;
stack_offset := !stack_offset + 8;
undef_cc()
| Lreloadretaddr ->
()
| Lreturn ->
output_epilogue();
` rts\n`;
undef_cc()
| Llabel lbl ->
`{emit_label lbl}:\n`;
undef_cc()
| Lbranch lbl ->
` bra {emit_label lbl}\n`
| Lcondbranch(tst, lbl) ->
begin match tst with
Itruetest ->
output_test i.arg.(0);
` bne {emit_label lbl}\n`
| Ifalsetest ->
output_test i.arg.(0);
` beq {emit_label lbl}\n`
| Iinttest cmp ->
` cmpl {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`;
let b = name_for_cond_branch cmp in
` b{emit_string b} {emit_label lbl}\n`
| Iinttest_imm(cmp, 0) ->
output_test i.arg.(0);
let b = name_for_cond_branch cmp in
` b{emit_string b} {emit_label lbl}\n`
| Iinttest_imm(cmp, n) ->
` cmpl #{emit_int n}, {emit_reg i.arg.(0)}\n`;
let b = name_for_cond_branch cmp in
` b{emit_string b} {emit_label lbl}\n`
| Ifloattest(cmp, neg) ->
` fcmp{emit_float_size2 i.arg.(0) i.arg.(1)} {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`;
let b = name_for_float_cond_branch cmp neg in
` fb{emit_string b} {emit_label lbl}\n`
| Ioddtest ->
begin match i.arg.(0) with
{typ = Addr; loc = Reg _} as arg ->
` movel {emit_reg arg}, d5\n`;
` btst #0, d5\n`
| arg ->
` btst #0, {emit_reg arg}\n`
end;
` bne {emit_label lbl}\n`
| Ieventest ->
begin match i.arg.(0) with
{typ = Addr; loc = Reg _} as arg ->
` movel {emit_reg arg}, d5\n`;
` btst #0, d5\n`
| arg ->
` btst #0, {emit_reg arg}\n`
end;
` beq {emit_label lbl}\n`
end;
undef_cc()
| Lcondbranch3(lbl0, lbl1, lbl2) ->
` cmpl #1, {emit_reg i.arg.(0)}\n`;
begin match lbl0 with
None -> ()
| Some lbl -> ` blt {emit_label lbl}\n`
end;
begin match lbl1 with
None -> ()
| Some lbl -> ` beq {emit_label lbl}\n`
end;
begin match lbl2 with
None -> ()
| Some lbl -> ` bgt {emit_label lbl}\n`
end;
undef_cc()
| Lswitch jumptbl ->
let lbl_load = new_label() in
let lbl_table = new_label() in
`{emit_label lbl_load}: movew pc@({emit_label lbl_table}-{emit_label lbl_load}-2:b, {emit_reg i.arg.(0)}:l:2), d0\n`;
` jmp pc@(2, d0:w)\n`;
`{emit_label lbl_table}:`;
for i = 0 to Array.length jumptbl - 1 do
` .word {emit_label jumptbl.(i)} - {emit_label lbl_table}\n`
done;
undef_cc()
| Lsetuptrap lbl ->
` bsr {emit_label lbl}\n`
| Lpushtrap ->
` movel d7, a7@-\n`;
` movel a7, d7\n`;
stack_offset := !stack_offset + 8;
undef_cc()
| Lpoptrap ->
` movel a7@+, d7\n`;
` addql #4, a7\n`;
stack_offset := !stack_offset - 8;
undef_cc()
| Lraise ->
` movel d7, a7\n`;
` movel a7@+, d7\n`;
` rts\n`
let rec emit_all i =
match i.desc with Lend -> () | _ -> emit_instr i; emit_all i.next
(* Emission of a function declaration *)
let fundecl fundecl =
function_name := fundecl.fun_name;
fastcode_flag := fundecl.fun_fast;
tailrec_entry_point := new_label();
stack_offset := 0;
range_check_trap := 0;
undef_cc();
` .text\n`;
` .globl {emit_symbol fundecl.fun_name}\n`;
`{emit_symbol fundecl.fun_name}:\n`;
let n = frame_size() - 4 in
if n > 0 then output_stack_offset n;
`{emit_label !tailrec_entry_point}:\n`;
emit_all fundecl.fun_body;
if !range_check_trap > 0 then
`{emit_label !range_check_trap}: jbsr {emit_symbol "array_bound_error"}\n`
(* Emission of data *)
let emit_item = function
Cdefine_symbol s ->
` .globl {emit_symbol s}\n`;
`{emit_symbol s}:\n`
| Cdefine_label lbl ->
`{emit_label (10000 + lbl)}:\n`
| Cint8 n ->
` .byte {emit_int n}\n`
| Cint16 n ->
` .word {emit_int n}\n`
| Cint n ->
` .long {emit_nativeint n}\n`
| Cfloat f ->
` .double 0r{emit_string f}\n`
| Csymbol_address s ->
` .long {emit_symbol s}\n`
| Clabel_address lbl ->
` .long {emit_label (10000 + lbl)}\n`
| Cstring s ->
emit_string_directive " .ascii " s
| Cskip n ->
if n > 0 then ` .skip {emit_int n}\n`
| Calign n ->
emit_align n
let data l =
` .data\n`;
List.iter emit_item l
(* Beginning / end of an assembly file *)
let begin_assembly() =
let lbl_begin = Compilenv.current_unit_name() ^ "_data_begin" in
` .data\n`;
` .globl {emit_symbol lbl_begin}\n`;
`{emit_symbol lbl_begin}:\n`;
let lbl_begin = Compilenv.current_unit_name() ^ "_code_begin" in
` .text\n`;
` .globl {emit_symbol lbl_begin}\n`;
`{emit_symbol lbl_begin}:\n`
let end_assembly() =
` .text\n`;
let lbl_end = Compilenv.current_unit_name() ^ "_code_end" in
` .globl {emit_symbol lbl_end}\n`;
`{emit_symbol lbl_end}:\n`;
` .data\n`;
let lbl_end = Compilenv.current_unit_name() ^ "_data_end" in
` .globl {emit_symbol lbl_end}\n`;
`{emit_symbol lbl_end}:\n`;
` .long 0\n`;
let lbl = Compilenv.current_unit_name() ^ "_frametable" in
` .globl {emit_symbol lbl}\n`;
`{emit_symbol lbl}:\n`;
` .long {emit_int (List.length !frame_descriptors)}\n`;
List.iter emit_frame !frame_descriptors;
frame_descriptors := []

View File

@ -1,634 +0,0 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Emission of Mips assembly code *)
open Misc
open Cmm
open Arch
open Proc
open Reg
open Mach
open Linearize
open Emitaux
(* Determine whether we're emitting PIC code (IRIX -32 model)
or absolute code *)
let pic =
match Config.system with
"ultrix" -> false
| "irix" -> true
| _ -> fatal_error "Emit_mips.pic"
(* Tradeoff between code size and code speed *)
let fastcode_flag = ref true
(* Output a label *)
let emit_label lbl =
emit_string "$"; emit_int lbl
(* Output a symbol *)
let emit_symbol s =
Emitaux.emit_symbol '$' s
(* Output a pseudo-register *)
let emit_reg r =
match r.loc with
Reg r -> emit_string (register_name r)
| _ -> fatal_error "Emit_mips.emit_reg"
(* Output the other half of a floating-point pseudo-register *)
let float_reg_twin_name = [|
(* 100-104 *) "$f1"; "$f3"; "$f5"; "$f7"; "$f9";
(* 105-108 *) "$f13"; "$f15"; "$f17"; "$f19";
(* 109-114 *) "$f21"; "$f23"; "$f25"; "$f27"; "$f29"; "$f31"
|]
let emit_twin_reg = function
{ loc = Reg r; typ = Float } -> emit_string (float_reg_twin_name.(r - 100))
| _ -> fatal_error "Emit_mips.emit_twin_reg"
let emit_lower_reg = if big_endian then emit_twin_reg else emit_reg
let emit_upper_reg = if big_endian then emit_reg else emit_twin_reg
(* Record if $gp is needed (in PIC mode) *)
let uses_gp = ref false
(* Layout of the stack frame *)
let stack_offset = ref 0
let frame_size () =
let size =
!stack_offset +
4 * num_stack_slots.(0) + 8 * num_stack_slots.(1) +
(if !contains_calls then if !uses_gp then 8 else 4 else 0) in
Misc.align size 8
let slot_offset loc cl =
match loc with
Incoming n -> frame_size() + n
| Local n ->
if cl = 0
then !stack_offset + num_stack_slots.(1) * 8 + n * 4
else !stack_offset + n * 8
| Outgoing n -> n
(* Output a stack reference *)
let emit_stack r =
match r.loc with
Stack s ->
let ofs = slot_offset s (register_class r) in `{emit_int ofs}($sp)`
| _ -> fatal_error "Emit_mips.emit_stack"
(* Output an addressing mode *)
let emit_addressing addr r n =
match addr with
Iindexed ofs ->
`{emit_int ofs}({emit_reg r.(n)})`
| Ibased(s, 0) ->
`{emit_symbol s}`
| Ibased(s, ofs) ->
`{emit_symbol s} + {emit_int ofs}`
(* Communicate live registers at call points to the assembler *)
let int_reg_number = [|
2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21
|]
let float_reg_number = [|
0; 2; 4; 6; 8; 12; 14; 16; 18; 20; 22; 24; 26; 28; 30
|]
let liveregs instr extra_msk =
(* The .livereg directive is not supported by old Ultrix versions of
the MIPS assembler... *)
if pic then begin
(* $22, $23, $30 always live *)
let int_mask = ref(0x00000302 lor extra_msk)
and float_mask = ref 0 in
let add_register = function
{loc = Reg r; typ = (Int | Addr)} ->
int_mask :=
!int_mask lor (1 lsl (31 - int_reg_number.(r)))
| {loc = Reg r; typ = Float} ->
float_mask :=
!float_mask lor (3 lsl (31 - float_reg_number.(r - 100)))
| _ -> () in
Reg.Set.iter add_register instr.live;
Array.iter add_register instr.arg;
emit_printf " .livereg 0x%08x, 0x%08x\n" !int_mask !float_mask
end
let live_24 = 1 lsl (31 - 24)
(* Record live pointers at call points *)
type frame_descr =
{ fd_lbl: int; (* Return address *)
fd_frame_size: int; (* Size of stack frame *)
fd_live_offset: int list } (* Offsets/regs of live addresses *)
let frame_descriptors = ref([] : frame_descr list)
let record_frame live =
let lbl = new_label() in
let live_offset = ref [] in
Reg.Set.iter
(function
{typ = Addr; loc = Reg r} ->
live_offset := ((int_reg_number.(r) lsl 1) + 1) :: !live_offset
| {typ = Addr; loc = Stack s} as reg ->
live_offset := slot_offset s (register_class reg) :: !live_offset
| _ -> ())
live;
frame_descriptors :=
{ fd_lbl = lbl;
fd_frame_size = frame_size();
fd_live_offset = !live_offset } :: !frame_descriptors;
`{emit_label lbl}:`
let emit_frame fd =
` .word {emit_label fd.fd_lbl}\n`;
` .half {emit_int fd.fd_frame_size}\n`;
` .half {emit_int (List.length fd.fd_live_offset)}\n`;
List.iter
(fun n ->
` .half {emit_int n}\n`)
fd.fd_live_offset;
` .align 2\n`
(* In PIC mode, determine if $gp is used in the function *)
let rec instr_uses_gp i =
match i.desc with
Lend -> false
| Lop(Iconst_symbol s) -> true
| Lop(Icall_imm s) -> true
| Lop(Itailcall_imm s) -> true
| Lop(Iextcall(_, _)) -> true
| Lop(Iload(_, Ibased(_, _))) -> true
| Lop(Istore(_, Ibased(_, _))) -> true
| Lop(Ialloc _) -> true
| Lop(Iintop(Icheckbound)) -> true
| Lop(Iintop_imm(Icheckbound, _)) -> true
| Lswitch jumptbl -> true
| _ -> instr_uses_gp i.next
(* Emit code to reload $gp after a jal *)
let reload_gp () =
if !uses_gp then
` lw $gp, {emit_int(frame_size() - 8)}($sp)\n`
(* Emit a branch to an external symbol. *)
let emit_branch_symbol s =
if not pic then
` j {emit_symbol s}\n`
else begin
` la $25, {emit_symbol s}\n`;
` j $25\n`
end
(* Names of various instructions *)
let name_for_comparison = function
Isigned Ceq -> "eq" | Isigned Cne -> "ne" | Isigned Cle -> "le"
| Isigned Cge -> "ge" | Isigned Clt -> "lt" | Isigned Cgt -> "gt"
| Iunsigned Ceq -> "eq" | Iunsigned Cne -> "ne" | Iunsigned Cle -> "leu"
| Iunsigned Cge -> "geu" | Iunsigned Clt -> "ltu" | Iunsigned Cgt -> "gtu"
let name_for_float_comparison cmp neg =
match cmp with
Ceq -> ("eq", neg) | Cne -> ("eq", not neg)
| Cle -> ("le", neg) | Cge -> ("ult", not neg)
| Clt -> ("lt", neg) | Cgt -> ("ule", not neg)
let name_for_int_operation = function
Iadd -> "addu"
| Isub -> "subu"
| Imul -> "mul"
| Idiv -> "div"
| Imod -> "rem"
| Iand -> "and"
| Ior -> "or"
| Ixor -> "xor"
| Ilsl -> "sll"
| Ilsr -> "srl"
| Iasr -> "sra"
| Icomp cmp -> "s" ^ name_for_comparison cmp
| _ -> Misc.fatal_error "Emit.name_for_int_operation"
let name_for_float_operation = function
Inegf -> "neg.d"
| Iabsf -> "abs.d"
| Iaddf -> "add.d"
| Isubf -> "sub.d"
| Imulf -> "mul.d"
| Idivf -> "div.d"
| _ -> Misc.fatal_error "Emit.name_for_float_operation"
(* Output the assembly code for an instruction *)
(* Name of current function *)
let function_name = ref ""
(* Entry point for tail recursive calls *)
let tailrec_entry_point = ref 0
(* Label of jump to caml_call_gc *)
let call_gc_label = ref 0
(* Label of trap for out-of-range accesses *)
let range_check_trap = ref 0
let emit_instr i =
match i.desc with
Lend -> ()
| Lop(Imove | Ispill | Ireload) ->
let src = i.arg.(0) and dst = i.res.(0) in
if src.loc <> dst.loc then begin
match (src, dst) with
{loc = Reg rs; typ = Int|Addr}, {loc = Reg rd; typ = Int|Addr} ->
` move {emit_reg dst}, {emit_reg src}\n`
| {loc = Reg rs; typ = Float}, {loc = Reg rd; typ = Float} ->
` mov.d {emit_reg dst}, {emit_reg src}\n`
| {loc = Reg rs; typ = Float}, {loc = Reg rd; typ = Int|Addr} ->
` mfc1.d {emit_reg dst}, {emit_reg src}\n`
| {loc = Reg rs; typ = Int|Addr}, {loc = Stack sd} ->
` sw {emit_reg src}, {emit_stack dst}\n`
| {loc = Reg rs; typ = Float}, {loc = Stack sd} ->
` s.d {emit_reg src}, {emit_stack dst}\n`
| {loc = Stack ss; typ = Int|Addr}, {loc = Reg rd} ->
` lw {emit_reg dst}, {emit_stack src}\n`
| {loc = Stack ss; typ = Float}, {loc = Reg rd} ->
` l.d {emit_reg dst}, {emit_stack src}\n`
| _ ->
fatal_error "Emit_mips: Imove"
end
| Lop(Iconst_int n) ->
if Nativeint.sign n = 0 then
` move {emit_reg i.res.(0)}, $0\n`
else
` li {emit_reg i.res.(0)}, {emit_nativeint n}\n`
| Lop(Iconst_float s) ->
` li.d {emit_reg i.res.(0)}, {emit_string s}\n`
| Lop(Iconst_symbol s) ->
` la {emit_reg i.res.(0)}, {emit_symbol s}\n`
| Lop(Icall_ind) ->
liveregs i 0;
if pic then
` move $25, {emit_reg i.arg.(0)}\n`;
` jal {emit_reg i.arg.(0)}\n`;
`{record_frame i.live}\n`;
reload_gp()
| Lop(Icall_imm s) ->
liveregs i 0;
` jal {emit_symbol s}\n`;
`{record_frame i.live}\n`;
reload_gp()
| Lop(Itailcall_ind) ->
let n = frame_size() in
if !contains_calls then
` lw $31, {emit_int(n - 4)}($sp)\n`;
if n > 0 then
` addu $sp, $sp, {emit_int n}\n`;
liveregs i 0;
if pic then
` move $25, {emit_reg i.arg.(0)}\n`;
` j {emit_reg i.arg.(0)}\n`
| Lop(Itailcall_imm s) ->
if s = !function_name then begin
` b {emit_label !tailrec_entry_point}\n`
end else begin
let n = frame_size() in
if !contains_calls then
` lw $31, {emit_int(n - 4)}($sp)\n`;
if n > 0 then
` addu $sp, $sp, {emit_int n}\n`;
liveregs i 0;
emit_branch_symbol s
end
| Lop(Iextcall(s, alloc)) ->
if alloc then begin
` la $24, {emit_symbol s}\n`;
liveregs i live_24;
` jal caml_c_call\n`;
`{record_frame i.live}\n`
end else begin
` jal {emit_symbol s}\n`
end;
reload_gp()
| Lop(Istackoffset n) ->
if n >= 0 then
` subu $sp, $sp, {emit_int n}\n`
else
` addu $sp, $sp, {emit_int (-n)}\n`;
stack_offset := !stack_offset + n
| Lop(Iload(chunk, addr)) ->
begin match i.res.(0).typ with
Int | Addr ->
let load_instr =
match chunk with
Word -> "lw"
| Byte_unsigned -> "lbu"
| Byte_signed -> "lb"
| Sixteen_unsigned -> "lhu"
| Sixteen_signed -> "lh" in
` {emit_string load_instr} {emit_reg i.res.(0)}, {emit_addressing addr i.arg 0}\n`
| Float ->
(* Destination is not necessarily 8-aligned, hence better not use l.d *)
` lwc1 {emit_lower_reg i.res.(0)}, {emit_addressing addr i.arg 0}\n`;
` lwc1 {emit_upper_reg i.res.(0)}, {emit_addressing (offset_addressing addr 4) i.arg 0}\n`
end
| Lop(Istore(chunk, addr)) ->
begin match i.arg.(0).typ with
Int | Addr ->
let store_instr =
match chunk with
Word -> "sw"
| Byte_unsigned | Byte_signed -> "sb"
| Sixteen_unsigned | Sixteen_signed -> "sh" in
` {emit_string store_instr} {emit_reg i.arg.(0)}, {emit_addressing addr i.arg 1}\n`
| Float ->
(* Destination is not necessarily 8-aligned, hence better not use s.d *)
` swc1 {emit_lower_reg i.arg.(0)}, {emit_addressing addr i.arg 1}\n`;
` swc1 {emit_upper_reg i.arg.(0)}, {emit_addressing (offset_addressing addr 4) i.arg 1}\n`
end
| Lop(Ialloc n) ->
if pic or !fastcode_flag then begin
if !call_gc_label = 0 then call_gc_label := new_label();
` .set noreorder\n`;
` subu $22, $22, {emit_int n}\n`;
` subu $24, $22, $23\n`;
` bltzal $24, {emit_label !call_gc_label}\n`;
` addu {emit_reg i.res.(0)}, $22, 4\n`;
`{record_frame i.live}\n`;
` .set reorder\n`
end else begin
begin match n with
8 -> liveregs i 0;
` jal caml_alloc1\n`
| 12 -> liveregs i 0;
` jal caml_alloc2\n`
| 16 -> liveregs i 0;
` jal caml_alloc3\n`
| _ -> ` li $24, {emit_int n}\n`;
liveregs i live_24;
` jal caml_alloc\n`
end;
`{record_frame i.live}\n`;
` addu {emit_reg i.res.(0)}, $22, 4\n`
end
| Lop(Iintop(Icheckbound)) ->
if !range_check_trap = 0 then range_check_trap := new_label();
` bleu {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, {emit_label !range_check_trap}\n`
| Lop(Iintop op) ->
let instr = name_for_int_operation op in
` {emit_string instr} {emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`
| Lop(Iintop_imm(Icheckbound, n)) ->
if !range_check_trap = 0 then range_check_trap := new_label();
` bleu {emit_reg i.arg.(0)}, {emit_int n}, {emit_label !range_check_trap}\n`
| Lop(Iintop_imm(op, n)) ->
let instr = name_for_int_operation op in
` {emit_string instr} {emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_int n}\n`
| Lop(Inegf | Iabsf as op) ->
let instr = name_for_float_operation op in
` {emit_string instr} {emit_reg i.res.(0)}, {emit_reg i.arg.(0)}\n`
| Lop(Iaddf | Isubf | Imulf | Idivf as op) ->
let instr = name_for_float_operation op in
` {emit_string instr} {emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`
| Lop(Ifloatofint) ->
` mtc1 {emit_reg i.arg.(0)}, {emit_reg i.res.(0)}\n`;
` cvt.d.w {emit_reg i.res.(0)}, {emit_reg i.res.(0)}\n`
| Lop(Iintoffloat) ->
` trunc.w.d $f10, {emit_reg i.arg.(0)}, $24\n`;
` mfc1 {emit_reg i.res.(0)}, $f10\n`
| Lop(Ispecific sop) ->
fatal_error "Emit_mips: Ispecific"
| Lreloadretaddr ->
let n = frame_size() in
` lw $31, {emit_int(n - 4)}($sp)\n`
| Lreturn ->
let n = frame_size() in
if n > 0 then
` addu $sp, $sp, {emit_int n}\n`;
liveregs i 0;
` j $31\n`
| Llabel lbl ->
`{emit_label lbl}:\n`
| Lbranch lbl ->
` b {emit_label lbl}\n`
| Lcondbranch(tst, lbl) ->
begin match tst with
Itruetest ->
` bne {emit_reg i.arg.(0)}, $0, {emit_label lbl}\n`
| Ifalsetest ->
` beq {emit_reg i.arg.(0)}, $0, {emit_label lbl}\n`
| Iinttest cmp ->
let comp = name_for_comparison cmp in
` b{emit_string comp} {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, {emit_label lbl}\n`
| Iinttest_imm(cmp, n) ->
let comp = name_for_comparison cmp in
` b{emit_string comp} {emit_reg i.arg.(0)}, {emit_int n}, {emit_label lbl}\n`
| Ifloattest(cmp, neg) ->
let (comp, branch) = name_for_float_comparison cmp neg in
` c.{emit_string comp}.d {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
if branch
then ` bc1f {emit_label lbl}\n`
else ` bc1t {emit_label lbl}\n`
| Ioddtest ->
` and $24, {emit_reg i.arg.(0)}, 1\n`;
` bne $24, $0, {emit_label lbl}\n`
| Ieventest ->
` and $24, {emit_reg i.arg.(0)}, 1\n`;
` beq $24, $0, {emit_label lbl}\n`
end
| Lcondbranch3(lbl0, lbl1, lbl2) ->
` subu $24, {emit_reg i.arg.(0)}, 1\n`;
begin match lbl0 with
None -> ()
| Some lbl -> ` beq {emit_reg i.arg.(0)}, $0, {emit_label lbl}\n`
end;
begin match lbl1 with
None -> ()
| Some lbl -> ` beq $24, $0, {emit_label lbl}\n`
end;
begin match lbl2 with
None -> ()
| Some lbl -> ` bgtz $24, {emit_label lbl}\n`
end
| Lswitch jumptbl ->
let lbl_jumptbl = new_label() in
` sll $24, {emit_reg i.arg.(0)}, 2\n`;
` lw $24, {emit_label lbl_jumptbl}($24)\n`;
if pic then
` .cpadd $24\n`;
liveregs i live_24;
` j $24\n`;
` .rdata\n`;
`{emit_label lbl_jumptbl}:\n`;
for i = 0 to Array.length jumptbl - 1 do
if pic
then ` .gpword {emit_label jumptbl.(i)}\n`
else ` .word {emit_label jumptbl.(i)}\n`
done;
` .text\n`
| Lsetuptrap lbl ->
` subu $sp, $sp, 8\n`;
` bal {emit_label lbl}\n`;
reload_gp()
| Lpushtrap ->
stack_offset := !stack_offset + 8;
` sw $30, 0($sp)\n`;
` sw $31, 4($sp)\n`;
` move $30, $sp\n`
| Lpoptrap ->
` lw $30, 0($sp)\n`;
` addu $sp, $sp, 8\n`;
stack_offset := !stack_offset - 8
| Lraise ->
` lw $25, 4($30)\n`;
` move $sp, $30\n`;
` lw $30, 0($sp)\n`;
` addu $sp, $sp, 8\n`;
liveregs i 0;
` jal $25\n` (* Keep retaddr in $31 for debugging *)
let rec emit_all i =
match i.desc with Lend -> () | _ -> emit_instr i; emit_all i.next
(* Emission of a function declaration *)
let fundecl fundecl =
function_name := fundecl.fun_name;
fastcode_flag := fundecl.fun_fast;
uses_gp := pic && instr_uses_gp fundecl.fun_body;
tailrec_entry_point := new_label();
stack_offset := 0;
call_gc_label := 0;
range_check_trap := 0;
` .text\n`;
` .align 2\n`;
` .globl {emit_symbol fundecl.fun_name}\n`;
` .ent {emit_symbol fundecl.fun_name}\n`;
`{emit_symbol fundecl.fun_name}:\n`;
if !uses_gp then begin
` .set noreorder\n`;
` .cpload $25\n`;
` .set reorder\n`
end;
let n = frame_size() in
if n > 0 then
` subu $sp, $sp, {emit_int n}\n`;
if !contains_calls then
` sw $31, {emit_int(n - 4)}($sp)\n`;
if !uses_gp && !contains_calls then
` sw $gp, {emit_int(n - 8)}($sp)\n`;
`{emit_label !tailrec_entry_point}:\n`;
emit_all fundecl.fun_body;
if !call_gc_label > 0 then begin
`{emit_label !call_gc_label}:\n`;
emit_branch_symbol "caml_call_gc"
end;
if !range_check_trap > 0 then begin
`{emit_label !range_check_trap}:\n`;
emit_branch_symbol "array_bound_error"
end;
` .end {emit_symbol fundecl.fun_name}\n`
(* Emission of data *)
let emit_item = function
Cdefine_symbol s ->
` .globl {emit_symbol s}\n`;
`{emit_symbol s}:\n`
| Cdefine_label lbl ->
`{emit_label (10000 + lbl)}:\n`
| Cint8 n ->
` .byte {emit_int n}\n`
| Cint16 n ->
` .half {emit_int n}\n`
| Cint n ->
` .word {emit_nativeint n}\n`
| Cfloat f ->
` .align 0\n`; (* Prevent alignment on 8-byte boundary *)
` .double {emit_string f}\n`
| Csymbol_address s ->
` .word {emit_symbol s}\n`
| Clabel_address lbl ->
` .word {emit_label (10000 + lbl)}\n`
| Cstring s ->
emit_string_directive " .ascii " s
| Cskip n ->
if n > 0 then ` .space {emit_int n}\n`
| Calign n ->
` .align {emit_int(Misc.log2 n)}\n`
let data l =
` .data\n`;
List.iter emit_item l
(* Beginning / end of an assembly file *)
let begin_assembly() =
(* There are really two groups of registers:
$sp and $30 always point to stack locations
$2 - $21 never point to stack locations. *)
` .noalias $2,$sp; .noalias $2,$30; .noalias $3,$sp; .noalias $3,$30\n`;
` .noalias $4,$sp; .noalias $4,$30; .noalias $5,$sp; .noalias $5,$30\n`;
` .noalias $6,$sp; .noalias $6,$30; .noalias $7,$sp; .noalias $7,$30\n`;
` .noalias $8,$sp; .noalias $8,$30; .noalias $9,$sp; .noalias $9,$30\n`;
` .noalias $10,$sp; .noalias $10,$30; .noalias $11,$sp; .noalias $11,$30\n`;
` .noalias $12,$sp; .noalias $12,$30; .noalias $13,$sp; .noalias $13,$30\n`;
` .noalias $14,$sp; .noalias $14,$30; .noalias $15,$sp; .noalias $15,$30\n`;
` .noalias $16,$sp; .noalias $16,$30; .noalias $17,$sp; .noalias $17,$30\n`;
` .noalias $18,$sp; .noalias $18,$30; .noalias $19,$sp; .noalias $19,$30\n`;
` .noalias $20,$sp; .noalias $20,$30; .noalias $21,$sp; .noalias $21,$30\n\n`;
(* The following .file directive is intended to prevent the generation
of line numbers for the debugger, since they make .o files larger. *)
` .file 1 \"{emit_string !Location.input_name}\"\n\n`;
let lbl_begin = Compilenv.current_unit_name() ^ "_data_begin" in
` .data\n`;
` .globl {emit_symbol lbl_begin}\n`;
`{emit_symbol lbl_begin}:\n`;
let lbl_begin = Compilenv.current_unit_name() ^ "_code_begin" in
` .text\n`;
` .globl {emit_symbol lbl_begin}\n`;
`{emit_symbol lbl_begin}:\n`
let end_assembly () =
let lbl_end = Compilenv.current_unit_name() ^ "_code_end" in
` .text\n`;
` .globl {emit_symbol lbl_end}\n`;
`{emit_symbol lbl_end}:\n`;
let lbl_end = Compilenv.current_unit_name() ^ "_end" in
` .data\n`;
` .globl {emit_symbol lbl_end}\n`;
`{emit_symbol lbl_end}:\n`;
` .word 0\n`;
let lbl = Compilenv.current_unit_name() ^ "_frametable" in
` .rdata\n`;
` .globl {emit_symbol lbl}\n`;
`{emit_symbol lbl}:\n`;
` .word {emit_int (List.length !frame_descriptors)}\n`;
List.iter emit_frame !frame_descriptors;
frame_descriptors := []

View File

@ -1,877 +0,0 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Emission of PowerPC assembly code *)
module StringSet = Set.Make(struct type t = string let compare = compare end)
open Misc
open Cmm
open Arch
open Proc
open Reg
open Mach
open Linearize
open Emitaux
(* Distinguish between the PowerPC and the RS6000 submodels *)
let powerpc =
match Config.model with
"ppc" -> true
| "rs6000" -> false
| _ -> fatal_error "wrong $(MODEL)"
(* Distinguish between the PowerOpen (AIX, MacOS) relative-addressing model
and the SVR4 (Solaris, MkLinux) absolute-addressing model. *)
let toc =
match Config.system with
"aix" -> true
| "elf" -> false
| _ -> fatal_error "wrong $(SYSTEM)"
(* Layout of the stack *)
(* In the TOC-based model:
The bottom 24 bytes of the stack are reserved at all times
for a standard linkage area.
In this area, the word at offset +20 is used by glue code and others to
save the TOC register.
The bottom two words are used as temporaries and for trap frames.
The stack is kept 8-aligned.
In the absolute-address model:
No reserved space at the bottom of the stack.
The stack is kept 8-aligned. *)
let stack_linkage_area = if toc then 24 else 0
let trap_frame_size = if toc then 24 else 8
let stack_offset = ref 0
let frame_size () =
let size =
stack_linkage_area + (* The bottom linkage area *)
!stack_offset + (* Trap frame, outgoing parameters *)
4 * num_stack_slots.(0) + 8 * num_stack_slots.(1) + (* Local variables *)
(if !contains_calls then 4 else 0) in (* The return address *)
Misc.align size 8
let slot_offset loc cls =
match loc with
Local n ->
if cls = 0
then stack_linkage_area + !stack_offset + num_stack_slots.(1) * 8 + n * 4
else stack_linkage_area + !stack_offset + n * 8
| Incoming n -> frame_size() + n
| Outgoing n -> n
(* Output a symbol *)
let emit_symbol s =
Emitaux.emit_symbol '.' s
let emit_codesymbol s =
if toc then emit_char '.';
emit_symbol s
(* Output a label *)
let label_prefix = if toc then "L.." else ".L"
let emit_label lbl =
emit_string label_prefix; emit_int lbl
(* Section switching *)
let data_space =
if toc
then " .csect .data[RW]\n"
else " .section \".data\"\n"
let code_space =
if toc
then " .csect .text[PR]\n"
else " .section \".text\"\n"
(* Output a pseudo-register *)
let emit_reg r =
match r.loc with
Reg r -> emit_string (register_name r)
| _ -> fatal_error "Emit.emit_reg"
(* Output a stack reference *)
let emit_stack r =
match r.loc with
Stack s ->
let ofs = slot_offset s (register_class r) in `{emit_int ofs}(1)`
| _ -> fatal_error "Emit.emit_stack"
(* Split a 32-bit integer constants in two 16-bit halves *)
let low n = n land 0xFFFF
let high n = n asr 16
let nativelow n = Nativeint.to_int n land 0xFFFF
let nativehigh n = Nativeint.to_int (Nativeint.shift n (-16))
let is_native_immediate n =
Nativeint.cmp n 32767 <= 0 && Nativeint.cmp n (-32768) >= 0
(* Output a load or store operation *)
let emit_symbol_offset s d =
emit_symbol s;
if d > 0 then `+`;
if d <> 0 then emit_int d
let emit_load_store instr addressing_mode addr n arg =
match addressing_mode with
Ibased(s, d) ->
(* Only relevant in the absolute model *)
` addis 11, 0, {emit_symbol_offset s d}@ha\n`;
` {emit_string instr} {emit_reg arg}, {emit_symbol_offset s d}@l(11)\n`
| Iindexed ofs ->
if is_immediate ofs then
` {emit_string instr} {emit_reg arg}, {emit_int ofs}({emit_reg addr.(n)})\n`
else begin
` lis 0, {emit_int(high ofs)}\n`;
if low ofs <> 0 then
` ori 0, 0, {emit_int(low ofs)}\n`;
` {emit_string instr}x {emit_reg arg}, {emit_reg addr.(n)}, 0\n`
end
| Iindexed2 ->
` {emit_string instr}x {emit_reg arg}, {emit_reg addr.(n)}, {emit_reg addr.(n+1)}\n`
(* After a comparison, extract the result as 0 or 1 *)
let emit_set_comp cmp res =
` mfcr 0\n`;
let bitnum =
match cmp with
Ceq | Cne -> 2
| Cgt | Cle -> 1
| Clt | Cge -> 0 in
` rlwinm {emit_reg res}, 0, {emit_int(bitnum+1)}, 1\n`;
begin match cmp with
Cne | Cle | Cge -> ` xori {emit_reg res}, {emit_reg res}, 1\n`
| _ -> ()
end
(* Record live pointers at call points *)
type frame_descr =
{ fd_lbl: int; (* Return address *)
fd_frame_size: int; (* Size of stack frame *)
fd_live_offset: int list } (* Offsets/regs of live addresses *)
let frame_descriptors = ref([] : frame_descr list)
let record_frame live =
let lbl = new_label() in
let live_offset = ref [] in
Reg.Set.iter
(function
{typ = Addr; loc = Reg r} ->
live_offset := (r lsl 1) + 1 :: !live_offset
| {typ = Addr; loc = Stack s} as reg ->
live_offset := slot_offset s (register_class reg) :: !live_offset
| _ -> ())
live;
frame_descriptors :=
{ fd_lbl = lbl;
fd_frame_size = frame_size();
fd_live_offset = !live_offset } :: !frame_descriptors;
`{emit_label lbl}:`
let emit_frame fd =
` .long {emit_label fd.fd_lbl} + 4\n`;
` .short {emit_int fd.fd_frame_size}\n`;
` .short {emit_int (List.length fd.fd_live_offset)}\n`;
List.iter
(fun n ->
` .short {emit_int n}\n`)
fd.fd_live_offset;
` .align 2\n`
(* Record symbols and floating-point constants (for the TOC model).
These will go in the toc section. *)
let label_constant table constant =
try
Hashtbl.find table constant
with Not_found ->
let lbl = new_label() in
Hashtbl.add table constant lbl;
lbl
let symbol_constants = (Hashtbl.create 17 : (string, int) Hashtbl.t)
let float_constants = (Hashtbl.create 11 : (string, int) Hashtbl.t)
let label_symbol s = label_constant symbol_constants s
let label_float s = label_constant float_constants s
let emit_symbol_constant symb lbl =
`{emit_label lbl}: .tc {emit_symbol symb}[TC], {emit_symbol symb}\n`
let emit_float_constant float lbl =
`{emit_label lbl}: .tc FD_`;
for i = 0 to 7 do
emit_printf "%02x" (Char.code (String.unsafe_get float i))
done;
`[TC], 0x`;
for i = 0 to 3 do
emit_printf "%02x" (Char.code (String.unsafe_get float i))
done;
`, 0x`;
for i = 4 to 7 do
emit_printf "%02x" (Char.code (String.unsafe_get float i))
done;
`\n`
(* Record floating-point literals (for the ELF model) *)
let float_literals = ref ([] : (string * int) list)
(* Names for conditional branches after comparisons *)
let branch_for_comparison = function
Ceq -> "beq" | Cne -> "bne"
| Cle -> "ble" | Cgt -> "bgt"
| Cge -> "bge" | Clt -> "blt"
let name_for_int_comparison = function
Isigned cmp -> ("cmpw", branch_for_comparison cmp)
| Iunsigned cmp -> ("cmplw", branch_for_comparison cmp)
(* Names for various instructions *)
let name_for_intop = function
Iadd -> "add"
| Imul -> "mullw"
| Idiv -> if powerpc then "divw" else "divs"
| Iand -> "and"
| Ior -> "or"
| Ixor -> "xor"
| Ilsl -> "slw"
| Ilsr -> "srw"
| Iasr -> "sraw"
| _ -> Misc.fatal_error "Emit.Intop"
let name_for_intop_imm = function
Iadd -> "addi"
| Imul -> "mulli"
| Iand -> "andi."
| Ior -> "ori"
| Ixor -> "xori"
| Ilsl -> "slwi"
| Ilsr -> "srwi"
| Iasr -> "srawi"
| _ -> Misc.fatal_error "Emit.Intop_imm"
let name_for_floatop1 = function
Inegf -> "fneg"
| Iabsf -> "fabs"
| _ -> Misc.fatal_error "Emit.Iopf1"
let name_for_floatop2 = function
Iaddf -> "fadd"
| Isubf -> "fsub"
| Imulf -> "fmul"
| Idivf -> "fdiv"
| _ -> Misc.fatal_error "Emit.Iopf2"
let name_for_specific = function
Imultaddf -> "fmadd"
| Imultsubf -> "fmsub"
(* Name of current function *)
let function_name = ref ""
(* Entry point for tail recursive calls *)
let tailrec_entry_point = ref 0
(* Names of functions defined in the current file *)
let defined_functions = ref StringSet.empty
(* Label of glue code for calling the GC *)
let call_gc_label = ref 0
(* Label of jump table *)
let lbl_jumptbl = ref 0
(* List of all labels in jumptable (reverse order) *)
let jumptbl_entries = ref []
(* Number of jumptable entries *)
let num_jumptbl_entries = ref 0
(* Output the assembly code for an instruction *)
let rec emit_instr i dslot =
match i.desc with
Lend -> ()
| Lop(Imove | Ispill | Ireload) ->
let src = i.arg.(0) and dst = i.res.(0) in
if src.loc <> dst.loc then begin
match (src, dst) with
{loc = Reg rs; typ = (Int | Addr)}, {loc = Reg rd} ->
` mr {emit_reg dst}, {emit_reg src}\n`
| {loc = Reg rs; typ = Float}, {loc = Reg rd; typ = Float} ->
` fmr {emit_reg dst}, {emit_reg src}\n`
| {loc = Reg rs; typ = (Int | Addr)}, {loc = Stack sd} ->
` stw {emit_reg src}, {emit_stack dst}\n`
| {loc = Reg rs; typ = Float}, {loc = Stack sd} ->
` stfd {emit_reg src}, {emit_stack dst}\n`
| {loc = Stack ss; typ = (Int | Addr)}, {loc = Reg rd} ->
` lwz {emit_reg dst}, {emit_stack src}\n`
| {loc = Stack ss; typ = Float}, {loc = Reg rd} ->
` lfd {emit_reg dst}, {emit_stack src}\n`
| (_, _) ->
fatal_error "Emit: Imove"
end
| Lop(Iconst_int n) ->
if is_native_immediate n then
` li {emit_reg i.res.(0)}, {emit_nativeint n}\n`
else begin
` lis {emit_reg i.res.(0)}, {emit_int(nativehigh n)}\n`;
if nativelow n <> 0 then
` ori {emit_reg i.res.(0)}, {emit_reg i.res.(0)}, {emit_int(nativelow n)}\n`
end
| Lop(Iconst_float s) ->
if toc then begin
let repr = (Obj.magic (float_of_string s) : string) in
let lbl = label_float repr in
` lfd {emit_reg i.res.(0)}, {emit_label lbl}(2) # {emit_string s}\n`
end else begin
let lbl = new_label() in
float_literals := (s, lbl) :: !float_literals;
` addis 11, 0, {emit_label lbl}@ha\n`;
` lfd {emit_reg i.res.(0)}, {emit_label lbl}@l(11)\n`
end
| Lop(Iconst_symbol s) ->
if toc then begin
let lbl = label_symbol s in
` lwz {emit_reg i.res.(0)}, {emit_label lbl}(2) # {emit_symbol s}\n`
end else begin
` addis {emit_reg i.res.(0)}, 0, {emit_symbol s}@ha\n`;
` addi {emit_reg i.res.(0)}, {emit_reg i.res.(0)}, {emit_symbol s}@l\n`
end
| Lop(Icall_ind) ->
if toc then begin
` lwz 0, 0({emit_reg i.arg.(0)})\n`;
` stw 2, 20(1)\n`;
` mtlr 0\n`;
` lwz 2, 4({emit_reg i.arg.(0)})\n`;
record_frame i.live;
` blrl\n`;
` lwz 2, 20(1)\n`
end else begin
` mtlr {emit_reg i.arg.(0)}\n`;
record_frame i.live;
` blrl\n`
end
| Lop(Icall_imm s) ->
record_frame i.live;
` bl {emit_codesymbol s}\n`;
if toc && not (StringSet.mem s !defined_functions) then
` cror 31, 31, 31\n` (* nop *)
| Lop(Itailcall_ind) ->
let n = frame_size() in
if toc then begin
` lwz 0, 0({emit_reg i.arg.(0)})\n`;
` lwz 2, 4({emit_reg i.arg.(0)})\n`;
` mtctr 0\n`
end else begin
` mtctr {emit_reg i.arg.(0)}\n`
end;
if !contains_calls then begin
` lwz 11, {emit_int(n - 4)}(1)\n`;
` addi 1, 1, {emit_int n}\n`;
` mtlr 11\n`
end else begin
` addi 1, 1, {emit_int n}\n`
end;
` bctr\n`
| Lop(Itailcall_imm s) ->
if s = !function_name then
` b {emit_label !tailrec_entry_point}\n`
else if not toc || StringSet.mem s !defined_functions then begin
let n = frame_size() in
if !contains_calls then begin
` lwz 11, {emit_int(n - 4)}(1)\n`;
` addi 1, 1, {emit_int n}\n`;
` mtlr 11\n`
end else begin
` addi 1, 1, {emit_int n}\n`
end;
` b {emit_codesymbol s}\n`
end else begin
(* Tailcalling a function that has a possibly different TOC
is difficult, because the callee's TOC must be loaded in r2,
but ours must not be stored in 20(r1), which would overwrite
our caller's saved TOC. Hence we can't go through the
standard glue code. Here, we just proceed as in tailcall_ind. *)
let lbl = label_symbol s in
let n = frame_size() in
` lwz 12, {emit_label lbl}(2) # {emit_symbol s}\n`;
if !contains_calls then begin
` lwz 11, {emit_int(n - 4)}(1)\n`;
` lwz 0, 0(12)\n`;
` lwz 2, 4(12)\n`;
` mtctr 0\n`;
` addi 1, 1, {emit_int n}\n`;
` mtlr 11\n`
end else begin
` lwz 0, 0(12)\n`;
` lwz 2, 4(12)\n`;
` mtctr 0\n`;
` addi 1, 1, {emit_int n}\n`
end;
` bctr\n`
end
| Lop(Iextcall(s, alloc)) ->
if alloc then begin
if toc then begin
let lbl = label_symbol s in
` lwz 11, {emit_label lbl}(2) # {emit_symbol s}\n`
end else begin
` addis 11, 0, {emit_symbol s}@ha\n`;
` addi 11, 11, {emit_symbol s}@l\n`
end;
record_frame i.live;
` bl {emit_codesymbol "caml_c_call"}\n`
end else begin
` bl {emit_codesymbol s}\n`
end;
if toc then
` cror 31, 31, 31\n` (* nop *)
| Lop(Istackoffset n) ->
` addi 1, 1, {emit_int (-n)}\n`;
stack_offset := !stack_offset + n
| Lop(Iload(chunk, addr)) ->
let loadinstr =
match chunk with
Word -> if i.res.(0).typ = Float then "lfd" else "lwz"
| Byte_unsigned -> "lbz"
| Byte_signed -> "lbz"
| Sixteen_unsigned -> "lhz"
| Sixteen_signed -> "lha" in
emit_load_store loadinstr addr i.arg 0 i.res.(0);
if chunk = Byte_signed then
` extsb {emit_reg i.res.(0)}, {emit_reg i.res.(0)}\n`
| Lop(Istore(chunk, addr)) ->
let storeinstr =
match chunk with
Word -> if i.arg.(0).typ = Float then "stfd" else "stw"
| Byte_unsigned | Byte_signed -> "stb"
| Sixteen_unsigned | Sixteen_signed -> "sth" in
emit_load_store storeinstr addr i.arg 1 i.arg.(0)
| Lop(Ialloc n) ->
if !call_gc_label = 0 then call_gc_label := new_label();
` addi 31, 31, {emit_int(-n)}\n`;
` cmplw 31, 30\n`;
` addi {emit_reg i.res.(0)}, 31, 4\n`;
record_frame i.live;
` bltl {emit_label !call_gc_label}\n`
| Lop(Iintop Isub) -> (* subf has swapped arguments *)
(* Use subfc instead of subf for RS6000 compatibility. *)
` subfc {emit_reg i.res.(0)}, {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`
| Lop(Iintop Imod) ->
if powerpc then begin
` divw 0, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
` mullw 0, 0, {emit_reg i.arg.(1)}\n`;
` subfc {emit_reg i.res.(0)}, 0, {emit_reg i.arg.(0)}\n`
end else begin
` divs 0, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
` mfmq {emit_reg i.res.(0)}\n`
end
| Lop(Iintop(Icomp cmp)) ->
begin match cmp with
Isigned c ->
` cmpw {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
emit_set_comp c i.res.(0)
| Iunsigned c ->
` cmplw {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
emit_set_comp c i.res.(0)
end
| Lop(Iintop Icheckbound) ->
` twlle {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`
| Lop(Iintop op) ->
let instr = name_for_intop op in
` {emit_string instr} {emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`
| Lop(Iintop_imm(Isub, n)) ->
` addi {emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_int(-n)}\n`
| Lop(Iintop_imm(Idiv, n)) -> (* n is guaranteed to be a power of 2 *)
let l = Misc.log2 n in
` srawi {emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_int l}\n`;
` addze {emit_reg i.res.(0)}, {emit_reg i.res.(0)}\n`
| Lop(Iintop_imm(Imod, n)) -> (* n is guaranteed to be a power of 2 *)
let l = Misc.log2 n in
` srawi 0, {emit_reg i.arg.(0)}, {emit_int l}\n`;
` addze 0, 0\n`;
` slwi 0, 0, {emit_int l}\n`;
` subfc {emit_reg i.res.(0)}, 0, {emit_reg i.arg.(0)}\n`
| Lop(Iintop_imm(Icomp cmp, n)) ->
begin match cmp with
Isigned c ->
` cmpwi {emit_reg i.arg.(0)}, {emit_int n}\n`;
emit_set_comp c i.res.(0)
| Iunsigned c ->
` cmplwi {emit_reg i.arg.(0)}, {emit_int n}\n`;
emit_set_comp c i.res.(0)
end
| Lop(Iintop_imm(Icheckbound, n)) ->
` twllei {emit_reg i.arg.(0)}, {emit_int n}\n`
| Lop(Iintop_imm(op, n)) ->
let instr = name_for_intop_imm op in
` {emit_string instr} {emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_int n}\n`
| Lop(Inegf | Iabsf as op) ->
let instr = name_for_floatop1 op in
` {emit_string instr} {emit_reg i.res.(0)}, {emit_reg i.arg.(0)}\n`
| Lop(Iaddf | Isubf | Imulf | Idivf as op) ->
let instr = name_for_floatop2 op in
` {emit_string instr} {emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`
| Lop(Ifloatofint) ->
if toc then begin
let lbl = label_float "\067\048\000\000\128\000\000\000" in
(* That string above represents 0x4330000080000000 *)
` lfd 0, {emit_label lbl}(2)\n`
end else begin
let lbl = new_label() in
float_literals := ("4.503601774854144e15", lbl) :: !float_literals;
(* That float above also represents 0x4330000080000000 *)
` addis 11, 0, {emit_label lbl}@ha\n`;
` lfd 0, {emit_label lbl}@l(11)\n`
end;
` lis 0, 0x4330\n`;
` stwu 0, -8(1)\n`;
` xoris 0, {emit_reg i.arg.(0)}, 0x8000\n`;
` stw 0, 4(1)\n`;
` lfd {emit_reg i.res.(0)}, 0(1)\n`;
` addi 1, 1, 8\n`;
` fsub {emit_reg i.res.(0)}, {emit_reg i.res.(0)}, 0\n`
| Lop(Iintoffloat) ->
` fctiwz 0, {emit_reg i.arg.(0)}\n`;
` stfdu 0, -8(1)\n`;
` lwz {emit_reg i.res.(0)}, 4(1)\n`;
` addi 1, 1, 8\n`
| Lop(Ispecific sop) ->
let instr = name_for_specific sop in
` {emit_string instr} {emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, {emit_reg i.arg.(2)}\n`
| Lreloadretaddr ->
let n = frame_size() in
` lwz 11, {emit_int(n - 4)}(1)\n`;
` mtlr 11\n`
| Lreturn ->
let n = frame_size() in
` addi 1, 1, {emit_int n}\n`;
` blr\n`
| Llabel lbl ->
`{emit_label lbl}:\n`
| Lbranch lbl ->
` b {emit_label lbl}\n`
| Lcondbranch(tst, lbl) ->
begin match tst with
Itruetest ->
` cmpwi {emit_reg i.arg.(0)}, 0\n`;
emit_delay dslot;
` bne {emit_label lbl}\n`
| Ifalsetest ->
` cmpwi {emit_reg i.arg.(0)}, 0\n`;
emit_delay dslot;
` beq {emit_label lbl}\n`
| Iinttest cmp ->
let (comp, branch) = name_for_int_comparison cmp in
` {emit_string comp} {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
emit_delay dslot;
` {emit_string branch} {emit_label lbl}\n`
| Iinttest_imm(cmp, n) ->
let (comp, branch) = name_for_int_comparison cmp in
` {emit_string comp}i {emit_reg i.arg.(0)}, {emit_int n}\n`;
emit_delay dslot;
` {emit_string branch} {emit_label lbl}\n`
| Ifloattest(cmp, neg) ->
` fcmpu 0, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
(* bit 0 = lt, bit 1 = gt, bit 2 = eq *)
let (bitnum, negtst) =
match cmp with
Ceq -> (2, neg)
| Cne -> (2, not neg)
| Cle -> ` cror 3, 0, 2\n`; (* lt or eq *)
(3, neg)
| Cgt -> (1, neg)
| Cge -> ` cror 3, 1, 2\n`; (* gt or eq *)
(3, neg)
| Clt -> (0, neg) in
emit_delay dslot;
if negtst
then ` bf {emit_int bitnum}, {emit_label lbl}\n`
else ` bt {emit_int bitnum}, {emit_label lbl}\n`
| Ioddtest ->
` andi. 0, {emit_reg i.arg.(0)}, 1\n`;
emit_delay dslot;
` bne {emit_label lbl}\n`
| Ieventest ->
` andi. 0, {emit_reg i.arg.(0)}, 1\n`;
emit_delay dslot;
` beq {emit_label lbl}\n`
end
| Lcondbranch3(lbl0, lbl1, lbl2) ->
` cmpwi {emit_reg i.arg.(0)}, 1\n`;
emit_delay dslot;
begin match lbl0 with
None -> ()
| Some lbl -> ` blt {emit_label lbl}\n`
end;
begin match lbl1 with
None -> ()
| Some lbl -> ` beq {emit_label lbl}\n`
end;
begin match lbl2 with
None -> ()
| Some lbl -> ` bgt {emit_label lbl}\n`
end
| Lswitch jumptbl ->
if !lbl_jumptbl = 0 then lbl_jumptbl := new_label();
if toc then begin
` lwz 11, {emit_label !lbl_jumptbl}(2)\n`
end else begin
` addis 11, 0, {emit_label !lbl_jumptbl}@ha\n`;
` addi 11, 11, {emit_label !lbl_jumptbl}@l\n`
end;
` addi 0, {emit_reg i.arg.(0)}, {emit_int !num_jumptbl_entries}\n`;
` slwi 0, 0, 2\n`;
` lwzx 0, 11, 0\n`;
` add 0, 11, 0\n`;
` mtctr 0\n`;
` bctr\n`;
for i = 0 to Array.length jumptbl - 1 do
jumptbl_entries := jumptbl.(i) :: !jumptbl_entries;
incr num_jumptbl_entries
done
| Lsetuptrap lbl ->
` bl {emit_label lbl}\n`
| Lpushtrap ->
stack_offset := !stack_offset + trap_frame_size;
` mflr 0\n`;
` stwu 0, -{emit_int trap_frame_size}(1)\n`;
` stw 29, 4(1)\n`;
if toc then
` stw 2, 20(1)\n`;
` mr 29, 1\n`
| Lpoptrap ->
` lwz 29, 4(1)\n`;
` addi 1, 1, {emit_int trap_frame_size}\n`;
stack_offset := !stack_offset - trap_frame_size
| Lraise ->
` lwz 0, 0(29)\n`;
` mr 1, 29\n`;
` mtlr 0\n`;
` lwz 29, 4(1)\n`;
if toc then
` lwz 2, 20(1)\n`;
` addi 1, 1, {emit_int trap_frame_size}\n\n`;
` blr\n`
and emit_delay = function
None -> ()
| Some i -> emit_instr i None
(* Checks if a pseudo-instruction expands to instructions
that do not branch and do not affect CR0 nor R12. *)
let is_simple_instr i =
match i.desc with
Lop op ->
begin match op with
Icall_imm _ | Icall_ind | Itailcall_imm _ | Itailcall_ind |
Iextcall(_, _) -> false
| Ialloc(_) -> false
| Iintop(Icomp _) -> false
| Iintop_imm(Iand, _) -> false
| Iintop_imm(Icomp _, _) -> false
| _ -> true
end
| Lreloadretaddr -> true
| _ -> false
let no_interference res arg =
try
for i = 0 to Array.length arg - 1 do
for j = 0 to Array.length res - 1 do
if arg.(i).loc = res.(j).loc then raise Exit
done
done;
true
with Exit ->
false
(* Emit a sequence of instructions, trying to fill delay slots for branches *)
let rec emit_all i =
match i with
{desc = Lend} -> ()
| {next = {desc = (Lcondbranch(_, _) | Lcondbranch3(_, _, _))}}
when is_simple_instr i & no_interference i.res i.next.arg ->
emit_instr i.next (Some i);
emit_all i.next.next
| _ ->
emit_instr i None;
emit_all i.next
(* Emission of a function declaration *)
let fundecl fundecl =
function_name := fundecl.fun_name;
defined_functions := StringSet.add fundecl.fun_name !defined_functions;
tailrec_entry_point := new_label();
stack_offset := 0;
call_gc_label := 0;
float_literals := [];
` .globl {emit_symbol fundecl.fun_name}\n`;
if toc then begin
` .globl .{emit_symbol fundecl.fun_name}\n`;
` .csect {emit_symbol fundecl.fun_name}[DS]\n`;
`{emit_symbol fundecl.fun_name}:\n`;
` .long .{emit_symbol fundecl.fun_name}, TOC[tc0], 0\n`
end else begin
` .type {emit_symbol fundecl.fun_name}, @function\n`
end;
emit_string code_space;
` .align 2\n`;
`{emit_codesymbol fundecl.fun_name}:\n`;
let n = frame_size() in
if !contains_calls then begin
` mflr 0\n`;
` addi 1, 1, {emit_int(-n)}\n`;
` stw 0, {emit_int(n - 4)}(1)\n`
end else
` addi 1, 1, {emit_int(-n)}\n`;
`{emit_label !tailrec_entry_point}:\n`;
emit_all fundecl.fun_body;
(* Emit the glue code to call the GC *)
if !call_gc_label > 0 then begin
`{emit_label !call_gc_label}:\n`;
if toc then begin
` mflr 0\n`; (* Save return address in r0 *)
` bl .caml_call_gc\n`;
` cror 31, 31, 31\n`; (* nop *)
` blr\n` (* Will re-execute the allocation *)
end else begin
` b caml_call_gc\n`
end
end;
(* Emit the floating-point literals *)
if !float_literals <> [] then begin
` .section \".rodata\"\n`;
` .align 3\n`;
List.iter
(fun (f, lbl) ->
`{emit_label lbl}: .double 0d{emit_string f}\n`)
!float_literals
end
(* Emission of data *)
let declare_global_data s =
` .globl {emit_symbol s}\n`;
if not toc then ` .type {emit_symbol s}, @object\n`
let emit_item = function
Cdefine_symbol s ->
`{emit_symbol s}:\n`;
declare_global_data s
| Cdefine_label lbl ->
`{emit_label (lbl + 10000)}:\n`
| Cint8 n ->
` .byte {emit_int n}\n`
| Cint16 n ->
` .short {emit_int n}\n`
| Cint n ->
` .long {emit_nativeint n}\n`
| Cfloat f ->
` .double 0d{emit_string f}\n`
| Csymbol_address s ->
` .long {emit_symbol s}\n`
| Clabel_address lbl ->
` .long {emit_label (lbl + 10000)}\n`
| Cstring s ->
emit_bytes_directive " .byte " s
| Cskip n ->
if n > 0 then ` .space {emit_int n}\n`
| Calign n ->
` .align {emit_int (Misc.log2 n)}\n`
let data l =
emit_string data_space;
List.iter emit_item l
(* Beginning / end of an assembly file *)
let begin_assembly() =
Hashtbl.clear symbol_constants;
Hashtbl.clear float_constants;
defined_functions := StringSet.empty;
num_jumptbl_entries := 0;
jumptbl_entries := [];
lbl_jumptbl := 0;
(* Emit the beginning of the segments *)
let lbl_begin = Compilenv.current_unit_name() ^ "_data_begin" in
emit_string data_space;
declare_global_data lbl_begin;
`{emit_symbol lbl_begin}:\n`;
let lbl_begin = Compilenv.current_unit_name() ^ "_code_begin" in
emit_string code_space;
declare_global_data lbl_begin;
`{emit_symbol lbl_begin}:\n`
let end_assembly() =
(* Emit the jump table *)
if !num_jumptbl_entries > 0 then begin
let lbl_tbl =
if toc then begin
let lbl_tbl = new_label() in
` .toc\n`;
`{emit_label !lbl_jumptbl}: .tc {emit_label lbl_tbl}[TC], {emit_label lbl_tbl}\n`;
` .csect .text[PR]\n`;
lbl_tbl
end else begin
` .section \".text\"\n`;
!lbl_jumptbl
end in
`{emit_label lbl_tbl}:\n`;
List.iter
(fun lbl -> ` .long {emit_label lbl} - {emit_label lbl_tbl}\n`)
(List.rev !jumptbl_entries);
jumptbl_entries := []
end;
if toc then begin
(* Emit the table of constants *)
` .toc\n`;
Hashtbl.iter emit_symbol_constant symbol_constants;
Hashtbl.iter emit_float_constant float_constants
end;
(* Emit the end of the segments *)
emit_string code_space;
let lbl_end = Compilenv.current_unit_name() ^ "_code_end" in
declare_global_data lbl_end;
`{emit_symbol lbl_end}:\n`;
emit_string data_space;
let lbl_end = Compilenv.current_unit_name() ^ "_data_end" in
declare_global_data lbl_end;
`{emit_symbol lbl_end}:\n`;
` .long 0\n`;
(* Emit the frame descriptors *)
let lbl = Compilenv.current_unit_name() ^ "_frametable" in
declare_global_data lbl;
`{emit_symbol lbl}:\n`;
` .long {emit_int (List.length !frame_descriptors)}\n`;
List.iter emit_frame !frame_descriptors;
frame_descriptors := []

View File

@ -1,684 +0,0 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Emission of Sparc assembly code *)
open Misc
open Cmm
open Arch
open Proc
open Reg
open Mach
open Linearize
open Emitaux
(* Tradeoff between code size and code speed *)
let fastcode_flag = ref true
(* Layout of the stack *)
(* Always keep the stack 8-aligned.
Always leave 96 bytes at the bottom of the stack *)
let stack_offset = ref 0
let frame_size () =
let size =
!stack_offset +
4 * num_stack_slots.(0) + 8 * num_stack_slots.(1) +
(if !contains_calls then 4 else 0) in
Misc.align size 8
let slot_offset loc cl =
match loc with
Incoming n -> frame_size() + n + 96
| Local n ->
if cl = 0
then !stack_offset + num_stack_slots.(1) * 8 + n * 4 + 96
else !stack_offset + n * 8 + 96
| Outgoing n -> n + 96
(* Return the other register in a register pair *)
let next_in_pair = function
{loc = Reg r; typ = (Int | Addr)} -> phys_reg (r + 1)
| {loc = Reg r; typ = Float} -> phys_reg (r + 15)
| _ -> fatal_error "Emit.next_in_pair"
(* Symbols are prefixed with _ under SunOS and BSD but not under Solaris *)
let symbol_prefix =
if Config.system = "solaris" then "" else "_"
let emit_symbol s =
if String.length s >= 1 & s.[0] = '.'
then emit_string s
else begin emit_string symbol_prefix; Emitaux.emit_symbol '$' s end
(* Check if a native integer is an immediate operand *)
let is_native_immediate n =
Nativeint.cmp n 4095 <= 0 && Nativeint.cmp n (-4096) >= 0
(* Output a label *)
let label_prefix =
if Config.system = "solaris" then ".L" else "L"
let emit_label lbl =
emit_string label_prefix; emit_int lbl
(* Output a pseudo-register *)
let emit_reg r =
match r.loc with
Reg r -> emit_string (register_name r)
| _ -> fatal_error "Emit.emit_reg"
(* Output a stack reference *)
let emit_stack r =
match r.loc with
Stack s ->
let ofs = slot_offset s (register_class r) in `[%sp + {emit_int ofs}]`
| _ -> fatal_error "Emit.emit_stack"
(* Output a load *)
let emit_load instr addr arg dst =
match addr with
Ibased(s, 0) ->
` sethi %hi({emit_symbol s}), %g1\n`;
` {emit_string instr} [%g1 + %lo({emit_symbol s})], {emit_reg dst}\n`
| Ibased(s, ofs) ->
` sethi %hi({emit_symbol s} + {emit_int ofs}), %g1\n`;
` {emit_string instr} [%g1 + %lo({emit_symbol s} + {emit_int ofs})], {emit_reg dst}\n`
| Iindexed ofs ->
if is_immediate ofs then
` {emit_string instr} [{emit_reg arg.(0)} + {emit_int ofs}], {emit_reg dst}\n`
else begin
` sethi %hi({emit_int ofs}), %g1\n`;
` or %g1, %lo({emit_int ofs}), %g1\n`;
` {emit_string instr} [{emit_reg arg.(0)} + %g1], {emit_reg dst}\n`
end
| Iindexed2 ofs ->
if ofs = 0 then
` {emit_string instr} [{emit_reg arg.(0)} + {emit_reg arg.(1)}], {emit_reg dst}\n`
else if is_immediate ofs then begin
` add {emit_reg arg.(1)}, {emit_int ofs}, %g1\n`;
` {emit_string instr} [{emit_reg arg.(0)} + %g1], {emit_reg dst}\n`
end else begin
` sethi %hi({emit_int ofs}), %g1\n`;
` or %g1, %lo({emit_int ofs}), %g1\n`;
` add {emit_reg arg.(1)}, %g1, %g1\n`;
` {emit_string instr} [{emit_reg arg.(0)} + %g1], {emit_reg dst}\n`
end
(* Output a store *)
let emit_store instr addr arg src =
match addr with
Ibased(s, 0) ->
` sethi %hi({emit_symbol s}), %g1\n`;
` {emit_string instr} {emit_reg src}, [%g1 + %lo({emit_symbol s})]\n`
| Ibased(s, ofs) ->
` sethi %hi({emit_symbol s} + {emit_int ofs}), %g1\n`;
` {emit_string instr} {emit_reg src}, [%g1 + %lo({emit_symbol s} + {emit_int ofs})]\n`
| Iindexed ofs ->
if is_immediate ofs then
` {emit_string instr} {emit_reg src}, [{emit_reg arg.(1)} + {emit_int ofs}]\n`
else begin
` sethi %hi({emit_int ofs}), %g1\n`;
` or %g1, %lo({emit_int ofs}), %g1\n`;
` {emit_string instr} {emit_reg src}, [{emit_reg arg.(1)} + %g1]\n`
end
| Iindexed2 ofs ->
if ofs = 0 then
` {emit_string instr} {emit_reg src}, [{emit_reg arg.(1)} + {emit_reg arg.(2)}]\n`
else if is_immediate ofs then begin
` add {emit_reg arg.(2)}, {emit_int ofs}, %g1\n`;
` {emit_string instr} {emit_reg src}, [{emit_reg arg.(1)} + %g1]\n`
end else begin
` sethi %hi({emit_int ofs}), %g1\n`;
` or %g1, %lo({emit_int ofs}), %g1\n`;
` add {emit_reg arg.(2)}, %g1, %g1\n`;
` {emit_string instr} {emit_reg src}, [{emit_reg arg.(1)} + %g1]\n`
end
(* Record live pointers at call points *)
type frame_descr =
{ fd_lbl: int; (* Return address *)
fd_frame_size: int; (* Size of stack frame *)
fd_live_offset: int list } (* Offsets/regs of live addresses *)
let frame_descriptors = ref([] : frame_descr list)
let record_frame live =
let lbl = new_label() in
let live_offset = ref [] in
Reg.Set.iter
(function
{typ = Addr; loc = Reg r} ->
live_offset := ((r lsl 1) + 1) :: !live_offset
| {typ = Addr; loc = Stack s} as reg ->
live_offset := slot_offset s (register_class reg) :: !live_offset
| _ -> ())
live;
frame_descriptors :=
{ fd_lbl = lbl;
fd_frame_size = frame_size();
fd_live_offset = !live_offset } :: !frame_descriptors;
`{emit_label lbl}:`
let emit_frame fd =
` .word {emit_label fd.fd_lbl}\n`;
` .half {emit_int fd.fd_frame_size}\n`;
` .half {emit_int (List.length fd.fd_live_offset)}\n`;
List.iter
(fun n ->
` .half {emit_int n}\n`)
fd.fd_live_offset;
` .align 4\n`
(* Record floating-point constants *)
let float_constants = ref ([] : (int * string) list)
let emit_float_constant (lbl, cst) =
` .data\n`;
` .align 8\n`;
`{emit_label lbl}: .double 0r{emit_string cst}\n`
(* Names of various instructions *)
let name_for_int_operation = function
Iadd -> "add"
| Isub -> "sub"
| Iand -> "and"
| Ior -> "or"
| Ixor -> "xor"
| Ilsl -> "sll"
| Ilsr -> "srl"
| Iasr -> "sra"
| _ -> Misc.fatal_error "Emit.name_for_int_operation"
let name_for_float_operation = function
Inegf -> "fnegs"
| Iabsf -> "fabss"
| Iaddf -> "faddd"
| Isubf -> "fsubd"
| Imulf -> "fmuld"
| Idivf -> "fdivd"
| _ -> Misc.fatal_error "Emit.name_for_float_operation"
let name_for_int_comparison = function
Isigned Ceq -> "be" | Isigned Cne -> "bne"
| Isigned Cle -> "ble" | Isigned Cgt -> "bg"
| Isigned Clt -> "bl" | Isigned Cge -> "bge"
| Iunsigned Ceq -> "be" | Iunsigned Cne -> "bne"
| Iunsigned Cle -> "bleu" | Iunsigned Cgt -> "bgu"
| Iunsigned Clt -> "blu" | Iunsigned Cge -> "bgeu"
let name_for_float_comparison cmp neg =
match cmp with
Ceq -> if neg then "fbne" else "fbe"
| Cne -> if neg then "fbe" else "fbne"
| Cle -> if neg then "fbug" else "fble"
| Cgt -> if neg then "fbule" else "fbg"
| Clt -> if neg then "fbuge" else "fbl"
| Cge -> if neg then "fbul" else "fbge"
(* Output the assembly code for an instruction *)
let function_name = ref ""
let tailrec_entry_point = ref 0
let rec emit_instr i dslot =
match i.desc with
Lend -> ()
| Lop(Imove | Ispill | Ireload) ->
let src = i.arg.(0) and dst = i.res.(0) in
begin match (src, dst) with
{loc = Reg rs; typ = (Int | Addr)}, {loc = Reg rd} ->
` mov {emit_reg src}, {emit_reg dst}\n`
| {loc = Reg rs; typ = Float}, {loc = Reg rd; typ = Float} ->
` fmovd {emit_reg src}, {emit_reg dst}\n`
| {loc = Reg rs; typ = Float}, {loc = Reg rd; typ = (Int | Addr)} ->
(* This happens when calling C functions and passing a float arg
in %o0...%o5 *)
` sub %sp, 8, %sp\n`;
` std {emit_reg src}, [%sp + 96]\n`;
if rd land 1 = 0 then
` ldd [%sp + 96], {emit_reg dst}\n`
else begin
` ld [%sp + 96], {emit_reg dst}\n`;
` ld [%sp + 100], {emit_reg(next_in_pair dst)}\n`
end;
` add %sp, 8, %sp\n`
| {loc = Reg rs; typ = (Int | Addr)}, {loc = Stack sd} ->
` st {emit_reg src}, {emit_stack dst}\n`
| {loc = Reg rs; typ = Float}, {loc = Stack sd} ->
` std {emit_reg src}, {emit_stack dst}\n`
| {loc = Stack ss; typ = (Int | Addr)}, {loc = Reg rd} ->
` ld {emit_stack src}, {emit_reg dst}\n`
| {loc = Stack ss; typ = Float}, {loc = Reg rd} ->
` ldd {emit_stack src}, {emit_reg dst}\n`
| (_, _) ->
fatal_error "Emit: Imove"
end
| Lop(Iconst_int n) ->
if is_native_immediate n then
` mov {emit_nativeint n}, {emit_reg i.res.(0)}\n`
else begin
` sethi %hi({emit_nativeint n}), %g1\n`;
` or %g1, %lo({emit_nativeint n}), {emit_reg i.res.(0)}\n`
end
| Lop(Iconst_float s) ->
let lbl = new_label() in
float_constants := (lbl, s) :: !float_constants;
` sethi %hi({emit_label lbl}), %g1\n`;
` ldd [%g1 + %lo({emit_label lbl})], {emit_reg i.res.(0)}\n`
| Lop(Iconst_symbol s) ->
` sethi %hi({emit_symbol s}), %g1\n`;
` or %g1, %lo({emit_symbol s}), {emit_reg i.res.(0)}\n`
| Lop(Icall_ind) ->
`{record_frame i.live} call {emit_reg i.arg.(0)}\n`;
fill_delay_slot dslot
| Lop(Icall_imm s) ->
`{record_frame i.live} call {emit_symbol s}\n`;
fill_delay_slot dslot
| Lop(Itailcall_ind) ->
let n = frame_size() in
if !contains_calls then
` ld [%sp + {emit_int(n - 4 + 96)}], %o7\n`;
` jmp {emit_reg i.arg.(0)}\n`;
` add %sp, {emit_int n}, %sp\n` (* in delay slot *)
| Lop(Itailcall_imm s) ->
let n = frame_size() in
if s = !function_name then begin
` b {emit_label !tailrec_entry_point}\n`;
fill_delay_slot dslot
end else begin
if !contains_calls then
` ld [%sp + {emit_int(n - 4 + 96)}], %o7\n`;
` sethi %hi({emit_symbol s}), %g1\n`;
` jmp %g1 + %lo({emit_symbol s})\n`;
` add %sp, {emit_int n}, %sp\n` (* in delay slot *)
end
| Lop(Iextcall(s, alloc)) ->
if alloc then begin
` sethi %hi({emit_symbol s}), %g2\n`;
`{record_frame i.live} call {emit_symbol "caml_c_call"}\n`;
` or %g2, %lo({emit_symbol s}), %g2\n` (* in delay slot *)
end else begin
` call {emit_symbol s}\n`;
fill_delay_slot dslot
end
| Lop(Istackoffset n) ->
` add %sp, {emit_int (-n)}, %sp\n`;
stack_offset := !stack_offset + n
| Lop(Iload(chunk, addr)) ->
begin match i.res.(0).typ with
Int | Addr ->
let loadinstr =
match chunk with
Word -> "ld"
| Byte_unsigned -> "ldub"
| Byte_signed -> "ldsb"
| Sixteen_unsigned -> "lduh"
| Sixteen_signed -> "ldsh" in
emit_load loadinstr addr i.arg i.res.(0)
| Float ->
emit_load "ld" addr i.arg i.res.(0);
emit_load "ld" (offset_addressing addr 4) i.arg (next_in_pair i.res.(0))
end
| Lop(Istore(chunk, addr)) ->
begin match i.arg.(0).typ with
Int | Addr ->
let storeinstr =
match chunk with
Word -> "st"
| Byte_unsigned | Byte_signed -> "stb"
| Sixteen_unsigned | Sixteen_signed -> "sth" in
emit_store storeinstr addr i.arg i.arg.(0)
| Float ->
emit_store "st" addr i.arg i.arg.(0);
emit_store "st" (offset_addressing addr 4) i.arg (next_in_pair i.arg.(0))
end
| Lop(Ialloc n) ->
if !fastcode_flag then begin
let lbl_cont = new_label() in
` ld [%l7], %g1\n`;
` sub %l6, {emit_int n}, %l6\n`;
` cmp %l6, %g1\n`;
` bgeu {emit_label lbl_cont}\n`;
` add %l6, 4, {emit_reg i.res.(0)}\n`; (* in delay slot *)
`{record_frame i.live} call {emit_symbol "caml_call_gc"}\n`;
` mov {emit_int n}, %g2\n`; (* in delay slot *)
` add %l6, 4, {emit_reg i.res.(0)}\n`;
`{emit_label lbl_cont}:\n`
end else begin
`{record_frame i.live} call {emit_symbol "caml_alloc"}\n`;
` mov {emit_int n}, %g2\n`; (* in delay slot *)
` add %l6, 4, {emit_reg i.res.(0)}\n`
end
| Lop(Iintop(Icomp cmp)) ->
let comp = name_for_int_comparison cmp in
` cmp {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
let lbl = new_label() in
` {emit_string comp},a {emit_label lbl}\n`;
` mov 1, {emit_reg i.res.(0)}\n`;
` mov 0, {emit_reg i.res.(0)}\n`;
`{emit_label lbl}:\n`
| Lop(Iintop Icheckbound) ->
` cmp {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
` tleu 5\n` (* 5 = ST_RANGE_CHECK *)
| Lop(Iintop op) ->
let instr = name_for_int_operation op in
` {emit_string instr} {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, {emit_reg i.res.(0)}\n`
| Lop(Iintop_imm(Idiv, n)) -> (* n is a power of 2 *)
let l = Misc.log2 n in
let lbl = new_label() in
` cmp {emit_reg i.arg.(0)}, 0\n`;
` bge {emit_label lbl}\n`;
` mov {emit_reg i.arg.(0)}, %g1\n`; (* in delay slot *)
` add %g1, {emit_int (n-1)}, %g1\n`;
`{emit_label lbl}:\n`;
` sra %g1, {emit_int l}, {emit_reg i.res.(0)}\n`
| Lop(Iintop_imm(Imod, n)) -> (* n is a power of 2 *)
let lbl = new_label() in
` tst {emit_reg i.arg.(0)}\n`;
` bge {emit_label lbl}\n`;
` andcc {emit_reg i.arg.(0)}, {emit_int (n-1)}, {emit_reg i.res.(0)}\n`; (* in delay slot *)
` be {emit_label lbl}\n`;
` nop\n`;
` sub {emit_reg i.res.(0)}, {emit_int n}, {emit_reg i.res.(0)}\n`;
`{emit_label lbl}:\n`
| Lop(Iintop_imm(Icomp cmp, n)) ->
let comp = name_for_int_comparison cmp in
` cmp {emit_reg i.arg.(0)}, {emit_int n}\n`;
let lbl = new_label() in
` {emit_string comp},a {emit_label lbl}\n`;
` mov 1, {emit_reg i.res.(0)}\n`;
` mov 0, {emit_reg i.res.(0)}\n`;
`{emit_label lbl}:\n`
| Lop(Iintop_imm(Icheckbound, n)) ->
` cmp {emit_reg i.arg.(0)}, {emit_int n}\n`;
` tleu 5\n` (* 5 = ST_RANGE_CHECK *)
| Lop(Iintop_imm(op, n)) ->
let instr = name_for_int_operation op in
` {emit_string instr} {emit_reg i.arg.(0)}, {emit_int n}, {emit_reg i.res.(0)}\n`
| Lop(Inegf | Iabsf as op) ->
let instr = name_for_float_operation op in
` {emit_string instr} {emit_reg i.arg.(0)}, {emit_reg i.res.(0)}\n`;
` fmovs {emit_reg(next_in_pair i.arg.(0))}, {emit_reg(next_in_pair i.res.(0))}\n`
| Lop(Iaddf | Isubf | Imulf | Idivf as op) ->
let instr = name_for_float_operation op in
` {emit_string instr} {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, {emit_reg i.res.(0)}\n`
| Lop(Ifloatofint) ->
` sub %sp, 8, %sp\n`;
` st {emit_reg i.arg.(0)}, [%sp + 96]\n`;
` ld [%sp + 96], %f30\n`;
` add %sp, 8, %sp\n`;
` fitod %f30, {emit_reg i.res.(0)}\n`
| Lop(Iintoffloat) ->
` fdtoi {emit_reg i.arg.(0)}, %f30\n`;
` sub %sp, 8, %sp\n`;
` st %f30, [%sp + 96]\n`;
` ld [%sp + 96], {emit_reg i.res.(0)}\n`;
` add %sp, 8, %sp\n`
| Lop(Ispecific sop) ->
fatal_error "Emit: specific"
| Lreloadretaddr ->
let n = frame_size() in
` ld [%sp + {emit_int(n - 4 + 96)}], %o7\n`
| Lreturn ->
let n = frame_size() in
` retl\n`;
` add %sp, {emit_int n}, %sp\n`
| Llabel lbl ->
`{emit_label lbl}:\n`
| Lbranch lbl ->
` b {emit_label lbl}\n`;
fill_delay_slot dslot
| Lcondbranch(tst, lbl) ->
begin match tst with
Itruetest ->
` tst {emit_reg i.arg.(0)}\n`;
` bne {emit_label lbl}\n`
| Ifalsetest ->
` tst {emit_reg i.arg.(0)}\n`;
` be {emit_label lbl}\n`
| Iinttest cmp ->
let comp = name_for_int_comparison cmp in
` cmp {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
` {emit_string comp} {emit_label lbl}\n`
| Iinttest_imm(cmp, n) ->
let comp = name_for_int_comparison cmp in
` cmp {emit_reg i.arg.(0)}, {emit_int n}\n`;
` {emit_string comp} {emit_label lbl}\n`
| Ifloattest(cmp, neg) ->
let comp = name_for_float_comparison cmp neg in
` fcmpd {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
` nop\n`;
` {emit_string comp} {emit_label lbl}\n`
| Ioddtest ->
` andcc {emit_reg i.arg.(0)}, 1, %g0\n`;
` bne {emit_label lbl}\n`
| Ieventest ->
` andcc {emit_reg i.arg.(0)}, 1, %g0\n`;
` be {emit_label lbl}\n`
end;
fill_delay_slot dslot
| Lcondbranch3(lbl0, lbl1, lbl2) ->
` cmp {emit_reg i.arg.(0)}, 1\n`;
begin match lbl0 with
None -> ()
| Some lbl -> ` bl {emit_label lbl}\n nop\n`
end;
begin match lbl1 with
None -> ()
| Some lbl -> ` be {emit_label lbl}\n nop\n`
end;
begin match lbl2 with
None -> ()
| Some lbl -> ` bg {emit_label lbl}\n nop\n`
end
| Lswitch jumptbl ->
let lbl_jumptbl = new_label() in
` sethi %hi({emit_label lbl_jumptbl}), %g1\n`;
` or %g1, %lo({emit_label lbl_jumptbl}), %g1\n`;
` sll {emit_reg i.arg.(0)}, 2, %g2\n`;
` ld [%g1 + %g2], %g1\n`;
` jmp %g1\n`; (* poor scheduling *)
` nop\n`;
`{emit_label lbl_jumptbl}:`;
for i = 0 to Array.length jumptbl - 1 do
` .word {emit_label jumptbl.(i)}\n`
done
| Lsetuptrap lbl ->
` call {emit_label lbl}\n`;
` sub %sp, 8, %sp\n` (* in delay slot *)
| Lpushtrap ->
stack_offset := !stack_offset + 8;
` st %o7, [%sp + 96]\n`;
` st %l5, [%sp + 100]\n`;
` mov %sp, %l5\n`
| Lpoptrap ->
` ld [%sp + 100], %l5\n`;
` add %sp, 8, %sp\n`;
stack_offset := !stack_offset - 8
| Lraise ->
` ld [%l5 + 96], %g1\n`;
` mov %l5, %sp\n`;
` ld [%sp + 100], %l5\n`;
` jmp %g1 + 8\n`;
` add %sp, 8, %sp\n`
and fill_delay_slot = function
None -> ` nop\n`
| Some i -> emit_instr i None
(* Checks if a pseudo-instruction expands to exactly one machine instruction
that does not branch. *)
let is_one_instr_op = function
Idiv | Imod | Icomp _ | Icheckbound -> false
| _ -> true
let is_one_instr i =
match i.desc with
Lop op ->
begin match op with
Imove | Ispill | Ireload -> i.arg.(0).typ = i.res.(0).typ
| Iconst_int n -> is_native_immediate n
| Istackoffset _ -> true
| Iload(_, Iindexed n) -> i.res.(0).typ <> Float & is_immediate n
| Iload(_, Iindexed2 n) -> i.res.(0).typ <> Float & n = 0
| Istore(_, Iindexed n) -> i.arg.(0).typ <> Float & is_immediate n
| Istore(_, Iindexed2 n) -> i.arg.(0).typ <> Float & n = 0
| Iintop(op) -> is_one_instr_op op
| Iintop_imm(op, _) -> is_one_instr_op op
| Iaddf | Isubf | Imulf | Idivf -> true
| _ -> false
end
| _ -> false
let no_interference res arg =
try
for i = 0 to Array.length arg - 1 do
for j = 0 to Array.length res - 1 do
if arg.(i).loc = res.(j).loc then raise Exit
done
done;
true
with Exit ->
false
(* Emit a sequence of instructions, trying to fill delay slots for branches *)
let rec emit_all i =
match i with
{desc = Lend} -> ()
| {next = {desc = Lop(Icall_imm _) | Lop(Iextcall(_, false)) | Lbranch _}}
when is_one_instr i ->
emit_instr i.next (Some i);
emit_all i.next.next
| {next = {desc = Lop(Itailcall_imm s)}}
when s = !function_name & is_one_instr i ->
emit_instr i.next (Some i);
emit_all i.next.next
| {next = {desc = Lop(Icall_ind)}}
when is_one_instr i & no_interference i.res i.next.arg ->
emit_instr i.next (Some i);
emit_all i.next.next
| {next = {desc = Lcondbranch(_, _)}}
when is_one_instr i & no_interference i.res i.next.arg ->
emit_instr i.next (Some i);
emit_all i.next.next
| _ ->
emit_instr i None;
emit_all i.next
(* Emission of a function declaration *)
let fundecl fundecl =
function_name := fundecl.fun_name;
fastcode_flag := fundecl.fun_fast;
tailrec_entry_point := new_label();
stack_offset := 0;
float_constants := [];
` .text\n`;
` .align 4\n`;
` .global {emit_symbol fundecl.fun_name}\n`;
`{emit_symbol fundecl.fun_name}:\n`;
let n = frame_size() in
if n > 0 then
` sub %sp, {emit_int n}, %sp\n`;
if !contains_calls then
` st %o7, [%sp + {emit_int(n - 4 + 96)}]\n`;
`{emit_label !tailrec_entry_point}:\n`;
emit_all fundecl.fun_body;
List.iter emit_float_constant !float_constants
(* Emission of data *)
let emit_item = function
Cdefine_symbol s ->
` .global {emit_symbol s}\n`;
`{emit_symbol s}:\n`
| Cdefine_label lbl ->
`{emit_label (lbl + 10000)}:\n`
| Cint8 n ->
` .byte {emit_int n}\n`
| Cint16 n ->
` .half {emit_int n}\n`
| Cint n ->
` .word {emit_nativeint n}\n`
| Cfloat f ->
` .double 0r{emit_string f}\n`
| Csymbol_address s ->
` .word {emit_symbol s}\n`
| Clabel_address lbl ->
` .word {emit_label (lbl + 10000)}\n`
| Cstring s ->
let l = String.length s in
if l = 0 then ()
else if l < 80 then
` .ascii {emit_string_literal s}\n`
else begin
let i = ref 0 in
while !i < l do
let n = min (l - !i) 80 in
` .ascii {emit_string_literal(String.sub s !i n)}\n`;
i := !i + n
done
end
| Cskip n ->
if n > 0 then ` .skip {emit_int n}\n`
| Calign n ->
` .align {emit_int n}\n`
let data l =
` .data\n`;
List.iter emit_item l
(* Beginning / end of an assembly file *)
let begin_assembly() =
let lbl_begin = Compilenv.current_unit_name() ^ "_data_begin" in
` .data\n`;
` .global {emit_symbol lbl_begin}\n`;
`{emit_symbol lbl_begin}:\n`;
let lbl_begin = Compilenv.current_unit_name() ^ "_code_begin" in
` .text\n`;
` .global {emit_symbol lbl_begin}\n`;
`{emit_symbol lbl_begin}:\n`
let end_assembly() =
` .text\n`;
let lbl_end = Compilenv.current_unit_name() ^ "_code_end" in
` .global {emit_symbol lbl_end}\n`;
`{emit_symbol lbl_end}:\n`;
` .data\n`;
let lbl_end = Compilenv.current_unit_name() ^ "_data_end" in
` .global {emit_symbol lbl_end}\n`;
`{emit_symbol lbl_end}:\n`;
` .word 0\n`;
let lbl = Compilenv.current_unit_name() ^ "_frametable" in
` .global {emit_symbol lbl}\n`;
`{emit_symbol lbl}:\n`;
` .word {emit_int (List.length !frame_descriptors)}\n`;
List.iter emit_frame !frame_descriptors;
frame_descriptors := []

View File

@ -11,7 +11,7 @@
(* $Id$ *)
(* Description of the HP PA-RICS processor *)
(* Description of the HP PA-RISC processor *)
open Misc
open Cmm
@ -93,90 +93,8 @@ let phys_reg n =
let stack_slot slot ty =
Reg.at_location ty (Stack slot)
(* Exceptions raised to signal cases not handled here *)
exception Use_default
(* Recognition of addressing modes *)
let select_addressing = function
Cconst_symbol s ->
(Ibased(s, 0), Ctuple [])
| Cop(Cadda, [Cconst_symbol s; Cconst_int n]) ->
(Ibased(s, n), Ctuple [])
| Cop(Cadda, [arg; Cconst_int n]) ->
(Iindexed n, arg)
| Cop(Cadda, [arg1; Cop(Caddi, [arg2; Cconst_int n])]) ->
(Iindexed n, Cop(Cadda, [arg1; arg2]))
| arg ->
(Iindexed 0, arg)
(* Instruction selection *)
let shiftadd = function
2 -> Ishift1add
| 4 -> Ishift2add
| 8 -> Ishift3add
| _ -> fatal_error "Proc_hppa.shiftadd"
let select_oper op args =
match (op, args) with
(* Recognize shift-add operations. *)
((Caddi|Cadda),
[arg2; Cop(Clsl, [arg1; Cconst_int(1|2|3 as shift)])]) ->
(Ispecific(shiftadd(1 lsl shift)), [arg1; arg2])
| ((Caddi|Cadda),
[arg2; Cop(Cmuli, [arg1; Cconst_int(2|4|8 as mult)])]) ->
(Ispecific(shiftadd mult), [arg1; arg2])
| ((Caddi|Cadda),
[arg2; Cop(Cmuli, [Cconst_int(2|4|8 as mult); arg1])]) ->
(Ispecific(shiftadd mult), [arg1; arg2])
| (Caddi, [Cop(Clsl, [arg1; Cconst_int(1|2|3 as shift)]); arg2]) ->
(Ispecific(shiftadd(1 lsl shift)), [arg1; arg2])
| (Caddi, [Cop(Cmuli, [arg1; Cconst_int(2|4|8 as mult)]); arg2]) ->
(Ispecific(shiftadd mult), [arg1; arg2])
| (Caddi, [Cop(Cmuli, [Cconst_int(2|4|8 as mult); arg1]); arg2]) ->
(Ispecific(shiftadd mult), [arg1; arg2])
(* Prevent the recognition of some immediate arithmetic operations *)
(* Cmuli : -> Ilsl if power of 2
Cdivi, Cmodi : only if power of 2
Cand, Cor, Cxor : never *)
| (Cmuli, ([arg1; Cconst_int n] as args)) ->
let l = Misc.log2 n in
if n = 1 lsl l
then (Iintop_imm(Ilsl, l), [arg1])
else (Iintop Imul, args)
| (Cmuli, ([Cconst_int n; arg1] as args)) ->
let l = Misc.log2 n in
if n = 1 lsl l
then (Iintop_imm(Ilsl, l), [arg1])
else (Iintop Imul, args)
| (Cmuli, args) -> (Iintop Imul, args)
| (Cdivi, [arg1; Cconst_int n]) when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Idiv, n), [arg1])
| (Cdivi, args) -> (Iintop Idiv, args)
| (Cmodi, [arg1; Cconst_int n]) when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Imod, n), [arg1])
| (Cmodi, args) -> (Iintop Imod, args)
| (Cand, args) -> (Iintop Iand, args)
| (Cor, args) -> (Iintop Ior, args)
| (Cxor, args) -> (Iintop Ixor, args)
| _ -> raise Use_default
let select_store addr exp = raise Use_default
let select_push exp = fatal_error "Proc: select_push"
let pseudoregs_for_operation op arg res =
match op with
Iintop(Idiv | Imod) -> (* handled via calls to millicode *)
([|phys_reg 20; phys_reg 19|], [|phys_reg 22|], true)
(* %r26, %r25, %r29 *)
| _ ->
raise Use_default
let is_immediate n = (n < 16) & (n >= -16) (* 5 bits *)
let word_addressed = false
(* Calling conventions *)
@ -252,8 +170,6 @@ let loc_external_arguments arg =
done;
(loc, Misc.align !ofs 8)
let extcall_use_push = false
let loc_external_results res =
let (loc, ofs) = calling_conventions 21 21 100 100 not_supported res in loc
@ -292,24 +208,6 @@ let max_register_pressure = function
| Iintop(Idiv | Imod) -> [| 19; 27 |]
| _ -> [| 23; 27 |]
(* Reloading *)
let reload_test makereg round tst args = raise Use_default
let reload_operation makereg round op args res = raise Use_default
(* Latencies (in cycles). Roughly based on the ``Mustang'' chips. *)
let need_scheduling = true
let oper_latency = function
Ireload -> 2
| Iload(_, _) -> 2
| Iconst_float _ -> 2 (* turned into a load *)
| Iintop Imul -> 2 (* ends up with a load *)
| Iaddf | Isubf | Imulf -> 3
| Idivf -> 12
| _ -> 1
(* Layout of the stack *)
let num_stack_slots = [| 0; 0 |]

17
asmcomp/hppa/reload.ml Normal file
View File

@ -0,0 +1,17 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Reloading for the HPPA *)
let fundecl f =
(new Reloadgen.reload_generic ())#fundecl f

View File

@ -0,0 +1,59 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Instruction scheduling for the HPPA *)
open Arch
open Mach
class scheduler () as self =
inherit Schedgen.scheduler_generic () as super
(* Latencies (in cycles). Roughly based on the ``Mustang'' chips. *)
method oper_latency = function
Ireload -> 2
| Iload(_, _) -> 2
| Iconst_float _ -> 2 (* turned into a load *)
| Iintop Imul -> 2 (* ends up with a load *)
| Iaddf | Isubf | Imulf -> 3
| Idivf -> 12
| _ -> 1
(* Issue cycles. Rough approximations. *)
method oper_issue_cycles = function
Iconst_float _ -> 3
| Iconst_symbol _ -> 2
| Iload(_, Ibased(_, _)) -> 2
| Istore(_, Ibased(_, _)) -> 2
| Ialloc _ -> 5
| Iintop Imul -> 10
| Iintop Ilsl -> 3
| Iintop Ilsr -> 2
| Iintop Iasr -> 3
| Iintop(Icomp _) -> 2
| Iintop(Icheckbound) -> 2
| Iintop_imm(Idiv, _) -> 4
| Iintop_imm(Imod, _) -> 5
| Iintop_imm(Icomp _, _) -> 2
| Iintop_imm(Icheckbound, _) -> 2
| Ifloatofint -> 4
| Iintoffloat -> 4
| _ -> 1
end
let fundecl f = (new scheduler ())#schedule_fundecl f

93
asmcomp/hppa/selection.ml Normal file
View File

@ -0,0 +1,93 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1997 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Instruction selection for the HPPA processor *)
open Misc
open Cmm
open Reg
open Arch
open Mach
let shiftadd = function
2 -> Ishift1add
| 4 -> Ishift2add
| 8 -> Ishift3add
| _ -> fatal_error "Proc_hppa.shiftadd"
class selector () as self =
inherit Selectgen.selector_generic() as super
method is_immediate n = (n < 16) && (n >= -16) (* 5 bits *)
method select_addressing = function
Cconst_symbol s ->
(Ibased(s, 0), Ctuple [])
| Cop(Cadda, [Cconst_symbol s; Cconst_int n]) ->
(Ibased(s, n), Ctuple [])
| Cop(Cadda, [arg; Cconst_int n]) ->
(Iindexed n, arg)
| Cop(Cadda, [arg1; Cop(Caddi, [arg2; Cconst_int n])]) ->
(Iindexed n, Cop(Cadda, [arg1; arg2]))
| arg ->
(Iindexed 0, arg)
method select_operation op args =
match (op, args) with
(* Recognize shift-add operations. *)
((Caddi|Cadda),
[arg2; Cop(Clsl, [arg1; Cconst_int(1|2|3 as shift)])]) ->
(Ispecific(shiftadd(1 lsl shift)), [arg1; arg2])
| ((Caddi|Cadda),
[arg2; Cop(Cmuli, [arg1; Cconst_int(2|4|8 as mult)])]) ->
(Ispecific(shiftadd mult), [arg1; arg2])
| ((Caddi|Cadda),
[arg2; Cop(Cmuli, [Cconst_int(2|4|8 as mult); arg1])]) ->
(Ispecific(shiftadd mult), [arg1; arg2])
| (Caddi, [Cop(Clsl, [arg1; Cconst_int(1|2|3 as shift)]); arg2]) ->
(Ispecific(shiftadd(1 lsl shift)), [arg1; arg2])
| (Caddi, [Cop(Cmuli, [arg1; Cconst_int(2|4|8 as mult)]); arg2]) ->
(Ispecific(shiftadd mult), [arg1; arg2])
| (Caddi, [Cop(Cmuli, [Cconst_int(2|4|8 as mult); arg1]); arg2]) ->
(Ispecific(shiftadd mult), [arg1; arg2])
(* Prevent the recognition of some immediate arithmetic operations *)
(* Cmuli : -> Ilsl if power of 2
Cdivi, Cmodi : only if power of 2
Cand, Cor, Cxor : never *)
| (Cmuli, ([arg1; Cconst_int n] as args)) ->
let l = Misc.log2 n in
if n = 1 lsl l
then (Iintop_imm(Ilsl, l), [arg1])
else (Iintop Imul, args)
| (Cmuli, ([Cconst_int n; arg1] as args)) ->
let l = Misc.log2 n in
if n = 1 lsl l
then (Iintop_imm(Ilsl, l), [arg1])
else (Iintop Imul, args)
| (Cmuli, args) -> (Iintop Imul, args)
| (Cdivi, [arg1; Cconst_int n]) when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Idiv, n), [arg1])
| (Cdivi, args) -> (Iintop Idiv, args)
| (Cmodi, [arg1; Cconst_int n]) when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Imod, n), [arg1])
| (Cmodi, args) -> (Iintop Imod, args)
| (Cand, args) -> (Iintop Iand, args)
| (Cor, args) -> (Iintop Ior, args)
| (Cxor, args) -> (Iintop Ixor, args)
| _ ->
super#select_operation op args
end
let fundecl f = (new selector ())#emit_fundecl f

View File

@ -504,8 +504,6 @@ let emit_instr i =
| Lop(Ispecific(Istore_symbol(s, addr))) ->
add_used_symbol s ;
` mov DWORD PTR {emit_addressing addr i.arg 0},OFFSET {emit_symbol s}\n`
| Lop(Ispecific(Ioffset_loc(n, addr))) ->
` add DWORD PTR {emit_addressing addr i.arg 0},{emit_int n}\n`
| Lop(Ispecific(Ipush)) ->
(* Push arguments in reverse order *)
for n = Array.length i.arg - 1 downto 0 do

174
asmcomp/i386/proc.ml Normal file
View File

@ -0,0 +1,174 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Description of the Intel 386 processor *)
open Misc
open Arch
open Format
open Cmm
open Reg
open Mach
(* Registers available for register allocation *)
(* Register map:
eax 0 eax - edi: function arguments and results
ebx 1 eax: C function results
ecx 2 ebx, esi, edi, ebp: preserved by C
edx 3
esi 4
edi 5
ebp 6
tos 100 top of floating-point stack. *)
let int_reg_name =
[| "%eax"; "%ebx"; "%ecx"; "%edx"; "%esi"; "%edi"; "%ebp" |]
let float_reg_name =
[| "%tos" |]
let num_register_classes = 2
let register_class r =
match r.typ with
Int -> 0
| Addr -> 0
| Float -> 1
let num_available_registers = [| 7; 0 |]
let first_available_register = [| 0; 100 |]
let register_name r =
if r < 100 then int_reg_name.(r) else float_reg_name.(r - 100)
(* There is little scheduling, and some operations are more compact
when their argument is %eax. *)
let rotate_registers = false
(* Representation of hard registers by pseudo-registers *)
let hard_int_reg =
let v = Array.create 7 Reg.dummy in
for i = 0 to 6 do v.(i) <- Reg.at_location Int (Reg i) done;
v
let hard_float_reg = [| Reg.at_location Float (Reg 100) |]
let all_phys_regs =
Array.append hard_int_reg hard_float_reg
let phys_reg n =
if n < 100 then hard_int_reg.(n) else hard_float_reg.(n - 100)
let eax = phys_reg 0
let ecx = phys_reg 2
let edx = phys_reg 3
let tos = phys_reg 100
let stack_slot slot ty =
Reg.at_location ty (Stack slot)
(* Instruction selection *)
let word_addressed = false
(* Calling conventions *)
let calling_conventions first_int last_int first_float last_float make_stack
arg =
let loc = Array.create (Array.length arg) Reg.dummy in
let int = ref first_int in
let float = ref first_float in
let ofs = ref 0 in
for i = 0 to Array.length arg - 1 do
match arg.(i).typ with
Int | Addr as ty ->
if !int <= last_int then begin
loc.(i) <- phys_reg !int;
incr int
end else begin
loc.(i) <- stack_slot (make_stack !ofs) ty;
ofs := !ofs + size_int
end
| Float ->
if !float <= last_float then begin
loc.(i) <- phys_reg !float;
incr float
end else begin
loc.(i) <- stack_slot (make_stack !ofs) Float;
ofs := !ofs + size_float
end
done;
(loc, !ofs)
let incoming ofs = Incoming ofs
let outgoing ofs = Outgoing ofs
let not_supported ofs = fatal_error "Proc.loc_results: cannot call"
let loc_arguments arg =
calling_conventions 0 5 100 99 outgoing arg
let loc_parameters arg =
let (loc, ofs) = calling_conventions 0 5 100 99 incoming arg in loc
let loc_results res =
let (loc, ofs) = calling_conventions 0 5 100 100 not_supported res in loc
let extcall_use_push = true
let loc_external_arguments arg =
fatal_error "Proc.loc_external_arguments"
let loc_external_results res =
let (loc, ofs) = calling_conventions 0 0 100 100 not_supported res in loc
let loc_exn_bucket = eax
(* Registers destroyed by operations *)
let destroyed_at_c_call = (* ebx, esi, edi, ebp preserved *)
[|eax; ecx; edx|]
let destroyed_at_oper = function
Iop(Icall_ind | Icall_imm _ | Iextcall(_, true)) -> all_phys_regs
| Iop(Iextcall(_, false)) -> destroyed_at_c_call
| Iop(Iintop(Idiv | Imod)) -> [| eax; edx |]
| Iop(Iintop_imm(Imod, _)) -> [| eax |]
| Iop(Ialloc _) -> [| eax |]
| Iop(Iintop(Icomp _) | Iintop_imm(Icomp _, _)) -> [| eax |]
| Iop(Iintoffloat) -> [| eax |]
| Iifthenelse(Ifloattest(_, _), _, _) -> [| eax |]
| _ -> [||]
let destroyed_at_raise = all_phys_regs
(* Maximal register pressure *)
let safe_register_pressure op = 4
let max_register_pressure = function
Iextcall(_, _) -> [| 4; max_int |]
| Iintop(Idiv | Imod) -> [| 5; max_int |]
| Ialloc _ | Iintop(Icomp _) | Iintop_imm(Icomp _, _) |
Iintoffloat -> [| 6; max_int |]
| _ -> [|7; max_int |]
(* Layout of the stack frame *)
let num_stack_slots = [| 0; 0 |]
let contains_calls = ref false
(* Calling the assembler *)
let assemble_file infile outfile =
Ccomp.command ("as -o " ^ outfile ^ " " ^ infile)

177
asmcomp/i386/proc_nt.ml Normal file
View File

@ -0,0 +1,177 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Description of the Intel 386 processor, for Windows NT *)
open Misc
open Arch
open Format
open Cmm
open Reg
open Mach
(* Registers available for register allocation *)
(* Register map:
eax 0 eax - edi: function arguments and results
ebx 1 eax: C function results
ecx 2 ebx, esi, edi, ebp: preserved by C
edx 3
esi 4
edi 5
ebp 6
tos 100 top of floating-point stack. *)
let int_reg_name =
[| "eax"; "ebx"; "ecx"; "edx"; "esi"; "edi"; "ebp" |]
let float_reg_name =
[| "tos" |]
let num_register_classes = 2
let register_class r =
match r.typ with
Int -> 0
| Addr -> 0
| Float -> 1
let num_available_registers = [| 7; 0 |]
let first_available_register = [| 0; 100 |]
let register_name r =
if r < 100 then int_reg_name.(r) else float_reg_name.(r - 100)
(* There is little scheduling, and some operations are more compact
when their argument is %eax. *)
let rotate_registers = false
(* Representation of hard registers by pseudo-registers *)
let hard_int_reg =
let v = Array.create 7 Reg.dummy in
for i = 0 to 6 do v.(i) <- Reg.at_location Int (Reg i) done;
v
let hard_float_reg = [| Reg.at_location Float (Reg 100) |]
let all_phys_regs =
Array.append hard_int_reg hard_float_reg
let phys_reg n =
if n < 100 then hard_int_reg.(n) else hard_float_reg.(n - 100)
let eax = phys_reg 0
let ecx = phys_reg 2
let edx = phys_reg 3
let tos = phys_reg 100
let stack_slot slot ty =
Reg.at_location ty (Stack slot)
(* Instruction selection *)
let word_addressed = false
(* Calling conventions *)
let calling_conventions first_int last_int first_float last_float make_stack
arg =
let loc = Array.create (Array.length arg) Reg.dummy in
let int = ref first_int in
let float = ref first_float in
let ofs = ref 0 in
for i = 0 to Array.length arg - 1 do
match arg.(i).typ with
Int | Addr as ty ->
if !int <= last_int then begin
loc.(i) <- phys_reg !int;
incr int
end else begin
loc.(i) <- stack_slot (make_stack !ofs) ty;
ofs := !ofs + size_int
end
| Float ->
if !float <= last_float then begin
loc.(i) <- phys_reg !float;
incr float
end else begin
loc.(i) <- stack_slot (make_stack !ofs) Float;
ofs := !ofs + size_float
end
done;
(loc, !ofs)
let incoming ofs = Incoming ofs
let outgoing ofs = Outgoing ofs
let not_supported ofs = fatal_error "Proc.loc_results: cannot call"
let loc_arguments arg =
calling_conventions 0 5 100 99 outgoing arg
let loc_parameters arg =
let (loc, ofs) = calling_conventions 0 5 100 99 incoming arg in loc
let loc_results res =
let (loc, ofs) = calling_conventions 0 5 100 100 not_supported res in loc
let extcall_use_push = true
let loc_external_arguments arg =
fatal_error "Proc.loc_external_arguments"
let loc_external_results res =
let (loc, ofs) = calling_conventions 0 0 100 100 not_supported res in loc
let loc_exn_bucket = eax
(* Registers destroyed by operations *)
let destroyed_at_c_call = (* ebx, esi, edi, ebp preserved *)
Array.of_list(List.map phys_reg [0;2;3])
let destroyed_at_oper = function
Iop(Icall_ind | Icall_imm _ | Iextcall(_, true)) -> all_phys_regs
| Iop(Iextcall(_, false)) -> destroyed_at_c_call
| Iop(Iintop(Idiv | Imod)) -> [| eax; edx |]
| Iop(Iintop_imm(Imod, _)) -> [| eax |]
| Iop(Ialloc _) -> [| eax |]
| Iop(Iintop(Icomp _) | Iintop_imm(Icomp _, _)) -> [| eax |]
| Iop(Iintoffloat) -> [| eax |]
| Iifthenelse(Ifloattest(_, _), _, _) -> [| eax |]
| _ -> [||]
let destroyed_at_raise = all_phys_regs
(* Maximal register pressure *)
let safe_register_pressure op = 4
let max_register_pressure = function
Iextcall(_, _) -> [| 4; max_int |]
| Iintop(Idiv | Imod) -> [| 5; max_int |]
| Ialloc _ | Iintop(Icomp _) | Iintop_imm(Icomp _, _) |
Iintoffloat -> [| 6; max_int |]
| _ -> [|7; max_int |]
(* Layout of the stack frame *)
let num_stack_slots = [| 0; 0 |]
let contains_calls = ref false
(* Calling the assembler *)
let assemble_file infile outfile =
Ccomp.command ("ml /nologo /coff /Cp /c /Fo" ^ outfile ^ " " ^ infile ^ ">NUL")
(* /Cp preserve case of all used identifiers
/c assemble only
/Fo output file name *)

69
asmcomp/i386/reload.ml Normal file
View File

@ -0,0 +1,69 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
open Cmm
open Arch
open Reg
open Mach
(* Reloading for the Intel x86 *)
let stackp r =
match r.loc with
Stack _ -> true
| _ -> false
class reload () as self =
inherit Reloadgen.reload_generic () as super
method makereg r =
match r.typ with
Float -> r
| _ -> super#makereg r
(* By overriding makereg, we make sure that pseudoregs of type float
will never be reloaded. Hence there is no need to make special cases for
floating-point operations. *)
method reload_operation op arg res =
match op with
Iintop(Iadd|Isub|Imul|Iand|Ior|Ixor|Icomp _|Icheckbound) ->
(* One of the two arguments can reside in the stack *)
if stackp arg.(0) && stackp arg.(1)
then ([|arg.(0); self#makereg arg.(1)|], res)
else (arg, res)
| Iintop(Ilsl|Ilsr|Iasr) | Iintop_imm(_, _) | Ifloatofint | Iintoffloat |
Ispecific(Ipush) ->
(* The argument(s) can be either in register or on stack *)
(arg, res)
| _ -> (* Other operations: all args and results in registers *)
super#reload_operation op arg res
method reload_test tst arg =
match tst with
Iinttest cmp ->
(* One of the two arguments can reside on stack *)
if stackp arg.(0) && stackp arg.(1)
then [| self#makereg arg.(0); arg.(1) |]
else arg
| _ ->
(* The argument(s) can be either in register or on stack *)
arg
end
let fundecl f =
(new reload ())#fundecl f

View File

@ -0,0 +1,21 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
open Schedgen (* to create a dependency *)
(* Scheduling is turned off because our model does not fit the 486
nor Pentium very well. In particular, it messes up with the
float reg stack. The Pentium Pro schedules at run-time much better
than what we could do. *)
let fundecl f = f

262
asmcomp/i386/selection.ml Normal file
View File

@ -0,0 +1,262 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1997 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Instruction selection for the Intel x86 *)
open Misc
open Arch
open Proc
open Cmm
open Reg
open Mach
(* Auxiliary for recognizing addressing modes *)
type addressing_expr =
Asymbol of string
| Alinear of expression
| Aadd of expression * expression
| Ascale of expression * int
| Ascaledadd of expression * expression * int
let rec select_addr exp =
match exp with
Cconst_symbol s ->
(Asymbol s, 0)
| Cop((Caddi | Cadda), [arg; Cconst_int m]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop((Csubi | Csuba), [arg; Cconst_int m]) ->
let (a, n) = select_addr arg in (a, n - m)
| Cop((Caddi | Cadda), [Cconst_int m; arg]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop(Clsl, [arg; Cconst_int(1|2|3 as shift)]) ->
begin match select_addr arg with
(Alinear e, n) -> (Ascale(e, 1 lsl shift), n lsl shift)
| _ -> (Alinear exp, 0)
end
| Cop(Cmuli, [arg; Cconst_int(2|4|8 as mult)]) ->
begin match select_addr arg with
(Alinear e, n) -> (Ascale(e, mult), n * mult)
| _ -> (Alinear exp, 0)
end
| Cop(Cmuli, [Cconst_int(2|4|8 as mult); arg]) ->
begin match select_addr arg with
(Alinear e, n) -> (Ascale(e, mult), n * mult)
| _ -> (Alinear exp, 0)
end
| Cop((Caddi | Cadda), [arg1; arg2]) ->
begin match (select_addr arg1, select_addr arg2) with
((Alinear e1, n1), (Alinear e2, n2)) ->
(Aadd(e1, e2), n1 + n2)
| ((Alinear e1, n1), (Ascale(e2, scale), n2)) ->
(Ascaledadd(e1, e2, scale), n1 + n2)
| ((Ascale(e1, scale), n1), (Alinear e2, n2)) ->
(Ascaledadd(e2, e1, scale), n1 + n2)
| (_, (Ascale(e2, scale), n2)) ->
(Ascaledadd(arg1, e2, scale), n2)
| ((Ascale(e1, scale), n1), _) ->
(Ascaledadd(arg2, e1, scale), n1)
| _ ->
(Aadd(arg1, arg2), 0)
end
| arg ->
(Alinear arg, 0)
(* Estimate number of float temporaries needed to evaluate expression
(Ershov's algorithm) *)
let rec float_needs = function
Cop((Caddf | Csubf | Cmulf | Cdivf), [arg1; arg2]) ->
let n1 = float_needs arg1 in
let n2 = float_needs arg2 in
if n1 = n2 then 1 + n1 else if n1 > n2 then n1 else n2
| _ ->
1
(* Special constraints on operand and result registers *)
exception Use_default
let eax = phys_reg 0
let ecx = phys_reg 2
let edx = phys_reg 3
let tos = phys_reg 100
let pseudoregs_for_operation op arg res =
match op with
(* Two-address binary operations *)
Iintop(Iadd|Isub|Imul|Iand|Ior|Ixor) ->
([|res.(0); arg.(1)|], res, false)
(* Two-address unary operations *)
| Iintop_imm((Iadd|Isub|Imul|Idiv|Iand|Ior|Ixor|Ilsl|Ilsr|Iasr), _) ->
(res, res, false)
(* For shifts with variable shift count, second arg must be in ecx *)
| Iintop(Ilsl|Ilsr|Iasr) ->
([|res.(0); ecx|], res, false)
(* For div and mod, first arg must be in eax, edx is clobbered,
and result is in eax or edx respectively.
Keep it simple, just force second argument in ecx. *)
| Iintop(Idiv) ->
([| eax; ecx |], [| eax |], true)
| Iintop(Imod) ->
([| eax; ecx |], [| edx |], true)
(* For mod with immediate operand, arg must not be in eax.
Keep it simple, force it in edx. *)
| Iintop_imm(Imod, _) ->
([| edx |], [| edx |], true)
(* For floating-point operations, the result is always left at the
top of the floating-point stack *)
| Iconst_float _ | Inegf | Iabsf | Iaddf | Isubf | Imulf | Idivf
| Ifloatofint |Ispecific(Isubfrev | Idivfrev | Ifloatarithmem(_, _)) ->
(arg, [| tos |], false) (* don't move it immediately *)
(* Same for a floating-point load *)
| Iload(Word, addr) when res.(0).typ = Float ->
(arg, [| tos |], false)
(* For storing a byte, the argument must be in eax...edx.
For storing a halfword, any reg is ok.
Keep it simple, just force it to be in edx in both cases. *)
| Istore(Word, addr) -> raise Use_default
| Istore(chunk, addr) ->
let newarg = Array.copy arg in
newarg.(0) <- edx;
(newarg, res, false)
(* Other instructions are regular *)
| _ -> raise Use_default
(* The selector class *)
class selector () as self =
inherit Selectgen.selector_generic() as super
method is_immediate (n : int) = true
method select_addressing exp =
match select_addr exp with
(Asymbol s, d) ->
(Ibased(s, d), Ctuple [])
| (Alinear e, d) ->
(Iindexed d, e)
| (Aadd(e1, e2), d) ->
(Iindexed2 d, Ctuple[e1; e2])
| (Ascale(e, scale), d) ->
(Iscaled(scale, d), e)
| (Ascaledadd(e1, e2, scale), d) ->
(Iindexed2scaled(scale, d), Ctuple[e1; e2])
method select_store addr exp =
match exp with
Cconst_int n -> (Ispecific(Istore_int(n, addr)), Ctuple [])
| Cconst_pointer n -> (Ispecific(Istore_int(n, addr)), Ctuple [])
| Cconst_symbol s -> (Ispecific(Istore_symbol(s, addr)), Ctuple [])
| _ -> super#select_store addr exp
method select_operation op args =
match op with
(* Recognize the LEA instruction *)
Caddi | Cadda | Csubi | Csuba ->
begin match self#select_addressing (Cop(op, args)) with
(Iindexed d, _) -> super#select_operation op args
| (Iindexed2 0, _) -> super#select_operation op args
| (addr, arg) -> (Ispecific(Ilea addr), [arg])
end
(* Recognize (x / cst) and (x % cst) only if cst is a power of 2. *)
| Cdivi ->
begin match args with
[arg1; Cconst_int n] when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Idiv, n), [arg1])
| _ -> (Iintop Idiv, args)
end
| Cmodi ->
begin match args with
[arg1; Cconst_int n] when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Imod, n), [arg1])
| _ -> (Iintop Imod, args)
end
(* Recognize float arithmetic with memory.
In passing, apply Ershov's algorithm to reduce stack usage *)
| Caddf ->
self#select_floatarith Iaddf Iaddf Ifloatadd Ifloatadd args
| Csubf ->
self#select_floatarith Isubf (Ispecific Isubfrev) Ifloatsub Ifloatsubrev args
| Cmulf ->
self#select_floatarith Imulf Imulf Ifloatmul Ifloatmul args
| Cdivf ->
self#select_floatarith Idivf (Ispecific Idivfrev) Ifloatdiv Ifloatdivrev args
| _ -> super#select_operation op args
(* Recognize float arithmetic with mem *)
method select_floatarith regular_op reversed_op mem_op mem_rev_op args =
match args with
[arg1; Cop(Cload _, [loc2])] ->
let (addr, arg2) = self#select_addressing loc2 in
(Ispecific(Ifloatarithmem(mem_op, addr)), [arg1; arg2])
| [Cop(Cload _, [loc1]); arg2] ->
let (addr, arg1) = self#select_addressing loc1 in
(Ispecific(Ifloatarithmem(mem_rev_op, addr)), [arg2; arg1])
| [arg1; arg2] ->
(* Evaluate bigger subexpression first to minimize stack usage.
Because of right-to-left evaluation, rightmost arg is evaluated
first *)
if float_needs arg1 <= float_needs arg2
then (regular_op, [arg1; arg2])
else (reversed_op, [arg2; arg1])
| _ ->
fatal_error "Proc_i386: select_floatarith"
(* Deal with register constraints *)
method insert_op op rs rd =
try
let (rsrc, rdst, move_res) = pseudoregs_for_operation op rs rd in
self#insert_moves rs rsrc;
self#insert (Iop op) rsrc rdst;
if move_res then begin
self#insert_moves rdst rd;
rd
end else
rdst
with Use_default ->
super#insert_op op rs rd
(* Selection of push instructions for external calls *)
method select_push exp =
match exp with
Cconst_int n -> (Ispecific(Ipush_int n), Ctuple [])
| Cconst_pointer n -> (Ispecific(Ipush_int n), Ctuple [])
| Cconst_symbol s -> (Ispecific(Ipush_symbol s), Ctuple [])
| Cop(Cload ty, [loc]) when ty = typ_float ->
let (addr, arg) = self#select_addressing loc in
(Ispecific(Ipush_load_float addr), arg)
| Cop(Cload ty, [loc]) when ty = typ_addr or ty = typ_int ->
let (addr, arg) = self#select_addressing loc in
(Ispecific(Ipush_load addr), arg)
| _ -> (Ispecific(Ipush), exp)
method emit_extcall_args env args =
let rec emit_pushes = function
[] -> 0
| e :: el ->
let ofs = emit_pushes el in
let (op, arg) = self#select_push e in
let r = self#emit_expr env arg in
self#insert (Iop op) r [||];
ofs + Selectgen.size_expr env e
in ([||], emit_pushes args)
end
let fundecl f = (new selector ())#emit_fundecl f

156
asmcomp/m68k/proc.ml Normal file
View File

@ -0,0 +1,156 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Description of the Motorola 680x0 processor *)
open Misc
open Arch
open Format
open Cmm
open Reg
open Mach
(* Registers available for register allocation *)
(* Register map:
A0 - A6 0-6 address registers (A2-A6 callee-save)
A7 stack pointer
D0 - D4 7-11 data registers (D2 - D7 callee-save)
D5 temporary
D6 allocation pointer
D7 trap pointer
FP0 - FP7 12-19 floating-point registers (FP2 - FP7 callee-save)
*)
let register_names =
[| "a0"; "a1"; "a2"; "a3"; "a4"; "a5"; "a6";
"d0"; "d1"; "d2"; "d3"; "d4";
"fp0"; "fp1"; "fp2"; "fp3"; "fp4"; "fp5"; "fp6"; "fp7" |]
let num_register_classes = 3
let register_class r =
match r.typ with
Addr -> 0
| Int -> 1
| Float -> 2
let num_available_registers = [| 7; 5; 8 |]
let first_available_register = [| 0; 7; 12 |]
let register_name r = register_names.(r)
(* There is no scheduling, so just pack registers. *)
let rotate_registers = false
(* Representation of hard registers by pseudo-registers *)
let all_phys_regs =
let v = Array.create 20 Reg.dummy in
for i = 0 to 6 do v.(i) <- Reg.at_location Addr (Reg i) done;
for i = 7 to 11 do v.(i) <- Reg.at_location Int (Reg i) done;
for i = 12 to 19 do v.(i) <- Reg.at_location Float (Reg i) done;
v
let phys_reg n = all_phys_regs.(n)
let stack_slot slot ty = Reg.at_location ty (Stack slot)
let reg_A0 = phys_reg 0
let reg_FP0 = phys_reg 12
(* Instruction selection *)
let word_addressed = false
(* Calling conventions *)
let calling_conventions first_addr last_addr first_float last_float
make_stack arg =
let loc = Array.create (Array.length arg) Reg.dummy in
let addr = ref first_addr in
let float = ref first_float in
let ofs = ref 0 in
for i = 0 to Array.length arg - 1 do
match arg.(i).typ with
(Addr | Int) as ty ->
if !addr <= last_addr then begin
loc.(i) <- phys_reg !addr;
incr addr
end else begin
loc.(i) <- stack_slot (make_stack !ofs) ty;
ofs := !ofs + size_addr
end
| Float ->
if !float <= last_float then begin
loc.(i) <- phys_reg !float;
incr float
end else begin
loc.(i) <- stack_slot (make_stack !ofs) Float;
ofs := !ofs + size_float
end
done;
(loc, !ofs)
let incoming ofs = Incoming ofs
let outgoing ofs = Outgoing ofs
let not_supported ofs = fatal_error "Proc.loc_results: cannot call"
let loc_arguments arg =
calling_conventions 0 5 12 18 outgoing arg
let loc_parameters arg =
let (loc, ofs) = calling_conventions 0 5 12 18 incoming arg in loc
let loc_results res =
let (loc, ofs) = calling_conventions 0 5 12 18 not_supported res in loc
let extcall_use_push = true
let loc_external_arguments arg =
fatal_error "Proc.loc_external_arguments"
let loc_external_results res =
let (loc, ofs) = calling_conventions 7 7 12 12 not_supported res in loc
let loc_exn_bucket = reg_A0
(* Registers destroyed by operations *)
let destroyed_at_c_call =
Array.of_list(List.map phys_reg [0; 1; 7; 8; 12; 13])
let destroyed_at_oper = function
Iop(Icall_ind | Icall_imm _ | Iextcall(_, true)) -> all_phys_regs
| Iop(Iextcall(_, false)) -> destroyed_at_c_call
| Iop(Iintoffloat) -> [| reg_FP0 |]
| _ -> [||]
let destroyed_at_raise = all_phys_regs
(* Maximal register pressure *)
let safe_register_pressure op = 5
let max_register_pressure = function
Iextcall(_, _) -> [| 5; 3; 6 |]
| Iintoffloat -> [| 7; 5; 7 |]
| _ -> num_available_registers
(* Layout of the stack frame *)
let num_stack_slots = [| 0; 0; 0 |]
let contains_calls = ref false
(* Calling the assembler *)
let assemble_file infile outfile =
Ccomp.command ("as -o " ^ outfile ^ " " ^ infile)

66
asmcomp/m68k/reload.ml Normal file
View File

@ -0,0 +1,66 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Reloading for the Motorola 68k *)
open Cmm
open Arch
open Reg
open Mach
let stackp r =
match r.loc with
Stack _ -> true
| _ -> false
class reload () as self =
inherit Reloadgen.reload_generic () as super
method reload_operation op arg res =
match op with
Imove | Ireload | Ispill |
Iintop_imm((Iadd | Isub | Iand | Ior | Ixor |
Icomp _ | Ilsl | Ilsr | Iasr), _) |
Ifloatofint | Iintoffloat | Ispecific(Ipush) ->
(* The argument(s) can be either in register or on stack *)
(arg, res)
| Iintop(Iadd | Isub | Iand | Ior | Ixor | Icomp _) ->
(* One of the two arguments can reside in the stack *)
if stackp arg.(0) && stackp arg.(1)
then ([|arg.(0); self#makereg arg.(1)|], res)
else (arg, res)
| Iintop(Ilsl | Ilsr | Iasr) ->
(* The first argument and result can reside in the stack *)
([|arg.(0); self#makereg arg.(1)|], res)
| Iintop(Imul | Idiv | Imod) | Iaddf | Isubf | Imulf | Idivf ->
(* The second argument can reside in the stack *)
let r = self#makereg arg.(0) in ([|r; arg.(1)|], [|r|])
| _ -> (* Other operations: all args and results in registers *)
super#reload_operation op arg res
method reload_test tst arg =
match tst with
Iinttest _ | Ifloattest _ ->
(* The second argument can be on stack *)
[| self#makereg arg.(0); arg.(1) |]
| _ ->
(* The argument can be on stack *)
arg
end
let fundecl f =
(new reload ())#fundecl f

View File

@ -0,0 +1,18 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
open Schedgen (* to create a dependency *)
(* No scheduling is needed for the Motorola 68k. *)
let fundecl f = f

188
asmcomp/m68k/selection.ml Normal file
View File

@ -0,0 +1,188 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1997 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Instruction selection for the Motorola 68k *)
open Misc
open Cmm
open Reg
open Arch
open Mach
(* Auxiliary for recognizing addressing modes *)
type addressing_expr =
Asymbol of string
| Alinear of expression
| Aadd of expression * expression
| Ascale of expression * int
| Ascaledadd of expression * expression * int
let rec select_addr exp =
match exp with
Cconst_symbol s ->
(Asymbol s, 0)
| Cop((Caddi | Cadda), [arg; Cconst_int m]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop((Csubi | Csuba), [arg; Cconst_int m]) ->
let (a, n) = select_addr arg in (a, n - m)
| Cop((Caddi | Cadda), [Cconst_int m; arg]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop(Clsl, [arg; Cconst_int(1|2|3 as shift)]) ->
begin match select_addr arg with
(Alinear e, n) -> (Ascale(e, 1 lsl shift), n lsl shift)
| _ -> (Alinear exp, 0)
end
| Cop(Cmuli, [arg; Cconst_int(2|4|8 as mult)]) ->
begin match select_addr arg with
(Alinear e, n) -> (Ascale(e, mult), n * mult)
| _ -> (Alinear exp, 0)
end
| Cop(Cmuli, [Cconst_int(2|4|8 as mult); arg]) ->
begin match select_addr arg with
(Alinear e, n) -> (Ascale(e, mult), n * mult)
| _ -> (Alinear exp, 0)
end
| Cop((Caddi | Cadda), [arg1; arg2]) ->
begin match (select_addr arg1, select_addr arg2) with
((Alinear e1, n1), (Alinear e2, n2)) ->
(Aadd(e1, e2), n1 + n2)
| ((Alinear e1, n1), (Ascale(e2, scale), n2)) ->
(Ascaledadd(e1, e2, scale), n1 + n2)
| ((Ascale(e1, scale), n1), (Alinear e2, n2)) ->
(Ascaledadd(e2, e1, scale), n1 + n2)
| (_, (Ascale(e2, scale), n2)) ->
(Ascaledadd(arg1, e2, scale), n2)
| ((Ascale(e1, scale), n1), _) ->
(Ascaledadd(arg2, e1, scale), n1)
| _ ->
(Aadd(arg1, arg2), 0)
end
| arg ->
(Alinear arg, 0)
(* Special constraints on operand and result registers for two-address
instructions *)
exception Use_default
let pseudoregs_for_operation op arg res =
match op with
(* Two-address binary operations *)
Iintop(Iadd | Isub | Imul | Idiv | Imod | Ilsl | Ilsr | Iasr) |
Iaddf | Isubf | Imulf | Idivf ->
([|res.(0); arg.(1)|], res, false)
(* Two-address binary operations, forcing the second argument to be
in a data register *)
| Iintop(Iand | Ior | Ixor) ->
let newarg1 = Reg.create Int in
([|res.(0); newarg1|], res, false)
(* Two-address unary operations *)
| Iintop_imm((Iadd | Isub | Imul | Idiv | Imod | Iand | Ior | Ixor |
Ilsl | Ilsr | Iasr), _) ->
(res, res, false)
(* Other instructions are regular *)
| _ -> raise Use_default
(* The selector *)
class selector () as self =
inherit Selectgen.selector_generic() as super
method is_immediate (n : int) = true
(* Select addressing modes *)
method select_addressing exp =
match select_addr exp with
(Asymbol s, d) ->
(Ibased(s, d), Ctuple [])
| (Alinear e, d) ->
(Iindexed d, e)
| (Aadd(e1, e2), d) ->
(Iindexed2 d, Ctuple[e1; e2])
| (Ascale(e, scale), d) ->
(Iscaled(scale, d), e)
| (Ascaledadd(e1, e2, scale), d) ->
(Iindexed2scaled(scale, d), Ctuple[e1; e2])
method select_operation op args =
match op with
(* Recognize the LEA instruction *)
Cadda | Csuba ->
begin match self#select_addressing (Cop(op, args)) with
(Iindexed d, _) -> super#select_operation op args
| (addr, arg) -> (Ispecific(Ilea addr), [arg])
end
| _ ->
super#select_operation op args
(* Selection of immediate shifts -- only if count is between 1 and 8 *)
method select_shift op = function
[arg1; Cconst_int n] when n >= 1 && n <= 8 -> (Iintop_imm(op, n), [arg1])
| args -> (Iintop op, args)
(* Select store operations *)
method select_store addr exp =
match exp with
Cconst_int n -> (Ispecific(Istore_int(n, addr)), Ctuple [])
| Cconst_pointer n -> (Ispecific(Istore_int(n, addr)), Ctuple [])
| Cconst_symbol s -> (Ispecific(Istore_symbol(s, addr)), Ctuple [])
| _ -> raise Use_default
(* Deal with register constraints *)
method insert_op op rs rd =
try
let (rsrc, rdst, move_res) = pseudoregs_for_operation op rs rd in
self#insert_moves rs rsrc;
self#insert (Iop op) rsrc rdst;
if move_res then begin
self#insert_moves rdst rd;
rd
end else
rdst
with Use_default ->
super#insert_op op rs rd
(* Select push operations for external calls *)
method select_push exp =
match exp with
Cconst_int n -> (Ispecific(Ipush_int n), Ctuple [])
| Cconst_pointer n -> (Ispecific(Ipush_int n), Ctuple [])
| Cconst_symbol s -> (Ispecific(Ipush_symbol s), Ctuple [])
| Cop(Cload ty, [loc]) when ty = typ_float ->
let (addr, arg) = self#select_addressing loc in
(Ispecific(Ipush_load_float addr), arg)
| Cop(Cload ty, [loc]) when ty = typ_addr or ty = typ_int ->
let (addr, arg) = self#select_addressing loc in
(Ispecific(Ipush_load addr), arg)
| _ -> (Ispecific(Ipush), exp)
method emit_extcall_args env args =
let rec emit_pushes = function
[] -> 0
| e :: el ->
let ofs = emit_pushes el in
let (op, arg) = self#select_push e in
let r = self#emit_expr env arg in
self#insert (Iop op) r [||];
ofs + Selectgen.size_expr env e
in ([||], emit_pushes args)
end
let fundecl f = (new selector ())#emit_fundecl f

View File

@ -25,28 +25,6 @@ exception Use_default
(* Instruction selection *)
let select_addressing = function
Cconst_symbol s ->
(Ibased(s, 0), Ctuple [])
| Cop(Cadda, [Cconst_symbol s; Cconst_int n]) ->
(Ibased(s, n), Ctuple [])
| Cop(Cadda, [arg; Cconst_int n]) ->
(Iindexed n, arg)
| Cop(Cadda, [arg1; Cop(Caddi, [arg2; Cconst_int n])]) ->
(Iindexed n, Cop(Cadda, [arg1; arg2]))
| arg ->
(Iindexed 0, arg)
let select_oper op args = raise Use_default
let select_store addr exp = raise Use_default
let select_push exp = fatal_error "Proc: select_push"
let pseudoregs_for_operation op arg res = raise Use_default
let is_immediate (n:int) = true
let word_addressed = false
(* Registers available for register allocation *)
@ -186,8 +164,6 @@ let loc_external_arguments arg =
| _ ->
fatal_error "Proc_mips.loc_external_arguments"
let extcall_use_push = false
let loc_external_results res =
let (loc, ofs) = calling_conventions 0 0 100 100 0 not_supported res in loc
@ -216,17 +192,6 @@ let max_register_pressure = function
Iextcall(_, _) -> [| 6; 6 |]
| _ -> [| 20; 15 |]
(* Reloading *)
let reload_test makereg round tst args = raise Use_default
let reload_operation makereg round op args res = raise Use_default
(* No scheduling is needed, the assembler does it better than us. *)
let need_scheduling = false
let oper_latency _ = 1
(* Layout of the stack *)
let num_stack_slots = [| 0; 0 |]

17
asmcomp/mips/reload.ml Normal file
View File

@ -0,0 +1,17 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Reloading for the Mips *)
let fundecl f =
(new Reloadgen.reload_generic ())#fundecl f

View File

@ -0,0 +1,19 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
open Schedgen (* to create a dependency *)
(* No scheduling is needed for the Mips, the assembler
does it better than us. *)
let fundecl f = f

42
asmcomp/mips/selection.ml Normal file
View File

@ -0,0 +1,42 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1997 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Instruction selection for the Mips processor *)
open Misc
open Cmm
open Reg
open Arch
open Mach
class selector () as self =
inherit Selectgen.selector_generic() as super
method is_immediate (n : int) = true
method select_addressing = function
Cconst_symbol s ->
(Ibased(s, 0), Ctuple [])
| Cop(Cadda, [Cconst_symbol s; Cconst_int n]) ->
(Ibased(s, n), Ctuple [])
| Cop(Cadda, [arg; Cconst_int n]) ->
(Iindexed n, arg)
| Cop(Cadda, [arg1; Cop(Caddi, [arg2; Cconst_int n])]) ->
(Iindexed n, Cop(Cadda, [arg1; arg2]))
| arg ->
(Iindexed 0, arg)
end
let fundecl f = (new selector ())#emit_fundecl f

View File

@ -70,3 +70,22 @@ let print_specific_operation printreg op arg =
| Imultsubf ->
printreg arg.(0); print_string " *f "; printreg arg.(1);
print_string " -f "; printreg arg.(2)
(* Distinguish between the PowerPC and the Power/RS6000 submodels *)
let powerpc =
match Config.model with
"ppc" -> true
| "rs6000" -> false
| _ -> Misc.fatal_error "wrong $(MODEL)"
(* Distinguish between the PowerOpen (AIX, MacOS) TOC-based,
relative-addressing model and the SVR4 (Solaris, MkLinux)
absolute-addressing model. *)
let toc =
match Config.system with
"aix" -> true
| "elf" -> false
| _ -> Misc.fatal_error "wrong $(SYSTEM)"

View File

@ -19,110 +19,8 @@ open Reg
open Arch
open Mach
(* Distinguish between the PowerPC and the Power/RS6000 submodels *)
let powerpc =
match Config.model with
"ppc" -> true
| "rs6000" -> false
| _ -> fatal_error "wrong $(MODEL)"
(* Distinguish between the PowerOpen (AIX, MacOS) relative-addressing model
and the SVR4 (Solaris, MkLinux) absolute-addressing model. *)
let svr4 =
match Config.system with
"aix" -> false
| "elf" -> true
| _ -> fatal_error "wrong $(SYSTEM)"
(* Exceptions raised to signal cases not handled here *)
exception Use_default
(* Recognition of addressing modes *)
type addressing_expr =
Asymbol of string
| Alinear of expression
| Aadd of expression * expression
let rec select_addr = function
Cconst_symbol s when svr4 -> (* don't recognize this mode in the TOC-based model *)
(Asymbol s, 0)
| Cop((Caddi | Cadda), [arg; Cconst_int m]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop((Caddi | Cadda), [Cconst_int m; arg]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop((Caddi | Cadda), [arg1; arg2]) ->
begin match (select_addr arg1, select_addr arg2) with
((Alinear e1, n1), (Alinear e2, n2)) ->
(Aadd(e1, e2), n1 + n2)
| _ ->
(Aadd(arg1, arg2), 0)
end
| exp ->
(Alinear exp, 0)
let select_addressing exp =
match select_addr exp with
(Asymbol s, d) ->
(Ibased(s, d), Ctuple [])
| (Alinear e, d) ->
(Iindexed d, e)
| (Aadd(e1, e2), d) ->
if d = 0
then (Iindexed2, Ctuple[e1; e2])
else (Iindexed d, Cop(Cadda, [e1; e2]))
(* Instruction selection *)
let select_logical op = function
[arg; Cconst_int n] when n >= 0 & n <= 0xFFFF ->
(Iintop_imm(op, n), [arg])
| [Cconst_int n; arg] when n >= 0 & n <= 0xFFFF ->
(Iintop_imm(op, n), [arg])
| args ->
(Iintop op, args)
let select_oper op args =
match (op, args) with
(* Prevent the recognition of (x / cst) and (x % cst) when cst is not
a power of 2, which do not correspond to an instruction. *)
(Cdivi, [arg; Cconst_int n]) when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Idiv, n), [arg])
| (Cdivi, _) ->
(Iintop Idiv, args)
| (Cmodi, [arg; Cconst_int n]) when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Imod, n), [arg])
| (Cmodi, _) ->
(Iintop Imod, args)
(* The and, or and xor instructions have a different range of immediate
operands than the other instructions *)
| (Cand, _) -> select_logical Iand args
| (Cor, _) -> select_logical Ior args
| (Cxor, _) -> select_logical Ixor args
(* intoffloat goes through a library function on the RS6000 *)
| (Cintoffloat, _) when not powerpc ->
(Iextcall("itrunc", false), args)
(* Recognize mult-add and mult-sub instructions *)
| (Caddf, [Cop(Cmulf, [arg1; arg2]); arg3]) ->
(Ispecific Imultaddf, [arg1; arg2; arg3])
| (Caddf, [arg3; Cop(Cmulf, [arg1; arg2])]) ->
(Ispecific Imultaddf, [arg1; arg2; arg3])
| (Csubf, [Cop(Cmulf, [arg1; arg2]); arg3]) ->
(Ispecific Imultsubf, [arg1; arg2; arg3])
| _ ->
raise Use_default
let select_store addr exp = raise Use_default
let select_push exp = fatal_error "Proc: select_push"
let pseudoregs_for_operation op arg res = raise Use_default
let is_immediate n = (n <= 32767) & (n >= -32768)
let word_addressed = false
(* Registers available for register allocation *)
@ -219,7 +117,7 @@ let calling_conventions
ofs := !ofs + size_float
end
done;
let final_ofs = if not svr4 && !ofs > 0 then !ofs + 24 else !ofs in
let final_ofs = if toc && !ofs > 0 then !ofs + 24 else !ofs in
(loc, Misc.align final_ofs 8)
(* Keep stack 8-aligned.
Under PowerOpen, keep a free 24 byte linkage area at the bottom
@ -279,9 +177,9 @@ let poweropen_external_conventions first_int last_int
(loc, Misc.align !ofs 8) (* Keep stack 8-aligned *)
let loc_external_arguments arg =
if svr4
then calling_conventions 0 7 100 107 outgoing 8 arg
else poweropen_external_conventions 0 7 100 112 arg
if toc
then poweropen_external_conventions 0 7 100 112 arg
else calling_conventions 0 7 100 107 outgoing 8 arg
let extcall_use_push = false
@ -318,29 +216,6 @@ let max_register_pressure = function
Iextcall(_, _) -> [| 15; 18 |]
| _ -> [| 23; 30 |]
(* Reloading *)
let reload_test makereg round tst args = raise Use_default
let reload_operation makereg round op args res = raise Use_default
(* Latencies (in cycles). Based roughly on the "common model". *)
let need_scheduling = true
let oper_latency = function
Ireload -> 2
| Iload(_, _) -> 2
| Iconst_float _ -> 2 (* turned into a load *)
| Iconst_symbol _ -> if svr4 then 1 else 2 (* turned into a load *)
| Iintop Imul -> 9
| Iintop_imm(Imul, _) -> 5
| Iintop(Idiv | Imod) -> 36
| Iaddf | Isubf -> 4
| Imulf -> 5
| Idivf -> 33
| Ispecific(Imultaddf | Imultsubf) -> 5
| _ -> 1
(* Layout of the stack *)
let num_stack_slots = [| 0; 0 |]

17
asmcomp/power/reload.ml Normal file
View File

@ -0,0 +1,17 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Reloading for the PowerPC *)
let fundecl f =
(new Reloadgen.reload_generic ())#fundecl f

View File

@ -0,0 +1,57 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Instruction scheduling for the Power PC *)
open Arch
open Mach
class scheduler () as self =
inherit Schedgen.scheduler_generic () as super
(* Latencies (in cycles). Based roughly on the "common model". *)
method oper_latency = function
Ireload -> 2
| Iload(_, _) -> 2
| Iconst_float _ -> 2 (* turned into a load *)
| Iconst_symbol _ -> if toc then 2 (* turned into a load *) else 1
| Iintop Imul -> 9
| Iintop_imm(Imul, _) -> 5
| Iintop(Idiv | Imod) -> 36
| Iaddf | Isubf -> 4
| Imulf -> 5
| Idivf -> 33
| Ispecific(Imultaddf | Imultsubf) -> 5
| _ -> 1
(* Issue cycles. Rough approximations. *)
method oper_issue_cycles = function
Iconst_float _ | Iconst_symbol _ -> if toc then 1 else 2
| Iload(_, Ibased(_, _)) -> 2
| Istore(_, Ibased(_, _)) -> 2
| Ialloc _ -> 4
| Iintop(Imod) -> 40 (* assuming full stall *)
| Iintop(Icomp _) -> 4
| Iintop_imm(Idiv, _) -> 2
| Iintop_imm(Imod, _) -> 4
| Iintop_imm(Icomp _, _) -> 4
| Ifloatofint -> 9
| Iintoffloat -> 4
| _ -> 1
end
let fundecl f = (new scheduler ())#schedule_fundecl f

106
asmcomp/power/selection.ml Normal file
View File

@ -0,0 +1,106 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1997 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Instruction selection for the Power PC processor *)
open Misc
open Cmm
open Reg
open Arch
open Mach
(* Recognition of addressing modes *)
type addressing_expr =
Asymbol of string
| Alinear of expression
| Aadd of expression * expression
let rec select_addr = function
Cconst_symbol s when not toc ->
(* don't recognize this mode in the TOC-based model *)
(Asymbol s, 0)
| Cop((Caddi | Cadda), [arg; Cconst_int m]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop((Caddi | Cadda), [Cconst_int m; arg]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop((Caddi | Cadda), [arg1; arg2]) ->
begin match (select_addr arg1, select_addr arg2) with
((Alinear e1, n1), (Alinear e2, n2)) ->
(Aadd(e1, e2), n1 + n2)
| _ ->
(Aadd(arg1, arg2), 0)
end
| exp ->
(Alinear exp, 0)
(* Instruction selection *)
class selector () as self =
inherit Selectgen.selector_generic() as super
method is_immediate n = (n <= 32767) & (n >= -32768)
method select_addressing exp =
match select_addr exp with
(Asymbol s, d) ->
(Ibased(s, d), Ctuple [])
| (Alinear e, d) ->
(Iindexed d, e)
| (Aadd(e1, e2), d) ->
if d = 0
then (Iindexed2, Ctuple[e1; e2])
else (Iindexed d, Cop(Cadda, [e1; e2]))
method select_operation op args =
match (op, args) with
(* Prevent the recognition of (x / cst) and (x % cst) when cst is not
a power of 2, which do not correspond to an instruction. *)
(Cdivi, [arg; Cconst_int n]) when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Idiv, n), [arg])
| (Cdivi, _) ->
(Iintop Idiv, args)
| (Cmodi, [arg; Cconst_int n]) when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Imod, n), [arg])
| (Cmodi, _) ->
(Iintop Imod, args)
(* The and, or and xor instructions have a different range of immediate
operands than the other instructions *)
| (Cand, _) -> self#select_logical Iand args
| (Cor, _) -> self#select_logical Ior args
| (Cxor, _) -> self#select_logical Ixor args
(* intoffloat goes through a library function on the RS6000 *)
| (Cintoffloat, _) when not powerpc ->
(Iextcall("itrunc", false), args)
(* Recognize mult-add and mult-sub instructions *)
| (Caddf, [Cop(Cmulf, [arg1; arg2]); arg3]) ->
(Ispecific Imultaddf, [arg1; arg2; arg3])
| (Caddf, [arg3; Cop(Cmulf, [arg1; arg2])]) ->
(Ispecific Imultaddf, [arg1; arg2; arg3])
| (Csubf, [Cop(Cmulf, [arg1; arg2]); arg3]) ->
(Ispecific Imultsubf, [arg1; arg2; arg3])
| _ ->
super#select_operation op args
method select_logical op = function
[arg; Cconst_int n] when n >= 0 & n <= 0xFFFF ->
(Iintop_imm(op, n), [arg])
| [Cconst_int n; arg] when n >= 0 & n <= 0xFFFF ->
(Iintop_imm(op, n), [arg])
| args ->
(Iintop op, args)
end
let fundecl f = (new selector ())#emit_fundecl f

View File

@ -13,24 +13,7 @@
(* Processor descriptions *)
(* The Use_default exception is raised by the selection and reloading
functions to signal cases they don't handle *)
exception Use_default
(* Instruction selection *)
val select_addressing:
Cmm.expression -> Arch.addressing_mode * Cmm.expression
val select_oper:
Cmm.operation -> Cmm.expression list ->
Mach.operation * Cmm.expression list
val select_store:
Arch.addressing_mode -> Cmm.expression -> Mach.operation * Cmm.expression
val select_push:
Cmm.expression -> Mach.operation * Cmm.expression
val pseudoregs_for_operation:
Mach.operation -> Reg.t array -> Reg.t array ->
Reg.t array * Reg.t array * bool
val is_immediate: int -> bool
val word_addressed: bool
(* Registers available for register allocation *)
@ -48,11 +31,9 @@ val loc_results: Reg.t array -> Reg.t array
val loc_parameters: Reg.t array -> Reg.t array
val loc_external_arguments: Reg.t array -> Reg.t array * int
val loc_external_results: Reg.t array -> Reg.t array
val extcall_use_push : bool
val loc_exn_bucket: Reg.t
(* Maximal register pressures for pre-spilling *)
val safe_register_pressure: Mach.operation -> int
val max_register_pressure: Mach.operation -> int array
@ -60,17 +41,6 @@ val max_register_pressure: Mach.operation -> int array
val destroyed_at_oper: Mach.instruction_desc -> Reg.t array
val destroyed_at_raise: Reg.t array
(* Reloading of instruction arguments, storing of instruction results *)
val reload_test:
(Reg.t -> Reg.t) -> int -> Mach.test -> Reg.t array -> Reg.t array
val reload_operation:
(Reg.t -> Reg.t) -> int -> Mach.operation ->
Reg.t array -> Reg.t array -> Reg.t array * Reg.t array
(* Latency info for instruction scheduling *)
val need_scheduling: bool
val oper_latency: Mach.operation -> int
(* Info for laying out the stack frame *)
val num_stack_slots: int array
val contains_calls: bool ref

View File

@ -1,434 +0,0 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Description of the Intel 386 processor *)
open Misc
open Arch
open Format
open Cmm
open Reg
open Mach
(* Registers available for register allocation *)
(* Register map:
eax 0 eax - edi: function arguments and results
ebx 1 eax: C function results
ecx 2 ebx, esi, edi, ebp: preserved by C
edx 3
esi 4
edi 5
ebp 6
tos 100 top of floating-point stack. *)
let int_reg_name =
[| "%eax"; "%ebx"; "%ecx"; "%edx"; "%esi"; "%edi"; "%ebp" |]
let float_reg_name =
[| "%tos" |]
let num_register_classes = 2
let register_class r =
match r.typ with
Int -> 0
| Addr -> 0
| Float -> 1
let num_available_registers = [| 7; 0 |]
let first_available_register = [| 0; 100 |]
let register_name r =
if r < 100 then int_reg_name.(r) else float_reg_name.(r - 100)
(* There is little scheduling, and some operations are more compact
when their argument is %eax. *)
let rotate_registers = false
(* Representation of hard registers by pseudo-registers *)
let hard_int_reg =
let v = Array.create 7 Reg.dummy in
for i = 0 to 6 do v.(i) <- Reg.at_location Int (Reg i) done;
v
let hard_float_reg = [| Reg.at_location Float (Reg 100) |]
let all_phys_regs =
Array.append hard_int_reg hard_float_reg
let phys_reg n =
if n < 100 then hard_int_reg.(n) else hard_float_reg.(n - 100)
let stack_slot slot ty =
Reg.at_location ty (Stack slot)
let eax = phys_reg 0
let ecx = phys_reg 2
let edx = phys_reg 3
let tos = phys_reg 100
(* Exceptions raised to signal cases not handled here *)
exception Use_default
(* Instruction selection *)
(* Auxiliary for recognizing addressing modes *)
type addressing_expr =
Asymbol of string
| Alinear of expression
| Aadd of expression * expression
| Ascale of expression * int
| Ascaledadd of expression * expression * int
let rec select_addr exp =
match exp with
Cconst_symbol s ->
(Asymbol s, 0)
| Cop((Caddi | Cadda), [arg; Cconst_int m]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop((Csubi | Csuba), [arg; Cconst_int m]) ->
let (a, n) = select_addr arg in (a, n - m)
| Cop((Caddi | Cadda), [Cconst_int m; arg]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop(Clsl, [arg; Cconst_int(1|2|3 as shift)]) ->
begin match select_addr arg with
(Alinear e, n) -> (Ascale(e, 1 lsl shift), n lsl shift)
| _ -> (Alinear exp, 0)
end
| Cop(Cmuli, [arg; Cconst_int(2|4|8 as mult)]) ->
begin match select_addr arg with
(Alinear e, n) -> (Ascale(e, mult), n * mult)
| _ -> (Alinear exp, 0)
end
| Cop(Cmuli, [Cconst_int(2|4|8 as mult); arg]) ->
begin match select_addr arg with
(Alinear e, n) -> (Ascale(e, mult), n * mult)
| _ -> (Alinear exp, 0)
end
| Cop((Caddi | Cadda), [arg1; arg2]) ->
begin match (select_addr arg1, select_addr arg2) with
((Alinear e1, n1), (Alinear e2, n2)) ->
(Aadd(e1, e2), n1 + n2)
| ((Alinear e1, n1), (Ascale(e2, scale), n2)) ->
(Ascaledadd(e1, e2, scale), n1 + n2)
| ((Ascale(e1, scale), n1), (Alinear e2, n2)) ->
(Ascaledadd(e2, e1, scale), n1 + n2)
| (_, (Ascale(e2, scale), n2)) ->
(Ascaledadd(arg1, e2, scale), n2)
| ((Ascale(e1, scale), n1), _) ->
(Ascaledadd(arg2, e1, scale), n1)
| _ ->
(Aadd(arg1, arg2), 0)
end
| arg ->
(Alinear arg, 0)
let select_addressing exp =
match select_addr exp with
(Asymbol s, d) ->
(Ibased(s, d), Ctuple [])
| (Alinear e, d) ->
(Iindexed d, e)
| (Aadd(e1, e2), d) ->
(Iindexed2 d, Ctuple[e1; e2])
| (Ascale(e, scale), d) ->
(Iscaled(scale, d), e)
| (Ascaledadd(e1, e2, scale), d) ->
(Iindexed2scaled(scale, d), Ctuple[e1; e2])
(* Estimate number of float temporaries needed to evaluate expression
(Ershov's algorithm) *)
let rec float_needs = function
Cop((Caddf | Csubf | Cmulf | Cdivf), [arg1; arg2]) ->
let n1 = float_needs arg1 in
let n2 = float_needs arg2 in
if n1 = n2 then 1 + n1 else if n1 > n2 then n1 else n2
| _ ->
1
(* Recognize float arithmetic with mem *)
let select_floatarith regular_op reversed_op mem_op mem_rev_op args =
match args with
[arg1; Cop(Cload _, [loc2])] ->
let (addr, arg2) = select_addressing loc2 in
(Ispecific(Ifloatarithmem(mem_op, addr)), [arg1; arg2])
| [Cop(Cload _, [loc1]); arg2] ->
let (addr, arg1) = select_addressing loc1 in
(Ispecific(Ifloatarithmem(mem_rev_op, addr)), [arg2; arg1])
| [arg1; arg2] ->
(* Evaluate bigger subexpression first to minimize stack usage.
Because of right-to-left evaluation, rightmost arg is evaluated
first *)
if float_needs arg1 <= float_needs arg2
then (regular_op, [arg1; arg2])
else (reversed_op, [arg2; arg1])
| _ ->
fatal_error "Proc_i386: select_floatarith"
(* Main instruction selection functions *)
let select_oper op args =
match op with
(* Recognize the LEA instruction *)
Caddi | Cadda | Csubi | Csuba ->
begin match select_addressing (Cop(op, args)) with
(Iindexed d, _) -> raise Use_default
| (Iindexed2 0, _) -> raise Use_default
| (addr, arg) -> (Ispecific(Ilea addr), [arg])
end
(* Recognize (x / cst) and (x % cst) only if cst is a power of 2. *)
| Cdivi ->
begin match args with
[arg1; Cconst_int n] when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Idiv, n), [arg1])
| _ -> (Iintop Idiv, args)
end
| Cmodi ->
begin match args with
[arg1; Cconst_int n] when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Imod, n), [arg1])
| _ -> (Iintop Imod, args)
end
(* Recognize float arithmetic with memory.
In passing, apply Ershov's algorithm to reduce stack usage *)
| Caddf ->
select_floatarith Iaddf Iaddf Ifloatadd Ifloatadd args
| Csubf ->
select_floatarith Isubf (Ispecific Isubfrev) Ifloatsub Ifloatsubrev args
| Cmulf ->
select_floatarith Imulf Imulf Ifloatmul Ifloatmul args
| Cdivf ->
select_floatarith Idivf (Ispecific Idivfrev) Ifloatdiv Ifloatdivrev args
(* Recognize store instructions *)
| Cstore ->
begin match args with
[loc; Cconst_int n] ->
let (addr, arg) = select_addressing loc in
(Ispecific(Istore_int(n, addr)), [arg])
| [loc; Cconst_pointer n] ->
let (addr, arg) = select_addressing loc in
(Ispecific(Istore_int(n, addr)), [arg])
| [loc; Cconst_symbol s] ->
let (addr, arg) = select_addressing loc in
(Ispecific(Istore_symbol(s, addr)), [arg])
| [loc; Cop(Caddi, [Cop(Cload _, [loc']); Cconst_int n])]
when loc = loc' ->
let (addr, arg) = select_addressing loc in
(Ispecific(Ioffset_loc(n, addr)), [arg])
| _ ->
raise Use_default
end
| _ -> raise Use_default
let select_store addr exp =
match exp with
Cconst_int n -> (Ispecific(Istore_int(n, addr)), Ctuple [])
| Cconst_pointer n -> (Ispecific(Istore_int(n, addr)), Ctuple [])
| Cconst_symbol s -> (Ispecific(Istore_symbol(s, addr)), Ctuple [])
| _ -> raise Use_default
let select_push exp =
match exp with
Cconst_int n -> (Ispecific(Ipush_int n), Ctuple [])
| Cconst_pointer n -> (Ispecific(Ipush_int n), Ctuple [])
| Cconst_symbol s -> (Ispecific(Ipush_symbol s), Ctuple [])
| Cop(Cload ty, [loc]) when ty = typ_float ->
let (addr, arg) = select_addressing loc in
(Ispecific(Ipush_load_float addr), arg)
| Cop(Cload ty, [loc]) when ty = typ_addr or ty = typ_int ->
let (addr, arg) = select_addressing loc in
(Ispecific(Ipush_load addr), arg)
| _ -> (Ispecific(Ipush), exp)
let pseudoregs_for_operation op arg res =
match op with
(* Two-address binary operations *)
Iintop(Iadd|Isub|Imul|Iand|Ior|Ixor) ->
([|res.(0); arg.(1)|], res, false)
(* Two-address unary operations *)
| Iintop_imm((Iadd|Isub|Imul|Idiv|Iand|Ior|Ixor|Ilsl|Ilsr|Iasr), _) ->
(res, res, false)
(* For shifts with variable shift count, second arg must be in ecx *)
| Iintop(Ilsl|Ilsr|Iasr) ->
([|res.(0); ecx|], res, false)
(* For div and mod, first arg must be in eax, edx is clobbered,
and result is in eax or edx respectively.
Keep it simple, just force second argument in ecx. *)
| Iintop(Idiv) ->
([| eax; ecx |], [| eax |], true)
| Iintop(Imod) ->
([| eax; ecx |], [| edx |], true)
(* For mod with immediate operand, arg must not be in eax.
Keep it simple, force it in edx. *)
| Iintop_imm(Imod, _) ->
([| edx |], [| edx |], true)
(* For floating-point operations, the result is always left at the
top of the floating-point stack *)
| Iconst_float _ | Inegf | Iabsf | Iaddf | Isubf | Imulf | Idivf
| Ifloatofint |Ispecific(Isubfrev | Idivfrev | Ifloatarithmem(_, _)) ->
(arg, [| tos |], false) (* don't move it immediately *)
(* Same for a floating-point load *)
| Iload(Word, addr) when res.(0).typ = Float ->
(arg, [| tos |], false)
(* For storing a byte, the argument must be in eax...edx.
For storing a halfword, any reg is ok.
Keep it simple, just force it to be in edx in both cases. *)
| Istore(Word, addr) -> raise Use_default
| Istore(chunk, addr) ->
let newarg = Array.copy arg in
newarg.(0) <- edx;
(newarg, res, false)
(* Other instructions are more or less regular *)
| _ -> raise Use_default
let is_immediate (n: int) = true
let word_addressed = false
(* Calling conventions *)
let calling_conventions first_int last_int first_float last_float make_stack
arg =
let loc = Array.create (Array.length arg) Reg.dummy in
let int = ref first_int in
let float = ref first_float in
let ofs = ref 0 in
for i = 0 to Array.length arg - 1 do
match arg.(i).typ with
Int | Addr as ty ->
if !int <= last_int then begin
loc.(i) <- phys_reg !int;
incr int
end else begin
loc.(i) <- stack_slot (make_stack !ofs) ty;
ofs := !ofs + size_int
end
| Float ->
if !float <= last_float then begin
loc.(i) <- phys_reg !float;
incr float
end else begin
loc.(i) <- stack_slot (make_stack !ofs) Float;
ofs := !ofs + size_float
end
done;
(loc, !ofs)
let incoming ofs = Incoming ofs
let outgoing ofs = Outgoing ofs
let not_supported ofs = fatal_error "Proc.loc_results: cannot call"
let loc_arguments arg =
calling_conventions 0 5 100 99 outgoing arg
let loc_parameters arg =
let (loc, ofs) = calling_conventions 0 5 100 99 incoming arg in loc
let loc_results res =
let (loc, ofs) = calling_conventions 0 5 100 100 not_supported res in loc
let extcall_use_push = true
let loc_external_arguments arg =
fatal_error "Proc.loc_external_arguments"
let loc_external_results res =
let (loc, ofs) = calling_conventions 0 0 100 100 not_supported res in loc
let loc_exn_bucket = eax
(* Registers destroyed by operations *)
let destroyed_at_c_call = (* ebx, esi, edi, ebp preserved *)
Array.of_list(List.map phys_reg [0;2;3])
let destroyed_at_oper = function
Iop(Icall_ind | Icall_imm _ | Iextcall(_, true)) -> all_phys_regs
| Iop(Iextcall(_, false)) -> destroyed_at_c_call
| Iop(Iintop(Idiv | Imod)) -> [| eax; edx |]
| Iop(Iintop_imm(Imod, _)) -> [| eax |]
| Iop(Ialloc _) -> [| eax |]
| Iop(Iintop(Icomp _) | Iintop_imm(Icomp _, _)) -> [| eax |]
| Iop(Iintoffloat) -> [| eax |]
| Iifthenelse(Ifloattest(_, _), _, _) -> [| eax |]
| _ -> [||]
let destroyed_at_raise = all_phys_regs
(* Maximal register pressure *)
let safe_register_pressure op = 4
let max_register_pressure = function
Iextcall(_, _) -> [| 4; max_int |]
| Iintop(Idiv | Imod) -> [| 5; max_int |]
| Ialloc _ | Iintop(Icomp _) | Iintop_imm(Icomp _, _) |
Iintoffloat -> [| 6; max_int |]
| _ -> [|7; max_int |]
(* Reloading of instruction arguments, storing of instruction results. *)
let stackp r =
match r.loc with
Stack _ -> true
| _ -> false
let reload_test makereg round tst arg =
match tst with
Iinttest cmp ->
if stackp arg.(0) & stackp arg.(1)
then [| makereg arg.(0); arg.(1) |]
else arg
| _ -> arg
(* Since #floatregs = 0, pseudoregs of type float will never be reloaded.
Hence there is no need to make special cases for
floating-point operations. *)
let reload_operation makereg round op arg res =
match op with
Iintop(Iadd|Isub|Imul|Iand|Ior|Ixor|Icomp _|Icheckbound) ->
(* One of the two arguments can reside in the stack *)
if stackp arg.(0) & stackp arg.(1)
then ([|arg.(0); makereg arg.(1)|], res)
else (arg, res)
| Iintop(Ilsl|Ilsr|Iasr) | Iintop_imm(_, _) | Ifloatofint | Iintoffloat |
Ispecific(Ipush) ->
(* The argument(s) can be either in register or on stack *)
(arg, res)
| _ -> (* Other operations: all args and results in registers *)
raise Use_default
(* Scheduling is turned off because our model does not fit the 486
nor Pentium very well. In particular, it messes up with the
float reg stack. The Pentium Pro schedules at run-time much better
than what we could do. *)
let need_scheduling = false
let oper_latency _ = 0
(* Layout of the stack frame *)
let num_stack_slots = [| 0; 0 |]
let contains_calls = ref false
(* Calling the assembler *)
let assemble_file infile outfile =
Ccomp.command ("as -o " ^ outfile ^ " " ^ infile)

View File

@ -1,436 +0,0 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Description of the Intel 386 processor, for Windows NT *)
open Misc
open Arch
open Format
open Cmm
open Reg
open Mach
(* Registers available for register allocation *)
(* Register map:
eax 0 eax - edi: function arguments and results
ebx 1 eax: C function results
ecx 2 ebx, esi, edi, ebp: preserved by C
edx 3
esi 4
edi 5
ebp 6
tos 100 top of floating-point stack. *)
let int_reg_name =
[| "eax"; "ebx"; "ecx"; "edx"; "esi"; "edi"; "ebp" |]
let float_reg_name =
[| "tos" |]
let num_register_classes = 2
let register_class r =
match r.typ with
Int -> 0
| Addr -> 0
| Float -> 1
let num_available_registers = [| 7; 0 |]
let first_available_register = [| 0; 100 |]
let register_name r =
if r < 100 then int_reg_name.(r) else float_reg_name.(r - 100)
(* There is little scheduling, and some operations are more compact
when their argument is %eax. *)
let rotate_registers = false
(* Representation of hard registers by pseudo-registers *)
let hard_int_reg =
let v = Array.create 7 Reg.dummy in
for i = 0 to 6 do v.(i) <- Reg.at_location Int (Reg i) done;
v
let hard_float_reg = [| Reg.at_location Float (Reg 100) |]
let all_phys_regs =
Array.append hard_int_reg hard_float_reg
let phys_reg n =
if n < 100 then hard_int_reg.(n) else hard_float_reg.(n - 100)
let stack_slot slot ty =
Reg.at_location ty (Stack slot)
let eax = phys_reg 0
let ecx = phys_reg 2
let edx = phys_reg 3
let tos = phys_reg 100
(* Exceptions raised to signal cases not handled here *)
exception Use_default
(* Instruction selection *)
(* Auxiliary for recognizing addressing modes *)
type addressing_expr =
Asymbol of string
| Alinear of expression
| Aadd of expression * expression
| Ascale of expression * int
| Ascaledadd of expression * expression * int
let rec select_addr exp =
match exp with
Cconst_symbol s ->
(Asymbol s, 0)
| Cop((Caddi | Cadda), [arg; Cconst_int m]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop((Csubi | Csuba), [arg; Cconst_int m]) ->
let (a, n) = select_addr arg in (a, n - m)
| Cop((Caddi | Cadda), [Cconst_int m; arg]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop(Clsl, [arg; Cconst_int(1|2|3 as shift)]) ->
begin match select_addr arg with
(Alinear e, n) -> (Ascale(e, 1 lsl shift), n lsl shift)
| _ -> (Alinear exp, 0)
end
| Cop(Cmuli, [arg; Cconst_int(2|4|8 as mult)]) ->
begin match select_addr arg with
(Alinear e, n) -> (Ascale(e, mult), n * mult)
| _ -> (Alinear exp, 0)
end
| Cop(Cmuli, [Cconst_int(2|4|8 as mult); arg]) ->
begin match select_addr arg with
(Alinear e, n) -> (Ascale(e, mult), n * mult)
| _ -> (Alinear exp, 0)
end
| Cop((Caddi | Cadda), [arg1; arg2]) ->
begin match (select_addr arg1, select_addr arg2) with
((Alinear e1, n1), (Alinear e2, n2)) ->
(Aadd(e1, e2), n1 + n2)
| ((Alinear e1, n1), (Ascale(e2, scale), n2)) ->
(Ascaledadd(e1, e2, scale), n1 + n2)
| ((Ascale(e1, scale), n1), (Alinear e2, n2)) ->
(Ascaledadd(e2, e1, scale), n1 + n2)
| (_, (Ascale(e2, scale), n2)) ->
(Ascaledadd(arg1, e2, scale), n2)
| ((Ascale(e1, scale), n1), _) ->
(Ascaledadd(arg2, e1, scale), n1)
| _ ->
(Aadd(arg1, arg2), 0)
end
| arg ->
(Alinear arg, 0)
let select_addressing exp =
match select_addr exp with
(Asymbol s, d) ->
(Ibased(s, d), Ctuple [])
| (Alinear e, d) ->
(Iindexed d, e)
| (Aadd(e1, e2), d) ->
(Iindexed2 d, Ctuple[e1; e2])
| (Ascale(e, scale), d) ->
(Iscaled(scale, d), e)
| (Ascaledadd(e1, e2, scale), d) ->
(Iindexed2scaled(scale, d), Ctuple[e1; e2])
(* Estimate number of float temporaries needed to evaluate expression
(Ershov's algorithm) *)
let rec float_needs = function
Cop((Caddf | Csubf | Cmulf | Cdivf), [arg1; arg2]) ->
let n1 = float_needs arg1 in
let n2 = float_needs arg2 in
if n1 = n2 then 1 + n1 else if n1 > n2 then n1 else n2
| _ ->
1
(* Recognize float arithmetic with mem *)
let select_floatarith regular_op reversed_op mem_op mem_rev_op args =
match args with
[arg1; Cop(Cload _, [loc2])] ->
let (addr, arg2) = select_addressing loc2 in
(Ispecific(Ifloatarithmem(mem_op, addr)), [arg1; arg2])
| [Cop(Cload _, [loc1]); arg2] ->
let (addr, arg1) = select_addressing loc1 in
(Ispecific(Ifloatarithmem(mem_rev_op, addr)), [arg2; arg1])
| [arg1; arg2] ->
(* Evaluate bigger subexpression first to minimize stack usage.
Because of right-to-left evaluation, rightmost arg is evaluated
first *)
if float_needs arg1 <= float_needs arg2
then (regular_op, [arg1; arg2])
else (reversed_op, [arg2; arg1])
| _ ->
fatal_error "Proc_i386: select_floatarith"
(* Main instruction selection functions *)
let select_oper op args =
match op with
(* Recognize the LEA instruction *)
Caddi | Cadda | Csubi | Csuba ->
begin match select_addressing (Cop(op, args)) with
(Iindexed d, _) -> raise Use_default
| (Iindexed2 0, _) -> raise Use_default
| (addr, arg) -> (Ispecific(Ilea addr), [arg])
end
(* Recognize (x / cst) and (x % cst) only if cst is a power of 2. *)
| Cdivi ->
begin match args with
[arg1; Cconst_int n] when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Idiv, n), [arg1])
| _ -> (Iintop Idiv, args)
end
| Cmodi ->
begin match args with
[arg1; Cconst_int n] when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Imod, n), [arg1])
| _ -> (Iintop Imod, args)
end
(* Recognize float arithmetic with memory.
In passing, apply Ershov's algorithm to reduce stack usage *)
| Caddf ->
select_floatarith Iaddf Iaddf Ifloatadd Ifloatadd args
| Csubf ->
select_floatarith Isubf (Ispecific Isubfrev) Ifloatsub Ifloatsubrev args
| Cmulf ->
select_floatarith Imulf Imulf Ifloatmul Ifloatmul args
| Cdivf ->
select_floatarith Idivf (Ispecific Idivfrev) Ifloatdiv Ifloatdivrev args
(* Recognize store instructions *)
| Cstore ->
begin match args with
[loc; Cconst_int n] ->
let (addr, arg) = select_addressing loc in
(Ispecific(Istore_int(n, addr)), [arg])
| [loc; Cconst_pointer n] ->
let (addr, arg) = select_addressing loc in
(Ispecific(Istore_int(n, addr)), [arg])
| [loc; Cconst_symbol s] ->
let (addr, arg) = select_addressing loc in
(Ispecific(Istore_symbol(s, addr)), [arg])
| [loc; Cop(Caddi, [Cop(Cload _, [loc']); Cconst_int n])]
when loc = loc' ->
let (addr, arg) = select_addressing loc in
(Ispecific(Ioffset_loc(n, addr)), [arg])
| _ ->
raise Use_default
end
| _ -> raise Use_default
let select_store addr exp =
match exp with
Cconst_int n -> (Ispecific(Istore_int(n, addr)), Ctuple [])
| Cconst_pointer n -> (Ispecific(Istore_int(n, addr)), Ctuple [])
| Cconst_symbol s -> (Ispecific(Istore_symbol(s, addr)), Ctuple [])
| _ -> raise Use_default
let select_push exp =
match exp with
Cconst_int n -> (Ispecific(Ipush_int n), Ctuple [])
| Cconst_pointer n -> (Ispecific(Ipush_int n), Ctuple [])
| Cconst_symbol s -> (Ispecific(Ipush_symbol s), Ctuple [])
| Cop(Cload ty, [loc]) when ty = typ_float ->
let (addr, arg) = select_addressing loc in
(Ispecific(Ipush_load_float addr), arg)
| Cop(Cload ty, [loc]) when ty = typ_addr or ty = typ_int ->
let (addr, arg) = select_addressing loc in
(Ispecific(Ipush_load addr), arg)
| _ -> (Ispecific(Ipush), exp)
let pseudoregs_for_operation op arg res =
match op with
(* Two-address binary operations *)
Iintop(Iadd|Isub|Imul|Iand|Ior|Ixor) ->
([|res.(0); arg.(1)|], res, false)
(* Two-address unary operations *)
| Iintop_imm((Iadd|Isub|Imul|Idiv|Iand|Ior|Ixor|Ilsl|Ilsr|Iasr), _) ->
(res, res, false)
(* For shifts with variable shift count, second arg must be in ecx *)
| Iintop(Ilsl|Ilsr|Iasr) ->
([|res.(0); ecx|], res, false)
(* For div and mod, first arg must be in eax, edx is clobbered,
and result is in eax or edx respectively.
Keep it simple, just force second argument in ecx. *)
| Iintop(Idiv) ->
([| eax; ecx |], [| eax |], true)
| Iintop(Imod) ->
([| eax; ecx |], [| edx |], true)
(* For mod with immediate operand, arg must not be in eax.
Keep it simple, force it in edx. *)
| Iintop_imm(Imod, _) ->
([| edx |], [| edx |], true)
(* For floating-point operations, the result is always left at the
top of the floating-point stack *)
| Iconst_float _ | Inegf | Iabsf | Iaddf | Isubf | Imulf | Idivf
| Ifloatofint |Ispecific(Isubfrev | Idivfrev | Ifloatarithmem(_, _)) ->
(arg, [| tos |], false) (* don't move it immediately *)
(* Same for a floating-point load *)
| Iload(Word, addr) when res.(0).typ = Float ->
(arg, [| tos |], false)
(* For storing a byte, the argument must be in eax...edx.
For storing a halfword, any reg is ok.
Keep it simple, just force it to be in edx in both cases. *)
| Istore(Word, addr) -> raise Use_default
| Istore(chunk, addr) ->
let newarg = Array.copy arg in
newarg.(0) <- edx;
(newarg, res, false)
(* Other instructions are more or less regular *)
| _ -> raise Use_default
let is_immediate (n: int) = true
let word_addressed = false
(* Calling conventions *)
let calling_conventions first_int last_int first_float last_float make_stack
arg =
let loc = Array.create (Array.length arg) Reg.dummy in
let int = ref first_int in
let float = ref first_float in
let ofs = ref 0 in
for i = 0 to Array.length arg - 1 do
match arg.(i).typ with
Int | Addr as ty ->
if !int <= last_int then begin
loc.(i) <- phys_reg !int;
incr int
end else begin
loc.(i) <- stack_slot (make_stack !ofs) ty;
ofs := !ofs + size_int
end
| Float ->
if !float <= last_float then begin
loc.(i) <- phys_reg !float;
incr float
end else begin
loc.(i) <- stack_slot (make_stack !ofs) Float;
ofs := !ofs + size_float
end
done;
(loc, !ofs)
let incoming ofs = Incoming ofs
let outgoing ofs = Outgoing ofs
let not_supported ofs = fatal_error "Proc.loc_results: cannot call"
let loc_arguments arg =
calling_conventions 0 5 100 99 outgoing arg
let loc_parameters arg =
let (loc, ofs) = calling_conventions 0 5 100 99 incoming arg in loc
let loc_results res =
let (loc, ofs) = calling_conventions 0 5 100 100 not_supported res in loc
let extcall_use_push = true
let loc_external_arguments arg =
fatal_error "Proc.loc_external_arguments"
let loc_external_results res =
let (loc, ofs) = calling_conventions 0 0 100 100 not_supported res in loc
let loc_exn_bucket = eax
(* Registers destroyed by operations *)
let destroyed_at_c_call = (* ebx, esi, edi, ebp preserved *)
Array.of_list(List.map phys_reg [0;2;3])
let destroyed_at_oper = function
Iop(Icall_ind | Icall_imm _ | Iextcall(_, true)) -> all_phys_regs
| Iop(Iextcall(_, false)) -> destroyed_at_c_call
| Iop(Iintop(Idiv | Imod)) -> [| eax; edx |]
| Iop(Iintop_imm(Imod, _)) -> [| eax |]
| Iop(Ialloc _) -> [| eax |]
| Iop(Iintop(Icomp _) | Iintop_imm(Icomp _, _)) -> [| eax |]
| Iop(Iintoffloat) -> [| eax |]
| Iifthenelse(Ifloattest(_, _), _, _) -> [| eax |]
| _ -> [||]
let destroyed_at_raise = all_phys_regs
(* Maximal register pressure *)
let safe_register_pressure op = 4
let max_register_pressure = function
Iextcall(_, _) -> [| 4; max_int |]
| Iintop(Idiv | Imod) -> [| 5; max_int |]
| Ialloc _ | Iintop(Icomp _) | Iintop_imm(Icomp _, _) |
Iintoffloat -> [| 6; max_int |]
| _ -> [|7; max_int |]
(* Reloading of instruction arguments, storing of instruction results *)
let stackp r =
match r.loc with
Stack _ -> true
| _ -> false
let reload_test makereg round tst arg =
match tst with
Iinttest cmp ->
if stackp arg.(0) & stackp arg.(1)
then [| makereg arg.(0); arg.(1) |]
else arg
| _ -> arg
(* Since #floatregs = 0, pseudoregs of type float will never be reloaded.
Hence there is no need to make special cases for
floating-point operations. *)
let reload_operation makereg round op arg res =
match op with
Iintop(Iadd|Isub|Imul|Iand|Ior|Ixor|Icomp _|Icheckbound) ->
(* One of the two arguments can reside in the stack *)
if stackp arg.(0) & stackp arg.(1)
then ([|arg.(0); makereg arg.(1)|], res)
else (arg, res)
| Iintop(Ilsl|Ilsr|Iasr) | Iintop_imm(_, _) | Ifloatofint | Iintoffloat |
Ispecific(Ipush) ->
(* The argument(s) can be either in register or on stack *)
(arg, res)
| _ -> (* Other operations: all args and results in registers *)
raise Use_default
(* Scheduling is turned off because our model does not fit the 486
nor Pentium very well. In particular, it messes up with the
float reg stack. *)
let need_scheduling = false
let oper_latency _ = 0
(* Layout of the stack frame *)
let num_stack_slots = [| 0; 0 |]
let contains_calls = ref false
(* Calling the assembler *)
let assemble_file infile outfile =
Ccomp.command ("ml /nologo /coff /Cp /c /Fo" ^ outfile ^ " " ^ infile ^ ">NUL")
(* /Cp preserve case of all used identifiers
/c assemble only
/Fo output file name *)

View File

@ -1,347 +0,0 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Description of the Motorola 68020 processor *)
open Misc
open Arch
open Format
open Cmm
open Reg
open Mach
(* Registers available for register allocation *)
(* Register map:
A0 - A6 0-6 address registers (A2-A6 callee-save)
A7 stack pointer
D0 - D4 7-11 data registers (D2 - D7 callee-save)
D5 temporary
D6 allocation pointer
D7 trap pointer
FP0 - FP7 12-19 floating-point registers (FP2 - FP7 callee-save)
*)
let register_names =
[| "a0"; "a1"; "a2"; "a3"; "a4"; "a5"; "a6";
"d0"; "d1"; "d2"; "d3"; "d4";
"fp0"; "fp1"; "fp2"; "fp3"; "fp4"; "fp5"; "fp6"; "fp7" |]
let num_register_classes = 3
let register_class r =
match r.typ with
Addr -> 0
| Int -> 1
| Float -> 2
let num_available_registers = [| 7; 5; 8 |]
let first_available_register = [| 0; 7; 12 |]
let register_name r = register_names.(r)
(* There is no scheduling, so just pack registers. *)
let rotate_registers = false
(* Representation of hard registers by pseudo-registers *)
let all_phys_regs =
let v = Array.create 20 Reg.dummy in
for i = 0 to 6 do v.(i) <- Reg.at_location Addr (Reg i) done;
for i = 7 to 11 do v.(i) <- Reg.at_location Int (Reg i) done;
for i = 12 to 19 do v.(i) <- Reg.at_location Float (Reg i) done;
v
let phys_reg n = all_phys_regs.(n)
let stack_slot slot ty = Reg.at_location ty (Stack slot)
let reg_A0 = phys_reg 0
let reg_FP0 = phys_reg 12
(* Exceptions raised to signal cases not handled here *)
exception Use_default
(* Instruction selection *)
(* Auxiliary for recognizing addressing modes *)
type addressing_expr =
Asymbol of string
| Alinear of expression
| Aadd of expression * expression
| Ascale of expression * int
| Ascaledadd of expression * expression * int
let rec select_addr exp =
match exp with
Cconst_symbol s ->
(Asymbol s, 0)
| Cop((Caddi | Cadda), [arg; Cconst_int m]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop((Csubi | Csuba), [arg; Cconst_int m]) ->
let (a, n) = select_addr arg in (a, n - m)
| Cop((Caddi | Cadda), [Cconst_int m; arg]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop(Clsl, [arg; Cconst_int(1|2|3 as shift)]) ->
begin match select_addr arg with
(Alinear e, n) -> (Ascale(e, 1 lsl shift), n lsl shift)
| _ -> (Alinear exp, 0)
end
| Cop(Cmuli, [arg; Cconst_int(2|4|8 as mult)]) ->
begin match select_addr arg with
(Alinear e, n) -> (Ascale(e, mult), n * mult)
| _ -> (Alinear exp, 0)
end
| Cop(Cmuli, [Cconst_int(2|4|8 as mult); arg]) ->
begin match select_addr arg with
(Alinear e, n) -> (Ascale(e, mult), n * mult)
| _ -> (Alinear exp, 0)
end
| Cop((Caddi | Cadda), [arg1; arg2]) ->
begin match (select_addr arg1, select_addr arg2) with
((Alinear e1, n1), (Alinear e2, n2)) ->
(Aadd(e1, e2), n1 + n2)
| ((Alinear e1, n1), (Ascale(e2, scale), n2)) ->
(Ascaledadd(e1, e2, scale), n1 + n2)
| ((Ascale(e1, scale), n1), (Alinear e2, n2)) ->
(Ascaledadd(e2, e1, scale), n1 + n2)
| (_, (Ascale(e2, scale), n2)) ->
(Ascaledadd(arg1, e2, scale), n2)
| ((Ascale(e1, scale), n1), _) ->
(Ascaledadd(arg2, e1, scale), n1)
| _ ->
(Aadd(arg1, arg2), 0)
end
| arg ->
(Alinear arg, 0)
let select_addressing exp =
match select_addr exp with
(Asymbol s, d) ->
(Ibased(s, d), Ctuple [])
| (Alinear e, d) ->
(Iindexed d, e)
| (Aadd(e1, e2), d) ->
(Iindexed2 d, Ctuple[e1; e2])
| (Ascale(e, scale), d) ->
(Iscaled(scale, d), e)
| (Ascaledadd(e1, e2, scale), d) ->
(Iindexed2scaled(scale, d), Ctuple[e1; e2])
(* Selection of immediate shifts *)
let select_shift op args =
match args with
[arg1; Cconst_int n] when n >= 1 && n <= 8 -> (Iintop_imm(op, n), [arg1])
| _ -> (Iintop op, args)
(* Main instruction selection functions *)
let select_oper op args =
match op with
(* Recognize the LEA instruction *)
Cadda | Csuba ->
begin match select_addressing (Cop(op, args)) with
(Iindexed d, _) -> raise Use_default
| (addr, arg) -> (Ispecific(Ilea addr), [arg])
end
(* Recognize immediate shifts only if 1 <= count <= 8 *)
| Clsl -> select_shift Ilsl args
| Clsr -> select_shift Ilsr args
| Casr -> select_shift Iasr args
(* Recognize store instructions *)
| Cstore ->
begin match args with
[loc; Cconst_int n] ->
let (addr, arg) = select_addressing loc in
(Ispecific(Istore_int(n, addr)), [arg])
| [loc; Cconst_pointer n] ->
let (addr, arg) = select_addressing loc in
(Ispecific(Istore_int(n, addr)), [arg])
| [loc; Cconst_symbol s] ->
let (addr, arg) = select_addressing loc in
(Ispecific(Istore_symbol(s, addr)), [arg])
| _ ->
raise Use_default
end
| _ -> raise Use_default
let select_store addr exp =
match exp with
Cconst_int n -> (Ispecific(Istore_int(n, addr)), Ctuple [])
| Cconst_pointer n -> (Ispecific(Istore_int(n, addr)), Ctuple [])
| Cconst_symbol s -> (Ispecific(Istore_symbol(s, addr)), Ctuple [])
| _ -> raise Use_default
let select_push exp =
match exp with
Cconst_int n -> (Ispecific(Ipush_int n), Ctuple [])
| Cconst_pointer n -> (Ispecific(Ipush_int n), Ctuple [])
| Cconst_symbol s -> (Ispecific(Ipush_symbol s), Ctuple [])
| Cop(Cload ty, [loc]) when ty = typ_float ->
let (addr, arg) = select_addressing loc in
(Ispecific(Ipush_load_float addr), arg)
| Cop(Cload ty, [loc]) when ty = typ_addr or ty = typ_int ->
let (addr, arg) = select_addressing loc in
(Ispecific(Ipush_load addr), arg)
| _ -> (Ispecific(Ipush), exp)
let pseudoregs_for_operation op arg res =
match op with
(* Two-address binary operations *)
Iintop(Iadd | Isub | Imul | Idiv | Imod | Ilsl | Ilsr | Iasr) |
Iaddf | Isubf | Imulf | Idivf ->
([|res.(0); arg.(1)|], res, false)
(* Two-address binary operations, forcing the second argument to be
in a data register *)
| Iintop(Iand | Ior | Ixor) ->
let newarg1 = Reg.create Int in
([|res.(0); newarg1|], res, false)
(* Two-address unary operations *)
| Iintop_imm((Iadd | Isub | Imul | Idiv | Imod | Iand | Ior | Ixor |
Ilsl | Ilsr | Iasr), _) ->
(res, res, false)
(* Other instructions are regular *)
| _ -> raise Use_default
let is_immediate (n: int) = true
let word_addressed = false
(* Calling conventions *)
let calling_conventions first_addr last_addr first_float last_float
make_stack arg =
let loc = Array.create (Array.length arg) Reg.dummy in
let addr = ref first_addr in
let float = ref first_float in
let ofs = ref 0 in
for i = 0 to Array.length arg - 1 do
match arg.(i).typ with
(Addr | Int) as ty ->
if !addr <= last_addr then begin
loc.(i) <- phys_reg !addr;
incr addr
end else begin
loc.(i) <- stack_slot (make_stack !ofs) ty;
ofs := !ofs + size_addr
end
| Float ->
if !float <= last_float then begin
loc.(i) <- phys_reg !float;
incr float
end else begin
loc.(i) <- stack_slot (make_stack !ofs) Float;
ofs := !ofs + size_float
end
done;
(loc, !ofs)
let incoming ofs = Incoming ofs
let outgoing ofs = Outgoing ofs
let not_supported ofs = fatal_error "Proc.loc_results: cannot call"
let loc_arguments arg =
calling_conventions 0 5 12 18 outgoing arg
let loc_parameters arg =
let (loc, ofs) = calling_conventions 0 5 12 18 incoming arg in loc
let loc_results res =
let (loc, ofs) = calling_conventions 0 5 12 18 not_supported res in loc
let extcall_use_push = true
let loc_external_arguments arg =
fatal_error "Proc.loc_external_arguments"
let loc_external_results res =
let (loc, ofs) = calling_conventions 7 7 12 12 not_supported res in loc
let loc_exn_bucket = reg_A0
(* Registers destroyed by operations *)
let destroyed_at_c_call =
Array.of_list(List.map phys_reg [0; 1; 7; 8; 12; 13])
let destroyed_at_oper = function
Iop(Icall_ind | Icall_imm _ | Iextcall(_, true)) -> all_phys_regs
| Iop(Iextcall(_, false)) -> destroyed_at_c_call
| Iop(Iintoffloat) -> [| reg_FP0 |]
| _ -> [||]
let destroyed_at_raise = all_phys_regs
(* Maximal register pressure *)
let safe_register_pressure op = 5
let max_register_pressure = function
Iextcall(_, _) -> [| 5; 3; 6 |]
| Iintoffloat -> [| 7; 5; 7 |]
| _ -> num_available_registers
(* Reloading of instruction arguments, storing of instruction results. *)
let stackp r =
match r.loc with
Stack _ -> true
| _ -> false
let reload_test makereg round tst arg =
match tst with
Iinttest _ | Ifloattest _ ->
(* The second argument can be on stack *)
[| makereg arg.(0); arg.(1) |]
| _ ->
(* The argument can be on stack *)
arg
let reload_operation makereg round op arg res =
match op with
Imove | Ireload | Ispill |
Iintop_imm((Iadd | Isub | Iand | Ior | Ixor |
Icomp _ | Ilsl | Ilsr | Iasr), _) |
Ifloatofint | Iintoffloat | Ispecific(Ipush) ->
(* The argument(s) can be either in register or on stack *)
(arg, res)
| Iintop(Iadd | Isub | Iand | Ior | Ixor | Icomp _) ->
(* One of the two arguments can reside in the stack *)
if stackp arg.(0) && stackp arg.(1)
then ([|arg.(0); makereg arg.(1)|], res)
else (arg, res)
| Iintop(Ilsl | Ilsr | Iasr) ->
(* The first argument and result can reside in the stack *)
([|arg.(0); makereg arg.(1)|], res)
| Iintop(Imul | Idiv | Imod) | Iaddf | Isubf | Imulf | Idivf ->
(* The second argument can reside in the stack *)
let r = makereg arg.(0) in ([|r; arg.(1)|], [|r|])
| _ -> (* Other operations: all args and results in registers *)
raise Use_default
(* Scheduling is turned off. *)
let need_scheduling = false
let oper_latency _ = 0
(* Layout of the stack frame *)
let num_stack_slots = [| 0; 0; 0 |]
let contains_calls = ref false
(* Calling the assembler *)
let assemble_file infile outfile =
Ccomp.command ("as -o " ^ outfile ^ " " ^ infile)

View File

@ -13,10 +13,5 @@
(* Insert load/stores for pseudoregs that got assigned to stack locations. *)
val fundecl: int -> Mach.fundecl -> Mach.fundecl * bool
val fundecl: Mach.fundecl -> Mach.fundecl * bool
(* Auxiliary functions for use by the processor description to do its own
reloading *)
val makereg: Reg.t -> Reg.t
val makeregs: Reg.t array -> Reg.t array

View File

@ -11,15 +11,12 @@
(* $Id$ *)
(* Insert load/stores for pseudoregs that got assigned to stack locations.
Insert moves to comply with calling conventions, etc. *)
(* Insert load/stores for pseudoregs that got assigned to stack locations. *)
open Misc
open Reg
open Mach
let redo_regalloc = ref false
let access_stack r =
try
for i = 0 to Array.length r - 1 do
@ -29,32 +26,6 @@ let access_stack r =
with Exit ->
true
let makereg r =
match r.loc with
Unknown -> fatal_error "Reload.makereg"
| Reg _ -> r
| Stack _ ->
if Proc.num_available_registers.(Proc.register_class r) = 0
then r
else begin
redo_regalloc := true;
let newr = Reg.clone r in
(* Strongly discourage spilling this register *)
newr.spill_cost <- 100000;
newr
end
let makeregs rv =
let n = Array.length rv in
let newv = Array.create n Reg.dummy in
for i = 0 to n-1 do newv.(i) <- makereg rv.(i) done;
newv
let makereg1 rv =
let newv = Array.copy rv in
newv.(0) <- makereg rv.(0);
newv
let insert_move src dst next =
if src.loc = dst.loc
then next
@ -67,10 +38,52 @@ let insert_moves src dst next =
else insert_move src.(i) dst.(i) (insmoves (i+1))
in insmoves 0
let reload_round = ref 0
class reload_generic () as self =
val private mutable redo_regalloc = false
let rec reload i =
method makereg r =
match r.loc with
Unknown -> fatal_error "Reload.makereg"
| Reg _ -> r
| Stack _ ->
redo_regalloc <- true;
let newr = Reg.clone r in
(* Strongly discourage spilling this register *)
newr.spill_cost <- 100000;
newr
method makeregs rv =
let n = Array.length rv in
let newv = Array.create n Reg.dummy in
for i = 0 to n-1 do newv.(i) <- self#makereg rv.(i) done;
newv
method makereg1 rv =
let newv = Array.copy rv in
newv.(0) <- self#makereg rv.(0);
newv
method reload_operation op arg res =
(* By default, assume that arguments and results must reside
in hardware registers. For moves, allow one arg or one
res to be stack-allocated, but do something for
stack-to-stack moves *)
match op with
Imove | Ireload | Ispill ->
begin match arg.(0), res.(0) with
{loc = Stack s1}, {loc = Stack s2} when s1 <> s2 ->
([| self#makereg arg.(0) |], res)
| _ ->
(arg, res)
end
| _ ->
(self#makeregs arg, self#makeregs res)
method reload_test tst args =
self#makeregs args
method reload i =
match i.desc with
(* For function calls, returns, etc: the arguments and results are
already at the correct position (e.g. on stack for some arguments).
@ -78,72 +91,48 @@ let rec reload i =
indirect calls. *)
Iend | Ireturn | Iop(Itailcall_imm _) | Iraise -> i
| Iop(Itailcall_ind) ->
let newarg = makereg1 i.arg in
let newarg = self#makereg1 i.arg in
insert_moves i.arg newarg
(instr_cons_live i.desc newarg i.res i.live i.next)
| Iop(Icall_imm _ | Iextcall(_, _)) ->
instr_cons_live i.desc i.arg i.res i.live (reload i.next)
instr_cons_live i.desc i.arg i.res i.live (self#reload i.next)
| Iop(Icall_ind) ->
let newarg = makereg1 i.arg in
let newarg = self#makereg1 i.arg in
insert_moves i.arg newarg
(instr_cons_live i.desc newarg i.res i.live (reload i.next))
(instr_cons_live i.desc newarg i.res i.live (self#reload i.next))
| Iop op ->
(* Let the machine description tell us whether some arguments / results
can reside on the stack *)
let (newarg, newres) =
try
Proc.reload_operation makereg !reload_round op i.arg i.res
with Proc.Use_default ->
(* By default, assume that arguments and results must reside
in hardware registers. For moves, allow one arg or one
res to be stack-allocated, but do something for
stack-to-stack moves *)
match op with
Imove | Ireload | Ispill ->
begin match i.arg.(0), i.res.(0) with
{loc = Stack s1}, {loc = Stack s2} when s1 <> s2 ->
([| makereg i.arg.(0) |], i.res)
| _ ->
(i.arg, i.res)
end
| _ ->
(makeregs i.arg, makeregs i.res) in
let (newarg, newres) = self#reload_operation op i.arg i.res in
insert_moves i.arg newarg
(instr_cons_live i.desc newarg newres i.live
(insert_moves newres i.res
(reload i.next)))
(self#reload i.next)))
| Iifthenelse(tst, ifso, ifnot) ->
(* Let the machine description tell us whether some arguments / results
can reside on the stack *)
let newarg =
try
Proc.reload_test makereg !reload_round tst i.arg
with Proc.Use_default ->
makeregs i.arg in
let newarg = self#reload_test tst i.arg in
insert_moves i.arg newarg
(instr_cons (Iifthenelse(tst, reload ifso, reload ifnot)) newarg [||]
(reload i.next))
(instr_cons
(Iifthenelse(tst, self#reload ifso, self#reload ifnot)) newarg [||]
(self#reload i.next))
| Iswitch(index, cases) ->
let newarg = makeregs i.arg in
let newarg = self#makeregs i.arg in
insert_moves i.arg newarg
(instr_cons (Iswitch(index, Array.map reload cases)) newarg [||]
(reload i.next))
(instr_cons (Iswitch(index, Array.map self#reload cases)) newarg [||]
(self#reload i.next))
| Iloop body ->
instr_cons (Iloop(reload body)) [||] [||] (reload i.next)
instr_cons (Iloop(self#reload body)) [||] [||] (self#reload i.next)
| Icatch(body, handler) ->
instr_cons (Icatch(reload body, reload handler)) [||] [||]
(reload i.next)
instr_cons (Icatch(self#reload body, self#reload handler)) [||] [||]
(self#reload i.next)
| Iexit ->
instr_cons Iexit [||] [||] dummy_instr
| Itrywith(body, handler) ->
instr_cons (Itrywith(reload body, reload handler)) [||] [||]
(reload i.next)
instr_cons (Itrywith(self#reload body, self#reload handler)) [||] [||]
(self#reload i.next)
let fundecl round f =
redo_regalloc := false;
reload_round := round;
let new_body = reload f.fun_body in
method fundecl f =
redo_regalloc <- false;
let new_body = self#reload f.fun_body in
({fun_name = f.fun_name; fun_args = f.fun_args;
fun_body = new_body; fun_fast = f.fun_fast},
!redo_regalloc)
redo_regalloc)
end

27
asmcomp/reloadgen.mli Normal file
View File

@ -0,0 +1,27 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1997 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
class reload_generic (unit) =
method reload_operation :
Mach.operation -> Reg.t array -> Reg.t array -> Reg.t array * Reg.t array
method reload_test : Mach.test -> Reg.t array -> Reg.t array
(* Can be overriden to reflect instructions that can operate
directly on stack locations *)
method makereg : Reg.t -> Reg.t
(* Can be overriden to avoid creating new registers of some class
(i.e. if all "registers" of that class are actually on stack) *)
method makereg1 : Reg.t array -> Reg.t array
method makeregs : Reg.t array -> Reg.t array
method reload : Mach.instruction -> Mach.instruction
method fundecl : Mach.fundecl -> Mach.fundecl * bool
end

View File

@ -18,41 +18,11 @@ open Reg
open Mach
open Linearize
(* Determine whether an instruction ends a basic block or not *)
let in_basic_block instr =
match instr.desc with
Lop op ->
begin match op with
Icall_ind | Icall_imm _ | Itailcall_ind | Itailcall_imm _ -> false
| Iextcall(_, _) -> false
| Istackoffset _ -> false
| Istore(_, _) -> false
| Ialloc _ -> false
| op -> Proc.oper_latency op >= 0
(* The processor description can return a latency of -1 to signal
a specific instruction that terminates a basic block, e.g.
Istore_symbol for the I386. *)
end
| Lreloadretaddr -> true
| _ -> false
(* Estimate the delay needed to evaluate an instruction. *)
let reload_retaddr_latency =
Proc.oper_latency (Iload(Cmm.Word, Arch.identity_addressing))
let instr_latency instr =
match instr.desc with
Lop op -> Proc.oper_latency op
| Lreloadretaddr -> reload_retaddr_latency
| _ -> fatal_error "Scheduling.instr_latency"
(* Representation of the code DAG. *)
type code_dag_node =
{ instr: instruction; (* The instruction *)
delay: int; (* How many cycles it needs *)
delay: int; (* How many cycles before result is available *)
mutable sons: (code_dag_node * int) list;
(* Instructions that depend on it *)
mutable date: int; (* Start date *)
@ -75,57 +45,12 @@ let clear_code_dag () =
Hashtbl.clear code_results;
Hashtbl.clear code_uses
(* Add an instruction to the code DAG *)
(* Add an edge to the code DAG *)
let add_edge ancestor son delay =
ancestor.sons <- (son, delay) :: ancestor.sons;
son.ancestors <- son.ancestors + 1
let add_instruction ready_queue instr =
let delay = instr_latency instr in
let node =
{ instr = instr;
delay = delay;
sons = [];
date = 0;
length = -1;
ancestors = 0;
emitted_ancestors = 0 } in
(* Add edges from all instructions that define one of the registers used *)
for i = 0 to Array.length instr.arg - 1 do
try
let ancestor = Hashtbl.find code_results instr.arg.(i).loc in
add_edge ancestor node ancestor.delay
with Not_found ->
()
done;
(* Also add edges from all instructions that use one of the results
of this instruction, so that evaluation order is preserved. *)
for i = 0 to Array.length instr.res - 1 do
let ancestors = Hashtbl.find_all code_uses instr.res.(i).loc in
List.iter (fun ancestor -> add_edge ancestor node 0) ancestors
done;
(* Also add edges from all instructions that have already defined one
of the results of this instruction, so that evaluation order
is preserved. *)
for i = 0 to Array.length instr.res - 1 do
try
let ancestor = Hashtbl.find code_results instr.res.(i).loc in
add_edge ancestor node 0
with Not_found ->
()
done;
(* Remember the registers used and produced by this instruction *)
for i = 0 to Array.length instr.res - 1 do
Hashtbl.add code_results instr.res.(i).loc node
done;
for i = 0 to Array.length instr.arg - 1 do
Hashtbl.add code_uses instr.arg.(i).loc node
done;
(* If this is a root instruction (all arguments already computed),
add it to the ready queue *)
if node.ancestors = 0 then node :: ready_queue else ready_queue
(* Compute length of longest path to a result.
For leafs of the DAG, see whether their result is used in the instruction
immediately following the basic block (a "critical" output). *)
@ -160,21 +85,6 @@ let rec longest_path critical_outputs node =
end;
node.length
(* Given a list of instructions with estimated start date, choose one
that we can start (start date <= current date) and that has
maximal distance to result. If we can't find any, return None. *)
let extract_ready_instr date queue =
let rec extract best = function
[] ->
if best == dummy_node then None else Some best
| instr :: rem ->
let new_best =
if instr.date <= date & instr.length > best.length
then instr else best in
extract new_best rem in
extract dummy_node queue
(* Remove an instruction from the ready queue *)
let rec remove_instr node = function
@ -182,66 +92,189 @@ let rec remove_instr node = function
| instr :: rem ->
if instr == node then rem else instr :: remove_instr node rem
(* We treat Lreloadretaddr as a word-sized load *)
let some_load = (Iload(Cmm.Word, Arch.identity_addressing))
(* The generic scheduler *)
class virtual scheduler_generic () as self =
(* Determine whether an operation ends a basic block or not.
Can be overriden for some processors to signal specific instructions
that terminate a basic block, e.g. Istore_symbol for the 386. *)
method oper_in_basic_block = function
Icall_ind -> false
| Icall_imm _ -> false
| Itailcall_ind -> false
| Itailcall_imm _ -> false
| Iextcall(_, _) -> false
| Istackoffset _ -> false
| Istore(_, _) -> false
| Ialloc _ -> false
| _ -> true
(* Determine whether an instruction ends a basic block or not *)
method instr_in_basic_block instr =
match instr.desc with
Lop op -> self#oper_in_basic_block op
| Lreloadretaddr -> true
| _ -> false
(* Estimate the delay needed to evaluate an operation. *)
virtual oper_latency : Mach.operation -> int
(* Estimate the delay needed to evaluate an instruction *)
method instr_latency instr =
match instr.desc with
Lop op ->
self#oper_latency op
| Lreloadretaddr ->
self#oper_latency some_load
| _ ->
assert false
(* Estimate the number of cycles consumed by emitting an operation. *)
virtual oper_issue_cycles : Mach.operation -> int
(* Estimate the number of cycles consumed by emitting an instruction. *)
method instr_issue_cycles instr =
match instr.desc with
Lop op ->
self#oper_issue_cycles op
| Lreloadretaddr ->
self#oper_issue_cycles some_load
| _ ->
assert false
(* Add an instruction to the code dag *)
method add_instruction ready_queue instr =
let delay = self#instr_latency instr in
let node =
{ instr = instr;
delay = delay;
sons = [];
date = 0;
length = -1;
ancestors = 0;
emitted_ancestors = 0 } in
(* Add edges from all instructions that define one of the registers used
(RAW dependencies) *)
for i = 0 to Array.length instr.arg - 1 do
try
let ancestor = Hashtbl.find code_results instr.arg.(i).loc in
add_edge ancestor node ancestor.delay
with Not_found ->
()
done;
(* Also add edges from all instructions that use one of the result regs
of this instruction (WAR dependencies). *)
for i = 0 to Array.length instr.res - 1 do
let ancestors = Hashtbl.find_all code_uses instr.res.(i).loc in
List.iter (fun ancestor -> add_edge ancestor node 0) ancestors
done;
(* Also add edges from all instructions that have already defined one
of the results of this instruction (WAW dependencies). *)
for i = 0 to Array.length instr.res - 1 do
try
let ancestor = Hashtbl.find code_results instr.res.(i).loc in
add_edge ancestor node 0
with Not_found ->
()
done;
(* Remember the registers used and produced by this instruction *)
for i = 0 to Array.length instr.res - 1 do
Hashtbl.add code_results instr.res.(i).loc node
done;
for i = 0 to Array.length instr.arg - 1 do
Hashtbl.add code_uses instr.arg.(i).loc node
done;
(* If this is a root instruction (all arguments already computed),
add it to the ready queue *)
if node.ancestors = 0 then node :: ready_queue else ready_queue
(* Given a list of instructions and a date, choose one or several
that are ready to be computed (start date <= current date)
and that we can emit in one cycle. Favor instructions with
maximal distance to result. If we can't find any, return None.
This does not take multiple issues into account, though. *)
method ready_instruction date queue =
let rec extract best = function
[] ->
if best == dummy_node then None else Some best
| instr :: rem ->
let new_best =
if instr.date <= date && instr.length > best.length
then instr else best in
extract new_best rem in
extract dummy_node queue
(* Schedule a basic block, adding its instructions in front of the given
instruction sequence *)
let rec reschedule ready_queue date cont =
match ready_queue with
[] -> cont
| _ ->
(* Find "most ready" instruction in queue *)
match extract_ready_instr date ready_queue with
None ->
(* Try again, one cycle later *)
reschedule ready_queue (date + 1) cont
| Some node ->
(* Update the start date and number of ancestors emitted of
all descendents of this node. Enter those that become ready
in the queue. *)
let new_queue = ref (remove_instr node ready_queue) in
List.iter
(fun (son, delay) ->
let completion_date = date + delay in
if son.date < completion_date then son.date <- completion_date;
son.emitted_ancestors <- son.emitted_ancestors + 1;
if son.emitted_ancestors = son.ancestors then
new_queue := son :: !new_queue)
node.sons;
instr_cons node.instr.desc node.instr.arg node.instr.res
(reschedule !new_queue (date + 1) cont)
(* Schedule basic blocks in an instruction sequence *)
let rec schedule i =
match i.desc with
Lend -> i
| _ ->
if in_basic_block i then begin
clear_code_dag();
schedule_block [] i
end else
{ desc = i.desc; arg = i.arg; res = i.res; live = i.live;
next = schedule i.next }
and schedule_block ready_queue i =
if in_basic_block i then
schedule_block (add_instruction ready_queue i) i.next
else begin
let critical_outputs =
match i.desc with
Lop(Icall_ind | Itailcall_ind) -> [| i.arg.(0) |]
| Lop(Icall_imm _ | Itailcall_imm _ | Iextcall(_, _)) -> [||]
| Lreturn -> [||]
| _ -> i.arg in
List.iter (longest_path critical_outputs) ready_queue;
reschedule ready_queue 0 (schedule i)
method reschedule ready_queue date cont =
if ready_queue = [] then cont else begin
match self#ready_instruction date ready_queue with
None ->
self#reschedule ready_queue (date + 1) cont
| Some node ->
(* Remove node from queue *)
let new_queue = ref (remove_instr node ready_queue) in
(* Update the start date and number of ancestors emitted of
all descendents of this node. Enter those that become ready
in the queue. *)
List.iter
(fun (son, delay) ->
let completion_date = date + delay in
if son.date < completion_date then son.date <- completion_date;
son.emitted_ancestors <- son.emitted_ancestors + 1;
if son.emitted_ancestors = son.ancestors then
new_queue := son :: !new_queue)
node.sons;
let issue_cycles = self#instr_issue_cycles node.instr in
instr_cons node.instr.desc node.instr.arg node.instr.res
(self#reschedule !new_queue (date + issue_cycles) cont)
end
(* Entry point *)
(* Don't bother to schedule for initialization code and the like. *)
let fundecl f =
if Proc.need_scheduling & f.fun_fast then begin
method schedule_fundecl f =
let rec schedule i =
match i.desc with
Lend -> i
| _ ->
if self#instr_in_basic_block i then begin
clear_code_dag();
schedule_block [] i
end else
{ desc = i.desc; arg = i.arg; res = i.res; live = i.live;
next = schedule i.next }
and schedule_block ready_queue i =
if self#instr_in_basic_block i then
schedule_block (self#add_instruction ready_queue i) i.next
else begin
let critical_outputs =
match i.desc with
Lop(Icall_ind | Itailcall_ind) -> [| i.arg.(0) |]
| Lop(Icall_imm _ | Itailcall_imm _ | Iextcall(_, _)) -> [||]
| Lreturn -> [||]
| _ -> i.arg in
List.iter (longest_path critical_outputs) ready_queue;
self#reschedule ready_queue 0 (schedule i)
end in
if f.fun_fast then begin
let new_body = schedule f.fun_body in
clear_code_dag();
{ fun_name = f.fun_name;
@ -249,3 +282,5 @@ let fundecl f =
fun_fast = f.fun_fast }
end else
f
end

48
asmcomp/schedgen.mli Normal file
View File

@ -0,0 +1,48 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1997 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Instruction scheduling *)
type code_dag_node =
{ instr: Linearize.instruction;
delay: int;
mutable sons: (code_dag_node * int) list;
mutable date: int;
mutable length: int;
mutable ancestors: int;
mutable emitted_ancestors: int }
class virtual scheduler_generic (unit) =
(* Can be overriden by processor description *)
virtual oper_issue_cycles : Mach.operation -> int
(* Number of cycles needed to issue the given operation *)
virtual oper_latency : Mach.operation -> int
(* Number of cycles needed to complete the given operation *)
method oper_in_basic_block : Mach.operation -> bool
(* Says whether the given operation terminates a basic block *)
(* Internal stuff, don't override *)
method add_instruction :
code_dag_node list -> Linearize.instruction -> code_dag_node list
method instr_in_basic_block : Linearize.instruction -> bool
method instr_issue_cycles : Linearize.instruction -> int
method instr_latency : Linearize.instruction -> int
method ready_instruction :
int -> code_dag_node list -> code_dag_node option
method reschedule :
code_dag_node list ->
int -> Linearize.instruction -> Linearize.instruction
(* Entry point *)
method schedule_fundecl : Linearize.fundecl -> Linearize.fundecl
end

View File

@ -19,6 +19,8 @@ open Cmm
open Reg
open Mach
type environment = (Ident.t, Reg.t array) Tbl.t
(* Infer the type of the result of an operation *)
let oper_result_type = function
@ -93,133 +95,12 @@ let rec is_simple_expr = function
end
| _ -> false
(* Default instruction selection for operators *)
(* Swap the two arguments of an integer comparison *)
let rec sel_operation op args =
match (op, args) with
(Capply ty, Cconst_symbol s :: rem) -> (Icall_imm s, rem)
| (Capply ty, _) -> (Icall_ind, args)
| (Cextcall(s, ty, alloc), _) -> (Iextcall(s, alloc), args)
| (Cload ty, [arg]) ->
let (addr, eloc) = Proc.select_addressing arg in
(Iload(Word, addr), [eloc])
| (Cloadchunk chunk, [arg]) ->
let (addr, eloc) = Proc.select_addressing arg in
(Iload(chunk, addr), [eloc])
| (Cstore, [arg1; arg2]) ->
let (addr, eloc) = Proc.select_addressing arg1 in
begin try
let (op, newarg2) = Proc.select_store addr arg2 in
(op, [newarg2; eloc])
with Proc.Use_default ->
(Istore(Word, addr), [arg2; eloc])
(* Inversion addr/datum in Istore *)
end
| (Cstorechunk chunk, [arg1; arg2]) ->
let (addr, eloc) = Proc.select_addressing arg1 in
(Istore(chunk, addr), [arg2; eloc])
(* Inversion addr/datum in Istore *)
| (Calloc, _) -> (Ialloc 0, args)
| (Caddi, _) -> sel_arith_comm Iadd args
| (Csubi, _) -> sel_arith Isub args
| (Cmuli, [arg1; Cconst_int n]) ->
let l = Misc.log2 n in
if n = 1 lsl l
then (Iintop_imm(Ilsl, l), [arg1])
else sel_arith_comm Imul args
| (Cmuli, [Cconst_int n; arg1]) ->
let l = Misc.log2 n in
if n = 1 lsl l
then (Iintop_imm(Ilsl, l), [arg1])
else sel_arith_comm Imul args
| (Cmuli, _) -> sel_arith_comm Imul args
| (Cdivi, _) -> sel_arith Idiv args
| (Cmodi, _) -> sel_arith_comm Imod args
| (Cand, _) -> sel_arith_comm Iand args
| (Cor, _) -> sel_arith_comm Ior args
| (Cxor, _) -> sel_arith_comm Ixor args
| (Clsl, _) -> sel_shift Ilsl args
| (Clsr, _) -> sel_shift Ilsr args
| (Casr, _) -> sel_shift Iasr args
| (Ccmpi comp, _) -> sel_arith_comp (Isigned comp) args
| (Cadda, _) -> sel_arith_comm Iadd args
| (Csuba, _) -> sel_arith Isub args
| (Ccmpa comp, _) -> sel_arith_comp (Iunsigned comp) args
| (Cnegf, _) -> (Inegf, args)
| (Cabsf, _) -> (Iabsf, args)
| (Caddf, _) -> (Iaddf, args)
| (Csubf, _) -> (Isubf, args)
| (Cmulf, _) -> (Imulf, args)
| (Cdivf, _) -> (Idivf, args)
| (Cfloatofint, _) -> (Ifloatofint, args)
| (Cintoffloat, _) -> (Iintoffloat, args)
| (Ccheckbound, _) -> sel_arith Icheckbound args
| _ -> fatal_error "Selection.sel_oper"
and sel_arith_comm op = function
[arg; Cconst_int n] when Proc.is_immediate n ->
(Iintop_imm(op, n), [arg])
| [arg; Cconst_pointer n] when Proc.is_immediate n ->
(Iintop_imm(op, n), [arg])
| [Cconst_int n; arg] when Proc.is_immediate n ->
(Iintop_imm(op, n), [arg])
| [Cconst_pointer n; arg] when Proc.is_immediate n ->
(Iintop_imm(op, n), [arg])
| args ->
(Iintop op, args)
and sel_arith op = function
[arg; Cconst_int n] when Proc.is_immediate n ->
(Iintop_imm(op, n), [arg])
| [arg; Cconst_pointer n] when Proc.is_immediate n ->
(Iintop_imm(op, n), [arg])
| args ->
(Iintop op, args)
and sel_shift op = function
[arg; Cconst_int n] when n >= 0 & n < Arch.size_int * 8 ->
(Iintop_imm(op, n), [arg])
| args ->
(Iintop op, args)
and sel_arith_comp cmp = function
[arg; Cconst_int n] when Proc.is_immediate n ->
(Iintop_imm(Icomp cmp, n), [arg])
| [arg; Cconst_pointer n] when Proc.is_immediate n ->
(Iintop_imm(Icomp cmp, n), [arg])
| [Cconst_int n; arg] when Proc.is_immediate n ->
(Iintop_imm(Icomp(swap_intcomp cmp), n), [arg])
| [Cconst_pointer n; arg] when Proc.is_immediate n ->
(Iintop_imm(Icomp(swap_intcomp cmp), n), [arg])
| args ->
(Iintop(Icomp cmp), args)
and swap_intcomp = function
let swap_intcomp = function
Isigned cmp -> Isigned(swap_comparison cmp)
| Iunsigned cmp -> Iunsigned(swap_comparison cmp)
(* Instruction selection for conditionals *)
let sel_condition = function
Cop(Ccmpi cmp, [arg1; Cconst_int n]) when Proc.is_immediate n ->
(Iinttest_imm(Isigned cmp, n), arg1)
| Cop(Ccmpi cmp, [Cconst_int n; arg2]) when Proc.is_immediate n ->
(Iinttest_imm(Isigned(swap_comparison cmp), n), arg2)
| Cop(Ccmpi cmp, args) ->
(Iinttest(Isigned cmp), Ctuple args)
| Cop(Ccmpa cmp, [arg1; Cconst_pointer n]) when Proc.is_immediate n ->
(Iinttest_imm(Iunsigned cmp, n), arg1)
| Cop(Ccmpa cmp, [Cconst_pointer n; arg2]) when Proc.is_immediate n ->
(Iinttest_imm(Iunsigned(swap_comparison cmp), n), arg2)
| Cop(Ccmpa cmp, args) ->
(Iinttest(Iunsigned cmp), Ctuple args)
| Cop(Ccmpf cmp, args) ->
(Ifloattest(cmp, false), Ctuple args)
| Cop(Cand, [arg; Cconst_int 1]) ->
(Ioddtest, arg)
| arg ->
(Itruetest, arg)
(* Naming of registers *)
let all_regs_anonymous rv =
@ -239,43 +120,6 @@ let name_regs id rv =
rv.(i).name <- Ident.name id ^ "#" ^ string_of_int i
done
(* Buffering of instruction sequences *)
type instruction_sequence = instruction ref
let new_sequence() = ref dummy_instr
let insert desc arg res seq =
seq := instr_cons desc arg res !seq
let extract_sequence seq =
let rec extract res i =
if i == dummy_instr
then res
else extract (instr_cons i.desc i.arg i.res res) i.next in
extract (end_instr()) !seq
(* Insert a sequence of moves from one pseudoreg set to another. *)
let insert_move src dst seq =
if src.stamp <> dst.stamp then
insert (Iop Imove) [|src|] [|dst|] seq
let insert_moves src dst seq =
for i = 0 to Array.length src - 1 do
insert_move src.(i) dst.(i) seq
done
(* Insert moves and stack offsets for function arguments and results *)
let insert_move_args arg loc stacksize seq =
if stacksize <> 0 then insert (Iop(Istackoffset stacksize)) [||] [||] seq;
insert_moves arg loc seq
let insert_move_results loc res stacksize seq =
if stacksize <> 0 then insert(Iop(Istackoffset(-stacksize))) [||] [||] seq;
insert_moves loc res seq
(* "Join" two instruction sequences, making sure they return their results
in the same registers. *)
@ -288,14 +132,14 @@ let join r1 seq1 r2 seq2 =
for i = 0 to l1-1 do
if String.length r1.(i).name = 0 then begin
r.(i) <- r1.(i);
insert_move r2.(i) r1.(i) seq2
seq2#insert_move r2.(i) r1.(i)
end else if String.length r2.(i).name = 0 then begin
r.(i) <- r2.(i);
insert_move r1.(i) r2.(i) seq1
seq1#insert_move r1.(i) r2.(i)
end else begin
r.(i) <- Reg.create r1.(i).typ;
insert_move r1.(i) r.(i) seq1;
insert_move r2.(i) r.(i) seq2
seq1#insert_move r1.(i) r.(i);
seq2#insert_move r2.(i) r.(i)
end
done;
r
@ -317,51 +161,211 @@ let join_array rs =
done;
for i = 0 to Array.length rs - 1 do
let (r, s) = rs.(i) in
if Array.length r > 0 then insert_moves r res s
if Array.length r > 0 then s#insert_moves r res
done;
res
end
(* Add an Iop opcode.
Offer the processor description an opportunity to insert moves
before and after the operation, i.e. for two-address
(* The default instruction selection class *)
class virtual selector_generic () as self =
(* Says whether an integer constant is a suitable immediate argument *)
virtual is_immediate : int -> bool
(* Selection of addressing modes *)
virtual select_addressing :
Cmm.expression -> Arch.addressing_mode * Cmm.expression
(* Default instruction selection for stores *)
method select_store addr arg =
(Istore(Word, addr), arg)
(* Default instruction selection for operators *)
method select_operation op args =
match (op, args) with
(Capply ty, Cconst_symbol s :: rem) -> (Icall_imm s, rem)
| (Capply ty, _) -> (Icall_ind, args)
| (Cextcall(s, ty, alloc), _) -> (Iextcall(s, alloc), args)
| (Cload ty, [arg]) ->
let (addr, eloc) = self#select_addressing arg in
(Iload(Word, addr), [eloc])
| (Cloadchunk chunk, [arg]) ->
let (addr, eloc) = self#select_addressing arg in
(Iload(chunk, addr), [eloc])
| (Cstore, [arg1; arg2]) ->
let (addr, eloc) = self#select_addressing arg1 in
let (op, newarg2) = self#select_store addr arg2 in
(op, [newarg2; eloc])
(* Inversion addr/datum in Istore *)
| (Cstorechunk chunk, [arg1; arg2]) ->
let (addr, eloc) = self#select_addressing arg1 in
(Istore(chunk, addr), [arg2; eloc])
(* Inversion addr/datum in Istore *)
| (Calloc, _) -> (Ialloc 0, args)
| (Caddi, _) -> self#select_arith_comm Iadd args
| (Csubi, _) -> self#select_arith Isub args
| (Cmuli, [arg1; Cconst_int n]) ->
let l = Misc.log2 n in
if n = 1 lsl l
then (Iintop_imm(Ilsl, l), [arg1])
else self#select_arith_comm Imul args
| (Cmuli, [Cconst_int n; arg1]) ->
let l = Misc.log2 n in
if n = 1 lsl l
then (Iintop_imm(Ilsl, l), [arg1])
else self#select_arith_comm Imul args
| (Cmuli, _) -> self#select_arith_comm Imul args
| (Cdivi, _) -> self#select_arith Idiv args
| (Cmodi, _) -> self#select_arith_comm Imod args
| (Cand, _) -> self#select_arith_comm Iand args
| (Cor, _) -> self#select_arith_comm Ior args
| (Cxor, _) -> self#select_arith_comm Ixor args
| (Clsl, _) -> self#select_shift Ilsl args
| (Clsr, _) -> self#select_shift Ilsr args
| (Casr, _) -> self#select_shift Iasr args
| (Ccmpi comp, _) -> self#select_arith_comp (Isigned comp) args
| (Cadda, _) -> self#select_arith_comm Iadd args
| (Csuba, _) -> self#select_arith Isub args
| (Ccmpa comp, _) -> self#select_arith_comp (Iunsigned comp) args
| (Cnegf, _) -> (Inegf, args)
| (Cabsf, _) -> (Iabsf, args)
| (Caddf, _) -> (Iaddf, args)
| (Csubf, _) -> (Isubf, args)
| (Cmulf, _) -> (Imulf, args)
| (Cdivf, _) -> (Idivf, args)
| (Cfloatofint, _) -> (Ifloatofint, args)
| (Cintoffloat, _) -> (Iintoffloat, args)
| (Ccheckbound, _) -> self#select_arith Icheckbound args
| _ -> fatal_error "Selection.select_oper"
method select_arith_comm op = function
[arg; Cconst_int n] when self#is_immediate n ->
(Iintop_imm(op, n), [arg])
| [arg; Cconst_pointer n] when self#is_immediate n ->
(Iintop_imm(op, n), [arg])
| [Cconst_int n; arg] when self#is_immediate n ->
(Iintop_imm(op, n), [arg])
| [Cconst_pointer n; arg] when self#is_immediate n ->
(Iintop_imm(op, n), [arg])
| args ->
(Iintop op, args)
method select_arith op = function
[arg; Cconst_int n] when self#is_immediate n ->
(Iintop_imm(op, n), [arg])
| [arg; Cconst_pointer n] when self#is_immediate n ->
(Iintop_imm(op, n), [arg])
| args ->
(Iintop op, args)
method select_shift op = function
[arg; Cconst_int n] when n >= 0 & n < Arch.size_int * 8 ->
(Iintop_imm(op, n), [arg])
| args ->
(Iintop op, args)
method select_arith_comp cmp = function
[arg; Cconst_int n] when self#is_immediate n ->
(Iintop_imm(Icomp cmp, n), [arg])
| [arg; Cconst_pointer n] when self#is_immediate n ->
(Iintop_imm(Icomp cmp, n), [arg])
| [Cconst_int n; arg] when self#is_immediate n ->
(Iintop_imm(Icomp(swap_intcomp cmp), n), [arg])
| [Cconst_pointer n; arg] when self#is_immediate n ->
(Iintop_imm(Icomp(swap_intcomp cmp), n), [arg])
| args ->
(Iintop(Icomp cmp), args)
(* Instruction selection for conditionals *)
method select_condition = function
Cop(Ccmpi cmp, [arg1; Cconst_int n]) when self#is_immediate n ->
(Iinttest_imm(Isigned cmp, n), arg1)
| Cop(Ccmpi cmp, [Cconst_int n; arg2]) when self#is_immediate n ->
(Iinttest_imm(Isigned(swap_comparison cmp), n), arg2)
| Cop(Ccmpi cmp, args) ->
(Iinttest(Isigned cmp), Ctuple args)
| Cop(Ccmpa cmp, [arg1; Cconst_pointer n]) when self#is_immediate n ->
(Iinttest_imm(Iunsigned cmp, n), arg1)
| Cop(Ccmpa cmp, [Cconst_pointer n; arg2]) when self#is_immediate n ->
(Iinttest_imm(Iunsigned(swap_comparison cmp), n), arg2)
| Cop(Ccmpa cmp, args) ->
(Iinttest(Iunsigned cmp), Ctuple args)
| Cop(Ccmpf cmp, args) ->
(Ifloattest(cmp, false), Ctuple args)
| Cop(Cand, [arg; Cconst_int 1]) ->
(Ioddtest, arg)
| arg ->
(Itruetest, arg)
(* Buffering of instruction sequences *)
val private mutable instr_seq = dummy_instr
method insert desc arg res =
instr_seq <- instr_cons desc arg res instr_seq
method extract =
let rec extract res i =
if i == dummy_instr
then res
else extract (instr_cons i.desc i.arg i.res res) i.next in
extract (end_instr()) instr_seq
(* Insert a sequence of moves from one pseudoreg set to another. *)
method insert_move src dst =
if src.stamp <> dst.stamp then
self#insert (Iop Imove) [|src|] [|dst|]
method insert_moves src dst =
for i = 0 to Array.length src - 1 do
self#insert_move src.(i) dst.(i)
done
(* Insert moves and stack offsets for function arguments and results *)
method insert_move_args arg loc stacksize =
if stacksize <> 0 then self#insert (Iop(Istackoffset stacksize)) [||] [||];
self#insert_moves arg loc
method insert_move_results loc res stacksize =
if stacksize <> 0 then self#insert(Iop(Istackoffset(-stacksize))) [||] [||];
self#insert_moves loc res
(* Add an Iop opcode. Can be overriden by processor description
to insert moves before and after the operation, i.e. for two-address
instructions, or instructions using dedicated registers. *)
let insert_op op rs rd seq =
try
let (rsrc, rdst, move_res) = Proc.pseudoregs_for_operation op rs rd in
insert_moves rs rsrc seq;
insert (Iop op) rsrc rdst seq;
if move_res then begin
insert_moves rdst rd seq;
rd
end else
rdst
with Proc.Use_default ->
(* Assume no constraints on arg and res registers *)
insert (Iop op) rs rd seq;
rd
method insert_op op rs rd =
self#insert (Iop op) rs rd;
rd
(* Add the instructions for the given expression
at the end of the given sequence *)
at the end of the self sequence *)
let rec emit_expr env exp seq =
method emit_expr env exp =
match exp with
Cconst_int n ->
let r = Reg.createv typ_int in
insert_op (Iconst_int(Nativeint.from n)) [||] r seq
self#insert_op (Iconst_int(Nativeint.from n)) [||] r
| Cconst_natint n ->
let r = Reg.createv typ_int in
insert_op (Iconst_int n) [||] r seq
self#insert_op (Iconst_int n) [||] r
| Cconst_float n ->
let r = Reg.createv typ_float in
insert_op (Iconst_float n) [||] r seq
self#insert_op (Iconst_float n) [||] r
| Cconst_symbol n ->
let r = Reg.createv typ_addr in
insert_op (Iconst_symbol n) [||] r seq
self#insert_op (Iconst_symbol n) [||] r
| Cconst_pointer n ->
let r = Reg.createv typ_addr in
insert_op (Iconst_int(Nativeint.from n)) [||] r seq
self#insert_op (Iconst_int(Nativeint.from n)) [||] r
| Cvar v ->
begin try
Tbl.find v env
@ -369,156 +373,136 @@ let rec emit_expr env exp seq =
fatal_error("Selection.emit_expr: unbound var " ^ Ident.name v)
end
| Clet(v, e1, e2) ->
emit_expr (emit_let env v e1 seq) e2 seq
self#emit_expr (self#emit_let env v e1) e2
| Cassign(v, e1) ->
let rv =
try
Tbl.find v env
with Not_found ->
fatal_error ("Selection.emit_expr: unbound var " ^ Ident.name v) in
let r1 = emit_expr env e1 seq in
insert_moves r1 rv seq;
let r1 = self#emit_expr env e1 in
self#insert_moves r1 rv;
[||]
| Ctuple [] ->
[||]
| Ctuple exp_list ->
let (simple_list, ext_env) = emit_parts_list env exp_list seq in
emit_tuple ext_env simple_list seq
let (simple_list, ext_env) = self#emit_parts_list env exp_list in
self#emit_tuple ext_env simple_list
| Cop(Cproj(ofs, len), [Cop(Cload ty, [arg])]) ->
let byte_offset = size_machtype(Array.sub ty 0 ofs) in
emit_expr env
self#emit_expr env
(Cop(Cload(Array.sub ty ofs len),
[Cop(Cadda, [arg; Cconst_int byte_offset])])) seq
[Cop(Cadda, [arg; Cconst_int byte_offset])]))
| Cop(Cproj(ofs, len), [arg]) ->
let r = emit_expr env arg seq in
let r = self#emit_expr env arg in
Array.sub r ofs len
| Cop(Craise, [arg]) ->
let r1 = emit_expr env arg seq in
let r1 = self#emit_expr env arg in
let rd = [|Proc.loc_exn_bucket|] in
insert (Iop Imove) r1 rd seq;
insert Iraise rd [||] seq;
self#insert (Iop Imove) r1 rd;
self#insert Iraise rd [||];
[||]
| Cop(Ccmpf comp, args) ->
emit_expr env (Cifthenelse(exp, Cconst_int 1, Cconst_int 0)) seq
self#emit_expr env (Cifthenelse(exp, Cconst_int 1, Cconst_int 0))
| Cop(op, args) ->
let (simple_args, env) = emit_parts_list env args seq in
let (simple_args, env) = self#emit_parts_list env args in
let ty = oper_result_type op in
let (new_op, new_args) =
try
Proc.select_oper op simple_args
with Proc.Use_default ->
sel_operation op simple_args in
let (new_op, new_args) = self#select_operation op simple_args in
begin match new_op with
Icall_ind ->
Proc.contains_calls := true;
let r1 = emit_tuple env new_args seq in
let r1 = self#emit_tuple env new_args in
let rarg = Array.sub r1 1 (Array.length r1 - 1) in
let rd = Reg.createv ty in
let (loc_arg, stack_ofs) = Proc.loc_arguments rarg in
let loc_res = Proc.loc_results rd in
insert_move_args rarg loc_arg stack_ofs seq;
insert (Iop Icall_ind) (Array.append [|r1.(0)|] loc_arg) loc_res seq;
insert_move_results loc_res rd stack_ofs seq;
self#insert_move_args rarg loc_arg stack_ofs;
self#insert (Iop Icall_ind)
(Array.append [|r1.(0)|] loc_arg) loc_res;
self#insert_move_results loc_res rd stack_ofs;
rd
| Icall_imm lbl ->
Proc.contains_calls := true;
let r1 = emit_tuple env new_args seq in
let r1 = self#emit_tuple env new_args in
let rd = Reg.createv ty in
let (loc_arg, stack_ofs) = Proc.loc_arguments r1 in
let loc_res = Proc.loc_results rd in
insert_move_args r1 loc_arg stack_ofs seq;
insert (Iop(Icall_imm lbl)) loc_arg loc_res seq;
insert_move_results loc_res rd stack_ofs seq;
self#insert_move_args r1 loc_arg stack_ofs;
self#insert (Iop(Icall_imm lbl)) loc_arg loc_res;
self#insert_move_results loc_res rd stack_ofs;
rd
| Iextcall(lbl, alloc) ->
Proc.contains_calls := true;
if Proc.extcall_use_push then begin
let stack_ofs = emit_pushes env new_args seq in
let rd = Reg.createv ty in
let loc_res = Proc.loc_external_results rd in
insert (Iop(Iextcall(lbl, alloc))) [||] loc_res seq;
insert_move_results loc_res rd stack_ofs seq;
rd
end else begin
let r1 = emit_tuple env new_args seq in
let rd = Reg.createv ty in
let (loc_arg, stack_ofs) = Proc.loc_external_arguments r1 in
let loc_res = Proc.loc_external_results rd in
insert_move_args r1 loc_arg stack_ofs seq;
insert (Iop(Iextcall(lbl, alloc))) loc_arg loc_res seq;
insert_move_results loc_res rd stack_ofs seq;
rd
end
| Iload(Word, addr) ->
let r1 = emit_tuple env new_args seq in
let (loc_arg, stack_ofs) = self#emit_extcall_args env new_args in
let rd = Reg.createv ty in
insert_op (Iload(Word, addr)) r1 rd seq
let loc_res = Proc.loc_external_results rd in
self#insert (Iop(Iextcall(lbl, alloc))) loc_arg loc_res;
self#insert_move_results loc_res rd stack_ofs;
rd
| Ialloc _ ->
Proc.contains_calls := true;
let rd = Reg.createv typ_addr in
let size = size_expr env (Ctuple new_args) in
insert (Iop(Ialloc size)) [||] rd seq;
emit_stores env new_args seq rd
self#insert (Iop(Ialloc size)) [||] rd;
self#emit_stores env new_args rd
(Arch.offset_addressing Arch.identity_addressing (-Arch.size_int));
rd
| op ->
let r1 = emit_tuple env new_args seq in
let r1 = self#emit_tuple env new_args in
let rd = Reg.createv ty in
insert_op op r1 rd seq
self#insert_op op r1 rd
end
| Csequence(e1, e2) ->
emit_expr env e1 seq;
emit_expr env e2 seq
self#emit_expr env e1;
self#emit_expr env e2
| Cifthenelse(econd, eif, eelse) ->
let (cond, earg) = sel_condition econd in
let rarg = emit_expr env earg seq in
let (rif, sif) = emit_sequence env eif in
let (relse, selse) = emit_sequence env eelse in
let (cond, earg) = self#select_condition econd in
let rarg = self#emit_expr env earg in
let (rif, sif) = self#emit_sequence env eif in
let (relse, selse) = self#emit_sequence env eelse in
let r = join rif sif relse selse in
insert (Iifthenelse(cond, extract_sequence sif, extract_sequence selse))
rarg [||] seq;
self#insert (Iifthenelse(cond, sif#extract, selse#extract)) rarg [||];
r
| Cswitch(esel, index, ecases) ->
let rsel = emit_expr env esel seq in
let rscases = Array.map (emit_sequence env) ecases in
let rsel = self#emit_expr env esel in
let rscases = Array.map (self#emit_sequence env) ecases in
let r = join_array rscases in
insert (Iswitch(index,
Array.map (fun (r, s) -> extract_sequence s) rscases))
rsel [||] seq;
self#insert (Iswitch(index, Array.map (fun (r, s) -> s#extract) rscases))
rsel [||];
r
| Cloop(ebody) ->
let (rarg, sbody) = emit_sequence env ebody in
insert (Iloop(extract_sequence sbody)) [||] [||] seq;
let (rarg, sbody) = self#emit_sequence env ebody in
self#insert (Iloop(sbody#extract)) [||] [||];
[||]
| Ccatch(e1, e2) ->
let (r1, s1) = emit_sequence env e1 in
let (r2, s2) = emit_sequence env e2 in
let (r1, s1) = self#emit_sequence env e1 in
let (r2, s2) = self#emit_sequence env e2 in
let r = join r1 s1 r2 s2 in
insert (Icatch(extract_sequence s1, extract_sequence s2)) [||] [||] seq;
self#insert (Icatch(s1#extract, s2#extract)) [||] [||];
r
| Cexit ->
insert Iexit [||] [||] seq;
self#insert Iexit [||] [||];
[||]
| Ctrywith(e1, v, e2) ->
Proc.contains_calls := true;
let (r1, s1) = emit_sequence env e1 in
let (r1, s1) = self#emit_sequence env e1 in
let rv = Reg.createv typ_addr in
let (r2, s2) = emit_sequence (Tbl.add v rv env) e2 in
let (r2, s2) = self#emit_sequence (Tbl.add v rv env) e2 in
let r = join r1 s1 r2 s2 in
insert
(Itrywith(extract_sequence s1,
self#insert
(Itrywith(s1#extract,
instr_cons (Iop Imove) [|Proc.loc_exn_bucket|] rv
(extract_sequence s2)))
[||] [||] seq;
s2#extract))
[||] [||];
r
and emit_sequence env exp =
let seq = new_sequence() in
let r = emit_expr env exp seq in
(r, seq)
method emit_sequence env exp =
let s = {< instr_seq = dummy_instr >} in
let r = s#emit_expr env exp in
(r, s)
and emit_let env v e1 seq =
let r1 = emit_expr env e1 seq in
method emit_let env v e1 =
let r1 = self#emit_expr env e1 in
if all_regs_anonymous r1 then begin
name_regs v r1;
Tbl.add v r1 env
@ -526,15 +510,15 @@ and emit_let env v e1 seq =
let rv = Array.create (Array.length r1) Reg.dummy in
for i = 0 to Array.length r1 - 1 do rv.(i) <- Reg.create r1.(i).typ done;
name_regs v rv;
insert_moves r1 rv seq;
self#insert_moves r1 rv;
Tbl.add v rv env
end
and emit_parts env exp seq =
method emit_parts env exp =
if is_simple_expr exp then
(exp, env)
else begin
let r = emit_expr env exp seq in
let r = self#emit_expr env exp in
if Array.length r = 0 then
(Ctuple [], env)
else begin
@ -546,160 +530,147 @@ and emit_parts env exp seq =
for i = 0 to Array.length r - 1 do
rv.(i) <- Reg.create r.(i).typ
done;
insert_moves r rv seq;
self#insert_moves r rv;
(Cvar id, Tbl.add id rv env)
end
end
end
and emit_parts_list env exp_list seq =
method emit_parts_list env exp_list =
match exp_list with
[] -> ([], env)
| exp :: rem ->
(* This ensures right-to-left evaluation, consistent with the
bytecode compiler *)
let (new_rem, new_env) = emit_parts_list env rem seq in
let (new_exp, fin_env) = emit_parts new_env exp seq in
let (new_rem, new_env) = self#emit_parts_list env rem in
let (new_exp, fin_env) = self#emit_parts new_env exp in
(new_exp :: new_rem, fin_env)
and emit_tuple env exp_list seq =
method emit_tuple env exp_list =
let rec emit_list = function
[] -> []
| exp :: rem ->
(* Again, force right-to-left evaluation *)
let loc_rem = emit_list rem in
let loc_exp = emit_expr env exp seq in
let loc_exp = self#emit_expr env exp in
loc_exp :: loc_rem in
Array.concat(emit_list exp_list)
and emit_stores env data seq regs_addr addr =
method emit_extcall_args env args =
let r1 = self#emit_tuple env args in
let (loc_arg, stack_ofs as arg_stack) = Proc.loc_external_arguments r1 in
self#insert_move_args r1 loc_arg stack_ofs;
arg_stack
method emit_stores env data regs_addr addr =
let a = ref addr in
List.iter
(fun e ->
try
(* Offer the machine description an opportunity to optimize
the store, e.g. if constant -> memory or memory -> memory
moves are available *)
let (op, arg) = Proc.select_store !a e in
let r = emit_expr env arg seq in
insert (Iop op) (Array.append r regs_addr) [||] seq;
a := Arch.offset_addressing !a (size_expr env e)
with Proc.Use_default ->
let r = emit_expr env e seq in
for i = 0 to Array.length r - 1 do
insert (Iop(Istore(Word, !a)))
(Array.append [|r.(i)|] regs_addr) [||] seq;
a := Arch.offset_addressing !a (size_component r.(i).typ)
done)
let (op, arg) = self#select_store !a e in
let r = self#emit_expr env arg in
self#insert (Iop op) (Array.append r regs_addr) [||];
a := Arch.offset_addressing !a (size_expr env e))
data
and emit_pushes env args seq =
match args with
[] -> 0
| e :: el ->
let ofs = emit_pushes env el seq in
let (op, arg) = Proc.select_push e in
let r = emit_expr env arg seq in
insert (Iop op) r [||] seq;
ofs + size_expr env e
(* Same, but in tail position *)
let emit_return env exp seq =
let r = emit_expr env exp seq in
method emit_return env exp =
let r = self#emit_expr env exp in
let loc = Proc.loc_results r in
insert_moves r loc seq;
insert Ireturn loc [||] seq
self#insert_moves r loc;
self#insert Ireturn loc [||]
let rec emit_tail env exp seq =
method emit_tail env exp =
match exp with
Clet(v, e1, e2) ->
emit_tail (emit_let env v e1 seq) e2 seq
self#emit_tail (self#emit_let env v e1) e2
| Cop(Capply ty as op, args) ->
let (simple_args, env) = emit_parts_list env args seq in
let (new_op, new_args) = sel_operation op simple_args in
let (simple_args, env) = self#emit_parts_list env args in
let (new_op, new_args) = self#select_operation op simple_args in
begin match new_op with
Icall_ind ->
let r1 = emit_tuple env new_args seq in
let r1 = self#emit_tuple env new_args in
let rarg = Array.sub r1 1 (Array.length r1 - 1) in
let (loc_arg, stack_ofs) = Proc.loc_arguments rarg in
if stack_ofs = 0 then begin
insert_moves rarg loc_arg seq;
insert (Iop Itailcall_ind)
(Array.append [|r1.(0)|] loc_arg) [||] seq
self#insert_moves rarg loc_arg;
self#insert (Iop Itailcall_ind)
(Array.append [|r1.(0)|] loc_arg) [||]
end else begin
Proc.contains_calls := true;
let rd = Reg.createv ty in
let loc_res = Proc.loc_results rd in
insert_move_args rarg loc_arg stack_ofs seq;
insert (Iop Icall_ind)
(Array.append [|r1.(0)|] loc_arg) loc_res seq;
insert(Iop(Istackoffset(-stack_ofs))) [||] [||] seq;
insert Ireturn loc_res [||] seq
self#insert_move_args rarg loc_arg stack_ofs;
self#insert (Iop Icall_ind)
(Array.append [|r1.(0)|] loc_arg) loc_res;
self#insert(Iop(Istackoffset(-stack_ofs))) [||] [||];
self#insert Ireturn loc_res [||]
end
| Icall_imm lbl ->
let r1 = emit_tuple env new_args seq in
let r1 = self#emit_tuple env new_args in
let (loc_arg, stack_ofs) = Proc.loc_arguments r1 in
if stack_ofs = 0 then begin
insert_moves r1 loc_arg seq;
insert (Iop(Itailcall_imm lbl)) loc_arg [||] seq
self#insert_moves r1 loc_arg;
self#insert (Iop(Itailcall_imm lbl)) loc_arg [||]
end else begin
Proc.contains_calls := true;
let rd = Reg.createv ty in
let loc_res = Proc.loc_results rd in
insert_move_args r1 loc_arg stack_ofs seq;
insert (Iop(Icall_imm lbl)) loc_arg loc_res seq;
insert(Iop(Istackoffset(-stack_ofs))) [||] [||] seq;
insert Ireturn loc_res [||] seq
self#insert_move_args r1 loc_arg stack_ofs;
self#insert (Iop(Icall_imm lbl)) loc_arg loc_res;
self#insert(Iop(Istackoffset(-stack_ofs))) [||] [||];
self#insert Ireturn loc_res [||]
end
| _ -> fatal_error "Selection.emit_tail"
end
| Cop(Craise, [e1]) ->
let r1 = emit_expr env e1 seq in
let r1 = self#emit_expr env e1 in
let rd = [|Proc.loc_exn_bucket|] in
insert (Iop Imove) r1 rd seq;
insert Iraise rd [||] seq
self#insert (Iop Imove) r1 rd;
self#insert Iraise rd [||]
| Csequence(e1, e2) ->
emit_expr env e1 seq;
emit_tail env e2 seq
self#emit_expr env e1;
self#emit_tail env e2
| Cifthenelse(econd, eif, eelse) ->
let (cond, earg) = sel_condition econd in
let rarg = emit_expr env earg seq in
insert (Iifthenelse(cond, emit_tail_sequence env eif,
emit_tail_sequence env eelse))
rarg [||] seq
let (cond, earg) = self#select_condition econd in
let rarg = self#emit_expr env earg in
self#insert (Iifthenelse(cond, self#emit_tail_sequence env eif,
self#emit_tail_sequence env eelse))
rarg [||]
| Cswitch(esel, index, ecases) ->
let rsel = emit_expr env esel seq in
insert (Iswitch(index, Array.map (emit_tail_sequence env) ecases))
rsel [||] seq
let rsel = self#emit_expr env esel in
self#insert
(Iswitch(index, Array.map (self#emit_tail_sequence env) ecases))
rsel [||]
| Ccatch(e1, e2) ->
insert (Icatch(emit_tail_sequence env e1, emit_tail_sequence env e2))
[||] [||] seq
self#insert (Icatch(self#emit_tail_sequence env e1,
self#emit_tail_sequence env e2))
[||] [||]
| Cexit ->
insert Iexit [||] [||] seq
self#insert Iexit [||] [||]
| Ctrywith(e1, v, e2) ->
Proc.contains_calls := true;
let (r1, s1) = emit_sequence env e1 in
let (r1, s1) = self#emit_sequence env e1 in
let rv = Reg.createv typ_addr in
let s2 = emit_tail_sequence (Tbl.add v rv env) e2 in
let s2 = self#emit_tail_sequence (Tbl.add v rv env) e2 in
let loc = Proc.loc_results r1 in
insert
(Itrywith(extract_sequence s1,
self#insert
(Itrywith(s1#extract,
instr_cons (Iop Imove) [|Proc.loc_exn_bucket|] rv s2))
[||] [||] seq;
insert_moves r1 loc seq;
insert Ireturn loc [||] seq
[||] [||];
self#insert_moves r1 loc;
self#insert Ireturn loc [||]
| _ ->
emit_return env exp seq
self#emit_return env exp
and emit_tail_sequence env exp =
let seq = new_sequence() in
emit_tail env exp seq;
extract_sequence seq
method emit_tail_sequence env exp =
let s = {< instr_seq = dummy_instr >} in
s#emit_tail env exp;
s#extract
(* Sequentialization of a function definition *)
let fundecl f =
method emit_fundecl f =
Proc.contains_calls := false;
let rargs =
List.map
@ -711,10 +682,11 @@ let fundecl f =
List.fold_right2
(fun (id, ty) r env -> Tbl.add id r env)
f.Cmm.fun_args rargs Tbl.empty in
let seq = new_sequence() in
insert_moves loc_arg rarg seq;
emit_tail env f.Cmm.fun_body seq;
self#insert_moves loc_arg rarg;
self#emit_tail env f.Cmm.fun_body;
{ fun_name = f.Cmm.fun_name;
fun_args = loc_arg;
fun_body = extract_sequence seq;
fun_body = self#extract;
fun_fast = f.Cmm.fun_fast }
end

90
asmcomp/selectgen.mli Normal file
View File

@ -0,0 +1,90 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Selection of pseudo-instructions, assignment of pseudo-registers,
sequentialization. *)
type environment = (Ident.t, Reg.t array) Tbl.t
val size_expr : environment -> Cmm.expression -> int
class virtual selector_generic (unit) : 'a =
(* The following methods must or can be overriden by the processor
description *)
virtual is_immediate : int -> bool
(* Must be defined to indicate whether a constant is a suitable
immediate operand to arithmetic instructions *)
virtual select_addressing :
Cmm.expression -> Arch.addressing_mode * Cmm.expression
(* Must be defined to select addressing modes *)
method select_operation :
Cmm.operation ->
Cmm.expression list -> Mach.operation * Cmm.expression list
(* Can be overriden to deal with special arithmetic instructions *)
method select_condition : Cmm.expression -> Mach.test * Cmm.expression
(* Can be overriden to deal with special test instructions *)
method select_store :
Arch.addressing_mode -> Cmm.expression -> Mach.operation * Cmm.expression
(* Can be overriden to deal with special store constant instructions *)
method insert_op :
Mach.operation -> Reg.t array -> Reg.t array -> Reg.t array
(* Can be overriden to deal with 2-address instructions
or instructions with hardwired input/output registers *)
method emit_extcall_args :
environment -> Cmm.expression list -> Reg.t array * int
(* Can be overriden to deal with stack-based calling conventions *)
(* The following methods should not be overriden *)
method emit_expr :
environment -> Cmm.expression -> Reg.t array
method emit_fundecl : Cmm.fundecl -> Mach.fundecl
method emit_let :
environment ->
Ident.t -> Cmm.expression -> environment
method emit_parts :
environment ->
Cmm.expression -> Cmm.expression * environment
method emit_parts_list :
environment ->
Cmm.expression list -> Cmm.expression list * environment
method emit_return : environment -> Cmm.expression -> unit
method emit_sequence :
environment -> Cmm.expression -> Reg.t array * 'a
method emit_stores :
environment ->
Cmm.expression list -> Reg.t array -> Arch.addressing_mode -> unit
method emit_tail : environment -> Cmm.expression -> unit
method emit_tail_sequence :
environment -> Cmm.expression -> Mach.instruction
method emit_tuple :
environment -> Cmm.expression list -> Reg.t array
method extract : Mach.instruction
method insert : Mach.instruction_desc -> Reg.t array -> Reg.t array -> unit
method insert_move : Reg.t -> Reg.t -> unit
method insert_move_args : Reg.t array -> Reg.t array -> int -> unit
method insert_move_results : Reg.t array -> Reg.t array -> int -> unit
method insert_moves : Reg.t array -> Reg.t array -> unit
method select_arith :
Mach.integer_operation ->
Cmm.expression list -> Mach.operation * Cmm.expression list
method select_arith_comm :
Mach.integer_operation ->
Cmm.expression list -> Mach.operation * Cmm.expression list
method select_arith_comp :
Mach.integer_comparison ->
Cmm.expression list -> Mach.operation * Cmm.expression list
method select_shift :
Mach.integer_operation ->
Cmm.expression list -> Mach.operation * Cmm.expression list
end

View File

@ -19,76 +19,8 @@ open Reg
open Arch
open Mach
(* Exceptions raised to signal cases not handled here *)
exception Use_default
(* Recognition of addressing modes *)
type addressing_expr =
Asymbol of string
| Alinear of expression
| Aadd of expression * expression
let rec select_addr = function
Cconst_symbol s ->
(Asymbol s, 0)
| Cop((Caddi | Cadda), [arg; Cconst_int m]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop((Caddi | Cadda), [Cconst_int m; arg]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop((Caddi | Cadda), [arg1; arg2]) ->
begin match (select_addr arg1, select_addr arg2) with
((Alinear e1, n1), (Alinear e2, n2)) ->
(Aadd(e1, e2), n1 + n2)
| _ ->
(Aadd(arg1, arg2), 0)
end
| exp ->
(Alinear exp, 0)
let select_addressing exp =
match select_addr exp with
(Asymbol s, d) ->
(Ibased(s, d), Ctuple [])
| (Alinear e, d) ->
(Iindexed d, e)
| (Aadd(e1, e2), d) ->
(Iindexed2 d, Ctuple[e1; e2])
(* Instruction selection *)
let is_immediate n = (n <= 4095) & (n >= -4096)
let select_oper op args =
match (op, args) with
(* Multiplication, division and modulus are turned into
calls to C library routines, except if the dividend is a power of 2. *)
(Cmuli, [arg; Cconst_int n]) when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Ilsl, Misc.log2 n), [arg])
| (Cmuli, [Cconst_int n; arg]) when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Ilsl, Misc.log2 n), [arg])
| (Cmuli, _) ->
(Iextcall(".umul", false), args)
| (Cdivi, [arg; Cconst_int n])
when is_immediate n & n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Idiv, n), [arg])
| (Cdivi, _) ->
(Iextcall(".div", false), args)
| (Cmodi, [arg; Cconst_int n])
when is_immediate n & n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Imod, n), [arg])
| (Cmodi, _) ->
(Iextcall(".rem", false), args)
| _ ->
raise Use_default
let select_store addr exp = raise Use_default
let select_push exp = fatal_error "Proc: select_push"
let pseudoregs_for_operation op arg res = raise Use_default
let word_addressed = false
(* Registers available for register allocation *)
@ -227,8 +159,6 @@ let loc_external_arguments arg =
done;
(loc, Misc.align (!ofs + 4) 8) (* Keep stack 8-aligned *)
let extcall_use_push = false
let loc_external_results res =
let (loc, ofs) = calling_conventions 0 0 100 100 not_supported res in loc
@ -259,11 +189,6 @@ let max_register_pressure = function
Iextcall(_, _) -> [| 11; 0 |]
| _ -> [| 19; 15 |]
(* Reloading *)
let reload_test makereg round tst args = raise Use_default
let reload_operation makereg round op args res = raise Use_default
(* Latencies (in cycles). Wild guesses. *)
let need_scheduling = true

17
asmcomp/sparc/reload.ml Normal file
View File

@ -0,0 +1,17 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Reloading for the Sparc *)
let fundecl f =
(new Reloadgen.reload_generic ())#fundecl f

View File

@ -0,0 +1,54 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1996 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
open Mach
(* Instruction scheduling for the Sparc *)
class scheduler () as self =
inherit Schedgen.scheduler_generic () as super
(* Latencies (in cycles). Wild guesses. *)
method oper_latency = function
Ireload -> 3
| Iload(_, _) -> 3
| Iconst_float _ -> 3 (* turned into a load *)
| Iaddf | Isubf -> 3
| Imulf -> 5
| Idivf -> 15
| _ -> 1
(* Issue cycles. Rough approximations. *)
method oper_issue_cycles = function
Iconst_float _ -> 2
| Iconst_symbol _ -> 2
| Ialloc _ -> 6
| Iintop(Icomp _) -> 4
| Iintop(Icheckbound) -> 2
| Iintop_imm(Idiv, _) -> 5
| Iintop_imm(Imod, _) -> 5
| Iintop_imm(Icomp _, _) -> 4
| Iintop_imm(Icheckbound, _) -> 2
| Inegf -> 2
| Iabsf -> 2
| Ifloatofint -> 6
| Iintoffloat -> 6
| _ -> 1
end
let fundecl f = (new scheduler ())#schedule_fundecl f

View File

@ -0,0 +1,86 @@
(***********************************************************************)
(* *)
(* Objective Caml *)
(* *)
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
(* *)
(* Copyright 1997 Institut National de Recherche en Informatique et *)
(* Automatique. Distributed only by permission. *)
(* *)
(***********************************************************************)
(* $Id$ *)
(* Instruction selection for the Sparc processor *)
open Misc
open Cmm
open Reg
open Arch
open Mach
(* Recognition of addressing modes *)
type addressing_expr =
Asymbol of string
| Alinear of expression
| Aadd of expression * expression
let rec select_addr = function
Cconst_symbol s ->
(Asymbol s, 0)
| Cop((Caddi | Cadda), [arg; Cconst_int m]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop((Caddi | Cadda), [Cconst_int m; arg]) ->
let (a, n) = select_addr arg in (a, n + m)
| Cop((Caddi | Cadda), [arg1; arg2]) ->
begin match (select_addr arg1, select_addr arg2) with
((Alinear e1, n1), (Alinear e2, n2)) ->
(Aadd(e1, e2), n1 + n2)
| _ ->
(Aadd(arg1, arg2), 0)
end
| exp ->
(Alinear exp, 0)
class selector () as self =
inherit Selectgen.selector_generic() as super
method is_immediate n = (n <= 4095) && (n >= -4096)
method select_addressing exp =
match select_addr exp with
(Asymbol s, d) ->
(Ibased(s, d), Ctuple [])
| (Alinear e, d) ->
(Iindexed d, e)
| (Aadd(e1, e2), d) ->
(Iindexed2 d, Ctuple[e1; e2])
method select_operation op args =
match (op, args) with
(* Multiplication, division and modulus are turned into
calls to C library routines, except if the dividend is a power of 2. *)
(Cmuli, [arg; Cconst_int n]) when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Ilsl, Misc.log2 n), [arg])
| (Cmuli, [Cconst_int n; arg]) when n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Ilsl, Misc.log2 n), [arg])
| (Cmuli, _) ->
(Iextcall(".umul", false), args)
| (Cdivi, [arg; Cconst_int n])
when self#is_immediate n & n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Idiv, n), [arg])
| (Cdivi, _) ->
(Iextcall(".div", false), args)
| (Cmodi, [arg; Cconst_int n])
when self#is_immediate n & n = 1 lsl (Misc.log2 n) ->
(Iintop_imm(Imod, n), [arg])
| (Cmodi, _) ->
(Iextcall(".rem", false), args)
| _ ->
super#select_operation op args
end
let fundecl f = (new selector ())#emit_fundecl f