Skip to content

Commit a71566a

Browse files
authored
sha2: remove RISC-V hack for unaligned loads (#879)
It simplifies the code of Zknh backends, but results in an atrocious codegen without `-mno-strict-align`. Arguably, it's both an LLVM and RISC-V problem (see llvm/llvm-project#110454 and riscv/riscv-profiles#187), so it's not our responsibility to work around this stupidity. I will mention the issue in the crate docs. Hopefully, in future we will get either [this optimization][1], the Zicclsm handling in LLVM will change, or the proposed Oilsm extension will become the default instead of the currently useless Zicclsm. [1]: llvm/llvm-project#150263
1 parent 0bdea4f commit a71566a

12 files changed

Lines changed: 154 additions & 353 deletions

File tree

sha2/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## 0.11.1 (UNRELEASED)
9+
### Changed
10+
- Removed workaround for unaligned loads in `riscv-zknh` backend ([#879])
11+
- `riscv-zknh` no longer requires `zbkb` (or `zbb`) target feature ([#879])
12+
13+
[#879]: https://github.com/RustCrypto/hashes/pull/879
14+
815
## 0.11.0 (2026-03-25)
916
### Changed
1017
- Edition changed to 2024 and MSRV bumped to 1.85 ([#652])

sha2/README.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ SHA-256 and SHA-512 backends:
6363
- `soft`: portable software implementation
6464
- `loongarch64-asm`: `asm!`-based implementation for LoongArch64 targets
6565
- `riscv-zknh`: uses the RISC-V `Zknh` scalar crypto extension. Experimental,
66-
requires Nightly compiler and to enable `Zknh` and `Zbkb` (or `Zbb`)
67-
target features at compile time.
66+
see the [section below](#about-riscv-zknh) for more information.
6867
- `wasm32-simd128`: uses the WASM `simd128` extension.
6968

7069
SHA-256 only backends:
@@ -91,10 +90,10 @@ You can force backend selection using the following configuration flags:
9190
- `sha2_512_backend`: select SHA-512 backend. Supported values: `aarch64-sha3`, `soft`,
9291
`riscv-zknh`, `x86-avx2`.
9392

94-
They can be enabled using either the `RUSTFLAGS` environment variable
93+
They can be enabled using either a `RUSTFLAGS` environment variable
9594
(e.g. `RUSTFLAGS='--cfg sha2_backend="soft"'`), or by modifying your `.cargo/config.toml` file.
9695

97-
Note that `sha2_backend` has higher priority than `sha2_256_backend` and `sha2_512_backend`.
96+
Note that `sha2_backend` has a higher priority than `sha2_256_backend` and `sha2_512_backend`.
9897
In other words, using `--cfg sha2_backend="soft" --cfg sha2_256_backend="x86_sha"` will result
9998
in selection of the software backend for SHA-256.
10099

@@ -103,6 +102,16 @@ performance at the cost of a bigger resulting binary. You can disable unrolling
103102
by using `sha2_backend_soft = "compact"` and `sha2_backend_riscv_zknh = "compact"` configuration
104103
flags respectively.
105104

105+
### About `riscv-zknh`
106+
107+
This is an experimental RISC-V-only backend which requires a Nightly compiler and
108+
to enable the `Zknh` target feature at compile time. For a more efficient code generation,
109+
it's recommended to enable the `Zbkb` (or `Zbb`) and `unaligned-scalar-mem` target features
110+
(see [this LLVM issue][Zicclsm] for more information). For example, you can do it by using
111+
`RUSTFLAGS="-C target-feature=+zknh,+zbkb,+unaligned-scalar-mem"`.
112+
113+
[Zicclsm]: https://github.com/llvm/llvm-project/issues/110454
114+
106115
## License
107116

108117
The crate is licensed under either of:

sha2/src/sha256.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@ cfg_if::cfg_if! {
55
} else if #[cfg(any(sha2_backend = "riscv-zknh", sha2_256_backend = "riscv-zknh"))] {
66
mod riscv_zknh;
77

8-
#[cfg(not(all(
9-
target_feature = "zknh",
10-
any(target_feature = "zbb", target_feature = "zbkb")
11-
)))]
12-
compile_error!("riscv-zknh backend requires zknh and zbkb (or zbb) target features");
8+
#[cfg(not(target_feature = "zknh"))]
9+
compile_error!("riscv-zknh backend requires `zknh` target feature");
1310

1411
fn compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
1512
// SAFETY: we checked above that the required target features are enabled
@@ -22,7 +19,7 @@ cfg_if::cfg_if! {
2219
target_feature = "sha",
2320
target_feature = "sse4.1",
2421
)))]
25-
compile_error!("x86-sha backend requires sha and sse4.1 target features");
22+
compile_error!("x86-sha backend requires `sha` and `sse4.1` target features");
2623

2724
fn compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
2825
// SAFETY: we checked above that the required target features are enabled
@@ -32,7 +29,7 @@ cfg_if::cfg_if! {
3229
mod aarch64_sha2;
3330

3431
#[cfg(not(target_feature = "sha2"))]
35-
compile_error!("aarch64-sha2 backend requires sha2 target feature");
32+
compile_error!("aarch64-sha2 backend requires `sha2` target feature");
3633

3734
fn compress(state: &mut [u64; 8], blocks: &[[u8; 128]]) {
3835
// SAFETY: we checked above that the required target features are enabled

sha2/src/sha256/riscv_zknh.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
#[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
22
compile_error!("riscv-zknh backend can be used only on riscv32 and riscv64 target arches");
33

4-
mod utils;
5-
64
#[cfg(target_arch = "riscv32")]
75
use core::arch::riscv32::{sha256sig0, sha256sig1, sha256sum0, sha256sum1};
86
#[cfg(target_arch = "riscv64")]
@@ -11,9 +9,20 @@ use core::arch::riscv64::{sha256sig0, sha256sig1, sha256sum0, sha256sum1};
119
cfg_if::cfg_if! {
1210
if #[cfg(sha2_backend_riscv_zknh = "compact")] {
1311
mod compact;
14-
pub(super) use compact::compress;
12+
use compact::compress_block;
1513
} else {
1614
mod unroll;
17-
pub(super) use unroll::compress;
15+
use unroll::compress_block;
16+
}
17+
}
18+
19+
#[target_feature(enable = "zknh")]
20+
pub(super) fn compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
21+
for block in blocks {
22+
let block: [u32; 16] = core::array::from_fn(|i| {
23+
let chunk = block[4 * i..][..4].try_into().unwrap();
24+
u32::from_be_bytes(chunk)
25+
});
26+
compress_block(state, block);
1827
}
1928
}

sha2/src/sha256/riscv_zknh/compact.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,7 @@ use super::{sha256sig0, sha256sig1, sha256sum0, sha256sum1};
22
use crate::consts::K32;
33

44
#[target_feature(enable = "zknh")]
5-
pub(in super::super) fn compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
6-
for block in blocks.iter().map(super::utils::load_block) {
7-
compress_block(state, block);
8-
}
9-
}
10-
11-
#[target_feature(enable = "zknh")]
12-
fn compress_block(state: &mut [u32; 8], mut block: [u32; 16]) {
5+
pub(super) fn compress_block(state: &mut [u32; 8], mut block: [u32; 16]) {
136
let mut s = *state;
147

158
for r in 0..64 {

sha2/src/sha256/riscv_zknh/unroll.rs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,7 @@ use super::{sha256sig0, sha256sig1, sha256sum0, sha256sum1};
22
use crate::consts::K32;
33

44
#[target_feature(enable = "zknh")]
5-
pub(in super::super) fn compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
6-
for block in blocks.iter().map(super::utils::load_block) {
7-
compress_block(state, block);
8-
}
9-
}
10-
11-
#[target_feature(enable = "zknh")]
12-
fn compress_block(state: &mut [u32; 8], mut block: [u32; 16]) {
5+
pub(super) fn compress_block(state: &mut [u32; 8], mut block: [u32; 16]) {
136
let s = &mut state.clone();
147
let b = &mut block;
158

@@ -82,7 +75,7 @@ fn round<const R: usize>(state: &mut [u32; 8], block: &[u32; 16], k: &[u32]) {
8275
state[h] = state[h]
8376
.wrapping_add(sha256sum1(state[e]))
8477
.wrapping_add(ch(state[e], state[f], state[g]))
85-
.wrapping_add(super::utils::opaque_load::<R>(k))
78+
.wrapping_add(opaque_load::<R>(k))
8679
.wrapping_add(block[R]);
8780
state[d] = state[d].wrapping_add(state[h]);
8881
state[h] = state[h]
@@ -99,3 +92,33 @@ fn ch(x: u32, y: u32, z: u32) -> u32 {
9992
fn maj(x: u32, y: u32, z: u32) -> u32 {
10093
(x & y) ^ (x & z) ^ (y & z)
10194
}
95+
96+
/// This function returns `k[R]`, but prevents the compiler from inlining the indexed value
97+
fn opaque_load<const R: usize>(k: &[u32]) -> u32 {
98+
assert!(R < k.len());
99+
let dst;
100+
101+
#[cfg(target_arch = "riscv64")]
102+
unsafe {
103+
core::arch::asm!(
104+
"lwu {dst}, 4*{R}({k})",
105+
R = const R,
106+
k = in(reg) k.as_ptr(),
107+
dst = out(reg) dst,
108+
options(pure, readonly, nostack, preserves_flags),
109+
);
110+
}
111+
112+
#[cfg(target_arch = "riscv32")]
113+
unsafe {
114+
core::arch::asm!(
115+
"lw {dst}, 4*{R}({k})",
116+
R = const R,
117+
k = in(reg) k.as_ptr(),
118+
dst = out(reg) dst,
119+
options(pure, readonly, nostack, preserves_flags),
120+
);
121+
}
122+
123+
dst
124+
}

sha2/src/sha256/riscv_zknh/utils.rs

Lines changed: 0 additions & 97 deletions
This file was deleted.

sha2/src/sha512.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@ cfg_if::cfg_if! {
55
} else if #[cfg(any(sha2_backend = "riscv-zknh", sha2_256_backend = "riscv-zknh"))] {
66
mod riscv_zknh;
77

8-
#[cfg(not(all(
9-
target_feature = "zknh",
10-
any(target_feature = "zbb", target_feature = "zbkb")
11-
)))]
12-
compile_error!("riscv-zknh backend requires zknh and zbkb (or zbb) target features");
8+
#[cfg(not(target_feature = "zknh"))]
9+
compile_error!("riscv-zknh backend requires `zknh` target features");
1310

1411
fn compress(state: &mut [u64; 8], blocks: &[[u8; 128]]) {
1512
// SAFETY: we checked above that the required target features are enabled
@@ -19,7 +16,7 @@ cfg_if::cfg_if! {
1916
mod x86_avx2;
2017

2118
#[cfg(not(target_feature = "avx2"))]
22-
compile_error!("x86-avx2 backend requires avx2 target feature");
19+
compile_error!("x86-avx2 backend requires `avx2` target feature");
2320

2421
fn compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
2522
// SAFETY: we checked above that the required target features are enabled
@@ -29,7 +26,7 @@ cfg_if::cfg_if! {
2926
mod aarch64_sha3;
3027

3128
#[cfg(not(target_feature = "sha3"))]
32-
compile_error!("aarch64-sha3 backend requires sha3 target feature");
29+
compile_error!("aarch64-sha3 backend requires `sha3` target feature");
3330

3431
fn compress(state: &mut [u64; 8], blocks: &[[u8; 128]]) {
3532
// SAFETY: we checked above that the required target features are enabled

0 commit comments

Comments
 (0)