mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-07-02 04:24:22 +00:00
InstCombine: look for masked compares with subset relation
Even in cases which aren't universally optimisable like "(A & B) != 0 && (A & C) != 0", the masks can make one of the comparisons completely redundant. In this case, since we've gone to the effort of spotting masked comparisons we should combine them. rdar://problem/7625728 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@189930 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@ -488,6 +488,26 @@ static unsigned getTypeOfMaskedICmp(Value* A, Value* B, Value* C,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert an analysis of a masked ICmp into its equivalent if all boolean
|
||||||
|
/// operations had the opposite sense. Since each "NotXXX" flag (recording !=)
|
||||||
|
/// is adjacent to the corresponding normal flag (recording ==), this just
|
||||||
|
/// involves swapping those bits over.
|
||||||
|
static unsigned conjugateICmpMask(unsigned Mask) {
|
||||||
|
unsigned NewMask;
|
||||||
|
NewMask = (Mask & (FoldMskICmp_AMask_AllOnes | FoldMskICmp_BMask_AllOnes |
|
||||||
|
FoldMskICmp_Mask_AllZeroes | FoldMskICmp_AMask_Mixed |
|
||||||
|
FoldMskICmp_BMask_Mixed))
|
||||||
|
<< 1;
|
||||||
|
|
||||||
|
NewMask |=
|
||||||
|
(Mask & (FoldMskICmp_AMask_NotAllOnes | FoldMskICmp_BMask_NotAllOnes |
|
||||||
|
FoldMskICmp_Mask_NotAllZeroes | FoldMskICmp_AMask_NotMixed |
|
||||||
|
FoldMskICmp_BMask_NotMixed))
|
||||||
|
>> 1;
|
||||||
|
|
||||||
|
return NewMask;
|
||||||
|
}
|
||||||
|
|
||||||
/// decomposeBitTestICmp - Decompose an icmp into the form ((X & Y) pred Z)
|
/// decomposeBitTestICmp - Decompose an icmp into the form ((X & Y) pred Z)
|
||||||
/// if possible. The returned predicate is either == or !=. Returns false if
|
/// if possible. The returned predicate is either == or !=. Returns false if
|
||||||
/// decomposition fails.
|
/// decomposition fails.
|
||||||
@ -618,8 +638,7 @@ static unsigned foldLogOpOfMaskedICmpsHelper(Value*& A,
|
|||||||
/// foldLogOpOfMaskedICmps:
|
/// foldLogOpOfMaskedICmps:
|
||||||
/// try to fold (icmp(A & B) ==/!= C) &/| (icmp(A & D) ==/!= E)
|
/// try to fold (icmp(A & B) ==/!= C) &/| (icmp(A & D) ==/!= E)
|
||||||
/// into a single (icmp(A & X) ==/!= Y)
|
/// into a single (icmp(A & X) ==/!= Y)
|
||||||
static Value* foldLogOpOfMaskedICmps(ICmpInst *LHS, ICmpInst *RHS,
|
static Value* foldLogOpOfMaskedICmps(ICmpInst *LHS, ICmpInst *RHS, bool IsAnd,
|
||||||
ICmpInst::Predicate NEWCC,
|
|
||||||
llvm::InstCombiner::BuilderTy* Builder) {
|
llvm::InstCombiner::BuilderTy* Builder) {
|
||||||
Value *A = 0, *B = 0, *C = 0, *D = 0, *E = 0;
|
Value *A = 0, *B = 0, *C = 0, *D = 0, *E = 0;
|
||||||
ICmpInst::Predicate LHSCC = LHS->getPredicate(), RHSCC = RHS->getPredicate();
|
ICmpInst::Predicate LHSCC = LHS->getPredicate(), RHSCC = RHS->getPredicate();
|
||||||
@ -629,8 +648,24 @@ static Value* foldLogOpOfMaskedICmps(ICmpInst *LHS, ICmpInst *RHS,
|
|||||||
assert(ICmpInst::isEquality(LHSCC) && ICmpInst::isEquality(RHSCC) &&
|
assert(ICmpInst::isEquality(LHSCC) && ICmpInst::isEquality(RHSCC) &&
|
||||||
"foldLogOpOfMaskedICmpsHelper must return an equality predicate.");
|
"foldLogOpOfMaskedICmpsHelper must return an equality predicate.");
|
||||||
|
|
||||||
if (NEWCC == ICmpInst::ICMP_NE)
|
// In full generality:
|
||||||
mask >>= 1; // treat "Not"-states as normal states
|
// (icmp (A & B) Op C) | (icmp (A & D) Op E)
|
||||||
|
// == ![ (icmp (A & B) !Op C) & (icmp (A & D) !Op E) ]
|
||||||
|
//
|
||||||
|
// If the latter can be converted into (icmp (A & X) Op Y) then the former is
|
||||||
|
// equivalent to (icmp (A & X) !Op Y).
|
||||||
|
//
|
||||||
|
// Therefore, we can pretend for the rest of this function that we're dealing
|
||||||
|
// with the conjunction, provided we flip the sense of any comparisons (both
|
||||||
|
// input and output).
|
||||||
|
|
||||||
|
// In most cases we're going to produce an EQ for the "&&" case.
|
||||||
|
ICmpInst::Predicate NEWCC = IsAnd ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE;
|
||||||
|
if (!IsAnd) {
|
||||||
|
// Convert the masking analysis into its equivalent with negated
|
||||||
|
// comparisons.
|
||||||
|
mask = conjugateICmpMask(mask);
|
||||||
|
}
|
||||||
|
|
||||||
if (mask & FoldMskICmp_Mask_AllZeroes) {
|
if (mask & FoldMskICmp_Mask_AllZeroes) {
|
||||||
// (icmp eq (A & B), 0) & (icmp eq (A & D), 0)
|
// (icmp eq (A & B), 0) & (icmp eq (A & D), 0)
|
||||||
@ -657,6 +692,40 @@ static Value* foldLogOpOfMaskedICmps(ICmpInst *LHS, ICmpInst *RHS,
|
|||||||
Value* newAnd = Builder->CreateAnd(A, newAnd1);
|
Value* newAnd = Builder->CreateAnd(A, newAnd1);
|
||||||
return Builder->CreateICmp(NEWCC, newAnd, A);
|
return Builder->CreateICmp(NEWCC, newAnd, A);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remaining cases assume at least that B and D are constant, and depend on
|
||||||
|
// their actual values. This isn't strictly, necessary, just a "handle the
|
||||||
|
// easy cases for now" decision.
|
||||||
|
ConstantInt *BCst = dyn_cast<ConstantInt>(B);
|
||||||
|
if (BCst == 0) return 0;
|
||||||
|
ConstantInt *DCst = dyn_cast<ConstantInt>(D);
|
||||||
|
if (DCst == 0) return 0;
|
||||||
|
|
||||||
|
if (mask & (FoldMskICmp_Mask_NotAllZeroes | FoldMskICmp_BMask_NotAllOnes)) {
|
||||||
|
// (icmp ne (A & B), 0) & (icmp ne (A & D), 0) and
|
||||||
|
// (icmp ne (A & B), B) & (icmp ne (A & D), D)
|
||||||
|
// -> (icmp ne (A & B), 0) or (icmp ne (A & D), 0)
|
||||||
|
// Only valid if one of the masks is a superset of the other (check "B&D" is
|
||||||
|
// the same as either B or D).
|
||||||
|
APInt NewMask = BCst->getValue() & DCst->getValue();
|
||||||
|
|
||||||
|
if (NewMask == BCst->getValue())
|
||||||
|
return LHS;
|
||||||
|
else if (NewMask == DCst->getValue())
|
||||||
|
return RHS;
|
||||||
|
}
|
||||||
|
if (mask & FoldMskICmp_AMask_NotAllOnes) {
|
||||||
|
// (icmp ne (A & B), B) & (icmp ne (A & D), D)
|
||||||
|
// -> (icmp ne (A & B), A) or (icmp ne (A & D), A)
|
||||||
|
// Only valid if one of the masks is a superset of the other (check "B|D" is
|
||||||
|
// the same as either B or D).
|
||||||
|
APInt NewMask = BCst->getValue() | DCst->getValue();
|
||||||
|
|
||||||
|
if (NewMask == BCst->getValue())
|
||||||
|
return LHS;
|
||||||
|
else if (NewMask == DCst->getValue())
|
||||||
|
return RHS;
|
||||||
|
}
|
||||||
if (mask & FoldMskICmp_BMask_Mixed) {
|
if (mask & FoldMskICmp_BMask_Mixed) {
|
||||||
// (icmp eq (A & B), C) & (icmp eq (A & D), E)
|
// (icmp eq (A & B), C) & (icmp eq (A & D), E)
|
||||||
// We already know that B & C == C && D & E == E.
|
// We already know that B & C == C && D & E == E.
|
||||||
@ -665,14 +734,9 @@ static Value* foldLogOpOfMaskedICmps(ICmpInst *LHS, ICmpInst *RHS,
|
|||||||
// contradict, then we can transform to
|
// contradict, then we can transform to
|
||||||
// -> (icmp eq (A & (B|D)), (C|E))
|
// -> (icmp eq (A & (B|D)), (C|E))
|
||||||
// Currently, we only handle the case of B, C, D, and E being constant.
|
// Currently, we only handle the case of B, C, D, and E being constant.
|
||||||
ConstantInt *BCst = dyn_cast<ConstantInt>(B);
|
|
||||||
if (BCst == 0) return 0;
|
|
||||||
ConstantInt *DCst = dyn_cast<ConstantInt>(D);
|
|
||||||
if (DCst == 0) return 0;
|
|
||||||
// we can't simply use C and E, because we might actually handle
|
// we can't simply use C and E, because we might actually handle
|
||||||
// (icmp ne (A & B), B) & (icmp eq (A & D), D)
|
// (icmp ne (A & B), B) & (icmp eq (A & D), D)
|
||||||
// with B and D, having a single bit set
|
// with B and D, having a single bit set
|
||||||
|
|
||||||
ConstantInt *CCst = dyn_cast<ConstantInt>(C);
|
ConstantInt *CCst = dyn_cast<ConstantInt>(C);
|
||||||
if (CCst == 0) return 0;
|
if (CCst == 0) return 0;
|
||||||
if (LHSCC != NEWCC)
|
if (LHSCC != NEWCC)
|
||||||
@ -715,7 +779,7 @@ Value *InstCombiner::FoldAndOfICmps(ICmpInst *LHS, ICmpInst *RHS) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handle (roughly): (icmp eq (A & B), C) & (icmp eq (A & D), E)
|
// handle (roughly): (icmp eq (A & B), C) & (icmp eq (A & D), E)
|
||||||
if (Value *V = foldLogOpOfMaskedICmps(LHS, RHS, ICmpInst::ICMP_EQ, Builder))
|
if (Value *V = foldLogOpOfMaskedICmps(LHS, RHS, true, Builder))
|
||||||
return V;
|
return V;
|
||||||
|
|
||||||
// This only handles icmp of constants: (icmp1 A, C1) & (icmp2 B, C2).
|
// This only handles icmp of constants: (icmp1 A, C1) & (icmp2 B, C2).
|
||||||
@ -1479,7 +1543,7 @@ Value *InstCombiner::FoldOrOfICmps(ICmpInst *LHS, ICmpInst *RHS) {
|
|||||||
|
|
||||||
// handle (roughly):
|
// handle (roughly):
|
||||||
// (icmp ne (A & B), C) | (icmp ne (A & D), E)
|
// (icmp ne (A & B), C) | (icmp ne (A & D), E)
|
||||||
if (Value *V = foldLogOpOfMaskedICmps(LHS, RHS, ICmpInst::ICMP_NE, Builder))
|
if (Value *V = foldLogOpOfMaskedICmps(LHS, RHS, false, Builder))
|
||||||
return V;
|
return V;
|
||||||
|
|
||||||
Value *Val = LHS->getOperand(0), *Val2 = RHS->getOperand(0);
|
Value *Val = LHS->getOperand(0), *Val2 = RHS->getOperand(0);
|
||||||
|
122
test/Transforms/InstCombine/icmp-logical.ll
Normal file
122
test/Transforms/InstCombine/icmp-logical.ll
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
; RUN: opt -instcombine -S -o - %s | FileCheck %s
|
||||||
|
|
||||||
|
define i1 @masked_and_notallzeroes(i32 %A) {
|
||||||
|
; CHECK-LABEL: @masked_and_notallzeroes
|
||||||
|
; CHECK: [[MASK:%.*]] = and i32 %A, 7
|
||||||
|
; CHECK: icmp ne i32 [[MASK]], 0
|
||||||
|
; CHECK-NOT: and i32 %A, 39
|
||||||
|
; CHECK: ret i1
|
||||||
|
|
||||||
|
%mask1 = and i32 %A, 7
|
||||||
|
%tst1 = icmp ne i32 %mask1, 0
|
||||||
|
|
||||||
|
%mask2 = and i32 %A, 39
|
||||||
|
%tst2 = icmp ne i32 %mask2, 0
|
||||||
|
|
||||||
|
%res = and i1 %tst1, %tst2
|
||||||
|
ret i1 %res
|
||||||
|
}
|
||||||
|
|
||||||
|
define i1 @masked_or_allzeroes(i32 %A) {
|
||||||
|
; CHECK-LABEL: @masked_or_allzeroes
|
||||||
|
; CHECK: [[MASK:%.*]] = and i32 %A, 7
|
||||||
|
; CHECK: icmp eq i32 [[MASK]], 0
|
||||||
|
; CHECK-NOT: and i32 %A, 39
|
||||||
|
; CHECK: ret i1
|
||||||
|
|
||||||
|
%mask1 = and i32 %A, 7
|
||||||
|
%tst1 = icmp eq i32 %mask1, 0
|
||||||
|
|
||||||
|
%mask2 = and i32 %A, 39
|
||||||
|
%tst2 = icmp eq i32 %mask2, 0
|
||||||
|
|
||||||
|
%res = or i1 %tst1, %tst2
|
||||||
|
ret i1 %res
|
||||||
|
}
|
||||||
|
|
||||||
|
define i1 @masked_and_notallones(i32 %A) {
|
||||||
|
; CHECK-LABEL: @masked_and_notallones
|
||||||
|
; CHECK: [[MASK:%.*]] = and i32 %A, 7
|
||||||
|
; CHECK: icmp ne i32 [[MASK]], 7
|
||||||
|
; CHECK-NOT: and i32 %A, 39
|
||||||
|
; CHECK: ret i1
|
||||||
|
|
||||||
|
%mask1 = and i32 %A, 7
|
||||||
|
%tst1 = icmp ne i32 %mask1, 7
|
||||||
|
|
||||||
|
%mask2 = and i32 %A, 39
|
||||||
|
%tst2 = icmp ne i32 %mask2, 39
|
||||||
|
|
||||||
|
%res = and i1 %tst1, %tst2
|
||||||
|
ret i1 %res
|
||||||
|
}
|
||||||
|
|
||||||
|
define i1 @masked_or_allones(i32 %A) {
|
||||||
|
; CHECK-LABEL: @masked_or_allones
|
||||||
|
; CHECK: [[MASK:%.*]] = and i32 %A, 7
|
||||||
|
; CHECK: icmp eq i32 [[MASK]], 7
|
||||||
|
; CHECK-NOT: and i32 %A, 39
|
||||||
|
; CHECK: ret i1
|
||||||
|
|
||||||
|
%mask1 = and i32 %A, 7
|
||||||
|
%tst1 = icmp eq i32 %mask1, 7
|
||||||
|
|
||||||
|
%mask2 = and i32 %A, 39
|
||||||
|
%tst2 = icmp eq i32 %mask2, 39
|
||||||
|
|
||||||
|
%res = or i1 %tst1, %tst2
|
||||||
|
ret i1 %res
|
||||||
|
}
|
||||||
|
|
||||||
|
define i1 @masked_and_notA(i32 %A) {
|
||||||
|
; CHECK-LABEL: @masked_and_notA
|
||||||
|
; CHECK: [[MASK:%.*]] = and i32 %A, 39
|
||||||
|
; CHECK: icmp ne i32 [[MASK]], %A
|
||||||
|
; CHECK-NOT: and i32 %A, 7
|
||||||
|
; CHECK: ret i1
|
||||||
|
|
||||||
|
%mask1 = and i32 %A, 7
|
||||||
|
%tst1 = icmp ne i32 %mask1, %A
|
||||||
|
|
||||||
|
%mask2 = and i32 %A, 39
|
||||||
|
%tst2 = icmp ne i32 %mask2, %A
|
||||||
|
|
||||||
|
%res = and i1 %tst1, %tst2
|
||||||
|
ret i1 %res
|
||||||
|
}
|
||||||
|
|
||||||
|
define i1 @masked_or_A(i32 %A) {
|
||||||
|
; CHECK-LABEL: @masked_or_A
|
||||||
|
; CHECK: [[MASK:%.*]] = and i32 %A, 39
|
||||||
|
; CHECK: icmp eq i32 [[MASK]], %A
|
||||||
|
; CHECK-NOT: and i32 %A, 7
|
||||||
|
; CHECK: ret i1
|
||||||
|
|
||||||
|
%mask1 = and i32 %A, 7
|
||||||
|
%tst1 = icmp eq i32 %mask1, %A
|
||||||
|
|
||||||
|
%mask2 = and i32 %A, 39
|
||||||
|
%tst2 = icmp eq i32 %mask2, %A
|
||||||
|
|
||||||
|
%res = or i1 %tst1, %tst2
|
||||||
|
ret i1 %res
|
||||||
|
}
|
||||||
|
|
||||||
|
define i1 @masked_or_allzeroes_notoptimised(i32 %A) {
|
||||||
|
; CHECK-LABEL: @masked_or_allzeroes_notoptimised
|
||||||
|
; CHECK: [[MASK:%.*]] = and i32 %A, 15
|
||||||
|
; CHECK: icmp eq i32 [[MASK]], 0
|
||||||
|
; CHECK: [[MASK:%.*]] = and i32 %A, 39
|
||||||
|
; CHECK: icmp eq i32 [[MASK]], 0
|
||||||
|
; CHECK: ret i1
|
||||||
|
|
||||||
|
%mask1 = and i32 %A, 15
|
||||||
|
%tst1 = icmp eq i32 %mask1, 0
|
||||||
|
|
||||||
|
%mask2 = and i32 %A, 39
|
||||||
|
%tst2 = icmp eq i32 %mask2, 0
|
||||||
|
|
||||||
|
%res = or i1 %tst1, %tst2
|
||||||
|
ret i1 %res
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user