1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 18:54:02 +01:00

[GISel]: Add support for CSEing continuously during GISel passes.

https://reviews.llvm.org/D52803

This patch adds support to continuously CSE instructions during
each of the GISel passes. It consists of a GISelCSEInfo analysis pass
that can be used by the CSEMIRBuilder.

llvm-svn: 351283
This commit is contained in:
Aditya Nandakumar 2019-01-16 00:40:37 +00:00
parent 61135b4791
commit c969a48f8c
34 changed files with 1499 additions and 231 deletions

View File

@ -0,0 +1,237 @@
//===- llvm/CodeGen/GlobalISel/CSEInfo.h ------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
/// Provides analysis for continuously CSEing during GISel passes.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_CSEINFO_H
#define LLVM_CODEGEN_GLOBALISEL_CSEINFO_H
#include "llvm/ADT/FoldingSet.h"
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
#include "llvm/CodeGen/GlobalISel/GISelWorkList.h"
#include "llvm/CodeGen/GlobalISel/Utils.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
#include "llvm/Support/Allocator.h"
namespace llvm {
/// A class that wraps MachineInstrs and derives from FoldingSetNode in order to
/// be uniqued in a CSEMap. The tradeoff here is extra memory allocations for
/// UniqueMachineInstr vs making MachineInstr bigger.
class UniqueMachineInstr : public FoldingSetNode {
friend class GISelCSEInfo;
const MachineInstr *MI;
explicit UniqueMachineInstr(const MachineInstr *MI) : MI(MI) {}
public:
void Profile(FoldingSetNodeID &ID);
};
// Class representing some configuration that can be done during CSE analysis.
// Currently it only supports shouldCSE method that each pass can set.
class CSEConfig {
public:
virtual ~CSEConfig() = default;
// Hook for defining which Generic instructions should be CSEd.
// GISelCSEInfo currently only calls this hook when dealing with generic
// opcodes.
virtual bool shouldCSEOpc(unsigned Opc);
};
// TODO: Find a better place for this.
// Commonly used for O0 config.
class CSEConfigConstantOnly : public CSEConfig {
public:
virtual ~CSEConfigConstantOnly() = default;
virtual bool shouldCSEOpc(unsigned Opc) override;
};
/// The CSE Analysis object.
/// This installs itself as a delegate to the MachineFunction to track
/// new instructions as well as deletions. It however will not be able to
/// track instruction mutations. In such cases, recordNewInstruction should be
/// called (for eg inside MachineIRBuilder::recordInsertion).
/// Also because of how just the instruction can be inserted without adding any
/// operands to the instruction, instructions are uniqued and inserted lazily.
/// CSEInfo should assert when trying to enter an incomplete instruction into
/// the CSEMap. There is Opcode level granularity on which instructions can be
/// CSE'd and for now, only Generic instructions are CSEable.
class GISelCSEInfo : public GISelChangeObserver {
// Make it accessible only to CSEMIRBuilder.
friend class CSEMIRBuilder;
BumpPtrAllocator UniqueInstrAllocator;
FoldingSet<UniqueMachineInstr> CSEMap;
MachineRegisterInfo *MRI = nullptr;
MachineFunction *MF = nullptr;
std::unique_ptr<CSEConfig> CSEOpt;
/// Keep a cache of UniqueInstrs for each MachineInstr. In GISel,
/// often instructions are mutated (while their ID has completely changed).
/// Whenever mutation happens, invalidate the UniqueMachineInstr for the
/// MachineInstr
DenseMap<const MachineInstr *, UniqueMachineInstr *> InstrMapping;
/// Store instructions that are not fully formed in TemporaryInsts.
/// Also because CSE insertion happens lazily, we can remove insts from this
/// list and avoid inserting and then removing from the CSEMap.
GISelWorkList<8> TemporaryInsts;
// Only used in asserts.
DenseMap<unsigned, unsigned> OpcodeHitTable;
bool isUniqueMachineInstValid(const UniqueMachineInstr &UMI) const;
void invalidateUniqueMachineInstr(UniqueMachineInstr *UMI);
UniqueMachineInstr *getNodeIfExists(FoldingSetNodeID &ID,
MachineBasicBlock *MBB, void *&InsertPos);
/// Allocate and construct a new UniqueMachineInstr for MI and return.
UniqueMachineInstr *getUniqueInstrForMI(const MachineInstr *MI);
void insertNode(UniqueMachineInstr *UMI, void *InsertPos = nullptr);
/// Get the MachineInstr(Unique) if it exists already in the CSEMap and the
/// same MachineBasicBlock.
MachineInstr *getMachineInstrIfExists(FoldingSetNodeID &ID,
MachineBasicBlock *MBB,
void *&InsertPos);
/// Use this method to allocate a new UniqueMachineInstr for MI and insert it
/// into the CSEMap. MI should return true for shouldCSE(MI->getOpcode())
void insertInstr(MachineInstr *MI, void *InsertPos = nullptr);
public:
GISelCSEInfo() = default;
virtual ~GISelCSEInfo();
void setMF(MachineFunction &MF);
/// Records a newly created inst in a list and lazily insert it to the CSEMap.
/// Sometimes, this method might be called with a partially constructed
/// MachineInstr,
// (right after BuildMI without adding any operands) - and in such cases,
// defer the hashing of the instruction to a later stage.
void recordNewInstruction(MachineInstr *MI);
/// Use this callback to inform CSE about a newly fully created instruction.
void handleRecordedInst(MachineInstr *MI);
/// Use this callback to insert all the recorded instructions. At this point,
/// all of these insts need to be fully constructed and should not be missing
/// any operands.
void handleRecordedInsts();
/// Remove this inst from the CSE map. If this inst has not been inserted yet,
/// it will be removed from the Tempinsts list if it exists.
void handleRemoveInst(MachineInstr *MI);
void releaseMemory();
void setCSEConfig(std::unique_ptr<CSEConfig> Opt) { CSEOpt = std::move(Opt); }
bool shouldCSE(unsigned Opc) const;
void analyze(MachineFunction &MF);
void countOpcodeHit(unsigned Opc);
void print();
// Observer API
void erasingInstr(MachineInstr &MI) override;
void createdInstr(MachineInstr &MI) override;
void changingInstr(MachineInstr &MI) override;
void changedInstr(MachineInstr &MI) override;
};
class TargetRegisterClass;
class RegisterBank;
// Simple builder class to easily profile properties about MIs.
class GISelInstProfileBuilder {
FoldingSetNodeID &ID;
const MachineRegisterInfo &MRI;
public:
GISelInstProfileBuilder(FoldingSetNodeID &ID, const MachineRegisterInfo &MRI)
: ID(ID), MRI(MRI) {}
// Profiling methods.
const GISelInstProfileBuilder &addNodeIDOpcode(unsigned Opc) const;
const GISelInstProfileBuilder &addNodeIDRegType(const LLT &Ty) const;
const GISelInstProfileBuilder &addNodeIDRegType(const unsigned) const;
const GISelInstProfileBuilder &
addNodeIDRegType(const TargetRegisterClass *RC) const;
const GISelInstProfileBuilder &addNodeIDRegType(const RegisterBank *RB) const;
const GISelInstProfileBuilder &addNodeIDRegNum(unsigned Reg) const;
const GISelInstProfileBuilder &addNodeIDImmediate(int64_t Imm) const;
const GISelInstProfileBuilder &
addNodeIDMBB(const MachineBasicBlock *MBB) const;
const GISelInstProfileBuilder &
addNodeIDMachineOperand(const MachineOperand &MO) const;
const GISelInstProfileBuilder &addNodeIDFlag(unsigned Flag) const;
const GISelInstProfileBuilder &addNodeID(const MachineInstr *MI) const;
};
/// Simple wrapper that does the following.
/// 1) Lazily evaluate the MachineFunction to compute CSEable instructions.
/// 2) Allows configuration of which instructions are CSEd through CSEConfig
/// object. Provides a method called get which takes a CSEConfig object.
class GISelCSEAnalysisWrapper {
GISelCSEInfo Info;
MachineFunction *MF = nullptr;
bool AlreadyComputed = false;
public:
/// Takes a CSEConfig object that defines what opcodes get CSEd.
/// If CSEConfig is already set, and the CSE Analysis has been preserved,
/// it will not use the new CSEOpt(use Recompute to force using the new
/// CSEOpt).
GISelCSEInfo &get(std::unique_ptr<CSEConfig> CSEOpt, bool ReCompute = false);
void setMF(MachineFunction &MFunc) { MF = &MFunc; }
void setComputed(bool Computed) { AlreadyComputed = Computed; }
void releaseMemory() { Info.releaseMemory(); }
};
/// The actual analysis pass wrapper.
class GISelCSEAnalysisWrapperPass : public MachineFunctionPass {
GISelCSEAnalysisWrapper Wrapper;
public:
static char ID;
GISelCSEAnalysisWrapperPass() : MachineFunctionPass(ID) {
initializeGISelCSEAnalysisWrapperPassPass(*PassRegistry::getPassRegistry());
}
void getAnalysisUsage(AnalysisUsage &AU) const override;
const GISelCSEAnalysisWrapper &getCSEWrapper() const { return Wrapper; }
GISelCSEAnalysisWrapper &getCSEWrapper() { return Wrapper; }
bool runOnMachineFunction(MachineFunction &MF) override;
void releaseMemory() override {
Wrapper.releaseMemory();
Wrapper.setComputed(false);
}
};
} // namespace llvm
#endif

View File

@ -0,0 +1,110 @@
//===-- llvm/CodeGen/GlobalISel/CSEMIRBuilder.h --*- C++ -*-==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/// \file
/// This file implements a version of MachineIRBuilder which CSEs insts within
/// a MachineBasicBlock.
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_GLOBALISEL_CSEMIRBUILDER_H
#define LLVM_CODEGEN_GLOBALISEL_CSEMIRBUILDER_H
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/GlobalISel/Utils.h"
namespace llvm {
/// Defines a builder that does CSE of MachineInstructions using GISelCSEInfo.
/// Eg usage.
///
///
/// GISelCSEInfo *Info =
/// &getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEInfo(); CSEMIRBuilder
/// CB(Builder.getState()); CB.setCSEInfo(Info); auto A = CB.buildConstant(s32,
/// 42); auto B = CB.buildConstant(s32, 42); assert(A == B); unsigned CReg =
/// MRI.createGenericVirtualRegister(s32); auto C = CB.buildConstant(CReg, 42);
/// assert(C->getOpcode() == TargetOpcode::COPY);
/// Explicitly passing in a register would materialize a copy if possible.
/// CSEMIRBuilder also does trivial constant folding for binary ops.
class CSEMIRBuilder : public MachineIRBuilder {
/// Returns true if A dominates B (within the same basic block).
/// Both iterators must be in the same basic block.
//
// TODO: Another approach for checking dominance is having two iterators and
// making them go towards each other until they meet or reach begin/end. Which
// approach is better? Should this even change dynamically? For G_CONSTANTS
// most of which will be at the top of the BB, the top down approach would be
// a better choice. Does IRTranslator placing constants at the beginning still
// make sense? Should this change based on Opcode?
bool dominates(MachineBasicBlock::const_iterator A,
MachineBasicBlock::const_iterator B) const;
/// For given ID, find a machineinstr in the CSE Map. If found, check if it
/// dominates the current insertion point and if not, move it just before the
/// current insertion point and return it. If not found, return Null
/// MachineInstrBuilder.
MachineInstrBuilder getDominatingInstrForID(FoldingSetNodeID &ID,
void *&NodeInsertPos);
/// Simple check if we can CSE (we have the CSEInfo) or if this Opcode is
/// safe to CSE.
bool canPerformCSEForOpc(unsigned Opc) const;
void profileDstOp(const DstOp &Op, GISelInstProfileBuilder &B) const;
void profileDstOps(ArrayRef<DstOp> Ops, GISelInstProfileBuilder &B) const {
for (const DstOp &Op : Ops)
profileDstOp(Op, B);
}
void profileSrcOp(const SrcOp &Op, GISelInstProfileBuilder &B) const;
void profileSrcOps(ArrayRef<SrcOp> Ops, GISelInstProfileBuilder &B) const {
for (const SrcOp &Op : Ops)
profileSrcOp(Op, B);
}
void profileMBBOpcode(GISelInstProfileBuilder &B, unsigned Opc) const;
void profileEverything(unsigned Opc, ArrayRef<DstOp> DstOps,
ArrayRef<SrcOp> SrcOps, Optional<unsigned> Flags,
GISelInstProfileBuilder &B) const;
// Takes a MachineInstrBuilder and inserts it into the CSEMap using the
// NodeInsertPos.
MachineInstrBuilder memoizeMI(MachineInstrBuilder MIB, void *NodeInsertPos);
// If we have can CSE an instruction, but still need to materialize to a VReg,
// we emit a copy from the CSE'd inst to the VReg.
MachineInstrBuilder generateCopiesIfRequired(ArrayRef<DstOp> DstOps,
MachineInstrBuilder &MIB);
// If we have can CSE an instruction, but still need to materialize to a VReg,
// check if we can generate copies. It's not possible to return a single MIB,
// while emitting copies to multiple vregs.
bool checkCopyToDefsPossible(ArrayRef<DstOp> DstOps);
public:
// Pull in base class constructors.
using MachineIRBuilder::MachineIRBuilder;
// Unhide buildInstr
MachineInstrBuilder buildInstr(unsigned Opc, ArrayRef<DstOp> DstOps,
ArrayRef<SrcOp> SrcOps,
Optional<unsigned> Flag = None) override;
// Bring in the other overload from the base class.
using MachineIRBuilder::buildConstant;
MachineInstrBuilder buildConstant(const DstOp &Res,
const ConstantInt &Val) override;
// Bring in the other overload from the base class.
using MachineIRBuilder::buildFConstant;
MachineInstrBuilder buildFConstant(const DstOp &Res,
const ConstantFP &Val) override;
};
} // namespace llvm
#endif

