Enable RISC-V 32-bit Linux tests (#10523)

Use the multilib RISC-V toolchain to build and test RISC-V 32-bit Linux
runtime.

* Update the bootstrap script to use the same toolchain/simulator
tarballs as the RISC-V CI docker
(https://github.com/iree-org/iree/pull/10490)
* Build and run unit tests under `runtime` and `tests` since RISC-V
32-bit Linux supports `iree-check-module` and other runtime testing
tools.
* Enable RISC-V 32-bit Linux tests in CI.
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0e13ec5..2d7c146 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -601,7 +601,7 @@
             gcr.io/iree-oss/android@sha256:76c2a52dcd6d07601227b965ac87d021c1d2d5e2d01f46ad58da28c89267f2ab \
             build_tools/cmake/build_android.sh
 
-  riscv32:
+  baremetal_riscv32:
     needs: [setup, build_all]
     if: needs.setup.outputs.should-run == 'true'
     runs-on:
@@ -625,7 +625,7 @@
         run: gcloud alpha storage cp "${BUILD_DIR_GCS_ARTIFACT}" "${BUILD_DIR_ARCHIVE}"
       - name: "Extracting install from build dir archive"
         run: tar -xf "${BUILD_DIR_ARCHIVE}" "${BUILD_DIR}/install"
-      - name: "Cross-compiling and testing riscv32"
+      - name: "Cross-compiling and testing riscv32 baremetal"
         run: |
           ./build_tools/github_actions/docker_run.sh \
             --env "RISCV_ARCH=${BUILD_ARCH}" \
@@ -636,7 +636,7 @@
             bash -euo pipefail -c \
             "./build_tools/cmake/build_riscv.sh && tests/riscv32/smoke.sh"
 
-  riscv64:
+  linux_riscv:
     needs: [setup, build_all, build_tf_integrations]
     if: needs.setup.outputs.should-run == 'true'
     runs-on:
@@ -645,9 +645,12 @@
       - environment=${{ needs.setup.outputs.runner-env }}
       - cpu
       - os-family=Linux
+    strategy:
+      matrix:
+        architecture: [rv64, rv32-linux]
     env:
-      BUILD_RISCV_DIR: "build-riscv64"
-      BUILD_ARCH: "rv64"
+      BUILD_RISCV_DIR: build-${{ matrix.architecture }}
+      BUILD_ARCH: ${{ matrix.architecture }}
       BUILD_DIR: ${{ needs.build_all.outputs.build-dir }}
       BUILD_DIR_ARCHIVE: ${{ needs.build_all.outputs.build-dir-archive }}
       BUILD_DIR_GCS_ARTIFACT: ${{ needs.build_all.outputs.build-dir-gcs-artifact }}
@@ -667,7 +670,7 @@
         run: gcloud alpha storage cp "${TF_BINARIES_GCS_ARTIFACT}" "${TF_BINARIES_ARCHIVE}"
       - name: "Extracting TF binaries archive"
         run: tar -xf "${TF_BINARIES_ARCHIVE}"
-      - name: "Cross-compiling and testing riscv64"
+      - name: "Cross-compiling and testing RISC-V Linux"
         run: |
           ./build_tools/github_actions/docker_run.sh \
             --env "RISCV_ARCH=${BUILD_ARCH}" \
@@ -678,7 +681,7 @@
             --env "LLVM_BIN_DIR=${BUILD_DIR}/third_party/llvm-project/llvm/bin" \
             gcr.io/iree-oss/riscv@sha256:d6f0e293a50faf5abbd564c1d1bb9dc6456d7ce93d07b131c381fa64c1daed62 \
             bash -euo pipefail -c \
-              "./build_tools/cmake/build_riscv.sh && ./build_tools/cmake/test_riscv64.sh"
+              "./build_tools/cmake/build_riscv.sh && ./build_tools/cmake/test_riscv.sh"
 
   ##############################################################################
 
@@ -715,8 +718,8 @@
 
       # Crosscompilation
       - android_arm64
-      - riscv32
-      - riscv64
+      - baremetal_riscv32
+      - linux_riscv
 
       # Benchmark pipeline
       - benchmarks
diff --git a/build_tools/cmake/build_riscv.sh b/build_tools/cmake/build_riscv.sh
index b7e5a56..d925a22 100755
--- a/build_tools/cmake/build_riscv.sh
+++ b/build_tools/cmake/build_riscv.sh
@@ -52,7 +52,7 @@
   -DIREE_ENABLE_CPUINFO=OFF
 )
 
-if [[ "${RISCV_ARCH}" == "rv64" ]]; then
+if [[ "${RISCV_ARCH}" == "rv64" || "${RISCV_ARCH}" == "rv32-linux" ]]; then
   args+=(
     -DRISCV_TOOLCHAIN_ROOT="${RISCV_RV64_LINUX_TOOLCHAIN_ROOT}"
   )
@@ -98,7 +98,7 @@
 "${CMAKE_BIN}" "${args[@]}" "${ROOT_DIR}"
 "${CMAKE_BIN}" --build "${BUILD_RISCV_DIR}" -- -k 0
 
-if [[ "${RISCV_ARCH}" == "rv64" ]]; then
+if [[ "${RISCV_ARCH}" == "rv64" || "${RISCV_ARCH}" == "rv32-linux" ]]; then
   echo "Building test deps for RISC-V"
   echo "-----------------------------"
   "${CMAKE_BIN}" --build "${BUILD_RISCV_DIR}" --target iree-test-deps -- -k 0
diff --git a/build_tools/cmake/iree_bytecode_module.cmake b/build_tools/cmake/iree_bytecode_module.cmake
index 6bab225..f8dd9e4 100644
--- a/build_tools/cmake/iree_bytecode_module.cmake
+++ b/build_tools/cmake/iree_bytecode_module.cmake
@@ -116,6 +116,13 @@
     # specific CPU flags. Add the llvm flags to support RV64 RVV codegen if
     # llvm-target-triple is not specified.
     list(APPEND _ARGS ${RISCV64_TEST_DEFAULT_LLVM_FLAGS})
+  elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv32" AND
+         CMAKE_SYSTEM_NAME STREQUAL "Linux" AND
+         NOT _RULE_FLAGS MATCHES "iree-llvm-target-triple")
+    # RV32 Linux crosscompile toolchain can support iree-compile with
+    # specific CPU flags. Add the llvm flags to support RV32 RVV codegen if
+    # llvm-target-triple is not specified.
+    list(APPEND _ARGS ${RISCV32_TEST_DEFAULT_LLVM_FLAGS})
   endif()
 
   if(_RULE_FRIENDLY_NAME)
