[llvm-branch-commits] [clang] [llvm] WebAssembly: Drive Emscripten EH from the exception model, drop the cl::opt (PR #223972)
Matt Arsenault via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Sep 16 05:14:25 PDT 2026
https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/223972
>From 919bc5d3592cc7b2b8bee2f31e5b63d82527a192 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Fri, 4 Sep 2026 16:51:41 +0200
Subject: [PATCH] WebAssembly: Drive Emscripten EH from the exception model,
drop the cl::opt
Remove the deprecated -enable-emscripten-cxx-exceptions flag. This should now
be driven by the generic exception-model mechanisms. There was also some special
case handling of the -mllvm flag in the clang driver which also needed removal.
Also reverts the test change in b6912258891caefffbf15edb1bceff3724b75af5 now that
the backend doesn't need to be built to test the flag behavior.
Co-authored-by: Claude (Claude-Opus-4.8) <noreply at anthropic.com>
---
clang/lib/Driver/ToolChains/WebAssembly.cpp | 16 +++---------
.../WebAssembly/exception-model-flag.c | 16 ------------
clang/test/CodeGen/exception-model-flag.c | 8 ++++++
clang/test/Driver/wasm-toolchain.c | 26 +------------------
.../WebAssembly/WebAssemblyAsmPrinter.cpp | 7 +++--
.../WebAssemblyCodeGenPassBuilder.cpp | 6 +----
.../WebAssemblyLowerEmscriptenEHSjLj.cpp | 3 +--
.../WebAssembly/WebAssemblyTargetMachine.cpp | 16 ++----------
.../WebAssembly/WebAssemblyTargetMachine.h | 1 -
9 files changed, 21 insertions(+), 78 deletions(-)
delete mode 100644 clang/test/CodeGen/WebAssembly/exception-model-flag.c
diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp
index b683fcd506a44..b28154772f9f9 100644
--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp
@@ -429,8 +429,7 @@ void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs,
for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) {
for (const auto *Option :
- {"-enable-emscripten-cxx-exceptions", "-enable-emscripten-sjlj",
- "-emscripten-cxx-exceptions-allowed"}) {
+ {"-enable-emscripten-sjlj", "-emscripten-cxx-exceptions-allowed"}) {
if (StringRef(A->getValue(0)) == Option)
getDriver().Diag(diag::err_drv_argument_not_allowed_with)
<< CurOption << Option;
@@ -466,17 +465,8 @@ void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs,
StringRef Opt = A->getValue(0);
if (Opt.starts_with("-emscripten-cxx-exceptions-allowed")) {
// '-mllvm -emscripten-cxx-exceptions-allowed' should be used with
- // '-femscripten-exceptions' (or the underlying
- // '-mllvm -enable-emscripten-cxx-exceptions').
- bool EmEHArgExists =
- DriverArgs.hasArg(options::OPT_femscripten_exceptions);
- for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) {
- if (StringRef(A->getValue(0)) == "-enable-emscripten-cxx-exceptions") {
- EmEHArgExists = true;
- break;
- }
- }
- if (!EmEHArgExists)
+ // '-femscripten-exceptions'.
+ if (!DriverArgs.hasArg(options::OPT_femscripten_exceptions))
getDriver().Diag(diag::err_drv_argument_only_allowed_with)
<< "-mllvm -emscripten-cxx-exceptions-allowed"
<< "-femscripten-exceptions";
diff --git a/clang/test/CodeGen/WebAssembly/exception-model-flag.c b/clang/test/CodeGen/WebAssembly/exception-model-flag.c
deleted file mode 100644
index 35b56db409b0b..0000000000000
--- a/clang/test/CodeGen/WebAssembly/exception-model-flag.c
+++ /dev/null
@@ -1,16 +0,0 @@
-// REQUIRES: webassembly-registered-target
-
-// Verify clang records the "exception-model" module flag for the WebAssembly
-// exception models. The target-independent models are covered in
-// exception-model-flag.c.
-
-// Wasm EH (needs the backend enable flag) records the "wasm" model.
-// RUN: %clang_cc1 -triple wasm32-unknown-unknown -fexceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm %s -o - | FileCheck %s --check-prefix=WASM
-
-// Emscripten EH records the "emscripten" model.
-// RUN: %clang_cc1 -triple wasm32-unknown-emscripten -fexceptions -exception-model=emscripten -emit-llvm %s -o - | FileCheck %s --check-prefix=EMSCRIPTEN
-
-void f(void) {}
-
-// WASM: !{i32 1, !"exception-model", !"wasm"}
-// EMSCRIPTEN: !{i32 1, !"exception-model", !"emscripten"}
diff --git a/clang/test/CodeGen/exception-model-flag.c b/clang/test/CodeGen/exception-model-flag.c
index f9713d88df592..3a3fc8a469c94 100644
--- a/clang/test/CodeGen/exception-model-flag.c
+++ b/clang/test/CodeGen/exception-model-flag.c
@@ -13,6 +13,12 @@
// SEH maps to the "wineh" spelling regardless of the requesting triple.
// RUN: %clang_cc1 -triple i686-unknown-windows-gnu -fexceptions -exception-model=seh -emit-llvm %s -o - | FileCheck %s --check-prefix=WINEH
+// Wasm EH (needs the backend enable flag) records the "wasm" model.
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -fexceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm %s -o - | FileCheck %s --check-prefix=WASM
+
+// Emscripten EH records the "emscripten" model.
+// RUN: %clang_cc1 -triple wasm32-unknown-emscripten -fexceptions -exception-model=emscripten -emit-llvm %s -o - | FileCheck %s --check-prefix=EMSCRIPTEN
+
// A requested model that matches the target default is still recorded, so that
// the flag's absence always means "unspecified".
// RUN: %clang_cc1 -triple i686-unknown-linux-gnu -fexceptions -exception-model=dwarf -emit-llvm %s -o - | FileCheck %s --check-prefix=DWARF
@@ -30,6 +36,8 @@ void f(void) {}
// SJLJ: !{i32 1, !"exception-model", !"sjlj"}
// WINEH: !{i32 1, !"exception-model", !"wineh"}
+// WASM: !{i32 1, !"exception-model", !"wasm"}
+// EMSCRIPTEN: !{i32 1, !"exception-model", !"emscripten"}
// DWARF: !{i32 1, !"exception-model", !"dwarf"}
// NONE: !{i32 1, !"exception-model", !"none"}
// UNSPEC-NOT: "exception-model"
diff --git a/clang/test/Driver/wasm-toolchain.c b/clang/test/Driver/wasm-toolchain.c
index 665cf4d1a667b..7a36440494f42 100644
--- a/clang/test/Driver/wasm-toolchain.c
+++ b/clang/test/Driver/wasm-toolchain.c
@@ -129,7 +129,7 @@
// EMSCRIPTEN_EH_ALLOWED_NOINLINE: "-cc1" {{.*}} "-mllvm" "--force-attribute=foo:noinline" "-mllvm" "--force-attribute=bar:noinline"
// '-mllvm -emscripten-cxx-exceptions-allowed' only allowed with
-// '-mllvm -enable-emscripten-cxx-exceptions'
+// '-femscripten-exceptions'
// RUN: not %clang -### --target=wasm32-unknown-unknown \
// RUN: --sysroot=/foo %s -mllvm -emscripten-cxx-exceptions-allowed 2>&1 \
// RUN: | FileCheck -check-prefix=EMSCRIPTEN_EH_ALLOWED_WO_ENABLE %s
@@ -161,14 +161,6 @@
// RUN: | FileCheck -check-prefix=WASM_EXCEPTIONS %s
// WASM_EXCEPTIONS: "-cc1" {{.*}} "-target-feature" "+exception-handling" "-target-feature" "+multivalue" "-target-feature" "+reference-types" "-exception-model=wasm" "-mllvm" "-wasm-enable-eh"
-// '-fwasm-exceptions' not allowed with
-// '-mllvm -enable-emscripten-cxx-exceptions'
-// RUN: not %clang -### --target=wasm32-unknown-unknown \
-// RUN: --sysroot=/foo %s -fwasm-exceptions \
-// RUN: -mllvm -enable-emscripten-cxx-exceptions 2>&1 \
-// RUN: | FileCheck -check-prefix=WASM_EXCEPTIONS_EMSCRIPTEN_EH %s
-// WASM_EXCEPTIONS_EMSCRIPTEN_EH: invalid argument '-fwasm-exceptions' not allowed with '-enable-emscripten-cxx-exceptions'
-
// '-fwasm-exceptions' not allowed with '-mllvm -enable-emscripten-sjlj'
// RUN: not %clang -### --target=wasm32-unknown-unknown \
// RUN: --sysroot=/foo %s -fwasm-exceptions \
@@ -201,14 +193,6 @@
// RUN: | FileCheck -check-prefix=WASM_SJLJ %s
// WASM_SJLJ: "-cc1" {{.*}} "-target-feature" "+exception-handling" "-target-feature" "+multivalue" "-target-feature" "+reference-types" "-exception-model=wasm"
-// '-mllvm -wasm-enable-sjlj' not allowed with
-// '-mllvm -enable-emscripten-cxx-exceptions'
-// RUN: not %clang -### --target=wasm32-unknown-unknown \
-// RUN: --sysroot=/foo %s -mllvm -wasm-enable-sjlj \
-// RUN: -mllvm -enable-emscripten-cxx-exceptions 2>&1 \
-// RUN: | FileCheck -check-prefix=WASM_SJLJ_EMSCRIPTEN_EH %s
-// WASM_SJLJ_EMSCRIPTEN_EH: invalid argument '-wasm-enable-sjlj' not allowed with '-enable-emscripten-cxx-exceptions'
-
// '-mllvm -wasm-enable-sjlj' not allowed with '-mllvm -enable-emscripten-sjlj'
// RUN: not %clang -### --target=wasm32-unknown-unknown \
// RUN: --sysroot=/foo %s -mllvm -wasm-enable-sjlj \
@@ -236,14 +220,6 @@
// RUN: | FileCheck -check-prefix=WASM_SJLJ_NO_REFERENCE_TYPES %s
// WASM_SJLJ_NO_REFERENCE_TYPES: invalid argument '-wasm-enable-sjlj' not allowed with '-mno-reference-types'
-// '-mllvm -wasm-use-legacy-eh' not allowed with
-// '-mllvm -enable-emscripten-cxx-exceptions'
-// RUN: not %clang -### --target=wasm32-unknown-unknown \
-// RUN: --sysroot=/foo %s -mllvm -wasm-use-legacy-eh \
-// RUN: -mllvm -enable-emscripten-cxx-exceptions 2>&1 \
-// RUN: | FileCheck -check-prefix=WASM_LEGACY_EH_EMSCRIPTEN_EH %s
-// WASM_LEGACY_EH_EMSCRIPTEN_EH: invalid argument '-wasm-use-legacy-eh' not allowed with '-enable-emscripten-cxx-exceptions'
-
// '-mllvm -wasm-use-legacy-eh' not allowed with '-mllvm -enable-emscripten-sjlj'
// RUN: not %clang -### --target=wasm32-unknown-unknown \
// RUN: --sysroot=/foo %s -mllvm -wasm-use-legacy-eh \
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
index 5929b0f77359c..0d5d0ae04fa2d 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
@@ -165,9 +165,12 @@ MCSymbolWasm *WebAssemblyAsmPrinter::getMCSymbolForFunction(
const Function *F, wasm::WasmSignature *Sig, bool &InvokeDetected) {
MCSymbolWasm *WasmSym = nullptr;
+ // Prefer the "exception-model" module flag, else the TargetOptions default.
+ ExceptionHandling EM = F->getParent()->getExceptionModel();
+ if (EM == ExceptionHandling::Default)
+ EM = TM.getExceptionModel();
const bool EnableEmEH =
- TM.Options.ExceptionModel == ExceptionHandling::Emscripten ||
- WebAssembly::WasmEnableEmEH || WebAssembly::WasmEnableEmSjLj;
+ EM == ExceptionHandling::Emscripten || WebAssembly::WasmEnableEmSjLj;
if (EnableEmEH && isEmscriptenInvokeName(F->getName())) {
assert(Sig);
InvokeDetected = true;
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCodeGenPassBuilder.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCodeGenPassBuilder.cpp
index 82f7fdd2b7079..c53072764bc82 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyCodeGenPassBuilder.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyCodeGenPassBuilder.cpp
@@ -42,14 +42,12 @@ using namespace llvm;
namespace WebAssembly {
extern cl::opt<bool> WasmDisableExplicitLocals;
extern cl::opt<bool> WasmEnableEH;
-extern cl::opt<bool> WasmEnableEmEH;
extern cl::opt<bool> WasmEnableEmSjLj;
extern cl::opt<bool> WasmEnableSjLj;
} // namespace WebAssembly
using llvm::WebAssembly::WasmDisableExplicitLocals;
using llvm::WebAssembly::WasmEnableEH;
-using llvm::WebAssembly::WasmEnableEmEH;
using llvm::WebAssembly::WasmEnableEmSjLj;
using llvm::WebAssembly::WasmEnableSjLj;
@@ -127,9 +125,7 @@ void WebAssemblyCodeGenPassBuilder::addIRPasses(PassManagerWrapper &PMW) {
// TargetPassConfig::addPassesToHandleExceptions, but that runs after these IR
// passes and Emscripten SjLj handling expects all invokes to be lowered
// before.
- bool EnableEmEH =
- TM.Options.ExceptionModel == ExceptionHandling::Emscripten ||
- WasmEnableEmEH;
+ bool EnableEmEH = TM.Options.ExceptionModel == ExceptionHandling::Emscripten;
if (!EnableEmEH && !WasmEnableEH) {
addFunctionPass(LowerInvokePass(), PMW);
// The lower invoke pass may create unreachable code. Remove it in order not
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
index de43564c3f2c5..0ee7731f8e4f5 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
@@ -361,8 +361,7 @@ class WebAssemblyLowerEmscriptenEHSjLjImpl {
WebAssemblyLowerEmscriptenEHSjLjImpl(
bool EnableEmEH,
std::function<DominatorTree &(Function &F)> GetDominatorTree)
- : EnableEmEH(EnableEmEH || WebAssembly::WasmEnableEmEH),
- EnableEmSjLj(WebAssembly::WasmEnableEmSjLj),
+ : EnableEmEH(EnableEmEH), EnableEmSjLj(WebAssembly::WasmEnableEmSjLj),
EnableWasmSjLj(WebAssembly::WasmEnableSjLj),
GetDominatorTree(GetDominatorTree) {
assert(!(EnableEmSjLj && EnableWasmSjLj) &&
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index 7cfc7a3c9c983..3b054f80027c5 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -52,11 +52,6 @@ cl::opt<bool> WebAssembly::WasmDisableExplicitLocals(
// Exception handling & setjmp-longjmp handling related options.
-// Emscripten's asm.js-style exception handling
-cl::opt<bool> WebAssembly::WasmEnableEmEH(
- "enable-emscripten-cxx-exceptions",
- cl::desc("WebAssembly Emscripten-style exception handling"),
- cl::init(false));
// Emscripten's asm.js-style setjmp/longjmp handling
cl::opt<bool> WebAssembly::WasmEnableEmSjLj(
"enable-emscripten-sjlj",
@@ -135,17 +130,12 @@ static Reloc::Model getEffectiveRelocModel(std::optional<Reloc::Model> RM) {
using WebAssembly::WasmDisableExplicitLocals;
using WebAssembly::WasmEnableEH;
-using WebAssembly::WasmEnableEmEH;
using WebAssembly::WasmEnableEmSjLj;
using WebAssembly::WasmEnableSjLj;
static void basicCheckForEHAndSjLj(TargetMachine *TM) {
- // Emscripten EH is selected by the exception model. WasmEnableEmEH is a
- // deprecated cl::opt alias, OR-ed in here until it is removed.
- bool EnableEmEH =
- TM->Options.ExceptionModel == ExceptionHandling::Emscripten ||
- WasmEnableEmEH;
+ bool EnableEmEH = TM->Options.ExceptionModel == ExceptionHandling::Emscripten;
// You can't enable two modes of EH at the same time
if (EnableEmEH && WasmEnableEH)
@@ -343,9 +333,7 @@ void WebAssemblyPassConfig::addIRPasses() {
// TargetPassConfig::addPassesToHandleExceptions, but that runs after these IR
// passes and Emscripten SjLj handling expects all invokes to be lowered
// before.
- bool EnableEmEH =
- TM->Options.ExceptionModel == ExceptionHandling::Emscripten ||
- WasmEnableEmEH;
+ bool EnableEmEH = TM->Options.ExceptionModel == ExceptionHandling::Emscripten;
if (!EnableEmEH && !WasmEnableEH) {
addPass(createLowerInvokePass());
// The lower invoke pass may create unreachable code. Remove it in order not
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h
index 55f8898e387da..cff4d345b8516 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.h
@@ -24,7 +24,6 @@ namespace llvm {
namespace WebAssembly {
// Exception handling / setjmp-longjmp handling command-line options
extern cl::opt<bool> WasmDisableExplicitLocals;
-extern cl::opt<bool> WasmEnableEmEH; // asm.js-style EH
extern cl::opt<bool> WasmEnableEmSjLj; // asm.js-style SjLJ
extern cl::opt<bool> WasmEnableEH; // EH using Wasm EH instructions
extern cl::opt<bool> WasmEnableSjLj; // SjLj using Wasm EH instructions
More information about the llvm-branch-commits
mailing list