Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2e2cd54
Improve diagnostics for non-well-formed associated items
ozankenangungor Sep 6, 2026
29d1ae1
Handle explicit associated item own bounds generally
ozankenangungor Oct 10, 2026
35b8ebc
Point at assoc item for trait that needs to be implemented
estebank Oct 12, 2026
bee7ba8
Simplify associated type bound checks
ozankenangungor Oct 12, 2026
192d095
Document associated type guard
ozankenangungor Oct 12, 2026
fbf53be
Test the interaction between `--remap-path-prefix` and doctests
Urgau Oct 12, 2026
f8d4856
Output remapped paths in rustdoc JSON with the `DOCUMENTATION` scope
Urgau Oct 13, 2026
c2a249d
Ignore cross-compile as doctests don't always work when cross-compiled
Urgau Oct 13, 2026
0d0f404
Add README.md and descriptions for the remap-path-prefix JSON tests
Urgau Oct 13, 2026
60ec7da
Move `thin_lto_buffer` to a OnceLock in ModuleLlvm
Zalathar Oct 14, 2026
d3f7014
Use a callback to enforce scoped lifetimes in ModuleLlvm
Zalathar Oct 9, 2026
d005c65
Avoid `&'static mut` references to an owned `llvm::Context`
Zalathar Oct 9, 2026
86d1817
add tests for local binding resolution in move expressions
TaKO8Ki Oct 10, 2026
1346617
reject out-of-scope local bindings in move expressions
TaKO8Ki Oct 10, 2026
6ecca07
Use OS `join` operation to have the expected path separator
Urgau Oct 14, 2026
dd49010
Test panics and wasm32-wasip3 in CI, not wasip1
alexcrichton Sep 17, 2026
7fec32e
Ignore the remap rustdoc-json tests on windows
Urgau Oct 14, 2026
566f9b9
Rollup merge of #163132 - alexcrichton:wasm-unwind-tests, r=saethlin
Zalathar Oct 15, 2026
bc1475c
Rollup merge of #164076 - TaKO8Ki:fix-move-expr-local-bindings-ice, r…
Zalathar Oct 15, 2026
22ec673
Rollup merge of #162432 - ozankenangungor:issue-162368, r=maxdexh,Sho…
Zalathar Oct 15, 2026
c92f017
Rollup merge of #164138 - Zalathar:llmod-refs, r=hanna-kruppe
Zalathar Oct 15, 2026
1c069fe
Rollup merge of #164220 - Urgau:remap-path-prefix-doctest-panic, r=Gu…
Zalathar Oct 15, 2026
4529d28
Rollup merge of #164225 - estebank:found-assoc, r=ShoyuVanilla
Zalathar Oct 15, 2026
48cbb18
Rollup merge of #164278 - Urgau:rustdoc-json-remap-path, r=GuillaumeG…
Zalathar Oct 15, 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
6 changes: 6 additions & 0 deletions bootstrap.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,12 @@
# want to enable this right now.
#rust.wasm-proc-macros = false

# Enables wasm targets to be built with `-Cpanic=unwind` by default instead of
# the current `-Cpanic=abort`. This is currently used in testing the Rust
# compiler itself and is only intended for local development/testing at this
# time.
#rust.wasm-panic-unwind-default = false

# =============================================================================
# Distribution options
#
Expand Down
98 changes: 74 additions & 24 deletions compiler/rustc_codegen_llvm/src/back/llvm_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@

use std::any::Any;
use std::ffi::CStr;
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::path::PathBuf;
use std::ptr;
use std::sync::OnceLock;

