Skip to content

Commit 4d589eb

Browse files
committed
aarch64: use target_feature=sha3 instructions if available
1 parent 69be1e4 commit 4d589eb

9 files changed

Lines changed: 341 additions & 13 deletions

File tree

admin/coverage

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ if [ `uname -p` == "x86_64" ] ; then
1212
env GRAVIOLA_CPU_DISABLE_sha=1 GRAVIOLA_CPU_DISABLE_bmi2=1 GRAVIOLA_CPU_DISABLE_avx512bw=1 GRAVIOLA_CPU_DISABLE_avx512f=1 GRAVIOLA_CPU_DISABLE_vaes=1 cargo test --locked
1313
env CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="sde -dmr --" cargo test --locked
1414
fi
15+
case `uname -m` in
16+
arm|arm64|aarch64)
17+
env GRAVIOLA_CPU_DISABLE_sha3=1 GRAVIOLA_CPU_DISABLE_dit=1 cargo test --locked
18+
;;
19+
esac
20+
1521
cargo run --example client https://jbp.io >/dev/null
1622

1723
cargo llvm-cov report "$@"

admin/parse-asm/driver.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ def get_labels(self):
228228
return set(self.labels)
229229

230230

231-
RUST_FUNCTION_DECL = re.compile(r"^fn (?P<name>[a-zA-Z0-9_]+)\(")
231+
RUST_FUNCTION_DECL = re.compile(r"^(unsafe )?fn (?P<name>[a-zA-Z0-9_]+)\(")
232232

233233

234234
# A pass to collect (1) all macro definitions that reference labels, and (2) the
@@ -494,6 +494,7 @@ def emit_rust_function(
494494
return_value=None,
495495
assertions=[],
496496
allow_inline=True,
497+
func_attrs="",
497498
hoist=None,
498499
):
499500
self.macro_pass.emit_rust_function(name, rust_decl)
@@ -505,6 +506,7 @@ def emit_rust_function(
505506
return_value=return_value,
506507
assertions=assertions,
507508
allow_inline=allow_inline,
509+
func_attrs=func_attrs,
508510
hoist=hoist,
509511
)
510512

@@ -585,6 +587,7 @@ def emit_rust_function(
585587
return_value=None,
586588
assertions=[],
587589
allow_inline=True,
590+
func_attrs="",
588591
hoist=None,
589592
):
590593
self.expected_functions[name] = (
@@ -595,6 +598,7 @@ def emit_rust_function(
595598
allow_inline,
596599
hoist,
597600
assertions,
601+
func_attrs,
598602
)
599603

600604
def set_att_syntax(self, att_syntax):
@@ -794,10 +798,12 @@ def on_function(self, contexts, name):
794798
allow_inline,
795799
hoist,
796800
assertions,
801+
func_attrs,
797802
) = defn
798803
self.function_state.parameter_map = parameter_map
799804
self.function_state.return_map = return_map
800805
self.function_state.rust_decl = rust_decl
806+
self.function_state.func_attrs = func_attrs
801807
self.function_state.return_value = return_value
802808
self.function_state.hoist = hoist
803809

@@ -819,11 +825,15 @@ def on_function(self, contexts, name):
819825
print("#[inline(never)]", file=self.output)
820826