diff --git a/build_tools/cmake/iree_cc_test.cmake b/build_tools/cmake/iree_cc_test.cmake
index 645532e..bbecd24 100644
--- a/build_tools/cmake/iree_cc_test.cmake
+++ b/build_tools/cmake/iree_cc_test.cmake
@@ -160,7 +160,8 @@
         TEST_TMPDIR=${_ANDROID_ABS_DIR}/test_tmpdir
     )
     set_property(TEST ${_NAME_PATH} PROPERTY ENVIRONMENT ${_ENVIRONMENT_VARS})
-  elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64" AND
+  elseif((CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64" OR
+          CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv32") AND
          CMAKE_SYSTEM_NAME STREQUAL "Linux")
     # The test target needs to run within the QEMU emulator for RV64 Linux
     # crosscompile build or on-device.
@@ -168,7 +169,7 @@
       NAME
         ${_NAME_PATH}
       COMMAND
-       "${IREE_ROOT_DIR}/build_tools/cmake/run_riscv64_test.sh"
+       "${IREE_ROOT_DIR}/build_tools/cmake/run_riscv_test.sh"
         "$<TARGET_FILE:${_NAME}>"
         ${_RULE_ARGS}
     )
diff --git a/build_tools/cmake/iree_check_test.cmake b/build_tools/cmake/iree_check_test.cmake
index 98e08fa..21c1482 100644
--- a/build_tools/cmake/iree_check_test.cmake
+++ b/build_tools/cmake/iree_check_test.cmake
@@ -50,6 +50,13 @@
     # specific CPU flags. Add the llvm flags to support RV64 RVV codegen if
     # llvm-target-triple is not specified.
     list(APPEND _RULE_FLAGS ${RISCV64_TEST_DEFAULT_LLVM_FLAGS})
+  elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv32" AND
+         CMAKE_SYSTEM_NAME STREQUAL "Linux" AND
+         NOT _RULE_FLAGS MATCHES "iree-llvm-target-triple")
+    # RV32 Linux crosscompile toolchain can support iree-compile with
+    # specific CPU flags. Add the llvm flags to support RV32 RVV codegen if
+    # llvm-target-triple is not specified.
+    list(APPEND _RULE_FLAGS ${RISCV32_TEST_DEFAULT_LLVM_FLAGS})
   endif()
 
   if(_RULE_TARGET_CPU_FEATURES)
diff --git a/build_tools/cmake/iree_hal_cts_test_suite.cmake b/build_tools/cmake/iree_hal_cts_test_suite.cmake
index ef1790c..d4685c5 100644
--- a/build_tools/cmake/iree_hal_cts_test_suite.cmake
+++ b/build_tools/cmake/iree_hal_cts_test_suite.cmake
@@ -92,6 +92,9 @@
     if(CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64" AND
        CMAKE_SYSTEM_NAME STREQUAL "Linux")
       list(APPEND  _TRANSLATE_FLAGS ${RISCV64_TEST_DEFAULT_LLVM_FLAGS})
+    elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv32" AND
+           CMAKE_SYSTEM_NAME STREQUAL "Linux")
+      list(APPEND _TRANSLATE_FLAGS ${RISCV32_TEST_DEFAULT_LLVM_FLAGS})
     endif()
 
     # Skip if already created (multiple suites using the same compiler setting).
diff --git a/build_tools/cmake/iree_native_test.cmake b/build_tools/cmake/iree_native_test.cmake
index 4915be4..5cb93c9 100644
--- a/build_tools/cmake/iree_native_test.cmake
+++ b/build_tools/cmake/iree_native_test.cmake
@@ -113,7 +113,8 @@
         "TEST_TMPDIR=${_ANDROID_ABS_DIR}/test_tmpdir"
     )
     set_property(TEST ${_TEST_NAME} PROPERTY ENVIRONMENT ${_ENVIRONMENT_VARS})
