Skip to content

Commit df68c22

Browse files
dtcxzywIcohedron
authored andcommitted
[InstCombine] Fold xor of bittests into bittest of xor'd value (llvm#125676)
Motivating case: https://github.com/llvm/llvm-project/blob/64927af52a3bedf2b20d6cdd98bb47d9bba630f9/llvm/lib/Analysis/ValueTracking.cpp#L8600-L8602 It is translated into `xor (X & 2) != 0, (Y & 2) != 0`. Alive2: https://alive2.llvm.org/ce/z/dJehZ8
1 parent c9e5d11 commit df68c22

File tree

2 files changed

+170
-3
lines changed

2 files changed

+170
-3
lines changed

llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4150,9 +4150,6 @@ Value *InstCombinerImpl::foldXorOfICmps(ICmpInst *LHS, ICmpInst *RHS,
41504150
}
41514151
}
41524152

4153-
// TODO: This can be generalized to compares of non-signbits using
4154-
// decomposeBitTestICmp(). It could be enhanced more by using (something like)
4155-
// foldLogOpOfMaskedICmps().
41564153
const APInt *LC, *RC;
41574154
if (match(LHS1, m_APInt(LC)) && match(RHS1, m_APInt(RC)) &&
41584155
LHS0->getType() == RHS0->getType() &&
@@ -4200,6 +4197,21 @@ Value *InstCombinerImpl::foldXorOfICmps(ICmpInst *LHS, ICmpInst *RHS,
42004197
}
42014198
}
42024199
}
4200+
4201+
// Fold (icmp eq/ne (X & Pow2), 0) ^ (icmp eq/ne (Y & Pow2), 0) into
4202+
// (icmp eq/ne ((X ^ Y) & Pow2), 0)
4203+
Value *X, *Y;
4204+
const APInt *Mask;
4205+
if (ICmpInst::isEquality(PredL) && ICmpInst::isEquality(PredR) &&
4206+
LC->isZero() && RC->isZero() && LHS->hasOneUse() && RHS->hasOneUse() &&
4207+
match(LHS0, m_And(m_Value(X), m_Power2(Mask))) &&
4208+
match(RHS0, m_And(m_Value(Y), m_SpecificInt(*Mask)))) {
4209+
Value *Xor = Builder.CreateXor(X, Y);
4210+
Value *And = Builder.CreateAnd(Xor, *Mask);
4211+
return Builder.CreateICmp(PredL == PredR ? ICmpInst::ICMP_NE
4212+
: ICmpInst::ICMP_EQ,
4213+
And, ConstantInt::getNullValue(Xor->getType()));
4214+
}
42034215
}
42044216

42054217
// Instead of trying to imitate the folds for and/or, decompose this 'xor'

