Clean up backend features and vendor curve25519_dalek_derive (#531)
* Vendor import unsafe_target_features as curve25519-dalek-derive Co-authored-by: Jan Bujak <jan@parity.io> * Remove feature gates from avx2/ifma * Add buildtime compile diagnostics about backend selection * Add build script tests * Documentation changes * Disable simd related features unless simd was determined via build * Add note and test about the override warning when unsuccesful * Reduce complexity in build gating via compile_error --------- Co-authored-by: Jan Bujak <jan@parity.io> Co-authored-by: Michael Rosenberg <michael@mrosenberg.pub>
This commit is contained in:
parent
e111b5d913
commit
e429bde88d
65
.github/workflows/rust.yml
vendored
65
.github/workflows/rust.yml
vendored
@ -11,7 +11,7 @@ env:
|
||||
RUSTFLAGS: '-D warnings'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
test-auto:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
@ -38,10 +38,58 @@ jobs:
|
||||
- run: cargo test --target ${{ matrix.target }} --features digest
|
||||
- run: cargo test --target ${{ matrix.target }} --features rand_core
|
||||
- run: cargo test --target ${{ matrix.target }} --features serde
|
||||
|
||||
test-fiat:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
# 32-bit target
|
||||
- target: i686-unknown-linux-gnu
|
||||
deps: sudo apt update && sudo apt install gcc-multilib
|
||||
|
||||
# 64-bit target
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: rustup target add ${{ matrix.target }}
|
||||
- run: ${{ matrix.deps }}
|
||||
- env:
|
||||
RUSTFLAGS: '--cfg curve25519_dalek_backend="fiat"'
|
||||
run: cargo test --target ${{ matrix.target }}
|
||||
|
||||
test-serial:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
# 32-bit target
|
||||
- target: i686-unknown-linux-gnu
|
||||
deps: sudo apt update && sudo apt install gcc-multilib
|
||||
|
||||
# 64-bit target
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: rustup target add ${{ matrix.target }}
|
||||
- run: ${{ matrix.deps }}
|
||||
- env:
|
||||
RUSTFLAGS: '--cfg curve25519_dalek_backend="serial"'
|
||||
run: cargo test --target ${{ matrix.target }}
|
||||
|
||||
build-script:
|
||||
name: Test Build Script
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
targets: wasm32-unknown-unknown,x86_64-unknown-linux-gnu,i686-unknown-linux-gnu
|
||||
- run: bash tests/build_tests.sh
|
||||
|
||||
build-nostd:
|
||||
name: Build on no_std target (thumbv7em-none-eabi)
|
||||
runs-on: ubuntu-latest
|
||||
@ -55,8 +103,8 @@ jobs:
|
||||
- run: cargo build --target thumbv7em-none-eabi --release
|
||||
- run: cargo build --target thumbv7em-none-eabi --release --features serde
|
||||
|
||||
test-simd-native:
|
||||
name: Test simd backend (native)
|
||||
test-simd-nightly:
|
||||
name: Test simd backend (nightly)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@ -66,11 +114,12 @@ jobs:
|
||||
# 1) build all of the x86_64 SIMD code,
|
||||
# 2) run all of the SIMD-specific tests that the test runner supports,
|
||||
# 3) run all of the normal tests using the best available SIMD backend.
|
||||
# This should automatically pick up the simd backend in a x84_64 runner
|
||||
RUSTFLAGS: '-C target_cpu=native'
|
||||
run: cargo test --features simd --target x86_64-unknown-linux-gnu
|
||||
run: cargo test --target x86_64-unknown-linux-gnu
|
||||
|
||||
test-simd-avx2:
|
||||
name: Test simd backend (avx2)
|
||||
test-simd-stable:
|
||||
name: Test simd backend (stable)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@ -78,8 +127,10 @@ jobs:
|
||||
- env:
|
||||
# This will run AVX2-specific tests and run all of the normal tests
|
||||
# with the AVX2 backend, even if the runner supports AVX512.
|
||||
# This should automatically pick up the simd backend in a x86_64 runner
|
||||
# It should pick AVX2 due to stable toolchain used since AVX512 requires nigthly
|
||||
RUSTFLAGS: '-C target_feature=+avx2'
|
||||
run: cargo test --no-default-features --features alloc,precomputed-tables,zeroize,simd_avx2 --target x86_64-unknown-linux-gnu
|
||||
run: cargo test --no-default-features --features alloc,precomputed-tables,zeroize --target x86_64-unknown-linux-gnu
|
||||
|
||||
build-docs:
|
||||
name: Build docs
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,6 +1,8 @@
|
||||
*/target/*
|
||||
target
|
||||
Cargo.lock
|
||||
|
||||
*/Cargo.lock
|
||||
build*.txt
|
||||
*~
|
||||
\#*
|
||||
.\#*
|
||||
|
14
Cargo.toml
14
Cargo.toml
@ -53,7 +53,6 @@ digest = { version = "0.10", default-features = false, optional = true }
|
||||
subtle = { version = "2.3.0", default-features = false }
|
||||
serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] }
|
||||
zeroize = { version = "1", default-features = false, optional = true }
|
||||
unsafe_target_feature = { version = "= 0.1.1", optional = true }
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||
cpufeatures = "0.2.6"
|
||||
@ -62,20 +61,13 @@ cpufeatures = "0.2.6"
|
||||
fiat-crypto = "0.1.19"
|
||||
|
||||
[features]
|
||||
default = ["alloc", "precomputed-tables", "zeroize", "simd"]
|
||||
default = ["alloc", "precomputed-tables", "zeroize"]
|
||||
alloc = ["zeroize?/alloc"]
|
||||
precomputed-tables = []
|
||||
legacy_compatibility = []
|
||||
|
||||
# Whether to allow the use of the AVX2 SIMD backend.
|
||||
simd_avx2 = ["unsafe_target_feature"]
|
||||
|
||||
# Whether to allow the use of the AVX512 SIMD backend.
|
||||
# (Note: This requires Rust nightly; on Rust stable this feature will be ignored.)
|
||||
simd_avx512 = ["unsafe_target_feature"]
|
||||
|
||||
# A meta-feature to allow all SIMD backends to be used.
|
||||
simd = ["simd_avx2", "simd_avx512"]
|
||||
[target.'cfg(all(not(curve25519_dalek_backend = "fiat"), not(curve25519_dalek_backend = "serial"), target_arch = "x86_64"))'.dependencies]
|
||||
curve25519-dalek-derive = { version = "0.1", path = "curve25519-dalek-derive" }
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 2
|
||||
|
74
README.md
74
README.md
@ -53,9 +53,6 @@ curve25519-dalek = "4.0.0-rc.2"
|
||||
| `alloc` | ✓ | Enables Edwards and Ristretto multiscalar multiplication, batch scalar inversion, and batch Ristretto double-and-compress. Also enables `zeroize`. |
|
||||
| `zeroize` | ✓ | Enables [`Zeroize`][zeroize-trait] for all scalar and curve point types. |
|
||||
| `precomputed-tables` | ✓ | Includes precomputed basepoint multiplication tables. This speeds up `EdwardsPoint::mul_base` and `RistrettoPoint::mul_base` by ~4x, at the cost of ~30KB added to the code size. |
|
||||
| `simd_avx2` | ✓ | Allows the AVX2 SIMD backend to be used, if available. |
|
||||
| `simd_avx512` | ✓ | Allows the AVX512 SIMD backend to be used, if available. |
|
||||
| `simd` | ✓ | Allows every SIMD backend to be used, if available. |
|
||||
| `rand_core` | | Enables `Scalar::random` and `RistrettoPoint::random`. This is an optional dependency whose version is not subject to SemVer. See [below](#public-api-semver-exemptions) for more details. |
|
||||
| `digest` | | Enables `RistrettoPoint::{from_hash, hash_from_bytes}` and `Scalar::{from_hash, hash_from_bytes}`. This is an optional dependency whose version is not subject to SemVer. See [below](#public-api-semver-exemptions) for more details. |
|
||||
| `serde` | | Enables `serde` serialization/deserialization for all the point and scalar types. |
|
||||
@ -90,25 +87,27 @@ latest breaking changes in high level are below:
|
||||
|
||||
This release also does a lot of dependency updates and relaxations to unblock upstream build issues.
|
||||
|
||||
### 4.0.0 - Open Breaking Changes
|
||||
|
||||
See tracking issue: [curve25519-dalek/issues/521](https://github.com/dalek-cryptography/curve25519-dalek/issues/521)
|
||||
|
||||
# Backends
|
||||
|
||||
Curve arithmetic is implemented and used by selecting one of the following backends:
|
||||
Curve arithmetic is implemented and used by one of the following backends:
|
||||
|
||||
| Backend | Implementation | Target backends |
|
||||
| :--- | :--- | :--- |
|
||||
| `[default]` | Automatic runtime backend selection (either serial or SIMD) | `u32` <br/> `u64` <br/> `avx2` <br/> `avx512` |
|
||||
| `fiat` | Formally verified field arithmetic from [fiat-crypto] | `fiat_u32` <br/> `fiat_u64` |
|
||||
| Backend | Selection | Implementation | Bits / Word sizes |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `serial` | Automatic | An optimized, non-parllel implementation | `32` and `64` |
|
||||
| `fiat` | Manual | Formally verified field arithmetic from [fiat-crypto] | `32` and `64` |
|
||||
| `simd` | Automatic | Intel AVX2 / AVX512 IFMA accelerated backend | `64` only |
|
||||
|
||||
To choose a backend other than the `[default]` backend, set the
|
||||
environment variable:
|
||||
At runtime, `curve25519-dalek` selects an arithmetic backend from the set of backends it was compiled to support. For Intel x86-64 targets, unless otherwise specified, it will build itself with `simd` support, and default to `serial` at runtime if the appropriate CPU features aren't detected. See [SIMD backend] for more details.
|
||||
|
||||
In the future, `simd` backend may be extended to cover more instruction sets. This change will be non-breaking as this is considered as implementation detail.
|
||||
|
||||
## Manual Backend Override
|
||||
|
||||
You can force the crate to compile with specific backend support, e.g., `serial` for x86-64 targets to save code size, or `fiat` to force the runtime to use verified code. To do this, set the environment variable:
|
||||
```sh
|
||||
RUSTFLAGS='--cfg curve25519_dalek_backend="BACKEND"'
|
||||
```
|
||||
where `BACKEND` is `fiat`. Equivalently, you can write to
|
||||
Equivalently, you can write to
|
||||
`~/.cargo/config`:
|
||||
```toml
|
||||
[build]
|
||||
@ -117,53 +116,49 @@ rustflags = ['--cfg=curve25519_dalek_backend="BACKEND"']
|
||||
More info [here](https://doc.rust-lang.org/cargo/reference/config.html#buildrustflags).
|
||||
|
||||
Note for contributors: The target backends are not entirely independent of each
|
||||
other. The SIMD backend directly depends on parts of the the `u64` backend to
|
||||
other. The [SIMD backend] directly depends on parts of the serial backend to
|
||||
function.
|
||||
|
||||
## Word size for serial backends
|
||||
## Bits / Word size
|
||||
|
||||
`curve25519-dalek` will automatically choose the word size for the `[default]`
|
||||
and `fiat` serial backends, based on the build target. For example, building
|
||||
for a 64-bit machine, the default `u64` target backend is automatically chosen
|
||||
when the `[default]` backend is selected, and `fiat_u64` is chosen when the
|
||||
`fiat backend is selected.
|
||||
`curve25519-dalek` will automatically choose the word size for the `fiat` and
|
||||
`serial` backends, based on the build target.
|
||||
For example, building for a 64-bit machine, the default 64 bit word size is
|
||||
automatically chosen when either the `serial` or `fiat` backend is selected.
|
||||
|
||||
Backend word size can be overridden for `[default]` and `fiat` by setting the
|
||||
In some targets it might be required to override the word size for better
|
||||
performance.
|
||||
Backend word size can be overridden for `serial` and `fiat` by setting the
|
||||
environment variable:
|
||||
```sh
|
||||
RUSTFLAGS='--cfg curve25519_dalek_bits="SIZE"'
|
||||
```
|
||||
where `SIZE` is `32` or `64`. As in the above section, this can also be placed
|
||||
`SIZE` is `32` or `64`. As in the above section, this can also be placed
|
||||
in `~/.cargo/config`.
|
||||
|
||||
**NOTE:** Using a word size of 32 will automatically disable SIMD support.
|
||||
Note: The [SIMD backend] requires a word size of 64 bits. Attempting to set bits=32 and backend=`simd` will yield a compile error.
|
||||
|
||||
### Cross-compilation
|
||||
|
||||
Because backend selection is done by target, cross-compiling will select the
|
||||
correct word size automatically. For example, on an x86-64 Linux machine,
|
||||
`curve25519-dalek` will use the `u32` target backend if the following is run:
|
||||
Because backend selection is done by target, cross-compiling will select the correct word size automatically. For example, if a x86-64 Linux machine runs the following commands, `curve25519-dalek` will be compiled with the 32-bit `serial` backend.
|
||||
```console
|
||||
$ sudo apt install gcc-multilib # (or whatever package manager you use)
|
||||
$ rustup target add i686-unknown-linux-gnu
|
||||
$ cargo build --target i686-unknown-linux-gnu
|
||||
```
|
||||
|
||||
## SIMD target backends
|
||||
## SIMD backend
|
||||
|
||||
The SIMD target backend selection is done automatically at runtime depending
|
||||
on the available CPU features, provided the appropriate feature flag is enabled.
|
||||
The specific SIMD backend (AVX512 / AVX2 / `serial` default) is selected automatically at runtime, depending on the currently available CPU features, and whether Rust nightly is being used for compilation. The precise conditions are specified below.
|
||||
|
||||
You can also specify an appropriate `-C target_feature` to build a binary
|
||||
which assumes the required SIMD instructions are always available.
|
||||
For a given CPU feature, you can also specify an appropriate `-C target_feature` to build a binary which assumes the required SIMD instructions are always available. Don't do this if you don't have a good reason.
|
||||
|
||||
| Backend | Feature flag | `RUSTFLAGS` | Requires nightly? |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| avx2 | `simd_avx2` | `-C target_feature=+avx2` | no |
|
||||
| avx512 | `simd_avx512` | `-C target_feature=+avx512ifma,+avx512vl` | yes |
|
||||
| Backend | `RUSTFLAGS` | Requires nightly? |
|
||||
| :--- | :--- | :--- |
|
||||
| avx2 | `-C target_feature=+avx2` | no |
|
||||
| avx512 | `-C target_feature=+avx512ifma,+avx512vl` | yes |
|
||||
|
||||
The AVX512 backend requires Rust nightly. When compiled on a non-nightly
|
||||
compiler it will always be disabled.
|
||||
If compiled on a non-nightly compiler, `curve25519-dalek` will not include AVX512 code, and therefore will never select it at runtime.
|
||||
|
||||
# Documentation
|
||||
|
||||
@ -326,3 +321,4 @@ contributions.
|
||||
[semver]: https://semver.org/spec/v2.0.0.html
|
||||
[rngcorestd]: https://github.com/rust-random/rand/tree/7aa25d577e2df84a5156f824077bb7f6bdf28d97/rand_core#crate-features
|
||||
[zeroize-trait]: https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html
|
||||
[SIMD backend]: #simd-backend
|
||||
|
33
build.rs
33
build.rs
@ -3,6 +3,7 @@
|
||||
#![deny(clippy::unwrap_used, dead_code)]
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(PartialEq, Debug)]
|
||||
enum DalekBits {
|
||||
Dalek32,
|
||||
Dalek64,
|
||||
@ -34,6 +35,38 @@ fn main() {
|
||||
// so for those we want to apply the `#[allow(unused_unsafe)]` attribute to get rid of that warning.
|
||||
println!("cargo:rustc-cfg=allow_unused_unsafe");
|
||||
}
|
||||
|
||||
let target_arch = match std::env::var("CARGO_CFG_TARGET_ARCH") {
|
||||
Ok(arch) => arch,
|
||||
_ => "".to_string(),
|
||||
};
|
||||
|
||||
// Backend overrides / defaults
|
||||
let curve25519_dalek_backend =
|
||||
match std::env::var("CARGO_CFG_CURVE25519_DALEK_BACKEND").as_deref() {
|
||||
Ok("fiat") => "fiat",
|
||||
Ok("serial") => "serial",
|
||||
Ok("simd") => {
|
||||
// simd can only be enabled on x86_64 & 64bit target_pointer_width
|
||||
match is_capable_simd(&target_arch, curve25519_dalek_bits) {
|
||||
true => "simd",
|
||||
// If override is not possible this must result to compile error
|
||||
// See: issues/532
|
||||
false => panic!("Could not override curve25519_dalek_backend to simd"),
|
||||
}
|
||||
}
|
||||
// default between serial / simd (if potentially capable)
|
||||
_ => match is_capable_simd(&target_arch, curve25519_dalek_bits) {
|
||||
true => "simd",
|
||||
false => "serial",
|
||||
},
|
||||
};
|
||||
println!("cargo:rustc-cfg=curve25519_dalek_backend=\"{curve25519_dalek_backend}\"");
|
||||
}
|
||||
|
||||
// Is the target arch & curve25519_dalek_bits potentially simd capable ?
|
||||
fn is_capable_simd(arch: &str, bits: DalekBits) -> bool {
|
||||
arch == "x86_64" && bits == DalekBits::Dalek64
|
||||
}
|
||||
|
||||
// Deterministic cfg(curve25519_dalek_bits) when this is not explicitly set.
|
||||
|
19
curve25519-dalek-derive/Cargo.toml
Normal file
19
curve25519-dalek-derive/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "curve25519-dalek-derive"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
repository = "https://github.com/dalek-cryptography/curve25519-dalek"
|
||||
homepage = "https://github.com/dalek-cryptography/curve25519-dalek"
|
||||
documentation = "https://docs.rs/curve25519-dalek-derive"
|
||||
license = "MIT/Apache-2.0"
|
||||
readme = "README.md"
|
||||
description = "curve25519-dalek Derives"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.53"
|
||||
quote = "1.0.26"
|
||||
syn = { version = "2.0.8", features = ["full"] }
|
191
curve25519-dalek-derive/README.md
Normal file
191
curve25519-dalek-derive/README.md
Normal file
@ -0,0 +1,191 @@
|
||||
# A more convenient `#[target_feature]` replacement
|
||||
|
||||
To get good performance out of SIMD everything on the SIMD codepath must be inlined.
|
||||
With how SIMD is currently implemented in Rust one of two things have to be true for
|
||||
a function using SIMD to be inlinable: (and this includes the SIMD intrinsics themselves)
|
||||
|
||||
a) The whole program has to be compiled with the relevant `-C target-cpu` or `-C target-feature` flags.
|
||||
|
||||
b) SIMD support must be automatically detected at runtime, and every function on the SIMD codepath must be marked with `#[target_feature]`.
|
||||
|
||||
Both have their downsides. Setting the `target-cpu` or `target-features` makes the resulting binary
|
||||
incompatible with older CPUs, while using `#[target_feature]` is incredibly inconvenient.
|
||||
|
||||
This crate is meant to make `#[target_feature]` less painful to use.
|
||||
|
||||
## Problems with `#[target_feature]`
|
||||
|
||||
When we're not compiling with the relevant `target-cpu`/`target-feature` flags everything on
|
||||
the SIMD codepath must be marked with the `#[target_feature]` attribute. This is not a problem
|
||||
when all of your SIMD code is neatly encapsulated inside of a single function, but once you start
|
||||
to build out more elaborate abstractions it starts to become painful to use.
|
||||
|
||||
* It can only be used on `unsafe` functions, so everything on your SIMD codepath now has to be `unsafe`.
|
||||
|
||||
In theory this is nice - these functions require the relevant SIMD instructions to be present at runtime,
|
||||
so calling them without checking is obviously unsafe! But in practice this is rarely what you want. When
|
||||
you build an abstraction over SIMD code you usually want to assume that *internally* within your module
|
||||
all of the necessary SIMD instructions are available, and you only want to check this at the boundaries
|
||||
when you're first entering your module. You do *not* want to infect everything *inside* of the module with
|
||||
`unsafe` since you've already checked this invariant at the module's API boundary.
|
||||
|
||||
* It cannot be used on non-`unsafe` trait methods.
|
||||
|
||||
If you're implementing a trait, say for example `std::ops::Add`, then you cannot mark the method `unsafe`
|
||||
unless the original trait also has it marked as `unsafe`, and usually it doesn't.
|
||||
|
||||
* It makes it impossible to abstract over a given SIMD instruction set using a trait.
|
||||
|
||||
For example, let's assume you want to abstract over which SIMD instructions you use using a trait in the following way:
|
||||
|
||||
```rust
|
||||
trait Backend {
|
||||
unsafe fn sum(input: &[u32]) -> u32;
|
||||
}
|
||||
|
||||
struct AVX;
|
||||
impl Backend for AVX {
|
||||
#[target_feature(enable = "avx")]
|
||||
unsafe fn sum(xs: &[u32]) -> u32 {
|
||||
// ...
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
struct AVX2;
|
||||
impl Backend for AVX2 {
|
||||
#[target_feature(enable = "avx2")]
|
||||
unsafe fn sum(xs: &[u32]) -> u32 {
|
||||
// ...
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
// And now you want a have function which calls into that trait:
|
||||
unsafe fn do_calculations<B>(xs: &[u32]) -> u32 where B: Backend {
|
||||
let value = B::sum(xs);
|
||||
// ...do some more calculations here...
|
||||
value
|
||||
}
|
||||
```
|
||||
|
||||
We have a problem here. This has to be marked with `#[target_feature]`, and that has to specify the concrete
|
||||
feature flag for a given SIMD instruction set, but this function is generic so we can't do that!
|
||||
|
||||
## How does this crate make it better?
|
||||
|
||||
### You can now mark safe functions with `#[target_feature]`
|
||||
|
||||
This crate exposes an `#[unsafe_target_feature]` macro which works just like `#[target_feature]` except
|
||||
it moves the `unsafe` from the function prototype into the macro name, and can be used on safe functions.
|
||||
|
||||
```rust,compile_fail
|
||||
// ERROR: `#[target_feature(..)]` can only be applied to `unsafe` functions
|
||||
#[target_feature(enable = "avx2")]
|
||||
fn func() {}
|
||||
```
|
||||
|
||||
```rust
|
||||
// It works, but must be `unsafe`
|
||||
#[target_feature(enable = "avx2")]
|
||||
unsafe fn func() {}
|
||||
```
|
||||
|
||||
```rust
|
||||
use curve25519_dalek_derive::unsafe_target_feature;
|
||||
|
||||
// No `unsafe` on the function itself!
|
||||
#[unsafe_target_feature("avx2")]
|
||||
fn func() {}
|
||||
```
|
||||
|
||||
It can also be used to mark functions inside of impls:
|
||||
|
||||
```rust,compile_fail
|
||||
struct S;
|
||||
|
||||
impl core::ops::Add for S {
|
||||
type Output = S;
|
||||
// ERROR: method `add` has an incompatible type for trait
|
||||
#[target_feature(enable = "avx2")]
|
||||
unsafe fn add(self, rhs: S) -> S {
|
||||
S
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```rust
|
||||
use curve25519_dalek_derive::unsafe_target_feature;
|
||||
|
||||
struct S;
|
||||
|
||||
#[unsafe_target_feature("avx2")]
|
||||
impl core::ops::Add for S {
|
||||
type Output = S;
|
||||
// No `unsafe` on the function itself!
|
||||
fn add(self, rhs: S) -> S {
|
||||
S
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### You can generate specialized copies of a module for each target feature
|
||||
|
||||
```rust
|
||||
use curve25519_dalek_derive::unsafe_target_feature_specialize;
|
||||
|
||||
#[unsafe_target_feature_specialize("sse2", "avx2", conditional("avx512ifma", nightly))]
|
||||
mod simd {
|
||||
#[for_target_feature("sse2")]
|
||||
pub const CONSTANT: u32 = 1;
|
||||
|
||||
#[for_target_feature("avx2")]
|
||||
pub const CONSTANT: u32 = 2;
|
||||
|
||||
#[for_target_feature("avx512ifma")]
|
||||
pub const CONSTANT: u32 = 3;
|
||||
|
||||
pub fn func() { /* ... */ }
|
||||
}
|
||||
|
||||
fn entry_point() {
|
||||
#[cfg(nightly)]
|
||||
if std::is_x86_feature_detected!("avx512ifma") {
|
||||
return simd_avx512ifma::func();
|
||||
}
|
||||
|
||||
if std::is_x86_feature_detected!("avx2") {
|
||||
return simd_avx2::func();
|
||||
}
|
||||
|
||||
if std::is_x86_feature_detected!("sse2") {
|
||||
return simd_sse2::func();
|
||||
}
|
||||
|
||||
unimplemented!();
|
||||
}
|
||||
```
|
||||
|
||||
## How to use `#[unsafe_target_feature]`?
|
||||
|
||||
- Can be used on `fn`s, `impl`s and `mod`s.
|
||||
- When used on a function will only apply to that function; it won't apply to any nested functions, traits, mods, etc.
|
||||
- When used on an `impl` will only apply to all of the functions directly defined inside of that `impl`.
|
||||
- When used on a `mod` will only apply to all of the `fn`s and `impl`s directly defined inside of that `mod`.
|
||||
- Cannot be used on methods which use `self` or `Self`; instead use it on the `impl` in which the method is defined.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
466
curve25519-dalek-derive/src/lib.rs
Normal file
466
curve25519-dalek-derive/src/lib.rs
Normal file
@ -0,0 +1,466 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
macro_rules! unsupported_if_some {
|
||||
($value:expr) => {
|
||||
if let Some(value) = $value {
|
||||
return syn::Error::new(value.span(), "unsupported by #[unsafe_target_feature(...)]")
|
||||
.into_compile_error()
|
||||
.into();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! unsupported {
|
||||
($value: expr) => {
|
||||
return syn::Error::new(
|
||||
$value.span(),
|
||||
"unsupported by #[unsafe_target_feature(...)]",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into()
|
||||
};
|
||||
}
|
||||
|
||||
mod kw {
|
||||
syn::custom_keyword!(conditional);
|
||||
}
|
||||
|
||||
enum SpecializeArg {
|
||||
LitStr(syn::LitStr),
|
||||
Conditional(Conditional),
|
||||
}
|
||||
|
||||
impl SpecializeArg {
|
||||
fn lit(&self) -> &syn::LitStr {
|
||||
match self {
|
||||
SpecializeArg::LitStr(lit) => lit,
|
||||
SpecializeArg::Conditional(conditional) => &conditional.lit,
|
||||
}
|
||||
}
|
||||
|
||||
fn condition(&self) -> Option<&TokenStream2> {
|
||||
match self {
|
||||
SpecializeArg::LitStr(..) => None,
|
||||
SpecializeArg::Conditional(conditional) => Some(&conditional.attr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Conditional {
|
||||
lit: syn::LitStr,
|
||||
attr: TokenStream2,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for Conditional {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let lit = input.parse()?;
|
||||
input.parse::<syn::Token![,]>()?;
|
||||
let attr = input.parse()?;
|
||||
|
||||
Ok(Conditional { lit, attr })
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for SpecializeArg {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(kw::conditional) {
|
||||
input.parse::<kw::conditional>()?;
|
||||
|
||||
let content;
|
||||
syn::parenthesized!(content in input);
|
||||
|
||||
let conditional = content.parse()?;
|
||||
Ok(SpecializeArg::Conditional(conditional))
|
||||
} else {
|
||||
Ok(SpecializeArg::LitStr(input.parse()?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SpecializeArgs(syn::punctuated::Punctuated<SpecializeArg, syn::Token![,]>);
|
||||
|
||||
impl syn::parse::Parse for SpecializeArgs {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
Ok(Self(syn::punctuated::Punctuated::parse_terminated(input)?))
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn unsafe_target_feature(attributes: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let attributes = syn::parse_macro_input!(attributes as syn::LitStr);
|
||||
let item = syn::parse_macro_input!(input as syn::Item);
|
||||
process_item(&attributes, item, true)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn unsafe_target_feature_specialize(
|
||||
attributes: TokenStream,
|
||||
input: TokenStream,
|
||||
) -> TokenStream {
|
||||
let attributes = syn::parse_macro_input!(attributes as SpecializeArgs);
|
||||
let item_mod = syn::parse_macro_input!(input as syn::ItemMod);
|
||||
|
||||
let mut out = Vec::new();
|
||||
for attributes in attributes.0 {
|
||||
let features: Vec<_> = attributes
|
||||
.lit()
|
||||
.value()
|
||||
.split(",")
|
||||
.map(|feature| feature.replace(" ", ""))
|
||||
.collect();
|
||||
let name = format!("{}_{}", item_mod.ident, features.join("_"));
|
||||
let ident = syn::Ident::new(&name, item_mod.ident.span());
|
||||
let mut attrs = item_mod.attrs.clone();
|
||||
if let Some(condition) = attributes.condition() {
|
||||
attrs.push(syn::Attribute {
|
||||
pound_token: Default::default(),
|
||||
style: syn::AttrStyle::Outer,
|
||||
bracket_token: Default::default(),
|
||||
meta: syn::Meta::List(syn::MetaList {
|
||||
path: syn::Ident::new("cfg", attributes.lit().span()).into(),
|
||||
delimiter: syn::MacroDelimiter::Paren(Default::default()),
|
||||
tokens: condition.clone(),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
let item_mod = process_mod(
|
||||
attributes.lit(),
|
||||
syn::ItemMod {
|
||||
attrs,
|
||||
ident,
|
||||
..item_mod.clone()
|
||||
},
|
||||
Some(features),
|
||||
);
|
||||
|
||||
out.push(item_mod);
|
||||
}
|
||||
|
||||
quote::quote! {
|
||||
#(#out)*
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
fn process_item(attributes: &syn::LitStr, item: syn::Item, strict: bool) -> TokenStream {
|
||||
match item {
|
||||
syn::Item::Fn(function) => process_function(attributes, function, None),
|
||||
syn::Item::Impl(item_impl) => process_impl(attributes, item_impl),
|
||||
syn::Item::Mod(item_mod) => process_mod(attributes, item_mod, None).into(),
|
||||
item => {
|
||||
if strict {
|
||||
unsupported!(item)
|
||||
} else {
|
||||
quote::quote! { #item }.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_mod(
|
||||
attributes: &syn::LitStr,
|
||||
mut item_mod: syn::ItemMod,
|
||||
spec_features: Option<Vec<String>>,
|
||||
) -> TokenStream2 {
|
||||
if let Some((_, ref mut content)) = item_mod.content {
|
||||
'next_item: for item in content {
|
||||
if let Some(ref spec_features) = spec_features {
|
||||
match item {
|
||||
syn::Item::Const(syn::ItemConst { ref mut attrs, .. })
|
||||
| syn::Item::Enum(syn::ItemEnum { ref mut attrs, .. })
|
||||
| syn::Item::ExternCrate(syn::ItemExternCrate { ref mut attrs, .. })
|
||||
| syn::Item::Fn(syn::ItemFn { ref mut attrs, .. })
|
||||
| syn::Item::ForeignMod(syn::ItemForeignMod { ref mut attrs, .. })
|
||||
| syn::Item::Impl(syn::ItemImpl { ref mut attrs, .. })
|
||||
| syn::Item::Macro(syn::ItemMacro { ref mut attrs, .. })
|
||||
| syn::Item::Mod(syn::ItemMod { ref mut attrs, .. })
|
||||
| syn::Item::Static(syn::ItemStatic { ref mut attrs, .. })
|
||||
| syn::Item::Struct(syn::ItemStruct { ref mut attrs, .. })
|
||||
| syn::Item::Trait(syn::ItemTrait { ref mut attrs, .. })
|
||||
| syn::Item::TraitAlias(syn::ItemTraitAlias { ref mut attrs, .. })
|
||||
| syn::Item::Type(syn::ItemType { ref mut attrs, .. })
|
||||
| syn::Item::Union(syn::ItemUnion { ref mut attrs, .. })
|
||||
| syn::Item::Use(syn::ItemUse { ref mut attrs, .. }) => {
|
||||
let mut index = 0;
|
||||
while index < attrs.len() {
|
||||
let attr = &attrs[index];
|
||||
if matches!(attr.style, syn::AttrStyle::Outer) {
|
||||
match attr.meta {
|
||||
syn::Meta::List(ref list)
|
||||
if is_path_eq(&list.path, "for_target_feature") =>
|
||||
{
|
||||
let feature: syn::LitStr = match list.parse_args() {
|
||||
Ok(feature) => feature,
|
||||
Err(error) => {
|
||||
return error.into_compile_error();
|
||||
}
|
||||
};
|
||||
|
||||
let feature = feature.value();
|
||||
if !spec_features
|
||||
.iter()
|
||||
.any(|enabled_feature| feature == *enabled_feature)
|
||||
{
|
||||
*item = syn::Item::Verbatim(Default::default());
|
||||
continue 'next_item;
|
||||
}
|
||||
|
||||
attrs.remove(index);
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unsupported!(item_mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*item = syn::Item::Verbatim(
|
||||
process_item(
|
||||
attributes,
|
||||
std::mem::replace(item, syn::Item::Verbatim(Default::default())),
|
||||
false,
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
quote::quote! {
|
||||
#item_mod
|
||||
}
|
||||
}
|
||||
|
||||
fn process_impl(attributes: &syn::LitStr, mut item_impl: syn::ItemImpl) -> TokenStream {
|
||||
unsupported_if_some!(item_impl.defaultness);
|
||||
unsupported_if_some!(item_impl.unsafety);
|
||||
|
||||
let mut items = Vec::new();
|
||||
for item in item_impl.items.drain(..) {
|
||||
match item {
|
||||
syn::ImplItem::Fn(function) => {
|
||||
unsupported_if_some!(function.defaultness);
|
||||
let function = syn::ItemFn {
|
||||
attrs: function.attrs,
|
||||
vis: function.vis,
|
||||
sig: function.sig,
|
||||
block: Box::new(function.block),
|
||||
};
|
||||
let output_item = process_function(
|
||||
attributes,
|
||||
function,
|
||||
Some((item_impl.generics.clone(), item_impl.self_ty.clone())),
|
||||
);
|
||||
items.push(syn::ImplItem::Verbatim(output_item.into()));
|
||||
}
|
||||
item => items.push(item),
|
||||
}
|
||||
}
|
||||
|
||||
item_impl.items = items;
|
||||
quote::quote! {
|
||||
#item_impl
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
fn is_path_eq(path: &syn::Path, ident: &str) -> bool {
|
||||
let segments: Vec<_> = ident.split("::").collect();
|
||||
path.segments.len() == segments.len()
|
||||
&& path
|
||||
.segments
|
||||
.iter()
|
||||
.zip(segments.iter())
|
||||
.all(|(segment, expected)| segment.ident == expected && segment.arguments.is_none())
|
||||
}
|
||||
|
||||
fn process_function(
|
||||
attributes: &syn::LitStr,
|
||||
function: syn::ItemFn,
|
||||
outer: Option<(syn::Generics, Box<syn::Type>)>,
|
||||
) -> TokenStream {
|
||||
if function.sig.unsafety.is_some() {
|
||||
return quote::quote! {
|
||||
#[target_feature(enable = #attributes)]
|
||||
#function
|
||||
}
|
||||
.into();
|
||||
}
|
||||
|
||||
unsupported_if_some!(function.sig.constness);
|
||||
unsupported_if_some!(function.sig.asyncness);
|
||||
unsupported_if_some!(function.sig.abi);
|
||||
unsupported_if_some!(function.sig.variadic);
|
||||
|
||||
let function_visibility = function.vis;
|
||||
let function_name = function.sig.ident;
|
||||
let function_return = function.sig.output;
|
||||
let function_inner_name =
|
||||
syn::Ident::new(&format!("_impl_{}", function_name), function_name.span());
|
||||
let function_args = function.sig.inputs;
|
||||
let function_body = function.block;
|
||||
let mut function_call_args = Vec::new();
|
||||
let mut function_args_outer = Vec::new();
|
||||
let mut function_args_inner = Vec::new();
|
||||
for (index, arg) in function_args.iter().enumerate() {
|
||||
match arg {
|
||||
syn::FnArg::Receiver(receiver) => {
|
||||
unsupported_if_some!(receiver.attrs.first());
|
||||
unsupported_if_some!(receiver.colon_token);
|
||||
|
||||
if outer.is_none() {
|
||||
return syn::Error::new(receiver.span(), "unsupported by #[unsafe_target_feature(...)]; put the attribute on the outer `impl`").into_compile_error().into();
|
||||
}
|
||||
|
||||
function_args_inner.push(syn::FnArg::Receiver(receiver.clone()));
|
||||
function_args_outer.push(syn::FnArg::Receiver(receiver.clone()));
|
||||
function_call_args.push(syn::Ident::new("self", receiver.self_token.span()));
|
||||
}
|
||||
syn::FnArg::Typed(ty) => {
|
||||
unsupported_if_some!(ty.attrs.first());
|
||||
|
||||
match &*ty.pat {
|
||||
syn::Pat::Ident(pat_ident) => {
|
||||
unsupported_if_some!(pat_ident.attrs.first());
|
||||
|
||||
function_args_inner.push(arg.clone());
|
||||
function_args_outer.push(syn::FnArg::Typed(syn::PatType {
|
||||
attrs: Vec::new(),
|
||||
pat: Box::new(syn::Pat::Ident(syn::PatIdent {
|
||||
attrs: Vec::new(),
|
||||
by_ref: None,
|
||||
mutability: None,
|
||||
ident: pat_ident.ident.clone(),
|
||||
subpat: None,
|
||||
})),
|
||||
colon_token: ty.colon_token,
|
||||
ty: ty.ty.clone(),
|
||||
}));
|
||||
function_call_args.push(pat_ident.ident.clone());
|
||||
}
|
||||
syn::Pat::Wild(pat_wild) => {
|
||||
unsupported_if_some!(pat_wild.attrs.first());
|
||||
|
||||
let ident = syn::Ident::new(
|
||||
&format!("__arg_{}__", index),
|
||||
pat_wild.underscore_token.span(),
|
||||
);
|
||||
function_args_inner.push(arg.clone());
|
||||
function_args_outer.push(syn::FnArg::Typed(syn::PatType {
|
||||
attrs: Vec::new(),
|
||||
pat: Box::new(syn::Pat::Ident(syn::PatIdent {
|
||||
attrs: Vec::new(),
|
||||
by_ref: None,
|
||||
mutability: None,
|
||||
ident: ident.clone(),
|
||||
subpat: None,
|
||||
})),
|
||||
colon_token: ty.colon_token,
|
||||
ty: ty.ty.clone(),
|
||||
}));
|
||||
function_call_args.push(ident);
|
||||
}
|
||||
_ => unsupported!(arg),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut maybe_inline = quote::quote! {};
|
||||
let mut maybe_outer_attributes = Vec::new();
|
||||
let mut maybe_cfg = quote::quote! {};
|
||||
for attribute in function.attrs {
|
||||
match &attribute.meta {
|
||||
syn::Meta::Path(path) if is_path_eq(path, "inline") => {
|
||||
maybe_inline = quote::quote! { #[inline] };
|
||||
}
|
||||
syn::Meta::Path(path) if is_path_eq(path, "test") => {
|
||||
maybe_outer_attributes.push(attribute);
|
||||
maybe_cfg = quote::quote! { #[cfg(target_feature = #attributes)] };
|
||||
}
|
||||
syn::Meta::List(syn::MetaList { path, tokens, .. })
|
||||
if is_path_eq(path, "inline") && tokens.to_string() == "always" =>
|
||||
{
|
||||
maybe_inline = quote::quote! { #[inline] };
|
||||
}
|
||||
syn::Meta::NameValue(syn::MetaNameValue { path, .. }) if is_path_eq(path, "doc") => {
|
||||
maybe_outer_attributes.push(attribute);
|
||||
}
|
||||
syn::Meta::List(syn::MetaList { path, .. })
|
||||
if is_path_eq(path, "cfg")
|
||||
|| is_path_eq(path, "allow")
|
||||
|| is_path_eq(path, "deny") =>
|
||||
{
|
||||
maybe_outer_attributes.push(attribute);
|
||||
}
|
||||
syn::Meta::Path(path) if is_path_eq(path, "rustfmt::skip") => {
|
||||
maybe_outer_attributes.push(attribute);
|
||||
}
|
||||
_ => unsupported!(attribute),
|
||||
}
|
||||
}
|
||||
|
||||
let (fn_impl_generics, fn_ty_generics, fn_where_clause) =
|
||||
function.sig.generics.split_for_impl();
|
||||
let fn_call_generics = fn_ty_generics.as_turbofish();
|
||||
|
||||
if let Some((generics, self_ty)) = outer {
|
||||
let (outer_impl_generics, outer_ty_generics, outer_where_clause) =
|
||||
generics.split_for_impl();
|
||||
let trait_ident =
|
||||
syn::Ident::new(&format!("__Impl_{}__", function_name), function_name.span());
|
||||
let item_trait = quote::quote! {
|
||||
#[allow(non_camel_case_types)]
|
||||
trait #trait_ident #outer_impl_generics #outer_where_clause {
|
||||
unsafe fn #function_inner_name #fn_impl_generics (#(#function_args_outer),*) #function_return #fn_where_clause;
|
||||
}
|
||||
};
|
||||
|
||||
let item_trait_impl = quote::quote! {
|
||||
impl #outer_impl_generics #trait_ident #outer_ty_generics for #self_ty #outer_where_clause {
|
||||
#[target_feature(enable = #attributes)]
|
||||
#maybe_inline
|
||||
unsafe fn #function_inner_name #fn_impl_generics (#(#function_args_inner),*) #function_return #fn_where_clause #function_body
|
||||
}
|
||||
};
|
||||
|
||||
quote::quote! {
|
||||
#[inline(always)]
|
||||
#(#maybe_outer_attributes)*
|
||||
#function_visibility fn #function_name #fn_impl_generics (#(#function_args_outer),*) #function_return #fn_where_clause {
|
||||
#item_trait
|
||||
#item_trait_impl
|
||||
unsafe {
|
||||
<Self as #trait_ident #outer_ty_generics> ::#function_inner_name #fn_call_generics (#(#function_call_args),*)
|
||||
}
|
||||
}
|
||||
}.into()
|
||||
} else {
|
||||
quote::quote! {
|
||||
#[inline(always)]
|
||||
#maybe_cfg
|
||||
#(#maybe_outer_attributes)*
|
||||
#function_visibility fn #function_name #fn_impl_generics (#(#function_args_outer),*) #function_return #fn_where_clause {
|
||||
#[target_feature(enable = #attributes)]
|
||||
#maybe_inline
|
||||
unsafe fn #function_inner_name #fn_impl_generics (#(#function_args_inner),*) #function_return #fn_where_clause #function_body
|
||||
unsafe {
|
||||
#function_inner_name #fn_call_generics (#(#function_call_args),*)
|
||||
}
|
||||
}
|
||||
}.into()
|
||||
}
|
||||
}
|
151
curve25519-dalek-derive/tests/tests.rs
Normal file
151
curve25519-dalek-derive/tests/tests.rs
Normal file
@ -0,0 +1,151 @@
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use curve25519_dalek_derive::{unsafe_target_feature, unsafe_target_feature_specialize};
|
||||
|
||||
#[unsafe_target_feature("sse2")]
|
||||
/// A doc comment.
|
||||
fn function(a: u32, b: u32) -> u32 {
|
||||
a - b
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("sse2")]
|
||||
fn function_with_const_arg<const N: u32>(b: u32) -> u32 {
|
||||
N - b
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("sse2")]
|
||||
fn function_with_where_clause<T>(a: T, b: T) -> T::Output
|
||||
where
|
||||
T: Copy + core::ops::Sub,
|
||||
{
|
||||
a - b
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("sse2")]
|
||||
#[cfg(feature = "dummy")]
|
||||
fn function_with_cfg() {}
|
||||
|
||||
#[unsafe_target_feature("sse2")]
|
||||
#[rustfmt::skip]
|
||||
fn function_with_rustfmt_skip() {}
|
||||
|
||||
struct Struct {
|
||||
a: u32,
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("sse2")]
|
||||
impl Struct {
|
||||
#[allow(unused_mut)]
|
||||
fn member_function(&self, mut b: u32) -> u32 {
|
||||
self.a - b
|
||||
}
|
||||
|
||||
fn member_function_with_const_arg<const N: u32>(self) -> u32 {
|
||||
self.a - N
|
||||
}
|
||||
|
||||
#[cfg(feature = "dummy")]
|
||||
fn member_function_with_cfg() {}
|
||||
}
|
||||
|
||||
struct StructWithGenerics<T>
|
||||
where
|
||||
T: Copy + core::ops::Sub,
|
||||
{
|
||||
a: T,
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("sse2")]
|
||||
impl<T> StructWithGenerics<T>
|
||||
where
|
||||
T: Copy + core::ops::Sub,
|
||||
{
|
||||
#[inline]
|
||||
fn member_function(&self, b: T) -> T::Output {
|
||||
self.a - b
|
||||
}
|
||||
}
|
||||
|
||||
struct StructWithGenericsNoWhere<T: Copy + core::ops::Sub> {
|
||||
a: T,
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("sse2")]
|
||||
impl<T: Copy + core::ops::Sub> StructWithGenericsNoWhere<T> {
|
||||
#[inline(always)]
|
||||
fn member_function(&self, b: T) -> T::Output {
|
||||
self.a - b
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("sse2")]
|
||||
#[allow(dead_code)]
|
||||
impl<'a> From<&'a Struct> for () {
|
||||
fn from(_: &'a Struct) -> Self {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("sse2")]
|
||||
mod inner {
|
||||
fn inner_function(a: u32, b: u32) -> u32 {
|
||||
a - b
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_target_feature_specialize("sse2", "avx2", conditional("avx512ifma", disabled))]
|
||||
mod inner_spec {
|
||||
use std;
|
||||
|
||||
#[for_target_feature("sse2")]
|
||||
const CONST: u32 = 1;
|
||||
|
||||
#[for_target_feature("avx2")]
|
||||
const CONST: u32 = 2;
|
||||
|
||||
pub fn spec_function(a: u32, b: u32) -> u32 {
|
||||
a - b - CONST
|
||||
}
|
||||
|
||||
#[for_target_feature("sse2")]
|
||||
const IS_AVX2: bool = false;
|
||||
|
||||
#[for_target_feature("avx2")]
|
||||
const IS_AVX2: bool = true;
|
||||
|
||||
#[test]
|
||||
fn test_specialized() {
|
||||
assert!(!IS_AVX2);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn test_specialized_inner() {
|
||||
assert!(!super::IS_AVX2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_target_feature("sse2")]
|
||||
#[test]
|
||||
fn test_sse2_only() {}
|
||||
|
||||
#[unsafe_target_feature("avx2")]
|
||||
#[test]
|
||||
fn test_avx2_only() {
|
||||
compile_error!();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function() {
|
||||
assert_eq!(function(10, 3), 7);
|
||||
assert_eq!(function_with_where_clause(10, 3), 7);
|
||||
assert_eq!(function_with_const_arg::<10>(3), 7);
|
||||
assert_eq!(Struct { a: 10 }.member_function(3), 7);
|
||||
assert_eq!(StructWithGenerics { a: 10 }.member_function(3), 7);
|
||||
assert_eq!(StructWithGenericsNoWhere { a: 10 }.member_function(3), 7);
|
||||
assert_eq!(inner_spec_sse2::spec_function(10, 3), 6);
|
||||
assert_eq!(inner_spec_avx2::spec_function(10, 3), 5);
|
||||
}
|
@ -39,41 +39,21 @@ use crate::Scalar;
|
||||
|
||||
pub mod serial;
|
||||
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
any(feature = "simd_avx2", all(feature = "simd_avx512", nightly)),
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(curve25519_dalek_backend = "simd")]
|
||||
pub mod vector;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum BackendKind {
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
feature = "simd_avx2",
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(curve25519_dalek_backend = "simd")]
|
||||
Avx2,
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
all(feature = "simd_avx512", nightly),
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
Avx512,
|
||||
Serial,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_selected_backend() -> BackendKind {
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
all(feature = "simd_avx512", nightly),
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
{
|
||||
cpufeatures::new!(cpuid_avx512, "avx512ifma", "avx512vl");
|
||||
let token_avx512: cpuid_avx512::InitToken = cpuid_avx512::init();
|
||||
@ -82,12 +62,7 @@ fn get_selected_backend() -> BackendKind {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
feature = "simd_avx2",
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(curve25519_dalek_backend = "simd")]
|
||||
{
|
||||
cpufeatures::new!(cpuid_avx2, "avx2");
|
||||
let token_avx2: cpuid_avx2::InitToken = cpuid_avx2::init();
|
||||
@ -110,10 +85,10 @@ where
|
||||
use crate::traits::VartimeMultiscalarMul;
|
||||
|
||||
match get_selected_backend() {
|
||||
#[cfg(all(target_arch = "x86_64", feature = "simd_avx2", curve25519_dalek_bits = "64", not(curve25519_dalek_backend = "fiat")))]
|
||||
#[cfg(curve25519_dalek_backend = "simd")]
|
||||
BackendKind::Avx2 =>
|
||||
self::vector::scalar_mul::pippenger::spec_avx2::Pippenger::optional_multiscalar_mul::<I, J>(scalars, points),
|
||||
#[cfg(all(target_arch = "x86_64", all(feature = "simd_avx512", nightly), curve25519_dalek_bits = "64", not(curve25519_dalek_backend = "fiat")))]
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
BackendKind::Avx512 =>
|
||||
self::vector::scalar_mul::pippenger::spec_avx512ifma_avx512vl::Pippenger::optional_multiscalar_mul::<I, J>(scalars, points),
|
||||
BackendKind::Serial =>
|
||||
@ -123,19 +98,9 @@ where
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub(crate) enum VartimePrecomputedStraus {
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
feature = "simd_avx2",
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(curve25519_dalek_backend = "simd")]
|
||||
Avx2(self::vector::scalar_mul::precomputed_straus::spec_avx2::VartimePrecomputedStraus),
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
all(feature = "simd_avx512", nightly),
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
Avx512ifma(
|
||||
self::vector::scalar_mul::precomputed_straus::spec_avx512ifma_avx512vl::VartimePrecomputedStraus,
|
||||
),
|
||||
@ -152,10 +117,10 @@ impl VartimePrecomputedStraus {
|
||||
use crate::traits::VartimePrecomputedMultiscalarMul;
|
||||
|
||||
match get_selected_backend() {
|
||||
#[cfg(all(target_arch = "x86_64", feature = "simd_avx2", curve25519_dalek_bits = "64", not(curve25519_dalek_backend = "fiat")))]
|
||||
#[cfg(curve25519_dalek_backend = "simd")]
|
||||
BackendKind::Avx2 =>
|
||||
VartimePrecomputedStraus::Avx2(self::vector::scalar_mul::precomputed_straus::spec_avx2::VartimePrecomputedStraus::new(static_points)),
|
||||
#[cfg(all(target_arch = "x86_64", all(feature = "simd_avx512", nightly), curve25519_dalek_bits = "64", not(curve25519_dalek_backend = "fiat")))]
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
BackendKind::Avx512 =>
|
||||
VartimePrecomputedStraus::Avx512ifma(self::vector::scalar_mul::precomputed_straus::spec_avx512ifma_avx512vl::VartimePrecomputedStraus::new(static_points)),
|
||||
BackendKind::Serial =>
|
||||
@ -179,23 +144,13 @@ impl VartimePrecomputedStraus {
|
||||
use crate::traits::VartimePrecomputedMultiscalarMul;
|
||||
|
||||
match self {
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
feature = "simd_avx2",
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(curve25519_dalek_backend = "simd")]
|
||||
VartimePrecomputedStraus::Avx2(inner) => inner.optional_mixed_multiscalar_mul(
|
||||
static_scalars,
|
||||
dynamic_scalars,
|
||||
dynamic_points,
|
||||
),
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
all(feature = "simd_avx512", nightly),
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
VartimePrecomputedStraus::Avx512ifma(inner) => inner.optional_mixed_multiscalar_mul(
|
||||
static_scalars,
|
||||
dynamic_scalars,
|
||||
@ -222,23 +177,13 @@ where
|
||||
use crate::traits::MultiscalarMul;
|
||||
|
||||
match get_selected_backend() {
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
feature = "simd_avx2",
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(curve25519_dalek_backend = "simd")]
|
||||
BackendKind::Avx2 => {
|
||||
self::vector::scalar_mul::straus::spec_avx2::Straus::multiscalar_mul::<I, J>(
|
||||
scalars, points,
|
||||
)
|
||||
}
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
all(feature = "simd_avx512", nightly),
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
BackendKind::Avx512 => {
|
||||
self::vector::scalar_mul::straus::spec_avx512ifma_avx512vl::Straus::multiscalar_mul::<
|
||||
I,
|
||||
@ -262,23 +207,13 @@ where
|
||||
use crate::traits::VartimeMultiscalarMul;
|
||||
|
||||
match get_selected_backend() {
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
feature = "simd_avx2",
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(curve25519_dalek_backend = "simd")]
|
||||
BackendKind::Avx2 => {
|
||||
self::vector::scalar_mul::straus::spec_avx2::Straus::optional_multiscalar_mul::<I, J>(
|
||||
scalars, points,
|
||||
)
|
||||
}
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
all(feature = "simd_avx512", nightly),
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
BackendKind::Avx512 => {
|
||||
self::vector::scalar_mul::straus::spec_avx512ifma_avx512vl::Straus::optional_multiscalar_mul::<
|
||||
I,
|
||||
@ -296,19 +231,9 @@ where
|
||||
/// Perform constant-time, variable-base scalar multiplication.
|
||||
pub fn variable_base_mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint {
|
||||
match get_selected_backend() {
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
feature = "simd_avx2",
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(curve25519_dalek_backend = "simd")]
|
||||
BackendKind::Avx2 => self::vector::scalar_mul::variable_base::spec_avx2::mul(point, scalar),
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
all(feature = "simd_avx512", nightly),
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
BackendKind::Avx512 => {
|
||||
self::vector::scalar_mul::variable_base::spec_avx512ifma_avx512vl::mul(point, scalar)
|
||||
}
|
||||
@ -320,19 +245,9 @@ pub fn variable_base_mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint
|
||||
#[allow(non_snake_case)]
|
||||
pub fn vartime_double_base_mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
|
||||
match get_selected_backend() {
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
feature = "simd_avx2",
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(curve25519_dalek_backend = "simd")]
|
||||
BackendKind::Avx2 => self::vector::scalar_mul::vartime_double_base::spec_avx2::mul(a, A, b),
|
||||
#[cfg(all(
|
||||
target_arch = "x86_64",
|
||||
all(feature = "simd_avx512", nightly),
|
||||
curve25519_dalek_bits = "64",
|
||||
not(curve25519_dalek_backend = "fiat")
|
||||
))]
|
||||
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||
BackendKind::Avx512 => {
|
||||
self::vector::scalar_mul::vartime_double_base::spec_avx512ifma_avx512vl::mul(a, A, b)
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ use core::ops::{Add, Neg, Sub};
|
||||
use subtle::Choice;
|
||||
use subtle::ConditionallySelectable;
|
||||
|
||||
use unsafe_target_feature::unsafe_target_feature;
|
||||
use curve25519_dalek_derive::unsafe_target_feature;
|
||||
|
||||
use crate::edwards;
|
||||
use crate::window::{LookupTable, NafLookupTable5};
|
||||
|
@ -48,7 +48,7 @@ use crate::backend::vector::avx2::constants::{
|
||||
P_TIMES_16_HI, P_TIMES_16_LO, P_TIMES_2_HI, P_TIMES_2_LO,
|
||||
};
|
||||
|
||||
use unsafe_target_feature::unsafe_target_feature;
|
||||
use curve25519_dalek_derive::unsafe_target_feature;
|
||||
|
||||
/// Unpack 32-bit lanes into 64-bit lanes:
|
||||
/// ```ascii,no_run
|
||||
|
@ -16,7 +16,7 @@ use core::ops::{Add, Neg, Sub};
|
||||
use subtle::Choice;
|
||||
use subtle::ConditionallySelectable;
|
||||
|
||||
use unsafe_target_feature::unsafe_target_feature;
|
||||
use curve25519_dalek_derive::unsafe_target_feature;
|
||||
|
||||
use crate::edwards;
|
||||
use crate::window::{LookupTable, NafLookupTable5};
|
||||
|
@ -16,7 +16,7 @@ use core::ops::{Add, Mul, Neg};
|
||||
|
||||
use crate::backend::serial::u64::field::FieldElement51;
|
||||
|
||||
use unsafe_target_feature::unsafe_target_feature;
|
||||
use curve25519_dalek_derive::unsafe_target_feature;
|
||||
|
||||
/// A wrapper around `vpmadd52luq` that works on `u64x4`.
|
||||
#[unsafe_target_feature("avx512ifma,avx512vl")]
|
||||
|
@ -14,10 +14,9 @@
|
||||
#[allow(missing_docs)]
|
||||
pub mod packed_simd;
|
||||
|
||||
#[cfg(feature = "simd_avx2")]
|
||||
pub mod avx2;
|
||||
|
||||
#[cfg(all(feature = "simd_avx512", nightly))]
|
||||
#[cfg(nightly)]
|
||||
pub mod ifma;
|
||||
|
||||
pub mod scalar_mul;
|
||||
|
@ -11,7 +11,7 @@
|
||||
//! by the callers of this code.
|
||||
use core::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitXor, BitXorAssign, Sub};
|
||||
|
||||
use unsafe_target_feature::unsafe_target_feature;
|
||||
use curve25519_dalek_derive::unsafe_target_feature;
|
||||
|
||||
macro_rules! impl_shared {
|
||||
(
|
||||
|
@ -9,9 +9,9 @@
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
#[unsafe_target_feature::unsafe_target_feature_specialize(
|
||||
conditional("avx2", feature = "simd_avx2"),
|
||||
conditional("avx512ifma,avx512vl", all(feature = "simd_avx512", nightly))
|
||||
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
|
||||
"avx2",
|
||||
conditional("avx512ifma,avx512vl", nightly)
|
||||
)]
|
||||
pub mod spec {
|
||||
|
||||
|
@ -11,9 +11,9 @@
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
#[unsafe_target_feature::unsafe_target_feature_specialize(
|
||||
conditional("avx2", feature = "simd_avx2"),
|
||||
conditional("avx512ifma,avx512vl", all(feature = "simd_avx512", nightly))
|
||||
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
|
||||
"avx2",
|
||||
conditional("avx512ifma,avx512vl", nightly)
|
||||
)]
|
||||
pub mod spec {
|
||||
|
||||
|
@ -11,9 +11,9 @@
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
#[unsafe_target_feature::unsafe_target_feature_specialize(
|
||||
conditional("avx2", feature = "simd_avx2"),
|
||||
conditional("avx512ifma,avx512vl", all(feature = "simd_avx512", nightly))
|
||||
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
|
||||
"avx2",
|
||||
conditional("avx512ifma,avx512vl", nightly)
|
||||
)]
|
||||
pub mod spec {
|
||||
|
||||
@ -22,6 +22,7 @@ pub mod spec {
|
||||
use core::borrow::Borrow;
|
||||
use core::cmp::Ordering;
|
||||
|
||||
#[cfg(feature = "zeroize")]
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
#[for_target_feature("avx2")]
|
||||
@ -67,12 +68,13 @@ pub mod spec {
|
||||
.map(|s| s.borrow().as_radix_16())
|
||||
.collect();
|
||||
// Pass ownership to a `Zeroizing` wrapper
|
||||
let scalar_digits = Zeroizing::new(scalar_digits_vec);
|
||||
#[cfg(feature = "zeroize")]
|
||||
let scalar_digits_vec = Zeroizing::new(scalar_digits_vec);
|
||||
|
||||
let mut Q = ExtendedPoint::identity();
|
||||
for j in (0..64).rev() {
|
||||
Q = Q.mul_by_pow_2(4);
|
||||
let it = scalar_digits.iter().zip(lookup_tables.iter());
|
||||
let it = scalar_digits_vec.iter().zip(lookup_tables.iter());
|
||||
for (s_i, lookup_table_i) in it {
|
||||
// Q = Q + s_{i,j} * P_i
|
||||
Q = &Q + &lookup_table_i.select(s_i[j]);
|
||||
|
@ -1,8 +1,8 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
#[unsafe_target_feature::unsafe_target_feature_specialize(
|
||||
conditional("avx2", feature = "simd_avx2"),
|
||||
conditional("avx512ifma,avx512vl", all(feature = "simd_avx512", nightly))
|
||||
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
|
||||
"avx2",
|
||||
conditional("avx512ifma,avx512vl", nightly)
|
||||
)]
|
||||
pub mod spec {
|
||||
|
||||
|
@ -11,9 +11,9 @@
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
#[unsafe_target_feature::unsafe_target_feature_specialize(
|
||||
conditional("avx2", feature = "simd_avx2"),
|
||||
conditional("avx512ifma,avx512vl", all(feature = "simd_avx512", nightly))
|
||||
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
|
||||
"avx2",
|
||||
conditional("avx512ifma,avx512vl", nightly)
|
||||
)]
|
||||
pub mod spec {
|
||||
|
||||
|
25
src/diagnostics.rs
Normal file
25
src/diagnostics.rs
Normal file
@ -0,0 +1,25 @@
|
||||
//! Build time diagnostics
|
||||
|
||||
// auto is assumed or selected
|
||||
#[cfg(curve25519_dalek_backend = "auto")]
|
||||
compile_error!("curve25519_dalek_backend is 'auto'");
|
||||
|
||||
// fiat was overriden
|
||||
#[cfg(curve25519_dalek_backend = "fiat")]
|
||||
compile_error!("curve25519_dalek_backend is 'fiat'");
|
||||
|
||||
// serial was assumed or overriden
|
||||
#[cfg(curve25519_dalek_backend = "serial")]
|
||||
compile_error!("curve25519_dalek_backend is 'serial'");
|
||||
|
||||
// simd was assumed over overriden
|
||||
#[cfg(curve25519_dalek_backend = "simd")]
|
||||
compile_error!("curve25519_dalek_backend is 'simd'");
|
||||
|
||||
// 32 bits target_pointer_width was assumed or overriden
|
||||
#[cfg(curve25519_dalek_bits = "32")]
|
||||
compile_error!("curve25519_dalek_bits is '32'");
|
||||
|
||||
// 64 bits target_pointer_width was assumed or overriden
|
||||
#[cfg(curve25519_dalek_bits = "64")]
|
||||
compile_error!("curve25519_dalek_bits is '64'");
|
11
src/lib.rs
11
src/lib.rs
@ -10,12 +10,9 @@
|
||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||
|
||||
#![no_std]
|
||||
#![cfg_attr(all(curve25519_dalek_backend = "simd", nightly), feature(stdsimd))]
|
||||
#![cfg_attr(
|
||||
all(target_arch = "x86_64", feature = "simd_avx512", nightly),
|
||||
feature(stdsimd)
|
||||
)]
|
||||
#![cfg_attr(
|
||||
all(target_arch = "x86_64", feature = "simd_avx512", nightly),
|
||||
all(curve25519_dalek_backend = "simd", nightly),
|
||||
feature(avx512_target_feature)
|
||||
)]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))]
|
||||
@ -92,3 +89,7 @@ pub(crate) mod window;
|
||||
pub use crate::{
|
||||
edwards::EdwardsPoint, montgomery::MontgomeryPoint, ristretto::RistrettoPoint, scalar::Scalar,
|
||||
};
|
||||
|
||||
// Build time diagnostics for validation
|
||||
#[cfg(curve25519_dalek_diagnostics = "build")]
|
||||
mod diagnostics;
|
||||
|
88
tests/build_tests.sh
Executable file
88
tests/build_tests.sh
Executable file
@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
|
||||
function match_and_report() {
|
||||
PATTERN=$1
|
||||
FILE=$2
|
||||
|
||||
if grep -q "$PATTERN" "$FILE"; then
|
||||
echo build OK "$FILE" : "$PATTERN"
|
||||
else
|
||||
echo build ERROR "$FILE" : "$PATTERN"
|
||||
echo ">>>>>>>>>>>>>>>>>>>>>>>>>>"
|
||||
cat "$FILE"
|
||||
echo "<<<<<<<<<<<<<<<<<<<<<<<<<<"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Assuming naively 64 bit host
|
||||
cargo clean
|
||||
OUT=build_1.txt
|
||||
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\"" cargo build > "$OUT" 2>&1
|
||||
match_and_report "curve25519_dalek_backend is 'simd'" "$OUT"
|
||||
match_and_report "curve25519_dalek_bits is '64'" "$OUT"
|
||||
|
||||
# Override to 32 bits assuming naively 64 bit build host
|
||||
cargo clean
|
||||
OUT=build_2.txt
|
||||
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_bits=\"32\"" cargo build > "$OUT" 2>&1
|
||||
match_and_report "curve25519_dalek_backend is 'serial'" "$OUT"
|
||||
match_and_report "curve25519_dalek_bits is '32'" "$OUT"
|
||||
|
||||
# Override to 64 bits on 32 bit target
|
||||
cargo clean
|
||||
OUT=build_3.txt
|
||||
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_bits=\"64\"" cargo build --target i686-unknown-linux-gnu > "$OUT" 2>&1
|
||||
match_and_report "curve25519_dalek_backend is 'serial'" "$OUT"
|
||||
match_and_report "curve25519_dalek_bits is '64'" "$OUT"
|
||||
|
||||
# 32 bit target default
|
||||
cargo clean
|
||||
OUT=build_4.txt
|
||||
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\"" cargo build --target i686-unknown-linux-gnu > "$OUT" 2>&1
|
||||
match_and_report "curve25519_dalek_backend is 'serial'" "$OUT"
|
||||
match_and_report "curve25519_dalek_bits is '32'" "$OUT"
|
||||
|
||||
# wasm 32 bit target default
|
||||
cargo clean
|
||||
OUT=build_5.txt
|
||||
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\"" cargo build --target wasm32-unknown-unknown > "$OUT" 2>&1
|
||||
match_and_report "curve25519_dalek_backend is 'serial'" "$OUT"
|
||||
match_and_report "curve25519_dalek_bits is '32'" "$OUT"
|
||||
|
||||
# wasm 32 bit target default
|
||||
# Attempted override w/ "simd" should result "serial" addition
|
||||
cargo clean
|
||||
OUT=build_5_1.txt
|
||||
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_backend=\"simd\"" cargo build --target wasm32-unknown-unknown > "$OUT" 2>&1
|
||||
# This overide must fail the compilation since "simd" is not available
|
||||
# See: issues/532
|
||||
match_and_report "Could not override curve25519_dalek_backend to simd" "$OUT"
|
||||
|
||||
# fiat override with default 64 bit naive host assumption
|
||||
cargo clean
|
||||
OUT=build_6.txt
|
||||
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_backend=\"fiat\"" cargo build > "$OUT" 2>&1
|
||||
match_and_report "curve25519_dalek_backend is 'fiat'" "$OUT"
|
||||
match_and_report "curve25519_dalek_bits is '64'" "$OUT"
|
||||
|
||||
# fiat 32 bit override
|
||||
cargo clean
|
||||
OUT=build_7.txt
|
||||
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_backend=\"fiat\" --cfg curve25519_dalek_bits=\"32\"" cargo build > "$OUT" 2>&1
|
||||
match_and_report "curve25519_dalek_backend is 'fiat'" "$OUT"
|
||||
match_and_report "curve25519_dalek_bits is '32'" "$OUT"
|
||||
|
||||
# serial override with default 64 bit naive host assumption
|
||||
cargo clean
|
||||
OUT=build_8.txt
|
||||
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_backend=\"serial\"" cargo build > "$OUT" 2>&1
|
||||
match_and_report "curve25519_dalek_backend is 'serial'" "$OUT"
|
||||
match_and_report "curve25519_dalek_bits is '64'" "$OUT"
|
||||
|
||||
# serial 32 bit override
|
||||
cargo clean
|
||||
OUT=build_9.txt
|
||||
env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_backend=\"serial\" --cfg curve25519_dalek_bits=\"32\"" cargo build > "$OUT" 2>&1
|
||||
match_and_report "curve25519_dalek_backend is 'serial'" "$OUT"
|
||||
match_and_report "curve25519_dalek_bits is '32'" "$OUT"
|
Loading…
x
Reference in New Issue
Block a user