Index: include/llvm/Target/TargetRegisterInfo.h =================================================================== --- include/llvm/Target/TargetRegisterInfo.h +++ include/llvm/Target/TargetRegisterInfo.h @@ -494,6 +494,11 @@ /// function. Used by MachineRegisterInfo::isConstantPhysReg(). virtual bool isConstantPhysReg(unsigned PhysReg) const { return false; } + /// Returns true if PhysReg can be spilled. It is safe to define this + /// function conservatively, i.e. it does not modify the usual rule that all + /// allocatable registers can be spilled. + virtual bool canSpillPhysReg(unsigned PhysReg) const { return false; } + /// Prior to adding the live-out mask to a stackmap or patchpoint /// instruction, provide the target the opportunity to adjust it (mainly to /// remove pseudo-registers that should be ignored). Index: lib/CodeGen/TargetInstrInfo.cpp =================================================================== --- lib/CodeGen/TargetInstrInfo.cpp +++ lib/CodeGen/TargetInstrInfo.cpp @@ -397,7 +397,8 @@ // If the COPY instruction in MI can be folded to a stack operation, return // the register class to use. static const TargetRegisterClass *canFoldCopy(const MachineInstr &MI, - unsigned FoldIdx) { + unsigned FoldIdx, + const TargetRegisterInfo &TRI) { assert(MI.isCopy() && "MI must be a COPY instruction"); if (MI.getNumOperands() != 2) return nullptr; @@ -418,8 +419,15 @@ const MachineRegisterInfo &MRI = MI.getParent()->getParent()->getRegInfo(); const TargetRegisterClass *RC = MRI.getRegClass(FoldReg); - if (TargetRegisterInfo::isPhysicalRegister(LiveOp.getReg())) - return RC->contains(LiveOp.getReg()) ? RC : nullptr; + if (TargetRegisterInfo::isPhysicalRegister(LiveOp.getReg())) { + if (RC->contains(LiveOp.getReg())) + return RC; + const TargetRegisterClass *LivePhysRC = + TRI.getMinimalPhysRegClass(LiveOp.getReg()); + if (LivePhysRC->getSize() == RC->getSize() && TRI.canSpillPhysReg(LiveOp.getReg())) + return LivePhysRC; + return nullptr; + } if (RC->hasSubClassEq(MRI.getRegClass(LiveReg))) return RC; @@ -552,13 +560,13 @@ if (!MI.isCopy() || Ops.size() != 1) return nullptr; - const TargetRegisterClass *RC = canFoldCopy(MI, Ops[0]); + const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo(); + const TargetRegisterClass *RC = canFoldCopy(MI, Ops[0], *TRI); if (!RC) return nullptr; const MachineOperand &MO = MI.getOperand(1 - Ops[0]); MachineBasicBlock::iterator Pos = MI; - const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo(); if (Flags == MachineMemOperand::MOStore) storeRegToStackSlot(*MBB, Pos, MO.getReg(), MO.isKill(), FI, RC, TRI); Index: lib/Target/AArch64/AArch64RegisterInfo.h =================================================================== --- lib/Target/AArch64/AArch64RegisterInfo.h +++ lib/Target/AArch64/AArch64RegisterInfo.h @@ -63,6 +63,7 @@ BitVector getReservedRegs(const MachineFunction &MF) const override; bool isConstantPhysReg(unsigned PhysReg) const override; + bool canSpillPhysReg(unsigned PhysReg) const override; const TargetRegisterClass * getPointerRegClass(const MachineFunction &MF, unsigned Kind = 0) const override; Index: lib/Target/AArch64/AArch64RegisterInfo.cpp =================================================================== --- lib/Target/AArch64/AArch64RegisterInfo.cpp +++ lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -171,6 +171,19 @@ return PhysReg == AArch64::WZR || PhysReg == AArch64::XZR; } +// This is needed to catch cases of spill copy folding where the COPY +// destination register class is incompatible with PhysReg, most commonly when +// spilling a COPY of WZR/XZR. +bool AArch64RegisterInfo::canSpillPhysReg(unsigned PhysReg) const { + switch (PhysReg) { + default: + return true; + case AArch64::WSP: + case AArch64::SP: + return false; + } +} + const TargetRegisterClass * AArch64RegisterInfo::getPointerRegClass(const MachineFunction &MF, unsigned Kind) const { Index: test/CodeGen/AArch64/zero-reg.ll =================================================================== --- test/CodeGen/AArch64/zero-reg.ll +++ test/CodeGen/AArch64/zero-reg.ll @@ -28,3 +28,57 @@ ret void ; CHECK: ret } + +declare i32 @bar() +declare i32 @baz() + +; Check that the spill of the zero value gets stored directly instead +; of being copied from wzr and then stored. +define i32 @test_zr_spill_copyprop1(i1 %c) { +; CHECK-LABEL: test_zr_spill_copyprop1: +entry: + br i1 %c, label %if.else, label %if.then + +if.else: +; CHECK: bl bar +; CHECK-NEXT: str w0, [sp, #[[SLOT:[0-9]+]]] + %call1 = tail call i32 @bar() + br label %if.end + +if.then: +; CHECK: bl baz +; CHECK-NEXT: str wzr, [sp, #[[SLOT]]] + %call2 = tail call i32 @baz() + br label %if.end + +if.end: + %x.0 = phi i32 [ 0, %if.then ], [ %call1, %if.else ] + call void asm sideeffect "", "~{x0},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x18},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{fp},~{lr},~{sp}"() nounwind + ret i32 %x.0 +} + +; Similar to test_zr_spill_copyprop1, but with mis-matched register +; class between %x.0 and the 0 from %if.then. +define i32 @test_zr_spill_copyprop2(i1 %c) { +; CHECK-LABEL: test_zr_spill_copyprop2: +entry: + br i1 %c, label %if.else, label %if.then + +if.else: +; CHECK: bl bar +; CHECK-NEXT: str w0, [sp, #[[SLOT:[0-9]+]]] + %call1 = tail call i32 @bar() + br label %if.end + +if.then: +; CHECK: bl baz +; CHECK-NEXT: str wzr, [sp, #[[SLOT]]] + %call2 = tail call i32 @baz() + br label %if.end + +if.end: + %x.0 = phi i32 [ 0, %if.then ], [ %call1, %if.else ] + call void asm sideeffect "", "~{x0},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x18},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{fp},~{lr},~{sp}"() nounwind + %x.1 = add i32 %x.0, 1 + ret i32 %x.1 +}