//===-- MipsExpandPseudoInsts.cpp - Expand pseudo instructions ------------===// // // 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 pseudo instructions into target // instructions to allow proper scheduling, if-conversion, and other late // optimizations. This pass should be run after register allocation but before // the post-regalloc scheduling pass. // // This is currently only used for expanding atomic pseudos after register // allocation. We do this to avoid the fast register allocator introducing // spills between ll and sc. These stores cause some MIPS implementations to // abort the atomic RMW sequence. // //===----------------------------------------------------------------------===// #include "Mips.h" #include "MipsInstrInfo.h" #include "MipsSubtarget.h" #include "llvm/CodeGen/LivePhysRegs.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" using namespace llvm; #define DEBUG_TYPE "mips-pseudo" namespace { class MipsExpandPseudo : public MachineFunctionPass { public: static char ID; MipsExpandPseudo() : MachineFunctionPass(ID) {} const MipsInstrInfo *TII; const MipsSubtarget *STI; bool runOnMachineFunction(MachineFunction &Fn) override; MachineFunctionProperties getRequiredProperties() const override { return MachineFunctionProperties().set( MachineFunctionProperties::Property::NoVRegs); } StringRef getPassName() const override { return "Mips pseudo instruction expansion pass"; } private: bool expandAtomicCmpSwap(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, MachineBasicBlock::iterator &NextMBBI); bool expandAtomicCmpSwapSubword(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, MachineBasicBlock::iterator &NextMBBI); bool expandAtomicBinOp(MachineBasicBlock &BB, MachineBasicBlock::iterator I, MachineBasicBlock::iterator &NMBBI, unsigned Size); bool expandAtomicBinOpSubword(MachineBasicBlock &BB, MachineBasicBlock::iterator I, MachineBasicBlock::iterator &NMBBI); bool expandMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, MachineBasicBlock::iterator &NMBB); bool expandMBB(MachineBasicBlock &MBB); }; char MipsExpandPseudo::ID = 0; } bool MipsExpandPseudo::expandAtomicCmpSwapSubword( MachineBasicBlock &BB, MachineBasicBlock::iterator I, MachineBasicBlock::iterator &NMBBI) { MachineFunction *MF = BB.getParent(); const bool ArePtrs64bit = STI->getABI().ArePtrs64bit(); DebugLoc DL = I->getDebugLoc(); unsigned LL, SC; unsigned ZERO = Mips::ZERO; unsigned BNE = Mips::BNE; unsigned BEQ = Mips::BEQ; unsigned SEOp = I->getOpcode() == Mips::ATOMIC_CMP_SWAP_I8_POSTRA ? Mips::SEB : Mips::SEH; if (STI->inMicroMipsMode()) { LL = STI->hasMips32r6() ? Mips::LL_MMR6 : Mips::LL_MM; SC = STI->hasMips32r6() ? Mips::SC_MMR6 : Mips::SC_MM; BNE = STI->hasMips32r6() ? Mips::BNEC_MMR6 : Mips::BNE_MM; BEQ = STI->hasMips32r6() ? Mips::BEQC_MMR6 : Mips::BEQ_MM; } else { LL = STI->hasMips32r6() ? (ArePtrs64bit ? Mips::LL64_R6 : Mips::LL_R6) : (ArePtrs64bit ? Mips::LL64 : Mips::LL); SC = STI->hasMips32r6() ? (ArePtrs64bit ? Mips::SC64_R6 : Mips::SC_R6) : (ArePtrs64bit ? Mips::SC64 : Mips::SC); } Register Dest = I->getOperand(0).getReg(); Register Ptr = I->getOperand(1).getReg(); Register Mask = I->getOperand(2).getReg(); Register ShiftCmpVal = I->getOperand(3).getReg(); Register Mask2 = I->getOperand(4).getReg(); Register ShiftNewVal = I->getOperand(5).getReg(); Register ShiftAmnt = I->getOperand(6).getReg(); Register Scratch = I->getOperand(7).getReg(); Register Scratch2 = I->getOperand(8).getReg(); // insert new blocks after the current block const BasicBlock *LLVM_BB = BB.getBasicBlock(); MachineBasicBlock *loop1MBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineBasicBlock *loop2MBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineBasicBlock *sinkMBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineFunction::iterator It = ++BB.getIterator(); MF->insert(It, loop1MBB); MF->insert(It, loop2MBB); MF->insert(It, sinkMBB); MF->insert(It, exitMBB); // Transfer the remainder of BB and its successor edges to exitMBB. exitMBB->splice(exitMBB->begin(), &BB, std::next(MachineBasicBlock::iterator(I)), BB.end()); exitMBB->transferSuccessorsAndUpdatePHIs(&BB); // thisMBB: // ... // fallthrough --> loop1MBB BB.addSuccessor(loop1MBB, BranchProbability::getOne()); loop1MBB->addSuccessor(sinkMBB); loop1MBB->addSuccessor(loop2MBB); loop1MBB->normalizeSuccProbs(); loop2MBB->addSuccessor(loop1MBB); loop2MBB->addSuccessor(sinkMBB); loop2MBB->normalizeSuccProbs(); sinkMBB->addSuccessor(exitMBB, BranchProbability::getOne()); // loop1MBB: // ll dest, 0(ptr) // and Mask', dest, Mask // bne Mask', ShiftCmpVal, exitMBB BuildMI(loop1MBB, DL, TII->get(LL), Scratch).addReg(Ptr).addImm(0); BuildMI(loop1MBB, DL, TII->get(Mips::AND), Scratch2) .addReg(Scratch) .addReg(Mask); BuildMI(loop1MBB, DL, TII->get(BNE)) .addReg(Scratch2).addReg(ShiftCmpVal).addMBB(sinkMBB); // loop2MBB: // and dest, dest, mask2 // or dest, dest, ShiftNewVal // sc dest, dest, 0(ptr) // beq dest, $0, loop1MBB BuildMI(loop2MBB, DL, TII->get(Mips::AND), Scratch) .addReg(Scratch, RegState::Kill) .addReg(Mask2); BuildMI(loop2MBB, DL, TII->get(Mips::OR), Scratch) .addReg(Scratch, RegState::Kill) .addReg(ShiftNewVal); BuildMI(loop2MBB, DL, TII->get(SC), Scratch) .addReg(Scratch, RegState::Kill) .addReg(Ptr) .addImm(0); BuildMI(loop2MBB, DL, TII->get(BEQ)) .addReg(Scratch, RegState::Kill) .addReg(ZERO) .addMBB(loop1MBB); // sinkMBB: // srl srlres, Mask', shiftamt // sign_extend dest,srlres BuildMI(sinkMBB, DL, TII->get(Mips::SRLV), Dest) .addReg(Scratch2) .addReg(ShiftAmnt); if (STI->hasMips32r2()) { BuildMI(sinkMBB, DL, TII->get(SEOp), Dest).addReg(Dest); } else { const unsigned ShiftImm = I->getOpcode() == Mips::ATOMIC_CMP_SWAP_I16_POSTRA ? 16 : 24; BuildMI(sinkMBB, DL, TII->get(Mips::SLL), Dest) .addReg(Dest, RegState::Kill) .addImm(ShiftImm); BuildMI(sinkMBB, DL, TII->get(Mips::SRA), Dest) .addReg(Dest, RegState::Kill) .addImm(ShiftImm); } LivePhysRegs LiveRegs; computeAndAddLiveIns(LiveRegs, *loop1MBB); computeAndAddLiveIns(LiveRegs, *loop2MBB); computeAndAddLiveIns(LiveRegs, *sinkMBB); computeAndAddLiveIns(LiveRegs, *exitMBB); NMBBI = BB.end(); I->eraseFromParent(); return true; } bool MipsExpandPseudo::expandAtomicCmpSwap(MachineBasicBlock &BB, MachineBasicBlock::iterator I, MachineBasicBlock::iterator &NMBBI) { const unsigned Size = I->getOpcode() == Mips::ATOMIC_CMP_SWAP_I32_POSTRA ? 4 : 8; MachineFunction *MF = BB.getParent(); const bool ArePtrs64bit = STI->getABI().ArePtrs64bit(); DebugLoc DL = I->getDebugLoc(); unsigned LL, SC, ZERO, BNE, BEQ, MOVE; if (Size == 4) { if (STI->inMicroMipsMode()) { LL = STI->hasMips32r6() ? Mips::LL_MMR6 : Mips::LL_MM; SC = STI->hasMips32r6() ? Mips::SC_MMR6 : Mips::SC_MM; BNE = STI->hasMips32r6() ? Mips::BNEC_MMR6 : Mips::BNE_MM; BEQ = STI->hasMips32r6() ? Mips::BEQC_MMR6 : Mips::BEQ_MM; } else { LL = STI->hasMips32r6() ? (ArePtrs64bit ? Mips::LL64_R6 : Mips::LL_R6) : (ArePtrs64bit ? Mips::LL64 : Mips::LL); SC = STI->hasMips32r6() ? (ArePtrs64bit ? Mips::SC64_R6 : Mips::SC_R6) : (ArePtrs64bit ? Mips::SC64 : Mips::SC); BNE = Mips::BNE; BEQ = Mips::BEQ; } ZERO = Mips::ZERO; MOVE = Mips::OR; } else { LL = STI->hasMips64r6() ? Mips::LLD_R6 : Mips::LLD; SC = STI->hasMips64r6() ? Mips::SCD_R6 : Mips::SCD; ZERO = Mips::ZERO_64; BNE = Mips::BNE64; BEQ = Mips::BEQ64; MOVE = Mips::OR64; } Register Dest = I->getOperand(0).getReg(); Register Ptr = I->getOperand(1).getReg(); Register OldVal = I->getOperand(2).getReg(); Register NewVal = I->getOperand(3).getReg(); Register Scratch = I->getOperand(4).getReg(); // insert new blocks after the current block const BasicBlock *LLVM_BB = BB.getBasicBlock(); MachineBasicBlock *loop1MBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineBasicBlock *loop2MBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineFunction::iterator It = ++BB.getIterator(); MF->insert(It, loop1MBB); MF->insert(It, loop2MBB); MF->insert(It, exitMBB); // Transfer the remainder of BB and its successor edges to exitMBB. exitMBB->splice(exitMBB->begin(), &BB, std::next(MachineBasicBlock::iterator(I)), BB.end()); exitMBB->transferSuccessorsAndUpdatePHIs(&BB); // thisMBB: // ... // fallthrough --> loop1MBB BB.addSuccessor(loop1MBB, BranchProbability::getOne()); loop1MBB->addSuccessor(exitMBB); loop1MBB->addSuccessor(loop2MBB); loop1MBB->normalizeSuccProbs(); loop2MBB->addSuccessor(loop1MBB); loop2MBB->addSuccessor(exitMBB); loop2MBB->normalizeSuccProbs(); // loop1MBB: // ll dest, 0(ptr) // bne dest, oldval, exitMBB BuildMI(loop1MBB, DL, TII->get(LL), Dest).addReg(Ptr).addImm(0); BuildMI(loop1MBB, DL, TII->get(BNE)) .addReg(Dest, RegState::Kill).addReg(OldVal).addMBB(exitMBB); // loop2MBB: // move scratch, NewVal // sc Scratch, Scratch, 0(ptr) // beq Scratch, $0, loop1MBB BuildMI(loop2MBB, DL, TII->get(MOVE), Scratch).addReg(NewVal).addReg(ZERO); BuildMI(loop2MBB, DL, TII->get(SC), Scratch) .addReg(Scratch).addReg(Ptr).addImm(0); BuildMI(loop2MBB, DL, TII->get(BEQ)) .addReg(Scratch, RegState::Kill).addReg(ZERO).addMBB(loop1MBB); LivePhysRegs LiveRegs; computeAndAddLiveIns(LiveRegs, *loop1MBB); computeAndAddLiveIns(LiveRegs, *loop2MBB); computeAndAddLiveIns(LiveRegs, *exitMBB); NMBBI = BB.end(); I->eraseFromParent(); return true; } bool MipsExpandPseudo::expandAtomicBinOpSubword( MachineBasicBlock &BB, MachineBasicBlock::iterator I, MachineBasicBlock::iterator &NMBBI) { MachineFunction *MF = BB.getParent(); const bool ArePtrs64bit = STI->getABI().ArePtrs64bit(); DebugLoc DL = I->getDebugLoc(); unsigned LL, SC, SLT, SLTu, OR, MOVN, MOVZ, SELNEZ, SELEQZ; unsigned BEQ = Mips::BEQ; unsigned SEOp = Mips::SEH; if (STI->inMicroMipsMode()) { LL = STI->hasMips32r6() ? Mips::LL_MMR6 : Mips::LL_MM; SC = STI->hasMips32r6() ? Mips::SC_MMR6 : Mips::SC_MM; BEQ = STI->hasMips32r6() ? Mips::BEQC_MMR6 : Mips::BEQ_MM; SLT = Mips::SLT_MM; SLTu = Mips::SLTu_MM; OR = STI->hasMips32r6() ? Mips::OR_MMR6 : Mips::OR_MM; MOVN = Mips::MOVN_I_MM; MOVZ = Mips::MOVZ_I_MM; SELNEZ = STI->hasMips32r6() ? Mips::SELNEZ_MMR6 : Mips::SELNEZ; SELEQZ = STI->hasMips32r6() ? Mips::SELEQZ_MMR6 : Mips::SELEQZ; } else { LL = STI->hasMips32r6() ? (ArePtrs64bit ? Mips::LL64_R6 : Mips::LL_R6) : (ArePtrs64bit ? Mips::LL64 : Mips::LL); SC = STI->hasMips32r6() ? (ArePtrs64bit ? Mips::SC64_R6 : Mips::SC_R6) : (ArePtrs64bit ? Mips::SC64 : Mips::SC); SLT = Mips::SLT; SLTu = Mips::SLTu; OR = Mips::OR; MOVN = Mips::MOVN_I_I; MOVZ = Mips::MOVZ_I_I; SELNEZ = Mips::SELNEZ; SELEQZ = Mips::SELEQZ; } bool IsSwap = false; bool IsNand = false; bool IsMin = false; bool IsMax = false; bool IsUnsigned = false; unsigned Opcode = 0; switch (I->getOpcode()) { case Mips::ATOMIC_LOAD_NAND_I8_POSTRA: SEOp = Mips::SEB; [[fallthrough]]; case Mips::ATOMIC_LOAD_NAND_I16_POSTRA: IsNand = true; break; case Mips::ATOMIC_SWAP_I8_POSTRA: SEOp = Mips::SEB; [[fallthrough]]; case Mips::ATOMIC_SWAP_I16_POSTRA: IsSwap = true; break; case Mips::ATOMIC_LOAD_ADD_I8_POSTRA: SEOp = Mips::SEB; [[fallthrough]]; case Mips::ATOMIC_LOAD_ADD_I16_POSTRA: Opcode = Mips::ADDu; break; case Mips::ATOMIC_LOAD_SUB_I8_POSTRA: SEOp = Mips::SEB; [[fallthrough]]; case Mips::ATOMIC_LOAD_SUB_I16_POSTRA: Opcode = Mips::SUBu; break; case Mips::ATOMIC_LOAD_AND_I8_POSTRA: SEOp = Mips::SEB; [[fallthrough]]; case Mips::ATOMIC_LOAD_AND_I16_POSTRA: Opcode = Mips::AND; break; case Mips::ATOMIC_LOAD_OR_I8_POSTRA: SEOp = Mips::SEB; [[fallthrough]]; case Mips::ATOMIC_LOAD_OR_I16_POSTRA: Opcode = Mips::OR; break; case Mips::ATOMIC_LOAD_XOR_I8_POSTRA: SEOp = Mips::SEB; [[fallthrough]]; case Mips::ATOMIC_LOAD_XOR_I16_POSTRA: Opcode = Mips::XOR; break; case Mips::ATOMIC_LOAD_UMIN_I8_POSTRA: case Mips::ATOMIC_LOAD_UMIN_I16_POSTRA: IsUnsigned = true; [[fallthrough]]; case Mips::ATOMIC_LOAD_MIN_I8_POSTRA: case Mips::ATOMIC_LOAD_MIN_I16_POSTRA: IsMin = true; break; case Mips::ATOMIC_LOAD_UMAX_I8_POSTRA: case Mips::ATOMIC_LOAD_UMAX_I16_POSTRA: IsUnsigned = true; [[fallthrough]]; case Mips::ATOMIC_LOAD_MAX_I8_POSTRA: case Mips::ATOMIC_LOAD_MAX_I16_POSTRA: IsMax = true; break; default: llvm_unreachable("Unknown subword atomic pseudo for expansion!"); } Register Dest = I->getOperand(0).getReg(); Register Ptr = I->getOperand(1).getReg(); Register Incr = I->getOperand(2).getReg(); Register Mask = I->getOperand(3).getReg(); Register Mask2 = I->getOperand(4).getReg(); Register ShiftAmnt = I->getOperand(5).getReg(); Register OldVal = I->getOperand(6).getReg(); Register BinOpRes = I->getOperand(7).getReg(); Register StoreVal = I->getOperand(8).getReg(); const BasicBlock *LLVM_BB = BB.getBasicBlock(); MachineBasicBlock *loopMBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineBasicBlock *sinkMBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineFunction::iterator It = ++BB.getIterator(); MF->insert(It, loopMBB); MF->insert(It, sinkMBB); MF->insert(It, exitMBB); exitMBB->splice(exitMBB->begin(), &BB, std::next(I), BB.end()); exitMBB->transferSuccessorsAndUpdatePHIs(&BB); BB.addSuccessor(loopMBB, BranchProbability::getOne()); loopMBB->addSuccessor(sinkMBB); loopMBB->addSuccessor(loopMBB); loopMBB->normalizeSuccProbs(); BuildMI(loopMBB, DL, TII->get(LL), OldVal).addReg(Ptr).addImm(0); if (IsNand) { // and andres, oldval, incr2 // nor binopres, $0, andres // and newval, binopres, mask BuildMI(loopMBB, DL, TII->get(Mips::AND), BinOpRes) .addReg(OldVal) .addReg(Incr); BuildMI(loopMBB, DL, TII->get(Mips::NOR), BinOpRes) .addReg(Mips::ZERO) .addReg(BinOpRes); BuildMI(loopMBB, DL, TII->get(Mips::AND), BinOpRes) .addReg(BinOpRes) .addReg(Mask); } else if (IsMin || IsMax) { assert(I->getNumOperands() == 10 && "Atomics min|max|umin|umax use an additional register"); Register Scratch4 = I->getOperand(9).getReg(); unsigned SLTScratch4 = IsUnsigned ? SLTu : SLT; unsigned SELIncr = IsMax ? SELNEZ : SELEQZ; unsigned SELOldVal = IsMax ? SELEQZ : SELNEZ; unsigned MOVIncr = IsMax ? MOVN : MOVZ; // For little endian we need to clear uninterested bits. if (STI->isLittle()) { // and OldVal, OldVal, Mask // and Incr, Incr, Mask BuildMI(loopMBB, DL, TII->get(Mips::AND), OldVal) .addReg(OldVal) .addReg(Mask); BuildMI(loopMBB, DL, TII->get(Mips::AND), Incr).addReg(Incr).addReg(Mask); } // unsigned: sltu Scratch4, oldVal, Incr // signed: slt Scratch4, oldVal, Incr BuildMI(loopMBB, DL, TII->get(SLTScratch4), Scratch4) .addReg(OldVal) .addReg(Incr); if (STI->hasMips64r6() || STI->hasMips32r6()) { // max: seleqz BinOpRes, OldVal, Scratch4 // selnez Scratch4, Incr, Scratch4 // or BinOpRes, BinOpRes, Scratch4 // min: selnqz BinOpRes, OldVal, Scratch4 // seleqz Scratch4, Incr, Scratch4 // or BinOpRes, BinOpRes, Scratch4 BuildMI(loopMBB, DL, TII->get(SELOldVal), BinOpRes) .addReg(OldVal) .addReg(Scratch4); BuildMI(loopMBB, DL, TII->get(SELIncr), Scratch4) .addReg(Incr) .addReg(Scratch4); BuildMI(loopMBB, DL, TII->get(OR), BinOpRes) .addReg(BinOpRes) .addReg(Scratch4); } else { // max: move BinOpRes, OldVal // movn BinOpRes, Incr, Scratch4, BinOpRes // min: move BinOpRes, OldVal // movz BinOpRes, Incr, Scratch4, BinOpRes BuildMI(loopMBB, DL, TII->get(OR), BinOpRes) .addReg(OldVal) .addReg(Mips::ZERO); BuildMI(loopMBB, DL, TII->get(MOVIncr), BinOpRes) .addReg(Incr) .addReg(Scratch4) .addReg(BinOpRes); } // and BinOpRes, BinOpRes, Mask BuildMI(loopMBB, DL, TII->get(Mips::AND), BinOpRes) .addReg(BinOpRes) .addReg(Mask); } else if (!IsSwap) { // binopres, oldval, incr2 // and newval, binopres, mask BuildMI(loopMBB, DL, TII->get(Opcode), BinOpRes) .addReg(OldVal) .addReg(Incr); BuildMI(loopMBB, DL, TII->get(Mips::AND), BinOpRes) .addReg(BinOpRes) .addReg(Mask); } else { // atomic.swap // and newval, incr2, mask BuildMI(loopMBB, DL, TII->get(Mips::AND), BinOpRes) .addReg(Incr) .addReg(Mask); } // and StoreVal, OlddVal, Mask2 // or StoreVal, StoreVal, BinOpRes // StoreVal = sc StoreVal, 0(Ptr) // beq StoreVal, zero, loopMBB BuildMI(loopMBB, DL, TII->get(Mips::AND), StoreVal) .addReg(OldVal).addReg(Mask2); BuildMI(loopMBB, DL, TII->get(Mips::OR), StoreVal) .addReg(StoreVal).addReg(BinOpRes); BuildMI(loopMBB, DL, TII->get(SC), StoreVal) .addReg(StoreVal).addReg(Ptr).addImm(0); BuildMI(loopMBB, DL, TII->get(BEQ)) .addReg(StoreVal).addReg(Mips::ZERO).addMBB(loopMBB); // sinkMBB: // and maskedoldval1,oldval,mask // srl srlres,maskedoldval1,shiftamt // sign_extend dest,srlres sinkMBB->addSuccessor(exitMBB, BranchProbability::getOne()); BuildMI(sinkMBB, DL, TII->get(Mips::AND), Dest) .addReg(OldVal).addReg(Mask); BuildMI(sinkMBB, DL, TII->get(Mips::SRLV), Dest) .addReg(Dest).addReg(ShiftAmnt); if (STI->hasMips32r2()) { BuildMI(sinkMBB, DL, TII->get(SEOp), Dest).addReg(Dest); } else { const unsigned ShiftImm = SEOp == Mips::SEH ? 16 : 24; BuildMI(sinkMBB, DL, TII->get(Mips::SLL), Dest) .addReg(Dest, RegState::Kill) .addImm(ShiftImm); BuildMI(sinkMBB, DL, TII->get(Mips::SRA), Dest) .addReg(Dest, RegState::Kill) .addImm(ShiftImm); } LivePhysRegs LiveRegs; computeAndAddLiveIns(LiveRegs, *loopMBB); computeAndAddLiveIns(LiveRegs, *sinkMBB); computeAndAddLiveIns(LiveRegs, *exitMBB); NMBBI = BB.end(); I->eraseFromParent(); return true; } bool MipsExpandPseudo::expandAtomicBinOp(MachineBasicBlock &BB, MachineBasicBlock::iterator I, MachineBasicBlock::iterator &NMBBI, unsigned Size) { MachineFunction *MF = BB.getParent(); const bool ArePtrs64bit = STI->getABI().ArePtrs64bit(); DebugLoc DL = I->getDebugLoc(); unsigned LL, SC, ZERO, BEQ, SLT, SLTu, OR, MOVN, MOVZ, SELNEZ, SELEQZ; if (Size == 4) { if (STI->inMicroMipsMode()) { LL = STI->hasMips32r6() ? Mips::LL_MMR6 : Mips::LL_MM; SC = STI->hasMips32r6() ? Mips::SC_MMR6 : Mips::SC_MM; BEQ = STI->hasMips32r6() ? Mips::BEQC_MMR6 : Mips::BEQ_MM; SLT = Mips::SLT_MM; SLTu = Mips::SLTu_MM; OR = STI->hasMips32r6() ? Mips::OR_MMR6 : Mips::OR_MM; MOVN = Mips::MOVN_I_MM; MOVZ = Mips::MOVZ_I_MM; SELNEZ = STI->hasMips32r6() ? Mips::SELNEZ_MMR6 : Mips::SELNEZ; SELEQZ = STI->hasMips32r6() ? Mips::SELEQZ_MMR6 : Mips::SELEQZ; } else { LL = STI->hasMips32r6() ? (ArePtrs64bit ? Mips::LL64_R6 : Mips::LL_R6) : (ArePtrs64bit ? Mips::LL64 : Mips::LL); SC = STI->hasMips32r6() ? (ArePtrs64bit ? Mips::SC64_R6 : Mips::SC_R6) : (ArePtrs64bit ? Mips::SC64 : Mips::SC); BEQ = Mips::BEQ; SLT = Mips::SLT; SLTu = Mips::SLTu; OR = Mips::OR; MOVN = Mips::MOVN_I_I; MOVZ = Mips::MOVZ_I_I; SELNEZ = Mips::SELNEZ; SELEQZ = Mips::SELEQZ; } ZERO = Mips::ZERO; } else { LL = STI->hasMips64r6() ? Mips::LLD_R6 : Mips::LLD; SC = STI->hasMips64r6() ? Mips::SCD_R6 : Mips::SCD; ZERO = Mips::ZERO_64; BEQ = Mips::BEQ64; SLT = Mips::SLT64; SLTu = Mips::SLTu64; OR = Mips::OR64; MOVN = Mips::MOVN_I64_I64; MOVZ = Mips::MOVZ_I64_I64; SELNEZ = Mips::SELNEZ64; SELEQZ = Mips::SELEQZ64; } Register OldVal = I->getOperand(0).getReg(); Register Ptr = I->getOperand(1).getReg(); Register Incr = I->getOperand(2).getReg(); Register Scratch = I->getOperand(3).getReg(); unsigned Opcode = 0; unsigned AND = 0; unsigned NOR = 0; bool IsOr = false; bool IsNand = false; bool IsMin = false; bool IsMax = false; bool IsUnsigned = false; switch (I->getOpcode()) { case Mips::ATOMIC_LOAD_ADD_I32_POSTRA: Opcode = Mips::ADDu; break; case Mips::ATOMIC_LOAD_SUB_I32_POSTRA: Opcode = Mips::SUBu; break; case Mips::ATOMIC_LOAD_AND_I32_POSTRA: Opcode = Mips::AND; break; case Mips::ATOMIC_LOAD_OR_I32_POSTRA: Opcode = Mips::OR; break; case Mips::ATOMIC_LOAD_XOR_I32_POSTRA: Opcode = Mips::XOR; break; case Mips::ATOMIC_LOAD_NAND_I32_POSTRA: IsNand = true; AND = Mips::AND; NOR = Mips::NOR; break; case Mips::ATOMIC_SWAP_I32_POSTRA: IsOr = true; break; case Mips::ATOMIC_LOAD_ADD_I64_POSTRA: Opcode = Mips::DADDu; break; case Mips::ATOMIC_LOAD_SUB_I64_POSTRA: Opcode = Mips::DSUBu; break; case Mips::ATOMIC_LOAD_AND_I64_POSTRA: Opcode = Mips::AND64; break; case Mips::ATOMIC_LOAD_OR_I64_POSTRA: Opcode = Mips::OR64; break; case Mips::ATOMIC_LOAD_XOR_I64_POSTRA: Opcode = Mips::XOR64; break; case Mips::ATOMIC_LOAD_NAND_I64_POSTRA: IsNand = true; AND = Mips::AND64; NOR = Mips::NOR64; break; case Mips::ATOMIC_SWAP_I64_POSTRA: IsOr = true; break; case Mips::ATOMIC_LOAD_UMIN_I32_POSTRA: case Mips::ATOMIC_LOAD_UMIN_I64_POSTRA: IsUnsigned = true; [[fallthrough]]; case Mips::ATOMIC_LOAD_MIN_I32_POSTRA: case Mips::ATOMIC_LOAD_MIN_I64_POSTRA: IsMin = true; break; case Mips::ATOMIC_LOAD_UMAX_I32_POSTRA: case Mips::ATOMIC_LOAD_UMAX_I64_POSTRA: IsUnsigned = true; [[fallthrough]]; case Mips::ATOMIC_LOAD_MAX_I32_POSTRA: case Mips::ATOMIC_LOAD_MAX_I64_POSTRA: IsMax = true; break; default: llvm_unreachable("Unknown pseudo atomic!"); } const BasicBlock *LLVM_BB = BB.getBasicBlock(); MachineBasicBlock *loopMBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineFunction::iterator It = ++BB.getIterator(); MF->insert(It, loopMBB); MF->insert(It, exitMBB); exitMBB->splice(exitMBB->begin(), &BB, std::next(I), BB.end()); exitMBB->transferSuccessorsAndUpdatePHIs(&BB); BB.addSuccessor(loopMBB, BranchProbability::getOne()); loopMBB->addSuccessor(exitMBB); loopMBB->addSuccessor(loopMBB); loopMBB->normalizeSuccProbs(); BuildMI(loopMBB, DL, TII->get(LL), OldVal).addReg(Ptr).addImm(0); assert((OldVal != Ptr) && "Clobbered the wrong ptr reg!"); assert((OldVal != Incr) && "Clobbered the wrong reg!"); if (IsMin || IsMax) { assert(I->getNumOperands() == 5 && "Atomics min|max|umin|umax use an additional register"); MCRegister Scratch2 = I->getOperand(4).getReg().asMCReg(); // On Mips64 result of slt is GPR32. MCRegister Scratch2_32 = (Size == 8) ? STI->getRegisterInfo()->getSubReg(Scratch2, Mips::sub_32) : Scratch2; unsigned SLTScratch2 = IsUnsigned ? SLTu : SLT; unsigned SELIncr = IsMax ? SELNEZ : SELEQZ; unsigned SELOldVal = IsMax ? SELEQZ : SELNEZ; unsigned MOVIncr = IsMax ? MOVN : MOVZ; // unsigned: sltu Scratch2, oldVal, Incr // signed: slt Scratch2, oldVal, Incr BuildMI(loopMBB, DL, TII->get(SLTScratch2), Scratch2_32) .addReg(OldVal) .addReg(Incr); if (STI->hasMips64r6() || STI->hasMips32r6()) { // max: seleqz Scratch, OldVal, Scratch2 // selnez Scratch2, Incr, Scratch2 // or Scratch, Scratch, Scratch2 // min: selnez Scratch, OldVal, Scratch2 // seleqz Scratch2, Incr, Scratch2 // or Scratch, Scratch, Scratch2 BuildMI(loopMBB, DL, TII->get(SELOldVal), Scratch) .addReg(OldVal) .addReg(Scratch2); BuildMI(loopMBB, DL, TII->get(SELIncr), Scratch2) .addReg(Incr) .addReg(Scratch2); BuildMI(loopMBB, DL, TII->get(OR), Scratch) .addReg(Scratch) .addReg(Scratch2); } else { // max: move Scratch, OldVal // movn Scratch, Incr, Scratch2, Scratch // min: move Scratch, OldVal // movz Scratch, Incr, Scratch2, Scratch BuildMI(loopMBB, DL, TII->get(OR), Scratch) .addReg(OldVal) .addReg(ZERO); BuildMI(loopMBB, DL, TII->get(MOVIncr), Scratch) .addReg(Incr) .addReg(Scratch2) .addReg(Scratch); } } else if (Opcode) { BuildMI(loopMBB, DL, TII->get(Opcode), Scratch).addReg(OldVal).addReg(Incr); } else if (IsNand) { assert(AND && NOR && "Unknown nand instruction for atomic pseudo expansion"); BuildMI(loopMBB, DL, TII->get(AND), Scratch).addReg(OldVal).addReg(Incr); BuildMI(loopMBB, DL, TII->get(NOR), Scratch).addReg(ZERO).addReg(Scratch); } else { assert(IsOr && OR && "Unknown instruction for atomic pseudo expansion!"); (void)IsOr; BuildMI(loopMBB, DL, TII->get(OR), Scratch).addReg(Incr).addReg(ZERO); } BuildMI(loopMBB, DL, TII->get(SC), Scratch) .addReg(Scratch) .addReg(Ptr) .addImm(0); BuildMI(loopMBB, DL, TII->get(BEQ)) .addReg(Scratch) .addReg(ZERO) .addMBB(loopMBB); NMBBI = BB.end(); I->eraseFromParent(); LivePhysRegs LiveRegs; computeAndAddLiveIns(LiveRegs, *loopMBB); computeAndAddLiveIns(LiveRegs, *exitMBB); return true; } bool MipsExpandPseudo::expandMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, MachineBasicBlock::iterator &NMBB) { bool Modified = false; switch (MBBI->getOpcode()) { case Mips::ATOMIC_CMP_SWAP_I32_POSTRA: case Mips::ATOMIC_CMP_SWAP_I64_POSTRA: return expandAtomicCmpSwap(MBB, MBBI, NMBB); case Mips::ATOMIC_CMP_SWAP_I8_POSTRA: case Mips::ATOMIC_CMP_SWAP_I16_POSTRA: return expandAtomicCmpSwapSubword(MBB, MBBI, NMBB); case Mips::ATOMIC_SWAP_I8_POSTRA: case Mips::ATOMIC_SWAP_I16_POSTRA: case Mips::ATOMIC_LOAD_NAND_I8_POSTRA: case Mips::ATOMIC_LOAD_NAND_I16_POSTRA: case Mips::ATOMIC_LOAD_ADD_I8_POSTRA: case Mips::ATOMIC_LOAD_ADD_I16_POSTRA: case Mips::ATOMIC_LOAD_SUB_I8_POSTRA: case Mips::ATOMIC_LOAD_SUB_I16_POSTRA: case Mips::ATOMIC_LOAD_AND_I8_POSTRA: case Mips::ATOMIC_LOAD_AND_I16_POSTRA: case Mips::ATOMIC_LOAD_OR_I8_POSTRA: case Mips::ATOMIC_LOAD_OR_I16_POSTRA: case Mips::ATOMIC_LOAD_XOR_I8_POSTRA: case Mips::ATOMIC_LOAD_XOR_I16_POSTRA: case Mips::ATOMIC_LOAD_MIN_I8_POSTRA: case Mips::ATOMIC_LOAD_MIN_I16_POSTRA: case Mips::ATOMIC_LOAD_MAX_I8_POSTRA: case Mips::ATOMIC_LOAD_MAX_I16_POSTRA: case Mips::ATOMIC_LOAD_UMIN_I8_POSTRA: case Mips::ATOMIC_LOAD_UMIN_I16_POSTRA: case Mips::ATOMIC_LOAD_UMAX_I8_POSTRA: case Mips::ATOMIC_LOAD_UMAX_I16_POSTRA: return expandAtomicBinOpSubword(MBB, MBBI, NMBB); case Mips::ATOMIC_LOAD_ADD_I32_POSTRA: case Mips::ATOMIC_LOAD_SUB_I32_POSTRA: case Mips::ATOMIC_LOAD_AND_I32_POSTRA: case Mips::ATOMIC_LOAD_OR_I32_POSTRA: case Mips::ATOMIC_LOAD_XOR_I32_POSTRA: case Mips::ATOMIC_LOAD_NAND_I32_POSTRA: case Mips::ATOMIC_SWAP_I32_POSTRA: case Mips::ATOMIC_LOAD_MIN_I32_POSTRA: case Mips::ATOMIC_LOAD_MAX_I32_POSTRA: case Mips::ATOMIC_LOAD_UMIN_I32_POSTRA: case Mips::ATOMIC_LOAD_UMAX_I32_POSTRA: return expandAtomicBinOp(MBB, MBBI, NMBB, 4); case Mips::ATOMIC_LOAD_ADD_I64_POSTRA: case Mips::ATOMIC_LOAD_SUB_I64_POSTRA: case Mips::ATOMIC_LOAD_AND_I64_POSTRA: case Mips::ATOMIC_LOAD_OR_I64_POSTRA: case Mips::ATOMIC_LOAD_XOR_I64_POSTRA: case Mips::ATOMIC_LOAD_NAND_I64_POSTRA: case Mips::ATOMIC_SWAP_I64_POSTRA: case Mips::ATOMIC_LOAD_MIN_I64_POSTRA: case Mips::ATOMIC_LOAD_MAX_I64_POSTRA: case Mips::ATOMIC_LOAD_UMIN_I64_POSTRA: case Mips::ATOMIC_LOAD_UMAX_I64_POSTRA: return expandAtomicBinOp(MBB, MBBI, NMBB, 8); default: return Modified; } } bool MipsExpandPseudo::expandMBB(MachineBasicBlock &MBB) { bool Modified = false; MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end(); while (MBBI != E) { MachineBasicBlock::iterator NMBBI = std::next(MBBI); Modified |= expandMI(MBB, MBBI, NMBBI); MBBI = NMBBI; } return Modified; } bool MipsExpandPseudo::runOnMachineFunction(MachineFunction &MF) { STI = &MF.getSubtarget(); TII = STI->getInstrInfo(); bool Modified = false; for (MachineBasicBlock &MBB : MF) Modified |= expandMBB(MBB); if (Modified) MF.RenumberBlocks(); return Modified; } /// createMipsExpandPseudoPass - returns an instance of the pseudo instruction /// expansion pass. FunctionPass *llvm::createMipsExpandPseudoPass() { return new MipsExpandPseudo(); }