Repository: bytecodealliance/wasmtime
Pull request #8549 (2024-05-04 11:14:07)

c-api: use `--release` when MinSizeRel and RelWithDebInfo is used


Patch:
diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle
index 8a4d37959..6a6704b16 100644
--- a/cranelift/codegen/src/isa/riscv64/inst.isle
+++ b/cranelift/codegen/src/isa/riscv64/inst.isle
@@ -249,14 +249,6 @@
     ;; A dummy use, useful to keep a value alive.
        (DummyUse
         (reg Reg))
-    ;;;
-    (FloatRound
-      (op FloatRoundOP)
-      (rd WritableReg)
-      (int_tmp WritableReg)
-      (f_tmp WritableReg)
-      (rs Reg)
-      (ty Type))
 
     ;; popcnt  if target doesn't support extension B
     ;; use iteration to implement.
@@ -358,14 +350,6 @@
       (vstate VState))
 ))
 
-
-(type FloatRoundOP (enum
-  (Nearest)
-  (Ceil)
-  (Floor)
-  (Trunc)
-))
-
 (type AtomicOP (enum
   (LrW)
   (ScW)
@@ -1059,16 +1043,6 @@
 (decl pure has_zbs () bool)
 (extern constructor has_zbs has_zbs)
 
-(decl gen_float_round (FloatRoundOP Reg Type) Reg)
-(rule
-  (gen_float_round op rs ty)
-  (let
-    ((rd WritableReg (temp_writable_reg ty))
-      (tmp WritableXReg (temp_writable_xreg))
-      (tmp2 WritableFReg (temp_writable_freg))
-      (_ Unit (emit (MInst.FloatRound op rd tmp tmp2 rs ty))))
-    (writable_reg_to_reg rd)))
-
 
 ;;;; Instruction Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -2489,6 +2463,37 @@
 )
 
 
+;; Rounds a FReg by converting the value into an integer and back with a specified
+;; float rounding mode.
+(decl float_round_fcvt (Type FRM FReg) FReg)
+(rule (float_round_fcvt $F32 frm rs) (rv_fcvtsw frm (rv_fcvtws frm rs)))
+(rule (float_round_fcvt $F64 frm rs) (rv_fcvtdl frm (rv_fcvtld frm rs)))
+
+(decl gen_float_round (FRM FReg Type) FReg)
+(rule (gen_float_round frm rs ty)
+  (let (;; if rs is NaN/+-Infinity/+-Zero or if the exponent is larger than # of bits
+        ;; in mantissa, the result is the same as src, check for these cases first.
+        (max FReg (imm ty (float_int_max ty)))
+        (abs FReg (rv_fabs ty rs))
+        (exact XReg (rv_flt ty abs max))
+
+        ;; Manually round the value using the fcvt instructions
+        ;; to move the value to an integer register and back.
+        (fcvt FReg (float_round_fcvt ty frm rs))
+        ;; Restore the sign bit from the initial value.
+        (rounded FReg (rv_fsgnj ty fcvt rs))
+
+        ;; We want to return a arithmetic nan if the input is a canonical nan.
+        ;; Convert them by adding 0.0 to the input.
+        (float_zero FReg (gen_bitcast (zero_reg) (float_int_of_same_size ty) ty))
+        (corrected_nan FReg (rv_fadd ty (FRM.RNE) rs float_zero)))
+
+    ;; Check if the value cannot be rounded exactly and return the source input if so
+    (gen_select_freg (cmp_eqz exact) corrected_nan rounded)))
+
+
+
+
 (decl gen_stack_addr (StackSlot Offset32) Reg)
 (extern constructor gen_stack_addr gen_stack_addr)
 
diff --git a/cranelift/codegen/src/isa/riscv64/inst/args.rs b/cranelift/codegen/src/isa/riscv64/inst/args.rs
index 683a0f781..15bb09f0a 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/args.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/args.rs
@@ -409,43 +409,6 @@ impl FpuOPRR {
         }
     }
 
