[llvm] 2375505 - [NFC][WebAssembly] Move WebAssemblyCoalesceFeaturesAndStripAtomics
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 15 07:01:59 PDT 2026
Author: Aiden Grossman
Date: 2026-07-15T07:01:54-07:00
New Revision: 2375505c7c19cf521b9ed5acaf7f359ddd9f216b
URL: https://github.com/llvm/llvm-project/commit/2375505c7c19cf521b9ed5acaf7f359ddd9f216b
DIFF: https://github.com/llvm/llvm-project/commit/2375505c7c19cf521b9ed5acaf7f359ddd9f216b.diff
LOG: [NFC][WebAssembly] Move WebAssemblyCoalesceFeaturesAndStripAtomics
Move WebAssemblyCoalesceFeaturesAndStripAtomics to a separate file.
Having a pass implemented directly in the TargetMachine file is a bit
weird.
Reviewers: dschuff, sbc100, aheejin
Pull Request: https://github.com/llvm/llvm-project/pull/209046
Added:
llvm/lib/Target/WebAssembly/WebAssemblyCoalesceFeaturesAndStripAtomics.cpp
Modified:
llvm/lib/Target/WebAssembly/CMakeLists.txt
llvm/lib/Target/WebAssembly/WebAssembly.h
llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt
index 980250198ff24..1a73567f535bb 100644
--- a/llvm/lib/Target/WebAssembly/CMakeLists.txt
+++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt
@@ -36,6 +36,7 @@ add_llvm_target(WebAssemblyCodeGen
WebAssemblyCFGStackify.cpp
WebAssemblyCleanCodeAfterTrap.cpp
WebAssemblyCFGSort.cpp
+ WebAssemblyCoalesceFeaturesAndStripAtomics.cpp
WebAssemblyCodeGenPassBuilder.cpp
WebAssemblyDebugFixup.cpp
WebAssemblyDebugValueManager.cpp
diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h
index 933062c86c8eb..6f84ff8adb4c0 100644
--- a/llvm/lib/Target/WebAssembly/WebAssembly.h
+++ b/llvm/lib/Target/WebAssembly/WebAssembly.h
@@ -85,6 +85,8 @@ class WebAssemblyReduceToAnyAllTruePass
FunctionPass *
createWebAssemblyReduceToAnyAllTrueLegacyPass(WebAssemblyTargetMachine &TM);
+ModulePass *
+createWebAssemblyCoalesceFeaturesAndStripAtomics(WebAssemblyTargetMachine &TM);
// GlobalISel
InstructionSelector *
@@ -160,6 +162,7 @@ void initializeWebAssemblyRegNumberingPass(PassRegistry &);
void initializeWebAssemblyRegStackifyPass(PassRegistry &);
void initializeWebAssemblyReplacePhysRegsPass(PassRegistry &);
void initializeWebAssemblySetP2AlignOperandsPass(PassRegistry &);
+void initializeWebAssemblyCoalesceFeaturesAndStripAtomicsPass(PassRegistry &);
namespace WebAssembly {
enum TargetIndex {
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCoalesceFeaturesAndStripAtomics.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCoalesceFeaturesAndStripAtomics.cpp
new file mode 100644
index 0000000000000..89c3842241bf7
--- /dev/null
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyCoalesceFeaturesAndStripAtomics.cpp
@@ -0,0 +1,197 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "WebAssembly.h"
+#include "WebAssemblyTargetMachine.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Transforms/Scalar/LowerAtomicPass.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "wasm-coalesce-features-and-strip-atomics"
+
+namespace {
+class WebAssemblyCoalesceFeaturesAndStripAtomics 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,
+ // also strip atomic operations and thread local storage.
+ WebAssemblyTargetMachine *WasmTM;
+
+public:
+ static char ID;
+
+ WebAssemblyCoalesceFeaturesAndStripAtomics(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);
+ }
+
+ 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;
+ }
+
+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::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;
+ }
+
+ void replaceFeatures(Function &F, const std::string &Features) {
+ F.removeFnAttr("target-features");
+ F.removeFnAttr("target-cpu");
+ F.addFnAttr("target-features", 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;
+ }
+ }
+ }
+ }
+
+ done:
+ if (!Stripped)
+ return false;
+
+ LowerAtomicPass Lowerer;
+ FunctionAnalysisManager FAM;
+ for (auto &F : M)
+ Lowerer.run(F, FAM);
+
+ 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();
+ }
+ }
+ }
+
+ Stripped = true;
+ GV.setThreadLocal(false);
+ }
+ }
+ 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);
+ }
+ }
+};
+} // namespace
+
+char WebAssemblyCoalesceFeaturesAndStripAtomics::ID = 0;
+INITIALIZE_PASS(WebAssemblyCoalesceFeaturesAndStripAtomics, DEBUG_TYPE,
+ "Coalesce features and strip atomics", true, false)
+
+ModulePass *llvm::createWebAssemblyCoalesceFeaturesAndStripAtomics(
+ WebAssemblyTargetMachine &TM) {
+ return new WebAssemblyCoalesceFeaturesAndStripAtomics(&TM);
+}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
index cdea4298aed20..d5b51c4233afb 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
@@ -255,174 +255,6 @@ WebAssemblyTargetMachine::getSubtargetImpl(const Function &F) const {
namespace {
-class CoalesceFeaturesAndStripAtomics 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,
- // also strip atomic operations and thread local storage.
- static char ID;
- WebAssemblyTargetMachine *WasmTM;
-
-public:
- CoalesceFeaturesAndStripAtomics(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);
- }
-
- 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;
- }
-
-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::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;
- }
-
- void replaceFeatures(Function &F, const std::string &Features) {
- F.removeFnAttr("target-features");
- F.removeFnAttr("target-cpu");
- F.addFnAttr("target-features", 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;
- }
- }
- }
- }
-
- done:
- if (!Stripped)
- return false;
-
- LowerAtomicPass Lowerer;
- FunctionAnalysisManager FAM;
- for (auto &F : M)
- Lowerer.run(F, FAM);
-
- 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();
- }
- }
- }
-
- Stripped = true;
- GV.setThreadLocal(false);
- }
- }
- 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);
- }
- }
-};
-char CoalesceFeaturesAndStripAtomics::ID = 0;
-
/// WebAssembly Code Generator Pass Configuration Options.
class WebAssemblyPassConfig final : public TargetPassConfig {
public:
@@ -535,7 +367,8 @@ void WebAssemblyPassConfig::addISelPrepare() {
// loads and stores are promoted to local.gets/local.sets.
addPass(createWebAssemblyRefTypeMem2LocalLegacyPass());
// Lower atomics and TLS if necessary
- addPass(new CoalesceFeaturesAndStripAtomics(&getWebAssemblyTargetMachine()));
+ addPass(createWebAssemblyCoalesceFeaturesAndStripAtomics(
+ getWebAssemblyTargetMachine()));
// This is a no-op if atomics are not used in the module
addPass(createAtomicExpandLegacyPass());
More information about the llvm-commits
mailing list