1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 20:51:52 +01:00

[SystemZ] Bugfix and improve the handling of CC values.

It was recently discovered that the handling of CC values was actually broken
since overflow was not properly handled ('nsw' flag not checked for).

Add and sub instructions now have a new target specific instruction flag
named SystemZII::CCIfNoSignedWrap. It means that the CC result can be used
instead of a compare with 0, but only if the instruction has the 'nsw' flag
set.

This patch also adds the improvements of conversion to logical instructions
and the analyzing of add with immediates, to be able to eliminate more
compares.

Review: Ulrich Weigand
https://reviews.llvm.org/D66868
This commit is contained in:
Jonas Paulsson 2019-12-17 10:14:34 -08:00
parent bbb6f6b671
commit 61f35cd2aa
11 changed files with 520 additions and 77 deletions

View File

@ -55,7 +55,7 @@ const unsigned CCMASK_ARITH = CCMASK_ANY;
// Condition-code mask assignments for logical operations. // Condition-code mask assignments for logical operations.
const unsigned CCMASK_LOGICAL_ZERO = CCMASK_0 | CCMASK_2; const unsigned CCMASK_LOGICAL_ZERO = CCMASK_0 | CCMASK_2;
const unsigned CCMASK_LOGICAL_NONZERO = CCMASK_1 | CCMASK_2; const unsigned CCMASK_LOGICAL_NONZERO = CCMASK_1 | CCMASK_3;
const unsigned CCMASK_LOGICAL_CARRY = CCMASK_2 | CCMASK_3; const unsigned CCMASK_LOGICAL_CARRY = CCMASK_2 | CCMASK_3;
const unsigned CCMASK_LOGICAL_NOCARRY = CCMASK_0 | CCMASK_1; const unsigned CCMASK_LOGICAL_NOCARRY = CCMASK_0 | CCMASK_1;
const unsigned CCMASK_LOGICAL_BORROW = CCMASK_LOGICAL_NOCARRY; const unsigned CCMASK_LOGICAL_BORROW = CCMASK_LOGICAL_NOCARRY;

View File

