blob: e5ff2397141418611289aa720187653b842015bc [file] [edit]
//=== lib/CodeGen/GlobalISel/AMDGPUPreLegalizerCombiner.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 pass does combining of machine instructions at the generic MI level,
// before the legalizer.
//
//===----------------------------------------------------------------------===//
#include "AMDGPU.h"
#include "AMDGPUCombinerHelper.h"
#include "AMDGPULegalizerInfo.h"
#include "GCNSubtarget.h"
#include "llvm/CodeGen/GlobalISel/CSEInfo.h"
#include "llvm/CodeGen/GlobalISel/Combiner.h"
#include "llvm/CodeGen/GlobalISel/CombinerHelper.h"
#include "llvm/CodeGen/GlobalISel/CombinerInfo.h"
#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h"
#include "llvm/CodeGen/GlobalISel/GISelValueTracking.h"
#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineFunctionAnalysisManager.h"
#include "llvm/CodeGen/MachinePassManager.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/Target/TargetMachine.h"
#define GET_GICOMBINER_DEPS
#include "AMDGPUGenPreLegalizeGICombiner.inc"
#undef GET_GICOMBINER_DEPS
#define DEBUG_TYPE "amdgpu-prelegalizer-combiner"
using namespace llvm;
using namespace MIPatternMatch;
namespace {
#define GET_GICOMBINER_TYPES
#include "AMDGPUGenPreLegalizeGICombiner.inc"
#undef GET_GICOMBINER_TYPES
class AMDGPUPreLegalizerCombinerImpl : public Combiner {
protected:
const AMDGPUPreLegalizerCombinerImplRuleConfig &RuleConfig;
const GCNSubtarget &STI;
const AMDGPUCombinerHelper Helper;
public:
AMDGPUPreLegalizerCombinerImpl(
MachineFunction &MF, CombinerInfo &CInfo, GISelValueTracking &VT,
GISelCSEInfo *CSEInfo,
const AMDGPUPreLegalizerCombinerImplRuleConfig &RuleConfig,
const GCNSubtarget &STI, MachineDominatorTree *MDT,
const LegalizerInfo *LI);
static const char *getName() { return "AMDGPUPreLegalizerCombinerImpl"; }
bool tryCombineAllImpl(MachineInstr &MI) const;
bool tryCombineAll(MachineInstr &I) const override;
struct ClampI64ToI16MatchInfo {
int64_t Cmp1 = 0;
int64_t Cmp2 = 0;
Register Origin;
};
bool matchClampI64ToI16(MachineInstr &MI, const MachineRegisterInfo &MRI,
const MachineFunction &MF,
ClampI64ToI16MatchInfo &MatchInfo) const;
void applyClampI64ToI16(MachineInstr &MI,
const ClampI64ToI16MatchInfo &MatchInfo) const;
private:
#define GET_GICOMBINER_CLASS_MEMBERS
#define AMDGPUSubtarget GCNSubtarget
#include "AMDGPUGenPreLegalizeGICombiner.inc"
#undef GET_GICOMBINER_CLASS_MEMBERS
#undef AMDGPUSubtarget
};
#define GET_GICOMBINER_IMPL
#define AMDGPUSubtarget GCNSubtarget
#include "AMDGPUGenPreLegalizeGICombiner.inc"
#undef AMDGPUSubtarget
#undef GET_GICOMBINER_IMPL
AMDGPUPreLegalizerCombinerImpl::AMDGPUPreLegalizerCombinerImpl(
MachineFunction &MF, CombinerInfo &CInfo, GISelValueTracking &VT,
GISelCSEInfo *CSEInfo,
const AMDGPUPreLegalizerCombinerImplRuleConfig &RuleConfig,
const GCNSubtarget &STI, MachineDominatorTree *MDT, const LegalizerInfo *LI)
: Combiner(MF, CInfo, &VT, CSEInfo), RuleConfig(RuleConfig), STI(STI),
Helper(Observer, B, /*IsPreLegalize*/ true, &VT, MDT, LI, STI),
#define GET_GICOMBINER_CONSTRUCTOR_INITS
#include "AMDGPUGenPreLegalizeGICombiner.inc"
#undef GET_GICOMBINER_CONSTRUCTOR_INITS
{
}
bool AMDGPUPreLegalizerCombinerImpl::tryCombineAll(MachineInstr &MI) const {
if (tryCombineAllImpl(MI))
return true;
return false;
}
bool AMDGPUPreLegalizerCombinerImpl::matchClampI64ToI16(
MachineInstr &MI, const MachineRegisterInfo &MRI, const MachineFunction &MF,
ClampI64ToI16MatchInfo &MatchInfo) const {
assert(MI.getOpcode() == TargetOpcode::G_TRUNC && "Invalid instruction!");
// Try to find a pattern where an i64 value should get clamped to short.
const LLT SrcType = MRI.getType(MI.getOperand(1).getReg());
if (SrcType != LLT::scalar(64))
return false;
const LLT DstType = MRI.getType(MI.getOperand(0).getReg());
if (DstType != LLT::scalar(16))
return false;
Register Base;
// Lo must not exceed Hi: with inverted bounds smin(smax(X, Lo), Hi) is
// constant, but the med3 built below would still clamp X to [Hi, Lo].
auto IsApplicableForCombine = [&MatchInfo](bool OuterIsMin) -> bool {
const int64_t Lo = OuterIsMin ? MatchInfo.Cmp2 : MatchInfo.Cmp1;
const int64_t Hi = OuterIsMin ? MatchInfo.Cmp1 : MatchInfo.Cmp2;
// Range-check first so Hi - Lo below can't overflow.
const int64_t Min = std::numeric_limits<int16_t>::min();
const int64_t Max = std::numeric_limits<int16_t>::max();
if (Lo < Min || Lo > Max || Hi < Min || Hi > Max)
return false;
// Reject inverted bounds, and bounds so close there is no need to clamp.
return Hi - Lo > 1;
};
// Try to match a combination of min / max MIR opcodes.
if (mi_match(MI.getOperand(1).getReg(), MRI,
m_GSMin(m_Reg(Base), m_ICst(MatchInfo.Cmp1)))) {
if (mi_match(Base, MRI,
m_GSMax(m_Reg(MatchInfo.Origin), m_ICst(MatchInfo.Cmp2)))) {
return IsApplicableForCombine(/*OuterIsMin=*/true);
}
}
if (mi_match(MI.getOperand(1).getReg(), MRI,
m_GSMax(m_Reg(Base), m_ICst(MatchInfo.Cmp1)))) {
if (mi_match(Base, MRI,
m_GSMin(m_Reg(MatchInfo.Origin), m_ICst(MatchInfo.Cmp2)))) {
return IsApplicableForCombine(/*OuterIsMin=*/false);
}
}
return false;
}
// We want to find a combination of instructions that
// gets generated when an i64 gets clamped to i16.
// The corresponding pattern is:
// G_MAX / G_MAX for i16 <= G_TRUNC i64.
// This can be efficiently written as following:
// v_cvt_pk_i16_i32 v0, v0, v1
// v_med3_i32 v0, Clamp_Min, v0, Clamp_Max
void AMDGPUPreLegalizerCombinerImpl::applyClampI64ToI16(
MachineInstr &MI, const ClampI64ToI16MatchInfo &MatchInfo) const {
Register Src = MatchInfo.Origin;
assert(MI.getMF()->getRegInfo().getType(Src) == LLT::scalar(64));
const LLT I32 = LLT::integer(32);
auto Unmerge = B.buildUnmerge(I32, Src);
assert(MI.getOpcode() != AMDGPU::G_AMDGPU_CVT_PK_I16_I32);
const LLT V2S16 = LLT::fixed_vector(2, 16);
auto CvtPk =
B.buildInstr(AMDGPU::G_AMDGPU_CVT_PK_I16_I32, {V2S16},
{Unmerge.getReg(0), Unmerge.getReg(1)}, MI.getFlags());
auto MinBoundary = std::min(MatchInfo.Cmp1, MatchInfo.Cmp2);
auto MaxBoundary = std::max(MatchInfo.Cmp1, MatchInfo.Cmp2);
auto MinBoundaryDst = B.buildConstant(I32, MinBoundary);
auto MaxBoundaryDst = B.buildConstant(I32, MaxBoundary);
auto Bitcast = B.buildBitcast({I32}, CvtPk);
auto Med3 = B.buildInstr(
AMDGPU::G_AMDGPU_SMED3, {I32},
{MinBoundaryDst.getReg(0), Bitcast.getReg(0), MaxBoundaryDst.getReg(0)},
MI.getFlags());
B.buildTrunc(MI.getOperand(0).getReg(), Med3);
MI.eraseFromParent();
}
static bool runCombiner(MachineFunction &MF,
function_ref<GISelCSEInfo *()> GetCSEInfo,
function_ref<GISelValueTracking *()> GetVT,
function_ref<MachineDominatorTree *()> GetMDT,
bool EnableOpt) {
AMDGPUPreLegalizerCombinerImplRuleConfig RuleConfig;
if (!RuleConfig.parseCommandLineOption())
reportFatalUsageError("Invalid rule identifier");
// If the ISel pipeline failed, do not bother running that pass.
if (MF.getProperties().hasFailedISel())
return false;
const GCNSubtarget &STI = MF.getSubtarget<GCNSubtarget>();
const Function &F = MF.getFunction();
CombinerInfo CInfo(/*AllowIllegalOps=*/true, /*ShouldLegalizeIllegal=*/false,
nullptr, EnableOpt, F.hasOptSize(), F.hasMinSize());
// Disable fixed-point iteration to reduce compile-time
CInfo.MaxIterations = 1;
CInfo.ObserverLvl = CombinerInfo::ObserverLevel::SinglePass;
// This is the first Combiner, so the input IR might contain dead
// instructions.
CInfo.EnableFullDCE = true;
GISelValueTracking *VT = GetVT();
GISelCSEInfo *CSEInfo = GetCSEInfo();
MachineDominatorTree *MDT = GetMDT();
AMDGPUPreLegalizerCombinerImpl Impl(MF, CInfo, *VT, CSEInfo, RuleConfig, STI,
MDT, STI.getLegalizerInfo());
return Impl.combineMachineInstrs();
}
// Pass boilerplate
// ================
class AMDGPUPreLegalizerCombinerLegacy : public MachineFunctionPass {
public:
static char ID;
AMDGPUPreLegalizerCombinerLegacy(bool IsOptLevelNone = false)
: MachineFunctionPass(ID), IsOptLevelNone(IsOptLevelNone) {}
StringRef getPassName() const override {
return "AMDGPUPreLegalizerCombiner";
}
bool runOnMachineFunction(MachineFunction &MF) override;
void getAnalysisUsage(AnalysisUsage &AU) const override;
private:
bool IsOptLevelNone;
};
} // end anonymous namespace
void AMDGPUPreLegalizerCombinerLegacy::getAnalysisUsage(
AnalysisUsage &AU) const {
AU.addRequired<TargetPassConfig>();
AU.setPreservesCFG();
getSelectionDAGFallbackAnalysisUsage(AU);
AU.addRequired<GISelValueTrackingAnalysisLegacy>();
AU.addPreserved<GISelValueTrackingAnalysisLegacy>();
if (!IsOptLevelNone) {
AU.addRequired<MachineDominatorTreeWrapperPass>();
}
AU.addRequired<GISelCSEAnalysisWrapperPass>();
AU.addPreserved<GISelCSEAnalysisWrapperPass>();
MachineFunctionPass::getAnalysisUsage(AU);
}
bool AMDGPUPreLegalizerCombinerLegacy::runOnMachineFunction(
MachineFunction &MF) {
const Function &F = MF.getFunction();
bool EnableOpt =
MF.getTarget().getOptLevel() != CodeGenOptLevel::None && !skipFunction(F);
return runCombiner(
MF,
[&]() {
// Enable CSE.
GISelCSEAnalysisWrapper &Wrapper =
getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEWrapper();
return &Wrapper.get(getAnalysis<TargetPassConfig>().getCSEConfig());
},
[&]() {
return &getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
},
[&]() -> MachineDominatorTree * {
return IsOptLevelNone ? nullptr
: &getAnalysis<MachineDominatorTreeWrapperPass>()
.getDomTree();
},
EnableOpt);
}
char AMDGPUPreLegalizerCombinerLegacy::ID = 0;
INITIALIZE_PASS_BEGIN(AMDGPUPreLegalizerCombinerLegacy, DEBUG_TYPE,
"Combine AMDGPU machine instrs before legalization",
false, false)
INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
INITIALIZE_PASS_DEPENDENCY(GISelValueTrackingAnalysisLegacy)
INITIALIZE_PASS_END(AMDGPUPreLegalizerCombinerLegacy, DEBUG_TYPE,
"Combine AMDGPU machine instrs before legalization", false,
false)
FunctionPass *
llvm::createAMDGPUPreLegalizeCombinerLegacyPass(bool IsOptLevelNone) {
return new AMDGPUPreLegalizerCombinerLegacy(IsOptLevelNone);
}
PreservedAnalyses
AMDGPUPreLegalizerCombinerPass::run(MachineFunction &MF,
MachineFunctionAnalysisManager &MFAM) {
bool IsOptLevelNone = MF.getTarget().getOptLevel() == CodeGenOptLevel::None;
if (!runCombiner(
MF, [&]() { return MFAM.getResult<GISelCSEAnalysis>(MF).get(); },
[&]() { return &MFAM.getResult<GISelValueTrackingAnalysis>(MF); },
[&]() -> MachineDominatorTree * {
return IsOptLevelNone
? nullptr
: &MFAM.getResult<MachineDominatorTreeAnalysis>(MF);
},
/*EnableOpt=*/!IsOptLevelNone))
return PreservedAnalyses::all();
PreservedAnalyses PA = getMachineFunctionPassPreservedAnalyses();
PA.preserveSet<CFGAnalyses>();
PA.preserve<GISelValueTrackingAnalysis>();
PA.preserve<GISelCSEAnalysis>();
return PA;
}