-  elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64" AND
+  elseif((CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64" OR
+          CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv32") AND
          CMAKE_SYSTEM_NAME STREQUAL "Linux")
     # The test target needs to run within the QEMU emulator for RV64 Linux
     # crosscompile build or on-device.
@@ -121,7 +122,7 @@
       NAME
         ${_TEST_NAME}
       COMMAND
-        "${IREE_ROOT_DIR}/build_tools/cmake/run_riscv64_test.sh"
+        "${IREE_ROOT_DIR}/build_tools/cmake/run_riscv_test.sh"
         "$<TARGET_FILE:${_SRC_TARGET}>"
         ${_RULE_ARGS}
     )
diff --git a/build_tools/cmake/riscv.toolchain.cmake b/build_tools/cmake/riscv.toolchain.cmake
index 590ac7e..4188b6a 100644
--- a/build_tools/cmake/riscv.toolchain.cmake
+++ b/build_tools/cmake/riscv.toolchain.cmake
@@ -18,13 +18,7 @@
 
 set(CMAKE_SYSTEM_PROCESSOR riscv)
 
-if(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux)
-  set(RISCV_HOST_TAG linux)
-elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin)
-  set(RISCV_HOST_TAG darwin)
-endif()
-
-set(RISCV_TOOLCHAIN_NAME clang)
+set(RISCV_HOST_TAG linux)
 
 set(RISCV_TOOL_PATH "$ENV{HOME}/riscv" CACHE PATH "RISC-V tool path")
 
@@ -49,7 +43,10 @@
   set(CMAKE_SYSTEM_PROCESSOR riscv64)
   set(CMAKE_SYSTEM_NAME Linux)
   set(CMAKE_SYSTEM_LIBRARY_PATH "${RISCV_TOOLCHAIN_ROOT}/sysroot/usr/lib")
-  set(RISCV_COMPILER_FLAGS "${RISCV_COMPILER_FLAGS} -march=rv64gc -mabi=lp64d")
+  # Specify ISP spec for march=rv64gc. This is to resolve the mismatch between
+  # llvm and binutil ISA version.
+  set(RISCV_COMPILER_FLAGS "${RISCV_COMPILER_FLAGS} \
+      -march=rv64i2p0ma2p0f2p0d2p0c2p0 -mabi=lp64d")
   set(RISCV_LINKER_FLAGS "${RISCV_LINKER_FLAGS} -lstdc++ -lpthread -lm -ldl")
   set(RISCV64_TEST_DEFAULT_LLVM_FLAGS
     "--iree-llvm-target-triple=riscv64"
@@ -59,6 +56,25 @@
     "--riscv-v-fixed-length-vector-lmul-max=8"
     "--riscv-v-vector-bits-min=512"
     CACHE INTERNAL "Default llvm codegen flags for testing purposes")
+elseif(RISCV_CPU STREQUAL "rv32-linux")
+  set(CMAKE_SYSTEM_PROCESSOR riscv32)
+  set(CMAKE_SYSTEM_NAME Linux)
+  list(APPEND CMAKE_SYSTEM_LIBRARY_PATH
+    "${RISCV_TOOLCHAIN_ROOT}/sysroot/usr/lib32"
+    "${RISCV_TOOLCHAIN_ROOT}/sysroot/usr/lib32/ilp32d"
+  )
+  set(RISCV_COMPILER_FLAGS "${RISCV_COMPILER_FLAGS} \
+      -march=rv32i2p0ma2p0f2p0d2p0c2p0 -mabi=ilp32d \
+      -Wno-atomic-alignment")
+  set(RISCV_LINKER_FLAGS "${RISCV_LINKER_FLAGS} -lstdc++ -lpthread -lm -ldl -latomic")
+  set(RISCV32_TEST_DEFAULT_LLVM_FLAGS
+    "--iree-llvm-target-triple=riscv32"
+    "--iree-llvm-target-cpu=generic-rv32"
+    "--iree-llvm-target-abi=ilp32d"
+    "--iree-llvm-target-cpu-features=+m,+a,+f,+d,+zvl512b,+zve32f"
+    "--riscv-v-fixed-length-vector-lmul-max=8"
+    "--riscv-v-vector-bits-min=512"
+    CACHE INTERNAL "Default llvm codegen flags for testing purposes")
 elseif(RISCV_CPU STREQUAL "rv32-baremetal")
   set(CMAKE_SYSTEM_PROCESSOR riscv32)
   set(CMAKE_SYSTEM_NAME Generic)
@@ -74,7 +90,10 @@
   set(IREE_HAL_EXECUTABLE_LOADER_VMVX_MODULE ON CACHE BOOL "" FORCE)
   set(CMAKE_SYSTEM_LIBRARY_PATH "${RISCV_TOOLCHAIN_ROOT}/riscv32-unknown-elf/lib")
   set(IREE_ENABLE_THREADING OFF CACHE BOOL "" FORCE)
