<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>