https://github.com/anutosh491 updated https://github.com/llvm/llvm-project/pull/197133
>From 2d4c29cc72b3a330ba2e789c07b107c1ad3bc041 Mon Sep 17 00:00:00 2001 From: anutosh491 <[email protected]> Date: Thu, 8 Oct 2026 09:15:51 +0530 Subject: [PATCH] Handle mllvm args for clang-repl before calling executeAction- #197133 --- .../include/clang/Frontend/CompilerInstance.h | 5 ++++ .../clang/Interpreter/IncrementalExecutor.h | 4 ++++ clang/lib/Frontend/CompilerInstance.cpp | 14 +++++++++++ .../ExecuteCompilerInvocation.cpp | 16 +------------ clang/lib/Interpreter/IncrementalExecutor.cpp | 2 +- clang/lib/Interpreter/Interpreter.cpp | 7 ++++++ clang/lib/Interpreter/Wasm.cpp | 21 ++++++++++++++++- clang/lib/Interpreter/Wasm.h | 5 +++- clang/unittests/Interpreter/CMakeLists.txt | 1 + .../unittests/Interpreter/InterpreterTest.cpp | 23 +++++++++++++++++++ .../Interpreter/InterpreterTestFixture.h | 12 +++++++++- 11 files changed, 91 insertions(+), 19 deletions(-) diff --git a/clang/include/clang/Frontend/CompilerInstance.h b/clang/include/clang/Frontend/CompilerInstance.h index 977180a8dc3aa..b87dd037480d6 100644 --- a/clang/include/clang/Frontend/CompilerInstance.h +++ b/clang/include/clang/Frontend/CompilerInstance.h @@ -264,6 +264,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..f1a68c0288d00 100644 --- a/clang/include/clang/Interpreter/IncrementalExecutor.h +++ b/clang/include/clang/Interpreter/IncrementalExecutor.h @@ -52,6 +52,10 @@ 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 7048bec2cdceb..b09c9a9f5a5be 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 5d01ba4774269..101120626f766 100644 --- a/clang/lib/Interpreter/IncrementalExecutor.cpp +++ b/clang/lib/Interpreter/IncrementalExecutor.cpp @@ -429,7 +429,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 40fbc83db9107..85cefa1270049 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -352,6 +352,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)); @@ -608,6 +611,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 573c8910d3a41..017b049fb470c 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> @@ -61,7 +62,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) @@ -142,6 +145,22 @@ llvm::Error WasmIncrementalExecutor::addModule(PartialTranslationUnit &PTU) { if (!Result.canRunAgain) lld::exitLld(Result.retCode); + // 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. + // + // FIXME: Remove this once library command-line options no longer rely on + // process-global cl::opt state. See: + // https://discourse.llvm.org/t/rfc-declare-library-command-line-options-in-tablegen-one-struct-per-library/91877 + 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 6d999104054c3..d18d7819001b1 100644 --- a/clang/unittests/Interpreter/CMakeLists.txt +++ b/clang/unittests/Interpreter/CMakeLists.txt @@ -77,6 +77,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 a0ed7e671ddbe..17cc6fc9b6ca8 100644 --- a/clang/unittests/Interpreter/InterpreterTest.cpp +++ b/clang/unittests/Interpreter/InterpreterTest.cpp @@ -174,6 +174,9 @@ TEST_F(InterpreterTest, TranslationUnitRedeclChainAcrossManyPTUs) { } TEST_F(InterpreterTest, UndoLeavesDeclsInTranslationUnitChain) { +#ifdef __EMSCRIPTEN__ + GTEST_SKIP() << "Undo is not supported for Emscripten builds"; +#endif std::unique_ptr<Interpreter> Interp = createInterpreter(); cantFail(Interp->Parse("struct Kept {};")); @@ -520,6 +523,11 @@ TEST_F(InterpreterTest, ValueSetRawBitsCopiesByteCount) { // Earlier the move ctor called Release() on the just-moved-into storage, // double-releasing on the next read. TEST_F(InterpreterTest, ValueMoveSemantics) { + // FIXME: Emscripten cannot resolve MoveT's destructor symbol + // `_ZN5MoveTD2Ev` from the incrementally loaded Wasm side module. +#ifdef __EMSCRIPTEN__ + GTEST_SKIP() << "Unresolved destructor symbol: _ZN5MoveTD2Ev"; +#endif std::vector<const char *> Args = {"-fno-sized-deallocation"}; std::unique_ptr<Interpreter> Interp = createInterpreter(Args); @@ -588,4 +596,19 @@ 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
