[llvm] [InstCombine] Canonicalize complex boolean expressions into ~((y | z) ^ x) via 3-input truth table (PR #149530)
Yingwei Zheng via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 22 09:38:38 PDT 2025
================
@@ -0,0 +1,375 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+; Tests for GitHub issue #97044 - Boolean expression canonicalization
+define i32 @test0_4way_or(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @test0_4way_or(
+; CHECK-NEXT: [[TMP1:%.*]] = or i32 [[Y:%.*]], [[Z:%.*]]
+; CHECK-NEXT: [[TMP2:%.*]] = xor i32 [[TMP1]], [[X:%.*]]
+; CHECK-NEXT: [[OR13:%.*]] = xor i32 [[TMP2]], -1
+; CHECK-NEXT: ret i32 [[OR13]]
+;
+ %not = xor i32 %z, -1
+ %and = and i32 %y, %not
+ %and1 = and i32 %and, %x
+ %not2 = xor i32 %y, -1
+ %and3 = and i32 %x, %not2
+ %and4 = and i32 %and3, %z
+ %or = or i32 %and1, %and4
+ %not5 = xor i32 %x, -1
+ %not6 = xor i32 %y, -1
+ %and7 = and i32 %not5, %not6
+ %not8 = xor i32 %z, -1
+ %and9 = and i32 %and7, %not8
+ %or10 = or i32 %or, %and9
+ %and11 = and i32 %x, %y
+ %and12 = and i32 %and11, %z
+ %or13 = or i32 %or10, %and12
+ ret i32 %or13
+}
+define i32 @test1_xor_pattern(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @test1_xor_pattern(
+; CHECK-NEXT: [[TMP1:%.*]] = or i32 [[Y:%.*]], [[Z:%.*]]
+; CHECK-NEXT: [[TMP2:%.*]] = xor i32 [[TMP1]], [[X:%.*]]
+; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[TMP2]], -1
+; CHECK-NEXT: ret i32 [[XOR]]
+;
+ %not = xor i32 %z, -1
+ %and = and i32 %x, %y
+ %not1 = xor i32 %x, -1
+ %not2 = xor i32 %y, -1
+ %and3 = and i32 %not1, %not2
+ %or = or i32 %and, %and3
+ %and4 = and i32 %not, %or
+ %and5 = and i32 %x, %y
+ %and6 = and i32 %x, %not2
+ %or7 = or i32 %and5, %and6
+ %and8 = and i32 %z, %or7
+ %xor = xor i32 %and4, %and8
+ ret i32 %xor
+}
+define i32 @test2_nested_xor(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @test2_nested_xor(
+; CHECK-NEXT: [[TMP1:%.*]] = or i32 [[Y:%.*]], [[Z:%.*]]
+; CHECK-NEXT: [[TMP2:%.*]] = xor i32 [[TMP1]], [[X:%.*]]
+; CHECK-NEXT: [[TMP3:%.*]] = xor i32 [[TMP2]], [[Y]]
+; CHECK-NEXT: ret i32 [[TMP3]]
+;
+ %and = and i32 %x, %y
+ %not = xor i32 %x, -1
+ %not1 = xor i32 %y, -1
+ %and2 = and i32 %not, %not1
+ %or = or i32 %and, %and2
+ %and3 = and i32 %x, %y
+ %not4 = xor i32 %y, -1
+ %and5 = and i32 %x, %not4
+ %or6 = or i32 %and3, %and5
+ %xor = xor i32 %or, %or6
+ %not7 = xor i32 %y, -1
+ %and8 = and i32 %z, %not7
+ %and9 = and i32 %xor, %and8
+ %xor10 = xor i32 %or, %and9
+ %xor11 = xor i32 %xor10, %y
+ %xor12 = xor i32 %xor11, -1
+ ret i32 %xor12
+}
+define i32 @test3_already_optimal(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @test3_already_optimal(
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[Y:%.*]], [[Z:%.*]]
+; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[OR]], [[X:%.*]]
+; CHECK-NEXT: [[NOT:%.*]] = xor i32 [[XOR]], -1
+; CHECK-NEXT: ret i32 [[NOT]]
+;
+ %or = or i32 %y, %z
+ %xor = xor i32 %or, %x
+ %not = xor i32 %xor, -1
+ ret i32 %not
+}
+
+define i32 @test_add_as_leaf(i32 %x, i32 %y, i32 %c) {
+; CHECK-LABEL: @test_add_as_leaf(
+; CHECK-NEXT: [[ADD:%.*]] = add i32 [[X:%.*]], [[C:%.*]]
+; CHECK-NEXT: [[NOT3:%.*]] = xor i32 [[X]], -1
+; CHECK-NEXT: [[AND4:%.*]] = and i32 [[ADD]], [[NOT3]]
+; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[Y:%.*]], [[AND4]]
+; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[TMP1]], [[ADD]]
+; CHECK-NEXT: ret i32 [[XOR]]
+;
+ %add = add i32 %x, %c
+ %not1 = xor i32 %add, -1
+ %and1 = and i32 %not1, %y
+ %not2 = xor i32 %y, -1
+ %and2 = and i32 %add, %not2
+ %or = or i32 %and1, %and2
+ %and3 = and i32 %x, %y
+ %not3 = xor i32 %x, -1
+ %and4 = and i32 %not3, %add
+ %xor = xor i32 %or, %and4
+ ret i32 %xor
+}
+
+define i32 @test_sub_as_leaf(i32 %a, i32 %b, i32 %offset) {
+; CHECK-LABEL: @test_sub_as_leaf(
+; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[A:%.*]], [[OFFSET:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = or i32 [[B:%.*]], [[SUB]]
+; CHECK-NEXT: [[TMP2:%.*]] = xor i32 [[TMP1]], [[A]]
+; CHECK-NEXT: [[RESULT:%.*]] = xor i32 [[TMP2]], -1
+; CHECK-NEXT: ret i32 [[RESULT]]
+;
+ %sub = sub i32 %a, %offset
+ %not1 = xor i32 %sub, -1
+ %and1 = and i32 %not1, %b
+ %and2 = and i32 %and1, %a
+ %not2 = xor i32 %b, -1
+ %and3 = and i32 %a, %not2
+ %and4 = and i32 %and3, %sub
+ %or = or i32 %and2, %and4
+ %not3 = xor i32 %a, -1
+ %not4 = xor i32 %b, -1
+ %and5 = and i32 %not3, %not4
+ %not5 = xor i32 %sub, -1
+ %and6 = and i32 %and5, %not5
+ %or2 = or i32 %or, %and6
+ %and7 = and i32 %a, %b
+ %and8 = and i32 %and7, %sub
+ %result = or i32 %or2, %and8
+ ret i32 %result
+}
+
+; ==============================
+; Negative Tests
+; ==============================
+
+define i32 @negative_non_bitwise_add(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @negative_non_bitwise_add(
+; CHECK-NEXT: [[ADD1:%.*]] = add i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[ADD2:%.*]] = add i32 [[ADD1]], [[Z:%.*]]
+; CHECK-NEXT: ret i32 [[ADD2]]
+;
+ %add1 = add i32 %x, %y
+ %add2 = add i32 %add1, %z
+ ret i32 %add2
+}
+define i32 @negative_two_variables(i32 %x, i32 %y) {
+; CHECK-LABEL: @negative_two_variables(
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[NOT:%.*]] = xor i32 [[AND]], -1
+; CHECK-NEXT: ret i32 [[NOT]]
+;
+ %and = and i32 %x, %y
+ %not = xor i32 %and, -1
+ ret i32 %not
+}
+define i32 @negative_four_variables(i32 %x, i32 %y, i32 %z, i32 %w) {
+; CHECK-LABEL: @negative_four_variables(
+; CHECK-NEXT: [[AND1:%.*]] = and i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[AND2:%.*]] = and i32 [[Z:%.*]], [[W:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND1]], [[AND2]]
+; CHECK-NEXT: ret i32 [[OR]]
+;
+ %and1 = and i32 %x, %y
+ %and2 = and i32 %z, %w
+ %or = or i32 %and1, %and2
+ ret i32 %or
+}
+define i32 @negative_simple_expression(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @negative_simple_expression(
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND]], [[Z:%.*]]
+; CHECK-NEXT: ret i32 [[OR]]
+;
+ %and = and i32 %x, %y
+ %or = or i32 %and, %z
+ ret i32 %or
+}
+define i32 @negative_different_basic_blocks(i32 %x, i32 %y, i32 %z, i1 %cond) {
+; CHECK-LABEL: @negative_different_basic_blocks(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[AND1:%.*]] = and i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: br i1 [[COND:%.*]], label [[IF_TRUE:%.*]], label [[IF_FALSE:%.*]]
+; CHECK: if.true:
+; CHECK-NEXT: [[AND2:%.*]] = and i32 [[AND1]], [[Z:%.*]]
+; CHECK-NEXT: ret i32 [[AND2]]
+; CHECK: if.false:
+; CHECK-NEXT: ret i32 [[AND1]]
+;
+entry:
+ %and1 = and i32 %x, %y
+ br i1 %cond, label %if.true, label %if.false
+if.true:
+ %and2 = and i32 %and1, %z
+ ret i32 %and2
+if.false:
+ ret i32 %and1
+}
+define i32 @negative_two_vars_one_const(i32 %x, i32 %y) {
+; CHECK-LABEL: @negative_two_vars_one_const(
+; CHECK-NEXT: [[AND1:%.*]] = and i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[AND2:%.*]] = and i32 [[AND1]], 42
+; CHECK-NEXT: [[AND3_DEMORGAN:%.*]] = or i32 [[X]], [[Y]]
+; CHECK-NEXT: [[AND3:%.*]] = xor i32 [[AND3_DEMORGAN]], -1
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND2]], [[AND3]]
+; CHECK-NEXT: ret i32 [[OR]]
+;
+ %and1 = and i32 %x, %y
+ %and2 = and i32 %and1, 42
+ %not_x = xor i32 %x, -1
+ %not_y = xor i32 %y, -1
+ %and3 = and i32 %not_x, %not_y
+ %or = or i32 %and2, %and3
+ ret i32 %or
+}
+
+define i32 @negative_one_var_two_consts(i32 %x) {
+; CHECK-LABEL: @negative_one_var_two_consts(
+; CHECK-NEXT: [[AND2:%.*]] = and i32 [[X:%.*]], 7
+; CHECK-NEXT: [[NOT_X:%.*]] = and i32 [[X]], 3
+; CHECK-NEXT: [[AND3:%.*]] = xor i32 [[NOT_X]], 3
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[AND2]], [[AND3]]
+; CHECK-NEXT: ret i32 [[OR]]
+;
+ %and1 = and i32 %x, 15
+ %and2 = and i32 %and1, 7
+ %not_x = xor i32 %x, -1
+ %and3 = and i32 %not_x, 3
+ %or = or i32 %and2, %and3
+ ret i32 %or
+}
+
+define i32 @negative_const_pattern_match(i32 %x, i32 %y) {
+; CHECK-LABEL: @negative_const_pattern_match(
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[Y:%.*]], 255
+; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[OR]], [[X:%.*]]
+; CHECK-NEXT: [[NOT:%.*]] = xor i32 [[XOR]], -1
+; CHECK-NEXT: ret i32 [[NOT]]
+;
+ %or = or i32 %y, 255
+ %xor = xor i32 %or, %x
+ %not = xor i32 %xor, -1
+ ret i32 %not
+}
+
+define i32 @negative_mixed_vars_consts(i32 %x, i32 %y) {
+; CHECK-LABEL: @negative_mixed_vars_consts(
+; CHECK-NEXT: [[AND1:%.*]] = and i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[AND2:%.*]] = and i32 [[AND1]], 96
+; CHECK-NEXT: [[NOT_X:%.*]] = and i32 [[X]], 170
+; CHECK-NEXT: [[AND3:%.*]] = xor i32 [[NOT_X]], 170
+; CHECK-NEXT: [[OR1:%.*]] = or i32 [[AND2]], [[AND3]]
+; CHECK-NEXT: [[AND4:%.*]] = and i32 [[Y]], 204
+; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[OR1]], [[AND4]]
+; CHECK-NEXT: ret i32 [[XOR]]
+;
+ %and1 = and i32 %x, %y
+ %and2 = and i32 %and1, 96
+ %not_x = xor i32 %x, -1
+ %and3 = and i32 %not_x, 170
+ %or1 = or i32 %and2, %and3
+ %and4 = and i32 %y, 204
+ %xor = xor i32 %or1, %and4
+ ret i32 %xor
+}
+
+define i32 @negative_const_blocks_extraction(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @negative_const_blocks_extraction(
+; CHECK-NEXT: [[AND1:%.*]] = and i32 [[X:%.*]], 42
+; CHECK-NEXT: [[AND2:%.*]] = and i32 [[Y:%.*]], [[Z:%.*]]
+; CHECK-NEXT: [[OR1:%.*]] = or i32 [[AND1]], [[AND2]]
+; CHECK-NEXT: [[AND3:%.*]] = and i32 [[X]], 24
+; CHECK-NEXT: [[XOR:%.*]] = xor i32 [[OR1]], [[AND3]]
+; CHECK-NEXT: ret i32 [[XOR]]
+;
+ %and1 = and i32 %x, 42
+ %and2 = and i32 %y, %z
+ %or1 = or i32 %and1, %and2
+ %and3 = and i32 %x, 24
+ %xor = xor i32 %or1, %and3
+ ret i32 %xor
+}
+
+define i32 @negative_single_use_add(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @negative_single_use_add(
+; CHECK-NEXT: [[ADD:%.*]] = add i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[ADD]], [[Z:%.*]]
+; CHECK-NEXT: ret i32 [[AND]]
+;
+ %add = add i32 %x, %y ; Single-use non-bitwise op
+ %and = and i32 %add, %z ; Only 2 variables: %add, %z (should not optimize)
+ ret i32 %and
+}
+
+; ==============================
+; Multi-use Tests
+; ==============================
+declare void @use(i32)
+define i32 @multi_use_not(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @multi_use_not(
+; CHECK-NEXT: [[NOT1:%.*]] = xor i32 [[X:%.*]], -1
+; CHECK-NEXT: call void @use(i32 [[NOT1]])
+; CHECK-NEXT: [[TMP1:%.*]] = and i32 [[Y:%.*]], [[Z:%.*]]
+; CHECK-NEXT: [[AND2:%.*]] = and i32 [[TMP1]], [[NOT1]]
+; CHECK-NEXT: ret i32 [[AND2]]
+;
+ %not1 = xor i32 %x, -1
+ call void @use(i32 %not1)
+ %and1 = and i32 %not1, %y
+ %and2 = and i32 %and1, %z
+ ret i32 %and2
+}
+define i32 @multi_use_binop(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @multi_use_binop(
+; CHECK-NEXT: [[AND1:%.*]] = and i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: call void @use(i32 [[AND1]])
+; CHECK-NEXT: [[AND2:%.*]] = and i32 [[AND1]], [[Z:%.*]]
+; CHECK-NEXT: ret i32 [[AND2]]
+;
+ %and1 = and i32 %x, %y
+ call void @use(i32 %and1)
+ %and2 = and i32 %and1, %z
+ ret i32 %and2
+}
+define i32 @multi_use_multiple(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: @multi_use_multiple(
+; CHECK-NEXT: [[NOT1:%.*]] = xor i32 [[X:%.*]], -1
+; CHECK-NEXT: [[AND1:%.*]] = and i32 [[Y:%.*]], [[NOT1]]
+; CHECK-NEXT: call void @use(i32 [[NOT1]])
+; CHECK-NEXT: call void @use(i32 [[AND1]])
+; CHECK-NEXT: [[AND2:%.*]] = and i32 [[AND1]], [[Z:%.*]]
+; CHECK-NEXT: ret i32 [[AND2]]
+;
+ %not1 = xor i32 %x, -1
+ %and1 = and i32 %not1, %y
+ call void @use(i32 %not1)
+ call void @use(i32 %and1)
+ %and2 = and i32 %and1, %z
+ ret i32 %and2
+}
+
+define i32 @multi_use_add_as_variable(i32 %x, i32 %y, i32 %offset) {
+; CHECK-LABEL: @multi_use_add_as_variable(
+; CHECK-NEXT: [[ADD:%.*]] = add i32 [[X:%.*]], [[OFFSET:%.*]]
+; CHECK-NEXT: call void @use(i32 [[ADD]])
+; CHECK-NEXT: [[TMP1:%.*]] = or i32 [[Y:%.*]], [[ADD]]
+; CHECK-NEXT: [[TMP2:%.*]] = xor i32 [[TMP1]], [[X]]
+; CHECK-NEXT: [[RESULT:%.*]] = xor i32 [[TMP2]], -1
+; CHECK-NEXT: ret i32 [[RESULT]]
+;
+ %add = add i32 %x, %offset ; Multi-use non-bitwise op
+ call void @use(i32 %add) ; Extra use
+ %not1 = xor i32 %add, -1
+ %and1 = and i32 %not1, %y
+ %and2 = and i32 %and1, %x
+ %not2 = xor i32 %y, -1
+ %and3 = and i32 %x, %not2
+ %and4 = and i32 %and3, %add
+ %or = or i32 %and2, %and4
+ %not3 = xor i32 %x, -1
+ %not4 = xor i32 %y, -1
+ %and5 = and i32 %not3, %not4
+ %not5 = xor i32 %add, -1
+ %and6 = and i32 %and5, %not5
+ %or2 = or i32 %or, %and6
+ %and7 = and i32 %x, %y
+ %and8 = and i32 %and7, %add
+ %result = or i32 %or2, %and8
+ ret i32 %result
+}
----------------
dtcxzyw wrote:
Missing new line.
https://github.com/llvm/llvm-project/pull/149530
More information about the llvm-commits
mailing list