-  set(RISCV_COMPILER_FLAGS "${RISCV_COMPILER_FLAGS} -march=rv32imf -mabi=ilp32 -DIREE_PLATFORM_GENERIC=1 -DIREE_SYNCHRONIZATION_DISABLE_UNSAFE=1 \
+  # Specify ISP spec for march=rv64gc. This is to resolve the mismatch between
+  # llvm and binutil ISA version.
+  set(RISCV_COMPILER_FLAGS "${RISCV_COMPILER_FLAGS} \
+      -march=rv32i2p0mf2p0 -mabi=ilp32 -DIREE_PLATFORM_GENERIC=1 -DIREE_SYNCHRONIZATION_DISABLE_UNSAFE=1 \
       -DIREE_FILE_IO_ENABLE=0 -DIREE_TIME_NOW_FN=\"\{ return 0; \}\" -DIREE_DEVICE_SIZE_T=uint32_t -DPRIdsz=PRIu32")
   set(RISCV_LINKER_FLAGS "${RISCV_LINKER_FLAGS} -lm")
 endif()
diff --git a/build_tools/cmake/run_riscv64_test.sh b/build_tools/cmake/run_riscv64_test.sh
deleted file mode 100755
index 0fe78b8..0000000
--- a/build_tools/cmake/run_riscv64_test.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-
-# Copyright 2022 The IREE Authors
-#
-# Licensed under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-# Wrapper script to run the artifact on RISC-V 64-bit Linux device.
-# This script checks if QEMU emulator is set, and use either the emulator or
-# the actual device to run the cross-compiled RISC-V 64-bit linux artifacts.
-
-set -x
-set -e
-
-# A QEMU 64 Linux emulator must be available at the path specified by the
-# `QEMU_RV64_BIN` environment variable to run the artifacts under the emulator.
-if [[ ! -z "${QEMU_RV64_BIN}" ]]; then
-  "${QEMU_RV64_BIN}" "-cpu" "rv64,x-v=true,x-k=true,vlen=512,elen=64,vext_spec=v1.0" \
-  "-L" "${RISCV_RV64_LINUX_TOOLCHAIN_ROOT}/sysroot" $*
-fi
-
-# TODO(dcaballe): Add on-device run commands.
diff --git a/build_tools/cmake/run_riscv_test.sh b/build_tools/cmake/run_riscv_test.sh
new file mode 100755
index 0000000..f0af625
--- /dev/null
+++ b/build_tools/cmake/run_riscv_test.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# Copyright 2022 The IREE Authors
+#
+# Licensed under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+# Wrapper script to run the artifact on RISC-V 32/64-bit Linux device.
+# This script checks if QEMU emulator is set, and use either the emulator or
+# the actual device to run the cross-compiled RISC-V linux artifacts.
+
+set -x
+set -e
+
+RISCV_ARCH="${RISCV_ARCH:-rv64}"
+
+# A QEMU Linux emulator must be available Within the system that matches the
+# processor architecturue. The emulators are at the path specified by the
+# `QEMU_RV64_BIN` or `QEMU_RV32_BIN` environment variable to run the artifacts
+# under the emulator.
+if [[ "${RISCV_ARCH}" == "rv64" ]] && [[ ! -z "${QEMU_RV64_BIN}" ]]; then
+  "${QEMU_RV64_BIN}" "-cpu" "rv64,x-v=true,x-k=true,vlen=512,elen=64,vext_spec=v1.0" \
+  "-L" "${RISCV_RV64_LINUX_TOOLCHAIN_ROOT}/sysroot" "$@"
+elif [[ "${RISCV_ARCH}" == "rv32-linux" ]] && [[ ! -z "${QEMU_RV32_BIN}" ]]; then
+  "${QEMU_RV32_BIN}" "-cpu" "rv32,x-v=true,x-k=true,vlen=512,elen=32,vext_spec=v1.0" \
+  "-L" "${RISCV_RV64_LINUX_TOOLCHAIN_ROOT}/sysroot" "$@"
+else
+# TODO(dcaballe): Add on-device run commands.
+  "$@"
+fi
diff --git a/build_tools/cmake/test_riscv.sh b/build_tools/cmake/test_riscv.sh
new file mode 100755
index 0000000..9b2c042
--- /dev/null
+++ b/build_tools/cmake/test_riscv.sh
@@ -0,0 +1,135 @@
+#!/bin/bash
+
+# Copyright 2021 The IREE Authors
+#
+# Licensed under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+# Test the cross-compiled RISCV 64-bit Linux targets.
+
+set -xeuo pipefail
+
+# Print the UTC time when set -x is on
+export PS4='[$(date -u "+%T %Z")] '
+
+ROOT_DIR="${ROOT_DIR:-$(git rev-parse --show-toplevel)}"
+
+RISCV_ARCH="${RISCV_ARCH:-rv64}"
+
+if [[ "${RISCV_ARCH}" == "rv64" ]]; then
+  IREE_IMPORT_TFLITE_BIN="${IREE_IMPORT_TFLITE_BIN:-iree-import-tflite}"
+  LLVM_BIN_DIR="${LLVM_BIN_DIR}"
+fi
+
+# Environment variable used by the emulator and iree-compile for the
+# llvm-cpu bytecode codegen.
+export RISCV_TOOLCHAIN_ROOT="${RISCV_RV64_LINUX_TOOLCHAIN_ROOT}"
+
+function generate_llvm_cpu_vmfb {
+  local target="${1}"; shift
+  local compile_args=(
+    --iree-hal-target-backends=llvm-cpu
+    --iree-llvm-embedded-linker-path="${IREE_HOST_BINARY_ROOT}/bin/lld"
+    --iree-llvm-target-triple=riscv64
+    --iree-llvm-target-cpu=generic-rv64
+    --iree-llvm-target-abi=lp64d
+  )
+  if [[ "${target}" == "mhlo" ]]; then
+    compile_args+=(
+      --iree-input-type=mhlo
+      --iree-llvm-target-cpu-features="+m,+a,+f,+d,+c"
+    )
+  elif [[ "${target}" == "tosa" ]]; then
+    local input_file="${1}"; shift
+    "${IREE_IMPORT_TFLITE_BIN}" -o "${BUILD_RISCV_DIR}/tosa.mlir" "${input_file}"
+    compile_args+=(
+      --iree-input-type=tosa
+      --iree-llvm-target-cpu-features="+m,+a,+f,+d,+c"
+      "${BUILD_RISCV_DIR}/tosa.mlir"
+    )
+  elif [[ "${target}" == "tosa-rvv" ]]; then
+    local input_file="${1}"; shift
+    "${IREE_IMPORT_TFLITE_BIN}" -o "${BUILD_RISCV_DIR}/tosa.mlir" "${input_file}"
+    compile_args+=(
+      --iree-input-type=tosa
+      --iree-llvm-target-cpu-features="+m,+a,+f,+d,+c,+v"
+      --riscv-v-fixed-length-vector-lmul-max=8
+      --riscv-v-vector-bits-min=512
+      "${BUILD_RISCV_DIR}/tosa.mlir"
+    )
+  fi
+  "${IREE_HOST_BINARY_ROOT}/bin/iree-compile" "${compile_args[@]}" "$@"
+}
+
+if [[ "${RISCV_ARCH}" == "rv64" ]]; then
+  generate_llvm_cpu_vmfb mhlo \
+    "${ROOT_DIR}/tools/test/iree-run-module.mlir" \
+    -o "${BUILD_RISCV_DIR}/iree-run-module-llvm_cpu.vmfb"
+
+  wget -P "${BUILD_RISCV_DIR}/" "https://storage.googleapis.com/iree-model-artifacts/person_detect.tflite"
+
+  generate_llvm_cpu_vmfb tosa \
+    "${BUILD_RISCV_DIR}/person_detect.tflite" \
+    -o "${BUILD_RISCV_DIR}/person_detect.vmfb"
+
+  generate_llvm_cpu_vmfb tosa-rvv \
+    "${BUILD_RISCV_DIR}/person_detect.tflite" \
+    -o "${BUILD_RISCV_DIR}/person_detect_rvv.vmfb"
+
+  ${PYTHON_BIN} "${ROOT_DIR}/third_party/llvm-project/llvm/utils/lit/lit.py" \
+    -v --path "${LLVM_BIN_DIR}" "${ROOT_DIR}/tests/riscv64"
+fi
+
+ctest_args=(
+  "--timeout 900"
+  "--output-on-failure"
+  "--no-tests=error"
+)
+
+declare -a label_exclude_args=(
+  "^nokokoro$"
+  "^driver=vulkan$"
+  "^driver=cuda$"
+  "^vulkan_uses_vk_khr_shader_float16_int8$"
+  "^requires-filesystem$"
+  "^requires-dtz$"
+)
+
+# Excluding mobilebert, fp16, and lowering_config regression
+# tests for now.
+# TODO(#10462): Investigate the lowering_config test issue.
+declare -a test_exclude_args=(
+  "bert"
+  "fp16"
+  "regression_llvm-cpu_lowering_config"
+)
+
+# Test runtime unit tests
+runtime_label_exclude_regex="($(IFS="|" ; echo "${label_exclude_args[*]}"))"
+runtime_ctest_args=(
+  "--test-dir ${BUILD_RISCV_DIR}/runtime/"
+  ${ctest_args[@]}
+  "--label-exclude ${runtime_label_exclude_regex}"
+)
+echo "******** Running runtime CTest ********"
+ctest ${runtime_ctest_args[@]}
+
+if [[ "${RISCV_ARCH}" == "rv32-linux" ]]; then
+  # mhlo.power is also disabled because musl math library is not compiled for
+  # 32-bit.
+  test_exclude_args+=(
+    "xla.*llvm-cpu.*pow"
+  )
+fi
+
+tests_label_exclude_regex="($(IFS="|" ; echo "${label_exclude_args[*]}"))"
+tests_exclude_regex="($(IFS="|" ; echo "${test_exclude_args[*]}"))"
+test_ctest_args=(
+  "--test-dir ${BUILD_RISCV_DIR}/tests/e2e"
+  ${ctest_args[@]}
+  "--label-exclude ${tests_label_exclude_regex}"
+  "--exclude-regex ${tests_exclude_regex}"
+)
+echo "******** Running e2e CTest ********"
+ctest  ${test_ctest_args[@]}
diff --git a/build_tools/cmake/test_riscv64.sh b/build_tools/cmake/test_riscv64.sh
deleted file mode 100755
index b091a83..0000000
--- a/build_tools/cmake/test_riscv64.sh
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/bin/bash
-
-# Copyright 2021 The IREE Authors
-#
-# Licensed under the Apache License v2.0 with LLVM Exceptions.
-# See https://llvm.org/LICENSE.txt for license information.
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-# Test the cross-compiled RISCV 64-bit Linux targets.
-
-set -xeuo pipefail
-
-# Print the UTC time when set -x is on
-export PS4='[$(date -u "+%T %Z")] '
-
-ROOT_DIR="${ROOT_DIR:-$(git rev-parse --show-toplevel)}"
-IREE_IMPORT_TFLITE_BIN="${IREE_IMPORT_TFLITE_BIN:-iree-import-tflite}"
-LLVM_BIN_DIR="${LLVM_BIN_DIR}"
-
-# Environment variable used by the emulator and iree-compile for the
-# llvm-cpu bytecode codegen.
-export RISCV_TOOLCHAIN_ROOT="${RISCV_RV64_LINUX_TOOLCHAIN_ROOT}"
-
-function generate_llvm_cpu_vmfb {
-  local target="${1}"; shift
-  local compile_args=(
-    --iree-hal-target-backends=llvm-cpu
-    --iree-llvm-embedded-linker-path="${IREE_HOST_BINARY_ROOT}/bin/lld"
-    --iree-llvm-target-triple=riscv64
-    --iree-llvm-target-cpu=generic-rv64
-    --iree-llvm-target-abi=lp64d
-  )
-  if [[ "${target}" == "mhlo" ]]; then
-    compile_args+=(
-      --iree-input-type=mhlo
-      --iree-llvm-target-cpu-features="+m,+a,+f,+d,+c"
-    )
-  elif [[ "${target}" == "tosa" ]]; then
-    local input_file="${1}"; shift
-    "${IREE_IMPORT_TFLITE_BIN}" -o "${BUILD_RISCV_DIR}/tosa.mlir" "${input_file}"
-    compile_args+=(
-      --iree-input-type=tosa
-      --iree-llvm-target-cpu-features="+m,+a,+f,+d,+c"
-      "${BUILD_RISCV_DIR}/tosa.mlir"
-    )
-  elif [[ "${target}" == "tosa-rvv" ]]; then
-    local input_file="${1}"; shift
-    "${IREE_IMPORT_TFLITE_BIN}" -o "${BUILD_RISCV_DIR}/tosa.mlir" "${input_file}"
-    compile_args+=(
-      --iree-input-type=tosa
-      --iree-llvm-target-cpu-features="+m,+a,+f,+d,+c,+v"
-      --riscv-v-fixed-length-vector-lmul-max=8
-      --riscv-v-vector-bits-min=512
-      "${BUILD_RISCV_DIR}/tosa.mlir"
-    )
-  fi
-  "${IREE_HOST_BINARY_ROOT}/bin/iree-compile" "${compile_args[@]}" "$@"
-}
-
-generate_llvm_cpu_vmfb mhlo \
-  "${ROOT_DIR}/tools/test/iree-run-module.mlir" \
-  -o "${BUILD_RISCV_DIR}/iree-run-module-llvm_cpu.vmfb"
-
-wget -P "${BUILD_RISCV_DIR}/" "https://storage.googleapis.com/iree-model-artifacts/person_detect.tflite"
-
-generate_llvm_cpu_vmfb tosa \
-  "${BUILD_RISCV_DIR}/person_detect.tflite" \
-  -o "${BUILD_RISCV_DIR}/person_detect.vmfb"
-
-generate_llvm_cpu_vmfb tosa-rvv \
-  "${BUILD_RISCV_DIR}/person_detect.tflite" \
-  -o "${BUILD_RISCV_DIR}/person_detect_rvv.vmfb"
-
-${PYTHON_BIN} "${ROOT_DIR}/third_party/llvm-project/llvm/utils/lit/lit.py" \
-  -v --path "${LLVM_BIN_DIR}" "${ROOT_DIR}/tests/riscv64"
-
-# Test runtime unit tests
-ctest --test-dir ${BUILD_RISCV_DIR}/runtime/ --timeout 900 --output-on-failure \
-  --no-tests=error --label-exclude \
-  '(^nokokoro$|^driver=vulkan$|^driver=cuda$|^vulkan_uses_vk_khr_shader_float16_int8$|^requires-filesystem$|^requires-dtz$)'
-
-# Test e2e models. Excluding mobilebert, fp16, and lowering_config regression
-# tests for now.
-# TODO(#10462): Investigate the lowering_config test issue.
-ctest --test-dir ${BUILD_RISCV_DIR}/tests/e2e --timeout 900 --output-on-failure \
-  --no-tests=error --label-exclude \
-  '(^nokokoro$|^driver=vulkan$|^driver=cuda$|^vulkan_uses_vk_khr_shader_float16_int8$)' \
-  -E '(bert|fp16|regression_llvm-cpu_lowering_config)'
diff --git a/build_tools/riscv/riscv_bootstrap.sh b/build_tools/riscv/riscv_bootstrap.sh
index 85dd799..06f2698 100755
--- a/build_tools/riscv/riscv_bootstrap.sh
+++ b/build_tools/riscv/riscv_bootstrap.sh
@@ -11,10 +11,8 @@
 set -e
 set -o pipefail
 
-BOOTSTRAP_SCRIPT_PATH=$(dirname "$0")
-BOOTSTRAP_WORK_DIR=${BOOTSTRAP_SCRIPT_PATH}/.bootstrap
-
-PREBUILT_DIR=${HOME}/riscv
+PREBUILT_DIR="${HOME}/riscv"
+IREE_ARTIFACT_URL="https://storage.googleapis.com/iree-shared-files"
 
 read -p "Enter the riscv tools root path(press enter to use default path:${PREBUILT_DIR}): " INPUT_PATH
 if [[ "${INPUT_PATH}" ]]; then
@@ -22,99 +20,70 @@
 fi
 echo "The riscv tool prefix path: ${PREBUILT_DIR}"
 
+BOOTSTRAP_WORK_DIR="${PREBUILT_DIR}/.bootstrap"
+
 if [[ "${OSTYPE}" == "linux-gnu" ]]; then
-  RISCV_CLANG_TOOLCHAIN_FILE_ID=13q6sYVlae-hRrgj7SNlvbJFYI3Q8sCI4
-  RISCV_CLANG_TOOLCHAIN_FILE_NAME=rvv-llvm-toolchain.tar.bz2
-  QEMU_FILE_ID=1JkLana7CGeD2wfwn8shHQxcYrv3j1l9s
-  QEMU_FILE_NAME=riscv-qemu-v5.2.0-rvv-rvb-zfh-856da0e-linux-ubuntu.tar.gz
+  RISCV_CLANG_TOOLCHAIN_FILE_NAME="toolchain_iree_20220918.tar.gz"
+  RISCV_CLANG_TOOLCHAIN_FILE_SHA="f73ae47b7f04df37e53c1737b5baa93f8e063a6602f6e5a022df3e3ae7af11e6"
+
+  QEMU_FILE_NAME="qemu-riscv.tar.gz"
+  QEMU_FILE_SHA="6e0bca77408e606add8577d6f1b6709f6ef3165b0e241ed2ba191183dfc931ec"
 
   TOOLCHAIN_PATH_PREFIX=${PREBUILT_DIR}/toolchain/clang/linux/RISCV
   QEMU_PATH_PREFIX=${PREBUILT_DIR}/qemu/linux/RISCV
-elif [[ "${OSTYPE}" == "darwin"* ]]; then
-  RISCV_CLANG_TOOLCHAIN_FILE_ID=empty
-  RISCV_CLANG_TOOLCHAIN_FILE_NAME=empty
-  QEMU_FILE_ID=empty
-  QEMU_FILE_NAME=empty
-
-  TOOLCHAIN_PATH_PREFIX=${PREBUILT_DIR}/toolchain/clang/darwin/RISCV
-  QEMU_PATH_PREFIX=${PREBUILT_DIR}/qemu/darwin/RISCV
-
-  echo "We haven't had the darwin prebuilt binary yet. Skip this script."
-  exit 1
 else
   echo "${OSTYPE} is not supported."
   exit 1
 fi
 
 function cleanup {
-  if [[ -d ${BOOTSTRAP_WORK_DIR} ]]; then
-    rm -rf ${BOOTSTRAP_WORK_DIR}
+  if [[ -d "${BOOTSTRAP_WORK_DIR}" ]]; then
+    rm -rf "${BOOTSTRAP_WORK_DIR}"
   fi
 }
 
 # Call the cleanup function when this tool exits.
 trap cleanup EXIT
 
-wget_google_drive() {
-  local file_id="$1"
-  local file_name="$2"
-  local install_path="$3"
-  local tar_option="$4"
-
-  wget --save-cookies ${BOOTSTRAP_WORK_DIR}/cookies.txt \
-    "https://docs.google.com/uc?export=download&id="$file_id -O- | \
-    sed -En "s/.*confirm=([0-9A-Za-z_]+).*/\1/p" > ${BOOTSTRAP_WORK_DIR}/confirm.txt
-  wget --progress=bar:force:noscroll --load-cookies ${BOOTSTRAP_WORK_DIR}/cookies.txt \
-    "https://docs.google.com/uc?export=download&id=$file_id&confirm=`cat ${BOOTSTRAP_WORK_DIR}/confirm.txt`" -O- | \
-    tar $tar_option - --no-same-owner --strip-components=1 -C $install_path
-}
-
+# Download and install the toolchain from IREE-OSS GCS
 download_file() {
-  # server name or google drive file_id
-  local file_download_info="$1"
-  local file_name="$2"
-  local install_path="$3"
-  # download method(e.g. wget_google_drive)
-  local download_method="$4"
+  local file_name="$1"
+  local install_path="$2"
+  local file_sha="$3"
 
-  echo "Install $2 to $3"
-  if [[ -e $3/file_info.txt ]]; then
+  echo "Install $1 to $2"
+  if [[ "$(ls -A $2)" ]]; then
     read -p "The file already exists. Keep it (y/n)? " replaced
     case ${replaced:0:1} in
       y|Y )
-        echo "Skip download $2."
+        echo "Skip download $1."
         return
       ;;
       * )