821827
print(
822-
"""pub(crate) %s {
828+
"""%spub(crate) %s {
823829
%s
824830
// SAFETY: inline assembly. see [crate::low::inline_assembly_safety] for safety info.
825831
unsafe { core::arch::asm!("""
826-
% (self.function_state.rust_decl, locals),
832+
% (
833+
self.function_state.func_attrs,
834+
self.function_state.rust_decl,
835+
locals,
836+
),
827837
file=self.output,
828838
)
829839

@@ -907,6 +917,7 @@ def on_asm(self, contexts, opcode, operands):
907917

908918
self.visit_operands(operands)
909919

920+
operands = operands.replace("{", "{{").replace("}", "}}")
910921
operands = self.expand_rust_macros_in_asm(operands)
911922

912923
parts = ['" %-15s "' % opcode]

admin/parse-asm/driver.sha3.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,19 @@
3535
rust_decl="fn sha3_keccak_f1600(a: &mut [u64; 25], rc: &[u64; 24])",
3636
)
3737
parse_file(input, d)
38+
39+
with (
40+
open("../../thirdparty/s2n-bignum/arm/sha3/sha3_keccak_f1600_alt.S") as input,
41+
open("../../graviola/src/low/aarch64/sha3_keccak_f1600_alt.rs", "w") as output,
42+
):
43+
d = RustDriver(output, Architecture_aarch64)
44+
d.emit_rust_function(
45+
"sha3_keccak_f1600_alt",
46+
parameter_map=[
47+
("inout", "a.as_mut_ptr() => _"),
48+
("inout", "rc.as_ptr() => _"),
49+
],
50+
func_attrs='#[target_feature(enable = "sha3")]\n',
51+
rust_decl="unsafe fn sha3_keccak_f1600(a: &mut [u64; 25], rc: &[u64; 24])",
52+
)
53+
parse_file(input, d)

admin/parse-asm/parse.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
MACRO = re.compile(r"^(?P<name>[a-z0-9_]+)\((?P<args>[a-z0-9_,\[\]\+\* \#]*)\);?$")
1313
ASM = re.compile(
14-
r"^(?P<opcode>[a-z][a-z0-9\.]*)\s?(?P<operands>[A-Za-z0-9_,\s\(\)\[\]\+\*\-~\t#\.!%$@]*) ?;? ?(//(?P<comment>[A-Za-z0-9 =\/@#\*\+\(\)^\.\<\>\-_:,\!\?\|])*)?$"
14+
r"^(?P<opcode>[a-z][a-z0-9\.]*)\s?(?P<operands>[A-Za-z0-9_,\s\(\)\[\]{}\+\*\-~\t#\.!%$@]*) ?;? ?(//(?P<comment>[A-Za-z0-9 =\/@#\*\+\(\)^\.\<\>\-_:,\!\?\|])*)?$"
1515
)
1616
CONST = re.compile(r"\s?(?P<type>\.(quad|long))\s+(?P<value>((0x[0-9a-fA-F]+),?)+)")
1717
LABEL = re.compile(r"^(?P<name>(\.L)?[a-zA-Z0-9_]+):$")
@@ -249,7 +249,7 @@ def tokenise_gen(s):
249249
label = re.compile(r"^\.?[a-zA-Z][a-zA-Z0-9_]+")
250250
comment = re.compile(r"^/\*.*?\*/")
251251
number = re.compile(r"^[\$#]?(-?0x[0-9a-fA-F]+|-?[0-9]+)")
252-
operator = re.compile(r'^["\(\)\[\]\+\*/\-,;:#\.!]')
252+
operator = re.compile(r'^["\(\)\[\]{}\+\*/\-,;:#\.!][{}]?')
253253
whitespace = re.compile(r"^\s+")
254254

255255
while s:

graviola/src/low/aarch64/cpu.rs

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Written for Graviola by Joe Birr-Pixton, 2024.
22
// SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT-0
33

4-
use std::arch::is_aarch64_feature_detected;
5-
64
pub(crate) fn enter_cpu_state() -> u32 {
75
dit::maybe_enable()
86
}
@@ -157,28 +155,77 @@ pub(in crate::low) unsafe fn ct_compare_bytes(a: *const u8, b: *const u8, len: u
157155
}
158156
}
159157

158+
/// This macro interdicts is_aarch64_feature_detected to allow testability.
159+
macro_rules! have_cpu_feature {
160+
("neon") => {
161+
crate::low::aarch64::cpu::test_toggle(
162+
"neon",
163+
std::arch::is_aarch64_feature_detected!("neon"),
164+
)
165+
};
166+
("aes") => {
167+
crate::low::aarch64::cpu::test_toggle("aes", std::arch::is_aarch64_feature_detected!("aes"))
168+
};
169+
("pmull") => {
170+
crate::low::aarch64::cpu::test_toggle(
171+
"pmull",
172+
std::arch::is_aarch64_feature_detected!("pmull"),
173+
)
174+
};
175+
("sha2") => {
176+
crate::low::aarch64::cpu::test_toggle(
177+
"sha2",
178+
std::arch::is_aarch64_feature_detected!("sha2"),
179+
)
180+
};
181+
("sha3") => {
182+
crate::low::aarch64::cpu::test_toggle(
183+
"sha3",
184+
std::arch::is_aarch64_feature_detected!("sha3"),
185+
)
186+
};
187+
("dit") => {
188+
crate::low::aarch64::cpu::test_toggle("dit", std::arch::is_aarch64_feature_detected!("dit"))
189+
};
190+
}
191+
192+
/// Token type reflecting the check for the SHA3 CPU feature
193+
///
194+
/// A value of this type is proof that the CPU dynamic feature check has happened.
195+
#[derive(Clone, Copy, Debug)]
196+
pub(crate) struct HaveSha3(());
197+
198+
impl HaveSha3 {
199+
pub(crate) fn check() -> Option<Self> {
200+
match have_cpu_feature!("sha3") {
201+
true => Some(Self(())),
202+
false => None,
203+
}
204+
}
205+
}
206+
160207
pub(crate) fn verify_cpu_features() {
161208
assert!(
162-
is_aarch64_feature_detected!("neon"),
209+
have_cpu_feature!("neon"),
163210
"graviola requires neon CPU support"
164211
);
165212
assert!(
166-
is_aarch64_feature_detected!("aes"),
213+
have_cpu_feature!("aes"),
167214
"graviola requires aes CPU support"
168215
);
169216
assert!(
170-
is_aarch64_feature_detected!("pmull"),
217+
have_cpu_feature!("pmull"),
171218
"graviola requires pmull CPU support"
172219
);
173220
assert!(
174-
is_aarch64_feature_detected!("sha2"),
221+
have_cpu_feature!("sha2"),
175222
"graviola requires sha2 CPU support"
176223
);
177224
}
178225

179226
mod dit {
180227
pub(super) fn maybe_enable() -> u32 {
181-
if super::is_aarch64_feature_detected!("dit") {
228+
if have_cpu_feature!("dit") {
182229
// SAFETY: in this branch, we verified `dit` cpu feature is supported
183230
match unsafe { read() } {
184231
0 => {
@@ -229,6 +276,21 @@ mod dit {
229276
}
230277
}
231278

279+
#[cfg(not(debug_assertions))]
280+
pub(crate) fn test_toggle(_id: &str, detected: bool) -> bool {
281+
detected
282+
}
283+
284+
#[cfg(debug_assertions)]
285+
pub(crate) fn test_toggle(id: &str, detected: bool) -> bool {
286+
if std::env::var(format!("GRAVIOLA_CPU_DISABLE_{id}")).is_ok() {
287+
println!("DEBUG: denying cpuid {id:?}");
288+
false
289+
} else {
290+
detected
291+
}
292+
}
293+
232294
/// Read-only prefetch hint.
233295
pub(in crate::low) fn prefetch_ro<T>(ptr: *const T) {
234296
// SAFETY: inline assembly

graviola/src/low/aarch64/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,5 @@ pub(crate) mod p384_montjadd;
6868
pub(crate) mod p384_montjdouble;
6969
pub(crate) mod sha256;
7070
pub(crate) mod sha3_keccak_f1600;
71+
pub(crate) mod sha3_keccak_f1600_alt;
72+
pub(crate) mod sha3_keccak_f1600_mux;

0 commit comments

Comments
 (0)