-    pub(crate) fn float_convert_2_int_op(from: Type, is_type_signed: bool, to: Type) -> Self {
-        let type_32 = to.bits() <= 32;
-        match from {
-            F32 => {
-                if is_type_signed {
-                    if type_32 {
-                        Self::FcvtWS
-                    } else {
-                        Self::FcvtLS
-                    }
-                } else {
-                    if type_32 {
-                        Self::FcvtWuS
-                    } else {
-                        Self::FcvtLuS
-                    }
-                }
-            }
-            F64 => {
-                if is_type_signed {
-                    if type_32 {
-                        Self::FcvtWD
-                    } else {
-                        Self::FcvtLD
-                    }
-                } else {
-                    if type_32 {
-                        Self::FcvtWuD
-                    } else {
-                        Self::FcvtLuD
-                    }
-                }
-            }
-            _ => unreachable!("from type:{}", from),
-        }
-    }
-
     pub(crate) fn op_code(self) -> u32 {
         match self {
             FpuOPRR::FsqrtS
@@ -1606,27 +1569,6 @@ impl Inst {
     }
 }
 
-impl FloatRoundOP {
-    pub(crate) fn op_name(self) -> &'static str {
-        match self {
-            FloatRoundOP::Nearest => "nearest",
-            FloatRoundOP::Ceil => "ceil",
-            FloatRoundOP::Floor => "floor",
-            FloatRoundOP::Trunc => "trunc",
-        }
-    }
-
-    pub(crate) fn to_frm(self) -> FRM {
-        match self {
-            FloatRoundOP::Nearest => FRM::RNE,
-            FloatRoundOP::Ceil => FRM::RUP,
-            FloatRoundOP::Floor => FRM::RDN,
-            FloatRoundOP::Trunc => FRM::RTZ,
-        }
-    }
-}
-
-///
 pub(crate) fn f32_cvt_to_int_bounds(signed: bool, out_bits: u32) -> (f32, f32) {
     match (signed, out_bits) {
         (true, 8) => (i8::min_value() as f32 - 1., i8::max_value() as f32 + 1.),
diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit.rs b/cranelift/codegen/src/isa/riscv64/inst/emit.rs
index 13cb03573..e0ceb87a2 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/emit.rs
@@ -141,35 +141,6 @@ impl Inst {
         }
     }
 
-    // emit a float is not a nan.
-    pub(crate) fn emit_not_nan(rd: Writable<Reg>, rs: Reg, ty: Type) -> Inst {
-        Inst::FpuRRR {
-            alu_op: if ty == F32 {
-                FpuOPRRR::FeqS
-            } else {
-                FpuOPRRR::FeqD
-            },
-            frm: FRM::RDN,
-            rd: rd,
-            rs1: rs,
-            rs2: rs,
-        }
-    }
-
-    pub(crate) fn emit_fabs(rd: Writable<Reg>, rs: Reg, ty: Type) -> Inst {
-        Inst::FpuRRR {
-            alu_op: if ty == F32 {
-                FpuOPRRR::FsgnjxS
-            } else {
-                FpuOPRRR::FsgnjxD
-            },
-            frm: FRM::RDN,
-            rd: rd,
-            rs1: rs,
-            rs2: rs,
-        }
-    }
-
     /// Returns Some(VState) if this insturction is expecting a specific vector state
     /// before emission.
     fn expected_vstate(&self) -> Option<&VState> {
@@ -219,7 +190,6 @@ impl Inst {
             | Inst::TrapIf { .. }
             | Inst::Unwind { .. }
             | Inst::DummyUse { .. }
-            | Inst::FloatRound { .. }
             | Inst::Popcnt { .. }
             | Inst::Cltz { .. }
             | Inst::Brev8 { .. }
@@ -2090,142 +2060,6 @@ impl Inst {
                 }
                 .emit(&[], sink, emit_info, state);
             }
-            &Inst::FloatRound {
-                op,
-                rd,
-                int_tmp,
-                f_tmp,
-                rs,
-                ty,
-            } => {
-                // this code is port from glibc ceil floor ... implementation.
-                let label_nan = sink.get_label();
-                let label_x = sink.get_label();
-                let label_jump_over = sink.get_label();
-                // check if is nan.
-                Inst::emit_not_nan(int_tmp, rs, ty).emit(&[], sink, emit_info, state);
-                Inst::CondBr {
-                    taken: CondBrTarget::Label(label_nan),
-                    not_taken: CondBrTarget::Fallthrough,
-                    kind: IntegerCompare {
-                        kind: IntCC::Equal,
-                        rs1: int_tmp.to_reg(),
-                        rs2: zero_reg(),
-                    },
-                }
-                .emit(&[], sink, emit_info, state);
-                fn max_value_need_round(ty: Type) -> u64 {
-                    match ty {
-                        F32 => {
-                            let x: u64 = 1 << f32::MANTISSA_DIGITS;
-                            let x = x as f32;
-                            let x = u32::from_le_bytes(x.to_le_bytes());
-                            x as u64
-                        }
-                        F64 => {
-                            let x: u64 = 1 << f64::MANTISSA_DIGITS;
-                            let x = x as f64;
-                            u64::from_le_bytes(x.to_le_bytes())
-                        }
-                        _ => unreachable!(),
-                    }
-                }
-                // load max value need to round.
-                if ty == F32 {
-                    Inst::load_fp_constant32(f_tmp, max_value_need_round(ty) as u32, &mut |_| {
-                        writable_spilltmp_reg()
-                    })
-                } else {
-                    Inst::load_fp_constant64(f_tmp, max_value_need_round(ty), &mut |_| {
-                        writable_spilltmp_reg()
-                    })
-                }
-                .into_iter()
-                .for_each(|i| i.emit(&[], sink, emit_info, state));
-
-                // get abs value.
-                Inst::emit_fabs(rd, rs, ty).emit(&[], sink, emit_info, state);
-
-                // branch if f_tmp < rd
-                Inst::FpuRRR {
-                    frm: FRM::RTZ,
-                    alu_op: if ty == F32 {
-                        FpuOPRRR::FltS
-                    } else {
-                        FpuOPRRR::FltD
-                    },
-                    rd: int_tmp,
-                    rs1: f_tmp.to_reg(),
-                    rs2: rd.to_reg(),
-                }
-                .emit(&[], sink, emit_info, state);
-
-                Inst::CondBr {
-                    taken: CondBrTarget::Label(label_x),
-                    not_taken: CondBrTarget::Fallthrough,
-                    kind: IntegerCompare {
-                        kind: IntCC::NotEqual,
-                        rs1: int_tmp.to_reg(),
-                        rs2: zero_reg(),
-                    },
-                }
-                .emit(&[], sink, emit_info, state);
-
-                //convert to int.
-                Inst::FpuRR {
-                    alu_op: FpuOPRR::float_convert_2_int_op(ty, true, I64),
-                    frm: op.to_frm(),
-                    rd: int_tmp,
-                    rs: rs,
-                }
-                .emit(&[], sink, emit_info, state);
-                //convert back.
-                Inst::FpuRR {
-                    alu_op: if ty == F32 {
-                        FpuOPRR::FcvtSL
-                    } else {
-                        FpuOPRR::FcvtDL
-                    },
-                    frm: op.to_frm(),
-                    rd,
-                    rs: int_tmp.to_reg(),
-                }
-                .emit(&[], sink, emit_info, state);
-                // copy sign.
-                Inst::FpuRRR {
-                    alu_op: if ty == F32 {
-                        FpuOPRRR::FsgnjS
-                    } else {
-                        FpuOPRRR::FsgnjD
-                    },
-                    frm: FRM::RNE,
-                    rd,
-                    rs1: rd.to_reg(),
-                    rs2: rs,
-                }
-                .emit(&[], sink, emit_info, state);
-                // jump over.
-                Inst::gen_jump(label_jump_over).emit(&[], sink, emit_info, state);
-                // here is nan.
-                sink.bind_label(label_nan, &mut state.ctrl_plane);
-                Inst::FpuRRR {
-                    alu_op: if ty == F32 {
-                        FpuOPRRR::FaddS
-                    } else {
-                        FpuOPRRR::FaddD
-                    },
-                    frm: FRM::RNE,
-                    rd: rd,
-                    rs1: rs,
-                    rs2: rs,
-                }
-                .emit(&[], sink, emit_info, state);
-                Inst::gen_jump(label_jump_over).emit(&[], sink, emit_info, state);
-                // here select origin x.
-                sink.bind_label(label_x, &mut state.ctrl_plane);
-                Inst::gen_move(rd, rs, ty).emit(&[], sink, emit_info, state);
-                sink.bind_label(label_jump_over, &mut state.ctrl_plane);
-            }
 
             &Inst::Popcnt {
                 sum,
diff --git a/cranelift/codegen/src/isa/riscv64/inst/mod.rs b/cranelift/codegen/src/isa/riscv64/inst/mod.rs
index ebf9ef9de..376a1cb5a 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/mod.rs
@@ -46,8 +46,8 @@ pub(crate) type VecU8 = Vec<u8>;
 // Instructions (top level): definition
 
 pub use crate::isa::riscv64::lower::isle::generated_code::{
-    AluOPRRI, AluOPRRR, AtomicOP, CsrImmOP, CsrRegOP, FClassResult, FFlagsException, FloatRoundOP,
-    FpuOPRR, FpuOPRRR, FpuOPRRRR, LoadOP, MInst as Inst, StoreOP, CSR, FRM,
+    AluOPRRI, AluOPRRR, AtomicOP, CsrImmOP, CsrRegOP, FClassResult, FFlagsException, FpuOPRR,
+    FpuOPRRR, FpuOPRRRR, LoadOP, MInst as Inst, StoreOP, CSR, FRM,
 };
 use crate::isa::riscv64::lower::isle::generated_code::{CjOp, MInst, VecAluOpRRImm5, VecAluOpRRR};
 
@@ -569,18 +569,6 @@ fn riscv64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) {
         Inst::DummyUse { reg } => {
             collector.reg_use(reg);
         }
-        Inst::FloatRound {
-            rd,
-            int_tmp,
-            f_tmp,
-            rs,
-            ..
-        } => {
-            collector.reg_use(rs);
-            collector.reg_early_def(int_tmp);
-            collector.reg_early_def(f_tmp);
-            collector.reg_early_def(rd);
-        }
         Inst::Popcnt {
             sum, step, rs, tmp, ..
         } => {
@@ -1018,28 +1006,6 @@ impl Inst {
                     guard_size, probe_count, tmp
                 )
             }
-            &Inst::FloatRound {
-                op,
-                rd,
-                int_tmp,
-                f_tmp,
-                rs,
-                ty,
-            } => {
-                let rs = format_reg(rs, allocs);
-                let int_tmp = format_reg(int_tmp.to_reg(), allocs);
-                let f_tmp = format_reg(f_tmp.to_reg(), allocs);
-                let rd = format_reg(rd.to_reg(), allocs);
-                format!(
-                    "{} {},{}##int_tmp={} f_tmp={} ty={}",
-                    op.op_name(),
-                    rd,
-                    rs,
-                    int_tmp,
-                    f_tmp,
-                    ty
-                )
-            }
             &Inst::AtomicStore { src, ty, p } => {
                 let src = format_reg(src, allocs);
                 let p = format_reg(p, allocs);
diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle
index bb8b14fc3..41cf71d47 100644
--- a/cranelift/codegen/src/isa/riscv64/lower.isle
+++ b/cranelift/codegen/src/isa/riscv64/lower.isle
@@ -2429,28 +2429,28 @@
 
 ;;;;;  Rules for `ceil`;;;;;;;;;
 (rule 0 (lower (has_type (ty_scalar_float ty) (ceil x)))
-  (gen_float_round (FloatRoundOP.Ceil) x ty))
+  (gen_float_round (FRM.RUP) x ty))
 
 (rule 1 (lower (has_type (ty_vec_fits_in_register ty) (ceil x)))
   (gen_vec_round x (FRM.RUP) ty))
 
 ;;;;;  Rules for `floor`;;;;;;;;;
 (rule 0 (lower (has_type (ty_scalar_float ty) (floor x)))
-  (gen_float_round (FloatRoundOP.Floor) x ty))
+  (gen_float_round (FRM.RDN) x ty))
 
 (rule 1 (lower (has_type (ty_vec_fits_in_register ty) (floor x)))
   (gen_vec_round x (FRM.RDN) ty))
 
 ;;;;;  Rules for `trunc`;;;;;;;;;
 (rule 0 (lower (has_type (ty_scalar_float ty) (trunc x)))
-  (gen_float_round (FloatRoundOP.Trunc) x ty))
+  (gen_float_round (FRM.RTZ) x ty))
 
 (rule 1 (lower (has_type (ty_vec_fits_in_register ty) (trunc x)))
   (gen_vec_round x (FRM.RTZ) ty))
 
 ;;;;;  Rules for `nearest`;;;;;;;;;
 (rule 0 (lower (has_type (ty_scalar_float ty) (nearest x)))
-  (gen_float_round (FloatRoundOP.Nearest) x ty))
+  (gen_float_round (FRM.RNE) x ty))
 
 (rule 1 (lower (has_type (ty_vec_fits_in_register ty) (nearest x)))
   (gen_vec_round x (FRM.RNE) ty))
