Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cd39f5a
Add waker_fn and local_waker_fn to std::task
tisonkun Dec 14, 2025
b007397
Update provider API docs
yaahc Dec 16, 2025
5610d84
rustc: Fix `-Zexport-executable-symbols` on wasm
alexcrichton Dec 19, 2025
f89cce3
`c_variadic`: provide `va_arg` for more targets
folkertdev Dec 17, 2025
38ab519
./x check miri: enable check_only feature
RalfJung Jan 5, 2026
dd948f9
Thread `--jobs` from `bootstrap` -> `compiletest` -> `run-make-support`
jieyouxu Jan 6, 2026
630c759
Ensure that static initializers are acyclic for NVPTX
kulst Jan 1, 2026
af69f15
Add AtomicPtr::null
orlp Jan 6, 2026
fff9c62
Add feature to doc example
orlp Jan 6, 2026
6346d14
Apply suggestion from @tisonkun
tisonkun Jan 7, 2026
600102c
Add myself as co-maintainer for s390x-unknown-linux-musl
Gelbpunkt Jan 8, 2026
a3b72d3
Fix copy-n-paste error in `vtable_for` docs
petertodd Jan 8, 2026
10ef358
Rollup merge of #149976 - waker-fn, r=jhpratt
matthiaskrgr Jan 8, 2026
b3f95da
Rollup merge of #150074 - provider-doc-update, r=Mark-Simulacrum
matthiaskrgr Jan 8, 2026
fc4464b
Rollup merge of #150094 - more-va-arg, r=workingjubilee
matthiaskrgr Jan 8, 2026
023cc3c
Rollup merge of #150164 - fix-wasm-export-executable-symbols, r=jdons…
matthiaskrgr Jan 8, 2026
3481c0d
Rollup merge of #150569 - check_static_initializer_acyclic, r=working…
matthiaskrgr Jan 8, 2026
219d95e
Rollup merge of #150694 - miri-check, r=clubby789,bjorn3
matthiaskrgr Jan 8, 2026
7a11f8b
Rollup merge of #150717 - jobs, r=Kobzol
matthiaskrgr Jan 8, 2026
eaff061
Rollup merge of #150736 - atomic-ptr-null, r=joboet
matthiaskrgr Jan 8, 2026
0b98c60
Rollup merge of #150787 - s390x-musl-co-maintainer, r=Noratrieb
matthiaskrgr Jan 8, 2026
5a5740f
Rollup merge of #150789 - 2025-fix-vtable-for-comment, r=joboet
matthiaskrgr Jan 8, 2026
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
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4421,6 +4421,7 @@ dependencies = [
"rustc_errors",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_macros",
"rustc_middle",
"rustc_session",
Expand Down
106 changes: 83 additions & 23 deletions compiler/rustc_codegen_llvm/src/va_arg.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use rustc_abi::{Align, BackendRepr, Endian, HasDataLayout, Primitive, Size, TyAndLayout};
use rustc_abi::{Align, BackendRepr, Endian, HasDataLayout, Primitive, Size};
use rustc_codegen_ssa::MemFlags;
use rustc_codegen_ssa::common::IntPredicate;
use rustc_codegen_ssa::mir::operand::OperandRef;
use rustc_codegen_ssa::traits::{
BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, LayoutTypeCodegenMethods,
};
use rustc_middle::bug;
use rustc_middle::ty::Ty;
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
use rustc_target::spec::{Abi, Arch, Env};

use crate::builder::Builder;
Expand Down Expand Up @@ -82,6 +83,7 @@ enum PassMode {
enum SlotSize {
Bytes8 = 8,
Bytes4 = 4,
Bytes1 = 1,
}

enum AllowHigherAlign {
Expand Down Expand Up @@ -728,7 +730,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
fn copy_to_temporary_if_more_aligned<'ll, 'tcx>(
bx: &mut Builder<'_, 'll, 'tcx>,
reg_addr: &'ll Value,
layout: TyAndLayout<'tcx, Ty<'tcx>>,
layout: TyAndLayout<'tcx>,
src_align: Align,
) -> &'ll Value {
if layout.layout.align.abi > src_align {
Expand All @@ -751,7 +753,7 @@ fn copy_to_temporary_if_more_aligned<'ll, 'tcx>(
fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>(
bx: &mut Builder<'_, 'll, 'tcx>,
va_list_addr: &'ll Value,
layout: TyAndLayout<'tcx, Ty<'tcx>>,
layout: TyAndLayout<'tcx>,
) -> &'ll Value {
let dl = bx.cx.data_layout();
let ptr_align_abi = dl.data_layout().pointer_align().abi;
Expand Down Expand Up @@ -1003,15 +1005,17 @@ fn emit_xtensa_va_arg<'ll, 'tcx>(
return bx.load(layout.llvm_type(bx), value_ptr, layout.align.abi);
}

/// Determine the va_arg implementation to use. The LLVM va_arg instruction
/// is lacking in some instances, so we should only use it as a fallback.
pub(super) fn emit_va_arg<'ll, 'tcx>(
bx: &mut Builder<'_, 'll, 'tcx>,
addr: OperandRef<'tcx, &'ll Value>,
target_ty: Ty<'tcx>,
) -> &'ll Value {
// Determine the va_arg implementation to use. The LLVM va_arg instruction
// is lacking in some instances, so we should only use it as a fallback.
let target = &bx.cx.tcx.sess.target;
let layout = bx.cx.layout_of(target_ty);
let target_ty_size = layout.layout.size().bytes();

let target = &bx.cx.tcx.sess.target;
match target.arch {
Arch::X86 => emit_ptr_va_arg(
bx,
Expand Down Expand Up @@ -1069,23 +1073,79 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
AllowHigherAlign::Yes,
ForceRightAdjust::No,
),
Arch::LoongArch32 => emit_ptr_va_arg(
bx,
addr,
target_ty,
if target_ty_size > 2 * 4 { PassMode::Indirect } else { PassMode::Direct },
SlotSize::Bytes4,
AllowHigherAlign::Yes,
ForceRightAdjust::No,
),
Arch::LoongArch64 => emit_ptr_va_arg(
bx,
addr,
target_ty,
if target_ty_size > 2 * 8 { PassMode::Indirect } else { PassMode::Direct },
SlotSize::Bytes8,
AllowHigherAlign::Yes,
ForceRightAdjust::No,
),
Arch::AmdGpu => emit_ptr_va_arg(
bx,
addr,
target_ty,
PassMode::Direct,
SlotSize::Bytes4,
AllowHigherAlign::No,
ForceRightAdjust::No,
),
Arch::Nvptx64 => emit_ptr_va_arg(
bx,
addr,
target_ty,
PassMode::Direct,
SlotSize::Bytes1,
AllowHigherAlign::Yes,
ForceRightAdjust::No,
),
Arch::Wasm32 => emit_ptr_va_arg(
bx,
addr,
target_ty,
if layout.is_aggregate() || layout.is_zst() || layout.is_1zst() {
PassMode::Indirect
} else {
PassMode::Direct
},
SlotSize::Bytes4,
AllowHigherAlign::Yes,
ForceRightAdjust::No,
),
Arch::Wasm64 => bug!("c-variadic functions are not fully implemented for wasm64"),
Arch::CSky => emit_ptr_va_arg(
bx,
addr,
target_ty,
PassMode::Direct,
SlotSize::Bytes4,
AllowHigherAlign::Yes,
ForceRightAdjust::No,
),
// Windows x86_64
Arch::X86_64 if target.is_like_windows => {
let target_ty_size = bx.cx.size_of(target_ty).bytes();
emit_ptr_va_arg(
bx,
addr,
target_ty,
if target_ty_size > 8 || !target_ty_size.is_power_of_two() {
PassMode::Indirect
} else {
PassMode::Direct
},
SlotSize::Bytes8,
AllowHigherAlign::No,
ForceRightAdjust::No,
)
}
Arch::X86_64 if target.is_like_windows => emit_ptr_va_arg(
bx,
addr,
target_ty,
if target_ty_size > 8 || !target_ty_size.is_power_of_two() {
PassMode::Indirect
} else {
PassMode::Direct
},
SlotSize::Bytes8,
AllowHigherAlign::No,
ForceRightAdjust::No,
),
// This includes `target.is_like_darwin`, which on x86_64 targets is like sysv64.
Arch::X86_64 => emit_x86_64_sysv64_va_arg(bx, addr, target_ty),
Arch::Xtensa => emit_xtensa_va_arg(bx, addr, target_ty),
Expand Down
10 changes: 5 additions & 5 deletions compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,11 @@ impl<'a> Linker for GccLinker<'a> {
self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error });
}
self.link_arg(path);
} else if self.sess.target.is_like_wasm {
self.link_arg("--no-export-dynamic");
for (sym, _) in symbols {
self.link_arg("--export").link_arg(sym);
}
} else if crate_type == CrateType::Executable && !self.sess.target.is_like_solaris {
let res: io::Result<()> = try {
let mut f = File::create_buffered(&path)?;
Expand All @@ -853,11 +858,6 @@ impl<'a> Linker for GccLinker<'a> {
self.sess.dcx().emit_fatal(errors::VersionScriptWriteFailure { error });
}
self.link_arg("--dynamic-list").link_arg(path);
} else if self.sess.target.is_like_wasm {
self.link_arg("--no-export-dynamic");
for (sym, _) in symbols {
self.link_arg("--export").link_arg(sym);
}
} else {
// Write an LD version script
let res: io::Result<()> = try {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_monomorphize/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }
rustc_session = { path = "../rustc_session" }
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_monomorphize/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,8 @@ monomorphize_recursion_limit =
monomorphize_start_not_found = using `fn main` requires the standard library
.help = use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]`

monomorphize_static_initializer_cyclic = static initializer forms a cycle involving `{$head}`
.label = part of this cycle
.note = cyclic static initializers are not supported for target `{$target}`

monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined
3 changes: 2 additions & 1 deletion compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,8 @@ pub(crate) struct UsageMap<'tcx> {
// Maps every mono item to the mono items used by it.
pub used_map: UnordMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>,

// Maps every mono item to the mono items that use it.
// Maps each mono item with users to the mono items that use it.
// Be careful: subsets `used_map`, so unused items are vacant.
user_map: UnordMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>,
}

Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_monomorphize/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,15 @@ pub(crate) struct AbiRequiredTargetFeature<'a> {
/// Whether this is a problem at a call site or at a declaration.
pub is_call: bool,
}

#[derive(Diagnostic)]
#[diag(monomorphize_static_initializer_cyclic)]
#[note]
pub(crate) struct StaticInitializerCyclic<'a> {
#[primary_span]
pub span: Span,
#[label]
pub labels: Vec<Span>,
pub head: &'a str,
pub target: &'a str,
}
18 changes: 18 additions & 0 deletions compiler/rustc_monomorphize/src/graph_checks/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//! Checks that need to operate on the entire mono item graph
use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::TyCtxt;

use crate::collector::UsageMap;
use crate::graph_checks::statics::check_static_initializers_are_acyclic;

mod statics;

pub(super) fn target_specific_checks<'tcx, 'a, 'b>(
tcx: TyCtxt<'tcx>,
mono_items: &'a [MonoItem<'tcx>],
usage_map: &'b UsageMap<'tcx>,
) {
if tcx.sess.target.options.static_initializer_must_be_acyclic {
check_static_initializers_are_acyclic(tcx, mono_items, usage_map);
}
}
115 changes: 115 additions & 0 deletions compiler/rustc_monomorphize/src/graph_checks/statics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::graph::scc::Sccs;
use rustc_data_structures::graph::{DirectedGraph, Successors};
use rustc_data_structures::unord::UnordMap;
use rustc_hir::def_id::DefId;
use rustc_index::{Idx, IndexVec, newtype_index};
use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::TyCtxt;

use crate::collector::UsageMap;
use crate::errors;

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
struct StaticNodeIdx(usize);

impl Idx for StaticNodeIdx {
fn new(idx: usize) -> Self {
Self(idx)
}

fn index(self) -> usize {
self.0
}
}

impl From<usize> for StaticNodeIdx {
fn from(value: usize) -> Self {
StaticNodeIdx(value)
}
}

newtype_index! {
#[derive(Ord, PartialOrd)]
struct StaticSccIdx {}
}

// Adjacency-list graph for statics using `StaticNodeIdx` as node type.
// We cannot use `DefId` as the node type directly because each node must be
// represented by an index in the range `0..num_nodes`.
struct StaticRefGraph<'a, 'b, 'tcx> {
// maps from `StaticNodeIdx` to `DefId` and vice versa
statics: &'a FxIndexSet<DefId>,
// contains for each `MonoItem` the `MonoItem`s it uses
used_map: &'b UnordMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>,
}

impl<'a, 'b, 'tcx> DirectedGraph for StaticRefGraph<'a, 'b, 'tcx> {
type Node = StaticNodeIdx;

fn num_nodes(&self) -> usize {
self.statics.len()
}
}

impl<'a, 'b, 'tcx> Successors for StaticRefGraph<'a, 'b, 'tcx> {
fn successors(&self, node_idx: StaticNodeIdx) -> impl Iterator<Item = StaticNodeIdx> {
let def_id = self.statics[node_idx.index()];
self.used_map[&MonoItem::Static(def_id)].iter().filter_map(|&mono_item| match mono_item {
MonoItem::Static(def_id) => self.statics.get_index_of(&def_id).map(|idx| idx.into()),
_ => None,
})
}
}

pub(super) fn check_static_initializers_are_acyclic<'tcx, 'a, 'b>(
tcx: TyCtxt<'tcx>,
mono_items: &'a [MonoItem<'tcx>],
usage_map: &'b UsageMap<'tcx>,
) {
// Collect statics
let statics: FxIndexSet<DefId> = mono_items
.iter()
.filter_map(|&mono_item| match mono_item {
MonoItem::Static(def_id) => Some(def_id),
_ => None,
})
.collect();

// If we don't have any statics the check is not necessary
if statics.is_empty() {
return;
}
// Create a subgraph from the mono item graph, which only contains statics
let graph = StaticRefGraph { statics: &statics, used_map: &usage_map.used_map };
// Calculate its SCCs
let sccs: Sccs<StaticNodeIdx, StaticSccIdx> = Sccs::new(&graph);
// Group statics by SCCs
let mut nodes_of_sccs: IndexVec<StaticSccIdx, Vec<StaticNodeIdx>> =
IndexVec::from_elem_n(Vec::new(), sccs.num_sccs());
for i in graph.iter_nodes() {
nodes_of_sccs[sccs.scc(i)].push(i);
}
let is_cyclic = |nodes_of_scc: &[StaticNodeIdx]| -> bool {
match nodes_of_scc.len() {
0 => false,
1 => graph.successors(nodes_of_scc[0]).any(|x| x == nodes_of_scc[0]),
2.. => true,
}
};
// Emit errors for all cycles
for nodes in nodes_of_sccs.iter_mut().filter(|nodes| is_cyclic(nodes)) {
// We sort the nodes by their Span to have consistent error line numbers
nodes.sort_by_key(|node| tcx.def_span(statics[node.index()]));

let head_def = statics[nodes[0].index()];
let head_span = tcx.def_span(head_def);

tcx.dcx().emit_err(errors::StaticInitializerCyclic {
span: head_span,
labels: nodes.iter().map(|&n| tcx.def_span(statics[n.index()])).collect(),
head: &tcx.def_path_str(head_def),
target: &tcx.sess.target.llvm_target,
});
}
}
1 change: 1 addition & 0 deletions compiler/rustc_monomorphize/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use rustc_span::ErrorGuaranteed;

mod collector;
mod errors;
mod graph_checks;
mod mono_checks;
mod partitioning;
mod util;
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_monomorphize/src/partitioning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ use tracing::debug;

use crate::collector::{self, MonoItemCollectionStrategy, UsageMap};
use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined};
use crate::graph_checks::target_specific_checks;

struct PartitioningCx<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
Expand Down Expand Up @@ -1135,6 +1136,8 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio
};

let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_strategy);
// Perform checks that need to operate on the entire mono item graph
target_specific_checks(tcx, &items, &usage_map);

// If there was an error during collection (e.g. from one of the constants we evaluated),
// then we stop here. This way codegen does not have to worry about failing constants.
Expand Down
Loading
Loading