142 lines
6.0 KiB
OCaml
142 lines
6.0 KiB
OCaml
(**************************************************************************)
|
|
(* *)
|
|
(* OCaml *)
|
|
(* *)
|
|
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
|
|
(* Mark Shinwell, Jane Street Europe *)
|
|
(* *)
|
|
(* Copyright 1996 Institut National de Recherche en Informatique et *)
|
|
(* en Automatique. *)
|
|
(* *)
|
|
(* All rights reserved. This file is distributed under the terms of *)
|
|
(* the GNU Lesser General Public License version 2.1, with the *)
|
|
(* special exception on linking described in the file LICENSE. *)
|
|
(* *)
|
|
(**************************************************************************)
|
|
|
|
open Mach
|
|
open Linearize
|
|
|
|
module Make (T : Branch_relaxation_intf.S) = struct
|
|
let label_map code =
|
|
let map = Hashtbl.create 37 in
|
|
let rec fill_map pc instr =
|
|
match instr.desc with
|
|
| Lend -> (pc, map)
|
|
| Llabel lbl -> Hashtbl.add map lbl pc; fill_map pc instr.next
|
|
| op -> fill_map (pc + T.instr_size op) instr.next
|
|
in
|
|
fill_map 0 code
|
|
|
|
let branch_overflows map pc_branch lbl_dest max_branch_offset =
|
|
let pc_dest = Hashtbl.find map lbl_dest in
|
|
let delta = pc_dest - (pc_branch + T.offset_pc_at_branch) in
|
|
delta <= -max_branch_offset || delta >= max_branch_offset
|
|
|
|
let opt_branch_overflows map pc_branch opt_lbl_dest max_branch_offset =
|
|
match opt_lbl_dest with
|
|
| None -> false
|
|
| Some lbl_dest ->
|
|
branch_overflows map pc_branch lbl_dest max_branch_offset
|
|
|
|
let instr_overflows ~code_size ~max_out_of_line_code_offset instr map pc =
|
|
match T.Cond_branch.classify_instr instr.desc with
|
|
| None -> false
|
|
| Some branch ->
|
|
let max_branch_offset =
|
|
(* Remember to cut some slack for multi-word instructions (in the
|
|
[Linearize] sense of the word) where the branch can be anywhere in
|
|
the middle. 12 words of slack is plenty. *)
|
|
T.Cond_branch.max_displacement branch - 12
|
|
in
|
|
match instr.desc with
|
|
| Lop (Ialloc _)
|
|
| Lop (Iintop Icheckbound)
|
|
| Lop (Iintop_imm (Icheckbound, _))
|
|
| Lop (Ispecific _) ->
|
|
(* We assume that any branches eligible for relaxation generated
|
|
by these instructions only branch forward. We further assume
|
|
that any of these may branch to an out-of-line code block. *)
|
|
code_size + max_out_of_line_code_offset - pc >= max_branch_offset
|
|
| Lcondbranch (_, lbl) ->
|
|
branch_overflows map pc lbl max_branch_offset
|
|
| Lcondbranch3 (lbl0, lbl1, lbl2) ->
|
|
opt_branch_overflows map pc lbl0 max_branch_offset
|
|
|| opt_branch_overflows map pc lbl1 max_branch_offset
|
|
|| opt_branch_overflows map pc lbl2 max_branch_offset
|
|
| _ ->
|
|
Misc.fatal_error "Unsupported instruction for branch relaxation"
|
|
|
|
let fixup_branches ~code_size ~max_out_of_line_code_offset map code =
|
|
let expand_optbranch lbl n arg next =
|
|
match lbl with
|
|
| None -> next
|
|
| Some l ->
|
|
instr_cons (Lcondbranch (Iinttest_imm (Isigned Cmm.Ceq, n), l))
|
|
arg [||] next
|
|
in
|
|
let rec fixup did_fix pc instr =
|
|
match instr.desc with
|
|
| Lend -> did_fix
|
|
| _ ->
|
|
let overflows =
|
|
instr_overflows ~code_size ~max_out_of_line_code_offset instr map pc
|
|
in
|
|
if not overflows then
|
|
fixup did_fix (pc + T.instr_size instr.desc) instr.next
|
|
else
|
|
match instr.desc with
|
|
| Lop (Ialloc num_words) ->
|
|
instr.desc <- T.relax_allocation ~num_words;
|
|
fixup true (pc + T.instr_size instr.desc) instr.next
|
|
| Lop (Iintop Icheckbound) ->
|
|
instr.desc <- T.relax_intop_checkbound ();
|
|
fixup true (pc + T.instr_size instr.desc) instr.next
|
|
| Lop (Iintop_imm (Icheckbound, bound)) ->
|
|
instr.desc <- T.relax_intop_imm_checkbound ~bound;
|
|
fixup true (pc + T.instr_size instr.desc) instr.next
|
|
| Lop (Ispecific specific) ->
|
|
instr.desc <- T.relax_specific_op specific;
|
|
fixup true (pc + T.instr_size instr.desc) instr.next
|
|
| Lcondbranch (test, lbl) ->
|
|
let lbl2 = new_label() in
|
|
let cont =
|
|
instr_cons (Lbranch lbl) [||] [||]
|
|
(instr_cons (Llabel lbl2) [||] [||] instr.next)
|
|
in
|
|
instr.desc <- Lcondbranch (invert_test test, lbl2);
|
|
instr.next <- cont;
|
|
fixup true (pc + T.instr_size instr.desc) instr.next
|
|
| Lcondbranch3 (lbl0, lbl1, lbl2) ->
|
|
let cont =
|
|
expand_optbranch lbl0 0 instr.arg
|
|
(expand_optbranch lbl1 1 instr.arg
|
|
(expand_optbranch lbl2 2 instr.arg instr.next))
|
|
in
|
|
instr.desc <- cont.desc;
|
|
instr.next <- cont.next;
|
|
fixup true pc instr
|
|
| _ ->
|
|
(* Any other instruction has already been rejected in
|
|
[instr_overflows] above.
|
|
We can *never* get here. *)
|
|
assert false
|
|
in
|
|
fixup false 0 code
|
|
|
|
(* Iterate branch expansion till all conditional branches are OK *)
|
|
|
|
let rec relax code ~max_out_of_line_code_offset =
|
|
let min_of_max_branch_offsets =
|
|
List.fold_left (fun min_of_max_branch_offsets branch ->
|
|
min min_of_max_branch_offsets
|
|
(T.Cond_branch.max_displacement branch))
|
|
max_int T.Cond_branch.all
|
|
in
|
|
let (code_size, map) = label_map code in
|
|
if code_size >= min_of_max_branch_offsets
|
|
&& fixup_branches ~code_size ~max_out_of_line_code_offset map code
|
|
then relax code ~max_out_of_line_code_offset
|
|
else ()
|
|
end
|