<div dir="ltr">Hi,<div><br></div><div>The new test is failing for us in non-asserts builds: it appears that the mapping from rule name to rule number (used by --aarch64prelegalizercombinerhelper-only-enable-rule) is guarded by #ifndef NDEBUG (se getRuleIdxForIdentifier in generated lib/Target/AArch64/AArch64GenPreLegalizeGICombiner.inc).</div><div><br></div><div>I think the test is just missing a REQUIRES: asserts.</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, 2 Sept 2021 at 15:05, Jessica Paquette via llvm-commits <<a href="mailto:llvm-commits@lists.llvm.org">llvm-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
Author: Jessica Paquette<br>
Date: 2021-09-02T15:05:31-07:00<br>
New Revision: 844d8e0337560bd73b5a78fd8ff162b1b262b46f<br>
<br>
URL: <a href="https://github.com/llvm/llvm-project/commit/844d8e0337560bd73b5a78fd8ff162b1b262b46f" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/844d8e0337560bd73b5a78fd8ff162b1b262b46f</a><br>
DIFF: <a href="https://github.com/llvm/llvm-project/commit/844d8e0337560bd73b5a78fd8ff162b1b262b46f.diff" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/844d8e0337560bd73b5a78fd8ff162b1b262b46f.diff</a><br>
<br>
LOG: [GlobalISel] Combine icmp eq/ne x, 0/1 -> x when x == 0 or 1<br>
<br>
This adds the following combines:<br>
<br>
```<br>
x = ... 0 or 1<br>
c = icmp eq x, 1<br>
<br>
-><br>
<br>
c = x<br>
```<br>
<br>
and<br>
<br>
```<br>
x = ... 0 or 1<br>
c = icmp ne x, 0<br>
<br>
-><br>
<br>
c = x<br>
```<br>
<br>
When the target's true value for the relevant types is 1.<br>
<br>
This showed up in the following situation:<br>
<br>
<a href="https://godbolt.org/z/M5jKexWTW" rel="noreferrer" target="_blank">https://godbolt.org/z/M5jKexWTW</a><br>
<br>
SDAG currently supports the `ne` case, but not the `eq` case. This can probably<br>
be further generalized, but I don't feel like thinking that hard right now.<br>
<br>
This gives some minor code size improvements across the board on CTMark at<br>
-Os for AArch64. (0.1% for 7zip and pairlocalalign in particular.)<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D109130" rel="noreferrer" target="_blank">https://reviews.llvm.org/D109130</a><br>
<br>
Added: <br>
    llvm/test/CodeGen/AArch64/GlobalISel/combine-icmp-to-lhs-known-bits.mir<br>
<br>
Modified: <br>
    llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h<br>
    llvm/include/llvm/Target/GlobalISel/Combine.td<br>
    llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp<br>
<br>
Removed: <br>
<br>
<br>
<br>
################################################################################<br>
diff  --git a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h<br>
index d892a7525a6d3..8bc89cbc40bb5 100644<br>
--- a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h<br>
+++ b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h<br>
@@ -553,6 +553,12 @@ class CombinerHelper {<br>
   /// or false constant based off of KnownBits information.<br>
   bool matchICmpToTrueFalseKnownBits(MachineInstr &MI, int64_t &MatchInfo);<br>
<br>
+  /// \returns true if a G_ICMP \p MI can be replaced with its LHS based off of<br>
+  /// KnownBits information.<br>
+  bool<br>
+  matchICmpToLHSKnownBits(MachineInstr &MI,<br>
+                          std::function<void(MachineIRBuilder &)> &MatchInfo);<br>
+<br>
   bool matchBitfieldExtractFromSExtInReg(<br>
       MachineInstr &MI, std::function<void(MachineIRBuilder &)> &MatchInfo);<br>
   /// Match: and (lshr x, cst), mask -> ubfx x, cst, width<br>
<br>
diff  --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td<br>
index e65073a1d28d0..1808aa6e6e66a 100644<br>
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td<br>
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td<br>
@@ -644,6 +644,12 @@ def icmp_to_true_false_known_bits : GICombineRule<<br>
          [{ return Helper.matchICmpToTrueFalseKnownBits(*${d}, ${matchinfo}); }]),<br>
   (apply [{ Helper.replaceInstWithConstant(*${d}, ${matchinfo}); }])>;<br>
