Skip to content

Commit 5c348f6

Browse files
author
Thorsten Schütt
authored
[GlobalIsel] Canonicalize G_ICMP (#108755)
As a side-effect, we start constant folding icmps. Split out from #105991.
1 parent 08efa23 commit 5c348f6

File tree

11 files changed

+345
-81
lines changed

11 files changed

+345
-81
lines changed

llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "llvm/ADT/DenseMap.h"
2121
#include "llvm/ADT/SmallVector.h"
2222
#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
23+
#include "llvm/CodeGen/GlobalISel/Utils.h"
2324
#include "llvm/CodeGen/Register.h"
2425
#include "llvm/CodeGenTypes/LowLevelType.h"
2526
#include "llvm/IR/InstrTypes.h"
@@ -909,6 +910,8 @@ class CombinerHelper {
909910
bool matchCastOfBuildVector(const MachineInstr &CastMI,
910911
const MachineInstr &BVMI, BuildFnTy &MatchInfo);
911912

913+
bool matchCanonicalizeICmp(const MachineInstr &MI, BuildFnTy &MatchInfo);
914+
912915
private:
913916
/// Checks for legality of an indexed variant of \p LdSt.
914917
bool isIndexedLoadStoreLegal(GLoadStore &LdSt) const;
@@ -1023,6 +1026,9 @@ class CombinerHelper {
10231026
bool tryFoldLogicOfFCmps(GLogicalBinOp *Logic, BuildFnTy &MatchInfo);
10241027

10251028
bool isCastFree(unsigned Opcode, LLT ToTy, LLT FromTy) const;
1029+
1030+
bool constantFoldICmp(const GICmp &ICmp, const GIConstant &LHSCst,
1031+
const GIConstant &RHSCst, BuildFnTy &MatchInfo);
10261032
};
10271033
} // namespace llvm
10281034

llvm/include/llvm/CodeGen/GlobalISel/GenericMachineInstrs.h

+10
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,16 @@ class GExtOrTruncOp : public GCastOp {
952952
};
953953
};
954954

955+
/// Represents a splat vector.
956+
class GSplatVector : public GenericMachineInstr {
957+
public:
958+
Register getScalarReg() const { return getOperand(1).getReg(); }
959+
960+
static bool classof(const MachineInstr *MI) {
961+
return MI->getOpcode() == TargetOpcode::G_SPLAT_VECTOR;
962+
};
963+
};
964+
955965
} // namespace llvm
956966

957967
#endif // LLVM_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H

llvm/include/llvm/CodeGen/GlobalISel/Utils.h

+39
Original file line numberDiff line numberDiff line change
@@ -593,5 +593,44 @@ bool isGuaranteedNotToBeUndef(Register Reg, const MachineRegisterInfo &MRI,
593593
/// estimate of the type.
594594
Type *getTypeForLLT(LLT Ty, LLVMContext &C);
595595

596+
/// An integer-like constant.
597+
///
598+
/// It abstracts over scalar, fixed-length vectors, and scalable vectors.
599+
/// In the common case, it provides a common API and feels like an APInt,
600+
/// while still providing low-level access.
601+
/// It can be used for constant-folding.
602+
///
603+
/// bool isZero()
604+
/// abstracts over the kind.
605+
///
606+
/// switch(const.getKind())
607+
/// {
608+
/// }
609+
/// provides low-level access.
610+
class GIConstant {
611+
public:
612+
enum class GIConstantKind { Scalar, FixedVector, ScalableVector };
613+
614+
private:
615+
GIConstantKind Kind;
616+
SmallVector<APInt> Values;
617+
APInt Value;
618+
619+
public:
620+
GIConstant(ArrayRef<APInt> Values)
621+
: Kind(GIConstantKind::FixedVector), Values(Values) {};
622+
GIConstant(const APInt &Value, GIConstantKind Kind)
623+
: Kind(Kind), Value(Value) {};
624+
625+
/// Returns the kind of of this constant, e.g, Scalar.
626+
GIConstantKind getKind() const { return Kind; }
627+
628+
/// Returns the value, if this constant is a scalar.
629+
APInt getScalarValue() const;
630+
631+
static std::optional<GIConstant> getConstant(Register Const,
632+
const MachineRegisterInfo &MRI);
633+
};
634+
596635
} // End namespace llvm.
597636
#endif

