| //===-------------- BPFMIChecking.cpp - MI Checking Legality -------------===// |
| // |
| // 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 performs checking to signal errors for certain illegal usages at |
| // MachineInstruction layer. Specially, the result of XADD{32,64} insn should |
| // not be used. The pass is done at the PreEmit pass right before the |
| // machine code is emitted at which point the register liveness information |
| // is still available. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "BPF.h" |
| #include "BPFInstrInfo.h" |
| #include "BPFTargetMachine.h" |
| #include "llvm/CodeGen/MachineInstrBuilder.h" |
| #include "llvm/CodeGen/MachineRegisterInfo.h" |
| #include "llvm/Support/Debug.h" |
| |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "bpf-mi-checking" |
| |
| namespace { |
| |
| struct BPFMIPreEmitChecking : public MachineFunctionPass { |
| |
| static char ID; |
| MachineFunction *MF; |
| const TargetRegisterInfo *TRI; |
| |
| BPFMIPreEmitChecking() : MachineFunctionPass(ID) { |
| initializeBPFMIPreEmitCheckingPass(*PassRegistry::getPassRegistry()); |
| } |
| |
| private: |
| // Initialize class variables. |
| void initialize(MachineFunction &MFParm); |
| |
| bool processAtomicInsts(void); |
| |
| public: |
| |
| // Main entry point for this pass. |
| bool runOnMachineFunction(MachineFunction &MF) override { |
| if (!skipFunction(MF.getFunction())) { |
| initialize(MF); |
| return processAtomicInsts(); |
| } |
| return false; |
| } |
| }; |
| |
| // Initialize class variables. |
| void BPFMIPreEmitChecking::initialize(MachineFunction &MFParm) { |
| MF = &MFParm; |
| TRI = MF->getSubtarget<BPFSubtarget>().getRegisterInfo(); |
| LLVM_DEBUG(dbgs() << "*** BPF PreEmit checking pass ***\n\n"); |
| } |
| |
| // Make sure all Defs of XADD are dead, meaning any result of XADD insn is not |
| // used. |
| // |
| // NOTE: BPF backend hasn't enabled sub-register liveness track, so when the |
| // source and destination operands of XADD are GPR32, there is no sub-register |
| // dead info. If we rely on the generic MachineInstr::allDefsAreDead, then we |
| // will raise false alarm on GPR32 Def. |
| // |
| // To support GPR32 Def, ideally we could just enable sub-registr liveness track |
| // on BPF backend, then allDefsAreDead could work on GPR32 Def. This requires |
| // implementing TargetSubtargetInfo::enableSubRegLiveness on BPF. |
| // |
| // However, sub-register liveness tracking module inside LLVM is actually |
| // designed for the situation where one register could be split into more than |
| // one sub-registers for which case each sub-register could have their own |
| // liveness and kill one of them doesn't kill others. So, tracking liveness for |
| // each make sense. |
| // |
| // For BPF, each 64-bit register could only have one 32-bit sub-register. This |
| // is exactly the case which LLVM think brings no benefits for doing |
| // sub-register tracking, because the live range of sub-register must always |
| // equal to its parent register, therefore liveness tracking is disabled even |
| // the back-end has implemented enableSubRegLiveness. The detailed information |
| // is at r232695: |
| // |
| // Author: Matthias Braun <matze@braunis.de> |
| // Date: Thu Mar 19 00:21:58 2015 +0000 |
| // Do not track subregister liveness when it brings no benefits |
| // |
| // Hence, for BPF, we enhance MachineInstr::allDefsAreDead. Given the solo |
| // sub-register always has the same liveness as its parent register, LLVM is |
| // already attaching a implicit 64-bit register Def whenever the there is |
| // a sub-register Def. The liveness of the implicit 64-bit Def is available. |
| // For example, for "lock *(u32 *)(r0 + 4) += w9", the MachineOperand info could |
| // be: |
| // |
| // $w9 = XADDW32 killed $r0, 4, $w9(tied-def 0), |
| // implicit killed $r9, implicit-def dead $r9 |
| // |
| // Even though w9 is not marked as Dead, the parent register r9 is marked as |
| // Dead correctly, and it is safe to use such information or our purpose. |
| static bool hasLiveDefs(const MachineInstr &MI, const TargetRegisterInfo *TRI) { |
| const MCRegisterClass *GPR64RegClass = |
| &BPFMCRegisterClasses[BPF::GPRRegClassID]; |
| std::vector<unsigned> GPR32LiveDefs; |
| std::vector<unsigned> GPR64DeadDefs; |
| |
| for (const MachineOperand &MO : MI.operands()) { |
| bool RegIsGPR64; |
| |
| if (!MO.isReg() || MO.isUse()) |
| continue; |
| |
| RegIsGPR64 = GPR64RegClass->contains(MO.getReg()); |
| if (!MO.isDead()) { |
| // It is a GPR64 live Def, we are sure it is live. */ |
| if (RegIsGPR64) |
| return true; |
| // It is a GPR32 live Def, we are unsure whether it is really dead due to |
| // no sub-register liveness tracking. Push it to vector for deferred |
| // check. |
| GPR32LiveDefs.push_back(MO.getReg()); |
| continue; |
| } |
| |
| // Record any GPR64 dead Def as some unmarked GPR32 could be alias of its |
| // low 32-bit. |
| if (RegIsGPR64) |
| GPR64DeadDefs.push_back(MO.getReg()); |
| } |
| |
| // No GPR32 live Def, safe to return false. |
| if (GPR32LiveDefs.empty()) |
| return false; |
| |
| // No GPR64 dead Def, so all those GPR32 live Def can't have alias, therefore |
| // must be truely live, safe to return true. |
| if (GPR64DeadDefs.empty()) |
| return true; |
| |
| // Otherwise, return true if any aliased SuperReg of GPR32 is not dead. |
| for (auto I : GPR32LiveDefs) |
| for (MCSuperRegIterator SR(I, TRI); SR.isValid(); ++SR) |
| if (!llvm::is_contained(GPR64DeadDefs, *SR)) |
| return true; |
| |
| return false; |
| } |
| |
| bool BPFMIPreEmitChecking::processAtomicInsts(void) { |
| for (MachineBasicBlock &MBB : *MF) { |
| for (MachineInstr &MI : MBB) { |
| if (MI.getOpcode() != BPF::XADDW && |
| MI.getOpcode() != BPF::XADDD && |
| MI.getOpcode() != BPF::XADDW32) |
| continue; |
| |
| LLVM_DEBUG(MI.dump()); |
| if (hasLiveDefs(MI, TRI)) { |
| DebugLoc Empty; |
| const DebugLoc &DL = MI.getDebugLoc(); |
| if (DL != Empty) |
| report_fatal_error(Twine("line ") + std::to_string(DL.getLine()) + |
| ": Invalid usage of the XADD return value", false); |
| else |
| report_fatal_error("Invalid usage of the XADD return value", false); |
| } |
| } |
| } |
| |
| // Check return values of atomic_fetch_and_{add,and,or,xor}. |
| // If the return is not used, the atomic_fetch_and_<op> instruction |
| // is replaced with atomic_<op> instruction. |
| MachineInstr *ToErase = nullptr; |
| bool Changed = false; |
| const BPFInstrInfo *TII = MF->getSubtarget<BPFSubtarget>().getInstrInfo(); |
| for (MachineBasicBlock &MBB : *MF) { |
| for (MachineInstr &MI : MBB) { |
| if (ToErase) { |
| ToErase->eraseFromParent(); |
| ToErase = nullptr; |
| } |
| |
| if (MI.getOpcode() != BPF::XFADDW32 && MI.getOpcode() != BPF::XFADDD && |
| MI.getOpcode() != BPF::XFANDW32 && MI.getOpcode() != BPF::XFANDD && |
| MI.getOpcode() != BPF::XFXORW32 && MI.getOpcode() != BPF::XFXORD && |
| MI.getOpcode() != BPF::XFORW32 && MI.getOpcode() != BPF::XFORD) |
| continue; |
| |
| if (hasLiveDefs(MI, TRI)) |
| continue; |
| |
| LLVM_DEBUG(dbgs() << "Transforming "; MI.dump()); |
| unsigned newOpcode; |
| switch (MI.getOpcode()) { |
| case BPF::XFADDW32: |
| newOpcode = BPF::XADDW32; |
| break; |
| case BPF::XFADDD: |
| newOpcode = BPF::XADDD; |
| break; |
| case BPF::XFANDW32: |
| newOpcode = BPF::XANDW32; |
| break; |
| case BPF::XFANDD: |
| newOpcode = BPF::XANDD; |
| break; |
| case BPF::XFXORW32: |
| newOpcode = BPF::XXORW32; |
| break; |
| case BPF::XFXORD: |
| newOpcode = BPF::XXORD; |
| break; |
| case BPF::XFORW32: |
| newOpcode = BPF::XORW32; |
| break; |
| case BPF::XFORD: |
| newOpcode = BPF::XORD; |
| break; |
| default: |
| llvm_unreachable("Incorrect Atomic Instruction Opcode"); |
| } |
| |
| BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(newOpcode)) |
| .add(MI.getOperand(0)) |
| .add(MI.getOperand(1)) |
| .add(MI.getOperand(2)) |
| .add(MI.getOperand(3)); |
| |
| ToErase = &MI; |
| Changed = true; |
| } |
| } |
| |
| return Changed; |
| } |
| |
| } // end default namespace |
| |
| INITIALIZE_PASS(BPFMIPreEmitChecking, "bpf-mi-pemit-checking", |
| "BPF PreEmit Checking", false, false) |
| |
| char BPFMIPreEmitChecking::ID = 0; |
| FunctionPass* llvm::createBPFMIPreEmitCheckingPass() |
| { |
| return new BPFMIPreEmitChecking(); |
| } |