Repository: bytecodealliance/wasmtime
Pull request #7327 (2023-10-22 20:49:49)

riscv64: Refactor FRM and fcvt-to-int management
This commit started out with the goal of deleting the `FcvtToInt` macro instruction by moving it into ISLE but it ended up having some surrounding refactors as well. The original goal is achieved and all float-to-int conversion now happens in ISLE. Additionally handling of the FRM (float rounding mode) state of each instruction is now explicit which means that Cranelift code emission now defaults to `FRM::RNE` (round-to-nearest ties-to-evens) rather than the runtime state in the `fcsr`. This was a theoretical bug with the previous code where if the register didn't have `RNE` in it then the code wouldn't perform correctly.

Closes #6015

github-actions[bot]: #### Subscribe to Label Action

cc @cfallin, @fitzgen

<details>
This issue or pull request has been labeled: "cranelift", "cranelift:meta", "isle"

Thus the following users have been cc'd because of the following labels:

* cfallin: isle
* fitzgen: isle

To subscribe or unsubscribe from this label, edit the <code>.github/subscribe-to-label.json</code> configuration file.

[Learn more.](https://github.com/bytecodealliance/subscribe-to-label-action)
</details>

Patch:
diff --git a/.github/actions/binary-compatible-builds/main.js b/.github/actions/binary-compatible-builds/main.js
index 378c5c202..6230126fc 100755
--- a/.github/actions/binary-compatible-builds/main.js
+++ b/.github/actions/binary-compatible-builds/main.js
@@ -34,7 +34,7 @@ if (process.env.CENTOS !== undefined) {
   return;
 }
 
-const name = process.env.INPUT_NAME;
+const name = process.env.INPUT_NAME.replace(/-min$/, '');
 
 child_process.execFileSync('docker', [
   'build',
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 2df6de8fa..92c5ae06e 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -116,6 +116,7 @@ jobs:
     outputs:
       run-full: ${{ steps.calculate.outputs.run-full }}
       test-matrix: ${{ steps.calculate.outputs.test-matrix }}
+      build-matrix: ${{ steps.calculate.outputs.build-matrix }}
       test-capi: ${{ steps.calculate.outputs.test-capi }}
       build-fuzz: ${{ steps.calculate.outputs.build-fuzz }}
       audit: ${{ steps.calculate.outputs.audit }}
@@ -161,6 +162,9 @@ jobs:
         echo "test-matrix={\"include\":$(echo $matrix)}" >> $GITHUB_OUTPUT
         echo "$matrix"
 
+        matrix="$(node ./ci/build-build-matrix.js)"
+        echo "build-matrix={\"include\":$(echo $matrix)}" >> $GITHUB_OUTPUT
+
         if [ "$run_full" = "true" ]; then
             echo run-full=true >> $GITHUB_OUTPUT
             echo test-capi=true >> $GITHUB_OUTPUT
@@ -658,40 +662,28 @@ jobs:
   # Perform release builds of `wasmtime` and `libwasmtime.so`. Builds a variety
   # of platforms and architectures and then uploads the release artifacts to
   # this workflow run's list of artifacts.
+  #
+  # Note that the full matrix is computed by `ci/build-build-matrix.js`.
   build:
     needs: determine
     if: needs.determine.outputs.run-full
     name: Release build for ${{ matrix.build }}
     runs-on: ${{ matrix.os }}
     strategy:
-      matrix:
-        include:
-        - build: x86_64-linux
-          os: ubuntu-latest
-        - build: x86_64-macos
-          os: macos-latest
-        - build: aarch64-macos
-          os: macos-latest
-          target: aarch64-apple-darwin
-        - build: x86_64-windows
-          os: windows-latest
-        - build: x86_64-mingw
-          os: windows-latest
-          target: x86_64-pc-windows-gnu
-        - build: aarch64-linux
-          os: ubuntu-latest
-          target: aarch64-unknown-linux-gnu
-        - build: s390x-linux
-          os: ubuntu-latest
-          target: s390x-unknown-linux-gnu
-        - build: riscv64gc-linux
-          os: ubuntu-latest
-          target: riscv64gc-unknown-linux-gnu
+      fail-fast: false
+      matrix: ${{ fromJson(needs.determine.outputs.build-matrix) }}
     steps:
     - uses: actions/checkout@v3
       with:
         submodules: true
+
     - uses: ./.github/actions/install-rust
+      with:
+        toolchain: ${{ matrix.rust }}
+    - run: |
+        rustup component add rust-src
+        rustup target add ${{ matrix.target }}
+
     # On one builder produce the source tarball since there's no need to produce
     # it everywhere
     - run: ./ci/build-src-tarball.sh
@@ -699,21 +691,13 @@ jobs:
     - uses: ./.github/actions/binary-compatible-builds
       with:
         name: ${{ matrix.build }}
-    - run: |
-        echo CARGO_BUILD_TARGET=${{ matrix.target }} >> $GITHUB_ENV
-        rustup target add ${{ matrix.target }}
-      if: matrix.target != ''
-
-    # Build `wasmtime` and executables. Note that we include some non-default
-    # features so the # release artifacts can be maximally feature-ful.
-    - run: $CENTOS cargo build --release --bin wasmtime --features all-arch,component-model
 
-    # Build `libwasmtime.so`
-    - run: $CENTOS cargo build --release --manifest-path crates/c-api/Cargo.toml
+    - run: $CENTOS ./ci/build-release-artifacts.sh "${{ matrix.build }}" "${{ matrix.target }}"
 
-    # Assemble release artifats appropriate for this platform, then upload them
+    # Assemble release artifacts appropriate for this platform, then upload them
     # unconditionally to this workflow's files so we have a copy of them.
     - run: ./ci/build-tarballs.sh "${{ matrix.build }}" "${{ matrix.target }}"
+
     - uses: actions/upload-artifact@v3
       with:
         name: bins-${{ matrix.build }}
diff --git a/.github/workflows/publish-artifacts.yml b/.github/workflows/publish-artifacts.yml
index e036474d2..8c149003d 100644
--- a/.github/workflows/publish-artifacts.yml
+++ b/.github/workflows/publish-artifacts.yml
@@ -28,6 +28,8 @@ jobs:
       env:
         GH_TOKEN: ${{ github.token }}
 
+    - run: ./ci/merge-artifacts.sh
+
     # Deploy the `gh-pages.tar.gz` artifact to the `gh-pages` branch.
     - run: tar xf gh-pages.tar.gz
       working-directory: gh-pages
@@ -41,10 +43,6 @@ jobs:
 
     - run: npm install --production
       working-directory: .github/actions/github-release
-    - run: |
-        mkdir dist
-        mv -t dist bins-*/*.tar.*
-        mv -t dist bins-*/*.{zip,msi,wasm}
     - name: Publish Release
       uses: ./.github/actions/github-release
       with:
diff --git a/Cargo.toml b/Cargo.toml
index 7068ab76d..b2b3f3b3c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,7 +36,7 @@ wasmtime-wasi-nn = { workspace = true, optional = true }
 wasmtime-wasi-threads = { workspace = true, optional = true }
 wasmtime-wasi-http = { workspace = true, optional = true }
 wasmtime-runtime = { workspace = true }
-clap = { workspace = true, features = ["color", "suggestions", "derive"] }
+clap = { workspace = true }
 anyhow = { workspace = true }
 target-lexicon = { workspace = true }
 once_cell = { workspace = true }
@@ -228,7 +228,7 @@ anyhow = "1.0.22"
 windows-sys = "0.48.0"
 env_logger = "0.10"
 log = { version = "0.4.8", default-features = false }
-clap = { version = "4.3.12", features = ["color", "suggestions", "derive"] }
+clap = { version = "4.3.12", default-features = false, features = ["std", "derive"] }
 hashbrown = { version = "0.14", default-features = false }
 capstone = "0.9.0"
 once_cell = "1.12.0"
@@ -303,6 +303,11 @@ default = [
   "coredump",
   "addr2line",
   "debug-builtins",
+
+  # Enable some nice features of clap by default, but they come at a binary size
+  # cost, so allow disabling this through disabling of our own `default`
+  # feature.
+  "clap/default",
 ]
 
 # ========================================
diff --git a/ci/build-build-matrix.js b/ci/build-build-matrix.js
new file mode 100644
index 000000000..23c2b6ebb
--- /dev/null
+++ b/ci/build-build-matrix.js
@@ -0,0 +1,68 @@
+// Small script used to calculate the matrix of builds that are going to be
+// done if CI decides to do a release build.
+//
+// This is a separate script primarily to write out all the release
+// targets/platforms once and then duplicate them all with a "min" build.
+
+const array = [
+  {
+    // The name of the build which shows up in the name of the artifact for
+    // Wasmtime's github releases.
+    "build": "x86_64-linux",
+    // The GitHub Actions platform that this build runs on
+    "os": "ubuntu-latest",
+    // The Rust target that will be used for the build.
+    "target": "x86_64-unknown-linux-gnu",
+  },
+  {
+    "build": "aarch64-linux",
+    "os": "ubuntu-latest",
+    "target": "aarch64-unknown-linux-gnu",
+  },
+  {
+    "build": "s390x-linux",
+    "os": "ubuntu-latest",
+    "target": "s390x-unknown-linux-gnu",
+  },
+  {
+    "build": "riscv64gc-linux",
+    "os": "ubuntu-latest",
+    "target": "riscv64gc-unknown-linux-gnu",
+  },
+  {
+    "build": "x86_64-macos",
+    "os": "macos-latest",
+    "target": "x86_64-apple-darwin",
+  },
+  {
+    "build": "aarch64-macos",
+    "os": "macos-latest",
+    "target": "aarch64-apple-darwin",
+  },
+  {
+    "build": "x86_64-windows",
+    "os": "windows-latest",
+    "target": "x86_64-pc-windows-msvc",
+  },
+  {
+    "build": "x86_64-mingw",
+    "os": "windows-latest",
+    "target": "x86_64-pc-windows-gnu",
+  },
+];
+
+const builds = [];
+for (let build of array) {
+  // Perform a "deep clone" roundtripping through JSON for a copy of the build
+  // that's normal
+  build.rust = 'stable';
+  builds.push(JSON.parse(JSON.stringify(build)));
+
+  // Next generate a "min" build and add it to the builds list. Min builds
+  // require Nightly rust due to some nightly build options that are configured.
+  build.build += '-min';
+  build.rust = 'nightly-2023-10-10';
+  builds.push(build);
+}
+
+console.log(JSON.stringify(builds));
diff --git a/ci/build-release-artifacts.sh b/ci/build-release-artifacts.sh
new file mode 100755
index 000000000..71c788839
--- /dev/null
+++ b/ci/build-release-artifacts.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+# A script to build the release artifacts of Wasmtime into the `target`
+# directory. For now this is the CLI and the C API. Note that this script only
+# produces the artifacts through Cargo and doesn't package things up. That's
+# intended for the `build-tarballs.sh` script.
+#
+# This script takes a Rust target as its first input and optionally a parameter
+# afterwards which can be "-min" to indicate that a minimal build should be
+# produced with as many features as possible stripped out.
+
+set -ex
+
+build=$1
+target=$2
+
+# Default build flags for release artifacts. Leave debugging for
+# builds-from-source which have richer information anyway, and additionally the
+# CLI won't benefit from catching unwinds and neither will the C API so use
+# panic=abort in both situations.
+export CARGO_PROFILE_RELEASE_STRIP=debuginfo
+export CARGO_PROFILE_RELEASE_PANIC=abort
+
+if [[ "$build" = *-min ]]; then
+  # Configure a whole bunch of compile-time options which help reduce the size
+  # of the binary artifact produced.
+  export CARGO_PROFILE_RELEASE_OPT_LEVEL=s
+  export RUSTFLAGS=-Zlocation-detail=none
+  export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1
+  export CARGO_PROFILE_RELEASE_LTO=true
+  flags="-Zbuild-std=std,panic_abort --no-default-features -Zbuild-std-features=std_detect_dlsym_getauxval"
+  flags="$flags --features disable-logging"
+else
+  # For release builds the CLI is built a bit more feature-ful than the Cargo
+  # defaults to provide artifacts that can do as much as possible.
+  bin_flags="--features all-arch,component-model"
+fi
+
+cargo build --release $flags --target $target -p wasmtime-cli $bin_flags
+cargo build --release $flags --target $target -p wasmtime-c-api
diff --git a/ci/build-src-tarball.sh b/ci/build-src-tarball.sh
index 571fcc47a..c5d3c10fa 100755
--- a/ci/build-src-tarball.sh
+++ b/ci/build-src-tarball.sh
@@ -18,3 +18,5 @@ cargo vendor > .cargo/config.toml
 tar -czf /tmp/$pkgname.tar.gz --transform "s/^\./$pkgname/S" --exclude=.git .
 mkdir -p dist
 mv /tmp/$pkgname.tar.gz dist/
+
+rm .cargo/config.toml
diff --git a/ci/build-tarballs.sh b/ci/build-tarballs.sh
index eb6197c2a..d8a249f11 100755
--- a/ci/build-tarballs.sh
+++ b/ci/build-tarballs.sh
@@ -1,19 +1,18 @@
 #!/bin/bash
 
 # A small script used for assembling release tarballs for both the `wasmtime`
-# binary and the C API. This is executed with two arguments, mostly coming from
-# the CI matrix.
+# binary and the C API. This is executed with two arguments, mostly coming
+# from the CI matrix.
 #
-# * The first argument is the name of the platform, used to name the release
-# * The second argument is the "target", if present, currently only for
-#   cross-compiles
+# * The first argument is the name of the "build", used to name the release.
+# * The second argument is the Rust target that the build was performed for.
 #
 # This expects the build to already be done and will assemble release artifacts
 # in `dist/`
 
 set -ex
 
-platform=$1
+build=$1
 target=$2
 
 rm -rf tmp
@@ -25,8 +24,15 @@ if [[ $GITHUB_REF == refs/heads/release-* ]]; then
   tag=v$(./ci/print-current-version.sh)
 fi
 
-bin_pkgname=wasmtime-$tag-$platform
-api_pkgname=wasmtime-$tag-$platform-c-api
+# For *-min builds produce the same named artifacts as the normal build and
+# they'll get unioned together in a later step in the CI.
+build_pkgname=$build
+if [[ $build == *-min ]]; then
+  build_pkgname=${build%-min}
+fi
+
+bin_pkgname=wasmtime-$tag-$build_pkgname
+api_pkgname=wasmtime-$tag-$build_pkgname-c-api
 
 mkdir tmp/$api_pkgname
 mkdir tmp/$api_pkgname/lib
@@ -37,47 +43,61 @@ cp LICENSE README.md tmp/$bin_pkgname
 cp -r crates/c-api/include tmp/$api_pkgname
 cp crates/c-api/wasm-c-api/include/wasm.h tmp/$api_pkgname/include
 
-fmt=tar
-if [ "$platform" = "x86_64-windows" ]; then
-  cp target/release/wasmtime.exe tmp/$bin_pkgname
-  cp target/release/{wasmtime.dll,wasmtime.lib,wasmtime.dll.lib} tmp/$api_pkgname/lib
-  fmt=zip
-
-  # Generate a `*.msi` installer for Windows as well
-  export WT_VERSION=`cat Cargo.toml | sed -n 's/^version = "\([^"]*\)".*/\1/p'`
-  "$WIX/bin/candle" -arch x64 -out target/wasmtime.wixobj ci/wasmtime.wxs
-  "$WIX/bin/light" -out dist/$bin_pkgname.msi target/wasmtime.wixobj -ext WixUtilExtension
-  rm dist/$bin_pkgname.wixpdb
-elif [ "$platform" = "x86_64-mingw" ]; then
-  cp target/x86_64-pc-windows-gnu/release/wasmtime.exe tmp/$bin_pkgname
-  cp target/x86_64-pc-windows-gnu/release/{wasmtime.dll,libwasmtime.a,libwasmtime.dll.a} tmp/$api_pkgname/lib
-  fmt=zip
-elif [ "$platform" = "x86_64-macos" ]; then
-  # Postprocess the macOS dylib a bit to have a more reasonable `LC_ID_DYLIB`
-  # directive than the default one that comes out of the linker when typically
-  # doing `cargo build`. For more info see #984
-  install_name_tool -id "@rpath/libwasmtime.dylib" target/release/libwasmtime.dylib
-  cp target/release/wasmtime tmp/$bin_pkgname
-  cp target/release/libwasmtime.{a,dylib} tmp/$api_pkgname/lib
-elif [ "$platform" = "aarch64-macos" ]; then
-  install_name_tool -id "@rpath/libwasmtime.dylib" target/aarch64-apple-darwin/release/libwasmtime.dylib
-  cp target/aarch64-apple-darwin/release/wasmtime tmp/$bin_pkgname
-  cp target/aarch64-apple-darwin/release/libwasmtime.{a,dylib} tmp/$api_pkgname/lib
-elif [ "$target" = "" ]; then
-  cp target/release/wasmtime tmp/$bin_pkgname
-  cp target/release/libwasmtime.{a,so} tmp/$api_pkgname/lib
-else
-  cp target/$target/release/wasmtime tmp/$bin_pkgname
-  cp target/$target/release/libwasmtime.{a,so} tmp/$api_pkgname/lib
+# For *-min builds rename artifacts with a `-min` suffix to avoid eventual
+# clashes with the normal builds when the tarballs are unioned together.
+if [[ $build == *-min ]]; then
+  min="-min"
 fi
 
+fmt=tar
+
+case $build in
+  x86_64-windows*)
+    cp target/$target/release/wasmtime.exe tmp/$bin_pkgname/wasmtime$min.exe
+    cp target/$target/release/wasmtime.dll tmp/$api_pkgname/lib/wasmtime$min.dll
+    cp target/$target/release/wasmtime.lib tmp/$api_pkgname/lib/wasmtime$min.lib
+    cp target/$target/release/wasmtime.dll.lib tmp/$api_pkgname/lib/wasmtime$min.dll.lib
+    fmt=zip
+
+    if [ "$min" = "" ]; then
+      # Generate a `*.msi` installer for Windows as well
+      export WT_VERSION=`cat Cargo.toml | sed -n 's/^version = "\([^"]*\)".*/\1/p'`
+      "$WIX/bin/candle" -arch x64 -out target/wasmtime.wixobj ci/wasmtime.wxs
+      "$WIX/bin/light" -out dist/$bin_pkgname.msi target/wasmtime.wixobj -ext WixUtilExtension
+      rm dist/$bin_pkgname.wixpdb
+    fi
+    ;;
+
+  x86_64-mingw*)
+    cp target/$target/release/wasmtime.exe tmp/$bin_pkgname/wasmtime$min.exe
+    cp target/$target/release/wasmtime.dll tmp/$api_pkgname/lib/wasmtime$min.dll
+    cp target/$target/release/libwasmtime.a tmp/$api_pkgname/lib/libwasmtime$min.a
+    cp target/$target/release/libwasmtime.dll.a tmp/$api_pkgname/lib/libwasmtime$min.dll.a
+    fmt=zip
+    ;;
+
+  *-macos*)
+    # Postprocess the macOS dylib a bit to have a more reasonable `LC_ID_DYLIB`
+    # directive than the default one that comes out of the linker when typically
+    # doing `cargo build`. For more info see #984
+    install_name_tool -id "@rpath/libwasmtime$min.dylib" target/$target/release/libwasmtime.dylib
+    cp target/$target/release/wasmtime tmp/$bin_pkgname/wasmtime$min
+    cp target/$target/release/libwasmtime.a tmp/$api_pkgname/lib/libwasmtime$min.a
+    cp target/$target/release/libwasmtime.dylib tmp/$api_pkgname/lib/libwasmtime$min.dylib
+    ;;
+
+  *)
+    cp target/$target/release/wasmtime tmp/$bin_pkgname/wasmtime$min
+    cp target/$target/release/libwasmtime.a tmp/$api_pkgname/lib/libwasmtime$min.a
+    cp target/$target/release/libwasmtime.so tmp/$api_pkgname/lib/libwasmtime$min.so
+    ;;
+esac
+
 
 mktarball() {
   dir=$1
   if [ "$fmt" = "tar" ]; then
-    # this is a bit wonky, but the goal is to use `xz` with threaded compression
-    # to ideally get better performance with the `-T0` flag.
-    tar -cvf - -C tmp $dir | xz -9 -T0 > dist/$dir.tar.xz
+    tar -czvf dist/$dir.tar.gz -C tmp $dir
   else
     # Note that this runs on Windows, and it looks like GitHub Actions doesn't
     # have a `zip` tool there, so we use something else
diff --git a/ci/merge-artifacts.sh b/ci/merge-artifacts.sh
new file mode 100755
index 000000000..aece95e6c
--- /dev/null
+++ b/ci/merge-artifacts.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+# Script to merge the outputs of a run on github actions to github releases.
+# This is invoked from `.github/workflows/publish-artifacts.yml`. All previous
+# artifacts from builds are located in `bins-*` folders. The main purpose of
+# this script is to take the "min" build and merge it into the "normal" build to
+# produce one final tarball. This means that the final artifacts will have both
+# a normal and a min build in them for comparison and usage.
+
+set -ex
+
+# Prepare the upload folder and move all aritfacts that aren't being merged into
+# this folder, e.g. the MSI installer and adapter wasm files.
+rm -rf dist
+mkdir dist
+mv -t dist bins-*/*.{msi,wasm}
+
+# Merge tarballs and zips by searching for `*-min` builds, unpacking the
+# min/normal builds, into the same destination, and then repacking into a
+# tarball.
+#
+# Note that for now xz compression is used for the final artifact to try to get
+# small artifacts, but it's left at the default level since a lot of artifacts
+# are processed here and turning it up to the max 9 compression might take
+# quite awhile on CI for this one builder to process.
+for min in bins-*-min/*.tar.*; do
+ normal=${min/-min\//\/}
+ filename=$(basename $normal)
+ dir=${filename%.tar.gz}
+
+ rm -rf tmp
+ mkdir tmp
+ tar xf $min -C tmp
+ tar xf $normal -C tmp
+ tar -cf - -C tmp $dir | xz -T0 > dist/$dir.tar.xz
+ rm $min $normal
+done
+
+for min in bins-*-min/*.zip; do
+  normal=${min/-min\//\/}
+  filename=$(basename $normal)
+  dir=${filename%.zip}
+
+  rm -rf tmp
+  mkdir tmp
+  (cd tmp && unzip -o ../$min)
+  (cd tmp && unzip -o ../$normal)
+  (cd tmp && 7z a ../dist/$dir.zip $dir/)
+  rm $min $normal
+done
+
+# Copy over remaining source tarball into the dist folder
+mv -t dist bins-*/*.tar.*
diff --git a/ci/wasmtime.wxs b/ci/wasmtime.wxs
index ec3a58f9a..0e947135d 100644
--- a/ci/wasmtime.wxs
+++ b/ci/wasmtime.wxs
@@ -62,7 +62,7 @@
 
         <DirectoryRef Id="BINDIR">
             <Component Id="wasmtime.exe" Guid="*">
-                <File Id="wasmtime.exe" Source="target\release\wasmtime.exe" KeyPath="yes" Checksum="yes"/>
+                <File Id="wasmtime.exe" Source="target\x86_64-pc-windows-msvc\release\wasmtime.exe" KeyPath="yes" Checksum="yes"/>
             </Component>
         </DirectoryRef>
 
diff --git a/cranelift/Cargo.toml b/cranelift/Cargo.toml
index f6b271112..24577ddc1 100644
--- a/cranelift/Cargo.toml
+++ b/cranelift/Cargo.toml
@@ -44,7 +44,7 @@ indicatif = "0.13.0"
 thiserror = { workspace = true }
 walkdir = { workspace = true }
 anyhow = { workspace = true }
-clap = { workspace = true }
+clap = { workspace = true, features = ['default'] }
 similar = { workspace = true }
 toml = { workspace = true }
 serde = { workspace = true }
diff --git a/cranelift/codegen/meta/src/shared/immediates.rs b/cranelift/codegen/meta/src/shared/immediates.rs
index 9f908c93d..5584b5564 100644
--- a/cranelift/codegen/meta/src/shared/immediates.rs
+++ b/cranelift/codegen/meta/src/shared/immediates.rs
@@ -181,6 +181,7 @@ impl Immediates {
                 trapcode_values.insert("heap_oob", "HeapOutOfBounds");
                 trapcode_values.insert("int_ovf", "IntegerOverflow");
                 trapcode_values.insert("int_divz", "IntegerDivisionByZero");
+                trapcode_values.insert("bad_toint", "BadConversionToInteger");
                 new_enum(
                     "code",
                     "ir::TrapCode",
diff --git a/cranelift/codegen/src/isa/aarch64/abi.rs b/cranelift/codegen/src/isa/aarch64/abi.rs
index bcf865d26..b5d6e56fc 100644
--- a/cranelift/codegen/src/isa/aarch64/abi.rs
+++ b/cranelift/codegen/src/isa/aarch64/abi.rs
@@ -1148,10 +1148,14 @@ impl ABIMachineSpec for AArch64MachineDeps {
     }
 
     fn get_ext_mode(
-        _call_conv: isa::CallConv,
-        _specified: ir::ArgumentExtension,
+        call_conv: isa::CallConv,
+        specified: ir::ArgumentExtension,
     ) -> ir::ArgumentExtension {
-        ir::ArgumentExtension::None
+        if call_conv == isa::CallConv::AppleAarch64 {
+            specified
+        } else {
+            ir::ArgumentExtension::None
+        }
     }
 
     fn compute_frame_layout(
diff --git a/cranelift/codegen/src/isa/riscv64/inst.isle b/cranelift/codegen/src/isa/riscv64/inst.isle
index 20f501105..8627f2a29 100644
--- a/cranelift/codegen/src/isa/riscv64/inst.isle
+++ b/cranelift/codegen/src/isa/riscv64/inst.isle
@@ -22,7 +22,7 @@
     ;; An ALU operation with one register sources and a register destination.
     (FpuRR
       (alu_op FpuOPRR)
-      (frm OptionFloatRoundingMode)
+      (frm FRM)
       (rd WritableReg)
       (rs Reg))
 
@@ -37,7 +37,7 @@
     ;; An ALU operation with two register sources and a register destination.
     (FpuRRR
       (alu_op FpuOPRRR)
-      (frm OptionFloatRoundingMode)
+      (frm FRM)
       (rd WritableReg)
       (rs1 Reg)
       (rs2 Reg))
@@ -45,7 +45,7 @@
     ;; An ALU operation with three register sources and a register destination.
     (FpuRRRR
       (alu_op FpuOPRRRR)
-      (frm OptionFloatRoundingMode)
+      (frm FRM)
       (rd WritableReg)
       (rs1 Reg)
       (rs2 Reg)
@@ -239,14 +239,6 @@
       (addr Reg)
       (v Reg)
       (ty Type))
-    (FcvtToInt
-      (is_sat bool)
-      (rd WritableReg)
-      (tmp WritableReg) ;; a float register to load bounds.
-      (rs Reg)
-      (is_signed bool)
-      (in_type Type)
-      (out_type Type))
 
     (RawData (data VecU8))
 
@@ -831,7 +823,6 @@
 (type Imm20 (primitive Imm20))
 (type Imm3 (primitive Imm3))
 (type CondBrTarget (primitive CondBrTarget))
-(type OptionFloatRoundingMode (primitive OptionFloatRoundingMode))
 (type VecU8 (primitive VecU8))
 (type AMO (primitive AMO))
 (type VecMachLabel extern (enum))
@@ -1360,104 +1351,156 @@
 ;; TODO: Enable these instructions only when we have the F or D extensions
 
 ;; Helper for emitting the `fadd` instruction.
-(decl rv_fadd (Type FReg FReg) FReg)
-(rule (rv_fadd $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FaddS) $F32 rs1 rs2))
-(rule (rv_fadd $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FaddD) $F64 rs1 rs2))
+(decl rv_fadd (Type FRM FReg FReg) FReg)
+(rule (rv_fadd $F32 frm rs1 rs2) (fpu_rrr (FpuOPRRR.FaddS) $F32 frm rs1 rs2))
+(rule (rv_fadd $F64 frm rs1 rs2) (fpu_rrr (FpuOPRRR.FaddD) $F64 frm rs1 rs2))
 
 ;; Helper for emitting the `fsub` instruction.
-(decl rv_fsub (Type FReg FReg) FReg)
-(rule (rv_fsub $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FsubS) $F32 rs1 rs2))
-(rule (rv_fsub $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FsubD) $F64 rs1 rs2))
+(decl rv_fsub (Type FRM FReg FReg) FReg)
+(rule (rv_fsub $F32 frm rs1 rs2) (fpu_rrr (FpuOPRRR.FsubS) $F32 frm rs1 rs2))
+(rule (rv_fsub $F64 frm rs1 rs2) (fpu_rrr (FpuOPRRR.FsubD) $F64 frm rs1 rs2))
 
 ;; Helper for emitting the `fmul` instruction.
-(decl rv_fmul (Type FReg FReg) FReg)
-(rule (rv_fmul $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FmulS) $F32 rs1 rs2))
-(rule (rv_fmul $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FmulD) $F64 rs1 rs2))
+(decl rv_fmul (Type FRM FReg FReg) FReg)
+(rule (rv_fmul $F32 frm rs1 rs2) (fpu_rrr (FpuOPRRR.FmulS) $F32 frm rs1 rs2))
+(rule (rv_fmul $F64 frm rs1 rs2) (fpu_rrr (FpuOPRRR.FmulD) $F64 frm rs1 rs2))
 
 ;; Helper for emitting the `fdiv` instruction.
-(decl rv_fdiv (Type FReg FReg) FReg)
-(rule (rv_fdiv $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FdivS) $F32 rs1 rs2))
-(rule (rv_fdiv $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FdivD) $F64 rs1 rs2))
+(decl rv_fdiv (Type FRM FReg FReg) FReg)
+(rule (rv_fdiv $F32 frm rs1 rs2) (fpu_rrr (FpuOPRRR.FdivS) $F32 frm rs1 rs2))
+(rule (rv_fdiv $F64 frm rs1 rs2) (fpu_rrr (FpuOPRRR.FdivD) $F64 frm rs1 rs2))
 
 ;; Helper for emitting the `fsqrt` instruction.
-(decl rv_fsqrt (Type FReg) FReg)
-(rule (rv_fsqrt $F32 rs1) (fpu_rr (FpuOPRR.FsqrtS) $F32 rs1))
-(rule (rv_fsqrt $F64 rs1) (fpu_rr (FpuOPRR.FsqrtD) $F64 rs1))
+(decl rv_fsqrt (Type FRM FReg) FReg)
+(rule (rv_fsqrt $F32 frm rs1) (fpu_rr (FpuOPRR.FsqrtS) $F32 frm rs1))
+(rule (rv_fsqrt $F64 frm rs1) (fpu_rr (FpuOPRR.FsqrtD) $F64 frm rs1))
 
 ;; Helper for emitting the `fmadd` instruction.
-(decl rv_fmadd (Type FReg FReg FReg) FReg)
-(rule (rv_fmadd $F32 rs1 rs2 rs3) (fpu_rrrr (FpuOPRRRR.FmaddS) $F32 rs1 rs2 rs3))
-(rule (rv_fmadd $F64 rs1 rs2 rs3) (fpu_rrrr (FpuOPRRRR.FmaddD) $F64 rs1 rs2 rs3))
+(decl rv_fmadd (Type FRM FReg FReg FReg) FReg)
+(rule (rv_fmadd $F32 frm rs1 rs2 rs3) (fpu_rrrr (FpuOPRRRR.FmaddS) $F32 frm rs1 rs2 rs3))
+(rule (rv_fmadd $F64 frm rs1 rs2 rs3) (fpu_rrrr (FpuOPRRRR.FmaddD) $F64 frm rs1 rs2 rs3))
 
 ;; Helper for emitting the `fmv.x.w` instruction.
 (decl rv_fmvxw (FReg) XReg)
-(rule (rv_fmvxw r) (fpu_rr (FpuOPRR.FmvXW) $I32 r))
+(rule (rv_fmvxw r) (fpu_rr (FpuOPRR.FmvXW) $I32 (FRM.RNE) r))
 
 ;; Helper for emitting the `fmv.x.d` instruction.
 (decl rv_fmvxd (FReg) XReg)
-(rule (rv_fmvxd r) (fpu_rr (FpuOPRR.FmvXD) $I64 r))
+(rule (rv_fmvxd r) (fpu_rr (FpuOPRR.FmvXD) $I64 (FRM.RNE) r))
 
 ;; Helper for emitting the `fmv.w.x` instruction.
 (decl rv_fmvwx (XReg) FReg)
-(rule (rv_fmvwx r) (fpu_rr (FpuOPRR.FmvWX) $F32 r))
+(rule (rv_fmvwx r) (fpu_rr (FpuOPRR.FmvWX) $F32 (FRM.RNE) r))
 
 ;; Helper for emitting the `fmv.d.x` instruction.
 (decl rv_fmvdx (XReg) FReg)
-(rule (rv_fmvdx r) (fpu_rr (FpuOPRR.FmvDX) $F64 r))
+(rule (rv_fmvdx r) (fpu_rr (FpuOPRR.FmvDX) $F64 (FRM.RNE) r))
 
 ;; Helper for emitting the `fcvt.d.s` ("Float Convert Double to Single") instruction.
 (decl rv_fcvtds (FReg) FReg)
-(rule (rv_fcvtds rs1) (fpu_rr (FpuOPRR.FcvtDS) $F32 rs1))
+(rule (rv_fcvtds rs1) (fpu_rr (FpuOPRR.FcvtDS) $F32 (FRM.RNE) rs1))
 
 ;; Helper for emitting the `fcvt.s.d` ("Float Convert Single to Double") instruction.
-(decl rv_fcvtsd (FReg) FReg)
-(rule (rv_fcvtsd rs1) (fpu_rr (FpuOPRR.FcvtSD) $F64 rs1))
+(decl rv_fcvtsd (FRM FReg) FReg)
+(rule (rv_fcvtsd frm rs1) (fpu_rr (FpuOPRR.FcvtSD) $F64 frm rs1))
 
 ;; Helper for emitting the `fcvt.s.w` instruction.
-(decl rv_fcvtsw (XReg) FReg)
-(rule (rv_fcvtsw rs1) (fpu_rr (FpuOPRR.FcvtSw) $F32 rs1))
+(decl rv_fcvtsw (FRM XReg) FReg)
+(rule (rv_fcvtsw frm rs1) (fpu_rr (FpuOPRR.FcvtSw) $F32 frm rs1))
 
 ;; Helper for emitting the `fcvt.s.wu` instruction.
-(decl rv_fcvtswu (XReg) FReg)
-(rule (rv_fcvtswu rs1) (fpu_rr (FpuOPRR.FcvtSwU) $F32 rs1))
+(decl rv_fcvtswu (FRM XReg) FReg)
+(rule (rv_fcvtswu frm rs1) (fpu_rr (FpuOPRR.FcvtSwU) $F32 frm rs1))
 
 ;; Helper for emitting the `fcvt.d.w` instruction.
 (decl rv_fcvtdw (XReg) FReg)
-(rule (rv_fcvtdw rs1) (fpu_rr (FpuOPRR.FcvtDW) $F32 rs1))
+(rule (rv_fcvtdw rs1) (fpu_rr (FpuOPRR.FcvtDW) $F32 (FRM.RNE) rs1))
 
 ;; Helper for emitting the `fcvt.d.wu` instruction.
 (decl rv_fcvtdwu (XReg) FReg)