llvm/include/llvm/Target/GlobalISel/Combine.td

+18-7
Original file line numberDiff line numberDiff line change
@@ -1007,9 +1007,6 @@ def double_icmp_zero_or_combine: GICombineRule<
10071007
(G_ICMP $root, $p, $ordst, 0))
10081008
>;
10091009

1010-
def double_icmp_zero_and_or_combine : GICombineGroup<[double_icmp_zero_and_combine,
1011-
double_icmp_zero_or_combine]>;
1012-
10131010
def and_or_disjoint_mask : GICombineRule<
10141011
(defs root:$root, build_fn_matchinfo:$info),
10151012
(match (wip_match_opcode G_AND):$root,
@@ -1918,6 +1915,20 @@ def cast_combines: GICombineGroup<[
19181915
integer_of_truncate
19191916
]>;
19201917

1918+
def canonicalize_icmp : GICombineRule<
1919+
(defs root:$root, build_fn_matchinfo:$matchinfo),
1920+
(match (G_ICMP $root, $pred, $lhs, $rhs):$cmp,
1921+
[{ return Helper.matchCanonicalizeICmp(*${cmp}, ${matchinfo}); }]),
1922+
(apply [{ Helper.applyBuildFn(*${cmp}, ${matchinfo}); }])>;
1923+
1924+
def icmp_combines: GICombineGroup<[
1925+
canonicalize_icmp,
1926+
icmp_to_true_false_known_bits,
1927+
icmp_to_lhs_known_bits,
1928+
double_icmp_zero_and_combine,
1929+
double_icmp_zero_or_combine,
1930+
redundant_binop_in_equality
1931+
]>;
19211932

19221933
// FIXME: These should use the custom predicate feature once it lands.
19231934
def undef_combines : GICombineGroup<[undef_to_fp_zero, undef_to_int_zero,
@@ -1951,7 +1962,7 @@ def const_combines : GICombineGroup<[constant_fold_fp_ops, const_ptradd_to_i2p,
19511962

19521963
def known_bits_simplifications : GICombineGroup<[
19531964
redundant_and, redundant_sext_inreg, redundant_or, urem_pow2_to_mask,
1954-
zext_trunc_fold, icmp_to_true_false_known_bits, icmp_to_lhs_known_bits,
1965+
zext_trunc_fold,
19551966
sext_inreg_to_zext_inreg]>;
19561967

19571968
def width_reduction_combines : GICombineGroup<[reduce_shl_of_extend,
@@ -1984,7 +1995,7 @@ def all_combines : GICombineGroup<[integer_reassoc_combines, trivial_combines,
19841995
combine_extracted_vector_load,
19851996
undef_combines, identity_combines, phi_combines,
19861997
simplify_add_to_sub, hoist_logic_op_with_same_opcode_hands, shifts_too_big,
1987-
reassocs, ptr_add_immed_chain,
1998+
reassocs, ptr_add_immed_chain, icmp_combines,
19881999
shl_ashr_to_sext_inreg, sext_inreg_of_load,
19892000
width_reduction_combines, select_combines,
19902001
known_bits_simplifications,
@@ -1998,9 +2009,9 @@ def all_combines : GICombineGroup<[integer_reassoc_combines, trivial_combines,
19982009
constant_fold_cast_op, fabs_fneg_fold,
19992010
intdiv_combines, mulh_combines, redundant_neg_operands,
20002011
and_or_disjoint_mask, fma_combines, fold_binop_into_select,
2001-
sub_add_reg, select_to_minmax, redundant_binop_in_equality,
2012+
sub_add_reg, select_to_minmax,
20022013
fsub_to_fneg, commute_constant_to_rhs, match_ands, match_ors,
2003-
combine_concat_vector, double_icmp_zero_and_or_combine, match_addos,
2014+
combine_concat_vector, match_addos,
20042015
sext_trunc, zext_trunc, prefer_sign_combines, combine_shuffle_concat]>;
20052016

20062017
// A combine group used to for prelegalizer combiners at -O0. The combines in

llvm/lib/CodeGen/GlobalISel/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ add_llvm_component_library(LLVMGlobalISel
77
Combiner.cpp
88
CombinerHelper.cpp
99
CombinerHelperCasts.cpp
10+
CombinerHelperCompares.cpp
1011
CombinerHelperVectorOps.cpp
1112
GIMatchTableExecutor.cpp
1213
GISelChangeObserver.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//===- CombinerHelperCompares.cpp------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file implements CombinerHelper for G_ICMP.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
#include "llvm/CodeGen/GlobalISel/CombinerHelper.h"
13+
#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
14+
#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h"
15+
#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
16+
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
17+
#include "llvm/CodeGen/GlobalISel/Utils.h"
18+
#include "llvm/CodeGen/LowLevelTypeUtils.h"
19+
#include "llvm/CodeGen/MachineInstr.h"
20+
#include "llvm/CodeGen/MachineOperand.h"
21+
#include "llvm/CodeGen/MachineRegisterInfo.h"
22+
#include "llvm/CodeGen/TargetOpcodes.h"
23+
#include "llvm/IR/Instructions.h"
24+
#include "llvm/Support/Casting.h"
25+
#include "llvm/Support/ErrorHandling.h"
26+
#include <cstdlib>
27+
28+
#define DEBUG_TYPE "gi-combiner"
29+
30+
using namespace llvm;
31+
32+
bool CombinerHelper::constantFoldICmp(const GICmp &ICmp,
33+
const GIConstant &LHSCst,
34+
const GIConstant &RHSCst,
35+
BuildFnTy &MatchInfo) {
36+
if (LHSCst.getKind() != GIConstant::GIConstantKind::Scalar)
37+
return false;
38+
39+
Register Dst = ICmp.getReg(0);
40+
LLT DstTy = MRI.getType(Dst);
41+
42+
if (!isConstantLegalOrBeforeLegalizer(DstTy))
43+
return false;
44+
45+
CmpInst::Predicate Pred = ICmp.getCond();
46+
APInt LHS = LHSCst.getScalarValue();
47+
APInt RHS = RHSCst.getScalarValue();
48+
49+
bool Result = ICmpInst::compare(LHS, RHS, Pred);
50+
51+
MatchInfo = [=](MachineIRBuilder &B) {
52+
if (Result)
53+
B.buildConstant(Dst, getICmpTrueVal(getTargetLowering(),
54+
/*IsVector=*/DstTy.isVector(),
55+
/*IsFP=*/false));
56+
else
57+
B.buildConstant(Dst, 0);
58+
};
59+
60+
return true;
61+
}
62+
63+
bool CombinerHelper::matchCanonicalizeICmp(const MachineInstr &MI,
64+
BuildFnTy &MatchInfo) {
65+
const GICmp *Cmp = cast<GICmp>(&MI);
66+
67+
Register Dst = Cmp->getReg(0);
68+
Register LHS = Cmp->getLHSReg();
69+
Register RHS = Cmp->getRHSReg();
70+
71+
CmpInst::Predicate Pred = Cmp->getCond();
72+
assert(CmpInst::isIntPredicate(Pred) && "Not an integer compare!");
73+
if (auto CLHS = GIConstant::getConstant(LHS, MRI)) {
74+
if (auto CRHS = GIConstant::getConstant(RHS, MRI))
75+
return constantFoldICmp(*Cmp, *CLHS, *CRHS, MatchInfo);
76+
77+
// If we have a constant, make sure it is on the RHS.
78+
std::swap(LHS, RHS);
79+
Pred = CmpInst::getSwappedPredicate(Pred);
80+
81+
MatchInfo = [=](MachineIRBuilder &B) { B.buildICmp(Pred, Dst, LHS, RHS); };
82+
return true;
83+
}
84+
85+
return false;
86+
}

llvm/lib/CodeGen/GlobalISel/Utils.cpp

+40
Original file line numberDiff line numberDiff line change
@@ -1968,3 +1968,43 @@ Type *llvm::getTypeForLLT(LLT Ty, LLVMContext &C) {
19681968
Ty.getElementCount());
19691969
return IntegerType::get(C, Ty.getSizeInBits());
19701970
}
1971+
1972+
APInt llvm::GIConstant::getScalarValue() const {
1973+
assert(Kind == GIConstantKind::Scalar && "Expected scalar constant");
1974+
1975+
return Value;
1976+
}
1977+
1978+
std::optional<GIConstant>
1979+
llvm::GIConstant::getConstant(Register Const, const MachineRegisterInfo &MRI) {
1980+
MachineInstr *Constant = getDefIgnoringCopies(Const, MRI);
1981+
1982+
if (GSplatVector *Splat = dyn_cast<GSplatVector>(Constant)) {
1983+
std::optional<ValueAndVReg> MayBeConstant =
1984+
getIConstantVRegValWithLookThrough(Splat->getScalarReg(), MRI);
1985+
if (!MayBeConstant)
1986+
return std::nullopt;
1987+
return GIConstant(MayBeConstant->Value, GIConstantKind::ScalableVector);
1988+
}
1989+
1990+
if (GBuildVector *Build = dyn_cast<GBuildVector>(Constant)) {
1991+
SmallVector<APInt> Values;
1992+
unsigned NumSources = Build->getNumSources();
1993+
for (unsigned I = 0; I < NumSources; ++I) {
1994+
Register SrcReg = Build->getSourceReg(I);
1995+
std::optional<ValueAndVReg> MayBeConstant =
1996+
getIConstantVRegValWithLookThrough(SrcReg, MRI);
1997+
if (!MayBeConstant)
1998+
return std::nullopt;
1999+
Values.push_back(MayBeConstant->Value);
2000+
}
2001+
return GIConstant(Values);
2002+
}
2003+
2004+
std::optional<ValueAndVReg> MayBeConstant =
2005+
getIConstantVRegValWithLookThrough(Const, MRI);
2006+
if (!MayBeConstant)
2007+
return std::nullopt;
2008+
2009+
return GIConstant(MayBeConstant->Value, GIConstantKind::Scalar);
2010+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
2+
# RUN: llc -o - -mtriple=aarch64-unknown-unknown -run-pass=aarch64-prelegalizer-combiner -verify-machineinstrs %s | FileCheck %s --check-prefixes=CHECK
3+
4+
---
5+
name: test_icmp_canon
6+
body: |
7+
bb.1:
8+
; CHECK-LABEL: name: test_icmp_canon
9+
; CHECK: %lhs:_(s64) = G_CONSTANT i64 11
10+
; CHECK-NEXT: %rhs:_(s64) = COPY $x0
11+
; CHECK-NEXT: %res:_(s32) = G_ICMP intpred(sgt), %rhs(s64), %lhs
12+
; CHECK-NEXT: $w0 = COPY %res(s32)
13+
%lhs:_(s64) = G_CONSTANT i64 11
14+
%rhs:_(s64) = COPY $x0
15+
%res:_(s32) = G_ICMP intpred(slt), %lhs(s64), %rhs
16+
$w0 = COPY %res(s32)
17+
...
18+
---
19+
name: test_icmp_no_canon
20+
body: |
21+
bb.1:
22+
; CHECK-LABEL: name: test_icmp_no_canon
23+
; CHECK: %lhs:_(s64) = COPY $x0
24+
; CHECK-NEXT: %rhs:_(s64) = G_CONSTANT i64 11
25+
; CHECK-NEXT: %res:_(s32) = G_ICMP intpred(slt), %lhs(s64), %rhs
26+
; CHECK-NEXT: $w0 = COPY %res(s32)
27+
%lhs:_(s64) = COPY $x0
28+
%rhs:_(s64) = G_CONSTANT i64 11
29+
%res:_(s32) = G_ICMP intpred(slt), %lhs(s64), %rhs
30+
$w0 = COPY %res(s32)
31+
...
32+
---
33+
name: test_icmp_canon_bv
34+
body: |
35+
bb.1:
36+
; CHECK-LABEL: name: test_icmp_canon_bv
37+
; CHECK: %opaque1:_(s64) = COPY $x0
38+
; CHECK-NEXT: %opaque2:_(s64) = COPY $x0
39+
; CHECK-NEXT: %const1:_(s64) = G_CONSTANT i64 11
40+
; CHECK-NEXT: %const2:_(s64) = G_CONSTANT i64 12
41+
; CHECK-NEXT: %lhs:_(<2 x s64>) = G_BUILD_VECTOR %const1(s64), %const2(s64)
42+
; CHECK-NEXT: %rhs:_(<2 x s64>) = G_BUILD_VECTOR %opaque1(s64), %opaque2(s64)
43+
; CHECK-NEXT: %res:_(<2 x s32>) = G_ICMP intpred(sgt), %rhs(<2 x s64>), %lhs
44+
; CHECK-NEXT: $x0 = COPY %res(<2 x s32>)
45+
%opaque1:_(s64) = COPY $x0
46+
%opaque2:_(s64) = COPY $x0
47+
%const1:_(s64) = G_CONSTANT i64 11
48+
%const2:_(s64) = G_CONSTANT i64 12
49+
%lhs:_(<2 x s64>) = G_BUILD_VECTOR %const1(s64), %const2(s64)
50+
%rhs:_(<2 x s64>) = G_BUILD_VECTOR %opaque1(s64), %opaque2(s64)
51+
%res:_(<2 x s32>) = G_ICMP intpred(slt), %lhs(<2 x s64>), %rhs
52+
$x0 = COPY %res(<2 x s32>)
53+
...
54+
---
55+
name: test_icmp_no_canon_bv_neither_const
56+
body: |
57+
bb.1:
58+
; CHECK-LABEL: name: test_icmp_no_canon_bv
59+
; CHECK: %opaque1:_(s64) = COPY $x0
60+
; CHECK-NEXT: %opaque2:_(s64) = COPY $x0
61+
; CHECK-NEXT: %const1:_(s64) = G_CONSTANT i64 11
62+
; CHECK-NEXT: %const2:_(s64) = G_CONSTANT i64 12
63+
; CHECK-NEXT: %lhs:_(<2 x s64>) = G_BUILD_VECTOR %const1(s64), %opaque2(s64)
64+
; CHECK-NEXT: %rhs:_(<2 x s64>) = G_BUILD_VECTOR %opaque1(s64), %const2(s64)
65+
; CHECK-NEXT: %res:_(<2 x s32>) = G_ICMP intpred(slt), %lhs(<2 x s64>), %rhs
66+
; CHECK-NEXT: $x0 = COPY %res(<2 x s32>)
67+
%opaque1:_(s64) = COPY $x0
68+
%opaque2:_(s64) = COPY $x0
69+
%const1:_(s64) = G_CONSTANT i64 11
70+
%const2:_(s64) = G_CONSTANT i64 12
71+
%lhs:_(<2 x s64>) = G_BUILD_VECTOR %const1(s64), %opaque2(s64)
72+
%rhs:_(<2 x s64>) = G_BUILD_VECTOR %opaque1(s64), %const2(s64)
73+
%res:_(<2 x s32>) = G_ICMP intpred(slt), %lhs(<2 x s64>), %rhs
74+
$x0 = COPY %res(<2 x s32>)
75+
...
76+
---
77+
name: test_icmp_canon_splat
78+
body: |
79+
bb.1:
80+
; CHECK-LABEL: name: test_icmp_canon_splat
81+
; CHECK: %const:_(s64) = G_CONSTANT i64 11
82+
; CHECK-NEXT: %lhs:_(<vscale x 2 x s64>) = G_SPLAT_VECTOR %const(s64)
83+
; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x1
84+
; CHECK-NEXT: %rhs:_(<vscale x 2 x s64>) = G_SPLAT_VECTOR [[COPY]](s64)
85+
; CHECK-NEXT: %res:_(<vscale x 2 x s32>) = G_ICMP intpred(sgt), %rhs(<vscale x 2 x s64>), %lhs
86+
; CHECK-NEXT: %z:_(<vscale x 2 x s64>) = G_ZEXT %res(<vscale x 2 x s32>)
87+
; CHECK-NEXT: $z0 = COPY %z(<vscale x 2 x s64>)
88+
%const:_(s64) = G_CONSTANT i64 11
89+
%lhs:_(<vscale x 2 x s64>) = G_SPLAT_VECTOR %const:_(s64)
90+
%1:_(s64) = COPY $x1
91+
%rhs:_(<vscale x 2 x s64>) = G_SPLAT_VECTOR %1:_(s64)
92+
%res:_(<vscale x 2 x s32>) = G_ICMP intpred(slt), %lhs(<vscale x 2 x s64>), %rhs
93+
%z:_(<vscale x 2 x s64>) = G_ZEXT %res
94+
$z0 = COPY %z(<vscale x 2 x s64>)
95+
...

0 commit comments

Comments
 (0)