[llvm] a2b6a1b - [WebAssembly] Port WebAssemblyCoalesceFeaturesAndStripAtomicsPass
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 15 07:03:02 PDT 2026
Author: Aiden Grossman
Date: 2026-07-15T07:02:57-07:00
New Revision: a2b6a1bae7904be696d8283b9bb34331c39a54ef
URL: https://github.com/llvm/llvm-project/commit/a2b6a1bae7904be696d8283b9bb34331c39a54ef
DIFF: https://github.com/llvm/llvm-project/commit/a2b6a1bae7904be696d8283b9bb34331c39a54ef.diff
LOG: [WebAssembly] Port WebAssemblyCoalesceFeaturesAndStripAtomicsPass
Standard NewPM pass porting.
Reviewers: sbc100, aheejin, dschuff
Pull Request: https://github.com/llvm/llvm-project/pull/209054
Added:
Modified:
llvm/lib/Target/WebAssembly/WebAssembly.h
llvm/lib/Target/WebAssembly/WebAssemblyCoalesceFeaturesAndStripAtomics.cpp
llvm/lib/Target/WebAssembly/WebAssemblyCodeGenPassBuilder.cpp
llvm/lib/Target/WebAssembly/WebAssemblyPassRegistry.def
llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h
index 6f84ff8adb4c0..c4820ce7b330a 100644
--- a/llvm/lib/Target/WebAssembly/WebAssembly.h
+++ b/llvm/lib/Target/WebAssembly/WebAssembly.h
@@ -85,8 +85,20 @@ class WebAssemblyReduceToAnyAllTruePass
FunctionPass *
createWebAssemblyReduceToAnyAllTrueLegacyPass(WebAssemblyTargetMachine &TM);
-ModulePass *
-createWebAssemblyCoalesceFeaturesAndStripAtomics(WebAssemblyTargetMachine &TM);
+
+class WebAssemblyCoalesceFeaturesAndStripAtomicsPass
+ : public RequiredPassInfoMixin<
+ WebAssemblyCoalesceFeaturesAndStripAtomicsPass> {
+ WebAssemblyTargetMachine &TM;
+
+public:
+ WebAssemblyCoalesceFeaturesAndStripAtomicsPass(WebAssemblyTargetMachine &TM)
+ : TM(TM) {}
+ PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
+};
+
+ModulePass *createWebAssemblyCoalesceFeaturesAndStripAtomicsLegacyPass(
+ WebAssemblyTargetMachine &TM);
// GlobalISel
InstructionSelector *
@@ -162,7 +174,8 @@ void initializeWebAssemblyRegNumberingPass(PassRegistry &);
void initializeWebAssemblyRegStackifyPass(PassRegistry &);
void initializeWebAssemblyReplacePhysRegsPass(PassRegistry &);
void initializeWebAssemblySetP2AlignOperandsPass(PassRegistry &);
-void initializeWebAssemblyCoalesceFeaturesAndStripAtomicsPass(PassRegistry &);
+void initializeWebAssemblyCoalesceFeaturesAndStripAtomicsLegacyPass(
+ PassRegistry &);
namespace WebAssembly {
enum TargetIndex {
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCoalesceFeaturesAndStripAtomics.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCoalesceFeaturesAndStripAtomics.cpp
index 89c3842241bf7..a040e531ee020 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyCoalesceFeaturesAndStripAtomics.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyCoalesceFeaturesAndStripAtomics.cpp
@@ -8,8 +8,10 @@
#include "WebAssembly.h"
#include "WebAssemblyTargetMachine.h"
+#include "llvm/IR/Analysis.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
#include "llvm/Transforms/Scalar/LowerAtomicPass.h"
@@ -18,7 +20,8 @@ using namespace llvm;
#define DEBUG_TYPE "wasm-coalesce-features-and-strip-atomics"
namespace {
-class WebAssemblyCoalesceFeaturesAndStripAtomics final : public ModulePass {
+class WebAssemblyCoalesceFeaturesAndStripAtomicsLegacy final
+ : public ModulePass {
// Take the union of all features used in the module and use it for each
// function individually, since having multiple feature sets in one module
// currently does not make sense for WebAssembly. If atomics are not enabled,
@@ -28,170 +31,184 @@ class WebAssemblyCoalesceFeaturesAndStripAtomics final : public ModulePass {
public:
static char ID;
- WebAssemblyCoalesceFeaturesAndStripAtomics(WebAssemblyTargetMachine *WasmTM)
+ WebAssemblyCoalesceFeaturesAndStripAtomicsLegacy(
+ WebAssemblyTargetMachine *WasmTM)
: ModulePass(ID), WasmTM(WasmTM) {}
- bool runOnModule(Module &M) override {
- auto [Features, FeatureStr] = coalesceFeatures(M);
-
- WasmTM->setTargetFeatureString(FeatureStr);
- for (auto &F : M)
- replaceFeatures(F, FeatureStr);
-
- bool StrippedAtomics = false;
- bool StrippedTLS = false;
-
- // In cooperative threading mode, thread locals are meaningful even without
- // atomics.
- const WebAssemblySubtarget *ST = WasmTM->getSubtargetImpl();
- bool CooperativeThreading = ST->hasCooperativeMultithreading();
-
- if (!Features[WebAssembly::FeatureAtomics]) {
- StrippedAtomics = stripAtomics(M);
- if (!CooperativeThreading)
- StrippedTLS = stripThreadLocals(M);
- }
- if (!Features[WebAssembly::FeatureBulkMemory] && !StrippedTLS) {
- StrippedTLS = stripThreadLocals(M);
- }
+ bool runOnModule(Module &M) override;
+};
+} // namespace
- if (StrippedAtomics && !StrippedTLS && !CooperativeThreading)
- stripThreadLocals(M);
- else if (StrippedTLS && !StrippedAtomics)
- stripAtomics(M);
+char WebAssemblyCoalesceFeaturesAndStripAtomicsLegacy::ID = 0;
+INITIALIZE_PASS(WebAssemblyCoalesceFeaturesAndStripAtomicsLegacy, DEBUG_TYPE,
+ "Coalesce features and strip atomics", true, false)
- recordFeatures(M, ST, Features, StrippedAtomics || StrippedTLS);
+ModulePass *llvm::createWebAssemblyCoalesceFeaturesAndStripAtomicsLegacyPass(
+ WebAssemblyTargetMachine &TM) {
+ return new WebAssemblyCoalesceFeaturesAndStripAtomicsLegacy(&TM);
+}
- // Conservatively assume we have made some change
- return true;
+static std::string getFeatureString(const WebAssemblySubtarget *ST,
+ const FeatureBitset &Features) {
+ std::string Ret;
+ for (const SubtargetFeatureKV &KV : ST->getAllProcessorFeatures()) {
+ if (Features[KV.Value])
+ Ret += (StringRef("+") + KV.key() + ",").str();
+ else
+ Ret += (StringRef("-") + KV.key() + ",").str();
}
+ // remove trailing ','
+ Ret.pop_back();
+ return Ret;
+}
-private:
- std::pair<FeatureBitset, std::string> coalesceFeatures(const Module &M) {
- // Union the features of all defined functions. Start with an empty set, so
- // that if a feature is disabled in every function, we'll compute it as
- // disabled. If any function lacks a target-features attribute, it'll
- // default to the target CPU from the `TargetMachine`.
- FeatureBitset Features;
- // We need any MCSubtargetInfo to access WebAssemblyFeatureKV.
- const WebAssemblySubtarget *AnyST = nullptr;
- for (auto &F : M) {
- if (F.isDeclaration())
- continue;
-
- AnyST = WasmTM->getSubtargetImpl(F);
- Features |= AnyST->getFeatureBits();
- }
-
- // If we have no defined functions, use the target CPU from the
- // `TargetMachine`.
- if (!AnyST) {
- AnyST = WasmTM->getSubtargetImpl(
- std::string(WasmTM->getTargetCPU()),
- std::string(WasmTM->getTargetFeatureString()));
- Features = AnyST->getFeatureBits();
- }
-
- return {Features, getFeatureString(AnyST, Features)};
+static std::pair<FeatureBitset, std::string>
+coalesceFeatures(const Module &M, WebAssemblyTargetMachine *WasmTM) {
+ // Union the features of all defined functions. Start with an empty set, so
+ // that if a feature is disabled in every function, we'll compute it as
+ // disabled. If any function lacks a target-features attribute, it'll
+ // default to the target CPU from the `TargetMachine`.
+ FeatureBitset Features;
+ // We need any MCSubtargetInfo to access WebAssemblyFeatureKV.
+ const WebAssemblySubtarget *AnyST = nullptr;
+ for (auto &F : M) {
+ if (F.isDeclaration())
+ continue;
+
+ AnyST = WasmTM->getSubtargetImpl(F);
+ Features |= AnyST->getFeatureBits();
}
- static std::string getFeatureString(const WebAssemblySubtarget *ST,
- const FeatureBitset &Features) {
- std::string Ret;
- for (const SubtargetFeatureKV &KV : ST->getAllProcessorFeatures()) {
- if (Features[KV.Value])
- Ret += (StringRef("+") + KV.key() + ",").str();
- else
- Ret += (StringRef("-") + KV.key() + ",").str();
- }
- // remove trailing ','
- Ret.pop_back();
- return Ret;
+ // If we have no defined functions, use the target CPU from the
+ // `TargetMachine`.
+ if (!AnyST) {
+ AnyST =
+ WasmTM->getSubtargetImpl(std::string(WasmTM->getTargetCPU()),
+ std::string(WasmTM->getTargetFeatureString()));
+ Features = AnyST->getFeatureBits();
}
- void replaceFeatures(Function &F, const std::string &Features) {
- F.removeFnAttr("target-features");
- F.removeFnAttr("target-cpu");
- F.addFnAttr("target-features", Features);
- }
+ return {Features, getFeatureString(AnyST, Features)};
+}
- bool stripAtomics(Module &M) {
- // Detect whether any atomics will be lowered, since there is no way to tell
- // whether the LowerAtomic pass lowers e.g. stores.
- bool Stripped = false;
- for (auto &F : M) {
- for (auto &B : F) {
- for (auto &I : B) {
- if (I.isAtomic()) {
- Stripped = true;
- goto done;
- }
+static void replaceFeatures(Function &F, const std::string &Features) {
+ F.removeFnAttr("target-features");
+ F.removeFnAttr("target-cpu");
+ F.addFnAttr("target-features", Features);
+}
+
+static bool stripAtomics(Module &M) {
+ // Detect whether any atomics will be lowered, since there is no way to tell
+ // whether the LowerAtomic pass lowers e.g. stores.
+ bool Stripped = false;
+ for (auto &F : M) {
+ for (auto &B : F) {
+ for (auto &I : B) {
+ if (I.isAtomic()) {
+ Stripped = true;
+ goto done;
}
}
}
+ }
- done:
- if (!Stripped)
- return false;
+done:
+ if (!Stripped)
+ return false;
- LowerAtomicPass Lowerer;
- FunctionAnalysisManager FAM;
- for (auto &F : M)
- Lowerer.run(F, FAM);
+ LowerAtomicPass Lowerer;
+ FunctionAnalysisManager FAM;
+ for (auto &F : M)
+ Lowerer.run(F, FAM);
- return true;
- }
+ return true;
+}
- bool stripThreadLocals(Module &M) {
- bool Stripped = false;
- for (auto &GV : M.globals()) {
- if (GV.isThreadLocal()) {
- // replace `@llvm.threadlocal.address.pX(GV)` with `GV`.
- for (Use &U : make_early_inc_range(GV.uses())) {
- if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(U.getUser())) {
- if (II->getIntrinsicID() == Intrinsic::threadlocal_address &&
- II->getArgOperand(0) == &GV) {
- II->replaceAllUsesWith(&GV);
- II->eraseFromParent();
- }
+static bool stripThreadLocals(Module &M) {
+ bool Stripped = false;
+ for (auto &GV : M.globals()) {
+ if (GV.isThreadLocal()) {
+ // replace `@llvm.threadlocal.address.pX(GV)` with `GV`.
+ for (Use &U : make_early_inc_range(GV.uses())) {
+ if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(U.getUser())) {
+ if (II->getIntrinsicID() == Intrinsic::threadlocal_address &&
+ II->getArgOperand(0) == &GV) {
+ II->replaceAllUsesWith(&GV);
+ II->eraseFromParent();
}
}
-
- Stripped = true;
- GV.setThreadLocal(false);
}
+
+ Stripped = true;
+ GV.setThreadLocal(false);
}
- return Stripped;
}
+ return Stripped;
+}
- void recordFeatures(Module &M, const WebAssemblySubtarget *ST,
- const FeatureBitset &Features, bool Stripped) {
- for (const SubtargetFeatureKV &KV : ST->getAllProcessorFeatures()) {
- if (Features[KV.Value]) {
- // Mark features as used
- std::string MDKey = (StringRef("wasm-feature-") + KV.key()).str();
- M.addModuleFlag(Module::ModFlagBehavior::Error, MDKey,
- wasm::WASM_FEATURE_PREFIX_USED);
- }
- }
- // Code compiled without atomics or bulk-memory may have had its atomics or
- // thread-local data lowered to nonatomic operations or non-thread-local
- // data. In that case, we mark the pseudo-feature "shared-mem" as disallowed
- // to tell the linker that it would be unsafe to allow this code to be used
- // in a module with shared memory.
- if (Stripped) {
- M.addModuleFlag(Module::ModFlagBehavior::Error, "wasm-feature-shared-mem",
- wasm::WASM_FEATURE_PREFIX_DISALLOWED);
+static void recordFeatures(Module &M, const WebAssemblySubtarget *ST,
+ const FeatureBitset &Features, bool Stripped) {
+ for (const SubtargetFeatureKV &KV : ST->getAllProcessorFeatures()) {
+ if (Features[KV.Value]) {
+ // Mark features as used
+ std::string MDKey = (StringRef("wasm-feature-") + KV.key()).str();
+ M.addModuleFlag(Module::ModFlagBehavior::Error, MDKey,
+ wasm::WASM_FEATURE_PREFIX_USED);
}
}
-};
-} // namespace
+ // Code compiled without atomics or bulk-memory may have had its atomics or
+ // thread-local data lowered to nonatomic operations or non-thread-local
+ // data. In that case, we mark the pseudo-feature "shared-mem" as disallowed
+ // to tell the linker that it would be unsafe to allow this code to be used
+ // in a module with shared memory.
+ if (Stripped) {
+ M.addModuleFlag(Module::ModFlagBehavior::Error, "wasm-feature-shared-mem",
+ wasm::WASM_FEATURE_PREFIX_DISALLOWED);
+ }
+}
-char WebAssemblyCoalesceFeaturesAndStripAtomics::ID = 0;
-INITIALIZE_PASS(WebAssemblyCoalesceFeaturesAndStripAtomics, DEBUG_TYPE,
- "Coalesce features and strip atomics", true, false)
+static bool coalesceFeaturesAndStripAtomics(Module &M,
+ WebAssemblyTargetMachine *WasmTM) {
+ auto [Features, FeatureStr] = coalesceFeatures(M, WasmTM);
-ModulePass *llvm::createWebAssemblyCoalesceFeaturesAndStripAtomics(
- WebAssemblyTargetMachine &TM) {
- return new WebAssemblyCoalesceFeaturesAndStripAtomics(&TM);
+ WasmTM->setTargetFeatureString(FeatureStr);
+ for (auto &F : M)
+ replaceFeatures(F, FeatureStr);
+
+ bool StrippedAtomics = false;
+ bool StrippedTLS = false;
+
+ // In cooperative threading mode, thread locals are meaningful even without
+ // atomics.
+ const WebAssemblySubtarget *ST = WasmTM->getSubtargetImpl();
+ bool CooperativeThreading = ST->hasCooperativeMultithreading();
+
+ if (!Features[WebAssembly::FeatureAtomics]) {
+ StrippedAtomics = stripAtomics(M);
+ if (!CooperativeThreading)
+ StrippedTLS = stripThreadLocals(M);
+ }
+ if (!Features[WebAssembly::FeatureBulkMemory] && !StrippedTLS) {
+ StrippedTLS = stripThreadLocals(M);
+ }
+
+ if (StrippedAtomics && !StrippedTLS && !CooperativeThreading)
+ stripThreadLocals(M);
+ else if (StrippedTLS && !StrippedAtomics)
+ stripAtomics(M);
+
+ recordFeatures(M, ST, Features, StrippedAtomics || StrippedTLS);
+
+ // Conservatively assume we have made some change
+ return true;
+}
+
+bool WebAssemblyCoalesceFeaturesAndStripAtomicsLegacy::runOnModule(Module &M) {
+ return coalesceFeaturesAndStripAtomics(M, WasmTM);
+}
+
+PreservedAnalyses WebAssemblyCoalesceFeaturesAndStripAtomicsPass::run(
+ Module &M, ModuleAnalysisManager &MAM) {
+ return coalesceFeaturesAndStripAtomics(M, &TM) ? PreservedAnalyses::none()
+ : PreservedAnalyses::all();
}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCodeGenPassBuilder.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCodeGenPassBuilder.cpp
index aa73e75165aff..55af1b1a4546e 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyCodeGenPassBuilder.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyCodeGenPassBuilder.cpp
@@ -134,7 +134,8 @@ void WebAssemblyCodeGenPassBuilder::addISelPrepare(
// loads and stores are promoted to local.gets/local.sets.
addFunctionPass(WebAssemblyRefTypeMem2LocalPass(), PMW);
// Lower atomics and TLS if necessary
- // TODO(boomanaiden154): CoalesceFeaturesAndStripAtomics
+ flushFPMsToMPM(PMW);
+ addModulePass(WebAssemblyCoalesceFeaturesAndStripAtomicsPass(TM), PMW);
// This is a no-op if atomics are not used in the module
addFunctionPass(AtomicExpandPass(TM), PMW);
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyPassRegistry.def b/llvm/lib/Target/WebAssembly/WebAssemblyPassRegistry.def
index 6767f7a6a6b77..a6da272bf3ba7 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyPassRegistry.def
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyPassRegistry.def
@@ -15,6 +15,8 @@
#define MODULE_PASS(NAME, CREATE_PASS)
#endif
MODULE_PASS("wasm-add-missing-prototypes", WebAssemblyAddMissingPrototypesPass())
+MODULE_PASS("wasm-coalesce-features-and-strip-atomics",
+ WebAssemblyCoalesceFeaturesAndStripAtomicsPass(*this))
MODULE_PASS("wasm-fix-function-bitcasts", WebAssemblyFixFunctionBitcastsPass())
MODULE_PASS("wasm-lower-em-ehsjlj", WebAssemblyLowerEmscriptenEHSjLjPass())
#undef MODULE_PASS
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index d5b51c4233afb..9d382451f5123 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -367,7 +367,7 @@ void WebAssemblyPassConfig::addISelPrepare() {
// loads and stores are promoted to local.gets/local.sets.
addPass(createWebAssemblyRefTypeMem2LocalLegacyPass());
// Lower atomics and TLS if necessary
- addPass(createWebAssemblyCoalesceFeaturesAndStripAtomics(
+ addPass(createWebAssemblyCoalesceFeaturesAndStripAtomicsLegacyPass(
getWebAssemblyTargetMachine()));
// This is a no-op if atomics are not used in the module
More information about the llvm-commits
mailing list