| //===-- PPCExpandAtomicPseudoInsts.cpp - Expand atomic pseudo instrs. -----===// |
| // |
| // 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 file contains a pass that expands atomic pseudo instructions into |
| // target instructions post RA. With such method, LL/SC loop is considered as |
| // a whole blob and make spilling unlikely happens in the LL/SC loop. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "MCTargetDesc/PPCPredicates.h" |
| #include "PPC.h" |
| #include "PPCInstrInfo.h" |
| #include "PPCTargetMachine.h" |
| |
| #include "llvm/CodeGen/LivePhysRegs.h" |
| #include "llvm/CodeGen/MachineFunctionPass.h" |
| #include "llvm/CodeGen/MachineInstrBuilder.h" |
| |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "ppc-atomic-expand" |
| |
| namespace { |
| |
| class PPCExpandAtomicPseudo : public MachineFunctionPass { |
| public: |
| const PPCInstrInfo *TII; |
| const PPCRegisterInfo *TRI; |
| static char ID; |
| |
| PPCExpandAtomicPseudo() : MachineFunctionPass(ID) { |
| initializePPCExpandAtomicPseudoPass(*PassRegistry::getPassRegistry()); |
| } |
| |
| bool runOnMachineFunction(MachineFunction &MF) override; |
| |
| private: |
| bool expandMI(MachineBasicBlock &MBB, MachineInstr &MI, |
| MachineBasicBlock::iterator &NMBBI); |
| bool expandAtomicRMW128(MachineBasicBlock &MBB, MachineInstr &MI, |
| MachineBasicBlock::iterator &NMBBI); |
| bool expandAtomicCmpSwap128(MachineBasicBlock &MBB, MachineInstr &MI, |
| MachineBasicBlock::iterator &NMBBI); |
| }; |
| |
| static void PairedCopy(const PPCInstrInfo *TII, MachineBasicBlock &MBB, |
| MachineBasicBlock::iterator MBBI, const DebugLoc &DL, |
| Register Dest0, Register Dest1, Register Src0, |
| Register Src1) { |
| const MCInstrDesc &OR = TII->get(PPC::OR8); |
| const MCInstrDesc &XOR = TII->get(PPC::XOR8); |
| if (Dest0 == Src1 && Dest1 == Src0) { |
| // The most tricky case, swapping values. |
| BuildMI(MBB, MBBI, DL, XOR, Dest0).addReg(Dest0).addReg(Dest1); |
| BuildMI(MBB, MBBI, DL, XOR, Dest1).addReg(Dest0).addReg(Dest1); |
| BuildMI(MBB, MBBI, DL, XOR, Dest0).addReg(Dest0).addReg(Dest1); |
| } else if (Dest0 != Src0 || Dest1 != Src1) { |
| if (Dest0 == Src1 || Dest1 != Src0) { |
| BuildMI(MBB, MBBI, DL, OR, Dest1).addReg(Src1).addReg(Src1); |
| BuildMI(MBB, MBBI, DL, OR, Dest0).addReg(Src0).addReg(Src0); |
| } else { |
| BuildMI(MBB, MBBI, DL, OR, Dest0).addReg(Src0).addReg(Src0); |
| BuildMI(MBB, MBBI, DL, OR, Dest1).addReg(Src1).addReg(Src1); |
| } |
| } |
| } |
| |
| bool PPCExpandAtomicPseudo::runOnMachineFunction(MachineFunction &MF) { |
| bool Changed = false; |
| TII = static_cast<const PPCInstrInfo *>(MF.getSubtarget().getInstrInfo()); |
| TRI = &TII->getRegisterInfo(); |
| for (MachineBasicBlock &MBB : MF) { |
| for (MachineBasicBlock::iterator MBBI = MBB.begin(), MBBE = MBB.end(); |
| MBBI != MBBE;) { |
| MachineInstr &MI = *MBBI; |
| MachineBasicBlock::iterator NMBBI = std::next(MBBI); |
| Changed |= expandMI(MBB, MI, NMBBI); |
| MBBI = NMBBI; |
| } |
| } |
| if (Changed) |
| MF.RenumberBlocks(); |
| return Changed; |
| } |
| |
| bool PPCExpandAtomicPseudo::expandMI(MachineBasicBlock &MBB, MachineInstr &MI, |
| MachineBasicBlock::iterator &NMBBI) { |
| switch (MI.getOpcode()) { |
| case PPC::ATOMIC_SWAP_I128: |
| case PPC::ATOMIC_LOAD_ADD_I128: |
| case PPC::ATOMIC_LOAD_SUB_I128: |
| case PPC::ATOMIC_LOAD_XOR_I128: |
| case PPC::ATOMIC_LOAD_NAND_I128: |
| case PPC::ATOMIC_LOAD_AND_I128: |
| case PPC::ATOMIC_LOAD_OR_I128: |
| return expandAtomicRMW128(MBB, MI, NMBBI); |
| case PPC::ATOMIC_CMP_SWAP_I128: |
| return expandAtomicCmpSwap128(MBB, MI, NMBBI); |
| case PPC::BUILD_QUADWORD: { |
| Register Dst = MI.getOperand(0).getReg(); |
| Register DstHi = TRI->getSubReg(Dst, PPC::sub_gp8_x0); |
| Register DstLo = TRI->getSubReg(Dst, PPC::sub_gp8_x1); |
| Register Lo = MI.getOperand(1).getReg(); |
| Register Hi = MI.getOperand(2).getReg(); |
| PairedCopy(TII, MBB, MI, MI.getDebugLoc(), DstHi, DstLo, Hi, Lo); |
| MI.eraseFromParent(); |
| return true; |
| } |
| default: |
| return false; |
| } |
| } |
| |
| bool PPCExpandAtomicPseudo::expandAtomicRMW128( |
| MachineBasicBlock &MBB, MachineInstr &MI, |
| MachineBasicBlock::iterator &NMBBI) { |
| const MCInstrDesc &LL = TII->get(PPC::LQARX); |
| const MCInstrDesc &SC = TII->get(PPC::STQCX); |
| DebugLoc DL = MI.getDebugLoc(); |
| MachineFunction *MF = MBB.getParent(); |
| const BasicBlock *BB = MBB.getBasicBlock(); |
| // Create layout of control flow. |
| MachineFunction::iterator MFI = ++MBB.getIterator(); |
| MachineBasicBlock *LoopMBB = MF->CreateMachineBasicBlock(BB); |
| MachineBasicBlock *ExitMBB = MF->CreateMachineBasicBlock(BB); |
| MF->insert(MFI, LoopMBB); |
| MF->insert(MFI, ExitMBB); |
| ExitMBB->splice(ExitMBB->begin(), &MBB, std::next(MI.getIterator()), |
| MBB.end()); |
| ExitMBB->transferSuccessorsAndUpdatePHIs(&MBB); |
| MBB.addSuccessor(LoopMBB); |
| |
| // For non-min/max operations, control flow is kinda like: |
| // MBB: |
| // ... |
| // LoopMBB: |
| // lqarx in, ptr |
| // addc out.sub_x1, in.sub_x1, op.sub_x1 |
| // adde out.sub_x0, in.sub_x0, op.sub_x0 |
| // stqcx out, ptr |
| // bne- LoopMBB |
| // ExitMBB: |
| // ... |
| Register Old = MI.getOperand(0).getReg(); |
| Register OldHi = TRI->getSubReg(Old, PPC::sub_gp8_x0); |
| Register OldLo = TRI->getSubReg(Old, PPC::sub_gp8_x1); |
| Register Scratch = MI.getOperand(1).getReg(); |
| Register ScratchHi = TRI->getSubReg(Scratch, PPC::sub_gp8_x0); |
| Register ScratchLo = TRI->getSubReg(Scratch, PPC::sub_gp8_x1); |
| Register RA = MI.getOperand(2).getReg(); |
| Register RB = MI.getOperand(3).getReg(); |
| Register IncrLo = MI.getOperand(4).getReg(); |
| Register IncrHi = MI.getOperand(5).getReg(); |
| unsigned RMWOpcode = MI.getOpcode(); |
| |
| MachineBasicBlock *CurrentMBB = LoopMBB; |
| BuildMI(CurrentMBB, DL, LL, Old).addReg(RA).addReg(RB); |
| |
| switch (RMWOpcode) { |
| case PPC::ATOMIC_SWAP_I128: |
| PairedCopy(TII, *CurrentMBB, CurrentMBB->end(), DL, ScratchHi, ScratchLo, |
| IncrHi, IncrLo); |
| break; |
| case PPC::ATOMIC_LOAD_ADD_I128: |
| BuildMI(CurrentMBB, DL, TII->get(PPC::ADDC8), ScratchLo) |
| .addReg(IncrLo) |
| .addReg(OldLo); |
| BuildMI(CurrentMBB, DL, TII->get(PPC::ADDE8), ScratchHi) |
| .addReg(IncrHi) |
| .addReg(OldHi); |
| break; |
| case PPC::ATOMIC_LOAD_SUB_I128: |
| BuildMI(CurrentMBB, DL, TII->get(PPC::SUBFC8), ScratchLo) |
| .addReg(IncrLo) |
| .addReg(OldLo); |
| BuildMI(CurrentMBB, DL, TII->get(PPC::SUBFE8), ScratchHi) |
| .addReg(IncrHi) |
| .addReg(OldHi); |
| break; |
| |
| #define TRIVIAL_ATOMICRMW(Opcode, Instr) \ |
| case Opcode: \ |
| BuildMI(CurrentMBB, DL, TII->get((Instr)), ScratchLo) \ |
| .addReg(IncrLo) \ |
| .addReg(OldLo); \ |
| BuildMI(CurrentMBB, DL, TII->get((Instr)), ScratchHi) \ |
| .addReg(IncrHi) \ |
| .addReg(OldHi); \ |
| break |
| |
| TRIVIAL_ATOMICRMW(PPC::ATOMIC_LOAD_OR_I128, PPC::OR8); |
| TRIVIAL_ATOMICRMW(PPC::ATOMIC_LOAD_XOR_I128, PPC::XOR8); |
| TRIVIAL_ATOMICRMW(PPC::ATOMIC_LOAD_AND_I128, PPC::AND8); |
| TRIVIAL_ATOMICRMW(PPC::ATOMIC_LOAD_NAND_I128, PPC::NAND8); |
| #undef TRIVIAL_ATOMICRMW |
| default: |
| llvm_unreachable("Unhandled atomic RMW operation"); |
| } |
| BuildMI(CurrentMBB, DL, SC).addReg(Scratch).addReg(RA).addReg(RB); |
| BuildMI(CurrentMBB, DL, TII->get(PPC::BCC)) |
| .addImm(PPC::PRED_NE) |
| .addReg(PPC::CR0) |
| .addMBB(LoopMBB); |
| CurrentMBB->addSuccessor(LoopMBB); |
| CurrentMBB->addSuccessor(ExitMBB); |
| recomputeLiveIns(*LoopMBB); |
| recomputeLiveIns(*ExitMBB); |
| NMBBI = MBB.end(); |
| MI.eraseFromParent(); |
| return true; |
| } |
| |
| bool PPCExpandAtomicPseudo::expandAtomicCmpSwap128( |
| MachineBasicBlock &MBB, MachineInstr &MI, |
| MachineBasicBlock::iterator &NMBBI) { |
| const MCInstrDesc &LL = TII->get(PPC::LQARX); |
| const MCInstrDesc &SC = TII->get(PPC::STQCX); |
| DebugLoc DL = MI.getDebugLoc(); |
| MachineFunction *MF = MBB.getParent(); |
| const BasicBlock *BB = MBB.getBasicBlock(); |
| Register Old = MI.getOperand(0).getReg(); |
| Register OldHi = TRI->getSubReg(Old, PPC::sub_gp8_x0); |
| Register OldLo = TRI->getSubReg(Old, PPC::sub_gp8_x1); |
| Register Scratch = MI.getOperand(1).getReg(); |
| Register ScratchHi = TRI->getSubReg(Scratch, PPC::sub_gp8_x0); |
| Register ScratchLo = TRI->getSubReg(Scratch, PPC::sub_gp8_x1); |
| Register RA = MI.getOperand(2).getReg(); |
| Register RB = MI.getOperand(3).getReg(); |
| Register CmpLo = MI.getOperand(4).getReg(); |
| Register CmpHi = MI.getOperand(5).getReg(); |
| Register NewLo = MI.getOperand(6).getReg(); |
| Register NewHi = MI.getOperand(7).getReg(); |
| // Create layout of control flow. |
| // loop: |
| // old = lqarx ptr |
| // <compare old, cmp> |
| // bne 0, fail |
| // succ: |
| // stqcx new ptr |
| // bne 0, loop |
| // b exit |
| // fail: |
| // stqcx old ptr |
| // exit: |
| // .... |
| MachineFunction::iterator MFI = ++MBB.getIterator(); |
| MachineBasicBlock *LoopCmpMBB = MF->CreateMachineBasicBlock(BB); |
| MachineBasicBlock *CmpSuccMBB = MF->CreateMachineBasicBlock(BB); |
| MachineBasicBlock *CmpFailMBB = MF->CreateMachineBasicBlock(BB); |
| MachineBasicBlock *ExitMBB = MF->CreateMachineBasicBlock(BB); |
| MF->insert(MFI, LoopCmpMBB); |
| MF->insert(MFI, CmpSuccMBB); |
| MF->insert(MFI, CmpFailMBB); |
| MF->insert(MFI, ExitMBB); |
| ExitMBB->splice(ExitMBB->begin(), &MBB, std::next(MI.getIterator()), |
| MBB.end()); |
| ExitMBB->transferSuccessorsAndUpdatePHIs(&MBB); |
| MBB.addSuccessor(LoopCmpMBB); |
| // Build loop. |
| MachineBasicBlock *CurrentMBB = LoopCmpMBB; |
| BuildMI(CurrentMBB, DL, LL, Old).addReg(RA).addReg(RB); |
| BuildMI(CurrentMBB, DL, TII->get(PPC::XOR8), ScratchLo) |
| .addReg(OldLo) |
| .addReg(CmpLo); |
| BuildMI(CurrentMBB, DL, TII->get(PPC::XOR8), ScratchHi) |
| .addReg(OldHi) |
| .addReg(CmpHi); |
| BuildMI(CurrentMBB, DL, TII->get(PPC::OR8_rec), ScratchLo) |
| .addReg(ScratchLo) |
| .addReg(ScratchHi); |
| BuildMI(CurrentMBB, DL, TII->get(PPC::BCC)) |
| .addImm(PPC::PRED_NE) |
| .addReg(PPC::CR0) |
| .addMBB(CmpFailMBB); |
| CurrentMBB->addSuccessor(CmpSuccMBB); |
| CurrentMBB->addSuccessor(CmpFailMBB); |
| // Build succ. |
| CurrentMBB = CmpSuccMBB; |
| PairedCopy(TII, *CurrentMBB, CurrentMBB->end(), DL, ScratchHi, ScratchLo, |
| NewHi, NewLo); |
| BuildMI(CurrentMBB, DL, SC).addReg(Scratch).addReg(RA).addReg(RB); |
| BuildMI(CurrentMBB, DL, TII->get(PPC::BCC)) |
| .addImm(PPC::PRED_NE) |
| .addReg(PPC::CR0) |
| .addMBB(LoopCmpMBB); |
| BuildMI(CurrentMBB, DL, TII->get(PPC::B)).addMBB(ExitMBB); |
| CurrentMBB->addSuccessor(LoopCmpMBB); |
| CurrentMBB->addSuccessor(ExitMBB); |
| CurrentMBB = CmpFailMBB; |
| BuildMI(CurrentMBB, DL, SC).addReg(Old).addReg(RA).addReg(RB); |
| CurrentMBB->addSuccessor(ExitMBB); |
| |
| recomputeLiveIns(*LoopCmpMBB); |
| recomputeLiveIns(*CmpSuccMBB); |
| recomputeLiveIns(*CmpFailMBB); |
| recomputeLiveIns(*ExitMBB); |
| NMBBI = MBB.end(); |
| MI.eraseFromParent(); |
| return true; |
| } |
| |
| } // namespace |
| |
| INITIALIZE_PASS(PPCExpandAtomicPseudo, DEBUG_TYPE, "PowerPC Expand Atomic", |
| false, false) |
| |
| char PPCExpandAtomicPseudo::ID = 0; |
| FunctionPass *llvm::createPPCExpandAtomicPseudoPass() { |
| return new PPCExpandAtomicPseudo(); |
| } |