From 0238ef92794018efe672439a5f6ed22bd58bbc75 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Thu, 3 Sep 2026 16:42:07 +0200 Subject: [PATCH 1/3] change compile-flags for asm ui tests to not embed bitcode or use LTO --- tests/ui/asm/aarch64/srcloc.rs | 2 +- tests/ui/asm/inline-syntax.arm.stderr | 11 +---------- tests/ui/asm/inline-syntax.rs | 5 ++--- tests/ui/asm/riscv/riscv32e-registers.rs | 2 +- tests/ui/asm/x86_64/srcloc.rs | 2 +- 5 files changed, 6 insertions(+), 16 deletions(-) diff --git a/tests/ui/asm/aarch64/srcloc.rs b/tests/ui/asm/aarch64/srcloc.rs index 91a2ef3514aee..b5e77c4023970 100644 --- a/tests/ui/asm/aarch64/srcloc.rs +++ b/tests/ui/asm/aarch64/srcloc.rs @@ -1,7 +1,7 @@ //@ add-minicore //@ build-fail //@ needs-asm-support -//@ compile-flags: --target aarch64-unknown-linux-gnu -Ccodegen-units=1 +//@ compile-flags: --target aarch64-unknown-linux-gnu -Ccodegen-units=1 -Cembed-bitcode=false -Clto=no //@ needs-llvm-components: aarch64 //@ ignore-backends: gcc #![crate_type = "lib"] diff --git a/tests/ui/asm/inline-syntax.arm.stderr b/tests/ui/asm/inline-syntax.arm.stderr index 5b193d26c8776..315f97bb09de9 100644 --- a/tests/ui/asm/inline-syntax.arm.stderr +++ b/tests/ui/asm/inline-syntax.arm.stderr @@ -6,15 +6,6 @@ note: instantiated into assembly here LL | .intel_syntax noprefix | ^ -error: unknown directive - | -note: instantiated into assembly here - --> :1:1 - | -LL | .intel_syntax noprefix - | ^ - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error: unknown directive --> $DIR/inline-syntax.rs:21:15 | @@ -87,5 +78,5 @@ note: instantiated into assembly here LL | .intel_syntax noprefix | ^ -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/asm/inline-syntax.rs b/tests/ui/asm/inline-syntax.rs index 63395c1096c09..d7c9fc8972cb7 100644 --- a/tests/ui/asm/inline-syntax.rs +++ b/tests/ui/asm/inline-syntax.rs @@ -1,9 +1,9 @@ //@ add-minicore //@ revisions: x86_64 arm -//@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu +//@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu -Cembed-bitcode=false -Clto=no //@[x86_64] check-pass //@[x86_64] needs-llvm-components: x86 -//@[arm] compile-flags: --target armv7-unknown-linux-gnueabihf +//@[arm] compile-flags: --target armv7-unknown-linux-gnueabihf -Cembed-bitcode=false -Clto=no //@[arm] build-fail //@[arm] needs-llvm-components: arm //@[arm] min-llvm-version: 23 @@ -49,4 +49,3 @@ global_asm!(".intel_syntax noprefix", "nop"); // Global assembly errors don't have line numbers, so no error on ARM. //[arm]~? ERROR unknown directive -//[arm]~? ERROR unknown directive diff --git a/tests/ui/asm/riscv/riscv32e-registers.rs b/tests/ui/asm/riscv/riscv32e-registers.rs index a5f4151b2c80a..77a2d92c3736b 100644 --- a/tests/ui/asm/riscv/riscv32e-registers.rs +++ b/tests/ui/asm/riscv/riscv32e-registers.rs @@ -4,7 +4,7 @@ //@ build-fail //@ revisions: riscv32e_llvm23 riscv32em_llvm23 riscv32emc_llvm23 //@ revisions: riscv32e_llvm24 riscv32em_llvm24 riscv32emc_llvm24 -//@ compile-flags: --crate-type=rlib +//@ compile-flags: --crate-type=rlib -Cembed-bitcode=false -Clto=no //@ [riscv32e_llvm23] needs-llvm-components: riscv //@ [riscv32e_llvm23] compile-flags: --target=riscv32e-unknown-none-elf //@ [riscv32e_llvm23] max-llvm-major-version: 23 diff --git a/tests/ui/asm/x86_64/srcloc.rs b/tests/ui/asm/x86_64/srcloc.rs index e73854acf1522..9bbd20340e3ab 100644 --- a/tests/ui/asm/x86_64/srcloc.rs +++ b/tests/ui/asm/x86_64/srcloc.rs @@ -1,6 +1,6 @@ //@ add-minicore //@ build-fail -//@ compile-flags: --target x86_64-unknown-linux-gnu -Ccodegen-units=1 +//@ compile-flags: --target x86_64-unknown-linux-gnu -Ccodegen-units=1 -Cembed-bitcode=false -Clto=no //@ needs-llvm-components: x86 //@ ignore-backends: gcc #![crate_type = "lib"] From fa5601dd03bd9dfabe322e2c78af832f0128960d Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Thu, 3 Sep 2026 16:05:14 +0200 Subject: [PATCH 2/3] add test for inline asm cookie reproducibility --- .../inline-asm-cookie-issue-150451.rs | 9 ++++ .../rmake.rs | 42 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 tests/run-make/parallel-reproducible-inline-asm-cookie/inline-asm-cookie-issue-150451.rs create mode 100644 tests/run-make/parallel-reproducible-inline-asm-cookie/rmake.rs diff --git a/tests/run-make/parallel-reproducible-inline-asm-cookie/inline-asm-cookie-issue-150451.rs b/tests/run-make/parallel-reproducible-inline-asm-cookie/inline-asm-cookie-issue-150451.rs new file mode 100644 index 0000000000000..be49be470091e --- /dev/null +++ b/tests/run-make/parallel-reproducible-inline-asm-cookie/inline-asm-cookie-issue-150451.rs @@ -0,0 +1,9 @@ +use std::thread; + +fn _main() { + let _t1 = thread::spawn(|| { + for _ in 0..100 { + println!("test"); + } + }); +} diff --git a/tests/run-make/parallel-reproducible-inline-asm-cookie/rmake.rs b/tests/run-make/parallel-reproducible-inline-asm-cookie/rmake.rs new file mode 100644 index 0000000000000..62ad5a46af860 --- /dev/null +++ b/tests/run-make/parallel-reproducible-inline-asm-cookie/rmake.rs @@ -0,0 +1,42 @@ +//@ needs-target-std +//@ ignore-cross-compile +//@ ignore-windows-gnu +// GNU Linker for Windows is non-deterministic. (from `reproducible-build-2` test in this suite) + +use std::rc::Rc; + +use run_make_support::{bin_name, is_windows_msvc, rfs, run_in_tmpdir, rustc}; + +/// Test that parallel compiler produces identical binaries. +fn main() { + const FILE_NAME: &str = "inline-asm-cookie-issue-150451"; + let bin_name = bin_name(FILE_NAME); + + let mut reference = None; + + for _ in 0..10 { + // Tmp dir as previous runs affect output binary on windows. + run_in_tmpdir(|| { + let mut rustc = rustc(); + rustc + .input(format!("{FILE_NAME}.rs")) + .arg("--crate-type=lib") + .arg("-Zthreads=3") + .arg("-Clink-dead-code=true") + .arg("-Copt-level=0") + .arg("-Cembed-bitcode=true") + .output(&bin_name); + + if is_windows_msvc() { + rustc.arg("-Clink-arg=/Brepro"); + } + + rustc.run(); + + let current = Rc::new(rfs::read(&bin_name)); + reference.get_or_insert(Rc::clone(¤t)); + + assert_eq!(Some(current), reference); + }); + } +} From 970cb98f0ea3472c8bbb9152f74025ec73e03ef7 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Thu, 30 Jul 2026 08:11:34 +0200 Subject: [PATCH 3/3] Disable inline asm line info cookies when llvm bitcode is saved or LTO is enabled The parallel frontend makes the cookies nondeterministic in their current form, resulting in nondeterministic outputs when bitcode is emitted or LTO is used. Causes minor diagnostic regression for inline asm in release builds. --- .../rustc_codegen_cranelift/src/driver/aot.rs | 1 + compiler/rustc_codegen_gcc/src/lib.rs | 1 + compiler/rustc_codegen_llvm/src/asm.rs | 55 ++++++++++++------- compiler/rustc_codegen_llvm/src/base.rs | 11 +++- compiler/rustc_codegen_llvm/src/context.rs | 3 + compiler/rustc_codegen_llvm/src/lib.rs | 3 +- compiler/rustc_codegen_ssa/src/back/write.rs | 14 ++--- compiler/rustc_codegen_ssa/src/base.rs | 22 ++++++-- .../rustc_codegen_ssa/src/traits/backend.rs | 1 + 9 files changed, 74 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index d6c25cf524a5c..e06549090226e 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -284,6 +284,7 @@ impl ExtraBackendMethods for AotDriver { &self, tcx: TyCtxt<'_>, cgu_name: Symbol, + _bitcode_needed: bool, ) -> (ModuleCodegen, u64) { let start_time = Instant::now(); diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index cbc7db8e9e23f..2fb5459a20283 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -365,6 +365,7 @@ impl ExtraBackendMethods for GccCodegenBackend { &self, tcx: TyCtxt<'_>, cgu_name: Symbol, + _bitcode_needed: bool, ) -> (ModuleCodegen, u64) { base::compile_codegen_unit( tcx, diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 0f74f5e81d684..9960ee2ec64d0 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -10,6 +10,8 @@ use rustc_middle::mir::interpret::{PointerArithmetic, Scalar as ConstScalar}; use rustc_middle::ty::Instance; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{bug, span_bug}; +use rustc_session::Session; +use rustc_session::config::Lto; use rustc_span::{Pos, Span, Symbol, sym}; use rustc_target::asm::*; use rustc_target::spec::HasTargetSpec; @@ -594,30 +596,45 @@ pub(crate) fn inline_asm_call<'ll>( let key = "srcloc"; let kind = bx.get_md_kind_id(key); - // `srcloc` contains one 64-bit integer for each line of assembly code, - // where the lower 32 bits hold the lo byte position and the upper 32 bits - // hold the hi byte position. - let mut srcloc = vec![]; - if dia == llvm::AsmDialect::Intel && line_spans.len() > 1 { - // LLVM inserts an extra line to add the ".intel_syntax", so add - // a dummy srcloc entry for it. - // - // Don't do this if we only have 1 line span since that may be - // due to the asm template string coming from a macro. LLVM will - // default to the first srcloc for lines that don't have an - // associated srcloc. - srcloc.push(llvm::LLVMValueAsMetadata(bx.const_u64(0))); + if allow_raw_span_inline_asm_srcloc(bx.tcx.sess, bx.bitcode_needed) { + // `srcloc` contains one 64-bit integer for each line of assembly code, + // where the lower 32 bits hold the lo byte position and the upper 32 bits + // hold the hi byte position. + let mut srcloc = vec![]; + if dia == llvm::AsmDialect::Intel && line_spans.len() > 1 { + // LLVM inserts an extra line to add the ".intel_syntax", so add + // a dummy srcloc entry for it. + // + // Don't do this if we only have 1 line span since that may be + // due to the asm template string coming from a macro. LLVM will + // default to the first srcloc for lines that don't have an + // associated srcloc. + srcloc.push(llvm::LLVMValueAsMetadata(bx.const_u64(0))); + } + srcloc.extend(line_spans.iter().map(|span| { + llvm::LLVMValueAsMetadata( + bx.const_u64(u64::from(span.lo().to_u32()) | (u64::from(span.hi().to_u32()) << 32)), + ) + })); + bx.cx.set_metadata_node(call, kind, &srcloc); } - srcloc.extend(line_spans.iter().map(|span| { - llvm::LLVMValueAsMetadata( - bx.const_u64(u64::from(span.lo().to_u32()) | (u64::from(span.hi().to_u32()) << 32)), - ) - })); - bx.cx.set_metadata_node(call, kind, &srcloc); Some(call) } +/// Whenever inline assembly bitcode is built, its `srcloc` contains the raw span numbers +/// as location cookies. This is problematic since that is nondeterministic when using +/// the parallel frontend. Even without parallelism, the cookies are meaningless in another +/// rustc session. +/// +/// Discussion about replacing the cookies with something stable: rust-lang/rust#150451 +fn allow_raw_span_inline_asm_srcloc(sess: &Session, bitcode_needed: bool) -> bool { + // even for Lto::ThinLocal, where the bitcode isn't serialized into files, the changes in + // raw span positions would reflect in the LTO module hashes, which could lead to + // nondeterminism + sess.lto() == Lto::No && !bitcode_needed +} + /// If the register is an xmm/ymm/zmm register then return its index. fn xmm_reg_index(reg: InlineAsmReg) -> Option { use X86InlineAsmReg::*; diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 14700266412dd..401b52318539b 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -64,6 +64,7 @@ pub(crate) fn iter_global_aliases(llmod: &llvm::Module) -> ValueIter<'_> { pub(crate) fn compile_codegen_unit( tcx: TyCtxt<'_>, cgu_name: Symbol, + bitcode_needed: bool, ) -> (ModuleCodegen, u64) { let start_time = Instant::now(); @@ -71,7 +72,7 @@ pub(crate) fn compile_codegen_unit( let (module, _) = tcx.dep_graph.with_task( dep_node, tcx, - || module_codegen(tcx, cgu_name), + || module_codegen(tcx, cgu_name, bitcode_needed), Some(dep_graph::hash_result), ); let time_to_codegen = start_time.elapsed(); @@ -80,7 +81,11 @@ pub(crate) fn compile_codegen_unit( // the time we needed for codegenning it. let cost = time_to_codegen.as_nanos() as u64; - fn module_codegen(tcx: TyCtxt<'_>, cgu_name: Symbol) -> ModuleCodegen { + fn module_codegen( + tcx: TyCtxt<'_>, + cgu_name: Symbol, + needs_bitcode: bool, + ) -> ModuleCodegen { let cgu = tcx.codegen_unit(cgu_name); let _prof_timer = tcx.prof.generic_activity_with_arg_recorder("codegen_module", |recorder| { @@ -90,7 +95,7 @@ pub(crate) fn compile_codegen_unit( // Instantiate monomorphizations without filling out definitions yet... let llvm_module = ModuleLlvm::new(tcx, cgu_name.as_str()); { - let mut cx = CodegenCx::new(tcx, cgu, &llvm_module); + let mut cx = CodegenCx::new(tcx, cgu, &llvm_module, needs_bitcode); // Declare and store globals shared by all offload kernels // diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 853c4bfc9ca3f..66886fc080e88 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -92,6 +92,7 @@ pub(crate) type CodegenCx<'ll, 'tcx> = GenericCx<'ll, FullCx<'ll, 'tcx>>; pub(crate) struct FullCx<'ll, 'tcx> { pub tcx: TyCtxt<'tcx>, + pub bitcode_needed: bool, pub scx: SimpleCx<'ll>, pub use_dll_storage_attrs: bool, pub tls_model: llvm::ThreadLocalMode, @@ -606,6 +607,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { tcx: TyCtxt<'tcx>, codegen_unit: &'tcx CodegenUnit<'tcx>, llvm_module: &'ll crate::ModuleLlvm, + bitcode_needed: bool, ) -> Self { // An interesting part of Windows which MSVC forces our hand on (and // apparently MinGW didn't) is the usage of `dllimport` and `dllexport` @@ -683,6 +685,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { GenericCx( FullCx { tcx, + bitcode_needed, scx: SimpleCx::new(llmod, llcx, tcx.data_layout.pointer_size()), use_dll_storage_attrs, tls_model, diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 775e1dcf2ffea..ca16d33b90256 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -112,8 +112,9 @@ impl ExtraBackendMethods for LlvmCodegenBackend { &self, tcx: TyCtxt<'_>, cgu_name: Symbol, + bitcode_needed: bool, ) -> (ModuleCodegen, u64) { - base::compile_codegen_unit(tcx, cgu_name) + base::compile_codegen_unit(tcx, cgu_name, bitcode_needed) } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 78cdd3e38f68c..e8b25eb4359d0 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -15,7 +15,6 @@ use rustc_errors::{ Level, MultiSpan, Style, Suggestions, catch_fatal_errors, }; use rustc_fs_util::link_or_copy; -use rustc_hir::find_attr; use rustc_incremental::{copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir_sess}; use rustc_macros::{Decodable, Encodable}; use rustc_metadata::fs::copy_to_stdout; @@ -114,7 +113,7 @@ pub struct ModuleConfig { } impl ModuleConfig { - fn new(kind: ModuleKind, tcx: TyCtxt<'_>, no_builtins: bool) -> ModuleConfig { + pub(crate) fn new(kind: ModuleKind, tcx: TyCtxt<'_>, no_builtins: bool) -> ModuleConfig { // If it's a regular module, use `$regular`, otherwise use `$other`. // `$regular` and `$other` are evaluated lazily. macro_rules! if_regular { @@ -426,15 +425,12 @@ fn need_pre_lto_bitcode_for_incr_comp(sess: &Session) -> bool { pub(crate) fn start_async_codegen( backend: B, tcx: TyCtxt<'_>, + regular_config: Arc, + allocator_config: Arc, allocator_module: Option>, ) -> OngoingCodegen { let (coordinator_send, coordinator_receive) = channel(); - let no_builtins = find_attr!(tcx, crate, NoBuiltins); - - let regular_config = ModuleConfig::new(ModuleKind::Regular, tcx, no_builtins); - let allocator_config = ModuleConfig::new(ModuleKind::Allocator, tcx, no_builtins); - let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); let (codegen_worker_send, codegen_worker_receive) = channel(); @@ -444,8 +440,8 @@ pub(crate) fn start_async_codegen( shared_emitter, codegen_worker_send, coordinator_receive, - Arc::new(regular_config), - Arc::new(allocator_config), + regular_config, + allocator_config, allocator_module, coordinator_send.clone(), ); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index c870d1694d068..8dd129f45cc5a 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -42,7 +42,7 @@ use tracing::{debug, info}; use crate::assert_module_sources::CguReuse; use crate::back::link::are_upstream_rust_objects_already_included; use crate::back::write::{ - ComputedLtoType, OngoingCodegen, compute_per_cgu_lto_type, start_async_codegen, + ComputedLtoType, ModuleConfig, OngoingCodegen, compute_per_cgu_lto_type, start_async_codegen, submit_codegened_module_to_llvm, submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm, }; use crate::common::{self, IntPredicate, RealPredicate, TypeKind}; @@ -52,7 +52,7 @@ use crate::mir::place::PlaceRef; use crate::traits::*; use crate::{ CachedModuleCodegen, CodegenLintLevelSpecs, CrateInfo, EiiLinkageImplInfo, EiiLinkageInfo, - ModuleCodegen, diagnostics, meth, mir, + ModuleCodegen, ModuleKind, diagnostics, meth, mir, }; pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate { @@ -762,7 +762,18 @@ pub fn codegen_crate< None }; - let ongoing_codegen = start_async_codegen(backend.clone(), tcx, allocator_module); + let no_builtins = find_attr!(tcx, crate, NoBuiltins); + let regular_module_config = ModuleConfig::new(ModuleKind::Regular, tcx, no_builtins); + let bitcode_needed = regular_module_config.bitcode_needed(); + let allocator_module_config = ModuleConfig::new(ModuleKind::Allocator, tcx, no_builtins); + + let ongoing_codegen = start_async_codegen( + backend.clone(), + tcx, + Arc::new(regular_module_config), + Arc::new(allocator_module_config), + allocator_module, + ); // For better throughput during parallel processing by LLVM, we used to sort // CGUs largest to smallest. This would lead to better thread utilization @@ -822,7 +833,8 @@ pub fn codegen_crate< let start_time = Instant::now(); let pre_compiled_cgus = par_map(cgus, |(i, _)| { - let module = backend.compile_codegen_unit(tcx, codegen_units[i].name()); + let module = + backend.compile_codegen_unit(tcx, codegen_units[i].name(), bitcode_needed); (i, IntoDynSyncSend(module)) }); @@ -846,7 +858,7 @@ pub fn codegen_crate< cgu.0 } else { let start_time = Instant::now(); - let module = backend.compile_codegen_unit(tcx, cgu.name()); + let module = backend.compile_codegen_unit(tcx, cgu.name(), bitcode_needed); total_codegen_time += start_time.elapsed(); module }; diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 11878c1f5165d..2435cca50a0f3 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -177,5 +177,6 @@ pub trait ExtraBackendMethods: Send + Sync + DynSend + DynSync { &self, tcx: TyCtxt<'_>, cgu_name: Symbol, + bitcode_needed: bool, ) -> (ModuleCodegen, u64); }