-(rule (rv_fcvtdwu rs1) (fpu_rr (FpuOPRR.FcvtDWU) $F32 rs1))
+(rule (rv_fcvtdwu rs1) (fpu_rr (FpuOPRR.FcvtDWU) $F32 (FRM.RNE) rs1))
 
 ;; Helper for emitting the `fcvt.s.l` instruction.
-(decl rv_fcvtsl (XReg) FReg)
-(rule (rv_fcvtsl rs1) (fpu_rr (FpuOPRR.FcvtSL) $F32 rs1))
+(decl rv_fcvtsl (FRM XReg) FReg)
+(rule (rv_fcvtsl frm rs1) (fpu_rr (FpuOPRR.FcvtSL) $F32 frm rs1))
 
 ;; Helper for emitting the `fcvt.s.lu` instruction.
-(decl rv_fcvtslu (XReg) FReg)
-(rule (rv_fcvtslu rs1) (fpu_rr (FpuOPRR.FcvtSLU) $F32 rs1))
+(decl rv_fcvtslu (FRM XReg) FReg)
+(rule (rv_fcvtslu frm rs1) (fpu_rr (FpuOPRR.FcvtSLU) $F32 frm rs1))
 
 ;; Helper for emitting the `fcvt.d.l` instruction.
-(decl rv_fcvtdl (XReg) FReg)
-(rule (rv_fcvtdl rs1) (fpu_rr (FpuOPRR.FcvtDL) $F32 rs1))
+(decl rv_fcvtdl (FRM XReg) FReg)
+(rule (rv_fcvtdl frm rs1) (fpu_rr (FpuOPRR.FcvtDL) $F32 frm rs1))
 
 ;; Helper for emitting the `fcvt.d.lu` instruction.
-(decl rv_fcvtdlu (XReg) FReg)
-(rule (rv_fcvtdlu rs1) (fpu_rr (FpuOPRR.FcvtDLu) $F32 rs1))
+(decl rv_fcvtdlu (FRM XReg) FReg)
+(rule (rv_fcvtdlu frm rs1) (fpu_rr (FpuOPRR.FcvtDLu) $F32 frm rs1))
+
+;; Helper for emitting the `fcvt.w.s` instruction.
+(decl rv_fcvtws (FRM FReg) XReg)
+(rule (rv_fcvtws frm rs1) (fpu_rr (FpuOPRR.FcvtWS) $I64 frm rs1))
+
+;; Helper for emitting the `fcvt.l.s` instruction.
+(decl rv_fcvtls (FRM FReg) XReg)
+(rule (rv_fcvtls frm rs1) (fpu_rr (FpuOPRR.FcvtLS) $I64 frm rs1))
+
+;; Helper for emitting the `fcvt.wu.s` instruction.
+(decl rv_fcvtwus (FRM FReg) XReg)
+(rule (rv_fcvtwus frm rs1) (fpu_rr (FpuOPRR.FcvtWuS) $I64 frm rs1))
+
+;; Helper for emitting the `fcvt.lu.s` instruction.
+(decl rv_fcvtlus (FRM FReg) XReg)
+(rule (rv_fcvtlus frm rs1) (fpu_rr (FpuOPRR.FcvtLuS) $I64 frm rs1))
+
+;; Helper for emitting the `fcvt.w.d` instruction.
+(decl rv_fcvtwd (FRM FReg) XReg)
+(rule (rv_fcvtwd frm rs1) (fpu_rr (FpuOPRR.FcvtWD) $I64 frm rs1))
+
+;; Helper for emitting the `fcvt.l.d` instruction.
+(decl rv_fcvtld (FRM FReg) XReg)
+(rule (rv_fcvtld frm rs1) (fpu_rr (FpuOPRR.FcvtLD) $I64 frm rs1))
+
+;; Helper for emitting the `fcvt.wu.d` instruction.
+(decl rv_fcvtwud (FRM FReg) XReg)
+(rule (rv_fcvtwud frm rs1) (fpu_rr (FpuOPRR.FcvtWuD) $I64 frm rs1))
+
+;; Helper for emitting the `fcvt.lu.d` instruction.
+(decl rv_fcvtlud (FRM FReg) XReg)
+(rule (rv_fcvtlud frm rs1) (fpu_rr (FpuOPRR.FcvtLuD) $I64 frm rs1))
+
+;; Helper for emitting the `fcvt.w.*` instructions.
+(decl rv_fcvtw (Type FRM FReg) XReg)
+(rule (rv_fcvtw $F32 frm rs1) (rv_fcvtws frm rs1))
+(rule (rv_fcvtw $F64 frm rs1) (rv_fcvtwd frm rs1))
+
+;; Helper for emitting the `fcvt.l.*` instructions.
+(decl rv_fcvtl (Type FRM FReg) XReg)
+(rule (rv_fcvtl $F32 frm rs1) (rv_fcvtls frm rs1))
+(rule (rv_fcvtl $F64 frm rs1) (rv_fcvtld frm rs1))
+
+;; Helper for emitting the `fcvt.wu.*` instructions.
+(decl rv_fcvtwu (Type FRM FReg) XReg)
+(rule (rv_fcvtwu $F32 frm rs1) (rv_fcvtwus frm rs1))
+(rule (rv_fcvtwu $F64 frm rs1) (rv_fcvtwud frm rs1))
+
+;; Helper for emitting the `fcvt.lu.*` instructions.
+(decl rv_fcvtlu (Type FRM FReg) XReg)
+(rule (rv_fcvtlu $F32 frm rs1) (rv_fcvtlus frm rs1))
+(rule (rv_fcvtlu $F64 frm rs1) (rv_fcvtlud frm rs1))
 
 ;; Helper for emitting the `fsgnj` ("Floating Point Sign Injection") instruction.
 ;; The output of this instruction is `rs1` with the sign bit from `rs2`
 ;; This implements the `copysign` operation
 (decl rv_fsgnj (Type FReg FReg) FReg)
-(rule (rv_fsgnj $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjS) $F32 rs1 rs2))
-(rule (rv_fsgnj $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjD) $F64 rs1 rs2))
+(rule (rv_fsgnj $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjS) $F32 (FRM.RNE) rs1 rs2))
+(rule (rv_fsgnj $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjD) $F64 (FRM.RNE) rs1 rs2))
 
 ;; Helper for emitting the `fsgnjn` ("Floating Point Sign Injection Negated") instruction.
 ;; The output of this instruction is `rs1` with the negated sign bit from `rs2`
 ;; When `rs1 == rs2` this implements the `neg` operation
 (decl rv_fsgnjn (Type FReg FReg) FReg)
-(rule (rv_fsgnjn $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjnS) $F32 rs1 rs2))
-(rule (rv_fsgnjn $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjnD) $F64 rs1 rs2))
+(rule (rv_fsgnjn $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjnS) $F32 (FRM.RTZ) rs1 rs2))
+(rule (rv_fsgnjn $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjnD) $F64 (FRM.RTZ) rs1 rs2))
 
 ;; Helper for emitting the `fneg` ("Floating Point Negate") instruction.
 ;; This instruction is a mnemonic for `fsgnjn rd, rs1, rs1`
@@ -1468,8 +1511,8 @@
 ;; The output of this instruction is `rs1` with the XOR of the sign bits from `rs1` and `rs2`.
 ;; When `rs1 == rs2` this implements `fabs`
 (decl rv_fsgnjx (Type FReg FReg) FReg)
-(rule (rv_fsgnjx $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjxS) $F32 rs1 rs2))
-(rule (rv_fsgnjx $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjxD) $F64 rs1 rs2))
+(rule (rv_fsgnjx $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjxS) $F32 (FRM.RDN) rs1 rs2))
+(rule (rv_fsgnjx $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjxD) $F64 (FRM.RDN) rs1 rs2))
 
 ;; Helper for emitting the `fabs` ("Floating Point Absolute") instruction.
 ;; This instruction is a mnemonic for `fsgnjx rd, rs1, rs1`
@@ -1478,18 +1521,18 @@
 
 ;; Helper for emitting the `feq` ("Float Equal") instruction.
 (decl rv_feq (Type FReg FReg) XReg)
-(rule (rv_feq $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FeqS) $I64 rs1 rs2))
-(rule (rv_feq $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FeqD) $I64 rs1 rs2))
+(rule (rv_feq $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FeqS) $I64 (FRM.RDN) rs1 rs2))
+(rule (rv_feq $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FeqD) $I64 (FRM.RDN) rs1 rs2))
 
 ;; Helper for emitting the `flt` ("Float Less Than") instruction.
 (decl rv_flt (Type FReg FReg) XReg)
-(rule (rv_flt $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FltS) $I64 rs1 rs2))
-(rule (rv_flt $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FltD) $I64 rs1 rs2))
+(rule (rv_flt $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FltS) $I64 (FRM.RTZ) rs1 rs2))
+(rule (rv_flt $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FltD) $I64 (FRM.RTZ) rs1 rs2))
 
 ;; Helper for emitting the `fle` ("Float Less Than or Equal") instruction.
 (decl rv_fle (Type FReg FReg) XReg)
-(rule (rv_fle $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FleS) $I64 rs1 rs2))
-(rule (rv_fle $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FleD) $I64 rs1 rs2))
+(rule (rv_fle $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FleS) $I64 (FRM.RNE) rs1 rs2))
+(rule (rv_fle $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FleD) $I64 (FRM.RNE) rs1 rs2))
 
 ;; Helper for emitting the `fgt` ("Float Greater Than") instruction.
 ;; Note: The arguments are reversed
@@ -1503,13 +1546,13 @@
 
 ;; Helper for emitting the `fmin` instruction.
 (decl rv_fmin (Type FReg FReg) FReg)
-(rule (rv_fmin $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FminS) $F32 rs1 rs2))
-(rule (rv_fmin $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FminD) $F64 rs1 rs2))
+(rule (rv_fmin $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FminS) $F32 (FRM.RNE) rs1 rs2))
+(rule (rv_fmin $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FminD) $F64 (FRM.RNE) rs1 rs2))
 
 ;; Helper for emitting the `fmax` instruction.
 (decl rv_fmax (Type FReg FReg) FReg)
-(rule (rv_fmax $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FmaxS) $F32 rs1 rs2))
-(rule (rv_fmax $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FmaxD) $F64 rs1 rs2))
+(rule (rv_fmax $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FmaxS) $F32 (FRM.RTZ) rs1 rs2))
+(rule (rv_fmax $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FmaxD) $F64 (FRM.RTZ) rs1 rs2))
 
 
 ;; `Zba` Extension Instructions
@@ -1937,14 +1980,11 @@
 (rule (canonical_nan_u64 $F32) 0x7fc00000)
 (rule (canonical_nan_u64 $F64) 0x7ff8000000000000)
 
-(decl gen_default_frm () OptionFloatRoundingMode)
-(extern constructor gen_default_frm gen_default_frm)
-
 ;; Helper for emitting `MInst.FpuRR` instructions.
-(decl fpu_rr (FpuOPRR Type Reg) Reg)
-(rule (fpu_rr op ty src)
+(decl fpu_rr (FpuOPRR Type FRM Reg) Reg)
+(rule (fpu_rr op ty frm src)
       (let ((dst WritableReg (temp_writable_reg ty))
-            (_ Unit (emit (MInst.FpuRR op (gen_default_frm) dst src))))
+            (_ Unit (emit (MInst.FpuRR op frm dst src))))
         dst))
 
 ;; Helper for emitting `MInst.AluRRR` instructions.
@@ -1955,17 +1995,17 @@
         dst))
 
 ;; Helper for emitting `MInst.AluRRR` instructions.
-(decl fpu_rrr (FpuOPRRR Type Reg Reg) Reg)
-(rule (fpu_rrr op ty src1 src2)
+(decl fpu_rrr (FpuOPRRR Type FRM Reg Reg) Reg)
+(rule (fpu_rrr op ty frm src1 src2)
       (let ((dst WritableReg (temp_writable_reg ty))
-            (_ Unit (emit (MInst.FpuRRR op (gen_default_frm) dst src1 src2))))
+            (_ Unit (emit (MInst.FpuRRR op frm dst src1 src2))))
         dst))
 
 ;; Helper for emitting `MInst.FpuRRRR` instructions.
-(decl fpu_rrrr (FpuOPRRRR Type Reg Reg Reg) Reg)
-(rule (fpu_rrrr op ty src1 src2 src3)
+(decl fpu_rrrr (FpuOPRRRR Type FRM Reg Reg Reg) Reg)
+(rule (fpu_rrrr op ty frm src1 src2 src3)
       (let ((dst WritableReg (temp_writable_reg ty))
-            (_ Unit (emit (MInst.FpuRRRR op (gen_default_frm) dst src1 src2 src3))))
+            (_ Unit (emit (MInst.FpuRRRR op frm dst src1 src2 src3))))
         dst))
 
 
@@ -2479,16 +2519,6 @@
             (_ Unit (emit (MInst.ElfTlsGetAddr dst name))))
         dst))
 
-;;;;
-(decl gen_fcvt_int (bool FReg bool Type Type) XReg)
-(rule
-  (gen_fcvt_int is_sat rs is_signed in_type out_type)
-  (let
-    ((result WritableReg (temp_writable_reg out_type))
-      (tmp WritableFReg (temp_writable_freg))
-      (_ Unit (emit (MInst.FcvtToInt is_sat result tmp rs is_signed in_type out_type))))
-    (writable_reg_to_reg result)))
-
 ;;; some float binary operation
 ;;; 1. need move into x reister.
 ;;; 2. do the operation.
diff --git a/cranelift/codegen/src/isa/riscv64/inst/args.rs b/cranelift/codegen/src/isa/riscv64/inst/args.rs
index 7539e3172..e791d31a7 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/args.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/args.rs
@@ -1,7 +1,5 @@
 //! Riscv64 ISA definitions: instruction arguments.
 
-// Some variants are never constructed, but we still want them as options in the future.
-#![allow(dead_code)]
 use super::*;
 use crate::ir::condcodes::CondCode;
 
@@ -251,17 +249,8 @@ impl BranchFunct3 {
             BranchFunct3::Geu => 0b111,
         }
     }
-    pub(crate) fn op_name(self) -> &'static str {
-        match self {
-            BranchFunct3::Eq => "eq",
-            BranchFunct3::Ne => "ne",
-            BranchFunct3::Lt => "lt",
-            BranchFunct3::Ge => "ge",
-            BranchFunct3::Ltu => "ltu",
-            BranchFunct3::Geu => "geu",
-        }
-    }
 }