use rustc_ast::expand::allocator::AllocatorMethod;
use rustc_codegen_ssa::back::lto::ThinModule;
Expand Down Expand Up @@ -59,11 +62,10 @@ impl ExtraBackendMethods for LlvmCodegenBackend {
methods: &[AllocatorMethod],
) -> ModuleLlvm {
let module_llvm = ModuleLlvm::new_metadata(tcx, module_name);
let cx =
SimpleCx::new(module_llvm.llmod(), &module_llvm.llcx, tcx.data_layout.pointer_size());
unsafe {
allocator::codegen(tcx, cx, module_name, methods);
}
module_llvm.with_module_refs(|ModuleRefs { llcx, llmod, .. }| {
let cx = SimpleCx::new(llmod, llcx, tcx.data_layout.pointer_size());
unsafe { allocator::codegen(tcx, cx, module_name, methods) };
});
module_llvm
}
fn compile_codegen_unit(
Expand Down Expand Up @@ -141,7 +143,7 @@ impl WriteBackendMethods for LlvmCodegenBackend {
module: &mut ModuleCodegen<Self::Module>,
config: &ModuleConfig,
) {
back::write::optimize(cgcx, prof, shared_emitter, module, config)
back::write::optimize(cgcx, prof, shared_emitter, module, config);
}
fn optimize_and_codegen_thin(
cgcx: &CodegenContext,
Expand All @@ -162,7 +164,9 @@ impl WriteBackendMethods for LlvmCodegenBackend {
back::write::codegen(cgcx, prof, shared_emitter, module, config)
}
fn serialize_module(module: Self::Module, is_thin: bool) -> Self::ModuleBuffer {
back::lto::ModuleBuffer::new(module.llmod(), is_thin)
module.with_module_refs(|ModuleRefs { llmod, .. }| {
back::lto::ModuleBuffer::new(llmod, is_thin)
})
}
}

Expand Down Expand Up @@ -421,40 +425,56 @@ impl CodegenBackend for LlvmCodegenBackend {
}

pub struct ModuleLlvm {
pub(crate) llcx: &'static mut llvm::Context,
llcx_raw: ptr::NonNull<llvm::Context>,
llmod_raw: *const llvm::Module,

// This field is `ManuallyDrop` because it is important that the `TargetMachine`
// is disposed prior to the `Context` being disposed otherwise UAFs can occur.
pub(crate) tm: ManuallyDrop<OwnedTargetMachine>,
tm: ManuallyDrop<OwnedTargetMachine>,

/// Saved ThinLTO buffer for embedding in the object file.
///
/// This is currently a `OnceLock` to avoid aliasing problems when writing to it
/// from code that also wants to pass around a shared ref to `ModuleCodegen`.
pub(crate) thin_lto_buffer: OnceLock<Vec<u8>>,
}

unsafe impl Send for ModuleLlvm {}
unsafe impl Sync for ModuleLlvm {}

/// Shared helper to call [`llvm::LLVMContextCreate`] and then configure
/// [`llvm::LLVMContextSetDiscardValueNames`].
fn create_context_and_set_fewer_names(fewer_names: bool) -> ptr::NonNull<llvm::Context> {
unsafe {
let llcx_raw = llvm::LLVMContextCreate();
llvm::LLVMContextSetDiscardValueNames(llcx_raw.as_ref(), fewer_names.to_llvm_bool());
llcx_raw
}
}

impl ModuleLlvm {
pub(crate) fn new(tcx: TyCtxt<'_>, mod_name: &str) -> Self {
unsafe {
let llcx = llvm::LLVMContextCreate();
llvm::LLVMContextSetDiscardValueNames(llcx, tcx.sess.fewer_names().to_llvm_bool());
let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _;
let llcx_raw = create_context_and_set_fewer_names(tcx.sess.fewer_names());
let llmod_raw = context::create_module(tcx, llcx_raw.as_ref(), mod_name);
ModuleLlvm {
llmod_raw,
llcx,
llcx_raw,
tm: ManuallyDrop::new(create_target_machine(tcx, mod_name)),
thin_lto_buffer: OnceLock::new(),
}
}
}

pub(crate) fn new_metadata(tcx: TyCtxt<'_>, mod_name: &str) -> Self {
unsafe {
let llcx = llvm::LLVMContextCreate();
llvm::LLVMContextSetDiscardValueNames(llcx, tcx.sess.fewer_names().to_llvm_bool());
let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _;
let llcx_raw = create_context_and_set_fewer_names(tcx.sess.fewer_names());
let llmod_raw = context::create_module(tcx, llcx_raw.as_ref(), mod_name);
ModuleLlvm {
llmod_raw,
llcx,
llcx_raw,
tm: ManuallyDrop::new(create_informational_target_machine(tcx.sess)),
thin_lto_buffer: OnceLock::new(),
}
}
}
Expand All @@ -467,25 +487,55 @@ impl ModuleLlvm {
dcx: DiagCtxtHandle<'_>,
) -> Self {
unsafe {
let llcx = llvm::LLVMContextCreate();
llvm::LLVMContextSetDiscardValueNames(llcx, cgcx.fewer_names.to_llvm_bool());
let llmod_raw = back::lto::parse_module(llcx, name, buffer, dcx);
let llcx_raw = create_context_and_set_fewer_names(cgcx.fewer_names);
let llmod_raw = back::lto::parse_module(llcx_raw.as_ref(), name, buffer, dcx);
let tm = tm_factory(dcx, TargetMachineFactoryConfig::new(cgcx, name.to_str().unwrap()));

ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(tm) }
ModuleLlvm {
llmod_raw,
llcx_raw,
tm: ManuallyDrop::new(tm),
thin_lto_buffer: OnceLock::new(),
}
}
}

pub(crate) fn llmod(&self) -> &llvm::Module {
unsafe { &*self.llmod_raw }
/// Passes a [`ModuleRefs`] struct to a callback function, giving it scoped
/// access to the resources owned by this module.
///
/// Passing the references to a callback ensures that they have coherent
/// `'ll` lifetimes, and that they can't escape the callback.
pub(crate) fn with_module_refs<R>(
&self,
inner_fn: impl for<'ll> FnOnce(ModuleRefs<'ll>) -> R,
) -> R {
inner_fn(ModuleRefs {
llcx: unsafe { self.llcx_raw.as_ref() },
llmod: unsafe { &*self.llmod_raw },
tm: self.tm.raw(),
_ph: PhantomData,
})
}
}

