[llvm] e7f501b - [GlobalISel][AArch64] Combine and (lshr x, cst), mask -> ubfx x, cst, width
Jessica Paquette via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 1 11:02:16 PDT 2021
Author: Jessica Paquette
Date: 2021-06-01T10:56:17-07:00
New Revision: e7f501b5e7dbcde2790986edd6e7fe5025f041fe
URL: https://github.com/llvm/llvm-project/commit/e7f501b5e7dbcde2790986edd6e7fe5025f041fe
DIFF: https://github.com/llvm/llvm-project/commit/e7f501b5e7dbcde2790986edd6e7fe5025f041fe.diff
LOG: [GlobalISel][AArch64] Combine and (lshr x, cst), mask -> ubfx x, cst, width
Also add a target hook which allows us to get around custom legalization on
AArch64.
Differential Revision: https://reviews.llvm.org/D99283
Added:
llvm/test/CodeGen/AArch64/GlobalISel/form-bitfield-extract-from-and.mir
Modified:
llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
llvm/include/llvm/CodeGen/TargetLowering.h
llvm/include/llvm/Target/GlobalISel/Combine.td
llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
llvm/lib/Target/AArch64/AArch64Combine.td
llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
llvm/lib/Target/AArch64/AArch64ISelLowering.h
Removed:
################################################################################
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
index 93ec4defc3d6c..1b4f496ce6ca9 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
@@ -517,6 +517,10 @@ class CombinerHelper {
/// or false constant based off of KnownBits information.
bool matchICmpToTrueFalseKnownBits(MachineInstr &MI, int64_t &MatchInfo);
+ /// Match: and (lshr x, cst), mask -> ubfx x, cst, width
+ bool matchBitfieldExtractFromAnd(
+ MachineInstr &MI, std::function<void(MachineIRBuilder &)> &MatchInfo);
+
/// Try to transform \p MI by using all of the above
/// combine functions. Returns true if changed.
bool tryCombine(MachineInstr &MI);
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index fe9f72e840dea..d311e8f4c31e2 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -1793,6 +1793,12 @@ class TargetLoweringBase {
/// Should be used only when getIRStackGuard returns nullptr.
virtual Function *getSSPStackGuardCheck(const Module &M) const;
+ /// \returns true if a constant G_UBFX is legal on the target.
+ virtual bool isConstantUnsignedBitfieldExtactLegal(unsigned Opc, LLT Ty1,
+ LLT Ty2) const {
+ return false;
+ }
+
protected:
Value *getDefaultSafeStackPointerLocation(IRBuilder<> &IRB,
bool UseTLS) const;
diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td
index b2303858f4567..3846997f0e8a8 100644
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td
@@ -620,6 +620,12 @@ def icmp_to_true_false_known_bits : GICombineRule<
[{ return Helper.matchICmpToTrueFalseKnownBits(*${d}, ${matchinfo}); }]),
(apply [{ Helper.replaceInstWithConstant(*${d}, ${matchinfo}); }])>;
+def bitfield_extract_from_and : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$info),
+ (match (wip_match_opcode G_AND):$root,
+ [{ return Helper.matchBitfieldExtractFromAnd(*${root}, ${info}); }]),
+ (apply [{ return Helper.applyBuildFn(*${root}, ${info}); }])>;
+
def funnel_shift_combines : GICombineGroup<[funnel_shift_to_rotate]>;
// FIXME: These should use the custom predicate feature once it lands.
@@ -664,7 +670,7 @@ def all_combines : GICombineGroup<[trivial_combines, insert_vec_elt_combines,
unmerge_zext_to_zext, trunc_ext_fold, trunc_shl,
const_combines, xor_of_and_with_same_reg, ptr_add_with_zero,
shift_immed_chain, shift_of_shifted_logic_chain, load_or_combine,
- div_rem_to_divrem, funnel_shift_combines]>;
+ div_rem_to_divrem, funnel_shift_combines, bitfield_extract_from_and]>;
// A combine group used to for prelegalizer combiners at -O0. The combines in
// this group have been selected based on experiments to balance code size and
diff --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
index 07efd9a7cc081..d510676fc6102 100644
--- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
@@ -3986,6 +3986,41 @@ bool CombinerHelper::matchICmpToTrueFalseKnownBits(MachineInstr &MI,
return true;
}
+bool CombinerHelper::matchBitfieldExtractFromAnd(
+ MachineInstr &MI, std::function<void(MachineIRBuilder &)> &MatchInfo) {
+ assert(MI.getOpcode() == TargetOpcode::G_AND);
+ Register Dst = MI.getOperand(0).getReg();
+ LLT Ty = MRI.getType(Dst);
+ if (!getTargetLowering().isConstantUnsignedBitfieldExtactLegal(
+ TargetOpcode::G_UBFX, Ty, Ty))
+ return false;
+
+ int64_t AndImm, LSBImm;
+ Register ShiftSrc;
+ const unsigned Size = Ty.getScalarSizeInBits();
+ if (!mi_match(MI.getOperand(0).getReg(), MRI,
+ m_GAnd(m_OneNonDBGUse(m_GLShr(m_Reg(ShiftSrc), m_ICst(LSBImm))),
+ m_ICst(AndImm))))
+ return false;
+
+ // The mask is a mask of the low bits iff imm & (imm+1) == 0.
+ auto MaybeMask = static_cast<uint64_t>(AndImm);
+ if (MaybeMask & (MaybeMask + 1))
+ return false;
+
+ // LSB must fit within the register.
+ if (static_cast<uint64_t>(LSBImm) >= Size)
+ return false;
+
+ uint64_t Width = APInt(Size, AndImm).countTrailingOnes();
+ MatchInfo = [=](MachineIRBuilder &B) {
+ auto WidthCst = B.buildConstant(Ty, Width);
+ auto LSBCst = B.buildConstant(Ty, LSBImm);
+ B.buildInstr(TargetOpcode::G_UBFX, {Dst}, {ShiftSrc, LSBCst, WidthCst});
+ };
+ return true;
+}
+
bool CombinerHelper::tryCombine(MachineInstr &MI) {
if (tryCombineCopy(MI))
return true;
diff --git a/llvm/lib/Target/AArch64/AArch64Combine.td b/llvm/lib/Target/AArch64/AArch64Combine.td
index fa4b88d334cde..b395a4ef81ff4 100644
--- a/llvm/lib/Target/AArch64/AArch64Combine.td
+++ b/llvm/lib/Target/AArch64/AArch64Combine.td
@@ -174,7 +174,8 @@ def bitfield_extract_from_sext_inreg : GICombineRule<
[{ return matchBitfieldExtractFromSExtInReg(*${root}, MRI, ${info}); }]),
(apply [{ return Helper.applyBuildFn(*${root}, ${info}); }])>;
-def form_bitfield_extract : GICombineGroup<[bitfield_extract_from_sext_inreg]>;
+def form_bitfield_extract : GICombineGroup<[bitfield_extract_from_sext_inreg,
+ bitfield_extract_from_and]>;
def lower_vector_fcmp : GICombineRule<
(defs root:$root),
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 0deba5ce40c15..409b63be8c48d 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -18179,3 +18179,8 @@ bool AArch64TargetLowering::SimplifyDemandedBitsForTargetNode(
return TargetLowering::SimplifyDemandedBitsForTargetNode(
Op, OriginalDemandedBits, OriginalDemandedElts, Known, TLO, Depth);
}
+
+bool AArch64TargetLowering::isConstantUnsignedBitfieldExtactLegal(
+ unsigned Opc, LLT Ty1, LLT Ty2) const {
+ return Ty1 == Ty2 && (Ty1 == LLT::scalar(32) || Ty1 == LLT::scalar(64));
+}
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.h b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
index 40734292f24d7..53d9baa85ba47 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.h
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.h
@@ -1100,6 +1100,9 @@ class AArch64TargetLowering : public TargetLowering {
// to transition between unpacked and packed types of the same element type,
// with BITCAST used otherwise.
SDValue getSVESafeBitCast(EVT VT, SDValue Op, SelectionDAG &DAG) const;
+
+ bool isConstantUnsignedBitfieldExtactLegal(unsigned Opc, LLT Ty1,
+ LLT Ty2) const override;
};
namespace AArch64 {
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/form-bitfield-extract-from-and.mir b/llvm/test/CodeGen/AArch64/GlobalISel/form-bitfield-extract-from-and.mir
new file mode 100644
index 0000000000000..7d04b4d86bafd
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/form-bitfield-extract-from-and.mir
@@ -0,0 +1,282 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple aarch64 -run-pass=aarch64-postlegalizer-combiner --aarch64postlegalizercombinerhelper-only-enable-rule="bitfield_extract_from_and" -verify-machineinstrs %s -o - | FileCheck %s
+# REQUIRES: asserts
+
+# Check that we can combine
+#
+# and (lshr x, cst), mask -> ubfx x, cst, width
+
+...
+---
+name: ubfx_s32
+tracksRegLiveness: true
+legalized: true
+body: |
+ bb.0:
+ liveins: $w0
+
+ ; LSB = 5
+ ; Width = LSB + trailing_ones(255) - 1 =
+ ; 5 + 8 - 1 = 12
+
+ ; CHECK-LABEL: name: ubfx_s32
+ ; CHECK: liveins: $w0
+ ; CHECK: %x:_(s32) = COPY $w0
+ ; CHECK: %lsb:_(s32) = G_CONSTANT i32 5
+ ; CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 8
+ ; CHECK: %and:_(s32) = G_UBFX %x, %lsb(s32), [[C]]
+ ; CHECK: $w0 = COPY %and(s32)
+ ; CHECK: RET_ReallyLR implicit $w0
+ %x:_(s32) = COPY $w0
+ %lsb:_(s32) = G_CONSTANT i32 5
+ %mask:_(s32) = G_CONSTANT i32 255
+ %shift:_(s32) = G_LSHR %x, %lsb
+ %and:_(s32) = G_AND %shift, %mask
+ $w0 = COPY %and
+ RET_ReallyLR implicit $w0
+
+...
+---
+name: ubfx_s64
+tracksRegLiveness: true
+legalized: true
+body: |
+ bb.0:
+ liveins: $x0
+
+ ; LSB = 5
+ ; Width = LSB + trailing_ones(1) - 1 =
+ ; 5 + 1 - 1 = 5
+
+ ; CHECK-LABEL: name: ubfx_s64
+ ; CHECK: liveins: $x0
+ ; CHECK: %x:_(s64) = COPY $x0
+ ; CHECK: %lsb:_(s64) = G_CONSTANT i64 5
+ ; CHECK: %mask:_(s64) = G_CONSTANT i64 1
+ ; CHECK: %and:_(s64) = G_UBFX %x, %lsb(s64), %mask
+ ; CHECK: $x0 = COPY %and(s64)
+ ; CHECK: RET_ReallyLR implicit $x0
+ %x:_(s64) = COPY $x0
+ %lsb:_(s64) = G_CONSTANT i64 5
+ %mask:_(s64) = G_CONSTANT i64 1
+ %shift:_(s64) = G_LSHR %x, %lsb
+ %and:_(s64) = G_AND %shift, %mask
+ $x0 = COPY %and
+ RET_ReallyLR implicit $x0
+
+...
+---
+name: dont_combine_no_and_cst
+tracksRegLiveness: true
+legalized: true
+body: |
+ bb.0:
+ liveins: $w0, $w1
+
+ ; UBFX needs to be selected to UBFMWri/UBFMXri, so we need constants.
+
+ ; CHECK-LABEL: name: dont_combine_no_and_cst
+ ; CHECK: liveins: $w0, $w1
+ ; CHECK: %x:_(s32) = COPY $w0
+ ; CHECK: %y:_(s32) = COPY $w1
+ ; CHECK: %lsb:_(s32) = G_CONSTANT i32 5
+ ; CHECK: %shift:_(s32) = G_LSHR %x, %lsb(s32)
+ ; CHECK: %and:_(s32) = G_AND %shift, %y
+ ; CHECK: $w0 = COPY %and(s32)
+ ; CHECK: RET_ReallyLR implicit $w0
+ %x:_(s32) = COPY $w0
+ %y:_(s32) = COPY $w1
+ %lsb:_(s32) = G_CONSTANT i32 5
+ %shift:_(s32) = G_LSHR %x, %lsb
+ %and:_(s32) = G_AND %shift, %y
+ $w0 = COPY %and
+ RET_ReallyLR implicit $w0
+
+...
+---
+name: dont_combine_and_cst_not_mask
+tracksRegLiveness: true
+legalized: true
+body: |
+ bb.0:
+ liveins: $w0
+ ; CHECK-LABEL: name: dont_combine_and_cst_not_mask
+ ; CHECK: liveins: $w0
+ ; CHECK: %x:_(s32) = COPY $w0
+ ; CHECK: %lsb:_(s32) = G_CONSTANT i32 5
+ ; CHECK: %not_a_mask:_(s32) = G_CONSTANT i32 2
+ ; CHECK: %shift:_(s32) = G_LSHR %x, %lsb(s32)
+ ; CHECK: %and:_(s32) = G_AND %shift, %not_a_mask
+ ; CHECK: $w0 = COPY %and(s32)
+ ; CHECK: RET_ReallyLR implicit $w0
+ %x:_(s32) = COPY $w0
+ %lsb:_(s32) = G_CONSTANT i32 5
+ %not_a_mask:_(s32) = G_CONSTANT i32 2
+ %shift:_(s32) = G_LSHR %x, %lsb
+ %and:_(s32) = G_AND %shift, %not_a_mask
+ $w0 = COPY %and
+ RET_ReallyLR implicit $w0
+
+...
+---
+name: dont_combine_shift_more_than_one_use
+tracksRegLiveness: true
+legalized: true
+body: |
+ bb.0:
+ liveins: $x0
+ ; CHECK-LABEL: name: dont_combine_shift_more_than_one_use
+ ; CHECK: liveins: $x0
+ ; CHECK: %x:_(s64) = COPY $x0
+ ; CHECK: %lsb:_(s64) = G_CONSTANT i64 5
+ ; CHECK: %mask:_(s64) = G_CONSTANT i64 1
+ ; CHECK: %shift:_(s64) = G_LSHR %x, %lsb(s64)
+ ; CHECK: %and:_(s64) = G_AND %shift, %mask
+ ; CHECK: %sub:_(s64) = G_SUB %and, %shift
+ ; CHECK: $x0 = COPY %sub(s64)
+ ; CHECK: RET_ReallyLR implicit $x0
+ %x:_(s64) = COPY $x0
+ %lsb:_(s64) = G_CONSTANT i64 5
+ %mask:_(s64) = G_CONSTANT i64 1
+ %shift:_(s64) = G_LSHR %x, %lsb
+ %and:_(s64) = G_AND %shift, %mask
+ %sub:_(s64) = G_SUB %and, %shift
+ $x0 = COPY %sub
+ RET_ReallyLR implicit $x0
+
+...
+---
+name: dont_combine_negative_lsb
+tracksRegLiveness: true
+legalized: true
+body: |
+ bb.0:
+ liveins: $w0
+
+ ; LSB must be in [0, reg_size)
+
+ ; CHECK-LABEL: name: dont_combine_negative_lsb
+ ; CHECK: liveins: $w0
+ ; CHECK: %x:_(s32) = COPY $w0
+ ; CHECK: %negative:_(s32) = G_CONSTANT i32 -1
+ ; CHECK: %mask:_(s32) = G_CONSTANT i32 255
+ ; CHECK: %shift:_(s32) = G_LSHR %x, %negative(s32)
+ ; CHECK: %and:_(s32) = G_AND %shift, %mask
+ ; CHECK: $w0 = COPY %and(s32)
+ ; CHECK: RET_ReallyLR implicit $w0
+ %x:_(s32) = COPY $w0
+ %negative:_(s32) = G_CONSTANT i32 -1
+ %mask:_(s32) = G_CONSTANT i32 255
+ %shift:_(s32) = G_LSHR %x, %negative
+ %and:_(s32) = G_AND %shift, %mask
+ $w0 = COPY %and
+ RET_ReallyLR implicit $w0
+
+...
+---
+name: dont_combine_lsb_too_large
+tracksRegLiveness: true
+legalized: true
+body: |
+ bb.0:
+ liveins: $w0
+
+ ; LSB must be in [0, reg_size)
+
+ ; CHECK-LABEL: name: dont_combine_lsb_too_large
+ ; CHECK: liveins: $w0
+ ; CHECK: %x:_(s32) = COPY $w0
+ ; CHECK: %too_large:_(s32) = G_CONSTANT i32 32
+ ; CHECK: %mask:_(s32) = G_CONSTANT i32 255
+ ; CHECK: %shift:_(s32) = G_LSHR %x, %too_large(s32)
+ ; CHECK: %and:_(s32) = G_AND %shift, %mask
+ ; CHECK: $w0 = COPY %and(s32)
+ ; CHECK: RET_ReallyLR implicit $w0
+ %x:_(s32) = COPY $w0
+ %too_large:_(s32) = G_CONSTANT i32 32
+ %mask:_(s32) = G_CONSTANT i32 255
+ %shift:_(s32) = G_LSHR %x, %too_large
+ %and:_(s32) = G_AND %shift, %mask
+ $w0 = COPY %and
+ RET_ReallyLR implicit $w0
+
+...
+---
+name: dont_combine_vector
+tracksRegLiveness: true
+legalized: true
+body: |
+ bb.0:
+ liveins: $d0
+ ; CHECK-LABEL: name: dont_combine_vector
+ ; CHECK: liveins: $d0
+ ; CHECK: %x:_(<2 x s32>) = COPY $d0
+ ; CHECK: %lsb_cst:_(s32) = G_CONSTANT i32 5
+ ; CHECK: %lsb:_(<2 x s32>) = G_BUILD_VECTOR %lsb_cst(s32), %lsb_cst(s32)
+ ; CHECK: %mask_cst:_(s32) = G_CONSTANT i32 255
+ ; CHECK: %mask:_(<2 x s32>) = G_BUILD_VECTOR %mask_cst(s32), %mask_cst(s32)
+ ; CHECK: %shift:_(<2 x s32>) = G_LSHR %x, %lsb(<2 x s32>)
+ ; CHECK: %and:_(<2 x s32>) = G_AND %shift, %mask
+ ; CHECK: $d0 = COPY %and(<2 x s32>)
+ ; CHECK: RET_ReallyLR implicit $d0
+ %x:_(<2 x s32>) = COPY $d0
+ %lsb_cst:_(s32) = G_CONSTANT i32 5
+ %lsb:_(<2 x s32>) = G_BUILD_VECTOR %lsb_cst, %lsb_cst
+ %mask_cst:_(s32) = G_CONSTANT i32 255
+ %mask:_(<2 x s32>) = G_BUILD_VECTOR %mask_cst, %mask_cst
+ %shift:_(<2 x s32>) = G_LSHR %x, %lsb
+ %and:_(<2 x s32>) = G_AND %shift, %mask
+ $d0 = COPY %and
+ RET_ReallyLR implicit $d0
+
+...
+---
+name: max_signed_int_mask
+tracksRegLiveness: true
+legalized: true
+body: |
+ bb.0:
+ liveins: $x0
+ ; mask = 0111 1111 1111 ... 1111
+ ; mask + 1 = 1000 0000 0000 ... 0000
+ ; CHECK-LABEL: name: max_signed_int_mask
+ ; CHECK: liveins: $x0
+ ; CHECK: %x:_(s64) = COPY $x0
+ ; CHECK: %lsb:_(s64) = G_CONSTANT i64 0
+ ; CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 63
+ ; CHECK: %and:_(s64) = G_UBFX %x, %lsb(s64), [[C]]
+ ; CHECK: $x0 = COPY %and(s64)
+ ; CHECK: RET_ReallyLR implicit $x0
+ %x:_(s64) = COPY $x0
+ %lsb:_(s64) = G_CONSTANT i64 0
+ %mask:_(s64) = G_CONSTANT i64 9223372036854775807
+ %shift:_(s64) = G_LSHR %x, %lsb
+ %and:_(s64) = G_AND %shift, %mask
+ $x0 = COPY %and
+ RET_ReallyLR implicit $x0
+
+...
+---
+name: max_unsigned_int_mask
+tracksRegLiveness: true
+legalized: true
+body: |
+ bb.0:
+ liveins: $x0
+ ; mask = 1111 1111 1111 ... 1111
+ ; mask + 1 = 0000 0000 0000 ... 000
+ ; CHECK-LABEL: name: max_unsigned_int_mask
+ ; CHECK: liveins: $x0
+ ; CHECK: %x:_(s64) = COPY $x0
+ ; CHECK: %lsb:_(s64) = G_CONSTANT i64 5
+ ; CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 64
+ ; CHECK: %and:_(s64) = G_UBFX %x, %lsb(s64), [[C]]
+ ; CHECK: $x0 = COPY %and(s64)
+ ; CHECK: RET_ReallyLR implicit $x0
+ %x:_(s64) = COPY $x0
+ %lsb:_(s64) = G_CONSTANT i64 5
+ %mask:_(s64) = G_CONSTANT i64 18446744073709551615
+ %shift:_(s64) = G_LSHR %x, %lsb
+ %and:_(s64) = G_AND %shift, %mask
+ $x0 = COPY %and
+ RET_ReallyLR implicit $x0
More information about the llvm-commits
mailing list