-        rm -rf $3
+        rm -rf "$2"
       ;;
     esac
   fi
 
-  local tar_option=""
-  if [[ "${file_name##*.}" == "gz" ]]; then
-    tar_option="zxpf"
-  elif [[ "${file_name##*.}" == "bz2" ]]; then
-    tar_option="jxpf"
-  fi
-  echo "tar option: $tar_option"
-
-  echo "Download $file_name ..."
+  echo "Download ${file_name} ..."
   mkdir -p $install_path
-  $download_method $file_download_info $file_name $install_path $tar_option
-
-  echo "$file_download_info $file_name" > $install_path/file_info.txt
+  wget --progress=bar:force:noscroll --directory-prefix="${BOOTSTRAP_WORK_DIR}" \
+    "${IREE_ARTIFACT_URL}/${file_name}" && \
+    echo "${file_sha} ${BOOTSTRAP_WORK_DIR}/${file_name}" | sha256sum -c -
+  echo "Extract ${file_name} ..."
+  tar -C "${install_path}" -xf "${BOOTSTRAP_WORK_DIR}/${file_name}" --no-same-owner \
+    --strip-components=1
 }
 
-mkdir -p ${BOOTSTRAP_WORK_DIR}
+mkdir -p "${BOOTSTRAP_WORK_DIR}"
 
 read -p "Install RISCV clang toolchain(y/n)? " answer
 case ${answer:0:1} in
   y|Y )
