0c4d3e
From 38ac4778744fb7137e04708998d4e856ada1c8b8 Mon Sep 17 00:00:00 2001
0c4d3e
From: Nicolas Ojeda Bar <n.oje.bar@gmail.com>
0c4d3e
Date: Fri, 27 Oct 2017 17:05:25 +0200
0c4d3e
Subject: [PATCH 4/8] Add RISC-V backend
0c4d3e
0c4d3e
---
0c4d3e
 README.adoc                 |   1 +
0c4d3e
 asmcomp/riscv/CSE.ml        |  36 ++
0c4d3e
 asmcomp/riscv/arch.ml       |  87 +++++
0c4d3e
 asmcomp/riscv/emit.mlp      | 653 ++++++++++++++++++++++++++++++++++++
0c4d3e
 asmcomp/riscv/proc.ml       | 301 +++++++++++++++++
0c4d3e
 asmcomp/riscv/reload.ml     |  16 +
0c4d3e
 asmcomp/riscv/scheduling.ml |  19 ++
0c4d3e
 asmcomp/riscv/selection.ml  |  72 ++++
0c4d3e
 asmrun/riscv.S              | 424 +++++++++++++++++++++++
0c4d3e
 byterun/caml/stack.h        |   5 +
0c4d3e
 config/gnu/config.guess     |   5 +-
0c4d3e
 configure                   |   5 +-
0c4d3e
 12 files changed, 1622 insertions(+), 2 deletions(-)
0c4d3e
 create mode 100644 asmcomp/riscv/CSE.ml
0c4d3e
 create mode 100644 asmcomp/riscv/arch.ml
0c4d3e
 create mode 100644 asmcomp/riscv/emit.mlp
0c4d3e
 create mode 100644 asmcomp/riscv/proc.ml
0c4d3e
 create mode 100644 asmcomp/riscv/reload.ml
0c4d3e
 create mode 100644 asmcomp/riscv/scheduling.ml
0c4d3e
 create mode 100644 asmcomp/riscv/selection.ml
0c4d3e
 create mode 100644 asmrun/riscv.S
0c4d3e
0c4d3e
diff --git a/README.adoc b/README.adoc
0c4d3e
index 74d1ec258..ac6c6eac3 100644
0c4d3e
--- a/README.adoc
0c4d3e
+++ b/README.adoc
0c4d3e
@@ -47,6 +47,7 @@ AMD64::              FreeBSD, OpenBSD, NetBSD
0c4d3e
 IA32 (Pentium)::     NetBSD, OpenBSD, Solaris 9
0c4d3e
 PowerPC::            NetBSD
0c4d3e
 ARM::                NetBSD
0c4d3e
+RISC-V::             Linux
0c4d3e
 
0c4d3e
 Other operating systems for the processors above have not been tested, but
0c4d3e
 the compiler may work under other operating systems with little work.
