| use std::{collections::BTreeMap, env, path::PathBuf, sync::atomic::Ordering}; |
| |
| mod configure; |
| |
| use configure::{configure_f16_f128, Target}; |
| |
| fn main() { |
| println!("cargo::rerun-if-changed=build.rs"); |
| println!("cargo::rerun-if-changed=configure.rs"); |
| |
| let target = Target::from_env(); |
| let cwd = env::current_dir().unwrap(); |
| |
| configure_check_cfg(); |
| configure_f16_f128(&target); |
| |
| println!("cargo:compiler-rt={}", cwd.join("compiler-rt").display()); |
| |
| // Activate libm's unstable features to make full use of Nightly. |
| println!("cargo::rustc-check-cfg=cfg(feature, values(\"unstable\", \"force-soft-floats\"))"); |
| println!("cargo:rustc-cfg=feature=\"unstable\""); |
| println!("cargo:rustc-cfg=feature=\"force-soft-floats\""); |
| |
| // Emscripten's runtime includes all the builtins |
| if target.os == "emscripten" { |
| return; |
| } |
| |
| // OpenBSD provides compiler_rt by default, use it instead of rebuilding it from source |
| if target.os == "openbsd" { |
| println!("cargo:rustc-link-search=native=/usr/lib"); |
| println!("cargo:rustc-link-lib=compiler_rt"); |
| return; |
| } |
| |
| // Forcibly enable memory intrinsics on wasm & SGX as we don't have a libc to |
| // provide them. |
| if (target.triple.contains("wasm") && !target.triple.contains("wasi")) |
| || (target.triple.contains("sgx") && target.triple.contains("fortanix")) |
| || target.triple.contains("-none") |
| || target.triple.contains("nvptx") |
| || target.triple.contains("uefi") |
| || target.triple.contains("xous") |
| { |
| println!("cargo:rustc-cfg=feature=\"mem\""); |
| } |
| |
| // These targets have hardware unaligned access support. |
| println!("cargo::rustc-check-cfg=cfg(feature, values(\"mem-unaligned\"))"); |
| if target.arch.contains("x86_64") |
| || target.arch.contains("x86") |
| || target.arch.contains("aarch64") |
| || target.arch.contains("bpf") |
| { |
| println!("cargo:rustc-cfg=feature=\"mem-unaligned\""); |
| } |
| |
| // NOTE we are going to assume that llvm-target, what determines our codegen option, matches the |
| // target triple. This is usually correct for our built-in targets but can break in presence of |
| // custom targets, which can have arbitrary names. |
| let llvm_target = target.triple.split('-').collect::<Vec<_>>(); |
| |
| // Build missing intrinsics from compiler-rt C source code. If we're |
| // mangling names though we assume that we're also in test mode so we don't |
| // build anything and we rely on the upstream implementation of compiler-rt |
| // functions |
| if !cfg!(feature = "mangled-names") && cfg!(feature = "c") { |
| // Don't use a C compiler for these targets: |
| // |
| // * nvptx - everything is bitcode, not compatible with mixed C/Rust |
| if !target.arch.contains("nvptx") { |
| #[cfg(feature = "c")] |
| c::compile(&llvm_target, &target); |
| } |
| } |
| |
| // To compile intrinsics.rs for thumb targets, where there is no libc |
| println!("cargo::rustc-check-cfg=cfg(thumb)"); |
| if llvm_target[0].starts_with("thumb") { |
| println!("cargo:rustc-cfg=thumb") |
| } |
| |
| // compiler-rt `cfg`s away some intrinsics for thumbv6m and thumbv8m.base because |
| // these targets do not have full Thumb-2 support but only original Thumb-1. |
| // We have to cfg our code accordingly. |
| println!("cargo::rustc-check-cfg=cfg(thumb_1)"); |
| if llvm_target[0] == "thumbv6m" || llvm_target[0] == "thumbv8m.base" { |
| println!("cargo:rustc-cfg=thumb_1") |
| } |
| |
| // Only emit the ARM Linux atomic emulation on pre-ARMv6 architectures. This |
| // includes the old androideabi. It is deprecated but it is available as a |
| // rustc target (arm-linux-androideabi). |
| println!("cargo::rustc-check-cfg=cfg(kernel_user_helpers)"); |
| if llvm_target[0] == "armv4t" |
| || llvm_target[0] == "armv5te" |
| || target.triple == "arm-linux-androideabi" |
| { |
| println!("cargo:rustc-cfg=kernel_user_helpers") |
| } |
| |
| if llvm_target[0].starts_with("aarch64") { |
| generate_aarch64_outlined_atomics(); |
| } |
| } |
| |
| fn aarch64_symbol(ordering: Ordering) -> &'static str { |
| match ordering { |
| Ordering::Relaxed => "relax", |
| Ordering::Acquire => "acq", |
| Ordering::Release => "rel", |
| Ordering::AcqRel => "acq_rel", |
| _ => panic!("unknown symbol for {:?}", ordering), |
| } |
| } |
| |
| /// The `concat_idents` macro is extremely annoying and doesn't allow us to define new items. |
| /// Define them from the build script instead. |
| /// Note that the majority of the code is still defined in `aarch64.rs` through inline macros. |
| fn generate_aarch64_outlined_atomics() { |
| use std::fmt::Write; |
| // #[macro_export] so that we can use this in tests |
| let gen_macro = |
| |name| format!("#[macro_export] macro_rules! foreach_{name} {{ ($macro:path) => {{\n"); |
| |
| // Generate different macros for add/clr/eor/set so that we can test them separately. |
| let sym_names = ["cas", "ldadd", "ldclr", "ldeor", "ldset", "swp"]; |
| let mut macros = BTreeMap::new(); |
| for sym in sym_names { |
| macros.insert(sym, gen_macro(sym)); |
| } |
| |
| // Only CAS supports 16 bytes, and it has a different implementation that uses a different macro. |
| let mut cas16 = gen_macro("cas16"); |
| |
| for ordering in [ |
| Ordering::Relaxed, |
| Ordering::Acquire, |
| Ordering::Release, |
| Ordering::AcqRel, |
| ] { |
| let sym_ordering = aarch64_symbol(ordering); |
| for size in [1, 2, 4, 8] { |
| for (sym, macro_) in &mut macros { |
| let name = format!("__aarch64_{sym}{size}_{sym_ordering}"); |
| writeln!(macro_, "$macro!( {ordering:?}, {size}, {name} );").unwrap(); |
| } |
| } |
| let name = format!("__aarch64_cas16_{sym_ordering}"); |
| writeln!(cas16, "$macro!( {ordering:?}, {name} );").unwrap(); |
| } |
| |
| let mut buf = String::new(); |
| for macro_def in macros.values().chain(std::iter::once(&cas16)) { |
| buf += macro_def; |
| buf += "}; }\n"; |
| } |
| let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); |
| std::fs::write(out_dir.join("outlined_atomics.rs"), buf).unwrap(); |
| } |
| |
| /// Emit directives for features we expect to support that aren't in `Cargo.toml`. |
| /// |
| /// These are mostly cfg elements emitted by this `build.rs`. |
| fn configure_check_cfg() { |
| // Functions where we can set the "optimized-c" flag |
| const HAS_OPTIMIZED_C: &[&str] = &[ |
| "__ashldi3", |
| "__ashlsi3", |
| "__ashrdi3", |
| "__ashrsi3", |
| "__bswapsi2", |
| "__bswapdi2", |
| "__bswapti2", |
| "__divdi3", |
| "__divsi3", |
| "__divmoddi4", |
| "__divmodsi4", |
| "__divmodsi4", |
| "__divmodti4", |
| "__lshrdi3", |
| "__lshrsi3", |
| "__moddi3", |
| "__modsi3", |
| "__muldi3", |
| "__udivdi3", |
| "__udivmoddi4", |
| "__udivmodsi4", |
| "__udivsi3", |
| "__umoddi3", |
| "__umodsi3", |
| ]; |
| |
| // Build a list of all aarch64 atomic operation functions |
| let mut aarch_atomic = Vec::new(); |
| for aarch_op in ["cas", "ldadd", "ldclr", "ldeor", "ldset", "swp"] { |
| let op_sizes = if aarch_op == "cas" { |
| [1, 2, 4, 8, 16].as_slice() |
| } else { |
| [1, 2, 4, 8].as_slice() |
| }; |
| |
| for op_size in op_sizes { |
| for ordering in ["relax", "acq", "rel", "acq_rel"] { |
| aarch_atomic.push(format!("__aarch64_{}{}_{}", aarch_op, op_size, ordering)); |
| } |
| } |
| } |
| |
| for fn_name in HAS_OPTIMIZED_C |
| .iter() |
| .copied() |
| .chain(aarch_atomic.iter().map(|s| s.as_str())) |
| { |
| println!( |
| "cargo::rustc-check-cfg=cfg({}, values(\"optimized-c\"))", |
| fn_name |
| ); |
| } |
| |
| // Rustc is unaware of sparc target features, but this does show up from |
| // `rustc --print target-features --target sparc64-unknown-linux-gnu`. |
| println!("cargo::rustc-check-cfg=cfg(target_feature, values(\"vis3\"))"); |
| |
| // FIXME: these come from libm and should be changed there |
| println!("cargo::rustc-check-cfg=cfg(feature, values(\"checked\"))"); |
| println!("cargo::rustc-check-cfg=cfg(assert_no_panic)"); |
| } |
| |
| #[cfg(feature = "c")] |
| mod c { |
| use std::collections::{BTreeMap, HashSet}; |
| use std::env; |
| use std::fs::{self, File}; |
| use std::io::Write; |
| use std::path::{Path, PathBuf}; |
| |
| use super::Target; |
| |
| struct Sources { |
| // SYMBOL -> PATH TO SOURCE |
| map: BTreeMap<&'static str, &'static str>, |
| } |
| |
| impl Sources { |
| fn new() -> Sources { |
| Sources { |
| map: BTreeMap::new(), |
| } |
| } |
| |
| fn extend(&mut self, sources: &[(&'static str, &'static str)]) { |
| // NOTE Some intrinsics have both a generic implementation (e.g. |
| // `floatdidf.c`) and an arch optimized implementation |
| // (`x86_64/floatdidf.c`). In those cases, we keep the arch optimized |
| // implementation and discard the generic implementation. If we don't |
| // and keep both implementations, the linker will yell at us about |
| // duplicate symbols! |
| for (symbol, src) in sources { |
| if src.contains("/") { |
| // Arch-optimized implementation (preferred) |
| self.map.insert(symbol, src); |
| } else { |
| // Generic implementation |
| if !self.map.contains_key(symbol) { |
| self.map.insert(symbol, src); |
| } |
| } |
| } |
| } |
| |
| fn remove(&mut self, symbols: &[&str]) { |
| for symbol in symbols { |
| self.map.remove(*symbol).unwrap(); |
| } |
| } |
| } |
| |
| /// Compile intrinsics from the compiler-rt C source code |
| pub fn compile(llvm_target: &[&str], target: &Target) { |
| let mut consider_float_intrinsics = true; |
| let cfg = &mut cc::Build::new(); |
| |
| // AArch64 GCCs exit with an error condition when they encounter any kind of floating point |
| // code if the `nofp` and/or `nosimd` compiler flags have been set. |
| // |
| // Therefore, evaluate if those flags are present and set a boolean that causes any |
| // compiler-rt intrinsics that contain floating point source to be excluded for this target. |
| if target.arch == "aarch64" { |
| let cflags_key = String::from("CFLAGS_") + &(target.triple.replace("-", "_")); |
| if let Ok(cflags_value) = env::var(cflags_key) { |
| if cflags_value.contains("+nofp") || cflags_value.contains("+nosimd") { |
| consider_float_intrinsics = false; |
| } |
| } |
| } |
| |
| // `compiler-rt` requires `COMPILER_RT_HAS_FLOAT16` to be defined to make it use the |
| // `_Float16` type for `f16` intrinsics. This shouldn't matter as all existing `f16` |
| // intrinsics have been ported to Rust in `compiler-builtins` as C compilers don't |
| // support `_Float16` on all targets (whereas Rust does). However, define the macro |
| // anyway to prevent issues like rust#118813 and rust#123885 silently reoccuring if more |
| // `f16` intrinsics get accidentally added here in the future. |
| cfg.define("COMPILER_RT_HAS_FLOAT16", None); |
| |
| cfg.warnings(false); |
| |
| if target.env == "msvc" { |
| // Don't pull in extra libraries on MSVC |
| cfg.flag("/Zl"); |
| |
| // Emulate C99 and C++11's __func__ for MSVC prior to 2013 CTP |
| cfg.define("__func__", Some("__FUNCTION__")); |
| } else { |
| // Turn off various features of gcc and such, mostly copying |
| // compiler-rt's build system already |
| cfg.flag("-fno-builtin"); |
| cfg.flag("-fvisibility=hidden"); |
| cfg.flag("-ffreestanding"); |
| // Avoid the following warning appearing once **per file**: |
| // clang: warning: optimization flag '-fomit-frame-pointer' is not supported for target 'armv7' [-Wignored-optimization-argument] |
| // |
| // Note that compiler-rt's build system also checks |
| // |
| // `check_cxx_compiler_flag(-fomit-frame-pointer COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG)` |
| // |
| // in https://github.com/rust-lang/compiler-rt/blob/c8fbcb3/cmake/config-ix.cmake#L19. |
| cfg.flag_if_supported("-fomit-frame-pointer"); |
| cfg.define("VISIBILITY_HIDDEN", None); |
| |
| if let "aarch64" | "arm64ec" = target.arch.as_str() { |
| // FIXME(llvm20): Older GCCs on A64 fail to build with |
| // -Werror=implicit-function-declaration due to a compiler-rt bug. |
| // With a newer LLVM we should be able to enable the flag everywhere. |
| // https://github.com/llvm/llvm-project/commit/8aa9d6206ce55bdaaf422839c351fbd63f033b89 |
| } else { |
| // Avoid implicitly creating references to undefined functions |
| cfg.flag("-Werror=implicit-function-declaration"); |
| } |
| } |
| |
| // int_util.c tries to include stdlib.h if `_WIN32` is defined, |
| // which it is when compiling UEFI targets with clang. This is |
| // at odds with compiling with `-ffreestanding`, as the header |
| // may be incompatible or not present. Create a minimal stub |
| // header to use instead. |
| if target.os == "uefi" { |
| let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); |
| let include_dir = out_dir.join("include"); |
| if !include_dir.exists() { |
| fs::create_dir(&include_dir).unwrap(); |
| } |
| fs::write(include_dir.join("stdlib.h"), "#include <stddef.h>").unwrap(); |
| cfg.flag(&format!("-I{}", include_dir.to_str().unwrap())); |
| } |
| |
| let mut sources = Sources::new(); |
| sources.extend(&[ |
| ("__absvdi2", "absvdi2.c"), |
| ("__absvsi2", "absvsi2.c"), |
| ("__addvdi3", "addvdi3.c"), |
| ("__addvsi3", "addvsi3.c"), |
| ("__cmpdi2", "cmpdi2.c"), |
| ("__int_util", "int_util.c"), |
| ("__mulvdi3", "mulvdi3.c"), |
| ("__mulvsi3", "mulvsi3.c"), |
| ("__negdi2", "negdi2.c"), |
| ("__negvdi2", "negvdi2.c"), |
| ("__negvsi2", "negvsi2.c"), |
| ("__paritydi2", "paritydi2.c"), |
| ("__paritysi2", "paritysi2.c"), |
| ("__popcountdi2", "popcountdi2.c"), |
| ("__popcountsi2", "popcountsi2.c"), |
| ("__subvdi3", "subvdi3.c"), |
| ("__subvsi3", "subvsi3.c"), |
| ("__ucmpdi2", "ucmpdi2.c"), |
| ]); |
| |
| if consider_float_intrinsics { |
| sources.extend(&[ |
| ("__divdc3", "divdc3.c"), |
| ("__divsc3", "divsc3.c"), |
| ("__muldc3", "muldc3.c"), |
| ("__mulsc3", "mulsc3.c"), |
| ("__negdf2", "negdf2.c"), |
| ("__negsf2", "negsf2.c"), |
| ]); |
| } |
| |
| // On iOS and 32-bit OSX these are all just empty intrinsics, no need to |
| // include them. |
| if target.vendor != "apple" || target.arch != "x86" { |
| sources.extend(&[ |
| ("__absvti2", "absvti2.c"), |
| ("__addvti3", "addvti3.c"), |
| ("__cmpti2", "cmpti2.c"), |
| ("__ffsti2", "ffsti2.c"), |
| ("__mulvti3", "mulvti3.c"), |
| ("__negti2", "negti2.c"), |
| ("__parityti2", "parityti2.c"), |
| ("__popcountti2", "popcountti2.c"), |
| ("__subvti3", "subvti3.c"), |
| ("__ucmpti2", "ucmpti2.c"), |
| ]); |
| |
| if consider_float_intrinsics { |
| sources.extend(&[("__negvti2", "negvti2.c")]); |
| } |
| } |
| |
| if target.vendor == "apple" { |
| sources.extend(&[ |
| ("atomic_flag_clear", "atomic_flag_clear.c"), |
| ("atomic_flag_clear_explicit", "atomic_flag_clear_explicit.c"), |
| ("atomic_flag_test_and_set", "atomic_flag_test_and_set.c"), |
| ( |
| "atomic_flag_test_and_set_explicit", |
| "atomic_flag_test_and_set_explicit.c", |
| ), |
| ("atomic_signal_fence", "atomic_signal_fence.c"), |
| ("atomic_thread_fence", "atomic_thread_fence.c"), |
| ]); |
| } |
| |
| if target.env != "msvc" { |
| if target.arch == "x86" { |
| sources.extend(&[ |
| ("__ashldi3", "i386/ashldi3.S"), |
| ("__ashrdi3", "i386/ashrdi3.S"), |
| ("__divdi3", "i386/divdi3.S"), |
| ("__lshrdi3", "i386/lshrdi3.S"), |
| ("__moddi3", "i386/moddi3.S"), |
| ("__muldi3", "i386/muldi3.S"), |
| ("__udivdi3", "i386/udivdi3.S"), |
| ("__umoddi3", "i386/umoddi3.S"), |
| ]); |
| } |
| } |
| |
| if target.arch == "arm" && target.vendor != "apple" && target.env != "msvc" { |
| sources.extend(&[ |
| ("__aeabi_div0", "arm/aeabi_div0.c"), |
| ("__aeabi_drsub", "arm/aeabi_drsub.c"), |
| ("__aeabi_frsub", "arm/aeabi_frsub.c"), |
| ("__bswapdi2", "arm/bswapdi2.S"), |
| ("__bswapsi2", "arm/bswapsi2.S"), |
| ("__divmodsi4", "arm/divmodsi4.S"), |
| ("__divsi3", "arm/divsi3.S"), |
| ("__modsi3", "arm/modsi3.S"), |
| ("__switch16", "arm/switch16.S"), |
| ("__switch32", "arm/switch32.S"), |
| ("__switch8", "arm/switch8.S"), |
| ("__switchu8", "arm/switchu8.S"), |
| ("__sync_synchronize", "arm/sync_synchronize.S"), |
| ("__udivmodsi4", "arm/udivmodsi4.S"), |
| ("__udivsi3", "arm/udivsi3.S"), |
| ("__umodsi3", "arm/umodsi3.S"), |
| ]); |
| |
| if target.os == "freebsd" { |
| sources.extend(&[("__clear_cache", "clear_cache.c")]); |
| } |
| |
| // First of all aeabi_cdcmp and aeabi_cfcmp are never called by LLVM. |
| // Second are little-endian only, so build fail on big-endian targets. |
| // Temporally workaround: exclude these files for big-endian targets. |
| if !llvm_target[0].starts_with("thumbeb") && !llvm_target[0].starts_with("armeb") { |
| sources.extend(&[ |
| ("__aeabi_cdcmp", "arm/aeabi_cdcmp.S"), |
| ("__aeabi_cdcmpeq_check_nan", "arm/aeabi_cdcmpeq_check_nan.c"), |
| ("__aeabi_cfcmp", "arm/aeabi_cfcmp.S"), |
| ("__aeabi_cfcmpeq_check_nan", "arm/aeabi_cfcmpeq_check_nan.c"), |
| ]); |
| } |
| } |
| |
| if llvm_target[0] == "armv7" { |
| sources.extend(&[ |
| ("__sync_fetch_and_add_4", "arm/sync_fetch_and_add_4.S"), |
| ("__sync_fetch_and_add_8", "arm/sync_fetch_and_add_8.S"), |
| ("__sync_fetch_and_and_4", "arm/sync_fetch_and_and_4.S"), |
| ("__sync_fetch_and_and_8", "arm/sync_fetch_and_and_8.S"), |
| ("__sync_fetch_and_max_4", "arm/sync_fetch_and_max_4.S"), |
| ("__sync_fetch_and_max_8", "arm/sync_fetch_and_max_8.S"), |
| ("__sync_fetch_and_min_4", "arm/sync_fetch_and_min_4.S"), |
| ("__sync_fetch_and_min_8", "arm/sync_fetch_and_min_8.S"), |
| ("__sync_fetch_and_nand_4", "arm/sync_fetch_and_nand_4.S"), |
| ("__sync_fetch_and_nand_8", "arm/sync_fetch_and_nand_8.S"), |
| ("__sync_fetch_and_or_4", "arm/sync_fetch_and_or_4.S"), |
| ("__sync_fetch_and_or_8", "arm/sync_fetch_and_or_8.S"), |
| ("__sync_fetch_and_sub_4", "arm/sync_fetch_and_sub_4.S"), |
| ("__sync_fetch_and_sub_8", "arm/sync_fetch_and_sub_8.S"), |
| ("__sync_fetch_and_umax_4", "arm/sync_fetch_and_umax_4.S"), |
| ("__sync_fetch_and_umax_8", "arm/sync_fetch_and_umax_8.S"), |
| ("__sync_fetch_and_umin_4", "arm/sync_fetch_and_umin_4.S"), |
| ("__sync_fetch_and_umin_8", "arm/sync_fetch_and_umin_8.S"), |
| ("__sync_fetch_and_xor_4", "arm/sync_fetch_and_xor_4.S"), |
| ("__sync_fetch_and_xor_8", "arm/sync_fetch_and_xor_8.S"), |
| ]); |
| } |
| |
| if llvm_target.last().unwrap().ends_with("eabihf") { |
| if !llvm_target[0].starts_with("thumbv7em") |
| && !llvm_target[0].starts_with("thumbv8m.main") |
| { |
| // The FPU option chosen for these architectures in cc-rs, ie: |
| // -mfpu=fpv4-sp-d16 for thumbv7em |
| // -mfpu=fpv5-sp-d16 for thumbv8m.main |
| // do not support double precision floating points conversions so the files |
| // that include such instructions are not included for these targets. |
| sources.extend(&[ |
| ("__fixdfsivfp", "arm/fixdfsivfp.S"), |
| ("__fixunsdfsivfp", "arm/fixunsdfsivfp.S"), |
| ("__floatsidfvfp", "arm/floatsidfvfp.S"), |
| ("__floatunssidfvfp", "arm/floatunssidfvfp.S"), |
| ]); |
| } |
| |
| sources.extend(&[ |
| ("__fixsfsivfp", "arm/fixsfsivfp.S"), |
| ("__fixunssfsivfp", "arm/fixunssfsivfp.S"), |
| ("__floatsisfvfp", "arm/floatsisfvfp.S"), |
| ("__floatunssisfvfp", "arm/floatunssisfvfp.S"), |
| ("__floatunssisfvfp", "arm/floatunssisfvfp.S"), |
| ("__restore_vfp_d8_d15_regs", "arm/restore_vfp_d8_d15_regs.S"), |
| ("__save_vfp_d8_d15_regs", "arm/save_vfp_d8_d15_regs.S"), |
| ("__negdf2vfp", "arm/negdf2vfp.S"), |
| ("__negsf2vfp", "arm/negsf2vfp.S"), |
| ]); |
| } |
| |
| if (target.arch == "aarch64" || target.arch == "arm64ec") && consider_float_intrinsics { |
| sources.extend(&[ |
| ("__comparetf2", "comparetf2.c"), |
| ("__floatditf", "floatditf.c"), |
| ("__floatsitf", "floatsitf.c"), |
| ("__floatunditf", "floatunditf.c"), |
| ("__floatunsitf", "floatunsitf.c"), |
| ("__fe_getround", "fp_mode.c"), |
| ("__fe_raise_inexact", "fp_mode.c"), |
| ]); |
| |
| if target.os != "windows" { |
| sources.extend(&[("__multc3", "multc3.c")]); |
| } |
| } |
| |
| if target.arch == "mips" || target.arch == "riscv32" || target.arch == "riscv64" { |
| sources.extend(&[("__bswapsi2", "bswapsi2.c")]); |
| } |
| |
| if target.arch == "mips64" { |
| sources.extend(&[ |
| ("__netf2", "comparetf2.c"), |
| ("__floatsitf", "floatsitf.c"), |
| ("__floatunsitf", "floatunsitf.c"), |
| ("__fe_getround", "fp_mode.c"), |
| ]); |
| } |
| |
| if target.arch == "loongarch64" { |
| sources.extend(&[ |
| ("__netf2", "comparetf2.c"), |
| ("__floatsitf", "floatsitf.c"), |
| ("__floatunsitf", "floatunsitf.c"), |
| ("__fe_getround", "fp_mode.c"), |
| ]); |
| } |
| |
| // Remove the assembly implementations that won't compile for the target |
| if llvm_target[0] == "thumbv6m" || llvm_target[0] == "thumbv8m.base" || target.os == "uefi" |
| { |
| let mut to_remove = Vec::new(); |
| for (k, v) in sources.map.iter() { |
| if v.ends_with(".S") { |
| to_remove.push(*k); |
| } |
| } |
| sources.remove(&to_remove); |
| } |
| |
| if llvm_target[0] == "thumbv7m" || llvm_target[0] == "thumbv7em" { |
| sources.remove(&["__aeabi_cdcmp", "__aeabi_cfcmp"]); |
| } |
| |
| // Android uses emulated TLS so we need a runtime support function. |
| if target.os == "android" { |
| sources.extend(&[("__emutls_get_address", "emutls.c")]); |
| |
| // Work around a bug in the NDK headers (fixed in |
| // https://r.android.com/2038949 which will be released in a future |
| // NDK version) by providing a definition of LONG_BIT. |
| cfg.define("LONG_BIT", "(8 * sizeof(long))"); |
| } |
| |
| // OpenHarmony also uses emulated TLS. |
| if target.env == "ohos" { |
| sources.extend(&[("__emutls_get_address", "emutls.c")]); |
| } |
| |
| // When compiling the C code we require the user to tell us where the |
| // source code is, and this is largely done so when we're compiling as |
| // part of rust-lang/rust we can use the same llvm-project repository as |
| // rust-lang/rust. |
| let root = match env::var_os("RUST_COMPILER_RT_ROOT") { |
| Some(s) => PathBuf::from(s), |
| None => { |
| panic!("RUST_COMPILER_RT_ROOT is not set. You may need to download compiler-rt.") |
| } |
| }; |
| if !root.exists() { |
| panic!("RUST_COMPILER_RT_ROOT={} does not exist", root.display()); |
| } |
| |
| // Support deterministic builds by remapping the __FILE__ prefix if the |
| // compiler supports it. This fixes the nondeterminism caused by the |
| // use of that macro in lib/builtins/int_util.h in compiler-rt. |
| cfg.flag_if_supported(&format!("-ffile-prefix-map={}=.", root.display())); |
| |
| // Include out-of-line atomics for aarch64, which are all generated by supplying different |
| // sets of flags to the same source file. |
| // Note: Out-of-line aarch64 atomics are not supported by the msvc toolchain (#430). |
| let src_dir = root.join("lib/builtins"); |
| if target.arch == "aarch64" && target.env != "msvc" { |
| // See below for why we're building these as separate libraries. |
| build_aarch64_out_of_line_atomics_libraries(&src_dir, cfg); |
| |
| // Some run-time CPU feature detection is necessary, as well. |
| let cpu_model_src = if src_dir.join("cpu_model.c").exists() { |
| "cpu_model.c" |
| } else { |
| "cpu_model/aarch64.c" |
| }; |
| sources.extend(&[("__aarch64_have_lse_atomics", cpu_model_src)]); |
| } |
| |
| let mut added_sources = HashSet::new(); |
| for (sym, src) in sources.map.iter() { |
| let src = src_dir.join(src); |
| if added_sources.insert(src.clone()) { |
| cfg.file(&src); |
| println!("cargo:rerun-if-changed={}", src.display()); |
| } |
| println!("cargo:rustc-cfg={}=\"optimized-c\"", sym); |
| } |
| |
| cfg.compile("libcompiler-rt.a"); |
| } |
| |
| fn build_aarch64_out_of_line_atomics_libraries(builtins_dir: &Path, cfg: &mut cc::Build) { |
| let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); |
| let outlined_atomics_file = builtins_dir.join("aarch64").join("lse.S"); |
| println!("cargo:rerun-if-changed={}", outlined_atomics_file.display()); |
| |
| cfg.include(&builtins_dir); |
| |
| for instruction_type in &["cas", "swp", "ldadd", "ldclr", "ldeor", "ldset"] { |
| for size in &[1, 2, 4, 8, 16] { |
| if *size == 16 && *instruction_type != "cas" { |
| continue; |
| } |
| |
| for (model_number, model_name) in |
| &[(1, "relax"), (2, "acq"), (3, "rel"), (4, "acq_rel")] |
| { |
| // The original compiler-rt build system compiles the same |
| // source file multiple times with different compiler |
| // options. Here we do something slightly different: we |
| // create multiple .S files with the proper #defines and |
| // then include the original file. |
| // |
| // This is needed because the cc crate doesn't allow us to |
| // override the name of object files and libtool requires |
| // all objects in an archive to have unique names. |
| let path = |
| out_dir.join(format!("lse_{}{}_{}.S", instruction_type, size, model_name)); |
| let mut file = File::create(&path).unwrap(); |
| writeln!(file, "#define L_{}", instruction_type).unwrap(); |
| writeln!(file, "#define SIZE {}", size).unwrap(); |
| writeln!(file, "#define MODEL {}", model_number).unwrap(); |
| writeln!( |
| file, |
| "#include \"{}\"", |
| outlined_atomics_file.canonicalize().unwrap().display() |
| ) |
| .unwrap(); |
| drop(file); |
| cfg.file(path); |
| |
| let sym = format!("__aarch64_{}{}_{}", instruction_type, size, model_name); |
| println!("cargo:rustc-cfg={}=\"optimized-c\"", sym); |
| } |
| } |
| } |
| } |
| } |