diff options
Diffstat (limited to 'thirdparty/BLAKE3/build.rs')
| -rw-r--r-- | thirdparty/BLAKE3/build.rs | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/thirdparty/BLAKE3/build.rs b/thirdparty/BLAKE3/build.rs new file mode 100644 index 000000000..ea657d8db --- /dev/null +++ b/thirdparty/BLAKE3/build.rs @@ -0,0 +1,260 @@ +use std::env; + +fn defined(var: &str) -> bool { + println!("cargo:rerun-if-env-changed={}", var); + env::var_os(var).is_some() +} + +fn is_pure() -> bool { + defined("CARGO_FEATURE_PURE") +} + +fn should_prefer_intrinsics() -> bool { + defined("CARGO_FEATURE_PREFER_INTRINSICS") +} + +fn is_neon() -> bool { + defined("CARGO_FEATURE_NEON") +} + +fn is_ci() -> bool { + defined("BLAKE3_CI") +} + +fn warn(warning: &str) { + assert!(!warning.contains("\n")); + println!("cargo:warning={}", warning); + if is_ci() { + println!("cargo:warning=Warnings in CI are treated as errors. Build failed."); + std::process::exit(1); + } +} + +fn target_components() -> Vec<String> { + let target = env::var("TARGET").unwrap(); + target.split("-").map(|s| s.to_string()).collect() +} + +fn is_x86_64() -> bool { + target_components()[0] == "x86_64" +} + +fn is_x86_32() -> bool { + let arch = &target_components()[0]; + arch == "i386" || arch == "i586" || arch == "i686" +} + +fn is_armv7() -> bool { + target_components()[0] == "armv7" +} + +// Windows targets may be using the MSVC toolchain or the GNU toolchain. The +// right compiler flags to use depend on the toolchain. (And we don't want to +// use flag_if_supported, because we don't want features to be silently +// disabled by old compilers.) +fn is_windows_msvc() -> bool { + // Some targets are only two components long, so check in steps. + target_components()[1] == "pc" + && target_components()[2] == "windows" + && target_components()[3] == "msvc" +} + +fn is_windows_gnu() -> bool { + // Some targets are only two components long, so check in steps. + target_components()[1] == "pc" + && target_components()[2] == "windows" + && target_components()[3] == "gnu" +} + +fn new_build() -> cc::Build { + let mut build = cc::Build::new(); + if !is_windows_msvc() { + build.flag("-std=c11"); + } + build +} + +#[derive(PartialEq)] +enum CCompilerSupport { + NoCompiler, + NoAVX512, + YesAVX512, +} +use CCompilerSupport::*; + +fn c_compiler_support() -> CCompilerSupport { + let build = new_build(); + let flags_checked; + let support_result: Result<bool, _> = if is_windows_msvc() { + flags_checked = "/arch:AVX512"; + build.is_flag_supported("/arch:AVX512") + } else { + // Check for both of the flags we use. If -mavx512f works, then -mavx512vl + // will probably always work too, but we might as well be thorough. + flags_checked = "-mavx512f and -mavx512vl"; + match build.is_flag_supported("-mavx512f") { + Ok(true) => build.is_flag_supported("-mavx512vl"), + false_or_error => false_or_error, + } + }; + match support_result { + Ok(true) => YesAVX512, + Ok(false) => { + warn(&format!( + "The C compiler {:?} does not support {}.", + build.get_compiler().path(), + flags_checked, + )); + NoAVX512 + } + Err(e) => { + println!("{:?}", e); + warn(&format!( + "No C compiler {:?} detected.", + build.get_compiler().path() + )); + NoCompiler + } + } +} + +fn build_sse2_sse41_avx2_rust_intrinsics() { + // No C code to compile here. Set the cfg flags that enable the Rust SSE2, + // SSE4.1, and AVX2 intrinsics modules. The regular Cargo build will compile + // them. + println!("cargo:rustc-cfg=blake3_sse2_rust"); + println!("cargo:rustc-cfg=blake3_sse41_rust"); + println!("cargo:rustc-cfg=blake3_avx2_rust"); +} + +fn build_sse2_sse41_avx2_assembly() { + // Build the assembly implementations for SSE4.1 and AVX2. This is + // preferred, but it only supports x86_64. + assert!(is_x86_64()); + println!("cargo:rustc-cfg=blake3_sse2_ffi"); + println!("cargo:rustc-cfg=blake3_sse41_ffi"); + println!("cargo:rustc-cfg=blake3_avx2_ffi"); + let mut build = new_build(); + if is_windows_msvc() { + build.file("c/blake3_sse2_x86-64_windows_msvc.asm"); + build.file("c/blake3_sse41_x86-64_windows_msvc.asm"); + build.file("c/blake3_avx2_x86-64_windows_msvc.asm"); + } else if is_windows_gnu() { + build.file("c/blake3_sse2_x86-64_windows_gnu.S"); + build.file("c/blake3_sse41_x86-64_windows_gnu.S"); + build.file("c/blake3_avx2_x86-64_windows_gnu.S"); + } else { + // All non-Windows implementations are assumed to support + // Linux-style assembly. These files do contain a small + // explicit workaround for macOS also. + build.file("c/blake3_sse2_x86-64_unix.S"); + build.file("c/blake3_sse41_x86-64_unix.S"); + build.file("c/blake3_avx2_x86-64_unix.S"); + } + build.compile("blake3_sse2_sse41_avx2_assembly"); +} + +fn build_avx512_c_intrinsics() { + // This is required on 32-bit x86 targets, since the assembly + // implementation doesn't support support those. + println!("cargo:rustc-cfg=blake3_avx512_ffi"); + let mut build = new_build(); + build.file("c/blake3_avx512.c"); + if is_windows_msvc() { + build.flag("/arch:AVX512"); + } else { + build.flag("-mavx512f"); + build.flag("-mavx512vl"); + } + if is_windows_gnu() { + // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65782. + build.flag("-fno-asynchronous-unwind-tables"); + } + build.compile("blake3_avx512_intrinsics"); +} + +fn build_avx512_assembly() { + // Build the assembly implementation for AVX-512. This is preferred, but it + // only supports x86_64. + assert!(is_x86_64()); + println!("cargo:rustc-cfg=blake3_avx512_ffi"); + let mut build = new_build(); + if is_windows_msvc() { + build.file("c/blake3_avx512_x86-64_windows_msvc.asm"); + } else { + if is_windows_gnu() { + build.file("c/blake3_avx512_x86-64_windows_gnu.S"); + } else { + // All non-Windows implementations are assumed to support Linux-style + // assembly. These files do contain a small explicit workaround for + // macOS also. + build.file("c/blake3_avx512_x86-64_unix.S"); + } + // Older versions of Clang require these flags, even for assembly. See + // https://github.com/BLAKE3-team/BLAKE3/issues/79. + build.flag("-mavx512f"); + build.flag("-mavx512vl"); + } + build.compile("blake3_avx512_assembly"); +} + +fn build_neon_c_intrinsics() { + let mut build = new_build(); + // Note that blake3_neon.c normally depends on the blake3_portable.c + // for the single-instance compression function, but we expose + // portable.rs over FFI instead. See ffi_neon.rs. + build.file("c/blake3_neon.c"); + // ARMv7 platforms that support NEON generally need the following + // flags. AArch64 supports NEON by default and does not support -mpfu. + if is_armv7() { + build.flag("-mfpu=neon-vfpv4"); + build.flag("-mfloat-abi=hard"); + } + build.compile("blake3_neon"); +} + +fn main() -> Result<(), Box<dyn std::error::Error>> { + if is_pure() && is_neon() { + panic!("It doesn't make sense to enable both \"pure\" and \"neon\"."); + } + + if is_x86_64() || is_x86_32() { + let support = c_compiler_support(); + if is_x86_32() || should_prefer_intrinsics() || is_pure() || support == NoCompiler { + build_sse2_sse41_avx2_rust_intrinsics(); + } else { + // We assume that all C compilers can assemble SSE4.1 and AVX2. We + // don't explicitly check for support. + build_sse2_sse41_avx2_assembly(); + } + + if is_pure() || support == NoCompiler || support == NoAVX512 { + // The binary will not include any AVX-512 code. + } else if is_x86_32() || should_prefer_intrinsics() { + build_avx512_c_intrinsics(); + } else { + build_avx512_assembly(); + } + } + + if is_neon() { + build_neon_c_intrinsics(); + } + + // The `cc` crate doesn't automatically emit rerun-if directives for the + // environment variables it supports, in particular for $CC. We expect to + // do a lot of benchmarking across different compilers, so we explicitly + // add the variables that we're likely to need. + println!("cargo:rerun-if-env-changed=CC"); + println!("cargo:rerun-if-env-changed=CFLAGS"); + + // Ditto for source files, though these shouldn't change as often. + for file in std::fs::read_dir("c")? { + println!( + "cargo:rerun-if-changed={}", + file?.path().to_str().expect("utf-8") + ); + } + + Ok(()) +} |