diff options
Diffstat (limited to 'ocaml-binary-annot/asmcomp/sparc/emit.mlp')
| -rw-r--r-- | ocaml-binary-annot/asmcomp/sparc/emit.mlp | 776 |
1 files changed, 776 insertions, 0 deletions
diff --git a/ocaml-binary-annot/asmcomp/sparc/emit.mlp b/ocaml-binary-annot/asmcomp/sparc/emit.mlp new file mode 100644 index 0000000000..f44f813e53 --- /dev/null +++ b/ocaml-binary-annot/asmcomp/sparc/emit.mlp @@ -0,0 +1,776 @@ +(***********************************************************************) +(* *) +(* Objective Caml *) +(* *) +(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) +(* *) +(* Copyright 1996 Institut National de Recherche en Informatique et *) +(* en Automatique. All rights reserved. This file is distributed *) +(* under the terms of the Q Public License version 1.0. *) +(* *) +(***********************************************************************) + +(* $Id$ *) + +(* Emission of Sparc assembly code *) + +open Location +open Misc +open Cmm +open Arch +open Proc +open Reg +open Mach +open Linearize +open Emitaux + +(* Solaris vs. the other ports *) + +let solaris = Config.system = "solaris" + +(* 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 + 16) + | _ -> fatal_error "Emit.next_in_pair" + +(* Symbols are prefixed with _ under SunOS *) + +let symbol_prefix = + if Config.system = "sunos" 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 + +let emit_size lbl = + if Config.system = "solaris" then + ` .size {emit_symbol lbl},.-{emit_symbol lbl}\n` + +let rodata () = + if Config.system = "solaris" (* || Config.system = "linux" *) + (* || Config.system = "gnu" *) then + ` .section \".rodata\"\n` + else + ` .data\n` + +(* Check if an integer or native integer is an immediate operand *) + +let is_immediate n = + n <= 4095 && n >= -4096 + +let is_native_immediate n = + n <= Nativeint.of_int 4095 && n >= Nativeint.of_int (-4096) + +(* Output a label *) + +let label_prefix = + if Config.system = "sunos" 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 + +(* 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 + +(* 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) = + rodata (); + ` .align 8\n`; + `{emit_label lbl}:`; + emit_float64_split_directive ".word" cst + +(* Emission of the profiling prelude *) +let emit_profile () = + begin match Config.system with + "solaris" -> + let lbl = new_label() in + ` .section \".bss\"\n`; + `{emit_label lbl}: .skip 4\n`; + ` .text\n`; + ` save %sp,-96,%sp\n`; + ` sethi %hi({emit_label lbl}),%o0\n`; + ` call _mcount\n`; + ` or %o0,%lo({emit_label lbl}),%o0\n`; + ` restore\n` + | _ -> () + end + +(* 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" + | Imul -> "smul" + | _ -> Misc.fatal_error "Emit.name_for_int_operation" + +let name_for_float_operation = function + Inegf -> if !arch_version = SPARC_V9 then "fnegd" else "fnegs" + | Iabsf -> if !arch_version = SPARC_V9 then "fabsd" else "fabss" + | Iaddf -> "faddd" + | Isubf -> "fsubd" + | Imulf -> "fmuld" + | Idivf -> "fdivd" + | _ -> Misc.fatal_error "Emit.name_for_float_operation" + +let name_for_int_movcc = 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 -> "leu" | Iunsigned Cgt -> "gu" + | Iunsigned Clt -> "lu" | Iunsigned Cge -> "geu" + +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 range_check_trap = 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} -> + if !arch_version = SPARC_V9 then + ` fmovd {emit_reg src}, {emit_reg dst}\n` + else begin + ` fmovs {emit_reg src}, {emit_reg dst}\n`; + ` fmovs {emit_reg(next_in_pair src)}, {emit_reg(next_in_pair dst)}\n` + end + | {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`; + ` ld [%sp + 96], {emit_reg dst}\n`; + ` ld [%sp + 100], {emit_reg(next_in_pair dst)}\n`; + ` 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) -> + (* On UltraSPARC, the fzero instruction could be used to set a + floating point register pair to zero. *) + 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)) -> + let dest = i.res.(0) in + begin match chunk with + Double_u -> + emit_load "ld" addr i.arg dest; + emit_load "ld" (offset_addressing addr 4) i.arg (next_in_pair dest) + | Single -> + emit_load "ld" addr i.arg dest; + ` fstod {emit_reg dest}, {emit_reg dest}\n` + | _ -> + let loadinstr = + match chunk with + Byte_unsigned -> "ldub" + | Byte_signed -> "ldsb" + | Sixteen_unsigned -> "lduh" + | Sixteen_signed -> "ldsh" + | Double -> "ldd" + | _ -> "ld" in + emit_load loadinstr addr i.arg dest + end + | Lop(Istore(chunk, addr)) -> + let src = i.arg.(0) in + begin match chunk with + Double_u -> + emit_store "st" addr i.arg src; + emit_store "st" (offset_addressing addr 4) i.arg (next_in_pair src) + | Single -> + ` fdtos {emit_reg src}, %f30\n`; + emit_store "st" addr i.arg (phys_reg 115) (* %f30 *) + | _ -> + let storeinstr = + match chunk with + | Byte_unsigned | Byte_signed -> "stb" + | Sixteen_unsigned | Sixteen_signed -> "sth" + | Double -> "std" + | _ -> "st" in + emit_store storeinstr addr i.arg src + end + | Lop(Ialloc n) -> + if !fastcode_flag then begin + let lbl_cont = new_label() in + if solaris then begin + ` sub %l6, {emit_int n}, %l6\n`; + ` cmp %l6, %l7\n` + end else begin + ` ld [%l7], %g1\n`; + ` sub %l6, {emit_int n}, %l6\n`; + ` cmp %l6, %g1\n` + end; + ` 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_allocN"}\n`; + ` mov {emit_int n}, %g2\n`; (* in delay slot *) + ` add %l6, 4, {emit_reg i.res.(0)}\n` + end + | Lop(Iintop(Icomp cmp)) -> + ` cmp {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`; + if !arch_version = SPARC_V9 then begin + let comp = name_for_int_movcc cmp in + ` mov 0, {emit_reg i.res.(0)}\n`; + ` mov{emit_string comp} %icc, 1, {emit_reg i.res.(0)}\n` + end + else begin + let comp = name_for_int_comparison cmp + and 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` + end + | Lop(Iintop Icheckbound) -> + ` cmp {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`; + if solaris then + ` tleu 5\n` (* 5 = ST_RANGE_CHECK *) + else begin + if !range_check_trap = 0 then range_check_trap := new_label(); + ` bleu {emit_label !range_check_trap}\n`; + ` nop\n` (* delay slot *) + end + | Lop(Iintop Idiv) -> + ` sra {emit_reg i.arg.(0)}, 31, %g1\n`; + ` wr %g1, %y\n`; + ` sdiv {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, {emit_reg i.res.(0)}\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(Ilsl, 1)) -> + (* UltraSPARC has two add units but only one shifter. *) + ` add {emit_reg i.arg.(0)}, {emit_reg i.arg.(0)}, {emit_reg i.res.(0)}\n` + | Lop(Iintop_imm(Idiv, n)) -> + let l = Misc.log2 n in + if n = 1 lsl l then begin + 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` + end else begin + ` sra {emit_reg i.arg.(0)}, 31, %g1\n`; + ` wr %g1, %y\n`; + ` sdiv {emit_reg i.arg.(0)}, {emit_int n}, {emit_reg i.res.(0)}\n` + end + | 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)) -> + ` cmp {emit_reg i.arg.(0)}, {emit_int n}\n`; + if !arch_version = SPARC_V9 then begin + let comp = name_for_int_movcc cmp in + ` mov 0, {emit_reg i.res.(0)}\n`; + ` mov{emit_string comp} %icc, 1, {emit_reg i.res.(0)}\n` + end else begin + let comp = name_for_int_comparison cmp + and 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` + end + | Lop(Iintop_imm(Icheckbound, n)) -> + ` cmp {emit_reg i.arg.(0)}, {emit_int n}\n`; + if solaris then + ` tleu 5\n` (* 5 = ST_RANGE_CHECK *) + else begin + if !range_check_trap = 0 then range_check_trap := new_label(); + ` bleu {emit_label !range_check_trap}\n`; + ` nop\n` (* delay slot *) + end + | 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`; + if !arch_version <> SPARC_V9 then + ` 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) -> + assert false + | Lreloadretaddr -> + let n = frame_size() in + ` ld [%sp + {emit_int(n - 4 + 96)}], %o7\n` + | Lreturn -> + let n = frame_size() in + ` retl\n`; + if n = 0 then + ` nop\n` + else + ` 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 <> Float && i.res.(0).typ <> Float + | Iconst_int n -> is_native_immediate n + | Istackoffset _ -> true + | Iload(_, Iindexed n) -> i.res.(0).typ <> Float & is_immediate n + | Istore(_, Iindexed n) -> i.arg.(0).typ <> Float & is_immediate n + | Iintop(op) -> is_one_instr_op op + | Iintop_imm(op, _) -> is_one_instr_op op + | Iaddf | Isubf | Imulf | Idivf -> true + | Iabsf | Inegf -> !arch_version = SPARC_V9 + | _ -> 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(); + range_check_trap := 0; + stack_offset := 0; + float_constants := []; + ` .text\n`; + ` .align 4\n`; + ` .global {emit_symbol fundecl.fun_name}\n`; + if Config.system = "solaris" then + ` .type {emit_symbol fundecl.fun_name},#function\n`; + `{emit_symbol fundecl.fun_name}:\n`; + if !Clflags.gprofile then emit_profile(); + 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; + if !range_check_trap > 0 then begin + `{emit_label !range_check_trap}:\n`; + ` call {emit_symbol "caml_ml_array_bound_error"}\n`; + ` nop\n` + end; + emit_size fundecl.fun_name; + List.iter emit_float_constant !float_constants + +(* Emission of data *) + +let emit_item = function + Cglobal_symbol s -> + ` .global {emit_symbol s}\n`; + | Cdefine_symbol s -> + `{emit_symbol s}:\n` + | Cdefine_label lbl -> + `{emit_label (lbl + 100000)}:\n` + | Cint8 n -> + ` .byte {emit_int n}\n` + | Cint16 n -> + ` .half {emit_int n}\n` + | Cint32 n -> + ` .word {emit_nativeint n}\n` + | Cint n -> + ` .word {emit_nativeint n}\n` + | Csingle f -> + emit_float32_directive ".word" f + | Cdouble f -> + emit_float64_split_directive ".word" f + | Csymbol_address s -> + ` .word {emit_symbol s}\n` + | Clabel_address lbl -> + ` .word {emit_label (lbl + 100000)}\n` + | Cstring s -> + emit_string_directive " .ascii " s + | 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.make_symbol (Some "data_begin") in + ` .data\n`; + ` .global {emit_symbol lbl_begin}\n`; + `{emit_symbol lbl_begin}:\n`; + let lbl_begin = Compilenv.make_symbol (Some "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.make_symbol (Some "code_end") in + ` .global {emit_symbol lbl_end}\n`; + `{emit_symbol lbl_end}:\n`; + ` .data\n`; + let lbl_end = Compilenv.make_symbol (Some "data_end") in + ` .global {emit_symbol lbl_end}\n`; + `{emit_symbol lbl_end}:\n`; + ` .word 0\n`; + let lbl = Compilenv.make_symbol (Some "frametable") in + rodata (); + ` .global {emit_symbol lbl}\n`; + if Config.system = "solaris" then + ` .type {emit_symbol lbl},#object\n`; + `{emit_symbol lbl}:\n`; + ` .word {emit_int (List.length !frame_descriptors)}\n`; + List.iter emit_frame !frame_descriptors; + emit_size lbl; + frame_descriptors := [] |
