Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4993,6 +4993,11 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {
if (Instruction *Abs = canonicalizeAbs(I, Builder))
return Abs;

if (KnownBits::haveNoCommonBitsSet(
computeKnownBits(I.getOperand(0), /*Depth=*/0, &I),
computeKnownBits(I.getOperand(1), /*Depth=*/0, &I)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can try to short circuit the first operand query if the second is unknown

return BinaryOperator::CreateDisjointOr(I.getOperand(0), I.getOperand(1));

// Otherwise, if all else failed, try to hoist the xor-by-constant:
// (X ^ C) ^ Y --> (X ^ Y) ^ C
// Just like we do in other places, we completely avoid the fold
Expand Down
49 changes: 49 additions & 0 deletions llvm/test/Transforms/InstCombine/xor.ll
Original file line number Diff line number Diff line change
Expand Up @@ -1664,3 +1664,52 @@ entry:
%or = or <2 x i32> %add, %c
ret <2 x i32> %or
}

declare i32 @callee()

define i32 @xor_disjoint() {
; CHECK-LABEL: @xor_disjoint(
; CHECK-NEXT: [[CALL1:%.*]] = call i32 @callee(), !range [[RNG0:![0-9]+]]
; CHECK-NEXT: [[XOR:%.*]] = or disjoint i32 [[CALL1]], 4096
; CHECK-NEXT: ret i32 [[XOR]]
;
%call1 = call i32 @callee(), !range !0
%xor = xor i32 %call1, 4096
ret i32 %xor
}

define i32 @xor_disjoint2() {
; CHECK-LABEL: @xor_disjoint2(
; CHECK-NEXT: [[CALL1:%.*]] = call i32 @callee(), !range [[RNG1:![0-9]+]]
; CHECK-NEXT: [[XOR:%.*]] = or disjoint i32 [[CALL1]], 512
; CHECK-NEXT: ret i32 [[XOR]]
;
%call1 = call i32 @callee(), !range !1
%xor = xor i32 %call1, 512
ret i32 %xor
}

define i32 @xor_non_disjoint() {
; CHECK-LABEL: @xor_non_disjoint(
; CHECK-NEXT: [[CALL1:%.*]] = call i32 @callee(), !range [[RNG0]]
; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[CALL1]], 1024
; CHECK-NEXT: ret i32 [[XOR]]
;
%call1 = call i32 @callee(), !range !0
%xor = xor i32 %call1, 1024
ret i32 %xor
}

define i32 @xor_non_disjoint2() {
; CHECK-LABEL: @xor_non_disjoint2(
; CHECK-NEXT: [[CALL1:%.*]] = call i32 @callee(), !range [[RNG1]]
; CHECK-NEXT: [[XOR:%.*]] = and i32 [[CALL1]], 511
; CHECK-NEXT: ret i32 [[XOR]]
;
%call1 = call i32 @callee(), !range !1
%xor = xor i32 %call1, 1024
ret i32 %xor
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test a vector version. Also could use alive2 proof link

!0 = !{ i32 0, i32 2048 }
!1 = !{ i32 1024, i32 1536 }