Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions compiler/rustc_codegen_llvm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,15 @@ pub(crate) unsafe fn create_module<'ll>(
);
}

if let Some(direct_access_external_data) = sess.direct_access_external_data() {
llvm::add_module_flag_u32(
llmod,
llvm::ModuleFlagMergeBehavior::Max,
"direct-access-external-data",
direct_access_external_data as u32,
);
}

match (sess.opts.unstable_opts.small_data_threshold, sess.target.small_data_threshold_support())
{
// Set up the small-data optimization limit for architectures that use
Expand Down
7 changes: 7 additions & 0 deletions tests/codegen-llvm/direct-access-external-data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,10 @@ pub fn refer() {
core::hint::black_box(EXTERNAL);
core::hint::black_box(WEAK);
}

// DIRECT: !{{[0-9]+}} = !{i32 7, !"direct-access-external-data", i32 1}

// INDIRECT: !{{[0-9]+}} = !{i32 7, !"direct-access-external-data", i32 0}

// DEFAULT-NOT: direct-access-external-data
// PIE-NOT: direct-access-external-data
25 changes: 25 additions & 0 deletions tests/run-make/lto-direct-access-external-data/dep.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#![feature(no_core, lang_items)]
#![no_std]
#![no_core]
#![crate_type = "lib"]

#[lang = "pointee_sized"]
trait PointeeSized {}
#[lang = "meta_sized"]
trait MetaSized: PointeeSized {}
#[lang = "sized"]
trait Sized: MetaSized {}

#[lang = "copy"]
pub trait Copy {}

impl Copy for i32 {}

unsafe extern "C" {
pub safe static VAR: i32;
}

#[no_mangle]
pub fn refer_dep() -> i32 {
unsafe { VAR }
}
74 changes: 74 additions & 0 deletions tests/run-make/lto-direct-access-external-data/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#![feature(no_core, lang_items)]
#![no_std]
#![no_core]

extern crate dep;

unsafe extern "C" {
pub safe static VAR: i32;
}

pub mod mod_0 {
use super::VAR;
#[no_mangle]
pub fn refer_0() -> i32 {
VAR
}
}

pub mod mod_1 {
use super::VAR;
#[no_mangle]
pub fn refer_1() -> i32 {
VAR
}
}

#[no_mangle]
pub fn call_dep() -> i32 {
dep::refer_dep()
}

// DEFAULT: @VAR = {{.*}}dso_local{{.*}}global i32
// DEFAULT-NOT: direct-access-external-data
// DEFAULT-NOT: PIE Level

// PIE: @VAR = external
// PIE-NOT: dso_local
// PIE-SAME: global i32
// PIE-NOT: direct-access-external-data
// PIE: !{{[0-9]+}} = !{i32 7, !"PIE Level", i32 2}

// DIRECT: @VAR = {{.*}}dso_local{{.*}}global i32
// DIRECT-DAG: !{{[0-9]+}} = !{i32 7, !"direct-access-external-data", i32 1}
// DIRECT-DAG: !{{[0-9]+}} = !{i32 7, !"PIE Level", i32 2}

// INDIRECT: @VAR = external
// INDIRECT-NOT: dso_local
// INDIRECT-SAME: global i32
// INDIRECT: !{{[0-9]+}} = !{i32 7, !"direct-access-external-data", i32 0}
// INDIRECT-NOT: PIE Level

// FIXME: Under normal circumstances PIE Level should be set consistently, so
// GOT relocations don't get emitted with direct-access-external-data enabled
// for all versions of Full/Thin LTO.
// DEP-NO-PIE-NOT: PIE Level

// Demonstrate incorrect GOT relocation under direct-access-external-data for ThinLTO.
// Even with direct-access enabled, the missing PIE Level on the dependency's module
// causes LLVM to fall back to GOT indirection.
// DIRECT-RELOC-THIN: R_X86_64_GOTPCREL{{.*}}VAR

// For Full LTO we expect direct PC-relative access since the merged module
// correctly inherits the main module's PIE Level.
// DIRECT-RELOC-FAT-NOT: R_X86_64_GOTPCREL{{.*}}VAR
// DIRECT-RELOC-FAT: R_X86_64_PC32{{.*}}VAR

// For indirect cases, we always expect GOT indirection.
// INDIRECT-RELOC: R_X86_64_GOTPCREL{{.*}}VAR
// INDIRECT-RELOC-NOT: R_X86_64_PC32{{.*}}VAR

// Default PIE without direct-access enabled should use GOT indirection.
// This is correct and is not changed by setting PIE Level consistently.
// PIE-RELOC: R_X86_64_GOTPCREL{{.*}}VAR
// PIE-RELOC-NOT: R_X86_64_PC32{{.*}}VAR
182 changes: 182 additions & 0 deletions tests/run-make/lto-direct-access-external-data/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
//@ needs-llvm-components: x86
//@ ignore-loongarch64 (handles dso_local differently)
//@ ignore-powerpc64 (handles dso_local differently)
//@ ignore-apple (handles dso_local differently)

// Test the various interleavings of LTO and relocation model.
// We know that in some cases the -Zdirect-access-external-data option will not
// result in the correct code generation due to the module having
// inconsistently set PIE Level. Test those cases so that the result is clear
// when future patches fix the problem.

use run_make_support::{
cwd, has_extension, llvm_dis, llvm_filecheck, llvm_readobj, rfs, rustc, shallow_find_files,
};

struct TestCase {
lto: &'static str,
reloc: &'static str,
direct_access: Option<&'static str>,
expected_prefix: &'static str,
dep_prefixes: &'static [&'static str],
}