diff --git a/crates/c-api/CMakeLists.txt b/crates/c-api/CMakeLists.txt
index 16b186f24..1bface845 100644
--- a/crates/c-api/CMakeLists.txt
+++ b/crates/c-api/CMakeLists.txt
@@ -5,7 +5,10 @@ set(WASMTIME_USER_CARGO_BUILD_OPTIONS "" CACHE STRING "Additional cargo flags (s
 option(BUILD_SHARED_LIBS "Build using shared libraries" OFF)
 option(WASMTIME_ALWAYS_BUILD "If cmake should always invoke cargo to build wasmtime" ON)
 
-if(CMAKE_BUILD_TYPE STREQUAL "Release")
+if(CMAKE_BUILD_TYPE STREQUAL "Release" OR
+	CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" OR
+	CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"
+)
 	set(WASMTIME_BUILD_TYPE_FLAG "--release")
 	set(WASMTIME_BUILD_TYPE "release")
 else()
diff --git a/crates/environ/src/compile/module_environ.rs b/crates/environ/src/compile/module_environ.rs
index af289cab1..dd36d2989 100644
--- a/crates/environ/src/compile/module_environ.rs
+++ b/crates/environ/src/compile/module_environ.rs
@@ -619,24 +619,11 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> {
                         } => {
                             let range = mk_range(&mut self.result.total_data)?;
                             let memory_index = MemoryIndex::from_u32(memory_index);
-                            let mut offset_expr_reader = offset_expr.get_binary_reader();
-                            let (base, offset) = match offset_expr_reader.read_operator()? {
-                                Operator::I32Const { value } => (None, value.unsigned().into()),
-                                Operator::I64Const { value } => (None, value.unsigned()),
-                                Operator::GlobalGet { global_index } => {
-                                    (Some(GlobalIndex::from_u32(global_index)), 0)
-                                }
-                                s => {
-                                    bail!(WasmError::Unsupported(format!(
-                                        "unsupported init expr in data section: {:?}",
-                                        s
-                                    )));
-                                }
-                            };
+                            let (offset, escaped) = ConstExpr::from_wasmparser(offset_expr)?;
+                            debug_assert!(escaped.is_empty());
 
                             initializers.push(MemoryInitializer {
                                 memory_index,
-                                base,
                                 offset,
                                 data: range,
                             });
@@ -1039,11 +1026,27 @@ impl ModuleTranslation<'_> {
                 segments: Vec::new(),
             });
         }
-        let mut idx = 0;
-        let ok = self.module.memory_initialization.init_memory(
-            &mut (),
-            InitMemory::CompileTime(&self.module),
-            |(), memory, init| {
+
+        struct InitMemoryAtCompileTime<'a> {
+            module: &'a Module,
+            info: &'a mut PrimaryMap<MemoryIndex, Memory>,
+            idx: usize,
+        }
+        impl InitMemory for InitMemoryAtCompileTime<'_> {
+            fn memory_size_in_pages(&mut self, memory_index: MemoryIndex) -> u64 {
+                self.module.memory_plans[memory_index].memory.minimum
+            }
+
+            fn eval_offset(&mut self, memory_index: MemoryIndex, expr: &ConstExpr) -> Option<u64> {
+                let mem64 = self.module.memory_plans[memory_index].memory.memory64;
+                match expr.ops() {
+                    &[ConstOp::I32Const(offset)] if !mem64 => Some(offset.unsigned().into()),
+                    &[ConstOp::I64Const(offset)] if mem64 => Some(offset.unsigned()),
+                    _ => None,
+                }
+            }
+
+            fn write(&mut self, memory: MemoryIndex, init: &StaticMemoryInitializer) -> bool {
                 // Currently `Static` only applies to locally-defined memories,
                 // so if a data segment references an imported memory then
                 // transitioning to a `Static` memory initializer is not
@@ -1051,18 +1054,26 @@ impl ModuleTranslation<'_> {
                 if self.module.defined_memory_index(memory).is_none() {
                     return false;
                 };
-                let info = &mut info[memory];
+                let info = &mut self.info[memory];
                 let data_len = u64::from(init.data.end - init.data.start);
                 if data_len > 0 {
                     info.data_size += data_len;
                     info.min_addr = info.min_addr.min(init.offset);
                     info.max_addr = info.max_addr.max(init.offset + data_len);
-                    info.segments.push((idx, init.clone()));
+                    info.segments.push((self.idx, init.clone()));
                 }
-                idx += 1;
+                self.idx += 1;
                 true
-            },
-        );
+            }
+        }
+        let ok = self
+            .module
+            .memory_initialization
+            .init_memory(&mut InitMemoryAtCompileTime {
+                idx: 0,
+                module: &self.module,
+                info: &mut info,
+            });
         if !ok {
             return;
         }
