• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::{collections::BTreeMap, env, path::PathBuf, sync::atomic::Ordering};
2 
3 mod configure;
4 
5 use configure::{configure_f16_f128, Target};
6 
main()7 fn main() {
8     println!("cargo::rerun-if-changed=build.rs");
9     println!("cargo::rerun-if-changed=configure.rs");
10 
11     let target = Target::from_env();
12     let cwd = env::current_dir().unwrap();
13 
14     configure_check_cfg();
15     configure_f16_f128(&target);
16 
17     println!("cargo:compiler-rt={}", cwd.join("compiler-rt").display());
18 
19     // Activate libm's unstable features to make full use of Nightly.
20     println!("cargo::rustc-check-cfg=cfg(feature, values(\"unstable\", \"force-soft-floats\"))");
21     println!("cargo:rustc-cfg=feature=\"unstable\"");
22     println!("cargo:rustc-cfg=feature=\"force-soft-floats\"");
23 
24     // Emscripten's runtime includes all the builtins
25     if target.os == "emscripten" {
26         return;
27     }
28 
29     // OpenBSD provides compiler_rt by default, use it instead of rebuilding it from source
30     if target.os == "openbsd" {
31         println!("cargo:rustc-link-search=native=/usr/lib");
32         println!("cargo:rustc-link-lib=compiler_rt");
33         return;
34     }
35 
36     // Forcibly enable memory intrinsics on wasm & SGX as we don't have a libc to
37     // provide them.
38     if (target.triple.contains("wasm") && !target.triple.contains("wasi"))
39         || (target.triple.contains("sgx") && target.triple.contains("fortanix"))
40         || target.triple.contains("-none")
41         || target.triple.contains("nvptx")
42         || target.triple.contains("uefi")
43         || target.triple.contains("xous")
44     {
45         println!("cargo:rustc-cfg=feature=\"mem\"");
46     }
47 
48     // These targets have hardware unaligned access support.
49     println!("cargo::rustc-check-cfg=cfg(feature, values(\"mem-unaligned\"))");
50     if target.arch.contains("x86_64")
51         || target.arch.contains("x86")
52         || target.arch.contains("aarch64")
53         || target.arch.contains("bpf")
54     {
55         println!("cargo:rustc-cfg=feature=\"mem-unaligned\"");
56     }
57 
58     // NOTE we are going to assume that llvm-target, what determines our codegen option, matches the
59     // target triple. This is usually correct for our built-in targets but can break in presence of
60     // custom targets, which can have arbitrary names.
61     let llvm_target = target.triple.split('-').collect::<Vec<_>>();
62 
63     // Build missing intrinsics from compiler-rt C source code. If we're
64     // mangling names though we assume that we're also in test mode so we don't
65     // build anything and we rely on the upstream implementation of compiler-rt
66     // functions
67     if !cfg!(feature = "mangled-names") && cfg!(feature = "c") {
68         // Don't use a C compiler for these targets:
69         //
70         // * nvptx - everything is bitcode, not compatible with mixed C/Rust
71         if !target.arch.contains("nvptx") {
72             #[cfg(feature = "c")]
73             c::compile(&llvm_target, &target);
74         }
75     }
76 
77     // To compile intrinsics.rs for thumb targets, where there is no libc
78     println!("cargo::rustc-check-cfg=cfg(thumb)");
79     if llvm_target[0].starts_with("thumb") {
80         println!("cargo:rustc-cfg=thumb")
81     }
82 
83     // compiler-rt `cfg`s away some intrinsics for thumbv6m and thumbv8m.base because
84     // these targets do not have full Thumb-2 support but only original Thumb-1.
85     // We have to cfg our code accordingly.
86     println!("cargo::rustc-check-cfg=cfg(thumb_1)");
87     if llvm_target[0] == "thumbv6m" || llvm_target[0] == "thumbv8m.base" {
88         println!("cargo:rustc-cfg=thumb_1")
89     }
90 
91     // Only emit the ARM Linux atomic emulation on pre-ARMv6 architectures. This
92     // includes the old androideabi. It is deprecated but it is available as a
93     // rustc target (arm-linux-androideabi).
94     println!("cargo::rustc-check-cfg=cfg(kernel_user_helpers)");
95     if llvm_target[0] == "armv4t"
96         || llvm_target[0] == "armv5te"
97         || target.triple == "arm-linux-androideabi"
98     {
99         println!("cargo:rustc-cfg=kernel_user_helpers")
100     }
101 
102     if llvm_target[0].starts_with("aarch64") {
103         generate_aarch64_outlined_atomics();
104     }
105 }
106 
aarch64_symbol(ordering: Ordering) -> &'static str107 fn aarch64_symbol(ordering: Ordering) -> &'static str {
108     match ordering {
109         Ordering::Relaxed => "relax",
110         Ordering::Acquire => "acq",
111         Ordering::Release => "rel",
112         Ordering::AcqRel => "acq_rel",
113         _ => panic!("unknown symbol for {:?}", ordering),
114     }
115 }
116 
117 /// The `concat_idents` macro is extremely annoying and doesn't allow us to define new items.
118 /// Define them from the build script instead.
119 /// Note that the majority of the code is still defined in `aarch64.rs` through inline macros.
generate_aarch64_outlined_atomics()120 fn generate_aarch64_outlined_atomics() {
121     use std::fmt::Write;
122     // #[macro_export] so that we can use this in tests
123     let gen_macro =
124         |name| format!("#[macro_export] macro_rules! foreach_{name} {{ ($macro:path) => {{\n");
125 
126     // Generate different macros for add/clr/eor/set so that we can test them separately.
127     let sym_names = ["cas", "ldadd", "ldclr", "ldeor", "ldset", "swp"];
128     let mut macros = BTreeMap::new();
129     for sym in sym_names {
130         macros.insert(sym, gen_macro(sym));
131     }
132 
133     // Only CAS supports 16 bytes, and it has a different implementation that uses a different macro.
134     let mut cas16 = gen_macro("cas16");
135 
136     for ordering in [
137         Ordering::Relaxed,
138         Ordering::Acquire,
139         Ordering::Release,
140         Ordering::AcqRel,
141     ] {
142         let sym_ordering = aarch64_symbol(ordering);
143         for size in [1, 2, 4, 8] {
144             for (sym, macro_) in &mut macros {
145                 let name = format!("__aarch64_{sym}{size}_{sym_ordering}");
146                 writeln!(macro_, "$macro!( {ordering:?}, {size}, {name} );").unwrap();
147             }
148         }
149         let name = format!("__aarch64_cas16_{sym_ordering}");
150         writeln!(cas16, "$macro!( {ordering:?}, {name} );").unwrap();
151     }
152 
153     let mut buf = String::new();
154     for macro_def in macros.values().chain(std::iter::once(&cas16)) {
155         buf += macro_def;
156         buf += "}; }\n";
157     }
158     let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
159     std::fs::write(out_dir.join("outlined_atomics.rs"), buf).unwrap();
160 }
161 
162 /// Emit directives for features we expect to support that aren't in `Cargo.toml`.
163 ///
164 /// These are mostly cfg elements emitted by this `build.rs`.
configure_check_cfg()165 fn configure_check_cfg() {
166     // Functions where we can set the "optimized-c" flag
167     const HAS_OPTIMIZED_C: &[&str] = &[
168         "__ashldi3",
169         "__ashlsi3",
170         "__ashrdi3",
171         "__ashrsi3",
172         "__bswapsi2",
173         "__bswapdi2",
174         "__bswapti2",
175         "__divdi3",
176         "__divsi3",
177         "__divmoddi4",
178         "__divmodsi4",
179         "__divmodsi4",
180         "__divmodti4",
181         "__lshrdi3",
182         "__lshrsi3",
183         "__moddi3",
184         "__modsi3",
185         "__muldi3",
186         "__udivdi3",
187         "__udivmoddi4",
188         "__udivmodsi4",
189         "__udivsi3",
190         "__umoddi3",
191         "__umodsi3",
192     ];
193 
194     // Build a list of all aarch64 atomic operation functions
195     let mut aarch_atomic = Vec::new();
196     for aarch_op in ["cas", "ldadd", "ldclr", "ldeor", "ldset", "swp"] {
197         let op_sizes = if aarch_op == "cas" {
198             [1, 2, 4, 8, 16].as_slice()
199         } else {
200             [1, 2, 4, 8].as_slice()
201         };
202 
203         for op_size in op_sizes {
204             for ordering in ["relax", "acq", "rel", "acq_rel"] {
205                 aarch_atomic.push(format!("__aarch64_{}{}_{}", aarch_op, op_size, ordering));
206             }
207         }
208     }
209 
210     for fn_name in HAS_OPTIMIZED_C
211         .iter()
212         .copied()
213         .chain(aarch_atomic.iter().map(|s| s.as_str()))
214     {
215         println!(
216             "cargo::rustc-check-cfg=cfg({}, values(\"optimized-c\"))",
217             fn_name
218         );
219     }
220 
221     // Rustc is unaware of sparc target features, but this does show up from
222     // `rustc --print target-features --target sparc64-unknown-linux-gnu`.
223     println!("cargo::rustc-check-cfg=cfg(target_feature, values(\"vis3\"))");
224 
225     // FIXME: these come from libm and should be changed there
226     println!("cargo::rustc-check-cfg=cfg(feature, values(\"checked\"))");
227     println!("cargo::rustc-check-cfg=cfg(assert_no_panic)");
228 }
229 
230 #[cfg(feature = "c")]
231 mod c {
232     use std::collections::{BTreeMap, HashSet};
233     use std::env;
234     use std::fs::{self, File};
235     use std::io::Write;
236     use std::path::{Path, PathBuf};
237 
238     use super::Target;
239 
240     struct Sources {
241         // SYMBOL -> PATH TO SOURCE
242         map: BTreeMap<&'static str, &'static str>,
243     }
244 
245     impl Sources {
new() -> Sources246         fn new() -> Sources {
247             Sources {
248                 map: BTreeMap::new(),
249             }
250         }
251 
extend(&mut self, sources: &[(&'static str, &'static str)])252         fn extend(&mut self, sources: &[(&'static str, &'static str)]) {
253             // NOTE Some intrinsics have both a generic implementation (e.g.
254             // `floatdidf.c`) and an arch optimized implementation
255             // (`x86_64/floatdidf.c`). In those cases, we keep the arch optimized
256             // implementation and discard the generic implementation. If we don't
257             // and keep both implementations, the linker will yell at us about
258             // duplicate symbols!
259             for (symbol, src) in sources {
260                 if src.contains("/") {
261                     // Arch-optimized implementation (preferred)
262                     self.map.insert(symbol, src);
263                 } else {
264                     // Generic implementation
265                     if !self.map.contains_key(symbol) {
266                         self.map.insert(symbol, src);
267                     }
268                 }
269             }
270         }
271 
remove(&mut self, symbols: &[&str])272         fn remove(&mut self, symbols: &[&str]) {
273             for symbol in symbols {
274                 self.map.remove(*symbol).unwrap();
275             }
276         }
277     }
278 
279     /// Compile intrinsics from the compiler-rt C source code
compile(llvm_target: &[&str], target: &Target)280     pub fn compile(llvm_target: &[&str], target: &Target) {
281         let mut consider_float_intrinsics = true;
282         let cfg = &mut cc::Build::new();
283 
284         // AArch64 GCCs exit with an error condition when they encounter any kind of floating point
285         // code if the `nofp` and/or `nosimd` compiler flags have been set.
286         //
287         // Therefore, evaluate if those flags are present and set a boolean that causes any
288         // compiler-rt intrinsics that contain floating point source to be excluded for this target.
289         if target.arch == "aarch64" {
290             let cflags_key = String::from("CFLAGS_") + &(target.triple.replace("-", "_"));
291             if let Ok(cflags_value) = env::var(cflags_key) {
292                 if cflags_value.contains("+nofp") || cflags_value.contains("+nosimd") {
293                     consider_float_intrinsics = false;
294                 }
295             }
296         }
297 
298         // `compiler-rt` requires `COMPILER_RT_HAS_FLOAT16` to be defined to make it use the
299         // `_Float16` type for `f16` intrinsics. This shouldn't matter as all existing `f16`
300         // intrinsics have been ported to Rust in `compiler-builtins` as C compilers don't
301         // support `_Float16` on all targets (whereas Rust does). However, define the macro
302         // anyway to prevent issues like rust#118813 and rust#123885 silently reoccuring if more
303         // `f16` intrinsics get accidentally added here in the future.
304         cfg.define("COMPILER_RT_HAS_FLOAT16", None);
305 
306         cfg.warnings(false);
307 
308         if target.env == "msvc" {
309             // Don't pull in extra libraries on MSVC
310             cfg.flag("/Zl");
311 
312             // Emulate C99 and C++11's __func__ for MSVC prior to 2013 CTP
313             cfg.define("__func__", Some("__FUNCTION__"));
314         } else {
315             // Turn off various features of gcc and such, mostly copying
316             // compiler-rt's build system already
317             cfg.flag("-fno-builtin");
318             cfg.flag("-fvisibility=hidden");
319             cfg.flag("-ffreestanding");
320             // Avoid the following warning appearing once **per file**:
321             // clang: warning: optimization flag '-fomit-frame-pointer' is not supported for target 'armv7' [-Wignored-optimization-argument]
322             //
323             // Note that compiler-rt's build system also checks
324             //
325             // `check_cxx_compiler_flag(-fomit-frame-pointer COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG)`
326             //
327             // in https://github.com/rust-lang/compiler-rt/blob/c8fbcb3/cmake/config-ix.cmake#L19.
328             cfg.flag_if_supported("-fomit-frame-pointer");
329             cfg.define("VISIBILITY_HIDDEN", None);
330 
331             if let "aarch64" | "arm64ec" = target.arch.as_str() {
332                 // FIXME(llvm20): Older GCCs on A64 fail to build with
333                 // -Werror=implicit-function-declaration due to a compiler-rt bug.
334                 // With a newer LLVM we should be able to enable the flag everywhere.
335                 // https://github.com/llvm/llvm-project/commit/8aa9d6206ce55bdaaf422839c351fbd63f033b89
336             } else {
337                 // Avoid implicitly creating references to undefined functions
338                 cfg.flag("-Werror=implicit-function-declaration");
339             }
340         }
341 
342         // int_util.c tries to include stdlib.h if `_WIN32` is defined,
343         // which it is when compiling UEFI targets with clang. This is
344         // at odds with compiling with `-ffreestanding`, as the header
345         // may be incompatible or not present. Create a minimal stub
346         // header to use instead.
347         if target.os == "uefi" {
348             let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
349             let include_dir = out_dir.join("include");
350             if !include_dir.exists() {
351                 fs::create_dir(&include_dir).unwrap();
352             }
353             fs::write(include_dir.join("stdlib.h"), "#include <stddef.h>").unwrap();
354             cfg.flag(&format!("-I{}", include_dir.to_str().unwrap()));
355         }
356 
357         let mut sources = Sources::new();
358         sources.extend(&[
359             ("__absvdi2", "absvdi2.c"),
360             ("__absvsi2", "absvsi2.c"),
361             ("__addvdi3", "addvdi3.c"),
362             ("__addvsi3", "addvsi3.c"),
363             ("__cmpdi2", "cmpdi2.c"),
364             ("__int_util", "int_util.c"),
365             ("__mulvdi3", "mulvdi3.c"),
366             ("__mulvsi3", "mulvsi3.c"),
367             ("__negdi2", "negdi2.c"),
368             ("__negvdi2", "negvdi2.c"),
369             ("__negvsi2", "negvsi2.c"),
370             ("__paritydi2", "paritydi2.c"),
371             ("__paritysi2", "paritysi2.c"),
372             ("__popcountdi2", "popcountdi2.c"),
373             ("__popcountsi2", "popcountsi2.c"),
374             ("__subvdi3", "subvdi3.c"),
375             ("__subvsi3", "subvsi3.c"),
376             ("__ucmpdi2", "ucmpdi2.c"),
377         ]);
378 
379         if consider_float_intrinsics {
380             sources.extend(&[
381                 ("__divdc3", "divdc3.c"),
382                 ("__divsc3", "divsc3.c"),
383                 ("__muldc3", "muldc3.c"),
384                 ("__mulsc3", "mulsc3.c"),
385                 ("__negdf2", "negdf2.c"),
386                 ("__negsf2", "negsf2.c"),
387             ]);
388         }
389 
390         // On iOS and 32-bit OSX these are all just empty intrinsics, no need to
391         // include them.
392         if target.vendor != "apple" || target.arch != "x86" {
393             sources.extend(&[
394                 ("__absvti2", "absvti2.c"),
395                 ("__addvti3", "addvti3.c"),
396                 ("__cmpti2", "cmpti2.c"),
397                 ("__ffsti2", "ffsti2.c"),
398                 ("__mulvti3", "mulvti3.c"),
399                 ("__negti2", "negti2.c"),
400                 ("__parityti2", "parityti2.c"),
401                 ("__popcountti2", "popcountti2.c"),
402                 ("__subvti3", "subvti3.c"),
403                 ("__ucmpti2", "ucmpti2.c"),
404             ]);
405 
406             if consider_float_intrinsics {
407                 sources.extend(&[("__negvti2", "negvti2.c")]);
408             }
409         }
410 
411         if target.env != "msvc" {
412             if target.arch == "x86" {
413                 sources.extend(&[
414                     ("__ashldi3", "i386/ashldi3.S"),
415                     ("__ashrdi3", "i386/ashrdi3.S"),
416                     ("__divdi3", "i386/divdi3.S"),
417                     ("__lshrdi3", "i386/lshrdi3.S"),
418                     ("__moddi3", "i386/moddi3.S"),
419                     ("__muldi3", "i386/muldi3.S"),
420                     ("__udivdi3", "i386/udivdi3.S"),
421                     ("__umoddi3", "i386/umoddi3.S"),
422                 ]);
423             }
424         }
425 
426         if target.arch == "arm" && target.vendor != "apple" && target.env != "msvc" {
427             sources.extend(&[
428                 ("__aeabi_div0", "arm/aeabi_div0.c"),
429                 ("__aeabi_drsub", "arm/aeabi_drsub.c"),
430                 ("__aeabi_frsub", "arm/aeabi_frsub.c"),
431                 ("__bswapdi2", "arm/bswapdi2.S"),
432                 ("__bswapsi2", "arm/bswapsi2.S"),
433                 ("__divmodsi4", "arm/divmodsi4.S"),
434                 ("__divsi3", "arm/divsi3.S"),
435                 ("__modsi3", "arm/modsi3.S"),
436                 ("__switch16", "arm/switch16.S"),
437                 ("__switch32", "arm/switch32.S"),
438                 ("__switch8", "arm/switch8.S"),
439                 ("__switchu8", "arm/switchu8.S"),
440                 ("__sync_synchronize", "arm/sync_synchronize.S"),
441                 ("__udivmodsi4", "arm/udivmodsi4.S"),
442                 ("__udivsi3", "arm/udivsi3.S"),
443                 ("__umodsi3", "arm/umodsi3.S"),
444             ]);
445 
446             if target.os == "freebsd" {
447                 sources.extend(&[("__clear_cache", "clear_cache.c")]);
448             }
449 
450             // First of all aeabi_cdcmp and aeabi_cfcmp are never called by LLVM.
451             // Second are little-endian only, so build fail on big-endian targets.
452             // Temporally workaround: exclude these files for big-endian targets.
453             if !llvm_target[0].starts_with("thumbeb") && !llvm_target[0].starts_with("armeb") {
454                 sources.extend(&[
455                     ("__aeabi_cdcmp", "arm/aeabi_cdcmp.S"),
456                     ("__aeabi_cdcmpeq_check_nan", "arm/aeabi_cdcmpeq_check_nan.c"),
457                     ("__aeabi_cfcmp", "arm/aeabi_cfcmp.S"),
458                     ("__aeabi_cfcmpeq_check_nan", "arm/aeabi_cfcmpeq_check_nan.c"),
459                 ]);
460             }
461         }
462 
463         if llvm_target[0] == "armv7" {
464             sources.extend(&[
465                 ("__sync_fetch_and_add_4", "arm/sync_fetch_and_add_4.S"),
466                 ("__sync_fetch_and_add_8", "arm/sync_fetch_and_add_8.S"),
467                 ("__sync_fetch_and_and_4", "arm/sync_fetch_and_and_4.S"),
468                 ("__sync_fetch_and_and_8", "arm/sync_fetch_and_and_8.S"),
469                 ("__sync_fetch_and_max_4", "arm/sync_fetch_and_max_4.S"),
470                 ("__sync_fetch_and_max_8", "arm/sync_fetch_and_max_8.S"),
471                 ("__sync_fetch_and_min_4", "arm/sync_fetch_and_min_4.S"),
472                 ("__sync_fetch_and_min_8", "arm/sync_fetch_and_min_8.S"),
473                 ("__sync_fetch_and_nand_4", "arm/sync_fetch_and_nand_4.S"),
474                 ("__sync_fetch_and_nand_8", "arm/sync_fetch_and_nand_8.S"),
475                 ("__sync_fetch_and_or_4", "arm/sync_fetch_and_or_4.S"),
476                 ("__sync_fetch_and_or_8", "arm/sync_fetch_and_or_8.S"),
477                 ("__sync_fetch_and_sub_4", "arm/sync_fetch_and_sub_4.S"),
478                 ("__sync_fetch_and_sub_8", "arm/sync_fetch_and_sub_8.S"),
479                 ("__sync_fetch_and_umax_4", "arm/sync_fetch_and_umax_4.S"),
480                 ("__sync_fetch_and_umax_8", "arm/sync_fetch_and_umax_8.S"),
481                 ("__sync_fetch_and_umin_4", "arm/sync_fetch_and_umin_4.S"),
482                 ("__sync_fetch_and_umin_8", "arm/sync_fetch_and_umin_8.S"),
483                 ("__sync_fetch_and_xor_4", "arm/sync_fetch_and_xor_4.S"),
484                 ("__sync_fetch_and_xor_8", "arm/sync_fetch_and_xor_8.S"),
485             ]);
486         }
487 
488         if llvm_target.last().unwrap().ends_with("eabihf") {
489             if !llvm_target[0].starts_with("thumbv7em")
490                 && !llvm_target[0].starts_with("thumbv8m.main")
491             {
492                 // The FPU option chosen for these architectures in cc-rs, ie:
493                 //     -mfpu=fpv4-sp-d16 for thumbv7em
494                 //     -mfpu=fpv5-sp-d16 for thumbv8m.main
495                 // do not support double precision floating points conversions so the files
496                 // that include such instructions are not included for these targets.
497                 sources.extend(&[
498                     ("__fixdfsivfp", "arm/fixdfsivfp.S"),
499                     ("__fixunsdfsivfp", "arm/fixunsdfsivfp.S"),
500                     ("__floatsidfvfp", "arm/floatsidfvfp.S"),
501                     ("__floatunssidfvfp", "arm/floatunssidfvfp.S"),
502                 ]);
503             }
504 
505             sources.extend(&[
506                 ("__fixsfsivfp", "arm/fixsfsivfp.S"),
507                 ("__fixunssfsivfp", "arm/fixunssfsivfp.S"),
508                 ("__floatsisfvfp", "arm/floatsisfvfp.S"),
509                 ("__floatunssisfvfp", "arm/floatunssisfvfp.S"),
510                 ("__floatunssisfvfp", "arm/floatunssisfvfp.S"),
511                 ("__restore_vfp_d8_d15_regs", "arm/restore_vfp_d8_d15_regs.S"),
512                 ("__save_vfp_d8_d15_regs", "arm/save_vfp_d8_d15_regs.S"),
513                 ("__negdf2vfp", "arm/negdf2vfp.S"),
514                 ("__negsf2vfp", "arm/negsf2vfp.S"),
515             ]);
516         }
517 
518         if (target.arch == "aarch64" || target.arch == "arm64ec") && consider_float_intrinsics {
519             sources.extend(&[
520                 ("__comparetf2", "comparetf2.c"),
521                 ("__floatditf", "floatditf.c"),
522                 ("__floatsitf", "floatsitf.c"),
523                 ("__floatunditf", "floatunditf.c"),
524                 ("__floatunsitf", "floatunsitf.c"),
525                 ("__fe_getround", "fp_mode.c"),
526                 ("__fe_raise_inexact", "fp_mode.c"),
527             ]);
528 
529             if target.os != "windows" {
530                 sources.extend(&[("__multc3", "multc3.c")]);
531             }
532         }
533 
534         if target.arch == "mips" || target.arch == "riscv32" || target.arch == "riscv64" {
535             sources.extend(&[("__bswapsi2", "bswapsi2.c")]);
536         }
537 
538         if target.arch == "mips64" {
539             sources.extend(&[
540                 ("__netf2", "comparetf2.c"),
541                 ("__floatsitf", "floatsitf.c"),
542                 ("__floatunsitf", "floatunsitf.c"),
543                 ("__fe_getround", "fp_mode.c"),
544             ]);
545         }
546 
547         if target.arch == "loongarch64" {
548             sources.extend(&[
549                 ("__netf2", "comparetf2.c"),
550                 ("__floatsitf", "floatsitf.c"),
551                 ("__floatunsitf", "floatunsitf.c"),
552                 ("__fe_getround", "fp_mode.c"),
553             ]);
554         }
555 
556         // Remove the assembly implementations that won't compile for the target
557         if llvm_target[0] == "thumbv6m" || llvm_target[0] == "thumbv8m.base" || target.os == "uefi"
558         {
559             let mut to_remove = Vec::new();
560             for (k, v) in sources.map.iter() {
561                 if v.ends_with(".S") {
562                     to_remove.push(*k);
563                 }
564             }
565             sources.remove(&to_remove);
566         }
567 
568         if llvm_target[0] == "thumbv7m" || llvm_target[0] == "thumbv7em" {
569             sources.remove(&["__aeabi_cdcmp", "__aeabi_cfcmp"]);
570         }
571 
572         // Android uses emulated TLS so we need a runtime support function.
573         if target.os == "android" {
574             sources.extend(&[("__emutls_get_address", "emutls.c")]);
575 
576             // Work around a bug in the NDK headers (fixed in
577             // https://r.android.com/2038949 which will be released in a future
578             // NDK version) by providing a definition of LONG_BIT.
579             cfg.define("LONG_BIT", "(8 * sizeof(long))");
580         }
581 
582         // OpenHarmony also uses emulated TLS.
583         if target.env == "ohos" {
584             sources.extend(&[("__emutls_get_address", "emutls.c")]);
585         }
586 
587         // When compiling the C code we require the user to tell us where the
588         // source code is, and this is largely done so when we're compiling as
589         // part of rust-lang/rust we can use the same llvm-project repository as
590         // rust-lang/rust.
591         let root = match env::var_os("RUST_COMPILER_RT_ROOT") {
592             Some(s) => PathBuf::from(s),
593             None => {
594                 panic!("RUST_COMPILER_RT_ROOT is not set. You may need to download compiler-rt.")
595             }
596         };
597         if !root.exists() {
598             panic!("RUST_COMPILER_RT_ROOT={} does not exist", root.display());
599         }
600 
601         // Support deterministic builds by remapping the __FILE__ prefix if the
602         // compiler supports it.  This fixes the nondeterminism caused by the
603         // use of that macro in lib/builtins/int_util.h in compiler-rt.
604         cfg.flag_if_supported(&format!("-ffile-prefix-map={}=.", root.display()));
605 
606         // Include out-of-line atomics for aarch64, which are all generated by supplying different
607         // sets of flags to the same source file.
608         // Note: Out-of-line aarch64 atomics are not supported by the msvc toolchain (#430).
609         let src_dir = root.join("lib/builtins");
610         if target.arch == "aarch64" && target.env != "msvc" {
611             // See below for why we're building these as separate libraries.
612             build_aarch64_out_of_line_atomics_libraries(&src_dir, cfg);
613 
614             // Some run-time CPU feature detection is necessary, as well.
615             let cpu_model_src = if src_dir.join("cpu_model.c").exists() {
616                 "cpu_model.c"
617             } else {
618                 "cpu_model/aarch64.c"
619             };
620             sources.extend(&[("__aarch64_have_lse_atomics", cpu_model_src)]);
621         }
622 
623         let mut added_sources = HashSet::new();
624         for (sym, src) in sources.map.iter() {
625             let src = src_dir.join(src);
626             if added_sources.insert(src.clone()) {
627                 cfg.file(&src);
628                 println!("cargo:rerun-if-changed={}", src.display());
629             }
630             println!("cargo:rustc-cfg={}=\"optimized-c\"", sym);
631         }
632 
633         cfg.compile("libcompiler-rt.a");
634     }
635 
build_aarch64_out_of_line_atomics_libraries(builtins_dir: &Path, cfg: &mut cc::Build)636     fn build_aarch64_out_of_line_atomics_libraries(builtins_dir: &Path, cfg: &mut cc::Build) {
637         let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
638         let outlined_atomics_file = builtins_dir.join("aarch64").join("lse.S");
639         println!("cargo:rerun-if-changed={}", outlined_atomics_file.display());
640 
641         cfg.include(&builtins_dir);
642 
643         for instruction_type in &["cas", "swp", "ldadd", "ldclr", "ldeor", "ldset"] {
644             for size in &[1, 2, 4, 8, 16] {
645                 if *size == 16 && *instruction_type != "cas" {
646                     continue;
647                 }
648 
649                 for (model_number, model_name) in
650                     &[(1, "relax"), (2, "acq"), (3, "rel"), (4, "acq_rel")]
651                 {
652                     // The original compiler-rt build system compiles the same
653                     // source file multiple times with different compiler
654                     // options. Here we do something slightly different: we
655                     // create multiple .S files with the proper #defines and
656                     // then include the original file.
657                     //
658                     // This is needed because the cc crate doesn't allow us to
659                     // override the name of object files and libtool requires
660                     // all objects in an archive to have unique names.
661                     let path =
662                         out_dir.join(format!("lse_{}{}_{}.S", instruction_type, size, model_name));
663                     let mut file = File::create(&path).unwrap();
664                     writeln!(file, "#define L_{}", instruction_type).unwrap();
665                     writeln!(file, "#define SIZE {}", size).unwrap();
666                     writeln!(file, "#define MODEL {}", model_number).unwrap();
667                     writeln!(
668                         file,
669                         "#include \"{}\"",
670                         outlined_atomics_file.canonicalize().unwrap().display()
671                     )
672                     .unwrap();
673                     drop(file);
674                     cfg.file(path);
675 
676                     let sym = format!("__aarch64_{}{}_{}", instruction_type, size, model_name);
677                     println!("cargo:rustc-cfg={}=\"optimized-c\"", sym);
678                 }
679             }
680         }
681     }
682 }
683