<br>
+def icmp_to_lhs_known_bits : GICombineRule<<br>
+  (defs root:$root, build_fn_matchinfo:$info),<br>
+  (match (wip_match_opcode G_ICMP):$root,<br>
+         [{ return Helper.matchICmpToLHSKnownBits(*${root}, ${info}); }]),<br>
+  (apply [{ Helper.applyBuildFn(*${root}, ${info}); }])>;<br>
+<br>
 def bitfield_extract_from_and : GICombineRule<<br>
   (defs root:$root, build_fn_matchinfo:$info),<br>
   (match (wip_match_opcode G_AND):$root,<br>
@@ -702,7 +708,7 @@ def const_combines : GICombineGroup<[constant_fp_op, const_ptradd_to_i2p,<br>
<br>
 def known_bits_simplifications : GICombineGroup<[<br>
   redundant_and, redundant_sext_inreg, redundant_or, urem_pow2_to_mask,<br>
-  zext_trunc_fold, icmp_to_true_false_known_bits]>;<br>
+  zext_trunc_fold, icmp_to_true_false_known_bits, icmp_to_lhs_known_bits]>;<br>
<br>
 def width_reduction_combines : GICombineGroup<[reduce_shl_of_extend,<br>
                                                narrow_binop_feeding_and]>;<br>
<br>
diff  --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp<br>
index 34322cb22d25c..cd363d7d449c6 100644<br>
--- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp<br>
+++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp<br>
@@ -4118,6 +4118,48 @@ bool CombinerHelper::matchICmpToTrueFalseKnownBits(MachineInstr &MI,<br>
   return true;<br>
 }<br>
<br>
+bool CombinerHelper::matchICmpToLHSKnownBits(<br>
+    MachineInstr &MI, std::function<void(MachineIRBuilder &)> &MatchInfo) {<br>
+  assert(MI.getOpcode() == TargetOpcode::G_ICMP);<br>
+  // Given:<br>
+  //<br>
+  // %x = G_WHATEVER (... x is known to be 0 or 1 ...)<br>
+  // %cmp = G_ICMP ne %x, 0<br>
+  //<br>
+  // Or:<br>
+  //<br>
+  // %x = G_WHATEVER (... x is known to be 0 or 1 ...)<br>
+  // %cmp = G_ICMP eq %x, 1<br>
+  //<br>
+  // We can replace %cmp with %x assuming true is 1 on the target.<br>
+  auto Pred = static_cast<CmpInst::Predicate>(MI.getOperand(1).getPredicate());<br>
+  if (!CmpInst::isEquality(Pred))<br>
+    return false;<br>
+  Register Dst = MI.getOperand(0).getReg();<br>
+  LLT DstTy = MRI.getType(Dst);<br>
+  if (getICmpTrueVal(getTargetLowering(), DstTy.isVector(),<br>
+                     /* IsFP = */ false) != 1)<br>
+    return false;<br>
+  int64_t OneOrZero = Pred == CmpInst::ICMP_EQ;<br>
+  if (!mi_match(MI.getOperand(3).getReg(), MRI, m_SpecificICst(OneOrZero)))<br>
+    return false;<br>
+  Register LHS = MI.getOperand(2).getReg();<br>
+  auto KnownLHS = KB->getKnownBits(LHS);<br>
+  if (KnownLHS.getMinValue() != 0 || KnownLHS.getMaxValue() != 1)<br>
+    return false;<br>
+  // Make sure replacing Dst with the LHS is a legal operation.<br>
+  LLT LHSTy = MRI.getType(LHS);<br>
+  unsigned LHSSize = LHSTy.getSizeInBits();<br>
+  unsigned DstSize = DstTy.getSizeInBits();<br>
+  unsigned Op = TargetOpcode::COPY;<br>
+  if (DstSize != LHSSize)<br>
+    Op = DstSize < LHSSize ? TargetOpcode::G_TRUNC : TargetOpcode::G_ZEXT;<br>
+  if (!isLegalOrBeforeLegalizer({Op, {DstTy, LHSTy}}))<br>
+    return false;<br>
+  MatchInfo = [=](MachineIRBuilder &B) { B.buildInstr(Op, {Dst}, {LHS}); };<br>
+  return true;<br>
+}<br>
+<br>
 /// Form a G_SBFX from a G_SEXT_INREG fed by a right shift.<br>
 bool CombinerHelper::matchBitfieldExtractFromSExtInReg(<br>
     MachineInstr &MI, std::function<void(MachineIRBuilder &)> &MatchInfo) {<br>
<br>
diff  --git a/llvm/test/CodeGen/AArch64/GlobalISel/combine-icmp-to-lhs-known-bits.mir b/llvm/test/CodeGen/AArch64/GlobalISel/combine-icmp-to-lhs-known-bits.mir<br>
new file mode 100644<br>
index 0000000000000..c9a648b893fa2<br>
--- /dev/null<br>
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/combine-icmp-to-lhs-known-bits.mir<br>
@@ -0,0 +1,229 @@<br>
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py<br>
+<br>
+# RUN: llc -mtriple aarch64 -run-pass=aarch64-prelegalizer-combiner --aarch64prelegalizercombinerhelper-only-enable-rule="icmp_to_lhs_known_bits" -global-isel -verify-machineinstrs %s -o - | FileCheck %s<br>
+<br>
+...<br>
+---<br>
+name:            apply_ne<br>
+alignment:       4<br>
+tracksRegLiveness: true<br>
+machineFunctionInfo: {}<br>
+body:             |<br>
+  bb.0:<br>
+    liveins: $w0<br>
+    ; CHECK-LABEL: name: apply_ne<br>
+    ; CHECK: liveins: $w0<br>
+    ; CHECK: %x:_(s32) = COPY $w0<br>
+    ; CHECK: %one:_(s32) = G_CONSTANT i32 1<br>
+    ; CHECK: %known_zero_or_one:_(s32) = G_AND %x, %one<br>
+    ; CHECK: %cmp:_(s1) = G_TRUNC %known_zero_or_one(s32)<br>
+    ; CHECK: %ext:_(s32) = G_ZEXT %cmp(s1)<br>
+    ; CHECK: $w0 = COPY %ext(s32)<br>
+    ; CHECK: RET_ReallyLR implicit $w0<br>
+    %x:_(s32) = COPY $w0<br>
+    %one:_(s32) = G_CONSTANT i32 1<br>
+    %known_zero_or_one:_(s32) = G_AND %x, %one<br>
+    %zero:_(s32) = G_CONSTANT i32 0<br>
+    %cmp:_(s1) = G_ICMP intpred(ne), %known_zero_or_one(s32), %zero<br>
+    %ext:_(s32) = G_ZEXT %cmp(s1)<br>
+    $w0 = COPY %ext(s32)<br>
+    RET_ReallyLR implicit $w0<br>
+<br>
+...<br>
+---<br>
+name:            apply_eq<br>
+alignment:       4<br>
+tracksRegLiveness: true<br>
+machineFunctionInfo: {}<br>
+body:             |<br>
+  bb.0:<br>
+    liveins: $w0<br>
+    ; CHECK-LABEL: name: apply_eq<br>
+    ; CHECK: liveins: $w0<br>
+    ; CHECK: %x:_(s32) = COPY $w0<br>
+    ; CHECK: %one:_(s32) = G_CONSTANT i32 1<br>
+    ; CHECK: %known_zero_or_one:_(s32) = G_AND %x, %one<br>
+    ; CHECK: %cmp:_(s1) = G_TRUNC %known_zero_or_one(s32)<br>
+    ; CHECK: %ext:_(s32) = G_ZEXT %cmp(s1)<br>
+    ; CHECK: $w0 = COPY %ext(s32)<br>
+    ; CHECK: RET_ReallyLR implicit $w0<br>
+    %x:_(s32) = COPY $w0<br>
+    %one:_(s32) = G_CONSTANT i32 1<br>
+    %known_zero_or_one:_(s32) = G_AND %x, %one<br>
+    %cmp:_(s1) = G_ICMP intpred(eq), %known_zero_or_one(s32), %one<br>
+    %ext:_(s32) = G_ZEXT %cmp(s1)<br>
+    $w0 = COPY %ext(s32)<br>
+    RET_ReallyLR implicit $w0<br>
+<br>
+...<br>
+---<br>
+name:            dont_apply_wrong_cst_eq<br>
+alignment:       4<br>
+tracksRegLiveness: true<br>
+machineFunctionInfo: {}<br>
+body:             |<br>
+  bb.0:<br>
+    liveins: $w0<br>
+    ; Wrong constant on the RHS of the compare.<br>
+<br>
+    ; CHECK-LABEL: name: dont_apply_wrong_cst_eq<br>
+    ; CHECK: liveins: $w0<br>
+    ; CHECK: %x:_(s32) = COPY $w0<br>
+    ; CHECK: %one:_(s32) = G_CONSTANT i32 1<br>
+    ; CHECK: %known_zero_or_one:_(s32) = G_AND %x, %one<br>
+    ; CHECK: %wrong_cst:_(s32) = G_CONSTANT i32 10<br>
+    ; CHECK: %cmp:_(s1) = G_ICMP intpred(eq), %known_zero_or_one(s32), %wrong_cst<br>
+    ; CHECK: %ext:_(s32) = G_ZEXT %cmp(s1)<br>
+    ; CHECK: $w0 = COPY %ext(s32)<br>
+    ; CHECK: RET_ReallyLR implicit $w0<br>
+    %x:_(s32) = COPY $w0<br>
+    %one:_(s32) = G_CONSTANT i32 1<br>
+    %known_zero_or_one:_(s32) = G_AND %x, %one<br>
+    %wrong_cst:_(s32) = G_CONSTANT i32 10<br>
+    %cmp:_(s1) = G_ICMP intpred(eq), %known_zero_or_one(s32), %wrong_cst<br>
+    %ext:_(s32) = G_ZEXT %cmp(s1)<br>
+    $w0 = COPY %ext(s32)<br>
+    RET_ReallyLR implicit $w0<br>
+<br>
+...<br>
+---<br>
+name:            dont_apply_wrong_cst_ne<br>
+alignment:       4<br>
+tracksRegLiveness: true<br>
+machineFunctionInfo: {}<br>
+body:             |<br>
+  bb.0:<br>
+    liveins: $w0<br>
+    ; Wrong constant on the RHS of the compare.<br>
+<br>
+    ; CHECK-LABEL: name: dont_apply_wrong_cst_ne<br>
+    ; CHECK: liveins: $w0<br>
+    ; CHECK: %x:_(s32) = COPY $w0<br>
+    ; CHECK: %one:_(s32) = G_CONSTANT i32 1<br>
+    ; CHECK: %known_zero_or_one:_(s32) = G_AND %x, %one<br>
+    ; CHECK: %wrong_cst:_(s32) = G_CONSTANT i32 10<br>
+    ; CHECK: %cmp:_(s1) = G_ICMP intpred(ne), %known_zero_or_one(s32), %wrong_cst<br>
+    ; CHECK: %ext:_(s32) = G_ZEXT %cmp(s1)<br>
+    ; CHECK: $w0 = COPY %ext(s32)<br>
+    ; CHECK: RET_ReallyLR implicit $w0<br>
+    %x:_(s32) = COPY $w0<br>
+    %one:_(s32) = G_CONSTANT i32 1<br>
+    %known_zero_or_one:_(s32) = G_AND %x, %one<br>
+    %wrong_cst:_(s32) = G_CONSTANT i32 10<br>
+    %cmp:_(s1) = G_ICMP intpred(ne), %known_zero_or_one(s32), %wrong_cst<br>
+    %ext:_(s32) = G_ZEXT %cmp(s1)<br>
+    $w0 = COPY %ext(s32)<br>
+    RET_ReallyLR implicit $w0<br>
+<br>
+...<br>
+---<br>
+name:            dont_apply_vector<br>
+alignment:       4<br>
+tracksRegLiveness: true<br>
+machineFunctionInfo: {}<br>
+body:             |<br>
+  bb.0:<br>
+    liveins: $x0<br>
+    ; True is -1 for vectors on AArch64 so we don't want to combine.<br>
+<br>
+    ; CHECK-LABEL: name: dont_apply_vector<br>
+    ; CHECK: liveins: $x0<br>
+    ; CHECK: %x:_(<2 x s32>) = COPY $x0<br>
+    ; CHECK: %one:_(s32) = G_CONSTANT i32 1<br>
+    ; CHECK: %one_vec:_(<2 x s32>) = G_BUILD_VECTOR %one(s32), %one(s32)<br>
+    ; CHECK: %vec_and:_(<2 x s32>) = G_AND %x, %one_vec<br>
+    ; CHECK: %zero:_(s32) = G_CONSTANT i32 0<br>
+    ; CHECK: %zero_vec:_(<2 x s32>) = G_BUILD_VECTOR %zero(s32), %zero(s32)<br>
+    ; CHECK: %cmp:_(<2 x s1>) = G_ICMP intpred(ne), %vec_and(<2 x s32>), %zero_vec<br>
+    ; CHECK: %elt:_(s1) = G_EXTRACT_VECTOR_ELT %cmp(<2 x s1>), %zero(s32)<br>
+    ; CHECK: %ext:_(s32) = G_ZEXT %elt(s1)<br>
+    ; CHECK: $w0 = COPY %ext(s32)<br>
+    ; CHECK: RET_ReallyLR implicit $w0<br>
+    %x:_(<2 x s32>) = COPY $x0<br>
+    %one:_(s32) = G_CONSTANT i32 1<br>
+    %one_vec:_(<2 x s32>) = G_BUILD_VECTOR %one, %one<br>
+    %vec_and:_(<2 x s32>) = G_AND %x, %one_vec<br>
+    %zero:_(s32) = G_CONSTANT i32 0<br>
+    %zero_vec:_(<2 x s32>) = G_BUILD_VECTOR %zero, %zero<br>
+    %cmp:_(<2 x s1>) = G_ICMP intpred(ne), %vec_and(<2 x s32>), %zero_vec<br>
+    %elt:_(s1) = G_EXTRACT_VECTOR_ELT %cmp, %zero<br>
+    %ext:_(s32) = G_ZEXT %elt(s1)<br>
+    $w0 = COPY %ext(s32)<br>
+    RET_ReallyLR implicit $w0<br>
+<br>
+...<br>
+---<br>
+name:            apply_no_zext_or_trunc<br>
+alignment:       4<br>
+tracksRegLiveness: true<br>
+machineFunctionInfo: {}<br>
+body:             |<br>
+  bb.0:<br>
+    liveins: $w0<br>
+    ; CHECK-LABEL: name: apply_no_zext_or_trunc<br>
+    ; CHECK: liveins: $w0<br>
+    ; CHECK: %x:_(s32) = COPY $w0<br>
+    ; CHECK: %one:_(s32) = G_CONSTANT i32 1<br>
+    ; CHECK: %known_zero_or_one:_(s32) = G_AND %x, %one<br>
+    ; CHECK: %cmp:_(s32) = COPY %known_zero_or_one(s32)<br>
+    ; CHECK: $w0 = COPY %cmp(s32)<br>
+    ; CHECK: RET_ReallyLR implicit $w0<br>
+    %x:_(s32) = COPY $w0<br>
+    %one:_(s32) = G_CONSTANT i32 1<br>
+    %known_zero_or_one:_(s32) = G_AND %x, %one<br>
+    %zero:_(s32) = G_CONSTANT i32 0<br>
+    %cmp:_(s32) = G_ICMP intpred(ne), %known_zero_or_one(s32), %zero<br>
+    $w0 = COPY %cmp(s32)<br>
+    RET_ReallyLR implicit $w0<br>
+<br>
+...<br>
+---<br>
+name:            apply_wide_cmp<br>
+alignment:       4<br>
+tracksRegLiveness: true<br>
+machineFunctionInfo: {}<br>
+body:             |<br>
+  bb.0:<br>
+    liveins: $w0<br>
+    ; CHECK-LABEL: name: apply_wide_cmp<br>
+    ; CHECK: liveins: $w0<br>
+    ; CHECK: %x:_(s64) = COPY $x0<br>
+    ; CHECK: %one:_(s64) = G_CONSTANT i64 1<br>
+    ; CHECK: %known_zero_or_one:_(s64) = G_AND %x, %one<br>
+    ; CHECK: %cmp:_(s64) = COPY %known_zero_or_one(s64)<br>
+    ; CHECK: %trunc:_(s32) = G_TRUNC %cmp(s64)<br>
+    ; CHECK: $w0 = COPY %trunc(s32)<br>
+    ; CHECK: RET_ReallyLR implicit $w0<br>
+    %x:_(s64) = COPY $x0<br>
+    %one:_(s64) = G_CONSTANT i64 1<br>
+    %known_zero_or_one:_(s64) = G_AND %x, %one<br>
+    %zero:_(s64) = G_CONSTANT i64 0<br>
+    %cmp:_(s64) = G_ICMP intpred(ne), %known_zero_or_one(s64), %zero<br>
+    %trunc:_(s32) = G_TRUNC %cmp<br>
+    $w0 = COPY %trunc(s32)<br>
+    RET_ReallyLR implicit $w0<br>
+<br>
+...<br>
+---<br>
+name:            apply_narrow_lhs<br>
+alignment:       4<br>
+tracksRegLiveness: true<br>
+machineFunctionInfo: {}<br>
+body:             |<br>
+  bb.0:<br>
+    liveins: $w0<br>
+    ; CHECK-LABEL: name: apply_narrow_lhs<br>
+    ; CHECK: liveins: $w0<br>
+    ; CHECK: %x:_(s32) = COPY $w0<br>
+    ; CHECK: %one:_(s32) = G_CONSTANT i32 1<br>
+    ; CHECK: %known_zero_or_one:_(s32) = G_AND %x, %one<br>
+    ; CHECK: %cmp:_(s64) = G_ZEXT %known_zero_or_one(s32)<br>
+    ; CHECK: $x0 = COPY %cmp(s64)<br>
+    ; CHECK: RET_ReallyLR implicit $x0<br>
+    %x:_(s32) = COPY $w0<br>
+    %one:_(s32) = G_CONSTANT i32 1<br>
+    %known_zero_or_one:_(s32) = G_AND %x, %one<br>
+    %zero:_(s32) = G_CONSTANT i32 0<br>
+    %cmp:_(s64) = G_ICMP intpred(ne), %known_zero_or_one(s32), %zero<br>
+    $x0 = COPY %cmp(s64)<br>
+    RET_ReallyLR implicit $x0<br>
<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br>
</blockquote></div>