#include "ARM.h"
#include "ARMBaseInstrInfo.h"
#include "ARMBaseRegisterInfo.h"
#include "ARMSubtarget.h"
#include "Utils/ARMBaseInfo.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineInstrBundleIterator.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/CodeGen/ReachingDefAnalysis.h"
#include "llvm/CodeGen/Register.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/InitializePasses.h"
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/Pass.h"
#include "llvm/PassRegistry.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <assert.h>
#include <stdint.h>
using namespace llvm;
#define DEBUG_TYPE "arm-fix-cortex-a57-aes-1742098"
namespace {
class ARMFixCortexA57AES1742098 : public MachineFunctionPass {
public:
static char ID;
explicit ARMFixCortexA57AES1742098() : MachineFunctionPass(ID) {
initializeARMFixCortexA57AES1742098Pass(*PassRegistry::getPassRegistry());
}
bool runOnMachineFunction(MachineFunction &F) override;
MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::NoVRegs);
}
StringRef getPassName() const override {
return "ARM fix for Cortex-A57 AES Erratum 1742098";
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<ReachingDefAnalysis>();
AU.setPreservesCFG();
MachineFunctionPass::getAnalysisUsage(AU);
}
private:
struct AESFixupLocation {
MachineBasicBlock *Block;
MachineInstr *InsertionPt;
MachineOperand *MOp;
};
void analyzeMF(MachineFunction &MF, ReachingDefAnalysis &RDA,
const ARMBaseRegisterInfo *TRI,
SmallVectorImpl<AESFixupLocation> &FixupLocsForFn) const;
void insertAESFixup(AESFixupLocation &FixupLoc, const ARMBaseInstrInfo *TII,
const ARMBaseRegisterInfo *TRI) const;
static bool isFirstAESPairInstr(MachineInstr &MI);
static bool isSafeAESInput(MachineInstr &MI);
};
char ARMFixCortexA57AES1742098::ID = 0;
}
INITIALIZE_PASS_BEGIN(ARMFixCortexA57AES1742098, DEBUG_TYPE,
"ARM fix for Cortex-A57 AES Erratum 1742098", false,
false)
INITIALIZE_PASS_DEPENDENCY(ReachingDefAnalysis);
INITIALIZE_PASS_END(ARMFixCortexA57AES1742098, DEBUG_TYPE,
"ARM fix for Cortex-A57 AES Erratum 1742098", false, false)
bool ARMFixCortexA57AES1742098::isFirstAESPairInstr(MachineInstr &MI) {
unsigned Opc = MI.getOpcode();
return Opc == ARM::AESD || Opc == ARM::AESE;
}
bool ARMFixCortexA57AES1742098::isSafeAESInput(MachineInstr &MI) {
auto CondCodeIsAL = [](MachineInstr &MI) -> bool {
int CCIdx = MI.findFirstPredOperandIdx();
if (CCIdx == -1)
return false;
return MI.getOperand(CCIdx).getImm() == (int64_t)ARMCC::AL;
};
switch (MI.getOpcode()) {
default:
return false;
case ARM::AESD:
case ARM::AESE:
case ARM::AESMC:
case ARM::AESIMC:
return true;
case ARM::VANDd:
case ARM::VANDq:
case ARM::VORRd:
case ARM::VORRq:
case ARM::VEORd:
case ARM::VEORq:
case ARM::VMVNd:
case ARM::VMVNq:
case ARM::VMOVD:
case ARM::VMOVDRR:
case ARM::VMOVv2i64:
case ARM::VMOVv1i64:
case ARM::VMOVv2f32:
case ARM::VMOVv4f32:
case ARM::VMOVv2i32:
case ARM::VMOVv4i32:
case ARM::VMOVv4i16:
case ARM::VMOVv8i16:
case ARM::VMOVv8i8:
case ARM::VMOVv16i8:
case ARM::VLDRD:
case ARM::VLDMDDB_UPD:
case ARM::VLDMDIA_UPD:
case ARM::VLDMDIA:
case ARM::VLD1d64:
case ARM::VLD1q64:
case ARM::VLD1d32:
case ARM::VLD1q32:
case ARM::VLD2b32:
case ARM::VLD2d32:
case ARM::VLD2q32:
case ARM::VLD1d16:
case ARM::VLD1q16:
case ARM::VLD2d16:
case ARM::VLD2q16:
case ARM::VLD1d8:
case ARM::VLD1q8:
case ARM::VLD2b8:
case ARM::VLD2d8:
case ARM::VLD2q8:
case ARM::VLD3d32:
case ARM::VLD3q32:
case ARM::VLD3d16:
case ARM::VLD3q16:
case ARM::VLD3d8:
case ARM::VLD3q8:
case ARM::VLD4d32:
case ARM::VLD4q32:
case ARM::VLD4d16:
case ARM::VLD4q16:
case ARM::VLD4d8:
case ARM::VLD4q8:
case ARM::VLD1LNd32:
case ARM::VLD1LNd32_UPD:
case ARM::VLD1LNd8:
case ARM::VLD1LNd8_UPD:
case ARM::VLD1LNd16:
case ARM::VLD1LNd16_UPD:
case ARM::VLD1DUPd32:
case ARM::VLD1DUPd32wb_fixed:
case ARM::VLD1DUPd32wb_register:
case ARM::VLD1DUPd16:
case ARM::VLD1DUPd16wb_fixed:
case ARM::VLD1DUPd16wb_register:
case ARM::VLD1DUPd8:
case ARM::VLD1DUPd8wb_fixed:
case ARM::VLD1DUPd8wb_register:
case ARM::VLD1DUPq32:
case ARM::VLD1DUPq32wb_fixed:
case ARM::VLD1DUPq32wb_register:
case ARM::VLD1DUPq16:
case ARM::VLD1DUPq16wb_fixed:
case ARM::VLD1DUPq16wb_register:
case ARM::VLD1DUPq8:
case ARM::VLD1DUPq8wb_fixed:
case ARM::VLD1DUPq8wb_register:
case ARM::VSETLNi32:
case ARM::VSETLNi16:
case ARM::VSETLNi8:
return CondCodeIsAL(MI);
};
return false;
}
bool ARMFixCortexA57AES1742098::runOnMachineFunction(MachineFunction &F) {
LLVM_DEBUG(dbgs() << "***** ARMFixCortexA57AES1742098 *****\n");
auto &STI = F.getSubtarget<ARMSubtarget>();
if (!STI.hasAES() || !STI.fixCortexA57AES1742098())
return false;
const ARMBaseRegisterInfo *TRI = STI.getRegisterInfo();
const ARMBaseInstrInfo *TII = STI.getInstrInfo();
auto &RDA = getAnalysis<ReachingDefAnalysis>();
SmallVector<AESFixupLocation> FixupLocsForFn{};
analyzeMF(F, RDA, TRI, FixupLocsForFn);
bool Changed = false;
LLVM_DEBUG(dbgs() << "Inserting " << FixupLocsForFn.size() << " fixup(s)\n");
for (AESFixupLocation &FixupLoc : FixupLocsForFn) {
insertAESFixup(FixupLoc, TII, TRI);
Changed |= true;
}
return Changed;
}
void ARMFixCortexA57AES1742098::analyzeMF(
MachineFunction &MF, ReachingDefAnalysis &RDA,
const ARMBaseRegisterInfo *TRI,
SmallVectorImpl<AESFixupLocation> &FixupLocsForFn) const {
unsigned MaxAllowedFixups = 0;
for (MachineBasicBlock &MBB : MF) {
for (MachineInstr &MI : MBB) {
if (!isFirstAESPairInstr(MI))
continue;
LLVM_DEBUG(dbgs() << "Found AES Pair starting: " << MI);
assert(MI.getNumExplicitOperands() == 3 && MI.getNumExplicitDefs() == 1 &&
"Unknown AES Instruction Format. Expected 1 def, 2 uses.");
MaxAllowedFixups += 2;
for (MachineOperand &MOp : MI.uses()) {
SmallPtrSet<MachineInstr *, 1> AllDefs{};
RDA.getGlobalReachingDefs(&MI, MOp.getReg(), AllDefs);
AESFixupLocation NewLoc{&MBB, &MI, &MOp};
bool IsLiveIn = MF.front().isLiveIn(MOp.getReg());
if (!IsLiveIn && AllDefs.size() == 0) {
LLVM_DEBUG(dbgs()
<< "Fixup Planned: No Defining Instrs found, not live-in: "
<< printReg(MOp.getReg(), TRI) << "\n");
FixupLocsForFn.emplace_back(NewLoc);
continue;
}
auto IsUnsafe = [](MachineInstr *MI) -> bool {
return !isSafeAESInput(*MI);
};
size_t UnsafeCount = llvm::count_if(AllDefs, IsUnsafe);
if (UnsafeCount == 0) {
if (!IsLiveIn) {
LLVM_DEBUG(dbgs() << "No Fixup: Defining instrs are all safe: "
<< printReg(MOp.getReg(), TRI) << "\n");
continue;
}
LLVM_DEBUG(dbgs()
<< "Fixup Planned: Live-In (with safe defining instrs): "
<< printReg(MOp.getReg(), TRI) << "\n");
NewLoc.Block = &MF.front();
NewLoc.InsertionPt = &*NewLoc.Block->begin();
LLVM_DEBUG(dbgs() << "Moving Fixup for Live-In to immediately before "
<< *NewLoc.InsertionPt);
FixupLocsForFn.emplace_back(NewLoc);
continue;
}
if (IsLiveIn || UnsafeCount > 1) {
LLVM_DEBUG(dbgs() << "Fixup Planned: Multiple unsafe defining instrs "
"(including live-ins): "
<< printReg(MOp.getReg(), TRI) << "\n");
FixupLocsForFn.emplace_back(NewLoc);
continue;
}
assert(UnsafeCount == 1 && !IsLiveIn &&
"At this point, there should be one unsafe defining instrs "
"and the defined register should not be a live-in.");
SmallPtrSetIterator<MachineInstr *> It =
llvm::find_if(AllDefs, IsUnsafe);
assert(It != AllDefs.end() &&
"UnsafeCount == 1 but No Unsafe MachineInstr found.");
MachineInstr *DefMI = *It;
LLVM_DEBUG(
dbgs() << "Fixup Planned: Found single unsafe defining instrs for "
<< printReg(MOp.getReg(), TRI) << ": " << *DefMI);
MachineBasicBlock::iterator DefIt = DefMI;
++DefIt;
if (DefIt != DefMI->getParent()->end()) {
LLVM_DEBUG(dbgs() << "Moving Fixup to immediately after " << *DefMI
<< "And immediately before " << *DefIt);
NewLoc.Block = DefIt->getParent();
NewLoc.InsertionPt = &*DefIt;
}
FixupLocsForFn.emplace_back(NewLoc);
}
}
}
assert(FixupLocsForFn.size() <= MaxAllowedFixups &&
"Inserted too many fixups for this function.");
(void)MaxAllowedFixups;
}
void ARMFixCortexA57AES1742098::insertAESFixup(
AESFixupLocation &FixupLoc, const ARMBaseInstrInfo *TII,
const ARMBaseRegisterInfo *TRI) const {
MachineOperand *OperandToFixup = FixupLoc.MOp;
assert(OperandToFixup->isReg() && "OperandToFixup must be a register");
Register RegToFixup = OperandToFixup->getReg();
LLVM_DEBUG(dbgs() << "Inserting VORRq of " << printReg(RegToFixup, TRI)
<< " before: " << *FixupLoc.InsertionPt);
unsigned Renamable = OperandToFixup->isRenamable() ? RegState::Renamable : 0;
BuildMI(*FixupLoc.Block, FixupLoc.InsertionPt, DebugLoc(),
TII->get(ARM::VORRq))
.addReg(RegToFixup, RegState::Define | Renamable)
.addReg(RegToFixup, RegState::Kill | Renamable)
.addReg(RegToFixup, RegState::Kill | Renamable)
.addImm((uint64_t)ARMCC::AL)
.addReg(ARM::NoRegister);
}
FunctionPass *llvm::createARMFixCortexA57AES1742098Pass() {
return new ARMFixCortexA57AES1742098();
}