Skip to content

Commit 16f306c

Browse files
Rollup merge of #162309 - sgasho:offload-clang-linker-wrapper, r=ZuseZ4
offload: automate manual clang-linker-wrapper step automate manual clang-linker-wrapper step from https://rustc-dev-guide.rust-lang.org/offload/usage.html extract bitcode from device.bin and then wraps it into the host module. needs some refactoring. r? @ZuseZ4
2 parents 745de6e + ff680e2 commit 16f306c

8 files changed

Lines changed: 315 additions & 122 deletions

File tree

compiler/rustc_codegen_llvm/src/back/write.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,8 @@ pub(crate) unsafe fn llvm_optimize(
612612
let pgo_use_path = get_pgo_use_path(config);
613613
let pgo_sample_use_path = get_pgo_sample_use_path(config);
614614
let is_lto = opt_stage == llvm::OptStage::ThinLTO || opt_stage == llvm::OptStage::FatLTO;
615+
let is_final_stage =
616+
!matches!(opt_stage, llvm::OptStage::PreLinkFatLTO | llvm::OptStage::PreLinkThinLTO);
615617
let instr_profile_output_path = get_instr_profile_output_path(config);
616618
let sanitize_dataflow_abilist: Vec<_> = config
617619
.sanitizer_dataflow_abilist
@@ -840,7 +842,7 @@ pub(crate) unsafe fn llvm_optimize(
840842
// don't need any other artifacts from the previous run. We will embed this artifact into our
841843
// LLVM-IR host module, to create a `host.o` ObjectFile, which we will write to disk.
842844
// The last, not yet automated steps uses the `clang-linker-wrapper` to process `host.o`.
843-
if !cgcx.target_is_like_gpu {
845+
if !cgcx.target_is_like_gpu && is_final_stage {
844846
if let Some(device_path) = config
845847
.offload
846848
.iter()
@@ -866,10 +868,11 @@ pub(crate) unsafe fn llvm_optimize(
866868
// 2) Finalize host: lib.bc + device.bin -> host.o (host TM)
867869
// We create a full clone of our LLVM host module, since we will embed the device IR
868870
// into it, and this might break caching or incremental compilation otherwise.
869-
let llmod2 = llvm::LLVMCloneModule(module.module_llvm.llmod());
870871
let ok = unsafe {
871-
llvm::RustOffloadWrapper::get_instance()
872-
.llvm_rust_offload_embed_buffer_in_module(llmod2, device_bin_c.as_c_str())
872+
llvm::RustOffloadWrapper::get_instance().llvm_rust_offload_embed_buffer_in_module(
873+
module.module_llvm.llmod(),
874+
device_bin_c.as_c_str(),
875+
)
873876
};
874877
if !ok {
875878
dcx.emit_err(crate::diagnostics::OffloadEmbedFailed);
@@ -878,7 +881,7 @@ pub(crate) unsafe fn llvm_optimize(
878881
dcx,
879882
module.module_llvm.tm.raw(),
880883
config.no_builtins,
881-
llmod2,
884+
module.module_llvm.llmod(),
882885
&out_obj,
883886
None,
884887
llvm::FileType::ObjectFile,
@@ -888,6 +891,16 @@ pub(crate) unsafe fn llvm_optimize(
888891
// We ignore cgcx.save_temps here and unconditionally always keep our `device.bin` artifact.
889892
// Otherwise, recompiling the host code would fail since we deleted that device artifact
890893
// in the previous host compilation, which would be confusing at best.
894+
895+
let ok = unsafe {
896+
llvm::RustOffloadWrapper::get_instance().llvm_rust_offload_wrap_images(
897+
module.module_llvm.llmod(),
898+
device_bin_c.as_c_str(),
899+
)
900+
};
901+
if !ok {
902+
dcx.emit_err(crate::diagnostics::OffloadWrapImagesFailed);
903+
}
891904
}
892905
}
893906
result.into_result().unwrap_or_else(|()| llvm_err(dcx, LlvmError::RunLlvmPasses))

compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs

Lines changed: 0 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -57,80 +57,6 @@ impl<'ll> OffloadGlobals<'ll> {
5757
}
5858
}
5959

60-
// We need to register offload before using it. We also should unregister it once we are done, for
61-
// good measures. Previously we have done so before and after each individual offload intrinsic
62-
// call, but that comes at a performance cost. The repeated (un)register calls might also confuse
63-
// the LLVM ompOpt pass, which tries to move operations to a better location. The easiest solution,
64-
// which we copy from clang, is to just have those two calls once, in the global ctor/dtor section
65-
// of the final binary.
66-
pub(crate) fn register_offload<'ll>(cx: &CodegenCx<'ll, '_>) {
67-
// First we check quickly whether we already have done our setup, in which case we return early.
68-
// Shouldn't be needed for correctness.
69-
let register_lib_name = "__tgt_register_lib";
70-
if cx.get_function(register_lib_name).is_some() {
71-
return;
72-
}
73-
74-
let reg_lib_decl = cx.type_func(&[cx.type_ptr()], cx.type_void());
75-
let register_lib = declare_offload_fn(&cx, register_lib_name, reg_lib_decl);
76-
let unregister_lib = declare_offload_fn(&cx, "__tgt_unregister_lib", reg_lib_decl);
77-
78-
let ptr_null = cx.const_null(cx.type_ptr());
79-
let const_struct = cx.const_struct(&[cx.get_const_i32(0), ptr_null, ptr_null, ptr_null], false);
80-
let omp_descriptor =
81-
add_global(cx, ".omp_offloading.descriptor", const_struct, InternalLinkage);
82-
// @.omp_offloading.descriptor = internal constant %__tgt_bin_desc { i32 1, ptr @.omp_offloading.device_images, ptr @__start_llvm_offload_entries, ptr @__stop_llvm_offload_entries }
83-
// @.omp_offloading.descriptor = internal constant %__tgt_bin_desc { i32 0, ptr null, ptr null, ptr null }
84-
85-
let atexit = cx.type_func(&[cx.type_ptr()], cx.type_i32());
86-
let atexit_fn = declare_offload_fn(cx, "atexit", atexit);
87-
88-
// FIXME(offload): Drop this, once we fully automated our offload compilation pipeline, since
89-
// LLVM will initialize them for us if it sees gpu kernels being registered.
90-
let init_ty = cx.type_func(&[], cx.type_void());
91-
let init_rtls = declare_offload_fn(cx, "__tgt_init_all_rtls", init_ty);
92-
93-
let desc_ty = cx.type_func(&[], cx.type_void());
94-
let reg_name = ".omp_offloading.descriptor_reg";
95-
let unreg_name = ".omp_offloading.descriptor_unreg";
96-
let desc_reg_fn = declare_offload_fn(cx, reg_name, desc_ty);
97-
let desc_unreg_fn = declare_offload_fn(cx, unreg_name, desc_ty);
98-
llvm::set_linkage(desc_reg_fn, InternalLinkage);
99-
llvm::set_linkage(desc_unreg_fn, InternalLinkage);
100-
llvm::set_section(desc_reg_fn, c".text.startup");
101-
llvm::set_section(desc_unreg_fn, c".text.startup");
102-
103-
// define internal void @.omp_offloading.descriptor_reg() section ".text.startup" {
104-
// entry:
105-
// call void @__tgt_register_lib(ptr @.omp_offloading.descriptor)
106-
// call void @__tgt_init_all_rtls()
107-
// %0 = call i32 @atexit(ptr @.omp_offloading.descriptor_unreg)
108-
// ret void
109-
// }
110-
let bb = Builder::append_block(cx, desc_reg_fn, "entry");
111-
let mut a = Builder::build(cx, bb);
112-
a.call(reg_lib_decl, None, None, register_lib, &[omp_descriptor], None, None);
113-
a.call(init_ty, None, None, init_rtls, &[], None, None);
114-
a.call(atexit, None, None, atexit_fn, &[desc_unreg_fn], None, None);
115-
a.ret_void();
116-
117-
// define internal void @.omp_offloading.descriptor_unreg() section ".text.startup" {
118-
// entry:
119-
// call void @__tgt_unregister_lib(ptr @.omp_offloading.descriptor)
120-
// ret void
121-
// }
122-
let bb = Builder::append_block(cx, desc_unreg_fn, "entry");
123-
let mut a = Builder::build(cx, bb);
124-
a.call(reg_lib_decl, None, None, unregister_lib, &[omp_descriptor], None, None);
125-
a.ret_void();
126-
127-
// @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 101, ptr @.omp_offloading.descriptor_reg, ptr null }]
128-
let args = vec![cx.get_const_i32(101), desc_reg_fn, ptr_null];
129-
let const_struct = cx.const_struct(&args, false);
130-
let arr = cx.const_array(cx.val_ty(const_struct), &[const_struct]);
131-
add_global(cx, "llvm.global_ctors", arr, AppendingLinkage);
132-
}
133-
13460
pub(crate) struct OffloadKernelDims<'ll> {
13561
num_workgroups: &'ll Value,
13662
threads_per_block: &'ll Value,

compiler/rustc_codegen_llvm/src/context.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -918,11 +918,6 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
918918
self.get_const_int(self.type_i8(), n)
919919
}
920920

921-
pub(crate) fn get_function(&self, name: &str) -> Option<&'ll Value> {
922-
let name = SmallCStr::new(name);
923-
unsafe { llvm::LLVMGetNamedFunction((**self).borrow().llmod, name.as_ptr()) }
924-
}
925-
926921
pub(crate) fn get_md_kind_id(&self, name: &str) -> llvm::MetadataKindId {
927922
unsafe {
928923
llvm::LLVMGetMDKindIDInContext(

compiler/rustc_codegen_llvm/src/diagnostics.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ pub(crate) struct OffloadBundleImagesFailed;
107107
#[diag("call to EmbedBufferInModule failed, `host.o` was not created")]
108108
pub(crate) struct OffloadEmbedFailed;
109109

110+
#[derive(Diagnostic)]
111+
#[diag("call to WrapImages failed, device image was not wrapped into the host module")]
112+
pub(crate) struct OffloadWrapImagesFailed;
113+
110114
#[derive(Diagnostic)]
111115
#[diag("failed to get bitcode from object file for LTO ({$err})")]
112116
pub(crate) struct LtoBitcodeFromRlib {

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@ use tracing::debug;
3636
use crate::abi::FnAbiLlvmExt;
3737
use crate::builder::Builder;
3838
use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call};
39-
use crate::builder::gpu_offload::{
40-
self, OffloadKernelDims, declare_omp_get_num_devices, register_offload,
41-
};
39+
use crate::builder::gpu_offload::{self, OffloadKernelDims, declare_omp_get_num_devices};
4240
use crate::context::CodegenCx;
4341
use crate::declare::declare_raw_fn;
4442
use crate::diagnostics::{
@@ -1900,7 +1898,6 @@ fn codegen_offload<'ll, 'tcx>(
19001898
return;
19011899
}
19021900
};
1903-
register_offload(cx);
19041901
let offload_data =
19051902
gpu_offload::gen_define_handling(&cx, &metadata, target_symbol, offload_globals);
19061903
gpu_offload::gen_call_handling(

compiler/rustc_codegen_llvm/src/llvm/offload_ffi.rs

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
use std::ffi::{CStr, c_char};
2+
use std::path::PathBuf;
23
use std::sync::OnceLock;
34

45
use super::ffi::{Module, TargetMachine, Value};
56

67
type LLVMRustBundleImagesFn = unsafe extern "C" fn(&Module, &TargetMachine, *const c_char) -> bool;
78
type LLVMRustOffloadEmbedBufferInModuleFn = unsafe extern "C" fn(&Module, *const c_char) -> bool;
89
type LLVMRustOffloadMapperFn = unsafe extern "C" fn(&Value, &Value, *const &Value);
10+
type LLVMRustOffloadWrapImagesFn =
11+
unsafe extern "C" fn(&Module, *const c_char, *const c_char) -> bool;
912

13+
use rustc_fs_util::path_to_c_string;
1014
use rustc_session::config::host_tuple;
1115
use rustc_session::filesearch;
1216

@@ -16,6 +20,8 @@ pub(crate) struct RustOffloadWrapper {
1620
LLVMRustBundleImages: LLVMRustBundleImagesFn,
1721
LLVMRustOffloadEmbedBufferInModule: LLVMRustOffloadEmbedBufferInModuleFn,
1822
LLVMRustOffloadMapper: LLVMRustOffloadMapperFn,
23+
LLVMRustOffloadWrapImages: LLVMRustOffloadWrapImagesFn,
24+
lld_path: Option<PathBuf>,
1925
// Keep the dynamic library loaded while the function pointers are used.
2026
_lib: libloading::Library,
2127
}
@@ -71,10 +77,21 @@ impl RustOffloadWrapper {
7177
unsafe { (self.LLVMRustOffloadMapper)(v1, v2, vs.as_ptr()) }
7278
}
7379

80+
pub(crate) unsafe fn llvm_rust_offload_wrap_images(
81+
&self,
82+
host_m: &Module,
83+
device_bin_path: &CStr,
84+
) -> bool {
85+
let lld_c = self.lld_path.as_deref().map(path_to_c_string).unwrap_or_default();
86+
unsafe {
87+
(self.LLVMRustOffloadWrapImages)(host_m, lld_c.as_ptr(), device_bin_path.as_ptr())
88+
}
89+
}
90+
7491
fn call_dynamic(
7592
sysroot: &rustc_session::config::Sysroot,
7693
) -> Result<Self, RustOffloadLibraryError> {
77-
let rust_offload_path = Self::get_rust_offload_path(sysroot)?;
94+
let (rust_offload_path, lld_path) = Self::get_offload_and_lld_paths(sysroot)?;
7895
let lib = unsafe { libloading::Library::new(rust_offload_path)? };
7996

8097
let llvm_rust_bundle_images =
@@ -86,48 +103,47 @@ impl RustOffloadWrapper {
86103
};
87104
let llvm_rust_offload_wrapper =
88105
*unsafe { lib.get::<LLVMRustOffloadMapperFn>(b"LLVMRustOffloadMapper\0")? };
106+
let llvm_rust_offload_wrap_images =
107+
*unsafe { lib.get::<LLVMRustOffloadWrapImagesFn>(b"LLVMRustOffloadWrapImages\0")? };
89108

90109
Ok(Self {
91110
LLVMRustBundleImages: llvm_rust_bundle_images,
92111
LLVMRustOffloadEmbedBufferInModule: llvm_rust_offload_embed_buffer_in_module,
93112
LLVMRustOffloadMapper: llvm_rust_offload_wrapper,
113+
LLVMRustOffloadWrapImages: llvm_rust_offload_wrap_images,
114+
lld_path,
94115
_lib: lib,
95116
})
96117
}
97118

98-
fn get_rust_offload_path(
119+
fn get_offload_and_lld_paths(
99120
sysroot: &rustc_session::config::Sysroot,
100-
) -> Result<String, RustOffloadLibraryError> {
121+
) -> Result<(PathBuf, Option<PathBuf>), RustOffloadLibraryError> {
101122
let llvm_version_major = unsafe { LLVMRustVersionMajor() };
102-
103-
let path_buf = sysroot
104-
.all_paths()
105-
.find_map(|p| {
106-
let candidate = filesearch::make_target_lib_path(p, host_tuple())
107-
.join(format!("libRustOffload-{}", llvm_version_major))
108-
.with_extension(std::env::consts::DLL_EXTENSION);
109-
110-
candidate.exists().then_some(candidate)
111-
})
112-
.ok_or_else(|| {
113-
let candidates = sysroot
114-
.all_paths()
115-
.map(|p| p.join("lib").display().to_string())
116-
.collect::<Vec<String>>()
117-
.join("\n* ");
118-
RustOffloadLibraryError::NotFound {
119-
err: format!(
120-
"failed to find a `libRustOffload-{llvm_version_major}` \
121-
in the sysroot candidates:\n* {candidates}"
122-
),
123-
}
124-
})?;
125-
126-
Ok(path_buf
127-
.to_str()
128-
.ok_or_else(|| RustOffloadLibraryError::LoadFailed {
129-
err: format!("invalid UTF-8 in path: {}", path_buf.display()),
130-
})?
131-
.to_string())
123+
let mut searched = Vec::new();
124+
125+
for root in sysroot.all_paths() {
126+
let rust_offload_path = filesearch::make_target_lib_path(root, host_tuple())
127+
.join(format!("libRustOffload-{llvm_version_major}"))
128+
.with_extension(std::env::consts::DLL_EXTENSION);
129+
130+
if !rust_offload_path.is_file() {
131+
searched.push(rust_offload_path);
132+
continue;
133+
}
134+
135+
let lld_path = filesearch::make_target_bin_path(root, host_tuple())
136+
.join(format!("rust-lld{}", std::env::consts::EXE_SUFFIX));
137+
let lld_path = lld_path.is_file().then_some(lld_path);
138+
139+
return Ok((rust_offload_path, lld_path));
140+
}
141+
142+
Err(RustOffloadLibraryError::NotFound {
143+
err: format!(
144+
"could not find libRustOffload-{llvm_version_major} in the sysroot candidates:\n* {}",
145+
searched.iter().map(|p| p.display().to_string()).collect::<Vec<_>>().join("\n* ")
146+
),
147+
})
132148
}
133149
}

compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3029,6 +3029,14 @@ fn linker_with_args(
30293029
link_output_kind,
30303030
);
30313031

3032+
if sess.opts.unstable_opts.offload.iter().any(|o| matches!(o, config::Offload::Host(_))) {
3033+
cmd.link_dylib_by_name("omptarget", false, true);
3034+
cmd.link_dylib_by_name("omp", false, true);
3035+
cmd.link_args(["-z", "nostart-stop-gc"]);
3036+
cmd.link_arg("-rpath");
3037+
cmd.link_arg(std::path::absolute(&*sess.target_tlib_path.dir).unwrap());
3038+
}
3039+
30323040
// Upstream rust crates and their non-dynamic native libraries.
30333041
add_upstream_rust_crates(
30343042
cmd,

0 commit comments

Comments
 (0)