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
52 changes: 47 additions & 5 deletions compiler/rustc_span/src/hygiene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1306,9 +1306,49 @@ pub struct HygieneEncodeContext {
serialized_expns: Lock<FxHashSet<ExpnId>>,

latest_expns: Lock<FxHashSet<ExpnId>>,

/// 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.
encoding_indices: Lock<FxHashMap<SyntaxContext, u32>>,
}

impl HygieneEncodeContext {
fn get_encoding_index(&self, ctxt: SyntaxContext) -> u32 {
if ctxt.is_root() {
return 0;
}

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`.
pub fn schedule_expn_data_for_encoding(&self, expn: ExpnId) {
if !self.serialized_expns.lock().contains(&expn) {
Expand All @@ -1333,18 +1373,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));

@petrochenkov petrochenkov Sep 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: here and below get_encoding_index takes the encoding_indices in every iteration of the loop, it could be more efficient to take the lock once.

View changes since the review


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);
}
}

Expand Down Expand Up @@ -1492,7 +1533,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`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#![crate_type = "lib"]
#[derive(Clone, Copy, Hash, PartialEq, PartialOrd)]
struct PackedPoint {
x: u32,
}
43 changes: 26 additions & 17 deletions tests/run-make/parallel-reproducible-build/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,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..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("-Zthreads=50").output(&bin_name);
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}.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(&current));
rustc.run();

assert_eq!(Some(current), reference);
});
let current = Rc::new(rfs::read(&bin_name));
reference.get_or_insert(Rc::clone(&current));

assert_eq!(Some(current), reference);
});
}
}
}
Loading