Index: llvm/lib/Target/AArch64/AArch64InstructionSelector.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64InstructionSelector.cpp +++ llvm/lib/Target/AArch64/AArch64InstructionSelector.cpp @@ -69,6 +69,10 @@ bool earlySelectSHL(MachineInstr &I, MachineRegisterInfo &MRI) const; bool earlySelectLoad(MachineInstr &I, MachineRegisterInfo &MRI) const; + /// Eliminate same-sized cross-bank copies into stores before selectImpl(). + void contractCrossBankCopyIntoStore(MachineInstr &I, + MachineRegisterInfo &MRI) const; + bool selectVaStartAAPCS(MachineInstr &I, MachineFunction &MF, MachineRegisterInfo &MRI) const; bool selectVaStartDarwin(MachineInstr &I, MachineFunction &MF, @@ -1160,6 +1164,52 @@ return constrainSelectedInstRegOperands(*NewI, TII, TRI, RBI); } +void AArch64InstructionSelector::contractCrossBankCopyIntoStore( + MachineInstr &I, MachineRegisterInfo &MRI) const { + assert(I.getOpcode() == TargetOpcode::G_STORE && "Expected G_STORE"); + // If we're storing 32 bits, it doesn't matter if it's 32 bits on a FPR or a + // GPR. So, if we run into something like this: + // + // %x:gpr(s64) = ... something ... + // %y:fpr(s64) = COPY %x:gpr(s64) + // G_STORE %y:fpr(s64) + // + // We can fix this up into something like this: + // + // G_STORE %x:gpr(s64) + // + // And then continue the selection process normally. + MachineInstr *Copy = MRI.getVRegDef(I.getOperand(0).getReg()); + if (!Copy || Copy->getOpcode() != TargetOpcode::COPY) + return; + Register CopyDstReg = Copy->getOperand(0).getReg(); + LLT CopyDstTy = MRI.getType(CopyDstReg); + Register CopySrcReg = Copy->getOperand(1).getReg(); + LLT CopySrcTy = MRI.getType(CopySrcReg); + + // If we get something strange like a physical register, then we shouldn't + // go any further. + if (!CopySrcTy.isValid()) + return; + + // Are the source and dst types the same size? + if (CopyDstTy.getSizeInBits() != CopySrcTy.getSizeInBits()) + return; + + if (RBI.getRegBank(CopySrcReg, MRI, TRI) == + RBI.getRegBank(CopyDstReg, MRI, TRI)) + return; + + // Is this copy used in anything other than a store? If it is, then we don't + // want to fold anything, since we'll need the copy anyway. + if (any_of(MRI.use_instructions(CopyDstReg), + [](const MachineInstr &MI) { return !MI.mayStore(); })) + return; + + // We have a cross-bank copy, which is entering a store. Let's fold it. + I.getOperand(0).setReg(CopySrcReg); +} + bool AArch64InstructionSelector::earlySelectLoad( MachineInstr &I, MachineRegisterInfo &MRI) const { // Try to fold in shifts, etc into the addressing mode of a load. @@ -1222,6 +1272,9 @@ switch (I.getOpcode()) { case TargetOpcode::G_SHL: return earlySelectSHL(I, MRI); + case TargetOpcode::G_STORE: + contractCrossBankCopyIntoStore(I, MRI); + return false; case TargetOpcode::G_LOAD: return earlySelectLoad(I, MRI); default: Index: llvm/test/CodeGen/AArch64/GlobalISel/contract-store.mir =================================================================== --- /dev/null +++ llvm/test/CodeGen/AArch64/GlobalISel/contract-store.mir @@ -0,0 +1,114 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -mtriple=aarch64-unknown-unknown -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s + +--- | + define void @contract_s64_gpr(i64* %addr) { ret void } + define void @contract_s32_gpr(i32* %addr) { ret void } + define void @contract_s64_fpr(i64* %addr) { ret void } + define void @contract_s32_fpr(i32* %addr) { ret void } + define void @contract_s16_fpr(i16* %addr) { ret void } + define void @dont_contract(i64* %addr) { ret void } +... +--- +name: contract_s64_gpr +legalized: true +regBankSelected: true +body: | + bb.0: + liveins: $x0, $x1 + ; CHECK-LABEL: name: contract_s64_gpr + ; CHECK: [[COPY:%[0-9]+]]:gpr64sp = COPY $x0 + ; CHECK: [[COPY1:%[0-9]+]]:gpr64 = COPY $x1 + ; CHECK: STRXui [[COPY1]], [[COPY]], 0 :: (store 8 into %ir.addr) + %0:gpr(p0) = COPY $x0 + %1:gpr(s64) = COPY $x1 + %2:fpr(s64) = COPY %1 + G_STORE %2:fpr(s64), %0 :: (store 8 into %ir.addr) +... +--- +name: contract_s32_gpr +legalized: true +regBankSelected: true +body: | + bb.0: + liveins: $x0, $w1 + ; CHECK-LABEL: name: contract_s32_gpr + ; CHECK: [[COPY:%[0-9]+]]:gpr64sp = COPY $x0 + ; CHECK: [[COPY1:%[0-9]+]]:gpr32 = COPY $w1 + ; CHECK: STRWui [[COPY1]], [[COPY]], 0 :: (store 4 into %ir.addr) + %0:gpr(p0) = COPY $x0 + %1:gpr(s32) = COPY $w1 + %2:fpr(s32) = COPY %1 + G_STORE %2:fpr(s32), %0 :: (store 4 into %ir.addr) +... +--- +name: contract_s64_fpr +legalized: true +regBankSelected: true +body: | + bb.0: + liveins: $x0, $d1 + ; CHECK-LABEL: name: contract_s64_fpr + ; CHECK: [[COPY:%[0-9]+]]:gpr64sp = COPY $x0 + ; CHECK: [[COPY1:%[0-9]+]]:fpr64 = COPY $d1 + ; CHECK: STRDui [[COPY1]], [[COPY]], 0 :: (store 8 into %ir.addr) + %0:gpr(p0) = COPY $x0 + %1:fpr(s64) = COPY $d1 + %2:gpr(s64) = COPY %1 + G_STORE %2:gpr(s64), %0 :: (store 8 into %ir.addr) +... +--- +name: contract_s32_fpr +legalized: true +regBankSelected: true +body: | + bb.0: + liveins: $x0, $s1 + ; CHECK-LABEL: name: contract_s32_fpr + ; CHECK: [[COPY:%[0-9]+]]:gpr64sp = COPY $x0 + ; CHECK: [[COPY1:%[0-9]+]]:fpr32 = COPY $s1 + ; CHECK: STRSui [[COPY1]], [[COPY]], 0 :: (store 4 into %ir.addr) + %0:gpr(p0) = COPY $x0 + %1:fpr(s32) = COPY $s1 + %2:gpr(s32) = COPY %1 + G_STORE %2:gpr(s32), %0 :: (store 4 into %ir.addr) +... +--- +name: contract_s16_fpr +legalized: true +regBankSelected: true +body: | + bb.0: + liveins: $x0, $h1 + ; CHECK-LABEL: name: contract_s16_fpr + ; CHECK: [[COPY:%[0-9]+]]:gpr64sp = COPY $x0 + ; CHECK: [[COPY1:%[0-9]+]]:fpr16 = COPY $h1 + ; CHECK: STRHui [[COPY1]], [[COPY]], 0 :: (store 2 into %ir.addr) + %0:gpr(p0) = COPY $x0 + %1:fpr(s16) = COPY $h1 + %2:gpr(s16) = COPY %1 + G_STORE %2:gpr(s16), %0 :: (store 2 into %ir.addr) +... +--- +name: dont_contract +legalized: true +regBankSelected: true +body: | + bb.0: + ; We need to copy %2 into $d2 in this case, so there's no reason to fold + ; away the copy of %1 into %2. + liveins: $x0, $x1, $d2 + ; CHECK-LABEL: name: dont_contract + ; CHECK: [[COPY:%[0-9]+]]:gpr64sp = COPY $x0 + ; CHECK: [[COPY1:%[0-9]+]]:gpr64all = COPY $x1 + ; CHECK: [[COPY2:%[0-9]+]]:fpr64 = COPY [[COPY1]] + ; CHECK: STRDui [[COPY2]], [[COPY]], 0 :: (store 8 into %ir.addr) + ; CHECK: $d2 = COPY [[COPY2]] + ; CHECK: RET_ReallyLR implicit $d2 + %0:gpr(p0) = COPY $x0 + %1:gpr(s64) = COPY $x1 + %2:fpr(s64) = COPY %1 + G_STORE %2:fpr(s64), %0 :: (store 8 into %ir.addr) + $d2 = COPY %2:fpr(s64) + RET_ReallyLR implicit $d2 +...