fn main() {
let test_cases = [
TestCase {
lto: "thin",
reloc: "static",
direct_access: None,
expected_prefix: "DEFAULT",
dep_prefixes: &[],
},
TestCase {
lto: "fat",
reloc: "static",
direct_access: None,
expected_prefix: "DEFAULT",
dep_prefixes: &[],
},
TestCase {
lto: "thin",
reloc: "pie",
direct_access: None,
expected_prefix: "PIE",
dep_prefixes: &["DEP-NO-PIE"],
},
TestCase {
lto: "fat",
reloc: "pie",
direct_access: None,
expected_prefix: "PIE",
dep_prefixes: &[],
},
TestCase {
lto: "thin",
reloc: "pie",
direct_access: Some("yes"),
expected_prefix: "DIRECT",
dep_prefixes: &["DEP-NO-PIE"],
},
TestCase {
lto: "fat",
reloc: "pie",
direct_access: Some("yes"),
expected_prefix: "DIRECT",
dep_prefixes: &[],
},
TestCase {
lto: "thin",
reloc: "static",
direct_access: Some("no"),
expected_prefix: "INDIRECT",
dep_prefixes: &[],
},
TestCase {
lto: "fat",
reloc: "static",
direct_access: Some("no"),
expected_prefix: "INDIRECT",
dep_prefixes: &[],
},
];

for case in test_cases {
// Remove all output files that are not source Rust code for cleanup.
for file in shallow_find_files(cwd(), |path| !has_extension(path, "rs")) {
rfs::remove_file(file);
}

let mut cmd = rustc();
cmd.input("dep.rs")
.target("x86_64-unknown-linux-gnu")
.crate_type("rlib")
.crate_name("dep")
.arg("-Cpanic=abort");

if let Some(da) = case.direct_access {
cmd.arg(format!("-Zdirect-access-external-data={da}"));
}
cmd.run();

cmd = rustc();
cmd.input("lib.rs")
.target("x86_64-unknown-linux-gnu")
.crate_type("staticlib")
.crate_name("lto_direct_access_test")
.extern_("dep", "libdep.rlib")
.arg("-Cpanic=abort")
.arg("-Csave-temps")
.arg(format!("-Clto={}", case.lto))
.arg(format!("-Crelocation-model={}", case.reloc))
.arg("-Ccodegen-units=2");

if let Some(da) = case.direct_access {
cmd.arg(format!("-Zdirect-access-external-data={da}"));
}
cmd.run();

let suffix = if case.lto == "thin" {
".rcgu.thin-lto-after-pm.bc"
} else {
".rcgu.lto.after-restriction.bc"
};

let bc_files = shallow_find_files(".", |path| {
let name = path.file_name().unwrap().to_str().unwrap();
name.starts_with("lto_direct_access_test.lto_direct_access_test.")
&& name.ends_with(suffix)
});
assert!(!bc_files.is_empty(), "No expected bitcode files were generated");

for bc_file in &bc_files {
llvm_dis().input(bc_file).run();
let ll_file = bc_file.with_extension("ll");
llvm_filecheck()
.input_file(&ll_file)
.patterns("lib.rs")
.check_prefix(case.expected_prefix)
.run();
}

let relocs = llvm_readobj().arg("--relocations").input("liblto_direct_access_test.a").run();
let relocs_out = "relocs.txt";
rfs::write(relocs_out, relocs.stdout_utf8());

let reloc_prefix = match case.expected_prefix {
"DIRECT" => {
if case.lto == "thin" {
Some("DIRECT-RELOC-THIN")
} else {
Some("DIRECT-RELOC-FAT")
}
}
"PIE" => Some("PIE-RELOC"),
"INDIRECT" => Some("INDIRECT-RELOC"),
_ => None,
};

if let Some(prefix) = reloc_prefix {
llvm_filecheck().input_file(relocs_out).patterns("lib.rs").check_prefix(prefix).run();
}

if !case.dep_prefixes.is_empty() {
let dep_bc_files = shallow_find_files(".", |path| {
let name = path.file_name().unwrap().to_str().unwrap();
name.contains("dep") && name.ends_with(suffix)
});
assert!(!dep_bc_files.is_empty(), "dep bitcode files not found");
for dep_bc in &dep_bc_files {
llvm_dis().input(dep_bc).run();
let dep_ll = dep_bc.with_extension("ll");
for dep_cp in case.dep_prefixes {
llvm_filecheck()
.input_file(&dep_ll)
.patterns("lib.rs")
.check_prefix(dep_cp)
.run();
}
}
}
}
}
Loading