
/llvm-project/llvm/lib/CodeGen/GlobalISel/Combiner.cpp:220:3: error: default label in switch which covers all enumeration values [-Werror,-Wcovered-switch-default] default: ^ 1 error generated.
356 lines
12 KiB
C++
356 lines
12 KiB
C++
//===-- lib/CodeGen/GlobalISel/Combiner.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 constains common code to combine machine functions at generic
|
|
// level.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/CodeGen/GlobalISel/Combiner.h"
|
|
#include "llvm/ADT/PostOrderIterator.h"
|
|
#include "llvm/ADT/SetVector.h"
|
|
#include "llvm/ADT/Statistic.h"
|
|
#include "llvm/CodeGen/GlobalISel/CSEInfo.h"
|
|
#include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h"
|
|
#include "llvm/CodeGen/GlobalISel/CombinerInfo.h"
|
|
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
|
|
#include "llvm/CodeGen/GlobalISel/GISelWorkList.h"
|
|
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
|
|
#include "llvm/CodeGen/GlobalISel/Utils.h"
|
|
#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
#define DEBUG_TYPE "gi-combiner"
|
|
|
|
using namespace llvm;
|
|
|
|
STATISTIC(NumOneIteration, "Number of functions with one iteration");
|
|
STATISTIC(NumTwoIterations, "Number of functions with two iterations");
|
|
STATISTIC(NumThreeOrMoreIterations,
|
|
"Number of functions with three or more iterations");
|
|
|
|
namespace llvm {
|
|
cl::OptionCategory GICombinerOptionCategory(
|
|
"GlobalISel Combiner",
|
|
"Control the rules which are enabled. These options all take a comma "
|
|
"separated list of rules to disable and may be specified by number "
|
|
"or number range (e.g. 1-10)."
|
|
#ifndef NDEBUG
|
|
" They may also be specified by name."
|
|
#endif
|
|
);
|
|
} // end namespace llvm
|
|
|
|
/// This class acts as the glue that joins the CombinerHelper to the overall
|
|
/// Combine algorithm. The CombinerHelper is intended to report the
|
|
/// modifications it makes to the MIR to the GISelChangeObserver and the
|
|
/// observer subclass will act on these events.
|
|
class Combiner::WorkListMaintainer : public GISelChangeObserver {
|
|
protected:
|
|
#ifndef NDEBUG
|
|
/// The instructions that have been created but we want to report once they
|
|
/// have their operands. This is only maintained if debug output is requested.
|
|
SmallSetVector<const MachineInstr *, 32> CreatedInstrs;
|
|
#endif
|
|
using Level = CombinerInfo::ObserverLevel;
|
|
|
|
public:
|
|
static std::unique_ptr<WorkListMaintainer>
|
|
create(Level Lvl, WorkListTy &WorkList, MachineRegisterInfo &MRI);
|
|
|
|
virtual ~WorkListMaintainer() = default;
|
|
|
|
void reportFullyCreatedInstrs() {
|
|
LLVM_DEBUG({
|
|
for (auto *MI : CreatedInstrs) {
|
|
dbgs() << "Created: " << *MI;
|
|
}
|
|
CreatedInstrs.clear();
|
|
});
|
|
}
|
|
|
|
virtual void reset() = 0;
|
|
virtual void appliedCombine() = 0;
|
|
};
|
|
|
|
/// A configurable WorkListMaintainer implementation.
|
|
/// The ObserverLevel determines how the WorkListMaintainer reacts to MIR
|
|
/// changes.
|
|
template <CombinerInfo::ObserverLevel Lvl>
|
|
class Combiner::WorkListMaintainerImpl : public Combiner::WorkListMaintainer {
|
|
WorkListTy &WorkList;
|
|
MachineRegisterInfo &MRI;
|
|
|
|
// Defer handling these instructions until the combine finishes.
|
|
SmallSetVector<MachineInstr *, 32> DeferList;
|
|
|
|
// Track VRegs that (might) have lost a use.
|
|
SmallSetVector<Register, 32> LostUses;
|
|
|
|
public:
|
|
WorkListMaintainerImpl(WorkListTy &WorkList, MachineRegisterInfo &MRI)
|
|
: WorkList(WorkList), MRI(MRI) {}
|
|
|
|
virtual ~WorkListMaintainerImpl() = default;
|
|
|
|
void reset() override {
|
|
DeferList.clear();
|
|
LostUses.clear();
|
|
}
|
|
|
|
void erasingInstr(MachineInstr &MI) override {
|
|
// MI will become dangling, remove it from all lists.
|
|
LLVM_DEBUG(dbgs() << "Erasing: " << MI; CreatedInstrs.remove(&MI));
|
|
WorkList.remove(&MI);
|
|
if constexpr (Lvl != Level::Basic) {
|
|
DeferList.remove(&MI);
|
|
noteLostUses(MI);
|
|
}
|
|
}
|
|
|
|
void createdInstr(MachineInstr &MI) override {
|
|
LLVM_DEBUG(dbgs() << "Creating: " << MI; CreatedInstrs.insert(&MI));
|
|
if constexpr (Lvl == Level::Basic)
|
|
WorkList.insert(&MI);
|
|
else
|
|
// Defer handling newly created instructions, because they don't have
|
|
// operands yet. We also insert them into the WorkList in reverse
|
|
// order so that they will be combined top down.
|
|
DeferList.insert(&MI);
|
|
}
|
|
|
|
void changingInstr(MachineInstr &MI) override {
|
|
LLVM_DEBUG(dbgs() << "Changing: " << MI);
|
|
// Some uses might get dropped when MI is changed.
|
|
// For now, overapproximate by assuming all uses will be dropped.
|
|
// TODO: Is a more precise heuristic or manual tracking of use count
|
|
// decrements worth it?
|
|
if constexpr (Lvl != Level::Basic)
|
|
noteLostUses(MI);
|
|
}
|
|
|
|
void changedInstr(MachineInstr &MI) override {
|
|
LLVM_DEBUG(dbgs() << "Changed: " << MI);
|
|
if constexpr (Lvl == Level::Basic)
|
|
WorkList.insert(&MI);
|
|
else
|
|
// Defer this for DCE
|
|
DeferList.insert(&MI);
|
|
}
|
|
|
|
// Only track changes during the combine and then walk the def/use-chains once
|
|
// the combine is finished, because:
|
|
// - instructions might have multiple defs during the combine.
|
|
// - use counts aren't accurate during the combine.
|
|
void appliedCombine() override {
|
|
if constexpr (Lvl == Level::Basic)
|
|
return;
|
|
|
|
// DCE deferred instructions and add them to the WorkList bottom up.
|
|
while (!DeferList.empty()) {
|
|
MachineInstr &MI = *DeferList.pop_back_val();
|
|
if (tryDCE(MI, MRI))
|
|
continue;
|
|
|
|
if constexpr (Lvl >= Level::SinglePass)
|
|
addUsersToWorkList(MI);
|
|
|
|
WorkList.insert(&MI);
|
|
}
|
|
|
|
// Handle instructions that have lost a user.
|
|
while (!LostUses.empty()) {
|
|
Register Use = LostUses.pop_back_val();
|
|
MachineInstr *UseMI = MRI.getVRegDef(Use);
|
|
if (!UseMI)
|
|
continue;
|
|
|
|
// If DCE succeeds, UseMI's uses are added back to LostUses by
|
|
// erasingInstr.
|
|
if (tryDCE(*UseMI, MRI))
|
|
continue;
|
|
|
|
if constexpr (Lvl >= Level::SinglePass) {
|
|
// OneUse checks are relatively common, so we might be able to combine
|
|
// the single remaining user of this Reg.
|
|
if (MRI.hasOneNonDBGUser(Use))
|
|
WorkList.insert(&*MRI.use_instr_nodbg_begin(Use));
|
|
|
|
WorkList.insert(UseMI);
|
|
}
|
|
}
|
|
}
|
|
|
|
void noteLostUses(MachineInstr &MI) {
|
|
for (auto &Use : MI.explicit_uses()) {
|
|
if (!Use.isReg() || !Use.getReg().isVirtual())
|
|
continue;
|
|
LostUses.insert(Use.getReg());
|
|
}
|
|
}
|
|
|
|
void addUsersToWorkList(MachineInstr &MI) {
|
|
for (auto &Def : MI.defs()) {
|
|
Register DefReg = Def.getReg();
|
|
if (!DefReg.isVirtual())
|
|
continue;
|
|
for (auto &UseMI : MRI.use_nodbg_instructions(DefReg)) {
|
|
WorkList.insert(&UseMI);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
std::unique_ptr<Combiner::WorkListMaintainer>
|
|
Combiner::WorkListMaintainer::create(Level Lvl, WorkListTy &WorkList,
|
|
MachineRegisterInfo &MRI) {
|
|
switch (Lvl) {
|
|
case Level::Basic:
|
|
return std::make_unique<WorkListMaintainerImpl<Level::Basic>>(WorkList,
|
|
MRI);
|
|
case Level::DCE:
|
|
return std::make_unique<WorkListMaintainerImpl<Level::DCE>>(WorkList, MRI);
|
|
case Level::SinglePass:
|
|
return std::make_unique<WorkListMaintainerImpl<Level::SinglePass>>(WorkList,
|
|
MRI);
|
|
}
|
|
llvm_unreachable("Illegal ObserverLevel");
|
|
}
|
|
|
|
Combiner::Combiner(MachineFunction &MF, CombinerInfo &CInfo,
|
|
const TargetPassConfig *TPC, GISelKnownBits *KB,
|
|
GISelCSEInfo *CSEInfo)
|
|
: Builder(CSEInfo ? std::make_unique<CSEMIRBuilder>()
|
|
: std::make_unique<MachineIRBuilder>()),
|
|
WLObserver(WorkListMaintainer::create(CInfo.ObserverLvl, WorkList,
|
|
MF.getRegInfo())),
|
|
ObserverWrapper(std::make_unique<GISelObserverWrapper>()), CInfo(CInfo),
|
|
Observer(*ObserverWrapper), B(*Builder), MF(MF), MRI(MF.getRegInfo()),
|
|
KB(KB), TPC(TPC), CSEInfo(CSEInfo) {
|
|
(void)this->TPC; // FIXME: Remove when used.
|
|
|
|
// Setup builder.
|
|
B.setMF(MF);
|
|
if (CSEInfo)
|
|
B.setCSEInfo(CSEInfo);
|
|
|
|
B.setChangeObserver(*ObserverWrapper);
|
|
}
|
|
|
|
Combiner::~Combiner() = default;
|
|
|
|
bool Combiner::tryDCE(MachineInstr &MI, MachineRegisterInfo &MRI) {
|
|
if (!isTriviallyDead(MI, MRI))
|
|
return false;
|
|
LLVM_DEBUG(dbgs() << "Dead: " << MI);
|
|
llvm::salvageDebugInfo(MRI, MI);
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
bool Combiner::combineMachineInstrs() {
|
|
// If the ISel pipeline failed, do not bother running this pass.
|
|
// FIXME: Should this be here or in individual combiner passes.
|
|
if (MF.getProperties().hasProperty(
|
|
MachineFunctionProperties::Property::FailedISel))
|
|
return false;
|
|
|
|
// We can't call this in the constructor because the derived class is
|
|
// uninitialized at that time.
|
|
if (!HasSetupMF) {
|
|
HasSetupMF = true;
|
|
setupMF(MF, KB);
|
|
}
|
|
|
|
LLVM_DEBUG(dbgs() << "Generic MI Combiner for: " << MF.getName() << '\n');
|
|
|
|
MachineOptimizationRemarkEmitter MORE(MF, /*MBFI=*/nullptr);
|
|
|
|
bool MFChanged = false;
|
|
bool Changed;
|
|
|
|
unsigned Iteration = 0;
|
|
while (true) {
|
|
++Iteration;
|
|
LLVM_DEBUG(dbgs() << "\n\nCombiner iteration #" << Iteration << '\n');
|
|
|
|
Changed = false;
|
|
WorkList.clear();
|
|
WLObserver->reset();
|
|
ObserverWrapper->clearObservers();
|
|
if (CSEInfo)
|
|
ObserverWrapper->addObserver(CSEInfo);
|
|
|
|
// If Observer-based DCE is enabled, perform full DCE only before the first
|
|
// iteration.
|
|
bool EnableDCE = CInfo.ObserverLvl >= CombinerInfo::ObserverLevel::DCE
|
|
? CInfo.EnableFullDCE && Iteration == 1
|
|
: CInfo.EnableFullDCE;
|
|
|
|
// Collect all instructions. Do a post order traversal for basic blocks and
|
|
// insert with list bottom up, so while we pop_back_val, we'll traverse top
|
|
// down RPOT.
|
|
RAIIMFObsDelInstaller DelInstall(MF, *ObserverWrapper);
|
|
for (MachineBasicBlock *MBB : post_order(&MF)) {
|
|
for (MachineInstr &CurMI :
|
|
llvm::make_early_inc_range(llvm::reverse(*MBB))) {
|
|
// Erase dead insts before even adding to the list.
|
|
if (EnableDCE && tryDCE(CurMI, MRI))
|
|
continue;
|
|
WorkList.deferred_insert(&CurMI);
|
|
}
|
|
}
|
|
WorkList.finalize();
|
|
|
|
// Only notify WLObserver during actual combines
|
|
ObserverWrapper->addObserver(WLObserver.get());
|
|
// Main Loop. Process the instructions here.
|
|
while (!WorkList.empty()) {
|
|
MachineInstr &CurrInst = *WorkList.pop_back_val();
|
|
LLVM_DEBUG(dbgs() << "\nTry combining " << CurrInst);
|
|
bool AppliedCombine = tryCombineAll(CurrInst);
|
|
LLVM_DEBUG(WLObserver->reportFullyCreatedInstrs());
|
|
Changed |= AppliedCombine;
|
|
if (AppliedCombine)
|
|
WLObserver->appliedCombine();
|
|
}
|
|
MFChanged |= Changed;
|
|
|
|
if (!Changed) {
|
|
LLVM_DEBUG(dbgs() << "\nCombiner reached fixed-point after iteration #"
|
|
<< Iteration << '\n');
|
|
break;
|
|
}
|
|
// Iterate until a fixed-point is reached if MaxIterations == 0,
|
|
// otherwise limit the number of iterations.
|
|
if (CInfo.MaxIterations && Iteration >= CInfo.MaxIterations) {
|
|
LLVM_DEBUG(
|
|
dbgs() << "\nCombiner reached iteration limit after iteration #"
|
|
<< Iteration << '\n');
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Iteration == 1)
|
|
++NumOneIteration;
|
|
else if (Iteration == 2)
|
|
++NumTwoIterations;
|
|
else
|
|
++NumThreeOrMoreIterations;
|
|
|
|
#ifndef NDEBUG
|
|
if (CSEInfo) {
|
|
if (auto E = CSEInfo->verify()) {
|
|
errs() << E << '\n';
|
|
assert(false && "CSEInfo is not consistent. Likely missing calls to "
|
|
"observer on mutations.");
|
|
}
|
|
}
|
|
#endif
|
|
return MFChanged;
|
|
}
|