Skip to content

test+fix: Add asm testing and fix core_hint_black_box #147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,6 @@ jobs:
with:
command: build
args: --no-default-features --target thumbv7em-none-eabi
- name: test assembly
run: ./test.sh
working-directory: ./asm_test
10 changes: 10 additions & 0 deletions asm_test/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "asm_test"
version = "0.1.0"
edition = "2024"

[dependencies]
subtle = { path = "../", default-features = false }

[features]
core_hint_black_box = ["subtle/core_hint_black_box"]
8 changes: 8 additions & 0 deletions asm_test/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![no_std]

use subtle::{Choice, ConditionallySelectable};

#[inline(never)]
pub fn select_byte(a: u8, b: u8) -> u8 {
ConditionallySelectable::conditional_select(&a, &b, Choice::from(1))
}
98 changes: 98 additions & 0 deletions asm_test/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/bin/bash

# Track failures globally
FAILED=0

# Allow some wiggle room for optimizer changes
BAD_BUFFER=2
GOOD_BUFFER=2

# Test different feature sets
function test_all() {
cargo --quiet install [email protected]
test_all_targets
test_all_targets core_hint_black_box

exit "${FAILED}"
}

# Test all the common architectures with their known good and bad values
function test_all_targets() {
test "aarch64-unknown-linux-gnu" 2 14 ${@}
test "i686-unknown-linux-gnu" 2 18 ${@}
test "x86_64-unknown-linux-gnu" 2 15 ${@}
test "arm-unknown-linux-gnueabi" 2 9 ${@}
test "loongarch64-unknown-linux-gnu" 2 19 ${@}
test "powerpc-unknown-linux-gnu" 2 27 ${@}
test "powerpc64-unknown-linux-gnu" 3 22 ${@}
test "powerpc64le-unknown-linux-gnu" 2 25 ${@}
test "riscv64gc-unknown-linux-gnu" 2 17 ${@}
test "s390x-unknown-linux-gnu" 2 13 ${@}
test "wasm32-unknown-unknown" 2 11 ${@}
test "thumbv7em-none-eabi" 2 10 ${@}
}

# Args:
# * The target name
# * The expected # of instructions for the bad case (optimizer defeated us)
# * The expected # of instructions for the good case (we beat the optimizer)
# * Any feature flags
#
# We take in both bad and good just to ensure there's no overlap.
function test() {
local TARGET=$1
local BAD=$(($2 + ${BAD_BUFFER}))
local GOOD=$(($3 - ${GOOD_BUFFER}))
shift 3
local FEATURES=""

# Build up the feature flags
for F in "${@}"; do
FEATURES+="--features=${F} "
done

# Make sure the good and bad ranges don't overlap
if [ "${GOOD}" -le "${BAD}" ]; then
echo "${TARGET} invalid params"
exit 1
fi

# Ensure the target is available
rustup -q target add "${TARGET}"

# Build and capture the assembly
catch INST INST_ERR env CARGO_TERM_QUIET=true cargo --quiet asm ${FEATURES} --target "${TARGET}" --simplify select_byte

# Emit any cargo errors
local STATUS=${?}
if [ ${STATUS} -ne 0 ]; then
FAILED=1
>&2 echo "${INST_ERR}"
return
fi

# Check the instruction count
local INST_COUNT="$(echo -n "${INST}" | wc -l)"
if [ "${INST_COUNT}" -lt "${BAD}" ]; then
FAILED=1
echo "${TARGET}: Less than ${BAD} instructions (${INST_COUNT})"
echo "${INST}"
elif [ "${INST_COUNT}" -lt "${GOOD}" ]; then
FAILED=1
echo "${TARGET}: WARNING Less than ${GOOD} instructions (${INST_COUNT})"
echo "Consider adjusting values"
echo "${INST}"
fi
}

# Capture both stderr and stdout
# https://stackoverflow.com/questions/11027679/capture-stdout-and-stderr-into-different-variables
function catch() {
{
IFS=$'\n' read -r -d '' "${1}";
IFS=$'\n' read -r -d '' "${2}";
(IFS=$'\n' read -r -d '' _ERRNO_; return ${_ERRNO_});
} < <((printf '\0%s\0%d\0' "$(((({ shift 2; "${@}"; echo "${?}" 1>&3-; } | tr -d '\0' 1>&4-) 4>&2- 2>&1- | tr -d '\0' 1>&4-) 3>&1- | exit "$(cat)") 4>&1-)" "${?}" 1>&2) 2>&1)
}

test_all
10 changes: 7 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,6 @@ use core::cmp;
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Neg, Not};
use core::option::Option;

#[cfg(feature = "core_hint_black_box")]
use core::hint::black_box;

/// The `Choice` struct represents a choice for use in conditional assignment.
///
/// It is a wrapper around a `u8`, which should have the value either `1` (true)
Expand Down Expand Up @@ -220,6 +217,7 @@ impl Not for Choice {
/// code may break in a non-destructive way in the future, “constant-time” code
/// is a continually moving target, and this is better than doing nothing.
#[inline(never)]
#[cfg(not(feature = "core_hint_black_box"))]
fn black_box<T: Copy>(input: T) -> T {
unsafe {
// Optimization barrier
Expand All @@ -232,6 +230,12 @@ fn black_box<T: Copy>(input: T) -> T {
}
}

#[cfg(feature = "core_hint_black_box")]
#[inline(never)]
const fn black_box<T>(input: T) -> T {
core::hint::black_box(input)
}

impl From<u8> for Choice {
#[inline]
fn from(input: u8) -> Choice {
Expand Down