ocaml/asmcomp/emit_power.mlp

844 lines
28 KiB
Plaintext

(***********************************************************************)
(* *)
(* 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
(* 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
(* 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_constants = (Hashtbl.create 7 : (int, int) Hashtbl.t)
let label_symbol s = label_constant symbol_constants s
let label_float s = label_constant float_constants s
let label_label lb = label_constant label_constants lb
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`
let emit_label_constant symb lbl =
`{emit_label lbl}: .tc {emit_label symb}[TC], {emit_label symb}\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)
(* 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
(* 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
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_immediate n then
` li {emit_reg i.res.(0)}, {emit_int n}\n`
else begin
` lis {emit_reg i.res.(0)}, {emit_int(high n)}\n`;
if low n <> 0 then
` ori {emit_reg i.res.(0)}, {emit_reg i.res.(0)}, {emit_int(low 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`;
if !contains_calls then begin
` lwz 11, {emit_int(n - 4)}(1)\n`;
` mtctr 0\n`;
` addi 1, 1, {emit_int n}\n`;
` mtlr 11\n`
end else begin
` mtctr 0\n`;
` addi 1, 1, {emit_int n}\n`
end
end else begin
if !contains_calls then begin
` lwz 11, {emit_int(n - 4)}(1)\n`;
` mtctr {emit_reg i.arg.(0)}\n`;
` addi 1, 1, {emit_int n}\n`;
` mtlr 11\n`
end else begin
` mtctr {emit_reg i.arg.(0)}\n`;
` addi 1, 1, {emit_int n}\n`
end
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
` addi 1, 1, {emit_int n}\n`;
` 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(11)\n`;
` lwz 2, 4(11)\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 =
match op with
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" 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 =
match op with
Iadd -> "addi"
| Imul -> "mulli"
| Iand -> "andi."
| Ior -> "ori"
| Ixor -> "xori"
| Ilsl -> "slwi"
| Ilsr -> "srwi"
| Iasr -> "srawi"
| _ -> Misc.fatal_error "Emit.Intop_imm" 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 =
match op with
Inegf -> "fneg"
| Iabsf -> "fabs"
| _ -> Misc.fatal_error "Emit.Iopf1" in
` {emit_string instr} {emit_reg i.res.(0)}, {emit_reg i.arg.(0)}\n`
| Lop(Iaddf | Isubf | Imulf | Idivf as op) ->
let instr =
match op with
Iaddf -> "fadd"
| Isubf -> "fsub"
| Imulf -> "fmul"
| Idivf -> "fdiv"
| _ -> Misc.fatal_error "Emit.Iopf2" 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 =
match sop with
Imultaddf -> "fmadd"
| Imultsubf -> "fmsub" 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 12, {emit_int(n - 4)}(1)\n`
(* This works because the reloadretaddr and the return are in the
same basic block, and none of the instructions that can be
part of a basic block modify r12 *)
| Lreturn ->
let n = frame_size() in
if !contains_calls then
` mtlr 12\n`;
` 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 ->
let lbl_table = new_label() in
if toc then begin
let lbl_addr = label_label lbl_table in
` lwz 11, {emit_label lbl_addr}(2)\n`
end else begin
` addis 11, 0, {emit_label lbl_table}@ha\n`;
` addi 11, 11, {emit_label lbl_table}@l\n`
end;
` slwi 0, {emit_reg i.arg.(0)}, 2\n`;
` lwzx 0, 11, 0\n`;
` add 0, 0, 11\n`;
` mtctr 0\n`;
` bctr\n`;
`{emit_label lbl_table}:\n`;
for i = 0 to Array.length jumptbl - 1 do
` .long {emit_label jumptbl.(i)} - {emit_label lbl_table}\n`
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 ->
` mr 1, 29\n`;
` lwz 0, 0(1)\n`;
` lwz 29, 4(1)\n`;
` mtlr 0\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. *)
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 | 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`;
` .csect .text[PR]\n`;
` .align 2\n`
end else begin
` .type {emit_symbol fundecl.fun_name}, @function\n`;
` .section \".text\"\n`;
` .align 2\n`
end;
`{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 data_space =
if toc
then " .csect .data[RW]\n"
else " .section \".data\"\n"
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_int n}\n`
| Cintlit s ->
` .long {emit_string s}\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;
Hashtbl.clear label_constants;
defined_functions := StringSet.empty;
(* Emit the beginning of the data segment *)
let lbl_begin = Compilenv.current_unit_name() ^ "_begin" in
emit_string data_space;
declare_global_data lbl_begin;
`{emit_symbol lbl_begin}:\n`
let end_assembly() =
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;
Hashtbl.iter emit_label_constant label_constants
end;
emit_string data_space;
(* Emit the end of the data segment *)
let lbl_end = Compilenv.current_unit_name() ^ "_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 := []