@ -88,6 +88,8 @@ private:
SmallVectorImpl<MachineInstr *> &CCUsers); SmallVectorImpl<MachineInstr *> &CCUsers);
bool convertToLoadAndTest(MachineInstr &MI, MachineInstr &Compare, bool convertToLoadAndTest(MachineInstr &MI, MachineInstr &Compare,
SmallVectorImpl<MachineInstr *> &CCUsers); SmallVectorImpl<MachineInstr *> &CCUsers);
bool convertToLogical(MachineInstr &MI, MachineInstr &Compare,
SmallVectorImpl<MachineInstr *> &CCUsers);
bool adjustCCMasksForInstr(MachineInstr &MI, MachineInstr &Compare, bool adjustCCMasksForInstr(MachineInstr &MI, MachineInstr &Compare,
SmallVectorImpl<MachineInstr *> &CCUsers, SmallVectorImpl<MachineInstr *> &CCUsers,
unsigned ConvOpc = 0); unsigned ConvOpc = 0);
@ -303,6 +305,52 @@ bool SystemZElimCompare::convertToLoadAndTest(
return true; return true;
} }
// See if MI is an instruction with an equivalent "logical" opcode that can
// be used and replace MI. This is useful for EQ/NE comparisons where the
// "nsw" flag is missing since the "logical" opcode always sets CC to reflect
// the result being zero or non-zero.
bool SystemZElimCompare::convertToLogical(
MachineInstr &MI, MachineInstr &Compare,
SmallVectorImpl<MachineInstr *> &CCUsers) {
unsigned ConvOpc = 0;
switch (MI.getOpcode()) {
case SystemZ::AR: ConvOpc = SystemZ::ALR; break;
case SystemZ::ARK: ConvOpc = SystemZ::ALRK; break;
case SystemZ::AGR: ConvOpc = SystemZ::ALGR; break;
case SystemZ::AGRK: ConvOpc = SystemZ::ALGRK; break;
case SystemZ::A: ConvOpc = SystemZ::AL; break;
case SystemZ::AY: ConvOpc = SystemZ::ALY; break;
case SystemZ::AG: ConvOpc = SystemZ::ALG; break;
default: break;
}
if (!ConvOpc || !adjustCCMasksForInstr(MI, Compare, CCUsers, ConvOpc))
return false;
// Operands should be identical, so just change the opcode and remove the
// dead flag on CC.
MI.setDesc(TII->get(ConvOpc));
MI.clearRegisterDeads(SystemZ::CC);
return true;
}
#ifndef NDEBUG
static bool isAddWithImmediate(unsigned Opcode) {
switch(Opcode) {
case SystemZ::AHI:
case SystemZ::AHIK:
case SystemZ::AGHI:
case SystemZ::AGHIK:
case SystemZ::AFI:
case SystemZ::AIH:
case SystemZ::AGFI:
return true;
default: break;
}
return false;
}
#endif
// The CC users in CCUsers are testing the result of a comparison of some // The CC users in CCUsers are testing the result of a comparison of some
// value X against zero and we know that any CC value produced by MI would // value X against zero and we know that any CC value produced by MI would
// also reflect the value of X. ConvOpc may be used to pass the transfomed // also reflect the value of X. ConvOpc may be used to pass the transfomed
@ -313,6 +361,8 @@ bool SystemZElimCompare::adjustCCMasksForInstr(
MachineInstr &MI, MachineInstr &Compare, MachineInstr &MI, MachineInstr &Compare,
SmallVectorImpl<MachineInstr *> &CCUsers, SmallVectorImpl<MachineInstr *> &CCUsers,
unsigned ConvOpc) { unsigned ConvOpc) {
unsigned CompareFlags = Compare.getDesc().TSFlags;
unsigned CompareCCValues = SystemZII::getCCValues(CompareFlags);
int Opcode = (ConvOpc ? ConvOpc : MI.getOpcode()); int Opcode = (ConvOpc ? ConvOpc : MI.getOpcode());
const MCInstrDesc &Desc = TII->get(Opcode); const MCInstrDesc &Desc = TII->get(Opcode);
unsigned MIFlags = Desc.TSFlags; unsigned MIFlags = Desc.TSFlags;
@ -330,60 +380,97 @@ bool SystemZElimCompare::adjustCCMasksForInstr(
} }
// See which compare-style condition codes are available. // See which compare-style condition codes are available.
unsigned ReusableCCMask = SystemZII::getCompareZeroCCMask(MIFlags); unsigned CCValues = SystemZII::getCCValues(MIFlags);
unsigned ReusableCCMask = CCValues;
// For unsigned comparisons with zero, only equality makes sense. // For unsigned comparisons with zero, only equality makes sense.
unsigned CompareFlags = Compare.getDesc().TSFlags;
if (CompareFlags & SystemZII::IsLogical) if (CompareFlags & SystemZII::IsLogical)
ReusableCCMask &= SystemZ::CCMASK_CMP_EQ; ReusableCCMask &= SystemZ::CCMASK_CMP_EQ;
unsigned OFImplies = 0;
bool LogicalMI = false;
bool MIEquivalentToCmp = false;
if (MI.getFlag(MachineInstr::NoSWrap) &&
(MIFlags & SystemZII::CCIfNoSignedWrap)) {
// If MI has the NSW flag set in combination with the
// SystemZII::CCIfNoSignedWrap flag, all CCValues are valid.
}
else if ((MIFlags & SystemZII::CCIfNoSignedWrap) &&
MI.getOperand(2).isImm()) {
// Signed addition of immediate. If adding a positive immediate
// overflows, the result must be less than zero. If adding a negative
// immediate overflows, the result must be larger than zero (except in
// the special case of adding the minimum value of the result range, in
// which case we cannot predict whether the result is larger than or
// equal to zero).
assert(isAddWithImmediate(Opcode) && "Expected an add with immediate.");
assert(!MI.mayLoadOrStore() && "Expected an immediate term.");
int64_t RHS = MI.getOperand(2).getImm();
if (SystemZ::GRX32BitRegClass.contains(MI.getOperand(0).getReg()) &&
RHS == INT32_MIN)
return false;
OFImplies = (RHS > 0 ? SystemZ::CCMASK_CMP_LT : SystemZ::CCMASK_CMP_GT);
}
else if ((MIFlags & SystemZII::IsLogical) && CCValues) {
// Use CCMASK_CMP_EQ to match with CCUsers. On success CCMask:s will be
// converted to CCMASK_LOGICAL_ZERO or CCMASK_LOGICAL_NONZERO.
LogicalMI = true;
ReusableCCMask = SystemZ::CCMASK_CMP_EQ;
}
else {
ReusableCCMask &= SystemZII::getCompareZeroCCMask(MIFlags);
assert((ReusableCCMask & ~CCValues) == 0 && "Invalid CCValues");
MIEquivalentToCmp =
ReusableCCMask == CCValues && CCValues == CompareCCValues;
}
if (ReusableCCMask == 0) if (ReusableCCMask == 0)
return false; return false;
unsigned CCValues = SystemZII::getCCValues(MIFlags);
assert((ReusableCCMask & ~CCValues) == 0 && "Invalid CCValues");
bool MIEquivalentToCmp =
(ReusableCCMask == CCValues &&
CCValues == SystemZII::getCCValues(CompareFlags));
if (!MIEquivalentToCmp) { if (!MIEquivalentToCmp) {
// Now check whether these flags are enough for all users. // Now check whether these flags are enough for all users.
SmallVector<MachineOperand *, 4> AlterMasks; SmallVector<MachineOperand *, 4> AlterMasks;
for (unsigned int I = 0, E = CCUsers.size(); I != E; ++I) { for (unsigned int I = 0, E = CCUsers.size(); I != E; ++I) {
MachineInstr *MI = CCUsers[I]; MachineInstr *CCUserMI = CCUsers[I];
// Fail if this isn't a use of CC that we understand. // Fail if this isn't a use of CC that we understand.
unsigned Flags = MI->getDesc().TSFlags; unsigned Flags = CCUserMI->getDesc().TSFlags;
unsigned FirstOpNum; unsigned FirstOpNum;
if (Flags & SystemZII::CCMaskFirst) if (Flags & SystemZII::CCMaskFirst)
FirstOpNum = 0; FirstOpNum = 0;
else if (Flags & SystemZII::CCMaskLast) else if (Flags & SystemZII::CCMaskLast)
FirstOpNum = MI->getNumExplicitOperands() - 2; FirstOpNum = CCUserMI->getNumExplicitOperands() - 2;
else else
return false; return false;
// Check whether the instruction predicate treats all CC values // Check whether the instruction predicate treats all CC values
// outside of ReusableCCMask in the same way. In that case it // outside of ReusableCCMask in the same way. In that case it
// doesn't matter what those CC values mean. // doesn't matter what those CC values mean.
unsigned CCValid = MI->getOperand(FirstOpNum).getImm(); unsigned CCValid = CCUserMI->getOperand(FirstOpNum).getImm();
unsigned CCMask = MI->getOperand(FirstOpNum + 1).getImm(); unsigned CCMask = CCUserMI->getOperand(FirstOpNum + 1).getImm();
assert(CCValid == CompareCCValues && (CCMask & ~CCValid) == 0 &&
"Corrupt CC operands of CCUser.");
unsigned OutValid = ~ReusableCCMask & CCValid; unsigned OutValid = ~ReusableCCMask & CCValid;
unsigned OutMask = ~ReusableCCMask & CCMask; unsigned OutMask = ~ReusableCCMask & CCMask;
if (OutMask != 0 && OutMask != OutValid) if (OutMask != 0 && OutMask != OutValid)
return false; return false;
AlterMasks.push_back(&MI->getOperand(FirstOpNum)); AlterMasks.push_back(&CCUserMI->getOperand(FirstOpNum));
AlterMasks.push_back(&MI->getOperand(FirstOpNum + 1)); AlterMasks.push_back(&CCUserMI->getOperand(FirstOpNum + 1));
} }
// All users are OK. Adjust the masks for MI. // All users are OK. Adjust the masks for MI.
for (unsigned I = 0, E = AlterMasks.size(); I != E; I += 2) { for (unsigned I = 0, E = AlterMasks.size(); I != E; I += 2) {
AlterMasks[I]->setImm(CCValues); AlterMasks[I]->setImm(CCValues);
unsigned CCMask = AlterMasks[I + 1]->getImm(); unsigned CCMask = AlterMasks[I + 1]->getImm();
if (CCMask & ~ReusableCCMask) if (LogicalMI) {
AlterMasks[I + 1]->setImm((CCMask & ReusableCCMask) | // Translate the CCMask into its "logical" value.
(CCValues & ~ReusableCCMask)); CCMask = (CCMask == SystemZ::CCMASK_CMP_EQ ?
SystemZ::CCMASK_LOGICAL_ZERO : SystemZ::CCMASK_LOGICAL_NONZERO);
CCMask &= CCValues; // Logical subtracts never set CC=0.
} else {
if (CCMask & ~ReusableCCMask)
CCMask = (CCMask & ReusableCCMask) | (CCValues & ~ReusableCCMask);
CCMask |= (CCMask & OFImplies) ? SystemZ::CCMASK_ARITH_OVERFLOW : 0;
}
AlterMasks[I + 1]->setImm(CCMask);
} }
} }
@ -460,7 +547,9 @@ bool SystemZElimCompare::optimizeCompareZero(
} }
// Try to eliminate Compare by reusing a CC result from MI. // Try to eliminate Compare by reusing a CC result from MI.
if ((!CCRefs && convertToLoadAndTest(MI, Compare, CCUsers)) || if ((!CCRefs && convertToLoadAndTest(MI, Compare, CCUsers)) ||
(!CCRefs.Def && adjustCCMasksForInstr(MI, Compare, CCUsers))) { (!CCRefs.Def &&
(adjustCCMasksForInstr(MI, Compare, CCUsers) ||
convertToLogical(MI, Compare, CCUsers)))) {
EliminatedComparisons += 1; EliminatedComparisons += 1;
return true; return true;
} }

