1*700637cbSDimitry Andric //===- SMEPeepholeOpt.cpp - SME peephole optimization pass-----------------===//
2*700637cbSDimitry Andric //
3*700637cbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*700637cbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*700637cbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*700637cbSDimitry Andric //
7*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
8*700637cbSDimitry Andric // This pass tries to remove back-to-back (smstart, smstop) and
9*700637cbSDimitry Andric // (smstop, smstart) sequences. The pass is conservative when it cannot
10*700637cbSDimitry Andric // determine that it is safe to remove these sequences.
11*700637cbSDimitry Andric //===----------------------------------------------------------------------===//
12*700637cbSDimitry Andric
13*700637cbSDimitry Andric #include "AArch64InstrInfo.h"
14*700637cbSDimitry Andric #include "AArch64MachineFunctionInfo.h"
15*700637cbSDimitry Andric #include "AArch64Subtarget.h"
16*700637cbSDimitry Andric #include "llvm/ADT/SmallVector.h"
17*700637cbSDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h"
18*700637cbSDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
19*700637cbSDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h"
20*700637cbSDimitry Andric #include "llvm/CodeGen/TargetRegisterInfo.h"
21*700637cbSDimitry Andric
22*700637cbSDimitry Andric using namespace llvm;
23*700637cbSDimitry Andric
24*700637cbSDimitry Andric #define DEBUG_TYPE "aarch64-sme-peephole-opt"
25*700637cbSDimitry Andric
26*700637cbSDimitry Andric namespace {
27*700637cbSDimitry Andric
28*700637cbSDimitry Andric struct SMEPeepholeOpt : public MachineFunctionPass {
29*700637cbSDimitry Andric static char ID;
30*700637cbSDimitry Andric
SMEPeepholeOpt__anon2e9980480111::SMEPeepholeOpt31*700637cbSDimitry Andric SMEPeepholeOpt() : MachineFunctionPass(ID) {}
32*700637cbSDimitry Andric
33*700637cbSDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override;
34*700637cbSDimitry Andric
getPassName__anon2e9980480111::SMEPeepholeOpt35*700637cbSDimitry Andric StringRef getPassName() const override {
36*700637cbSDimitry Andric return "SME Peephole Optimization pass";
37*700637cbSDimitry Andric }
38*700637cbSDimitry Andric
getAnalysisUsage__anon2e9980480111::SMEPeepholeOpt39*700637cbSDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override {
40*700637cbSDimitry Andric AU.setPreservesCFG();
41*700637cbSDimitry Andric MachineFunctionPass::getAnalysisUsage(AU);
42*700637cbSDimitry Andric }
43*700637cbSDimitry Andric
44*700637cbSDimitry Andric bool optimizeStartStopPairs(MachineBasicBlock &MBB,
45*700637cbSDimitry Andric bool &HasRemovedAllSMChanges) const;
46*700637cbSDimitry Andric bool visitRegSequence(MachineInstr &MI);
47*700637cbSDimitry Andric };
48*700637cbSDimitry Andric
49*700637cbSDimitry Andric char SMEPeepholeOpt::ID = 0;
50*700637cbSDimitry Andric
51*700637cbSDimitry Andric } // end anonymous namespace
52*700637cbSDimitry Andric
isConditionalStartStop(const MachineInstr * MI)53*700637cbSDimitry Andric static bool isConditionalStartStop(const MachineInstr *MI) {
54*700637cbSDimitry Andric return MI->getOpcode() == AArch64::MSRpstatePseudo;
55*700637cbSDimitry Andric }
56*700637cbSDimitry Andric
isMatchingStartStopPair(const MachineInstr * MI1,const MachineInstr * MI2)57*700637cbSDimitry Andric static bool isMatchingStartStopPair(const MachineInstr *MI1,
58*700637cbSDimitry Andric const MachineInstr *MI2) {
59*700637cbSDimitry Andric // We only consider the same type of streaming mode change here, i.e.
60*700637cbSDimitry Andric // start/stop SM, or start/stop ZA pairs.
61*700637cbSDimitry Andric if (MI1->getOperand(0).getImm() != MI2->getOperand(0).getImm())
62*700637cbSDimitry Andric return false;
63*700637cbSDimitry Andric
64*700637cbSDimitry Andric // One must be 'start', the other must be 'stop'
65*700637cbSDimitry Andric if (MI1->getOperand(1).getImm() == MI2->getOperand(1).getImm())
66*700637cbSDimitry Andric return false;
67*700637cbSDimitry Andric
68*700637cbSDimitry Andric bool IsConditional = isConditionalStartStop(MI2);
69*700637cbSDimitry Andric if (isConditionalStartStop(MI1) != IsConditional)
70*700637cbSDimitry Andric return false;
71*700637cbSDimitry Andric
72*700637cbSDimitry Andric if (!IsConditional)
73*700637cbSDimitry Andric return true;
74*700637cbSDimitry Andric
75*700637cbSDimitry Andric // Check to make sure the conditional start/stop pairs are identical.
76*700637cbSDimitry Andric if (MI1->getOperand(2).getImm() != MI2->getOperand(2).getImm())
77*700637cbSDimitry Andric return false;
78*700637cbSDimitry Andric
79*700637cbSDimitry Andric // Ensure reg masks are identical.
80*700637cbSDimitry Andric if (MI1->getOperand(4).getRegMask() != MI2->getOperand(4).getRegMask())
81*700637cbSDimitry Andric return false;
82*700637cbSDimitry Andric
83*700637cbSDimitry Andric // This optimisation is unlikely to happen in practice for conditional
84*700637cbSDimitry Andric // smstart/smstop pairs as the virtual registers for pstate.sm will always
85*700637cbSDimitry Andric // be different.
86*700637cbSDimitry Andric // TODO: For this optimisation to apply to conditional smstart/smstop,
87*700637cbSDimitry Andric // this pass will need to do more work to remove redundant calls to
88*700637cbSDimitry Andric // __arm_sme_state.
89*700637cbSDimitry Andric
90*700637cbSDimitry Andric // Only consider conditional start/stop pairs which read the same register
91*700637cbSDimitry Andric // holding the original value of pstate.sm, as some conditional start/stops
92*700637cbSDimitry Andric // require the state on entry to the function.
93*700637cbSDimitry Andric if (MI1->getOperand(3).isReg() && MI2->getOperand(3).isReg()) {
94*700637cbSDimitry Andric Register Reg1 = MI1->getOperand(3).getReg();
95*700637cbSDimitry Andric Register Reg2 = MI2->getOperand(3).getReg();
96*700637cbSDimitry Andric if (Reg1.isPhysical() || Reg2.isPhysical() || Reg1 != Reg2)
97*700637cbSDimitry Andric return false;
98*700637cbSDimitry Andric }
99*700637cbSDimitry Andric
100*700637cbSDimitry Andric return true;
101*700637cbSDimitry Andric }
102*700637cbSDimitry Andric
ChangesStreamingMode(const MachineInstr * MI)103*700637cbSDimitry Andric static bool ChangesStreamingMode(const MachineInstr *MI) {
104*700637cbSDimitry Andric assert((MI->getOpcode() == AArch64::MSRpstatesvcrImm1 ||
105*700637cbSDimitry Andric MI->getOpcode() == AArch64::MSRpstatePseudo) &&
106*700637cbSDimitry Andric "Expected MI to be a smstart/smstop instruction");
107*700637cbSDimitry Andric return MI->getOperand(0).getImm() == AArch64SVCR::SVCRSM ||
108*700637cbSDimitry Andric MI->getOperand(0).getImm() == AArch64SVCR::SVCRSMZA;
109*700637cbSDimitry Andric }
110*700637cbSDimitry Andric
isSVERegOp(const TargetRegisterInfo & TRI,const MachineRegisterInfo & MRI,const MachineOperand & MO)111*700637cbSDimitry Andric static bool isSVERegOp(const TargetRegisterInfo &TRI,
112*700637cbSDimitry Andric const MachineRegisterInfo &MRI,
113*700637cbSDimitry Andric const MachineOperand &MO) {
114*700637cbSDimitry Andric if (!MO.isReg())
115*700637cbSDimitry Andric return false;
116*700637cbSDimitry Andric
117*700637cbSDimitry Andric Register R = MO.getReg();
118*700637cbSDimitry Andric if (R.isPhysical())
119*700637cbSDimitry Andric return llvm::any_of(TRI.subregs_inclusive(R), [](const MCPhysReg &SR) {
120*700637cbSDimitry Andric return AArch64::ZPRRegClass.contains(SR) ||
121*700637cbSDimitry Andric AArch64::PPRRegClass.contains(SR);
122*700637cbSDimitry Andric });
123*700637cbSDimitry Andric
124*700637cbSDimitry Andric const TargetRegisterClass *RC = MRI.getRegClass(R);
125*700637cbSDimitry Andric return TRI.getCommonSubClass(&AArch64::ZPRRegClass, RC) ||
126*700637cbSDimitry Andric TRI.getCommonSubClass(&AArch64::PPRRegClass, RC);
127*700637cbSDimitry Andric }
128*700637cbSDimitry Andric
optimizeStartStopPairs(MachineBasicBlock & MBB,bool & HasRemovedAllSMChanges) const129*700637cbSDimitry Andric bool SMEPeepholeOpt::optimizeStartStopPairs(
130*700637cbSDimitry Andric MachineBasicBlock &MBB, bool &HasRemovedAllSMChanges) const {
131*700637cbSDimitry Andric const MachineRegisterInfo &MRI = MBB.getParent()->getRegInfo();
132*700637cbSDimitry Andric const TargetRegisterInfo &TRI =
133*700637cbSDimitry Andric *MBB.getParent()->getSubtarget().getRegisterInfo();
134*700637cbSDimitry Andric
135*700637cbSDimitry Andric bool Changed = false;
136*700637cbSDimitry Andric MachineInstr *Prev = nullptr;
137*700637cbSDimitry Andric SmallVector<MachineInstr *, 4> ToBeRemoved;
138*700637cbSDimitry Andric
139*700637cbSDimitry Andric // Convenience function to reset the matching of a sequence.
140*700637cbSDimitry Andric auto Reset = [&]() {
141*700637cbSDimitry Andric Prev = nullptr;
142*700637cbSDimitry Andric ToBeRemoved.clear();
143*700637cbSDimitry Andric };
144*700637cbSDimitry Andric
145*700637cbSDimitry Andric // Walk through instructions in the block trying to find pairs of smstart
146*700637cbSDimitry Andric // and smstop nodes that cancel each other out. We only permit a limited
147*700637cbSDimitry Andric // set of instructions to appear between them, otherwise we reset our
148*700637cbSDimitry Andric // tracking.
149*700637cbSDimitry Andric unsigned NumSMChanges = 0;
150*700637cbSDimitry Andric unsigned NumSMChangesRemoved = 0;
151*700637cbSDimitry Andric for (MachineInstr &MI : make_early_inc_range(MBB)) {
152*700637cbSDimitry Andric switch (MI.getOpcode()) {
153*700637cbSDimitry Andric case AArch64::MSRpstatesvcrImm1:
154*700637cbSDimitry Andric case AArch64::MSRpstatePseudo: {
155*700637cbSDimitry Andric if (ChangesStreamingMode(&MI))
156*700637cbSDimitry Andric NumSMChanges++;
157*700637cbSDimitry Andric
158*700637cbSDimitry Andric if (!Prev)
159*700637cbSDimitry Andric Prev = &MI;
160*700637cbSDimitry Andric else if (isMatchingStartStopPair(Prev, &MI)) {
161*700637cbSDimitry Andric // If they match, we can remove them, and possibly any instructions
162*700637cbSDimitry Andric // that we marked for deletion in between.
163*700637cbSDimitry Andric Prev->eraseFromParent();
164*700637cbSDimitry Andric MI.eraseFromParent();
165*700637cbSDimitry Andric for (MachineInstr *TBR : ToBeRemoved)
166*700637cbSDimitry Andric TBR->eraseFromParent();
167*700637cbSDimitry Andric ToBeRemoved.clear();
168*700637cbSDimitry Andric Prev = nullptr;
169*700637cbSDimitry Andric Changed = true;
170*700637cbSDimitry Andric NumSMChangesRemoved += 2;
171*700637cbSDimitry Andric } else {
172*700637cbSDimitry Andric Reset();
173*700637cbSDimitry Andric Prev = &MI;
174*700637cbSDimitry Andric }
175*700637cbSDimitry Andric continue;
176*700637cbSDimitry Andric }
177*700637cbSDimitry Andric default:
178*700637cbSDimitry Andric if (!Prev)
179*700637cbSDimitry Andric // Avoid doing expensive checks when Prev is nullptr.
180*700637cbSDimitry Andric continue;
181*700637cbSDimitry Andric break;
182*700637cbSDimitry Andric }
183*700637cbSDimitry Andric
184*700637cbSDimitry Andric // Test if the instructions in between the start/stop sequence are agnostic
185*700637cbSDimitry Andric // of streaming mode. If not, the algorithm should reset.
186*700637cbSDimitry Andric switch (MI.getOpcode()) {
187*700637cbSDimitry Andric default:
188*700637cbSDimitry Andric Reset();
189*700637cbSDimitry Andric break;
190*700637cbSDimitry Andric case AArch64::COALESCER_BARRIER_FPR16:
191*700637cbSDimitry Andric case AArch64::COALESCER_BARRIER_FPR32:
192*700637cbSDimitry Andric case AArch64::COALESCER_BARRIER_FPR64:
193*700637cbSDimitry Andric case AArch64::COALESCER_BARRIER_FPR128:
194*700637cbSDimitry Andric case AArch64::COPY:
195*700637cbSDimitry Andric // These instructions should be safe when executed on their own, but
196*700637cbSDimitry Andric // the code remains conservative when SVE registers are used. There may
197*700637cbSDimitry Andric // exist subtle cases where executing a COPY in a different mode results
198*700637cbSDimitry Andric // in different behaviour, even if we can't yet come up with any
199*700637cbSDimitry Andric // concrete example/test-case.
200*700637cbSDimitry Andric if (isSVERegOp(TRI, MRI, MI.getOperand(0)) ||
201*700637cbSDimitry Andric isSVERegOp(TRI, MRI, MI.getOperand(1)))
202*700637cbSDimitry Andric Reset();
203*700637cbSDimitry Andric break;
204*700637cbSDimitry Andric case AArch64::ADJCALLSTACKDOWN:
205*700637cbSDimitry Andric case AArch64::ADJCALLSTACKUP:
206*700637cbSDimitry Andric case AArch64::ANDXri:
207*700637cbSDimitry Andric case AArch64::ADDXri:
208*700637cbSDimitry Andric // We permit these as they don't generate SVE/NEON instructions.
209*700637cbSDimitry Andric break;
210*700637cbSDimitry Andric case AArch64::VGRestorePseudo:
211*700637cbSDimitry Andric case AArch64::VGSavePseudo:
212*700637cbSDimitry Andric // When the smstart/smstop are removed, we should also remove
213*700637cbSDimitry Andric // the pseudos that save/restore the VG value for CFI info.
214*700637cbSDimitry Andric ToBeRemoved.push_back(&MI);
215*700637cbSDimitry Andric break;
216*700637cbSDimitry Andric case AArch64::MSRpstatesvcrImm1:
217*700637cbSDimitry Andric case AArch64::MSRpstatePseudo:
218*700637cbSDimitry Andric llvm_unreachable("Should have been handled");
219*700637cbSDimitry Andric }
220*700637cbSDimitry Andric }
221*700637cbSDimitry Andric
222*700637cbSDimitry Andric HasRemovedAllSMChanges =
223*700637cbSDimitry Andric NumSMChanges && (NumSMChanges == NumSMChangesRemoved);
224*700637cbSDimitry Andric return Changed;
225*700637cbSDimitry Andric }
226*700637cbSDimitry Andric
227*700637cbSDimitry Andric // Using the FORM_TRANSPOSED_REG_TUPLE pseudo can improve register allocation
228*700637cbSDimitry Andric // of multi-vector intrinsics. However, the pseudo should only be emitted if
229*700637cbSDimitry Andric // the input registers of the REG_SEQUENCE are copy nodes where the source
230*700637cbSDimitry Andric // register is in a StridedOrContiguous class. For example:
231*700637cbSDimitry Andric //
232*700637cbSDimitry Andric // %3:zpr2stridedorcontiguous = LD1B_2Z_IMM_PSEUDO ..
233*700637cbSDimitry Andric // %4:zpr = COPY %3.zsub1:zpr2stridedorcontiguous
234*700637cbSDimitry Andric // %5:zpr = COPY %3.zsub0:zpr2stridedorcontiguous
235*700637cbSDimitry Andric // %6:zpr2stridedorcontiguous = LD1B_2Z_PSEUDO ..
236*700637cbSDimitry Andric // %7:zpr = COPY %6.zsub1:zpr2stridedorcontiguous
237*700637cbSDimitry Andric // %8:zpr = COPY %6.zsub0:zpr2stridedorcontiguous
238*700637cbSDimitry Andric // %9:zpr2mul2 = REG_SEQUENCE %5:zpr, %subreg.zsub0, %8:zpr, %subreg.zsub1
239*700637cbSDimitry Andric //
240*700637cbSDimitry Andric // -> %9:zpr2mul2 = FORM_TRANSPOSED_REG_TUPLE_X2_PSEUDO %5:zpr, %8:zpr
241*700637cbSDimitry Andric //
visitRegSequence(MachineInstr & MI)242*700637cbSDimitry Andric bool SMEPeepholeOpt::visitRegSequence(MachineInstr &MI) {
243*700637cbSDimitry Andric assert(MI.getMF()->getRegInfo().isSSA() && "Expected to be run on SSA form!");
244*700637cbSDimitry Andric
245*700637cbSDimitry Andric MachineRegisterInfo &MRI = MI.getMF()->getRegInfo();
246*700637cbSDimitry Andric switch (MRI.getRegClass(MI.getOperand(0).getReg())->getID()) {
247*700637cbSDimitry Andric case AArch64::ZPR2RegClassID:
248*700637cbSDimitry Andric case AArch64::ZPR4RegClassID:
249*700637cbSDimitry Andric case AArch64::ZPR2Mul2RegClassID:
250*700637cbSDimitry Andric case AArch64::ZPR4Mul4RegClassID:
251*700637cbSDimitry Andric break;
252*700637cbSDimitry Andric default:
253*700637cbSDimitry Andric return false;
254*700637cbSDimitry Andric }
255*700637cbSDimitry Andric
256*700637cbSDimitry Andric // The first operand is the register class created by the REG_SEQUENCE.
257*700637cbSDimitry Andric // Each operand pair after this consists of a vreg + subreg index, so
258*700637cbSDimitry Andric // for example a sequence of 2 registers will have a total of 5 operands.
259*700637cbSDimitry Andric if (MI.getNumOperands() != 5 && MI.getNumOperands() != 9)
260*700637cbSDimitry Andric return false;
261*700637cbSDimitry Andric
262*700637cbSDimitry Andric MCRegister SubReg = MCRegister::NoRegister;
263*700637cbSDimitry Andric for (unsigned I = 1; I < MI.getNumOperands(); I += 2) {
264*700637cbSDimitry Andric MachineOperand &MO = MI.getOperand(I);
265*700637cbSDimitry Andric
266*700637cbSDimitry Andric MachineOperand *Def = MRI.getOneDef(MO.getReg());
267*700637cbSDimitry Andric if (!Def || !Def->getParent()->isCopy())
268*700637cbSDimitry Andric return false;
269*700637cbSDimitry Andric
270*700637cbSDimitry Andric const MachineOperand &CopySrc = Def->getParent()->getOperand(1);
271*700637cbSDimitry Andric unsigned OpSubReg = CopySrc.getSubReg();
272*700637cbSDimitry Andric if (SubReg == MCRegister::NoRegister)
273*700637cbSDimitry Andric SubReg = OpSubReg;
274*700637cbSDimitry Andric
275*700637cbSDimitry Andric MachineOperand *CopySrcOp = MRI.getOneDef(CopySrc.getReg());
276*700637cbSDimitry Andric if (!CopySrcOp || !CopySrcOp->isReg() || OpSubReg != SubReg ||
277*700637cbSDimitry Andric CopySrcOp->getReg().isPhysical())
278*700637cbSDimitry Andric return false;
279*700637cbSDimitry Andric
280*700637cbSDimitry Andric const TargetRegisterClass *CopySrcClass =
281*700637cbSDimitry Andric MRI.getRegClass(CopySrcOp->getReg());
282*700637cbSDimitry Andric if (CopySrcClass != &AArch64::ZPR2StridedOrContiguousRegClass &&
283*700637cbSDimitry Andric CopySrcClass != &AArch64::ZPR4StridedOrContiguousRegClass)
284*700637cbSDimitry Andric return false;
285*700637cbSDimitry Andric }
286*700637cbSDimitry Andric
287*700637cbSDimitry Andric unsigned Opc = MI.getNumOperands() == 5
288*700637cbSDimitry Andric ? AArch64::FORM_TRANSPOSED_REG_TUPLE_X2_PSEUDO
289*700637cbSDimitry Andric : AArch64::FORM_TRANSPOSED_REG_TUPLE_X4_PSEUDO;
290*700637cbSDimitry Andric
291*700637cbSDimitry Andric const TargetInstrInfo *TII =
292*700637cbSDimitry Andric MI.getMF()->getSubtarget<AArch64Subtarget>().getInstrInfo();
293*700637cbSDimitry Andric MachineInstrBuilder MIB = BuildMI(*MI.getParent(), MI, MI.getDebugLoc(),
294*700637cbSDimitry Andric TII->get(Opc), MI.getOperand(0).getReg());
295*700637cbSDimitry Andric for (unsigned I = 1; I < MI.getNumOperands(); I += 2)
296*700637cbSDimitry Andric MIB.addReg(MI.getOperand(I).getReg());
297*700637cbSDimitry Andric
298*700637cbSDimitry Andric MI.eraseFromParent();
299*700637cbSDimitry Andric return true;
300*700637cbSDimitry Andric }
301*700637cbSDimitry Andric
302*700637cbSDimitry Andric INITIALIZE_PASS(SMEPeepholeOpt, "aarch64-sme-peephole-opt",
303*700637cbSDimitry Andric "SME Peephole Optimization", false, false)
304*700637cbSDimitry Andric
runOnMachineFunction(MachineFunction & MF)305*700637cbSDimitry Andric bool SMEPeepholeOpt::runOnMachineFunction(MachineFunction &MF) {
306*700637cbSDimitry Andric if (skipFunction(MF.getFunction()))
307*700637cbSDimitry Andric return false;
308*700637cbSDimitry Andric
309*700637cbSDimitry Andric if (!MF.getSubtarget<AArch64Subtarget>().hasSME())
310*700637cbSDimitry Andric return false;
311*700637cbSDimitry Andric
312*700637cbSDimitry Andric assert(MF.getRegInfo().isSSA() && "Expected to be run on SSA form!");
313*700637cbSDimitry Andric
314*700637cbSDimitry Andric bool Changed = false;
315*700637cbSDimitry Andric bool FunctionHasAllSMChangesRemoved = false;
316*700637cbSDimitry Andric
317*700637cbSDimitry Andric // Even if the block lives in a function with no SME attributes attached we
318*700637cbSDimitry Andric // still have to analyze all the blocks because we may call a streaming
319*700637cbSDimitry Andric // function that requires smstart/smstop pairs.
320*700637cbSDimitry Andric for (MachineBasicBlock &MBB : MF) {
321*700637cbSDimitry Andric bool BlockHasAllSMChangesRemoved;
322*700637cbSDimitry Andric Changed |= optimizeStartStopPairs(MBB, BlockHasAllSMChangesRemoved);
323*700637cbSDimitry Andric FunctionHasAllSMChangesRemoved |= BlockHasAllSMChangesRemoved;
324*700637cbSDimitry Andric
325*700637cbSDimitry Andric if (MF.getSubtarget<AArch64Subtarget>().isStreaming()) {
326*700637cbSDimitry Andric for (MachineInstr &MI : make_early_inc_range(MBB))
327*700637cbSDimitry Andric if (MI.getOpcode() == AArch64::REG_SEQUENCE)
328*700637cbSDimitry Andric Changed |= visitRegSequence(MI);
329*700637cbSDimitry Andric }
330*700637cbSDimitry Andric }
331*700637cbSDimitry Andric
332*700637cbSDimitry Andric AArch64FunctionInfo *AFI = MF.getInfo<AArch64FunctionInfo>();
333*700637cbSDimitry Andric if (FunctionHasAllSMChangesRemoved)
334*700637cbSDimitry Andric AFI->setHasStreamingModeChanges(false);
335*700637cbSDimitry Andric
336*700637cbSDimitry Andric return Changed;
337*700637cbSDimitry Andric }
338*700637cbSDimitry Andric
createSMEPeepholeOptPass()339*700637cbSDimitry Andric FunctionPass *llvm::createSMEPeepholeOptPass() { return new SMEPeepholeOpt(); }
340