-    download_file ${RISCV_CLANG_TOOLCHAIN_FILE_ID} \
-                  ${RISCV_CLANG_TOOLCHAIN_FILE_NAME} \
-                  ${TOOLCHAIN_PATH_PREFIX} \
-                  wget_google_drive
+    download_file "${RISCV_CLANG_TOOLCHAIN_FILE_NAME}" \
+                  "${TOOLCHAIN_PATH_PREFIX}" \
+                  "${RISCV_CLANG_TOOLCHAIN_FILE_SHA}"
+
     echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
     echo " PLEASE run 'export RISCV_TOOLCHAIN_ROOT=${TOOLCHAIN_PATH_PREFIX}'   "
     echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
@@ -127,10 +96,9 @@
 read -p "Install RISCV qemu(y/n)? " answer
 case ${answer:0:1} in
   y|Y )
-    download_file $QEMU_FILE_ID \
-                  ${QEMU_FILE_NAME} \
-                  ${QEMU_PATH_PREFIX} \
-                  wget_google_drive
+    download_file "${QEMU_FILE_NAME}" \
+                  "${QEMU_PATH_PREFIX}" \
+                  "${QEMU_FILE_SHA}"
   ;;
   * )
     echo "Skip RISCV qemu."
diff --git a/samples/simple_embedding/BUILD b/samples/simple_embedding/BUILD
index 9856586..00725c3 100644
--- a/samples/simple_embedding/BUILD
+++ b/samples/simple_embedding/BUILD
@@ -185,6 +185,7 @@
     ],
     deps = [
         ":simple_embedding_test_bytecode_module_cpu_arm_64_c",
+        ":simple_embedding_test_bytecode_module_cpu_riscv_32_c",
         ":simple_embedding_test_bytecode_module_cpu_riscv_64_c",
         ":simple_embedding_test_bytecode_module_cpu_x86_64_c",
         "//runtime/src/iree/base",
diff --git a/samples/simple_embedding/CMakeLists.txt b/samples/simple_embedding/CMakeLists.txt
index 6ed7711..9a41000 100644
--- a/samples/simple_embedding/CMakeLists.txt
+++ b/samples/simple_embedding/CMakeLists.txt
@@ -181,6 +181,7 @@
     "simple_embedding.c"
   DEPS
     ::simple_embedding_test_bytecode_module_cpu_arm_64_c
+    ::simple_embedding_test_bytecode_module_cpu_riscv_32_c
     ::simple_embedding_test_bytecode_module_cpu_riscv_64_c
     ::simple_embedding_test_bytecode_module_cpu_x86_64_c
     iree::base
diff --git a/samples/simple_embedding/device_embedded.c b/samples/simple_embedding/device_embedded.c
index 5d365e6..351ebad 100644
--- a/samples/simple_embedding/device_embedded.c
+++ b/samples/simple_embedding/device_embedded.c
@@ -17,9 +17,9 @@
 
 // Compiled module embedded here to avoid file IO:
 #include "samples/simple_embedding/simple_embedding_test_bytecode_module_cpu_arm_64_c.h"
+#include "samples/simple_embedding/simple_embedding_test_bytecode_module_cpu_riscv_32_c.h"
 #include "samples/simple_embedding/simple_embedding_test_bytecode_module_cpu_riscv_64_c.h"
 #include "samples/simple_embedding/simple_embedding_test_bytecode_module_cpu_x86_64_c.h"
-
 iree_status_t create_sample_device(iree_allocator_t host_allocator,
                                    iree_hal_device_t** out_device) {
   // Set parameters for the device created in the next step.
@@ -61,6 +61,9 @@
 #if IREE_ARCH_X86_64
   const struct iree_file_toc_t* module_file_toc =
       iree_samples_simple_embedding_test_module_cpu_x86_64_create();
+#elif IREE_ARCH_RISCV_32
+  const struct iree_file_toc_t* module_file_toc =
+      iree_samples_simple_embedding_test_module_cpu_riscv_32_create();
 #elif IREE_ARCH_RISCV_64
   const struct iree_file_toc_t* module_file_toc =
       iree_samples_simple_embedding_test_module_cpu_riscv_64_create();