Skip to content

Commit fffba52

Browse files
committed
Auto merge of rust-lang#113923 - DianQK:restore-no-builtins-lto, r=pnkfelix
Restore `#![no_builtins]` crates participation in LTO. After rust-lang#113716, we can make `#![no_builtins]` crates participate in LTO again. `#![no_builtins]` with LTO does not result in undefined references to the error. I believe this type of issue won't happen again. Fixes rust-lang#72140. Fixes rust-lang#112245. Fixes rust-lang#110606. Fixes rust-lang#105734. Fixes rust-lang#96486. Fixes rust-lang#108853. Fixes rust-lang#108893. Fixes rust-lang#78744. Fixes rust-lang#91158. Fixes rust-lang/cargo#10118. Fixes rust-lang/compiler-builtins#347. The `nightly-2023-07-20` version does not always reproduce problems due to changes in compiler-builtins, core, and user code. That's why this issue recurs and disappears. Some issues were not tested due to the difficulty of reproducing them. r? pnkfelix cc `@bjorn3` `@japaric` `@alexcrichton` `@Amanieu`
2 parents 525c91d + 8d69a1e commit fffba52

File tree

16 files changed

+132
-89
lines changed

16 files changed

+132
-89
lines changed

compiler/rustc_codegen_llvm/src/back/write.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,6 @@ pub(crate) unsafe fn llvm_optimize(
571571
unroll_loops,
572572
config.vectorize_slp,
573573
config.vectorize_loop,
574-
config.no_builtins,
575574
config.emit_lifetime_markers,
576575
sanitizer_options.as_ref(),
577576
pgo_gen_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
@@ -686,15 +685,14 @@ pub(crate) unsafe fn codegen(
686685
unsafe fn with_codegen<'ll, F, R>(
687686
tm: &'ll llvm::TargetMachine,
688687
llmod: &'ll llvm::Module,
689-
no_builtins: bool,
690688
f: F,
691689
) -> R
692690
where
693691
F: FnOnce(&'ll mut PassManager<'ll>) -> R,
694692
{
695693
let cpm = llvm::LLVMCreatePassManager();
696694
llvm::LLVMAddAnalysisPasses(tm, cpm);
697-
llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins);
695+
llvm::LLVMRustAddLibraryInfo(cpm, llmod);
698696
f(cpm)
699697
}
700698

