llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-backend-webassembly Author: Aiden Grossman (boomanaiden154) <details> <summary>Changes</summary> Move WebAssemblyCoalesceFeaturesAndStripAtomics to a separate file. Having a pass implemented directly in the TargetMachine file is a bit weird. --- Full diff: https://github.com/llvm/llvm-project/pull/209046.diff 4 Files Affected: - (modified) llvm/lib/Target/WebAssembly/CMakeLists.txt (+1) - (modified) llvm/lib/Target/WebAssembly/WebAssembly.h (+3) - (added) llvm/lib/Target/WebAssembly/WebAssemblyCoalesceFeaturesAndStripAtomics.cpp (+195) - (modified) llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp (+2-169) ``````````diff 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..99127117f2615 --- /dev/null +++ b/llvm/lib/Target/WebAssembly/WebAssemblyCoalesceFeaturesAndStripAtomics.cpp @@ -0,0 +1,195 @@ +//===----------------------------------------------------------------------===// +// +// 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/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()); `````````` </details> https://github.com/llvm/llvm-project/pull/209046 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
