https://github.com/devajithvs updated https://github.com/llvm/llvm-project/pull/166071
>From a27e27b1b42076d152a729ab5f58c6d95571333b Mon Sep 17 00:00:00 2001 From: Devajith Valaparambil Sreeramaswamy <[email protected]> Date: Sun, 2 Nov 2025 13:57:11 +0100 Subject: [PATCH 1/3] [ORC] Fix platform state cleanup after failed materialization When a module's initialization fails, the GenericLLVMIRPlatformSupport would retain stale entries in its InitFunctions and DeInitFunctions maps. This caused the next module initialization to fail with "Failed to materialize symbols" errors, as it contains stale symbols from the previously failed module. This patch introduces a notifyFailed hook that allows platforms to clean up bookkeeping when materialization fails. Fixes the clang-repl issue where after calling an undefined function any statement would throw an error. --- llvm/include/llvm/ExecutionEngine/Orc/Core.h | 7 ++++ llvm/lib/ExecutionEngine/Orc/Core.cpp | 4 ++ llvm/lib/ExecutionEngine/Orc/LLJIT.cpp | 43 ++++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Core.h b/llvm/include/llvm/ExecutionEngine/Orc/Core.h index 24a0cb74b1fbc..2f1a66e1af5f5 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/Core.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/Core.h @@ -1289,6 +1289,13 @@ class LLVM_ABI Platform { /// ResourceTracker is removed. virtual Error notifyRemoving(ResourceTracker &RT) = 0; + /// This method will be called when materialization fails for symbols managed + /// by the given MaterializationResponsibility. Platforms can override this to + /// clean up internal bookkeeping (e.g., init/deinit symbol tracking). + virtual Error notifyFailed(MaterializationResponsibility &MR) { + return Error::success(); + } + /// A utility function for looking up initializer symbols. Performs a blocking /// lookup for the given symbols in each of the given JITDylibs. /// diff --git a/llvm/lib/ExecutionEngine/Orc/Core.cpp b/llvm/lib/ExecutionEngine/Orc/Core.cpp index d029ac587fb9a..9f0dc1def9001 100644 --- a/llvm/lib/ExecutionEngine/Orc/Core.cpp +++ b/llvm/lib/ExecutionEngine/Orc/Core.cpp @@ -3162,6 +3162,10 @@ void ExecutionSession::OL_notifyFailed(MaterializationResponsibility &MR) { if (MR.SymbolFlags.empty()) return; + // Notify the platform to clean up any failed-symbol bookkeeping. + if (auto *Plat = getPlatform()) + cantFail(Plat->notifyFailed(MR)); + SymbolNameVector SymbolsToFail; for (auto &[Name, Flags] : MR.SymbolFlags) SymbolsToFail.push_back(Name); diff --git a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp index 7487526c5d059..b2da46bc89264 100644 --- a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp +++ b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp @@ -99,6 +99,7 @@ class GenericLLVMIRPlatform : public Platform { // Noop -- Nothing to do (yet). return Error::success(); } + Error notifyFailed(MaterializationResponsibility &MR) override; private: GenericLLVMIRPlatformSupport &S; @@ -227,6 +228,44 @@ class GenericLLVMIRPlatformSupport : public LLJIT::PlatformSupport { return Error::success(); } + Error notifyFailed(MaterializationResponsibility &MR) { + auto &JD = MR.getTargetJITDylib(); + + // We only care about symbols with our known (init/deinit) prefixes + DenseSet<SymbolStringPtr> FailedInitSyms; + DenseSet<SymbolStringPtr> FailedDeInitSyms; + + for (auto &[Name, Flags] : MR.getSymbols()) { + if ((*Name).starts_with(InitFunctionPrefix)) + FailedInitSyms.insert(Name); + else if ((*Name).starts_with(DeInitFunctionPrefix)) + FailedDeInitSyms.insert(Name); + } + + // Remove failed symbols from tracking maps. + auto cleanMap = [&](auto &Map, + const DenseSet<SymbolStringPtr> &FailedSyms) { + if (FailedSyms.empty()) + return; + + auto It = Map.find(&JD); + if (It == Map.end()) + return; + + It->second.remove_if([&](const SymbolStringPtr &Name, SymbolLookupFlags) { + return FailedSyms.contains(Name); + }); + + if (It->second.empty()) + Map.erase(It); + }; + + cleanMap(InitFunctions, FailedInitSyms); + cleanMap(DeInitFunctions, FailedDeInitSyms); + + return Error::success(); + } + Error initialize(JITDylib &JD) override { LLVM_DEBUG({ dbgs() << "GenericLLVMIRPlatformSupport getting initializers to run\n"; @@ -505,6 +544,10 @@ Error GenericLLVMIRPlatform::notifyAdding(ResourceTracker &RT, return S.notifyAdding(RT, MU); } +Error GenericLLVMIRPlatform::notifyFailed(MaterializationResponsibility &MR) { + return S.notifyFailed(MR); +} + Expected<ThreadSafeModule> GlobalCtorDtorScraper::operator()(ThreadSafeModule TSM, MaterializationResponsibility &R) { >From 7497b076793b873b7174bfa426bd57c377e42613 Mon Sep 17 00:00:00 2001 From: Devajith Valaparambil Sreeramaswamy <[email protected]> Date: Sun, 2 Nov 2025 11:12:55 +0100 Subject: [PATCH 2/3] [ORC][test] Add LLJIT platform cleanup tests --- .../ExecutionEngine/Orc/CMakeLists.txt | 1 + .../ExecutionEngine/Orc/LLJITTest.cpp | 105 ++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 llvm/unittests/ExecutionEngine/Orc/LLJITTest.cpp diff --git a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt index 7b563d7bcc68c..92bc935c3fd78 100644 --- a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt +++ b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt @@ -27,6 +27,7 @@ add_llvm_unittest(OrcJITTests JITTargetMachineBuilderTest.cpp LazyCallThroughAndReexportsTest.cpp LibraryResolverTest.cpp + LLJITTest.cpp LookupAndRecordAddrsTest.cpp MachOPlatformTest.cpp MapperJITLinkMemoryManagerTest.cpp diff --git a/llvm/unittests/ExecutionEngine/Orc/LLJITTest.cpp b/llvm/unittests/ExecutionEngine/Orc/LLJITTest.cpp new file mode 100644 index 0000000000000..a8432f2d0e2e6 --- /dev/null +++ b/llvm/unittests/ExecutionEngine/Orc/LLJITTest.cpp @@ -0,0 +1,105 @@ +#include "llvm/ExecutionEngine/Orc/LLJIT.h" +#include "OrcTestCommon.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Testing/Support/Error.h" + +using namespace llvm; +using namespace llvm::orc; + +namespace { + +static ThreadSafeModule parseModule(llvm::StringRef Source, + llvm::StringRef Name) { + auto Ctx = std::make_unique<LLVMContext>(); + SMDiagnostic Err; + auto M = parseIR(MemoryBufferRef(Source, Name), Err, *Ctx); + if (!M) { + Err.print("Testcase source failed to parse: ", errs()); + exit(1); + } + return ThreadSafeModule(std::move(M), std::move(Ctx)); +} + +TEST(LLJITTest, CleanupFailedInitializers) { + OrcNativeTarget::initialize(); + auto J = cantFail(LLJITBuilder().create()); + auto &JD = J->getMainJITDylib(); + + // ctor references undefined symbol 'testing' + auto TSM_A = parseModule(R"( + declare void @testing() + + define internal void @ctor_A() { + call void @testing() + ret void + } + + @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [ + { i32, ptr, ptr } { i32 65535, ptr @ctor_A, ptr null } + ] + )", + "A"); + + cantFail(J->addIRModule(std::move(TSM_A))); + + // Initialize fails: "Symbols not found: [ testing ]" + EXPECT_THAT_ERROR(J->initialize(JD), Failed()); + + // Clean module should succeed if A's bookkeeping was cleaned up + auto TSM_B = parseModule(R"( + @i = global i32 42 + )", + "B"); + + cantFail(J->addIRModule(std::move(TSM_B))); + + EXPECT_THAT_ERROR(J->initialize(JD), Succeeded()); +} + +TEST(LLJITTest, RepeatedInitializationFailures) { + // Consecutive failures don't accumulate stale state + OrcNativeTarget::initialize(); + auto J = cantFail(LLJITBuilder().create()); + auto &JD = J->getMainJITDylib(); + + // First failure + auto TSM_A = parseModule(R"( + declare void @undefined_a() + define internal void @ctor_A() { + call void @undefined_a() + ret void + } + @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [ + { i32, ptr, ptr } { i32 65535, ptr @ctor_A, ptr null } + ] + )", + "A"); + cantFail(J->addIRModule(std::move(TSM_A))); + EXPECT_THAT_ERROR(J->initialize(JD), Failed()); + + // Second failure + auto TSM_B = parseModule(R"( + declare void @undefined_b() + define internal void @ctor_B() { + call void @undefined_b() + ret void + } + @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [ + { i32, ptr, ptr } { i32 65535, ptr @ctor_B, ptr null } + ] + )", + "B"); + cantFail(J->addIRModule(std::move(TSM_B))); + EXPECT_THAT_ERROR(J->initialize(JD), Failed()); + + // Should succeed, both A and B cleaned up + auto TSM_C = parseModule(R"( + @x = global i32 0 + )", + "C"); + cantFail(J->addIRModule(std::move(TSM_C))); + EXPECT_THAT_ERROR(J->initialize(JD), Succeeded()); +} + +} // anonymous namespace >From e23938793877847e5e2f99cc89717648675c2402 Mon Sep 17 00:00:00 2001 From: Devajith Valaparambil Sreeramaswamy <[email protected]> Date: Sun, 2 Nov 2025 13:16:43 +0100 Subject: [PATCH 3/3] [clang-repl][test] Add test for failed materialization recovery --- clang/test/Interpreter/recovery-after-failure.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 clang/test/Interpreter/recovery-after-failure.cpp diff --git a/clang/test/Interpreter/recovery-after-failure.cpp b/clang/test/Interpreter/recovery-after-failure.cpp new file mode 100644 index 0000000000000..c9f1b52cbede9 --- /dev/null +++ b/clang/test/Interpreter/recovery-after-failure.cpp @@ -0,0 +1,13 @@ +// REQUIRES: host-supports-jit +// UNSUPPORTED: system-aix +// RUN: cat %s | clang-repl 2>&1 | FileCheck %s + +// Failed materialization shouldn't poison subsequent statements +extern "C" int undefined_function(); +int result = undefined_function(); +// CHECK: error: Failed to materialize symbols + +int x = 42; +// CHECK-NOT: error: Failed to materialize symbols + +%quit _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
