ocaml/asmcomp/selectgen.ml

693 lines
24 KiB
OCaml

(***********************************************************************)
(* *)
(* 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. *)
open Misc
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
Capply ty -> ty
| Cextcall(s, ty, alloc) -> ty
| Cload ty -> ty
| Cloadchunk c -> typ_int
| Calloc -> typ_addr
| Cstore -> typ_void
| Cstorechunk c -> typ_void
| Caddi | Csubi | Cmuli | Cdivi | Cmodi
| Cand | Cor | Cxor | Clsl | Clsr | Casr
| Ccmpi _ | Ccmpa _ | Ccmpf _ -> typ_int
| Cadda | Csuba -> typ_addr
| Cnegf | Cabsf | Caddf | Csubf | Cmulf | Cdivf -> typ_float
| Cfloatofint -> typ_float
| Cintoffloat -> typ_int
| Craise -> typ_void
| Ccheckbound -> typ_void
| _ -> fatal_error "Selection.oper_result_type"
(* Infer the size in bytes of the result of a simple expression *)
let size_expr env exp =
let rec size localenv = function
Cconst_int _ | Cconst_natint _ -> Arch.size_int
| Cconst_symbol _ | Cconst_pointer _ -> Arch.size_addr
| Cconst_float _ -> Arch.size_float
| Cvar id ->
begin try
Tbl.find id localenv
with Not_found ->
try
let regs = Tbl.find id env in
size_machtype (Array.map (fun r -> r.typ) regs)
with Not_found ->
fatal_error("Selection.size_expr: unbound var " ^ Ident.name id)
end
| Ctuple el ->
List.fold_right (fun e sz -> size localenv e + sz) el 0
| Cop(op, args) ->
size_machtype(oper_result_type op)
| Clet(id, arg, body) ->
size (Tbl.add id (size localenv arg) localenv) body
| _ ->
fatal_error "Selection.size_expr"
in size Tbl.empty exp
(* Says if an expression is "simple". A "simple" expression has no
side-effects and its execution can be delayed until its value
is really needed. In the case of e.g. an [alloc] instruction,
the non-simple arguments are computed in right-to-left order
first, then the block is allocated, then the simple arguments are
evaluated and stored. *)
let rec is_simple_expr = function
Cconst_int _ -> true
| Cconst_natint _ -> true
| Cconst_float _ -> true
| Cconst_symbol _ -> true
| Cconst_pointer _ -> true
| Cvar _ -> true
| Ctuple el -> List.for_all is_simple_expr el
| Clet(id, arg, body) -> is_simple_expr arg && is_simple_expr body
| Cop(op, args) ->
begin match op with
(* The following may have side effects *)
Capply _ | Cextcall(_, _, _) | Calloc | Cstore | Cstorechunk _ |
Craise -> false
(* The remaining operations are simple if their args are *)
| _ -> List.for_all is_simple_expr args
end
| _ -> false
(* Swap the two arguments of an integer comparison *)
let swap_intcomp = function
Isigned cmp -> Isigned(swap_comparison cmp)
| Iunsigned cmp -> Iunsigned(swap_comparison cmp)
(* Naming of registers *)
let all_regs_anonymous rv =
try
for i = 0 to Array.length rv - 1 do
if String.length rv.(i).name > 0 then raise Exit
done;
true
with Exit ->
false
let name_regs id rv =
if Array.length rv = 1 then
rv.(0).name <- Ident.name id
else
for i = 0 to Array.length rv - 1 do
rv.(i).name <- Ident.name id ^ "#" ^ string_of_int i
done
(* "Join" two instruction sequences, making sure they return their results
in the same registers. *)
let join r1 seq1 r2 seq2 =
let l1 = Array.length r1 and l2 = Array.length r2 in
if l1 = 0 then r2
else if l2 = 0 then r1
else begin
let r = Array.create l1 Reg.dummy in
for i = 0 to l1-1 do
if String.length r1.(i).name = 0 then begin
r.(i) <- r1.(i);
seq2#insert_move r2.(i) r1.(i)
end else if String.length r2.(i).name = 0 then begin
r.(i) <- r2.(i);
seq1#insert_move r1.(i) r2.(i)
end else begin
r.(i) <- Reg.create r1.(i).typ;
seq1#insert_move r1.(i) r.(i);
seq2#insert_move r2.(i) r.(i)
end
done;
r
end
(* Same, for N branches *)
let join_array rs =
let some_res = ref [||] in
for i = 0 to Array.length rs - 1 do
let (r, s) = rs.(i) in
if Array.length r > 0 then some_res := r
done;
let size_res = Array.length !some_res in
if size_res = 0 then [||] else begin
let res = Array.create size_res Reg.dummy in
for i = 0 to size_res - 1 do
res.(i) <- Reg.create (!some_res).(i).typ
done;
for i = 0 to Array.length rs - 1 do
let (r, s) = rs.(i) in
if Array.length r > 0 then s#insert_moves r res
done;
res
end
(* 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 protected 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 protected 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 protected 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 protected 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. *)
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 self sequence *)
method emit_expr env exp =
match exp with
Cconst_int n ->
let r = Reg.createv typ_int in
self#insert_op (Iconst_int(Nativeint.from n)) [||] r
| Cconst_natint n ->
let r = Reg.createv typ_int in
self#insert_op (Iconst_int n) [||] r
| Cconst_float n ->
let r = Reg.createv typ_float in
self#insert_op (Iconst_float n) [||] r
| Cconst_symbol n ->
let r = Reg.createv typ_addr in
self#insert_op (Iconst_symbol n) [||] r
| Cconst_pointer n ->
let r = Reg.createv typ_addr in
self#insert_op (Iconst_int(Nativeint.from n)) [||] r
| Cvar v ->
begin try
Tbl.find v env
with Not_found ->
fatal_error("Selection.emit_expr: unbound var " ^ Ident.name v)
end
| Clet(v, e1, e2) ->
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 = self#emit_expr env e1 in
self#insert_moves r1 rv;
[||]
| Ctuple [] ->
[||]
| Ctuple exp_list ->
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
self#emit_expr env
(Cop(Cload(Array.sub ty ofs len),
[Cop(Cadda, [arg; Cconst_int byte_offset])]))
| Cop(Cproj(ofs, len), [arg]) ->
let r = self#emit_expr env arg in
Array.sub r ofs len
| Cop(Craise, [arg]) ->
let r1 = self#emit_expr env arg in
let rd = [|Proc.loc_exn_bucket|] in
self#insert (Iop Imove) r1 rd;
self#insert Iraise rd [||];
[||]
| Cop(Ccmpf comp, args) ->
self#emit_expr env (Cifthenelse(exp, Cconst_int 1, Cconst_int 0))
| Cop(op, args) ->
let (simple_args, env) = self#emit_parts_list env args in
let ty = oper_result_type op 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 = 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
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 = 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
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;
let (loc_arg, stack_ofs) = self#emit_extcall_args env new_args in
let rd = Reg.createv ty in
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
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 = self#emit_tuple env new_args in
let rd = Reg.createv ty in
self#insert_op op r1 rd
end
| Csequence(e1, e2) ->
self#emit_expr env e1;
self#emit_expr env e2
| Cifthenelse(econd, eif, eelse) ->
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
self#insert (Iifthenelse(cond, sif#extract, selse#extract)) rarg [||];
r
| Cswitch(esel, index, ecases) ->
let rsel = self#emit_expr env esel in
let rscases = Array.map (self#emit_sequence env) ecases in
let r = join_array rscases in
self#insert (Iswitch(index, Array.map (fun (r, s) -> s#extract) rscases))
rsel [||];
r
| Cloop(ebody) ->
let (rarg, sbody) = self#emit_sequence env ebody in
self#insert (Iloop(sbody#extract)) [||] [||];
[||]
| Ccatch(e1, e2) ->
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
self#insert (Icatch(s1#extract, s2#extract)) [||] [||];
r
| Cexit ->
self#insert Iexit [||] [||];
[||]
| Ctrywith(e1, v, e2) ->
Proc.contains_calls := true;
let (r1, s1) = self#emit_sequence env e1 in
let rv = Reg.createv typ_addr in
let (r2, s2) = self#emit_sequence (Tbl.add v rv env) e2 in
let r = join r1 s1 r2 s2 in
self#insert
(Itrywith(s1#extract,
instr_cons (Iop Imove) [|Proc.loc_exn_bucket|] rv
s2#extract))
[||] [||];
r
method protected emit_sequence env exp =
let s = {< instr_seq = dummy_instr >} in
let r = s#emit_expr env exp in
(r, s)
method protected 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
end else begin
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;
self#insert_moves r1 rv;
Tbl.add v rv env
end
method protected emit_parts env exp =
if is_simple_expr exp then
(exp, env)
else begin
let r = self#emit_expr env exp in
if Array.length r = 0 then
(Ctuple [], env)
else begin
let id = Ident.create "bind" in
if all_regs_anonymous r then
(Cvar id, Tbl.add id r env)
else begin
let rv = Array.create (Array.length r) Reg.dummy in
for i = 0 to Array.length r - 1 do
rv.(i) <- Reg.create r.(i).typ
done;
self#insert_moves r rv;
(Cvar id, Tbl.add id rv env)
end
end
end
method protected 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) = 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)
method protected 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 = self#emit_expr env exp in
loc_exp :: loc_rem in
Array.concat(emit_list exp_list)
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 protected emit_stores env data regs_addr addr =
let a = ref addr in
List.iter
(fun e ->
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
(* Same, but in tail position *)
method protected emit_return env exp =
let r = self#emit_expr env exp in
let loc = Proc.loc_results r in
self#insert_moves r loc;
self#insert Ireturn loc [||]
method emit_tail env exp =
match exp with
Clet(v, e1, e2) ->
self#emit_tail (self#emit_let env v e1) e2
| Cop(Capply ty as op, args) ->
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 = 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
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
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 = self#emit_tuple env new_args in
let (loc_arg, stack_ofs) = Proc.loc_arguments r1 in
if stack_ofs = 0 then begin
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
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 = self#emit_expr env e1 in
let rd = [|Proc.loc_exn_bucket|] in
self#insert (Iop Imove) r1 rd;
self#insert Iraise rd [||]
| Csequence(e1, e2) ->
self#emit_expr env e1;
self#emit_tail env e2
| Cifthenelse(econd, eif, eelse) ->
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 = self#emit_expr env esel in
self#insert
(Iswitch(index, Array.map (self#emit_tail_sequence env) ecases))
rsel [||]
| Ccatch(e1, e2) ->
self#insert (Icatch(self#emit_tail_sequence env e1,
self#emit_tail_sequence env e2))
[||] [||]
| Cexit ->
self#insert Iexit [||] [||]
| Ctrywith(e1, v, e2) ->
Proc.contains_calls := true;
let (r1, s1) = self#emit_sequence env e1 in
let rv = Reg.createv typ_addr in
let s2 = self#emit_tail_sequence (Tbl.add v rv env) e2 in
let loc = Proc.loc_results r1 in
self#insert
(Itrywith(s1#extract,
instr_cons (Iop Imove) [|Proc.loc_exn_bucket|] rv s2))
[||] [||];
self#insert_moves r1 loc;
self#insert Ireturn loc [||]
| _ ->
self#emit_return env exp
method protected emit_tail_sequence env exp =
let s = {< instr_seq = dummy_instr >} in
s#emit_tail env exp;
s#extract
(* Sequentialization of a function definition *)
method emit_fundecl f =
Proc.contains_calls := false;
let rargs =
List.map
(fun (id, ty) -> let r = Reg.createv ty in name_regs id r; r)
f.Cmm.fun_args in
let rarg = Array.concat rargs in
let loc_arg = Proc.loc_parameters rarg in
let env =
List.fold_right2
(fun (id, ty) r env -> Tbl.add id r env)
f.Cmm.fun_args rargs Tbl.empty in
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 = self#extract;
fun_fast = f.Cmm.fun_fast }
end