31#define GET_GICOMBINER_DEPS
32#include "AArch64GenPreLegalizeGICombiner.inc"
33#undef GET_GICOMBINER_DEPS
35#define DEBUG_TYPE "aarch64-prelegalizer-combiner"
42#define GET_GICOMBINER_TYPES
43#include "AArch64GenPreLegalizeGICombiner.inc"
44#undef GET_GICOMBINER_TYPES
51 assert(
MI.getOpcode() == TargetOpcode::G_ICMP && VT);
81 assert(
MI.getOpcode() == TargetOpcode::G_ICMP);
86 Builder.setInstrAndDebugLoc(
MI);
87 auto WideZero = Builder.buildConstant(WideTy, 0);
89 MI.getOperand(2).setReg(WideReg);
90 MI.getOperand(3).setReg(WideZero.getReg(0));
100 std::pair<uint64_t, uint64_t> &MatchInfo) {
101 assert(
MI.getOpcode() == TargetOpcode::G_GLOBAL_VALUE);
103 auto &GlobalOp =
MI.getOperand(1);
104 auto *GV = GlobalOp.getGlobal();
105 if (GV->isThreadLocal())
130 if (UseInstr.getOpcode() != TargetOpcode::G_PTR_ADD)
133 UseInstr.getOperand(2).getReg(), MRI);
136 MinOffset = std::min(MinOffset, Cst->Value.getZExtValue());
141 uint64_t CurrOffset = GlobalOp.getOffset();
142 uint64_t NewOffset = MinOffset + CurrOffset;
143 if (NewOffset <= CurrOffset)
155 if (NewOffset >= (1 << 20))
158 Type *
T = GV->getValueType();
160 NewOffset > GV->getDataLayout().getTypeAllocSize(
T))
162 MatchInfo = std::make_pair(NewOffset, MinOffset);
168 std::pair<uint64_t, uint64_t> &MatchInfo) {
190 std::tie(
Offset, MinOffset) = MatchInfo;
191 B.setInstrAndDebugLoc(*std::next(
MI.getIterator()));
193 auto &GlobalOp =
MI.getOperand(1);
194 auto *GV = GlobalOp.getGlobal();
195 GlobalOp.ChangeToGA(GV,
Offset, GlobalOp.getTargetFlags());
198 MI.getOperand(0).setReg(NewGVDst);
202 B.buildConstant(
LLT::scalar(64), -
static_cast<int64_t
>(MinOffset)));
211 std::tuple<Register, Register, bool> &MatchInfo) {
212 assert(
MI.getOpcode() == TargetOpcode::G_VECREDUCE_ADD &&
213 "Expected a G_VECREDUCE_ADD instruction");
214 assert(STI.hasDotProd() &&
"Target should have Dot Product feature");
243 if ((I1Opc == TargetOpcode::G_ZEXT || I1Opc == TargetOpcode::G_SEXT) &&
253 unsigned I1Opc =
I1->getOpcode();
254 if (I1Opc == TargetOpcode::G_MUL) {
256 if (!tryMatchingMulOfExt(I1, Out1, Out2, I1Opc))
259 std::get<0>(MatchInfo) = Out1;
260 std::get<1>(MatchInfo) = Out2;
261 }
else if (I1Opc == TargetOpcode::G_ZEXT || I1Opc == TargetOpcode::G_SEXT) {
265 if (M->getOpcode() == TargetOpcode::G_MUL &&
266 tryMatchingMulOfExt(M, Out1, Out2, I1Opc)) {
268 std::get<0>(MatchInfo) = Out1;
269 std::get<1>(MatchInfo) = Out2;
272 std::get<0>(MatchInfo) = I1Op;
273 std::get<1>(MatchInfo) = 0;
279 if (I1Opc == TargetOpcode::G_ZEXT)
280 std::get<2>(MatchInfo) = 0;
281 else if (I1Opc == TargetOpcode::G_SEXT)
282 std::get<2>(MatchInfo) = 1;
286 if (SrcTy.getScalarSizeInBits() != 8 || SrcTy.getNumElements() % 8 != 0)
296 std::tuple<Register, Register, bool> &MatchInfo) {
297 assert(
MI.getOpcode() == TargetOpcode::G_VECREDUCE_ADD &&
298 "Expected a G_VECREDUCE_ADD instruction");
299 assert(STI.hasDotProd() &&
"Target should have Dot Product feature");
303 std::get<2>(MatchInfo) ? AArch64::G_SDOT : AArch64::G_UDOT;
304 Register Ext1SrcReg = std::get<0>(MatchInfo);
309 if (std::get<1>(MatchInfo) == 0)
310 Ext2SrcReg = Builder.buildConstant(MRI.
getType(Ext1SrcReg), 1)
314 Ext2SrcReg = std::get<1>(MatchInfo);
320 if (SrcTy.getNumElements() % 16 == 0) {
321 NumOfDotMI = SrcTy.getNumElements() / 16;
323 }
else if (SrcTy.getNumElements() % 8 == 0) {
324 NumOfDotMI = SrcTy.getNumElements() / 8;
331 if (NumOfDotMI == 1) {
332 auto Zeroes = Builder.buildConstant(MidTy, 0)->getOperand(0).getReg();
333 auto Dot = Builder.buildInstr(DotOpcode, {MidTy},
334 {Zeroes, Ext1SrcReg, Ext2SrcReg});
335 Builder.buildVecReduceAdd(
MI.getOperand(0), Dot->getOperand(0));
340 if (SrcTy.getNumElements() % 16 != 0) {
346 LLT LeftoverTy1, LeftoverTy2;
348 LeftoverTy1, Ext1UnmergeReg, Leftover1, Builder,
351 LeftoverTy2, Ext2UnmergeReg, Leftover2, Builder,
364 {Leftover1[0], v8Zeroes})
369 {Leftover2[0], v8Zeroes})
374 unsigned SrcNumElts = SrcTy.getNumElements();
376 Ext1UnmergeReg, Builder, MRI);
378 Ext2UnmergeReg, Builder, MRI);
383 unsigned NumElements = 0;
384 for (
unsigned i = 0; i < Ext1UnmergeReg.
size(); i++) {
394 auto Zeroes = Builder.buildConstant(ZeroesLLT, 0)->getOperand(0).getReg();
397 .buildInstr(DotOpcode, {MRI.
getType(Zeroes)},
398 {Zeroes, Ext1UnmergeReg[i], Ext2UnmergeReg[i]})
407 Builder.buildVecReduceAdd(
MI.getOperand(0).getReg(),
408 ConcatMI->getOperand(0).getReg());
412 MI.eraseFromParent();
418 std::pair<Register, bool> &MatchInfo) {
419 assert(
MI.getOpcode() == TargetOpcode::G_VECREDUCE_ADD &&
420 "Expected G_VECREDUCE_ADD Opcode");
426 if (ExtOpc == TargetOpcode::G_ZEXT)
427 std::get<1>(MatchInfo) = 0;
428 else if (ExtOpc == TargetOpcode::G_SEXT)
429 std::get<1>(MatchInfo) = 1;
445 std::get<0>(MatchInfo) = ExtSrcReg;
453 std::pair<Register, bool> &MatchInfo) {
454 assert(
MI.getOpcode() == TargetOpcode::G_VECREDUCE_ADD &&
455 "Expected G_VECREDUCE_ADD Opcode");
457 unsigned Opc = std::get<1>(MatchInfo) ? AArch64::G_SADDLV : AArch64::G_UADDLV;
458 Register SrcReg = std::get<0>(MatchInfo);
467 unsigned SrcScalSize = SrcTy.getScalarSizeInBits();
468 unsigned SrcNumElem = SrcTy.getNumElements();
469 if ((SrcScalSize == 8 && SrcNumElem > 16) ||
470 (SrcScalSize == 16 && SrcNumElem > 8) ||
471 (SrcScalSize == 32 && SrcNumElem > 4)) {
475 if (SrcScalSize == 8)
477 else if (SrcScalSize == 16)
479 else if (SrcScalSize == 32)
486 extractParts(SrcReg, SrcTy, MainTy, LeftoverTy, WorkingRegisters,
487 LeftoverRegs,
B, MRI);
497 for (
unsigned I = 0;
I < WorkingRegisters.
size();
I++) {
500 LLT WorkingRegTy = MRI.
getType(WorkingRegisters[
I]);
503 WorkingRegisters[
I] =
504 B.buildInstr(std::get<1>(MatchInfo) ? TargetOpcode::G_SEXT
505 : TargetOpcode::G_ZEXT,
513 :
LLT::fixed_vector(2, 64);
515 B.buildInstr(
Opc, {AddlvTy}, {WorkingRegisters[
I]}).getReg(0);
522 if (MidScalarSize == 32 || MidScalarSize == 64) {
523 WorkingRegisters[
I] =
B.buildInstr(AArch64::G_EXTRACT_VECTOR_ELT,
524 {MidScalarLLT}, {AddlvReg, ZeroReg})
527 Register ExtractReg =
B.buildInstr(AArch64::G_EXTRACT_VECTOR_ELT,
530 WorkingRegisters[
I] =
531 B.buildTrunc({MidScalarLLT}, {ExtractReg}).
getReg(0);
536 if (WorkingRegisters.
size() > 1) {
537 OutReg =
B.buildAdd(MidScalarLLT, WorkingRegisters[0], WorkingRegisters[1])
539 for (
unsigned I = 2;
I < WorkingRegisters.
size();
I++) {
540 OutReg =
B.buildAdd(MidScalarLLT, OutReg, WorkingRegisters[
I]).getReg(0);
543 OutReg = WorkingRegisters[0];
549 B.buildInstr(std::get<1>(MatchInfo) ? TargetOpcode::G_SEXT
550 : TargetOpcode::G_ZEXT,
553 B.buildCopy(DstReg, OutReg);
556 MI.eraseFromParent();
564 assert((
MI.getOpcode() == TargetOpcode::G_ADD ||
565 MI.getOpcode() == TargetOpcode::G_SUB ||
566 MI.getOpcode() == TargetOpcode::G_MUL) &&
567 "Expected a G_ADD, G_SUB or G_MUL instruction\n");
575 Register ExtDstReg =
MI.getOperand(1).getReg();
580 if (((Ext1SrcScal == 8 && ExtDstScal == 32) ||
581 ((Ext1SrcScal == 8 || Ext1SrcScal == 16) && ExtDstScal == 64)) &&
582 Ext1SrcTy == Ext2SrcTy)
593 unsigned Opc = isSExt ? TargetOpcode::G_SEXT : TargetOpcode::G_ZEXT;
597 B.buildInstr(
MI.getOpcode(), {MidTy}, {Ext1Reg, Ext2Reg}).getReg(0);
602 if (
MI.getOpcode() == TargetOpcode::G_ADD ||
603 MI.getOpcode() == TargetOpcode::G_MUL)
604 B.buildInstr(
Opc, {DstReg}, {AddReg});
606 B.buildSExt(DstReg, AddReg);
608 MI.eraseFromParent();
642 auto &MRI = *
B.getMRI();
661 if (Op0WideDef->
getOpcode() != TargetOpcode::G_ASSERT_ZEXT ||
662 Op1WideDef->
getOpcode() != TargetOpcode::G_ASSERT_ZEXT ||
670 (OpTySize != 8 && OpTySize != 16))
674 Register ResStatus =
MI.getOperand(1).getReg();
678 if (CondUser->
getOpcode() != TargetOpcode::G_BRCOND)
689 (I.getParent() == FailMBB || I.getParent() == CurrentMBB);
694 B.setInstrAndDebugLoc(*
MI.getNextNode());
695 MI.eraseFromParent();
699 B.buildInstr(TargetOpcode::G_ADD, {AddDst}, {Op0Wide, Op1Wide});
706 B.buildConstant(
LLT::scalar(32), OpTySize == 8 ? 1 << 8 : 1 << 16));
712 B.buildZExtOrTrunc(ResVal, AddDst);
716 auto OldR = U.getParent()->getOperand(0).getReg();
718 U.getParent()->eraseFromParent();
726class AArch64PreLegalizerCombinerImpl :
public Combiner {
728 const CombinerHelper Helper;
729 const AArch64PreLegalizerCombinerImplRuleConfig &RuleConfig;
730 const AArch64Subtarget &STI;
731 const LibcallLoweringInfo &Libcalls;
734 AArch64PreLegalizerCombinerImpl(
735 MachineFunction &MF, CombinerInfo &CInfo, GISelValueTracking &VT,
736 GISelCSEInfo *CSEInfo,
737 const AArch64PreLegalizerCombinerImplRuleConfig &RuleConfig,
738 const AArch64Subtarget &STI,
const LibcallLoweringInfo &Libcalls,
739 MachineDominatorTree *MDT,
const LegalizerInfo *LI);
741 static const char *
getName() {
return "AArch6400PreLegalizerCombiner"; }
743 bool tryCombineAll(MachineInstr &
I)
const override;
745 bool tryCombineAllImpl(MachineInstr &
I)
const;
748#define GET_GICOMBINER_CLASS_MEMBERS
749#include "AArch64GenPreLegalizeGICombiner.inc"
750#undef GET_GICOMBINER_CLASS_MEMBERS
753#define GET_GICOMBINER_IMPL
754#include "AArch64GenPreLegalizeGICombiner.inc"
755#undef GET_GICOMBINER_IMPL
757AArch64PreLegalizerCombinerImpl::AArch64PreLegalizerCombinerImpl(
760 const AArch64PreLegalizerCombinerImplRuleConfig &RuleConfig,
763 :
Combiner(MF, CInfo, &VT, CSEInfo),
764 Helper(Observer,
B,
true, &VT, MDT, LI),
765 RuleConfig(RuleConfig), STI(STI), Libcalls(Libcalls),
767#include
"AArch64GenPreLegalizeGICombiner.inc"
772bool AArch64PreLegalizerCombinerImpl::tryCombineAll(
MachineInstr &
MI)
const {
773 if (tryCombineAllImpl(
MI))
776 unsigned Opc =
MI.getOpcode();
778 case TargetOpcode::G_SHUFFLE_VECTOR:
780 case TargetOpcode::G_UADDO:
781 return tryToSimplifyUADDO(
MI,
B, Helper, Observer);
782 case TargetOpcode::G_MEMCPY_INLINE:
784 case TargetOpcode::G_MEMCPY:
785 case TargetOpcode::G_MEMMOVE:
786 case TargetOpcode::G_MEMSET: {
789 unsigned MaxLen = CInfo.EnableOpt ? 0 : 32;
793 if (
Opc == TargetOpcode::G_MEMSET)
795 CInfo.EnableMinSize);
806class AArch64PreLegalizerCombiner :
public MachineFunctionPass {
810 AArch64PreLegalizerCombiner();
812 StringRef getPassName()
const override {
813 return "AArch64PreLegalizerCombiner";
816 bool runOnMachineFunction(MachineFunction &MF)
override;
818 void getAnalysisUsage(AnalysisUsage &AU)
const override;
821 AArch64PreLegalizerCombinerImplRuleConfig RuleConfig;
825void AArch64PreLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU)
const {
828 AU.
addRequired<GISelValueTrackingAnalysisLegacy>();
838AArch64PreLegalizerCombiner::AArch64PreLegalizerCombiner()
839 : MachineFunctionPass(
ID) {
840 if (!RuleConfig.parseCommandLineOption())
844bool AArch64PreLegalizerCombiner::runOnMachineFunction(
MachineFunction &MF) {
849 getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEWrapper();
854 const auto *LI =
ST.getLegalizerInfo();
859 getAnalysis<LibcallLoweringInfoWrapper>().getLibcallLowering(
865 &getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
867 &getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
869 nullptr, EnableOpt,
F.hasOptSize(),
872 CInfo.MaxIterations = 1;
876 CInfo.EnableFullDCE =
true;
877 AArch64PreLegalizerCombinerImpl Impl(MF, CInfo, *VT, CSEInfo, RuleConfig, ST,
879 return Impl.combineMachineInstrs();
882char AArch64PreLegalizerCombiner::ID = 0;
884 "Combine AArch64 machine instrs before legalization",
890 "Combine AArch64 machine instrs before legalization",
false,
895 return new AArch64PreLegalizerCombiner();
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
#define GET_GICOMBINER_CONSTRUCTOR_INITS
amdgpu aa AMDGPU Address space based Alias Analysis Wrapper
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
Provides analysis for continuously CSEing during GISel passes.
This contains common combine transformations that may be used in a combine pass,or by the target else...
Option class for Targets to specify which operations are combined how and when.
This contains the base class for all Combiners generated by TableGen.
Provides analysis for querying information about KnownBits during GISel passes.
Contains matchers for matching SSA Machine Instructions.
This file declares the MachineIRBuilder class.
static MCRegister getReg(const MCDisassembler *D, unsigned RC, unsigned RegNo)
#define INITIALIZE_PASS_DEPENDENCY(depName)
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
static StringRef getName(Value *V)
unsigned ClassifyGlobalReference(const GlobalValue *GV, const TargetMachine &TM) const
ClassifyGlobalReference - Find the target operand flags that describe how a global value should be re...
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Predicate
This enumeration lists the possible predicates for CmpInst subclasses.
void replaceRegWith(MachineRegisterInfo &MRI, Register FromReg, Register ToReg) const
MachineRegisterInfo::replaceRegWith() and inform the observer of the changes.
bool tryEmitMemcpyInline(MachineInstr &MI) const
Emit loads and stores that perform the given memcpy.
bool tryCombineShuffleVector(MachineInstr &MI) const
Try to combine G_SHUFFLE_VECTOR into G_CONCAT_VECTORS.
bool tryCombineMemCpyFamily(MachineInstr &MI, unsigned MaxLen=0) const
Optimize memcpy intrinsics et al, e.g.
FunctionPass class - This class is used to implement most global optimizations.
The actual analysis pass wrapper.
Simple wrapper that does the following.
Abstract class that contains various methods for clients to notify about changes.
virtual void changingInstr(MachineInstr &MI)=0
This instruction is about to be mutated in some way.
virtual void changedInstr(MachineInstr &MI)=0
This instruction was mutated in some way.
virtual void erasingInstr(MachineInstr &MI)=0
An instruction is about to be erased.
To use KnownBitsInfo analysis in a pass, KnownBitsInfo &Info = getAnalysis<GISelValueTrackingInfoAnal...
unsigned computeNumSignBits(Register R, const APInt &DemandedElts, unsigned Depth=0)
static bool isEquality(Predicate P)
Return true if this predicate is either EQ or NE.
constexpr unsigned getScalarSizeInBits() const
constexpr bool isScalar() const
static constexpr LLT scalar(unsigned SizeInBits)
Get a low-level scalar or aggregate "bag of bits".
constexpr uint16_t getNumElements() const
Returns the number of elements in a vector LLT.
constexpr bool isVector() const
constexpr TypeSize getSizeInBits() const
Returns the total size of the type. Must only be called on sized types.
constexpr LLT changeElementSize(unsigned NewEltSize) const
If this type is a vector, return a vector with the same number of elements but the new element size.
static constexpr LLT fixed_vector(unsigned NumElements, unsigned ScalarSizeInBits)
Get a low-level fixed-width vector of some number of elements and element width.
Tracks which library functions to use for a particular subtarget.
DominatorTree Class - Concrete subclass of DominatorTreeBase that is used to compute a normal dominat...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
Function & getFunction()
Return the LLVM function that this machine code represents.
const MachineFunctionProperties & getProperties() const
Get the function properties.
const TargetMachine & getTarget() const
getTarget - Return the target machine this machine code is compiled with
Helper class to build MachineInstr.
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
const MachineBasicBlock * getParent() const
const MachineOperand & getOperand(unsigned i) const
MachineOperand class - Representation of each machine instruction operand.
MachineBasicBlock * getMBB() const
MachineInstr * getParent()
getParent - Return the instruction that this operand belongs to.
Register getReg() const
getReg - Returns the register number.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
LLVM_ABI bool hasOneNonDBGUse(Register RegNo) const
hasOneNonDBGUse - Return true if there is exactly one non-Debug use of the specified register.
LLVM_ABI MachineInstr * getVRegDef(Register Reg) const
getVRegDef - Return the machine instr that defines the specified virtual register or null if none is ...
MachineOperand * getOneDef(Register Reg) const
Returns the defining operand if there is exactly one operand defining the specified register,...
LLT getType(Register Reg) const
Get the low-level type of Reg or LLT{} if Reg is not a generic (target independent) virtual register.
use_instr_nodbg_iterator use_instr_nodbg_begin(Register RegNo) const
iterator_range< use_instr_nodbg_iterator > use_nodbg_instructions(Register Reg) const
LLVM_ABI Register cloneVirtualRegister(Register VReg, StringRef Name="")
Create and return a new virtual register in the function with the same attributes as the given regist...
iterator_range< use_iterator > use_operands(Register Reg) const
Wrapper class representing virtual and physical registers.
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
CodeGenOptLevel getOptLevel() const
Returns the optimization level: None, Less, Default, or Aggressive.
The instances of the Type class are immutable: once they are created, they are never changed.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
bool tryEmitBZero(MachineInstr &MI, MachineIRBuilder &MIRBuilder, const LibcallLoweringInfo &Libcalls, bool MinSize)
Replace a G_MEMSET with a value of 0 with a G_BZERO instruction if it is supported and beneficial to ...
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
operand_type_match m_Reg()
SpecificConstantMatch m_SpecificICst(const APInt &RequestedValue)
Matches a constant equal to RequestedValue.
UnaryOp_match< SrcTy, TargetOpcode::G_ZEXT > m_GZExt(const SrcTy &Src)
bool mi_match(Reg R, const MachineRegisterInfo &MRI, Pattern &&P)
UnaryOp_match< SrcTy, TargetOpcode::G_TRUNC > m_GTrunc(const SrcTy &Src)
This is an optimization pass for GlobalISel generic memory operations.
FunctionPass * createAArch64PreLegalizerCombiner()
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
LLVM_ABI MachineInstr * getDefIgnoringCopies(Register Reg, const MachineRegisterInfo &MRI)
Find the def instruction for Reg, folding away any trivial copies.
LLVM_ABI std::unique_ptr< CSEConfigBase > getStandardCSEConfigForOpt(CodeGenOptLevel Level)
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
LLVM_ABI void extractParts(Register Reg, LLT Ty, int NumParts, SmallVectorImpl< Register > &VRegs, MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI)
Helper function to split a wide generic register into bitwise blocks with the given Type (which impli...
LLVM_ABI void getSelectionDAGFallbackAnalysisUsage(AnalysisUsage &AU)
Modify analysis usage so it preserves passes required for the SelectionDAG fallback.
LLVM_ABI std::optional< ValueAndVReg > getIConstantVRegValWithLookThrough(Register VReg, const MachineRegisterInfo &MRI, bool LookThroughInstrs=true)
If VReg is defined by a statically evaluable chain of instructions rooted on a G_CONSTANT returns its...
@ SinglePass
Enables Observer-based DCE and additional heuristics that retry combining defined and used instructio...