0c4d3e
diff --git a/asmcomp/riscv/CSE.ml b/asmcomp/riscv/CSE.ml
0c4d3e
new file mode 100644
0c4d3e
index 000000000..302811a99
0c4d3e
--- /dev/null
0c4d3e
+++ b/asmcomp/riscv/CSE.ml
0c4d3e
@@ -0,0 +1,36 @@
0c4d3e
+(***********************************************************************)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*                                OCaml                                *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*               Nicolas Ojeda Bar <n.oje.bar@gmail.com>               *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*  Copyright 2106 Institut National de Recherche en Informatique et   *)
0c4d3e
+(*  en Automatique.  All rights reserved.  This file is distributed    *)
0c4d3e
+(*  under the terms of the Q Public License version 1.0.               *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(***********************************************************************)
0c4d3e
+
0c4d3e
+(* CSE for the RISC-V *)
0c4d3e
+
0c4d3e
+open Arch
0c4d3e
+open Mach
0c4d3e
+open CSEgen
0c4d3e
+
0c4d3e
+class cse = object (_self)
0c4d3e
+
0c4d3e
+inherit cse_generic as super
0c4d3e
+
0c4d3e
+method! class_of_operation op =
0c4d3e
+  match op with
0c4d3e
+  | Ispecific(Imultaddf _ | Imultsubf _) -> Op_pure
0c4d3e
+  | _ -> super#class_of_operation op
0c4d3e
+
0c4d3e
+method! is_cheap_operation op =
0c4d3e
+  match op with
0c4d3e
+  | Iconst_int n -> n <= 0x7FFn && n >= -0x800n
0c4d3e
+  | _ -> false
0c4d3e
+
0c4d3e
+end
0c4d3e
+
0c4d3e
+let fundecl f =
0c4d3e
+  (new cse)#fundecl f
0c4d3e
diff --git a/asmcomp/riscv/arch.ml b/asmcomp/riscv/arch.ml
0c4d3e
new file mode 100644
0c4d3e
index 000000000..22c807c49
0c4d3e
--- /dev/null
0c4d3e
+++ b/asmcomp/riscv/arch.ml
0c4d3e
@@ -0,0 +1,87 @@
0c4d3e
+(***********************************************************************)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*                                OCaml                                *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*               Nicolas Ojeda Bar <n.oje.bar@gmail.com>               *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*  Copyright 2016 Institut National de Recherche en Informatique et   *)
0c4d3e
+(*  en Automatique.  All rights reserved.  This file is distributed    *)
0c4d3e
+(*  under the terms of the Q Public License version 1.0.               *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(***********************************************************************)
0c4d3e
+
0c4d3e
+(* Specific operations for the RISC-V processor *)
0c4d3e
+
0c4d3e
+open Format
0c4d3e
+
0c4d3e
+(* Machine-specific command-line options *)
0c4d3e
+
0c4d3e
+let command_line_options = []
0c4d3e
+
0c4d3e
+(* Specific operations *)
0c4d3e
+
0c4d3e
+type specific_operation =
0c4d3e
+  | Imultaddf of bool                   (* multiply, optionally negate, and add *)
0c4d3e
+  | Imultsubf of bool                   (* multiply, optionally negate, and subtract *)
0c4d3e
+
0c4d3e
+let spacetime_node_hole_pointer_is_live_before = function
0c4d3e
+  | Imultaddf _ | Imultsubf _ -> false
0c4d3e
+
0c4d3e
+(* Addressing modes *)
0c4d3e
+
0c4d3e
+type addressing_mode =
0c4d3e
+  | Iindexed of int                     (* reg + displ *)
0c4d3e
+
0c4d3e
+let is_immediate n =
0c4d3e
+  (n <= 2047) && (n >= -2048)
0c4d3e
+
0c4d3e
+(* Sizes, endianness *)
0c4d3e
+
0c4d3e
+let big_endian = false
0c4d3e
+
0c4d3e
+let rv64 =
0c4d3e
+  match Config.model with "riscv64" -> true | "riscv32" -> false | _ -> assert false
0c4d3e
+
0c4d3e
+let size_addr = if rv64 then 8 else 4
0c4d3e
+let size_int = size_addr
0c4d3e
+let size_float = 8
0c4d3e
+
0c4d3e
+let allow_unaligned_access = false
0c4d3e
+
0c4d3e
+(* Behavior of division *)
0c4d3e
+
0c4d3e
+let division_crashes_on_overflow = false
0c4d3e
+
0c4d3e
+(* Operations on addressing modes *)
0c4d3e
+
0c4d3e
+let identity_addressing = Iindexed 0
0c4d3e
+
0c4d3e
+let offset_addressing addr delta =
0c4d3e
+  match addr with
0c4d3e
+  | Iindexed n -> Iindexed(n + delta)
0c4d3e
+
0c4d3e
+let num_args_addressing = function
0c4d3e
+  | Iindexed _ -> 1
0c4d3e
+
0c4d3e
+(* Printing operations and addressing modes *)
0c4d3e
+
0c4d3e
+let print_addressing printreg addr ppf arg =
0c4d3e
+  match addr with
0c4d3e
+  | Iindexed n ->
0c4d3e
+      let idx = if n <> 0 then Printf.sprintf " + %i" n else "" in
0c4d3e
+      fprintf ppf "%a%s" printreg arg.(0) idx
0c4d3e
+
0c4d3e
+let print_specific_operation printreg op ppf arg =
0c4d3e
+  match op with
0c4d3e
+  | Imultaddf false ->
0c4d3e
+      fprintf ppf "%a *f %a +f %a"
0c4d3e
+        printreg arg.(0) printreg arg.(1) printreg arg.(2)
0c4d3e
+  | Imultaddf true ->
0c4d3e
+      fprintf ppf "-f (%a *f %a +f %a)"
0c4d3e
+        printreg arg.(0) printreg arg.(1) printreg arg.(2)
0c4d3e
+  | Imultsubf false ->
0c4d3e
+      fprintf ppf "%a *f %a -f %a"
0c4d3e
+        printreg arg.(0) printreg arg.(1) printreg arg.(2)
0c4d3e
+  | Imultsubf true ->
0c4d3e
+      fprintf ppf "-f (%a *f %a -f %a)"
0c4d3e
+        printreg arg.(0) printreg arg.(1) printreg arg.(2)
0c4d3e
diff --git a/asmcomp/riscv/emit.mlp b/asmcomp/riscv/emit.mlp
0c4d3e
new file mode 100644
0c4d3e
index 000000000..51165d0f1
0c4d3e
--- /dev/null
0c4d3e
+++ b/asmcomp/riscv/emit.mlp
0c4d3e
@@ -0,0 +1,653 @@
0c4d3e
+(***********************************************************************)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*                                OCaml                                *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*               Nicolas Ojeda Bar <n.oje.bar@gmail.com>               *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*  Copyright 2016 Institut National de Recherche en Informatique et   *)
0c4d3e
+(*  en Automatique.  All rights reserved.  This file is distributed    *)
0c4d3e
+(*  under the terms of the Q Public License version 1.0.               *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(***********************************************************************)
0c4d3e
+
0c4d3e
+(* Emission of RISC-V assembly code *)
0c4d3e
+
0c4d3e
+open Misc
0c4d3e
+open Cmm
0c4d3e
+open Arch
0c4d3e
+open Proc
0c4d3e
+open Reg
0c4d3e
+open Mach
0c4d3e
+open Linearize
0c4d3e
+open Emitaux
0c4d3e
+
0c4d3e
+(* Layout of the stack.  The stack is kept 16-aligned. *)
0c4d3e
+
0c4d3e
+let stack_offset = ref 0
0c4d3e
+
0c4d3e
+let frame_size () =
0c4d3e
+  let size =
0c4d3e
+    !stack_offset +                     (* Trap frame, outgoing parameters *)
0c4d3e
+    size_int * num_stack_slots.(0) +    (* Local int variables *)
0c4d3e
+    size_float * num_stack_slots.(1) +  (* Local float variables *)
0c4d3e
+    (if !contains_calls then size_addr else 0) in (* The return address *)
0c4d3e
+  Misc.align size 16
0c4d3e
+
0c4d3e
+let slot_offset loc cls =
0c4d3e
+  match loc with
0c4d3e
+  | Local n ->
0c4d3e
+      if cls = 0
0c4d3e
+      then !stack_offset + num_stack_slots.(1) * size_float + n * size_int
0c4d3e
+      else !stack_offset + n * size_float
0c4d3e
+  | Incoming n -> frame_size() + n
0c4d3e
+  | Outgoing n -> n
0c4d3e
+
0c4d3e
+(* Output a symbol *)
0c4d3e
+
0c4d3e
+let emit_symbol s =
0c4d3e
+  Emitaux.emit_symbol '.' s
0c4d3e
+
0c4d3e
+(* Output a label *)
0c4d3e
+
0c4d3e
+let label_prefix = "L"
0c4d3e
+
0c4d3e
+let emit_label lbl =
0c4d3e
+  emit_string label_prefix; emit_int lbl
0c4d3e
+
0c4d3e
+(* Section switching *)
0c4d3e
+
0c4d3e
+let data_space =
0c4d3e
+  ".section .data"
0c4d3e
+
0c4d3e
+let code_space =
0c4d3e
+  ".section .text"
0c4d3e
+
0c4d3e
+let rodata_space =
0c4d3e
+  ".section .rodata"
0c4d3e
+
0c4d3e
+let reg_tmp1 = phys_reg 21 (* used by the assembler *)
0c4d3e
+let reg_tmp2 = phys_reg 22
0c4d3e
+let reg_t2 = phys_reg 16
0c4d3e
+(* let reg_fp = phys_reg 23 *)
0c4d3e
+let reg_trap = phys_reg 24
0c4d3e
+let reg_alloc_ptr = phys_reg 25
0c4d3e
+let reg_alloc_lim = phys_reg 26
0c4d3e
+
0c4d3e
+(* Names of instructions that differ in 32 and 64-bit modes *)
0c4d3e
+
0c4d3e
+let lg = if rv64 then "ld" else "lw"
0c4d3e
+let stg = if rv64 then "sd" else "sw"
0c4d3e
+let datag = if rv64 then ".quad" else ".long"
0c4d3e
+
0c4d3e
+(* Output a pseudo-register *)
0c4d3e
+
0c4d3e
+let emit_reg = function
0c4d3e
+  | {loc = Reg r} -> emit_string (register_name r)
0c4d3e
+  | _ -> fatal_error "Emit.emit_reg"
0c4d3e
+
0c4d3e
+(* Adjust sp by the given byte amount *)
0c4d3e
+
0c4d3e
+let emit_stack_adjustment = function
0c4d3e
+  | 0 -> ()
0c4d3e
+  | n when is_immediate n ->
0c4d3e
+      `	addi	sp, sp, {emit_int n}\n`
0c4d3e
+  | n ->
0c4d3e
+      `	li	{emit_reg reg_tmp1}, {emit_int n}\n`;
0c4d3e
+      `	add	sp, sp, {emit_reg reg_tmp1}\n`
0c4d3e
+
0c4d3e
+let reload_ra n =
0c4d3e
+  let ofs = n - size_addr in
0c4d3e
+  if is_immediate ofs then
0c4d3e
+    `	{emit_string lg}	ra, {emit_int ofs}(sp)\n`
0c4d3e
+  else begin
0c4d3e
+    `	li	{emit_reg reg_tmp1}, {emit_int ofs}\n`;
0c4d3e
+    `	add	{emit_reg reg_tmp1}, sp, {emit_reg reg_tmp1}\n`;
0c4d3e
+    `	{emit_string lg}	ra, 0({emit_reg reg_tmp1})\n`
0c4d3e
+  end
0c4d3e
+
0c4d3e
+let store_ra n =
0c4d3e
+  let ofs = n - size_addr in
0c4d3e
+  if is_immediate ofs then
0c4d3e
+    `	{emit_string stg}	ra, {emit_int(n - size_addr)}(sp)\n`
0c4d3e
+  else begin
0c4d3e
+    `	li	{emit_reg reg_tmp1}, {emit_int ofs}\n`;
0c4d3e
+    `	add	{emit_reg reg_tmp1}, sp, {emit_reg reg_tmp1}\n`;
0c4d3e
+    `	{emit_string stg}	ra, 0({emit_reg reg_tmp1})\n`
0c4d3e
+  end
0c4d3e
+
0c4d3e
+let emit_store stg src ofs =
0c4d3e
+  if is_immediate ofs then
0c4d3e
+    `	{emit_string stg}	{emit_reg src}, {emit_int ofs}(sp)\n`
0c4d3e
+  else begin
0c4d3e
+    `	li	{emit_reg reg_tmp1}, {emit_int ofs}\n`;
0c4d3e
+    `	add	{emit_reg reg_tmp1}, sp, {emit_reg reg_tmp1}\n`;
0c4d3e
+    `	{emit_string stg}	{emit_reg src}, 0({emit_reg reg_tmp1})\n`
0c4d3e
+  end
0c4d3e
+
0c4d3e
+let emit_load lg dst ofs =
0c4d3e
+  if is_immediate ofs then
0c4d3e
+    `	{emit_string lg}	{emit_reg dst}, {emit_int ofs}(sp)\n`
0c4d3e
+  else begin
0c4d3e
+    `	li	{emit_reg reg_tmp1}, {emit_int ofs}\n`;
0c4d3e
+    `	add	{emit_reg reg_tmp1}, sp, {emit_reg reg_tmp1}\n`;
0c4d3e
+    `	{emit_string lg}	{emit_reg dst}, 0({emit_reg reg_tmp1})\n`
0c4d3e
+  end
0c4d3e
+
0c4d3e
+(* Record live pointers at call points *)
0c4d3e
+
0c4d3e
+let record_frame_label ?label live raise_ dbg =
0c4d3e
+  let lbl =
0c4d3e
+    match label with
0c4d3e
+    | None -> new_label()
0c4d3e
+    | Some label -> label
0c4d3e
+  in
0c4d3e
+  let live_offset = ref [] in
0c4d3e
+  Reg.Set.iter
0c4d3e
+    (function
0c4d3e
+        {typ = Val; loc = Reg r} ->
0c4d3e
+          live_offset := (r lsl 1) + 1 :: !live_offset
0c4d3e
+      | {typ = Val; loc = Stack s} as reg ->
0c4d3e
+          live_offset := slot_offset s (register_class reg) :: !live_offset
0c4d3e
+      | {typ = Addr} as r ->
0c4d3e
+          Misc.fatal_error ("bad GC root " ^ Reg.name r)
0c4d3e
+      | _ -> ()
0c4d3e
+    )
0c4d3e
+    live;
0c4d3e
+  record_frame_descr ~label:lbl ~frame_size:(frame_size())
0c4d3e
+    ~live_offset:!live_offset ~raise_frame:raise_ dbg;
0c4d3e
+  lbl
0c4d3e
+
0c4d3e
+let record_frame ?label live raise_ dbg =
0c4d3e
+  let lbl = record_frame_label ?label live raise_ dbg in
0c4d3e
+  `{emit_label lbl}:\n`
0c4d3e
+
0c4d3e
+(* Record calls to the GC -- we've moved them out of the way *)
0c4d3e
+
0c4d3e
+type gc_call =
0c4d3e
+  { gc_lbl: label;                      (* Entry label *)
0c4d3e
+    gc_return_lbl: label;               (* Where to branch after GC *)
0c4d3e
+    gc_frame_lbl: label }               (* Label of frame descriptor *)
0c4d3e
+
0c4d3e
+let call_gc_sites = ref ([] : gc_call list)
0c4d3e
+
0c4d3e
+let emit_call_gc gc =
0c4d3e
+  `{emit_label gc.gc_lbl}:\n`;
0c4d3e
+  `	call	{emit_symbol "caml_call_gc"}\n`;
0c4d3e
+  `{emit_label gc.gc_frame_lbl}:\n`;
0c4d3e
+  `	j	{emit_label gc.gc_return_lbl}\n`
0c4d3e
+
0c4d3e
+(* Record calls to caml_ml_array_bound_error.
0c4d3e
+   In debug mode, we maintain one call to caml_ml_array_bound_error
0c4d3e
+   per bound check site.  Otherwise, we can share a single call. *)
0c4d3e
+
0c4d3e
+type bound_error_call =
0c4d3e
+  { bd_lbl: label;                      (* Entry label *)
0c4d3e
+    bd_frame_lbl: label }               (* Label of frame descriptor *)
0c4d3e
+
0c4d3e
+let bound_error_sites = ref ([] : bound_error_call list)
0c4d3e
+
0c4d3e
+let bound_error_label ?label dbg =
0c4d3e
+  if !Clflags.debug || !bound_error_sites = [] then begin
0c4d3e
+    let lbl_bound_error = new_label() in
0c4d3e
+    let lbl_frame = record_frame_label ?label Reg.Set.empty false dbg in
0c4d3e
+    bound_error_sites :=
0c4d3e
+      { bd_lbl = lbl_bound_error;
0c4d3e
+        bd_frame_lbl = lbl_frame } :: !bound_error_sites;
0c4d3e
+    lbl_bound_error
0c4d3e
+  end else
0c4d3e
+    let bd = List.hd !bound_error_sites in
0c4d3e
+    bd.bd_lbl
0c4d3e
+
0c4d3e
+let emit_call_bound_error bd =
0c4d3e
+  `{emit_label bd.bd_lbl}:\n`;
0c4d3e
+  `	call	{emit_symbol "caml_ml_array_bound_error"}\n`;
0c4d3e
+  `{emit_label bd.bd_frame_lbl}:\n`
0c4d3e
+
0c4d3e
+(* Record floating-point literals *)
0c4d3e
+
0c4d3e
+let float_literals = ref ([] : (int64 * int) list)
0c4d3e
+
0c4d3e
+(* Names for various instructions *)
0c4d3e
+
0c4d3e
+let name_for_intop = function
0c4d3e
+  | Iadd  -> "add"
0c4d3e
+  | Isub  -> "sub"
0c4d3e
+  | Imul  -> "mul"
0c4d3e
+  | Imulh -> "mulh"
0c4d3e
+  | Idiv  -> "div"
0c4d3e
+  | Iand  -> "and"
0c4d3e
+  | Ior   -> "or"
0c4d3e
+  | Ixor  -> "xor"
0c4d3e
+  | Ilsl  -> "sll"
0c4d3e
+  | Ilsr  -> "srl"
0c4d3e
+  | Iasr  -> "sra"
0c4d3e
+  | Imod  -> "rem"
0c4d3e
+  | _ -> fatal_error "Emit.Intop"
0c4d3e
+
0c4d3e
+let name_for_intop_imm = function
0c4d3e
+  | Iadd -> "addi"
0c4d3e
+  | Iand -> "andi"
0c4d3e
+  | Ior  -> "ori"
0c4d3e
+  | Ixor -> "xori"
0c4d3e
+  | Ilsl -> "slli"
0c4d3e
+  | Ilsr -> "srli"
0c4d3e
+  | Iasr -> "srai"
0c4d3e
+  | _ -> fatal_error "Emit.Intop_imm"
0c4d3e
+
0c4d3e
+let name_for_floatop1 = function
0c4d3e
+  | Inegf -> "fneg.d"
0c4d3e
+  | Iabsf -> "fabs.d"
0c4d3e
+  | _ -> fatal_error "Emit.Iopf1"
0c4d3e
+
0c4d3e
+let name_for_floatop2 = function
0c4d3e
+  | Iaddf -> "fadd.d"
0c4d3e
+  | Isubf -> "fsub.d"
0c4d3e
+  | Imulf -> "fmul.d"
0c4d3e
+  | Idivf -> "fdiv.d"
0c4d3e
+  | _ -> fatal_error "Emit.Iopf2"
0c4d3e
+
0c4d3e
+let name_for_specific = function
0c4d3e
+  | Imultaddf false -> "fmadd.d"
0c4d3e
+  | Imultaddf true  -> "fnmadd.d"
0c4d3e
+  | Imultsubf false -> "fmsub.d"
0c4d3e
+  | Imultsubf true  -> "fnmsub.d"
0c4d3e
+
0c4d3e
+(* Name of current function *)
0c4d3e
+let function_name = ref ""
0c4d3e
+
0c4d3e
+(* Entry point for tail recursive calls *)
0c4d3e
+let tailrec_entry_point = ref 0
0c4d3e
+
0c4d3e
+(* Output the assembly code for an instruction *)
0c4d3e
+
0c4d3e
+let emit_instr i =
0c4d3e
+  match i.desc with
0c4d3e
+    Lend -> ()
0c4d3e
+  | Lop(Imove | Ispill | Ireload) ->
0c4d3e
+      let src = i.arg.(0) and dst = i.res.(0) in
0c4d3e
+      if src.loc <> dst.loc then begin
0c4d3e
+        match (src, dst) with
0c4d3e
+        | {loc = Reg _; typ = (Val | Int | Addr)}, {loc = Reg _} ->
0c4d3e
+            `	mv      {emit_reg dst}, {emit_reg src}\n`
0c4d3e
+        | {loc = Reg _; typ = Float}, {loc = Reg _; typ = Float} ->
0c4d3e
+            `	fmv.d   {emit_reg dst}, {emit_reg src}\n`
0c4d3e
+        | {loc = Reg _; typ = (Val | Int | Addr)}, {loc = Stack s} ->
0c4d3e
+            let ofs = slot_offset s (register_class dst) in
0c4d3e
+            emit_store stg src ofs
0c4d3e
+        | {loc = Reg _; typ = Float}, {loc = Stack s} ->
0c4d3e
+            let ofs = slot_offset s (register_class dst) in
0c4d3e
+            emit_store "fsd" src ofs
0c4d3e
+        | {loc = Stack s; typ = (Val | Int | Addr)}, {loc = Reg _} ->
0c4d3e
+            let ofs = slot_offset s (register_class src) in
0c4d3e
+            emit_load lg dst ofs
0c4d3e
+        | {loc = Stack s; typ = Float}, {loc = Reg _} ->
0c4d3e
+            let ofs = slot_offset s (register_class src) in
0c4d3e
+            emit_load "fld" dst ofs
0c4d3e
+        | _ ->
0c4d3e
+            fatal_error "Emit: Imove"
0c4d3e
+      end
0c4d3e
+  | Lop(Iconst_int n) ->
0c4d3e
+      `	li	{emit_reg i.res.(0)}, {emit_nativeint n}\n`
0c4d3e
+  | Lop(Iconst_float f) ->
0c4d3e
+      let lbl = new_label() in
0c4d3e
+      float_literals := (f, lbl) :: !float_literals;
0c4d3e
+      `	fld	{emit_reg i.res.(0)}, {emit_label lbl}, {emit_reg reg_tmp1}\n`
0c4d3e
+  | Lop(Iconst_symbol s) ->
0c4d3e
+      `	la	{emit_reg i.res.(0)}, {emit_symbol s}\n`
0c4d3e
+  | Lop(Icall_ind {label_after = label}) ->
0c4d3e
+      `	jalr	{emit_reg i.arg.(0)}\n`;
0c4d3e
+      record_frame ~label i.live false i.dbg
0c4d3e
+  | Lop(Icall_imm {func; label_after = label}) ->
0c4d3e
+      `	call	{emit_symbol func}\n`;
0c4d3e
+      record_frame ~label i.live false i.dbg
0c4d3e
+  | Lop(Itailcall_ind {label_after = _}) ->
0c4d3e
+      let n = frame_size() in
0c4d3e
+      if !contains_calls then reload_ra n;
0c4d3e
+      emit_stack_adjustment n;
0c4d3e
+      `	jr	{emit_reg i.arg.(0)}\n`
0c4d3e
+  | Lop(Itailcall_imm {func; label_after = _}) ->
0c4d3e
+      if func = !function_name then begin
0c4d3e
+        `	j	{emit_label !tailrec_entry_point}\n`
0c4d3e
+      end else begin
0c4d3e
+        let n = frame_size() in
0c4d3e
+        if !contains_calls then reload_ra n;
0c4d3e
+        emit_stack_adjustment n;
0c4d3e
+        `	tail	{emit_symbol func}\n`
0c4d3e
+      end
0c4d3e
+  | Lop(Iextcall{func; alloc = true; label_after = label}) ->
0c4d3e
+      `	la	{emit_reg reg_t2}, {emit_symbol func}\n`;
0c4d3e
+      `	call	{emit_symbol "caml_c_call"}\n`;
0c4d3e
+      record_frame ~label i.live false i.dbg
0c4d3e
+  | Lop(Iextcall{func; alloc = false; label_after = _}) ->
0c4d3e
+      `	call	{emit_symbol func}\n`
0c4d3e
+  | Lop(Istackoffset n) ->
0c4d3e
+      assert (n mod 16 = 0);
0c4d3e
+      emit_stack_adjustment (-n);
0c4d3e
+      stack_offset := !stack_offset + n
0c4d3e
+  | Lop(Iload(Single, Iindexed ofs)) ->
0c4d3e
+      `	flw	{emit_reg i.res.(0)}, {emit_int ofs}({emit_reg i.arg.(0)})\n`;
0c4d3e
+      `	fcvt.d.s	{emit_reg i.res.(0)}, {emit_reg i.res.(0)}\n`
0c4d3e
+  | Lop(Iload(chunk, Iindexed ofs)) ->
0c4d3e
+      let instr =
0c4d3e
+        match chunk with
0c4d3e
+        | Byte_unsigned -> "lbu"
0c4d3e
+        | Byte_signed -> "lb"
0c4d3e
+        | Sixteen_unsigned -> "lhu"
0c4d3e
+        | Sixteen_signed -> "lh"
0c4d3e
+        | Thirtytwo_unsigned -> if rv64 then "lwu" else "lw"
0c4d3e
+        | Thirtytwo_signed -> "lw"
0c4d3e
+        | Word_int | Word_val -> lg
0c4d3e
+        | Single -> assert false
0c4d3e
+        | Double | Double_u -> "fld"
0c4d3e
+      in
0c4d3e
+      `	{emit_string instr}	{emit_reg i.res.(0)}, {emit_int ofs}({emit_reg i.arg.(0)})\n`
0c4d3e
+  | Lop(Istore(Single, Iindexed ofs, _)) ->
0c4d3e
+      `	fmv.x.d	{emit_reg reg_tmp1}, {emit_reg i.arg.(0)}\n`;
0c4d3e
+      `	fcvt.s.d	{emit_reg i.arg.(0)}, {emit_reg i.arg.(0)}\n`;
0c4d3e
+      `	fsw	{emit_reg i.arg.(0)}, {emit_int ofs}({emit_reg i.arg.(1)})\n`;
0c4d3e
+      `	fmv.d.x	{emit_reg i.arg.(0)}, {emit_reg reg_tmp1}\n`
0c4d3e
+  | Lop(Istore(chunk, Iindexed ofs, _)) ->
0c4d3e
+      let instr =
0c4d3e
+        match chunk with
0c4d3e
+        | Byte_unsigned | Byte_signed -> "sb"
0c4d3e
+        | Sixteen_unsigned | Sixteen_signed -> "sh"
0c4d3e
+        | Thirtytwo_unsigned | Thirtytwo_signed -> "sw"
0c4d3e
+        | Word_int | Word_val -> stg
0c4d3e
+        | Single -> assert false
0c4d3e
+        | Double | Double_u -> "fsd"
0c4d3e
+      in
0c4d3e
+      `	{emit_string instr}	{emit_reg i.arg.(0)}, {emit_int ofs}({emit_reg i.arg.(1)})\n`
0c4d3e
+  | Lop(Ialloc {words = n; label_after_call_gc = label; _}) ->
0c4d3e
+      let lbl_frame_lbl = record_frame_label ?label i.live false i.dbg in
0c4d3e
+      let lbl_redo = new_label () in
0c4d3e
+      let lbl_call_gc = new_label () in
0c4d3e
+      `{emit_label lbl_redo}:\n`;
0c4d3e
+      `	addi	{emit_reg reg_alloc_ptr}, {emit_reg reg_alloc_ptr}, -{emit_int n}\n`;
0c4d3e
+      `	addi	{emit_reg i.res.(0)}, {emit_reg reg_alloc_ptr}, {emit_int size_addr}\n`;
0c4d3e
+      `	bltu	{emit_reg reg_alloc_ptr}, {emit_reg reg_alloc_lim}, {emit_label lbl_call_gc}\n`;
0c4d3e
+      call_gc_sites :=
0c4d3e
+        { gc_lbl = lbl_call_gc;
0c4d3e
+          gc_return_lbl = lbl_redo;
0c4d3e
+          gc_frame_lbl = lbl_frame_lbl } :: !call_gc_sites
0c4d3e
+  | Lop(Iintop(Icomp cmp)) ->
0c4d3e
+      begin match cmp with
0c4d3e
+      | Isigned Clt ->
0c4d3e
+          `	slt	{emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`
0c4d3e
+      | Isigned Cge ->
0c4d3e
+          `	slt	{emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
0c4d3e
+          `	xori	{emit_reg i.res.(0)}, {emit_reg i.res.(0)}, 1\n`;
0c4d3e
+      | Isigned Cgt ->
0c4d3e
+          `	slt	{emit_reg i.res.(0)}, {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`
0c4d3e
+      | Isigned Cle ->
0c4d3e
+          `	slt	{emit_reg i.res.(0)}, {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`;
0c4d3e
+          `	xori	{emit_reg i.res.(0)}, {emit_reg i.res.(0)}, 1\n`;
0c4d3e
+      | Isigned Ceq | Iunsigned Ceq ->
0c4d3e
+          `	sub	{emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
0c4d3e
+          `	seqz	{emit_reg i.res.(0)}, {emit_reg i.res.(0)}\n`
0c4d3e
+      | Isigned Cne | Iunsigned Cne ->
0c4d3e
+          `	sub	{emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
0c4d3e
+          `	snez	{emit_reg i.res.(0)}, {emit_reg i.res.(0)}\n`
0c4d3e
+      | Iunsigned Clt ->
0c4d3e
+          `	sltu	{emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`
0c4d3e
+      | Iunsigned Cge ->
0c4d3e
+          `	sltu	{emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
0c4d3e
+          `	xori	{emit_reg i.res.(0)}, {emit_reg i.res.(0)}, 1\n`;
0c4d3e
+      | Iunsigned Cgt ->
0c4d3e
+          `	sltu	{emit_reg i.res.(0)}, {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`
0c4d3e
+      | Iunsigned Cle ->
0c4d3e
+          `	sltu	{emit_reg i.res.(0)}, {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`;
0c4d3e
+          `	xori	{emit_reg i.res.(0)}, {emit_reg i.res.(0)}, 1\n`;
0c4d3e
+      end
0c4d3e
+  | Lop(Iintop (Icheckbound {label_after_error = label; _})) ->
0c4d3e
+      let lbl = bound_error_label ?label i.dbg in
0c4d3e
+      `	bleu	{emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, {emit_label lbl}\n`
0c4d3e
+  | Lop(Iintop op) ->
0c4d3e
+      let instr = name_for_intop op in
0c4d3e
+      `	{emit_string instr}	{emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`
0c4d3e
+  | Lop(Iintop_imm(Isub, n)) ->
0c4d3e
+      `	addi	{emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_int(-n)}\n`
0c4d3e
+  | Lop(Iintop_imm(Icomp _, _)) ->
0c4d3e
+      fatal_error "Emit.emit_instr (Iintop_imm (Icomp _, _))"
0c4d3e
+  | Lop(Iintop_imm(Icheckbound {label_after_error = label; _}, n)) ->
0c4d3e
+      let lbl = bound_error_label ?label i.dbg in
0c4d3e
+      `	li	{emit_reg reg_tmp1}, {emit_int n}\n`;
0c4d3e
+      `	bleu	{emit_reg i.arg.(0)}, {emit_reg reg_tmp1}, {emit_label lbl}\n`
0c4d3e
+  | Lop(Iintop_imm(op, n)) ->
0c4d3e
+      let instr = name_for_intop_imm op in
0c4d3e
+      `	{emit_string instr}	{emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_int n}\n`
0c4d3e
+  | Lop(Inegf | Iabsf as op) ->
0c4d3e
+      let instr = name_for_floatop1 op in
0c4d3e
+      `	{emit_string instr}	{emit_reg i.res.(0)}, {emit_reg i.arg.(0)}\n`
0c4d3e
+  | Lop(Iaddf | Isubf | Imulf | Idivf as op) ->
0c4d3e
+      let instr = name_for_floatop2 op in
0c4d3e
+      `	{emit_string instr}	{emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`
0c4d3e
+  | Lop(Ifloatofint) ->
0c4d3e
+      let name = if rv64 then "fcvt.d.l" else "fcvt.d.w" in
0c4d3e
+      `	{emit_string name}	{emit_reg i.res.(0)}, {emit_reg i.arg.(0)}\n`
0c4d3e
+  | Lop(Iintoffloat) ->
0c4d3e
+      let name = if rv64 then "fcvt.l.d" else "fcvt.w.d" in
0c4d3e
+      `	{emit_string name}	{emit_reg i.res.(0)}, {emit_reg i.arg.(0)}\n`
0c4d3e
+  | Lop(Ispecific sop) ->
0c4d3e
+      let instr = name_for_specific sop in
0c4d3e
+      `	{emit_string instr}	{emit_reg i.res.(0)}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, {emit_reg i.arg.(2)}\n`
0c4d3e
+  | Lop (Iname_for_debugger _) ->
0c4d3e
+      ()
0c4d3e
+  | Lreloadretaddr ->
0c4d3e
+      let n = frame_size () in
0c4d3e
+      reload_ra n
0c4d3e
+  | Lreturn ->
0c4d3e
+      let n = frame_size() in
0c4d3e
+      emit_stack_adjustment n;
0c4d3e
+      `	ret\n`
0c4d3e
+  | Llabel lbl ->
0c4d3e
+      `{emit_label lbl}:\n`
0c4d3e
+  | Lbranch lbl ->
0c4d3e
+      `	j	{emit_label lbl}\n`
0c4d3e
+  | Lcondbranch(tst, lbl) ->
0c4d3e
+      begin match tst with
0c4d3e
+      | Itruetest ->
0c4d3e
+          `	bnez	{emit_reg i.arg.(0)}, {emit_label lbl}\n`
0c4d3e
+      | Ifalsetest ->
0c4d3e
+          `	beqz	{emit_reg i.arg.(0)}, {emit_label lbl}\n`
0c4d3e
+      | Iinttest cmp ->
0c4d3e
+          let name = match cmp with
0c4d3e
+            | Iunsigned Ceq | Isigned Ceq -> "beq"
0c4d3e
+            | Iunsigned Cne | Isigned Cne -> "bne"
0c4d3e
+            | Iunsigned Cle -> "bleu" | Isigned Cle -> "ble"
0c4d3e
+            | Iunsigned Cge -> "bgeu" | Isigned Cge -> "bge"
0c4d3e
+            | Iunsigned Clt -> "bltu" | Isigned Clt -> "blt"
0c4d3e
+            | Iunsigned Cgt -> "bgtu" | Isigned Cgt -> "bgt"
0c4d3e
+          in
0c4d3e
+          `	{emit_string name}	{emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}, {emit_label lbl}\n`
0c4d3e
+      | Iinttest_imm _ ->
0c4d3e
+          fatal_error "Emit.emit_instr (Iinttest_imm _)"
0c4d3e
+      | Ifloattest(cmp, neg) ->
0c4d3e
+          let neg = match cmp with
0c4d3e
+            | Ceq -> `	feq.d	{emit_reg reg_tmp1}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`; neg
0c4d3e
+            | Cne -> `	feq.d	{emit_reg reg_tmp1}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`; not neg
0c4d3e
+            | Clt -> `	flt.d	{emit_reg reg_tmp1}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`; neg
0c4d3e
+            | Cgt -> `	flt.d	{emit_reg reg_tmp1}, {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`; neg
0c4d3e
+            | Cle -> `	fle.d	{emit_reg reg_tmp1}, {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`; neg
0c4d3e
+            | Cge -> `	fle.d	{emit_reg reg_tmp1}, {emit_reg i.arg.(1)}, {emit_reg i.arg.(0)}\n`; neg
0c4d3e
+          in
0c4d3e
+          if neg then
0c4d3e
+            `	beqz	{emit_reg reg_tmp1}, {emit_label lbl}\n`
0c4d3e
+          else
0c4d3e
+            `	bnez	{emit_reg reg_tmp1}, {emit_label lbl}\n`
0c4d3e
+      | Ioddtest ->
0c4d3e
+          `	andi	{emit_reg reg_tmp1}, {emit_reg i.arg.(0)}, 1\n`;
0c4d3e
+          `	bnez	{emit_reg reg_tmp1}, {emit_label lbl}\n`
0c4d3e
+      | Ieventest ->
0c4d3e
+          `	andi	{emit_reg reg_tmp1}, {emit_reg i.arg.(0)}, 1\n`;
0c4d3e
+          `	beqz	{emit_reg reg_tmp1}, {emit_label lbl}\n`
0c4d3e
+      end
0c4d3e
+  | Lcondbranch3(lbl0, lbl1, lbl2) ->
0c4d3e
+      `	addi	{emit_reg reg_tmp1}, {emit_reg i.arg.(0)}, -1\n`;
0c4d3e
+      begin match lbl0 with
0c4d3e
+      | None -> ()
0c4d3e
+      | Some lbl -> `	bltz	{emit_reg reg_tmp1}, {emit_label lbl}\n`
0c4d3e
+      end;
0c4d3e
+      begin match lbl1 with
0c4d3e
+      | None -> ()
0c4d3e
+      | Some lbl -> `	beqz	{emit_reg reg_tmp1}, {emit_label lbl}\n`
0c4d3e
+      end;
0c4d3e
+      begin match lbl2 with
0c4d3e
+      | None -> ()
0c4d3e
+      | Some lbl -> `	bgtz	{emit_reg reg_tmp1}, {emit_label lbl}\n`
0c4d3e
+      end
0c4d3e
+  | Lswitch jumptbl -> (* FIXME FIXME ? *)
0c4d3e
+      let lbl = new_label() in
0c4d3e
+      `	la	{emit_reg reg_tmp1}, {emit_label lbl}\n`;
0c4d3e
+      `	slli	{emit_reg reg_tmp2}, {emit_reg i.arg.(0)}, 2\n`;
0c4d3e
+      `	add	{emit_reg reg_tmp1}, {emit_reg reg_tmp1}, {emit_reg reg_tmp2}\n`;
0c4d3e
+      `	jr	{emit_reg reg_tmp1}\n`;
0c4d3e
+      `{emit_label lbl}:\n`;
0c4d3e
+      for i = 0 to Array.length jumptbl - 1 do
0c4d3e
+        `	j	{emit_label jumptbl.(i)}\n`
0c4d3e
+      done
0c4d3e
+  | Lsetuptrap lbl ->
0c4d3e
+      `	addi	sp, sp, -16\n`;
0c4d3e
+      `	jal	{emit_label lbl}\n`
0c4d3e
+  | Lpushtrap ->
0c4d3e
+      stack_offset := !stack_offset + 16;
0c4d3e
+      `	{emit_string stg}	ra, {emit_int size_addr}(sp)\n`;
0c4d3e
+      `	{emit_string stg}	{emit_reg reg_trap}, 0(sp)\n`;
0c4d3e
+      `	mv	{emit_reg reg_trap}, sp\n`
0c4d3e
+  | Lpoptrap ->
0c4d3e
+      `	{emit_string lg}	{emit_reg reg_trap}, 0(sp)\n`;
0c4d3e
+      `	addi	sp, sp, 16\n`;
0c4d3e
+      stack_offset := !stack_offset - 16
0c4d3e
+  | Lraise k ->
0c4d3e
+      begin match !Clflags.debug, k with
0c4d3e
+      | true, Cmm.Raise_withtrace ->
0c4d3e
+          `	call	{emit_symbol "caml_raise_exn"}\n`;
0c4d3e
+          record_frame Reg.Set.empty true i.dbg
0c4d3e
+      | false, _
0c4d3e
+      | true, Cmm.Raise_notrace ->
0c4d3e
+          `	mv	sp, {emit_reg reg_trap}\n`;
0c4d3e
+          `	{emit_string lg}	{emit_reg reg_tmp1}, {emit_int size_addr}(sp)\n`;
0c4d3e
+          `	{emit_string lg}	{emit_reg reg_trap}, 0(sp)\n`;
0c4d3e
+          `	addi	sp, sp, 16\n`;
0c4d3e
+          `	jalr	{emit_reg reg_tmp1}\n`
0c4d3e
+      end
0c4d3e
+
0c4d3e
+(* Emit a sequence of instructions *)
0c4d3e
+
0c4d3e
+let rec emit_all = function
0c4d3e
+  | {desc = Lend} -> () | i -> emit_instr i; emit_all i.next
0c4d3e
+
0c4d3e
+(* Emission of a function declaration *)
0c4d3e
+
0c4d3e
+let fundecl fundecl =
0c4d3e
+  function_name := fundecl.fun_name;
0c4d3e
+  tailrec_entry_point := new_label();
0c4d3e
+  stack_offset := 0;
0c4d3e
+  call_gc_sites := [];
0c4d3e
+  bound_error_sites := [];
0c4d3e
+  float_literals := [];
0c4d3e
+  `	.globl	{emit_symbol fundecl.fun_name}\n`;
0c4d3e
+  `	.type	{emit_symbol fundecl.fun_name}, @function\n`;
0c4d3e
+  `	{emit_string code_space}\n`;
0c4d3e
+  `	.align	2\n`;
0c4d3e
+  `{emit_symbol fundecl.fun_name}:\n`;
0c4d3e
+  let n = frame_size() in
0c4d3e
+  emit_stack_adjustment (-n);
0c4d3e
+  if !contains_calls then store_ra n;
0c4d3e
+  `{emit_label !tailrec_entry_point}:\n`;
0c4d3e
+  emit_all fundecl.fun_body;
0c4d3e
+  List.iter emit_call_gc !call_gc_sites;
0c4d3e
+  List.iter emit_call_bound_error !bound_error_sites;
0c4d3e
+  `	.size	{emit_symbol fundecl.fun_name}, .-{emit_symbol fundecl.fun_name}\n`;
0c4d3e
+  (* Emit the float literals *)
0c4d3e
+  if !float_literals <> [] then begin
0c4d3e
+    `	{emit_string rodata_space}\n`;
0c4d3e
+    `	.align	3\n`;
0c4d3e
+    List.iter
0c4d3e
+      (fun (f, lbl) ->
0c4d3e
+        `{emit_label lbl}:\n`;
0c4d3e
+        if rv64
0c4d3e
+        then emit_float64_directive ".quad" f
0c4d3e
+        else emit_float64_split_directive ".long" f)
0c4d3e
+      !float_literals;
0c4d3e
+  end
0c4d3e
+
0c4d3e
+(* Emission of data *)
0c4d3e
+
0c4d3e
+let declare_global_data s =
0c4d3e
+  `	.globl	{emit_symbol s}\n`;
0c4d3e
+  `	.type	{emit_symbol s}, @object\n`
0c4d3e
+
0c4d3e
+let emit_item = function
0c4d3e
+  | Cglobal_symbol s ->
0c4d3e
+      declare_global_data s
0c4d3e
+  | Cdefine_symbol s ->
0c4d3e
+      `{emit_symbol s}:\n`;
0c4d3e
+  | Cint8 n ->
0c4d3e
+      `	.byte	{emit_int n}\n`
0c4d3e
+  | Cint16 n ->
0c4d3e
+      `	.short	{emit_int n}\n`
0c4d3e
+  | Cint32 n ->
0c4d3e
+      `	.long	{emit_nativeint n}\n`
0c4d3e
+  | Cint n ->
0c4d3e
+      `	{emit_string datag}	{emit_nativeint n}\n`
0c4d3e
+  | Csingle f ->
0c4d3e
+      emit_float32_directive ".long" (Int32.bits_of_float f)
0c4d3e
+  | Cdouble f ->
0c4d3e
+      if rv64
0c4d3e
+      then emit_float64_directive ".quad" (Int64.bits_of_float f)
0c4d3e
+      else emit_float64_split_directive ".long" (Int64.bits_of_float f)
0c4d3e
+  | Csymbol_address s ->
0c4d3e
+      `	{emit_string datag}	{emit_symbol s}\n`
0c4d3e
+  | Cstring s ->
0c4d3e
+      emit_bytes_directive "	.byte	" s
0c4d3e
+  | Cskip n ->
0c4d3e
+      if n > 0 then `	.space	{emit_int n}\n`
0c4d3e
+  | Calign n ->
0c4d3e
+      `	.align	{emit_int (Misc.log2 n)}\n`
0c4d3e
+
0c4d3e
+let data l =
0c4d3e
+  `	{emit_string data_space}\n`;
0c4d3e
+  List.iter emit_item l
0c4d3e
+
0c4d3e
+(* Beginning / end of an assembly file *)
0c4d3e
+
0c4d3e
+let begin_assembly() =
0c4d3e
+  `	.file \"\"\n`; (* PR#7073 *)
0c4d3e
+  (* Emit the beginning of the segments *)
0c4d3e
+  let lbl_begin = Compilenv.make_symbol (Some "data_begin") in
0c4d3e
+  `	{emit_string data_space}\n`;
0c4d3e
+  declare_global_data lbl_begin;
0c4d3e
+  `{emit_symbol lbl_begin}:\n`;
0c4d3e
+  let lbl_begin = Compilenv.make_symbol (Some "code_begin") in
0c4d3e
+  `	{emit_string code_space}\n`;
0c4d3e
+  declare_global_data lbl_begin;
0c4d3e
+  `{emit_symbol lbl_begin}:\n`
0c4d3e
+
0c4d3e
+let end_assembly() =
0c4d3e
+  `	{emit_string code_space}\n`;
0c4d3e
+  let lbl_end = Compilenv.make_symbol (Some "code_end") in
0c4d3e
+  declare_global_data lbl_end;
0c4d3e
+  `{emit_symbol lbl_end}:\n`;
0c4d3e
+  `	.long	0\n`;
0c4d3e
+  `	{emit_string data_space}\n`;
0c4d3e
+  let lbl_end = Compilenv.make_symbol (Some "data_end") in
0c4d3e
+  declare_global_data lbl_end;
0c4d3e
+  `{emit_symbol lbl_end}:\n`;
0c4d3e
+  `	{emit_string datag}	0\n`;
0c4d3e
+  (* Emit the frame descriptors *)
0c4d3e
+  `	{emit_string rodata_space}\n`;
0c4d3e
+  let lbl = Compilenv.make_symbol (Some "frametable") in
0c4d3e
+  declare_global_data lbl;
0c4d3e
+  `{emit_symbol lbl}:\n`;
0c4d3e
+  emit_frames
0c4d3e
+    { efa_code_label = (fun l -> `	{emit_string datag}	{emit_label l}\n`);
0c4d3e
+      efa_data_label = (fun l -> `	{emit_string datag}	{emit_label l}\n`);
0c4d3e
+      efa_16 = (fun n -> `	.short	{emit_int n}\n`);
0c4d3e
+      efa_32 = (fun n -> `	.long	{emit_int32 n}\n`);
0c4d3e
+      efa_word = (fun n -> `	{emit_string datag}	{emit_int n}\n`);
0c4d3e
+      efa_align = (fun n -> `	.align	{emit_int (Misc.log2 n)}\n`);
0c4d3e
+      efa_label_rel = (fun lbl ofs ->
0c4d3e
+                           `	.long	({emit_label lbl} - .) + {emit_int32 ofs}\n`);
0c4d3e
+      efa_def_label = (fun l -> `{emit_label l}:\n`);
0c4d3e
+      efa_string = (fun s -> emit_bytes_directive "	.byte	" (s ^ "\000"))
0c4d3e
+     }
0c4d3e
diff --git a/asmcomp/riscv/proc.ml b/asmcomp/riscv/proc.ml
0c4d3e
new file mode 100644
0c4d3e
index 000000000..c0b0dcdb8
0c4d3e
--- /dev/null
0c4d3e
+++ b/asmcomp/riscv/proc.ml
0c4d3e
@@ -0,0 +1,301 @@
0c4d3e
+(***********************************************************************)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*                                OCaml                                *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*               Nicolas Ojeda Bar <n.oje.bar@gmail.com>               *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*  Copyright 2016 Institut National de Recherche en Informatique et   *)
0c4d3e
+(*  en Automatique.  All rights reserved.  This file is distributed    *)
0c4d3e
+(*  under the terms of the Q Public License version 1.0.               *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(***********************************************************************)
0c4d3e
+
0c4d3e
+(* Description of the RISC-V *)
0c4d3e
+
0c4d3e
+open Misc
0c4d3e
+open Cmm
0c4d3e
+open Reg
0c4d3e
+open Arch
0c4d3e
+open Mach
0c4d3e
+
0c4d3e
+(* Instruction selection *)
0c4d3e
+
0c4d3e
+let word_addressed = false
0c4d3e
+
0c4d3e
+(* Registers available for register allocation *)
0c4d3e
+
0c4d3e
+(* Integer register map:
0c4d3e
+    zero                     always zero
0c4d3e
+    ra                       return address
0c4d3e
+    sp, gp, tp               stack pointer, global pointer, thread pointer (preserved by C)
0c4d3e
+    a0 - a7      0 - 7       arguments/results
0c4d3e
+    s2 - s9      8 - 15      arguments/results (preserved by C)
0c4d3e
+    t2 - t6      16 - 20     temporary
0c4d3e
+    t0           21          temporary (used by assembler)
0c4d3e
+    t1           22          temporary (reserved for code gen)
0c4d3e
+    s0           23          frame pointer (preserved by C)
0c4d3e
+    s1           24          trap pointer (preserved by C)
0c4d3e
+    s10          25          allocation pointer (preserved by C)
0c4d3e
+    s11          26          allocation limit (preserved by C)
0c4d3e
+  Floating-point register map:
0c4d3e
+    ft0 - ft7    100 - 107   temporary
0c4d3e
+    fs0 - fs1    108 - 109   general purpose (preserved by C)
0c4d3e
+    fa0 - fa7    110 - 117   arguments/results
0c4d3e
+    fs2 - fs9    118 - 125   arguments/results (preserved by C)
0c4d3e
+    fs10 - fs11  126 - 127   general purpose (preserved by C)
0c4d3e
+    ft8 - ft11   128 - 131   temporary
0c4d3e
+*)
0c4d3e
+
0c4d3e
+let int_reg_name =
0c4d3e
+  [| "a0"; "a1"; "a2"; "a3"; "a4"; "a5"; "a6"; "a7";
0c4d3e
+     "s2"; "s3"; "s4"; "s5"; "s6"; "s7"; "s8"; "s9";
0c4d3e
+     "t2"; "t3"; "t4"; "t5"; "t6";
0c4d3e
+     "t0"; "t1";
0c4d3e
+     "s0"; "s1"; "s10"; "s11" |]
0c4d3e
+
0c4d3e
+let float_reg_name =
0c4d3e
+  [| "ft0"; "ft1"; "ft2"; "ft3"; "ft4"; "ft5"; "ft6"; "ft7";
0c4d3e
+     "fs0"; "fs1";
0c4d3e
+     "fa0"; "fa1"; "fa2"; "fa3"; "fa4"; "fa5"; "fa6"; "fa7";
0c4d3e
+     "fs2"; "fs3"; "fs4"; "fs5"; "fs6"; "fs7"; "fs8"; "fs9"; "fs10"; "fs11";
0c4d3e
+     "ft8"; "ft9"; "ft10"; "ft11" |]
0c4d3e
+
0c4d3e
+let num_register_classes = 2
0c4d3e
+
0c4d3e
+let register_class r =
0c4d3e
+  match r.typ with
0c4d3e
+  | Val | Int | Addr -> 0
0c4d3e
+  | Float -> 1
0c4d3e
+
0c4d3e
+let num_available_registers = [| 21; 32 |]
0c4d3e
+
0c4d3e
+let first_available_register = [| 0; 100 |]
0c4d3e
+
0c4d3e
+let register_name r =
0c4d3e
+  if r < 100 then int_reg_name.(r) else float_reg_name.(r - 100)
0c4d3e
+
0c4d3e
+let rotate_registers = true
0c4d3e
+
0c4d3e
+(* Representation of hard registers by pseudo-registers *)
0c4d3e
+
0c4d3e
+let hard_int_reg =
0c4d3e
+  let v = Array.make 27 Reg.dummy in
0c4d3e
+  for i = 0 to 26 do
0c4d3e
+    v.(i) <- Reg.at_location Int (Reg i)
0c4d3e
+  done;
0c4d3e
+  v
0c4d3e
+
0c4d3e
+let hard_float_reg =
0c4d3e
+  let v = Array.make 32 Reg.dummy in
0c4d3e
+  for i = 0 to 31 do
0c4d3e
+    v.(i) <- Reg.at_location Float (Reg(100 + i))
0c4d3e
+  done;
0c4d3e
+  v
0c4d3e
+
0c4d3e
+let all_phys_regs =
0c4d3e
+  Array.append hard_int_reg hard_float_reg
0c4d3e
+
0c4d3e
+let phys_reg n =
0c4d3e
+  if n < 100 then hard_int_reg.(n) else hard_float_reg.(n - 100)
0c4d3e
+
0c4d3e
+let stack_slot slot ty =
0c4d3e
+  Reg.at_location ty (Stack slot)
0c4d3e
+
0c4d3e
+(* Calling conventions *)
0c4d3e
+
0c4d3e
+let calling_conventions
0c4d3e
+    first_int last_int first_float last_float make_stack arg =
0c4d3e
+  let loc = Array.make (Array.length arg) Reg.dummy in
0c4d3e
+  let int = ref first_int in
0c4d3e
+  let float = ref first_float in
0c4d3e
+  let ofs = ref 0 in
0c4d3e
+  for i = 0 to Array.length arg - 1 do
0c4d3e
+    match arg.(i).typ with
0c4d3e
+    | Val | Int | Addr as ty ->
0c4d3e
+        if !int <= last_int then begin
0c4d3e
+          loc.(i) <- phys_reg !int;
0c4d3e
+          incr int
0c4d3e
+        end else begin
0c4d3e
+          loc.(i) <- stack_slot (make_stack !ofs) ty;
0c4d3e
+          ofs := !ofs + size_int
0c4d3e
+        end
0c4d3e
+    | Float ->
0c4d3e
+        if !float <= last_float then begin
0c4d3e
+          loc.(i) <- phys_reg !float;
0c4d3e
+          incr float
0c4d3e
+        end else begin
0c4d3e
+          loc.(i) <- stack_slot (make_stack !ofs) Float;
0c4d3e
+          ofs := !ofs + size_float
0c4d3e
+        end
0c4d3e
+  done;
0c4d3e
+  (loc, Misc.align !ofs 16) (* Keep stack 16-aligned. *)
0c4d3e
+
0c4d3e
+let incoming ofs = Incoming ofs
0c4d3e
+let outgoing ofs = Outgoing ofs
0c4d3e
+let not_supported _ = fatal_error "Proc.loc_results: cannot call"
0c4d3e
+
0c4d3e
+let max_arguments_for_tailcalls = 16
0c4d3e
+
0c4d3e
+let loc_spacetime_node_hole = Reg.dummy  (* Spacetime unsupported *)
0c4d3e
+
0c4d3e
+(* OCaml calling convention:
0c4d3e
+     first integer args in a0 .. a7, s2 .. s9
0c4d3e
+     first float args in fa0 .. fa7, fs2 .. fs9
0c4d3e
+     remaining args on stack.
0c4d3e
+   Return values in a0 .. a7, s2 .. s9 or fa0 .. fa7, fs2 .. fs9. *)
0c4d3e
+
0c4d3e
+let single_regs arg = Array.map (fun arg -> [| arg |]) arg
0c4d3e
+let ensure_single_regs res =
0c4d3e
+  Array.map (function
0c4d3e
+      | [| res |] -> res
0c4d3e
+      | _ -> failwith "proc.ensure_single_regs"
0c4d3e
+    ) res
0c4d3e
+
0c4d3e
+let loc_arguments arg =
0c4d3e
+  calling_conventions 0 15 110 125 outgoing arg
0c4d3e
+
0c4d3e
+let loc_parameters arg =
0c4d3e
+  let (loc, _ofs) =
0c4d3e
+    calling_conventions 0 15 110 125 incoming arg
0c4d3e
+  in
0c4d3e
+  loc
0c4d3e
+
0c4d3e
+let loc_results res =
0c4d3e
+  let (loc, _ofs) =
0c4d3e
+    calling_conventions 0 15 110 125 not_supported res
0c4d3e
+  in
0c4d3e
+  loc
0c4d3e
+
0c4d3e
+(* C calling convention:
0c4d3e
+     first integer args in a0 .. a7
0c4d3e
+     first float args in fa0 .. fa7
0c4d3e
+     remaining args on stack.
0c4d3e
+   Return values in a0 .. a1 or fa0 .. fa1. *)
0c4d3e
+
0c4d3e
+let external_calling_conventions
0c4d3e
+    first_int last_int first_float last_float make_stack arg =
0c4d3e
+  let loc = Array.make (Array.length arg) [| Reg.dummy |] in
0c4d3e
+  let int = ref first_int in
0c4d3e
+  let float = ref first_float in
0c4d3e
+  let ofs = ref 0 in
0c4d3e
+  for i = 0 to Array.length arg - 1 do
0c4d3e
+    match arg.(i) with
0c4d3e
+    | [| arg |] ->
0c4d3e
+        begin match arg.typ with
0c4d3e
+        | Val | Int | Addr as ty ->
0c4d3e
+            if !int <= last_int then begin
0c4d3e
+              loc.(i) <- [| phys_reg !int |];
0c4d3e
+              incr int;
0c4d3e
+              incr float;
0c4d3e
+            end else begin
0c4d3e
+              loc.(i) <- [| stack_slot (make_stack !ofs) ty |];
0c4d3e
+              ofs := !ofs + size_int
0c4d3e
+            end
0c4d3e
+        | Float ->
0c4d3e
+            if !float <= last_float then begin
0c4d3e
+              loc.(i) <- [| phys_reg !float |];
0c4d3e
+              incr float;
0c4d3e
+              incr int;
0c4d3e
+            end else begin
0c4d3e
+              loc.(i) <- [| stack_slot (make_stack !ofs) Float |];
0c4d3e
+              ofs := !ofs + size_float
0c4d3e
+            end
0c4d3e
+        end
0c4d3e
+    | [| arg1; arg2 |] ->
0c4d3e
+        (* Passing of 64-bit quantities to external functions on 32-bit
0c4d3e
+           platform. *)
0c4d3e
+        assert (size_int = 4);
0c4d3e
+        begin match arg1.typ, arg2.typ with
0c4d3e
+        | Int, Int ->
0c4d3e
+            int := Misc.align !int 2;
0c4d3e
+            if !int <= last_int - 1 then begin
0c4d3e
+              let reg_lower = phys_reg !int in
0c4d3e
+              let reg_upper = phys_reg (!int + 1) in
0c4d3e
+              loc.(i) <- [| reg_lower; reg_upper |];
0c4d3e
+              int := !int + 2
0c4d3e
+            end else begin
0c4d3e
+              let size_int64 = 8 in
0c4d3e
+              ofs := Misc.align !ofs size_int64;
0c4d3e
+              let ofs_lower = !ofs in
0c4d3e
+              let ofs_upper = !ofs + size_int in
0c4d3e
+              let stack_lower = stack_slot (make_stack ofs_lower) Int in
0c4d3e
+              let stack_upper = stack_slot (make_stack ofs_upper) Int in
0c4d3e
+              loc.(i) <- [| stack_lower; stack_upper |];
0c4d3e
+              ofs := !ofs + size_int64
0c4d3e
+            end
0c4d3e
+        | _ ->
0c4d3e
+            let f = function Int -> "I" | Addr -> "A" | Val -> "V" | Float -> "F" in
0c4d3e
+            fatal_error (Printf.sprintf "Proc.calling_conventions: bad register \
0c4d3e
+                                         type(s) for multi-register argument: %s, %s"
0c4d3e
+                           (f arg1.typ) (f arg2.typ))
0c4d3e
+        end
0c4d3e
+    | _ ->
0c4d3e
+        fatal_error "Proc.calling_conventions: bad number of register for \
0c4d3e
+                     multi-register argument"
0c4d3e
+  done;
0c4d3e
+  (loc, Misc.align !ofs 16) (* Keep stack 16-aligned. *)
0c4d3e
+
0c4d3e
+let loc_external_arguments arg =
0c4d3e
+  external_calling_conventions 0 7 110 117 outgoing arg
0c4d3e
+
0c4d3e
+let loc_external_results res =
0c4d3e
+  let (loc, _ofs) =
0c4d3e
+    external_calling_conventions 0 1 110 111 not_supported (single_regs res)
0c4d3e
+  in
0c4d3e
+  ensure_single_regs loc
0c4d3e
+
0c4d3e
+(* Exceptions are in GPR 3 *)
0c4d3e
+
0c4d3e
+let loc_exn_bucket = phys_reg 0
0c4d3e
+
0c4d3e
+(* Volatile registers: none *)
0c4d3e
+
0c4d3e
+let regs_are_volatile _ = false
0c4d3e
+
0c4d3e
+(* Registers destroyed by operations *)
0c4d3e
+
0c4d3e
+let destroyed_at_c_call =
0c4d3e
+  Array.of_list(List.map phys_reg
0c4d3e
+    [0; 1; 2; 3; 4; 5; 6; 7; 16; 17; 18; 19; 20; (* 21; 22; *)
0c4d3e
+     100; 101; 102; 103; 104; 105; 106; 107; 110; 111; 112; 113; 114; 115; 116;
0c4d3e
+     117; 128; 129; 130; 131])
0c4d3e
+
0c4d3e
+let destroyed_at_oper = function
0c4d3e
+  | Iop(Icall_ind _ | Icall_imm _ | Iextcall{alloc = true; _}) -> all_phys_regs
0c4d3e
+  | Iop(Iextcall{alloc = false; _}) -> destroyed_at_c_call
0c4d3e
+  | _ -> [||]
0c4d3e
+
0c4d3e
+let destroyed_at_raise = all_phys_regs
0c4d3e
+
0c4d3e
+(* Maximal register pressure *)
0c4d3e
+
0c4d3e
+let safe_register_pressure = function
0c4d3e
+  | Iextcall _ -> 15
0c4d3e
+  | _ -> 21
0c4d3e
+
0c4d3e
+let max_register_pressure = function
0c4d3e
+  | Iextcall _ -> [| 15; 18 |]
0c4d3e
+  | _ -> [| 21; 30 |]
0c4d3e
+
0c4d3e
+(* Pure operations (without any side effect besides updating their result
0c4d3e
+   registers). *)
0c4d3e
+
0c4d3e
+let op_is_pure = function
0c4d3e
+  | Icall_ind _ | Icall_imm _ | Itailcall_ind _ | Itailcall_imm _
0c4d3e
+  | Iextcall _ | Istackoffset _ | Istore _ | Ialloc _
0c4d3e
+  | Iintop(Icheckbound _) | Iintop_imm(Icheckbound _, _) -> false
0c4d3e
+  | Ispecific(Imultaddf _ | Imultsubf _) -> true
0c4d3e
+  | _ -> true
0c4d3e
+
0c4d3e
+(* Layout of the stack *)
0c4d3e
+
0c4d3e
+let num_stack_slots = [| 0; 0 |]
0c4d3e
+let contains_calls = ref false
0c4d3e
+
0c4d3e
+(* Calling the assembler *)
0c4d3e
+
0c4d3e
+let assemble_file infile outfile =
0c4d3e
+  Ccomp.command
0c4d3e
+    (Config.asm ^ " -o " ^ Filename.quote outfile ^ " " ^ Filename.quote infile)
0c4d3e
+
0c4d3e
+let init () = ()
0c4d3e
diff --git a/asmcomp/riscv/reload.ml b/asmcomp/riscv/reload.ml
0c4d3e
new file mode 100644
0c4d3e
index 000000000..85b970342
0c4d3e
--- /dev/null
0c4d3e
+++ b/asmcomp/riscv/reload.ml
0c4d3e
@@ -0,0 +1,16 @@
0c4d3e
+(***********************************************************************)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*                                OCaml                                *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*               Nicolas Ojeda Bar <n.oje.bar@gmail.com>               *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*  Copyright 2016 Institut National de Recherche en Informatique et   *)
0c4d3e
+(*  en Automatique.  All rights reserved.  This file is distributed    *)
0c4d3e
+(*  under the terms of the Q Public License version 1.0.               *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(***********************************************************************)
0c4d3e
+
0c4d3e
+(* Reloading for the RISC-V *)
0c4d3e
+
0c4d3e
+let fundecl f =
0c4d3e
+  (new Reloadgen.reload_generic)#fundecl f
0c4d3e
diff --git a/asmcomp/riscv/scheduling.ml b/asmcomp/riscv/scheduling.ml
0c4d3e
new file mode 100644
0c4d3e
index 000000000..e436be1cc
0c4d3e
--- /dev/null
0c4d3e
+++ b/asmcomp/riscv/scheduling.ml
0c4d3e
@@ -0,0 +1,19 @@
0c4d3e
+(***********************************************************************)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*                                OCaml                                *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*               Nicolas Ojeda Bar <n.oje.bar@gmail.com>               *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*  Copyright 2016 Institut National de Recherche en Informatique et   *)
0c4d3e
+(*  en Automatique.  All rights reserved.  This file is distributed    *)
0c4d3e
+(*  under the terms of the Q Public License version 1.0.               *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(***********************************************************************)
0c4d3e
+
0c4d3e
+(* Instruction scheduling for the RISC-V *)
0c4d3e
+
0c4d3e
+let _ = let module M = Schedgen in () (* to create a dependency *)
0c4d3e
+
0c4d3e
+(* Scheduling is turned off. *)
0c4d3e
+
0c4d3e
+let fundecl f = f
0c4d3e
diff --git a/asmcomp/riscv/selection.ml b/asmcomp/riscv/selection.ml
0c4d3e
new file mode 100644
0c4d3e
index 000000000..092ca88aa
0c4d3e
--- /dev/null
0c4d3e
+++ b/asmcomp/riscv/selection.ml
0c4d3e
@@ -0,0 +1,72 @@
0c4d3e
+(***********************************************************************)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*                                OCaml                                *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*               Nicolas Ojeda Bar <n.oje.bar@gmail.com>               *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(*  Copyright 2016 Institut National de Recherche en Informatique et   *)
0c4d3e
+(*  en Automatique.  All rights reserved.  This file is distributed    *)
0c4d3e
+(*  under the terms of the Q Public License version 1.0.               *)
0c4d3e
+(*                                                                     *)
0c4d3e
+(***********************************************************************)
0c4d3e
+
0c4d3e
+(* Instruction selection for the RISC-V processor *)
0c4d3e
+
0c4d3e
+open Cmm
0c4d3e
+open Arch
0c4d3e
+open Mach
0c4d3e
+
0c4d3e
+(* Instruction selection *)
0c4d3e
+
0c4d3e
+class selector = object (self)
0c4d3e
+
0c4d3e
+inherit Selectgen.selector_generic as super
0c4d3e
+
0c4d3e
+method is_immediate n = is_immediate n
0c4d3e
+
0c4d3e
+method select_addressing _ = function
0c4d3e
+  | Cop(Cadda, [arg; Cconst_int n], _) when self#is_immediate n ->
0c4d3e
+      (Iindexed n, arg)
0c4d3e
+  | Cop(Cadda, [arg1; Cop(Caddi, [arg2; Cconst_int n], _)], dbg) when self#is_immediate n ->
0c4d3e
+      (Iindexed n, Cop(Caddi, [arg1; arg2], dbg))
0c4d3e
+  | arg ->
0c4d3e
+      (Iindexed 0, arg)
0c4d3e
+
0c4d3e
+method! select_operation op args dbg =
0c4d3e
+  match (op, args) with
0c4d3e
+  (* RISC-V does not support immediate operands for multiply high *)
0c4d3e
+  | (Cmulhi, _) -> (Iintop Imulh, args)
0c4d3e
+  (* Recognize (neg-)mult-add and (neg-)mult-sub instructions *)
0c4d3e
+  | (Caddf, [Cop(Cmulf, [arg1; arg2], _); arg3])
0c4d3e
+  | (Caddf, [arg3; Cop(Cmulf, [arg1; arg2], _)]) ->
0c4d3e
+      (Ispecific (Imultaddf false), [arg1; arg2; arg3])
0c4d3e
+  | (Csubf, [Cop(Cmulf, [arg1; arg2], _); arg3]) ->
0c4d3e
+      (Ispecific (Imultsubf false), [arg1; arg2; arg3])
0c4d3e
+  | (Cnegf, [Cop(Csubf, [Cop(Cmulf, [arg1; arg2], _); arg3], _)]) ->
0c4d3e
+      (Ispecific (Imultsubf true), [arg1; arg2; arg3])
0c4d3e
+  | (Cnegf, [Cop(Caddf, [Cop(Cmulf, [arg1; arg2], _); arg3], _)]) ->
0c4d3e
+      (Ispecific (Imultaddf true), [arg1; arg2; arg3])
0c4d3e
+  (* RISC-V does not support immediate operands for comparison operators *)
0c4d3e
+  | (Ccmpi comp, args) -> (Iintop(Icomp (Isigned comp)), args)
0c4d3e
+  | (Ccmpa comp, args) -> (Iintop(Icomp (Iunsigned comp)), args)
0c4d3e
+  | (Cmuli, _) -> (Iintop Imul, args)
0c4d3e
+  | _ ->
0c4d3e
+      super#select_operation op args dbg
0c4d3e
+
0c4d3e
+(* Instruction selection for conditionals *)
0c4d3e
+
0c4d3e
+method! select_condition = function
0c4d3e
+  | Cop(Ccmpi cmp, args, _) ->
0c4d3e
+      (Iinttest(Isigned cmp), Ctuple args)
0c4d3e
+  | Cop(Ccmpa cmp, args, _) ->
0c4d3e
+      (Iinttest(Iunsigned cmp), Ctuple args)
0c4d3e
+  | Cop(Ccmpf cmp, args, _) ->
0c4d3e
+      (Ifloattest(cmp, false), Ctuple args)
0c4d3e
+  | Cop(Cand, [arg; Cconst_int 1], _) ->
0c4d3e
+      (Ioddtest, arg)
0c4d3e
+  | arg ->
0c4d3e
+      (Itruetest, arg)
0c4d3e
+
0c4d3e
+end
0c4d3e
+
0c4d3e
+let fundecl f = (new selector)#emit_fundecl f
0c4d3e
diff --git a/asmrun/riscv.S b/asmrun/riscv.S
0c4d3e
new file mode 100644
0c4d3e
index 000000000..a82048efc
0c4d3e
--- /dev/null
0c4d3e
+++ b/asmrun/riscv.S
0c4d3e
@@ -0,0 +1,424 @@
0c4d3e
+/***********************************************************************/
0c4d3e
+/*                                                                     */
0c4d3e
+/*                                OCaml                                */
0c4d3e
+/*                                                                     */
0c4d3e
+/*               Nicolas Ojeda Bar <n.oje.bar@gmail.com>               */
0c4d3e
+/*                                                                     */
0c4d3e
+/*  Copyright 1996 Institut National de Recherche en Informatique et   */
0c4d3e
+/*  en Automatique.  All rights reserved.  This file is distributed    */
0c4d3e
+/*  under the terms of the GNU Library General Public License, with    */
0c4d3e
+/*  the special exception on linking described in file ../LICENSE.     */
0c4d3e
+/*                                                                     */
0c4d3e
+/***********************************************************************/
0c4d3e
+
0c4d3e
+/* Asm part of the runtime system, RISC-V processor, 64-bit mode */
0c4d3e
+/* Must be preprocessed by cpp */
0c4d3e
+
0c4d3e
+#define TRAP_PTR s1
0c4d3e
+#define ALLOC_PTR s10
0c4d3e
+#define ALLOC_LIMIT s11
0c4d3e
+#define TMP0 t0
0c4d3e
+#define TMP1 t1
0c4d3e
+#define ARG t2
0c4d3e
+
0c4d3e
+#if defined(MODEL_riscv64)
0c4d3e
+#define store sd
0c4d3e
+#define load ld
0c4d3e
+#define WSZ 8
0c4d3e
+#else
0c4d3e
+#define store sw
0c4d3e
+#define load lw
0c4d3e
+#define WSZ 4
0c4d3e
+#endif
0c4d3e
+
0c4d3e
+#if defined(__PIC__)
0c4d3e
+        .option pic
0c4d3e
+#else
0c4d3e
+        .option nopic
0c4d3e
+#endif
0c4d3e
+
0c4d3e
+	.section	.text
0c4d3e
+/* Invoke the garbage collector. */
0c4d3e
+
0c4d3e
+	.globl	caml_system__code_begin
0c4d3e
+caml_system__code_begin:
0c4d3e
+
0c4d3e
+        .align	2
0c4d3e
+        .globl	caml_call_gc
0c4d3e
+        .type	caml_call_gc, @function
0c4d3e
+caml_call_gc:
0c4d3e
+        /* Record return address */
0c4d3e
+        store	ra, caml_last_return_address, TMP0
0c4d3e
+        /* Record lowest stack address */
0c4d3e
+        mv	TMP1, sp
0c4d3e
+        store	sp, caml_bottom_of_stack, TMP0
0c4d3e
+.Lcaml_call_gc:
0c4d3e
+	/* Set up stack space, saving return address */
0c4d3e
+        /* (1 reg for RA, 1 reg for FP, 21 allocatable int regs, 20 caller-save float regs) * 8 */
0c4d3e
+        /* + 1 for alignment */
0c4d3e
+        addi	sp, sp, -0x160
0c4d3e
+        mv	s0, sp
0c4d3e
+        store	ra, 0x8(sp)
0c4d3e
+        store	s0, 0x0(sp)
0c4d3e
+        /* Save allocatable integer registers on the stack,
0c4d3e
+           in the order given in proc.ml */
0c4d3e
+        store	a0, 0x10(sp)
0c4d3e
+        store	a1, 0x18(sp)
0c4d3e
+        store	a2, 0x20(sp)
0c4d3e
+        store	a3, 0x28(sp)
0c4d3e
+        store	a4, 0x30(sp)
0c4d3e
+        store	a5, 0x38(sp)
0c4d3e
+        store	a6, 0x40(sp)
0c4d3e
+        store	a7, 0x48(sp)
0c4d3e
+        store	s2, 0x50(sp)
0c4d3e
+        store	s3, 0x58(sp)
0c4d3e
+        store	s4, 0x60(sp)
0c4d3e
+        store	s5, 0x68(sp)
0c4d3e
+        store	s6, 0x70(sp)
0c4d3e
+        store	s7, 0x78(sp)
0c4d3e
+        store	s8, 0x80(sp)
0c4d3e
+        store	s9, 0x88(sp)
0c4d3e
+        store	t2, 0x90(sp)
0c4d3e
+        store	t3, 0x98(sp)
0c4d3e
+        store	t4, 0xa0(sp)
0c4d3e
+        store	t5, 0xa8(sp)
0c4d3e
+        store	t6, 0xb0(sp)
0c4d3e
+        /* Save caller-save floating-point registers on the stack
0c4d3e
+           (callee-saves are preserved by caml_garbage_collection) */
0c4d3e
+        fsd	ft0, 0xb8(sp)
0c4d3e
+        fsd	ft1, 0xc0(sp)
0c4d3e
+        fsd	ft2, 0xc8(sp)
0c4d3e
+        fsd	ft3, 0xd0(sp)
0c4d3e
+        fsd	ft4, 0xd8(sp)
0c4d3e
+        fsd	ft5, 0xe0(sp)
0c4d3e
+        fsd	ft6, 0xe8(sp)
0c4d3e
+        fsd	ft7, 0xf0(sp)
0c4d3e
+        fsd	fa0, 0xf8(sp)
0c4d3e
+        fsd	fa1, 0x100(sp)
0c4d3e
+        fsd	fa2, 0x108(sp)
0c4d3e
+        fsd	fa3, 0x110(sp)
0c4d3e
+        fsd	fa4, 0x118(sp)
0c4d3e
+        fsd	fa5, 0x120(sp)
0c4d3e
+        fsd	fa6, 0x128(sp)
0c4d3e
+        fsd	fa7, 0x130(sp)
0c4d3e
+        fsd	ft8, 0x138(sp)
0c4d3e
+        fsd	ft9, 0x140(sp)
0c4d3e
+        fsd	ft9, 0x148(sp)
0c4d3e
+        fsd	ft10, 0x150(sp)
0c4d3e
+        fsd	ft11, 0x158(sp)
0c4d3e
+        /* Store pointer to saved integer registers in caml_gc_regs */
0c4d3e
+        addi	TMP1, sp, 16
0c4d3e
+        store	TMP1, caml_gc_regs, TMP0
0c4d3e
+        /* Save current allocation pointer for debugging purposes */
0c4d3e
+        store	ALLOC_PTR, caml_young_ptr, TMP0
0c4d3e
+        /* Save trap pointer in case an exception is raised during GC */
0c4d3e
+        store	TRAP_PTR, caml_exception_pointer, TMP0
0c4d3e
+        /* Call the garbage collector */
0c4d3e
+        call	caml_garbage_collection
0c4d3e
+        /* Restore registers */
0c4d3e
+        load	a0, 0x10(sp)
0c4d3e
+        load	a1, 0x18(sp)
0c4d3e
+        load	a2, 0x20(sp)
0c4d3e
+        load	a3, 0x28(sp)
0c4d3e
+        load	a4, 0x30(sp)
0c4d3e
+        load	a5, 0x38(sp)
0c4d3e
+        load	a6, 0x40(sp)
0c4d3e
+        load	a7, 0x48(sp)
0c4d3e
+        load	s2, 0x50(sp)
0c4d3e
+        load	s3, 0x58(sp)
0c4d3e
+        load	s4, 0x60(sp)
0c4d3e
+        load	s5, 0x68(sp)
0c4d3e
+        load	s6, 0x70(sp)
0c4d3e
+        load	s7, 0x78(sp)
0c4d3e
+        load	s8, 0x80(sp)
0c4d3e
+        load	s9, 0x88(sp)
0c4d3e
+        load	t2, 0x90(sp)
0c4d3e
+        load	t3, 0x98(sp)
0c4d3e
+        load	t4, 0xa0(sp)
0c4d3e
+        load	t5, 0xa8(sp)
0c4d3e
+        load	t6, 0xb0(sp)
0c4d3e
+        fld	ft0, 0xb8(sp)
0c4d3e
+        fld	ft1, 0xc0(sp)
0c4d3e
+        fld	ft2, 0xc8(sp)
0c4d3e
+        fld	ft3, 0xd0(sp)
0c4d3e
+        fld	ft4, 0xd8(sp)
0c4d3e
+        fld	ft5, 0xe0(sp)
0c4d3e
+        fld	ft6, 0xe8(sp)
0c4d3e
+        fld	ft7, 0xf0(sp)
0c4d3e
+        fld	fa0, 0xf8(sp)
0c4d3e
+        fld	fa1, 0x100(sp)
0c4d3e
+        fld	fa2, 0x108(sp)
0c4d3e
+        fld	fa3, 0x110(sp)
0c4d3e
+        fld	fa4, 0x118(sp)
0c4d3e
+        fld	fa5, 0x120(sp)
0c4d3e
+        fld	fa6, 0x128(sp)
0c4d3e
+        fld	fa7, 0x130(sp)
0c4d3e
+        fld	ft8, 0x138(sp)
0c4d3e
+        fld	ft9, 0x140(sp)
0c4d3e
+        fld	ft9, 0x148(sp)
0c4d3e
+        fld	ft10, 0x150(sp)
0c4d3e
+        fld	ft11, 0x158(sp)
0c4d3e
+        /* Reload new allocation pointer and allocation limit */
0c4d3e
+        load	ALLOC_PTR, caml_young_ptr
0c4d3e
+        load	ALLOC_LIMIT, caml_young_limit
0c4d3e
+        /* Free stack space and return to caller */
0c4d3e
+        load	ra, 0x8(sp)
0c4d3e
+        load	s0, 0x0(sp)
0c4d3e
+        addi	sp, sp, 0x160
0c4d3e
+        ret
0c4d3e
+        .size	caml_call_gc, .-caml_call_gc
0c4d3e
+
0c4d3e
+/* Call a C function from OCaml */
0c4d3e
+/* Function to call is in ARG */
0c4d3e
+
0c4d3e
+        .align	2
0c4d3e
+	.globl	caml_c_call
0c4d3e
+	.type	caml_c_call, @function
0c4d3e
+caml_c_call:
0c4d3e
+        /* Preserve return address in callee-save register s2 */
0c4d3e
+        mv	s2, ra
0c4d3e
+        /* Record lowest stack address and return address */
0c4d3e
+        store	ra, caml_last_return_address, TMP0
0c4d3e
+        store	sp, caml_bottom_of_stack, TMP0
0c4d3e
+        /* Make the exception handler alloc ptr available to the C code */
0c4d3e
+        store	ALLOC_PTR, caml_young_ptr, TMP0
0c4d3e
+        store	TRAP_PTR, caml_exception_pointer, TMP0
0c4d3e
+        /* Call the function */
0c4d3e
+        jalr	ARG
0c4d3e
+        /* Reload alloc ptr and alloc limit */
0c4d3e
+        load	ALLOC_PTR, caml_young_ptr
0c4d3e
+        load	TRAP_PTR, caml_exception_pointer
0c4d3e
+        /* Return */
0c4d3e
+        jr	s2
0c4d3e
+        .size	caml_c_call, .-caml_c_call
0c4d3e
+
0c4d3e
+/* Raise an exception from OCaml */
0c4d3e
+        .align	2
0c4d3e
+	.globl	caml_raise_exn
0c4d3e
+	.type	caml_raise_exn, @function
0c4d3e
+caml_raise_exn:
0c4d3e
+        /* Test if backtrace is active */
0c4d3e
+        load	TMP1, caml_backtrace_active
0c4d3e
+        bnez	TMP1, 2f
0c4d3e
+1:      /* Cut stack at current trap handler */
0c4d3e
+        mv	sp, TRAP_PTR
0c4d3e
+        /* Pop previous handler and jump to it */
0c4d3e
+        load	TMP1, 8(sp)
0c4d3e
+        load	TRAP_PTR, 0(sp)
0c4d3e
+        addi	sp, sp, 16
0c4d3e
+        jr	TMP1
0c4d3e
+2:      /* Preserve exception bucket in callee-save register s2 */
0c4d3e
+        mv	s2, a0
0c4d3e
+        /* Stash the backtrace */
0c4d3e
+        mv	a1, ra
0c4d3e
+        mv	a2, sp
0c4d3e
+        mv	a3, TRAP_PTR
0c4d3e
+        call	caml_stash_backtrace
0c4d3e
+        /* Restore exception bucket and raise */
0c4d3e
+        mv	a0, s2
0c4d3e
+        j	1b
0c4d3e
+        .size	caml_raise_exn, .-caml_raise_exn
0c4d3e
+
0c4d3e
+	.globl	caml_reraise_exn
0c4d3e
+	.type	caml_reraise_exn, @function
0c4d3e
+
0c4d3e
+/* Raise an exception from C */
0c4d3e
+
0c4d3e
+        .align	2
0c4d3e
+	.globl	caml_raise_exception
0c4d3e
+	.type	caml_raise_exception, @function
0c4d3e
+caml_raise_exception:
0c4d3e
+	load	TRAP_PTR, caml_exception_pointer
0c4d3e
+        load	ALLOC_PTR, caml_young_ptr
0c4d3e
+        load	ALLOC_LIMIT, caml_young_limit
0c4d3e
+        load	TMP1, caml_backtrace_active
0c4d3e
+        bnez	TMP1, 2f
0c4d3e
+1:      /* Cut stack at current trap handler */
0c4d3e
+        mv	sp, TRAP_PTR
0c4d3e
+        load	TMP1, 8(sp)
0c4d3e
+        load	TRAP_PTR, 0(sp)
0c4d3e
+        addi	sp, sp, 16
0c4d3e
+        jr	TMP1
0c4d3e
+2:      /* Preserve exception bucket in callee-save register s2 */
0c4d3e
+        mv	s2, a0
0c4d3e
+        load	a1, caml_last_return_address
0c4d3e
+        load	a2, caml_bottom_of_stack
0c4d3e
+        mv	a3, TRAP_PTR
0c4d3e
+        call	caml_stash_backtrace
0c4d3e
+        mv	a0, s2
0c4d3e
+        j	1b
0c4d3e
+        .size	caml_raise_exception, .-caml_raise_exception
0c4d3e
+
0c4d3e
+/* Start the OCaml program */
0c4d3e
+
0c4d3e
+        .align	2
0c4d3e
+	.globl	caml_start_program
0c4d3e
+	.type	caml_start_program, @function
0c4d3e
+caml_start_program:
0c4d3e
+
0c4d3e
+        la	ARG, caml_program
0c4d3e
+        /* Code shared with caml_callback* */
0c4d3e
+        /* Address of OCaml code to call is in ARG */
0c4d3e
+        /* Arguments to the OCaml code are in a0 ... a7 */
0c4d3e
+.Ljump_to_caml:
0c4d3e
+	/* Set up stack frame and save callee-save registers */
0c4d3e
+	addi	sp, sp, -0xd0
0c4d3e
+	store	ra, 0xc0(sp)
0c4d3e
+	store	s0, 0x0(sp)
0c4d3e
+	store	s1, 0x8(sp)
0c4d3e
+	store	s2, 0x10(sp)
0c4d3e
+	store	s3, 0x18(sp)
0c4d3e
+	store	s4, 0x20(sp)
0c4d3e
+	store	s5, 0x28(sp)
0c4d3e
+	store	s6, 0x30(sp)
0c4d3e
+	store	s7, 0x38(sp)
0c4d3e
+	store	s8, 0x40(sp)
0c4d3e
+	store	s9, 0x48(sp)
0c4d3e
+	store	s10, 0x50(sp)
0c4d3e
+	store	s11, 0x58(sp)
0c4d3e
+	fsd	fs0, 0x60(sp)
0c4d3e
+	fsd	fs1, 0x68(sp)
0c4d3e
+	fsd	fs2, 0x70(sp)
0c4d3e
+	fsd	fs3, 0x78(sp)
0c4d3e
+	fsd	fs4, 0x80(sp)
0c4d3e
+	fsd	fs5, 0x88(sp)
0c4d3e
+	fsd	fs6, 0x90(sp)
0c4d3e
+	fsd	fs7, 0x98(sp)
0c4d3e
+	fsd	fs8, 0xa0(sp)
0c4d3e
+	fsd	fs9, 0xa8(sp)
0c4d3e
+	fsd	fs10, 0xb0(sp)
0c4d3e
+	fsd	fs11, 0xb8(sp)
0c4d3e
+	addi	sp, sp, -32
0c4d3e
+        /* Setup a callback link on the stack */
0c4d3e
+	load	TMP1, caml_bottom_of_stack
0c4d3e
+	store	TMP1, 0(sp)
0c4d3e
+	load	TMP1, caml_last_return_address
0c4d3e
+	store	TMP1, 8(sp)
0c4d3e
+	load	TMP1, caml_gc_regs
0c4d3e
+	store	TMP1, 16(sp)
0c4d3e
+	/* set up a trap frame */
0c4d3e
+	addi	sp, sp, -16
0c4d3e
+	load	TMP1, caml_exception_pointer
0c4d3e
+	store	TMP1, 0(sp)
0c4d3e
+	lla	TMP0, .Ltrap_handler
0c4d3e
+	store	TMP0, 8(sp)
0c4d3e
+	mv	TRAP_PTR, sp
0c4d3e
+	load	ALLOC_PTR, caml_young_ptr
0c4d3e
+	load	ALLOC_LIMIT, caml_young_limit
0c4d3e
+	store	x0, caml_last_return_address, TMP0
0c4d3e
+	jalr	ARG
0c4d3e
+.Lcaml_retaddr:	/* pop trap frame, restoring caml_exception_pointer */
0c4d3e
+	load	TMP1, 0(sp)
0c4d3e
+	store	TMP1, caml_exception_pointer, TMP0
0c4d3e
+	addi	sp, sp, 16
0c4d3e
+.Lreturn_result:	/* pop callback link, restoring global variables */
0c4d3e
+	load	TMP1, 0(sp)
0c4d3e
+	store	TMP1, caml_bottom_of_stack, TMP0
0c4d3e
+	load	TMP1, 8(sp)
0c4d3e
+	store	TMP1, caml_last_return_address, TMP0
0c4d3e
+	load	TMP1, 16(sp)
0c4d3e
+	store	TMP1, caml_gc_regs, TMP0
0c4d3e
+	addi	sp, sp, 32
0c4d3e
+	/* Update allocation pointer */
0c4d3e
+	store	ALLOC_PTR, caml_young_ptr, TMP0
0c4d3e
+	/* reload callee-save registers and return */
0c4d3e
+	load	ra, 0xc0(sp)
0c4d3e
+	load	s0, 0x0(sp)
0c4d3e
+	load	s1, 0x8(sp)
0c4d3e
+	load	s2, 0x10(sp)
0c4d3e
+	load	s3, 0x18(sp)
0c4d3e
+	load	s4, 0x20(sp)
0c4d3e
+	load	s5, 0x28(sp)
0c4d3e
+	load	s6, 0x30(sp)
0c4d3e
+	load	s7, 0x38(sp)
0c4d3e
+	load	s8, 0x40(sp)
0c4d3e
+	load	s9, 0x48(sp)
0c4d3e
+	load	s10, 0x50(sp)
0c4d3e
+	load	s11, 0x58(sp)
0c4d3e
+	fld	fs0, 0x60(sp)
0c4d3e
+	fld	fs1, 0x68(sp)
0c4d3e
+	fld	fs2, 0x70(sp)
0c4d3e
+	fld	fs3, 0x78(sp)
0c4d3e
+	fld	fs4, 0x80(sp)
0c4d3e
+	fld	fs5, 0x88(sp)
0c4d3e
+	fld	fs6, 0x90(sp)
0c4d3e
+	fld	fs7, 0x98(sp)
0c4d3e
+	fld	fs8, 0xa0(sp)
0c4d3e
+	fld	fs9, 0xa8(sp)
0c4d3e
+	fld	fs10, 0xb0(sp)
0c4d3e
+	fld	fs11, 0xb8(sp)
0c4d3e
+	addi	sp, sp, 0xd0
0c4d3e
+	ret
0c4d3e
+.Ltrap_handler:
0c4d3e
+	store	TRAP_PTR, caml_exception_pointer, TMP0
0c4d3e
+	ori	a0, a0, 2
0c4d3e
+	j	.Lreturn_result
0c4d3e
+	.size	caml_start_program, .-caml_start_program
0c4d3e
+
0c4d3e
+/* Callback from C to OCaml */
0c4d3e
+
0c4d3e
+        .align	2
0c4d3e
+	.globl	caml_callback_exn
0c4d3e
+	.type	caml_callback_exn, @function
0c4d3e
+caml_callback_exn:
0c4d3e
+        /* Initial shuffling of arguments (a0 = closure, a1 = first arg) */
0c4d3e
+        mv	TMP1, a0
0c4d3e
+        mv	a0, a1		/* a0 = first arg */
0c4d3e
+        mv	a1, TMP1	/* a1 = closure environment */
0c4d3e
+        load	ARG, 0(TMP1)	/* code pointer */
0c4d3e
+        j	.Ljump_to_caml
0c4d3e
+        .size	caml_callback_exn, .-caml_callback_exn
0c4d3e
+
0c4d3e
+        .align	2
0c4d3e
+	.globl	caml_callback2_exn
0c4d3e
+	.type	caml_callback2_exn, @function
0c4d3e
+caml_callback2_exn:
0c4d3e
+        /* Initial shuffling of arguments (a0 = closure, a1 = arg1, a2 = arg2) */
0c4d3e
+        mv	TMP1, a0
0c4d3e
+        mv	a0, a1
0c4d3e
+        mv	a1, a2
0c4d3e
+        mv	a2, TMP1
0c4d3e
+        la	ARG, caml_apply2
0c4d3e
+        j	.Ljump_to_caml
0c4d3e
+        .size	caml_callback2_exn, .-caml_callback2_exn
0c4d3e
+
0c4d3e
+        .align	2
0c4d3e
+	.globl	caml_callback3_exn
0c4d3e
+	.type	caml_callback3_exn, @function
0c4d3e
+caml_callback3_exn:
0c4d3e
+        /* Initial shuffling of argumnets */
0c4d3e
+        /* (a0 = closure, a1 = arg1, a2 = arg2, a3 = arg3) */
0c4d3e
+        mv	TMP1, a0
0c4d3e
+        mv	a0, a1
0c4d3e
+        mv	a1, a2
0c4d3e
+        mv	a2, a3
0c4d3e
+        mv	a3, TMP1
0c4d3e
+        la	ARG, caml_apply3
0c4d3e
+        j	.Ljump_to_caml
0c4d3e
+        .size	caml_callback3_exn, .-caml_callback3_exn
0c4d3e
+
0c4d3e
+        .align	2
0c4d3e
+	.globl	caml_ml_array_bound_error
0c4d3e
+	.type	caml_ml_array_bound_error, @function
0c4d3e
+caml_ml_array_bound_error:
0c4d3e
+        /* Load address of [caml_array_bound_error] in ARG */
0c4d3e
+        la	ARG, caml_array_bound_error
0c4d3e
+        /* Call that function */
0c4d3e
+        j	caml_c_call
0c4d3e
+
0c4d3e
+	.globl	caml_system__code_end
0c4d3e
+caml_system__code_end:
0c4d3e
+
0c4d3e
+/* GC roots for callback */
0c4d3e
+
0c4d3e
+	.section .data
0c4d3e
+        .align	3
0c4d3e
+	.globl	caml_system__frametable
0c4d3e
+	.type	caml_system__frametable, @object
0c4d3e
+caml_system__frametable:
0c4d3e
+	.quad	1		/* one descriptor */
0c4d3e
+	.quad	.Lcaml_retaddr  /* return address into callback */
0c4d3e
+	.short	-1		/* negative frame size => use callback link */
0c4d3e
+	.short	0		/* no roots */
0c4d3e
+	.align	3
0c4d3e
+	.size	caml_system__frametable, .-caml_system__frametable
0c4d3e
diff --git a/byterun/caml/stack.h b/byterun/caml/stack.h
0c4d3e
index 266863986..e198be0a6 100644
0c4d3e
--- a/byterun/caml/stack.h
0c4d3e
+++ b/byterun/caml/stack.h
0c4d3e
@@ -70,6 +70,11 @@
0c4d3e
 #define Callback_link(sp) ((struct caml_context *)((sp) + 16))
0c4d3e
 #endif
0c4d3e
 
0c4d3e
+#ifdef TARGET_riscv /* FIXME FIXME */
0c4d3e
+#define Saved_return_address(sp) *((intnat *)((sp) - 8))
0c4d3e
+#define Callback_link(sp) ((struct caml_context *)((sp) + 16))
0c4d3e
+#endif
0c4d3e
+
0c4d3e
 /* Structure of OCaml callback contexts */
0c4d3e
 
0c4d3e
 struct caml_context {
0c4d3e
diff --git a/config/gnu/config.guess b/config/gnu/config.guess
0c4d3e
index b79252d6b..8335398b2 100755
0c4d3e
--- a/config/gnu/config.guess
0c4d3e
+++ b/config/gnu/config.guess
0c4d3e
@@ -2,7 +2,7 @@
0c4d3e
 # Attempt to guess a canonical system name.
0c4d3e
 #   Copyright 1992-2013 Free Software Foundation, Inc.
0c4d3e
 
0c4d3e
-timestamp='2013-06-10'
0c4d3e
+timestamp='2016-10-23'
0c4d3e
 
0c4d3e
 # This file is free software; you can redistribute it and/or modify it
0c4d3e
 # under the terms of the GNU General Public License as published by
0c4d3e
@@ -1001,6 +1001,9 @@ EOF
0c4d3e
     ppcle:Linux:*:*)
0c4d3e
 	echo powerpcle-unknown-linux-${LIBC}
0c4d3e
 	exit ;;
0c4d3e
+    riscv*:Linux:*:*)
0c4d3e
+        echo ${UNAME_MACHINE}-unknown-linux
0c4d3e
+        exit ;;
0c4d3e
     s390:Linux:*:* | s390x:Linux:*:*)
0c4d3e
 	echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
0c4d3e
 	exit ;;
0c4d3e
diff --git a/configure b/configure
0c4d3e
index 53f45f85b..cf5a4a02a 100755
0c4d3e
--- a/configure
0c4d3e
+++ b/configure
0c4d3e
@@ -928,6 +928,7 @@ if $with_sharedlibs; then
0c4d3e
     arm*-*-freebsd*)              natdynlink=true;;
0c4d3e
     earm*-*-netbsd*)              natdynlink=true;;
0c4d3e
     aarch64-*-linux*)             natdynlink=true;;
0c4d3e
+    riscv*-*-linux*)              natdynlink=true;;
0c4d3e
   esac
0c4d3e
 fi
0c4d3e
 
0c4d3e
@@ -1004,6 +1005,8 @@ case "$target" in
0c4d3e
   x86_64-*-mingw*)              arch=amd64; system=mingw;;
0c4d3e
   aarch64-*-linux*)             arch=arm64; system=linux;;
0c4d3e
   x86_64-*-cygwin*)             arch=amd64; system=cygwin;;
0c4d3e
+  riscv32-*-linux*)             arch=riscv; model=riscv32; system=linux;;
0c4d3e
+  riscv64-*-linux*)             arch=riscv; model=riscv64; system=linux;;
0c4d3e
 esac
0c4d3e
 
0c4d3e
 # Some platforms exist both in 32-bit and 64-bit variants, not distinguished
0c4d3e
@@ -1062,7 +1065,7 @@ case "$arch,$system" in
0c4d3e
                   aspp="${TOOLPREF}cc -c";;
0c4d3e
   *,freebsd)      as="${TOOLPREF}as"
0c4d3e
                   aspp="${TOOLPREF}cc -c";;
0c4d3e
-  amd64,*|arm,*|arm64,*|i386,*|power,bsd*|power,netbsd)
0c4d3e
+  amd64,*|arm,*|arm64,*|i386,*|power,bsd*|power,netbsd|riscv,*)
0c4d3e
                   as="${TOOLPREF}as"
0c4d3e
                   case "$ccfamily" in
0c4d3e
                       clang-*)
0c4d3e
-- 
0c4d3e
2.17.1
0c4d3e