+
 impl IntegerCompare {
     pub(crate) fn op_code(self) -> u32 {
         0b1100011
@@ -345,10 +334,6 @@ impl FpuOPRRRR {
         }
     }
 
-    pub(crate) fn funct3(self, rounding_mode: Option<FRM>) -> u32 {
-        rounding_mode.unwrap_or_default().as_u32()
-    }
-
     pub(crate) fn op_code(self) -> u32 {
         match self {
             FpuOPRRRR::FmaddS => 0b1000011,
@@ -417,15 +402,6 @@ impl FpuOPRR {
         }
     }
 
-    // move from f register to x register.
-    pub(crate) fn move_f_to_x_op(ty: Type) -> Self {
-        match ty {
-            F32 => Self::FmvXW,
-            F64 => Self::FmvXD,
-            _ => unreachable!("ty:{:?}", ty),
-        }
-    }
-
     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 {
@@ -554,38 +530,6 @@ impl FpuOPRR {
             FpuOPRR::FsqrtD => 0b0101101,
         }
     }
-
-    pub(crate) fn funct3(self, rounding_mode: Option<FRM>) -> u32 {
-        let rounding_mode = rounding_mode.unwrap_or_default().as_u32();
-        match self {
-            FpuOPRR::FsqrtS => rounding_mode,
-            FpuOPRR::FcvtWS => rounding_mode,
-            FpuOPRR::FcvtWuS => rounding_mode,
-            FpuOPRR::FmvXW => 0b000,
-            FpuOPRR::FclassS => 0b001,
-            FpuOPRR::FcvtSw => rounding_mode,
-            FpuOPRR::FcvtSwU => rounding_mode,
-            FpuOPRR::FmvWX => 0b000,
-            FpuOPRR::FcvtLS => rounding_mode,
-            FpuOPRR::FcvtLuS => rounding_mode,
-            FpuOPRR::FcvtSL => rounding_mode,
-            FpuOPRR::FcvtSLU => rounding_mode,
-            FpuOPRR::FcvtLD => rounding_mode,
-            FpuOPRR::FcvtLuD => rounding_mode,
-            FpuOPRR::FmvXD => 0b000,
-            FpuOPRR::FcvtDL => rounding_mode,
-            FpuOPRR::FcvtDLu => rounding_mode,
-            FpuOPRR::FmvDX => 0b000,
-            FpuOPRR::FcvtSD => rounding_mode,
-            FpuOPRR::FcvtDS => rounding_mode,
-            FpuOPRR::FclassD => 0b001,
-            FpuOPRR::FcvtWD => rounding_mode,
-            FpuOPRR::FcvtWuD => rounding_mode,
-            FpuOPRR::FcvtDW => rounding_mode,
-            FpuOPRR::FcvtDWU => 0b000,
-            FpuOPRR::FsqrtD => rounding_mode,
-        }
-    }
 }
 
 impl FpuOPRRR {
@@ -618,41 +562,6 @@ impl FpuOPRRR {
         }
     }
 
-    pub fn funct3(self, rounding_mode: Option<FRM>) -> u32 {
-        let rounding_mode = rounding_mode.unwrap_or_default();
-        let rounding_mode = rounding_mode.as_u32();
-        match self {
-            Self::FaddS => rounding_mode,
-            Self::FsubS => rounding_mode,
-            Self::FmulS => rounding_mode,
-            Self::FdivS => rounding_mode,
-
-            Self::FsgnjS => 0b000,
-            Self::FsgnjnS => 0b001,
-            Self::FsgnjxS => 0b010,
-            Self::FminS => 0b000,
-            Self::FmaxS => 0b001,
-
-            Self::FeqS => 0b010,
-            Self::FltS => 0b001,
-            Self::FleS => 0b000,
-
-            Self::FaddD => rounding_mode,
-            Self::FsubD => rounding_mode,
-            Self::FmulD => rounding_mode,
-            Self::FdivD => rounding_mode,
-
-            Self::FsgnjD => 0b000,
-            Self::FsgnjnD => 0b001,
-            Self::FsgnjxD => 0b010,
-            Self::FminD => 0b000,
-            Self::FmaxD => 0b001,
-            Self::FeqD => 0b010,
-            Self::FltD => 0b001,
-            Self::FleD => 0b000,
-        }
-    }
-
     pub fn op_code(self) -> u32 {
         match self {
             Self::FaddS
@@ -1239,6 +1148,7 @@ impl FRM {
 
 impl FFlagsException {
     #[inline]
+    #[allow(dead_code)]
     pub(crate) fn mask(self) -> u32 {
         match self {
             FFlagsException::NV => 1 << 4,
@@ -1363,6 +1273,7 @@ impl StoreOP {
     }
 }
 
+#[allow(dead_code)]
 impl FClassResult {
     pub(crate) const fn bit(self) -> u32 {
         match self {
@@ -1695,15 +1606,8 @@ impl FloatRoundOP {
     }
 }
 
-pub(crate) fn f32_bits(f: f32) -> u32 {
-    u32::from_le_bytes(f.to_le_bytes())
-}
-pub(crate) fn f64_bits(f: f64) -> u64 {
-    u64::from_le_bytes(f.to_le_bytes())
-}
-
 ///
-pub(crate) fn f32_cvt_to_int_bounds(signed: bool, out_bits: u8) -> (f32, f32) {
+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.),
         (true, 16) => (i16::min_value() as f32 - 1., i16::max_value() as f32 + 1.),
@@ -1717,7 +1621,7 @@ pub(crate) fn f32_cvt_to_int_bounds(signed: bool, out_bits: u8) -> (f32, f32) {
     }
 }
 
-pub(crate) fn f64_cvt_to_int_bounds(signed: bool, out_bits: u8) -> (f64, f64) {
+pub(crate) fn f64_cvt_to_int_bounds(signed: bool, out_bits: u32) -> (f64, f64) {
     match (signed, out_bits) {
         (true, 8) => (i8::min_value() as f64 - 1., i8::max_value() as f64 + 1.),
         (true, 16) => (i16::min_value() as f64 - 1., i16::max_value() as f64 + 1.),
diff --git a/cranelift/codegen/src/isa/riscv64/inst/emit.rs b/cranelift/codegen/src/isa/riscv64/inst/emit.rs
index e85c466e4..cdc7619f8 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/emit.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/emit.rs
@@ -68,10 +68,6 @@ impl EmitState {
         self.stack_map.take()
     }
 
-    fn clear_post_insn(&mut self) {
-        self.stack_map = None;
-    }
-
     fn cur_srcloc(&self) -> RelSourceLoc {
         self.cur_srcloc
     }
@@ -159,7 +155,7 @@ impl Inst {
             } else {
                 FpuOPRRR::FeqD
             },
-            frm: None,
+            frm: FRM::RDN,
             rd: rd,
             rs1: rs,
             rs2: rs,
@@ -173,49 +169,12 @@ impl Inst {
             } else {
                 FpuOPRRR::FsgnjxD
             },
-            frm: None,
+            frm: FRM::RDN,
             rd: rd,
             rs1: rs,
             rs2: rs,
         }
     }
-    /// If a float is zero.
-    pub(crate) fn emit_if_float_not_zero(
-        tmp: Writable<Reg>,
-        rs: Reg,
-        ty: Type,
-        taken: CondBrTarget,
-        not_taken: CondBrTarget,
-    ) -> SmallInstVec<Inst> {
-        let mut insts = SmallInstVec::new();
-        let class_op = if ty == F32 {
-            FpuOPRR::FclassS
-        } else {
-            FpuOPRR::FclassD
-        };
-        insts.push(Inst::FpuRR {
-            alu_op: class_op,
-            frm: None,
-            rd: tmp,
-            rs: rs,
-        });
-        insts.push(Inst::AluRRImm12 {
-            alu_op: AluOPRRI::Andi,
-            rd: tmp,
-            rs: tmp.to_reg(),
-            imm12: Imm12::from_i16(FClassResult::is_zero_bits() as i16),
-        });
-        insts.push(Inst::CondBr {
-            taken,
-            not_taken,
-            kind: IntegerCompare {
-                kind: IntCC::Equal,
-                rs1: tmp.to_reg(),
-                rs2: zero_reg(),
-            },
-        });
-        insts
-    }
 
     /// Returns Some(VState) if this insturction is expecting a specific vector state
     /// before emission.
@@ -259,7 +218,6 @@ impl Inst {
             | Inst::Atomic { .. }
             | Inst::Select { .. }
             | Inst::AtomicCas { .. }
-            | Inst::FcvtToInt { .. }
             | Inst::RawData { .. }
             | Inst::AtomicStore { .. }
             | Inst::AtomicLoad { .. }
@@ -968,7 +926,7 @@ impl Inst {
             } => {
                 let x = alu_op.op_code()
                     | reg_to_gpr_num(rd.to_reg()) << 7
-                    | alu_op.funct3(frm) << 12
+                    | frm.as_u32() << 12
                     | reg_to_gpr_num(rs) << 15
                     | alu_op.rs2_funct5() << 20
                     | alu_op.funct7() << 25;
@@ -988,7 +946,7 @@ impl Inst {
             } => {
                 let x = alu_op.op_code()
                     | reg_to_gpr_num(rd.to_reg()) << 7
-                    | alu_op.funct3(frm) << 12
+                    | frm.as_u32() << 12
                     | reg_to_gpr_num(rs1) << 15
                     | reg_to_gpr_num(rs2) << 20
                     | alu_op.funct2() << 25
@@ -1005,7 +963,7 @@ impl Inst {
             } => {
                 let x: u32 = alu_op.op_code()
                     | reg_to_gpr_num(rd.to_reg()) << 7
-                    | (alu_op.funct3(frm)) << 12
+                    | frm.as_u32() << 12
                     | reg_to_gpr_num(rs1) << 15
                     | reg_to_gpr_num(rs2) << 20
                     | alu_op.funct7() << 25;
@@ -1337,7 +1295,7 @@ impl Inst {
                         } else {
                             FpuOPRRR::FsgnjD
                         },
-                        frm: None,
+                        frm: FRM::RNE,
                         rd: rd,
                         rs1: rm,
                         rs2: rm,
@@ -1951,175 +1909,6 @@ impl Inst {
                 .emit(&[], sink, emit_info, state);
             }
 
-            &Inst::FcvtToInt {
-                is_sat,
-                rd,
-                rs,
-                is_signed,
-                in_type,
-                out_type,
-                tmp,
-            } => {
-                let label_nan = sink.get_label();
-                let label_jump_over = sink.get_label();
-                // get if nan.
-                Inst::emit_not_nan(rd, rs, in_type).emit(&[], sink, emit_info, state);
-                // jump to nan.
-                Inst::CondBr {
-                    taken: CondBrTarget::Label(label_nan),
-                    not_taken: CondBrTarget::Fallthrough,
-                    kind: IntegerCompare {
-                        kind: IntCC::Equal,
-                        rs2: zero_reg(),
-                        rs1: rd.to_reg(),
-                    },
-                }
-                .emit(&[], sink, emit_info, state);
-
-                if !is_sat {
-                    let f32_bounds = f32_cvt_to_int_bounds(is_signed, out_type.bits() as u8);
-                    let f64_bounds = f64_cvt_to_int_bounds(is_signed, out_type.bits() as u8);
-                    if in_type == F32 {
-                        Inst::load_fp_constant32(tmp, f32_bits(f32_bounds.0), |_| {
-                            writable_spilltmp_reg()
-                        })
-                    } else {
-                        Inst::load_fp_constant64(tmp, f64_bits(f64_bounds.0), |_| {
-                            writable_spilltmp_reg()
-                        })
-                    }
-                    .iter()
-                    .for_each(|i| i.emit(&[], sink, emit_info, state));
-
-                    let le_op = if in_type == F32 {
-                        FpuOPRRR::FleS
-                    } else {
-                        FpuOPRRR::FleD
-                    };
-
-                    // rd := rs <= tmp
-                    Inst::FpuRRR {
-                        alu_op: le_op,
-                        frm: None,
-                        rd,
-                        rs1: rs,
-                        rs2: tmp.to_reg(),
-                    }
-                    .emit(&[], sink, emit_info, state);
-                    Inst::TrapIf {
-                        cc: IntCC::NotEqual,
-                        rs1: rd.to_reg(),
-                        rs2: zero_reg(),
-                        trap_code: TrapCode::IntegerOverflow,
-                    }
-                    .emit(&[], sink, emit_info, state);
-
-                    if in_type == F32 {
-                        Inst::load_fp_constant32(tmp, f32_bits(f32_bounds.1), |_| {
-                            writable_spilltmp_reg()
-                        })
-                    } else {
-                        Inst::load_fp_constant64(tmp, f64_bits(f64_bounds.1), |_| {
-                            writable_spilltmp_reg()
-                        })
-                    }
-                    .iter()
-                    .for_each(|i| i.emit(&[], sink, emit_info, state));
-
-                    // rd := rs >= tmp
-                    Inst::FpuRRR {
-                        alu_op: le_op,
-                        frm: None,
-                        rd,
-                        rs1: tmp.to_reg(),
-                        rs2: rs,
-                    }
-                    .emit(&[], sink, emit_info, state);
-
-                    Inst::TrapIf {
-                        cc: IntCC::NotEqual,
-                        rs1: rd.to_reg(),
-                        rs2: zero_reg(),
-                        trap_code: TrapCode::IntegerOverflow,
-                    }
-                    .emit(&[], sink, emit_info, state);
-                }
-                // convert to int normally.
-                Inst::FpuRR {
-                    frm: Some(FRM::RTZ),
-                    alu_op: FpuOPRR::float_convert_2_int_op(in_type, is_signed, out_type),
-                    rd,
-                    rs,
-                }
-                .emit(&[], sink, emit_info, state);
-                if out_type.bits() < 32 && is_signed {
-                    // load value part mask.
-                    Inst::load_constant_u32(
-                        writable_spilltmp_reg(),
-                        if 16 == out_type.bits() {
-                            (u16::MAX >> 1) as u64
-                        } else {
-                            // I8
-                            (u8::MAX >> 1) as u64
-                        },
-                    )
-                    .into_iter()
-                    .for_each(|x| x.emit(&[], sink, emit_info, state));
-                    // keep value part.
-                    Inst::AluRRR {
-                        alu_op: AluOPRRR::And,
-                        rd: writable_spilltmp_reg(),
-                        rs1: rd.to_reg(),
-                        rs2: spilltmp_reg(),
-                    }
-                    .emit(&[], sink, emit_info, state);
-                    // extact sign bit.
-                    Inst::AluRRImm12 {
-                        alu_op: AluOPRRI::Srli,
-                        rd: rd,
-                        rs: rd.to_reg(),
-                        imm12: Imm12::from_i16(31),
-                    }
-                    .emit(&[], sink, emit_info, state);
-                    Inst::AluRRImm12 {
-                        alu_op: AluOPRRI::Slli,
-                        rd: rd,
-                        rs: rd.to_reg(),
-                        imm12: Imm12::from_i16(if 16 == out_type.bits() {
-                            15
-                        } else {
-                            // I8
-                            7
-                        }),
-                    }
-                    .emit(&[], sink, emit_info, state);
-                    // make result,sign bit and value part.
-                    Inst::AluRRR {
-                        alu_op: AluOPRRR::Or,
-                        rd: rd,
-                        rs1: rd.to_reg(),
-                        rs2: spilltmp_reg(),
-                    }
-                    .emit(&[], sink, emit_info, state);
-                }
-
-                // I already have the result,jump over.
-                Inst::gen_jump(label_jump_over).emit(&[], sink, emit_info, state);
-                // here is nan , move 0 into rd register
-                sink.bind_label(label_nan, &mut state.ctrl_plane);
-                if is_sat {
-                    Inst::load_imm12(rd, Imm12::ZERO).emit(&[], sink, emit_info, state);
-                } else {
-                    // here is ud2.
-                    Inst::Udf {
-                        trap_code: TrapCode::BadConversionToInteger,
-                    }
-                    .emit(&[], sink, emit_info, state);
-                }
-                // bind jump_over
-                sink.bind_label(label_jump_over, &mut state.ctrl_plane);
-            }
-
             &Inst::LoadExtName {
                 rd,
                 ref name,
@@ -2368,7 +2157,7 @@ impl Inst {
 
                 // branch if f_tmp < rd
                 Inst::FpuRRR {
-                    frm: None,
+                    frm: FRM::RTZ,
                     alu_op: if ty == F32 {
                         FpuOPRRR::FltS
                     } else {
@@ -2394,7 +2183,7 @@ impl Inst {
                 //convert to int.
                 Inst::FpuRR {
                     alu_op: FpuOPRR::float_convert_2_int_op(ty, true, I64),
-                    frm: Some(op.to_frm()),
+                    frm: op.to_frm(),
                     rd: int_tmp,
                     rs: rs,
                 }
@@ -2406,7 +2195,7 @@ impl Inst {
                     } else {
                         FpuOPRR::FcvtDL
                     },
-                    frm: Some(op.to_frm()),
+                    frm: op.to_frm(),
                     rd,
                     rs: int_tmp.to_reg(),
                 }
@@ -2418,7 +2207,7 @@ impl Inst {
                     } else {
                         FpuOPRRR::FsgnjD
                     },
-                    frm: None,
+                    frm: FRM::RNE,
                     rd,
                     rs1: rd.to_reg(),
                     rs2: rs,
@@ -2434,7 +2223,7 @@ impl Inst {
                     } else {
                         FpuOPRRR::FaddD
                     },
-                    frm: None,
+                    frm: FRM::RNE,
                     rd: rd,
                     rs1: rs,
                     rs2: rs,
@@ -3296,24 +3085,6 @@ impl Inst {
                 dst: allocs.next_writable(dst),
             },
 
-            Inst::FcvtToInt {
-                is_sat,
-                rd,
-                rs,
-                is_signed,
-                in_type,
-                out_type,
-                tmp,
-            } => Inst::FcvtToInt {
-                is_sat,
-                is_signed,
-                in_type,
-                out_type,
-                rs: allocs.next(rs),
-                tmp: allocs.next_writable(tmp),
-                rd: allocs.next_writable(rd),
-            },
-
             Inst::LoadExtName { rd, name, offset } => Inst::LoadExtName {
                 rd: allocs.next_writable(rd),
                 name,
diff --git a/cranelift/codegen/src/isa/riscv64/inst/imms.rs b/cranelift/codegen/src/isa/riscv64/inst/imms.rs
index f1126fd91..259599b53 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/imms.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/imms.rs
@@ -199,10 +199,6 @@ impl Imm6 {
         value.try_into().ok().and_then(Imm6::maybe_from_i16)
     }
 
-    pub fn maybe_from_i64(value: i64) -> Option<Self> {
-        value.try_into().ok().and_then(Imm6::maybe_from_i16)
-    }
-
     pub fn maybe_from_imm12(value: Imm12) -> Option<Self> {
         Imm6::maybe_from_i16(value.as_i16())
     }
diff --git a/cranelift/codegen/src/isa/riscv64/inst/mod.rs b/cranelift/codegen/src/isa/riscv64/inst/mod.rs
index 5b588943c..875b53561 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/mod.rs
@@ -1,9 +1,5 @@
 //! This module defines riscv64-specific machine instruction types.
 
-// Some variants are not constructed, but we still want them as options in the future.
-#![allow(dead_code)]
-#![allow(non_camel_case_types)]
-
 use super::lower::isle::generated_code::{VecAMode, VecElementWidth, VecOpMasking};
 use crate::binemit::{Addend, CodeOffset, Reloc};
 pub use crate::ir::condcodes::IntCC;
@@ -44,12 +40,8 @@ mod emit_tests;
 
 use std::fmt::{Display, Formatter};
 
-pub(crate) type OptionReg = Option<Reg>;
-pub(crate) type OptionImm12 = Option<Imm12>;
-pub(crate) type OptionUimm5 = Option<UImm5>;
-pub(crate) type OptionFloatRoundingMode = Option<FRM>;
 pub(crate) type VecU8 = Vec<u8>;
-pub(crate) type VecWritableReg = Vec<Writable<Reg>>;
+
 //=============================================================================
 // Instructions (top level): definition
 
@@ -59,10 +51,6 @@ pub use crate::isa::riscv64::lower::isle::generated_code::{
 };
 use crate::isa::riscv64::lower::isle::generated_code::{CjOp, MInst, VecAluOpRRImm5, VecAluOpRRR};
 
-type BoxCallInfo = Box<CallInfo>;
-type BoxCallIndInfo = Box<CallIndInfo>;
-type BoxReturnCallInfo = Box<ReturnCallInfo>;
-
 /// Additional information for (direct) Call instructions, left out of line to lower the size of
 /// the Inst enum.
 #[derive(Clone, Debug)]
@@ -259,7 +247,7 @@ impl Inst {
         let tmp = alloc_tmp(I64);
         insts.extend(Self::load_constant_u32(tmp, const_data as u64));
         insts.push(Inst::FpuRR {
-            frm: None,
+            frm: FRM::RNE,
             alu_op: FpuOPRR::move_x_to_f_op(F32),
             rd,
             rs: tmp.to_reg(),
@@ -277,7 +265,7 @@ impl Inst {
         let tmp = alloc_tmp(I64);
         insts.extend(Self::load_constant_u64(tmp, const_data));
         insts.push(Inst::FpuRR {
-            frm: None,
+            frm: FRM::RNE,
             alu_op: FpuOPRR::move_x_to_f_op(F64),
             rd,
             rs: tmp.to_reg(),
@@ -556,11 +544,6 @@ fn riscv64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
             collector.reg_early_def(dst);
         }
 
-        &Inst::FcvtToInt { rd, rs, tmp, .. } => {
-            collector.reg_use(rs);
-            collector.reg_early_def(tmp);
-            collector.reg_early_def(rd);
-        }
         &Inst::RawData { .. } => {}
         &Inst::AtomicStore { src, p, .. } => {
             collector.reg_use(src);
@@ -1044,12 +1027,8 @@ impl Inst {
             x
         };
 
-        fn format_frm(rounding_mode: Option<FRM>) -> String {
-            if let Some(r) = rounding_mode {
-                format!(",{}", r.to_static_str(),)
-            } else {
-                "".into()
-            }
+        fn format_frm(rounding_mode: FRM) -> String {
+            format!(",{}", rounding_mode.to_static_str())
         }
 
         let mut empty_allocs = AllocationConsumer::default();
@@ -1203,29 +1182,6 @@ impl Inst {
                     step
                 )
             }
-            &Inst::FcvtToInt {
-                is_sat,
-                rd,
-                rs,
-                is_signed,
-                in_type,
-                out_type,
-                tmp,
-            } => {
-                let rs = format_reg(rs, allocs);
-                let tmp = format_reg(tmp.to_reg(), allocs);
-                let rd = format_reg(rd.to_reg(), allocs);
-                format!(
-                    "fcvt_to_{}int{}.{} {},{}##in_ty={} tmp={}",
-                    if is_signed { "s" } else { "u" },
-                    if is_sat { "_sat" } else { "" },
-                    out_type,
-                    rd,
-                    rs,
-                    in_type,
-                    tmp
-                )
-            }
             &Inst::AtomicCas {
                 offset,
                 t0,
@@ -1317,7 +1273,18 @@ impl Inst {
             } => {
                 let rs = format_reg(rs, allocs);
                 let rd = format_reg(rd.to_reg(), allocs);
-                format!("{} {},{}{}", alu_op.op_name(), rd, rs, format_frm(frm))
+                let frm = match alu_op {
+                    FpuOPRR::FmvXW
+                    | FpuOPRR::FmvWX
+                    | FpuOPRR::FmvXD
+                    | FpuOPRR::FmvDX
+                    | FpuOPRR::FclassS
+                    | FpuOPRR::FclassD
+                    | FpuOPRR::FcvtDW
+                    | FpuOPRR::FcvtDWU => String::new(),
+                    _ => format_frm(frm),
+                };
+                format!("{} {rd},{rs}{frm}", alu_op.op_name())
             }
             &Inst::FpuRRR {
                 alu_op,
@@ -1332,35 +1299,32 @@ impl Inst {
                 let rs1_is_rs2 = rs1 == rs2;
                 if rs1_is_rs2 && alu_op.is_copy_sign() {
                     // this is move instruction.
-                    format!(
-                        "fmv.{} {},{}",
-                        if alu_op.is_32() { "s" } else { "d" },
-                        rd,
-                        rs1
-                    )
+                    format!("fmv.{} {rd},{rs1}", if alu_op.is_32() { "s" } else { "d" })
                 } else if rs1_is_rs2 && alu_op.is_copy_neg_sign() {
-                    format!(
-                        "fneg.{} {},{}",
-                        if alu_op.is_32() { "s" } else { "d" },
-                        rd,
-                        rs1
-                    )
+                    format!("fneg.{} {rd},{rs1}", if alu_op.is_32() { "s" } else { "d" })
                 } else if rs1_is_rs2 && alu_op.is_copy_xor_sign() {
-                    format!(
-                        "fabs.{} {},{}",
-                        if alu_op.is_32() { "s" } else { "d" },
-                        rd,
-                        rs1
-                    )
+                    format!("fabs.{} {rd},{rs1}", if alu_op.is_32() { "s" } else { "d" })
                 } else {
-                    format!(
-                        "{} {},{},{}{}",
-                        alu_op.op_name(),
-                        rd,
-                        rs1,
-                        rs2,
-                        format_frm(frm)
-                    )
+                    let frm = match alu_op {
+                        FpuOPRRR::FsgnjS
+                        | FpuOPRRR::FsgnjnS
+                        | FpuOPRRR::FsgnjxS
+                        | FpuOPRRR::FsgnjD
+                        | FpuOPRRR::FsgnjnD
+                        | FpuOPRRR::FsgnjxD
+                        | FpuOPRRR::FminS
+                        | FpuOPRRR::FminD
+                        | FpuOPRRR::FmaxS
+                        | FpuOPRRR::FmaxD
+                        | FpuOPRRR::FeqS
+                        | FpuOPRRR::FeqD
+                        | FpuOPRRR::FltS
+                        | FpuOPRRR::FltD
+                        | FpuOPRRR::FleS
+                        | FpuOPRRR::FleD => String::new(),
+                        _ => format_frm(frm),
+                    };
+                    format!("{} {rd},{rs1},{rs2}{frm}", alu_op.op_name())
                 }
             }
             &Inst::FpuRRRR {
@@ -1969,6 +1933,7 @@ impl MachInstLabelUse for LabelUse {
 }
 
 impl LabelUse {
+    #[allow(dead_code)] // in case it's needed in the future
     fn offset_in_range(self, offset: i64) -> bool {
         let min = -(self.max_neg_range() as i64);
         let max = self.max_pos_range() as i64;
diff --git a/cranelift/codegen/src/isa/riscv64/inst/regs.rs b/cranelift/codegen/src/isa/riscv64/inst/regs.rs
index e78acee06..d17e19b37 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/regs.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/regs.rs
@@ -18,38 +18,46 @@ pub fn a0() -> Reg {
 
 // second argument of function call
 #[inline]
+#[allow(dead_code)]
 pub fn a1() -> Reg {
     x_reg(11)
 }
 
 // third argument of function call
 #[inline]
+#[allow(dead_code)]
 pub fn a2() -> Reg {
     x_reg(12)
 }
 
 #[inline]
+#[allow(dead_code)]
 pub fn writable_a0() -> Writable<Reg> {
     Writable::from_reg(a0())
 }
 #[inline]
+#[allow(dead_code)]
 pub fn writable_a1() -> Writable<Reg> {
     Writable::from_reg(a1())
 }
 #[inline]
+#[allow(dead_code)]
 pub fn writable_a2() -> Writable<Reg> {
     Writable::from_reg(a2())
 }
 
 #[inline]
+#[allow(dead_code)]
 pub fn fa0() -> Reg {
     f_reg(10)
 }
 #[inline]
+#[allow(dead_code)]
 pub fn writable_fa0() -> Writable<Reg> {
     Writable::from_reg(fa0())
 }
 #[inline]
+#[allow(dead_code)]
 pub fn writable_fa1() -> Writable<Reg> {
     Writable::from_reg(fa1())
 }
@@ -58,11 +66,6 @@ pub fn fa1() -> Reg {
     f_reg(11)
 }
 
-#[inline]
-pub fn fa7() -> Reg {
-    f_reg(17)
-}
-
 /// Get a reference to the zero-register.
 #[inline]
 pub fn zero_reg() -> Reg {
@@ -167,12 +170,6 @@ pub(crate) fn x_reg_range(start: usize, end: usize) -> Vec<Writable<Reg>> {
     regs
 }
 
-#[inline]
-pub fn v_reg(enc: usize) -> Reg {
-    let p_reg = PReg::new(enc, RegClass::Vector);
-    let v_reg = VReg::new(p_reg.index(), p_reg.class());
-    Reg::from(v_reg)
-}
 pub const fn pv_reg(enc: usize) -> PReg {
     PReg::new(enc, RegClass::Vector)
 }
diff --git a/cranelift/codegen/src/isa/riscv64/inst/vector.rs b/cranelift/codegen/src/isa/riscv64/inst/vector.rs
index 6ab2016f1..4d5ac4f61 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/vector.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/vector.rs
@@ -1,5 +1,4 @@
 use crate::isa::riscv64::inst::AllocationConsumer;
-use crate::isa::riscv64::inst::EmitState;
 use crate::isa::riscv64::lower::isle::generated_code::VecAluOpRRRR;
 use crate::isa::riscv64::lower::isle::generated_code::{
     VecAMode, VecAluOpRImm5, VecAluOpRR, VecAluOpRRImm5, VecAluOpRRR, VecAluOpRRRImm5, VecAvl,
@@ -1085,12 +1084,6 @@ impl VecAMode {
         }
     }
 
-    pub(crate) fn get_offset_with_state(&self, state: &EmitState) -> i64 {
-        match self {
-            VecAMode::UnitStride { base, .. } => base.get_offset_with_state(state),
-        }
-    }
-
     /// `mop` field, described in Table 7 of Section 7.2. Vector Load/Store Addressing Modes
     /// https://github.com/riscv/riscv-v-spec/blob/master/v-spec.adoc#72-vector-loadstore-addressing-modes
     pub fn mop(&self) -> u32 {
diff --git a/cranelift/codegen/src/isa/riscv64/lower.isle b/cranelift/codegen/src/isa/riscv64/lower.isle
index de00086d8..0d3a271f4 100644
--- a/cranelift/codegen/src/isa/riscv64/lower.isle
+++ b/cranelift/codegen/src/isa/riscv64/lower.isle
@@ -1500,7 +1500,7 @@
 
 ;;;; Rules for `fma` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 (rule 0 (lower (has_type (ty_scalar_float ty) (fma x y z)))
-  (rv_fmadd ty x y z))
+  (rv_fmadd ty (FRM.RNE) x y z))
 
 ;; (fma x y z) computes x * y + z
 ;; vfmacc computes vd[i] = +(vs1[i] * vs2[i]) + vd[i]
@@ -1539,7 +1539,7 @@
 
 ;;;; Rules for `sqrt` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 (rule 0 (lower (has_type (ty_scalar_float ty) (sqrt x)))
-  (rv_fsqrt ty x))
+  (rv_fsqrt ty (FRM.RNE) x))
 
 (rule 1 (lower (has_type (ty_vec_fits_in_register ty) (sqrt x)))
   (rv_vfsqrt_v x (unmasked) ty))
@@ -1651,7 +1651,7 @@
 
 ;;;;;  Rules for `fdemote`;;;;;;;;;;;;;;;;;;
 (rule (lower (fdemote x))
-  (rv_fcvtsd x))
+  (rv_fcvtsd (FRM.RNE) x))
 
 ;;;;;  Rules for `fvdemote`;;;;;;;;;;;;;;;;;
 
@@ -1670,7 +1670,7 @@
 ;;;; Rules for `fadd` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (rule 0 (lower (has_type (ty_scalar_float ty) (fadd x y)))
-  (rv_fadd ty x y))
+  (rv_fadd ty (FRM.RNE) x y))
 
 (rule 1 (lower (has_type (ty_vec_fits_in_register ty) (fadd x y)))
   (rv_vfadd_vv x y (unmasked) ty))
@@ -1684,7 +1684,7 @@
 
 ;;;; Rules for `fsub` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 (rule 0 (lower (has_type (ty_scalar_float ty) (fsub x y)))
-  (rv_fsub ty x y))
+  (rv_fsub ty (FRM.RNE) x y))
 
 (rule 1 (lower (has_type (ty_vec_fits_in_register ty) (fsub x y)))
   (rv_vfsub_vv x y (unmasked) ty))
@@ -1697,7 +1697,7 @@
 
 ;;;; Rules for `fmul` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 (rule 0 (lower (has_type (ty_scalar_float ty) (fmul x y)))
-  (rv_fmul ty x y))
+  (rv_fmul ty (FRM.RNE) x y))
 
 (rule 1 (lower (has_type (ty_vec_fits_in_register ty) (fmul x y)))
   (rv_vfmul_vv x y (unmasked) ty))
@@ -1711,7 +1711,7 @@
 
 ;;;; Rules for `fdiv` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 (rule 0 (lower (has_type (ty_scalar_float ty) (fdiv x y)))
-  (rv_fdiv ty x y))
+  (rv_fdiv ty (FRM.RNE) x y))
 
 (rule 1 (lower (has_type (ty_vec_fits_in_register ty) (fdiv x y)))
   (rv_vfdiv_vv x y (unmasked) ty))
@@ -1731,7 +1731,7 @@
   (let (;; Check if both inputs are not nan.
         (is_ordered FloatCompare (fcmp_to_float_compare (FloatCC.Ordered) ty x y))
         ;; `fadd` returns a nan if any of the inputs is a NaN.
-        (nan FReg (rv_fadd ty x y))
+        (nan FReg (rv_fadd ty (FRM.RNE) x y))
         (min FReg (rv_fmin ty x y)))
     (gen_select_freg is_ordered min nan)))
 
@@ -1757,7 +1757,7 @@
   (let (;; Check if both inputs are not nan.
         (is_ordered FloatCompare (fcmp_to_float_compare (FloatCC.Ordered) ty x y))
         ;; `fadd` returns a NaN if any of the inputs is a NaN.
-        (nan FReg (rv_fadd ty x y))
+        (nan FReg (rv_fadd ty (FRM.RNE) x y))
         (max FReg (rv_fmax ty x y)))
     (gen_select_freg is_ordered max nan)))
 
@@ -2231,17 +2231,81 @@
   (load_ext_name name 0))
 
 ;;;;;  Rules for `fcvt_to_uint`;;;;;;;;;
-(rule
-  (lower (has_type to (fcvt_to_uint v @ (value_type from))))
-  (gen_fcvt_int $false v $false from to))
+
+;; RISC-V float-to-integer conversion does not trap, but Cranelift semantics are
+;; to trap. This manually performs checks for NaN and out-of-bounds values and
+;; traps in such cases.
+;;
+;; TODO: could this perhaps be more optimal through inspection of the `fcsr`?
+;; Unsure whether that needs to be preserved across function calls and/or would
+;; cause other problems. Also unsure whether it's actually more performant.
+(rule (lower (has_type ity (fcvt_to_uint v @ (value_type fty))))
+  (let ((_ InstOutput (gen_trapz (rv_feq fty v v) (TrapCode.BadConversionToInteger)))
+        (min FReg (imm fty (fcvt_umin_bound fty $false)))
+        (_ InstOutput (gen_trapnz (rv_fle fty v min) (TrapCode.IntegerOverflow)))
+        (max FReg (imm fty (fcvt_umax_bound fty ity $false)))
+        (_ InstOutput (gen_trapnz (rv_fge fty v max) (TrapCode.IntegerOverflow))))
+    (lower_inbounds_fcvt_to_uint ity fty v)))
+
+(decl lower_inbounds_fcvt_to_uint (Type Type FReg) XReg)
+(rule 0 (lower_inbounds_fcvt_to_uint (fits_in_32 _) fty v)
+  (rv_fcvtwu fty (FRM.RTZ) v))
+(rule 1 (lower_inbounds_fcvt_to_uint $I64 fty v)
+  (rv_fcvtlu fty (FRM.RTZ) v))
 
 ;;;;;  Rules for `fcvt_to_sint`;;;;;;;;;
-(rule 0 (lower (has_type to (fcvt_to_sint v @ (value_type (ty_scalar_float from)))))
-  (gen_fcvt_int $false v $true from to))
+
+;; NB: see above with `fcvt_to_uint` as this is similar
+(rule (lower (has_type ity (fcvt_to_sint v @ (value_type fty))))
+  (let ((_ InstOutput (gen_trapz (rv_feq fty v v) (TrapCode.BadConversionToInteger)))
+        (min FReg (imm fty (fcvt_smin_bound fty ity $false)))
+        (_ InstOutput (gen_trapnz (rv_fle fty v min) (TrapCode.IntegerOverflow)))
+        (max FReg (imm fty (fcvt_smax_bound fty ity $false)))
+        (_ InstOutput (gen_trapnz (rv_fge fty v max) (TrapCode.IntegerOverflow))))
+    (lower_inbounds_fcvt_to_sint ity fty v)))
+
+(decl lower_inbounds_fcvt_to_sint (Type Type FReg) XReg)
+(rule 0 (lower_inbounds_fcvt_to_sint (fits_in_32 _) fty v)
+  (rv_fcvtw fty (FRM.RTZ) v))
+(rule 1 (lower_inbounds_fcvt_to_sint $I64 fty v)
+  (rv_fcvtl fty (FRM.RTZ) v))
 
 ;;;;;  Rules for `fcvt_to_sint_sat`;;;;;;;;;
+
 (rule 0 (lower (has_type to (fcvt_to_sint_sat v @ (value_type (ty_scalar_float from)))))
-  (gen_fcvt_int $true v $true from to))
+  (handle_fcvt_to_int_nan from v (lower_fcvt_to_sint_sat from to v)))
+
+;; Lowers to a `rv_fcvt*` instruction but handles 8/16-bit cases where the
+;; float is clamped before the conversion.
+(decl lower_fcvt_to_sint_sat (Type Type FReg) XReg)
+(rule 0 (lower_fcvt_to_sint_sat ty (fits_in_16 out_ty) v)
+  (let ((max FReg (imm ty (fcvt_smax_bound ty out_ty $true)))
+        (min FReg (imm ty (fcvt_smin_bound ty out_ty $true)))
+        (clamped FReg (rv_fmin ty max (rv_fmax ty min v))))
+    (rv_fcvtw ty (FRM.RTZ) clamped)))
+(rule 1 (lower_fcvt_to_sint_sat ty $I32 v) (rv_fcvtw ty (FRM.RTZ) v))
+(rule 1 (lower_fcvt_to_sint_sat ty $I64 v) (rv_fcvtl ty (FRM.RTZ) v))
+
+(decl fcvt_smax_bound (Type Type bool) u64)
+(extern constructor fcvt_smax_bound fcvt_smax_bound)
+(decl fcvt_smin_bound (Type Type bool) u64)
+(extern constructor fcvt_smin_bound fcvt_smin_bound)
+
+;; RISC-V float-to-int conversions generate the same output for NaN and +Inf,
+;; but Cranelift semantics are to produce 0 for NaN instead. This helper
+;; translates these semantics by taking the float being converted (with the type
+;; specified) and the native RISC-V output as an `XReg`. The returned `XReg`
+;; will be zeroed out if the float is NaN.
+;;
+;; This is done by comparing the float to itself, generating 0 if it's NaN. This
+;; bit is then negated to become either all-ones or all-zeros which is then
+;; and-ed against the native output. That'll produce all zeros if the input is
+;; NaN or the native output otherwise.
+(decl handle_fcvt_to_int_nan (Type FReg XReg) XReg)
+(rule (handle_fcvt_to_int_nan ty freg xreg)
+  (let ((is_not_nan XReg (rv_feq ty freg freg))
+        (not_nan_mask XReg (rv_neg is_not_nan)))
+    (rv_and xreg not_nan_mask)))
 
 (rule 1 (lower (has_type (ty_vec_fits_in_register _) (fcvt_to_sint_sat v @ (value_type from_ty))))
   (if-let zero (i8_to_imm5 0))
@@ -2250,8 +2314,25 @@
     (rv_vmerge_vim cvt zero is_nan from_ty)))
 
 ;;;;;  Rules for `fcvt_to_uint_sat`;;;;;;;;;
+
 (rule 0 (lower (has_type to (fcvt_to_uint_sat v @ (value_type (ty_scalar_float from)))))
-  (gen_fcvt_int $true v $false from to))
+  (handle_fcvt_to_int_nan from v (lower_fcvt_to_uint_sat from to v)))
+
+;; Lowers to a `rv_fcvt*` instruction but handles 8/16-bit cases where the
+;; float is clamped before the conversion.
+(decl lower_fcvt_to_uint_sat (Type Type FReg) XReg)
+(rule 0 (lower_fcvt_to_uint_sat ty (fits_in_16 out_ty) v)
+  (let ((max FReg (imm ty (fcvt_umax_bound ty out_ty $true)))
+        (min FReg (rv_fmvdx (zero_reg)))
+        (clamped FReg (rv_fmin ty max (rv_fmax ty min v))))
+    (rv_fcvtwu ty (FRM.RTZ) clamped)))
+(rule 1 (lower_fcvt_to_uint_sat ty $I32 v) (rv_fcvtwu ty (FRM.RTZ) v))
+(rule 1 (lower_fcvt_to_uint_sat ty $I64 v) (rv_fcvtlu ty (FRM.RTZ) v))
+
+(decl fcvt_umax_bound (Type Type bool) u64)
+(extern constructor fcvt_umax_bound fcvt_umax_bound)
+(decl fcvt_umin_bound (Type bool) u64)
+(extern constructor fcvt_umin_bound fcvt_umin_bound)
 
 (rule 1 (lower (has_type (ty_vec_fits_in_register _) (fcvt_to_uint_sat v @ (value_type from_ty))))
   (if-let zero (i8_to_imm5 0))
@@ -2261,44 +2342,44 @@
 
 ;;;;;  Rules for `fcvt_from_sint`;;;;;;;;;
 (rule 0 (lower (has_type $F32 (fcvt_from_sint v @ (value_type (fits_in_16 ty)))))
-  (rv_fcvtsl (sext v)))
+  (rv_fcvtsl (FRM.RNE) (sext v)))
 
 (rule 1 (lower (has_type $F32 (fcvt_from_sint v @ (value_type $I32))))
-  (rv_fcvtsw v))
+  (rv_fcvtsw (FRM.RNE) v))
 
 (rule 1 (lower (has_type $F32 (fcvt_from_sint v @ (value_type $I64))))
-  (rv_fcvtsl v))
+  (rv_fcvtsl (FRM.RNE) v))
 
 (rule 0 (lower (has_type $F64 (fcvt_from_sint v @ (value_type (fits_in_16 ty)))))
-  (rv_fcvtdl (sext v)))
+  (rv_fcvtdl (FRM.RNE) (sext v)))
 
 (rule 1 (lower (has_type $F64 (fcvt_from_sint v @ (value_type $I32))))
   (rv_fcvtdw v))
 
 (rule 1 (lower (has_type $F64 (fcvt_from_sint v @ (value_type $I64))))
-  (rv_fcvtdl v))
+  (rv_fcvtdl (FRM.RNE) v))
 
 (rule 2 (lower (has_type (ty_vec_fits_in_register _) (fcvt_from_sint v @ (value_type from_ty))))
   (rv_vfcvt_f_x_v v (unmasked) from_ty))
 
 ;;;;;  Rules for `fcvt_from_uint`;;;;;;;;;
 (rule 0 (lower (has_type $F32 (fcvt_from_uint v @ (value_type (fits_in_16 ty)))))
-  (rv_fcvtslu (zext v)))
+  (rv_fcvtslu (FRM.RNE) (zext v)))
 
 (rule 1 (lower (has_type $F32 (fcvt_from_uint v @ (value_type $I32))))
-  (rv_fcvtswu v))
+  (rv_fcvtswu (FRM.RNE) v))
 
 (rule 1 (lower (has_type $F32 (fcvt_from_uint v @ (value_type $I64))))
-  (rv_fcvtslu v))
+  (rv_fcvtslu (FRM.RNE) v))
 
 (rule 0 (lower (has_type $F64 (fcvt_from_uint v @ (value_type (fits_in_16 ty)))))
-  (rv_fcvtdlu (zext v)))
+  (rv_fcvtdlu (FRM.RNE) (zext v)))
 
 (rule 1 (lower (has_type $F64 (fcvt_from_uint v @ (value_type $I32))))
   (rv_fcvtdwu v))
 
 (rule 1 (lower (has_type $F64 (fcvt_from_uint v @ (value_type $I64))))
-  (rv_fcvtdlu v))
+  (rv_fcvtdlu (FRM.RNE) v))
 
 (rule 2 (lower (has_type (ty_vec_fits_in_register _) (fcvt_from_uint v @ (value_type from_ty))))
   (rv_vfcvt_f_xu_v v (unmasked) from_ty))
diff --git a/cranelift/codegen/src/isa/riscv64/lower/isle.rs b/cranelift/codegen/src/isa/riscv64/lower/isle.rs
index 232e35e08..ab5c6ddf1 100644
--- a/cranelift/codegen/src/isa/riscv64/lower/isle.rs
+++ b/cranelift/codegen/src/isa/riscv64/lower/isle.rs
@@ -303,10 +303,6 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend>
         Imm12::maybe_from_i64(val).unwrap()
     }
 
-    fn gen_default_frm(&mut self) -> OptionFloatRoundingMode {
-        None
-    }
-
     fn frm_bits(&mut self, frm: &FRM) -> UImm5 {
         UImm5::maybe_from_u8(frm.bits()).unwrap()
     }
@@ -541,6 +537,63 @@ impl generated_code::Context for RV64IsleContext<'_, '_, MInst, Riscv64Backend>
     fn bseti_imm(&mut self, i: u64) -> Option<Imm12> {
         self.binvi_imm(i)
     }
+
+    fn fcvt_smin_bound(&mut self, float: Type, int: Type, saturating: bool) -> u64 {
+        match (int, float) {
+            // Saturating cases for larger integers are handled using the
+            // `fcvt.{w,d}.{s,d}` instruction directly, that automatically
+            // saturates up/down to the correct limit.
+            //
+            // NB: i32/i64 don't use this function because the native RISC-V
+            // instruction does everything we already need, so only cases for
+            // i8/i16 are listed here.
+            (I8, F32) if saturating => f32::from(i8::MIN).to_bits().into(),
+            (I8, F64) if saturating => f64::from(i8::MIN).to_bits(),
+            (I16, F32) if saturating => f32::from(i16::MIN).to_bits().into(),
+            (I16, F64) if saturating => f64::from(i16::MIN).to_bits(),
+
+            (_, F32) if !saturating => f32_cvt_to_int_bounds(true, int.bits()).0.to_bits().into(),
+            (_, F64) if !saturating => f64_cvt_to_int_bounds(true, int.bits()).0.to_bits(),
+            _ => unimplemented!(),
+        }
+    }
+
+    fn fcvt_smax_bound(&mut self, float: Type, int: Type, saturating: bool) -> u64 {
+        // NB: see `fcvt_smin_bound` for some more comments
+        match (int, float) {
+            (I8, F32) if saturating => f32::from(i8::MAX).to_bits().into(),
+            (I8, F64) if saturating => f64::from(i8::MAX).to_bits(),
+            (I16, F32) if saturating => f32::from(i16::MAX).to_bits().into(),
+            (I16, F64) if saturating => f64::from(i16::MAX).to_bits(),
+
+            (_, F32) if !saturating => f32_cvt_to_int_bounds(true, int.bits()).1.to_bits().into(),
+            (_, F64) if !saturating => f64_cvt_to_int_bounds(true, int.bits()).1.to_bits(),
+            _ => unimplemented!(),
+        }
+    }
+
+    fn fcvt_umax_bound(&mut self, float: Type, int: Type, saturating: bool) -> u64 {
+        // NB: see `fcvt_smin_bound` for some more comments
+        match (int, float) {
+            (I8, F32) if saturating => f32::from(u8::MAX).to_bits().into(),
+            (I8, F64) if saturating => f64::from(u8::MAX).to_bits(),
+            (I16, F32) if saturating => f32::from(u16::MAX).to_bits().into(),
+            (I16, F64) if saturating => f64::from(u16::MAX).to_bits(),
+
+            (_, F32) if !saturating => f32_cvt_to_int_bounds(false, int.bits()).1.to_bits().into(),
+            (_, F64) if !saturating => f64_cvt_to_int_bounds(false, int.bits()).1.to_bits(),
+            _ => unimplemented!(),
+        }
+    }
+
+    fn fcvt_umin_bound(&mut self, float: Type, saturating: bool) -> u64 {
+        assert!(!saturating);
+        match float {
+            F32 => (-1.0f32).to_bits().into(),
+            F64 => (-1.0f64).to_bits(),
+            _ => unimplemented!(),
+        }
+    }
 }
 
 /// The main entry point for lowering with ISLE.
diff --git a/cranelift/codegen/src/isa/x64/abi.rs b/cranelift/codegen/src/isa/x64/abi.rs
index be005e102..d99933500 100644
--- a/cranelift/codegen/src/isa/x64/abi.rs
+++ b/cranelift/codegen/src/isa/x64/abi.rs
@@ -809,9 +809,9 @@ impl ABIMachineSpec for X64ABIMachineSpec {
 
     fn get_ext_mode(
         _call_conv: isa::CallConv,
-        _specified: ir::ArgumentExtension,
+        specified: ir::ArgumentExtension,
     ) -> ir::ArgumentExtension {
-        ir::ArgumentExtension::None
+        specified
     }
 
     fn compute_frame_layout(
diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs
index 4b0e7a071..651b784b2 100644
--- a/cranelift/fuzzgen/src/function_generator.rs
+++ b/cranelift/fuzzgen/src/function_generator.rs
@@ -757,7 +757,7 @@ fn valid_for_target(triple: &Triple, op: Opcode, args: &[Type], rets: &[Type]) -
                 (
                     Opcode::FcvtToUintSat | Opcode::FcvtToSintSat,
                     &[F32 | F64],
-                    &[I8 | I16 | I128]
+                    &[I128]
                 ),
                 // https://github.com/bytecodealliance/wasmtime/issues/5528
                 (
diff --git a/cranelift/isle/islec/Cargo.toml b/cranelift/isle/islec/Cargo.toml
index 96dee8579..340c2aafd 100644
--- a/cranelift/isle/islec/Cargo.toml
+++ b/cranelift/isle/islec/Cargo.toml
@@ -9,4 +9,4 @@ publish = false
 [dependencies]
 cranelift-isle = { version = "*", path = "../isle/", features = ["fancy-errors", "logging"] }
 env_logger = { workspace = true }
-clap = { workspace = true }
+clap = { workspace = true, features = ['default'] }
diff --git a/crates/bench-api/src/lib.rs b/crates/bench-api/src/lib.rs
index a2ec33a9f..bb4f6ed55 100644
--- a/crates/bench-api/src/lib.rs
+++ b/crates/bench-api/src/lib.rs
@@ -522,7 +522,7 @@ impl BenchState {
             store.set_epoch_deadline(1);
         }
         if let Some(fuel) = self.fuel {
-            store.add_fuel(fuel).unwrap();
+            store.set_fuel(fuel).unwrap();
         }
 
         let instance = self.linker.instantiate(&mut store, &module)?;
diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml
index 43980e8dc..1548b0634 100644
--- a/crates/c-api/Cargo.toml
+++ b/crates/c-api/Cargo.toml
@@ -11,7 +11,7 @@ publish = false
 
 [lib]
 name = "wasmtime"
-crate-type = ["staticlib", "cdylib"]
+crate-type = ["staticlib", "cdylib", "rlib"]
 doc = false
 test = false
 doctest = false
diff --git a/crates/c-api/include/wasmtime/async.h b/crates/c-api/include/wasmtime/async.h
index 955944c72..1b490003a 100644
--- a/crates/c-api/include/wasmtime/async.h
+++ b/crates/c-api/include/wasmtime/async.h
@@ -71,19 +71,24 @@ WASMTIME_CONFIG_PROP(void, async_support, bool)
 WASMTIME_CONFIG_PROP(void, async_stack_size, uint64_t)
 
 /**
- * \brief Configures a Store to yield execution of async WebAssembly code periodically.
+ * \brief Configures a Store to yield execution of async WebAssembly code
+ * periodically.
  *
- * When a Store is configured to consume fuel with `wasmtime_config_consume_fuel` 
- * this method will configure what happens when fuel runs out. Specifically executing
- * WebAssembly will be suspended and control will be yielded back to the caller.
+ * When a Store is configured to consume fuel with
+ * `wasmtime_config_consume_fuel` this method will configure what happens when
+ * fuel runs out. Specifically executing WebAssembly will be suspended and
+ * control will be yielded back to the caller.
  *
- * This is only suitable with use of a store associated with an async config because
- * only then are futures used and yields are possible.
+ * This is only suitable with use of a store associated with an async config
+ * because only then are futures used and yields are possible.
+ *
+ * \param context the context for the store to configure.
+ * \param interval the amount of fuel at which to yield. A value of 0 will
+ *        disable yielding.
  */
-WASM_API_EXTERN void wasmtime_context_out_of_fuel_async_yield(
-    wasmtime_context_t *context,
-    uint64_t injection_count,
-    uint64_t fuel_to_inject);
+WASM_API_EXTERN void
+wasmtime_context_fuel_async_yield_interval(wasmtime_context_t *context,
+                                           uint64_t interval);
 
 /**
  * \brief Configures epoch-deadline expiration to yield to the async caller and the update the deadline.
@@ -94,7 +99,7 @@ WASM_API_EXTERN void wasmtime_context_out_of_fuel_async_yield(
  * See the Rust documentation for more:
  * https://docs.wasmtime.dev/api/wasmtime/struct.Store.html#method.epoch_deadline_async_yield_and_update
  */
-WASM_API_EXTERN void wasmtime_context_epoch_deadline_async_yield_and_update(
+WASM_API_EXTERN wasmtime_error_t* wasmtime_context_epoch_deadline_async_yield_and_update(
     wasmtime_context_t *context,
     uint64_t delta);
 
@@ -170,8 +175,8 @@ typedef struct wasmtime_call_future wasmtime_call_future_t;
  * called again for a given future.
  *
  * This function returns false if execution has yielded either due to being out of fuel 
- * (see wasmtime_store_out_of_fuel_async_yield), or the epoch has been incremented enough 
- * (see wasmtime_store_epoch_deadline_async_yield_and_update). The function may also return false if 
+ * (see wasmtime_context_fuel_async_yield_interval), or the epoch has been incremented enough 
+ * (see wasmtime_context_epoch_deadline_async_yield_and_update). The function may also return false if 
  * asynchronous host functions have been called, which then calling this  function will call the 
  * continuation from the async host function.
  *
diff --git a/crates/c-api/include/wasmtime/config.h b/crates/c-api/include/wasmtime/config.h
index 38429f84b..b299d048e 100644
--- a/crates/c-api/include/wasmtime/config.h
+++ b/crates/c-api/include/wasmtime/config.h
@@ -371,7 +371,7 @@ WASM_API_EXTERN void wasmtime_config_cranelift_flag_set(wasm_config_t*, const ch
 /**
  * Return the data from a LinearMemory instance.
  *
- * The size in bytes as well as the maximum number of bytes that can be allocated should be 
+ * The size in bytes as well as the maximum number of bytes that can be allocated should be
  * returned as well.
  *
  * For more information about see the Rust documentation at
@@ -398,7 +398,7 @@ typedef wasmtime_error_t *(*wasmtime_memory_grow_callback_t)(
  * For more information see the Rust documentation at
  * https://docs.wasmtime.dev/api/wasmtime/trait.LinearMemory.html
  */
-typedef struct {
+typedef struct wasmtime_linear_memory {
   /// User provided value to be passed to get_memory and grow_memory
   void *env;
   /// Callback to get the memory and size of this LinearMemory
@@ -412,7 +412,7 @@ typedef struct {
 /**
  * A callback to create a new LinearMemory from the specified parameters.
  *
- * The result should be written to `memory_ret` and wasmtime will own the values written 
+ * The result should be written to `memory_ret` and wasmtime will own the values written
  * into that struct.
  *
  * This callback must be thread-safe.
@@ -435,7 +435,7 @@ typedef wasmtime_error_t *(*wasmtime_new_memory_callback_t)(
  * For more information see the Rust documentation at
  * https://docs.wasmtime.dev/api/wasmtime/trait.MemoryCreator.html
  */
-typedef struct {
+typedef struct wasmtime_memory_creator {
   /// User provided value to be passed to new_memory
   void* env;
   /// The callback to create new memory, must be thread safe
@@ -450,7 +450,7 @@ typedef struct {
  * Custom memory creators are used when creating host Memory objects or when creating instance
  * linear memories for the on-demand instance allocation strategy.
  *
- * The config does **not** take ownership of the #wasmtime_memory_creator_t passed in, but 
+ * The config does **not** take ownership of the #wasmtime_memory_creator_t passed in, but
  * instead copies all the values in the struct.
  *
  * For more information see the Rust documentation at
@@ -463,8 +463,8 @@ WASM_API_EXTERN void wasmtime_config_host_memory_creator_set(
 /**
  * \brief Configures whether copy-on-write memory-mapped data is used to initialize a linear memory.
  *
- * Initializing linear memory via a copy-on-write mapping can drastically improve instantiation costs 
- * of a WebAssembly module because copying memory is deferred. Additionally if a page of memory is 
+ * Initializing linear memory via a copy-on-write mapping can drastically improve instantiation costs
+ * of a WebAssembly module because copying memory is deferred. Additionally if a page of memory is
  * only ever read from WebAssembly and never written too then the same underlying page of data will
  * be reused between all instantiations of a module meaning that if a module is instantiated many
  * times this can lower the overall memory required needed to run that module.
diff --git a/crates/c-api/include/wasmtime/store.h b/crates/c-api/include/wasmtime/store.h
index ac67d1a1d..740d98a33 100644
--- a/crates/c-api/include/wasmtime/store.h
+++ b/crates/c-api/include/wasmtime/store.h
@@ -147,25 +147,6 @@ WASM_API_EXTERN void wasmtime_context_set_data(wasmtime_context_t* context, void
  */
 WASM_API_EXTERN void wasmtime_context_gc(wasmtime_context_t* context);
 
-/**
- * \brief Adds fuel to this context's store for wasm to consume while executing.
- *
- * For this method to work fuel consumption must be enabled via
- * #wasmtime_config_consume_fuel_set. By default a store starts with 0 fuel
- * for wasm to execute with (meaning it will immediately trap).
- * This function must be called for the store to have
- * some fuel to allow WebAssembly to execute.
- *
- * Note by default when fuel is entirely consumed it will cause
- * wasm to trap. If async_support is enabled, you can use 
- * #wasmtime_context_out_of_fuel_async_yield and the async APIs to yield
- * execution instead.
- *
- * If fuel is not enabled within this store then an error is returned. If fuel
- * is successfully added then NULL is returned.
- */
-WASM_API_EXTERN wasmtime_error_t *wasmtime_context_add_fuel(wasmtime_context_t *store, uint64_t fuel);
-
 /**
  * \brief Set fuel to this context's store for wasm to consume while executing.
  *
@@ -175,55 +156,24 @@ WASM_API_EXTERN wasmtime_error_t *wasmtime_context_add_fuel(wasmtime_context_t *
  * This function must be called for the store to have
  * some fuel to allow WebAssembly to execute.
  *
- * Note by default when fuel is entirely consumed it will cause
- * wasm to trap. If async_support is enabled, you can use 
- * #wasmtime_context_out_of_fuel_async_yield and the async APIs to yield
- * execution instead.
+ * Note that when fuel is entirely consumed it will cause wasm to trap.
  *
  * If fuel is not enabled within this store then an error is returned. If fuel
  * is successfully added then NULL is returned.
  */
-WASM_API_EXTERN wasmtime_error_t *wasmtime_context_reset_fuel(wasmtime_context_t *store, uint64_t fuel);
+WASM_API_EXTERN wasmtime_error_t *wasmtime_context_set_fuel(wasmtime_context_t *store, uint64_t fuel);
 
 /**
- * \brief Returns the amount of fuel consumed by this context's store execution
- * so far.
+ * \brief Returns the amount of fuel remaining in this context's store.
  *
  * If fuel consumption is not enabled via #wasmtime_config_consume_fuel_set
- * then this function will return false. Otherwise true is returned and the
+ * then this function will return an error. Otherwise `NULL` is returned and the
  * fuel parameter is filled in with fuel consumed so far.
  *
  * Also note that fuel, if enabled, must be originally configured via
- * #wasmtime_context_add_fuel.
- */
-WASM_API_EXTERN bool wasmtime_context_fuel_consumed(const wasmtime_context_t *context, uint64_t *fuel);
-
-/**
- * \brief Returns the amount of fuel remaining in this context's store execution
- * before engine traps execution.
- *
- * If fuel consumption is not enabled via #wasmtime_config_consume_fuel_set
- * then this function will return false. Otherwise true is returned and the
- * fuel parameter is filled in with remaining fuel.
- *
- * Also note that fuel, if enabled, must be originally configured via
- * #wasmtime_context_add_fuel.
- */
-WASM_API_EXTERN bool wasmtime_context_fuel_remaining(const wasmtime_context_t *context, uint64_t *fuel);
-
-/**
- * \brief Attempt to manually consume fuel from the store.
- *
- * If fuel consumption is not enabled via #wasmtime_config_consume_fuel_set then
- * this function will return an error. Otherwise this will attempt to consume
- * the specified amount of `fuel` from the store. If successful the remaining
- * amount of fuel is stored into `remaining`. If `fuel` couldn't be consumed
- * then an error is returned.
- *
- * Also note that fuel, if enabled, must be originally configured via
- * #wasmtime_context_add_fuel.
+ * #wasmtime_context_set_fuel.
  */
-WASM_API_EXTERN wasmtime_error_t *wasmtime_context_consume_fuel(wasmtime_context_t *context, uint64_t fuel, uint64_t *remaining);
+WASM_API_EXTERN wasmtime_error_t* wasmtime_context_get_fuel(const wasmtime_context_t *context, uint64_t *fuel);
 
 /**
  * \brief Configures WASI state within the specified store.
diff --git a/crates/c-api/src/async.rs b/crates/c-api/src/async.rs
index 60cf7d308..ffd9b10c1 100644
--- a/crates/c-api/src/async.rs
+++ b/crates/c-api/src/async.rs
@@ -1,6 +1,7 @@
 use std::ffi::c_void;
 use std::future::Future;
 use std::mem::{self, MaybeUninit};
+use std::num::NonZeroU64;
 use std::ops::Range;
 use std::pin::Pin;
 use std::sync::Arc;
@@ -36,12 +37,14 @@ pub extern "C" fn wasmtime_context_epoch_deadline_async_yield_and_update(
 }
 
 #[no_mangle]
-pub extern "C" fn wasmtime_context_out_of_fuel_async_yield(
+pub extern "C" fn wasmtime_context_fuel_async_yield_interval(
     mut store: CStoreContextMut<'_>,
-    injection_count: u64,
-    fuel_to_inject: u64,
-) {
-    store.out_of_fuel_async_yield(injection_count, fuel_to_inject);
+    interval: Option<NonZeroU64>,
+) -> Option<Box<wasmtime_error_t>> {
+    handle_result(
+        store.fuel_async_yield_interval(interval.map(|n| n.get())),
+        |()| {},
+    )
 }
 
 pub type wasmtime_func_async_callback_t = extern "C" fn(
diff --git a/crates/c-api/src/store.rs b/crates/c-api/src/store.rs
index d6c95e00c..844357fb2 100644
--- a/crates/c-api/src/store.rs
+++ b/crates/c-api/src/store.rs
@@ -209,54 +209,20 @@ pub extern "C" fn wasmtime_context_gc(mut context: CStoreContextMut<'_>) {
 }
 
 #[no_mangle]
-pub extern "C" fn wasmtime_context_add_fuel(
+pub extern "C" fn wasmtime_context_set_fuel(
     mut store: CStoreContextMut<'_>,
     fuel: u64,
 ) -> Option<Box<wasmtime_error_t>> {
-    crate::handle_result(store.add_fuel(fuel), |()| {})
+    crate::handle_result(store.set_fuel(fuel), |()| {})
 }
 
 #[no_mangle]
-pub extern "C" fn wasmtime_context_reset_fuel(
-    mut store: CStoreContextMut<'_>,
-    fuel: u64,
-) -> Option<Box<wasmtime_error_t>> {
-    crate::handle_result(store.reset_fuel(fuel), |()| {})
-}
-
-#[no_mangle]
-pub extern "C" fn wasmtime_context_fuel_consumed(store: CStoreContext<'_>, fuel: &mut u64) -> bool {
-    match store.fuel_consumed() {
-        Some(amt) => {
-            *fuel = amt;
-            true
-        }
-        None => false,
-    }
-}
-
-#[no_mangle]
-pub extern "C" fn wasmtime_context_fuel_remaining(
-    store: CStoreContextMut<'_>,
+pub extern "C" fn wasmtime_context_get_fuel(
+    store: CStoreContext<'_>,
     fuel: &mut u64,
-) -> bool {
-    match store.fuel_remaining() {
-        Some(remaining) => {
-            *fuel = remaining;
-            true
-        }
-        None => false,
-    }
-}
-
-#[no_mangle]
-pub extern "C" fn wasmtime_context_consume_fuel(
-    mut store: CStoreContextMut<'_>,
-    fuel: u64,
-    remaining_fuel: &mut u64,
 ) -> Option<Box<wasmtime_error_t>> {
-    crate::handle_result(store.consume_fuel(fuel), |remaining| {
-        *remaining_fuel = remaining;
+    crate::handle_result(store.get_fuel(), |amt| {
+        *fuel = amt;
     })
 }
 
diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml
index b09f3cfd4..3651744ea 100644
--- a/crates/environ/Cargo.toml
+++ b/crates/environ/Cargo.toml
@@ -28,7 +28,7 @@ wasmprinter = { workspace = true, optional = true }
 wasmtime-component-util = { workspace = true, optional = true }
 
 [dev-dependencies]
-clap = { workspace = true }
+clap = { workspace = true, features = ['default'] }
 env_logger = { workspace = true }
 wat = { workspace = true }
 
diff --git a/crates/fuzzing/src/generators/config.rs b/crates/fuzzing/src/generators/config.rs
index 74b0bfb72..a5914ee36 100644
--- a/crates/fuzzing/src/generators/config.rs
+++ b/crates/fuzzing/src/generators/config.rs
@@ -260,7 +260,7 @@ impl Config {
     pub fn configure_store(&self, store: &mut Store<StoreLimits>) {
         store.limiter(|s| s as &mut dyn wasmtime::ResourceLimiter);
         if self.wasmtime.consume_fuel {
-            store.add_fuel(u64::max_value()).unwrap();
+            store.set_fuel(u64::MAX).unwrap();
         }
         if self.wasmtime.epoch_interruption {
             // Without fuzzing of async execution, we can't test the
diff --git a/crates/fuzzing/src/oracles.rs b/crates/fuzzing/src/oracles.rs
index 11910cc0b..844654b4e 100644
--- a/crates/fuzzing/src/oracles.rs
+++ b/crates/fuzzing/src/oracles.rs
@@ -145,7 +145,7 @@ pub fn instantiate(wasm: &[u8], known_valid: bool, config: &generators::Config,
 
     let mut timeout_state = SignalOnDrop::default();
     match timeout {
-        Timeout::Fuel(fuel) => set_fuel(&mut store, fuel),
+        Timeout::Fuel(fuel) => store.set_fuel(fuel).unwrap(),
 
         // If a timeout is requested then we spawn a helper thread to wait for
         // the requested time and then send us a signal to get interrupted. We
@@ -598,7 +598,7 @@ pub fn table_ops(
     {
         fuzz_config.wasmtime.consume_fuel = true;
         let mut store = fuzz_config.to_store();
-        set_fuel(&mut store, 1_000);
+        store.set_fuel(1_000).unwrap();
 
         let wasm = ops.to_wasm_binary();
         log_wasm(&wasm);
@@ -836,20 +836,6 @@ impl Drop for SignalOnDrop {
     }
 }
 
-/// Set the amount of fuel in a store to a given value
-pub fn set_fuel<T>(store: &mut Store<T>, fuel: u64) {
-    // Determine the amount of fuel already within the store, if any, and
-    // add/consume as appropriate to set the remaining amount to` fuel`.
-    let remaining = store.consume_fuel(0).unwrap();
-    if fuel > remaining {
-        store.add_fuel(fuel - remaining).unwrap();
-    } else {
-        store.consume_fuel(remaining - fuel).unwrap();
-    }
-    // double-check that the store has the expected amount of fuel remaining
-    assert_eq!(store.consume_fuel(0).unwrap(), fuel);
-}
-
 /// Generate and execute a `crate::generators::component_types::TestCase` using the specified `input` to create
 /// arbitrary types and values.
 pub fn dynamic_component_api_target(input: &mut arbitrary::Unstructured) -> arbitrary::Result<()> {
diff --git a/crates/runtime/src/trampolines/riscv64.rs b/crates/runtime/src/trampolines/riscv64.rs
index e3b06863b..b43afcbf4 100644
--- a/crates/runtime/src/trampolines/riscv64.rs
+++ b/crates/runtime/src/trampolines/riscv64.rs
@@ -17,7 +17,9 @@ macro_rules! wasm_to_libcall_trampoline {
                     sd ra, 32(t0)
 
                     // Tail call to the actual implementation of this libcall.
-                    j {}
+                .Lhi_{0}:
+                    auipc t0, %pcrel_hi({0})
+                    jalr x0, %pcrel_lo(.Lhi_{0})(t0)
 
                     .cfi_endproc
                 ",
diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs
index 31bc85e41..1e26adb2c 100644
--- a/crates/wasmtime/src/config.rs
+++ b/crates/wasmtime/src/config.rs
@@ -338,7 +338,7 @@ impl Config {
     ///
     /// * Alternatively you can enable the
     ///   [`Config::consume_fuel`](crate::Config::consume_fuel) method as well
-    ///   as [`crate::Store::out_of_fuel_async_yield`] When doing so this will
+    ///   as [`crate::Store::fuel_async_yield_interval`] When doing so this will
     ///   configure Wasmtime futures to yield periodically while they're
     ///   executing WebAssembly code. After consuming the specified amount of
     ///   fuel wasm futures will return `Poll::Pending` from their `poll`
@@ -456,8 +456,9 @@ impl Config {
     ///
     /// This can be used to deterministically prevent infinitely-executing
     /// WebAssembly code by instrumenting generated code to consume fuel as it
-    /// executes. When fuel runs out the behavior is defined by configuration
-    /// within a [`Store`], and by default a trap is raised.
+    /// executes. When fuel runs out a trap is raised, however [`Store`] can be
+    /// configured to yield execution periodically via
+    /// [`crate::Store::fuel_async_yield_interval`].
     ///
     /// Note that a [`Store`] starts with no fuel, so if you enable this option
     /// you'll have to be sure to pour some fuel into [`Store`] before
diff --git a/crates/wasmtime/src/func.rs b/crates/wasmtime/src/func.rs
index a551b64da..3a23d51d4 100644
--- a/crates/wasmtime/src/func.rs
+++ b/crates/wasmtime/src/func.rs
@@ -1895,50 +1895,26 @@ impl<T> Caller<'_, T> {
         self.store.gc()
     }
 
-    /// Returns the fuel consumed by this store.
-    ///
-    /// For more information see [`Store::fuel_consumed`](crate::Store::fuel_consumed)
-    pub fn fuel_consumed(&self) -> Option<u64> {
-        self.store.fuel_consumed()
-    }
-
     /// Returns the remaining fuel in the store.
     ///
-    /// For more information see [`Store::fuel_remaining`](crate::Store::fuel_remaining)
-    pub fn fuel_remaining(&self) -> Option<u64> {
-        self.store.fuel_remaining()
-    }
-
-    /// Inject more fuel into this store to be consumed when executing wasm code.
-    ///
-    /// For more information see [`Store::add_fuel`](crate::Store::add_fuel)
-    pub fn add_fuel(&mut self, fuel: u64) -> Result<()> {
-        self.store.add_fuel(fuel)
+    /// For more information see [`Store::get_fuel`](crate::Store::get_fuel)
+    pub fn get_fuel(&self) -> Result<u64> {
+        self.store.get_fuel()
     }
 
-    /// Synthetically consumes fuel from the store.
+    /// Set the amount of fuel in this store to be consumed when executing wasm code.
     ///
-    /// For more information see [`Store::consume_fuel`](crate::Store::consume_fuel)
-    pub fn consume_fuel(&mut self, fuel: u64) -> Result<u64> {
-        self.store.consume_fuel(fuel)
-    }
-
-    /// Configures this `Store` to trap whenever fuel runs out.
-    ///
-    /// For more information see
-    /// [`Store::out_of_fuel_trap`](crate::Store::out_of_fuel_trap)
-    pub fn out_of_fuel_trap(&mut self) {
-        self.store.out_of_fuel_trap()
+    /// For more information see [`Store::set_fuel`](crate::Store::set_fuel)
+    pub fn set_fuel(&mut self, fuel: u64) -> Result<()> {
+        self.store.set_fuel(fuel)
     }
 
-    /// Configures this `Store` to yield while executing futures whenever fuel
-    /// runs out.
+    /// Configures this `Store` to yield while executing futures every N units of fuel.
     ///
     /// For more information see
-    /// [`Store::out_of_fuel_async_yield`](crate::Store::out_of_fuel_async_yield)
-    pub fn out_of_fuel_async_yield(&mut self, injection_count: u64, fuel_to_inject: u64) {
-        self.store
-            .out_of_fuel_async_yield(injection_count, fuel_to_inject)
+    /// [`Store::fuel_async_yield_interval`](crate::Store::fuel_async_yield_interval)
+    pub fn fuel_async_yield_interval(&mut self, interval: Option<u64>) -> Result<()> {
+        self.store.fuel_async_yield_interval(interval)
     }
 }
 
diff --git a/crates/wasmtime/src/store.rs b/crates/wasmtime/src/store.rs
index e4745d3ad..edc6d2107 100644
--- a/crates/wasmtime/src/store.rs
+++ b/crates/wasmtime/src/store.rs
@@ -84,11 +84,11 @@ use crate::{module::ModuleRegistry, Engine, Module, Trap, Val, ValRaw};
 use crate::{Global, Instance, Memory};
 use anyhow::{anyhow, bail, Result};
 use std::cell::UnsafeCell;
-use std::convert::TryFrom;
 use std::fmt;
 use std::future::Future;
 use std::marker;
 use std::mem::{self, ManuallyDrop};
+use std::num::NonZeroU64;
 use std::ops::{Deref, DerefMut};
 use std::pin::Pin;
 use std::ptr;
@@ -306,12 +306,14 @@ pub struct StoreOpaque {
     memory_limit: usize,
     table_count: usize,
     table_limit: usize,
-    /// An adjustment to add to the fuel consumed value in `runtime_limits` above
-    /// to get the true amount of fuel consumed.
-    fuel_adj: i64,
     #[cfg(feature = "async")]
     async_state: AsyncState,
-    out_of_gas_behavior: OutOfGas,
+    // If fuel_yield_interval is enabled, then we store the remaining fuel (that isn't in
+    // runtime_limits) here. The total amount of fuel is the runtime limits and reserve added
+    // together. Then when we run out of gas, we inject the yield amount from the reserve
+    // until the reserve is empty.
+    fuel_reserve: u64,
+    fuel_yield_interval: Option<NonZeroU64>,
     /// Indexed data within this `Store`, used to store information about
     /// globals, functions, memories, etc.
     ///
@@ -458,15 +460,6 @@ enum StoreInstanceKind {
     Dummy,
 }
 
-#[derive(Copy, Clone)]
-enum OutOfGas {
-    Trap,
-    InjectFuel {
-        injection_count: u64,
-        fuel_to_inject: u64,
-    },
-}
-
 impl<T> Store<T> {
     /// Creates a new [`Store`] to be associated with the given [`Engine`] and
     /// `data` provided.
@@ -498,13 +491,13 @@ impl<T> Store<T> {
                 memory_limit: crate::DEFAULT_MEMORY_LIMIT,
                 table_count: 0,
                 table_limit: crate::DEFAULT_TABLE_LIMIT,
-                fuel_adj: 0,
                 #[cfg(feature = "async")]
                 async_state: AsyncState {
                     current_suspend: UnsafeCell::new(ptr::null()),
                     current_poll_cx: UnsafeCell::new(ptr::null_mut()),
                 },
-                out_of_gas_behavior: OutOfGas::Trap,
+                fuel_reserve: 0,
+                fuel_yield_interval: None,
                 store_data: ManuallyDrop::new(StoreData::new()),
                 default_caller: InstanceHandle::null(),
                 hostcall_val_storage: Vec::new(),
@@ -787,31 +780,17 @@ impl<T> Store<T> {
         self.inner.gc()
     }
 
-    /// Returns the amount of fuel consumed by this store's execution so far.
-    ///
-    /// If fuel consumption is not enabled via
-    /// [`Config::consume_fuel`](crate::Config::consume_fuel) then this
-    /// function will return `None`. Also note that fuel, if enabled, must be
-    /// originally configured via [`Store::add_fuel`] or [`Store::reset_fuel`].
-    ///
-    /// Note that this function returns the amount of fuel consumed since the
-    /// last time [`reset_fuel`][Store::reset_fuel] was called, or since the creation
-    /// of the store if it's never been called.
-    pub fn fuel_consumed(&self) -> Option<u64> {
-        self.inner.fuel_consumed()
-    }
-
-    /// Returns remaining fuel in this [`Store`].
+    /// Returns the amount fuel in this [`Store`].
     ///
     /// If fuel consumption is not enabled via
     /// [`Config::consume_fuel`](crate::Config::consume_fuel) then this
     /// function will return `None`. Also note that fuel, if enabled, must be
-    /// originally configured via [`Store::add_fuel`].
-    pub fn fuel_remaining(&mut self) -> Option<u64> {
-        self.inner.fuel_remaining()
+    /// originally configured via [`Store::set_fuel`].
+    pub fn get_fuel(&self) -> Result<u64> {
+        self.inner.get_fuel()
     }
 
-    /// Adds fuel to this [`Store`] for wasm to consume while executing.
+    /// Set the fuel to this [`Store`] for wasm to consume while executing.
     ///
     /// For this method to work fuel consumption must be enabled via
     /// [`Config::consume_fuel`](crate::Config::consume_fuel). By default a
@@ -824,66 +803,14 @@ impl<T> Store<T> {
     /// units, as any execution cost associated with them involves other
     /// instructions which do consume fuel.
     ///
-    /// Note that at this time when fuel is entirely consumed it will cause
-    /// wasm to trap. More usages of fuel are planned for the future.
+    /// Note that when fuel is entirely consumed it will cause wasm to trap.
     ///
     /// # Errors
     ///
     /// This function will return an error if fuel consumption is not enabled via
     /// [`Config::consume_fuel`](crate::Config::consume_fuel).
-    pub fn add_fuel(&mut self, fuel: u64) -> Result<()> {
-        self.inner.add_fuel(fuel)
-    }
-    ///
-    /// Set the remaining fuel to this [`Store`] for wasm to consume while executing.
-    ///
-    /// See [`Store::add_fuel`] for more information about fuel.
-    ///
-    /// This method will also reset the amount of fuel that has been consumed.
-    ///
-    /// # Errors
-    ///
-    /// This function will return an error if fuel consumption is not enabled via
-    /// [`Config::consume_fuel`](crate::Config::consume_fuel).
-    pub fn reset_fuel(&mut self, fuel: u64) -> Result<()> {
-        self.inner.reset_fuel(fuel)
-    }
-
-    /// Synthetically consumes fuel from this [`Store`].
-    ///
-    /// For this method to work fuel consumption must be enabled via
-    /// [`Config::consume_fuel`](crate::Config::consume_fuel).
-    ///
-    /// WebAssembly execution will automatically consume fuel but if so desired
-    /// the embedder can also consume fuel manually to account for relative
-    /// costs of host functions, for example.
-    ///
-    /// This function will attempt to consume `fuel` units of fuel from within
-    /// this store. If the remaining amount of fuel allows this then `Ok(N)` is
-    /// returned where `N` is the amount of remaining fuel. Otherwise an error
-    /// is returned and no fuel is consumed.
-    ///
-    /// # Errors
-    ///
-    /// This function will return an error either if fuel consumption is not
-    /// enabled via [`Config::consume_fuel`](crate::Config::consume_fuel) or if
-    /// `fuel` exceeds the amount of remaining fuel within this store.
-    pub fn consume_fuel(&mut self, fuel: u64) -> Result<u64> {
-        self.inner.consume_fuel(fuel)
-    }
-
-    /// Configures a [`Store`] to generate a [`Trap`] whenever it runs out of
-    /// fuel.
-    ///
-    /// When a [`Store`] is configured to consume fuel with
-    /// [`Config::consume_fuel`](crate::Config::consume_fuel) this method will
-    /// configure what happens when fuel runs out. Specifically a WebAssembly
-    /// trap will be raised and the current execution of WebAssembly will be
-    /// aborted.
-    ///
-    /// This is the default behavior for running out of fuel.
-    pub fn out_of_fuel_trap(&mut self) {
-        self.inner.out_of_fuel_trap()
+    pub fn set_fuel(&mut self, fuel: u64) -> Result<()> {
+        self.inner.set_fuel(fuel)
     }
 
     /// Configures a [`Store`] to yield execution of async WebAssembly code
@@ -891,11 +818,10 @@ impl<T> Store<T> {
     ///
     /// When a [`Store`] is configured to consume fuel with
     /// [`Config::consume_fuel`](crate::Config::consume_fuel) this method will
-    /// configure what happens when fuel runs out. Specifically executing
-    /// WebAssembly will be suspended and control will be yielded back to the
-    /// caller. This is only suitable with use of a store associated with an [async
-    /// config](crate::Config::async_support) because only then are futures used and yields
-    /// are possible.
+    /// configure WebAssembly to be suspended and control will be yielded back to the
+    /// caller every `interval` units of fuel consumed. This is only suitable with use of
+    /// a store associated with an [async config](crate::Config::async_support) because
+    /// only then are futures used and yields are possible.
     ///
     /// The purpose of this behavior is to ensure that futures which represent
     /// execution of WebAssembly do not execute too long inside their
@@ -908,21 +834,15 @@ impl<T> Store<T> {
     /// WebAssembly will continue to execute, just after giving the host an
     /// opportunity to do something else.
     ///
-    /// The `fuel_to_inject` parameter indicates how much fuel should be
-    /// automatically re-injected after fuel runs out. This is how much fuel
-    /// will be consumed between yields of an async future.
+    /// The `interval` parameter indicates how much fuel should be
+    /// consumed between yields of an async future. When fuel runs out wasm will trap.
     ///
-    /// The `injection_count` parameter indicates how many times this fuel will
-    /// be injected. Multiplying the two parameters is the total amount of fuel
-    /// this store is allowed before wasm traps.
+    /// # Error
     ///
-    /// # Panics
-    ///
-    /// This method will panic if it is not called on a store associated with an [async
+    /// This method will error if it is not called on a store associated with an [async
     /// config](crate::Config::async_support).
-    pub fn out_of_fuel_async_yield(&mut self, injection_count: u64, fuel_to_inject: u64) {
-        self.inner
-            .out_of_fuel_async_yield(injection_count, fuel_to_inject)
+    pub fn fuel_async_yield_interval(&mut self, interval: Option<u64>) -> Result<()> {
+        self.inner.fuel_async_yield_interval(interval)
     }
 
     /// Sets the epoch deadline to a certain number of ticks in the future.
@@ -1066,18 +986,11 @@ impl<'a, T> StoreContext<'a, T> {
         self.0.data()
     }
 
-    /// Returns the fuel consumed by this store.
-    ///
-    /// For more information see [`Store::fuel_consumed`].
-    pub fn fuel_consumed(&self) -> Option<u64> {
-        self.0.fuel_consumed()
-    }
-
-    /// Returns remaining fuel in this store.
+    /// Returns the remaining fuel in this store.
     ///
-    /// For more information see [`Store::fuel_remaining`]
-    pub fn fuel_remaining(&mut self) -> Option<u64> {
-        self.0.fuel_remaining()
+    /// For more information see [`Store::get_fuel`].
+    pub fn get_fuel(&self) -> Result<u64> {
+        self.0.get_fuel()
     }
 }
 
@@ -1108,55 +1021,25 @@ impl<'a, T> StoreContextMut<'a, T> {
         self.0.gc()
     }
 
-    /// Returns the fuel consumed by this store.
-    ///
-    /// For more information see [`Store::fuel_consumed`].
-    pub fn fuel_consumed(&self) -> Option<u64> {
-        self.0.fuel_consumed()
-    }
-
     /// Returns remaining fuel in this store.
     ///
-    /// For more information see [`Store::fuel_remaining`]
-    pub fn fuel_remaining(&self) -> Option<u64> {
-        self.0.fuel_remaining()
-    }
-
-    /// Inject more fuel into this store to be consumed when executing wasm code.
-    ///
-    /// For more information see [`Store::add_fuel`]
-    pub fn add_fuel(&mut self, fuel: u64) -> Result<()> {
-        self.0.add_fuel(fuel)
-    }
-
-    /// Synthetically consume fuel from this store.
-    ///
-    /// For more information see [`Store::consume_fuel`]
-    pub fn consume_fuel(&mut self, fuel: u64) -> Result<u64> {
-        self.0.consume_fuel(fuel)
-    }
-
-    /// Set the fuel for this store to be consumed when executing wasm code.
-    ///
-    /// For more information see [`Store::reset_fuel`]
-    pub fn reset_fuel(&mut self, fuel: u64) -> Result<()> {
-        self.0.reset_fuel(fuel)
+    /// For more information see [`Store::get_fuel`]
+    pub fn get_fuel(&self) -> Result<u64> {
+        self.0.get_fuel()
     }
 
-    /// Configures this `Store` to trap whenever fuel runs out.
+    /// Set the amount of fuel in this store.
     ///
-    /// For more information see [`Store::out_of_fuel_trap`]
-    pub fn out_of_fuel_trap(&mut self) {
-        self.0.out_of_fuel_trap()
+    /// For more information see [`Store::set_fuel`]
+    pub fn set_fuel(&mut self, fuel: u64) -> Result<()> {
+        self.0.set_fuel(fuel)
     }
 
-    /// Configures this `Store` to yield while executing futures whenever fuel
-    /// runs out.
+    /// Configures this `Store` to periodically yield while executing futures.
     ///
-    /// For more information see [`Store::out_of_fuel_async_yield`]
-    pub fn out_of_fuel_async_yield(&mut self, injection_count: u64, fuel_to_inject: u64) {
-        self.0
-            .out_of_fuel_async_yield(injection_count, fuel_to_inject)
+    /// For more information see [`Store::fuel_async_yield_interval`]
+    pub fn fuel_async_yield_interval(&mut self, interval: Option<u64>) -> Result<()> {
+        self.0.fuel_async_yield_interval(interval)
     }
 
     /// Sets the epoch deadline to a certain number of ticks in the future.
@@ -1224,6 +1107,45 @@ impl<T> StoreInner<T> {
     }
 }
 
+fn get_fuel(injected_fuel: i64, fuel_reserve: u64) -> u64 {
+    fuel_reserve.saturating_add_signed(-injected_fuel)
+}
+
+// Add remaining fuel from the reserve into the active fuel if there is any left.
+fn refuel(
+    injected_fuel: &mut i64,
+    fuel_reserve: &mut u64,
+    yield_interval: Option<NonZeroU64>,
+) -> bool {
+    let fuel = get_fuel(*injected_fuel, *fuel_reserve);
+    if fuel > 0 {
+        set_fuel(injected_fuel, fuel_reserve, yield_interval, fuel);
+        true
+    } else {
+        false
+    }
+}
+
+fn set_fuel(
+    injected_fuel: &mut i64,
+    fuel_reserve: &mut u64,
+    yield_interval: Option<NonZeroU64>,
+    new_fuel_amount: u64,
+) {
+    let interval = yield_interval.unwrap_or(NonZeroU64::MAX).get();
+    // If we're yielding periodically we only store the "active" amount of fuel into consumed_ptr
+    // for the VM to use.
+    let injected = std::cmp::min(interval, new_fuel_amount);
+    // Fuel in the VM is stored as an i64, so we have to cap the amount of fuel we inject into the
+    // VM at once to be i64 range.
+    let injected = std::cmp::min(injected, i64::MAX as u64);
+    // Add whatever is left over after injection to the reserve for later use.
+    *fuel_reserve = new_fuel_amount - injected;
+    // Within the VM we increment to count fuel, so inject a negative amount. The VM will halt when
+    // this counter is positive.
+    *injected_fuel = -(injected as i64);
+}
+
 #[doc(hidden)]
 impl StoreOpaque {
     pub fn id(&self) -> StoreId {
@@ -1465,35 +1387,55 @@ impl StoreOpaque {
         })
     }
 
-    pub fn fuel_consumed(&self) -> Option<u64> {
-        if !self.engine.config().tunables.consume_fuel {
-            return None;
-        }
-        let consumed = unsafe { *self.runtime_limits.fuel_consumed.get() };
-        Some(u64::try_from(self.fuel_adj + consumed).unwrap())
+    pub fn get_fuel(&self) -> Result<u64> {
+        anyhow::ensure!(
+            self.engine().config().tunables.consume_fuel,
+            "fuel is not configured in this store"
+        );
+        let injected_fuel = unsafe { *self.runtime_limits.fuel_consumed.get() };
+        Ok(get_fuel(injected_fuel, self.fuel_reserve))
     }
 
-    fn fuel_remaining(&self) -> Option<u64> {
-        if !self.engine.config().tunables.consume_fuel {
-            return None;
-        }
-        let consumed = unsafe { *self.runtime_limits.fuel_consumed.get() };
-        Some(u64::try_from(-consumed).unwrap())
+    fn refuel(&mut self) -> bool {
+        let injected_fuel = unsafe { &mut *self.runtime_limits.fuel_consumed.get() };
+        refuel(
+            injected_fuel,
+            &mut self.fuel_reserve,
+            self.fuel_yield_interval,
+        )
     }
 
-    fn out_of_fuel_trap(&mut self) {
-        self.out_of_gas_behavior = OutOfGas::Trap;
+    pub fn set_fuel(&mut self, fuel: u64) -> Result<()> {
+        anyhow::ensure!(
+            self.engine().config().tunables.consume_fuel,
+            "fuel is not configured in this store"
+        );
+        let injected_fuel = unsafe { &mut *self.runtime_limits.fuel_consumed.get() };
+        set_fuel(
+            injected_fuel,
+            &mut self.fuel_reserve,
+            self.fuel_yield_interval,
+            fuel,
+        );
+        Ok(())
     }
 
-    fn out_of_fuel_async_yield(&mut self, injection_count: u64, fuel_to_inject: u64) {
-        assert!(
-            self.async_support(),
-            "cannot use `out_of_fuel_async_yield` without enabling async support in the config"
+    pub fn fuel_async_yield_interval(&mut self, interval: Option<u64>) -> Result<()> {
+        anyhow::ensure!(
+            self.engine().config().tunables.consume_fuel,
+            "fuel is not configured in this store"
         );
-        self.out_of_gas_behavior = OutOfGas::InjectFuel {
-            injection_count,
-            fuel_to_inject,
-        };
+        anyhow::ensure!(
+            self.engine().config().async_support,
+            "async support is not configured in this store"
+        );
+        anyhow::ensure!(
+            interval != Some(0),
+            "fuel_async_yield_interval must not be 0"
+        );
+        self.fuel_yield_interval = interval.and_then(|i| NonZeroU64::new(i));
+        // Reset the fuel active + reserve states by resetting the amount.
+        self.set_fuel(self.get_fuel()?)
     }
 
     /// Yields execution to the caller on out-of-gas or epoch interruption.
@@ -1541,69 +1483,6 @@ impl StoreOpaque {
         }
     }
 
-    fn add_fuel(&mut self, fuel: u64) -> Result<()> {
-        anyhow::ensure!(
-            self.engine().config().tunables.consume_fuel,
-            "fuel is not configured in this store"
-        );
-
-        // Fuel is stored as an i64, so we need to cast it. If the provided fuel
-        // value overflows that just assume that i64::max will suffice. Wasm
-        // execution isn't fast enough to burn through i64::max fuel in any
-        // reasonable amount of time anyway.
-        let fuel = i64::try_from(fuel).unwrap_or(i64::max_value());
-        let adj = self.fuel_adj;
-        let consumed_ptr = unsafe { &mut *self.runtime_limits.fuel_consumed.get() };
-
-        match (consumed_ptr.checked_sub(fuel), adj.checked_add(fuel)) {
-            // If we succesfully did arithmetic without overflowing then we can
-            // just update our fields.
-            (Some(consumed), Some(adj)) => {
-                self.fuel_adj = adj;
-                *consumed_ptr = consumed;
-            }
-
-            // Otherwise something overflowed. Make sure that we preserve the
-            // amount of fuel that's already consumed, but otherwise assume that
-            // we were given infinite fuel.
-            _ => {
-                self.fuel_adj = i64::max_value();
-                *consumed_ptr = (*consumed_ptr + adj) - i64::max_value();
-            }
-        }
-
-        Ok(())
-    }
-
-    fn consume_fuel(&mut self, fuel: u64) -> Result<u64> {
-        anyhow::ensure!(
-            self.engine().config().tunables.consume_fuel,
-            "fuel is not configured in this store"
-        );
-        let consumed_ptr = unsafe { &mut *self.runtime_limits.fuel_consumed.get() };
-        match i64::try_from(fuel)
-            .ok()
-            .and_then(|fuel| consumed_ptr.checked_add(fuel))
-        {
-            Some(consumed) if consumed <= 0 => {
-                *consumed_ptr = consumed;
-                Ok(u64::try_from(-consumed).unwrap())
-            }
-            _ => bail!("not enough fuel remaining in store"),
-        }
-    }
-
-    fn reset_fuel(&mut self, fuel: u64) -> Result<()> {
-        anyhow::ensure!(
-            self.engine().config().tunables.consume_fuel,
-            "fuel is not configured in this store"
-        );
-        let fuel = i64::try_from(fuel).unwrap_or(i64::max_value());
-        self.fuel_adj = fuel;
-        unsafe { *self.runtime_limits.fuel_consumed.get() = -fuel };
-        Ok(())
-    }
-
     #[inline]
     pub fn signal_handler(&self) -> Option<*const SignalHandler<'static>> {
         let handler = self.signal_handler.as_ref()?;
@@ -2212,28 +2091,15 @@ unsafe impl<T> wasmtime_runtime::Store for StoreInner<T> {
         }
     }
 
-    fn out_of_gas(&mut self) -> Result<(), anyhow::Error> {
-        return match &mut self.out_of_gas_behavior {
-            OutOfGas::Trap => Err(Trap::OutOfFuel.into()),
-            #[cfg(feature = "async")]
-            OutOfGas::InjectFuel {
-                injection_count,
-                fuel_to_inject,
-            } => {
-                if *injection_count == 0 {
-                    return Err(Trap::OutOfFuel.into());
-                }
-                *injection_count -= 1;
-                let fuel = *fuel_to_inject;
-                self.async_yield_impl()?;
-                if fuel > 0 {
-                    self.add_fuel(fuel).unwrap();
-                }
-                Ok(())
-            }
-            #[cfg(not(feature = "async"))]
-            OutOfGas::InjectFuel { .. } => unreachable!(),
-        };
+    fn out_of_gas(&mut self) -> Result<()> {
+        if !self.refuel() {
+            return Err(Trap::OutOfFuel.into());
+        }
+        #[cfg(feature = "async")]
+        if self.fuel_yield_interval.is_some() {
+            self.async_yield_impl()?;
+        }
+        Ok(())
     }
 
     fn new_epoch(&mut self) -> Result<u64, anyhow::Error> {
@@ -2398,3 +2264,125 @@ impl<T: Copy> Drop for Reset<T> {
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::{get_fuel, refuel, set_fuel};
+    use std::num::NonZeroU64;
+
+    struct FuelTank {
+        pub consumed_fuel: i64,
+        pub reserve_fuel: u64,
+        pub yield_interval: Option<NonZeroU64>,
+    }
+
+    impl FuelTank {
+        fn new() -> Self {
+            FuelTank {
+                consumed_fuel: 0,
+                reserve_fuel: 0,
+                yield_interval: None,
+            }
+        }
+        fn get_fuel(&self) -> u64 {
+            get_fuel(self.consumed_fuel, self.reserve_fuel)
+        }
+        fn refuel(&mut self) -> bool {
+            refuel(
+                &mut self.consumed_fuel,
+                &mut self.reserve_fuel,
+                self.yield_interval,
+            )
+        }
+        fn set_fuel(&mut self, fuel: u64) {
+            set_fuel(
+                &mut self.consumed_fuel,
+                &mut self.reserve_fuel,
+                self.yield_interval,
+                fuel,
+            );
+        }
+    }
+
+    #[test]
+    fn smoke() {
+        let mut tank = FuelTank::new();
+        tank.set_fuel(10);
+        assert_eq!(tank.consumed_fuel, -10);
+        assert_eq!(tank.reserve_fuel, 0);
+
+        tank.yield_interval = NonZeroU64::new(10);
+        tank.set_fuel(25);
+        assert_eq!(tank.consumed_fuel, -10);
+        assert_eq!(tank.reserve_fuel, 15);
+    }
+
+    #[test]
+    fn does_not_lose_precision() {
+        let mut tank = FuelTank::new();
+        tank.set_fuel(u64::MAX);
+        assert_eq!(tank.get_fuel(), u64::MAX);
+
+        tank.set_fuel(i64::MAX as u64);
+        assert_eq!(tank.get_fuel(), i64::MAX as u64);
+
+        tank.set_fuel(i64::MAX as u64 + 1);
+        assert_eq!(tank.get_fuel(), i64::MAX as u64 + 1);
+    }
+
+    #[test]
+    fn yielding_does_not_lose_precision() {
+        let mut tank = FuelTank::new();
+
+        tank.yield_interval = NonZeroU64::new(10);
+        tank.set_fuel(u64::MAX);
+        assert_eq!(tank.get_fuel(), u64::MAX);
+        assert_eq!(tank.consumed_fuel, -10);
+        assert_eq!(tank.reserve_fuel, u64::MAX - 10);
+
+        tank.yield_interval = NonZeroU64::new(u64::MAX);
+        tank.set_fuel(u64::MAX);
+        assert_eq!(tank.get_fuel(), u64::MAX);
+        assert_eq!(tank.consumed_fuel, -i64::MAX);
+        assert_eq!(tank.reserve_fuel, u64::MAX - (i64::MAX as u64));
+
+        tank.yield_interval = NonZeroU64::new((i64::MAX as u64) + 1);
+        tank.set_fuel(u64::MAX);
+        assert_eq!(tank.get_fuel(), u64::MAX);
+        assert_eq!(tank.consumed_fuel, -i64::MAX);
+        assert_eq!(tank.reserve_fuel, u64::MAX - (i64::MAX as u64));
+    }
+
+    #[test]
+    fn refueling() {
+        // It's possible to fuel to have consumed over the limit as some instructions can consume
+        // multiple units of fuel at once. Refueling should be strict in it's consumption and not
+        // add more fuel than there is.
+        let mut tank = FuelTank::new();
+
+        tank.yield_interval = NonZeroU64::new(10);
+        tank.reserve_fuel = 42;
+        tank.consumed_fuel = 4;
+        assert!(tank.refuel());
+        assert_eq!(tank.reserve_fuel, 28);
+        assert_eq!(tank.consumed_fuel, -10);
+
+        tank.yield_interval = NonZeroU64::new(1);
+        tank.reserve_fuel = 8;
+        tank.consumed_fuel = 4;
+        assert_eq!(tank.get_fuel(), 4);
+        assert!(tank.refuel());
+        assert_eq!(tank.reserve_fuel, 3);
+        assert_eq!(tank.consumed_fuel, -1);
+        assert_eq!(tank.get_fuel(), 4);
+
+        tank.yield_interval = NonZeroU64::new(10);
+        tank.reserve_fuel = 3;
+        tank.consumed_fuel = 4;
+        assert_eq!(tank.get_fuel(), 0);
+        assert!(!tank.refuel());
+        assert_eq!(tank.reserve_fuel, 3);
+        assert_eq!(tank.consumed_fuel, 4);
+        assert_eq!(tank.get_fuel(), 0);
+    }
+}
diff --git a/docs/contributing-ci.md b/docs/contributing-ci.md
index 5b7306f66..fc69f243b 100644
--- a/docs/contributing-ci.md
+++ b/docs/contributing-ci.md
@@ -13,14 +13,32 @@ directory][dir] of the repository.
 
 ## PRs and CI
 
-Currently the full CI test suite runs on every Pull Request. All PRs need to
-have that lovely green checkmark before being candidates for being merged. If a
-test is failing you'll want to check out the logs on CI and fix it before the PR
-can be merged.
+Currently on sample of the full CI test suite is run on every Pull Request. CI
+on PRs is intended to be relatively quick and catch the majority of mistakes and
+errors. By default the test suite is run on x86\_64 Linux but this may change
+depending on what files the PR is modifying. The intention is to run "mostly
+relevant" CI on a PR by default.
 
 PR authors are expected to fix CI failures in their PR, unless the CI failure is
 systemic and unrelated to the PR. In that case other maintainers should be
-alerted to ensure that the problem can be addressed.
+alerted to ensure that the problem can be addressed. Some reviewers may also
+wait to perform a review until CI is green on the PR as otherwise it may
+indicate changes are needed.
+
+The Wasmtime repository uses GitHub's Merge Queue feature to merge PRs which.
+Entry in to the merge queue requires green CI on the PR beforehand. Maintainers
+who have approved a PR will flag it for entry into the merge queue, and the PR
+will automatically enter the merge queue once CI is green.
+
+When entering the merge queue a PR will have the full test suite executed which
+may include tests that weren't previously run on the PR. This may surface new
+failures, and contributors are expected to fix these failures as well.
+
+To force PRs to execute the full test suite, which takes longer than the default
+test suite for PRs, then contributors can place the string "prtest:full"
+somewhere in any commit of the PR. From that point on the PR will automatically
+run the full test suite as-if it were in the merge queue. Note that when going
+through the merge queue this will rerun tests.
 
 ## Tests run on CI
 
@@ -64,22 +82,23 @@ documentation of Wasmtime and Cranelift. Currently this consists of:
   Linux build in a really old CentOS container to have a very low glibc
   requirement.
 
-* Tarballs of the Python extension - also produced on the main three platforms
-  these wheels are compiled on each commit.
+* Tarballs of the Wasmtime C API - produced for the same set of platforms as the
+  CLI above.
 
 * Book and API documentation - the book is rendered with `mdbook` and we also
   build all documentation with `cargo doc`.
 
-Artifacts are produced for every single commit and every single PR. You should
-be able to find a downloadable version of all artifacts produced on the "runs"
-page in GitHub Actions. For example [here's an example
-job](https://github.com/bytecodealliance/wasmtime/actions/runs/50372673), and if
-you're looking at [a specific
-builder](https://github.com/bytecodealliance/wasmtime/runs/488719677?check_suite_focus=true)
-you can see the artifacts link in the top right. Note that artifacts don't
-become available until the whole run finishes.
-
-Commits merged into the `main` branch will rerun CI and will also produce
-artifacts as usual. On the `main` branch, however, documentation is pushed to
-the `gh-pages` branch as well, and binaries are pushed to the `dev` release on
-GitHub. Finally, tagged commits get a whole dedicated release to them too.
+* A source code tarball which is entirely self-contained. This source tarball
+  has all dependencies vendored so the network is not needed to build it.
+
+* WebAssembly adapters for the component model to translate
+  `wasi_snapshot_preview1` to WASI Preview 2.
+
+Artifacts are produced as part of the full CI suite. This means that artifacts
+are not produced on a PR by default but can be requested via "prtest:full". All
+runs through the merge queue though, which means all merges to `main`, will
+produce a full suite of artifacts. The latest artifacts are available through
+Wasmtime's [`dev` release][dev] and downloads are also available for recent CI
+runs through the CI page in GitHub Actions.
+
+[dev]: https://github.com/bytecodealliance/wasmtime/releases/tag/dev
diff --git a/docs/examples-minimal.md b/docs/examples-minimal.md
index 3f9e9891f..6c0d11cd9 100644
--- a/docs/examples-minimal.md
+++ b/docs/examples-minimal.md
@@ -7,8 +7,11 @@ of Wasmtime and how to best produce a minimal build of Wasmtime.
 ## Building a minimal CLI
 
 > *Note*: the exact numbers in this section were last updated on 2023-10-18 on a
-> macOS aarch64 host. They should provide a general ballpark estimate but should
-> be confirmed locally again before being totally relied upon.
+> macOS aarch64 host. For up-to-date numbers consult the artifacts in the [`dev`
+> release of Wasmtime][dev] where the `wasmtime-min` executable represents the
+> culmination of these steps.
+
+[dev]: https://github.com/bytecodealliance/wasmtime/releases/tag/dev
 
 Many Wasmtime embeddings go through the `wasmtime` crate as opposed to the
 `wasmtime` CLI executable, but to start out let's take a look at minimizing the
diff --git a/examples/async.cpp b/examples/async.cpp
index 2656e73d1..a5feae115 100644
--- a/examples/async.cpp
+++ b/examples/async.cpp
@@ -181,9 +181,8 @@ int main() {
   // This pointer is unowned.
   auto *context = wasmtime_store_context(store.get());
   // Configure the store to periodically yield control
-  wasmtime_context_out_of_fuel_async_yield(context,
-                                           /*injection_count=*/10,
-                                           /*fuel_to_inject=*/10000);
+  wasmtime_context_set_fuel(context, 100000);
+  wasmtime_context_fuel_async_yield_interval(context, /*interval=*/10000);
 
   auto compiled_module =
       compile_wat_module_from_file(engine.get(), "examples/async.wat");
diff --git a/examples/fuel.c b/examples/fuel.c
index 9b0eaa6b0..173bdcf58 100644
--- a/examples/fuel.c
+++ b/examples/fuel.c
@@ -43,9 +43,9 @@ int main() {
   assert(store != NULL);
   wasmtime_context_t *context = wasmtime_store_context(store);
 
-  error = wasmtime_context_add_fuel(context, 10000);
+  error = wasmtime_context_set_fuel(context, 10000);
   if (error != NULL)
-    exit_with_error("failed to add fuel", error, NULL);
+    exit_with_error("failed to set fuel", error, NULL);
 
   // Load our input file to parse it next
   FILE* file = fopen("examples/fuel.wat", "r");
@@ -93,7 +93,7 @@ int main() {
   // Call it repeatedly until it fails
   for (int n = 1; ; n++) {
     uint64_t fuel_before;
-    wasmtime_context_fuel_consumed(context, &fuel_before);
+    wasmtime_context_get_fuel(context, &fuel_before);
     wasmtime_val_t params[1];
     params[0].kind = WASMTIME_I32;
     params[0].of.i32 = n;
@@ -110,13 +110,13 @@ int main() {
     }
 
     uint64_t fuel_after;
-    wasmtime_context_fuel_consumed(context, &fuel_after);
+    wasmtime_context_get_fuel(context, &fuel_after);
     assert(results[0].kind == WASMTIME_I32);
-    printf("fib(%d) = %d [consumed %lld fuel]\n", n, results[0].of.i32, fuel_after - fuel_before);
+    printf("fib(%d) = %d [consumed %lu fuel]\n", n, results[0].of.i32, fuel_after - fuel_before);
 
-    error = wasmtime_context_add_fuel(context, fuel_after - fuel_before);
+    error = wasmtime_context_set_fuel(context, 10000);
     if (error != NULL)
-      exit_with_error("failed to add fuel", error, NULL);
+      exit_with_error("failed to set fuel", error, NULL);
   }
 
   // Clean up after ourselves at this point
diff --git a/examples/fuel.rs b/examples/fuel.rs
index e6f4c74ec..179c68d1c 100644
--- a/examples/fuel.rs
+++ b/examples/fuel.rs
@@ -10,14 +10,14 @@ fn main() -> Result<()> {
     config.consume_fuel(true);
     let engine = Engine::new(&config)?;
     let mut store = Store::new(&engine, ());
-    store.add_fuel(10_000)?;
+    store.set_fuel(10_000)?;
     let module = Module::from_file(store.engine(), "examples/fuel.wat")?;
     let instance = Instance::new(&mut store, &module, &[])?;
 
     // Invoke `fibonacci` export with higher and higher numbers until we exhaust our fuel.
     let fibonacci = instance.get_typed_func::<i32, i32>(&mut store, "fibonacci")?;
     for n in 1.. {
-        let fuel_before = store.fuel_consumed().unwrap();
+        let fuel_before = store.get_fuel().unwrap();
         let output = match fibonacci.call(&mut store, n) {
             Ok(v) => v,
             Err(e) => {
@@ -26,9 +26,9 @@ fn main() -> Result<()> {
                 break;
             }
         };
-        let fuel_consumed = store.fuel_consumed().unwrap() - fuel_before;
+        let fuel_consumed = fuel_before - store.get_fuel().unwrap();
         println!("fib({}) = {} [consumed {} fuel]", n, output, fuel_consumed);
-        store.add_fuel(fuel_consumed)?;
+        store.set_fuel(10_000)?;
     }
     Ok(())
 }
diff --git a/examples/threads.rs b/examples/threads.rs
index 3c3564833..1c75bd167 100644
--- a/examples/threads.rs
+++ b/examples/threads.rs
@@ -15,7 +15,7 @@ const N_REPS: i32 = 3;
 fn main() -> Result<()> {
     println!("Initializing...");
 
-    // Initialize global per-process state. This state will be shared amonst all
+    // Initialize global per-process state. This state will be shared amongst all
     // threads. Notably this includes the compiled module as well as a `Linker`,
     // which contains all our host functions we want to define.
     let engine = Engine::default();
diff --git a/examples/tokio/main.rs b/examples/tokio/main.rs
index e90f6de97..2073f3888 100644
--- a/examples/tokio/main.rs
+++ b/examples/tokio/main.rs
@@ -94,9 +94,11 @@ async fn run_wasm(inputs: Inputs) -> Result<(), Error> {
         .build();
     let mut store = Store::new(&inputs.env.engine, wasi);
 
+    // Put effectively unlimited fuel so it can run forever.
+    store.set_fuel(u64::MAX)?;
     // WebAssembly execution will be paused for an async yield every time it
-    // consumes 10000 fuel. Fuel will be refilled u64::MAX times.
-    store.out_of_fuel_async_yield(u64::MAX, 10000);
+    // consumes 10000 fuel.
+    store.fuel_async_yield_interval(Some(10000))?;
 
     // Instantiate into our own unique store using the shared linker, afterwards
     // acquiring the `_start` function for the module and executing it.
diff --git a/src/commands/run.rs b/src/commands/run.rs
index dfd24a002..df2fd673d 100644
--- a/src/commands/run.rs
+++ b/src/commands/run.rs
@@ -171,7 +171,7 @@ impl RunCommand {
         // If fuel has been configured, we want to add the configured
         // fuel amount to this store.
         if let Some(fuel) = self.run.common.wasm.fuel {
-            store.add_fuel(fuel)?;
+            store.set_fuel(fuel)?;
         }
 
         // Load the preload wasm modules.
diff --git a/src/commands/serve.rs b/src/commands/serve.rs
index ce9c2560a..a064100a8 100644
--- a/src/commands/serve.rs
+++ b/src/commands/serve.rs
@@ -179,7 +179,7 @@ impl ServeCommand {
         // If fuel has been configured, we want to add the configured
         // fuel amount to this store.
         if let Some(fuel) = self.run.common.wasm.fuel {
-            store.add_fuel(fuel)?;
+            store.set_fuel(fuel)?;
         }
 
         Ok(store)
diff --git a/winch/Cargo.toml b/winch/Cargo.toml
index d86a0b22a..cf83b27d1 100644
--- a/winch/Cargo.toml
+++ b/winch/Cargo.toml
@@ -19,7 +19,7 @@ wasmtime-environ = { workspace = true }
 target-lexicon = { workspace = true }
 anyhow = { workspace = true }
 wasmparser = { workspace = true }
-clap = { workspace = true }
+clap = { workspace = true, features = ['default'] }
 wat = { workspace = true }
 cranelift-codegen = { workspace = true }
 capstone = { workspace = true }


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 d42312e60..d0975502a 100644
--- a/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs
+++ b/cranelift/codegen/src/isa/riscv64/inst/emit_tests.rs
@@ -5,6 +5,10 @@ use crate::settings;
 use alloc::vec::Vec;
 use std::borrow::Cow;
 
+fn fa7() -> Reg {
+    f_reg(17)
+}
+
 #[test]
 fn test_riscv64_binemit() {
     struct TestUnit {
@@ -1156,7 +1160,7 @@ fn test_riscv64_binemit() {
     //
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: Some(FRM::RNE),
+            frm: FRM::RNE,
             alu_op: FpuOPRRR::FaddS,
             rd: writable_fa0(),
             rs1: fa0(),
@@ -1167,7 +1171,7 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: Some(FRM::RTZ),
+            frm: FRM::RTZ,
             alu_op: FpuOPRRR::FsubS,
             rd: writable_fa0(),
             rs1: fa0(),
@@ -1178,7 +1182,7 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: Some(FRM::RUP),
+            frm: FRM::RUP,
             alu_op: FpuOPRRR::FmulS,
             rd: writable_fa0(),
             rs1: fa0(),
@@ -1189,18 +1193,18 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRRR::FdivS,
             rd: writable_fa0(),
             rs1: fa0(),
             rs2: fa1(),
         },
-        "fdiv.s fa0,fa0,fa1",
+        "fdiv.s fa0,fa0,fa1,fcsr",
         0x18b57553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RNE,
             alu_op: FpuOPRRR::FsgnjS,
             rd: writable_fa0(),
             rs1: fa0(),
@@ -1211,7 +1215,7 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RTZ,
             alu_op: FpuOPRRR::FsgnjnS,
             rd: writable_fa0(),
             rs1: fa0(),
@@ -1223,7 +1227,7 @@ fn test_riscv64_binemit() {
 
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RDN,
             alu_op: FpuOPRRR::FsgnjxS,
             rd: writable_fa0(),
             rs1: fa0(),
@@ -1234,7 +1238,7 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RNE,
             alu_op: FpuOPRRR::FminS,
             rd: writable_fa0(),
             rs1: fa0(),
@@ -1246,7 +1250,7 @@ fn test_riscv64_binemit() {
 
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RTZ,
             alu_op: FpuOPRRR::FmaxS,
             rd: writable_fa0(),
             rs1: fa0(),
@@ -1257,7 +1261,7 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RDN,
             alu_op: FpuOPRRR::FeqS,
             rd: writable_a0(),
             rs1: fa0(),
@@ -1268,7 +1272,7 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RTZ,
             alu_op: FpuOPRRR::FltS,
             rd: writable_a0(),
             rs1: fa0(),
@@ -1279,7 +1283,7 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RNE,
             alu_op: FpuOPRRR::FleS,
             rd: writable_a0(),
             rs1: fa0(),
@@ -1292,51 +1296,51 @@ fn test_riscv64_binemit() {
     //
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRRR::FaddD,
             rd: writable_fa0(),
             rs1: fa0(),
             rs2: fa1(),
         },
-        "fadd.d fa0,fa0,fa1",
+        "fadd.d fa0,fa0,fa1,fcsr",
         0x2b57553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRRR::FsubD,
             rd: writable_fa0(),
             rs1: fa0(),
             rs2: fa1(),
         },
-        "fsub.d fa0,fa0,fa1",
+        "fsub.d fa0,fa0,fa1,fcsr",
         0xab57553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRRR::FmulD,
             rd: writable_fa0(),
             rs1: fa0(),
             rs2: fa1(),
         },
-        "fmul.d fa0,fa0,fa1",
+        "fmul.d fa0,fa0,fa1,fcsr",
         0x12b57553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRRR::FdivD,
             rd: writable_fa0(),
             rs1: fa0(),
             rs2: fa1(),
         },
-        "fdiv.d fa0,fa0,fa1",
+        "fdiv.d fa0,fa0,fa1,fcsr",
         0x1ab57553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RNE,
             alu_op: FpuOPRRR::FsgnjD,
             rd: writable_fa0(),
             rs1: fa0(),
@@ -1347,7 +1351,7 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RTZ,
             alu_op: FpuOPRRR::FsgnjnD,
             rd: writable_fa0(),
             rs1: fa0(),
@@ -1359,7 +1363,7 @@ fn test_riscv64_binemit() {
 
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RDN,
             alu_op: FpuOPRRR::FsgnjxD,
             rd: writable_fa0(),
             rs1: fa0(),
@@ -1370,7 +1374,7 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RNE,
             alu_op: FpuOPRRR::FminD,
             rd: writable_fa0(),
             rs1: fa0(),
@@ -1382,7 +1386,7 @@ fn test_riscv64_binemit() {
 
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RTZ,
             alu_op: FpuOPRRR::FmaxD,
             rd: writable_fa0(),
             rs1: fa0(),
@@ -1393,7 +1397,7 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RDN,
             alu_op: FpuOPRRR::FeqD,
             rd: writable_a0(),
             rs1: fa0(),
@@ -1404,7 +1408,7 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RTZ,
             alu_op: FpuOPRRR::FltD,
             rd: writable_a0(),
             rs1: fa0(),
@@ -1415,7 +1419,7 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRR {
-            frm: None,
+            frm: FRM::RNE,
             alu_op: FpuOPRRR::FleD,
             rd: writable_a0(),
             rs1: fa0(),
@@ -1428,7 +1432,7 @@ fn test_riscv64_binemit() {
     //
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: Some(FRM::RNE),
+            frm: FRM::RNE,
             alu_op: FpuOPRR::FsqrtS,
             rd: writable_fa0(),
             rs: fa1(),
@@ -1438,28 +1442,28 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRR::FcvtWS,
             rd: writable_a0(),
             rs: fa1(),
         },
-        "fcvt.w.s a0,fa1",
+        "fcvt.w.s a0,fa1,fcsr",
         0xc005f553,
     ));
 
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRR::FcvtWuS,
             rd: writable_a0(),
             rs: fa1(),
         },
-        "fcvt.wu.s a0,fa1",
+        "fcvt.wu.s a0,fa1,fcsr",
         0xc015f553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::RNE,
             alu_op: FpuOPRR::FmvXW,
             rd: writable_a0(),
             rs: fa1(),
@@ -1469,7 +1473,7 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::RTZ,
             alu_op: FpuOPRR::FclassS,
             rd: writable_a0(),
             rs: fa1(),
@@ -1480,28 +1484,28 @@ fn test_riscv64_binemit() {
 
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRR::FcvtSw,
             rd: writable_fa0(),
             rs: a0(),
         },
-        "fcvt.s.w fa0,a0",
+        "fcvt.s.w fa0,a0,fcsr",
         0xd0057553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRR::FcvtSwU,
             rd: writable_fa0(),
             rs: a0(),
         },
-        "fcvt.s.wu fa0,a0",
+        "fcvt.s.wu fa0,a0,fcsr",
         0xd0157553,
     ));
 
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::RNE,
             alu_op: FpuOPRR::FmvWX,
             rd: writable_fa0(),
             rs: a0(),
@@ -1511,81 +1515,81 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRR::FcvtLS,
             rd: writable_a0(),
             rs: fa0(),
         },
-        "fcvt.l.s a0,fa0",
+        "fcvt.l.s a0,fa0,fcsr",
         0xc0257553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRR::FcvtLuS,
             rd: writable_a0(),
             rs: fa0(),
         },
-        "fcvt.lu.s a0,fa0",
+        "fcvt.lu.s a0,fa0,fcsr",
         0xc0357553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
 
             alu_op: FpuOPRR::FcvtSL,
             rd: writable_fa0(),
             rs: a0(),
         },
-        "fcvt.s.l fa0,a0",
+        "fcvt.s.l fa0,a0,fcsr",
         0xd0257553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRR::FcvtSLU,
             rd: writable_fa0(),
             rs: a0(),
         },
-        "fcvt.s.lu fa0,a0",
+        "fcvt.s.lu fa0,a0,fcsr",
         0xd0357553,
     ));
 
     //
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRR::FsqrtD,
             rd: writable_fa0(),
             rs: fa1(),
         },
-        "fsqrt.d fa0,fa1",
+        "fsqrt.d fa0,fa1,fcsr",
         0x5a05f553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRR::FcvtWD,
             rd: writable_a0(),
             rs: fa1(),
         },
-        "fcvt.w.d a0,fa1",
+        "fcvt.w.d a0,fa1,fcsr",
         0xc205f553,
     ));
 
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRR::FcvtWuD,
             rd: writable_a0(),
             rs: fa1(),
         },
-        "fcvt.wu.d a0,fa1",
+        "fcvt.wu.d a0,fa1,fcsr",
         0xc215f553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::RNE,
             alu_op: FpuOPRR::FmvXD,
             rd: writable_a0(),
             rs: fa1(),
@@ -1595,7 +1599,7 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::RTZ,
             alu_op: FpuOPRR::FclassD,
             rd: writable_a0(),
             rs: fa1(),
@@ -1606,17 +1610,17 @@ fn test_riscv64_binemit() {
 
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRR::FcvtSD,
             rd: writable_fa0(),
             rs: fa0(),
         },
-        "fcvt.s.d fa0,fa0",
+        "fcvt.s.d fa0,fa0,fcsr",
         0x40157553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::RNE,
             alu_op: FpuOPRR::FcvtDWU,
             rd: writable_fa0(),
             rs: a0(),
@@ -1627,7 +1631,7 @@ fn test_riscv64_binemit() {
 
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::RNE,
             alu_op: FpuOPRR::FmvDX,
             rd: writable_fa0(),
             rs: a0(),
@@ -1637,49 +1641,49 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRR::FcvtLD,
             rd: writable_a0(),
             rs: fa0(),
         },
-        "fcvt.l.d a0,fa0",
+        "fcvt.l.d a0,fa0,fcsr",
         0xc2257553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRR::FcvtLuD,
             rd: writable_a0(),
             rs: fa0(),
         },
-        "fcvt.lu.d a0,fa0",
+        "fcvt.lu.d a0,fa0,fcsr",
         0xc2357553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRR::FcvtDL,
             rd: writable_fa0(),
             rs: a0(),
         },
-        "fcvt.d.l fa0,a0",
+        "fcvt.d.l fa0,a0,fcsr",
         0xd2257553,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRR::FcvtDLu,
             rd: writable_fa0(),
             rs: a0(),
         },
-        "fcvt.d.lu fa0,a0",
+        "fcvt.d.lu fa0,a0,fcsr",
         0xd2357553,
     ));
     //////////////////////
 
     insns.push(TestUnit::new(
         Inst::FpuRRRR {
-            frm: Some(FRM::RNE),
+            frm: FRM::RNE,
             alu_op: FpuOPRRRR::FmaddS,
             rd: writable_fa0(),
             rs1: fa0(),
@@ -1691,56 +1695,56 @@ fn test_riscv64_binemit() {
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRRRR::FmsubS,
             rd: writable_fa0(),
             rs1: fa0(),
             rs2: fa1(),
             rs3: fa7(),
         },
-        "fmsub.s fa0,fa0,fa1,fa7",
+        "fmsub.s fa0,fa0,fa1,fa7,fcsr",
         0x88b57547,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRRRR::FnmsubS,
             rd: writable_fa0(),
             rs1: fa0(),
             rs2: fa1(),
             rs3: fa7(),
         },
-        "fnmsub.s fa0,fa0,fa1,fa7",
+        "fnmsub.s fa0,fa0,fa1,fa7,fcsr",
         0x88b5754b,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRRRR::FnmaddS,
             rd: writable_fa0(),
             rs1: fa0(),
             rs2: fa1(),
             rs3: fa7(),
         },
-        "fnmadd.s fa0,fa0,fa1,fa7",
+        "fnmadd.s fa0,fa0,fa1,fa7,fcsr",
         0x88b5754f,
     ));
 
     insns.push(TestUnit::new(
         Inst::FpuRRRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRRRR::FmaddD,
             rd: writable_fa0(),
             rs1: fa0(),
             rs2: fa1(),
             rs3: fa7(),
         },
-        "fmadd.d fa0,fa0,fa1,fa7",
+        "fmadd.d fa0,fa0,fa1,fa7,fcsr",
         0x8ab57543,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRRR {
-            frm: None,
+            frm: FRM::Fcsr,
 
             alu_op: FpuOPRRRR::FmsubD,
             rd: writable_fa0(),
@@ -1748,31 +1752,31 @@ fn test_riscv64_binemit() {
             rs2: fa1(),
             rs3: fa7(),
         },
-        "fmsub.d fa0,fa0,fa1,fa7",
+        "fmsub.d fa0,fa0,fa1,fa7,fcsr",
         0x8ab57547,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRRRR::FnmsubD,
             rd: writable_fa0(),
             rs1: fa0(),
             rs2: fa1(),
             rs3: fa7(),
         },
-        "fnmsub.d fa0,fa0,fa1,fa7",
+        "fnmsub.d fa0,fa0,fa1,fa7,fcsr",
         0x8ab5754b,
     ));
     insns.push(TestUnit::new(
         Inst::FpuRRRR {
-            frm: None,
+            frm: FRM::Fcsr,
             alu_op: FpuOPRRRR::FnmaddD,
             rd: writable_fa0(),
             rs1: fa0(),
             rs2: fa1(),
             rs3: fa7(),
         },
-        "fnmadd.d fa0,fa0,fa1,fa7",
+        "fnmadd.d fa0,fa0,fa1,fa7,fcsr",
         0x8ab5754f,
     ));
 
@@ -2039,7 +2043,7 @@ fn test_riscv64_binemit() {
     insns.push(TestUnit::new(
         Inst::FpuRRR {
             alu_op: FpuOPRRR::FsgnjS,
-            frm: None,
+            frm: FRM::RNE,
             rd: writable_fa0(),
             rs1: fa1(),
             rs2: fa1(),
@@ -2050,7 +2054,7 @@ fn test_riscv64_binemit() {
     insns.push(TestUnit::new(
         Inst::FpuRRR {
             alu_op: FpuOPRRR::FsgnjD,
-            frm: None,
+            frm: FRM::RNE,
             rd: writable_fa0(),
             rs1: fa1(),
             rs2: fa1(),
@@ -2062,7 +2066,7 @@ fn test_riscv64_binemit() {
     insns.push(TestUnit::new(
         Inst::FpuRRR {
             alu_op: FpuOPRRR::FsgnjnS,
-            frm: None,
+            frm: FRM::RTZ,
             rd: writable_fa0(),
             rs1: fa1(),
             rs2: fa1(),
@@ -2073,7 +2077,7 @@ fn test_riscv64_binemit() {
     insns.push(TestUnit::new(
         Inst::FpuRRR {
             alu_op: FpuOPRRR::FsgnjnD,
-            frm: None,
+            frm: FRM::RTZ,
             rd: writable_fa0(),
             rs1: fa1(),
             rs2: fa1(),
@@ -2110,111 +2114,6 @@ fn make_test_flags() -> (settings::Flags, super::super::riscv_settings::Flags) {
     (flags, isa_flags)
 }
 
-#[derive(Debug)]
-pub(crate) struct DebugRTypeInst {
-    op_code: u32,
-    rd: u32,
-    funct3: u32,
-    rs1: u32,
-    rs2: u32,
-    funct7: u32,
-}
-
-impl DebugRTypeInst {
-    pub(crate) fn from_bs(x: &[u8]) -> Option<Self> {
-        if x.len() != 4 {
-            return None;
-        }
-        let a = [x[0], x[1], x[2], x[3]];
-        Some(Self::from_u32(u32::from_le_bytes(a)))
-    }
-
-    pub(crate) fn from_u32(x: u32) -> Self {
-        let op_code = x & 0b111_1111;
-        let x = x >> 7;
-        let rd = x & 0b1_1111;
-        let x = x >> 5;
-        let funct3 = x & 0b111;
-        let x = x >> 3;
-        let rs1 = x & 0b1_1111;
-        let x = x >> 5;
-        let rs2 = x & 0b1_1111;
-        let x = x >> 5;
-        let funct7 = x & 0b111_1111;
-        Self {
-            op_code,
-            rd,
-            funct3,
-            rs1,
-            rs2,
-            funct7,
-        }
-    }
-}
-
-#[derive(Debug)]
-pub(crate) struct DebugITypeInst {
-    op_code: u32,
-    rd: u32,
-    funct3: u32,
-    rs: u32,
-    imm12: u32,
-    shamt5: u32,
-    shamt6: u32,
-    funct7: u32,
-    funct6: u32,
-}
-
-impl DebugITypeInst {
-    pub(crate) fn from_bs(x: &[u8]) -> Self {
-        let a = [x[0], x[1], x[2], x[3]];
-        Self::from_u32(u32::from_le_bytes(a))
-    }
-    pub(crate) fn from_u32(x: u32) -> Self {
-        let op_code = x & 0b111_1111;
-        let x = x >> 7;
-        let rd = x & 0b1_1111;
-        let x = x >> 5;
-        let funct3 = x & 0b111;
-        let x = x >> 3;
-        let rs = x & 0b1_1111;
-        let x = x >> 5;
-        let imm12 = x & 0b1111_1111_1111;
-        let shamt5 = imm12 & 0b1_1111;
-        let shamt6 = imm12 & 0b11_1111;
-        let funct7 = imm12 >> 5;
-        let funct6 = funct7 >> 1;
-        Self {
-            op_code,
-            rd,
-            funct3,
-            rs,
-            imm12,
-            shamt5,
-            shamt6,
-            funct7,
-            funct6,
-        }
-    }
-    fn print_b(self) {
-        println!("opcode:{:b}", self.op_code);
-        println!("rd:{}", self.rd);
-        println!("funct3:{:b}", self.funct3);
-        println!("rs:{}", self.rs);
-        println!("shamt5:{:b}", self.shamt5);
-        println!("shamt6:{:b}", self.shamt6);
-        println!("funct6:{:b}", self.funct6);
-        println!("funct7:{:b}", self.funct7);
-    }
-}
-
-#[test]
-fn xxx() {
-    let x = 1240847763;
-    let x = DebugITypeInst::from_u32(x);
-    x.print_b();
-}
-
 #[test]
 fn riscv64_worst_case_instruction_size() {
     let (flags, isa_flags) = make_test_flags();
@@ -2223,61 +2122,6 @@ 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::FcvtToInt {
-        rd: writable_a0(),
-        rs: fa0(),
-        is_signed: true,
-        in_type: F64,
-        out_type: I8,
-        is_sat: false,
-        tmp: writable_a1(),
-    });
-    candidates.push(Inst::FcvtToInt {
-        rd: writable_a0(),
-        rs: fa0(),
-        is_signed: true,
-        in_type: F64,
-        out_type: I16,
-        is_sat: false,
-        tmp: writable_a1(),
-    });
-    candidates.push(Inst::FcvtToInt {
-        rd: writable_a0(),
-        rs: fa0(),
-        is_signed: true,
-        in_type: F32,
-        out_type: I8,
-        is_sat: false,
-        tmp: writable_a1(),
-    });
-    candidates.push(Inst::FcvtToInt {
-        rd: writable_a0(),
-        rs: fa0(),
-        is_signed: true,
-        in_type: F32,
-        out_type: I16,
-        is_sat: false,
-        tmp: writable_a1(),
-    });
-    candidates.push(Inst::FcvtToInt {
-        rd: writable_a0(),
-        rs: fa0(),
-        is_signed: true,
-        in_type: F64,
-        out_type: I8,
-        is_sat: false,
-        tmp: writable_a1(),
-    });
-    candidates.push(Inst::FcvtToInt {
-        rd: writable_a0(),
-        rs: fa0(),
-        is_signed: true,
-        in_type: F64,
-        out_type: I16,
-        is_sat: false,
-        tmp: writable_a1(),
-    });
-
     candidates.push(Inst::FloatRound {
         op: FloatRoundOP::Trunc,
         int_tmp: writable_a0(),
diff --git a/cranelift/filetests/filetests/isa/aarch64/uext-sext-handling.clif b/cranelift/filetests/filetests/isa/aarch64/uext-sext-handling.clif
new file mode 100644
index 000000000..473f3b7f1
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/aarch64/uext-sext-handling.clif
@@ -0,0 +1,36 @@
+test compile
+target aarch64
+
+; The aapcs64 call conv ignores the uext and sext flags
+function u0:0(i8) system_v {
+    sig0 = (i8 uext) system_v
+    fn0 = u0:0 sig0
+
+block0(v0: i8):
+    call fn0(v0)
+    return
+}
+
+; check: stp fp, lr, [sp, #-16]!
+; nextln: mov fp, sp
+; nextln: block0:
+; check-not: uxtb w0, w0
+; nextln: load_ext_name x2, User(userextname0)+0
+; nextln: blr x2
+
+; The aaple aarch64 call conv respects the uext and sext flags
+function u0:0(i8) apple_aarch64 {
+    sig0 = (i8 uext) apple_aarch64
+    fn0 = u0:0 sig0
+
+block0(v0: i8):
+    call fn0(v0)
+    return
+}
+
+; check: stp fp, lr, [sp, #-16]!
+; nextln: mov fp, sp
+; nextln: block0:
+; nextln: uxtb w0, w0
+; nextln: load_ext_name x4, User(userextname0)+0
+; nextln: blr x4
diff --git a/cranelift/filetests/filetests/isa/riscv64/fcvt-small.clif b/cranelift/filetests/filetests/isa/riscv64/fcvt-small.clif
index 26745e25a..24ec8d5ea 100644
--- a/cranelift/filetests/filetests/isa/riscv64/fcvt-small.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/fcvt-small.clif
@@ -11,13 +11,13 @@ block0(v0: i8):
 ; VCode:
 ; block0:
 ;   andi a2,a0,255
-;   fcvt.s.lu fa0,a2
+;   fcvt.s.lu fa0,a2,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   andi a2, a0, 0xff
-;   fcvt.s.lu fa0, a2
+;   fcvt.s.lu fa0, a2, rne
 ;   ret
 
 function u0:0(i8) -> f64 {
@@ -29,13 +29,13 @@ block0(v0: i8):
 ; VCode:
 ; block0:
 ;   andi a2,a0,255
-;   fcvt.d.lu fa0,a2
+;   fcvt.d.lu fa0,a2,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   andi a2, a0, 0xff
-;   fcvt.d.lu fa0, a2
+;   fcvt.d.lu fa0, a2, rne
 ;   ret
 
 function u0:0(i16) -> f32 {
@@ -48,14 +48,14 @@ block0(v0: i16):
 ; block0:
 ;   slli a2,a0,48
 ;   srli a4,a2,48
-;   fcvt.s.lu fa0,a4
+;   fcvt.s.lu fa0,a4,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   slli a2, a0, 0x30
 ;   srli a4, a2, 0x30
-;   fcvt.s.lu fa0, a4
+;   fcvt.s.lu fa0, a4, rne
 ;   ret
 
 function u0:0(i16) -> f64 {
@@ -68,14 +68,14 @@ block0(v0: i16):
 ; block0:
 ;   slli a2,a0,48
 ;   srli a4,a2,48
-;   fcvt.d.lu fa0,a4
+;   fcvt.d.lu fa0,a4,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
 ;   slli a2, a0, 0x30
 ;   srli a4, a2, 0x30
-;   fcvt.d.lu fa0, a4
+;   fcvt.d.lu fa0, a4, rne
 ;   ret
 
 function u0:0(f32) -> i8 {
@@ -86,29 +86,35 @@ block0(v0: f32):
 
 ; VCode:
 ; block0:
-;   fcvt_to_uint.i8 a0,fa0##in_ty=f32 tmp=fa3
+;   feq.s a2,fa0,fa0
+;   trap_if bad_toint##(a2 eq zero)
+;   lui a5,-264192
+;   fmv.w.x fa1,a5
+;   fle.s a3,fa0,fa1
+;   trap_if int_ovf##(a3 ne zero)
+;   lui a0,276480
+;   fmv.w.x fa2,a0
+;   fle.s a4,fa2,fa0
+;   trap_if int_ovf##(a4 ne zero)
+;   fcvt.wu.s a0,fa0,rtz
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.s a0, fa0, fa0
-;   beqz a0, 0x40
-;   auipc t6, 0
-;   lw t6, 0xc(t6)
-;   j 8
-;   .byte 0x00, 0x00, 0x80, 0xbf
-;   fmv.w.x fa3, t6
-;   fle.s a0, fa0, fa3
-;   beqz a0, 8
+;   feq.s a2, fa0, fa0
+;   bnez a2, 8
+;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
+;   lui a5, 0xbf800
+;   fmv.w.x fa1, a5
+;   fle.s a3, fa0, fa1
+;   beqz a3, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
-;   lui t6, 0x43800
-;   fmv.w.x fa3, t6
-;   fle.s a0, fa3, fa0
-;   beqz a0, 8
+;   lui a0, 0x43800
+;   fmv.w.x fa2, a0
+;   fle.s a4, fa2, fa0
+;   beqz a4, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
 ;   fcvt.wu.s a0, fa0, rtz
-;   j 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
 ;   ret
 
 function u0:0(f64) -> i8 {
@@ -119,34 +125,39 @@ block0(v0: f64):
 
 ; VCode:
 ; block0:
-;   fcvt_to_uint.i8 a0,fa0##in_ty=f64 tmp=fa3
+;   feq.d a2,fa0,fa0
+;   trap_if bad_toint##(a2 eq zero)
+;   lui a5,3071
+;   slli a1,a5,40
+;   fmv.d.x fa3,a1
+;   fle.d a5,fa0,fa3
+;   trap_if int_ovf##(a5 ne zero)
+;   lui a2,1031
+;   slli a4,a2,40
+;   fmv.d.x fa1,a4
+;   fle.d a2,fa1,fa0
+;   trap_if int_ovf##(a2 ne zero)
+;   fcvt.wu.d a0,fa0,rtz
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.d a0, fa0, fa0
-;   beqz a0, 0x54
-;   auipc t6, 0
-;   ld t6, 0xc(t6)
-;   j 0xc
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   .byte 0x00, 0x00, 0xf0, 0xbf
-;   fmv.d.x fa3, t6
-;   fle.d a0, fa0, fa3
-;   beqz a0, 8
+;   feq.d a2, fa0, fa0
+;   bnez a2, 8
+;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
+;   lui a5, 0xbff
+;   slli a1, a5, 0x28
+;   fmv.d.x fa3, a1
+;   fle.d a5, fa0, fa3
+;   beqz a5, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
-;   auipc t6, 0
-;   ld t6, 0xc(t6)
-;   j 0xc
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   .byte 0x00, 0x00, 0x70, 0x40
-;   fmv.d.x fa3, t6
-;   fle.d a0, fa3, fa0
-;   beqz a0, 8
+;   lui a2, 0x407
+;   slli a4, a2, 0x28
+;   fmv.d.x fa1, a4
+;   fle.d a2, fa1, fa0
+;   beqz a2, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
 ;   fcvt.wu.d a0, fa0, rtz
-;   j 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
 ;   ret
 
 function u0:0(f32) -> i16 {
@@ -157,29 +168,35 @@ block0(v0: f32):
 
 ; VCode:
 ; block0:
-;   fcvt_to_uint.i16 a0,fa0##in_ty=f32 tmp=fa3
+;   feq.s a2,fa0,fa0
+;   trap_if bad_toint##(a2 eq zero)
+;   lui a5,-264192
+;   fmv.w.x fa1,a5
+;   fle.s a3,fa0,fa1
+;   trap_if int_ovf##(a3 ne zero)
+;   lui a0,292864
+;   fmv.w.x fa2,a0
+;   fle.s a4,fa2,fa0
+;   trap_if int_ovf##(a4 ne zero)
+;   fcvt.wu.s a0,fa0,rtz
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.s a0, fa0, fa0
-;   beqz a0, 0x40
-;   auipc t6, 0
-;   lw t6, 0xc(t6)
-;   j 8
-;   .byte 0x00, 0x00, 0x80, 0xbf
-;   fmv.w.x fa3, t6
-;   fle.s a0, fa0, fa3
-;   beqz a0, 8
+;   feq.s a2, fa0, fa0
+;   bnez a2, 8
+;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
+;   lui a5, 0xbf800
+;   fmv.w.x fa1, a5
+;   fle.s a3, fa0, fa1
+;   beqz a3, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
-;   lui t6, 0x47800
-;   fmv.w.x fa3, t6
-;   fle.s a0, fa3, fa0
-;   beqz a0, 8
+;   lui a0, 0x47800
+;   fmv.w.x fa2, a0
+;   fle.s a4, fa2, fa0
+;   beqz a4, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
 ;   fcvt.wu.s a0, fa0, rtz
-;   j 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
 ;   ret
 
 function u0:0(f64) -> i16 {
@@ -190,33 +207,38 @@ block0(v0: f64):
 
 ; VCode:
 ; block0:
-;   fcvt_to_uint.i16 a0,fa0##in_ty=f64 tmp=fa3
+;   feq.d a2,fa0,fa0
+;   trap_if bad_toint##(a2 eq zero)
+;   lui a5,3071
+;   slli a1,a5,40
+;   fmv.d.x fa3,a1
+;   fle.d a5,fa0,fa3
+;   trap_if int_ovf##(a5 ne zero)
+;   lui a2,1039
+;   slli a4,a2,40
+;   fmv.d.x fa1,a4
+;   fle.d a2,fa1,fa0
+;   trap_if int_ovf##(a2 ne zero)
+;   fcvt.wu.d a0,fa0,rtz
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.d a0, fa0, fa0
-;   beqz a0, 0x54
-;   auipc t6, 0
-;   ld t6, 0xc(t6)
-;   j 0xc
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   .byte 0x00, 0x00, 0xf0, 0xbf
-;   fmv.d.x fa3, t6
-;   fle.d a0, fa0, fa3
-;   beqz a0, 8
+;   feq.d a2, fa0, fa0
+;   bnez a2, 8
+;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
+;   lui a5, 0xbff
+;   slli a1, a5, 0x28
+;   fmv.d.x fa3, a1
+;   fle.d a5, fa0, fa3
+;   beqz a5, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
-;   auipc t6, 0
-;   ld t6, 0xc(t6)
-;   j 0xc
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   .byte 0x00, 0x00, 0xf0, 0x40
-;   fmv.d.x fa3, t6
-;   fle.d a0, fa3, fa0
-;   beqz a0, 8
+;   lui a2, 0x40f
+;   slli a4, a2, 0x28
+;   fmv.d.x fa1, a4
+;   fle.d a2, fa1, fa0
+;   beqz a2, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
 ;   fcvt.wu.d a0, fa0, rtz
-;   j 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
 ;   ret
 
diff --git a/cranelift/filetests/filetests/isa/riscv64/float.clif b/cranelift/filetests/filetests/isa/riscv64/float.clif
index fc14272e3..dae86033d 100644
--- a/cranelift/filetests/filetests/isa/riscv64/float.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/float.clif
@@ -10,12 +10,12 @@ block0(v0: f32, v1: f32):
 
 ; VCode:
 ; block0:
-;   fadd.s fa0,fa0,fa1
+;   fadd.s fa0,fa0,fa1,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fadd.s fa0, fa0, fa1
+;   fadd.s fa0, fa0, fa1, rne
 ;   ret
 
 function %f2(f64, f64) -> f64 {
@@ -26,12 +26,12 @@ block0(v0: f64, v1: f64):
 
 ; VCode:
 ; block0:
-;   fadd.d fa0,fa0,fa1
+;   fadd.d fa0,fa0,fa1,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fadd.d fa0, fa0, fa1
+;   fadd.d fa0, fa0, fa1, rne
 ;   ret
 
 function %f3(f32, f32) -> f32 {
@@ -42,12 +42,12 @@ block0(v0: f32, v1: f32):
 
 ; VCode:
 ; block0:
-;   fsub.s fa0,fa0,fa1
+;   fsub.s fa0,fa0,fa1,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fsub.s fa0, fa0, fa1
+;   fsub.s fa0, fa0, fa1, rne
 ;   ret
 
 function %f4(f64, f64) -> f64 {
@@ -58,12 +58,12 @@ block0(v0: f64, v1: f64):
 
 ; VCode:
 ; block0:
-;   fsub.d fa0,fa0,fa1
+;   fsub.d fa0,fa0,fa1,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fsub.d fa0, fa0, fa1
+;   fsub.d fa0, fa0, fa1, rne
 ;   ret
 
 function %f5(f32, f32) -> f32 {
@@ -74,12 +74,12 @@ block0(v0: f32, v1: f32):
 
 ; VCode:
 ; block0:
-;   fmul.s fa0,fa0,fa1
+;   fmul.s fa0,fa0,fa1,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fmul.s fa0, fa0, fa1
+;   fmul.s fa0, fa0, fa1, rne
 ;   ret
 
 function %f6(f64, f64) -> f64 {
@@ -90,12 +90,12 @@ block0(v0: f64, v1: f64):
 
 ; VCode:
 ; block0:
-;   fmul.d fa0,fa0,fa1
+;   fmul.d fa0,fa0,fa1,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fmul.d fa0, fa0, fa1
+;   fmul.d fa0, fa0, fa1, rne
 ;   ret
 
 function %f7(f32, f32) -> f32 {
@@ -106,12 +106,12 @@ block0(v0: f32, v1: f32):
 
 ; VCode:
 ; block0:
-;   fdiv.s fa0,fa0,fa1
+;   fdiv.s fa0,fa0,fa1,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fdiv.s fa0, fa0, fa1
+;   fdiv.s fa0, fa0, fa1, rne
 ;   ret
 
 function %f8(f64, f64) -> f64 {
@@ -122,15 +122,14 @@ block0(v0: f64, v1: f64):
 
 ; VCode:
 ; block0:
-;   fdiv.d fa0,fa0,fa1
+;   fdiv.d fa0,fa0,fa1,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fdiv.d fa0, fa0, fa1
+;   fdiv.d fa0, fa0, fa1, rne
 ;   ret
 
-
 function %f13(f32) -> f32 {
 block0(v0: f32):
   v1 = sqrt v0
@@ -139,12 +138,12 @@ block0(v0: f32):
 
 ; VCode:
 ; block0:
-;   fsqrt.s fa0,fa0
+;   fsqrt.s fa0,fa0,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fsqrt.s fa0, fa0
+;   fsqrt.s fa0, fa0, rne
 ;   ret
 
 function %f15(f64) -> f64 {
@@ -155,12 +154,12 @@ block0(v0: f64):
 
 ; VCode:
 ; block0:
-;   fsqrt.d fa0,fa0
+;   fsqrt.d fa0,fa0,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fsqrt.d fa0, fa0
+;   fsqrt.d fa0, fa0, rne
 ;   ret
 
 function %f16(f32) -> f32 {
@@ -235,12 +234,12 @@ block0(v0: f32):
 
 ; VCode:
 ; block0:
-;   fcvt.d.s fa0,fa0
+;   fcvt.d.s fa0,fa0,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   .byte 0x53, 0x75, 0x05, 0x42
+;   fcvt.d.s fa0, fa0
 ;   ret
 
 function %f21(f64) -> f32 {
@@ -251,12 +250,12 @@ block0(v0: f64):
 
 ; VCode:
 ; block0:
-;   fcvt.s.d fa0,fa0
+;   fcvt.s.d fa0,fa0,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fcvt.s.d fa0, fa0
+;   fcvt.s.d fa0, fa0, rne
 ;   ret
 
 function %f22(f32) -> f32 {
@@ -285,7 +284,7 @@ block0(v0: f32):
 ;   fcvt.s.l fa0, a3, rup
 ;   fsgnj.s fa0, fa0, fa5
 ;   j 0x10
-;   fadd.s fa0, fa5, fa5
+;   fadd.s fa0, fa5, fa5, rne
 ;   j 8
 ;   fmv.s fa0, fa5
 ;   ret
@@ -320,7 +319,7 @@ block0(v0: f64):
 ;   fcvt.d.l fa0, a3, rup
 ;   fsgnj.d fa0, fa0, fa5
 ;   j 0x10
-;   fadd.d fa0, fa5, fa5
+;   fadd.d fa0, fa5, fa5, rne
 ;   j 8
 ;   fmv.d fa0, fa5
 ;   ret
@@ -351,7 +350,7 @@ block0(v0: f32):
 ;   fcvt.s.l fa0, a3, rdn
 ;   fsgnj.s fa0, fa0, fa5
 ;   j 0x10
-;   fadd.s fa0, fa5, fa5
+;   fadd.s fa0, fa5, fa5, rne
 ;   j 8
 ;   fmv.s fa0, fa5
 ;   ret
@@ -386,7 +385,7 @@ block0(v0: f64):
 ;   fcvt.d.l fa0, a3, rdn
 ;   fsgnj.d fa0, fa0, fa5
 ;   j 0x10
-;   fadd.d fa0, fa5, fa5
+;   fadd.d fa0, fa5, fa5, rne
 ;   j 8
 ;   fmv.d fa0, fa5
 ;   ret
@@ -417,7 +416,7 @@ block0(v0: f32):
 ;   fcvt.s.l fa0, a3, rtz
 ;   fsgnj.s fa0, fa0, fa5
 ;   j 0x10
-;   fadd.s fa0, fa5, fa5
+;   fadd.s fa0, fa5, fa5, rne
 ;   j 8
 ;   fmv.s fa0, fa5
 ;   ret
@@ -452,7 +451,7 @@ block0(v0: f64):
 ;   fcvt.d.l fa0, a3, rtz
 ;   fsgnj.d fa0, fa0, fa5
 ;   j 0x10
-;   fadd.d fa0, fa5, fa5
+;   fadd.d fa0, fa5, fa5, rne
 ;   j 8
 ;   fmv.d fa0, fa5
 ;   ret
@@ -483,7 +482,7 @@ block0(v0: f32):
 ;   fcvt.s.l fa0, a3, rne
 ;   fsgnj.s fa0, fa0, fa5
 ;   j 0x10
-;   fadd.s fa0, fa5, fa5
+;   fadd.s fa0, fa5, fa5, rne
 ;   j 8
 ;   fmv.s fa0, fa5
 ;   ret
@@ -518,7 +517,7 @@ block0(v0: f64):
 ;   fcvt.d.l fa0, a3, rne
 ;   fsgnj.d fa0, fa0, fa5
 ;   j 0x10
-;   fadd.d fa0, fa5, fa5
+;   fadd.d fa0, fa5, fa5, rne
 ;   j 8
 ;   fmv.d fa0, fa5
 ;   ret
@@ -531,12 +530,12 @@ block0(v0: f32, v1: f32, v2: f32):
 
 ; VCode:
 ; block0:
-;   fmadd.s fa0,fa0,fa1,fa2
+;   fmadd.s fa0,fa0,fa1,fa2,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fmadd.s fa0, fa0, fa1, fa2
+;   fmadd.s fa0, fa0, fa1, fa2, rne
 ;   ret
 
 function %f30(f64, f64, f64) -> f64 {
@@ -547,12 +546,12 @@ block0(v0: f64, v1: f64, v2: f64):
 
 ; VCode:
 ; block0:
-;   fmadd.d fa0,fa0,fa1,fa2
+;   fmadd.d fa0,fa0,fa1,fa2,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fmadd.d fa0, fa0, fa1, fa2
+;   fmadd.d fa0, fa0, fa1, fa2, rne
 ;   ret
 
 function %f31(f32, f32) -> f32 {
@@ -595,29 +594,35 @@ block0(v0: f32):
 
 ; VCode:
 ; block0:
-;   fcvt_to_uint.i32 a0,fa0##in_ty=f32 tmp=fa3
+;   feq.s a2,fa0,fa0
+;   trap_if bad_toint##(a2 eq zero)
+;   lui a5,-264192
+;   fmv.w.x fa1,a5
+;   fle.s a3,fa0,fa1
+;   trap_if int_ovf##(a3 ne zero)
+;   lui a0,325632
+;   fmv.w.x fa2,a0
+;   fle.s a4,fa2,fa0
+;   trap_if int_ovf##(a4 ne zero)
+;   fcvt.wu.s a0,fa0,rtz
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.s a0, fa0, fa0
-;   beqz a0, 0x40
-;   auipc t6, 0
-;   lw t6, 0xc(t6)
-;   j 8
-;   .byte 0x00, 0x00, 0x80, 0xbf
-;   fmv.w.x fa3, t6
-;   fle.s a0, fa0, fa3
-;   beqz a0, 8
+;   feq.s a2, fa0, fa0
+;   bnez a2, 8
+;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
+;   lui a5, 0xbf800
+;   fmv.w.x fa1, a5
+;   fle.s a3, fa0, fa1
+;   beqz a3, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
-;   lui t6, 0x4f800
-;   fmv.w.x fa3, t6
-;   fle.s a0, fa3, fa0
-;   beqz a0, 8
+;   lui a0, 0x4f800
+;   fmv.w.x fa2, a0
+;   fle.s a4, fa2, fa0
+;   beqz a4, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
 ;   fcvt.wu.s a0, fa0, rtz
-;   j 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
 ;   ret
 
 function %f34(f32) -> i32 {
@@ -628,29 +633,37 @@ block0(v0: f32):
 
 ; VCode:
 ; block0:
-;   fcvt_to_sint.i32 a0,fa0##in_ty=f32 tmp=fa3
+;   feq.s a2,fa0,fa0
+;   trap_if bad_toint##(a2 eq zero)
+;   lui a5,-200704
+;   addi a1,a5,1
+;   fmv.w.x fa3,a1
+;   fle.s a5,fa0,fa3
+;   trap_if int_ovf##(a5 ne zero)
+;   lui a2,323584
+;   fmv.w.x fa4,a2
+;   fle.s a0,fa4,fa0
+;   trap_if int_ovf##(a0 ne zero)
+;   fcvt.w.s a0,fa0,rtz
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.s a0, fa0, fa0
-;   beqz a0, 0x40
-;   auipc t6, 0
-;   lw t6, 0xc(t6)
-;   j 8
-;   .byte 0x01, 0x00, 0x00, 0xcf
-;   fmv.w.x fa3, t6
-;   fle.s a0, fa0, fa3
-;   beqz a0, 8
+;   feq.s a2, fa0, fa0
+;   bnez a2, 8
+;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
+;   lui a5, 0xcf000
+;   addi a1, a5, 1
+;   fmv.w.x fa3, a1
+;   fle.s a5, fa0, fa3
+;   beqz a5, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
-;   lui t6, 0x4f000
-;   fmv.w.x fa3, t6
-;   fle.s a0, fa3, fa0
+;   lui a2, 0x4f000
+;   fmv.w.x fa4, a2
+;   fle.s a0, fa4, fa0
 ;   beqz a0, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
 ;   fcvt.w.s a0, fa0, rtz
-;   j 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
 ;   ret
 
 function %f35(f32) -> i64 {
@@ -661,29 +674,35 @@ block0(v0: f32):
 
 ; VCode:
 ; block0:
-;   fcvt_to_uint.i64 a0,fa0##in_ty=f32 tmp=fa3
+;   feq.s a2,fa0,fa0
+;   trap_if bad_toint##(a2 eq zero)
+;   lui a5,-264192
+;   fmv.w.x fa1,a5
+;   fle.s a3,fa0,fa1
+;   trap_if int_ovf##(a3 ne zero)
+;   lui a0,391168
+;   fmv.w.x fa2,a0
+;   fle.s a4,fa2,fa0
+;   trap_if int_ovf##(a4 ne zero)
+;   fcvt.lu.s a0,fa0,rtz
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.s a0, fa0, fa0
-;   beqz a0, 0x40
-;   auipc t6, 0
-;   lw t6, 0xc(t6)
-;   j 8
-;   .byte 0x00, 0x00, 0x80, 0xbf
-;   fmv.w.x fa3, t6
-;   fle.s a0, fa0, fa3
-;   beqz a0, 8
+;   feq.s a2, fa0, fa0
+;   bnez a2, 8
+;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
+;   lui a5, 0xbf800
+;   fmv.w.x fa1, a5
+;   fle.s a3, fa0, fa1
+;   beqz a3, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
-;   lui t6, 0x5f800
-;   fmv.w.x fa3, t6
-;   fle.s a0, fa3, fa0
-;   beqz a0, 8
+;   lui a0, 0x5f800
+;   fmv.w.x fa2, a0
+;   fle.s a4, fa2, fa0
+;   beqz a4, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
 ;   fcvt.lu.s a0, fa0, rtz
-;   j 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
 ;   ret
 
 function %f36(f32) -> i64 {
@@ -694,29 +713,37 @@ block0(v0: f32):
 
 ; VCode:
 ; block0:
-;   fcvt_to_sint.i64 a0,fa0##in_ty=f32 tmp=fa3
+;   feq.s a2,fa0,fa0
+;   trap_if bad_toint##(a2 eq zero)
+;   lui a5,-135168
+;   addi a1,a5,1
+;   fmv.w.x fa3,a1
+;   fle.s a5,fa0,fa3
+;   trap_if int_ovf##(a5 ne zero)
+;   lui a2,389120
+;   fmv.w.x fa4,a2
+;   fle.s a0,fa4,fa0
+;   trap_if int_ovf##(a0 ne zero)
+;   fcvt.l.s a0,fa0,rtz
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.s a0, fa0, fa0
-;   beqz a0, 0x40
-;   auipc t6, 0
-;   lw t6, 0xc(t6)
-;   j 8
-;   .byte 0x01, 0x00, 0x00, 0xdf
-;   fmv.w.x fa3, t6
-;   fle.s a0, fa0, fa3
-;   beqz a0, 8
+;   feq.s a2, fa0, fa0
+;   bnez a2, 8
+;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
+;   lui a5, 0xdf000
+;   addi a1, a5, 1
+;   fmv.w.x fa3, a1
+;   fle.s a5, fa0, fa3
+;   beqz a5, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
-;   lui t6, 0x5f000
-;   fmv.w.x fa3, t6
-;   fle.s a0, fa3, fa0
+;   lui a2, 0x5f000
+;   fmv.w.x fa4, a2
+;   fle.s a0, fa4, fa0
 ;   beqz a0, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
 ;   fcvt.l.s a0, fa0, rtz
-;   j 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
 ;   ret
 
 function %f37(f64) -> i32 {
@@ -727,34 +754,39 @@ block0(v0: f64):
 
 ; VCode:
 ; block0:
-;   fcvt_to_uint.i32 a0,fa0##in_ty=f64 tmp=fa3
+;   feq.d a2,fa0,fa0
+;   trap_if bad_toint##(a2 eq zero)
+;   lui a5,3071
+;   slli a1,a5,40
+;   fmv.d.x fa3,a1
+;   fle.d a5,fa0,fa3
+;   trap_if int_ovf##(a5 ne zero)
+;   lui a2,1055
+;   slli a4,a2,40
+;   fmv.d.x fa1,a4
+;   fle.d a2,fa1,fa0
+;   trap_if int_ovf##(a2 ne zero)
+;   fcvt.wu.d a0,fa0,rtz
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.d a0, fa0, fa0
-;   beqz a0, 0x54
-;   auipc t6, 0
-;   ld t6, 0xc(t6)
-;   j 0xc
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   .byte 0x00, 0x00, 0xf0, 0xbf
-;   fmv.d.x fa3, t6
-;   fle.d a0, fa0, fa3
-;   beqz a0, 8
+;   feq.d a2, fa0, fa0
+;   bnez a2, 8
+;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
+;   lui a5, 0xbff
+;   slli a1, a5, 0x28
+;   fmv.d.x fa3, a1
+;   fle.d a5, fa0, fa3
+;   beqz a5, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
-;   auipc t6, 0
-;   ld t6, 0xc(t6)
-;   j 0xc
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   .byte 0x00, 0x00, 0xf0, 0x41
-;   fmv.d.x fa3, t6
-;   fle.d a0, fa3, fa0
-;   beqz a0, 8
+;   lui a2, 0x41f
+;   slli a4, a2, 0x28
+;   fmv.d.x fa1, a4
+;   fle.d a2, fa1, fa0
+;   beqz a2, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
 ;   fcvt.wu.d a0, fa0, rtz
-;   j 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
 ;   ret
 
 function %f38(f64) -> i32 {
@@ -765,35 +797,42 @@ block0(v0: f64):
 
 ; VCode:
 ; block0:
-;   fcvt_to_sint.i32 a0,fa0##in_ty=f64 tmp=fa3
+;   feq.d a2,fa0,fa0
+;   trap_if bad_toint##(a2 eq zero)
+;   ld a5,[const(0)]
+;   fmv.d.x fa1,a5
+;   fle.d a3,fa0,fa1
+;   trap_if int_ovf##(a3 ne zero)
+;   lui a0,527
+;   slli a2,a0,41
+;   fmv.d.x fa4,a2
+;   fle.d a0,fa4,fa0
+;   trap_if int_ovf##(a0 ne zero)
+;   fcvt.w.d a0,fa0,rtz
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.d a0, fa0, fa0
-;   beqz a0, 0x54
-;   auipc t6, 0
-;   ld t6, 0xc(t6)
-;   j 0xc
-;   .byte 0x00, 0x00, 0x20, 0x00
-;   .byte 0x00, 0x00, 0xe0, 0xc1
-;   fmv.d.x fa3, t6
-;   fle.d a0, fa0, fa3
-;   beqz a0, 8
+;   feq.d a2, fa0, fa0
+;   bnez a2, 8
+;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
+;   auipc a5, 0
+;   ld a5, 0x3c(a5)
+;   fmv.d.x fa1, a5
+;   fle.d a3, fa0, fa1
+;   beqz a3, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
-;   auipc t6, 0
-;   ld t6, 0xc(t6)
-;   j 0xc
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   .byte 0x00, 0x00, 0xe0, 0x41
-;   fmv.d.x fa3, t6
-;   fle.d a0, fa3, fa0
+;   lui a0, 0x20f
+;   slli a2, a0, 0x29
+;   fmv.d.x fa4, a2
+;   fle.d a0, fa4, fa0
 ;   beqz a0, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
 ;   fcvt.w.d a0, fa0, rtz
-;   j 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
 ;   ret
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   .byte 0x00, 0x00, 0x20, 0x00
+;   .byte 0x00, 0x00, 0xe0, 0xc1
 
 function %f39(f64) -> i64 {
 block0(v0: f64):
@@ -803,34 +842,39 @@ block0(v0: f64):
 
 ; VCode:
 ; block0:
-;   fcvt_to_uint.i64 a0,fa0##in_ty=f64 tmp=fa3
+;   feq.d a2,fa0,fa0
+;   trap_if bad_toint##(a2 eq zero)
+;   lui a5,3071
+;   slli a1,a5,40
+;   fmv.d.x fa3,a1
+;   fle.d a5,fa0,fa3
+;   trap_if int_ovf##(a5 ne zero)
+;   lui a2,1087
+;   slli a4,a2,40
+;   fmv.d.x fa1,a4
+;   fle.d a2,fa1,fa0
+;   trap_if int_ovf##(a2 ne zero)
+;   fcvt.lu.d a0,fa0,rtz
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.d a0, fa0, fa0
-;   beqz a0, 0x54
-;   auipc t6, 0
-;   ld t6, 0xc(t6)
-;   j 0xc
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   .byte 0x00, 0x00, 0xf0, 0xbf
-;   fmv.d.x fa3, t6
-;   fle.d a0, fa0, fa3
-;   beqz a0, 8
+;   feq.d a2, fa0, fa0
+;   bnez a2, 8
+;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
+;   lui a5, 0xbff
+;   slli a1, a5, 0x28
+;   fmv.d.x fa3, a1
+;   fle.d a5, fa0, fa3
+;   beqz a5, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
-;   auipc t6, 0
-;   ld t6, 0xc(t6)
-;   j 0xc
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   .byte 0x00, 0x00, 0xf0, 0x43
-;   fmv.d.x fa3, t6
-;   fle.d a0, fa3, fa0
-;   beqz a0, 8
+;   lui a2, 0x43f
+;   slli a4, a2, 0x28
+;   fmv.d.x fa1, a4
+;   fle.d a2, fa1, fa0
+;   beqz a2, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
 ;   fcvt.lu.d a0, fa0, rtz
-;   j 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
 ;   ret
 
 function %f40(f64) -> i64 {
@@ -841,35 +885,42 @@ block0(v0: f64):
 
 ; VCode:
 ; block0:
-;   fcvt_to_sint.i64 a0,fa0##in_ty=f64 tmp=fa3
+;   feq.d a2,fa0,fa0
+;   trap_if bad_toint##(a2 eq zero)
+;   ld a5,[const(0)]
+;   fmv.d.x fa1,a5
+;   fle.d a3,fa0,fa1
+;   trap_if int_ovf##(a3 ne zero)
+;   lui a0,543
+;   slli a2,a0,41
+;   fmv.d.x fa4,a2
+;   fle.d a0,fa4,fa0
+;   trap_if int_ovf##(a0 ne zero)
+;   fcvt.l.d a0,fa0,rtz
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.d a0, fa0, fa0
-;   beqz a0, 0x54
-;   auipc t6, 0
-;   ld t6, 0xc(t6)
-;   j 0xc
-;   .byte 0x01, 0x00, 0x00, 0x00
-;   .byte 0x00, 0x00, 0xe0, 0xc3
-;   fmv.d.x fa3, t6
-;   fle.d a0, fa0, fa3
-;   beqz a0, 8
+;   feq.d a2, fa0, fa0
+;   bnez a2, 8
+;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
+;   auipc a5, 0
+;   ld a5, 0x3c(a5)
+;   fmv.d.x fa1, a5
+;   fle.d a3, fa0, fa1
+;   beqz a3, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
-;   auipc t6, 0
-;   ld t6, 0xc(t6)
-;   j 0xc
-;   .byte 0x00, 0x00, 0x00, 0x00
-;   .byte 0x00, 0x00, 0xe0, 0x43
-;   fmv.d.x fa3, t6
-;   fle.d a0, fa3, fa0
+;   lui a0, 0x21f
+;   slli a2, a0, 0x29
+;   fmv.d.x fa4, a2
+;   fle.d a0, fa4, fa0
 ;   beqz a0, 8
 ;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: int_ovf
 ;   fcvt.l.d a0, fa0, rtz
-;   j 8
-;   .byte 0x00, 0x00, 0x00, 0x00 ; trap: bad_toint
 ;   ret
+;   .byte 0x00, 0x00, 0x00, 0x00
+;   .byte 0x01, 0x00, 0x00, 0x00
+;   .byte 0x00, 0x00, 0xe0, 0xc3
 
 function %f41(i32) -> f32 {
 block0(v0: i32):
@@ -879,12 +930,12 @@ block0(v0: i32):
 
 ; VCode:
 ; block0:
-;   fcvt.s.wu fa0,a0
+;   fcvt.s.wu fa0,a0,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fcvt.s.wu fa0, a0
+;   fcvt.s.wu fa0, a0, rne
 ;   ret
 
 function %f42(i32) -> f32 {
@@ -895,12 +946,12 @@ block0(v0: i32):
 
 ; VCode:
 ; block0:
-;   fcvt.s.w fa0,a0
+;   fcvt.s.w fa0,a0,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fcvt.s.w fa0, a0
+;   fcvt.s.w fa0, a0, rne
 ;   ret
 
 function %f43(i64) -> f32 {
@@ -911,12 +962,12 @@ block0(v0: i64):
 
 ; VCode:
 ; block0:
-;   fcvt.s.lu fa0,a0
+;   fcvt.s.lu fa0,a0,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fcvt.s.lu fa0, a0
+;   fcvt.s.lu fa0, a0, rne
 ;   ret
 
 function %f44(i64) -> f32 {
@@ -927,12 +978,12 @@ block0(v0: i64):
 
 ; VCode:
 ; block0:
-;   fcvt.s.l fa0,a0
+;   fcvt.s.l fa0,a0,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fcvt.s.l fa0, a0
+;   fcvt.s.l fa0, a0, rne
 ;   ret
 
 function %f45(i32) -> f64 {
@@ -964,7 +1015,7 @@ block0(v0: i32):
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   .byte 0x53, 0x75, 0x05, 0xd2
+;   fcvt.d.w fa0, a0
 ;   ret
 
 function %f47(i64) -> f64 {
@@ -975,12 +1026,12 @@ block0(v0: i64):
 
 ; VCode:
 ; block0:
-;   fcvt.d.lu fa0,a0
+;   fcvt.d.lu fa0,a0,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fcvt.d.lu fa0, a0
+;   fcvt.d.lu fa0, a0, rne
 ;   ret
 
 function %f48(i64) -> f64 {
@@ -991,12 +1042,12 @@ block0(v0: i64):
 
 ; VCode:
 ; block0:
-;   fcvt.d.l fa0,a0
+;   fcvt.d.l fa0,a0,rne
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   fcvt.d.l fa0, a0
+;   fcvt.d.l fa0, a0, rne
 ;   ret
 
 function %f49(f32) -> i32 {
@@ -1007,16 +1058,18 @@ block0(v0: f32):
 
 ; VCode:
 ; block0:
-;   fcvt_to_uint_sat.i32 a0,fa0##in_ty=f32 tmp=fa3
+;   fcvt.wu.s a2,fa0,rtz
+;   feq.s a4,fa0,fa0
+;   sub a0,zero,a4
+;   and a0,a2,a0
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.s a0, fa0, fa0
-;   beqz a0, 0xc
-;   fcvt.wu.s a0, fa0, rtz
-;   j 8
-;   mv a0, zero
+;   fcvt.wu.s a2, fa0, rtz
+;   feq.s a4, fa0, fa0
+;   neg a0, a4
+;   and a0, a2, a0
 ;   ret
 
 function %f50(f32) -> i32 {
@@ -1027,16 +1080,18 @@ block0(v0: f32):
 
 ; VCode:
 ; block0:
-;   fcvt_to_sint_sat.i32 a0,fa0##in_ty=f32 tmp=fa3
+;   fcvt.w.s a2,fa0,rtz
+;   feq.s a4,fa0,fa0
+;   sub a0,zero,a4
+;   and a0,a2,a0
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.s a0, fa0, fa0
-;   beqz a0, 0xc
-;   fcvt.w.s a0, fa0, rtz
-;   j 8
-;   mv a0, zero
+;   fcvt.w.s a2, fa0, rtz
+;   feq.s a4, fa0, fa0
+;   neg a0, a4
+;   and a0, a2, a0
 ;   ret
 
 function %f51(f32) -> i64 {
@@ -1047,16 +1102,18 @@ block0(v0: f32):
 
 ; VCode:
 ; block0:
-;   fcvt_to_uint_sat.i64 a0,fa0##in_ty=f32 tmp=fa3
+;   fcvt.lu.s a2,fa0,rtz
+;   feq.s a4,fa0,fa0
+;   sub a0,zero,a4
+;   and a0,a2,a0
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.s a0, fa0, fa0
-;   beqz a0, 0xc
-;   fcvt.lu.s a0, fa0, rtz
-;   j 8
-;   mv a0, zero
+;   fcvt.lu.s a2, fa0, rtz
+;   feq.s a4, fa0, fa0
+;   neg a0, a4
+;   and a0, a2, a0
 ;   ret
 
 function %f52(f32) -> i64 {
@@ -1067,16 +1124,18 @@ block0(v0: f32):
 
 ; VCode:
 ; block0:
-;   fcvt_to_sint_sat.i64 a0,fa0##in_ty=f32 tmp=fa3
+;   fcvt.l.s a2,fa0,rtz
+;   feq.s a4,fa0,fa0
+;   sub a0,zero,a4
+;   and a0,a2,a0
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.s a0, fa0, fa0
-;   beqz a0, 0xc
-;   fcvt.l.s a0, fa0, rtz
-;   j 8
-;   mv a0, zero
+;   fcvt.l.s a2, fa0, rtz
+;   feq.s a4, fa0, fa0
+;   neg a0, a4
+;   and a0, a2, a0
 ;   ret
 
 function %f53(f64) -> i32 {
@@ -1087,16 +1146,18 @@ block0(v0: f64):
 
 ; VCode:
 ; block0:
-;   fcvt_to_uint_sat.i32 a0,fa0##in_ty=f64 tmp=fa3
+;   fcvt.wu.d a2,fa0,rtz
+;   feq.d a4,fa0,fa0
+;   sub a0,zero,a4
+;   and a0,a2,a0
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.d a0, fa0, fa0
-;   beqz a0, 0xc
-;   fcvt.wu.d a0, fa0, rtz
-;   j 8
-;   mv a0, zero
+;   fcvt.wu.d a2, fa0, rtz
+;   feq.d a4, fa0, fa0
+;   neg a0, a4
+;   and a0, a2, a0
 ;   ret
 
 function %f54(f64) -> i32 {
@@ -1107,16 +1168,18 @@ block0(v0: f64):
 
 ; VCode:
 ; block0:
-;   fcvt_to_sint_sat.i32 a0,fa0##in_ty=f64 tmp=fa3
+;   fcvt.w.d a2,fa0,rtz
+;   feq.d a4,fa0,fa0
+;   sub a0,zero,a4
+;   and a0,a2,a0
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.d a0, fa0, fa0
-;   beqz a0, 0xc
-;   fcvt.w.d a0, fa0, rtz
-;   j 8
-;   mv a0, zero
+;   fcvt.w.d a2, fa0, rtz
+;   feq.d a4, fa0, fa0
+;   neg a0, a4
+;   and a0, a2, a0
 ;   ret
 
 function %f55(f64) -> i64 {
@@ -1127,16 +1190,18 @@ block0(v0: f64):
 
 ; VCode:
 ; block0:
-;   fcvt_to_uint_sat.i64 a0,fa0##in_ty=f64 tmp=fa3
+;   fcvt.lu.d a2,fa0,rtz
+;   feq.d a4,fa0,fa0
+;   sub a0,zero,a4
+;   and a0,a2,a0
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.d a0, fa0, fa0
-;   beqz a0, 0xc
-;   fcvt.lu.d a0, fa0, rtz
-;   j 8
-;   mv a0, zero
+;   fcvt.lu.d a2, fa0, rtz
+;   feq.d a4, fa0, fa0
+;   neg a0, a4
+;   and a0, a2, a0
 ;   ret
 
 function %f56(f64) -> i64 {
@@ -1147,15 +1212,17 @@ block0(v0: f64):
 
 ; VCode:
 ; block0:
-;   fcvt_to_sint_sat.i64 a0,fa0##in_ty=f64 tmp=fa3
+;   fcvt.l.d a2,fa0,rtz
+;   feq.d a4,fa0,fa0
+;   sub a0,zero,a4
+;   and a0,a2,a0
 ;   ret
 ;
 ; Disassembled:
 ; block0: ; offset 0x0
-;   feq.d a0, fa0, fa0
-;   beqz a0, 0xc
-;   fcvt.l.d a0, fa0, rtz
-;   j 8
-;   mv a0, zero
+;   fcvt.l.d a2, fa0, rtz
+;   feq.d a4, fa0, fa0
+;   neg a0, a4
+;   and a0, a2, a0
 ;   ret
 
diff --git a/cranelift/filetests/filetests/isa/riscv64/fmax.clif b/cranelift/filetests/filetests/isa/riscv64/fmax.clif
index 04fcdc262..c7939f36b 100644
--- a/cranelift/filetests/filetests/isa/riscv64/fmax.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/fmax.clif
@@ -13,7 +13,7 @@ block0(v0: f32, v1: f32):
 ;   feq.s a3,fa0,fa0
 ;   feq.s a5,fa1,fa1
 ;   and a1,a3,a5
-;   fadd.s fa3,fa0,fa1
+;   fadd.s fa3,fa0,fa1,rne
 ;   fmax.s fa5,fa0,fa1
 ;   select fa0,fa5,fa3##condition=(a1 ne zero)
 ;   ret
@@ -23,7 +23,7 @@ block0(v0: f32, v1: f32):
 ;   feq.s a3, fa0, fa0
 ;   feq.s a5, fa1, fa1
 ;   and a1, a3, a5
-;   fadd.s fa3, fa0, fa1
+;   fadd.s fa3, fa0, fa1, rne
 ;   fmax.s fa5, fa0, fa1
 ;   beqz a1, 0xc
 ;   fmv.d fa0, fa5
@@ -42,7 +42,7 @@ block0(v0: f64, v1: f64):
 ;   feq.d a3,fa0,fa0
 ;   feq.d a5,fa1,fa1
 ;   and a1,a3,a5
-;   fadd.d fa3,fa0,fa1
+;   fadd.d fa3,fa0,fa1,rne
 ;   fmax.d fa5,fa0,fa1
 ;   select fa0,fa5,fa3##condition=(a1 ne zero)
 ;   ret
@@ -52,7 +52,7 @@ block0(v0: f64, v1: f64):
 ;   feq.d a3, fa0, fa0
 ;   feq.d a5, fa1, fa1
 ;   and a1, a3, a5
-;   fadd.d fa3, fa0, fa1
+;   fadd.d fa3, fa0, fa1, rne
 ;   fmax.d fa5, fa0, fa1
 ;   beqz a1, 0xc
 ;   fmv.d fa0, fa5
diff --git a/cranelift/filetests/filetests/isa/riscv64/fmin.clif b/cranelift/filetests/filetests/isa/riscv64/fmin.clif
index eb68c53f9..cdc3b15bb 100644
--- a/cranelift/filetests/filetests/isa/riscv64/fmin.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/fmin.clif
@@ -13,7 +13,7 @@ block0(v0: f32, v1: f32):
 ;   feq.s a3,fa0,fa0
 ;   feq.s a5,fa1,fa1
 ;   and a1,a3,a5
-;   fadd.s fa3,fa0,fa1
+;   fadd.s fa3,fa0,fa1,rne
 ;   fmin.s fa5,fa0,fa1
 ;   select fa0,fa5,fa3##condition=(a1 ne zero)
 ;   ret
@@ -23,7 +23,7 @@ block0(v0: f32, v1: f32):
 ;   feq.s a3, fa0, fa0
 ;   feq.s a5, fa1, fa1
 ;   and a1, a3, a5
-;   fadd.s fa3, fa0, fa1
+;   fadd.s fa3, fa0, fa1, rne
 ;   fmin.s fa5, fa0, fa1
 ;   beqz a1, 0xc
 ;   fmv.d fa0, fa5
@@ -42,7 +42,7 @@ block0(v0: f64, v1: f64):
 ;   feq.d a3,fa0,fa0
 ;   feq.d a5,fa1,fa1
 ;   and a1,a3,a5
-;   fadd.d fa3,fa0,fa1
+;   fadd.d fa3,fa0,fa1,rne
 ;   fmin.d fa5,fa0,fa1
 ;   select fa0,fa5,fa3##condition=(a1 ne zero)
 ;   ret
@@ -52,7 +52,7 @@ block0(v0: f64, v1: f64):
 ;   feq.d a3, fa0, fa0
 ;   feq.d a5, fa1, fa1
 ;   and a1, a3, a5
-;   fadd.d fa3, fa0, fa1
+;   fadd.d fa3, fa0, fa1, rne
 ;   fmin.d fa5, fa0, fa1
 ;   beqz a1, 0xc
 ;   fmv.d fa0, fa5
diff --git a/cranelift/filetests/filetests/isa/riscv64/prologue.clif b/cranelift/filetests/filetests/isa/riscv64/prologue.clif
index a3691a234..9a611af5d 100644
--- a/cranelift/filetests/filetests/isa/riscv64/prologue.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/prologue.clif
@@ -93,68 +93,68 @@ block0(v0: f64):
 ;   fsd fs11,-88(sp)
 ;   addi sp,sp,-96
 ; block0:
-;   fadd.d fa3,fa0,fa0
-;   fadd.d fa4,fa0,fa0
-;   fadd.d fa5,fa0,fa0
-;   fadd.d fa1,fa0,fa0
-;   fadd.d fa2,fa0,fa0
-;   fadd.d ft9,fa0,fa0
-;   fadd.d ft10,fa0,fa0
-;   fadd.d ft11,fa0,fa0
-;   fadd.d fs0,fa0,fa0
-;   fadd.d fs1,fa0,fa0
-;   fadd.d fs2,fa0,fa0
-;   fadd.d fs3,fa0,fa0
-;   fadd.d fs4,fa0,fa0
-;   fadd.d fs5,fa0,fa0
-;   fadd.d fs6,fa0,fa0
-;   fadd.d fs7,fa0,fa0
-;   fadd.d fs8,fa0,fa0
-;   fadd.d fs9,fa0,fa0
-;   fadd.d fs10,fa0,fa0
-;   fadd.d fs11,fa0,fa0
-;   fadd.d ft0,fa0,fa0
-;   fadd.d ft1,fa0,fa0
-;   fadd.d ft2,fa0,fa0
-;   fadd.d ft3,fa0,fa0
-;   fadd.d ft4,fa0,fa0
-;   fadd.d ft5,fa0,fa0
-;   fadd.d ft6,fa0,fa0
-;   fadd.d ft7,fa0,fa0
-;   fadd.d fa6,fa0,fa0
-;   fadd.d fa7,fa0,fa0
-;   fadd.d ft8,fa0,fa0
-;   fadd.d fa3,fa0,fa3
-;   fadd.d fa4,fa4,fa5
-;   fadd.d fa5,fa1,fa2
-;   fadd.d fa0,ft9,ft10
-;   fadd.d fa1,ft11,fs0
-;   fadd.d fa2,fs1,fs2
-;   fadd.d ft9,fs3,fs4
-;   fadd.d ft10,fs5,fs6
-;   fadd.d ft11,fs7,fs8
-;   fadd.d fs0,fs9,fs10
-;   fadd.d fs1,fs11,ft0
-;   fadd.d fs2,ft1,ft2
-;   fadd.d fs3,ft3,ft4
-;   fadd.d fs4,ft5,ft6
-;   fadd.d fs5,ft7,fa6
-;   fadd.d fs6,fa7,ft8
-;   fadd.d fa3,fa3,fa4
-;   fadd.d fa4,fa5,fa0
-;   fadd.d fa5,fa1,fa2
-;   fadd.d fa0,ft9,ft10
-;   fadd.d fa1,ft11,fs0
-;   fadd.d fa2,fs1,fs2
-;   fadd.d ft9,fs3,fs4
-;   fadd.d ft10,fs5,fs6
-;   fadd.d fa3,fa3,fa4
-;   fadd.d fa4,fa5,fa0
-;   fadd.d fa5,fa1,fa2
-;   fadd.d fa0,ft9,ft10
-;   fadd.d fa3,fa3,fa4
-;   fadd.d fa4,fa5,fa0
-;   fadd.d fa0,fa3,fa4
+;   fadd.d fa3,fa0,fa0,rne
+;   fadd.d fa4,fa0,fa0,rne
+;   fadd.d fa5,fa0,fa0,rne
+;   fadd.d fa1,fa0,fa0,rne
+;   fadd.d fa2,fa0,fa0,rne
+;   fadd.d ft9,fa0,fa0,rne
+;   fadd.d ft10,fa0,fa0,rne
+;   fadd.d ft11,fa0,fa0,rne
+;   fadd.d fs0,fa0,fa0,rne
+;   fadd.d fs1,fa0,fa0,rne
+;   fadd.d fs2,fa0,fa0,rne
+;   fadd.d fs3,fa0,fa0,rne
+;   fadd.d fs4,fa0,fa0,rne
+;   fadd.d fs5,fa0,fa0,rne
+;   fadd.d fs6,fa0,fa0,rne
+;   fadd.d fs7,fa0,fa0,rne
+;   fadd.d fs8,fa0,fa0,rne
+;   fadd.d fs9,fa0,fa0,rne
+;   fadd.d fs10,fa0,fa0,rne
+;   fadd.d fs11,fa0,fa0,rne
+;   fadd.d ft0,fa0,fa0,rne
+;   fadd.d ft1,fa0,fa0,rne
+;   fadd.d ft2,fa0,fa0,rne
+;   fadd.d ft3,fa0,fa0,rne
+;   fadd.d ft4,fa0,fa0,rne
+;   fadd.d ft5,fa0,fa0,rne
+;   fadd.d ft6,fa0,fa0,rne
+;   fadd.d ft7,fa0,fa0,rne
+;   fadd.d fa6,fa0,fa0,rne
+;   fadd.d fa7,fa0,fa0,rne
+;   fadd.d ft8,fa0,fa0,rne
+;   fadd.d fa3,fa0,fa3,rne
+;   fadd.d fa4,fa4,fa5,rne
+;   fadd.d fa5,fa1,fa2,rne
+;   fadd.d fa0,ft9,ft10,rne
+;   fadd.d fa1,ft11,fs0,rne
+;   fadd.d fa2,fs1,fs2,rne
+;   fadd.d ft9,fs3,fs4,rne
+;   fadd.d ft10,fs5,fs6,rne
+;   fadd.d ft11,fs7,fs8,rne
+;   fadd.d fs0,fs9,fs10,rne
+;   fadd.d fs1,fs11,ft0,rne
+;   fadd.d fs2,ft1,ft2,rne
+;   fadd.d fs3,ft3,ft4,rne
+;   fadd.d fs4,ft5,ft6,rne
+;   fadd.d fs5,ft7,fa6,rne
+;   fadd.d fs6,fa7,ft8,rne
+;   fadd.d fa3,fa3,fa4,rne
+;   fadd.d fa4,fa5,fa0,rne
+;   fadd.d fa5,fa1,fa2,rne
+;   fadd.d fa0,ft9,ft10,rne
+;   fadd.d fa1,ft11,fs0,rne
+;   fadd.d fa2,fs1,fs2,rne
+;   fadd.d ft9,fs3,fs4,rne
+;   fadd.d ft10,fs5,fs6,rne
+;   fadd.d fa3,fa3,fa4,rne
+;   fadd.d fa4,fa5,fa0,rne
+;   fadd.d fa5,fa1,fa2,rne
+;   fadd.d fa0,ft9,ft10,rne
+;   fadd.d fa3,fa3,fa4,rne
+;   fadd.d fa4,fa5,fa0,rne
+;   fadd.d fa0,fa3,fa4,rne
 ;   addi sp,sp,96
 ;   fld fs0,-8(sp)
 ;   fld fs2,-16(sp)
@@ -191,68 +191,68 @@ block0(v0: f64):
 ;   fsd fs11, -0x58(sp)
 ;   addi sp, sp, -0x60
 ; block1: ; offset 0x40
-;   fadd.d fa3, fa0, fa0
-;   fadd.d fa4, fa0, fa0
-;   fadd.d fa5, fa0, fa0
-;   fadd.d fa1, fa0, fa0
-;   fadd.d fa2, fa0, fa0
-;   fadd.d ft9, fa0, fa0
-;   fadd.d ft10, fa0, fa0
-;   fadd.d ft11, fa0, fa0
-;   fadd.d fs0, fa0, fa0
-;   fadd.d fs1, fa0, fa0
-;   fadd.d fs2, fa0, fa0
-;   fadd.d fs3, fa0, fa0
-;   fadd.d fs4, fa0, fa0
-;   fadd.d fs5, fa0, fa0
-;   fadd.d fs6, fa0, fa0
-;   fadd.d fs7, fa0, fa0
-;   fadd.d fs8, fa0, fa0
-;   fadd.d fs9, fa0, fa0
-;   fadd.d fs10, fa0, fa0
-;   fadd.d fs11, fa0, fa0
-;   fadd.d ft0, fa0, fa0
-;   fadd.d ft1, fa0, fa0
-;   fadd.d ft2, fa0, fa0
-;   fadd.d ft3, fa0, fa0
-;   fadd.d ft4, fa0, fa0
-;   fadd.d ft5, fa0, fa0
-;   fadd.d ft6, fa0, fa0
-;   fadd.d ft7, fa0, fa0
-;   fadd.d fa6, fa0, fa0
-;   fadd.d fa7, fa0, fa0
-;   fadd.d ft8, fa0, fa0
-;   fadd.d fa3, fa0, fa3
-;   fadd.d fa4, fa4, fa5
-;   fadd.d fa5, fa1, fa2
-;   fadd.d fa0, ft9, ft10
-;   fadd.d fa1, ft11, fs0
-;   fadd.d fa2, fs1, fs2
-;   fadd.d ft9, fs3, fs4
-;   fadd.d ft10, fs5, fs6
-;   fadd.d ft11, fs7, fs8
-;   fadd.d fs0, fs9, fs10
-;   fadd.d fs1, fs11, ft0
-;   fadd.d fs2, ft1, ft2
-;   fadd.d fs3, ft3, ft4
-;   fadd.d fs4, ft5, ft6
-;   fadd.d fs5, ft7, fa6
-;   fadd.d fs6, fa7, ft8
-;   fadd.d fa3, fa3, fa4
-;   fadd.d fa4, fa5, fa0
-;   fadd.d fa5, fa1, fa2
-;   fadd.d fa0, ft9, ft10
-;   fadd.d fa1, ft11, fs0
-;   fadd.d fa2, fs1, fs2
-;   fadd.d ft9, fs3, fs4
-;   fadd.d ft10, fs5, fs6
-;   fadd.d fa3, fa3, fa4
-;   fadd.d fa4, fa5, fa0
-;   fadd.d fa5, fa1, fa2
-;   fadd.d fa0, ft9, ft10
-;   fadd.d fa3, fa3, fa4
-;   fadd.d fa4, fa5, fa0
-;   fadd.d fa0, fa3, fa4
+;   fadd.d fa3, fa0, fa0, rne
+;   fadd.d fa4, fa0, fa0, rne
+;   fadd.d fa5, fa0, fa0, rne
+;   fadd.d fa1, fa0, fa0, rne
+;   fadd.d fa2, fa0, fa0, rne
+;   fadd.d ft9, fa0, fa0, rne
+;   fadd.d ft10, fa0, fa0, rne
+;   fadd.d ft11, fa0, fa0, rne
+;   fadd.d fs0, fa0, fa0, rne
+;   fadd.d fs1, fa0, fa0, rne
+;   fadd.d fs2, fa0, fa0, rne
+;   fadd.d fs3, fa0, fa0, rne
+;   fadd.d fs4, fa0, fa0, rne
+;   fadd.d fs5, fa0, fa0, rne
+;   fadd.d fs6, fa0, fa0, rne
+;   fadd.d fs7, fa0, fa0, rne
+;   fadd.d fs8, fa0, fa0, rne
+;   fadd.d fs9, fa0, fa0, rne
+;   fadd.d fs10, fa0, fa0, rne
+;   fadd.d fs11, fa0, fa0, rne
+;   fadd.d ft0, fa0, fa0, rne
+;   fadd.d ft1, fa0, fa0, rne
+;   fadd.d ft2, fa0, fa0, rne
+;   fadd.d ft3, fa0, fa0, rne
+;   fadd.d ft4, fa0, fa0, rne
+;   fadd.d ft5, fa0, fa0, rne
+;   fadd.d ft6, fa0, fa0, rne
+;   fadd.d ft7, fa0, fa0, rne
+;   fadd.d fa6, fa0, fa0, rne
+;   fadd.d fa7, fa0, fa0, rne
+;   fadd.d ft8, fa0, fa0, rne
+;   fadd.d fa3, fa0, fa3, rne
+;   fadd.d fa4, fa4, fa5, rne
+;   fadd.d fa5, fa1, fa2, rne
+;   fadd.d fa0, ft9, ft10, rne
+;   fadd.d fa1, ft11, fs0, rne
+;   fadd.d fa2, fs1, fs2, rne
+;   fadd.d ft9, fs3, fs4, rne
+;   fadd.d ft10, fs5, fs6, rne
+;   fadd.d ft11, fs7, fs8, rne
+;   fadd.d fs0, fs9, fs10, rne
+;   fadd.d fs1, fs11, ft0, rne
+;   fadd.d fs2, ft1, ft2, rne
+;   fadd.d fs3, ft3, ft4, rne
+;   fadd.d fs4, ft5, ft6, rne
+;   fadd.d fs5, ft7, fa6, rne
+;   fadd.d fs6, fa7, ft8, rne
+;   fadd.d fa3, fa3, fa4, rne
+;   fadd.d fa4, fa5, fa0, rne
+;   fadd.d fa5, fa1, fa2, rne
+;   fadd.d fa0, ft9, ft10, rne
+;   fadd.d fa1, ft11, fs0, rne
+;   fadd.d fa2, fs1, fs2, rne
+;   fadd.d ft9, fs3, fs4, rne
+;   fadd.d ft10, fs5, fs6, rne
+;   fadd.d fa3, fa3, fa4, rne
+;   fadd.d fa4, fa5, fa0, rne
+;   fadd.d fa5, fa1, fa2, rne
+;   fadd.d fa0, ft9, ft10, rne
+;   fadd.d fa3, fa3, fa4, rne
+;   fadd.d fa4, fa5, fa0, rne
+;   fadd.d fa0, fa3, fa4, rne
 ;   addi sp, sp, 0x60
 ;   fld fs0, -8(sp)
 ;   fld fs2, -0x10(sp)
diff --git a/cranelift/filetests/filetests/isa/riscv64/return-call.clif b/cranelift/filetests/filetests/isa/riscv64/return-call.clif
index ba742c238..2ea34afb4 100644
--- a/cranelift/filetests/filetests/isa/riscv64/return-call.clif
+++ b/cranelift/filetests/filetests/isa/riscv64/return-call.clif
@@ -99,7 +99,7 @@ block0(v0: f64):
 ;   lui a3,1027
 ;   slli a5,a3,40
 ;   fmv.d.x fa1,a5
-;   fadd.d ft0,ft0,fa1
+;   fadd.d ft0,ft0,fa1,rne
 ;   ret
 ;
 ; Disassembled:
@@ -107,7 +107,7 @@ block0(v0: f64):
 ;   lui a3, 0x403
 ;   slli a5, a3, 0x28
 ;   fmv.d.x fa1, a5
-;   fadd.d ft0, ft0, fa1
+;   fadd.d ft0, ft0, fa1, rne
 ;   ret
 
 function %call_f64(f64) -> f64 tail {
diff --git a/cranelift/filetests/filetests/isa/x64/uext-sext-handling.clif b/cranelift/filetests/filetests/isa/x64/uext-sext-handling.clif
new file mode 100644
index 000000000..0908bec08
--- /dev/null
+++ b/cranelift/filetests/filetests/isa/x64/uext-sext-handling.clif
@@ -0,0 +1,38 @@
+test compile
+target x86_64
+
+; The x86_64 system_v call conv respects uext and sext
+function u0:0(i8) system_v {
+    sig0 = (i8 uext) system_v
+    fn0 = u0:0 sig0
+
+block0(v0: i8):
+    call fn0(v0)
+    return
+}
+
+; check: pushq   %rbp
+; nextln: movq    %rsp, %rbp
+; nextln: block0:
+; nextln: movzbq  %dil, %rdi
+; nextln: load_ext_name userextname0+0, %rdx
+; nextln: call    *%rdx
+
+; The x86_64 windows_fastcall call conv respects uext and sext
+function u0:0(i8) windows_fastcall {
+    sig0 = (i8 uext) windows_fastcall
+    fn0 = u0:0 sig0
+
+block0(v0: i8):
+    call fn0(v0)
+    return
+}
+
+; check: pushq   %rbp
+; nextln: movq    %rsp, %rbp
+; nextln: block0:
+; nextln: subq    %rsp, $$32, %rsp
+; nextln: virtual_sp_offset_adjust 32
+; nextln: movzbq  %cl, %rcx
+; nextln: load_ext_name userextname0+0, %r9
+; nextln: call    *%r9
diff --git a/cranelift/filetests/filetests/runtests/conversion-i8-i16.clif b/cranelift/filetests/filetests/runtests/conversion-i8-i16.clif
new file mode 100644
index 000000000..dde4635be
--- /dev/null
+++ b/cranelift/filetests/filetests/runtests/conversion-i8-i16.clif
@@ -0,0 +1,234 @@
+test interpret
+test run
+target aarch64
+target s390x
+target riscv64 has_c has_zcb
+target riscv64
+
+function %f32_to_i8(f32) -> i8 {
+block0(v0: f32):
+    v1 = fcvt_to_sint.i8 v0
+    return v1
+}
+; run: %f32_to_i8(0x0.0) == 0
+; run: %f32_to_i8(0x1.0) == 1
+; run: %f32_to_i8(0x8.1) == 8
+; run: %f32_to_i8(-0x8.1) == -8
+; run: %f32_to_i8(-0x80.1) == 0x80
+; run: %f32_to_i8(0x7f.1) == 0x7f
+; run: %f32_to_i8(-0x0.1) == 0
+
+function %f64_to_i8(f64) -> i8 {
+block0(v0: f64):
+    v1 = fcvt_to_sint.i8 v0
+    return v1
+}
+; run: %f64_to_i8(0x0.0) == 0
+; run: %f64_to_i8(0x1.0) == 1
+; run: %f64_to_i8(0x8.1) == 8
+; run: %f64_to_i8(-0x8.1) == -8
+; run: %f64_to_i8(-0x80.1) == 0x80
+; run: %f64_to_i8(0x7f.1) == 0x7f
+; run: %f64_to_i8(-0x0.1) == 0
+
+function %f32_to_i16(f32) -> i16 {
+block0(v0: f32):
+    v1 = fcvt_to_sint.i16 v0
+    return v1
+}
+; run: %f32_to_i16(0x0.0) == 0
+; run: %f32_to_i16(0x1.0) == 1
+; run: %f32_to_i16(0x8.1) == 8
+; run: %f32_to_i16(-0x8.1) == -8
+; run: %f32_to_i16(-0x8000.1) == 0x8000
+; run: %f32_to_i16(0x7fff.1) == 0x7fff
+; run: %f32_to_i16(-0x0.1) == 0
+
+function %f64_to_i16(f64) -> i16 {
+block0(v0: f64):
+    v1 = fcvt_to_sint.i16 v0
+    return v1
+}
+; run: %f64_to_i16(0x0.0) == 0
+; run: %f64_to_i16(0x1.0) == 1
+; run: %f64_to_i16(0x8.1) == 8
+; run: %f64_to_i16(-0x8.1) == -8
+; run: %f64_to_i16(-0x8000.1) == 0x8000
+; run: %f64_to_i16(0x7fff.1) == 0x7fff
+; run: %f64_to_i16(-0x0.1) == 0
+
+function %f32_to_u8(f32) -> i8 {
+block0(v0:f32):
+    v1 = fcvt_to_uint.i8 v0
+    return v1
+}
+; run: %f32_to_u8(0x0.0) == 0
+; run: %f32_to_u8(0x1.0) == 1
+; run: %f32_to_u8(0x4.2) == 4
+; run: %f32_to_u8(0x4.6) == 4
+; run: %f32_to_u8(-0x0.1) == 0
+
+function %f64_to_u8(f64) -> i8 {
+block0(v0:f64):
+    v1 = fcvt_to_uint.i8 v0
+    return v1
+}
+; run: %f64_to_u8(0x0.0) == 0
+; run: %f64_to_u8(0x1.0) == 1
+; run: %f64_to_u8(0x4.2) == 4
+; run: %f64_to_u8(0x4.6) == 4
+; run: %f64_to_u8(-0x0.1) == 0
+
+function %f32_to_u16(f32) -> i16 {
+block0(v0:f32):
+    v1 = fcvt_to_uint.i16 v0
+    return v1
+}
+; run: %f32_to_u16(0x0.0) == 0
+; run: %f32_to_u16(0x1.0) == 1
+; run: %f32_to_u16(0x4.2) == 4
+; run: %f32_to_u16(0x4.6) == 4
+; run: %f32_to_u16(-0x0.1) == 0
+
+function %f64_to_u16(f64) -> i16 {
+block0(v0:f64):
+    v1 = fcvt_to_uint.i16 v0
+    return v1
+}
+; run: %f64_to_u16(0x0.0) == 0
+; run: %f64_to_u16(0x1.0) == 1
+; run: %f64_to_u16(0x4.2) == 4
+; run: %f64_to_u16(0x4.6) == 4
+; run: %f64_to_u16(-0x0.1) == 0
+
+function %f32_to_i8_sat(f32) -> i8 {
+block0(v0: f32):
+    v1 = fcvt_to_sint_sat.i8 v0
+    return v1
+}
+; run: %f32_to_i8_sat(0x0.0) == 0
+; run: %f32_to_i8_sat(0x1.0) == 1
+; run: %f32_to_i8_sat(0x8.1) == 8
+; run: %f32_to_i8_sat(-0x1.0) == -1
+; run: %f32_to_i8_sat(0x1.fffffep127) == 0x7f
+; run: %f32_to_i8_sat(-0x1.fffffep127) == 0x80
+; run: %f32_to_i8_sat(+NaN) == 0
+; run: %f32_to_i8_sat(-NaN) == 0
+; run: %f32_to_i8_sat(+Inf) == 0x7f
+; run: %f32_to_i8_sat(-Inf) == 0x80
+
+function %f64_to_i8_sat(f64) -> i8 {
+block0(v0: f64):
+    v1 = fcvt_to_sint_sat.i8 v0
+    return v1
+}
+; run: %f64_to_i8_sat(0x0.0) == 0
+; run: %f64_to_i8_sat(0x1.0) == 1
+; run: %f64_to_i8_sat(0x8.1) == 8
+; run: %f64_to_i8_sat(-0x1.0) == -1
+; run: %f64_to_i8_sat(0x1.fffffep127) == 0x7f
+; run: %f64_to_i8_sat(-0x1.fffffep127) == 0x80
+; run: %f64_to_i8_sat(+NaN) == 0
+; run: %f64_to_i8_sat(-NaN) == 0
+; run: %f64_to_i8_sat(+Inf) == 0x7f
+; run: %f64_to_i8_sat(-Inf) == 0x80
+
+function %f32_to_i16_sat(f32) -> i16 {
+block0(v0: f32):
+    v1 = fcvt_to_sint_sat.i16 v0
+    return v1
+}
+; run: %f32_to_i16_sat(0x0.0) == 0
+; run: %f32_to_i16_sat(0x1.0) == 1
+; run: %f32_to_i16_sat(0x8.1) == 8
+; run: %f32_to_i16_sat(-0x1.0) == -1
+; run: %f32_to_i16_sat(0x1.fffffep127) == 0x7fff
+; run: %f32_to_i16_sat(-0x1.fffffep127) == 0x8000
+; run: %f32_to_i16_sat(+NaN) == 0
+; run: %f32_to_i16_sat(-NaN) == 0
+; run: %f32_to_i16_sat(+Inf) == 0x7fff
+; run: %f32_to_i16_sat(-Inf) == 0x8000
+
+function %f64_to_i16_sat(f64) -> i16 {
+block0(v0: f64):
+    v1 = fcvt_to_sint_sat.i16 v0
+    return v1
+}
+; run: %f64_to_i16_sat(0x0.0) == 0
+; run: %f64_to_i16_sat(0x1.0) == 1
+; run: %f64_to_i16_sat(0x8.1) == 8
+; run: %f64_to_i16_sat(-0x1.0) == -1
+; run: %f64_to_i16_sat(0x1.fffffep127) == 0x7fff
+; run: %f64_to_i16_sat(-0x1.fffffep127) == 0x8000
+; run: %f64_to_i16_sat(+NaN) == 0
+; run: %f64_to_i16_sat(-NaN) == 0
+; run: %f64_to_i16_sat(+Inf) == 0x7fff
+; run: %f64_to_i16_sat(-Inf) == 0x8000
+
+function %f32_to_u8_sat(f32) -> i8 {
+block0(v0:f32):
+    v1 = fcvt_to_uint_sat.i8 v0
+    return v1
+}
+; run: %f32_to_u8_sat(0x0.0) == 0
+; run: %f32_to_u8_sat(0x1.0) == 1
+; run: %f32_to_u8_sat(0x4.2) == 4
+; run: %f32_to_u8_sat(0x4.6) == 4
+; run: %f32_to_u8_sat(-0x1.0) == 0
+; run: %f32_to_u8_sat(0x1.fffffep127) == 0xff
+; run: %f32_to_u8_sat(-0x1.fffffep127) == 0
+; run: %f32_to_u8_sat(+NaN) == 0
+; run: %f32_to_u8_sat(-NaN) == 0
+; run: %f32_to_u8_sat(+Inf) == 0xff
+; run: %f32_to_u8_sat(-Inf) == 0
+
+function %f64_to_u8_sat(f64) -> i8 {
+block0(v0:f64):
+    v1 = fcvt_to_uint_sat.i8 v0
+    return v1
+}
+; run: %f64_to_u8_sat(0x0.0) == 0
+; run: %f64_to_u8_sat(0x1.0) == 1
+; run: %f64_to_u8_sat(0x4.2) == 4
+; run: %f64_to_u8_sat(0x4.6) == 4
+; run: %f64_to_u8_sat(-0x1.0) == 0
+; run: %f64_to_u8_sat(0x1.fffffep127) == 0xff
+; run: %f64_to_u8_sat(-0x1.fffffep127) == 0
+; run: %f64_to_u8_sat(+NaN) == 0
+; run: %f64_to_u8_sat(-NaN) == 0
+; run: %f64_to_u8_sat(+Inf) == 0xff
+; run: %f64_to_u8_sat(-Inf) == 0
+
+function %f32_to_u16_sat(f32) -> i16 {
+block0(v0:f32):
+    v1 = fcvt_to_uint_sat.i16 v0
+    return v1
+}
+; run: %f32_to_u16_sat(0x0.0) == 0
+; run: %f32_to_u16_sat(0x1.0) == 1
+; run: %f32_to_u16_sat(0x4.2) == 4
+; run: %f32_to_u16_sat(0x4.6) == 4
+; run: %f32_to_u16_sat(-0x1.0) == 0
+; run: %f32_to_u16_sat(0x1.fffffep127) == 0xffff
+; run: %f32_to_u16_sat(-0x1.fffffep127) == 0
+; run: %f32_to_u16_sat(+NaN) == 0
+; run: %f32_to_u16_sat(-NaN) == 0
+; run: %f32_to_u16_sat(+Inf) == 0xffff
+; run: %f32_to_u16_sat(-Inf) == 0
+
+function %f64_to_u16_sat(f64) -> i16 {
+block0(v0:f64):
+    v1 = fcvt_to_uint_sat.i16 v0
+    return v1
+}
+; run: %f64_to_u16_sat(0x0.0) == 0
+; run: %f64_to_u16_sat(0x1.0) == 1
+; run: %f64_to_u16_sat(0x4.2) == 4
+; run: %f64_to_u16_sat(0x4.6) == 4
+; run: %f64_to_u16_sat(-0x1.0) == 0
+; run: %f64_to_u16_sat(0x1.fffffep127) == 0xffff
+; run: %f64_to_u16_sat(-0x1.fffffep127) == 0
+; run: %f64_to_u16_sat(+NaN) == 0
+; run: %f64_to_u16_sat(-NaN) == 0
+; run: %f64_to_u16_sat(+Inf) == 0xffff
+; run: %f64_to_u16_sat(-Inf) == 0
diff --git a/cranelift/filetests/filetests/runtests/conversion.clif b/cranelift/filetests/filetests/runtests/conversion.clif
index 1040e7e10..5b4a554e0 100644
--- a/cranelift/filetests/filetests/runtests/conversion.clif
+++ b/cranelift/filetests/filetests/runtests/conversion.clif
@@ -7,52 +7,248 @@ target x86_64 has_avx
 target riscv64 has_c has_zcb
 target riscv64
 
-function %fcvt_to_sint(f32) -> i32 {
+function %f32_to_i32(f32) -> i32 {
 block0(v0: f32):
     v1 = fcvt_to_sint.i32 v0
     return v1
 }
-; run: %fcvt_to_sint(0x0.0) == 0
-; run: %fcvt_to_sint(0x1.0) == 1
-; run: %fcvt_to_sint(0x1.d6f346p26) == 123456792
-; run: %fcvt_to_sint(0x8.1) == 8
+; run: %f32_to_i32(0x0.0) == 0
+; run: %f32_to_i32(0x1.0) == 1
+; run: %f32_to_i32(0x1.d6f346p26) == 123456792
+; run: %f32_to_i32(0x8.1) == 8
+; run: %f32_to_i32(-0x8.1) == -8
+; run: %f32_to_i32(-0x0.1) == 0
+; run: %f32_to_i32(-0x1.0p31) == 0x8000_0000
 
-function %fcvt_to_uint(f32) -> i32 {
+function %f64_to_i32(f64) -> i32 {
+block0(v0: f64):
+    v1 = fcvt_to_sint.i32 v0
+    return v1
+}
+; run: %f64_to_i32(0x0.0) == 0
+; run: %f64_to_i32(0x1.0) == 1
+; run: %f64_to_i32(0x1.d6f346p26) == 123456792
+; run: %f64_to_i32(0x8.1) == 8
+; run: %f64_to_i32(-0x8.1) == -8
+; run: %f64_to_i32(-0x0.1) == 0
+; run: %f64_to_i32(-0x1.0p31) == 0x8000_0000
+
+function %f32_to_i64(f32) -> i64 {
+block0(v0: f32):
+    v1 = fcvt_to_sint.i64 v0
+    return v1
+}
+; run: %f32_to_i64(0x0.0) == 0
+; run: %f32_to_i64(0x1.0) == 1
+; run: %f32_to_i64(0x1.d6f346p26) == 123456792
+; run: %f32_to_i64(0x8.1) == 8
+; run: %f32_to_i64(-0x8.1) == -8
+; run: %f32_to_i64(-0x0.1) == 0
+
+function %f64_to_i64(f64) -> i64 {
+block0(v0: f64):
+    v1 = fcvt_to_sint.i64 v0
+    return v1
+}
+; run: %f64_to_i64(0x0.0) == 0
+; run: %f64_to_i64(0x1.0) == 1
+; run: %f64_to_i64(0x1.d6f346p26) == 123456792
+; run: %f64_to_i64(0x8.1) == 8
+; run: %f64_to_i64(-0x8.1) == -8
+; run: %f64_to_i64(-0x0.1) == 0
+
+function %f32_to_u32(f32) -> i32 {
 block0(v0:f32):
     v1 = fcvt_to_uint.i32 v0
     return v1
 }
-; run: %fcvt_to_uint(0x0.0) == 0
-; run: %fcvt_to_uint(0x1.0) == 1
-; run: %fcvt_to_uint(0x4.2) == 4
-; run: %fcvt_to_uint(0x4.6) == 4
-; run: %fcvt_to_uint(0x1.d6f346p26) == 123456792
-; run: %fcvt_to_uint(0xB2D05E00.0) == 3000000000
+; run: %f32_to_u32(0x0.0) == 0
+; run: %f32_to_u32(0x1.0) == 1
+; run: %f32_to_u32(0x4.2) == 4
+; run: %f32_to_u32(0x4.6) == 4
+; run: %f32_to_u32(0x1.d6f346p26) == 123456792
+; run: %f32_to_u32(0xB2D05E00.0) == 3000000000
+; run: %f32_to_u32(-0x0.1) == 0
+
+function %f64_to_u32(f64) -> i32 {
+block0(v0:f64):
+    v1 = fcvt_to_uint.i32 v0
+    return v1
+}
+; run: %f64_to_u32(0x0.0) == 0
+; run: %f64_to_u32(0x1.0) == 1
+; run: %f64_to_u32(0x4.2) == 4
+; run: %f64_to_u32(0x4.6) == 4
+; run: %f64_to_u32(0x1.d6f346p26) == 123456792
+; run: %f64_to_u32(0xB2D05E00.0) == 3000000000
+; run: %f64_to_u32(-0x0.1) == 0
+
+function %f32_to_u64(f32) -> i64 {
+block0(v0:f32):
+    v1 = fcvt_to_uint.i64 v0
+    return v1
+}
+; run: %f32_to_u64(0x0.0) == 0
+; run: %f32_to_u64(0x1.0) == 1
+; run: %f32_to_u64(0x4.2) == 4
+; run: %f32_to_u64(0x4.6) == 4
+; run: %f32_to_u64(0x1.d6f346p26) == 123456792
+; run: %f32_to_u64(0xB2D05E00.0) == 3000000000
+; run: %f32_to_u64(-0x0.1) == 0
+
+function %f64_to_u64(f64) -> i64 {
+block0(v0:f64):
+    v1 = fcvt_to_uint.i64 v0
+    return v1
+}
+; run: %f64_to_u64(0x0.0) == 0
+; run: %f64_to_u64(0x1.0) == 1
+; run: %f64_to_u64(0x4.2) == 4
+; run: %f64_to_u64(0x4.6) == 4
+; run: %f64_to_u64(0x1.d6f346p26) == 123456792
+; run: %f64_to_u64(0xB2D05E00.0) == 3000000000
+; run: %f64_to_u64(-0x0.1) == 0
 
-function %fcvt_to_sint_sat(f32) -> i32 {
+function %f32_to_i32_sat(f32) -> i32 {
 block0(v0: f32):
     v1 = fcvt_to_sint_sat.i32 v0
     return v1
 }
-; run: %fcvt_to_sint_sat(0x0.0) == 0
-; run: %fcvt_to_sint_sat(0x1.0) == 1
-; run: %fcvt_to_sint_sat(0x1.d6f346p26) == 123456792
-; run: %fcvt_to_sint_sat(0x8.1) == 8
-; run: %fcvt_to_sint_sat(-0x1.0) == -1
-; run: %fcvt_to_sint_sat(0x1.fffffep127) == 2147483647
-; run: %fcvt_to_sint_sat(-0x1.fffffep127) == -2147483648
+; run: %f32_to_i32_sat(0x0.0) == 0
+; run: %f32_to_i32_sat(0x1.0) == 1
+; run: %f32_to_i32_sat(0x1.d6f346p26) == 123456792
+; run: %f32_to_i32_sat(0x8.1) == 8
+; run: %f32_to_i32_sat(-0x1.0) == -1
+; run: %f32_to_i32_sat(0x1.fffffep127) == 2147483647
+; run: %f32_to_i32_sat(-0x1.fffffep127) == -2147483648
+; run: %f32_to_i32_sat(+NaN) == 0
+; run: %f32_to_i32_sat(-NaN) == 0
+; run: %f32_to_i32_sat(+Inf) == 2147483647
+; run: %f32_to_i32_sat(-Inf) == -2147483648
 
-function %fcvt_to_uint_sat(f32) -> i32 {
+function %f64_to_i32_sat(f64) -> i32 {
+block0(v0: f64):
+    v1 = fcvt_to_sint_sat.i32 v0
+    return v1
+}
+; run: %f64_to_i32_sat(0x0.0) == 0
+; run: %f64_to_i32_sat(0x1.0) == 1
+; run: %f64_to_i32_sat(0x1.d6f346p26) == 123456792
+; run: %f64_to_i32_sat(0x8.1) == 8
+; run: %f64_to_i32_sat(-0x1.0) == -1
+; run: %f64_to_i32_sat(0x1.fffffep127) == 2147483647
+; run: %f64_to_i32_sat(-0x1.fffffep127) == -2147483648
+; run: %f64_to_i32_sat(+NaN) == 0
+; run: %f64_to_i32_sat(-NaN) == 0
+; run: %f64_to_i32_sat(+Inf) == 2147483647
+; run: %f64_to_i32_sat(-Inf) == -2147483648
+
+function %f32_to_i64_sat(f32) -> i64 {
+block0(v0: f32):
+    v1 = fcvt_to_sint_sat.i64 v0
+    return v1
+}
+; run: %f32_to_i64_sat(0x0.0) == 0
+; run: %f32_to_i64_sat(0x1.0) == 1
+; run: %f32_to_i64_sat(0x1.d6f346p26) == 123456792
+; run: %f32_to_i64_sat(0x8.1) == 8
+; run: %f32_to_i64_sat(-0x1.0) == -1
+; run: %f32_to_i64_sat(0x1.fffffep127) == 0x7fffffff_ffffffff
+; run: %f32_to_i64_sat(-0x1.fffffep127) == 0x80000000_00000000
+; run: %f32_to_i64_sat(+NaN) == 0
+; run: %f32_to_i64_sat(-NaN) == 0
+; run: %f32_to_i64_sat(+Inf) == 0x7fffffff_ffffffff
+; run: %f32_to_i64_sat(-Inf) == 0x80000000_00000000
+
+function %f64_to_i64_sat(f64) -> i64 {
+block0(v0: f64):
+    v1 = fcvt_to_sint_sat.i64 v0
+    return v1
+}
+; run: %f64_to_i64_sat(0x0.0) == 0
+; run: %f64_to_i64_sat(0x1.0) == 1
+; run: %f64_to_i64_sat(0x1.d6f346p26) == 123456792
+; run: %f64_to_i64_sat(0x8.1) == 8
+; run: %f64_to_i64_sat(-0x1.0) == -1
+; run: %f64_to_i64_sat(0x1.fffffep127) == 0x7fffffff_ffffffff
+; run: %f64_to_i64_sat(-0x1.fffffep127) == 0x80000000_00000000
+; run: %f64_to_i64_sat(+NaN) == 0
+; run: %f64_to_i64_sat(-NaN) == 0
+; run: %f64_to_i64_sat(+Inf) == 0x7fffffff_ffffffff
+; run: %f64_to_i64_sat(-Inf) == 0x80000000_00000000
+
+function %f32_to_u32_sat(f32) -> i32 {
 block0(v0:f32):
     v1 = fcvt_to_uint_sat.i32 v0
     return v1
 }
-; run: %fcvt_to_uint_sat(0x0.0) == 0
-; run: %fcvt_to_uint_sat(0x1.0) == 1
-; run: %fcvt_to_uint_sat(0x4.2) == 4
-; run: %fcvt_to_uint_sat(0x4.6) == 4
-; run: %fcvt_to_uint_sat(0x1.d6f346p26) == 123456792
-; run: %fcvt_to_uint_sat(0xB2D05E00.0) == 3000000000
-; run: %fcvt_to_uint_sat(-0x1.0) == 0
-; run: %fcvt_to_uint_sat(0x1.fffffep127) == 4294967295
-; run: %fcvt_to_uint_sat(-0x1.fffffep127) == 0
+; run: %f32_to_u32_sat(0x0.0) == 0
+; run: %f32_to_u32_sat(0x1.0) == 1
+; run: %f32_to_u32_sat(0x4.2) == 4
+; run: %f32_to_u32_sat(0x4.6) == 4
+; run: %f32_to_u32_sat(0x1.d6f346p26) == 123456792
+; run: %f32_to_u32_sat(0xB2D05E00.0) == 3000000000
+; run: %f32_to_u32_sat(-0x1.0) == 0
+; run: %f32_to_u32_sat(0x1.fffffep127) == 4294967295
+; run: %f32_to_u32_sat(-0x1.fffffep127) == 0
+; run: %f32_to_u32_sat(+NaN) == 0
+; run: %f32_to_u32_sat(-NaN) == 0
+; run: %f32_to_u32_sat(+Inf) == 0xffffffff
+; run: %f32_to_u32_sat(-Inf) == 0
+
+function %f64_to_u32_sat(f64) -> i32 {
+block0(v0:f64):
+    v1 = fcvt_to_uint_sat.i32 v0
+    return v1
+}
+; run: %f64_to_u32_sat(0x0.0) == 0
+; run: %f64_to_u32_sat(0x1.0) == 1
+; run: %f64_to_u32_sat(0x4.2) == 4
+; run: %f64_to_u32_sat(0x4.6) == 4
+; run: %f64_to_u32_sat(0x1.d6f346p26) == 123456792
+; run: %f64_to_u32_sat(0xB2D05E00.0) == 3000000000
+; run: %f64_to_u32_sat(-0x1.0) == 0
+; run: %f64_to_u32_sat(0x1.fffffep127) == 4294967295
+; run: %f64_to_u32_sat(-0x1.fffffep127) == 0
+; run: %f64_to_u32_sat(+NaN) == 0
+; run: %f64_to_u32_sat(-NaN) == 0
+; run: %f64_to_u32_sat(+Inf) == 0xffffffff
+; run: %f64_to_u32_sat(-Inf) == 0
+
+function %f32_to_u64_sat(f32) -> i64 {
+block0(v0:f32):
+    v1 = fcvt_to_uint_sat.i64 v0
+    return v1
+}
+; run: %f32_to_u64_sat(0x0.0) == 0
+; run: %f32_to_u64_sat(0x1.0) == 1
+; run: %f32_to_u64_sat(0x4.2) == 4
+; run: %f32_to_u64_sat(0x4.6) == 4
+; run: %f32_to_u64_sat(0x1.d6f346p26) == 123456792
+; run: %f32_to_u64_sat(0xB2D05E00.0) == 3000000000
+; run: %f32_to_u64_sat(-0x1.0) == 0
+; run: %f32_to_u64_sat(0x1.fffffep127) == 0xffffffff_ffffffff
+; run: %f32_to_u64_sat(-0x1.fffffep127) == 0
+; run: %f32_to_u64_sat(+NaN) == 0
+; run: %f32_to_u64_sat(-NaN) == 0
+; run: %f32_to_u64_sat(+Inf) == 0xffffffff_ffffffff
+; run: %f32_to_u64_sat(-Inf) == 0
+
+function %f64_to_u64_sat(f64) -> i64 {
+block0(v0:f64):
+    v1 = fcvt_to_uint_sat.i64 v0
+    return v1
+}
+; run: %f64_to_u64_sat(0x0.0) == 0
+; run: %f64_to_u64_sat(0x1.0) == 1
+; run: %f64_to_u64_sat(0x4.2) == 4
+; run: %f64_to_u64_sat(0x4.6) == 4
+; run: %f64_to_u64_sat(0x1.d6f346p26) == 123456792
+; run: %f64_to_u64_sat(0xB2D05E00.0) == 3000000000
+; run: %f64_to_u64_sat(-0x1.0) == 0
+; run: %f64_to_u64_sat(0x1.fffffep127) == 0xffffffff_ffffffff
+; run: %f64_to_u64_sat(-0x1.fffffep127) == 0
+; run: %f64_to_u64_sat(+NaN) == 0
+; run: %f64_to_u64_sat(-NaN) == 0
+; run: %f64_to_u64_sat(+Inf) == 0xffffffff_ffffffff
+; run: %f64_to_u64_sat(-Inf) == 0
diff --git a/cranelift/filetests/filetests/runtests/issue-5992.clif b/cranelift/filetests/filetests/runtests/issue-5992.clif
new file mode 100644
index 000000000..a1d9e3692
--- /dev/null
+++ b/cranelift/filetests/filetests/runtests/issue-5992.clif
@@ -0,0 +1,36 @@
+test interpret
+test run
+target riscv64
+target aarch64
+target s390x
+
+function %a_f32(f32) -> i16 {
+block0(v0: f32):
+    v1 = fcvt_to_uint_sat.i16 v0
+    return v1
+}
+; run: %a_f32(0x1.949400p21) == -1
+
+
+function %b_f32(f32) -> i8 {
+block0(v0: f32):
+    v1 = fcvt_to_uint_sat.i8 v0
+    return v1
+}
+; run: %b_f32(0x1.949400p21) == -1
+
+
+function %a_f64(f64) -> i16 {
+block0(v0: f64):
+    v1 = fcvt_to_uint_sat.i16 v0
+    return v1
+}
+; run: %a_f64(0x1.949400p21) == -1
+
+
+function %b_f64(f64) -> i8 {
+block0(v0: f64):
+    v1 = fcvt_to_uint_sat.i8 v0
+    return v1
+}
+; run: %b_f64(0x1.949400p21) == -1
diff --git a/cranelift/filetests/filetests/runtests/issue-5993.clif b/cranelift/filetests/filetests/runtests/issue-5993.clif
new file mode 100644
index 000000000..b19a865b2
--- /dev/null
+++ b/cranelift/filetests/filetests/runtests/issue-5993.clif
@@ -0,0 +1,19 @@
+test interpret
+test run
+target riscv64
+target aarch64
+target s390x
+
+function %a(f64) -> i8 {
+block0(v0: f64):
+    v1 = fcvt_to_sint_sat.i8 v0
+    return v1
+}
+; run: %a(-0x1.811d818400000p30) == -128
+
+function %b(f64) -> i16 {
+block0(v0: f64):
+    v1 = fcvt_to_sint_sat.i16 v0
+    return v1
+}
+; run: %b(-0x1.811d818400000p30) == -32768
diff --git a/tests/all/async_functions.rs b/tests/all/async_functions.rs
index f2cac9ffd..3796c9b05 100644
--- a/tests/all/async_functions.rs
+++ b/tests/all/async_functions.rs
@@ -311,7 +311,8 @@ async fn cancel_during_run() {
 async fn iloop_with_fuel() {
     let engine = Engine::new(Config::new().async_support(true).consume_fuel(true)).unwrap();
     let mut store = Store::new(&engine, ());
-    store.out_of_fuel_async_yield(1_000, 10);
+    store.set_fuel(10_000).unwrap();
+    store.fuel_async_yield_interval(Some(100)).unwrap();
     let module = Module::new(
         &engine,
         "
@@ -326,14 +327,15 @@ async fn iloop_with_fuel() {
 
     // This should yield a bunch of times but eventually finish
     let (_, pending) = CountPending::new(Box::pin(instance)).await;
-    assert!(pending > 100);
+    assert_eq!(pending, 99);
 }
 
 #[tokio::test]
 async fn fuel_eventually_finishes() {
     let engine = Engine::new(Config::new().async_support(true).consume_fuel(true)).unwrap();
     let mut store = Store::new(&engine, ());
-    store.out_of_fuel_async_yield(u64::max_value(), 10);
+    store.set_fuel(u64::MAX).unwrap();
+    store.fuel_async_yield_interval(Some(10)).unwrap();
     let module = Module::new(
         &engine,
         "
diff --git a/tests/all/component_model/async.rs b/tests/all/component_model/async.rs
index 116ced19c..cbb398fdd 100644
--- a/tests/all/component_model/async.rs
+++ b/tests/all/component_model/async.rs
@@ -155,7 +155,8 @@ async fn resume_separate_thread() -> Result<()> {
 
     execute_across_threads(async move {
         let mut store = Store::new(&engine, ());
-        store.out_of_fuel_async_yield(u64::MAX, 1);
+        store.set_fuel(u64::MAX).unwrap();
+        store.fuel_async_yield_interval(Some(1)).unwrap();
         linker.instantiate_async(&mut store, &component).await?;
         Ok::<_, anyhow::Error>(())
     })
@@ -207,7 +208,8 @@ async fn poll_through_wasm_activation() -> Result<()> {
         let engine = engine.clone();
         async move {
             let mut store = Store::new(&engine, ());
-            store.out_of_fuel_async_yield(u64::MAX, 1);
+            store.set_fuel(u64::MAX).unwrap();
+            store.fuel_async_yield_interval(Some(1)).unwrap();
             let instance = linker.instantiate_async(&mut store, &component).await?;
             let func = instance.get_typed_func::<(Vec<u8>,), ()>(&mut store, "run")?;
             func.call_async(&mut store, (vec![1, 2, 3],)).await?;
diff --git a/tests/all/fuel.rs b/tests/all/fuel.rs
index df046763c..6686daf5c 100644
--- a/tests/all/fuel.rs
+++ b/tests/all/fuel.rs
@@ -53,9 +53,9 @@ fn fuel_consumed(wasm: &[u8]) -> u64 {
     let engine = Engine::new(&config).unwrap();
     let module = Module::new(&engine, wasm).unwrap();
     let mut store = Store::new(&engine, ());
-    store.add_fuel(u64::max_value()).unwrap();
+    store.set_fuel(u64::MAX).unwrap();
     drop(Instance::new(&mut store, &module, &[]));
-    store.fuel_consumed().unwrap()
+    u64::MAX - store.get_fuel().unwrap()
 }
 
 #[test]
@@ -116,7 +116,7 @@ fn iloop() {
         let engine = Engine::new(&config).unwrap();
         let module = Module::new(&engine, wat).unwrap();
         let mut store = Store::new(&engine, ());
-        store.add_fuel(10_000).unwrap();
+        store.set_fuel(10_000).unwrap();
         let error = Instance::new(&mut store, &module, &[]).err().unwrap();
         assert_eq!(error.downcast::<Trap>().unwrap(), Trap::OutOfFuel);
     }
@@ -128,29 +128,10 @@ fn manual_fuel() {
     config.consume_fuel(true);
     let engine = Engine::new(&config).unwrap();
     let mut store = Store::new(&engine, ());
-    store.add_fuel(10_000).unwrap();
-    assert_eq!(store.fuel_consumed(), Some(0));
-    assert_eq!(store.fuel_remaining(), Some(10_000));
-    assert_eq!(store.consume_fuel(1).unwrap(), 9_999);
-    assert_eq!(store.fuel_consumed(), Some(1));
-    assert_eq!(store.fuel_remaining(), Some(9_999));
-    assert!(store.consume_fuel(10_000).is_err());
-    assert_eq!(store.consume_fuel(999).unwrap(), 9_000);
-    assert!(store.consume_fuel(10_000).is_err());
-    assert_eq!(store.consume_fuel(8998).unwrap(), 2);
-    assert!(store.consume_fuel(3).is_err());
-    assert_eq!(store.consume_fuel(1).unwrap(), 1);
-    assert_eq!(store.consume_fuel(1).unwrap(), 0);
-    assert_eq!(store.consume_fuel(0).unwrap(), 0);
-    assert_eq!(store.fuel_remaining(), Some(0));
-    store.add_fuel(5_000).unwrap();
-    assert_eq!(store.fuel_consumed(), Some(10_000));
-    assert_eq!(store.consume_fuel(2_500).unwrap(), 2_500);
-    assert_eq!(store.fuel_consumed(), Some(12_500));
-    store.reset_fuel(5_000).unwrap();
-    assert_eq!(store.fuel_consumed(), Some(0));
-    assert_eq!(store.consume_fuel(2_500).unwrap(), 2_500);
-    assert_eq!(store.fuel_consumed(), Some(2_500));
+    store.set_fuel(10_000).unwrap();
+    assert_eq!(store.get_fuel().ok(), Some(10_000));
+    assert_eq!(store.set_fuel(1).ok(), Some(()));
+    assert_eq!(store.get_fuel().ok(), Some(1));
 }
 
 #[test]
@@ -173,12 +154,11 @@ fn host_function_consumes_all() {
     )
     .unwrap();
     let mut store = Store::new(&engine, ());
-    store.add_fuel(FUEL).unwrap();
+    store.set_fuel(FUEL).unwrap();
     let func = Func::wrap(&mut store, |mut caller: Caller<'_, ()>| {
-        let consumed = caller.fuel_consumed().unwrap();
-        let remaining = caller.fuel_remaining().unwrap();
-        assert_eq!(remaining, FUEL - consumed);
-        assert_eq!(caller.consume_fuel(remaining - 1).unwrap(), 1);
+        let remaining = caller.get_fuel().unwrap();
+        assert_eq!(remaining, FUEL - 2);
+        assert!(caller.set_fuel(1).is_ok());
     });
 
     let instance = Instance::new(&mut store, &module, &[func.into()]).unwrap();
@@ -193,11 +173,8 @@ fn manual_edge_cases() {
     config.consume_fuel(true);
     let engine = Engine::new(&config).unwrap();
     let mut store = Store::new(&engine, ());
-    store.add_fuel(u64::MAX).unwrap();
-    assert_eq!(store.fuel_consumed(), Some(0));
-    assert!(store.consume_fuel(u64::MAX).is_err());
-    assert!(store.consume_fuel(i64::MAX as u64 + 1).is_err());
-    assert_eq!(store.consume_fuel(i64::MAX as u64).unwrap(), 0);
+    store.set_fuel(u64::MAX).unwrap();
+    assert_eq!(store.get_fuel().unwrap(), u64::MAX);
 }
 
 #[test]
@@ -227,8 +204,8 @@ fn unconditionally_trapping_memory_accesses_save_fuel_before_trapping() {
 
     let mut store = Store::new(&engine, ());
     let init_fuel = 1_000;
-    store.add_fuel(init_fuel).unwrap();
-    assert_eq!(init_fuel, store.fuel_remaining().unwrap());
+    store.set_fuel(init_fuel).unwrap();
+    assert_eq!(init_fuel, store.get_fuel().unwrap());
 
     let instance = Instance::new(&mut store, &module, &[]).unwrap();
     let f = instance
@@ -240,7 +217,6 @@ fn unconditionally_trapping_memory_accesses_save_fuel_before_trapping() {
 
     // The `i32.add` consumed some fuel before the unconditionally trapping
     // memory access.
-    let consumed_fuel = store.fuel_consumed().unwrap();
+    let consumed_fuel = init_fuel - store.get_fuel().unwrap();
     assert!(consumed_fuel > 0);
-    assert_eq!(init_fuel, consumed_fuel + store.fuel_remaining().unwrap());
 }