From 2016bf9ac44658cfaebe32f681ac4e70b4769d21 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Wed, 19 Aug 2026 15:34:35 +0300 Subject: [PATCH 1/4] Add test for parallel compiler reproducible build --- .../parallel-reproducible-build/rmake.rs | 33 +++++++++++++++++++ .../static-muts-issue-140413.rs | 20 +++++++++++ 2 files changed, 53 insertions(+) create mode 100644 tests/run-make/parallel-reproducible-build/rmake.rs create mode 100644 tests/run-make/parallel-reproducible-build/static-muts-issue-140413.rs diff --git a/tests/run-make/parallel-reproducible-build/rmake.rs b/tests/run-make/parallel-reproducible-build/rmake.rs new file mode 100644 index 0000000000000..9c90842e5af36 --- /dev/null +++ b/tests/run-make/parallel-reproducible-build/rmake.rs @@ -0,0 +1,33 @@ +//@ 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 = "static-muts-issue-140413"; + let bin_name = bin_name(FILE_NAME); + + let mut reference = None; + + for _ in 0..100 { + // Tmp dir as previous runs affect output binary on windows. + run_in_tmpdir(|| { + let mut rustc = rustc(); + rustc.input(format!("{FILE_NAME}.rs")).arg("-Zthreads=50").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); + }); + } +} diff --git a/tests/run-make/parallel-reproducible-build/static-muts-issue-140413.rs b/tests/run-make/parallel-reproducible-build/static-muts-issue-140413.rs new file mode 100644 index 0000000000000..3b07333bb9c64 --- /dev/null +++ b/tests/run-make/parallel-reproducible-build/static-muts-issue-140413.rs @@ -0,0 +1,20 @@ +// Checks that mutable static items can have mutable slices and other references + +pub static mut TEST: &'static mut [isize] = &mut [1]; +pub static mut EMPTY: &'static mut [isize] = &mut []; +pub static mut INT: &'static mut isize = &mut 1; + +// And the same for raw pointers. + +pub static mut TEST_RAW: *mut [isize] = &mut [1isize] as *mut _; +pub static mut EMPTY_RAW: *mut [isize] = &mut [] as *mut _; +pub static mut INT_RAW: *mut isize = &mut 1isize as *mut _; + +pub fn main() { + unsafe { + TEST[0] += 1; + assert_eq!(TEST[0], 2); + *INT_RAW += 1; + assert_eq!(*INT_RAW, 2); + } +} From 5fa08c7ceff41b96afb54f535e82121d289d5a97 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 21 Aug 2026 17:13:06 +0300 Subject: [PATCH 2/4] Fix non-determinism in encoding of syntax contexts --- compiler/rustc_span/src/hygiene.rs | 73 +++++++++++++++++-- .../derives-issue-129094.rs | 5 ++ .../parallel-reproducible-build/rmake.rs | 43 ++++++----- 3 files changed, 99 insertions(+), 22 deletions(-) create mode 100644 tests/run-make/parallel-reproducible-build/derives-issue-129094.rs diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index d57f21fc42228..099545dac1fc3 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -25,6 +25,7 @@ // trigger runtime aborts. (Fortunately these are obvious and easy to fix.) use std::hash::Hash; +use std::ops::DerefMut; use std::sync::Arc; use std::{fmt, iter, mem}; @@ -1302,9 +1303,69 @@ pub struct HygieneEncodeContext { serialized_expns: Lock>, latest_expns: Lock>, + + /// Maps every `SyntaxContext` into its encoding index. + /// Earlier the `ctxt.0` was used when writing metadata, however, + /// this results into non-deterministic metadata (see #129094). + /// The non-determinism is encountered when decoding syntax contexts + /// in `decode_syntax_context` function below. The syntax contexts from + /// other crate metadata can be decoded in different order, which results + /// into different ids assigned to decoded syntax contexts. + /// First invocation: + /// (ALLOC - syntax context id, ORIG - original id of decoded syntax context: + /// `raw_id` in `decode_syntax_context`) + /// ALLOC: #3, ORIG: 1 + /// ALLOC: #9, ORIG: 18769 + /// ALLOC: #10, ORIG: 25868 + /// ALLOC: #11, ORIG: 18822 + /// ALLOC: #12, ORIG: 23092 + /// + /// Second invocation: + /// ALLOC: #3, ORIG: 1 + /// ALLOC: #9, ORIG: 25868 + /// ALLOC: #10, ORIG: 18769 + /// ALLOC: #11, ORIG: 18822 + /// ALLOC: #12, ORIG: 23092 + /// + /// We see that `18769` and `25868` assigned different syntax context ids, + /// however, the order of encoding is deterministic, so we can remap allocated + /// syntax context ids into encoding indices and use them, thus outputting + /// same metadata. + /// + /// We can use index vec as when allocating syntax context ids we use + /// `SyntaxContext::from_usize(self.syntax_context_data.len())` in + /// `alloc_ctxt`, so the indices are from continuous range from + /// `0` to `self.syntax_context_data.len()`. + encoding_indices: Lock<( + u32, /* next encoding idnex */ + IndexVec, /* synt. ctxt -> enc. index, `0` at value == unfilled */ + )>, } impl HygieneEncodeContext { + fn get_encoding_index(&self, ctxt: SyntaxContext) -> u32 { + if ctxt.is_root() { + return 0; + } + + let mut state = self.encoding_indices.lock(); + let (next_index, map) = state.deref_mut(); + + if let Some(idx) = map.get(ctxt.0).copied() + && idx != 0 + { + idx + } else { + // Zero is taken by root syntax context. + *next_index += 1; + let encoding_index = *next_index; + + *map.ensure_contains_elem(ctxt.0, || 0) = encoding_index; + + encoding_index + } + } + /// Record the fact that we need to serialize the corresponding `ExpnData`. pub fn schedule_expn_data_for_encoding(&self, expn: ExpnId) { if !self.serialized_expns.lock().contains(&expn) { @@ -1329,18 +1390,19 @@ impl HygieneEncodeContext { // Consume the current round of syntax contexts. // Drop the lock() temporary early. - // It's fine to iterate over a HashMap, because the serialization of the table - // that we insert data into doesn't depend on insertion order. #[allow(rustc::potential_query_instability)] let latest_ctxts = { mem::take(&mut *self.latest_ctxts.lock()) }.into_iter(); - let all_ctxt_data: Vec<_> = HygieneData::with(|data| { + let mut all_ctxt_data: Vec<_> = HygieneData::with(|data| { latest_ctxts .map(|ctxt| (ctxt, data.syntax_context_data[ctxt.0 as usize].key())) .collect() }); + + all_ctxt_data.sort_by_key(|&(ctxt, _)| self.get_encoding_index(ctxt)); + for (ctxt, ctxt_key) in all_ctxt_data { if self.serialized_ctxts.lock().insert(ctxt) { - encode_ctxt(encoder, ctxt.0, &ctxt_key); + encode_ctxt(encoder, self.get_encoding_index(ctxt), &ctxt_key); } } @@ -1488,7 +1550,8 @@ pub fn raw_encode_syntax_context( if !context.serialized_ctxts.lock().contains(&ctxt) { context.latest_ctxts.lock().insert(ctxt); } - ctxt.0.encode(e); + + context.get_encoding_index(ctxt).encode(e); } /// Updates the `disambiguator` field of the corresponding `ExpnData` diff --git a/tests/run-make/parallel-reproducible-build/derives-issue-129094.rs b/tests/run-make/parallel-reproducible-build/derives-issue-129094.rs new file mode 100644 index 0000000000000..fc0ad2bc344da --- /dev/null +++ b/tests/run-make/parallel-reproducible-build/derives-issue-129094.rs @@ -0,0 +1,5 @@ +#![crate_type = "lib"] +#[derive(Clone, Copy, Hash, PartialEq, PartialOrd)] +struct PackedPoint { + x: u32, +} diff --git a/tests/run-make/parallel-reproducible-build/rmake.rs b/tests/run-make/parallel-reproducible-build/rmake.rs index 9c90842e5af36..4a98ed274a394 100644 --- a/tests/run-make/parallel-reproducible-build/rmake.rs +++ b/tests/run-make/parallel-reproducible-build/rmake.rs @@ -5,29 +5,38 @@ 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. +/// Test that parallel compiler produces identical artifacts (binaries, metadata). fn main() { - const FILE_NAME: &str = "static-muts-issue-140413"; - let bin_name = bin_name(FILE_NAME); + const TESTS: &[(&str, &[&str])] = &[ + ("static-muts-issue-140413", &["-Zthreads=50"]), + ("derives-issue-129094", &["-Zthreads=16", "-Copt-level=3"]), + ]; - let mut reference = None; + for (file, args) in TESTS { + let mut reference = None; + let bin_name = bin_name(file); - for _ in 0..100 { - // Tmp dir as previous runs affect output binary on windows. - run_in_tmpdir(|| { - let mut rustc = rustc(); - rustc.input(format!("{FILE_NAME}.rs")).arg("-Zthreads=50").output(&bin_name); + for _ in 0..100 { + // Tmp dir as previous runs affect output binary on windows. + run_in_tmpdir(|| { + let mut rustc = rustc(); + rustc.input(format!("{file}.rs")).output(&bin_name); - if is_windows_msvc() { - rustc.arg("-Clink-arg=/Brepro"); - } + for arg in *args { + rustc.arg(arg); + } - rustc.run(); + if is_windows_msvc() { + rustc.arg("-Clink-arg=/Brepro"); + } - let current = Rc::new(rfs::read(&bin_name)); - reference.get_or_insert(Rc::clone(¤t)); + rustc.run(); - assert_eq!(Some(current), reference); - }); + let current = Rc::new(rfs::read(&bin_name)); + reference.get_or_insert(Rc::clone(¤t)); + + assert_eq!(Some(current), reference); + }); + } } } From a34d24f27b99836298485ffde3a2a09d86324183 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 24 Aug 2026 14:46:00 +0300 Subject: [PATCH 3/4] Don't use `IndexVec`, use `FxHashMap` instead --- compiler/rustc_span/src/hygiene.rs | 31 +++++------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 099545dac1fc3..51c98e8016541 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -25,7 +25,6 @@ // trigger runtime aborts. (Fortunately these are obvious and easy to fix.) use std::hash::Hash; -use std::ops::DerefMut; use std::sync::Arc; use std::{fmt, iter, mem}; @@ -1331,15 +1330,7 @@ pub struct HygieneEncodeContext { /// however, the order of encoding is deterministic, so we can remap allocated /// syntax context ids into encoding indices and use them, thus outputting /// same metadata. - /// - /// We can use index vec as when allocating syntax context ids we use - /// `SyntaxContext::from_usize(self.syntax_context_data.len())` in - /// `alloc_ctxt`, so the indices are from continuous range from - /// `0` to `self.syntax_context_data.len()`. - encoding_indices: Lock<( - u32, /* next encoding idnex */ - IndexVec, /* synt. ctxt -> enc. index, `0` at value == unfilled */ - )>, + encoding_indices: Lock>, } impl HygieneEncodeContext { @@ -1348,22 +1339,10 @@ impl HygieneEncodeContext { return 0; } - let mut state = self.encoding_indices.lock(); - let (next_index, map) = state.deref_mut(); - - if let Some(idx) = map.get(ctxt.0).copied() - && idx != 0 - { - idx - } else { - // Zero is taken by root syntax context. - *next_index += 1; - let encoding_index = *next_index; - - *map.ensure_contains_elem(ctxt.0, || 0) = encoding_index; - - encoding_index - } + let mut map = self.encoding_indices.lock(); + // Zero is taken by root syntax context. + let encoding_index = map.len() + 1; + *map.entry(ctxt).or_insert(encoding_index as u32) } /// Record the fact that we need to serialize the corresponding `ExpnData`. From 6e7643a7d0fb3fd4eeb5a6dba9dfde638790a1bd Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Tue, 25 Aug 2026 18:03:48 +0300 Subject: [PATCH 4/4] Output deterministic `AllocId`s when emitting MIR --- compiler/rustc_middle/src/mir/pretty.rs | 155 ++++++++++++------ .../mir-alloc-ids-issue-154278.rs | 24 +++ .../parallel-reproducible-build/rmake.rs | 1 + 3 files changed, 132 insertions(+), 48 deletions(-) create mode 100644 tests/run-make/parallel-reproducible-build/mir-alloc-ids-issue-154278.rs diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 021c1c176d788..ea37e09619115 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1,5 +1,7 @@ +use std::cell::RefCell; use std::collections::BTreeSet; use std::fmt::{Display, Write as _}; +use std::num::NonZero; use std::path::{Path, PathBuf}; use std::{fs, io}; @@ -10,10 +12,10 @@ use ty::print::PrettyPrinter; use super::graphviz::write_mir_fn_graphviz; use crate::mir::interpret::{ - AllocBytes, AllocId, Allocation, ConstAllocation, GlobalAlloc, Pointer, Provenance, - alloc_range, read_target_uint, + AllocBytes, AllocId, Allocation, ConstAllocation, CtfeProvenance, GlobalAlloc, Pointer, + Provenance, alloc_range, read_target_uint, }; -use crate::mir::visit::Visitor; +use crate::mir::visit::{MutVisitor, Visitor}; use crate::mir::*; use crate::ty::CoroutineArgsExt; @@ -318,6 +320,10 @@ pub fn write_mir_pretty<'tcx>(tcx: TyCtxt<'tcx>, w: &mut dyn io::Write) -> io::R writeln!(w, "// WARNING: This output format is intended for human consumers only")?; writeln!(w, "// and is subject to change without notice. Knock yourself out.")?; writeln!(w, "// HINT: See also -Z dump-mir for MIR at specific points during compilation.")?; + writeln!( + w, + "// WARNING: Allocation ids were remapped for deterministic output, they may be differ from real ones." + )?; let mut first = true; for &def_id in tcx.mir_keys(()) { @@ -330,7 +336,6 @@ pub fn write_mir_pretty<'tcx>(tcx: TyCtxt<'tcx>, w: &mut dyn io::Write) -> io::R let render_body = |w: &mut dyn io::Write, body| -> io::Result<()> { writer.write_mir_fn(body, w)?; - for body in tcx.promoted_mir(def_id) { writeln!(w)?; writer.write_mir_fn(body, w)?; @@ -367,15 +372,42 @@ pub struct MirWriter<'a, 'tcx> { tcx: TyCtxt<'tcx>, extra_data: &'a dyn Fn(PassWhere, &mut dyn io::Write) -> io::Result<()>, options: PrettyPrintMirOptions, + alloc_map: RefCell>, + reverse_alloc_map: RefCell>, } impl<'a, 'tcx> MirWriter<'a, 'tcx> { pub fn new(tcx: TyCtxt<'tcx>) -> Self { - MirWriter { tcx, extra_data: &|_, _| Ok(()), options: PrettyPrintMirOptions::from_cli(tcx) } + MirWriter { + tcx, + extra_data: &|_, _| Ok(()), + options: PrettyPrintMirOptions::from_cli(tcx), + alloc_map: Default::default(), + reverse_alloc_map: Default::default(), + } + } + + fn remap_alloc_id(&self, alloc_id: AllocId) -> AllocId { + let next_remap_id = self.alloc_map.borrow().len() as u64 + 1; + + *self.alloc_map.borrow_mut().entry(alloc_id).or_insert_with(|| { + let remapped_id = AllocId(NonZero::new(next_remap_id).expect("can't be zero")); + + self.reverse_alloc_map.borrow_mut().insert(remapped_id, alloc_id); + + remapped_id + }) } /// Write out a human-readable textual representation for the given function. pub fn write_mir_fn(&self, body: &Body<'tcx>, w: &mut dyn io::Write) -> io::Result<()> { + let mut body = body.clone(); + + let mut visitor = AllocIdsRemapper { writer: self, ids: Default::default() }; + visitor.visit_body(&mut body); + + let body = &body; + write_mir_intro(self.tcx, body, w, self.options)?; for block in body.basic_blocks.indices() { (self.extra_data)(PassWhere::BeforeBlock(block), w)?; @@ -387,7 +419,7 @@ impl<'a, 'tcx> MirWriter<'a, 'tcx> { writeln!(w, "}}")?; - write_allocations(self.tcx, body, w)?; + write_allocations(self, body, w, visitor.ids)?; Ok(()) } @@ -1565,12 +1597,58 @@ fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String { /////////////////////////////////////////////////////////////////////////// // Allocations +/// Remaps allocation ids for deterministic output. Despite the fact that allocation +/// ids are not deterministic, we can remap them into deterministic output for serialization, +/// as serialization order is deterministic. +struct AllocIdsRemapper<'a, 'b, 'tcx> { + writer: &'a MirWriter<'b, 'tcx>, + ids: BTreeSet, +} + +impl AllocIdsRemapper<'_, '_, '_> { + fn remap_alloc_id(&mut self, alloc_id: AllocId) -> AllocId { + let remapped_id = self.writer.remap_alloc_id(alloc_id); + self.ids.insert(remapped_id); + + remapped_id + } +} + +impl<'tcx> MutVisitor<'tcx> for AllocIdsRemapper<'_, '_, 'tcx> { + fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { + self.writer.tcx + } + + fn visit_const_operand(&mut self, constant: &mut ConstOperand<'tcx>, _: Location) { + match &mut constant.const_ { + Const::Val(const_value, _) => { + match const_value { + ConstValue::Scalar(Scalar::Ptr(pointer, ..)) => { + let mut parts = pointer.provenance.into_parts(); + parts.0 = self.remap_alloc_id(parts.0); + + pointer.provenance = CtfeProvenance::from_parts(parts); + } + ConstValue::Slice { alloc_id, .. } | ConstValue::Indirect { alloc_id, .. } => { + // FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR. + // Really we'd want `pretty_print_const_value` to decide which allocations to print, instead of having a separate visitor. + *alloc_id = self.remap_alloc_id(*alloc_id); + } + ConstValue::Scalar(Scalar::Int { .. }) | ConstValue::ZeroSized => {} + }; + } + Const::Ty(_, _) | Const::Unevaluated(..) => {} + } + } +} + /// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding /// allocations. pub fn write_allocations<'tcx>( - tcx: TyCtxt<'tcx>, + mir_writer: &MirWriter<'_, 'tcx>, body: &Body<'_>, w: &mut dyn io::Write, + initial_alloc_ids: BTreeSet, ) -> io::Result<()> { fn alloc_ids_from_alloc( alloc: ConstAllocation<'_>, @@ -1578,53 +1656,34 @@ pub fn write_allocations<'tcx>( alloc.inner().provenance().ptrs().values().map(|p| p.alloc_id()) } - fn alloc_id_from_const_val(val: ConstValue) -> Option { - match val { - ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => Some(ptr.provenance.alloc_id()), - ConstValue::Scalar(interpret::Scalar::Int { .. }) => None, - ConstValue::ZeroSized => None, - ConstValue::Slice { alloc_id, .. } | ConstValue::Indirect { alloc_id, .. } => { - // FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR. - // Really we'd want `pretty_print_const_value` to decide which allocations to print, instead of having a separate visitor. - Some(alloc_id) - } - } - } - struct CollectAllocIds(BTreeSet); - - impl<'tcx> Visitor<'tcx> for CollectAllocIds { - fn visit_const_operand(&mut self, c: &ConstOperand<'tcx>, _: Location) { - match c.const_ { - Const::Ty(_, _) | Const::Unevaluated(..) => {} - Const::Val(val, _) => { - if let Some(id) = alloc_id_from_const_val(val) { - self.0.insert(id); - } - } - } - } - } - - let mut visitor = CollectAllocIds(Default::default()); - visitor.visit_body(body); + let tcx = mir_writer.tcx; // `seen` contains all seen allocations, including the ones we have *not* printed yet. // The protocol is to first `insert` into `seen`, and only if that returns `true` // then push to `todo`. - let mut seen = visitor.0; + let mut seen = initial_alloc_ids; let mut todo: Vec<_> = seen.iter().copied().collect(); + + // Invariant: all ids in this loop are remapped. while let Some(id) = todo.pop() { - let mut write_allocation_track_relocs = - |w: &mut dyn io::Write, alloc: ConstAllocation<'tcx>| -> io::Result<()> { - // `.rev()` because we are popping them from the back of the `todo` vector. - for id in alloc_ids_from_alloc(alloc).rev() { - if seen.insert(id) { - todo.push(id); - } + let mut write_allocation_track_relocs = |mir_writer: &MirWriter<'_, 'tcx>, + w: &mut dyn io::Write, + alloc: ConstAllocation<'tcx>| + -> io::Result<()> { + // `.rev()` because we are popping them from the back of the `todo` vector. + for id in alloc_ids_from_alloc(alloc).rev() { + let mapped_id = mir_writer.remap_alloc_id(id); + if seen.insert(mapped_id) { + todo.push(mapped_id); } - write!(w, "{}", display_allocation(tcx, alloc.inner())) - }; + } + write!(w, "{}", display_allocation(tcx, alloc.inner())) + }; + write!(w, "\n{id:?}")?; + + let id = mir_writer.reverse_alloc_map.borrow()[&id]; + match tcx.try_get_global_alloc(id) { // This can't really happen unless there are bugs, but it doesn't cost us anything to // gracefully handle it and allow buggy rustc to be debugged via allocation printing. @@ -1651,7 +1710,7 @@ pub fn write_allocations<'tcx>( match tcx.eval_static_initializer(did) { Ok(alloc) => { write!(w, ", ")?; - write_allocation_track_relocs(w, alloc)?; + write_allocation_track_relocs(mir_writer, w, alloc)?; } Err(_) => write!(w, ", error during initializer evaluation)")?, } @@ -1662,7 +1721,7 @@ pub fn write_allocations<'tcx>( } Some(GlobalAlloc::Memory(alloc)) => { write!(w, " (")?; - write_allocation_track_relocs(w, alloc)? + write_allocation_track_relocs(mir_writer, w, alloc)? } } writeln!(w)?; diff --git a/tests/run-make/parallel-reproducible-build/mir-alloc-ids-issue-154278.rs b/tests/run-make/parallel-reproducible-build/mir-alloc-ids-issue-154278.rs new file mode 100644 index 0000000000000..db6aea143325b --- /dev/null +++ b/tests/run-make/parallel-reproducible-build/mir-alloc-ids-issue-154278.rs @@ -0,0 +1,24 @@ +pub struct A { + pub v: T, +} +pub struct B { + pub v: T, +} + +pub mod test { + pub struct A { + pub v: T, + } + + impl A { + pub fn foo(&self) -> isize { + static a: isize = 5; + return a; + } + + pub fn bar(&self) -> isize { + static a: isize = 6; + return a; + } + } +} diff --git a/tests/run-make/parallel-reproducible-build/rmake.rs b/tests/run-make/parallel-reproducible-build/rmake.rs index 4a98ed274a394..c103d182f3c7a 100644 --- a/tests/run-make/parallel-reproducible-build/rmake.rs +++ b/tests/run-make/parallel-reproducible-build/rmake.rs @@ -10,6 +10,7 @@ fn main() { const TESTS: &[(&str, &[&str])] = &[ ("static-muts-issue-140413", &["-Zthreads=50"]), ("derives-issue-129094", &["-Zthreads=16", "-Copt-level=3"]), + ("mir-alloc-ids-issue-154278", &["-Zthreads=60", "--emit=mir", "--crate-type=lib"]), ]; for (file, args) in TESTS {