diff --git a/build.rs b/build.rs index 16cf5119d..1f6324deb 100644 --- a/build.rs +++ b/build.rs @@ -1,202 +1,287 @@ +#![deny(clippy::all)] + #[cfg(feature = "asm")] -use core::panic; -#[cfg(feature = "asm")] -use std::env; -#[cfg(feature = "asm")] -use std::path::Path; +mod asm { + use std::env; + use std::fmt::Display; + use std::fs; + use std::path::PathBuf; + use std::str::FromStr; -fn main() { - #[cfg(feature = "asm")] - { - let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); - - if arch == "x86_64" { - println!("cargo:rustc-cfg={}", "nasm_x86_64"); - build_nasm_files(true) - } else if arch == "x86" { - println!("cargo:rustc-cfg={}", "nasm_x86"); - build_nasm_files(false) - } else if arch == "aarch64" { - println!("cargo:rustc-cfg={}", "asm_neon"); - build_asm_files(true) - } else if arch == "arm" { - println!("cargo:rustc-cfg={}", "asm_neon"); - build_asm_files(false) - } else { - panic!("unknown arch: {}", arch); - } + #[derive(Clone, Copy, PartialEq, Eq)] + enum Arch { + X86(ArchX86), + Arm(ArchArm), } -} -#[cfg(feature = "asm")] -fn build_nasm_files(x86_64: bool) { - use std::fs::File; - use std::io::Write; - let out_dir = env::var("OUT_DIR").unwrap(); - - let dest_path = Path::new(&out_dir).join("config.asm"); - let mut config_file = File::create(&dest_path).unwrap(); - config_file - .write(b"%define private_prefix dav1d\n") - .unwrap(); - if x86_64 { - config_file.write(b"%define ARCH_X86_32 0\n").unwrap(); - config_file.write(b"%define ARCH_X86_64 1\n").unwrap(); - config_file.write(b"%define STACK_ALIGNMENT 16\n").unwrap(); - } else { - config_file.write(b"%define ARCH_X86_32 1\n").unwrap(); - config_file.write(b"%define ARCH_X86_64 0\n").unwrap(); - config_file.write(b"%define STACK_ALIGNMENT 4\n").unwrap(); + #[derive(Clone, Copy, PartialEq, Eq)] + enum ArchX86 { + X86_32, + X86_64, } - config_file.write(b"%define PIC 1\n").unwrap(); - #[cfg(target_os = "macos")] - config_file.write(b"%define PREFIX 1\n").unwrap(); - config_file - .write(b"%define FORCE_VEX_ENCODING 0\n") - .unwrap(); - - let mut asm_files = vec![ - "src/x86/cdef_avx2.asm", - "src/x86/cdef_sse.asm", - "src/x86/itx_avx2.asm", - "src/x86/itx_avx512.asm", - "src/x86/itx_sse.asm", - "src/x86/looprestoration_avx2.asm", - "src/x86/msac.asm", - "src/x86/refmvs.asm", - ]; - - #[cfg(feature = "bitdepth_8")] - asm_files.extend_from_slice(&[ - "src/x86/cdef_avx512.asm", - "src/x86/filmgrain_avx2.asm", - "src/x86/filmgrain_avx512.asm", - "src/x86/filmgrain_sse.asm", - "src/x86/ipred_avx2.asm", - "src/x86/ipred_avx512.asm", - "src/x86/ipred_sse.asm", - "src/x86/loopfilter_avx2.asm", - "src/x86/loopfilter_avx512.asm", - "src/x86/loopfilter_sse.asm", - "src/x86/looprestoration_avx512.asm", - "src/x86/looprestoration_sse.asm", - "src/x86/mc_avx2.asm", - "src/x86/mc_avx512.asm", - "src/x86/mc_sse.asm", - ]); - - #[cfg(feature = "bitdepth_16")] - asm_files.extend_from_slice(&[ - "src/x86/cdef16_avx2.asm", - "src/x86/cdef16_avx512.asm", - "src/x86/cdef16_sse.asm", - "src/x86/filmgrain16_avx2.asm", - "src/x86/filmgrain16_avx512.asm", - "src/x86/filmgrain16_sse.asm", - "src/x86/ipred16_avx2.asm", - "src/x86/ipred16_avx512.asm", - "src/x86/ipred16_sse.asm", - "src/x86/itx16_avx2.asm", - "src/x86/itx16_avx512.asm", - "src/x86/itx16_sse.asm", - "src/x86/loopfilter16_avx2.asm", - "src/x86/loopfilter16_avx512.asm", - "src/x86/loopfilter16_sse.asm", - "src/x86/looprestoration16_avx2.asm", - "src/x86/looprestoration16_avx512.asm", - "src/x86/looprestoration16_sse.asm", - "src/x86/mc16_avx2.asm", - "src/x86/mc16_avx512.asm", - "src/x86/mc16_sse.asm", - ]); - - let mut config_include_arg = String::from("-I"); - config_include_arg.push_str(&out_dir); - config_include_arg.push('/'); - - let mut nasm = nasm_rs::Build::new(); - nasm.min_version(2, 14, 0); - for file in asm_files { - nasm.file(file); + + #[derive(Clone, Copy, PartialEq, Eq)] + enum ArchArm { + Arm32, + Arm64, } - nasm.flag(&config_include_arg); - nasm.flag("-Isrc/"); - let obj = nasm.compile_objects().unwrap_or_else(|e| { - println!("cargo:warning={e}"); - panic!("NASM build failed. Make sure you have nasm installed or disable the \"asm\" feature.\n\ - You can get NASM from https://nasm.us or your system's package manager.\n\nerror: {e}"); - }); - - // cc is better at finding the correct archiver - let mut cc = cc::Build::new(); - for o in obj { - cc.object(o); + + impl FromStr for Arch { + type Err = String; + + fn from_str(arch: &str) -> Result { + Ok(match arch { + "x86" => Self::X86(ArchX86::X86_32), + "x86_64" => Self::X86(ArchX86::X86_64), + "arm" => Self::Arm(ArchArm::Arm32), + "aarch64" => Self::Arm(ArchArm::Arm64), + _ => return Err(format!("unexpected arch: {arch}")), + }) + } } - cc.compile("rav1dasm"); - println!("cargo:rustc-link-lib=static=rav1dasm"); -} + struct Define { + name: &'static str, + value: String, + } -#[cfg(feature = "asm")] -fn build_asm_files(aarch64: bool) { - use std::fs::File; - use std::io::Write; - let out_dir = env::var("OUT_DIR").unwrap(); - - let dest_path = Path::new(&out_dir).join("config.h"); - let mut config_file = File::create(&dest_path).unwrap(); - if env::var("CARGO_CFG_TARGET_VENDOR").unwrap() == "apple" { - config_file.write(b" #define PREFIX 1\n").unwrap(); + impl Define { + pub fn new(name: &'static str, value: impl Display) -> Self { + Self { + name, + value: value.to_string(), + } + } + + pub fn bool(name: &'static str, value: bool) -> Self { + Self::new(name, value as u8) + } + } + + pub fn main() { + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + + let arch = env::var("CARGO_CFG_TARGET_ARCH") + .unwrap() + .parse::() + .unwrap(); + let vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap(); + let pointer_width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap(); + + let vendor = vendor.as_str(); + let pointer_width = pointer_width.as_str(); + + let rustc_cfg = match arch { + Arch::X86(ArchX86::X86_32) => "nasm_x86", + Arch::X86(ArchX86::X86_64) => "nasm_x86_64", + Arch::Arm(..) => "asm_neon", + }; + println!("cargo:rustc-cfg={rustc_cfg}"); + + let mut defines = Vec::new(); + let mut define = |define: Define| { + defines.push(define); + }; + + // TODO(kkysen) incorrect, dav1d defines these for all arches + if matches!(arch, Arch::Arm(..)) { + define(Define::bool("CONFIG_ASM", true)); + define(Define::bool("CONFIG_LOG", true)); + } + + // TODO(kkysen) incorrect since we may cross compile + if (matches!(arch, Arch::X86(..)) && cfg!(target_os = "macos")) + || (matches!(arch, Arch::Arm(..)) && vendor == "apple") + { + define(Define::bool("PREFIX", true)); + } + + if matches!(arch, Arch::X86(..)) { + define(Define::new("private_prefix", "dav1d")); + } + if matches!(arch, Arch::Arm(..)) { + define(Define::new("PRIVATE_PREFIX", "dav1d_")); + } + + if let Arch::X86(arch) = arch { + define(Define::bool("ARCH_X86_32", arch == ArchX86::X86_32)); + define(Define::bool("ARCH_X86_64", arch == ArchX86::X86_64)); + } + if let Arch::Arm(arch) = arch { + define(Define::bool("ARCH_ARM", arch == ArchArm::Arm32)); + define(Define::bool("ARCH_AARCH64", arch == ArchArm::Arm64)); + } + + if let Arch::X86(arch) = arch { + define(Define::new( + "STACK_ALIGNMENT", + match arch { + ArchX86::X86_32 => 4, + ArchX86::X86_64 => 16, + }, + )); + } + + if matches!(arch, Arch::X86(..)) { + define(Define::bool("PIC", true)); + define(Define::bool("FORCE_VEX_ENCODING", false)); // TODO(kkysen) incorrect, not what dav1d does + } + + let use_nasm = match arch { + Arch::X86(..) => true, + Arch::Arm(..) => false, + }; + + let define_prefix = if use_nasm { "%" } else { " #" }; + + let config_lines = defines + .iter() + .map(|Define { name, value }| format!("{define_prefix}define {name} {value}")) + .collect::>(); + + let config_contents = config_lines.join("\n"); + let config_file_name = if use_nasm { "config.asm" } else { "config.h" }; + let config_path = out_dir.join(config_file_name); + fs::write(&config_path, &config_contents).unwrap(); + + let x86_generic = &[ + "cdef_avx2", + "cdef_sse", + "itx_avx2", + "itx_avx512", + "itx_sse", + "looprestoration_avx2", + "msac", + "refmvs", + ][..]; + let x86_bpc8 = &[ + "cdef_avx512", + "filmgrain_avx2", + "filmgrain_avx512", + "filmgrain_sse", + "ipred_avx2", + "ipred_avx512", + "ipred_sse", + "loopfilter_avx2", + "loopfilter_avx512", + "loopfilter_sse", + "looprestoration_avx512", + "looprestoration_sse", + "mc_avx2", + "mc_avx512", + "mc_sse", + ][..]; + let x86_bpc16 = &[ + "cdef16_avx2", + "cdef16_avx512", + "cdef16_sse", + "filmgrain16_avx2", + "filmgrain16_avx512", + "filmgrain16_sse", + "ipred16_avx2", + "ipred16_avx512", + "ipred16_sse", + "itx16_avx2", + "itx16_avx512", + "itx16_sse", + "loopfilter16_avx2", + "loopfilter16_avx512", + "loopfilter16_sse", + "looprestoration16_avx2", + "looprestoration16_avx512", + "looprestoration16_sse", + "mc16_avx2", + "mc16_avx512", + "mc16_sse", + ][..]; + + let arm_generic = &["itx", "msac", "refmvs", "looprestoration_common"][..]; + let arm_bpc8 = &[ + "cdef", + "filmgrain", + "ipred", + "loopfilter", + "looprestoration", + "mc", + ][..]; + let arm_bpc16 = &[ + "cdef16", + "filmgrain16", + "ipred16", + "itx16", + "loopfilter16", + "looprestoration16", + "mc16", + ][..]; + + // TODO(kkysen) Should not compile avx on x86. + let asm_file_names = match arch { + Arch::X86(..) => [ + x86_generic, + #[cfg(feature = "bitdepth_8")] + x86_bpc8, + #[cfg(feature = "bitdepth_16")] + x86_bpc16, + ], + Arch::Arm(..) => [ + arm_generic, + #[cfg(feature = "bitdepth_8")] + arm_bpc8, + #[cfg(feature = "bitdepth_16")] + arm_bpc16, + ], + }; + + let asm_file_dir = match arch { + Arch::X86(..) => ["x86", "."], + Arch::Arm(..) => ["arm", pointer_width], + }; + let asm_extension = if use_nasm { "asm" } else { "S" }; + + let asm_file_paths = asm_file_names.iter().flat_map(|a| *a).map(|file_name| { + let mut path = [&["src"], &asm_file_dir[..], &[file_name]] + .into_iter() + .flatten() + .collect::(); + path.set_extension(asm_extension); + path + }); + + let rav1dasm = "rav1dasm"; + + if use_nasm { + let mut nasm = nasm_rs::Build::new(); + nasm.min_version(2, 14, 0); + nasm.files(asm_file_paths); + nasm.flag(&format!("-I{}/", out_dir.as_os_str().to_str().unwrap())); + nasm.flag("-Isrc/"); + let obj = nasm.compile_objects().unwrap_or_else(|e| { + println!("cargo:warning={e}"); + panic!("NASM build failed. Make sure you have nasm installed or disable the \"asm\" feature.\n\ + You can get NASM from https://nasm.us or your system's package manager.\n\nerror: {e}"); + }); + + // cc is better at finding the correct archiver + let mut cc = cc::Build::new(); + for o in obj { + cc.object(o); + } + cc.compile(rav1dasm); + } else { + cc::Build::new() + .files(asm_file_paths) + .include(".") + .include(&out_dir) + .compile(rav1dasm); + } + + println!("cargo:rustc-link-lib=static={rav1dasm}"); } - config_file - .write(b" #define PRIVATE_PREFIX dav1d_\n") - .unwrap(); - - if aarch64 { - config_file.write(b" #define ARCH_AARCH64 1\n").unwrap(); - config_file.write(b" #define ARCH_ARM 0\n").unwrap(); - } else { - config_file.write(b" #define ARCH_AARCH64 0\n").unwrap(); - config_file.write(b" #define ARCH_ARM 1\n").unwrap(); +} + +fn main() { + #[cfg(feature = "asm")] + { + asm::main(); } - config_file.write(b" #define CONFIG_LOG 1 \n").unwrap(); - config_file.write(b" #define HAVE_ASM 1\n").unwrap(); - config_file.sync_all().unwrap(); - - let arch_dir = if aarch64 { "64" } else { "32" }; - let mut asm_files = vec![ - format!("src/arm/{}/itx.S", arch_dir), - format!("src/arm/{}/msac.S", arch_dir), - format!("src/arm/{}/refmvs.S", arch_dir), - format!("src/arm/{}/looprestoration_common.S", arch_dir), - ]; - - #[cfg(feature = "bitdepth_8")] - asm_files.extend_from_slice(&[ - format!("src/arm/{}/cdef.S", arch_dir), - format!("src/arm/{}/filmgrain.S", arch_dir), - format!("src/arm/{}/ipred.S", arch_dir), - format!("src/arm/{}/loopfilter.S", arch_dir), - format!("src/arm/{}/looprestoration.S", arch_dir), - format!("src/arm/{}/mc.S", arch_dir), - ]); - - #[cfg(feature = "bitdepth_16")] - asm_files.extend_from_slice(&[ - format!("src/arm/{}/cdef16.S", arch_dir), - format!("src/arm/{}/filmgrain16.S", arch_dir), - format!("src/arm/{}/ipred16.S", arch_dir), - format!("src/arm/{}/itx16.S", arch_dir), - format!("src/arm/{}/loopfilter16.S", arch_dir), - format!("src/arm/{}/looprestoration16.S", arch_dir), - format!("src/arm/{}/mc16.S", arch_dir), - ]); - - cc::Build::new() - .files(asm_files) - .include(".") - .include(&out_dir) - .compile("rav1dasm"); - - println!("cargo:rustc-link-lib=static=rav1dasm"); }