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