| //===- ReducerWorkItem.cpp - Wrapper for Module and MachineFunction -------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ReducerWorkItem.h" |
| #include "TestRunner.h" |
| #include "llvm/Analysis/ModuleSummaryAnalysis.h" |
| #include "llvm/Analysis/ProfileSummaryInfo.h" |
| #include "llvm/Bitcode/BitcodeReader.h" |
| #include "llvm/Bitcode/BitcodeWriter.h" |
| #include "llvm/CodeGen/CommandFlags.h" |
| #include "llvm/CodeGen/MIRParser/MIRParser.h" |
| #include "llvm/CodeGen/MIRPrinter.h" |
| #include "llvm/CodeGen/MachineDominators.h" |
| #include "llvm/CodeGen/MachineFrameInfo.h" |
| #include "llvm/CodeGen/MachineFunction.h" |
| #include "llvm/CodeGen/MachineFunctionPass.h" |
| #include "llvm/CodeGen/MachineModuleInfo.h" |
| #include "llvm/CodeGen/MachineRegisterInfo.h" |
| #include "llvm/CodeGen/TargetInstrInfo.h" |
| #include "llvm/IR/Constants.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/IR/ModuleSummaryIndex.h" |
| #include "llvm/IR/Operator.h" |
| #include "llvm/IR/Verifier.h" |
| #include "llvm/IRReader/IRReader.h" |
| #include "llvm/MC/TargetRegistry.h" |
| #include "llvm/Passes/PassBuilder.h" |
| #include "llvm/Support/MemoryBufferRef.h" |
| #include "llvm/Support/SourceMgr.h" |
| #include "llvm/Support/TargetSelect.h" |
| #include "llvm/Support/ToolOutputFile.h" |
| #include "llvm/Support/WithColor.h" |
| #include "llvm/Target/TargetMachine.h" |
| #include "llvm/TargetParser/Host.h" |
| #include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h" |
| #include "llvm/Transforms/Utils/Cloning.h" |
| #include <optional> |
| |
| using namespace llvm; |
| |
| ReducerWorkItem::ReducerWorkItem() = default; |
| ReducerWorkItem::~ReducerWorkItem() = default; |
| |
| extern cl::OptionCategory LLVMReduceOptions; |
| static cl::opt<std::string> TargetTriple("mtriple", |
| cl::desc("Set the target triple"), |
| cl::cat(LLVMReduceOptions)); |
| |
| static cl::opt<bool> TmpFilesAsBitcode( |
| "write-tmp-files-as-bitcode", |
| cl::desc("Always write temporary files as bitcode instead of textual IR"), |
| cl::init(false), cl::cat(LLVMReduceOptions)); |
| |
| static void cloneFrameInfo( |
| MachineFrameInfo &DstMFI, const MachineFrameInfo &SrcMFI, |
| const DenseMap<MachineBasicBlock *, MachineBasicBlock *> &Src2DstMBB) { |
| DstMFI.setFrameAddressIsTaken(SrcMFI.isFrameAddressTaken()); |
| DstMFI.setReturnAddressIsTaken(SrcMFI.isReturnAddressTaken()); |
| DstMFI.setHasStackMap(SrcMFI.hasStackMap()); |
| DstMFI.setHasPatchPoint(SrcMFI.hasPatchPoint()); |
| DstMFI.setUseLocalStackAllocationBlock( |
| SrcMFI.getUseLocalStackAllocationBlock()); |
| DstMFI.setOffsetAdjustment(SrcMFI.getOffsetAdjustment()); |
| |
| DstMFI.ensureMaxAlignment(SrcMFI.getMaxAlign()); |
| assert(DstMFI.getMaxAlign() == SrcMFI.getMaxAlign() && |
| "we need to set exact alignment"); |
| |
| DstMFI.setAdjustsStack(SrcMFI.adjustsStack()); |
| DstMFI.setHasCalls(SrcMFI.hasCalls()); |
| DstMFI.setHasOpaqueSPAdjustment(SrcMFI.hasOpaqueSPAdjustment()); |
| DstMFI.setHasCopyImplyingStackAdjustment( |
| SrcMFI.hasCopyImplyingStackAdjustment()); |
| DstMFI.setHasVAStart(SrcMFI.hasVAStart()); |
| DstMFI.setHasMustTailInVarArgFunc(SrcMFI.hasMustTailInVarArgFunc()); |
| DstMFI.setHasTailCall(SrcMFI.hasTailCall()); |
| |
| if (SrcMFI.isMaxCallFrameSizeComputed()) |
| DstMFI.setMaxCallFrameSize(SrcMFI.getMaxCallFrameSize()); |
| |
| DstMFI.setCVBytesOfCalleeSavedRegisters( |
| SrcMFI.getCVBytesOfCalleeSavedRegisters()); |
| |
| if (MachineBasicBlock *SavePt = SrcMFI.getSavePoint()) |
| DstMFI.setSavePoint(Src2DstMBB.find(SavePt)->second); |
| if (MachineBasicBlock *RestorePt = SrcMFI.getRestorePoint()) |
| DstMFI.setRestorePoint(Src2DstMBB.find(RestorePt)->second); |
| |
| |
| auto CopyObjectProperties = [](MachineFrameInfo &DstMFI, |
| const MachineFrameInfo &SrcMFI, int FI) { |
| if (SrcMFI.isStatepointSpillSlotObjectIndex(FI)) |
| DstMFI.markAsStatepointSpillSlotObjectIndex(FI); |
| DstMFI.setObjectSSPLayout(FI, SrcMFI.getObjectSSPLayout(FI)); |
| DstMFI.setObjectZExt(FI, SrcMFI.isObjectZExt(FI)); |
| DstMFI.setObjectSExt(FI, SrcMFI.isObjectSExt(FI)); |
| }; |
| |
| for (int i = 0, e = SrcMFI.getNumObjects() - SrcMFI.getNumFixedObjects(); |
| i != e; ++i) { |
| int NewFI; |
| |
| assert(!SrcMFI.isFixedObjectIndex(i)); |
| if (SrcMFI.isVariableSizedObjectIndex(i)) { |
| NewFI = DstMFI.CreateVariableSizedObject(SrcMFI.getObjectAlign(i), |
| SrcMFI.getObjectAllocation(i)); |
| } else { |
| NewFI = DstMFI.CreateStackObject( |
| SrcMFI.getObjectSize(i), SrcMFI.getObjectAlign(i), |
| SrcMFI.isSpillSlotObjectIndex(i), SrcMFI.getObjectAllocation(i), |
| SrcMFI.getStackID(i)); |
| DstMFI.setObjectOffset(NewFI, SrcMFI.getObjectOffset(i)); |
| } |
| |
| CopyObjectProperties(DstMFI, SrcMFI, i); |
| |
| (void)NewFI; |
| assert(i == NewFI && "expected to keep stable frame index numbering"); |
| } |
| |
| // Copy the fixed frame objects backwards to preserve frame index numbers, |
| // since CreateFixedObject uses front insertion. |
| for (int i = -1; i >= (int)-SrcMFI.getNumFixedObjects(); --i) { |
| assert(SrcMFI.isFixedObjectIndex(i)); |
| int NewFI = DstMFI.CreateFixedObject( |
| SrcMFI.getObjectSize(i), SrcMFI.getObjectOffset(i), |
| SrcMFI.isImmutableObjectIndex(i), SrcMFI.isAliasedObjectIndex(i)); |
| CopyObjectProperties(DstMFI, SrcMFI, i); |
| |
| (void)NewFI; |
| assert(i == NewFI && "expected to keep stable frame index numbering"); |
| } |
| |
| for (unsigned I = 0, E = SrcMFI.getLocalFrameObjectCount(); I < E; ++I) { |
| auto LocalObject = SrcMFI.getLocalFrameObjectMap(I); |
| DstMFI.mapLocalFrameObject(LocalObject.first, LocalObject.second); |
| } |
| |
| DstMFI.setCalleeSavedInfo(SrcMFI.getCalleeSavedInfo()); |
| |
| if (SrcMFI.hasStackProtectorIndex()) { |
| DstMFI.setStackProtectorIndex(SrcMFI.getStackProtectorIndex()); |
| } |
| |
| // FIXME: Needs test, missing MIR serialization. |
| if (SrcMFI.hasFunctionContextIndex()) { |
| DstMFI.setFunctionContextIndex(SrcMFI.getFunctionContextIndex()); |
| } |
| } |
| |
| static void cloneMemOperands(MachineInstr &DstMI, MachineInstr &SrcMI, |
| MachineFunction &SrcMF, MachineFunction &DstMF) { |
| // The new MachineMemOperands should be owned by the new function's |
| // Allocator. |
| PseudoSourceValueManager &PSVMgr = DstMF.getPSVManager(); |
| |
| // We also need to remap the PseudoSourceValues from the new function's |
| // PseudoSourceValueManager. |
| SmallVector<MachineMemOperand *, 2> NewMMOs; |
| for (MachineMemOperand *OldMMO : SrcMI.memoperands()) { |
| MachinePointerInfo NewPtrInfo(OldMMO->getPointerInfo()); |
| if (const PseudoSourceValue *PSV = |
| dyn_cast_if_present<const PseudoSourceValue *>(NewPtrInfo.V)) { |
| switch (PSV->kind()) { |
| case PseudoSourceValue::Stack: |
| NewPtrInfo.V = PSVMgr.getStack(); |
| break; |
| case PseudoSourceValue::GOT: |
| NewPtrInfo.V = PSVMgr.getGOT(); |
| break; |
| case PseudoSourceValue::JumpTable: |
| NewPtrInfo.V = PSVMgr.getJumpTable(); |
| break; |
| case PseudoSourceValue::ConstantPool: |
| NewPtrInfo.V = PSVMgr.getConstantPool(); |
| break; |
| case PseudoSourceValue::FixedStack: |
| NewPtrInfo.V = PSVMgr.getFixedStack( |
| cast<FixedStackPseudoSourceValue>(PSV)->getFrameIndex()); |
| break; |
| case PseudoSourceValue::GlobalValueCallEntry: |
| NewPtrInfo.V = PSVMgr.getGlobalValueCallEntry( |
| cast<GlobalValuePseudoSourceValue>(PSV)->getValue()); |
| break; |
| case PseudoSourceValue::ExternalSymbolCallEntry: |
| NewPtrInfo.V = PSVMgr.getExternalSymbolCallEntry( |
| cast<ExternalSymbolPseudoSourceValue>(PSV)->getSymbol()); |
| break; |
| case PseudoSourceValue::TargetCustom: |
| default: |
| // FIXME: We have no generic interface for allocating custom PSVs. |
| report_fatal_error("Cloning TargetCustom PSV not handled"); |
| } |
| } |
| |
| MachineMemOperand *NewMMO = DstMF.getMachineMemOperand( |
| NewPtrInfo, OldMMO->getFlags(), OldMMO->getMemoryType(), |
| OldMMO->getBaseAlign(), OldMMO->getAAInfo(), OldMMO->getRanges(), |
| OldMMO->getSyncScopeID(), OldMMO->getSuccessOrdering(), |
| OldMMO->getFailureOrdering()); |
| NewMMOs.push_back(NewMMO); |
| } |
| |
| DstMI.setMemRefs(DstMF, NewMMOs); |
| } |
| |
| static std::unique_ptr<MachineFunction> cloneMF(MachineFunction *SrcMF, |
| MachineModuleInfo &DestMMI) { |
| auto DstMF = std::make_unique<MachineFunction>( |
| SrcMF->getFunction(), SrcMF->getTarget(), SrcMF->getSubtarget(), |
| SrcMF->getFunctionNumber(), DestMMI); |
| DenseMap<MachineBasicBlock *, MachineBasicBlock *> Src2DstMBB; |
| |
| auto *SrcMRI = &SrcMF->getRegInfo(); |
| auto *DstMRI = &DstMF->getRegInfo(); |
| |
| // Clone blocks. |
| for (MachineBasicBlock &SrcMBB : *SrcMF) { |
| MachineBasicBlock *DstMBB = |
| DstMF->CreateMachineBasicBlock(SrcMBB.getBasicBlock()); |
| Src2DstMBB[&SrcMBB] = DstMBB; |
| |
| if (SrcMBB.isIRBlockAddressTaken()) |
| DstMBB->setAddressTakenIRBlock(SrcMBB.getAddressTakenIRBlock()); |
| if (SrcMBB.isMachineBlockAddressTaken()) |
| DstMBB->setMachineBlockAddressTaken(); |
| |
| // FIXME: This is not serialized |
| if (SrcMBB.hasLabelMustBeEmitted()) |
| DstMBB->setLabelMustBeEmitted(); |
| |
| DstMBB->setAlignment(SrcMBB.getAlignment()); |
| |
| // FIXME: This is not serialized |
| DstMBB->setMaxBytesForAlignment(SrcMBB.getMaxBytesForAlignment()); |
| |
| DstMBB->setIsEHPad(SrcMBB.isEHPad()); |
| DstMBB->setIsEHScopeEntry(SrcMBB.isEHScopeEntry()); |
| DstMBB->setIsEHCatchretTarget(SrcMBB.isEHCatchretTarget()); |
| DstMBB->setIsEHFuncletEntry(SrcMBB.isEHFuncletEntry()); |
| |
| // FIXME: These are not serialized |
| DstMBB->setIsCleanupFuncletEntry(SrcMBB.isCleanupFuncletEntry()); |
| DstMBB->setIsBeginSection(SrcMBB.isBeginSection()); |
| DstMBB->setIsEndSection(SrcMBB.isEndSection()); |
| |
| DstMBB->setSectionID(SrcMBB.getSectionID()); |
| DstMBB->setIsInlineAsmBrIndirectTarget( |
| SrcMBB.isInlineAsmBrIndirectTarget()); |
| |
| // FIXME: This is not serialized |
| if (std::optional<uint64_t> Weight = SrcMBB.getIrrLoopHeaderWeight()) |
| DstMBB->setIrrLoopHeaderWeight(*Weight); |
| } |
| |
| const MachineFrameInfo &SrcMFI = SrcMF->getFrameInfo(); |
| MachineFrameInfo &DstMFI = DstMF->getFrameInfo(); |
| |
| // Copy stack objects and other info |
| cloneFrameInfo(DstMFI, SrcMFI, Src2DstMBB); |
| |
| // Remap the debug info frame index references. |
| DstMF->VariableDbgInfos = SrcMF->VariableDbgInfos; |
| |
| // Clone virtual registers |
| for (unsigned I = 0, E = SrcMRI->getNumVirtRegs(); I != E; ++I) { |
| Register Reg = Register::index2VirtReg(I); |
| Register NewReg = DstMRI->createIncompleteVirtualRegister( |
| SrcMRI->getVRegName(Reg)); |
| assert(NewReg == Reg && "expected to preserve virtreg number"); |
| |
| DstMRI->setRegClassOrRegBank(NewReg, SrcMRI->getRegClassOrRegBank(Reg)); |
| |
| LLT RegTy = SrcMRI->getType(Reg); |
| if (RegTy.isValid()) |
| DstMRI->setType(NewReg, RegTy); |
| |
| // Copy register allocation hints. |
| const auto &Hints = SrcMRI->getRegAllocationHints(Reg); |
| for (Register PrefReg : Hints.second) |
| DstMRI->addRegAllocationHint(NewReg, PrefReg); |
| } |
| |
| const TargetSubtargetInfo &STI = DstMF->getSubtarget(); |
| const TargetInstrInfo *TII = STI.getInstrInfo(); |
| const TargetRegisterInfo *TRI = STI.getRegisterInfo(); |
| |
| // Link blocks. |
| for (auto &SrcMBB : *SrcMF) { |
| auto *DstMBB = Src2DstMBB[&SrcMBB]; |
| DstMF->push_back(DstMBB); |
| |
| for (auto It = SrcMBB.succ_begin(), IterEnd = SrcMBB.succ_end(); |
| It != IterEnd; ++It) { |
| auto *SrcSuccMBB = *It; |
| auto *DstSuccMBB = Src2DstMBB[SrcSuccMBB]; |
| DstMBB->addSuccessor(DstSuccMBB, SrcMBB.getSuccProbability(It)); |
| } |
| |
| for (auto &LI : SrcMBB.liveins_dbg()) |
| DstMBB->addLiveIn(LI); |
| |
| // Make sure MRI knows about registers clobbered by unwinder. |
| if (DstMBB->isEHPad()) { |
| if (auto *RegMask = TRI->getCustomEHPadPreservedMask(*DstMF)) |
| DstMRI->addPhysRegsUsedFromRegMask(RegMask); |
| } |
| } |
| |
| DenseSet<const uint32_t *> ConstRegisterMasks; |
| |
| // Track predefined/named regmasks which we ignore. |
| for (const uint32_t *Mask : TRI->getRegMasks()) |
| ConstRegisterMasks.insert(Mask); |
| |
| // Clone instructions. |
| for (auto &SrcMBB : *SrcMF) { |
| auto *DstMBB = Src2DstMBB[&SrcMBB]; |
| for (auto &SrcMI : SrcMBB) { |
| const auto &MCID = TII->get(SrcMI.getOpcode()); |
| auto *DstMI = DstMF->CreateMachineInstr(MCID, SrcMI.getDebugLoc(), |
| /*NoImplicit=*/true); |
| DstMI->setFlags(SrcMI.getFlags()); |
| DstMI->setAsmPrinterFlag(SrcMI.getAsmPrinterFlags()); |
| |
| DstMBB->push_back(DstMI); |
| for (auto &SrcMO : SrcMI.operands()) { |
| MachineOperand DstMO(SrcMO); |
| DstMO.clearParent(); |
| |
| // Update MBB. |
| if (DstMO.isMBB()) |
| DstMO.setMBB(Src2DstMBB[DstMO.getMBB()]); |
| else if (DstMO.isRegMask()) { |
| DstMRI->addPhysRegsUsedFromRegMask(DstMO.getRegMask()); |
| |
| if (!ConstRegisterMasks.count(DstMO.getRegMask())) { |
| uint32_t *DstMask = DstMF->allocateRegMask(); |
| std::memcpy(DstMask, SrcMO.getRegMask(), |
| sizeof(*DstMask) * |
| MachineOperand::getRegMaskSize(TRI->getNumRegs())); |
| DstMO.setRegMask(DstMask); |
| } |
| } |
| |
| DstMI->addOperand(DstMO); |
| } |
| |
| cloneMemOperands(*DstMI, SrcMI, *SrcMF, *DstMF); |
| } |
| } |
| |
| DstMF->setAlignment(SrcMF->getAlignment()); |
| DstMF->setExposesReturnsTwice(SrcMF->exposesReturnsTwice()); |
| DstMF->setHasInlineAsm(SrcMF->hasInlineAsm()); |
| DstMF->setHasWinCFI(SrcMF->hasWinCFI()); |
| |
| DstMF->getProperties().reset().set(SrcMF->getProperties()); |
| |
| if (!SrcMF->getFrameInstructions().empty() || |
| !SrcMF->getLongjmpTargets().empty() || |
| !SrcMF->getCatchretTargets().empty()) |
| report_fatal_error("cloning not implemented for machine function property"); |
| |
| DstMF->setCallsEHReturn(SrcMF->callsEHReturn()); |
| DstMF->setCallsUnwindInit(SrcMF->callsUnwindInit()); |
| DstMF->setHasEHCatchret(SrcMF->hasEHCatchret()); |
| DstMF->setHasEHScopes(SrcMF->hasEHScopes()); |
| DstMF->setHasEHFunclets(SrcMF->hasEHFunclets()); |
| DstMF->setIsOutlined(SrcMF->isOutlined()); |
| |
| if (!SrcMF->getLandingPads().empty() || |
| !SrcMF->getCodeViewAnnotations().empty() || |
| !SrcMF->getTypeInfos().empty() || |
| !SrcMF->getFilterIds().empty() || |
| SrcMF->hasAnyWasmLandingPadIndex() || |
| SrcMF->hasAnyCallSiteLandingPad() || |
| SrcMF->hasAnyCallSiteLabel() || |
| !SrcMF->getCallSitesInfo().empty()) |
| report_fatal_error("cloning not implemented for machine function property"); |
| |
| DstMF->setDebugInstrNumberingCount(SrcMF->DebugInstrNumberingCount); |
| |
| if (!DstMF->cloneInfoFrom(*SrcMF, Src2DstMBB)) |
| report_fatal_error("target does not implement MachineFunctionInfo cloning"); |
| |
| DstMRI->freezeReservedRegs(*DstMF); |
| |
| DstMF->verify(nullptr, "", /*AbortOnError=*/true); |
| return DstMF; |
| } |
| |
| static void initializeTargetInfo() { |
| InitializeAllTargets(); |
| InitializeAllTargetMCs(); |
| InitializeAllAsmPrinters(); |
| InitializeAllAsmParsers(); |
| } |
| |
| void ReducerWorkItem::print(raw_ostream &ROS, void *p) const { |
| if (MMI) { |
| printMIR(ROS, *M); |
| for (Function &F : *M) { |
| if (auto *MF = MMI->getMachineFunction(F)) |
| printMIR(ROS, *MF); |
| } |
| } else { |
| M->print(ROS, /*AssemblyAnnotationWriter=*/nullptr, |
| /*ShouldPreserveUseListOrder=*/true); |
| } |
| } |
| |
| bool ReducerWorkItem::verify(raw_fd_ostream *OS) const { |
| if (verifyModule(*M, OS)) |
| return true; |
| |
| if (!MMI) |
| return false; |
| |
| for (const Function &F : getModule()) { |
| if (const MachineFunction *MF = MMI->getMachineFunction(F)) { |
| if (!MF->verify(nullptr, "", /*AbortOnError=*/false)) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool ReducerWorkItem::isReduced(const TestRunner &Test) const { |
| const bool UseBitcode = Test.inputIsBitcode() || TmpFilesAsBitcode; |
| |
| SmallString<128> CurrentFilepath; |
| |
| // Write ReducerWorkItem to tmp file |
| int FD; |
| std::error_code EC = sys::fs::createTemporaryFile( |
| "llvm-reduce", isMIR() ? "mir" : (UseBitcode ? "bc" : "ll"), FD, |
| CurrentFilepath, |
| UseBitcode && !isMIR() ? sys::fs::OF_None : sys::fs::OF_Text); |
| if (EC) { |
| WithColor::error(errs(), Test.getToolName()) |
| << "error making unique filename: " << EC.message() << '\n'; |
| exit(1); |
| } |
| |
| ToolOutputFile Out(CurrentFilepath, FD); |
| |
| writeOutput(Out.os(), UseBitcode); |
| |
| Out.os().close(); |
| if (Out.os().has_error()) { |
| WithColor::error(errs(), Test.getToolName()) |
| << "error emitting bitcode to file '" << CurrentFilepath |
| << "': " << Out.os().error().message() << '\n'; |
| exit(1); |
| } |
| |
| // Current Chunks aren't interesting |
| return Test.run(CurrentFilepath); |
| } |
| |
| std::unique_ptr<ReducerWorkItem> |
| ReducerWorkItem::clone(const TargetMachine *TM) const { |
| auto CloneMMM = std::make_unique<ReducerWorkItem>(); |
| if (TM) { |
| // We're assuming the Module IR contents are always unchanged by MIR |
| // reductions, and can share it as a constant. |
| CloneMMM->M = M; |
| |
| // MachineModuleInfo contains a lot of other state used during codegen which |
| // we won't be using here, but we should be able to ignore it (although this |
| // is pretty ugly). |
| const LLVMTargetMachine *LLVMTM = |
| static_cast<const LLVMTargetMachine *>(TM); |
| CloneMMM->MMI = std::make_unique<MachineModuleInfo>(LLVMTM); |
| |
| for (const Function &F : getModule()) { |
| if (auto *MF = MMI->getMachineFunction(F)) |
| CloneMMM->MMI->insertFunction(F, cloneMF(MF, *CloneMMM->MMI)); |
| } |
| } else { |
| CloneMMM->M = CloneModule(*M); |
| } |
| return CloneMMM; |
| } |
| |
| /// Try to produce some number that indicates a function is getting smaller / |
| /// simpler. |
| static uint64_t computeMIRComplexityScoreImpl(const MachineFunction &MF) { |
| uint64_t Score = 0; |
| const MachineFrameInfo &MFI = MF.getFrameInfo(); |
| |
| // Add for stack objects |
| Score += MFI.getNumObjects(); |
| |
| // Add in the block count. |
| Score += 2 * MF.size(); |
| |
| const MachineRegisterInfo &MRI = MF.getRegInfo(); |
| for (unsigned I = 0, E = MRI.getNumVirtRegs(); I != E; ++I) { |
| Register Reg = Register::index2VirtReg(I); |
| Score += MRI.getRegAllocationHints(Reg).second.size(); |
| } |
| |
| for (const MachineBasicBlock &MBB : MF) { |
| for (const MachineInstr &MI : MBB) { |
| const unsigned Opc = MI.getOpcode(); |
| |
| // Reductions may want or need to introduce implicit_defs, so don't count |
| // them. |
| // TODO: These probably should count in some way. |
| if (Opc == TargetOpcode::IMPLICIT_DEF || |
| Opc == TargetOpcode::G_IMPLICIT_DEF) |
| continue; |
| |
| // Each instruction adds to the score |
| Score += 4; |
| |
| if (Opc == TargetOpcode::PHI || Opc == TargetOpcode::G_PHI || |
| Opc == TargetOpcode::INLINEASM || Opc == TargetOpcode::INLINEASM_BR) |
| ++Score; |
| |
| if (MI.getFlags() != 0) |
| ++Score; |
| |
| // Increase weight for more operands. |
| for (const MachineOperand &MO : MI.operands()) { |
| ++Score; |
| |
| // Treat registers as more complex. |
| if (MO.isReg()) { |
| ++Score; |
| |
| // And subregisters as even more complex. |
| if (MO.getSubReg()) { |
| ++Score; |
| if (MO.isDef()) |
| ++Score; |
| } |
| } else if (MO.isRegMask()) |
| ++Score; |
| } |
| } |
| } |
| |
| return Score; |
| } |
| |
| uint64_t ReducerWorkItem::computeMIRComplexityScore() const { |
| uint64_t Score = 0; |
| |
| for (const Function &F : getModule()) { |
| if (auto *MF = MMI->getMachineFunction(F)) |
| Score += computeMIRComplexityScoreImpl(*MF); |
| } |
| |
| return Score; |
| } |
| |
| // FIXME: ReduceOperandsSkip has similar function, except it uses larger numbers |
| // for more reduced. |
| static unsigned classifyReductivePower(const Value *V) { |
| if (auto *C = dyn_cast<ConstantData>(V)) { |
| if (C->isNullValue()) |
| return 0; |
| if (C->isOneValue()) |
| return 1; |
| if (isa<UndefValue>(V)) |
| return 2; |
| return 3; |
| } |
| |
| if (isa<GlobalValue>(V)) |
| return 4; |
| |
| // TODO: Account for expression size |
| if (isa<ConstantExpr>(V)) |
| return 5; |
| |
| if (isa<Constant>(V)) |
| return 1; |
| |
| if (isa<Argument>(V)) |
| return 6; |
| |
| if (isa<Instruction>(V)) |
| return 7; |
| |
| return 0; |
| } |
| |
| // TODO: Additional flags and attributes may be complexity reducing. If we start |
| // adding flags and attributes, they could have negative cost. |
| static uint64_t computeIRComplexityScoreImpl(const Function &F) { |
| uint64_t Score = 1; // Count the function itself |
| SmallVector<std::pair<unsigned, MDNode *>> MDs; |
| |
| AttributeList Attrs = F.getAttributes(); |
| for (AttributeSet AttrSet : Attrs) |
| Score += AttrSet.getNumAttributes(); |
| |
| for (const BasicBlock &BB : F) { |
| ++Score; |
| |
| for (const Instruction &I : BB) { |
| ++Score; |
| |
| if (const auto *OverflowOp = dyn_cast<OverflowingBinaryOperator>(&I)) { |
| if (OverflowOp->hasNoUnsignedWrap()) |
| ++Score; |
| if (OverflowOp->hasNoSignedWrap()) |
| ++Score; |
| } else if (const auto *GEP = dyn_cast<GEPOperator>(&I)) { |
| if (GEP->isInBounds()) |
| ++Score; |
| } else if (const auto *ExactOp = dyn_cast<PossiblyExactOperator>(&I)) { |
| if (ExactOp->isExact()) |
| ++Score; |
| } else if (const auto *FPOp = dyn_cast<FPMathOperator>(&I)) { |
| FastMathFlags FMF = FPOp->getFastMathFlags(); |
| if (FMF.allowReassoc()) |
| ++Score; |
| if (FMF.noNaNs()) |
| ++Score; |
| if (FMF.noInfs()) |
| ++Score; |
| if (FMF.noSignedZeros()) |
| ++Score; |
| if (FMF.allowReciprocal()) |
| ++Score; |
| if (FMF.allowContract()) |
| ++Score; |
| if (FMF.approxFunc()) |
| ++Score; |
| } |
| |
| for (const Value *Operand : I.operands()) { |
| ++Score; |
| Score += classifyReductivePower(Operand); |
| } |
| |
| I.getAllMetadata(MDs); |
| Score += MDs.size(); |
| MDs.clear(); |
| } |
| } |
| |
| return Score; |
| } |
| |
| uint64_t ReducerWorkItem::computeIRComplexityScore() const { |
| uint64_t Score = 0; |
| |
| const Module &M = getModule(); |
| Score += M.named_metadata_size(); |
| |
| SmallVector<std::pair<unsigned, MDNode *>, 32> GlobalMetadata; |
| for (const GlobalVariable &GV : M.globals()) { |
| ++Score; |
| |
| if (GV.hasInitializer()) |
| Score += classifyReductivePower(GV.getInitializer()); |
| |
| // TODO: Account for linkage? |
| |
| GV.getAllMetadata(GlobalMetadata); |
| Score += GlobalMetadata.size(); |
| GlobalMetadata.clear(); |
| } |
| |
| for (const GlobalAlias &GA : M.aliases()) |
| Score += classifyReductivePower(GA.getAliasee()); |
| |
| for (const GlobalIFunc &GI : M.ifuncs()) |
| Score += classifyReductivePower(GI.getResolver()); |
| |
| for (const Function &F : M) |
| Score += computeIRComplexityScoreImpl(F); |
| |
| return Score; |
| } |
| |
| void ReducerWorkItem::writeOutput(raw_ostream &OS, bool EmitBitcode) const { |
| // Requesting bitcode emission with mir is nonsense, so just ignore it. |
| if (EmitBitcode && !isMIR()) |
| writeBitcode(OS); |
| else |
| print(OS, /*AnnotationWriter=*/nullptr); |
| } |
| |
| void ReducerWorkItem::readBitcode(MemoryBufferRef Data, LLVMContext &Ctx, |
| StringRef ToolName) { |
| Expected<BitcodeFileContents> IF = llvm::getBitcodeFileContents(Data); |
| if (!IF) { |
| WithColor::error(errs(), ToolName) << IF.takeError(); |
| exit(1); |
| } |
| BitcodeModule BM = IF->Mods[0]; |
| Expected<BitcodeLTOInfo> LI = BM.getLTOInfo(); |
| Expected<std::unique_ptr<Module>> MOrErr = BM.parseModule(Ctx); |
| if (!LI || !MOrErr) { |
| WithColor::error(errs(), ToolName) << IF.takeError(); |
| exit(1); |
| } |
| LTOInfo = std::make_unique<BitcodeLTOInfo>(*LI); |
| M = std::move(MOrErr.get()); |
| } |
| |
| void ReducerWorkItem::writeBitcode(raw_ostream &OutStream) const { |
| if (LTOInfo && LTOInfo->IsThinLTO && LTOInfo->EnableSplitLTOUnit) { |
| PassBuilder PB; |
| LoopAnalysisManager LAM; |
| FunctionAnalysisManager FAM; |
| CGSCCAnalysisManager CGAM; |
| ModuleAnalysisManager MAM; |
| PB.registerModuleAnalyses(MAM); |
| PB.registerCGSCCAnalyses(CGAM); |
| PB.registerFunctionAnalyses(FAM); |
| PB.registerLoopAnalyses(LAM); |
| PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); |
| ModulePassManager MPM; |
| MPM.addPass(ThinLTOBitcodeWriterPass(OutStream, nullptr)); |
| MPM.run(*M, MAM); |
| } else { |
| std::unique_ptr<ModuleSummaryIndex> Index; |
| if (LTOInfo && LTOInfo->HasSummary) { |
| ProfileSummaryInfo PSI(*M); |
| Index = std::make_unique<ModuleSummaryIndex>( |
| buildModuleSummaryIndex(*M, nullptr, &PSI)); |
| } |
| WriteBitcodeToFile(getModule(), OutStream, Index.get()); |
| } |
| } |
| |
| std::pair<std::unique_ptr<ReducerWorkItem>, bool> |
| llvm::parseReducerWorkItem(StringRef ToolName, StringRef Filename, |
| LLVMContext &Ctxt, |
| std::unique_ptr<TargetMachine> &TM, bool IsMIR) { |
| bool IsBitcode = false; |
| Triple TheTriple; |
| |
| auto MMM = std::make_unique<ReducerWorkItem>(); |
| |
| if (IsMIR) { |
| initializeTargetInfo(); |
| |
| auto FileOrErr = MemoryBuffer::getFileOrSTDIN(Filename, /*IsText=*/true); |
| if (std::error_code EC = FileOrErr.getError()) { |
| WithColor::error(errs(), ToolName) << EC.message() << '\n'; |
| return {nullptr, false}; |
| } |
| |
| std::unique_ptr<MIRParser> MParser = |
| createMIRParser(std::move(FileOrErr.get()), Ctxt); |
| |
| auto SetDataLayout = [&](StringRef DataLayoutTargetTriple, |
| StringRef OldDLStr) -> std::optional<std::string> { |
| // If we are supposed to override the target triple, do so now. |
| std::string IRTargetTriple = DataLayoutTargetTriple.str(); |
| if (!TargetTriple.empty()) |
| IRTargetTriple = Triple::normalize(TargetTriple); |
| TheTriple = Triple(IRTargetTriple); |
| if (TheTriple.getTriple().empty()) |
| TheTriple.setTriple(sys::getDefaultTargetTriple()); |
| |
| std::string Error; |
| const Target *TheTarget = |
| TargetRegistry::lookupTarget(codegen::getMArch(), TheTriple, Error); |
| if (!TheTarget) { |
| WithColor::error(errs(), ToolName) << Error; |
| exit(1); |
| } |
| |
| // Hopefully the MIR parsing doesn't depend on any options. |
| TargetOptions Options; |
| std::optional<Reloc::Model> RM = codegen::getExplicitRelocModel(); |
| std::string CPUStr = codegen::getCPUStr(); |
| std::string FeaturesStr = codegen::getFeaturesStr(); |
| TM = std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine( |
| TheTriple.getTriple(), CPUStr, FeaturesStr, Options, RM, |
| codegen::getExplicitCodeModel(), CodeGenOpt::Default)); |
| assert(TM && "Could not allocate target machine!"); |
| |
| return TM->createDataLayout().getStringRepresentation(); |
| }; |
| |
| std::unique_ptr<Module> M = MParser->parseIRModule(SetDataLayout); |
| LLVMTargetMachine *LLVMTM = static_cast<LLVMTargetMachine *>(TM.get()); |
| |
| MMM->MMI = std::make_unique<MachineModuleInfo>(LLVMTM); |
| MParser->parseMachineFunctions(*M, *MMM->MMI); |
| MMM->M = std::move(M); |
| } else { |
| SMDiagnostic Err; |
| ErrorOr<std::unique_ptr<MemoryBuffer>> MB = |
| MemoryBuffer::getFileOrSTDIN(Filename); |
| if (std::error_code EC = MB.getError()) { |
| WithColor::error(errs(), ToolName) |
| << Filename << ": " << EC.message() << "\n"; |
| return {nullptr, false}; |
| } |
| |
| if (!isBitcode((const unsigned char *)(*MB)->getBufferStart(), |
| (const unsigned char *)(*MB)->getBufferEnd())) { |
| std::unique_ptr<Module> Result = parseIR(**MB, Err, Ctxt); |
| if (!Result) { |
| Err.print(ToolName.data(), errs()); |
| return {nullptr, false}; |
| } |
| MMM->M = std::move(Result); |
| } else { |
| IsBitcode = true; |
| MMM->readBitcode(MemoryBufferRef(**MB), Ctxt, ToolName); |
| |
| if (MMM->LTOInfo->IsThinLTO && MMM->LTOInfo->EnableSplitLTOUnit) |
| initializeTargetInfo(); |
| } |
| } |
| if (MMM->verify(&errs())) { |
| WithColor::error(errs(), ToolName) |
| << Filename << " - input module is broken!\n"; |
| return {nullptr, false}; |
| } |
| return {std::move(MMM), IsBitcode}; |
| } |