|
| 1 | +//! The compiler_builtins library is special. It exists to export a number of intrinsics which may |
| 2 | +//! also be provided by libgcc or compiler-rt, and when an intrinsic is provided by another |
| 3 | +//! library, we want that definition to override the one in compiler_builtins because we expect |
| 4 | +//! that those implementations are more optimized than compiler_builtins. To make sure that an |
| 5 | +//! attempt to override a compiler_builtins intrinsic does not result in a multiple definitions |
| 6 | +//! linker error, the compiler has special CGU partitioning logic for compiler_builtins that |
| 7 | +//! ensures every intrinsic gets its own CGU. |
| 8 | +//! |
| 9 | +//! This test is slightly overfit to the current compiler_builtins CGU naming strategy; it doesn't |
| 10 | +//! distinguish between "multiple intrinsics are in one object file!" which would be very bad, and |
| 11 | +//! "This object file has an intrinsic and also some of its helper functions!" which would be okay. |
| 12 | +//! |
| 13 | +//! This test ensures that the compiler_builtins rlib has only one intrinsic in each object file. |
| 14 | +
|
| 15 | +// wasm and nvptx targets don't produce rlib files that object can parse. |
| 16 | +//@ ignore-wasm |
| 17 | +//@ ignore-nvptx64 |
| 18 | + |
| 19 | +#![deny(warnings)] |
| 20 | + |
| 21 | +use std::str; |
| 22 | + |
| 23 | +use run_make_support::object::read::Object; |
| 24 | +use run_make_support::object::read::archive::ArchiveFile; |
| 25 | +use run_make_support::object::{ObjectSection, SectionKind}; |
| 26 | +use run_make_support::rfs::{read, read_dir}; |
| 27 | +use run_make_support::{cargo, object, path, target}; |
| 28 | + |
| 29 | +fn main() { |
| 30 | + println!("Testing compiler_builtins CGU partitioning for {}", target()); |
| 31 | + |
| 32 | + // CGU partitioning has some special cases for codegen-units=1, so we also test 2 CGUs. |
| 33 | + for cgus in [1, 2] { |
| 34 | + for profile in ["debug", "release"] { |
| 35 | + run_test(profile, cgus); |
| 36 | + } |
| 37 | + } |
| 38 | +} |
| 39 | + |
| 40 | +fn run_test(profile: &str, cgus: usize) { |
| 41 | + println!("Testing with profile {profile} and -Ccodegen-units={cgus}"); |
| 42 | + |
| 43 | + let target_dir = path("target"); |
| 44 | + |
| 45 | + let mut cmd = cargo(); |
| 46 | + cmd.args(&[ |
| 47 | + "build", |
| 48 | + "--manifest-path", |
| 49 | + "Cargo.toml", |
| 50 | + "-Zbuild-std=core", |
| 51 | + "--target", |
| 52 | + &target(), |
| 53 | + ]) |
| 54 | + .env("RUSTFLAGS", &format!("-Ccodegen-units={cgus}")) |
| 55 | + .env("CARGO_TARGET_DIR", &target_dir) |
| 56 | + .env("RUSTC_BOOTSTRAP", "1") |
| 57 | + // Visual Studio 2022 requires that the LIB env var be set so it can |
| 58 | + // find the Windows SDK. |
| 59 | + .env("LIB", std::env::var("LIB").unwrap_or_default()); |
| 60 | + if profile == "release" { |
| 61 | + cmd.arg("--release"); |
| 62 | + } |
| 63 | + cmd.run(); |
| 64 | + |
| 65 | + let rlibs_path = target_dir.join(target()).join(profile).join("deps"); |
| 66 | + let compiler_builtins_rlib = read_dir(rlibs_path) |
| 67 | + .find_map(|e| { |
| 68 | + let path = e.unwrap().path(); |
| 69 | + let file_name = path.file_name().unwrap().to_str().unwrap(); |
| 70 | + if file_name.starts_with("libcompiler_builtins") && file_name.ends_with(".rlib") { |
| 71 | + Some(path) |
| 72 | + } else { |
| 73 | + None |
| 74 | + } |
| 75 | + }) |
| 76 | + .unwrap(); |
| 77 | + |
| 78 | + // rlib files are archives, where the archive members are our CGUs, and we also have one called |
| 79 | + // lib.rmeta which is the encoded metadata. Each of the CGUs is an object file. |
| 80 | + let data = read(compiler_builtins_rlib); |
| 81 | + |
| 82 | + let archive = ArchiveFile::parse(&*data).unwrap(); |
| 83 | + for member in archive.members() { |
| 84 | + let member = member.unwrap(); |
| 85 | + if member.name() == b"lib.rmeta" { |
| 86 | + continue; |
| 87 | + } |
| 88 | + let data = member.data(&*data).unwrap(); |
| 89 | + let object = object::File::parse(&*data).unwrap(); |
| 90 | + |
| 91 | + let mut text_sections_count = 0; |
| 92 | + println!("Inspecting object {}", str::from_utf8(&member.name()).unwrap()); |
| 93 | + for section in object |
| 94 | + .sections() |
| 95 | + .filter(|section| section.kind() == SectionKind::Text && section.size() > 0) |
| 96 | + { |
| 97 | + println!("section: {:?}", section.name().unwrap()); |
| 98 | + text_sections_count += 1; |
| 99 | + } |
| 100 | + // Assert that we have 0 or 1 text sections. Occasionally, we will encounter an object file |
| 101 | + // that just contains a static. Which is weird, but valid. |
| 102 | + assert!(text_sections_count <= 1); |
| 103 | + } |
| 104 | +} |
0 commit comments