From 2160998a06d10879cb4fb790f32ecde1eb317186 Mon Sep 17 00:00:00 2001 From: Aiden Grossman Date: Wed, 9 Sep 2026 13:52:30 -0700 Subject: [PATCH 1/3] Refactor CodeGen Pipeline Setup into a Single Function This patch refactors setup of the LegacyPassManager for CodeGen into a single function. This simplifies some things (lifetime for the LegacyPM object), but is primarily aimed at enabling rustc to use the new runCodeGenPipeline abstraction (introduced in LLVM 24) which handles all of these details within LLVM and additionally defaults to the NewPM when a target opts-in/the correct LLVM flag is passed. --- compiler/rustc_codegen_llvm/src/back/write.rs | 5 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 15 +---- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 65 +++++++++---------- 3 files changed, 31 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index dd2dbdff6b82c..c7cfa87d86e5e 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -72,17 +72,14 @@ fn write_output_file<'ll>( std::ptr::null() }; let result = unsafe { - let pm = llvm::LLVMCreatePassManager(); - llvm::LLVMAddAnalysisPasses(target, pm); - llvm::LLVMRustAddLibraryInfo(target, pm, m, no_builtins); llvm::LLVMRustWriteOutputFile( target, - pm, m, output_c.as_ptr(), dwo_output_ptr, file_type, verify_llvm_ir, + no_builtins, ) }; diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 655a79e341bd1..eca5fe429f541 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -715,8 +715,6 @@ unsafe extern "C" { } #[repr(C)] pub(crate) struct Builder<'a>(InvariantOpaque<'a>); -#[repr(C)] -pub(crate) struct PassManager<'a>(InvariantOpaque<'a>); unsafe extern "C" { pub type TargetMachine; } @@ -1637,11 +1635,6 @@ unsafe extern "C" { /// Writes a module to the specified path. Returns 0 on success. pub(crate) fn LLVMWriteBitcodeToFile(M: &Module, Path: *const c_char) -> c_int; - /// Creates a legacy pass manager -- only used for final codegen. - pub(crate) fn LLVMCreatePassManager<'a>() -> &'a mut PassManager<'a>; - - pub(crate) fn LLVMAddAnalysisPasses<'a>(T: &'a TargetMachine, PM: &PassManager<'a>); - pub(crate) fn LLVMGetHostCPUFeatures() -> *mut c_char; pub(crate) fn LLVMDisposeMessage(message: *mut c_char); @@ -2430,20 +2423,14 @@ unsafe extern "C" { pub(crate) fn LLVMRustDisposeMCSubtargetInfo(MCInfo: ptr::NonNull); - pub(crate) fn LLVMRustAddLibraryInfo<'a>( - T: &TargetMachine, - PM: &PassManager<'a>, - M: &'a Module, - DisableSimplifyLibCalls: bool, - ); pub(crate) fn LLVMRustWriteOutputFile<'a>( T: &'a TargetMachine, - PM: *mut PassManager<'a>, M: &'a Module, Output: *const c_char, DwoOutput: *const c_char, FileType: FileType, VerifyIR: bool, + DisableSimplifyLibCalls: bool, ) -> LLVMRustResult; pub(crate) fn LLVMRustOptimize<'a>( M: &'a Module, diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index beb63ad61493c..5c510a74078c2 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -447,30 +447,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( return wrap(TM); } -// Unfortunately, the LLVM C API doesn't provide a way to create the -// TargetLibraryInfo pass, so we use this method to do so. -extern "C" void LLVMRustAddLibraryInfo(LLVMTargetMachineRef T, - LLVMPassManagerRef PMR, LLVMModuleRef M, - bool DisableSimplifyLibCalls) { - auto TargetTriple = Triple(unwrap(M)->getTargetTriple()); - TargetOptions *Options = &unwrap(T)->Options; - auto TLII = TargetLibraryInfoImpl(TargetTriple); - if (DisableSimplifyLibCalls) - TLII.disableAllFunctions(); - unwrap(PMR)->add(new TargetLibraryInfoWrapperPass(TLII)); -#if LLVM_VERSION_GE(24, 0) - // LLVM 24 removed TargetOptions::EABIVersion and ExceptionModel; the EABI - // version and exception model are now derived from the target triple and - // module flags respectively instead. - unwrap(PMR)->add(new RuntimeLibraryInfoWrapper(Options->MCOptions.ABIName, - Options->VecLib)); -#elif LLVM_VERSION_GE(22, 0) - unwrap(PMR)->add(new RuntimeLibraryInfoWrapper( - TargetTriple, Options->ExceptionModel, Options->FloatABIType, - Options->EABIVersion, Options->MCOptions.ABIName, Options->VecLib)); -#endif -} - extern "C" void LLVMRustSetLLVMOptions(int Argc, char **Argv) { // Initializing the command-line options more than once is not allowed. So, // check if they've already been initialized. (This could happen if we're @@ -500,10 +476,31 @@ static CodeGenFileType fromRust(LLVMRustFileType Type) { } extern "C" LLVMRustResult -LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, - LLVMModuleRef M, const char *Path, const char *DwoPath, - LLVMRustFileType RustFileType, bool VerifyIR) { - llvm::legacy::PassManager *PM = unwrap(PMR); +LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMModuleRef M, + const char *Path, const char *DwoPath, + LLVMRustFileType RustFileType, bool VerifyIR, + bool DisableSimplifyLibCalls) { + llvm::legacy::PassManager PM; + + PM.add(createTargetTransformInfoWrapperPass( + unwrap(Target)->getTargetIRAnalysis())); + + auto TargetTriple = Triple(unwrap(M)->getTargetTriple()); + TargetOptions *Options = &unwrap(Target)->Options; + auto TLII = TargetLibraryInfoImpl(TargetTriple); + if (DisableSimplifyLibCalls) + TLII.disableAllFunctions(); + PM.add(new TargetLibraryInfoWrapperPass(TLII)); +#if LLVM_VERSION_GE(24, 0) + PM.add(new RuntimeLibraryInfoWrapper( + Options->ExceptionModel, Options->EABIVersion, Options->MCOptions.ABIName, + Options->VecLib)); +#elif LLVM_VERSION_GE(22, 0) + PM.add(new RuntimeLibraryInfoWrapper( + TargetTriple, Options->ExceptionModel, Options->FloatABIType, + Options->EABIVersion, Options->MCOptions.ABIName, Options->VecLib)); +#endif + auto FileType = fromRust(RustFileType); std::string ErrorInfo; @@ -527,17 +524,13 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, return LLVMRustResult::Failure; } auto DBOS = buffer_ostream(DOS); - unwrap(Target)->addPassesToEmitFile(*PM, BOS, &DBOS, FileType, !VerifyIR); - PM->run(*unwrap(M)); + unwrap(Target)->addPassesToEmitFile(PM, BOS, &DBOS, FileType, !VerifyIR); + PM.run(*unwrap(M)); } else { - unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, !VerifyIR); - PM->run(*unwrap(M)); + unwrap(Target)->addPassesToEmitFile(PM, BOS, nullptr, FileType, !VerifyIR); + PM.run(*unwrap(M)); } - // Apparently `addPassesToEmitFile` adds a pointer to our on-the-stack output - // stream (OS), so the only real safe place to delete this is here? Don't we - // wish this was written in Rust? - LLVMDisposePassManager(PMR); return LLVMRustResult::Success; } From dc7db04ab19b66846f7f0d082495151b13593bd7 Mon Sep 17 00:00:00 2001 From: Aiden Grossman Date: Thu, 24 Sep 2026 07:53:15 -0700 Subject: [PATCH 2/3] feedback --- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 5c510a74078c2..303c0456833ad 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -480,9 +480,10 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMModuleRef M, const char *Path, const char *DwoPath, LLVMRustFileType RustFileType, bool VerifyIR, bool DisableSimplifyLibCalls) { - llvm::legacy::PassManager PM; + std::unique_ptr PM = + std::make_unique(); - PM.add(createTargetTransformInfoWrapperPass( + PM->add(createTargetTransformInfoWrapperPass( unwrap(Target)->getTargetIRAnalysis())); auto TargetTriple = Triple(unwrap(M)->getTargetTriple()); @@ -490,13 +491,15 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMModuleRef M, auto TLII = TargetLibraryInfoImpl(TargetTriple); if (DisableSimplifyLibCalls) TLII.disableAllFunctions(); - PM.add(new TargetLibraryInfoWrapperPass(TLII)); + PM->add(new TargetLibraryInfoWrapperPass(TLII)); #if LLVM_VERSION_GE(24, 0) - PM.add(new RuntimeLibraryInfoWrapper( - Options->ExceptionModel, Options->EABIVersion, Options->MCOptions.ABIName, - Options->VecLib)); + // LLVM 24 removed TargetOptions::EABIVersion and ExceptionModel; the EABI + // version and exception model are now derived from the target triple and + // module flags respectively instead. + PM->add(new RuntimeLibraryInfoWrapper(Options->MCOptions.ABIName, + Options->VecLib)); #elif LLVM_VERSION_GE(22, 0) - PM.add(new RuntimeLibraryInfoWrapper( + PM->add(new RuntimeLibraryInfoWrapper( TargetTriple, Options->ExceptionModel, Options->FloatABIType, Options->EABIVersion, Options->MCOptions.ABIName, Options->VecLib)); #endif @@ -524,13 +527,18 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMModuleRef M, return LLVMRustResult::Failure; } auto DBOS = buffer_ostream(DOS); - unwrap(Target)->addPassesToEmitFile(PM, BOS, &DBOS, FileType, !VerifyIR); - PM.run(*unwrap(M)); + unwrap(Target)->addPassesToEmitFile(*PM, BOS, &DBOS, FileType, !VerifyIR); + PM->run(*unwrap(M)); } else { - unwrap(Target)->addPassesToEmitFile(PM, BOS, nullptr, FileType, !VerifyIR); - PM.run(*unwrap(M)); + unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, !VerifyIR); + PM->run(*unwrap(M)); } + // TargetMachine::addPassesToEmitFile stores a pointer to the output stream + // in a couple of places inside of the object. Explicitly delete the PM here + // to ensure that the output stream always outlives the PM. + PM.reset(); + return LLVMRustResult::Success; } From 5f463ae6f6cfb8eaab1e9bae204bddc2ae87f48a Mon Sep 17 00:00:00 2001 From: Aiden Grossman Date: Thu, 24 Sep 2026 08:11:02 -0700 Subject: [PATCH 3/3] more lifetime fixes --- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 303c0456833ad..f73ca2ea30c16 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -516,6 +516,9 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMModuleRef M, return LLVMRustResult::Failure; } + // TargetMachine::addPassesToEmitFile stores pointers to the output streams + // in a couple of places inside of the object. Explicitly delete the PM after + // we call run() to avoid dangling references. auto BOS = buffer_ostream(OS); if (DwoPath) { auto DOS = raw_fd_ostream(DwoPath, EC, sys::fs::OF_None); @@ -529,16 +532,13 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMModuleRef M, auto DBOS = buffer_ostream(DOS); unwrap(Target)->addPassesToEmitFile(*PM, BOS, &DBOS, FileType, !VerifyIR); PM->run(*unwrap(M)); + PM.reset(); } else { unwrap(Target)->addPassesToEmitFile(*PM, BOS, nullptr, FileType, !VerifyIR); PM->run(*unwrap(M)); + PM.reset(); } - // TargetMachine::addPassesToEmitFile stores a pointer to the output stream - // in a couple of places inside of the object. Explicitly delete the PM here - // to ensure that the output stream always outlives the PM. - PM.reset(); - return LLVMRustResult::Success; }