@@ -797,7 +795,7 @@ pub(crate) unsafe fn codegen(
797795
} else {
798796
llmod
799797
};
800-
with_codegen(tm, llmod, config.no_builtins, |cpm| {
798+
with_codegen(tm, llmod, |cpm| {
801799
write_output_file(
802800
diag_handler,
803801
tm,
@@ -832,7 +830,7 @@ pub(crate) unsafe fn codegen(
832830
(_, SplitDwarfKind::Split) => Some(dwo_out.as_path()),
833831
};
834832

835-
with_codegen(tm, llmod, config.no_builtins, |cpm| {
833+
with_codegen(tm, llmod, |cpm| {
836834
write_output_file(
837835
diag_handler,
838836
tm,

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -2138,13 +2138,8 @@ extern "C" {
21382138
ArgsCstrBuff: *const c_char,
21392139
ArgsCstrBuffLen: usize,
21402140
) -> *mut TargetMachine;
2141-
21422141
pub fn LLVMRustDisposeTargetMachine(T: *mut TargetMachine);
2143-
pub fn LLVMRustAddLibraryInfo<'a>(
2144-
PM: &PassManager<'a>,
2145-
M: &'a Module,
2146-
DisableSimplifyLibCalls: bool,
2147-
);
2142+
pub fn LLVMRustAddLibraryInfo<'a>(PM: &PassManager<'a>, M: &'a Module);
21482143
pub fn LLVMRustWriteOutputFile<'a>(
21492144
T: &'a TargetMachine,
21502145
PM: &PassManager<'a>,
@@ -2166,7 +2161,6 @@ extern "C" {
21662161
UnrollLoops: bool,
21672162
SLPVectorize: bool,
21682163
LoopVectorize: bool,
2169-
DisableSimplifyLibCalls: bool,
21702164
EmitLifetimeMarkers: bool,
21712165
SanitizerOptions: Option<&SanitizerOptions>,
21722166
PGOGenPath: *const c_char,

compiler/rustc_codegen_ssa/src/back/link.rs

+12-34
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,14 @@ pub fn each_linked_rlib(
270270

271271
for &cnum in crates {
272272
match fmts.get(cnum.as_usize() - 1) {
273-
Some(&Linkage::NotLinked | &Linkage::Dynamic | &Linkage::IncludedFromDylib) => continue,
274-
Some(_) => {}
273+
Some(&Linkage::NotLinked | &Linkage::Dynamic) => continue,
274+
Some(&Linkage::IncludedFromDylib) => {
275+
// We always link crate `compiler_builtins` statically. When enabling LTO, we include it as well.
276+
if info.compiler_builtins != Some(cnum) {
277+
continue;
278+
}
279+
}
280+
Some(&Linkage::Static) => {}
275281
None => return Err(errors::LinkRlibError::MissingFormat),
276282
}
277283
let crate_name = info.crate_name[&cnum];
@@ -520,8 +526,7 @@ fn link_staticlib<'a>(
520526
&codegen_results.crate_info,
521527
Some(CrateType::Staticlib),
522528
&mut |cnum, path| {
523-
let lto = are_upstream_rust_objects_already_included(sess)
524-
&& !ignored_for_lto(sess, &codegen_results.crate_info, cnum);
529+
let lto = are_upstream_rust_objects_already_included(sess);
525530

526531
let native_libs = codegen_results.crate_info.native_libraries[&cnum].iter();
527532
let relevant = native_libs.clone().filter(|lib| relevant_lib(sess, &lib));
@@ -1256,24 +1261,6 @@ fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) {
12561261
}
12571262
}
12581263

1259-
/// Returns a boolean indicating whether the specified crate should be ignored
1260-
/// during LTO.
1261-
///
1262-
/// Crates ignored during LTO are not lumped together in the "massive object
1263-
/// file" that we create and are linked in their normal rlib states. See
1264-
/// comments below for what crates do not participate in LTO.
1265-
///
1266-
/// It's unusual for a crate to not participate in LTO. Typically only
1267-
/// compiler-specific and unstable crates have a reason to not participate in
1268-
/// LTO.
1269-
pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool {
1270-
// If our target enables builtin function lowering in LLVM then the
1271-
// crates providing these functions don't participate in LTO (e.g.
1272-
// no_builtins or compiler builtins crates).
1273-
!sess.target.no_builtins
1274-
&& (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum))
1275-
}
1276-
12771264
/// This functions tries to determine the appropriate linker (and corresponding LinkerFlavor) to use
12781265
pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
12791266
fn infer_from(
@@ -2734,10 +2721,6 @@ fn rehome_sysroot_lib_dir<'a>(sess: &'a Session, lib_dir: &Path) -> PathBuf {
27342721
// symbols). We must continue to include the rest of the rlib, however, as
27352722
// it may contain static native libraries which must be linked in.
27362723
//
2737-
// (*) Crates marked with `#![no_builtins]` don't participate in LTO and
2738-
// their bytecode wasn't included. The object files in those libraries must
2739-
// still be passed to the linker.
2740-
//
27412724
// Note, however, that if we're not doing LTO we can just pass the rlib
27422725
// blindly to the linker (fast) because it's fine if it's not actually
27432726
// included as we're at the end of the dependency chain.
@@ -2763,9 +2746,7 @@ fn add_static_crate<'a>(
27632746
cmd.link_rlib(&rlib_path);
27642747
};
27652748

2766-
if !are_upstream_rust_objects_already_included(sess)
2767-
|| ignored_for_lto(sess, &codegen_results.crate_info, cnum)
2768-
{
2749+
if !are_upstream_rust_objects_already_included(sess) {
27692750
link_upstream(cratepath);
27702751
return;
27712752
}
@@ -2779,8 +2760,6 @@ fn add_static_crate<'a>(
27792760
let canonical_name = name.replace('-', "_");
27802761
let upstream_rust_objects_already_included =
27812762
are_upstream_rust_objects_already_included(sess);
2782-
let is_builtins =
2783-
sess.target.no_builtins || !codegen_results.crate_info.is_no_builtins.contains(&cnum);
27842763

27852764
let mut archive = archive_builder_builder.new_archive_builder(sess);
27862765
if let Err(error) = archive.add_archive(
@@ -2797,9 +2776,8 @@ fn add_static_crate<'a>(
27972776

27982777
// If we're performing LTO and this is a rust-generated object
27992778
// file, then we don't need the object file as it's part of the
2800-
// LTO module. Note that `#![no_builtins]` is excluded from LTO,
2801-
// though, so we let that object file slide.
2802-
if upstream_rust_objects_already_included && is_rust_object && is_builtins {
2779+
// LTO module.
2780+
if upstream_rust_objects_already_included && is_rust_object {
28032781
return true;
28042782
}
28052783

compiler/rustc_codegen_ssa/src/back/symbol_export.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
5454
// export level, however, as they're just implementation details.
5555
// Down below we'll hardwire all of the symbols to the `Rust` export
5656
// level instead.
57-
let special_runtime_crate =
58-
tcx.is_panic_runtime(LOCAL_CRATE) || tcx.is_compiler_builtins(LOCAL_CRATE);
57+
let is_compiler_builtins = tcx.is_compiler_builtins(LOCAL_CRATE);
58+
let special_runtime_crate = tcx.is_panic_runtime(LOCAL_CRATE) || is_compiler_builtins;
5959

6060
let mut reachable_non_generics: DefIdMap<_> = tcx
6161
.reachable_set(())
@@ -107,7 +107,11 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
107107
.map(|def_id| {
108108
// We won't link right if this symbol is stripped during LTO.
109109
let name = tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())).name;
110-
let used = name == "rust_eh_personality";
110+
// We have to preserve the symbols of the built-in functions during LTO.
111+
let is_builtin_fn = is_compiler_builtins
112+
&& symbol_export_level(tcx, def_id.to_def_id())
113+
.is_below_threshold(SymbolExportLevel::C);
114+
let used = is_builtin_fn || name == "rust_eh_personality";
111115

112116
let export_level = if special_runtime_crate {
113117
SymbolExportLevel::Rust

compiler/rustc_codegen_ssa/src/back/write.rs

+1-15
Original file line numberDiff line numberDiff line change
@@ -148,23 +148,12 @@ impl ModuleConfig {
148148

149149
let emit_obj = if !should_emit_obj {
150150
EmitObj::None
151-
} else if sess.target.obj_is_bitcode
152-
|| (sess.opts.cg.linker_plugin_lto.enabled() && !no_builtins)
153-
{
151+
} else if sess.target.obj_is_bitcode || sess.opts.cg.linker_plugin_lto.enabled() {
154152
// This case is selected if the target uses objects as bitcode, or
155153
// if linker plugin LTO is enabled. In the linker plugin LTO case
156154
// the assumption is that the final link-step will read the bitcode
157155
// and convert it to object code. This may be done by either the
158156
// native linker or rustc itself.
159-
//
160-
// Note, however, that the linker-plugin-lto requested here is
161-
// explicitly ignored for `#![no_builtins]` crates. These crates are
162-
// specifically ignored by rustc's LTO passes and wouldn't work if
163-
// loaded into the linker. These crates define symbols that LLVM
164-
// lowers intrinsics to, and these symbol dependencies aren't known
165-
// until after codegen. As a result any crate marked
166-
// `#![no_builtins]` is assumed to not participate in LTO and
167-
// instead goes on to generate object code.
168157
EmitObj::Bitcode
169158
} else if need_bitcode_in_object(tcx) {
170159
EmitObj::ObjectCode(BitcodeSection::Full)
@@ -1037,9 +1026,6 @@ fn start_executing_work<B: ExtraBackendMethods>(
10371026

10381027
let mut each_linked_rlib_for_lto = Vec::new();
10391028
drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| {
1040-
if link::ignored_for_lto(sess, crate_info, cnum) {
1041-
return;
1042-
}
10431029
each_linked_rlib_for_lto.push((cnum, path.to_path_buf()));
10441030
}));
10451031

compiler/rustc_codegen_ssa/src/base.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,6 @@ impl CrateInfo {
854854
local_crate_name,
855855
compiler_builtins,
856856
profiler_runtime: None,
857-
is_no_builtins: Default::default(),
858857
native_libraries: Default::default(),
859858
used_libraries: tcx.native_libraries(LOCAL_CRATE).iter().map(Into::into).collect(),
860859
crate_name: Default::default(),
@@ -881,19 +880,14 @@ impl CrateInfo {
881880
if tcx.is_profiler_runtime(cnum) {
882881
info.profiler_runtime = Some(cnum);
883882
}
884-
if tcx.is_no_builtins(cnum) {
885-
info.is_no_builtins.insert(cnum);
886-
}
887883
}
888884

889885
// Handle circular dependencies in the standard library.
890886
// See comment before `add_linked_symbol_object` function for the details.
891887
// If global LTO is enabled then almost everything (*) is glued into a single object file,
892888
// so this logic is not necessary and can cause issues on some targets (due to weak lang
893889
// item symbols being "privatized" to that object file), so we disable it.
894-
// (*) Native libs, and `#[compiler_builtins]` and `#[no_builtins]` crates are not glued,
895-
// and we assume that they cannot define weak lang items. This is not currently enforced
896-
// by the compiler, but that's ok because all this stuff is unstable anyway.
890+
// (*) Native libs are not glued, and we assume that they cannot define weak lang items.
897891
let target = &tcx.sess.target;
898892
if !are_upstream_rust_objects_already_included(tcx.sess) {
899893
let missing_weak_lang_items: FxHashSet<Symbol> = info

compiler/rustc_codegen_ssa/src/lib.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ extern crate tracing;
2525
extern crate rustc_middle;
2626

2727
use rustc_ast as ast;
28-
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
28+
use rustc_data_structures::fx::FxHashMap;
2929
use rustc_data_structures::sync::Lrc;
3030
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
3131
use rustc_fluent_macro::fluent_messages;
@@ -159,7 +159,6 @@ pub struct CrateInfo {
159159
pub local_crate_name: Symbol,
160160
pub compiler_builtins: Option<CrateNum>,
161161
pub profiler_runtime: Option<CrateNum>,
162-
pub is_no_builtins: FxHashSet<CrateNum>,
163162
pub native_libraries: FxHashMap<CrateNum, Vec<NativeLib>>,
164163
pub crate_name: FxHashMap<CrateNum, Symbol>,
165164
pub used_libraries: Vec<NativeLib>,

compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp

+2-7
Original file line numberDiff line numberDiff line change
@@ -535,12 +535,9 @@ extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) {
535535

536536
// Unfortunately, the LLVM C API doesn't provide a way to create the
537537
// TargetLibraryInfo pass, so we use this method to do so.
538-
extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M,
539-
bool DisableSimplifyLibCalls) {
538+
extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M) {
540539
Triple TargetTriple(unwrap(M)->getTargetTriple());
541540
TargetLibraryInfoImpl TLII(TargetTriple);
542-
if (DisableSimplifyLibCalls)
543-
TLII.disableAllFunctions();
544541
unwrap(PMR)->add(new TargetLibraryInfoWrapperPass(TLII));
545542
}
546543

@@ -707,7 +704,7 @@ LLVMRustOptimize(
707704
bool IsLinkerPluginLTO,
708705
bool NoPrepopulatePasses, bool VerifyIR, bool UseThinLTOBuffers,
709706
bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize,
710-
bool DisableSimplifyLibCalls, bool EmitLifetimeMarkers,
707+
bool EmitLifetimeMarkers,
711708
LLVMRustSanitizerOptions *SanitizerOptions,
712709
const char *PGOGenPath, const char *PGOUsePath,
713710
bool InstrumentCoverage, const char *InstrProfileOutput,
@@ -813,8 +810,6 @@ LLVMRustOptimize(
813810

814811
Triple TargetTriple(TheModule->getTargetTriple());
815812
std::unique_ptr<TargetLibraryInfoImpl> TLII(new TargetLibraryInfoImpl(TargetTriple));
816-
if (DisableSimplifyLibCalls)
817-
TLII->disableAllFunctions();
818813
FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); });
819814

820815
PB.registerModuleAnalyses(MAM);
+12-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
include ../tools.mk
22

3+
# only-x86_64
4+
5+
# We want to check that `no_builtins` is correctly participating in LTO.
6+
# First, verify that the `foo::foo` symbol can be found when linking.
7+
# Next, verify that `memcpy` can be customized using `no_builtins` under LTO.
8+
# Others will use the built-in memcpy.
9+
310
all:
4-
# Compile a `#![no_builtins]` rlib crate
5-
$(RUSTC) no_builtins.rs
6-
# Build an executable that depends on that crate using LTO. The no_builtins crate doesn't
7-
# participate in LTO, so its rlib must be explicitly linked into the final binary. Verify this by
8-
# grepping the linker arguments.
9-
$(RUSTC) main.rs -C lto --print link-args | $(CGREP) 'libno_builtins.rlib'
11+
$(RUSTC) -C linker-plugin-lto -C opt-level=2 -C debuginfo=0 foo.rs
12+
$(RUSTC) -C linker-plugin-lto -C opt-level=2 -C debuginfo=0 no_builtins.rs
13+
$(RUSTC) main.rs -C lto -C opt-level=2 -C debuginfo=0 -C save-temps -C metadata=1 -C codegen-units=1
14+
$(LLVM_BIN_DIR)/llvm-dis $(TMPDIR)/main.main.*-cgu.0.rcgu.lto.input.bc -o $(TMPDIR)/lto.ll
15+
cat "$(TMPDIR)"/lto.ll | "$(LLVM_FILECHECK)" filecheck.lto.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
CHECK: define{{.*}} void @bar
2+
CHECK-NEXT: call void @no_builtins
3+
CHECK-NEXT: call void @llvm.memcpy
4+
5+
CHECK: define{{.*}} i32 @main
6+
CHECK: call void @bar
7+
8+
CHECK: define{{.*}} void @foo
9+
CHECK-NEXT: call void @llvm.memcpy
10+
11+
CHECK: define{{.*}} void @no_builtins
12+
CHECK-SAME: #[[ATTR:[0-9]+]] {
13+
CHECK: call void @foo
14+
CHECK-NEXT: call{{.*}} @memcpy
15+
16+
CHECK: attributes #[[ATTR]]
17+
CHECK-SAME: no-builtins

tests/run-make/no-builtins-lto/foo.rs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#![feature(lang_items, no_core)]
2+
#![no_std]
3+
#![no_core]
4+
#![crate_type = "lib"]
5+
6+
#[inline(never)]
7+
#[no_mangle]
8+
pub unsafe fn foo(dest: *mut u8, src: *const u8) {
9+
// should call `@llvm.memcpy`.
10+
memcpy(dest, src, 1024);
11+
}
12+
13+
#[no_mangle]
14+
#[inline(never)]
15+
pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, _n: usize) -> *mut u8 {
16+
*dest = 0;
17+
return src as *mut u8;
18+
}
19+
20+
#[lang = "sized"]
21+
trait Sized {}
22+
#[lang = "copy"]
23+
trait Copy {}
24+
impl Copy for *mut u8 {}
25+
impl Copy for *const u8 {}
26+
27+
#[lang = "drop_in_place"]
28+
#[allow(unconditional_recursion)]
29+
pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
30+
// Code here does not matter - this is replaced by the
31+
// real drop glue by the compiler.
32+
drop_in_place(to_drop);
33+
}

0 commit comments

Comments
 (0)