View File

@ -21,6 +21,7 @@
namespace llvm {
class MachineRegisterInfo;
class CombinerInfo;
class GISelCSEInfo;
class TargetPassConfig;
class MachineFunction;
@ -28,14 +29,17 @@ class Combiner {
public:
Combiner(CombinerInfo &CombinerInfo, const TargetPassConfig *TPC);
bool combineMachineInstrs(MachineFunction &MF);
/// If CSEInfo is not null, then the Combiner will setup observer for
/// CSEInfo and instantiate a CSEMIRBuilder. Pass nullptr if CSE is not
/// needed.
bool combineMachineInstrs(MachineFunction &MF, GISelCSEInfo *CSEInfo);
protected:
CombinerInfo &CInfo;
MachineRegisterInfo *MRI = nullptr;
const TargetPassConfig *TPC;
MachineIRBuilder Builder;
std::unique_ptr<MachineIRBuilder> Builder;
};
} // End namespace llvm.

View File

@ -15,57 +15,6 @@
namespace llvm {
static Optional<APInt> ConstantFoldBinOp(unsigned Opcode, const unsigned Op1,
const unsigned Op2,
const MachineRegisterInfo &MRI) {
auto MaybeOp1Cst = getConstantVRegVal(Op1, MRI);
auto MaybeOp2Cst = getConstantVRegVal(Op2, MRI);
if (MaybeOp1Cst && MaybeOp2Cst) {
LLT Ty = MRI.getType(Op1);
APInt C1(Ty.getSizeInBits(), *MaybeOp1Cst, true);
APInt C2(Ty.getSizeInBits(), *MaybeOp2Cst, true);
switch (Opcode) {
default:
break;
case TargetOpcode::G_ADD:
return C1 + C2;
case TargetOpcode::G_AND:
return C1 & C2;
case TargetOpcode::G_ASHR:
return C1.ashr(C2);
case TargetOpcode::G_LSHR:
return C1.lshr(C2);
case TargetOpcode::G_MUL:
return C1 * C2;
case TargetOpcode::G_OR:
return C1 | C2;
case TargetOpcode::G_SHL:
return C1 << C2;
case TargetOpcode::G_SUB:
return C1 - C2;
case TargetOpcode::G_XOR:
return C1 ^ C2;
case TargetOpcode::G_UDIV:
if (!C2.getBoolValue())
break;
return C1.udiv(C2);
case TargetOpcode::G_SDIV:
if (!C2.getBoolValue())
break;
return C1.sdiv(C2);
case TargetOpcode::G_UREM:
if (!C2.getBoolValue())
break;
return C1.urem(C2);
case TargetOpcode::G_SREM:
if (!C2.getBoolValue())
break;
return C1.srem(C2);
}
}
return None;
}
/// An MIRBuilder which does trivial constant folding of binary ops.
/// Calls to buildInstr will also try to constant fold binary ops.
class ConstantFoldingMIRBuilder : public MachineIRBuilder {

View File

@ -15,6 +15,7 @@
#define LLVM_CODEGEN_GLOBALISEL_GISELCHANGEOBSERVER_H
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/CodeGen/MachineFunction.h"
namespace llvm {
class MachineInstr;
@ -32,13 +33,13 @@ public:
virtual ~GISelChangeObserver() {}
/// An instruction is about to be erased.
virtual void erasingInstr(const MachineInstr &MI) = 0;
virtual void erasingInstr(MachineInstr &MI) = 0;
/// An instruction was created and inserted into the function.
virtual void createdInstr(const MachineInstr &MI) = 0;
virtual void createdInstr(MachineInstr &MI) = 0;
/// This instruction is about to be mutated in some way.
virtual void changingInstr(const MachineInstr &MI) = 0;
virtual void changingInstr(MachineInstr &MI) = 0;
/// This instruction was mutated in some way.
virtual void changedInstr(const MachineInstr &MI) = 0;
virtual void changedInstr(MachineInstr &MI) = 0;
/// All the instructions using the given register are being changed.
/// For convenience, finishedChangingAllUsesOfReg() will report the completion
@ -51,5 +52,60 @@ public:
};
/// Simple wrapper observer that takes several observers, and calls
/// each one for each event. If there are multiple observers (say CSE,
/// Legalizer, Combiner), it's sufficient to register this to the machine
/// function as the delegate.
class GISelObserverWrapper : public MachineFunction::Delegate,
public GISelChangeObserver {
SmallVector<GISelChangeObserver *, 4> Observers;
public:
GISelObserverWrapper() = default;
GISelObserverWrapper(ArrayRef<GISelChangeObserver *> Obs)
: Observers(Obs.begin(), Obs.end()) {}
// Adds an observer.
void addObserver(GISelChangeObserver *O) { Observers.push_back(O); }
// Removes an observer from the list and does nothing if observer is not
// present.
void removeObserver(GISelChangeObserver *O) {
auto It = std::find(Observers.begin(), Observers.end(), O);
if (It != Observers.end())
Observers.erase(It);
}
// API for Observer.
void erasingInstr(MachineInstr &MI) override {
for (auto &O : Observers)
O->erasingInstr(MI);
}
void createdInstr(MachineInstr &MI) override {
for (auto &O : Observers)
O->createdInstr(MI);
}
void changingInstr(MachineInstr &MI) override {
for (auto &O : Observers)
O->changingInstr(MI);
}
void changedInstr(MachineInstr &MI) override {
for (auto &O : Observers)
O->changedInstr(MI);
}
// API for MachineFunction::Delegate
void MF_HandleInsertion(MachineInstr &MI) override { createdInstr(MI); }
void MF_HandleRemoval(MachineInstr &MI) override { erasingInstr(MI); }
};
/// A simple RAII based CSEInfo installer.
/// Use this in a scope to install a delegate to the MachineFunction and reset
/// it at the end of the scope.
class RAIIDelegateInstaller {
MachineFunction &MF;
MachineFunction::Delegate *Delegate;
public:
RAIIDelegateInstaller(MachineFunction &MF, MachineFunction::Delegate *Del);
~RAIIDelegateInstaller();
};
} // namespace llvm
#endif

View File

@ -18,6 +18,7 @@
namespace llvm {
class MachineInstr;
class MachineFunction;
// Worklist which mostly works similar to InstCombineWorkList, but on
@ -25,23 +26,15 @@ class MachineFunction;
// erasing an element doesn't move all elements over one place - instead just
// nulls out the element of the vector.
//
// This worklist operates on instructions within a particular function. This is
// important for acquiring the rights to modify/replace instructions a
// GISelChangeObserver reports as the observer doesn't have the right to make
// changes to the instructions it sees so we use our access to the
// MachineFunction to establish that it's ok to add a given instruction to the
// worklist.
//
// FIXME: Does it make sense to factor out common code with the
// instcombinerWorkList?
template<unsigned N>
class GISelWorkList {
MachineFunction *MF;
SmallVector<MachineInstr *, N> Worklist;
DenseMap<MachineInstr *, unsigned> WorklistMap;
public:
GISelWorkList(MachineFunction *MF) : MF(MF) {}
GISelWorkList() {}
bool empty() const { return WorklistMap.empty(); }
@ -49,27 +42,8 @@ public:
/// Add the specified instruction to the worklist if it isn't already in it.
void insert(MachineInstr *I) {
// It would be safe to add this instruction to the worklist regardless but
// for consistency with the const version, check that the instruction we're
// adding would have been accepted if we were given a const pointer instead.
insert(const_cast<const MachineInstr *>(I));
}
void insert(const MachineInstr *I) {
// Confirm we'd be able to find the non-const pointer we want to schedule if
// we wanted to. We have the right to schedule work that may modify any
// instruction in MF.
assert(I->getParent() && "Expected parent BB");
assert(I->getParent()->getParent() && "Expected parent function");
assert((!MF || I->getParent()->getParent() == MF) &&
"Expected parent function to be current function or not given");
// But don't actually do the search since we can derive it from the const
// pointer.
MachineInstr *NonConstI = const_cast<MachineInstr *>(I);
if (WorklistMap.try_emplace(NonConstI, Worklist.size()).second) {
Worklist.push_back(NonConstI);
}
if (WorklistMap.try_emplace(I, Worklist.size()).second)
Worklist.push_back(I);
}
/// Remove I from the worklist if it exists.
@ -83,6 +57,11 @@ public:
WorklistMap.erase(It);
}
void clear() {
Worklist.clear();
WorklistMap.clear();
}
MachineInstr *pop_back_val() {
MachineInstr *I;
do {

View File

@ -21,11 +21,11 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h"
#include "llvm/CodeGen/GlobalISel/Types.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/Support/Allocator.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/Support/Allocator.h"
#include <memory>
#include <utility>
@ -444,11 +444,13 @@ private:
// I.e., compared to regular MIBuilder, this one also inserts the instruction
// in the current block, it can creates block, etc., basically a kind of
// IRBuilder, but for Machine IR.
MachineIRBuilder CurBuilder;
// CSEMIRBuilder CurBuilder;
std::unique_ptr<MachineIRBuilder> CurBuilder;
// Builder set to the entry block (just after ABI lowering instructions). Used
// as a convenient location for Constants.
MachineIRBuilder EntryBuilder;
// CSEMIRBuilder EntryBuilder;
std::unique_ptr<MachineIRBuilder> EntryBuilder;
// The MachineFunction currently being translated.
MachineFunction *MF;

View File

@ -49,9 +49,10 @@ public:
UnableToLegalize,
};
LegalizerHelper(MachineFunction &MF, GISelChangeObserver &Observer);
LegalizerHelper(MachineFunction &MF, GISelChangeObserver &Observer,
MachineIRBuilder &B);
LegalizerHelper(MachineFunction &MF, const LegalizerInfo &LI,
GISelChangeObserver &Observer);
GISelChangeObserver &Observer, MachineIRBuilder &B);
/// Replace \p MI by a sequence of legal instructions that can implement the
/// same operation. Note that this means \p MI may be deleted, so any iterator
@ -90,7 +91,7 @@ public:
/// Expose MIRBuilder so clients can set their own RecordInsertInstruction
/// functions
MachineIRBuilder MIRBuilder;
MachineIRBuilder &MIRBuilder;
/// Expose LegalizerInfo so the clients can re-use.
const LegalizerInfo &getLegalizerInfo() const { return LI; }

View File

@ -14,6 +14,7 @@
#ifndef LLVM_CODEGEN_GLOBALISEL_MACHINEIRBUILDER_H
#define LLVM_CODEGEN_GLOBALISEL_MACHINEIRBUILDER_H
#include "llvm/CodeGen/GlobalISel/CSEInfo.h"
#include "llvm/CodeGen/GlobalISel/Types.h"
#include "llvm/CodeGen/LowLevelType.h"
@ -52,6 +53,8 @@ struct MachineIRBuilderState {
/// @}
GISelChangeObserver *Observer;
GISelCSEInfo *CSEInfo;
};
class DstOp {
@ -81,8 +84,6 @@ public:
}
}
DstType getType() const { return Ty; }
LLT getLLTTy(const MachineRegisterInfo &MRI) const {
switch (Ty) {
case DstType::Ty_RC:
@ -95,6 +96,20 @@ public:
llvm_unreachable("Unrecognised DstOp::DstType enum");
}
unsigned getReg() const {
assert(Ty == DstType::Ty_Reg && "Not a register");
return Reg;
}
const TargetRegisterClass *getRegClass() const {
switch (Ty) {
case DstType::Ty_RC:
return RC;
default:
llvm_unreachable("Not a RC Operand");
}
}
DstType getDstOpKind() const { return Ty; }
private:
@ -220,16 +235,25 @@ public:
/// Getter for MRI
MachineRegisterInfo *getMRI() { return State.MRI; }
const MachineRegisterInfo *getMRI() const { return State.MRI; }
/// Getter for the State
MachineIRBuilderState &getState() { return State; }
/// Getter for the basic block we currently build.
MachineBasicBlock &getMBB() {
const MachineBasicBlock &getMBB() const {
assert(State.MBB && "MachineBasicBlock is not set");
return *State.MBB;
}
MachineBasicBlock &getMBB() {
return const_cast<MachineBasicBlock &>(
const_cast<const MachineIRBuilder *>(this)->getMBB());
}
GISelCSEInfo *getCSEInfo() { return State.CSEInfo; }
const GISelCSEInfo *getCSEInfo() const { return State.CSEInfo; }
/// Current insertion point for new instructions.
MachineBasicBlock::iterator getInsertPt() { return State.II; }
@ -239,10 +263,12 @@ public:
void setInsertPt(MachineBasicBlock &MBB, MachineBasicBlock::iterator II);
/// @}
void setCSEInfo(GISelCSEInfo *Info);
/// \name Setters for the insertion point.
/// @{
/// Set the MachineFunction where to build instructions.
void setMF(MachineFunction &);
void setMF(MachineFunction &MF);
/// Set the insertion point to the end of \p MBB.
/// \pre \p MBB must be contained by getMF().
@ -534,7 +560,8 @@ public:
/// type.
///
/// \return The newly created instruction.
MachineInstrBuilder buildConstant(const DstOp &Res, const ConstantInt &Val);
virtual MachineInstrBuilder buildConstant(const DstOp &Res,
const ConstantInt &Val);
/// Build and insert \p Res = G_CONSTANT \p Val
///
@ -555,7 +582,8 @@ public:
/// \pre \p Res must be a generic virtual register with scalar type.
///
/// \return The newly created instruction.
MachineInstrBuilder buildFConstant(const DstOp &Res, const ConstantFP &Val);
virtual MachineInstrBuilder buildFConstant(const DstOp &Res,
const ConstantFP &Val);
MachineInstrBuilder buildFConstant(const DstOp &Res, double Val);

View File

@ -108,5 +108,8 @@ APFloat getAPFloatFromSize(double Val, unsigned Size);
/// fallback.
void getSelectionDAGFallbackAnalysisUsage(AnalysisUsage &AU);
Optional<APInt> ConstantFoldBinOp(unsigned Opcode, const unsigned Op1,
const unsigned Op2,
const MachineRegisterInfo &MRI);
} // End namespace llvm.
#endif

View File

@ -372,16 +372,18 @@ public:
public:
virtual ~Delegate() = default;
virtual void MF_HandleInsertion(const MachineInstr &MI) = 0;
virtual void MF_HandleRemoval(const MachineInstr &MI) = 0;
/// Callback after an insertion. This should not modify the MI directly.
virtual void MF_HandleInsertion(MachineInstr &MI) = 0;
/// Callback before a removal. This should not modify the MI directly.
virtual void MF_HandleRemoval(MachineInstr &MI) = 0;
};
private:
Delegate *TheDelegate = nullptr;
// Callbacks for insertion and removal.
void handleInsertion(const MachineInstr &MI);
void handleRemoval(const MachineInstr &MI);
void handleInsertion(MachineInstr &MI);
void handleRemoval(MachineInstr &MI);
friend struct ilist_traits<MachineInstr>;
public:

View File

@ -199,6 +199,7 @@ void initializeLegacyDivergenceAnalysisPass(PassRegistry&);
void initializeLegacyLICMPassPass(PassRegistry&);
void initializeLegacyLoopSinkPassPass(PassRegistry&);
void initializeLegalizerPass(PassRegistry&);
void initializeGISelCSEAnalysisWrapperPassPass(PassRegistry &);
void initializeLibCallsShrinkWrapLegacyPassPass(PassRegistry&);
void initializeLintPass(PassRegistry&);
void initializeLiveDebugValuesPass(PassRegistry&);

View File

@ -147,6 +147,7 @@ public:
bool operator!=(const LLT &RHS) const { return !(*this == RHS); }
friend struct DenseMapInfo<LLT>;
friend class GISelInstProfileBuilder;
private:
/// LLT is packed into 64 bits as follows:
@ -231,6 +232,11 @@ private:
maskAndShift(AddressSpace, PointerVectorAddressSpaceFieldInfo);
}
}
uint64_t getUniqueRAWLLTData() const {
return ((uint64_t)RawData) << 2 | ((uint64_t)IsPointer) << 1 |
((uint64_t)IsVector);
}
};
inline raw_ostream& operator<<(raw_ostream &OS, const LLT &Ty) {
@ -250,8 +256,7 @@ template<> struct DenseMapInfo<LLT> {
return Invalid;
}
static inline unsigned getHashValue(const LLT &Ty) {
uint64_t Val = ((uint64_t)Ty.RawData) << 2 | ((uint64_t)Ty.IsPointer) << 1 |
((uint64_t)Ty.IsVector);
uint64_t Val = Ty.getUniqueRAWLLTData();
return DenseMapInfo<uint64_t>::getHashValue(Val);
}
static bool isEqual(const LLT &LHS, const LLT &RHS) {

View File

@ -1,4 +1,6 @@
add_llvm_library(LLVMGlobalISel
CSEInfo.cpp
CSEMIRBuilder.cpp
CallLowering.cpp
GlobalISel.cpp
Combiner.cpp

View File

@ -0,0 +1,370 @@
//===- CSEInfo.cpp ------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//
//===----------------------------------------------------------------------===//
#include "llvm/CodeGen/GlobalISel/CSEInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#define DEBUG_TYPE "cseinfo"
using namespace llvm;
char llvm::GISelCSEAnalysisWrapperPass::ID = 0;
INITIALIZE_PASS_BEGIN(GISelCSEAnalysisWrapperPass, DEBUG_TYPE,
"Analysis containing CSE Info", false, true)
INITIALIZE_PASS_END(GISelCSEAnalysisWrapperPass, DEBUG_TYPE,
"Analysis containing CSE Info", false, true)
/// -------- UniqueMachineInstr -------------//
void UniqueMachineInstr::Profile(FoldingSetNodeID &ID) {
GISelInstProfileBuilder(ID, MI->getMF()->getRegInfo()).addNodeID(MI);
}
/// -----------------------------------------
/// --------- CSEConfig ---------- ///
bool CSEConfig::shouldCSEOpc(unsigned Opc) {
switch (Opc) {
default:
break;
case TargetOpcode::G_ADD:
case TargetOpcode::G_AND:
case TargetOpcode::G_ASHR:
case TargetOpcode::G_LSHR:
case TargetOpcode::G_MUL:
case TargetOpcode::G_OR:
case TargetOpcode::G_SHL:
case TargetOpcode::G_SUB:
case TargetOpcode::G_XOR:
case TargetOpcode::G_UDIV:
case TargetOpcode::G_SDIV:
case TargetOpcode::G_UREM:
case TargetOpcode::G_SREM:
case TargetOpcode::G_CONSTANT:
case TargetOpcode::G_FCONSTANT:
case TargetOpcode::G_ZEXT:
case TargetOpcode::G_SEXT:
case TargetOpcode::G_ANYEXT:
case TargetOpcode::G_UNMERGE_VALUES:
case TargetOpcode::G_TRUNC:
return true;
}
return false;
}
bool CSEConfigConstantOnly::shouldCSEOpc(unsigned Opc) {
return Opc == TargetOpcode::G_CONSTANT;
}
/// -----------------------------------------
/// -------- GISelCSEInfo -------------//
void GISelCSEInfo::setMF(MachineFunction &MF) {
this->MF = &MF;
this->MRI = &MF.getRegInfo();
}
GISelCSEInfo::~GISelCSEInfo() {}
bool GISelCSEInfo::isUniqueMachineInstValid(
const UniqueMachineInstr &UMI) const {
// Should we check here and assert that the instruction has been fully
// constructed?
// FIXME: Any other checks required to be done here? Remove this method if
// none.
return true;
}
void GISelCSEInfo::invalidateUniqueMachineInstr(UniqueMachineInstr *UMI) {
bool Removed = CSEMap.RemoveNode(UMI);
(void)Removed;
assert(Removed && "Invalidation called on invalid UMI");
// FIXME: Should UMI be deallocated/destroyed?
}
UniqueMachineInstr *GISelCSEInfo::getNodeIfExists(FoldingSetNodeID &ID,
MachineBasicBlock *MBB,
void *&InsertPos) {
auto *Node = CSEMap.FindNodeOrInsertPos(ID, InsertPos);
if (Node) {
if (!isUniqueMachineInstValid(*Node)) {
invalidateUniqueMachineInstr(Node);
return nullptr;
}
if (Node->MI->getParent() != MBB)
return nullptr;
}
return Node;
}
void GISelCSEInfo::insertNode(UniqueMachineInstr *UMI, void *InsertPos) {
handleRecordedInsts();
assert(UMI);
UniqueMachineInstr *MaybeNewNode = UMI;
if (InsertPos)
CSEMap.InsertNode(UMI, InsertPos);
else
MaybeNewNode = CSEMap.GetOrInsertNode(UMI);
if (MaybeNewNode != UMI) {
// A similar node exists in the folding set. Let's ignore this one.
return;
}
assert(InstrMapping.count(UMI->MI) == 0 &&
"This instruction should not be in the map");
InstrMapping[UMI->MI] = MaybeNewNode;
}
UniqueMachineInstr *GISelCSEInfo::getUniqueInstrForMI(const MachineInstr *MI) {
assert(shouldCSE(MI->getOpcode()) && "Trying to CSE an unsupported Node");
auto *Node = new (UniqueInstrAllocator) UniqueMachineInstr(MI);
return Node;
}
void GISelCSEInfo::insertInstr(MachineInstr *MI, void *InsertPos) {
assert(MI);
// If it exists in temporary insts, remove it.
TemporaryInsts.remove(MI);
auto *Node = getUniqueInstrForMI(MI);
insertNode(Node, InsertPos);
}
MachineInstr *GISelCSEInfo::getMachineInstrIfExists(FoldingSetNodeID &ID,
MachineBasicBlock *MBB,
void *&InsertPos) {
handleRecordedInsts();
if (auto *Inst = getNodeIfExists(ID, MBB, InsertPos)) {
LLVM_DEBUG(dbgs() << "CSEInfo: Found Instr " << *Inst->MI << "\n";);
return const_cast<MachineInstr *>(Inst->MI);
}
return nullptr;
}
void GISelCSEInfo::countOpcodeHit(unsigned Opc) {
#ifndef NDEBUG
if (OpcodeHitTable.count(Opc))
OpcodeHitTable[Opc] += 1;
else
OpcodeHitTable[Opc] = 1;
#endif
// Else do nothing.
}
void GISelCSEInfo::recordNewInstruction(MachineInstr *MI) {
if (shouldCSE(MI->getOpcode())) {
TemporaryInsts.insert(MI);
LLVM_DEBUG(dbgs() << "CSEInfo: Recording new MI" << *MI << "\n";);
}
}
void GISelCSEInfo::handleRecordedInst(MachineInstr *MI) {
assert(shouldCSE(MI->getOpcode()) && "Invalid instruction for CSE");
auto *UMI = InstrMapping.lookup(MI);
LLVM_DEBUG(dbgs() << "CSEInfo: Handling recorded MI" << *MI << "\n";);
if (UMI) {
// Invalidate this MI.
invalidateUniqueMachineInstr(UMI);
InstrMapping.erase(MI);
}
/// Now insert the new instruction.
if (UMI) {
/// We'll reuse the same UniqueMachineInstr to avoid the new
/// allocation.
*UMI = UniqueMachineInstr(MI);
insertNode(UMI, nullptr);
} else {
/// This is a new instruction. Allocate a new UniqueMachineInstr and
/// Insert.
insertInstr(MI);
}
}
void GISelCSEInfo::handleRemoveInst(MachineInstr *MI) {
if (auto *UMI = InstrMapping.lookup(MI)) {
invalidateUniqueMachineInstr(UMI);
InstrMapping.erase(MI);
}
TemporaryInsts.remove(MI);
}
void GISelCSEInfo::handleRecordedInsts() {
while (!TemporaryInsts.empty()) {
auto *MI = TemporaryInsts.pop_back_val();
handleRecordedInst(MI);
}
}
bool GISelCSEInfo::shouldCSE(unsigned Opc) const {
// Only GISel opcodes are CSEable
if (!isPreISelGenericOpcode(Opc))
return false;
assert(CSEOpt.get() && "CSEConfig not set");
return CSEOpt->shouldCSEOpc(Opc);
}
void GISelCSEInfo::erasingInstr(MachineInstr &MI) { handleRemoveInst(&MI); }
void GISelCSEInfo::createdInstr(MachineInstr &MI) { recordNewInstruction(&MI); }
void GISelCSEInfo::changingInstr(MachineInstr &MI) {
// For now, perform erase, followed by insert.
erasingInstr(MI);
createdInstr(MI);
}
void GISelCSEInfo::changedInstr(MachineInstr &MI) { changingInstr(MI); }
void GISelCSEInfo::analyze(MachineFunction &MF) {
setMF(MF);
for (auto &MBB : MF) {
if (MBB.empty())
continue;
for (MachineInstr &MI : MBB) {
if (!shouldCSE(MI.getOpcode()))
continue;
LLVM_DEBUG(dbgs() << "CSEInfo::Add MI: " << MI << "\n";);
insertInstr(&MI);
}
}
}
void GISelCSEInfo::releaseMemory() {
// print();
CSEMap.clear();
InstrMapping.clear();
UniqueInstrAllocator.Reset();
TemporaryInsts.clear();
CSEOpt.reset();
MRI = nullptr;
MF = nullptr;
#ifndef NDEBUG
OpcodeHitTable.clear();
#endif
}
void GISelCSEInfo::print() {
#ifndef NDEBUG
for (auto &It : OpcodeHitTable) {
dbgs() << "CSE Count for Opc " << It.first << " : " << It.second << "\n";
};
#endif
}
/// -----------------------------------------
// ---- Profiling methods for FoldingSetNode --- //
const GISelInstProfileBuilder &
GISelInstProfileBuilder::addNodeID(const MachineInstr *MI) const {
addNodeIDMBB(MI->getParent());
addNodeIDOpcode(MI->getOpcode());
for (auto &Op : MI->operands())
addNodeIDMachineOperand(Op);
addNodeIDFlag(MI->getFlags());
return *this;
}
const GISelInstProfileBuilder &
GISelInstProfileBuilder::addNodeIDOpcode(unsigned Opc) const {
ID.AddInteger(Opc);
return *this;
}
const GISelInstProfileBuilder &
GISelInstProfileBuilder::addNodeIDRegType(const LLT &Ty) const {
uint64_t Val = Ty.getUniqueRAWLLTData();
ID.AddInteger(Val);
return *this;
}
const GISelInstProfileBuilder &
GISelInstProfileBuilder::addNodeIDRegType(const TargetRegisterClass *RC) const {
ID.AddPointer(RC);
return *this;
}
const GISelInstProfileBuilder &
GISelInstProfileBuilder::addNodeIDRegType(const RegisterBank *RB) const {
ID.AddPointer(RB);
return *this;
}
const GISelInstProfileBuilder &
GISelInstProfileBuilder::addNodeIDImmediate(int64_t Imm) const {
ID.AddInteger(Imm);
return *this;
}
const GISelInstProfileBuilder &
GISelInstProfileBuilder::addNodeIDRegNum(unsigned Reg) const {
ID.AddInteger(Reg);
return *this;
}
const GISelInstProfileBuilder &
GISelInstProfileBuilder::addNodeIDRegType(const unsigned Reg) const {
addNodeIDMachineOperand(MachineOperand::CreateReg(Reg, false));
return *this;
}
const GISelInstProfileBuilder &
GISelInstProfileBuilder::addNodeIDMBB(const MachineBasicBlock *MBB) const {
ID.AddPointer(MBB);
return *this;
}
const GISelInstProfileBuilder &
GISelInstProfileBuilder::addNodeIDFlag(unsigned Flag) const {
if (Flag)
ID.AddInteger(Flag);
return *this;
}
const GISelInstProfileBuilder &GISelInstProfileBuilder::addNodeIDMachineOperand(
const MachineOperand &MO) const {
if (MO.isReg()) {
unsigned Reg = MO.getReg();
if (!MO.isDef())
addNodeIDRegNum(Reg);
LLT Ty = MRI.getType(Reg);
if (Ty.isValid())
addNodeIDRegType(Ty);
auto *RB = MRI.getRegBankOrNull(Reg);
if (RB)
addNodeIDRegType(RB);
auto *RC = MRI.getRegClassOrNull(Reg);
if (RC)
addNodeIDRegType(RC);
assert(!MO.isImplicit() && "Unhandled case");
} else if (MO.isImm())
ID.AddInteger(MO.getImm());
else if (MO.isCImm())
ID.AddPointer(MO.getCImm());
else if (MO.isFPImm())
ID.AddPointer(MO.getFPImm());
else if (MO.isPredicate())
ID.AddInteger(MO.getPredicate());
else
llvm_unreachable("Unhandled operand type");
// Handle other types
return *this;
}
GISelCSEInfo &GISelCSEAnalysisWrapper::get(std::unique_ptr<CSEConfig> CSEOpt,
bool Recompute) {
if (!AlreadyComputed || Recompute) {
Info.setCSEConfig(std::move(CSEOpt));
Info.analyze(*MF);
AlreadyComputed = true;
}
return Info;
}
void GISelCSEAnalysisWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesAll();
MachineFunctionPass::getAnalysisUsage(AU);
}
bool GISelCSEAnalysisWrapperPass::runOnMachineFunction(MachineFunction &MF) {
releaseMemory();
Wrapper.setMF(MF);
return false;
}

View File

@ -0,0 +1,231 @@
//===-- llvm/CodeGen/GlobalISel/CSEMIRBuilder.cpp - MIBuilder--*- C++ -*-==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/// \file
/// This file implements the CSEMIRBuilder class which CSEs as it builds
/// instructions.
//===----------------------------------------------------------------------===//
//
#include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h"
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
using namespace llvm;
bool CSEMIRBuilder::dominates(MachineBasicBlock::const_iterator A,
MachineBasicBlock::const_iterator B) const {
auto MBBEnd = getMBB().end();
if (B == MBBEnd)
return true;
assert(A->getParent() == B->getParent() &&
"Iterators should be in same block");
const MachineBasicBlock *BBA = A->getParent();
MachineBasicBlock::const_iterator I = BBA->begin();
for (; &*I != A && &*I != B; ++I)
;
return &*I == A;
}
MachineInstrBuilder
CSEMIRBuilder::getDominatingInstrForID(FoldingSetNodeID &ID,
void *&NodeInsertPos) {
GISelCSEInfo *CSEInfo = getCSEInfo();
assert(CSEInfo && "Can't get here without setting CSEInfo");
MachineBasicBlock *CurMBB = &getMBB();
MachineInstr *MI =
CSEInfo->getMachineInstrIfExists(ID, CurMBB, NodeInsertPos);
if (MI) {
auto CurrPos = getInsertPt();
if (!dominates(MI, CurrPos))
CurMBB->splice(CurrPos, CurMBB, MI);
return MachineInstrBuilder(getMF(), MI);
}
return MachineInstrBuilder();
}
bool CSEMIRBuilder::canPerformCSEForOpc(unsigned Opc) const {
const GISelCSEInfo *CSEInfo = getCSEInfo();
if (!CSEInfo || !CSEInfo->shouldCSE(Opc))
return false;
return true;
}
void CSEMIRBuilder::profileDstOp(const DstOp &Op,
GISelInstProfileBuilder &B) const {
switch (Op.getDstOpKind()) {
case DstOp::DstType::Ty_RC:
B.addNodeIDRegType(Op.getRegClass());
break;
default:
B.addNodeIDRegType(Op.getLLTTy(*getMRI()));
break;
}
}
void CSEMIRBuilder::profileSrcOp(const SrcOp &Op,
GISelInstProfileBuilder &B) const {
switch (Op.getSrcOpKind()) {
case SrcOp::SrcType::Ty_Predicate:
B.addNodeIDImmediate(static_cast<int64_t>(Op.getPredicate()));
break;
default:
B.addNodeIDRegType(Op.getReg());
break;
}
}
void CSEMIRBuilder::profileMBBOpcode(GISelInstProfileBuilder &B,
unsigned Opc) const {
// First add the MBB (Local CSE).
B.addNodeIDMBB(&getMBB());
// Then add the opcode.
B.addNodeIDOpcode(Opc);
}
void CSEMIRBuilder::profileEverything(unsigned Opc, ArrayRef<DstOp> DstOps,
ArrayRef<SrcOp> SrcOps,
Optional<unsigned> Flags,
GISelInstProfileBuilder &B) const {
profileMBBOpcode(B, Opc);
// Then add the DstOps.
profileDstOps(DstOps, B);
// Then add the SrcOps.
profileSrcOps(SrcOps, B);
// Add Flags if passed in.
if (Flags)
B.addNodeIDFlag(*Flags);
}
MachineInstrBuilder CSEMIRBuilder::memoizeMI(MachineInstrBuilder MIB,
void *NodeInsertPos) {
assert(canPerformCSEForOpc(MIB->getOpcode()) &&
"Attempting to CSE illegal op");
MachineInstr *MIBInstr = MIB;
getCSEInfo()->insertInstr(MIBInstr, NodeInsertPos);
return MIB;
}
bool CSEMIRBuilder::checkCopyToDefsPossible(ArrayRef<DstOp> DstOps) {
if (DstOps.size() == 1)
return true; // always possible to emit copy to just 1 vreg.
return std::all_of(DstOps.begin(), DstOps.end(), [](const DstOp &Op) {
DstOp::DstType DT = Op.getDstOpKind();
return DT == DstOp::DstType::Ty_LLT || DT == DstOp::DstType::Ty_RC;
});
}
MachineInstrBuilder
CSEMIRBuilder::generateCopiesIfRequired(ArrayRef<DstOp> DstOps,
MachineInstrBuilder &MIB) {
assert(checkCopyToDefsPossible(DstOps) &&
"Impossible return a single MIB with copies to multiple defs");
if (DstOps.size() == 1) {
const DstOp &Op = DstOps[0];
if (Op.getDstOpKind() == DstOp::DstType::Ty_Reg)
return buildCopy(Op.getReg(), MIB->getOperand(0).getReg());
}
return MIB;
}
MachineInstrBuilder CSEMIRBuilder::buildInstr(unsigned Opc,
ArrayRef<DstOp> DstOps,
ArrayRef<SrcOp> SrcOps,
Optional<unsigned> Flag) {
switch (Opc) {
default:
break;
case TargetOpcode::G_ADD:
case TargetOpcode::G_AND:
case TargetOpcode::G_ASHR:
case TargetOpcode::G_LSHR:
case TargetOpcode::G_MUL:
case TargetOpcode::G_OR:
case TargetOpcode::G_SHL:
case TargetOpcode::G_SUB:
case TargetOpcode::G_XOR:
case TargetOpcode::G_UDIV:
case TargetOpcode::G_SDIV:
case TargetOpcode::G_UREM:
case TargetOpcode::G_SREM: {
// Try to constant fold these.
assert(SrcOps.size() == 2 && "Invalid sources");
assert(DstOps.size() == 1 && "Invalid dsts");
if (Optional<APInt> Cst = ConstantFoldBinOp(Opc, SrcOps[0].getReg(),
SrcOps[1].getReg(), *getMRI()))
return buildConstant(DstOps[0], Cst->getSExtValue());
break;
}
}
bool CanCopy = checkCopyToDefsPossible(DstOps);
if (!canPerformCSEForOpc(Opc))
return MachineIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
// If we can CSE this instruction, but involves generating copies to multiple
// regs, give up. This frequently happens to UNMERGEs.
if (!CanCopy) {
auto MIB = MachineIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
// CSEInfo would have tracked this instruction. Remove it from the temporary
// insts.
getCSEInfo()->handleRemoveInst(&*MIB);
return MIB;
}
FoldingSetNodeID ID;
GISelInstProfileBuilder ProfBuilder(ID, *getMRI());
void *InsertPos = nullptr;
profileEverything(Opc, DstOps, SrcOps, Flag, ProfBuilder);
MachineInstrBuilder MIB = getDominatingInstrForID(ID, InsertPos);
if (MIB) {
// Handle generating copies here.
return generateCopiesIfRequired(DstOps, MIB);
}
// This instruction does not exist in the CSEInfo. Build it and CSE it.
MachineInstrBuilder NewMIB =
MachineIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag);
return memoizeMI(NewMIB, InsertPos);
}
MachineInstrBuilder CSEMIRBuilder::buildConstant(const DstOp &Res,
const ConstantInt &Val) {
constexpr unsigned Opc = TargetOpcode::G_CONSTANT;
if (!canPerformCSEForOpc(Opc))
return MachineIRBuilder::buildConstant(Res, Val);
FoldingSetNodeID ID;
GISelInstProfileBuilder ProfBuilder(ID, *getMRI());
void *InsertPos = nullptr;
profileMBBOpcode(ProfBuilder, Opc);
profileDstOp(Res, ProfBuilder);
ProfBuilder.addNodeIDMachineOperand(MachineOperand::CreateCImm(&Val));
MachineInstrBuilder MIB = getDominatingInstrForID(ID, InsertPos);
if (MIB) {
// Handle generating copies here.
return generateCopiesIfRequired({Res}, MIB);
}
MachineInstrBuilder NewMIB = MachineIRBuilder::buildConstant(Res, Val);
return memoizeMI(NewMIB, InsertPos);
}
MachineInstrBuilder CSEMIRBuilder::buildFConstant(const DstOp &Res,
const ConstantFP &Val) {
constexpr unsigned Opc = TargetOpcode::G_FCONSTANT;
if (!canPerformCSEForOpc(Opc))
return MachineIRBuilder::buildFConstant(Res, Val);
FoldingSetNodeID ID;
GISelInstProfileBuilder ProfBuilder(ID, *getMRI());
void *InsertPos = nullptr;
profileMBBOpcode(ProfBuilder, Opc);
profileDstOp(Res, ProfBuilder);
ProfBuilder.addNodeIDMachineOperand(MachineOperand::CreateFPImm(&Val));
MachineInstrBuilder MIB = getDominatingInstrForID(ID, InsertPos);
if (MIB) {
// Handle generating copies here.
return generateCopiesIfRequired({Res}, MIB);
}
MachineInstrBuilder NewMIB = MachineIRBuilder::buildFConstant(Res, Val);
return memoizeMI(NewMIB, InsertPos);
}

View File

@ -13,7 +13,9 @@
#include "llvm/CodeGen/GlobalISel/Combiner.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/CodeGen/GlobalISel/CSEInfo.h"
#include "llvm/CodeGen/GlobalISel/CombinerInfo.h"
#include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h"
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
#include "llvm/CodeGen/GlobalISel/GISelWorkList.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
@ -35,38 +37,33 @@ namespace {
/// instruction creation will schedule that instruction for a future visit.
/// Other Combiner implementations may require more complex behaviour from
/// their GISelChangeObserver subclass.
class WorkListMaintainer : public GISelChangeObserver,
public MachineFunction::Delegate {
class WorkListMaintainer : public GISelChangeObserver {
using WorkListTy = GISelWorkList<512>;
MachineFunction &MF;
WorkListTy &WorkList;
/// 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.
SmallPtrSet<const MachineInstr *, 4> CreatedInstrs;
public:
WorkListMaintainer(MachineFunction &MF, WorkListTy &WorkList)
: GISelChangeObserver(), MF(MF), WorkList(WorkList) {
MF.setDelegate(this);
}
WorkListMaintainer(WorkListTy &WorkList)
: GISelChangeObserver(), WorkList(WorkList) {}
virtual ~WorkListMaintainer() {
MF.resetDelegate(this);
}
void erasingInstr(const MachineInstr &MI) override {
void erasingInstr(MachineInstr &MI) override {
LLVM_DEBUG(dbgs() << "Erased: " << MI << "\n");
WorkList.remove(&MI);
}
void createdInstr(const MachineInstr &MI) override {
void createdInstr(MachineInstr &MI) override {
LLVM_DEBUG(dbgs() << "Creating: " << MI << "\n");
WorkList.insert(&MI);
LLVM_DEBUG(CreatedInstrs.insert(&MI));
}
void changingInstr(const MachineInstr &MI) override {
void changingInstr(MachineInstr &MI) override {
LLVM_DEBUG(dbgs() << "Changing: " << MI << "\n");
WorkList.insert(&MI);
}
void changedInstr(const MachineInstr &MI) override {
void changedInstr(MachineInstr &MI) override {
LLVM_DEBUG(dbgs() << "Changed: " << MI << "\n");
WorkList.insert(&MI);
}
@ -79,13 +76,6 @@ public:
});
LLVM_DEBUG(CreatedInstrs.clear());
}
void MF_HandleInsertion(const MachineInstr &MI) override {
createdInstr(MI);
}
void MF_HandleRemoval(const MachineInstr &MI) override {
erasingInstr(MI);
}
};
}
@ -94,15 +84,20 @@ Combiner::Combiner(CombinerInfo &Info, const TargetPassConfig *TPC)
(void)this->TPC; // FIXME: Remove when used.
}
bool Combiner::combineMachineInstrs(MachineFunction &MF) {
bool Combiner::combineMachineInstrs(MachineFunction &MF,
GISelCSEInfo *CSEInfo) {
// 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;
Builder =
CSEInfo ? make_unique<CSEMIRBuilder>() : make_unique<MachineIRBuilder>();
MRI = &MF.getRegInfo();
Builder.setMF(MF);
Builder->setMF(MF);
if (CSEInfo)
Builder->setCSEInfo(CSEInfo);
LLVM_DEBUG(dbgs() << "Generic MI Combiner for: " << MF.getName() << '\n');
@ -110,14 +105,19 @@ bool Combiner::combineMachineInstrs(MachineFunction &MF) {
bool MFChanged = false;
bool Changed;
MachineIRBuilder &B = *Builder.get();
do {
// 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.
Changed = false;
GISelWorkList<512> WorkList(&MF);
WorkListMaintainer Observer(MF, WorkList);
GISelWorkList<512> WorkList;
WorkListMaintainer Observer(WorkList);
GISelObserverWrapper WrapperObserver(&Observer);
if (CSEInfo)
WrapperObserver.addObserver(CSEInfo);
RAIIDelegateInstaller DelInstall(MF, &WrapperObserver);
for (MachineBasicBlock *MBB : post_order(&MF)) {
if (MBB->empty())
continue;
@ -137,7 +137,7 @@ bool Combiner::combineMachineInstrs(MachineFunction &MF) {
while (!WorkList.empty()) {
MachineInstr *CurrInst = WorkList.pop_back_val();
LLVM_DEBUG(dbgs() << "\nTry combining " << *CurrInst;);
Changed |= CInfo.combine(Observer, *CurrInst, Builder);
Changed |= CInfo.combine(WrapperObserver, *CurrInst, B);
Observer.reportFullyCreatedInstrs();
}
MFChanged |= Changed;

View File

@ -29,3 +29,12 @@ void GISelChangeObserver::finishedChangingAllUsesOfReg() {
changedInstr(*ChangedMI);
}
RAIIDelegateInstaller::RAIIDelegateInstaller(MachineFunction &MF,
MachineFunction::Delegate *Del)
: MF(MF), Delegate(Del) {
// Register this as the delegate for handling insertions and deletions of
// instructions.
MF.setDelegate(Del);
}
RAIIDelegateInstaller::~RAIIDelegateInstaller() { MF.resetDelegate(Delegate); }

View File

@ -19,6 +19,7 @@
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/CodeGen/GlobalISel/CallLowering.h"
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
#include "llvm/CodeGen/LowLevelType.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
@ -75,11 +76,16 @@
using namespace llvm;
static cl::opt<bool>
EnableCSEInIRTranslator("enable-cse-in-irtranslator",
cl::desc("Should enable CSE in irtranslator"),
cl::Optional, cl::init(false));
char IRTranslator::ID = 0;
INITIALIZE_PASS_BEGIN(IRTranslator, DEBUG_TYPE, "IRTranslator LLVM IR -> MI",
false, false)
INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
INITIALIZE_PASS_DEPENDENCY(GISelCSEAnalysisWrapperPass)
INITIALIZE_PASS_END(IRTranslator, DEBUG_TYPE, "IRTranslator LLVM IR -> MI",
false, false)
@ -108,18 +114,21 @@ IRTranslator::IRTranslator() : MachineFunctionPass(ID) {
namespace {
/// Verify that every instruction created has the same DILocation as the
/// instruction being translated.
class DILocationVerifier : MachineFunction::Delegate {
MachineFunction &MF;
class DILocationVerifier : public GISelChangeObserver {
const Instruction *CurrInst = nullptr;
public:
DILocationVerifier(MachineFunction &MF) : MF(MF) { MF.setDelegate(this); }
~DILocationVerifier() { MF.resetDelegate(this); }
DILocationVerifier() = default;
~DILocationVerifier() = default;
const Instruction *getCurrentInst() const { return CurrInst; }
void setCurrentInst(const Instruction *Inst) { CurrInst = Inst; }
void MF_HandleInsertion(const MachineInstr &MI) override {
void erasingInstr(MachineInstr &MI) override {}
void changingInstr(MachineInstr &MI) override {}
void changedInstr(MachineInstr &MI) override {}
void createdInstr(MachineInstr &MI) override {
assert(getCurrentInst() && "Inserted instruction without a current MI");
// Only print the check message if we're actually checking it.
@ -130,7 +139,6 @@ public:
assert(CurrInst->getDebugLoc() == MI.getDebugLoc() &&
"Line info was not transferred to all instructions");
}
void MF_HandleRemoval(const MachineInstr &MI) override {}
};
} // namespace
#endif // ifndef NDEBUG
@ -139,6 +147,7 @@ public:
void IRTranslator::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<StackProtector>();
AU.addRequired<TargetPassConfig>();
AU.addRequired<GISelCSEAnalysisWrapperPass>();
getSelectionDAGFallbackAnalysisUsage(AU);
MachineFunctionPass::getAnalysisUsage(AU);
}
@ -1553,12 +1562,14 @@ bool IRTranslator::translateAtomicRMW(const User &U,
void IRTranslator::finishPendingPhis() {
#ifndef NDEBUG
DILocationVerifier Verifier(*MF);
DILocationVerifier Verifier;
GISelObserverWrapper WrapperObserver(&Verifier);
RAIIDelegateInstaller DelInstall(*MF, &WrapperObserver);
#endif // ifndef NDEBUG
for (auto &Phi : PendingPHIs) {
const PHINode *PI = Phi.first;
ArrayRef<MachineInstr *> ComponentPHIs = Phi.second;
EntryBuilder.setDebugLoc(PI->getDebugLoc());
EntryBuilder->setDebugLoc(PI->getDebugLoc());
#ifndef NDEBUG
Verifier.setCurrentInst(PI);
#endif // ifndef NDEBUG
@ -1599,11 +1610,12 @@ bool IRTranslator::valueIsSplit(const Value &V,
}
bool IRTranslator::translate(const Instruction &Inst) {
CurBuilder.setDebugLoc(Inst.getDebugLoc());
EntryBuilder.setDebugLoc(Inst.getDebugLoc());
CurBuilder->setDebugLoc(Inst.getDebugLoc());
EntryBuilder->setDebugLoc(Inst.getDebugLoc());
switch(Inst.getOpcode()) {
#define HANDLE_INST(NUM, OPCODE, CLASS) \
case Instruction::OPCODE: return translate##OPCODE(Inst, CurBuilder);
#define HANDLE_INST(NUM, OPCODE, CLASS) \
case Instruction::OPCODE: \
return translate##OPCODE(Inst, *CurBuilder.get());
#include "llvm/IR/Instruction.def"
default:
return false;
@ -1612,11 +1624,11 @@ bool IRTranslator::translate(const Instruction &Inst) {
bool IRTranslator::translate(const Constant &C, unsigned Reg) {
if (auto CI = dyn_cast<ConstantInt>(&C))
EntryBuilder.buildConstant(Reg, *CI);
EntryBuilder->buildConstant(Reg, *CI);
else if (auto CF = dyn_cast<ConstantFP>(&C))
EntryBuilder.buildFConstant(Reg, *CF);
EntryBuilder->buildFConstant(Reg, *CF);
else if (isa<UndefValue>(C))
EntryBuilder.buildUndef(Reg);
EntryBuilder->buildUndef(Reg);
else if (isa<ConstantPointerNull>(C)) {
// As we are trying to build a constant val of 0 into a pointer,
// insert a cast to make them correct with respect to types.
@ -1624,9 +1636,9 @@ bool IRTranslator::translate(const Constant &C, unsigned Reg) {
auto *ZeroTy = Type::getIntNTy(C.getContext(), NullSize);
auto *ZeroVal = ConstantInt::get(ZeroTy, 0);
unsigned ZeroReg = getOrCreateVReg(*ZeroVal);
EntryBuilder.buildCast(Reg, ZeroReg);
EntryBuilder->buildCast(Reg, ZeroReg);
} else if (auto GV = dyn_cast<GlobalValue>(&C))
EntryBuilder.buildGlobalValue(Reg, GV);
EntryBuilder->buildGlobalValue(Reg, GV);
else if (auto CAZ = dyn_cast<ConstantAggregateZero>(&C)) {
if (!CAZ->getType()->isVectorTy())
return false;
@ -1638,7 +1650,7 @@ bool IRTranslator::translate(const Constant &C, unsigned Reg) {
Constant &Elt = *CAZ->getElementValue(i);
Ops.push_back(getOrCreateVReg(Elt));
}
EntryBuilder.buildBuildVector(Reg, Ops);
EntryBuilder->buildBuildVector(Reg, Ops);
} else if (auto CV = dyn_cast<ConstantDataVector>(&C)) {
// Return the scalar if it is a <1 x Ty> vector.
if (CV->getNumElements() == 1)
@ -1648,11 +1660,12 @@ bool IRTranslator::translate(const Constant &C, unsigned Reg) {
Constant &Elt = *CV->getElementAsConstant(i);
Ops.push_back(getOrCreateVReg(Elt));
}
EntryBuilder.buildBuildVector(Reg, Ops);
EntryBuilder->buildBuildVector(Reg, Ops);
} else if (auto CE = dyn_cast<ConstantExpr>(&C)) {
switch(CE->getOpcode()) {
#define HANDLE_INST(NUM, OPCODE, CLASS) \
case Instruction::OPCODE: return translate##OPCODE(*CE, EntryBuilder);
#define HANDLE_INST(NUM, OPCODE, CLASS) \
case Instruction::OPCODE: \
return translate##OPCODE(*CE, *EntryBuilder.get());
#include "llvm/IR/Instruction.def"
default:
return false;
@ -1664,9 +1677,9 @@ bool IRTranslator::translate(const Constant &C, unsigned Reg) {
for (unsigned i = 0; i < CV->getNumOperands(); ++i) {
Ops.push_back(getOrCreateVReg(*CV->getOperand(i)));
}
EntryBuilder.buildBuildVector(Reg, Ops);
EntryBuilder->buildBuildVector(Reg, Ops);
} else if (auto *BA = dyn_cast<BlockAddress>(&C)) {
EntryBuilder.buildBlockAddress(Reg, BA);
EntryBuilder->buildBlockAddress(Reg, BA);
} else
return false;
@ -1683,8 +1696,8 @@ void IRTranslator::finalizeFunction() {
// MachineIRBuilder::DebugLoc can outlive the DILocation it holds. Clear it
// to avoid accessing freed memory (in runOnMachineFunction) and to avoid
// destroying it twice (in ~IRTranslator() and ~LLVMContext())
EntryBuilder = MachineIRBuilder();
CurBuilder = MachineIRBuilder();
EntryBuilder.reset();
CurBuilder.reset();
}
bool IRTranslator::runOnMachineFunction(MachineFunction &CurMF) {
@ -1692,12 +1705,30 @@ bool IRTranslator::runOnMachineFunction(MachineFunction &CurMF) {
const Function &F = MF->getFunction();
if (F.empty())
return false;
GISelCSEAnalysisWrapper &Wrapper =
getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEWrapper();
// Set the CSEConfig and run the analysis.
GISelCSEInfo *CSEInfo = nullptr;
TPC = &getAnalysis<TargetPassConfig>();
bool IsO0 = TPC->getOptLevel() == CodeGenOpt::Level::None;
// Disable CSE for O0.
bool EnableCSE = !IsO0 && EnableCSEInIRTranslator;
if (EnableCSE) {
EntryBuilder = make_unique<CSEMIRBuilder>(CurMF);
std::unique_ptr<CSEConfig> Config = make_unique<CSEConfig>();
CSEInfo = &Wrapper.get(std::move(Config));
EntryBuilder->setCSEInfo(CSEInfo);
CurBuilder = make_unique<CSEMIRBuilder>(CurMF);
CurBuilder->setCSEInfo(CSEInfo);
} else {
EntryBuilder = make_unique<MachineIRBuilder>();
CurBuilder = make_unique<MachineIRBuilder>();
}
CLI = MF->getSubtarget().getCallLowering();
CurBuilder.setMF(*MF);
EntryBuilder.setMF(*MF);
CurBuilder->setMF(*MF);
EntryBuilder->setMF(*MF);
MRI = &MF->getRegInfo();
DL = &F.getParent()->getDataLayout();
TPC = &getAnalysis<TargetPassConfig>();
ORE = llvm::make_unique<OptimizationRemarkEmitter>(&F);
assert(PendingPHIs.empty() && "stale PHIs");
@ -1716,7 +1747,7 @@ bool IRTranslator::runOnMachineFunction(MachineFunction &CurMF) {
// Setup a separate basic-block for the arguments and constants
MachineBasicBlock *EntryBB = MF->CreateMachineBasicBlock();
MF->push_back(EntryBB);
EntryBuilder.setMBB(*EntryBB);
EntryBuilder->setMBB(*EntryBB);
// Create all blocks, in IR order, to preserve the layout.
for (const BasicBlock &BB: F) {
@ -1753,7 +1784,7 @@ bool IRTranslator::runOnMachineFunction(MachineFunction &CurMF) {
}
}
if (!CLI->lowerFormalArguments(EntryBuilder, F, VRegArgs)) {
if (!CLI->lowerFormalArguments(*EntryBuilder.get(), F, VRegArgs)) {
OptimizationRemarkMissed R("gisel-irtranslator", "GISelFailure",
F.getSubprogram(), &F.getEntryBlock());
R << "unable to lower arguments: " << ore::NV("Prototype", F.getType());
@ -1770,22 +1801,27 @@ bool IRTranslator::runOnMachineFunction(MachineFunction &CurMF) {
assert(VRegs.empty() && "VRegs already populated?");
VRegs.push_back(VArg);
} else {
unpackRegs(*ArgIt, VArg, EntryBuilder);
unpackRegs(*ArgIt, VArg, *EntryBuilder.get());
}
ArgIt++;
}
// Need to visit defs before uses when translating instructions.
GISelObserverWrapper WrapperObserver;
if (EnableCSE && CSEInfo)
WrapperObserver.addObserver(CSEInfo);
{
ReversePostOrderTraversal<const Function *> RPOT(&F);
#ifndef NDEBUG
DILocationVerifier Verifier(*MF);
DILocationVerifier Verifier;
WrapperObserver.addObserver(&Verifier);
#endif // ifndef NDEBUG
RAIIDelegateInstaller DelInstall(*MF, &WrapperObserver);
for (const BasicBlock *BB : RPOT) {
MachineBasicBlock &MBB = getMBB(*BB);
// Set the insertion point of all the following translations to
// the end of this basic block.
CurBuilder.setMBB(MBB);
CurBuilder->setMBB(MBB);
for (const Instruction &Inst : *BB) {
#ifndef NDEBUG
@ -1810,6 +1846,9 @@ bool IRTranslator::runOnMachineFunction(MachineFunction &CurMF) {
return false;
}
}
#ifndef NDEBUG
WrapperObserver.removeObserver(&Verifier);
#endif
}
finishPendingPhis();

View File

@ -16,6 +16,8 @@
#include "llvm/CodeGen/GlobalISel/Legalizer.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/CodeGen/GlobalISel/CSEInfo.h"
#include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h"
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
#include "llvm/CodeGen/GlobalISel/GISelWorkList.h"
#include "llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h"
@ -33,11 +35,17 @@
using namespace llvm;
static cl::opt<bool>
EnableCSEInLegalizer("enable-cse-in-legalizer",
cl::desc("Should enable CSE in Legalizer"),
cl::Optional, cl::init(false));
char Legalizer::ID = 0;
INITIALIZE_PASS_BEGIN(Legalizer, DEBUG_TYPE,
"Legalize the Machine IR a function's Machine IR", false,
false)
INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
INITIALIZE_PASS_DEPENDENCY(GISelCSEAnalysisWrapperPass)
INITIALIZE_PASS_END(Legalizer, DEBUG_TYPE,
"Legalize the Machine IR a function's Machine IR", false,
false)
@ -48,6 +56,8 @@ Legalizer::Legalizer() : MachineFunctionPass(ID) {
void Legalizer::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<TargetPassConfig>();
AU.addRequired<GISelCSEAnalysisWrapperPass>();
AU.addPreserved<GISelCSEAnalysisWrapperPass>();
getSelectionDAGFallbackAnalysisUsage(AU);
MachineFunctionPass::getAnalysisUsage(AU);
}
@ -82,7 +92,7 @@ public:
LegalizerWorkListManager(InstListTy &Insts, ArtifactListTy &Arts)
: InstList(Insts), ArtifactList(Arts) {}
void createdInstr(const MachineInstr &MI) override {
void createdInstr(MachineInstr &MI) override {
// Only legalize pre-isel generic instructions.
// Legalization process could generate Target specific pseudo
// instructions with generic types. Don't record them
@ -95,17 +105,17 @@ public:
LLVM_DEBUG(dbgs() << ".. .. New MI: " << MI);
}
void erasingInstr(const MachineInstr &MI) override {
void erasingInstr(MachineInstr &MI) override {
LLVM_DEBUG(dbgs() << ".. .. Erasing: " << MI);
InstList.remove(&MI);
ArtifactList.remove(&MI);
}
void changingInstr(const MachineInstr &MI) override {
void changingInstr(MachineInstr &MI) override {
LLVM_DEBUG(dbgs() << ".. .. Changing MI: " << MI);
}
void changedInstr(const MachineInstr &MI) override {
void changedInstr(MachineInstr &MI) override {
// When insts change, we want to revisit them to legalize them again.
// We'll consider them the same as created.
LLVM_DEBUG(dbgs() << ".. .. Changed MI: " << MI);
@ -122,14 +132,16 @@ bool Legalizer::runOnMachineFunction(MachineFunction &MF) {
LLVM_DEBUG(dbgs() << "Legalize Machine IR for: " << MF.getName() << '\n');
init(MF);
const TargetPassConfig &TPC = getAnalysis<TargetPassConfig>();
GISelCSEAnalysisWrapper &Wrapper =
getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEWrapper();
MachineOptimizationRemarkEmitter MORE(MF, /*MBFI=*/nullptr);
const size_t NumBlocks = MF.size();
MachineRegisterInfo &MRI = MF.getRegInfo();
// Populate Insts
InstListTy InstList(&MF);
ArtifactListTy ArtifactList(&MF);
InstListTy InstList;
ArtifactListTy ArtifactList;
ReversePostOrderTraversal<MachineFunction *> RPOT(&MF);
// Perform legalization bottom up so we can DCE as we legalize.
// Traverse BB in RPOT and within each basic block, add insts top down,
@ -148,12 +160,34 @@ bool Legalizer::runOnMachineFunction(MachineFunction &MF) {
InstList.insert(&MI);
}
}
std::unique_ptr<MachineIRBuilder> MIRBuilder;
GISelCSEInfo *CSEInfo = nullptr;
bool IsO0 = TPC.getOptLevel() == CodeGenOpt::Level::None;
// Disable CSE for O0.
bool EnableCSE = !IsO0 && EnableCSEInLegalizer;
if (EnableCSE) {
MIRBuilder = make_unique<CSEMIRBuilder>();
std::unique_ptr<CSEConfig> Config = make_unique<CSEConfig>();
CSEInfo = &Wrapper.get(std::move(Config));
MIRBuilder->setCSEInfo(CSEInfo);
} else
MIRBuilder = make_unique<MachineIRBuilder>();
// This observer keeps the worklist updated.
LegalizerWorkListManager WorkListObserver(InstList, ArtifactList);
LegalizerHelper Helper(MF, WorkListObserver);
// We want both WorkListObserver as well as CSEInfo to observe all changes.
// Use the wrapper observer.
GISelObserverWrapper WrapperObserver(&WorkListObserver);
if (EnableCSE && CSEInfo)
WrapperObserver.addObserver(CSEInfo);
// Now install the observer as the delegate to MF.
// This will keep all the observers notified about new insertions/deletions.
RAIIDelegateInstaller DelInstall(MF, &WrapperObserver);
LegalizerHelper Helper(MF, WrapperObserver, *MIRBuilder.get());
const LegalizerInfo &LInfo(Helper.getLegalizerInfo());
LegalizationArtifactCombiner ArtCombiner(Helper.MIRBuilder, MF.getRegInfo(), LInfo);
auto RemoveDeadInstFromLists = [&WorkListObserver](MachineInstr *DeadMI) {
WorkListObserver.erasingInstr(*DeadMI);
LegalizationArtifactCombiner ArtCombiner(*MIRBuilder.get(), MF.getRegInfo(),
LInfo);
auto RemoveDeadInstFromLists = [&WrapperObserver](MachineInstr *DeadMI) {
WrapperObserver.erasingInstr(*DeadMI);
};
bool Changed = false;
do {

View File

@ -31,16 +31,18 @@ using namespace llvm;
using namespace LegalizeActions;
LegalizerHelper::LegalizerHelper(MachineFunction &MF,
GISelChangeObserver &Observer)
: MRI(MF.getRegInfo()), LI(*MF.getSubtarget().getLegalizerInfo()),
Observer(Observer) {
GISelChangeObserver &Observer,
MachineIRBuilder &Builder)
: MIRBuilder(Builder), MRI(MF.getRegInfo()),
LI(*MF.getSubtarget().getLegalizerInfo()), Observer(Observer) {
MIRBuilder.setMF(MF);
MIRBuilder.setChangeObserver(Observer);
}
LegalizerHelper::LegalizerHelper(MachineFunction &MF, const LegalizerInfo &LI,
GISelChangeObserver &Observer)
: MRI(MF.getRegInfo()), LI(LI), Observer(Observer) {
GISelChangeObserver &Observer,
MachineIRBuilder &B)
: MIRBuilder(B), MRI(MF.getRegInfo()), LI(LI), Observer(Observer) {
MIRBuilder.setMF(MF);
MIRBuilder.setChangeObserver(Observer);
}

View File

@ -46,6 +46,8 @@ void MachineIRBuilder::setInstr(MachineInstr &MI) {
State.II = MI.getIterator();
}
void MachineIRBuilder::setCSEInfo(GISelCSEInfo *Info) { State.CSEInfo = Info; }
void MachineIRBuilder::setInsertPt(MachineBasicBlock &MBB,
MachineBasicBlock::iterator II) {
assert(MBB.getParent() == &getMF() &&

View File

@ -235,6 +235,57 @@ APFloat llvm::getAPFloatFromSize(double Val, unsigned Size) {
return APF;
}
Optional<APInt> llvm::ConstantFoldBinOp(unsigned Opcode, const unsigned Op1,
const unsigned Op2,
const MachineRegisterInfo &MRI) {
auto MaybeOp1Cst = getConstantVRegVal(Op1, MRI);
auto MaybeOp2Cst = getConstantVRegVal(Op2, MRI);
if (MaybeOp1Cst && MaybeOp2Cst) {
LLT Ty = MRI.getType(Op1);
APInt C1(Ty.getSizeInBits(), *MaybeOp1Cst, true);
APInt C2(Ty.getSizeInBits(), *MaybeOp2Cst, true);
switch (Opcode) {
default:
break;
case TargetOpcode::G_ADD:
return C1 + C2;
case TargetOpcode::G_AND:
return C1 & C2;
case TargetOpcode::G_ASHR:
return C1.ashr(C2);
case TargetOpcode::G_LSHR:
return C1.lshr(C2);
case TargetOpcode::G_MUL:
return C1 * C2;
case TargetOpcode::G_OR:
return C1 | C2;
case TargetOpcode::G_SHL:
return C1 << C2;
case TargetOpcode::G_SUB:
return C1 - C2;
case TargetOpcode::G_XOR:
return C1 ^ C2;
case TargetOpcode::G_UDIV:
if (!C2.getBoolValue())
break;
return C1.udiv(C2);
case TargetOpcode::G_SDIV:
if (!C2.getBoolValue())
break;
return C1.sdiv(C2);
case TargetOpcode::G_UREM:
if (!C2.getBoolValue())
break;
return C1.urem(C2);
case TargetOpcode::G_SREM:
if (!C2.getBoolValue())
break;
return C1.srem(C2);
}
}
return None;
}
void llvm::getSelectionDAGFallbackAnalysisUsage(AnalysisUsage &AU) {
AU.addPreserved<StackProtector>();
}

View File

@ -139,12 +139,12 @@ MachineFunction::MachineFunction(const Function &F,
init();
}
void MachineFunction::handleInsertion(const MachineInstr &MI) {
void MachineFunction::handleInsertion(MachineInstr &MI) {
if (TheDelegate)
TheDelegate->MF_HandleInsertion(MI);
}
void MachineFunction::handleRemoval(const MachineInstr &MI) {
void MachineFunction::handleRemoval(MachineInstr &MI) {
if (TheDelegate)
TheDelegate->MF_HandleRemoval(MI);
}

View File

@ -88,7 +88,7 @@ bool AArch64PreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
auto *TPC = &getAnalysis<TargetPassConfig>();
AArch64PreLegalizerCombinerInfo PCInfo;
Combiner C(PCInfo, TPC);
return C.combineMachineInstrs(MF);
return C.combineMachineInstrs(MF, /*CSEInfo*/ nullptr);
}
char AArch64PreLegalizerCombiner::ID = 0;

View File

@ -73,7 +73,7 @@ bool MipsPreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
auto *TPC = &getAnalysis<TargetPassConfig>();
MipsPreLegalizerCombinerInfo PCInfo;
Combiner C(PCInfo, TPC);
return C.combineMachineInstrs(MF);
return C.combineMachineInstrs(MF, nullptr);
}
char MipsPreLegalizerCombiner::ID = 0;

View File

@ -0,0 +1,34 @@
; RUN: llc -mtriple=aarch64-linux-gnu -O1 -stop-after=irtranslator -enable-cse-in-irtranslator=1 -global-isel -verify-machineinstrs %s -o - 2>&1 | FileCheck %s
; CHECK-LABEL: name: test_split_struct
; CHECK: [[ADDR:%[0-9]+]]:_(p0) = COPY $x0
; CHECK: [[LO:%[0-9]+]]:_(s64) = G_LOAD %0(p0) :: (load 8 from %ir.ptr)
; CHECK: [[CST:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
; CHECK: [[GEP:%[0-9]+]]:_(p0) = G_GEP [[ADDR]], [[CST]](s64)
; CHECK: [[HI:%[0-9]+]]:_(s64) = G_LOAD [[GEP]](p0) :: (load 8 from %ir.ptr + 8)
; CHECK: [[IMPDEF:%[0-9]+]]:_(s128) = G_IMPLICIT_DEF
; CHECK: [[INS1:%[0-9]+]]:_(s128) = G_INSERT [[IMPDEF]], [[LO]](s64), 0
; CHECK: [[INS2:%[0-9]+]]:_(s128) = G_INSERT [[INS1]], [[HI]](s64), 64
; CHECK: [[EXTLO:%[0-9]+]]:_(s64) = G_EXTRACT [[INS2]](s128), 0
; CHECK: [[EXTHI:%[0-9]+]]:_(s64) = G_EXTRACT [[INS2]](s128), 64
; CHECK: [[SP:%[0-9]+]]:_(p0) = COPY $sp
; CHECK: [[CST2:%[0-9]+]]:_(s64) = G_CONSTANT i64 0
; CHECK: [[GEP2:%[0-9]+]]:_(p0) = G_GEP [[SP]], [[CST2]](s64)
; CHECK: G_STORE [[EXTLO]](s64), [[GEP2]](p0) :: (store 8 into stack, align 0)
; CHECK: [[SP:%[0-9]+]]:_(p0) = COPY $sp
; CHECK: [[CST3:%[0-9]+]]:_(s64) = COPY [[CST]]
; CHECK: [[GEP3:%[0-9]+]]:_(p0) = G_GEP [[SP]], [[CST3]](s64)
; CHECK: G_STORE [[EXTHI]](s64), [[GEP3]](p0) :: (store 8 into stack + 8, align 0)
define void @test_split_struct([2 x i64]* %ptr) {
%struct = load [2 x i64], [2 x i64]* %ptr
call void @take_split_struct([2 x i64]* null, i64 1, i64 2, i64 3,
i64 4, i64 5, i64 6,
[2 x i64] %struct)
ret void
}
declare void @take_split_struct([2 x i64]* %ptr, i64, i64, i64,
i64, i64, i64,
[2 x i64] %in) ;

View File

@ -45,6 +45,7 @@
; VERIFY-NEXT: Verify generated machine code
; ENABLED-NEXT: PreLegalizerCombiner
; VERIFY-NEXT: Verify generated machine code
; ENABLED-NEXT: Analysis containing CSE Info
; ENABLED-NEXT: Legalizer
; VERIFY-NEXT: Verify generated machine code
; ENABLED-NEXT: RegBankSelect

View File

@ -0,0 +1,21 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
# RUN: llc -march=aarch64 -run-pass=legalizer %s -o - -enable-cse-in-legalizer=1 -O1 | FileCheck %s
---
name: test_cse_in_legalizer
body: |
bb.0.entry:
; CHECK-LABEL: name: test_cse_in_legalizer
; CHECK: [[COPY:%[0-9]+]]:_(s64) = COPY $x0
; CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 255
; CHECK: [[TRUNC:%[0-9]+]]:_(s32) = G_TRUNC [[COPY]](s64)
; CHECK: [[AND:%[0-9]+]]:_(s32) = G_AND [[TRUNC]], [[C]]
; CHECK: [[COPY1:%[0-9]+]]:_(s32) = COPY [[AND]](s32)
; CHECK: $w0 = COPY [[COPY1]](s32)
; CHECK: $w0 = COPY [[AND]](s32)
%0:_(s64) = COPY $x0
%1:_(s8) = G_TRUNC %0(s64)
%19:_(s32) = G_ZEXT %1(s8)
$w0 = COPY %19(s32)
%2:_(s8) = G_TRUNC %0(s64)
%20:_(s32) = G_ZEXT %2(s8)
$w0 = COPY %20(s32)

View File

@ -32,8 +32,10 @@
; CHECK-NEXT: Safe Stack instrumentation pass
; CHECK-NEXT: Insert stack protectors
; CHECK-NEXT: Module Verifier
; CHECK-NEXT: Analysis containing CSE Info
; CHECK-NEXT: IRTranslator
; CHECK-NEXT: AArch64PreLegalizerCombiner
; CHECK-NEXT: Analysis containing CSE Info
; CHECK-NEXT: Legalizer
; CHECK-NEXT: RegBankSelect
; CHECK-NEXT: Localizer

View File

@ -13,4 +13,5 @@ add_llvm_unittest(GlobalISelTests
LegalizerInfoTest.cpp
PatternMatchTest.cpp
LegalizerHelperTest.cpp
CSETest.cpp
)

View File

@ -0,0 +1,87 @@
//===- CSETest.cpp -----------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "GISelMITest.h"
#include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h"
namespace {
TEST_F(GISelMITest, TestCSE) {
if (!TM)
return;
LLT s16{LLT::scalar(16)};
LLT s32{LLT::scalar(32)};
auto MIBInput = B.buildInstr(TargetOpcode::G_TRUNC, {s16}, {Copies[0]});
auto MIBInput1 = B.buildInstr(TargetOpcode::G_TRUNC, {s16}, {Copies[1]});
auto MIBAdd = B.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput});
GISelCSEInfo CSEInfo;
CSEInfo.setCSEConfig(make_unique<CSEConfig>());
CSEInfo.analyze(*MF);
B.setCSEInfo(&CSEInfo);
CSEMIRBuilder CSEB(B.getState());
CSEB.setInsertPt(*EntryMBB, EntryMBB->begin());
unsigned AddReg = MRI->createGenericVirtualRegister(s16);
auto MIBAddCopy =
CSEB.buildInstr(TargetOpcode::G_ADD, {AddReg}, {MIBInput, MIBInput});
ASSERT_EQ(MIBAddCopy->getOpcode(), TargetOpcode::COPY);
auto MIBAdd2 =
CSEB.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput});
ASSERT_TRUE(&*MIBAdd == &*MIBAdd2);
auto MIBAdd4 =
CSEB.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput});
ASSERT_TRUE(&*MIBAdd == &*MIBAdd4);
auto MIBAdd5 =
CSEB.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput1});
ASSERT_TRUE(&*MIBAdd != &*MIBAdd5);
// Try building G_CONSTANTS.
auto MIBCst = CSEB.buildConstant(s32, 0);
auto MIBCst1 = CSEB.buildConstant(s32, 0);
ASSERT_TRUE(&*MIBCst == &*MIBCst1);
// Try the CFing of BinaryOps.
auto MIBCF1 = CSEB.buildInstr(TargetOpcode::G_ADD, {s32}, {MIBCst, MIBCst});
ASSERT_TRUE(&*MIBCF1 == &*MIBCst);
// Try out building FCONSTANTs.
auto MIBFP0 = CSEB.buildFConstant(s32, 1.0);
auto MIBFP0_1 = CSEB.buildFConstant(s32, 1.0);
ASSERT_TRUE(&*MIBFP0 == &*MIBFP0_1);
CSEInfo.print();
// Check G_UNMERGE_VALUES
auto MIBUnmerge = CSEB.buildUnmerge({s32, s32}, Copies[0]);
auto MIBUnmerge2 = CSEB.buildUnmerge({s32, s32}, Copies[0]);
ASSERT_TRUE(&*MIBUnmerge == &*MIBUnmerge2);
}
TEST_F(GISelMITest, TestCSEConstantConfig) {
if (!TM)
return;
LLT s16{LLT::scalar(16)};
auto MIBInput = B.buildInstr(TargetOpcode::G_TRUNC, {s16}, {Copies[0]});
auto MIBAdd = B.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput});
auto MIBZero = B.buildConstant(s16, 0);
GISelCSEInfo CSEInfo;
CSEInfo.setCSEConfig(make_unique<CSEConfigConstantOnly>());
CSEInfo.analyze(*MF);
B.setCSEInfo(&CSEInfo);
CSEMIRBuilder CSEB(B.getState());
CSEB.setInsertPt(*EntryMBB, EntryMBB->begin());
auto MIBAdd1 =
CSEB.buildInstr(TargetOpcode::G_ADD, {s16}, {MIBInput, MIBInput});
// We should CSE constants only. Adds should not be CSEd.
ASSERT_TRUE(MIBAdd1->getOpcode() != TargetOpcode::COPY);
ASSERT_TRUE(&*MIBAdd1 != &*MIBAdd);
// We should CSE constant.
auto MIBZeroTmp = CSEB.buildConstant(s16, 0);
ASSERT_TRUE(&*MIBZero == &*MIBZeroTmp);
}
} // namespace

View File

@ -1,4 +1,4 @@
//===- LegalizerHelperTest.h
//===- GISelMITest.h
//-----------------------------------------------===//
//
// The LLVM Compiler Infrastructure
@ -7,6 +7,8 @@
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_UNITTEST_CODEGEN_GLOBALISEL_GISELMI_H
#define LLVM_UNITTEST_CODEGEN_GLOBALISEL_GISELMI_H
#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h"
@ -32,7 +34,7 @@
using namespace llvm;
using namespace MIPatternMatch;
void initLLVM() {
static inline void initLLVM() {
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmPrinters();
@ -45,7 +47,7 @@ void initLLVM() {
/// Create a TargetMachine. As we lack a dedicated always available target for
/// unittests, we go for "AArch64".
std::unique_ptr<LLVMTargetMachine> createTargetMachine() {
static std::unique_ptr<LLVMTargetMachine> createTargetMachine() {
Triple TargetTriple("aarch64--");
std::string Error;
const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error);
@ -53,15 +55,16 @@ std::unique_ptr<LLVMTargetMachine> createTargetMachine() {
return nullptr;
TargetOptions Options;
return std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine*>(
T->createTargetMachine("AArch64", "", "", Options, None, None,
CodeGenOpt::Aggressive)));
return std::unique_ptr<LLVMTargetMachine>(
static_cast<LLVMTargetMachine *>(T->createTargetMachine(
"AArch64", "", "", Options, None, None, CodeGenOpt::Aggressive)));
}
std::unique_ptr<Module> parseMIR(LLVMContext &Context,
std::unique_ptr<MIRParser> &MIR,
const TargetMachine &TM, StringRef MIRCode,
const char *FuncName, MachineModuleInfo &MMI) {
static std::unique_ptr<Module> parseMIR(LLVMContext &Context,
std::unique_ptr<MIRParser> &MIR,
const TargetMachine &TM,
StringRef MIRCode, const char *FuncName,
MachineModuleInfo &MMI) {
SMDiagnostic Diagnostic;
std::unique_ptr<MemoryBuffer> MBuffer = MemoryBuffer::getMemBuffer(MIRCode);
MIR = createMIRParser(std::move(MBuffer), Context);
@ -80,7 +83,7 @@ std::unique_ptr<Module> parseMIR(LLVMContext &Context,
return M;
}
std::pair<std::unique_ptr<Module>, std::unique_ptr<MachineModuleInfo>>
static std::pair<std::unique_ptr<Module>, std::unique_ptr<MachineModuleInfo>>
createDummyModule(LLVMContext &Context, const LLVMTargetMachine &TM,
StringRef MIRFunc) {
SmallString<512> S;
@ -123,9 +126,9 @@ static void collectCopies(SmallVectorImpl<unsigned> &Copies,
}
}
class LegalizerHelperTest : public ::testing::Test {
class GISelMITest : public ::testing::Test {
protected:
LegalizerHelperTest() : ::testing::Test() {
GISelMITest() : ::testing::Test() {
TM = createTargetMachine();
if (!TM)
return;
@ -168,8 +171,8 @@ protected:
} \
};
static bool CheckMachineFunction(const MachineFunction &MF,
StringRef CheckStr) {
static inline bool CheckMachineFunction(const MachineFunction &MF,
StringRef CheckStr) {
SmallString<512> Msg;
raw_svector_ostream OS(Msg);
MF.print(OS);
@ -190,3 +193,4 @@ static bool CheckMachineFunction(const MachineFunction &MF,
SM.AddNewSourceBuffer(std::move(OutputBuf), SMLoc());
return FC.CheckInput(SM, OutBuffer, CheckStrings);
}
#endif

View File

@ -1,4 +1,5 @@
//===- PatternMatchTest.cpp -----------------------------------------------===//
//===- LegalizerHelperTest.cpp
//-----------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@ -7,21 +8,21 @@
//
//===----------------------------------------------------------------------===//
#include "LegalizerHelperTest.h"
#include "GISelMITest.h"
namespace {
class DummyGISelObserver : public GISelChangeObserver {
public:
void changingInstr(const MachineInstr &MI) override {}
void changedInstr(const MachineInstr &MI) override {}
void createdInstr(const MachineInstr &MI) override {}
void erasingInstr(const MachineInstr &MI) override {}
void changingInstr(MachineInstr &MI) override {}
void changedInstr(MachineInstr &MI) override {}
void createdInstr(MachineInstr &MI) override {}
void erasingInstr(MachineInstr &MI) override {}
};
// Test CTTZ expansion when CTTZ_ZERO_UNDEF is legal or custom,
// in which case it becomes CTTZ_ZERO_UNDEF with select.
TEST_F(LegalizerHelperTest, LowerBitCountingCTTZ0) {
TEST_F(GISelMITest, LowerBitCountingCTTZ0) {
if (!TM)
return;
@ -33,7 +34,7 @@ TEST_F(LegalizerHelperTest, LowerBitCountingCTTZ0) {
B.buildInstr(TargetOpcode::G_CTTZ, {LLT::scalar(64)}, {Copies[0]});
AInfo Info(MF->getSubtarget());
DummyGISelObserver Observer;
LegalizerHelper Helper(*MF, Info, Observer);
LegalizerHelper Helper(*MF, Info, Observer, B);
// Perform Legalization
ASSERT_TRUE(Helper.lower(*MIBCTTZ, 0, LLT::scalar(64)) ==
LegalizerHelper::LegalizeResult::Legalized);
@ -51,7 +52,7 @@ TEST_F(LegalizerHelperTest, LowerBitCountingCTTZ0) {
}
// CTTZ expansion in terms of CTLZ
TEST_F(LegalizerHelperTest, LowerBitCountingCTTZ1) {
TEST_F(GISelMITest, LowerBitCountingCTTZ1) {
if (!TM)
return;
@ -63,7 +64,7 @@ TEST_F(LegalizerHelperTest, LowerBitCountingCTTZ1) {
B.buildInstr(TargetOpcode::G_CTTZ, {LLT::scalar(64)}, {Copies[0]});
AInfo Info(MF->getSubtarget());
DummyGISelObserver Observer;
LegalizerHelper Helper(*MF, Info, Observer);
LegalizerHelper Helper(*MF, Info, Observer, B);
// Perform Legalization
ASSERT_TRUE(Helper.lower(*MIBCTTZ, 0, LLT::scalar(64)) ==
LegalizerHelper::LegalizeResult::Legalized);
@ -83,7 +84,7 @@ TEST_F(LegalizerHelperTest, LowerBitCountingCTTZ1) {
}
// CTTZ expansion in terms of CTPOP
TEST_F(LegalizerHelperTest, LowerBitCountingCTTZ2) {
TEST_F(GISelMITest, LowerBitCountingCTTZ2) {
if (!TM)
return;
@ -95,7 +96,7 @@ TEST_F(LegalizerHelperTest, LowerBitCountingCTTZ2) {
B.buildInstr(TargetOpcode::G_CTTZ, {LLT::scalar(64)}, {Copies[0]});
AInfo Info(MF->getSubtarget());
DummyGISelObserver Observer;
LegalizerHelper Helper(*MF, Info, Observer);
LegalizerHelper Helper(*MF, Info, Observer, B);
ASSERT_TRUE(Helper.lower(*MIBCTTZ, 0, LLT::scalar(64)) ==
LegalizerHelper::LegalizeResult::Legalized);
@ -112,7 +113,7 @@ TEST_F(LegalizerHelperTest, LowerBitCountingCTTZ2) {
}
// CTTZ_ZERO_UNDEF expansion in terms of CTTZ
TEST_F(LegalizerHelperTest, LowerBitCountingCTTZ3) {
TEST_F(GISelMITest, LowerBitCountingCTTZ3) {
if (!TM)
return;
@ -124,7 +125,7 @@ TEST_F(LegalizerHelperTest, LowerBitCountingCTTZ3) {
{LLT::scalar(64)}, {Copies[0]});
AInfo Info(MF->getSubtarget());
DummyGISelObserver Observer;
LegalizerHelper Helper(*MF, Info, Observer);
LegalizerHelper Helper(*MF, Info, Observer, B);
ASSERT_TRUE(Helper.lower(*MIBCTTZ, 0, LLT::scalar(64)) ==
LegalizerHelper::LegalizeResult::Legalized);
@ -137,7 +138,7 @@ TEST_F(LegalizerHelperTest, LowerBitCountingCTTZ3) {
}
// CTLZ expansion in terms of CTLZ_ZERO_UNDEF
TEST_F(LegalizerHelperTest, LowerBitCountingCTLZ0) {
TEST_F(GISelMITest, LowerBitCountingCTLZ0) {
if (!TM)
return;
@ -149,7 +150,7 @@ TEST_F(LegalizerHelperTest, LowerBitCountingCTLZ0) {
B.buildInstr(TargetOpcode::G_CTLZ, {LLT::scalar(64)}, {Copies[0]});
AInfo Info(MF->getSubtarget());
DummyGISelObserver Observer;
LegalizerHelper Helper(*MF, Info, Observer);
LegalizerHelper Helper(*MF, Info, Observer, B);
ASSERT_TRUE(Helper.lower(*MIBCTLZ, 0, LLT::scalar(64)) ==
LegalizerHelper::LegalizeResult::Legalized);
@ -166,7 +167,7 @@ TEST_F(LegalizerHelperTest, LowerBitCountingCTLZ0) {
}
// CTLZ expansion in terms of CTLZ_ZERO_UNDEF if the latter is a libcall
TEST_F(LegalizerHelperTest, LowerBitCountingCTLZLibcall) {
TEST_F(GISelMITest, LowerBitCountingCTLZLibcall) {
if (!TM)
return;
@ -178,7 +179,7 @@ TEST_F(LegalizerHelperTest, LowerBitCountingCTLZLibcall) {
B.buildInstr(TargetOpcode::G_CTLZ, {LLT::scalar(64)}, {Copies[0]});
AInfo Info(MF->getSubtarget());
DummyGISelObserver Observer;
LegalizerHelper Helper(*MF, Info, Observer);
LegalizerHelper Helper(*MF, Info, Observer, B);
ASSERT_TRUE(Helper.lower(*MIBCTLZ, 0, LLT::scalar(64)) ==
LegalizerHelper::LegalizeResult::Legalized);
@ -195,7 +196,7 @@ TEST_F(LegalizerHelperTest, LowerBitCountingCTLZLibcall) {
}
// CTLZ expansion
TEST_F(LegalizerHelperTest, LowerBitCountingCTLZ1) {
TEST_F(GISelMITest, LowerBitCountingCTLZ1) {
if (!TM)
return;
@ -209,7 +210,7 @@ TEST_F(LegalizerHelperTest, LowerBitCountingCTLZ1) {
auto MIBCTLZ = B.buildInstr(TargetOpcode::G_CTLZ, {s8}, {MIBTrunc});
AInfo Info(MF->getSubtarget());
DummyGISelObserver Observer;
LegalizerHelper Helper(*MF, Info, Observer);
LegalizerHelper Helper(*MF, Info, Observer, B);
ASSERT_TRUE(Helper.lower(*MIBCTLZ, 0, s8) ==
LegalizerHelper::LegalizeResult::Legalized);
@ -234,7 +235,7 @@ TEST_F(LegalizerHelperTest, LowerBitCountingCTLZ1) {
}
// CTLZ widening.
TEST_F(LegalizerHelperTest, WidenBitCountingCTLZ) {
TEST_F(GISelMITest, WidenBitCountingCTLZ) {
if (!TM)
return;
@ -249,7 +250,7 @@ TEST_F(LegalizerHelperTest, WidenBitCountingCTLZ) {
auto MIBCTLZ = B.buildInstr(TargetOpcode::G_CTLZ, {s8}, {MIBTrunc});
AInfo Info(MF->getSubtarget());
DummyGISelObserver Observer;
LegalizerHelper Helper(*MF, Info, Observer);
LegalizerHelper Helper(*MF, Info, Observer, B);
ASSERT_TRUE(Helper.widenScalar(*MIBCTLZ, 0, s16) ==
LegalizerHelper::LegalizeResult::Legalized);
@ -267,7 +268,7 @@ TEST_F(LegalizerHelperTest, WidenBitCountingCTLZ) {
}
// CTLZ_ZERO_UNDEF widening.
TEST_F(LegalizerHelperTest, WidenBitCountingCTLZZeroUndef) {
TEST_F(GISelMITest, WidenBitCountingCTLZZeroUndef) {
if (!TM)
return;
@ -283,7 +284,7 @@ TEST_F(LegalizerHelperTest, WidenBitCountingCTLZZeroUndef) {
B.buildInstr(TargetOpcode::G_CTLZ_ZERO_UNDEF, {s8}, {MIBTrunc});
AInfo Info(MF->getSubtarget());
DummyGISelObserver Observer;
LegalizerHelper Helper(*MF, Info, Observer);
LegalizerHelper Helper(*MF, Info, Observer, B);
ASSERT_TRUE(Helper.widenScalar(*MIBCTLZ_ZU, 0, s16) ==
LegalizerHelper::LegalizeResult::Legalized);
@ -301,7 +302,7 @@ TEST_F(LegalizerHelperTest, WidenBitCountingCTLZZeroUndef) {
}
// CTPOP widening.
TEST_F(LegalizerHelperTest, WidenBitCountingCTPOP) {
TEST_F(GISelMITest, WidenBitCountingCTPOP) {
if (!TM)
return;
@ -316,7 +317,7 @@ TEST_F(LegalizerHelperTest, WidenBitCountingCTPOP) {
auto MIBCTPOP = B.buildInstr(TargetOpcode::G_CTPOP, {s8}, {MIBTrunc});
AInfo Info(MF->getSubtarget());
DummyGISelObserver Observer;
LegalizerHelper Helper(*MF, Info, Observer);
LegalizerHelper Helper(*MF, Info, Observer, B);
ASSERT_TRUE(Helper.widenScalar(*MIBCTPOP, 0, s16) ==
LegalizerHelper::LegalizeResult::Legalized);
@ -332,7 +333,7 @@ TEST_F(LegalizerHelperTest, WidenBitCountingCTPOP) {
}
// CTTZ_ZERO_UNDEF widening.
TEST_F(LegalizerHelperTest, WidenBitCountingCTTZ_ZERO_UNDEF) {
TEST_F(GISelMITest, WidenBitCountingCTTZ_ZERO_UNDEF) {
if (!TM)
return;
@ -348,7 +349,7 @@ TEST_F(LegalizerHelperTest, WidenBitCountingCTTZ_ZERO_UNDEF) {
B.buildInstr(TargetOpcode::G_CTTZ_ZERO_UNDEF, {s8}, {MIBTrunc});
AInfo Info(MF->getSubtarget());
DummyGISelObserver Observer;
LegalizerHelper Helper(*MF, Info, Observer);
LegalizerHelper Helper(*MF, Info, Observer, B);
ASSERT_TRUE(Helper.widenScalar(*MIBCTTZ_ZERO_UNDEF, 0, s16) ==
LegalizerHelper::LegalizeResult::Legalized);
@ -364,7 +365,7 @@ TEST_F(LegalizerHelperTest, WidenBitCountingCTTZ_ZERO_UNDEF) {
}
// CTTZ widening.
TEST_F(LegalizerHelperTest, WidenBitCountingCTTZ) {
TEST_F(GISelMITest, WidenBitCountingCTTZ) {
if (!TM)
return;
@ -379,7 +380,7 @@ TEST_F(LegalizerHelperTest, WidenBitCountingCTTZ) {
auto MIBCTTZ = B.buildInstr(TargetOpcode::G_CTTZ, {s8}, {MIBTrunc});
AInfo Info(MF->getSubtarget());
DummyGISelObserver Observer;
LegalizerHelper Helper(*MF, Info, Observer);
LegalizerHelper Helper(*MF, Info, Observer, B);
ASSERT_TRUE(Helper.widenScalar(*MIBCTTZ, 0, s16) ==
LegalizerHelper::LegalizeResult::Legalized);
@ -396,7 +397,7 @@ TEST_F(LegalizerHelperTest, WidenBitCountingCTTZ) {
ASSERT_TRUE(CheckMachineFunction(*MF, CheckStr));
}
// UADDO widening.
TEST_F(LegalizerHelperTest, WidenUADDO) {
TEST_F(GISelMITest, WidenUADDO) {
if (!TM)
return;
@ -413,7 +414,7 @@ TEST_F(LegalizerHelperTest, WidenUADDO) {
B.buildInstr(TargetOpcode::G_UADDO, {s8, CarryReg}, {MIBTrunc, MIBTrunc});
AInfo Info(MF->getSubtarget());
DummyGISelObserver Observer;
LegalizerHelper Helper(*MF, Info, Observer);
LegalizerHelper Helper(*MF, Info, Observer, B);
ASSERT_TRUE(Helper.widenScalar(*MIBUAddO, 0, s16) ==
LegalizerHelper::LegalizeResult::Legalized);
@ -433,7 +434,7 @@ TEST_F(LegalizerHelperTest, WidenUADDO) {
}
// USUBO widening.
TEST_F(LegalizerHelperTest, WidenUSUBO) {
TEST_F(GISelMITest, WidenUSUBO) {
if (!TM)
return;
@ -450,7 +451,7 @@ TEST_F(LegalizerHelperTest, WidenUSUBO) {
B.buildInstr(TargetOpcode::G_USUBO, {s8, CarryReg}, {MIBTrunc, MIBTrunc});
AInfo Info(MF->getSubtarget());
DummyGISelObserver Observer;
LegalizerHelper Helper(*MF, Info, Observer);
LegalizerHelper Helper(*MF, Info, Observer, B);
ASSERT_TRUE(Helper.widenScalar(*MIBUSUBO, 0, s16) ==
LegalizerHelper::LegalizeResult::Legalized);