-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[GlobalIsel] Canonicalize G_ICMP #108755
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[GlobalIsel] Canonicalize G_ICMP #108755
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,86 @@ | ||||
//===- CombinerHelperCompares.cpp------------------------------------------===// | ||||
// | ||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||||
// See https://llvm.org/LICENSE.txt for license information. | ||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||||
// | ||||
//===----------------------------------------------------------------------===// | ||||
// | ||||
// This file implements CombinerHelper for G_ICMP. | ||||
// | ||||
//===----------------------------------------------------------------------===// | ||||
#include "llvm/CodeGen/GlobalISel/CombinerHelper.h" | ||||
#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h" | ||||
#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h" | ||||
#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" | ||||
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" | ||||
#include "llvm/CodeGen/GlobalISel/Utils.h" | ||||
#include "llvm/CodeGen/LowLevelTypeUtils.h" | ||||
#include "llvm/CodeGen/MachineInstr.h" | ||||
#include "llvm/CodeGen/MachineOperand.h" | ||||
#include "llvm/CodeGen/MachineRegisterInfo.h" | ||||
#include "llvm/CodeGen/TargetOpcodes.h" | ||||
#include "llvm/IR/Instructions.h" | ||||
#include "llvm/Support/Casting.h" | ||||
#include "llvm/Support/ErrorHandling.h" | ||||
#include <cstdlib> | ||||
|
||||
#define DEBUG_TYPE "gi-combiner" | ||||
|
||||
using namespace llvm; | ||||
|
||||
bool CombinerHelper::constantFoldICmp(const GICmp &ICmp, | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not put this in the builder like other constant folds? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The builder only folds icmps that it builds. This PR adds one combine that matches G_ICMP. While running the combiner, we could find G_ICMPs where the parameters are constants due to combining. The builder would never build this G_ICMP again. In DAG terms, this is visitICmp. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We already have ConstantFoldICmp, but I needed this on top of GIConstants.
This used by the builder. |
||||
const GIConstant &LHSCst, | ||||
const GIConstant &RHSCst, | ||||
BuildFnTy &MatchInfo) { | ||||
if (LHSCst.getKind() != GIConstant::GIConstantKind::Scalar) | ||||
return false; | ||||
|
||||
Register Dst = ICmp.getReg(0); | ||||
LLT DstTy = MRI.getType(Dst); | ||||
|
||||
if (!isConstantLegalOrBeforeLegalizer(DstTy)) | ||||
return false; | ||||
|
||||
CmpInst::Predicate Pred = ICmp.getCond(); | ||||
APInt LHS = LHSCst.getScalarValue(); | ||||
APInt RHS = RHSCst.getScalarValue(); | ||||
|
||||
bool Result = ICmpInst::compare(LHS, RHS, Pred); | ||||
|
||||
MatchInfo = [=](MachineIRBuilder &B) { | ||||
if (Result) | ||||
B.buildConstant(Dst, getICmpTrueVal(getTargetLowering(), | ||||
/*IsVector=*/DstTy.isVector(), | ||||
/*IsFP=*/false)); | ||||
else | ||||
B.buildConstant(Dst, 0); | ||||
}; | ||||
|
||||
return true; | ||||
} | ||||
|
||||
bool CombinerHelper::matchCanonicalizeICmp(const MachineInstr &MI, | ||||
BuildFnTy &MatchInfo) { | ||||
const GICmp *Cmp = cast<GICmp>(&MI); | ||||
|
||||
Register Dst = Cmp->getReg(0); | ||||
Register LHS = Cmp->getLHSReg(); | ||||
Register RHS = Cmp->getRHSReg(); | ||||
|
||||
CmpInst::Predicate Pred = Cmp->getCond(); | ||||
assert(CmpInst::isIntPredicate(Pred) && "Not an integer compare!"); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could also handle fcmp, you don't need to know what the constant kind or value is, just that it's a constant There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would be a different PR. This PR has one pattern that matches G_ICMP. We would need a different pattern for G_FCMP. This PR introduces GIConstant, which is a wrapper around a vector of APInt. For G_FCMP, we would need to introduce GFConstant, which is a wrapper around a vector of APFloat. This PR canonicalizes and constant folds icmps. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a split out from #105991. It only handles icmps. We could add the same for fcmps, but the market share is probably lower without having data. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah we can always generalize this later. |
||||
if (auto CLHS = GIConstant::getConstant(LHS, MRI)) { | ||||
if (auto CRHS = GIConstant::getConstant(RHS, MRI)) | ||||
return constantFoldICmp(*Cmp, *CLHS, *CRHS, MatchInfo); | ||||
|
||||
// If we have a constant, make sure it is on the RHS. | ||||
std::swap(LHS, RHS); | ||||
Pred = CmpInst::getSwappedPredicate(Pred); | ||||
|
||||
MatchInfo = [=](MachineIRBuilder &B) { B.buildICmp(Pred, Dst, LHS, RHS); }; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could also mutate the instruction in place without creating a new one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I never do that! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That's not a real answer to a review suggestion. If it's easy to mutate in place we should generally try to do that first. The idea is that it's cheaper to do that than creating a new MachineInstr and the operands. For niche optimizations it's not a big deal, but it's a nice to have. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed! But I don't like this low-level manipulation of instructions. It is error prone. I prefer the build and erase pattern. |
||||
return true; | ||||
} | ||||
|
||||
return false; | ||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py | ||
# RUN: llc -o - -mtriple=aarch64-unknown-unknown -run-pass=aarch64-prelegalizer-combiner -verify-machineinstrs %s | FileCheck %s --check-prefixes=CHECK | ||
|
||
--- | ||
name: test_icmp_canon | ||
body: | | ||
bb.1: | ||
; CHECK-LABEL: name: test_icmp_canon | ||
; CHECK: %lhs:_(s64) = G_CONSTANT i64 11 | ||
; CHECK-NEXT: %rhs:_(s64) = COPY $x0 | ||
; CHECK-NEXT: %res:_(s32) = G_ICMP intpred(sgt), %rhs(s64), %lhs | ||
; CHECK-NEXT: $w0 = COPY %res(s32) | ||
%lhs:_(s64) = G_CONSTANT i64 11 | ||
%rhs:_(s64) = COPY $x0 | ||
%res:_(s32) = G_ICMP intpred(slt), %lhs(s64), %rhs | ||
$w0 = COPY %res(s32) | ||
... | ||
--- | ||
name: test_icmp_no_canon | ||
body: | | ||
bb.1: | ||
; CHECK-LABEL: name: test_icmp_no_canon | ||
; CHECK: %lhs:_(s64) = COPY $x0 | ||
; CHECK-NEXT: %rhs:_(s64) = G_CONSTANT i64 11 | ||
; CHECK-NEXT: %res:_(s32) = G_ICMP intpred(slt), %lhs(s64), %rhs | ||
; CHECK-NEXT: $w0 = COPY %res(s32) | ||
%lhs:_(s64) = COPY $x0 | ||
%rhs:_(s64) = G_CONSTANT i64 11 | ||
%res:_(s32) = G_ICMP intpred(slt), %lhs(s64), %rhs | ||
$w0 = COPY %res(s32) | ||
... | ||
--- | ||
name: test_icmp_canon_bv | ||
body: | | ||
bb.1: | ||
; CHECK-LABEL: name: test_icmp_canon_bv | ||
; CHECK: %opaque1:_(s64) = COPY $x0 | ||
; CHECK-NEXT: %opaque2:_(s64) = COPY $x0 | ||
; CHECK-NEXT: %const1:_(s64) = G_CONSTANT i64 11 | ||
; CHECK-NEXT: %const2:_(s64) = G_CONSTANT i64 12 | ||
; CHECK-NEXT: %lhs:_(<2 x s64>) = G_BUILD_VECTOR %const1(s64), %const2(s64) | ||
; CHECK-NEXT: %rhs:_(<2 x s64>) = G_BUILD_VECTOR %opaque1(s64), %opaque2(s64) | ||
; CHECK-NEXT: %res:_(<2 x s32>) = G_ICMP intpred(sgt), %rhs(<2 x s64>), %lhs | ||
; CHECK-NEXT: $x0 = COPY %res(<2 x s32>) | ||
%opaque1:_(s64) = COPY $x0 | ||
%opaque2:_(s64) = COPY $x0 | ||
%const1:_(s64) = G_CONSTANT i64 11 | ||
%const2:_(s64) = G_CONSTANT i64 12 | ||
%lhs:_(<2 x s64>) = G_BUILD_VECTOR %const1(s64), %const2(s64) | ||
%rhs:_(<2 x s64>) = G_BUILD_VECTOR %opaque1(s64), %opaque2(s64) | ||
%res:_(<2 x s32>) = G_ICMP intpred(slt), %lhs(<2 x s64>), %rhs | ||
$x0 = COPY %res(<2 x s32>) | ||
... | ||
--- | ||
name: test_icmp_no_canon_bv_neither_const | ||
body: | | ||
bb.1: | ||
; CHECK-LABEL: name: test_icmp_no_canon_bv | ||
; CHECK: %opaque1:_(s64) = COPY $x0 | ||
; CHECK-NEXT: %opaque2:_(s64) = COPY $x0 | ||
; CHECK-NEXT: %const1:_(s64) = G_CONSTANT i64 11 | ||
; CHECK-NEXT: %const2:_(s64) = G_CONSTANT i64 12 | ||
; CHECK-NEXT: %lhs:_(<2 x s64>) = G_BUILD_VECTOR %const1(s64), %opaque2(s64) | ||
; CHECK-NEXT: %rhs:_(<2 x s64>) = G_BUILD_VECTOR %opaque1(s64), %const2(s64) | ||
; CHECK-NEXT: %res:_(<2 x s32>) = G_ICMP intpred(slt), %lhs(<2 x s64>), %rhs | ||
; CHECK-NEXT: $x0 = COPY %res(<2 x s32>) | ||
%opaque1:_(s64) = COPY $x0 | ||
%opaque2:_(s64) = COPY $x0 | ||
%const1:_(s64) = G_CONSTANT i64 11 | ||
%const2:_(s64) = G_CONSTANT i64 12 | ||
%lhs:_(<2 x s64>) = G_BUILD_VECTOR %const1(s64), %opaque2(s64) | ||
%rhs:_(<2 x s64>) = G_BUILD_VECTOR %opaque1(s64), %const2(s64) | ||
%res:_(<2 x s32>) = G_ICMP intpred(slt), %lhs(<2 x s64>), %rhs | ||
$x0 = COPY %res(<2 x s32>) | ||
... | ||
--- | ||
name: test_icmp_canon_splat | ||
body: | | ||
bb.1: | ||
; CHECK-LABEL: name: test_icmp_canon_splat | ||
; CHECK: %const:_(s64) = G_CONSTANT i64 11 | ||
; CHECK-NEXT: %lhs:_(<vscale x 2 x s64>) = G_SPLAT_VECTOR %const(s64) | ||
; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x1 | ||
; CHECK-NEXT: %rhs:_(<vscale x 2 x s64>) = G_SPLAT_VECTOR [[COPY]](s64) | ||
; CHECK-NEXT: %res:_(<vscale x 2 x s32>) = G_ICMP intpred(sgt), %rhs(<vscale x 2 x s64>), %lhs | ||
; CHECK-NEXT: %z:_(<vscale x 2 x s64>) = G_ZEXT %res(<vscale x 2 x s32>) | ||
; CHECK-NEXT: $z0 = COPY %z(<vscale x 2 x s64>) | ||
%const:_(s64) = G_CONSTANT i64 11 | ||
%lhs:_(<vscale x 2 x s64>) = G_SPLAT_VECTOR %const:_(s64) | ||
%1:_(s64) = COPY $x1 | ||
%rhs:_(<vscale x 2 x s64>) = G_SPLAT_VECTOR %1:_(s64) | ||
%res:_(<vscale x 2 x s32>) = G_ICMP intpred(slt), %lhs(<vscale x 2 x s64>), %rhs | ||
%z:_(<vscale x 2 x s64>) = G_ZEXT %res | ||
$z0 = COPY %z(<vscale x 2 x s64>) | ||
... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class could do with more documentation about its intended purpose, in what situation it's supposed to be used.