View File

@ -75,8 +75,9 @@ class InstSystemZ<int size, dag outs, dag ins, string asmstr,
// SystemZ::CCMASK_*. // SystemZ::CCMASK_*.
bits<4> CCValues = 0; bits<4> CCValues = 0;
// The subset of CCValues that have the same meaning as they would after // The subset of CCValues that have the same meaning as they would after a
// a comparison of the first operand against zero. // comparison of the first operand against zero. "Logical" instructions
// leave this blank as they set CC in a different way.
bits<4> CompareZeroCCMask = 0; bits<4> CompareZeroCCMask = 0;
// True if the instruction is conditional and if the CC mask operand // True if the instruction is conditional and if the CC mask operand
@ -87,9 +88,16 @@ class InstSystemZ<int size, dag outs, dag ins, string asmstr,
bit CCMaskLast = 0; bit CCMaskLast = 0;
// True if the instruction is the "logical" rather than "arithmetic" form, // True if the instruction is the "logical" rather than "arithmetic" form,
// in cases where a distinction exists. // in cases where a distinction exists. Except for logical compares, if the
// instruction sets this flag along with a non-zero CCValues field, it is
// assumed to set CC to either CCMASK_LOGICAL_ZERO or
// CCMASK_LOGICAL_NONZERO.
bit IsLogical = 0; bit IsLogical = 0;
// True if the (add or sub) instruction sets CC like a compare of the
// result against zero, but only if the 'nsw' flag is set.
bit CCIfNoSignedWrap = 0;
let TSFlags{0} = SimpleBDXLoad; let TSFlags{0} = SimpleBDXLoad;
let TSFlags{1} = SimpleBDXStore; let TSFlags{1} = SimpleBDXStore;
let TSFlags{2} = Has20BitOffset; let TSFlags{2} = Has20BitOffset;
@ -101,6 +109,7 @@ class InstSystemZ<int size, dag outs, dag ins, string asmstr,
let TSFlags{18} = CCMaskFirst; let TSFlags{18} = CCMaskFirst;
let TSFlags{19} = CCMaskLast; let TSFlags{19} = CCMaskLast;
let TSFlags{20} = IsLogical; let TSFlags{20} = IsLogical;
let TSFlags{21} = CCIfNoSignedWrap;
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@ -945,6 +945,12 @@ static void transferDeadCC(MachineInstr *OldMI, MachineInstr *NewMI) {
} }
} }
static void transferMIFlag(MachineInstr *OldMI, MachineInstr *NewMI,
MachineInstr::MIFlag Flag) {
if (OldMI->getFlag(Flag))
NewMI->setFlag(Flag);
}
MachineInstr *SystemZInstrInfo::convertToThreeAddress( MachineInstr *SystemZInstrInfo::convertToThreeAddress(
MachineFunction::iterator &MFI, MachineInstr &MI, LiveVariables *LV) const { MachineFunction::iterator &MFI, MachineInstr &MI, LiveVariables *LV) const {
MachineBasicBlock *MBB = MI.getParent(); MachineBasicBlock *MBB = MI.getParent();
@ -1050,6 +1056,7 @@ MachineInstr *SystemZInstrInfo::foldMemoryOperandImpl(
.addImm(0) .addImm(0)
.addImm(MI.getOperand(2).getImm()); .addImm(MI.getOperand(2).getImm());
transferDeadCC(&MI, BuiltMI); transferDeadCC(&MI, BuiltMI);
transferMIFlag(&MI, BuiltMI, MachineInstr::NoSWrap);
return BuiltMI; return BuiltMI;
} }
@ -1200,6 +1207,7 @@ MachineInstr *SystemZInstrInfo::foldMemoryOperandImpl(
if (MemDesc.TSFlags & SystemZII::HasIndex) if (MemDesc.TSFlags & SystemZII::HasIndex)
MIB.addReg(0); MIB.addReg(0);
transferDeadCC(&MI, MIB); transferDeadCC(&MI, MIB);
transferMIFlag(&MI, MIB, MachineInstr::NoSWrap);
return MIB; return MIB;
} }
} }

View File