impl Drop for ModuleLlvm {
fn drop(&mut self) {
unsafe {
ManuallyDrop::drop(&mut self.tm);
llvm::LLVMContextDispose(&mut *(self.llcx as *mut _));
llvm::LLVMContextDispose(self.llcx_raw);
}
}
}

/// Bundle of references provided by [`ModuleLlvm::with_module_refs`].
#[derive(Clone, Copy)]
pub(crate) struct ModuleRefs<'ll> {
pub(crate) llcx: &'ll llvm::Context,
pub(crate) llmod: &'ll llvm::Module,
pub(crate) tm: &'ll llvm::TargetMachine,
/// Make sure `'ll` is invariant, even if no fields happen to require that.
///
/// FIXME(Zalathar): Consider using `PhantomInvariantLifetime` when the API
/// for that is settled.
_ph: PhantomData<fn(&'ll ()) -> &'ll ()>,
}
77 changes: 44 additions & 33 deletions compiler/rustc_codegen_llvm/src/back/lto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use rustc_span::bug;
use rustc_structures::SanitizerSet;
use tracing::{debug, info};

use crate::back::llvm_backend::ModuleRefs;
use crate::back::write::{
self, CodegenDiagnosticsStage, DiagnosticHandlers, bitcode_section_name, codegen,
save_temp_bitcode,
Expand Down Expand Up @@ -249,7 +250,9 @@ fn fat_lto(
.enumerate()
.filter(|&(_, module)| module.kind == ModuleKind::Regular)
.map(|(i, module)| {
let cost = unsafe { llvm::LLVMRustModuleCost(module.module_llvm.llmod()) };
let cost = module.module_llvm.with_module_refs(|ModuleRefs { llmod, .. }| unsafe {
llvm::LLVMRustModuleCost(llmod)
});
(cost, i)
})
.max();
Expand All @@ -269,11 +272,7 @@ fn fat_lto(
ModuleCodegen::new_regular(name.into_string().unwrap(), llvm_module)
}
};
{
let (llcx, llmod) = {
let llvm = &module.module_llvm;
(&llvm.llcx, llvm.llmod())
};
module.module_llvm.with_module_refs(|ModuleRefs { llcx, llmod, .. }| {
info!("using {:?} as a base module", module.name);

// The linking steps below may produce errors and diagnostics within LLVM
Expand All @@ -293,7 +292,9 @@ fn fat_lto(
// way we know of to do that is to serialize them to a string and them parse
// them later. Not great but hey, that's why it's "fat" LTO, right?
for module in in_memory {
let buffer = ModuleBuffer::new(module.module_llvm.llmod(), false);
let buffer = module
.module_llvm
.with_module_refs(|ModuleRefs { llmod, .. }| ModuleBuffer::new(llmod, false));
let llmod_id = CString::new(&module.name[..]).unwrap();
serialized_modules.push((SerializedModule::Local(buffer), llmod_id));
}
Expand Down Expand Up @@ -330,7 +331,7 @@ fn fat_lto(
);
}
save_temp_bitcode(cgcx, &module, "lto.after-restriction");
}
});

module
}
Expand Down Expand Up @@ -584,7 +585,7 @@ pub(crate) fn run_pass_manager(
cgcx: &CodegenContext,
prof: &SelfProfilerRef,
dcx: DiagCtxtHandle<'_>,
module: &mut ModuleCodegen<ModuleLlvm>,
module: &ModuleCodegen<ModuleLlvm>,
thin: bool,
) {
let _timer = prof.generic_activity_with_arg("LLVM_lto_optimize", &*module.name);
Expand Down Expand Up @@ -612,29 +613,40 @@ pub(crate) fn run_pass_manager(
if enable_ad { write::AutodiffStage::DuringAD } else { write::AutodiffStage::PostAD }
};

unsafe {
write::llvm_optimize(
cgcx, prof, dcx, module, None, None, config, opt_level, opt_stage, stage,
);
}
let llvm_optimize = |opt_stage, stage| {
module.module_llvm.with_module_refs(|module_refs| unsafe {
write::llvm_optimize(
cgcx,
prof,
dcx,
module_refs,
None,
None,
config,
opt_level,
opt_stage,
stage,
)
});
};

llvm_optimize(opt_stage, stage);

if enable_ad && !thin {
let opt_stage = llvm::OptStage::FatLTO;
let stage = write::AutodiffStage::PostAD;
if !config.autodiff.contains(&config::AutoDiff::NoPostopt)
&& config.autodiff_post_passes.as_deref() != Some("")
{
unsafe {
write::llvm_optimize(
cgcx, prof, dcx, module, None, None, config, opt_level, opt_stage, stage,
);
}
llvm_optimize(opt_stage, stage);
}

// This is the final IR, so people should be able to inspect the optimized autodiff output,
// for manual inspection.
if config.autodiff.contains(&config::AutoDiff::PrintModFinal) {
unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) };
module.module_llvm.with_module_refs(|ModuleRefs { llmod, .. }| unsafe {
llvm::LLVMDumpModule(llmod)
});
}
}

