ocaml / asmcomp / sequence.ml

(* "Sequentialization": from C-- to sequences of pseudo-instructions
   with pseudo-registers. *)

open Misc
open Cmm
open Reg
open Selection
open Mach

(* 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

(* 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_moves src dst seq =
  for i = 0 to Array.length src - 1 do
    if src.(i).stamp <> dst.(i).stamp then
      insert (Iop Imove) [|src.(i)|] [|dst.(i)|] seq
  done

(* Insert moves and stackstores for function arguments and function 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. *)

let join r1 seq1 r2 seq2 =
  if Array.length r1 = 0 then r2
  else if Array.length r2 = 0 then r1
  else begin insert_moves r2 r1 seq2; r1 end

(* Same, for N branches *)

let join_array rs =
  let dest = ref [||] in
  for i = 0 to Array.length rs - 1 do
    let (r, s) = rs.(i) in
    if Array.length r > 0 then dest := r
  done;
  if Array.length !dest > 0 then
    for i = 0 to Array.length rs - 1 do
      let (r, s) = rs.(i) in
      if Array.length r > 0 then insert_moves r !dest s
    done;
  !dest

(* Add the instructions for the given expression
   at the end of the given sequence *)

let rec emit_expr env exp seq =
  match exp with
    Sconst c ->
      let ty =
        match c with
          Const_int n -> typ_int
        | Const_float f -> typ_float
        | Const_symbol s -> typ_addr
        | Const_pointer n -> typ_addr in
      let r = Reg.newv ty in
      insert (Iop(Iconstant c)) [||] r seq;
      r
  | Svar v ->
      begin try
        Tbl.find v env
      with Not_found ->
        fatal_error("Sequence.emit_expr: unbound var " ^ Ident.name v)
      end
  | Slet(v, e1, e2) ->
      emit_expr (emit_let env v e1 seq) e2 seq
  | Sassign(v, e1) ->
      let rv =
        try
          Tbl.find v env
        with Not_found ->
          fatal_error ("Sequence.emit_expr: unbound var " ^ Ident.name v) in
      let r1 = emit_expr env e1 seq in
      insert_moves r1 rv seq;
      [||]
  | Stuple(ev, perm) ->
      let rv = Array.new (Array.length ev) [||] in
      List.iter (fun i -> rv.(i) <- emit_expr env ev.(i) seq) perm;
      Array.concat(Array.to_list rv)
  | Sop(Icall_ind, e1, ty) ->
      Proc.contains_calls := true;
      let r1 = emit_expr env e1 seq in
      let rarg = Array.sub r1 1 (Array.length r1 - 1) in
      let rd = Reg.newv 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;
      rd
  | Sop(Icall_imm lbl, e1, ty) ->
      Proc.contains_calls := true;
      let r1 = emit_expr env e1 seq in
      let rd = Reg.newv 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;
      rd
  | Sop(Iextcall lbl, e1, ty) ->
      Proc.contains_calls := true;
      let r1 = emit_expr env e1 seq in
      let rd = Reg.newv 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)) loc_arg loc_res seq;
      insert_move_results loc_res rd stack_ofs seq;
      rd
  | Sop(Iload(Word, addr), e1, ty) ->
      let r1 = emit_expr env e1 seq in
      let rd = Reg.newv ty in
      let a = ref addr in
      for i = 0 to Array.length ty - 1 do
        insert(Iop(Iload(Word, !a))) r1 [|rd.(i)|] seq;
        a := Arch.offset_addressing !a (size_component ty.(i))
      done;
      rd
  | Sop(Istore(Word, addr), e1, _) ->
      let r1 = emit_expr env e1 seq in
      let na = Arch.num_args_addressing addr in
      let ra = Array.sub r1 0 na in
      let a = ref addr in
      for i = na to Array.length r1 - 1 do
        insert(Iop(Istore(Word, !a))) (Array.append [|r1.(i)|] ra) [||] seq;
        a := Arch.offset_addressing !a (size_component r1.(i).typ)
      done;
      [||]
  | Sop(Ialloc _, e1, _) ->
      Proc.contains_calls := true;
      let r1 = emit_expr env e1 seq in
      let rd = Reg.newv typ_addr in
      insert (Iop(Ialloc(Cmm.size_machtype(Array.map (fun r -> r.typ) r1))))
             [||] rd seq;
      let a =
        ref (Arch.offset_addressing Arch.identity_addressing
                                    (-Arch.size_int)) in
      for i = 0 to Array.length r1 - 1 do
        insert(Iop(Istore(Word, !a))) [|r1.(i); rd.(0)|] [||] seq;
        a := Arch.offset_addressing !a (size_component r1.(i).typ)
      done;
      rd
  | Sop(op, e1, ty) ->
      begin match op with
        Imodify -> Proc.contains_calls := true | _ -> ()
      end;
      let r1 = emit_expr env e1 seq in
      let rd = Reg.newv ty in
      begin try
        (* Offer the processor description an opportunity to insert moves
           before and after the operation, i.e. for two-address instructions,
           or instructions using dedicated registers. *)
        let (rsrc, rdst) = Proc.pseudoregs_for_operation op r1 rd in
        insert_moves r1 rsrc seq;
        insert (Iop op) rsrc rdst seq;
        insert_moves rdst rd seq
      with Proc.Use_default ->
        (* Assume no constraints on arg and res registers *)
        insert (Iop op) r1 rd seq
      end;
      rd
  | Sproj(e1, ofs, len) ->
      let r1 = emit_expr env e1 seq in
      Array.sub r1 ofs len
  | Ssequence(e1, e2) ->
      emit_expr env e1 seq;
      emit_expr env e2 seq
  | Sifthenelse(cond, earg, eif, eelse) ->
      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 r = join rif sif relse selse in
      insert (Iifthenelse(cond, extract_sequence sif, extract_sequence selse))
             rarg [||] seq;
      r
  | Sswitch(esel, index, ecases) ->
      let rsel = emit_expr env esel seq in
      let rscases = Array.map (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;
      r
  | Sloop(ebody) ->
      let (rarg, sbody) = emit_sequence env ebody in
      insert (Iloop(extract_sequence sbody)) [||] [||] seq;
      [||]
  | Scatch(e1, e2) ->
      let (r1, s1) = emit_sequence env e1 in
      let (r2, s2) = emit_sequence env e2 in
      let r = join r1 s1 r2 s2 in
      insert (Icatch(extract_sequence s1, extract_sequence s2)) [||] [||] seq;
      r
  | Sexit ->
      insert Iexit [||] [||] seq;
      [||]
  | Strywith(e1, v, e2) ->
      let (r1, s1) = emit_sequence env e1 in
      let rv = Reg.newv typ_addr in
      let (r2, s2) = emit_sequence (Tbl.add v rv env) e2 in
      let r = join r1 s1 r2 s2 in
      insert
        (Itrywith(extract_sequence s1,
                  instr_cons (Iop Imove) [|Proc.loc_exn_bucket|] rv
                     (extract_sequence s2)))
        [||] [||] seq;
      r
  | Sraise e1 ->
      let r1 = emit_expr env e1 seq in
      insert Iraise r1 [||] seq;
      [||]

and emit_sequence env exp =
  let seq = new_sequence() in
  let r = emit_expr env exp seq in
  (r, seq)

and emit_let env v e1 seq =
  let r1 = emit_expr env e1 seq in
  if all_regs_anonymous r1 then begin
    name_regs v r1;
    Tbl.add v r1 env
  end else begin
    let rv = Array.new (Array.length r1) Reg.dummy in
    for i = 0 to Array.length r1 - 1 do rv.(i) <- Reg.new r1.(i).typ done;
    name_regs v rv;
    insert_moves r1 rv seq;
    Tbl.add v rv env
  end

(* Same, but in tail position *)

let emit_return env exp seq =
  let r = emit_expr env exp seq in
  let loc = Proc.loc_results r in
  insert_moves r loc seq;
  insert Ireturn loc [||] seq

let rec emit_tail env exp seq =
  match exp with
    Slet(v, e1, e2) ->
      emit_tail (emit_let env v e1 seq) e2 seq
  | Sop(Icall_ind, e1, ty) ->
      let r1 = emit_expr env e1 seq 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
        emit_return env exp seq
      else begin
        insert_moves rarg loc_arg seq;
        insert (Iop Itailcall_ind) (Array.append [|r1.(0)|] loc_arg) [||] seq
      end
  | Sop(Icall_imm lbl, e1, ty) ->
      let r1 = emit_expr env e1 seq in
      let (loc_arg, stack_ofs) = Proc.loc_arguments r1 in
      if stack_ofs <> 0 then
        emit_return env exp seq
      else begin
        insert_moves r1 loc_arg seq;
        insert (Iop(Itailcall_imm lbl)) loc_arg [||] seq
      end
  | Ssequence(e1, e2) ->
      emit_expr env e1 seq;
      emit_tail env e2 seq
  | Sifthenelse(cond, earg, eif, eelse) ->
      let rarg = emit_expr env earg seq in
      insert (Iifthenelse(cond, emit_tail_sequence env eif,
                                emit_tail_sequence env eelse))
             rarg [||] seq
  | Sswitch(esel, index, ecases) ->
      let rsel = emit_expr env esel seq in
      insert (Iswitch(index, Array.map (emit_tail_sequence env) ecases))
             rsel [||] seq
  | Scatch(e1, e2) ->
      insert (Icatch(emit_tail_sequence env e1, emit_tail_sequence env e2))
             [||] [||] seq
  | Sexit ->
      insert Iexit [||] [||] seq
  | Sraise e1 ->
      let r1 = emit_expr env e1 seq in
      let rd = [|Proc.loc_exn_bucket|] in
      insert (Iop Imove) r1 rd seq;
      insert Iraise rd [||] seq
  | _ ->
      emit_return env exp seq

and emit_tail_sequence env exp =
  let seq = new_sequence() in
  emit_tail env exp seq;
  extract_sequence seq

(* Sequentialization of a function definition *)

let fundecl f =
  Proc.contains_calls := false;
  let rargs =
    List.map
      (fun (id, ty) -> let r = Reg.newv 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
  let seq = new_sequence() in
  insert_moves loc_arg rarg seq;
  emit_tail env (Selection.expression f.Cmm.fun_body) seq;
  { fun_name = f.Cmm.fun_name;
    fun_args = loc_arg;
    fun_body = extract_sequence seq }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.