llvm/test/Transforms/InstCombine/xor-icmps.ll

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,3 +319,158 @@ define i1 @xor_icmp_to_icmp_add_multiuse2(i32 %a) {
319319
%cmp3 = xor i1 %cmp, %cmp1
320320
ret i1 %cmp3
321321
}
322+
323+
define i1 @test_xor_of_bittest_ne_ne(i8 %x, i8 %y) {
324+
; CHECK-LABEL: @test_xor_of_bittest_ne_ne(
325+
; CHECK-NEXT: [[Y:%.*]] = xor i8 [[X:%.*]], [[Y1:%.*]]
326+
; CHECK-NEXT: [[MASK2:%.*]] = and i8 [[Y]], 2
327+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i8 [[MASK2]], 0
328+
; CHECK-NEXT: ret i1 [[CMP2]]
329+
;
330+
%mask1 = and i8 %x, 2
331+
%cmp1 = icmp ne i8 %mask1, 0
332+
%mask2 = and i8 %y, 2
333+
%cmp2 = icmp ne i8 %mask2, 0
334+
%xor = xor i1 %cmp1, %cmp2
335+
ret i1 %xor
336+
}
337+
338+
define i1 @test_xor_of_bittest_eq_eq(i8 %x, i8 %y) {
339+
; CHECK-LABEL: @test_xor_of_bittest_eq_eq(
340+
; CHECK-NEXT: [[Y:%.*]] = xor i8 [[X:%.*]], [[Y1:%.*]]
341+
; CHECK-NEXT: [[MASK2:%.*]] = and i8 [[Y]], 2
342+
; CHECK-NEXT: [[XOR:%.*]] = icmp ne i8 [[MASK2]], 0
343+
; CHECK-NEXT: ret i1 [[XOR]]
344+
;
345+
%mask1 = and i8 %x, 2
346+
%cmp1 = icmp eq i8 %mask1, 0
347+
%mask2 = and i8 %y, 2
348+
%cmp2 = icmp eq i8 %mask2, 0
349+
%xor = xor i1 %cmp1, %cmp2
350+
ret i1 %xor
351+
}
352+
353+
define i1 @test_xor_of_bittest_ne_eq(i8 %x, i8 %y) {
354+
; CHECK-LABEL: @test_xor_of_bittest_ne_eq(
355+
; CHECK-NEXT: [[Y:%.*]] = xor i8 [[X:%.*]], [[Y1:%.*]]
356+
; CHECK-NEXT: [[MASK2:%.*]] = and i8 [[Y]], 2
357+
; CHECK-NEXT: [[CMP2:%.*]] = icmp eq i8 [[MASK2]], 0
358+
; CHECK-NEXT: ret i1 [[CMP2]]
359+
;
360+
%mask1 = and i8 %x, 2
361+
%cmp1 = icmp ne i8 %mask1, 0
362+
%mask2 = and i8 %y, 2
363+
%cmp2 = icmp eq i8 %mask2, 0
364+
%xor = xor i1 %cmp1, %cmp2
365+
ret i1 %xor
366+
}
367+
368+
define i1 @test_xor_of_bittest_eq_ne(i8 %x, i8 %y) {
369+
; CHECK-LABEL: @test_xor_of_bittest_eq_ne(
370+
; CHECK-NEXT: [[X:%.*]] = xor i8 [[X1:%.*]], [[Y:%.*]]
371+
; CHECK-NEXT: [[MASK1:%.*]] = and i8 [[X]], 2
372+
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i8 [[MASK1]], 0
373+
; CHECK-NEXT: ret i1 [[CMP1]]
374+
;
375+
%mask1 = and i8 %x, 2
376+
%cmp1 = icmp eq i8 %mask1, 0
377+
%mask2 = and i8 %y, 2
378+
%cmp2 = icmp ne i8 %mask2, 0
379+
%xor = xor i1 %cmp1, %cmp2
380+
ret i1 %xor
381+
}
382+
383+
define i1 @test_xor_of_bittest_ne_ne_multiuse1(i8 %x, i8 %y) {
384+
; CHECK-LABEL: @test_xor_of_bittest_ne_ne_multiuse1(
385+
; CHECK-NEXT: [[MASK1:%.*]] = and i8 [[X:%.*]], 2
386+
; CHECK-NEXT: call void @usei8(i8 [[MASK1]])
387+
; CHECK-NEXT: [[MASK2:%.*]] = and i8 [[Y:%.*]], 2
388+
; CHECK-NEXT: call void @usei8(i8 [[MASK2]])
389+
; CHECK-NEXT: [[TMP1:%.*]] = xor i8 [[X]], [[Y]]
390+
; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], 2
391+
; CHECK-NEXT: [[XOR:%.*]] = icmp ne i8 [[TMP2]], 0
392+
; CHECK-NEXT: ret i1 [[XOR]]
393+
;
394+
%mask1 = and i8 %x, 2
395+
call void @usei8(i8 %mask1)
396+
%cmp1 = icmp ne i8 %mask1, 0
397+
%mask2 = and i8 %y, 2
398+
call void @usei8(i8 %mask2)
399+
%cmp2 = icmp ne i8 %mask2, 0
400+
%xor = xor i1 %cmp1, %cmp2
401+
ret i1 %xor
402+
}
403+
404+
; Negative tests
405+
406+
define i1 @test_xor_of_bittest_ne_ne_type_mismatch(i8 %x, i16 %y) {
407+
; CHECK-LABEL: @test_xor_of_bittest_ne_ne_type_mismatch(
408+
; CHECK-NEXT: [[MASK1:%.*]] = and i8 [[X:%.*]], 2
409+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[MASK1]], 0
410+
; CHECK-NEXT: [[MASK2:%.*]] = and i16 [[Y:%.*]], 2
411+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i16 [[MASK2]], 0
412+
; CHECK-NEXT: [[XOR:%.*]] = xor i1 [[CMP1]], [[CMP2]]
413+
; CHECK-NEXT: ret i1 [[XOR]]
414+
;
415+
%mask1 = and i8 %x, 2
416+
%cmp1 = icmp ne i8 %mask1, 0
417+
%mask2 = and i16 %y, 2
418+
%cmp2 = icmp ne i16 %mask2, 0
419+
%xor = xor i1 %cmp1, %cmp2
420+
ret i1 %xor
421+
}
422+
423+
define i1 @test_xor_of_bittest_ne_ne_mask_mismatch(i8 %x, i8 %y) {
424+
; CHECK-LABEL: @test_xor_of_bittest_ne_ne_mask_mismatch(
425+
; CHECK-NEXT: [[MASK1:%.*]] = and i8 [[X:%.*]], 4
426+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[MASK1]], 0
427+
; CHECK-NEXT: [[MASK2:%.*]] = and i8 [[Y:%.*]], 2
428+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i8 [[MASK2]], 0
429+
; CHECK-NEXT: [[XOR:%.*]] = xor i1 [[CMP1]], [[CMP2]]
430+
; CHECK-NEXT: ret i1 [[XOR]]
431+
;
432+
%mask1 = and i8 %x, 4
433+
%cmp1 = icmp ne i8 %mask1, 0
434+
%mask2 = and i8 %y, 2
435+
%cmp2 = icmp ne i8 %mask2, 0
436+
%xor = xor i1 %cmp1, %cmp2
437+
ret i1 %xor
438+
}
439+
440+
define i1 @test_xor_of_bittest_ne_ne_nonpower2(i8 %x, i8 %y) {
441+
; CHECK-LABEL: @test_xor_of_bittest_ne_ne_nonpower2(
442+
; CHECK-NEXT: [[MASK1:%.*]] = and i8 [[X:%.*]], 3
443+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[MASK1]], 0
444+
; CHECK-NEXT: [[MASK2:%.*]] = and i8 [[Y:%.*]], 3
445+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i8 [[MASK2]], 0
446+
; CHECK-NEXT: [[XOR:%.*]] = xor i1 [[CMP1]], [[CMP2]]
447+
; CHECK-NEXT: ret i1 [[XOR]]
448+
;
449+
%mask1 = and i8 %x, 3
450+
%cmp1 = icmp ne i8 %mask1, 0
451+
%mask2 = and i8 %y, 3
452+
%cmp2 = icmp ne i8 %mask2, 0
453+
%xor = xor i1 %cmp1, %cmp2
454+
ret i1 %xor
455+
}
456+
457+
define i1 @test_xor_of_bittest_ne_ne_multiuse2(i8 %x, i8 %y) {
458+
; CHECK-LABEL: @test_xor_of_bittest_ne_ne_multiuse2(
459+
; CHECK-NEXT: [[MASK1:%.*]] = and i8 [[X:%.*]], 2
460+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ne i8 [[MASK1]], 0
461+
; CHECK-NEXT: call void @use(i1 [[CMP1]])
462+
; CHECK-NEXT: [[MASK2:%.*]] = and i8 [[Y:%.*]], 2
463+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ne i8 [[MASK2]], 0
464+
; CHECK-NEXT: [[XOR:%.*]] = xor i1 [[CMP1]], [[CMP2]]
465+
; CHECK-NEXT: ret i1 [[XOR]]
466+
;
467+
%mask1 = and i8 %x, 2
468+
%cmp1 = icmp ne i8 %mask1, 0
469+
call void @use(i1 %cmp1)
470+
%mask2 = and i8 %y, 2
471+
%cmp2 = icmp ne i8 %mask2, 0
472+
%xor = xor i1 %cmp1, %cmp2
473+
ret i1 %xor
474+
}
475+
476+
declare void @usei8(i8)

0 commit comments

Comments
 (0)