771 lines
26 KiB
Plaintext
771 lines
26 KiB
Plaintext
#2 "asmcomp/sparc/emit.mlp"
|
|
(**************************************************************************)
|
|
(* *)
|
|
(* OCaml *)
|
|
(* *)
|
|
(* 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 GNU Lesser General Public License version 2.1, with the *)
|
|
(* special exception on linking described in the file LICENSE. *)
|
|
(* *)
|
|
(**************************************************************************)
|
|
|
|
(* Emission of Sparc assembly code *)
|
|
|
|
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 | Val)} -> 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 ?label live =
|
|
let lbl =
|
|
match label with
|
|
| None -> new_label()
|
|
| Some label -> label
|
|
in
|
|
let live_offset = ref [] in
|
|
Reg.Set.iter
|
|
(function
|
|
| {typ = Val; loc = Reg r} ->
|
|
live_offset := ((r lsl 1) + 1) :: !live_offset
|
|
| {typ = Val; loc = Stack s} as reg ->
|
|
live_offset := slot_offset s (register_class reg) :: !live_offset
|
|
| {typ = Addr} as r ->
|
|
Misc.fatal_error ("bad GC root " ^ Reg.name r)
|
|
| _ -> ())
|
|
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 * int64) 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 _; typ = (Int | Addr | Val)}, {loc = Reg _} ->
|
|
` mov {emit_reg src}, {emit_reg dst}\n`
|
|
| {loc = Reg _; typ = Float}, {loc = Reg _; 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 _; typ = Float}, {loc = Reg _; typ = (Int | Addr | Val)} ->
|
|
(* 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`;
|
|
let dst2 = i.res.(1) in
|
|
begin match dst2 with
|
|
| {loc = Reg _; typ = Int} ->
|
|
` ld [%sp + 100], {emit_reg dst2}\n`;
|
|
| {loc = Stack _; typ = Int} ->
|
|
` ld [%sp + 100], %g1\n`;
|
|
` st %g1, {emit_stack dst2}\n`;
|
|
| _ ->
|
|
fatal_error "Emit: Imove Float [| _; _ |]"
|
|
end;
|
|
` add %sp, 8, %sp\n`
|
|
| {loc = Reg _; typ = (Int | Addr | Val)}, {loc = Stack _} ->
|
|
` st {emit_reg src}, {emit_stack dst}\n`
|
|
| {loc = Reg _; typ = Float}, {loc = Stack _} ->
|
|
` std {emit_reg src}, {emit_stack dst}\n`
|
|
| {loc = Stack _; typ = (Int | Addr | Val)}, {loc = Reg _} ->
|
|
` ld {emit_stack src}, {emit_reg dst}\n`
|
|
| {loc = Stack _; typ = Float}, {loc = Reg _} ->
|
|
` 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 f) ->
|
|
(* 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, f) :: !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 { label_after; }) ->
|
|
`{record_frame i.live ~label:label_after} call {emit_reg i.arg.(0)}\n`;
|
|
fill_delay_slot dslot
|
|
| Lop(Icall_imm { func; label_after; }) ->
|
|
`{record_frame i.live ~label:label_after} call {emit_symbol func}\n`;
|
|
fill_delay_slot dslot
|
|
| Lop(Itailcall_ind { label_after = _; }) ->
|
|
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 { func; label_after = _; }) ->
|
|
let n = frame_size() in
|
|
if func = !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 func}), %g1\n`;
|
|
` jmp %g1 + %lo({emit_symbol func})\n`;
|
|
` add %sp, {emit_int n}, %sp\n` (* in delay slot *)
|
|
end
|
|
| Lop(Iextcall { func; alloc; label_after; }) ->
|
|
if alloc then begin
|
|
` sethi %hi({emit_symbol func}), %g2\n`;
|
|
`{record_frame i.live ~label:label_after} call {emit_symbol "caml_c_call"}\n`;
|
|
` or %g2, %lo({emit_symbol func}), %g2\n` (* in delay slot *)
|
|
end else begin
|
|
` call {emit_symbol func}\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 { words = n; label_after_call_gc; }) ->
|
|
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 ?label:label_after_call_gc} 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 Imulh) ->
|
|
` smul {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, %g1\n`;
|
|
` rd %y, {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(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(Imulh, n)) ->
|
|
` smul {emit_reg i.arg.(0)}, {emit_int n}, %g1\n`;
|
|
` rd %y, {emit_reg i.res.(0)}\n`
|
|
| 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 _) ->
|
|
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
|
|
Imulh | 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 { alloc = false; }) | Lbranch _}}
|
|
when is_one_instr i ->
|
|
emit_instr i.next (Some i);
|
|
emit_all i.next.next
|
|
| {next = {desc = Lop(Itailcall_imm { func; _ })}}
|
|
when func = !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`
|
|
| 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" (Int32.bits_of_float f)
|
|
| Cdouble f ->
|
|
emit_float64_split_directive ".word" (Int64.bits_of_float f)
|
|
| Csymbol_address s ->
|
|
` .word {emit_symbol s}\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 := []
|