[llvm] [LLVM][SVE] Add MachineInst pass to coalesce PTRUE instructions. (PR #204820)
Paul Walker via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 26 09:04:56 PDT 2026
================
@@ -0,0 +1,226 @@
+//===- AArch64PTrueCoalescing.cpp - Coalesce SVE PTRUEs ---------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass coalesces compatible all-active SVE PTRUE instructions.
+//
+// Consider two all-active PTRUE instructions X and Y with element sizes XSize
+// and YSize. If X dominates Y and XSize <= YSize, then every predicate bit that
+// Y sets is also set by X. In that case, uses of Y can be redirected to X as
+// long as each user of Y only reads predicate bits at YSize granularity or
+// larger.
+//
+// If the dominating PTRUE has a larger element size, we can coalesce the pair
+// by changing the dominating PTRUE to the smaller element size, provided that
+// all of its existing users are also safe with that granularity.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AArch64.h"
+#include "AArch64InstrInfo.h"
+#include "AArch64Subtarget.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/CodeGen/MachineDominators.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineInstr.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "aarch64-ptrue-coalesce"
+
+static cl::opt<bool> EnablePTrueCoalescing(
+ "aarch64-enable-ptrue-coalescing", cl::init(false), cl::Hidden,
+ cl::desc("Enable coalescing of compatible AArch64 SVE PTRUE instructions"));
+
+namespace {
+
+static bool isAllActivePTrue(const MachineInstr &MI) {
+ switch (MI.getOpcode()) {
+ default:
+ return false;
+ case AArch64::PTRUE_B:
+ case AArch64::PTRUE_H:
+ case AArch64::PTRUE_S:
+ case AArch64::PTRUE_D:
+ return MI.getOperand(1).getImm() == 31;
+ }
+}
+
+class AArch64PTrueCoalescingImpl {
+ const AArch64InstrInfo *TII = nullptr;
+ MachineRegisterInfo *MRI = nullptr;
+ MachineDominatorTree *MDT = nullptr;
+
+public:
+ explicit AArch64PTrueCoalescingImpl(MachineDominatorTree &MDT) : MDT(&MDT) {}
+
+ bool run(MachineFunction &MF);
+
+private:
+ bool allUsersSafeForElementSize(Register Reg, uint64_t ElementSize) const;
+ bool tryCoalesce(MachineInstr &DomPTrue, MachineInstr &PTrue) const;
+};
+
+class AArch64PTrueCoalescingLegacy : public MachineFunctionPass {
+public:
+ static char ID;
+
+ AArch64PTrueCoalescingLegacy() : MachineFunctionPass(ID) {}
+
+ bool runOnMachineFunction(MachineFunction &MF) override;
+
+ StringRef getPassName() const override { return "AArch64 PTRUE Coalescing"; }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesCFG();
+ AU.addRequired<MachineDominatorTreeWrapperPass>();
+ AU.addPreserved<MachineDominatorTreeWrapperPass>();
+ MachineFunctionPass::getAnalysisUsage(AU);
+ }
+};
+
+char AArch64PTrueCoalescingLegacy::ID = 0;
+
+} // end anonymous namespace
+
+INITIALIZE_PASS_BEGIN(AArch64PTrueCoalescingLegacy, DEBUG_TYPE,
+ "AArch64 PTRUE Coalescing", false, false)
+INITIALIZE_PASS_DEPENDENCY(MachineDominatorTreeWrapperPass)
+INITIALIZE_PASS_END(AArch64PTrueCoalescingLegacy, DEBUG_TYPE,
+ "AArch64 PTRUE Coalescing", false, false)
+
+bool AArch64PTrueCoalescingImpl::allUsersSafeForElementSize(
+ Register Reg, uint64_t ElementSize) const {
+ for (MachineOperand &UseMO : MRI->use_nodbg_operands(Reg)) {
+ if (UseMO.getSubReg())
+ return false;
+
+ MachineInstr *UseMI = UseMO.getParent();
+ uint64_t UseElementSize = TII->getElementSizeForOpcode(UseMI->getOpcode());
+ if (UseElementSize == AArch64::ElementSizeNone ||
+ UseElementSize < ElementSize)
+ return false;
+ }
+
+ return true;
+}
+
+bool AArch64PTrueCoalescingImpl::tryCoalesce(MachineInstr &DomPTrue,
+ MachineInstr &PTrue) const {
+ assert(isAllActivePTrue(DomPTrue) && "Expected all-active PTRUE");
+ assert(isAllActivePTrue(PTrue) && "Expected all-active PTRUE");
+
+ if (&DomPTrue == &PTrue || !MDT->dominates(&DomPTrue, &PTrue))
+ return false;
+
+ Register DomReg = DomPTrue.getOperand(0).getReg();
+ Register Reg = PTrue.getOperand(0).getReg();
+
+ uint64_t DomElementSize = TII->getElementSizeForOpcode(DomPTrue.getOpcode());
+ uint64_t ElementSize = TII->getElementSizeForOpcode(PTrue.getOpcode());
+ assert(DomElementSize != AArch64::ElementSizeNone &&
+ "PTRUE should have an element size");
+ assert(ElementSize != AArch64::ElementSizeNone &&
+ "PTRUE should have an element size");
+
+ if (!MRI->constrainRegClass(DomReg, MRI->getRegClass(Reg)))
+ return false;
+
+ bool MutateDomPTrue = false;
+ if (DomElementSize < ElementSize) {
+ // DomPTrue sets all lanes set by PTrue, plus extra lanes. Prefer to reuse
+ // DomPTrue as-is when PTrue's users do not observe those extra lanes.
+ if (!allUsersSafeForElementSize(Reg, ElementSize)) {
+ if (!allUsersSafeForElementSize(DomReg, ElementSize))
+ return false;
+ MutateDomPTrue = true;
+ }
+ } else if (DomElementSize > ElementSize) {
+ if (!allUsersSafeForElementSize(DomReg, ElementSize))
+ return false;
+ MutateDomPTrue = true;
----------------
paulwalker-arm wrote:
I've reworked and hopefully simplified the algorithm so the PTRUE element size and using-instructions smallest element size is store independently, which I then use to remove the bogus behaviour.
https://github.com/llvm/llvm-project/pull/204820
More information about the llvm-commits
mailing list