xref: /freebsd/contrib/llvm-project/llvm/lib/Target/AArch64/AArch64A53Fix835769.cpp (revision 18054d0220cfc8df9c9568c437bd6fbb59d53c3c)
1 //===-- AArch64A53Fix835769.cpp -------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 // This pass changes code to work around Cortex-A53 erratum 835769.
9 // It works around it by inserting a nop instruction in code sequences that
10 // in some circumstances may trigger the erratum.
11 // It inserts a nop instruction between a sequence of the following 2 classes
12 // of instructions:
13 // instr 1: mem-instr (including loads, stores and prefetches).
14 // instr 2: non-SIMD integer multiply-accumulate writing 64-bit X registers.
15 //===----------------------------------------------------------------------===//
16 
17 #include "AArch64.h"
18 #include "AArch64Subtarget.h"
19 #include "llvm/ADT/Statistic.h"
20 #include "llvm/CodeGen/MachineFunction.h"
21 #include "llvm/CodeGen/MachineFunctionPass.h"
22 #include "llvm/CodeGen/MachineInstr.h"
23 #include "llvm/CodeGen/MachineInstrBuilder.h"
24 #include "llvm/CodeGen/MachineRegisterInfo.h"
25 #include "llvm/CodeGen/TargetInstrInfo.h"
26 #include "llvm/Support/Debug.h"
27 #include "llvm/Support/raw_ostream.h"
28 
29 using namespace llvm;
30 
31 #define DEBUG_TYPE "aarch64-fix-cortex-a53-835769"
32 
33 STATISTIC(NumNopsAdded, "Number of Nops added to work around erratum 835769");
34 
35 //===----------------------------------------------------------------------===//
36 // Helper functions
37 
38 // Is the instruction a match for the instruction that comes first in the
39 // sequence of instructions that can trigger the erratum?
40 static bool isFirstInstructionInSequence(MachineInstr *MI) {
41   // Must return true if this instruction is a load, a store or a prefetch.
42   switch (MI->getOpcode()) {
43   case AArch64::PRFMl:
44   case AArch64::PRFMroW:
45   case AArch64::PRFMroX:
46   case AArch64::PRFMui:
47   case AArch64::PRFUMi:
48     return true;
49   default:
50     return MI->mayLoadOrStore();
51   }
52 }
53 
54 // Is the instruction a match for the instruction that comes second in the
55 // sequence that can trigger the erratum?
56 static bool isSecondInstructionInSequence(MachineInstr *MI) {
57   // Must return true for non-SIMD integer multiply-accumulates, writing
58   // to a 64-bit register.
59   switch (MI->getOpcode()) {
60   // Erratum cannot be triggered when the destination register is 32 bits,
61   // therefore only include the following.
62   case AArch64::MSUBXrrr:
63   case AArch64::MADDXrrr:
64   case AArch64::SMADDLrrr:
65   case AArch64::SMSUBLrrr:
66   case AArch64::UMADDLrrr:
67   case AArch64::UMSUBLrrr:
68     // Erratum can only be triggered by multiply-adds, not by regular
69     // non-accumulating multiplies, i.e. when Ra=XZR='11111'
70     return MI->getOperand(3).getReg() != AArch64::XZR;
71   default:
72     return false;
73   }
74 }
75 
76 
77 //===----------------------------------------------------------------------===//
78 
79 namespace {
80 class AArch64A53Fix835769 : public MachineFunctionPass {
81   const TargetInstrInfo *TII;
82 
83 public:
84   static char ID;
85   explicit AArch64A53Fix835769() : MachineFunctionPass(ID) {
86     initializeAArch64A53Fix835769Pass(*PassRegistry::getPassRegistry());
87   }
88 
89   bool runOnMachineFunction(MachineFunction &F) override;
90 
91   MachineFunctionProperties getRequiredProperties() const override {
92     return MachineFunctionProperties().set(
93         MachineFunctionProperties::Property::NoVRegs);
94   }
95 
96   StringRef getPassName() const override {
97     return "Workaround A53 erratum 835769 pass";
98   }
99 
100   void getAnalysisUsage(AnalysisUsage &AU) const override {
101     AU.setPreservesCFG();
102     MachineFunctionPass::getAnalysisUsage(AU);
103   }
104 
105 private:
106   bool runOnBasicBlock(MachineBasicBlock &MBB);
107 };
108 char AArch64A53Fix835769::ID = 0;
109 
110 } // end anonymous namespace
111 
112 INITIALIZE_PASS(AArch64A53Fix835769, "aarch64-fix-cortex-a53-835769-pass",
113                 "AArch64 fix for A53 erratum 835769", false, false)
114 
115 //===----------------------------------------------------------------------===//
116 
117 bool
118 AArch64A53Fix835769::runOnMachineFunction(MachineFunction &F) {
119   LLVM_DEBUG(dbgs() << "***** AArch64A53Fix835769 *****\n");
120   auto &STI = F.getSubtarget<AArch64Subtarget>();
121   // Fix not requested, skip pass.
122   if (!STI.fixCortexA53_835769())
123     return false;
124 
125   bool Changed = false;
126   TII = STI.getInstrInfo();
127 
128   for (auto &MBB : F) {
129     Changed |= runOnBasicBlock(MBB);
130   }
131   return Changed;
132 }
133 
134 // Return the block that was fallen through to get to MBB, if any,
135 // otherwise nullptr.
136 static MachineBasicBlock *getBBFallenThrough(MachineBasicBlock *MBB,
137                                              const TargetInstrInfo *TII) {
138   // Get the previous machine basic block in the function.
139   MachineFunction::iterator MBBI(MBB);
140 
141   // Can't go off top of function.
142   if (MBBI == MBB->getParent()->begin())
143     return nullptr;
144 
145   MachineBasicBlock *TBB = nullptr, *FBB = nullptr;
146   SmallVector<MachineOperand, 2> Cond;
147 
148   MachineBasicBlock *PrevBB = &*std::prev(MBBI);
149   for (MachineBasicBlock *S : MBB->predecessors())
150     if (S == PrevBB && !TII->analyzeBranch(*PrevBB, TBB, FBB, Cond) && !TBB &&
151         !FBB)
152       return S;
153 
154   return nullptr;
155 }
156 
157 // Iterate through fallen through blocks trying to find a previous non-pseudo if
158 // there is one, otherwise return nullptr. Only look for instructions in
159 // previous blocks, not the current block, since we only use this to look at
160 // previous blocks.
161 static MachineInstr *getLastNonPseudo(MachineBasicBlock &MBB,
162                                       const TargetInstrInfo *TII) {
163   MachineBasicBlock *FMBB = &MBB;
164 
165   // If there is no non-pseudo in the current block, loop back around and try
166   // the previous block (if there is one).
167   while ((FMBB = getBBFallenThrough(FMBB, TII))) {
168     for (MachineInstr &I : llvm::reverse(*FMBB))
169       if (!I.isPseudo())
170         return &I;
171   }
172 
173   // There was no previous non-pseudo in the fallen through blocks
174   return nullptr;
175 }
176 
177 static void insertNopBeforeInstruction(MachineBasicBlock &MBB, MachineInstr* MI,
178                                        const TargetInstrInfo *TII) {
179   // If we are the first instruction of the block, put the NOP at the end of
180   // the previous fallthrough block
181   if (MI == &MBB.front()) {
182     MachineInstr *I = getLastNonPseudo(MBB, TII);
183     assert(I && "Expected instruction");
184     DebugLoc DL = I->getDebugLoc();
185     BuildMI(I->getParent(), DL, TII->get(AArch64::HINT)).addImm(0);
186   }
187   else {
188     DebugLoc DL = MI->getDebugLoc();
189     BuildMI(MBB, MI, DL, TII->get(AArch64::HINT)).addImm(0);
190   }
191 
192   ++NumNopsAdded;
193 }
194 
195 bool
196 AArch64A53Fix835769::runOnBasicBlock(MachineBasicBlock &MBB) {
197   bool Changed = false;
198   LLVM_DEBUG(dbgs() << "Running on MBB: " << MBB
199                     << " - scanning instructions...\n");
200 
201   // First, scan the basic block, looking for a sequence of 2 instructions
202   // that match the conditions under which the erratum may trigger.
203 
204   // List of terminating instructions in matching sequences
205   std::vector<MachineInstr*> Sequences;
206   unsigned Idx = 0;
207   MachineInstr *PrevInstr = nullptr;
208 
209   // Try and find the last non-pseudo instruction in any fallen through blocks,
210   // if there isn't one, then we use nullptr to represent that.
211   PrevInstr = getLastNonPseudo(MBB, TII);
212 
213   for (auto &MI : MBB) {
214     MachineInstr *CurrInstr = &MI;
215     LLVM_DEBUG(dbgs() << "  Examining: " << MI);
216     if (PrevInstr) {
217       LLVM_DEBUG(dbgs() << "    PrevInstr: " << *PrevInstr
218                         << "    CurrInstr: " << *CurrInstr
219                         << "    isFirstInstructionInSequence(PrevInstr): "
220                         << isFirstInstructionInSequence(PrevInstr) << "\n"
221                         << "    isSecondInstructionInSequence(CurrInstr): "
222                         << isSecondInstructionInSequence(CurrInstr) << "\n");
223       if (isFirstInstructionInSequence(PrevInstr) &&
224           isSecondInstructionInSequence(CurrInstr)) {
225         LLVM_DEBUG(dbgs() << "   ** pattern found at Idx " << Idx << "!\n");
226         Sequences.push_back(CurrInstr);
227       }
228     }
229     if (!CurrInstr->isPseudo())
230       PrevInstr = CurrInstr;
231     ++Idx;
232   }
233 
234   LLVM_DEBUG(dbgs() << "Scan complete, " << Sequences.size()
235                     << " occurrences of pattern found.\n");
236 
237   // Then update the basic block, inserting nops between the detected sequences.
238   for (auto &MI : Sequences) {
239     Changed = true;
240     insertNopBeforeInstruction(MBB, MI, TII);
241   }
242 
243   return Changed;
244 }
245 
246 // Factory function used by AArch64TargetMachine to add the pass to
247 // the passmanager.
248 FunctionPass *llvm::createAArch64A53Fix835769() {
249   return new AArch64A53Fix835769();
250 }
251