Expand Down Expand Up @@ -715,14 +727,16 @@ pub(crate) fn optimize_and_codegen_thin_module(
// crates but for locally codegened modules we may be able to reuse
// that LLVM Context and Module.
let module_llvm = ModuleLlvm::parse(cgcx, tm_factory, module_name, thin_module.data(), dcx);
let mut module = ModuleCodegen::new_regular(thin_module.name(), module_llvm);
let module = ModuleCodegen::new_regular(thin_module.name(), module_llvm);
// Given that the newly created module lacks a thinlto buffer for embedding, we need to re-add it here.
if cgcx.module_config.embed_bitcode() {
module.thin_lto_buffer = Some(thin_module.data().to_vec());
module
.module_llvm
.thin_lto_buffer
.set(thin_module.data().to_vec())
.expect("thin_lto_buffer should be unset");
}
{
let target = &*module.module_llvm.tm;
let llmod = module.module_llvm.llmod();
module.module_llvm.with_module_refs(|ModuleRefs { llmod, tm, .. }| {
save_temp_bitcode(cgcx, &module, "thin-lto-input");

// Up next comes the per-module local analyses that we do for Thin LTO.
Expand All @@ -735,9 +749,7 @@ pub(crate) fn optimize_and_codegen_thin_module(
// bindings we've got (currently `PassWrapper.cpp`)
{
let _timer = prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name());
unsafe {
llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target.raw())
};
unsafe { llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, tm) };
save_temp_bitcode(cgcx, &module, "thin-lto-after-rename");
}

Expand All @@ -763,9 +775,8 @@ pub(crate) fn optimize_and_codegen_thin_module(

{
let _timer = prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name());
if unsafe {
!llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, target.raw())
} {
if unsafe { !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, tm) }
{
write::llvm_err(dcx, LlvmError::PrepareThinLtoModule);
}
save_temp_bitcode(cgcx, &module, "thin-lto-after-import");
Expand All @@ -778,10 +789,10 @@ pub(crate) fn optimize_and_codegen_thin_module(
// little differently.
{
info!("running thin lto passes over {}", module.name);
run_pass_manager(cgcx, prof, dcx, &mut module, true);
run_pass_manager(cgcx, prof, dcx, &module, true);
save_temp_bitcode(cgcx, &module, "thin-lto-after-pm");
}
}
});
codegen(cgcx, prof, shared_emitter, module, &cgcx.module_config)
}

Expand Down
Loading
Loading