https://github.com/anutosh491 updated https://github.com/llvm/llvm-project/pull/197133
>From 4afff3aee886a871a60b76335d9f9c8a7968be30 Mon Sep 17 00:00:00 2001 From: anutosh491 <[email protected]> Date: Tue, 12 May 2026 14:22:33 +0530 Subject: [PATCH 1/3] Handle -mllvm args for clang-repl before calling executeAction --- clang/include/clang/Frontend/CompilerInstance.h | 5 +++++ .../clang/Interpreter/IncrementalExecutor.h | 3 +++ clang/lib/Frontend/CompilerInstance.cpp | 14 ++++++++++++++ .../FrontendTool/ExecuteCompilerInvocation.cpp | 16 +--------------- clang/lib/Interpreter/IncrementalExecutor.cpp | 2 +- clang/lib/Interpreter/Interpreter.cpp | 7 +++++++ clang/lib/Interpreter/Wasm.cpp | 17 ++++++++++++++++- clang/lib/Interpreter/Wasm.h | 5 ++++- clang/unittests/Interpreter/CMakeLists.txt | 1 + clang/unittests/Interpreter/InterpreterTest.cpp | 16 ++++++++++++++++ .../Interpreter/InterpreterTestFixture.h | 12 +++++++++++- 11 files changed, 79 insertions(+), 19 deletions(-) diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h index 21b83dfefa842..cf70feff553b9 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -259,6 +259,11 @@ class CompilerInstance : public ModuleLoader { /// Load the list of plugins requested in the \c FrontendOptions. void LoadRequestedPlugins(); + /// Parse and apply LLVM command line arguments from FrontendOptions. + /// This processes the LLVMArgs option that comes from -mllvm flags. + /// This should be called after plugins are loaded and before ExecuteAction. + void parseLLVMArgs(); + /// @} /// @name Compiler Invocation and Options /// @{ diff --git a/clang/include/clang/Interpreter/IncrementalExecutor.h b/clang/include/clang/Interpreter/IncrementalExecutor.h index 913da9230a947..18d482878c4fe 100644 --- a/clang/include/clang/Interpreter/IncrementalExecutor.h +++ b/clang/include/clang/Interpreter/IncrementalExecutor.h @@ -52,6 +52,9 @@ class IncrementalExecutorBuilder { std::optional<llvm::CodeModel::Model> CM = std::nullopt; /// An optional external IncrementalExecutor std::unique_ptr<IncrementalExecutor> IE; + /// mllvm args from the frontend; on wasm these are re-applied after each + /// lldMain call because lld resets all cl options for test-isolation purposes. + std::vector<std::string> LLVMArgs; /// An optional external orc jit builder std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder; /// A default callback that can be used in the IncrementalCompilerBuilder to diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 87abcd38c1a92..7b755deb5863b 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -1135,6 +1135,20 @@ void CompilerInstance::LoadRequestedPlugins() { } } +void CompilerInstance::parseLLVMArgs() { + if (!getFrontendOpts().LLVMArgs.empty()) { + unsigned NumArgs = getFrontendOpts().LLVMArgs.size(); + auto Args = std::make_unique<const char *[]>(NumArgs + 2); + Args[0] = "clang (LLVM option parsing)"; + for (unsigned i = 0; i != NumArgs; ++i) + Args[i + 1] = getFrontendOpts().LLVMArgs[i].c_str(); + Args[NumArgs + 1] = nullptr; + llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get(), /*Overview=*/"", + /*Errs=*/nullptr, + /*VFS=*/&getVirtualFileSystem()); + } +} + /// Determine the appropriate source input kind based on language /// options. static Language getLanguageFromOptions(const LangOptions &LangOpts) { diff --git a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index e872f0823f23e..24e699b42ca7f 100644 --- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -253,21 +253,7 @@ bool ExecuteCompilerInvocation(CompilerInstance *Clang) { Clang->LoadRequestedPlugins(); - // Honor -mllvm. - // - // FIXME: Remove this, one day. - // This should happen AFTER plugins have been loaded! - if (!Clang->getFrontendOpts().LLVMArgs.empty()) { - unsigned NumArgs = Clang->getFrontendOpts().LLVMArgs.size(); - auto Args = std::make_unique<const char*[]>(NumArgs + 2); - Args[0] = "clang (LLVM option parsing)"; - for (unsigned i = 0; i != NumArgs; ++i) - Args[i + 1] = Clang->getFrontendOpts().LLVMArgs[i].c_str(); - Args[NumArgs + 1] = nullptr; - llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get(), /*Overview=*/"", - /*Errs=*/nullptr, - /*VFS=*/&Clang->getVirtualFileSystem()); - } + Clang->parseLLVMArgs(); #if CLANG_ENABLE_STATIC_ANALYZER // These should happen AFTER plugins have been loaded! diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp index 6d337e7848699..34b8691554680 100644 --- a/clang/lib/Interpreter/IncrementalExecutor.cpp +++ b/clang/lib/Interpreter/IncrementalExecutor.cpp @@ -443,7 +443,7 @@ IncrementalExecutorBuilder::create(llvm::orc::ThreadSafeContext &TSC, llvm::Error Err = llvm::Error::success(); std::unique_ptr<IncrementalExecutor> Executor; #ifdef __EMSCRIPTEN__ - Executor = std::make_unique<WasmIncrementalExecutor>(Err); + Executor = std::make_unique<WasmIncrementalExecutor>(Err, LLVMArgs); #else Executor = std::make_unique<OrcIncrementalExecutor>(TSC, *JITBuilder, Err); #endif diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 092f3ede771f6..21ad9438ee44a 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -351,6 +351,9 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> Instance, auto LLVMCtx = std::make_unique<llvm::LLVMContext>(); TSCtx = std::make_unique<llvm::orc::ThreadSafeContext>(std::move(LLVMCtx)); + // Honor -mllvm options + CI->parseLLVMArgs(); + Act = TSCtx->withContextDo([&](llvm::LLVMContext *Ctx) { return std::make_unique<IncrementalAction>(*CI, *Ctx, ErrOut, *this, std::move(Consumer)); @@ -604,6 +607,10 @@ llvm::Error Interpreter::CreateExecutor() { if (!IncrExecutorBuilder) IncrExecutorBuilder = std::make_unique<IncrementalExecutorBuilder>(); + // Propagate mllvm args so the wasm executor can restore them after each + // lldMain invocation (which resets all cl options for test isolation). + IncrExecutorBuilder->LLVMArgs = CI->getFrontendOpts().LLVMArgs; + auto ExecutorOrErr = IncrExecutorBuilder->create(*TSCtx, CI->getTarget()); if (ExecutorOrErr) IncrExecutor = std::move(*ExecutorOrErr); diff --git a/clang/lib/Interpreter/Wasm.cpp b/clang/lib/Interpreter/Wasm.cpp index 96600cf9fa6d0..9ebd23da5f564 100644 --- a/clang/lib/Interpreter/Wasm.cpp +++ b/clang/lib/Interpreter/Wasm.cpp @@ -12,6 +12,7 @@ #include "Wasm.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include <llvm/IR/LegacyPassManager.h> @@ -59,7 +60,9 @@ bool link(llvm::ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS, namespace clang { -WasmIncrementalExecutor::WasmIncrementalExecutor(llvm::Error &Err) { +WasmIncrementalExecutor::WasmIncrementalExecutor(llvm::Error &Err, + std::vector<std::string> LLVMArgs) + : StoredLLVMArgs(std::move(LLVMArgs)) { llvm::ErrorAsOutParameter EAO(&Err); if (Err) @@ -132,6 +135,18 @@ llvm::Error WasmIncrementalExecutor::addModule(PartialTranslationUnit &PTU) { lld::Result Result = lld::lldMain(LinkerArgs, llvm::outs(), llvm::errs(), WasmDriverArgs); + // lld::wasm::linkerMain calls cl::ResetAllOptionOccurrences() which wipes + // all global LLVM cl options, including mllvm flags set by the frontend + // (e.g. -wasm-enable-eh, -wasm-enable-sjlj). Re-apply them so the next + // Parse() call's WebAssemblyTargetMachine creation finds the correct state. + if (!StoredLLVMArgs.empty()) { + std::vector<const char *> ArgPtrs; + ArgPtrs.push_back("clang-repl (restoring LLVM options)"); + for (const std::string &Arg : StoredLLVMArgs) + ArgPtrs.push_back(Arg.c_str()); + llvm::cl::ParseCommandLineOptions(ArgPtrs.size(), ArgPtrs.data()); + } + if (Result.retCode) return llvm::make_error<llvm::StringError>( "Failed to link incremental module", llvm::inconvertibleErrorCode()); diff --git a/clang/lib/Interpreter/Wasm.h b/clang/lib/Interpreter/Wasm.h index bf8777a41eac2..6340393dc6b9b 100644 --- a/clang/lib/Interpreter/Wasm.h +++ b/clang/lib/Interpreter/Wasm.h @@ -19,12 +19,14 @@ #include "clang/Interpreter/IncrementalExecutor.h" #include "llvm/ADT/SmallString.h" +#include <string> +#include <vector> namespace clang { class WasmIncrementalExecutor : public IncrementalExecutor { public: - WasmIncrementalExecutor(llvm::Error &Err); + WasmIncrementalExecutor(llvm::Error &Err, std::vector<std::string> LLVMArgs); ~WasmIncrementalExecutor() override; llvm::Error addModule(PartialTranslationUnit &PTU) override; @@ -38,6 +40,7 @@ class WasmIncrementalExecutor : public IncrementalExecutor { private: llvm::SmallString<256> TempDir; + std::vector<std::string> StoredLLVMArgs; }; } // namespace clang diff --git a/clang/unittests/Interpreter/CMakeLists.txt b/clang/unittests/Interpreter/CMakeLists.txt index 66b396b53cb55..9fdd69ea49069 100644 --- a/clang/unittests/Interpreter/CMakeLists.txt +++ b/clang/unittests/Interpreter/CMakeLists.txt @@ -76,6 +76,7 @@ get_target_property(LINKED_LIBS ClangReplInterpreterTests LINK_LIBRARIES) list(REMOVE_ITEM LINKED_LIBS LLVMSupport) set_target_properties(ClangReplInterpreterTests PROPERTIES LINK_LIBRARIES "${LINKED_LIBS}") target_link_options(ClangReplInterpreterTests + PUBLIC "SHELL: -fwasm-exceptions" PUBLIC "SHELL: -s MAIN_MODULE=1" PUBLIC "SHELL: -s ALLOW_MEMORY_GROWTH=1" PUBLIC "SHELL: -s STACK_SIZE=32mb" diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp index 450be2a25a12f..ee498ac4e5d2b 100644 --- a/clang/unittests/Interpreter/InterpreterTest.cpp +++ b/clang/unittests/Interpreter/InterpreterTest.cpp @@ -573,4 +573,20 @@ TEST_F(InterpreterTest, TranslationUnit_CanonicalDecl) { sema.getASTContext().getTranslationUnitDecl()->getCanonicalDecl()); } +TEST_F(InterpreterTest, EmscriptenExceptionHandling) { +#ifndef __EMSCRIPTEN__ + GTEST_SKIP() << "This test only applies to Emscripten builds."; +#endif + + using Args = std::vector<const char *>; + Args ExtraArgs = {"-std=c++23", "-v", + "-fwasm-exceptions", + "-mllvm", "-wasm-enable-sjlj"}; + + std::unique_ptr<Interpreter> Interp = createInterpreter(ExtraArgs); + + llvm::cantFail( + Interp->ParseAndExecute("try { throw 1; } catch (...) { 0; }")); +} + } // end anonymous namespace diff --git a/clang/unittests/Interpreter/InterpreterTestFixture.h b/clang/unittests/Interpreter/InterpreterTestFixture.h index b088fa4a5f896..c4a30dd06220c 100644 --- a/clang/unittests/Interpreter/InterpreterTestFixture.h +++ b/clang/unittests/Interpreter/InterpreterTestFixture.h @@ -55,7 +55,17 @@ class InterpreterTestBase : public ::testing::Test { llvm::InitializeNativeTargetAsmPrinter(); } - static void TearDownTestSuite() { llvm::llvm_shutdown(); } + static void TearDownTestSuite() { + // llvm_shutdown() cleans up JIT (LLJIT/ORC) state between test suites. + // On Emscripten, WasmIncrementalExecutor is used instead of LLJIT, so + // there is no JIT state to tear down. Calling llvm_shutdown() here would + // destroy the ManagedStatic<CommandLineParser> global, permanently + // deregistering all cl::opt options (wasm-enable-eh, etc.) since their + // constructors already ran and will not fire again after the reset. +#ifndef __EMSCRIPTEN__ + llvm::llvm_shutdown(); +#endif + } }; } // namespace clang >From f2fd8d99b1f8478e61bbd0f421a293ef1d4c9c09 Mon Sep 17 00:00:00 2001 From: anutosh491 <[email protected]> Date: Tue, 12 May 2026 14:59:50 +0530 Subject: [PATCH 2/3] clang format suggestions --- clang/include/clang/Interpreter/IncrementalExecutor.h | 3 ++- clang/lib/Interpreter/Wasm.cpp | 4 ++-- clang/unittests/Interpreter/InterpreterTest.cpp | 5 ++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clang/include/clang/Interpreter/IncrementalExecutor.h b/clang/include/clang/Interpreter/IncrementalExecutor.h index 18d482878c4fe..f1a68c0288d00 100644 --- a/clang/include/clang/Interpreter/IncrementalExecutor.h +++ b/clang/include/clang/Interpreter/IncrementalExecutor.h @@ -53,7 +53,8 @@ class IncrementalExecutorBuilder { /// An optional external IncrementalExecutor std::unique_ptr<IncrementalExecutor> IE; /// mllvm args from the frontend; on wasm these are re-applied after each - /// lldMain call because lld resets all cl options for test-isolation purposes. + /// lldMain call because lld resets all cl options for test-isolation + /// purposes. std::vector<std::string> LLVMArgs; /// An optional external orc jit builder std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder; diff --git a/clang/lib/Interpreter/Wasm.cpp b/clang/lib/Interpreter/Wasm.cpp index 9ebd23da5f564..f55530e54b0a7 100644 --- a/clang/lib/Interpreter/Wasm.cpp +++ b/clang/lib/Interpreter/Wasm.cpp @@ -60,8 +60,8 @@ bool link(llvm::ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS, namespace clang { -WasmIncrementalExecutor::WasmIncrementalExecutor(llvm::Error &Err, - std::vector<std::string> LLVMArgs) +WasmIncrementalExecutor::WasmIncrementalExecutor( + llvm::Error &Err, std::vector<std::string> LLVMArgs) : StoredLLVMArgs(std::move(LLVMArgs)) { llvm::ErrorAsOutParameter EAO(&Err); diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp index ee498ac4e5d2b..e52a1063c92a9 100644 --- a/clang/unittests/Interpreter/InterpreterTest.cpp +++ b/clang/unittests/Interpreter/InterpreterTest.cpp @@ -579,9 +579,8 @@ TEST_F(InterpreterTest, EmscriptenExceptionHandling) { #endif using Args = std::vector<const char *>; - Args ExtraArgs = {"-std=c++23", "-v", - "-fwasm-exceptions", - "-mllvm", "-wasm-enable-sjlj"}; + Args ExtraArgs = {"-std=c++23", "-v", "-fwasm-exceptions", "-mllvm", + "-wasm-enable-sjlj"}; std::unique_ptr<Interpreter> Interp = createInterpreter(ExtraArgs); >From 654d31316acbeadc23681905ffa9d82395e0e7bc Mon Sep 17 00:00:00 2001 From: anutosh491 <[email protected]> Date: Wed, 2 Sep 2026 17:49:52 +0530 Subject: [PATCH 3/3] [clang] Process -mllvm arguments during frontend execution preparation --- .../include/clang/Frontend/CompilerInstance.h | 3 ++ clang/lib/Frontend/CompilerInstance.cpp | 8 +++++- clang/lib/Interpreter/Interpreter.cpp | 3 -- .../Frontend/CompilerInstanceTest.cpp | 28 +++++++++++++++++++ .../unittests/Interpreter/InterpreterTest.cpp | 2 ++ 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h index cf70feff553b9..728fbfcc8ab51 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -136,6 +136,9 @@ class CompilerInstance : public ModuleLoader { /// Back-end pass plugins. std::vector<std::unique_ptr<llvm::PassPlugin>> PassPlugins; + /// Whether LLVMArgs have already been parsed. + bool LLVMArgsParsed = false; + /// The frontend timer group. std::unique_ptr<llvm::TimerGroup> timerGroup; diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 7b755deb5863b..23dfb343d330d 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -983,9 +983,11 @@ void CompilerInstance::PrepareForExecution() { getFrontendTimer().startTimer(); } + parseLLVMArgs(); + // FIXME: Consider consolidating additional per-instance setup here: // - llvm::timeTraceProfilerInitialize) when TimeTracePath is set. - // - Plugin loading (LoadRequestedPlugins) and -mllvm argument processing. + // - Plugin loading (LoadRequestedPlugins). } bool CompilerInstance::ExecuteAction(FrontendAction &Act) { @@ -1136,6 +1138,10 @@ void CompilerInstance::LoadRequestedPlugins() { } void CompilerInstance::parseLLVMArgs() { + if (LLVMArgsParsed) + return; + LLVMArgsParsed = true; + if (!getFrontendOpts().LLVMArgs.empty()) { unsigned NumArgs = getFrontendOpts().LLVMArgs.size(); auto Args = std::make_unique<const char *[]>(NumArgs + 2); diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 21ad9438ee44a..b9eb4b46391a9 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -351,9 +351,6 @@ Interpreter::Interpreter(std::unique_ptr<CompilerInstance> Instance, auto LLVMCtx = std::make_unique<llvm::LLVMContext>(); TSCtx = std::make_unique<llvm::orc::ThreadSafeContext>(std::move(LLVMCtx)); - // Honor -mllvm options - CI->parseLLVMArgs(); - Act = TSCtx->withContextDo([&](llvm::LLVMContext *Ctx) { return std::make_unique<IncrementalAction>(*CI, *Ctx, ErrOut, *this, std::move(Consumer)); diff --git a/clang/unittests/Frontend/CompilerInstanceTest.cpp b/clang/unittests/Frontend/CompilerInstanceTest.cpp index f4377498d6ff6..e9f5fd04c4095 100644 --- a/clang/unittests/Frontend/CompilerInstanceTest.cpp +++ b/clang/unittests/Frontend/CompilerInstanceTest.cpp @@ -16,6 +16,7 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Lex/PreprocessorOptions.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" #include "llvm/Support/MemoryBuffer.h" @@ -95,6 +96,33 @@ TEST(CompilerInstance, CreateVFSWithoutDiagnosticConsumer) { Instance.createVirtualFileSystem(std::move(BaseFS), /*DC=*/nullptr)); } +TEST(CompilerInstance, ExecuteActionProcessesMllvmArgsOnce) { + llvm::cl::opt<bool> TestOption("compiler-instance-test-mllvm", + llvm::cl::init(false)); + + auto Invocation = std::make_shared<CompilerInvocation>(); + Invocation->getPreprocessorOpts().addRemappedFile( + "test.cc", MemoryBuffer::getMemBuffer("").release()); + Invocation->getFrontendOpts().Inputs.emplace_back("test.cc", Language::CXX); + // The driver stores the argument following `-mllvm` in LLVMArgs. Direct + // CompilerInstance clients populate the same field without using the driver. + Invocation->getFrontendOpts().LLVMArgs.emplace_back( + "-compiler-instance-test-mllvm"); + Invocation->getTargetOpts().Triple = "x86_64-unknown-linux-gnu"; + + CompilerInstance Instance(std::move(Invocation)); + Instance.setVirtualFileSystem(llvm::vfs::getRealFileSystem()); + Instance.createDiagnostics(); + + SyntaxOnlyAction Action; + EXPECT_TRUE(Instance.ExecuteAction(Action)); + EXPECT_TRUE(TestOption); + EXPECT_EQ(TestOption.getNumOccurrences(), 1); + + Instance.parseLLVMArgs(); + EXPECT_EQ(TestOption.getNumOccurrences(), 1); +} + TEST(CompilerInstance, AllowDiagnosticLogWithUnownedDiagnosticConsumer) { DiagnosticOptions DiagOpts; // Tell the diagnostics engine to emit the diagnostic log to STDERR. This diff --git a/clang/unittests/Interpreter/InterpreterTest.cpp b/clang/unittests/Interpreter/InterpreterTest.cpp index e52a1063c92a9..1e1167118507c 100644 --- a/clang/unittests/Interpreter/InterpreterTest.cpp +++ b/clang/unittests/Interpreter/InterpreterTest.cpp @@ -586,6 +586,8 @@ TEST_F(InterpreterTest, EmscriptenExceptionHandling) { llvm::cantFail( Interp->ParseAndExecute("try { throw 1; } catch (...) { 0; }")); + llvm::cantFail( + Interp->ParseAndExecute("try { throw 2; } catch (...) { 0; }")); } } // end anonymous namespace _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
