[llvm-branch-commits] [clang] release/23.x: [clang-repl] Handle mllvm args for clang-repl before calling executeAction (#197133) (PR #230032)
Anutosh Bhat via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Oct 13 01:46:12 PDT 2026
https://github.com/anutosh491 updated https://github.com/llvm/llvm-project/pull/230032
>From 6b9a085f0ee1efc91aca03631da7eaf096547800 Mon Sep 17 00:00:00 2001
From: anutosh491 <andersonbhat491 at gmail.com>
Date: Thu, 8 Oct 2026 14:17:53 +0530
Subject: [PATCH 1/2] [clang-repl] Handle mllvm args before executeAction
(#197133)
(cherry picked from commit 415b70a8a11d369176ea2db5151424d9354ba41a)
---
.../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 24488e053c628..b498992c30b25 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..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 941c9e40f0c1d..0489edfd09ebf 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -1128,6 +1128,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 a206770c8490f..667c5d50b38c7 100644
--- a/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -240,21 +240,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 65cb29a2f441a..51a601433e552 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.cpp
+++ b/clang/lib/Interpreter/IncrementalExecutor.cpp
@@ -393,7 +393,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 ef1ca31538352..686b49888fed2 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -350,6 +350,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));
@@ -597,6 +600,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..87b9c38e4b459 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,22 @@ 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.
+ //
+ // 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 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..d0ab1c6517c11 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 {};"));
@@ -505,6 +508,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);
@@ -573,4 +581,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
>From 069f04e65d56af267007daf8c33b5f32038a8dc6 Mon Sep 17 00:00:00 2001
From: anutosh491 <andersonbhat491 at gmail.com>
Date: Tue, 13 Oct 2026 14:16:01 +0530
Subject: [PATCH 2/2] [clang-repl] Preserve executor builder ABI
---
clang/include/clang/Interpreter/IncrementalExecutor.h | 11 +++++++----
clang/lib/Interpreter/IncrementalExecutor.cpp | 7 +++++++
clang/lib/Interpreter/Interpreter.cpp | 7 ++-----
3 files changed, 16 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/Interpreter/IncrementalExecutor.h b/clang/include/clang/Interpreter/IncrementalExecutor.h
index f1a68c0288d00..84e39a918cfb8 100644
--- a/clang/include/clang/Interpreter/IncrementalExecutor.h
+++ b/clang/include/clang/Interpreter/IncrementalExecutor.h
@@ -16,6 +16,8 @@
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Error.h"
+#include <vector>
+
namespace llvm {
namespace orc {
class ExecutorAddr;
@@ -52,10 +54,6 @@ 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
@@ -70,6 +68,11 @@ class IncrementalExecutorBuilder {
llvm::Expected<std::unique_ptr<IncrementalExecutor>>
create(llvm::orc::ThreadSafeContext &TSC, const clang::TargetInfo &TI);
+ /// Pass LLVM arguments to restore after linking Wasm modules.
+ llvm::Expected<std::unique_ptr<IncrementalExecutor>>
+ create(llvm::orc::ThreadSafeContext &TSC, const clang::TargetInfo &TI,
+ const std::vector<std::string> &LLVMArgs);
+
private:
llvm::Error UpdateOrcRuntimePath(const driver::Compilation &C);
};
diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp
index 51a601433e552..d2fd63960311b 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.cpp
+++ b/clang/lib/Interpreter/IncrementalExecutor.cpp
@@ -361,6 +361,13 @@ outOfProcessJITBuilder(const IncrementalExecutorBuilder &IncrExecutorBuilder) {
llvm::Expected<std::unique_ptr<IncrementalExecutor>>
IncrementalExecutorBuilder::create(llvm::orc::ThreadSafeContext &TSC,
const clang::TargetInfo &TI) {
+ return create(TSC, TI, {});
+}
+
+llvm::Expected<std::unique_ptr<IncrementalExecutor>>
+IncrementalExecutorBuilder::create(llvm::orc::ThreadSafeContext &TSC,
+ const clang::TargetInfo &TI,
+ const std::vector<std::string> &LLVMArgs) {
if (IE)
return std::move(IE);
llvm::Triple TT = TI.getTriple();
diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp
index 686b49888fed2..76d832ead7aaa 100644
--- a/clang/lib/Interpreter/Interpreter.cpp
+++ b/clang/lib/Interpreter/Interpreter.cpp
@@ -600,11 +600,8 @@ 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());
+ auto ExecutorOrErr = IncrExecutorBuilder->create(
+ *TSCtx, CI->getTarget(), CI->getFrontendOpts().LLVMArgs);
if (ExecutorOrErr)
IncrExecutor = std::move(*ExecutorOrErr);
More information about the llvm-branch-commits
mailing list