@ -46,7 +46,8 @@ enum {
CompareZeroCCMaskShift = 14, CompareZeroCCMaskShift = 14,
CCMaskFirst = (1 << 18), CCMaskFirst = (1 << 18),
CCMaskLast = (1 << 19), CCMaskLast = (1 << 19),
IsLogical = (1 << 20) IsLogical = (1 << 20),
CCIfNoSignedWrap = (1 << 21)
}; };
static inline unsigned getAccessSize(unsigned int Flags) { static inline unsigned getAccessSize(unsigned int Flags) {

View File

@ -915,7 +915,7 @@ def : Pat<(or (zext32 GR32:$src), imm64hf32:$imm),
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Addition producing a signed overflow flag. // Addition producing a signed overflow flag.
let Defs = [CC], CCValues = 0xF, CompareZeroCCMask = 0x8 in { let Defs = [CC], CCValues = 0xF, CCIfNoSignedWrap = 1 in {
// Addition of a register. // Addition of a register.
let isCommutable = 1 in { let isCommutable = 1 in {
defm AR : BinaryRRAndK<"ar", 0x1A, 0xB9F8, z_sadd, GR32, GR32>; defm AR : BinaryRRAndK<"ar", 0x1A, 0xB9F8, z_sadd, GR32, GR32>;
@ -957,7 +957,7 @@ let Defs = [CC], CCValues = 0xF, CompareZeroCCMask = 0x8 in {
defm : SXB<z_sadd, GR64, AGFR>; defm : SXB<z_sadd, GR64, AGFR>;
// Addition producing a carry. // Addition producing a carry.
let Defs = [CC] in { let Defs = [CC], CCValues = 0xF, IsLogical = 1 in {
// Addition of a register. // Addition of a register.
let isCommutable = 1 in { let isCommutable = 1 in {
defm ALR : BinaryRRAndK<"alr", 0x1E, 0xB9FA, z_uadd, GR32, GR32>; defm ALR : BinaryRRAndK<"alr", 0x1E, 0xB9FA, z_uadd, GR32, GR32>;
@ -997,7 +997,7 @@ let Defs = [CC] in {
defm : ZXB<z_uadd, GR64, ALGFR>; defm : ZXB<z_uadd, GR64, ALGFR>;
// Addition producing and using a carry. // Addition producing and using a carry.
let Defs = [CC], Uses = [CC] in { let Defs = [CC], Uses = [CC], CCValues = 0xF, IsLogical = 1 in {
// Addition of a register. // Addition of a register.
def ALCR : BinaryRRE<"alcr", 0xB998, z_addcarry, GR32, GR32>; def ALCR : BinaryRRE<"alcr", 0xB998, z_addcarry, GR32, GR32>;
def ALCGR : BinaryRRE<"alcgr", 0xB988, z_addcarry, GR64, GR64>; def ALCGR : BinaryRRE<"alcgr", 0xB988, z_addcarry, GR64, GR64>;
@ -1017,7 +1017,8 @@ def ALSIHN : BinaryRIL<"alsihn", 0xCCB, null_frag, GRH32, simm32>,
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Subtraction producing a signed overflow flag. // Subtraction producing a signed overflow flag.
let Defs = [CC], CCValues = 0xF, CompareZeroCCMask = 0x8 in { let Defs = [CC], CCValues = 0xF, CompareZeroCCMask = 0x8,
CCIfNoSignedWrap = 1 in {
// Subtraction of a register. // Subtraction of a register.
defm SR : BinaryRRAndK<"sr", 0x1B, 0xB9F9, z_ssub, GR32, GR32>; defm SR : BinaryRRAndK<"sr", 0x1B, 0xB9F9, z_ssub, GR32, GR32>;
def SGFR : BinaryRRE<"sgfr", 0xB919, null_frag, GR64, GR32>; def SGFR : BinaryRRE<"sgfr", 0xB919, null_frag, GR64, GR32>;
@ -1066,7 +1067,7 @@ def : Pat<(z_saddo GR64:$src1, imm64lf32n:$src2),
(SGR GR64:$src1, (LLILF imm64lf32n:$src2))>; (SGR GR64:$src1, (LLILF imm64lf32n:$src2))>;
// Subtraction producing a carry. // Subtraction producing a carry.
let Defs = [CC] in { let Defs = [CC], CCValues = 0x7, IsLogical = 1 in {
// Subtraction of a register. // Subtraction of a register.
defm SLR : BinaryRRAndK<"slr", 0x1F, 0xB9FB, z_usub, GR32, GR32>; defm SLR : BinaryRRAndK<"slr", 0x1F, 0xB9FB, z_usub, GR32, GR32>;
def SLGFR : BinaryRRE<"slgfr", 0xB91B, null_frag, GR64, GR32>; def SLGFR : BinaryRRE<"slgfr", 0xB91B, null_frag, GR64, GR32>;
@ -1104,7 +1105,7 @@ def : Pat<(add GR64:$src1, imm64zx32n:$src2),
(SLGFI GR64:$src1, imm64zx32n:$src2)>; (SLGFI GR64:$src1, imm64zx32n:$src2)>;
// Subtraction producing and using a carry. // Subtraction producing and using a carry.
let Defs = [CC], Uses = [CC] in { let Defs = [CC], Uses = [CC], CCValues = 0xF, IsLogical = 1 in {
// Subtraction of a register. // Subtraction of a register.
def SLBR : BinaryRRE<"slbr", 0xB999, z_subcarry, GR32, GR32>; def SLBR : BinaryRRE<"slbr", 0xB999, z_subcarry, GR32, GR32>;
def SLBGR : BinaryRRE<"slbgr", 0xB989, z_subcarry, GR64, GR64>; def SLBGR : BinaryRRE<"slbgr", 0xB989, z_subcarry, GR64, GR64>;

View File

@ -6,15 +6,16 @@
declare void @foo() declare void @foo()
; Addition provides enough for equality comparisons with zero. First teest ; Addition provides enough for comparisons with zero if we know no
; the EQ case. ; signed overflow happens, which is when the "nsw" flag is set.
; First test the EQ case.
define i32 @f1(i32 %a, i32 %b, i32 *%dest) { define i32 @f1(i32 %a, i32 %b, i32 *%dest) {
; CHECK-LABEL: f1: ; CHECK-LABEL: f1:
; CHECK: afi %r2, 1000000 ; CHECK: afi %r2, 1000000
; CHECK-NEXT: ber %r14 ; CHECK-NEXT: ber %r14
; CHECK: br %r14 ; CHECK: br %r14
entry: entry:
%res = add i32 %a, 1000000 %res = add nsw i32 %a, 1000000
%cmp = icmp eq i32 %res, 0 %cmp = icmp eq i32 %res, 0
br i1 %cmp, label %exit, label %store br i1 %cmp, label %exit, label %store
@ -30,10 +31,10 @@ exit:
define i32 @f2(i32 %a, i32 %b, i32 *%dest) { define i32 @f2(i32 %a, i32 %b, i32 *%dest) {
; CHECK-LABEL: f2: ; CHECK-LABEL: f2:
; CHECK: afi %r2, 1000000 ; CHECK: afi %r2, 1000000
; CHECK-NEXT: bner %r14 ; CHECK-NEXT: blhr %r14
; CHECK: br %r14 ; CHECK: br %r14
entry: entry:
%res = add i32 %a, 1000000 %res = add nsw i32 %a, 1000000
%cmp = icmp ne i32 %res, 0 %cmp = icmp ne i32 %res, 0
br i1 %cmp, label %exit, label %store br i1 %cmp, label %exit, label %store
@ -45,14 +46,13 @@ exit:
ret i32 %res ret i32 %res
} }
; SLT requires a comparison. ; ...and again with SLT.
define i32 @f3(i32 %a, i32 %b, i32 *%dest) { define i32 @f3(i32 %a, i32 %b, i32 *%dest) {
; CHECK-LABEL: f3: ; CHECK-LABEL: f3:
; CHECK: afi %r2, 1000000 ; CHECK: afi %r2, 1000000
; CHECK-NEXT: cibl %r2, 0, 0(%r14) ; CHECK-NEXT: blr %r14
; CHECK: br %r14
entry: entry:
%res = add i32 %a, 1000000 %res = add nsw i32 %a, 1000000
%cmp = icmp slt i32 %res, 0 %cmp = icmp slt i32 %res, 0
br i1 %cmp, label %exit, label %store br i1 %cmp, label %exit, label %store
@ -64,14 +64,13 @@ exit:
ret i32 %res ret i32 %res
} }
; ...SLE too. ; ...and again with SLE.
define i32 @f4(i32 %a, i32 %b, i32 *%dest) { define i32 @f4(i32 %a, i32 %b, i32 *%dest) {
; CHECK-LABEL: f4: ; CHECK-LABEL: f4:
; CHECK: afi %r2, 1000000 ; CHECK: afi %r2, 1000000
; CHECK-NEXT: cible %r2, 0, 0(%r14) ; CHECK-NEXT: bler %r14
; CHECK: br %r14
entry: entry:
%res = add i32 %a, 1000000 %res = add nsw i32 %a, 1000000
%cmp = icmp sle i32 %res, 0 %cmp = icmp sle i32 %res, 0
br i1 %cmp, label %exit, label %store br i1 %cmp, label %exit, label %store
@ -83,14 +82,13 @@ exit:
ret i32 %res ret i32 %res
} }
; ...SGT too. ; ...and again with SGT.
define i32 @f5(i32 %a, i32 %b, i32 *%dest) { define i32 @f5(i32 %a, i32 %b, i32 *%dest) {
; CHECK-LABEL: f5: ; CHECK-LABEL: f5:
; CHECK: afi %r2, 1000000 ; CHECK: afi %r2, 1000000
; CHECK-NEXT: cibh %r2, 0, 0(%r14) ; CHECK-NEXT: bhr %r14
; CHECK: br %r14
entry: entry:
%res = add i32 %a, 1000000 %res = add nsw i32 %a, 1000000
%cmp = icmp sgt i32 %res, 0 %cmp = icmp sgt i32 %res, 0
br i1 %cmp, label %exit, label %store br i1 %cmp, label %exit, label %store
@ -102,14 +100,13 @@ exit:
ret i32 %res ret i32 %res
} }
; ...SGE too. ; ...and again with SGE.
define i32 @f6(i32 %a, i32 %b, i32 *%dest) { define i32 @f6(i32 %a, i32 %b, i32 *%dest) {
; CHECK-LABEL: f6: ; CHECK-LABEL: f6:
; CHECK: afi %r2, 1000000 ; CHECK: afi %r2, 1000000
; CHECK-NEXT: cibhe %r2, 0, 0(%r14) ; CHECK-NEXT: bher %r14
; CHECK: br %r14
entry: entry:
%res = add i32 %a, 1000000 %res = add nsw i32 %a, 1000000
%cmp = icmp sge i32 %res, 0 %cmp = icmp sge i32 %res, 0
br i1 %cmp, label %exit, label %store br i1 %cmp, label %exit, label %store
@ -121,7 +118,8 @@ exit:
ret i32 %res ret i32 %res
} }
; Subtraction also provides enough for equality comparisons with zero. ; Subtraction provides in addition also enough for equality comparisons with
; zero even without "nsw".
define i32 @f7(i32 %a, i32 %b, i32 *%dest) { define i32 @f7(i32 %a, i32 %b, i32 *%dest) {
; CHECK-LABEL: f7: ; CHECK-LABEL: f7:
; CHECK: s %r2, 0(%r4) ; CHECK: s %r2, 0(%r4)
@ -141,15 +139,14 @@ exit:
ret i32 %res ret i32 %res
} }
; ...but not for ordered comparisons. ; ...and again with SLT.
define i32 @f8(i32 %a, i32 %b, i32 *%dest) { define i32 @f8(i32 %a, i32 %b, i32 *%dest) {
; CHECK-LABEL: f8: ; CHECK-LABEL: f8:
; CHECK: s %r2, 0(%r4) ; CHECK: s %r2, 0(%r4)
; CHECK-NEXT: cibl %r2, 0, 0(%r14) ; CHECK-NEXT: blr %r14
; CHECK: br %r14
entry: entry:
%cur = load i32, i32 *%dest %cur = load i32, i32 *%dest
%res = sub i32 %a, %cur %res = sub nsw i32 %a, %cur
%cmp = icmp slt i32 %res, 0 %cmp = icmp slt i32 %res, 0
br i1 %cmp, label %exit, label %store br i1 %cmp, label %exit, label %store
@ -445,10 +442,10 @@ define i32 @f23(i32 %a, i32 %b, i32 *%dest1, i32 *%dest2) {
; CHECK-LABEL: f23: ; CHECK-LABEL: f23:
; CHECK: afi %r2, 1000000 ; CHECK: afi %r2, 1000000
; CHECK-NEXT: st %r2, 0(%r4) ; CHECK-NEXT: st %r2, 0(%r4)
; CHECK-NEXT: bner %r14 ; CHECK-NEXT: blhr %r14
; CHECK: br %r14 ; CHECK: br %r14
entry: entry:
%res = add i32 %a, 1000000 %res = add nsw i32 %a, 1000000
store i32 %res, i32 *%dest1 store i32 %res, i32 *%dest1
%cmp = icmp ne i32 %res, 0 %cmp = icmp ne i32 %res, 0
br i1 %cmp, label %exit, label %store br i1 %cmp, label %exit, label %store
@ -491,10 +488,10 @@ define void @f25(i32 %a, i32 *%ptr) {
; CHECK-NEXT: #APP ; CHECK-NEXT: #APP
; CHECK-NEXT: blah ; CHECK-NEXT: blah
; CHECK-NEXT: #NO_APP ; CHECK-NEXT: #NO_APP
; CHECK-NEXT: bner %r14 ; CHECK-NEXT: blhr %r14
; CHECK: br %r14 ; CHECK: br %r14
entry: entry:
%add = add i32 %a, 1000000 %add = add nsw i32 %a, 1000000
call void asm sideeffect "blah", "r"(i32 %add) call void asm sideeffect "blah", "r"(i32 %add)
%cmp = icmp ne i32 %add, 0 %cmp = icmp ne i32 %add, 0
br i1 %cmp, label %exit, label %store br i1 %cmp, label %exit, label %store
@ -540,7 +537,7 @@ define i32 @f27(i32 %a, i32 %b, i32 *%dest1, i32 *%dest2) {
; CHECK-NEXT: cibe %r2, 0, 0(%r14) ; CHECK-NEXT: cibe %r2, 0, 0(%r14)
; CHECK: br %r14 ; CHECK: br %r14
entry: entry:
%add = add i32 %a, 1000000 %add = add nsw i32 %a, 1000000
%sub = sub i32 %b, %add %sub = sub i32 %b, %add
store i32 %sub, i32 *%dest1 store i32 %sub, i32 *%dest1
%cmp = icmp eq i32 %add, 0 %cmp = icmp eq i32 %add, 0

View File

@ -3,14 +3,15 @@
; ;
; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z196 -no-integrated-as | FileCheck %s ; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z196 -no-integrated-as | FileCheck %s
; Addition provides enough for equality comparisons with zero. First teest ; Addition provides enough for comparisons with zero if we know no
; the EQ case with LOC. ; signed overflow happens, which is when the "nsw" flag is set.
; First test the EQ case with LOC.
define i32 @f1(i32 %a, i32 %b, i32 *%cptr) { define i32 @f1(i32 %a, i32 %b, i32 *%cptr) {
; CHECK-LABEL: f1: ; CHECK-LABEL: f1:
; CHECK: afi %r2, 1000000 ; CHECK: afi %r2, 1000000
; CHECK-NEXT: loce %r3, 0(%r4) ; CHECK-NEXT: loce %r3, 0(%r4)
; CHECK: br %r14 ; CHECK: br %r14
%add = add i32 %a, 1000000 %add = add nsw i32 %a, 1000000
%cmp = icmp eq i32 %add, 0 %cmp = icmp eq i32 %add, 0
%c = load i32, i32 *%cptr %c = load i32, i32 *%cptr
%arg = select i1 %cmp, i32 %c, i32 %b %arg = select i1 %cmp, i32 %c, i32 %b
@ -24,7 +25,7 @@ define i32 @f2(i32 %a, i32 %b, i32 *%cptr) {
; CHECK: afi %r2, 1000000 ; CHECK: afi %r2, 1000000
; CHECK-NEXT: stoce %r3, 0(%r4) ; CHECK-NEXT: stoce %r3, 0(%r4)
; CHECK: br %r14 ; CHECK: br %r14
%add = add i32 %a, 1000000 %add = add nsw i32 %a, 1000000
%cmp = icmp eq i32 %add, 0 %cmp = icmp eq i32 %add, 0
%c = load i32, i32 *%cptr %c = load i32, i32 *%cptr
%newval = select i1 %cmp, i32 %b, i32 %c %newval = select i1 %cmp, i32 %b, i32 %c
@ -36,9 +37,9 @@ define i32 @f2(i32 %a, i32 %b, i32 *%cptr) {
define i32 @f3(i32 %a, i32 %b, i32 %c) { define i32 @f3(i32 %a, i32 %b, i32 %c) {
; CHECK-LABEL: f3: ; CHECK-LABEL: f3:
; CHECK: afi %r2, 1000000 ; CHECK: afi %r2, 1000000
; CHECK-NEXT: locrne %r3, %r4 ; CHECK-NEXT: locrlh %r3, %r4
; CHECK: br %r14 ; CHECK: br %r14
%add = add i32 %a, 1000000 %add = add nsw i32 %a, 1000000
%cmp = icmp eq i32 %add, 0 %cmp = icmp eq i32 %add, 0
%arg = select i1 %cmp, i32 %b, i32 %c %arg = select i1 %cmp, i32 %b, i32 %c
call void asm sideeffect "blah $0", "{r3}"(i32 %arg) call void asm sideeffect "blah $0", "{r3}"(i32 %arg)
@ -49,9 +50,9 @@ define i32 @f3(i32 %a, i32 %b, i32 %c) {
define i32 @f4(i32 %a, i32 %b, i32 *%cptr) { define i32 @f4(i32 %a, i32 %b, i32 *%cptr) {
; CHECK-LABEL: f4: ; CHECK-LABEL: f4:
; CHECK: afi %r2, 1000000 ; CHECK: afi %r2, 1000000
; CHECK-NEXT: locne %r3, 0(%r4) ; CHECK-NEXT: loclh %r3, 0(%r4)
; CHECK: br %r14 ; CHECK: br %r14
%add = add i32 %a, 1000000 %add = add nsw i32 %a, 1000000
%cmp = icmp eq i32 %add, 0 %cmp = icmp eq i32 %add, 0
%c = load i32, i32 *%cptr %c = load i32, i32 *%cptr
%arg = select i1 %cmp, i32 %b, i32 %c %arg = select i1 %cmp, i32 %b, i32 %c
@ -63,9 +64,9 @@ define i32 @f4(i32 %a, i32 %b, i32 *%cptr) {
define i32 @f5(i32 %a, i32 %b, i32 *%cptr) { define i32 @f5(i32 %a, i32 %b, i32 *%cptr) {
; CHECK-LABEL: f5: ; CHECK-LABEL: f5:
; CHECK: afi %r2, 1000000 ; CHECK: afi %r2, 1000000
; CHECK-NEXT: stocne %r3, 0(%r4) ; CHECK-NEXT: stoclh %r3, 0(%r4)
; CHECK: br %r14 ; CHECK: br %r14
%add = add i32 %a, 1000000 %add = add nsw i32 %a, 1000000
%cmp = icmp eq i32 %add, 0 %cmp = icmp eq i32 %add, 0
%c = load i32, i32 *%cptr %c = load i32, i32 *%cptr
%newval = select i1 %cmp, i32 %c, i32 %b %newval = select i1 %cmp, i32 %c, i32 %b
@ -79,7 +80,7 @@ define i32 @f6(i32 %a, i32 %b, i32 %c) {
; CHECK: afi %r2, 1000000 ; CHECK: afi %r2, 1000000
; CHECK-NEXT: locre %r3, %r4 ; CHECK-NEXT: locre %r3, %r4
; CHECK: br %r14 ; CHECK: br %r14
%add = add i32 %a, 1000000 %add = add nsw i32 %a, 1000000
%cmp = icmp ne i32 %add, 0 %cmp = icmp ne i32 %add, 0
%arg = select i1 %cmp, i32 %b, i32 %c %arg = select i1 %cmp, i32 %b, i32 %c
call void asm sideeffect "blah $0", "{r3}"(i32 %arg) call void asm sideeffect "blah $0", "{r3}"(i32 %arg)
@ -92,7 +93,7 @@ define i32 @f7(i32 %a, i32 %b, i32 *%cptr) {
; CHECK: afi %r2, 1000000 ; CHECK: afi %r2, 1000000
; CHECK-NEXT: loce %r3, 0(%r4) ; CHECK-NEXT: loce %r3, 0(%r4)
; CHECK: br %r14 ; CHECK: br %r14
%add = add i32 %a, 1000000 %add = add nsw i32 %a, 1000000
%cmp = icmp ne i32 %add, 0 %cmp = icmp ne i32 %add, 0
%c = load i32, i32 *%cptr %c = load i32, i32 *%cptr
%arg = select i1 %cmp, i32 %b, i32 %c %arg = select i1 %cmp, i32 %b, i32 %c
@ -106,7 +107,7 @@ define i32 @f8(i32 %a, i32 %b, i32 *%cptr) {
; CHECK: afi %r2, 1000000 ; CHECK: afi %r2, 1000000
; CHECK-NEXT: stoce %r3, 0(%r4) ; CHECK-NEXT: stoce %r3, 0(%r4)
; CHECK: br %r14 ; CHECK: br %r14
%add = add i32 %a, 1000000 %add = add nsw i32 %a, 1000000
%cmp = icmp ne i32 %add, 0 %cmp = icmp ne i32 %add, 0
%c = load i32, i32 *%cptr %c = load i32, i32 *%cptr
%newval = select i1 %cmp, i32 %c, i32 %b %newval = select i1 %cmp, i32 %c, i32 %b

View File

@ -0,0 +1,163 @@
; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z14 | FileCheck %s
;
; Check that signed comparisons against 0 are only eliminated if the "nsw"
; flag is present on the defining add (with register) instruction. For an
; equality comparison, add logical can be used.
define i32 @fun0(i32 %arg, i32 %arg2, i32 %arg3) {
; CHECK-LABEL: fun0:
; CHECK: jle .LBB0_2{{$}}
; CHECK: je .LBB0_4{{$}}
bb:
%tmp2 = add nsw i32 %arg, %arg2
%tmp3 = icmp sgt i32 %tmp2, 0
br i1 %tmp3, label %bb3, label %bb1
bb1:
%tmp4 = add nsw i32 %arg, %arg3
%tmp5 = icmp eq i32 %tmp4, 0
br i1 %tmp5, label %bb4, label %bb2
bb2:
ret i32 0
bb3:
ret i32 1
bb4:
ret i32 2
}
; No "nsw" flag
define i32 @fun1(i32 %arg, i32 %arg2, i32 %arg3) {
; CHECK-LABEL: fun1:
; CHECK: cijle
; CHECK: alr
; CHECK: jhe
bb:
%tmp2 = add i32 %arg, %arg2
%tmp3 = icmp sgt i32 %tmp2, 0
br i1 %tmp3, label %bb3, label %bb1
bb1:
%tmp4 = add i32 %arg, %arg3
%tmp5 = icmp eq i32 %tmp4, 0
br i1 %tmp5, label %bb4, label %bb2
bb2:
ret i32 0
bb3:
ret i32 1
bb4:
ret i32 2
}
; "nuw" flag
define i32 @fun2(i32 %arg, i32 %arg2, i32 %arg3) {
; CHECK-LABEL: fun2:
; CHECK: cijle
; CHECK: alr
; CHECK: jhe
bb:
%tmp2 = add nuw i32 %arg, %arg2
%tmp3 = icmp sgt i32 %tmp2, 0
br i1 %tmp3, label %bb3, label %bb1
bb1:
%tmp4 = add nuw i32 %arg, %arg3
%tmp5 = icmp eq i32 %tmp4, 0
br i1 %tmp5, label %bb4, label %bb2
bb2:
ret i32 0
bb3:
ret i32 1
bb4:
ret i32 2
}
; Subtraction does not produce the value of zero in case of overflow, so
; "nsw" is not needed for the equality check against zero.
define i32 @fun3(i32 %arg, i32 %arg2, i32 %arg3) {
; CHECK-LABEL: fun3:
; CHECK: jle .LBB3_2{{$}}
; CHECK: je .LBB3_4{{$}}
bb:
%tmp2 = sub nsw i32 %arg, %arg2
%tmp3 = icmp sgt i32 %tmp2, 0
br i1 %tmp3, label %bb3, label %bb1
bb1:
%tmp4 = sub nsw i32 %arg, %arg3
%tmp5 = icmp eq i32 %tmp4, 0
br i1 %tmp5, label %bb4, label %bb2
bb2:
ret i32 0
bb3:
ret i32 1
bb4:
ret i32 2
}
; No "nsw" flag
define i32 @fun4(i32 %arg, i32 %arg2, i32 %arg3) {
; CHECK-LABEL: fun4:
; CHECK: cijle
; CHECK: je .LBB4_4{{$}}
bb:
%tmp2 = sub i32 %arg, %arg2
%tmp3 = icmp sgt i32 %tmp2, 0
br i1 %tmp3, label %bb3, label %bb1
bb1:
%tmp4 = sub i32 %arg, %arg3
%tmp5 = icmp eq i32 %tmp4, 0
br i1 %tmp5, label %bb4, label %bb2
bb2:
ret i32 0
bb3:
ret i32 1
bb4:
ret i32 2
}
; "nuw" flag
define i32 @fun5(i32 %arg, i32 %arg2, i32 %arg3) {
; CHECK-LABEL: fun5:
; CHECK: cijle
; CHECK: je .LBB5_4{{$}}
bb:
%tmp2 = sub nuw i32 %arg, %arg2
%tmp3 = icmp sgt i32 %tmp2, 0
br i1 %tmp3, label %bb3, label %bb1
bb1:
%tmp4 = sub nuw i32 %arg, %arg3
%tmp5 = icmp eq i32 %tmp4, 0
br i1 %tmp5, label %bb4, label %bb2
bb2:
ret i32 0
bb3:
ret i32 1
bb4:
ret i32 2
}

View File

@ -0,0 +1,103 @@
; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z14 -disable-cgp | FileCheck %s
;
; Check that signed comparisons against 0 are eliminated if the defining
; instruction is an add with immediate.
;
; Addition of an immediate does not depend on the "nsw" flag, since the
; result can be predicted in case of overflow. For example, if adding a
; positive immediate gives overflow, the result must be negative.
; Addition of a negative immediate gives a positive result in case of
; overflow (except for the case of the minimum value which may also result in
; a zero result).
define i32 @fun0(i32 %arg) {
; CHECK-LABEL: fun0:
; CHECK: ahik
; CHECK-NEXT: locre
bb:
%tmp = add i32 %arg, -1
%tmp1 = icmp eq i32 %tmp, 0
%res = select i1 %tmp1, i32 %tmp, i32 %arg
ret i32 %res
}
define i32 @fun1(i32 %arg) {
; CHECK-LABEL: fun1:
; CHECK: ahik
; CHECK-NEXT: locrnle
bb:
%tmp = add i32 %arg, -1
%tmp1 = icmp sgt i32 %tmp, 0
%res = select i1 %tmp1, i32 %tmp, i32 %arg
ret i32 %res
}
define i32 @fun2(i32 %arg) {
; CHECK-LABEL: fun2:
; CHECK: ahik
; CHECK-NEXT: locrl
bb:
%tmp = add i32 %arg, -1
%tmp1 = icmp slt i32 %tmp, 0
%res = select i1 %tmp1, i32 %tmp, i32 %arg
ret i32 %res
}
; Addition of a positive immediate gives a negative result in case of overflow.
define i32 @fun3(i32 %arg) {
; CHECK-LABEL: fun3:
; CHECK: ahik
; CHECK-NEXT: locre
bb:
%tmp = add i32 %arg, 1
%tmp1 = icmp eq i32 %tmp, 0
%res = select i1 %tmp1, i32 %tmp, i32 %arg
ret i32 %res
}
define i32 @fun4(i32 %arg) {
; CHECK-LABEL: fun4:
; CHECK: ahik
; CHECK-NEXT: locrh
bb:
%tmp = add i32 %arg, 1
%tmp1 = icmp sgt i32 %tmp, 0
%res = select i1 %tmp1, i32 %tmp, i32 %arg
ret i32 %res
}
define i32 @fun5(i32 %arg) {
; CHECK-LABEL: fun5:
; CHECK: ahik
; CHECK-NEXT: locrnhe
bb:
%tmp = add i32 %arg, 1
%tmp1 = icmp slt i32 %tmp, 0
%res = select i1 %tmp1, i32 %tmp, i32 %arg
ret i32 %res
}
; Addition of the minimum value gives a positive or zero result.
define i32 @fun6(i32 %arg) {
; CHECK-LABEL: fun6:
; CHECK: afi
; CHECK-NEXT: chi
; CHECK-NEXT: locrlh
bb:
%tmp = add i32 %arg, -2147483648
%tmp1 = icmp eq i32 %tmp, 0
%res = select i1 %tmp1, i32 %tmp, i32 %arg
ret i32 %res
}
define i32 @fun7(i32 %arg) {
; CHECK-LABEL: fun7:
; CHECK: afi
; CHECK-NEXT: chi
; CHECK-NEXT: locrle
bb:
%tmp = add i32 %arg, -2147483648
%tmp1 = icmp sgt i32 %tmp, 0
%res = select i1 %tmp1, i32 %tmp, i32 %arg
ret i32 %res
}

View File

@ -0,0 +1,71 @@
# RUN: llc -mtriple=s390x-linux-gnu -mcpu=z14 -start-before=postrapseudos %s \
# RUN: -o - | FileCheck %s
#
# Test that the CC values of logical adds and subs can be used in compare
# elimination in the cases of EQ/NE.
# CHECK-LABEL: fun0:
# CHECK: alr %r3, %r2
# CHECK-NEXT: locrhe %r2, %r3
# CHECK-NEXT: alr %r3, %r2
# CHECK-NEXT: locrnhe %r2, %r3
# CHECK-NEXT: alr %r3, %r2
# CHECK-NEXT: chi %r3, 0
# CHECK-NEXT: locrle %r2, %r3
# CHECK-NEXT: alr %r3, %r2
# CHECK-NEXT: chi %r3, 0
# CHECK-NEXT: locrhe %r2, %r3
# CHECK-NEXT: slrk %r3, %r2, %r3
# CHECK-NEXT: locrh %r2, %r3
# CHECK-NEXT: slrk %r3, %r2, %r3
# CHECK-NEXT: locrnhe %r2, %r3
# CHECK-NEXT: slrk %r3, %r2, %r3
# CHECK-NEXT: chi %r3, 0
# CHECK-NEXT: locrle %r2, %r3
# CHECK-NEXT: slrk %r3, %r2, %r3
# CHECK-NEXT: chi %r3, 0
# CHECK-NEXT: locrhe %r2, %r3
--- |
define i32 @fun0(i32 %arg1, i32 %arg2) { bb: ret i32 0 }
...
---
name: fun0
body: |
bb.0:
renamable $r3l = ALRK renamable $r2l, killed renamable $r3l, implicit-def dead $cc
CHIMux renamable $r3l, 0, implicit-def $cc
renamable $r2l = LOCR killed renamable $r2l, killed renamable $r3l, 14, 8, implicit killed $cc
renamable $r3l = ALRK renamable $r2l, killed renamable $r3l, implicit-def dead $cc
CHIMux renamable $r3l, 0, implicit-def $cc
renamable $r2l = LOCR killed renamable $r2l, killed renamable $r3l, 14, 6, implicit killed $cc
renamable $r3l = ALRK renamable $r2l, killed renamable $r3l, implicit-def dead $cc
CHIMux renamable $r3l, 0, implicit-def $cc
renamable $r2l = LOCR killed renamable $r2l, killed renamable $r3l, 14, 12, implicit killed $cc
renamable $r3l = ALRK renamable $r2l, killed renamable $r3l, implicit-def dead $cc
CHIMux renamable $r3l, 0, implicit-def $cc
renamable $r2l = LOCR killed renamable $r2l, killed renamable $r3l, 14, 10, implicit killed $cc
renamable $r3l = SLRK renamable $r2l, killed renamable $r3l, implicit-def dead $cc
CHIMux renamable $r3l, 0, implicit-def $cc
renamable $r2l = LOCR killed renamable $r2l, killed renamable $r3l, 14, 8, implicit killed $cc
renamable $r3l = SLRK renamable $r2l, killed renamable $r3l, implicit-def dead $cc
CHIMux renamable $r3l, 0, implicit-def $cc
renamable $r2l = LOCR killed renamable $r2l, killed renamable $r3l, 14, 6, implicit killed $cc
renamable $r3l = SLRK renamable $r2l, killed renamable $r3l, implicit-def dead $cc
CHIMux renamable $r3l, 0, implicit-def $cc
renamable $r2l = LOCR killed renamable $r2l, killed renamable $r3l, 14, 12, implicit killed $cc
renamable $r3l = SLRK renamable $r2l, killed renamable $r3l, implicit-def dead $cc
CHIMux renamable $r3l, 0, implicit-def $cc
renamable $r2l = LOCR killed renamable $r2l, killed renamable $r3l, 14, 10, implicit killed $cc
Return implicit $r2l
...