mirror of
https://github.com/saymrwulf/risc0-curve25519-dalek-source.git
synced 2026-09-04 20:03:40 +00:00
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
24 changed files with 1124 additions and 193 deletions
65
.github/workflows/rust.yml
vendored
65
.github/workflows/rust.yml
vendored
|
|
@ -11,7 +11,7 @@ env:
|
||||||
RUSTFLAGS: '-D warnings'
|
RUSTFLAGS: '-D warnings'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test-auto:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
|
@ -38,10 +38,58 @@ jobs:
|
||||||
- run: cargo test --target ${{ matrix.target }} --features digest
|
- run: cargo test --target ${{ matrix.target }} --features digest
|
||||||
- run: cargo test --target ${{ matrix.target }} --features rand_core
|
- run: cargo test --target ${{ matrix.target }} --features rand_core
|
||||||
- run: cargo test --target ${{ matrix.target }} --features serde
|
- 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:
|
- env:
|
||||||
RUSTFLAGS: '--cfg curve25519_dalek_backend="fiat"'
|
RUSTFLAGS: '--cfg curve25519_dalek_backend="fiat"'
|
||||||
run: cargo test --target ${{ matrix.target }}
|
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:
|
build-nostd:
|
||||||
name: Build on no_std target (thumbv7em-none-eabi)
|
name: Build on no_std target (thumbv7em-none-eabi)
|
||||||
runs-on: ubuntu-latest
|
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
|
||||||
- run: cargo build --target thumbv7em-none-eabi --release --features serde
|
- run: cargo build --target thumbv7em-none-eabi --release --features serde
|
||||||
|
|
||||||
test-simd-native:
|
test-simd-nightly:
|
||||||
name: Test simd backend (native)
|
name: Test simd backend (nightly)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
@ -66,11 +114,12 @@ jobs:
|
||||||
# 1) build all of the x86_64 SIMD code,
|
# 1) build all of the x86_64 SIMD code,
|
||||||
# 2) run all of the SIMD-specific tests that the test runner supports,
|
# 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.
|
# 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'
|
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:
|
test-simd-stable:
|
||||||
name: Test simd backend (avx2)
|
name: Test simd backend (stable)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
@ -78,8 +127,10 @@ jobs:
|
||||||
- env:
|
- env:
|
||||||
# This will run AVX2-specific tests and run all of the normal tests
|
# This will run AVX2-specific tests and run all of the normal tests
|
||||||
# with the AVX2 backend, even if the runner supports AVX512.
|
# 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'
|
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:
|
build-docs:
|
||||||
name: Build docs
|
name: Build docs
|
||||||
|
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,6 +1,8 @@
|
||||||
|
*/target/*
|
||||||
target
|
target
|
||||||
Cargo.lock
|
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 }
|
subtle = { version = "2.3.0", default-features = false }
|
||||||
serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] }
|
serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] }
|
||||||
zeroize = { version = "1", default-features = false, optional = true }
|
zeroize = { version = "1", default-features = false, optional = true }
|
||||||
unsafe_target_feature = { version = "= 0.1.1", optional = true }
|
|
||||||
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||||
cpufeatures = "0.2.6"
|
cpufeatures = "0.2.6"
|
||||||
|
|
@ -62,20 +61,13 @@ cpufeatures = "0.2.6"
|
||||||
fiat-crypto = "0.1.19"
|
fiat-crypto = "0.1.19"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["alloc", "precomputed-tables", "zeroize", "simd"]
|
default = ["alloc", "precomputed-tables", "zeroize"]
|
||||||
alloc = ["zeroize?/alloc"]
|
alloc = ["zeroize?/alloc"]
|
||||||
precomputed-tables = []
|
precomputed-tables = []
|
||||||
legacy_compatibility = []
|
legacy_compatibility = []
|
||||||
|
|
||||||
# Whether to allow the use of the AVX2 SIMD backend.
|
[target.'cfg(all(not(curve25519_dalek_backend = "fiat"), not(curve25519_dalek_backend = "serial"), target_arch = "x86_64"))'.dependencies]
|
||||||
simd_avx2 = ["unsafe_target_feature"]
|
curve25519-dalek-derive = { version = "0.1", path = "curve25519-dalek-derive" }
|
||||||
|
|
||||||
# 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"]
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 2
|
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`. |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `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.
|
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
|
# 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 |
|
| Backend | Selection | Implementation | Bits / Word sizes |
|
||||||
| :--- | :--- | :--- |
|
| :--- | :--- | :--- | :--- |
|
||||||
| `[default]` | Automatic runtime backend selection (either serial or SIMD) | `u32` <br/> `u64` <br/> `avx2` <br/> `avx512` |
|
| `serial` | Automatic | An optimized, non-parllel implementation | `32` and `64` |
|
||||||
| `fiat` | Formally verified field arithmetic from [fiat-crypto] | `fiat_u32` <br/> `fiat_u64` |
|
| `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
|
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.
|
||||||
environment variable:
|
|
||||||
|
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
|
```sh
|
||||||
RUSTFLAGS='--cfg curve25519_dalek_backend="BACKEND"'
|
RUSTFLAGS='--cfg curve25519_dalek_backend="BACKEND"'
|
||||||
```
|
```
|
||||||
where `BACKEND` is `fiat`. Equivalently, you can write to
|
Equivalently, you can write to
|
||||||
`~/.cargo/config`:
|
`~/.cargo/config`:
|
||||||
```toml
|
```toml
|
||||||
[build]
|
[build]
|
||||||
|
|
@ -117,53 +116,49 @@ rustflags = ['--cfg=curve25519_dalek_backend="BACKEND"']
|
||||||
More info [here](https://doc.rust-lang.org/cargo/reference/config.html#buildrustflags).
|
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
|
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.
|
function.
|
||||||
|
|
||||||
## Word size for serial backends
|
## Bits / Word size
|
||||||
|
|
||||||
`curve25519-dalek` will automatically choose the word size for the `[default]`
|
`curve25519-dalek` will automatically choose the word size for the `fiat` and
|
||||||
and `fiat` serial backends, based on the build target. For example, building
|
`serial` backends, based on the build target.
|
||||||
for a 64-bit machine, the default `u64` target backend is automatically chosen
|
For example, building for a 64-bit machine, the default 64 bit word size is
|
||||||
when the `[default]` backend is selected, and `fiat_u64` is chosen when the
|
automatically chosen when either the `serial` or `fiat` backend is selected.
|
||||||
`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:
|
environment variable:
|
||||||
```sh
|
```sh
|
||||||
RUSTFLAGS='--cfg curve25519_dalek_bits="SIZE"'
|
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`.
|
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
|
### Cross-compilation
|
||||||
|
|
||||||
Because backend selection is done by target, cross-compiling will select the
|
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.
|
||||||
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:
|
|
||||||
```console
|
```console
|
||||||
$ sudo apt install gcc-multilib # (or whatever package manager you use)
|
$ sudo apt install gcc-multilib # (or whatever package manager you use)
|
||||||
$ rustup target add i686-unknown-linux-gnu
|
$ rustup target add i686-unknown-linux-gnu
|
||||||
$ cargo build --target 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
|
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.
|
||||||
on the available CPU features, provided the appropriate feature flag is enabled.
|
|
||||||
|
|
||||||
You can also specify an appropriate `-C target_feature` to build a binary
|
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.
|
||||||
which assumes the required SIMD instructions are always available.
|
|
||||||
|
|
||||||
| Backend | Feature flag | `RUSTFLAGS` | Requires nightly? |
|
| Backend | `RUSTFLAGS` | Requires nightly? |
|
||||||
| :--- | :--- | :--- | :--- |
|
| :--- | :--- | :--- |
|
||||||
| avx2 | `simd_avx2` | `-C target_feature=+avx2` | no |
|
| avx2 | `-C target_feature=+avx2` | no |
|
||||||
| avx512 | `simd_avx512` | `-C target_feature=+avx512ifma,+avx512vl` | yes |
|
| avx512 | `-C target_feature=+avx512ifma,+avx512vl` | yes |
|
||||||
|
|
||||||
The AVX512 backend requires Rust nightly. When compiled on a non-nightly
|
If compiled on a non-nightly compiler, `curve25519-dalek` will not include AVX512 code, and therefore will never select it at runtime.
|
||||||
compiler it will always be disabled.
|
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
|
|
||||||
|
|
@ -326,3 +321,4 @@ contributions.
|
||||||
[semver]: https://semver.org/spec/v2.0.0.html
|
[semver]: https://semver.org/spec/v2.0.0.html
|
||||||
[rngcorestd]: https://github.com/rust-random/rand/tree/7aa25d577e2df84a5156f824077bb7f6bdf28d97/rand_core#crate-features
|
[rngcorestd]: https://github.com/rust-random/rand/tree/7aa25d577e2df84a5156f824077bb7f6bdf28d97/rand_core#crate-features
|
||||||
[zeroize-trait]: https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html
|
[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)]
|
#![deny(clippy::unwrap_used, dead_code)]
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
enum DalekBits {
|
enum DalekBits {
|
||||||
Dalek32,
|
Dalek32,
|
||||||
Dalek64,
|
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.
|
// 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");
|
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.
|
// 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;
|
pub mod serial;
|
||||||
|
|
||||||
#[cfg(all(
|
#[cfg(curve25519_dalek_backend = "simd")]
|
||||||
target_arch = "x86_64",
|
|
||||||
any(feature = "simd_avx2", all(feature = "simd_avx512", nightly)),
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
pub mod vector;
|
pub mod vector;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
enum BackendKind {
|
enum BackendKind {
|
||||||
#[cfg(all(
|
#[cfg(curve25519_dalek_backend = "simd")]
|
||||||
target_arch = "x86_64",
|
|
||||||
feature = "simd_avx2",
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
Avx2,
|
Avx2,
|
||||||
#[cfg(all(
|
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||||
target_arch = "x86_64",
|
|
||||||
all(feature = "simd_avx512", nightly),
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
Avx512,
|
Avx512,
|
||||||
Serial,
|
Serial,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_selected_backend() -> BackendKind {
|
fn get_selected_backend() -> BackendKind {
|
||||||
#[cfg(all(
|
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||||
target_arch = "x86_64",
|
|
||||||
all(feature = "simd_avx512", nightly),
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
{
|
{
|
||||||
cpufeatures::new!(cpuid_avx512, "avx512ifma", "avx512vl");
|
cpufeatures::new!(cpuid_avx512, "avx512ifma", "avx512vl");
|
||||||
let token_avx512: cpuid_avx512::InitToken = cpuid_avx512::init();
|
let token_avx512: cpuid_avx512::InitToken = cpuid_avx512::init();
|
||||||
|
|
@ -82,12 +62,7 @@ fn get_selected_backend() -> BackendKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(
|
#[cfg(curve25519_dalek_backend = "simd")]
|
||||||
target_arch = "x86_64",
|
|
||||||
feature = "simd_avx2",
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
{
|
{
|
||||||
cpufeatures::new!(cpuid_avx2, "avx2");
|
cpufeatures::new!(cpuid_avx2, "avx2");
|
||||||
let token_avx2: cpuid_avx2::InitToken = cpuid_avx2::init();
|
let token_avx2: cpuid_avx2::InitToken = cpuid_avx2::init();
|
||||||
|
|
@ -110,10 +85,10 @@ where
|
||||||
use crate::traits::VartimeMultiscalarMul;
|
use crate::traits::VartimeMultiscalarMul;
|
||||||
|
|
||||||
match get_selected_backend() {
|
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 =>
|
BackendKind::Avx2 =>
|
||||||
self::vector::scalar_mul::pippenger::spec_avx2::Pippenger::optional_multiscalar_mul::<I, J>(scalars, points),
|
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 =>
|
BackendKind::Avx512 =>
|
||||||
self::vector::scalar_mul::pippenger::spec_avx512ifma_avx512vl::Pippenger::optional_multiscalar_mul::<I, J>(scalars, points),
|
self::vector::scalar_mul::pippenger::spec_avx512ifma_avx512vl::Pippenger::optional_multiscalar_mul::<I, J>(scalars, points),
|
||||||
BackendKind::Serial =>
|
BackendKind::Serial =>
|
||||||
|
|
@ -123,19 +98,9 @@ where
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
pub(crate) enum VartimePrecomputedStraus {
|
pub(crate) enum VartimePrecomputedStraus {
|
||||||
#[cfg(all(
|
#[cfg(curve25519_dalek_backend = "simd")]
|
||||||
target_arch = "x86_64",
|
|
||||||
feature = "simd_avx2",
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
Avx2(self::vector::scalar_mul::precomputed_straus::spec_avx2::VartimePrecomputedStraus),
|
Avx2(self::vector::scalar_mul::precomputed_straus::spec_avx2::VartimePrecomputedStraus),
|
||||||
#[cfg(all(
|
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||||
target_arch = "x86_64",
|
|
||||||
all(feature = "simd_avx512", nightly),
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
Avx512ifma(
|
Avx512ifma(
|
||||||
self::vector::scalar_mul::precomputed_straus::spec_avx512ifma_avx512vl::VartimePrecomputedStraus,
|
self::vector::scalar_mul::precomputed_straus::spec_avx512ifma_avx512vl::VartimePrecomputedStraus,
|
||||||
),
|
),
|
||||||
|
|
@ -152,10 +117,10 @@ impl VartimePrecomputedStraus {
|
||||||
use crate::traits::VartimePrecomputedMultiscalarMul;
|
use crate::traits::VartimePrecomputedMultiscalarMul;
|
||||||
|
|
||||||
match get_selected_backend() {
|
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 =>
|
BackendKind::Avx2 =>
|
||||||
VartimePrecomputedStraus::Avx2(self::vector::scalar_mul::precomputed_straus::spec_avx2::VartimePrecomputedStraus::new(static_points)),
|
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 =>
|
BackendKind::Avx512 =>
|
||||||
VartimePrecomputedStraus::Avx512ifma(self::vector::scalar_mul::precomputed_straus::spec_avx512ifma_avx512vl::VartimePrecomputedStraus::new(static_points)),
|
VartimePrecomputedStraus::Avx512ifma(self::vector::scalar_mul::precomputed_straus::spec_avx512ifma_avx512vl::VartimePrecomputedStraus::new(static_points)),
|
||||||
BackendKind::Serial =>
|
BackendKind::Serial =>
|
||||||
|
|
@ -179,23 +144,13 @@ impl VartimePrecomputedStraus {
|
||||||
use crate::traits::VartimePrecomputedMultiscalarMul;
|
use crate::traits::VartimePrecomputedMultiscalarMul;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
#[cfg(all(
|
#[cfg(curve25519_dalek_backend = "simd")]
|
||||||
target_arch = "x86_64",
|
|
||||||
feature = "simd_avx2",
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
VartimePrecomputedStraus::Avx2(inner) => inner.optional_mixed_multiscalar_mul(
|
VartimePrecomputedStraus::Avx2(inner) => inner.optional_mixed_multiscalar_mul(
|
||||||
static_scalars,
|
static_scalars,
|
||||||
dynamic_scalars,
|
dynamic_scalars,
|
||||||
dynamic_points,
|
dynamic_points,
|
||||||
),
|
),
|
||||||
#[cfg(all(
|
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||||
target_arch = "x86_64",
|
|
||||||
all(feature = "simd_avx512", nightly),
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
VartimePrecomputedStraus::Avx512ifma(inner) => inner.optional_mixed_multiscalar_mul(
|
VartimePrecomputedStraus::Avx512ifma(inner) => inner.optional_mixed_multiscalar_mul(
|
||||||
static_scalars,
|
static_scalars,
|
||||||
dynamic_scalars,
|
dynamic_scalars,
|
||||||
|
|
@ -222,23 +177,13 @@ where
|
||||||
use crate::traits::MultiscalarMul;
|
use crate::traits::MultiscalarMul;
|
||||||
|
|
||||||
match get_selected_backend() {
|
match get_selected_backend() {
|
||||||
#[cfg(all(
|
#[cfg(curve25519_dalek_backend = "simd")]
|
||||||
target_arch = "x86_64",
|
|
||||||
feature = "simd_avx2",
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
BackendKind::Avx2 => {
|
BackendKind::Avx2 => {
|
||||||
self::vector::scalar_mul::straus::spec_avx2::Straus::multiscalar_mul::<I, J>(
|
self::vector::scalar_mul::straus::spec_avx2::Straus::multiscalar_mul::<I, J>(
|
||||||
scalars, points,
|
scalars, points,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
#[cfg(all(
|
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||||
target_arch = "x86_64",
|
|
||||||
all(feature = "simd_avx512", nightly),
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
BackendKind::Avx512 => {
|
BackendKind::Avx512 => {
|
||||||
self::vector::scalar_mul::straus::spec_avx512ifma_avx512vl::Straus::multiscalar_mul::<
|
self::vector::scalar_mul::straus::spec_avx512ifma_avx512vl::Straus::multiscalar_mul::<
|
||||||
I,
|
I,
|
||||||
|
|
@ -262,23 +207,13 @@ where
|
||||||
use crate::traits::VartimeMultiscalarMul;
|
use crate::traits::VartimeMultiscalarMul;
|
||||||
|
|
||||||
match get_selected_backend() {
|
match get_selected_backend() {
|
||||||
#[cfg(all(
|
#[cfg(curve25519_dalek_backend = "simd")]
|
||||||
target_arch = "x86_64",
|
|
||||||
feature = "simd_avx2",
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
BackendKind::Avx2 => {
|
BackendKind::Avx2 => {
|
||||||
self::vector::scalar_mul::straus::spec_avx2::Straus::optional_multiscalar_mul::<I, J>(
|
self::vector::scalar_mul::straus::spec_avx2::Straus::optional_multiscalar_mul::<I, J>(
|
||||||
scalars, points,
|
scalars, points,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
#[cfg(all(
|
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||||
target_arch = "x86_64",
|
|
||||||
all(feature = "simd_avx512", nightly),
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
BackendKind::Avx512 => {
|
BackendKind::Avx512 => {
|
||||||
self::vector::scalar_mul::straus::spec_avx512ifma_avx512vl::Straus::optional_multiscalar_mul::<
|
self::vector::scalar_mul::straus::spec_avx512ifma_avx512vl::Straus::optional_multiscalar_mul::<
|
||||||
I,
|
I,
|
||||||
|
|
@ -296,19 +231,9 @@ where
|
||||||
/// Perform constant-time, variable-base scalar multiplication.
|
/// Perform constant-time, variable-base scalar multiplication.
|
||||||
pub fn variable_base_mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint {
|
pub fn variable_base_mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint {
|
||||||
match get_selected_backend() {
|
match get_selected_backend() {
|
||||||
#[cfg(all(
|
#[cfg(curve25519_dalek_backend = "simd")]
|
||||||
target_arch = "x86_64",
|
|
||||||
feature = "simd_avx2",
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
BackendKind::Avx2 => self::vector::scalar_mul::variable_base::spec_avx2::mul(point, scalar),
|
BackendKind::Avx2 => self::vector::scalar_mul::variable_base::spec_avx2::mul(point, scalar),
|
||||||
#[cfg(all(
|
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||||
target_arch = "x86_64",
|
|
||||||
all(feature = "simd_avx512", nightly),
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
BackendKind::Avx512 => {
|
BackendKind::Avx512 => {
|
||||||
self::vector::scalar_mul::variable_base::spec_avx512ifma_avx512vl::mul(point, scalar)
|
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)]
|
#[allow(non_snake_case)]
|
||||||
pub fn vartime_double_base_mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
|
pub fn vartime_double_base_mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
|
||||||
match get_selected_backend() {
|
match get_selected_backend() {
|
||||||
#[cfg(all(
|
#[cfg(curve25519_dalek_backend = "simd")]
|
||||||
target_arch = "x86_64",
|
|
||||||
feature = "simd_avx2",
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
BackendKind::Avx2 => self::vector::scalar_mul::vartime_double_base::spec_avx2::mul(a, A, b),
|
BackendKind::Avx2 => self::vector::scalar_mul::vartime_double_base::spec_avx2::mul(a, A, b),
|
||||||
#[cfg(all(
|
#[cfg(all(curve25519_dalek_backend = "simd", nightly))]
|
||||||
target_arch = "x86_64",
|
|
||||||
all(feature = "simd_avx512", nightly),
|
|
||||||
curve25519_dalek_bits = "64",
|
|
||||||
not(curve25519_dalek_backend = "fiat")
|
|
||||||
))]
|
|
||||||
BackendKind::Avx512 => {
|
BackendKind::Avx512 => {
|
||||||
self::vector::scalar_mul::vartime_double_base::spec_avx512ifma_avx512vl::mul(a, A, b)
|
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::Choice;
|
||||||
use subtle::ConditionallySelectable;
|
use subtle::ConditionallySelectable;
|
||||||
|
|
||||||
use unsafe_target_feature::unsafe_target_feature;
|
use curve25519_dalek_derive::unsafe_target_feature;
|
||||||
|
|
||||||
use crate::edwards;
|
use crate::edwards;
|
||||||
use crate::window::{LookupTable, NafLookupTable5};
|
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,
|
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:
|
/// Unpack 32-bit lanes into 64-bit lanes:
|
||||||
/// ```ascii,no_run
|
/// ```ascii,no_run
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ use core::ops::{Add, Neg, Sub};
|
||||||
use subtle::Choice;
|
use subtle::Choice;
|
||||||
use subtle::ConditionallySelectable;
|
use subtle::ConditionallySelectable;
|
||||||
|
|
||||||
use unsafe_target_feature::unsafe_target_feature;
|
use curve25519_dalek_derive::unsafe_target_feature;
|
||||||
|
|
||||||
use crate::edwards;
|
use crate::edwards;
|
||||||
use crate::window::{LookupTable, NafLookupTable5};
|
use crate::window::{LookupTable, NafLookupTable5};
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ use core::ops::{Add, Mul, Neg};
|
||||||
|
|
||||||
use crate::backend::serial::u64::field::FieldElement51;
|
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`.
|
/// A wrapper around `vpmadd52luq` that works on `u64x4`.
|
||||||
#[unsafe_target_feature("avx512ifma,avx512vl")]
|
#[unsafe_target_feature("avx512ifma,avx512vl")]
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,9 @@
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub mod packed_simd;
|
pub mod packed_simd;
|
||||||
|
|
||||||
#[cfg(feature = "simd_avx2")]
|
|
||||||
pub mod avx2;
|
pub mod avx2;
|
||||||
|
|
||||||
#[cfg(all(feature = "simd_avx512", nightly))]
|
#[cfg(nightly)]
|
||||||
pub mod ifma;
|
pub mod ifma;
|
||||||
|
|
||||||
pub mod scalar_mul;
|
pub mod scalar_mul;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
//! by the callers of this code.
|
//! by the callers of this code.
|
||||||
use core::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitXor, BitXorAssign, Sub};
|
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 {
|
macro_rules! impl_shared {
|
||||||
(
|
(
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@
|
||||||
|
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
#[unsafe_target_feature::unsafe_target_feature_specialize(
|
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
|
||||||
conditional("avx2", feature = "simd_avx2"),
|
"avx2",
|
||||||
conditional("avx512ifma,avx512vl", all(feature = "simd_avx512", nightly))
|
conditional("avx512ifma,avx512vl", nightly)
|
||||||
)]
|
)]
|
||||||
pub mod spec {
|
pub mod spec {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@
|
||||||
|
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
#[unsafe_target_feature::unsafe_target_feature_specialize(
|
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
|
||||||
conditional("avx2", feature = "simd_avx2"),
|
"avx2",
|
||||||
conditional("avx512ifma,avx512vl", all(feature = "simd_avx512", nightly))
|
conditional("avx512ifma,avx512vl", nightly)
|
||||||
)]
|
)]
|
||||||
pub mod spec {
|
pub mod spec {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@
|
||||||
|
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
#[unsafe_target_feature::unsafe_target_feature_specialize(
|
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
|
||||||
conditional("avx2", feature = "simd_avx2"),
|
"avx2",
|
||||||
conditional("avx512ifma,avx512vl", all(feature = "simd_avx512", nightly))
|
conditional("avx512ifma,avx512vl", nightly)
|
||||||
)]
|
)]
|
||||||
pub mod spec {
|
pub mod spec {
|
||||||
|
|
||||||
|
|
@ -22,6 +22,7 @@ pub mod spec {
|
||||||
use core::borrow::Borrow;
|
use core::borrow::Borrow;
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
|
|
||||||
|
#[cfg(feature = "zeroize")]
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
#[for_target_feature("avx2")]
|
#[for_target_feature("avx2")]
|
||||||
|
|
@ -67,12 +68,13 @@ pub mod spec {
|
||||||
.map(|s| s.borrow().as_radix_16())
|
.map(|s| s.borrow().as_radix_16())
|
||||||
.collect();
|
.collect();
|
||||||
// Pass ownership to a `Zeroizing` wrapper
|
// 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();
|
let mut Q = ExtendedPoint::identity();
|
||||||
for j in (0..64).rev() {
|
for j in (0..64).rev() {
|
||||||
Q = Q.mul_by_pow_2(4);
|
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 {
|
for (s_i, lookup_table_i) in it {
|
||||||
// Q = Q + s_{i,j} * P_i
|
// Q = Q + s_{i,j} * P_i
|
||||||
Q = &Q + &lookup_table_i.select(s_i[j]);
|
Q = &Q + &lookup_table_i.select(s_i[j]);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
#[unsafe_target_feature::unsafe_target_feature_specialize(
|
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
|
||||||
conditional("avx2", feature = "simd_avx2"),
|
"avx2",
|
||||||
conditional("avx512ifma,avx512vl", all(feature = "simd_avx512", nightly))
|
conditional("avx512ifma,avx512vl", nightly)
|
||||||
)]
|
)]
|
||||||
pub mod spec {
|
pub mod spec {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@
|
||||||
|
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
#[unsafe_target_feature::unsafe_target_feature_specialize(
|
#[curve25519_dalek_derive::unsafe_target_feature_specialize(
|
||||||
conditional("avx2", feature = "simd_avx2"),
|
"avx2",
|
||||||
conditional("avx512ifma,avx512vl", all(feature = "simd_avx512", nightly))
|
conditional("avx512ifma,avx512vl", nightly)
|
||||||
)]
|
)]
|
||||||
pub mod spec {
|
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>
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![cfg_attr(all(curve25519_dalek_backend = "simd", nightly), feature(stdsimd))]
|
||||||
#![cfg_attr(
|
#![cfg_attr(
|
||||||
all(target_arch = "x86_64", feature = "simd_avx512", nightly),
|
all(curve25519_dalek_backend = "simd", nightly),
|
||||||
feature(stdsimd)
|
|
||||||
)]
|
|
||||||
#![cfg_attr(
|
|
||||||
all(target_arch = "x86_64", feature = "simd_avx512", nightly),
|
|
||||||
feature(avx512_target_feature)
|
feature(avx512_target_feature)
|
||||||
)]
|
)]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))]
|
||||||
|
|
@ -92,3 +89,7 @@ pub(crate) mod window;
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
edwards::EdwardsPoint, montgomery::MontgomeryPoint, ristretto::RistrettoPoint, scalar::Scalar,
|
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…
Reference in a new issue