diff --git a/crates/environ/src/module.rs b/crates/environ/src/module.rs
index e25b481a1..36868e23a 100644
--- a/crates/environ/src/module.rs
+++ b/crates/environ/src/module.rs
@@ -102,10 +102,8 @@ impl MemoryPlan {
 pub struct MemoryInitializer {
     /// The index of a linear memory to initialize.
     pub memory_index: MemoryIndex,
-    /// Optionally, a global variable giving a base index.
-    pub base: Option<GlobalIndex>,
-    /// The offset to add to the base.
-    pub offset: u64,
+    /// The base offset to start this segment at.
+    pub offset: ConstExpr,
     /// The range of the data to write within the linear memory.
     ///
     /// This range indexes into a separately stored data section which will be
@@ -201,24 +199,6 @@ impl MemoryInitialization {
     /// only be called as part of initialization, however, as it's structured to
     /// allow learning about memory ahead-of-time at compile time possibly.
     ///
-    /// The various callbacks provided here are used to drive the smaller bits
-    /// of initialization, such as:
-    ///
-    /// * `get_cur_size_in_pages` - gets the current size, in wasm pages, of the
-    ///   memory specified. For compile-time purposes this would be the memory
-    ///   type's minimum size.
-    ///
-    /// * `get_global` - gets the value of the global specified. This is
-    ///   statically, via validation, a pointer to the global of the correct
-    ///   type (either u32 or u64 depending on the memory), but the value
-    ///   returned here is `u64`. A `None` value can be returned to indicate
-    ///   that the global's value isn't known yet.
-    ///
-    /// * `write` - a callback used to actually write data. This indicates that
-    ///   the specified memory must receive the specified range of data at the
-    ///   specified offset. This can internally return an false error if it
-    ///   wants to fail.
-    ///
     /// This function will return true if all memory initializers are processed
     /// successfully. If any initializer hits an error or, for example, a
     /// global value is needed but `None` is returned, then false will be
@@ -226,12 +206,7 @@ impl MemoryInitialization {
     /// question needs to be deferred to runtime, and at runtime this means
     /// that an invalid initializer has been found and a trap should be
     /// generated.
-    pub fn init_memory<T>(
-        &self,
-        state: &mut T,
-        init: InitMemory<'_, T>,
-        mut write: impl FnMut(&mut T, MemoryIndex, &StaticMemoryInitializer) -> bool,
-    ) -> bool {
+    pub fn init_memory(&self, state: &mut dyn InitMemory) -> bool {
         let initializers = match self {
             // Fall through below to the segmented memory one-by-one
             // initialization.
@@ -245,7 +220,7 @@ impl MemoryInitialization {
             MemoryInitialization::Static { map } => {
                 for (index, init) in map {
                     if let Some(init) = init {
-                        let result = write(state, index, init);
+                        let result = state.write(index, init);
                         if !result {
                             return result;
                         }
@@ -256,28 +231,18 @@ impl MemoryInitialization {
         };
 
         for initializer in initializers {
-            let MemoryInitializer {
+            let &MemoryInitializer {
                 memory_index,
-                base,
-                offset,
+                ref offset,
                 ref data,
-            } = *initializer;
+            } = initializer;
 
             // First up determine the start/end range and verify that they're
             // in-bounds for the initial size of the memory at `memory_index`.
             // Note that this can bail if we don't have access to globals yet
             // (e.g. this is a task happening before instantiation at
             // compile-time).
-            let base = match base {
-                Some(index) => match &init {
-                    InitMemory::Runtime {
-                        get_global_as_u64, ..
-                    } => get_global_as_u64(state, index),
-                    InitMemory::CompileTime(_) => return false,
-                },
-                None => 0,
-            };
-            let start = match base.checked_add(offset) {
+            let start = match state.eval_offset(memory_index, offset) {
                 Some(start) => start,
                 None => return false,
             };
@@ -287,13 +252,7 @@ impl MemoryInitialization {
                 None => return false,
             };
 
-            let cur_size_in_pages = match &init {
-                InitMemory::CompileTime(module) => module.memory_plans[memory_index].memory.minimum,
-                InitMemory::Runtime {
-                    memory_size_in_pages,
-                    ..
-                } => memory_size_in_pages(state, memory_index),
-            };
+            let cur_size_in_pages = state.memory_size_in_pages(memory_index);
 
             // Note that this `minimum` can overflow if `minimum` is
             // `1 << 48`, the maximum number of minimum pages for 64-bit
@@ -318,7 +277,7 @@ impl MemoryInitialization {
                 offset: start,
                 data: data.clone(),
             };
-            let result = write(state, memory_index, &init);
+            let result = state.write(memory_index, &init);
             if !result {
                 return result;
             }
@@ -328,25 +287,23 @@ impl MemoryInitialization {
     }
 }
 
-/// Argument to [`MemoryInitialization::init_memory`] indicating the current
-/// status of the instance.
-pub enum InitMemory<'a, T> {
-    /// This evaluation of memory initializers is happening at compile time.
-    /// This means that the current state of memories is whatever their initial
-    /// state is, and additionally globals are not available if data segments
-    /// have global offsets.
-    CompileTime(&'a Module),
-
-    /// Evaluation of memory initializers is happening at runtime when the
-    /// instance is available, and callbacks are provided to learn about the
-    /// instance's state.
-    Runtime {
-        /// Returns the size, in wasm pages, of the the memory specified.
-        memory_size_in_pages: &'a dyn Fn(&mut T, MemoryIndex) -> u64,
-        /// Returns the value of the global, as a `u64`. Note that this may
-        /// involve zero-extending a 32-bit global to a 64-bit number.
-        get_global_as_u64: &'a dyn Fn(&mut T, GlobalIndex) -> u64,
-    },
+/// The various callbacks provided here are used to drive the smaller bits of
+/// memory initialization.
+pub trait InitMemory {
+    /// Returns the size, in wasm pages, of the the memory specified. For
+    /// compile-time purposes this would be the memory type's minimum size.
+    fn memory_size_in_pages(&mut self, memory_index: MemoryIndex) -> u64;
+
+    /// Returns the value of the constant expression, as a `u64`. Note that
+    /// this may involve zero-extending a 32-bit global to a 64-bit number. May
+    /// return `None` to indicate that the expression involves a value which is
+    /// not available yet.
+    fn eval_offset(&mut self, memory_index: MemoryIndex, expr: &ConstExpr) -> Option<u64>;
+
+    /// A callback used to actually write data. This indicates that the
+    /// specified memory must receive the specified range of data at the
+    /// specified offset. This can return false on failure.
+    fn write(&mut self, memory_index: MemoryIndex, init: &StaticMemoryInitializer) -> bool;
 }
 
 /// Implementation styles for WebAssembly tables.
diff --git a/crates/wasmtime/src/runtime/vm/instance/allocator.rs b/crates/wasmtime/src/runtime/vm/instance/allocator.rs
index b5e701fc4..f2ff6dbfc 100644
--- a/crates/wasmtime/src/runtime/vm/instance/allocator.rs
+++ b/crates/wasmtime/src/runtime/vm/instance/allocator.rs
@@ -5,12 +5,12 @@ use crate::runtime::vm::memory::Memory;
 use crate::runtime::vm::mpk::ProtectionKey;
 use crate::runtime::vm::table::Table;
 use crate::runtime::vm::{CompiledModuleId, ModuleRuntimeInfo, Store, VMFuncRef, VMGcRef};
-use anyhow::{anyhow, bail, Result};
+use anyhow::{bail, Result};
 use std::{alloc, any::Any, mem, ptr, sync::Arc};
 use wasmtime_environ::{
     DefinedMemoryIndex, DefinedTableIndex, HostPtr, InitMemory, MemoryInitialization,
     MemoryInitializer, MemoryPlan, Module, PrimaryMap, TableInitialValue, TablePlan, Trap,
-    VMOffsets, WasmHeapTopType, WasmValType, WASM_PAGE_SIZE,
+    VMOffsets, WasmHeapTopType, WASM_PAGE_SIZE,
 };
 
 #[cfg(feature = "gc")]
@@ -623,36 +623,33 @@ fn initialize_tables(instance: &mut Instance, module: &Module) -> Result<()> {
     Ok(())
 }
 
-fn get_memory_init_start(init: &MemoryInitializer, instance: &mut Instance) -> Result<u64> {
-    match init.base {
-        Some(base) => {
-            let mem64 = instance.module().memory_plans[init.memory_index]
-                .memory
-                .memory64;
-            let val = unsafe {
-                let global = instance.defined_or_imported_global_ptr(base);
-                if mem64 {
-                    *(*global).as_u64()
-                } else {
-                    u64::from(*(*global).as_u32())
-                }
-            };
-
-            init.offset
-                .checked_add(val)
-                .ok_or_else(|| anyhow!("data segment global base overflows"))
+fn get_memory_init_start(
+    init: &MemoryInitializer,
+    instance: &mut Instance,
+    module: &Module,
+) -> Result<u64> {
+    let mem64 = instance.module().memory_plans[init.memory_index]
+        .memory
+        .memory64;
+    let mut context = ConstEvalContext::new(instance, module);
+    let mut const_evaluator = ConstExprEvaluator::default();
+    unsafe { const_evaluator.eval(&mut context, &init.offset) }.map(|v| {
+        if mem64 {
+            v.get_u64()
+        } else {
+            v.get_u32().into()
         }
-        None => Ok(init.offset),
-    }
+    })
 }
 
 fn check_memory_init_bounds(
     instance: &mut Instance,
+    module: &Module,
     initializers: &[MemoryInitializer],
 ) -> Result<()> {
     for init in initializers {
         let memory = instance.get_memory(init.memory_index);
-        let start = get_memory_init_start(init, instance)?;
+        let start = get_memory_init_start(init, instance, module)?;
         let end = usize::try_from(start)
             .ok()
             .and_then(|start| start.checked_add(init.data.len()));
@@ -671,21 +668,6 @@ fn check_memory_init_bounds(
 }
 
 fn initialize_memories(instance: &mut Instance, module: &Module) -> Result<()> {
-    let memory_size_in_pages = &|instance: &mut Instance, memory| {
-        (instance.get_memory(memory).current_length() as u64) / u64::from(WASM_PAGE_SIZE)
-    };
-
-    // Loads the `global` value and returns it as a `u64`, but sign-extends
-    // 32-bit globals which can be used as the base for 32-bit memories.
-    let get_global_as_u64 = &mut |instance: &mut Instance, global| unsafe {
-        let def = instance.defined_or_imported_global_ptr(global);
-        if module.globals[global].wasm_ty == WasmValType::I64 {
-            *(*def).as_u64()
-        } else {
-            u64::from(*(*def).as_u32())
-        }
-    };
-
     // Delegates to the `init_memory` method which is sort of a duplicate of
     // `instance.memory_init_segment` but is used at compile-time in other
     // contexts so is shared here to have only one method of memory
@@ -694,26 +676,52 @@ fn initialize_memories(instance: &mut Instance, module: &Module) -> Result<()> {
     // This call to `init_memory` notably implements all the bells and whistles
     // so errors only happen if an out-of-bounds segment is found, in which case
     // a trap is returned.
-    let ok = module.memory_initialization.init_memory(
-        instance,
-        InitMemory::Runtime {
-            memory_size_in_pages,
-            get_global_as_u64,
-        },
-        |instance, memory_index, init| {
+
+    struct InitMemoryAtInstantiation<'a> {
+        instance: &'a mut Instance,
+        module: &'a Module,
+        const_evaluator: ConstExprEvaluator,
+    }
+
+    impl InitMemory for InitMemoryAtInstantiation<'_> {
+        fn memory_size_in_pages(&mut self, memory: wasmtime_environ::MemoryIndex) -> u64 {
+            (self.instance.get_memory(memory).current_length() as u64) / u64::from(WASM_PAGE_SIZE)
+        }
+
+        fn eval_offset(
+            &mut self,
+            memory: wasmtime_environ::MemoryIndex,
+            expr: &wasmtime_environ::ConstExpr,
+        ) -> Option<u64> {
+            let mem64 = self.instance.module().memory_plans[memory].memory.memory64;
+            let mut context = ConstEvalContext::new(self.instance, self.module);
+            let val = unsafe { self.const_evaluator.eval(&mut context, expr) }
+                .expect("const expression should be valid");
+            Some(if mem64 {
+                val.get_u64()
+            } else {
+                val.get_u32().into()
+            })
+        }
+
+        fn write(
+            &mut self,
+            memory_index: wasmtime_environ::MemoryIndex,
+            init: &wasmtime_environ::StaticMemoryInitializer,
+        ) -> bool {
             // If this initializer applies to a defined memory but that memory
             // doesn't need initialization, due to something like copy-on-write
             // pre-initializing it via mmap magic, then this initializer can be
             // skipped entirely.
-            if let Some(memory_index) = module.defined_memory_index(memory_index) {
-                if !instance.memories[memory_index].1.needs_init() {
+            if let Some(memory_index) = self.module.defined_memory_index(memory_index) {
+                if !self.instance.memories[memory_index].1.needs_init() {
                     return true;
                 }
             }
-            let memory = instance.get_memory(memory_index);
+            let memory = self.instance.get_memory(memory_index);
 
             unsafe {
-                let src = instance.wasm_data(init.data.clone());
+                let src = self.instance.wasm_data(init.data.clone());
                 let dst = memory.base.add(usize::try_from(init.offset).unwrap());
                 // FIXME audit whether this is safe in the presence of shared
                 // memory
@@ -721,8 +729,16 @@ fn initialize_memories(instance: &mut Instance, module: &Module) -> Result<()> {
                 ptr::copy_nonoverlapping(src.as_ptr(), dst, src.len())
             }
             true
-        },
-    );
+        }
+    }
+
+    let ok = module
+        .memory_initialization
+        .init_memory(&mut InitMemoryAtInstantiation {
+            instance,
+            module,
+            const_evaluator: ConstExprEvaluator::default(),
+        });
     if !ok {
         return Err(Trap::MemoryOutOfBounds.into());
     }
@@ -735,7 +751,7 @@ fn check_init_bounds(instance: &mut Instance, module: &Module) -> Result<()> {
 
     match &module.memory_initialization {
         MemoryInitialization::Segmented(initializers) => {
-            check_memory_init_bounds(instance, initializers)?;
+            check_memory_init_bounds(instance, module, initializers)?;
         }
         // Statically validated already to have everything in-bounds.
         MemoryInitialization::Static { .. } => {}


Test patch:
diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs b/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs
index 6a258db30..b7c0df9cc 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs
@@ -2120,15 +2120,66 @@ fn riscv64_worst_case_instruction_size() {
     //there are all candidates potential generate a lot of bytes.
     let mut candidates: Vec<MInst> = vec![];
 
-    candidates.push(Inst::FloatRound {
-        op: FloatRoundOP::Trunc,
-        int_tmp: writable_a0(),
-        f_tmp: writable_a0(),
-        rd: writable_fa0(),
-        rs: fa0(),
-        ty: F64,
+    candidates.push(Inst::Popcnt {
+        sum: writable_a0(),
+        tmp: writable_a0(),
+        step: writable_a0(),
+        rs: a0(),
+        ty: I64,
     });
 
+    candidates.push(Inst::Cltz {
+        sum: writable_a0(),
+        tmp: writable_a0(),
+        step: writable_a0(),
+        rs: a0(),
+        leading: true,
+        ty: I64,
+    });
+
+    candidates.push(Inst::Brev8 {
+        rd: writable_a0(),
+        tmp: writable_a0(),
+        step: writable_a0(),
+        tmp2: writable_a0(),
+        rs: a0(),
+        ty: I64,
+    });
+
+    candidates.push(Inst::AtomicCas {
+        offset: a0(),
+        t0: writable_a0(),
+        dst: writable_a0(),
+        e: a0(),
+        addr: a0(),
+        v: a0(),
+        ty: I64,
+    });
+
+    candidates.push(Inst::AtomicCas {
+        offset: a0(),
+        t0: writable_a0(),
+        dst: writable_a0(),
+        e: a0(),
+        addr: a0(),
+        v: a0(),
+        ty: I16,
+    });
+
+    candidates.extend(
+        crate::ir::AtomicRmwOp::all()
+            .iter()
+            .map(|op| Inst::AtomicRmwLoop {
+                op: *op,
+                offset: a0(),
+                dst: writable_a0(),
+                ty: I16,
+                p: a0(),
+                x: a0(),
+                t0: writable_a0(),
+            }),
+    );
+
     let mut max: (u32, MInst) = (0, Inst::Nop0);
     for i in candidates {
         let mut buffer = MachBuffer::new();
diff --git a/cranelift/filetests/filetests/isa/riscv64/ceil.clif b/cranelift/filetests/filetests/isa/riscv64/ceil.clif
new file mode 100644
index 000000000..f7f0a32e9
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/riscv64/ceil.clif
@@ -0,0 +1,79 @@
+test compile precise-output
+set unwind_info=false
+target riscv64
+
+
+function %ceil_f32(f32) -> f32 {
+block0(v0: f32):
+  v1 = ceil v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   lui a2,307200
+;   fmv.w.x fa4,a2
+;   fabs.s fa1,fa0
+;   flt.s a2,fa1,fa4
+;   fcvt.w.s a4,fa0,rup
+;   fcvt.s.w fa1,a4,rup
+;   fsgnj.s fa2,fa1,fa0
+;   fmv.w.x fa4,zero
+;   fadd.s fa0,fa0,fa4,rne
+;   select fa0,fa0,fa2##condition=(a2 eq zero)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   lui a2, 0x4b000
+;   fmv.w.x fa4, a2
+;   fabs.s fa1, fa0
+;   flt.s a2, fa1, fa4
+;   fcvt.w.s a4, fa0, rup ; trap: bad_toint
+;   fcvt.s.w fa1, a4, rup
+;   fsgnj.s fa2, fa1, fa0
+;   fmv.w.x fa4, zero
+;   fadd.s fa0, fa0, fa4, rne
+;   beqz a2, 8
+;   fmv.d fa0, fa2
+;   ret
+
+function %ceil_f64(f64) -> f64 {
+block0(v0: f64):
+  v1 = ceil v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   lui a2,1075
+;   slli a4,a2,40
+;   fmv.d.x fa1,a4
+;   fabs.d fa2,fa0
+;   flt.d a4,fa2,fa1
+;   fcvt.l.d a0,fa0,rup
+;   fcvt.d.l fa2,a0,rup
+;   fsgnj.d fa4,fa2,fa0
+;   fmv.d.x fa1,zero
+;   fadd.d fa2,fa0,fa1,rne
+;   select fa0,fa2,fa4##condition=(a4 eq zero)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   lui a2, 0x433
+;   slli a4, a2, 0x28
+;   fmv.d.x fa1, a4
+;   fabs.d fa2, fa0
+;   flt.d a4, fa2, fa1
+;   fcvt.l.d a0, fa0, rup ; trap: bad_toint
+;   fcvt.d.l fa2, a0, rup
+;   fsgnj.d fa4, fa2, fa0
+;   fmv.d.x fa1, zero
+;   fadd.d fa2, fa0, fa1, rne
+;   bnez a4, 0xc
+;   fmv.d fa0, fa2
+;   j 8
+;   fmv.d fa0, fa4
+;   ret
+
diff --git a/cranelift/filetests/filetests/isa/riscv64/float.clif b/cranelift/filetests/filetests/isa/riscv64/float.clif
index 632b692ad..62bbb2bae 100644
--- a/cranelift/filetests/filetests/isa/riscv64/float.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/float.clif
@@ -258,269 +258,6 @@ block0(v0: f64):
 ;   fcvt.s.d fa0, fa0, rne
 ;   ret
 
-function %f22(f32) -> f32 {
-block0(v0: f32):
-  v1 = ceil v0
-  return v1
-}
-
-; VCode:
-; block0:
-;   fmv.d fa5,fa0
-;   ceil fa0,fa5##int_tmp=a3 f_tmp=fa4 ty=f32
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   fmv.d fa5, fa0
-;   feq.s a3, fa5, fa5
-;   beqz a3, 0x28
-;   lui t6, 0x4b800
-;   fmv.w.x fa4, t6
-;   fabs.s fa0, fa5
-;   flt.s a3, fa4, fa0
-;   bnez a3, 0x1c
-;   fcvt.l.s a3, fa5, rup ; trap: bad_toint
-;   fcvt.s.l fa0, a3, rup
-;   fsgnj.s fa0, fa0, fa5
-;   j 0x10
-;   fadd.s fa0, fa5, fa5, rne
-;   j 8
-;   fmv.s fa0, fa5
-;   ret
-
-function %f22(f64) -> f64 {
-block0(v0: f64):
-  v1 = ceil v0
-  return v1
-}
-
-; VCode:
-; block0:
-;   fmv.d fa5,fa0
-;   ceil fa0,fa5##int_tmp=a3 f_tmp=fa4 ty=f64
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   fmv.d fa5, fa0
-;   feq.d a3, fa5, fa5
-;   beqz a3, 0x38
-;   auipc t6, 0
-;   ld t6, 0xc(t6) ; trap: heap_oob
-;   j 0xc
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   .byte 0x00, 0x00, 0x40, 0x43
-;   fmv.d.x fa4, t6
-;   fabs.d fa0, fa5
-;   flt.d a3, fa4, fa0
-;   bnez a3, 0x1c
-;   fcvt.l.d a3, fa5, rup ; trap: bad_toint
-;   fcvt.d.l fa0, a3, rup
-;   fsgnj.d fa0, fa0, fa5
-;   j 0x10
-;   fadd.d fa0, fa5, fa5, rne
-;   j 8
-;   fmv.d fa0, fa5
-;   ret
-
-function %f23(f32) -> f32 {
-block0(v0: f32):
-  v1 = floor v0
-  return v1
-}
-
-; VCode:
-; block0:
-;   fmv.d fa5,fa0
-;   floor fa0,fa5##int_tmp=a3 f_tmp=fa4 ty=f32
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   fmv.d fa5, fa0
-;   feq.s a3, fa5, fa5
-;   beqz a3, 0x28
-;   lui t6, 0x4b800
-;   fmv.w.x fa4, t6
-;   fabs.s fa0, fa5
-;   flt.s a3, fa4, fa0
-;   bnez a3, 0x1c
-;   fcvt.l.s a3, fa5, rdn ; trap: bad_toint
-;   fcvt.s.l fa0, a3, rdn
-;   fsgnj.s fa0, fa0, fa5
-;   j 0x10
-;   fadd.s fa0, fa5, fa5, rne
-;   j 8
-;   fmv.s fa0, fa5
-;   ret
-
-function %f24(f64) -> f64 {
-block0(v0: f64):
-  v1 = floor v0
-  return v1
-}
-
-; VCode:
-; block0:
-;   fmv.d fa5,fa0
-;   floor fa0,fa5##int_tmp=a3 f_tmp=fa4 ty=f64
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   fmv.d fa5, fa0
-;   feq.d a3, fa5, fa5
-;   beqz a3, 0x38
-;   auipc t6, 0
-;   ld t6, 0xc(t6) ; trap: heap_oob
-;   j 0xc
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   .byte 0x00, 0x00, 0x40, 0x43
-;   fmv.d.x fa4, t6
-;   fabs.d fa0, fa5
-;   flt.d a3, fa4, fa0
-;   bnez a3, 0x1c
-;   fcvt.l.d a3, fa5, rdn ; trap: bad_toint
-;   fcvt.d.l fa0, a3, rdn
-;   fsgnj.d fa0, fa0, fa5
-;   j 0x10
-;   fadd.d fa0, fa5, fa5, rne
-;   j 8
-;   fmv.d fa0, fa5
-;   ret
-
-function %f25(f32) -> f32 {
-block0(v0: f32):
-  v1 = trunc v0
-  return v1
-}
-
-; VCode:
-; block0:
-;   fmv.d fa5,fa0
-;   trunc fa0,fa5##int_tmp=a3 f_tmp=fa4 ty=f32
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   fmv.d fa5, fa0
-;   feq.s a3, fa5, fa5
-;   beqz a3, 0x28
-;   lui t6, 0x4b800
-;   fmv.w.x fa4, t6
-;   fabs.s fa0, fa5
-;   flt.s a3, fa4, fa0
-;   bnez a3, 0x1c
-;   fcvt.l.s a3, fa5, rtz ; trap: bad_toint
-;   fcvt.s.l fa0, a3, rtz
-;   fsgnj.s fa0, fa0, fa5
-;   j 0x10
-;   fadd.s fa0, fa5, fa5, rne
-;   j 8
-;   fmv.s fa0, fa5
-;   ret
-
-function %f26(f64) -> f64 {
-block0(v0: f64):
-  v1 = trunc v0
-  return v1
-}
-
-; VCode:
-; block0:
-;   fmv.d fa5,fa0
-;   trunc fa0,fa5##int_tmp=a3 f_tmp=fa4 ty=f64
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   fmv.d fa5, fa0
-;   feq.d a3, fa5, fa5
-;   beqz a3, 0x38
-;   auipc t6, 0
-;   ld t6, 0xc(t6) ; trap: heap_oob
-;   j 0xc
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   .byte 0x00, 0x00, 0x40, 0x43
-;   fmv.d.x fa4, t6
-;   fabs.d fa0, fa5
-;   flt.d a3, fa4, fa0
-;   bnez a3, 0x1c
-;   fcvt.l.d a3, fa5, rtz ; trap: bad_toint
-;   fcvt.d.l fa0, a3, rtz
-;   fsgnj.d fa0, fa0, fa5
-;   j 0x10
-;   fadd.d fa0, fa5, fa5, rne
-;   j 8
-;   fmv.d fa0, fa5
-;   ret
-
-function %f27(f32) -> f32 {
-block0(v0: f32):
-  v1 = nearest v0
-  return v1
-}
-
-; VCode:
-; block0:
-;   fmv.d fa5,fa0
-;   nearest fa0,fa5##int_tmp=a3 f_tmp=fa4 ty=f32
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   fmv.d fa5, fa0
-;   feq.s a3, fa5, fa5
-;   beqz a3, 0x28
-;   lui t6, 0x4b800
-;   fmv.w.x fa4, t6
-;   fabs.s fa0, fa5
-;   flt.s a3, fa4, fa0
-;   bnez a3, 0x1c
-;   fcvt.l.s a3, fa5, rne ; trap: bad_toint
-;   fcvt.s.l fa0, a3, rne
-;   fsgnj.s fa0, fa0, fa5
-;   j 0x10
-;   fadd.s fa0, fa5, fa5, rne
-;   j 8
-;   fmv.s fa0, fa5
-;   ret
-
-function %f28(f64) -> f64 {
-block0(v0: f64):
-  v1 = nearest v0
-  return v1
-}
-
-; VCode:
-; block0:
-;   fmv.d fa5,fa0
-;   nearest fa0,fa5##int_tmp=a3 f_tmp=fa4 ty=f64
-;   ret
-;
-; Disassembled:
-; block0: ; offset 0x0
-;   fmv.d fa5, fa0
-;   feq.d a3, fa5, fa5
-;   beqz a3, 0x38
-;   auipc t6, 0
-;   ld t6, 0xc(t6) ; trap: heap_oob
-;   j 0xc
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   .byte 0x00, 0x00, 0x40, 0x43
-;   fmv.d.x fa4, t6
-;   fabs.d fa0, fa5
-;   flt.d a3, fa4, fa0
-;   bnez a3, 0x1c
-;   fcvt.l.d a3, fa5, rne ; trap: bad_toint
-;   fcvt.d.l fa0, a3, rne
-;   fsgnj.d fa0, fa0, fa5
-;   j 0x10
-;   fadd.d fa0, fa5, fa5, rne
-;   j 8
-;   fmv.d fa0, fa5
-;   ret
 
 function %f29(f32, f32, f32) -> f32 {
 block0(v0: f32, v1: f32, v2: f32):
diff --git a/cranelift/filetests/filetests/isa/riscv64/floor.clif b/cranelift/filetests/filetests/isa/riscv64/floor.clif
new file mode 100644
index 000000000..0ccdc9183
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/riscv64/floor.clif
@@ -0,0 +1,79 @@
+test compile precise-output
+set unwind_info=false
+target riscv64
+
+
+function %floor_f32(f32) -> f32 {
+block0(v0: f32):
+  v1 = floor v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   lui a2,307200
+;   fmv.w.x fa4,a2
+;   fabs.s fa1,fa0
+;   flt.s a2,fa1,fa4
+;   fcvt.w.s a4,fa0,rdn
+;   fcvt.s.w fa1,a4,rdn
+;   fsgnj.s fa2,fa1,fa0
+;   fmv.w.x fa4,zero
+;   fadd.s fa0,fa0,fa4,rne
+;   select fa0,fa0,fa2##condition=(a2 eq zero)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   lui a2, 0x4b000
+;   fmv.w.x fa4, a2
+;   fabs.s fa1, fa0
+;   flt.s a2, fa1, fa4
+;   fcvt.w.s a4, fa0, rdn ; trap: bad_toint
+;   fcvt.s.w fa1, a4, rdn
+;   fsgnj.s fa2, fa1, fa0
+;   fmv.w.x fa4, zero
+;   fadd.s fa0, fa0, fa4, rne
+;   beqz a2, 8
+;   fmv.d fa0, fa2
+;   ret
+
+function %floor_f64(f64) -> f64 {
+block0(v0: f64):
+  v1 = floor v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   lui a2,1075
+;   slli a4,a2,40
+;   fmv.d.x fa1,a4
+;   fabs.d fa2,fa0
+;   flt.d a4,fa2,fa1
+;   fcvt.l.d a0,fa0,rdn
+;   fcvt.d.l fa2,a0,rdn
+;   fsgnj.d fa4,fa2,fa0
+;   fmv.d.x fa1,zero
+;   fadd.d fa2,fa0,fa1,rne
+;   select fa0,fa2,fa4##condition=(a4 eq zero)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   lui a2, 0x433
+;   slli a4, a2, 0x28
+;   fmv.d.x fa1, a4
+;   fabs.d fa2, fa0
+;   flt.d a4, fa2, fa1
+;   fcvt.l.d a0, fa0, rdn ; trap: bad_toint
+;   fcvt.d.l fa2, a0, rdn
+;   fsgnj.d fa4, fa2, fa0
+;   fmv.d.x fa1, zero
+;   fadd.d fa2, fa0, fa1, rne
+;   bnez a4, 0xc
+;   fmv.d fa0, fa2
+;   j 8
+;   fmv.d fa0, fa4
+;   ret
+
diff --git a/cranelift/filetests/filetests/isa/riscv64/nearest.clif b/cranelift/filetests/filetests/isa/riscv64/nearest.clif
new file mode 100644
index 000000000..166248c1c
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/riscv64/nearest.clif
@@ -0,0 +1,78 @@
+test compile precise-output
+set unwind_info=false
+target riscv64
+
+function %nearest_f32(f32) -> f32 {
+block0(v0: f32):
+  v1 = nearest v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   lui a2,307200
+;   fmv.w.x fa4,a2
+;   fabs.s fa1,fa0
+;   flt.s a2,fa1,fa4
+;   fcvt.w.s a4,fa0,rne
+;   fcvt.s.w fa1,a4,rne
+;   fsgnj.s fa2,fa1,fa0
+;   fmv.w.x fa4,zero
+;   fadd.s fa0,fa0,fa4,rne
+;   select fa0,fa0,fa2##condition=(a2 eq zero)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   lui a2, 0x4b000
+;   fmv.w.x fa4, a2
+;   fabs.s fa1, fa0
+;   flt.s a2, fa1, fa4
+;   fcvt.w.s a4, fa0, rne ; trap: bad_toint
+;   fcvt.s.w fa1, a4, rne
+;   fsgnj.s fa2, fa1, fa0
+;   fmv.w.x fa4, zero
+;   fadd.s fa0, fa0, fa4, rne
+;   beqz a2, 8
+;   fmv.d fa0, fa2
+;   ret
+
+function %nearest_f64(f64) -> f64 {
+block0(v0: f64):
+  v1 = nearest v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   lui a2,1075
+;   slli a4,a2,40
+;   fmv.d.x fa1,a4
+;   fabs.d fa2,fa0
+;   flt.d a4,fa2,fa1
+;   fcvt.l.d a0,fa0,rne
+;   fcvt.d.l fa2,a0,rne
+;   fsgnj.d fa4,fa2,fa0
+;   fmv.d.x fa1,zero
+;   fadd.d fa2,fa0,fa1,rne
+;   select fa0,fa2,fa4##condition=(a4 eq zero)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   lui a2, 0x433
+;   slli a4, a2, 0x28
+;   fmv.d.x fa1, a4
+;   fabs.d fa2, fa0
+;   flt.d a4, fa2, fa1
+;   fcvt.l.d a0, fa0, rne ; trap: bad_toint
+;   fcvt.d.l fa2, a0, rne
+;   fsgnj.d fa4, fa2, fa0
+;   fmv.d.x fa1, zero
+;   fadd.d fa2, fa0, fa1, rne
+;   bnez a4, 0xc
+;   fmv.d fa0, fa2
+;   j 8
+;   fmv.d fa0, fa4
+;   ret
+
diff --git a/cranelift/filetests/filetests/isa/riscv64/trunc.clif b/cranelift/filetests/filetests/isa/riscv64/trunc.clif
new file mode 100644
index 000000000..05fe998e6
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/riscv64/trunc.clif
@@ -0,0 +1,78 @@
+test compile precise-output
+set unwind_info=false
+target riscv64
+
+function %f25(f32) -> f32 {
+block0(v0: f32):
+  v1 = trunc v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   lui a2,307200
+;   fmv.w.x fa4,a2
+;   fabs.s fa1,fa0
+;   flt.s a2,fa1,fa4
+;   fcvt.w.s a4,fa0,rtz
+;   fcvt.s.w fa1,a4,rtz
+;   fsgnj.s fa2,fa1,fa0
+;   fmv.w.x fa4,zero
+;   fadd.s fa0,fa0,fa4,rne
+;   select fa0,fa0,fa2##condition=(a2 eq zero)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   lui a2, 0x4b000
+;   fmv.w.x fa4, a2
+;   fabs.s fa1, fa0
+;   flt.s a2, fa1, fa4
+;   fcvt.w.s a4, fa0, rtz ; trap: bad_toint
+;   fcvt.s.w fa1, a4, rtz
+;   fsgnj.s fa2, fa1, fa0
+;   fmv.w.x fa4, zero
+;   fadd.s fa0, fa0, fa4, rne
+;   beqz a2, 8
+;   fmv.d fa0, fa2
+;   ret
+
+function %f26(f64) -> f64 {
+block0(v0: f64):
+  v1 = trunc v0
+  return v1
+}
+
+; VCode:
+; block0:
+;   lui a2,1075
+;   slli a4,a2,40
+;   fmv.d.x fa1,a4
+;   fabs.d fa2,fa0
+;   flt.d a4,fa2,fa1
+;   fcvt.l.d a0,fa0,rtz
+;   fcvt.d.l fa2,a0,rtz
+;   fsgnj.d fa4,fa2,fa0
+;   fmv.d.x fa1,zero
+;   fadd.d fa2,fa0,fa1,rne
+;   select fa0,fa2,fa4##condition=(a4 eq zero)
+;   ret
+;
+; Disassembled:
+; block0: ; offset 0x0
+;   lui a2, 0x433
+;   slli a4, a2, 0x28
+;   fmv.d.x fa1, a4
+;   fabs.d fa2, fa0
+;   flt.d a4, fa2, fa1
+;   fcvt.l.d a0, fa0, rtz ; trap: bad_toint
+;   fcvt.d.l fa2, a0, rtz
+;   fsgnj.d fa4, fa2, fa0
+;   fmv.d.x fa1, zero
+;   fadd.d fa2, fa0, fa1, rne
+;   bnez a4, 0xc
+;   fmv.d fa0, fa2
+;   j 8
+;   fmv.d fa0, fa4
+;   ret
+