|  | //===------- X86InsertPrefetch.cpp - Insert cache prefetch hints ----------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // This pass applies cache prefetch instructions based on a profile. The pass | 
|  | // assumes DiscriminateMemOps ran immediately before, to ensure debug info | 
|  | // matches the one used at profile generation time. The profile is encoded in | 
|  | // afdo format (text or binary). It contains prefetch hints recommendations. | 
|  | // Each recommendation is made in terms of debug info locations, a type (i.e. | 
|  | // nta, t{0|1|2}) and a delta. The debug info identifies an instruction with a | 
|  | // memory operand (see X86DiscriminateMemOps). The prefetch will be made for | 
|  | // a location at that memory operand + the delta specified in the | 
|  | // recommendation. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "X86.h" | 
|  | #include "X86InstrBuilder.h" | 
|  | #include "X86InstrInfo.h" | 
|  | #include "X86MachineFunctionInfo.h" | 
|  | #include "X86Subtarget.h" | 
|  | #include "llvm/CodeGen/MachineModuleInfo.h" | 
|  | #include "llvm/IR/DebugInfoMetadata.h" | 
|  | #include "llvm/ProfileData/SampleProf.h" | 
|  | #include "llvm/ProfileData/SampleProfReader.h" | 
|  | #include "llvm/Transforms/IPO/SampleProfile.h" | 
|  | using namespace llvm; | 
|  | using namespace sampleprof; | 
|  |  | 
|  | static cl::opt<std::string> | 
|  | PrefetchHintsFile("prefetch-hints-file", | 
|  | cl::desc("Path to the prefetch hints profile. See also " | 
|  | "-x86-discriminate-memops"), | 
|  | cl::Hidden); | 
|  | namespace { | 
|  |  | 
|  | class X86InsertPrefetch : public MachineFunctionPass { | 
|  | void getAnalysisUsage(AnalysisUsage &AU) const override; | 
|  | bool doInitialization(Module &) override; | 
|  |  | 
|  | bool runOnMachineFunction(MachineFunction &MF) override; | 
|  | struct PrefetchInfo { | 
|  | unsigned InstructionID; | 
|  | int64_t Delta; | 
|  | }; | 
|  | typedef SmallVectorImpl<PrefetchInfo> Prefetches; | 
|  | bool findPrefetchInfo(const FunctionSamples *Samples, const MachineInstr &MI, | 
|  | Prefetches &prefetches) const; | 
|  |  | 
|  | public: | 
|  | static char ID; | 
|  | X86InsertPrefetch(const std::string &PrefetchHintsFilename); | 
|  | StringRef getPassName() const override { | 
|  | return "X86 Insert Cache Prefetches"; | 
|  | } | 
|  |  | 
|  | private: | 
|  | std::string Filename; | 
|  | std::unique_ptr<SampleProfileReader> Reader; | 
|  | }; | 
|  |  | 
|  | using PrefetchHints = SampleRecord::CallTargetMap; | 
|  |  | 
|  | // Return any prefetching hints for the specified MachineInstruction. The hints | 
|  | // are returned as pairs (name, delta). | 
|  | ErrorOr<PrefetchHints> getPrefetchHints(const FunctionSamples *TopSamples, | 
|  | const MachineInstr &MI) { | 
|  | if (const auto &Loc = MI.getDebugLoc()) | 
|  | if (const auto *Samples = TopSamples->findFunctionSamples(Loc)) | 
|  | return Samples->findCallTargetMapAt(FunctionSamples::getOffset(Loc), | 
|  | Loc->getBaseDiscriminator()); | 
|  | return std::error_code(); | 
|  | } | 
|  |  | 
|  | // The prefetch instruction can't take memory operands involving vector | 
|  | // registers. | 
|  | bool IsMemOpCompatibleWithPrefetch(const MachineInstr &MI, int Op) { | 
|  | unsigned BaseReg = MI.getOperand(Op + X86::AddrBaseReg).getReg(); | 
|  | unsigned IndexReg = MI.getOperand(Op + X86::AddrIndexReg).getReg(); | 
|  | return (BaseReg == 0 || | 
|  | X86MCRegisterClasses[X86::GR64RegClassID].contains(BaseReg) || | 
|  | X86MCRegisterClasses[X86::GR32RegClassID].contains(BaseReg)) && | 
|  | (IndexReg == 0 || | 
|  | X86MCRegisterClasses[X86::GR64RegClassID].contains(IndexReg) || | 
|  | X86MCRegisterClasses[X86::GR32RegClassID].contains(IndexReg)); | 
|  | } | 
|  |  | 
|  | } // end anonymous namespace | 
|  |  | 
|  | //===----------------------------------------------------------------------===// | 
|  | //            Implementation | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | char X86InsertPrefetch::ID = 0; | 
|  |  | 
|  | X86InsertPrefetch::X86InsertPrefetch(const std::string &PrefetchHintsFilename) | 
|  | : MachineFunctionPass(ID), Filename(PrefetchHintsFilename) {} | 
|  |  | 
|  | /// Return true if the provided MachineInstruction has cache prefetch hints. In | 
|  | /// that case, the prefetch hints are stored, in order, in the Prefetches | 
|  | /// vector. | 
|  | bool X86InsertPrefetch::findPrefetchInfo(const FunctionSamples *TopSamples, | 
|  | const MachineInstr &MI, | 
|  | Prefetches &Prefetches) const { | 
|  | assert(Prefetches.empty() && | 
|  | "Expected caller passed empty PrefetchInfo vector."); | 
|  | static const std::pair<const StringRef, unsigned> HintTypes[] = { | 
|  | {"_nta_", X86::PREFETCHNTA}, | 
|  | {"_t0_", X86::PREFETCHT0}, | 
|  | {"_t1_", X86::PREFETCHT1}, | 
|  | {"_t2_", X86::PREFETCHT2}, | 
|  | }; | 
|  | static const char *SerializedPrefetchPrefix = "__prefetch"; | 
|  |  | 
|  | const ErrorOr<PrefetchHints> T = getPrefetchHints(TopSamples, MI); | 
|  | if (!T) | 
|  | return false; | 
|  | int16_t max_index = -1; | 
|  | // Convert serialized prefetch hints into PrefetchInfo objects, and populate | 
|  | // the Prefetches vector. | 
|  | for (const auto &S_V : *T) { | 
|  | StringRef Name = S_V.getKey(); | 
|  | if (Name.consume_front(SerializedPrefetchPrefix)) { | 
|  | int64_t D = static_cast<int64_t>(S_V.second); | 
|  | unsigned IID = 0; | 
|  | for (const auto &HintType : HintTypes) { | 
|  | if (Name.startswith(HintType.first)) { | 
|  | Name = Name.drop_front(HintType.first.size()); | 
|  | IID = HintType.second; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (IID == 0) | 
|  | return false; | 
|  | uint8_t index = 0; | 
|  | Name.consumeInteger(10, index); | 
|  |  | 
|  | if (index >= Prefetches.size()) | 
|  | Prefetches.resize(index + 1); | 
|  | Prefetches[index] = {IID, D}; | 
|  | max_index = std::max(max_index, static_cast<int16_t>(index)); | 
|  | } | 
|  | } | 
|  | assert(max_index + 1 >= 0 && | 
|  | "Possible overflow: max_index + 1 should be positive."); | 
|  | assert(static_cast<size_t>(max_index + 1) == Prefetches.size() && | 
|  | "The number of prefetch hints received should match the number of " | 
|  | "PrefetchInfo objects returned"); | 
|  | return !Prefetches.empty(); | 
|  | } | 
|  |  | 
|  | bool X86InsertPrefetch::doInitialization(Module &M) { | 
|  | if (Filename.empty()) | 
|  | return false; | 
|  |  | 
|  | LLVMContext &Ctx = M.getContext(); | 
|  | ErrorOr<std::unique_ptr<SampleProfileReader>> ReaderOrErr = | 
|  | SampleProfileReader::create(Filename, Ctx); | 
|  | if (std::error_code EC = ReaderOrErr.getError()) { | 
|  | std::string Msg = "Could not open profile: " + EC.message(); | 
|  | Ctx.diagnose(DiagnosticInfoSampleProfile(Filename, Msg, | 
|  | DiagnosticSeverity::DS_Warning)); | 
|  | return false; | 
|  | } | 
|  | Reader = std::move(ReaderOrErr.get()); | 
|  | Reader->read(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void X86InsertPrefetch::getAnalysisUsage(AnalysisUsage &AU) const { | 
|  | AU.setPreservesAll(); | 
|  | AU.addRequired<MachineModuleInfo>(); | 
|  | } | 
|  |  | 
|  | bool X86InsertPrefetch::runOnMachineFunction(MachineFunction &MF) { | 
|  | if (!Reader) | 
|  | return false; | 
|  | const FunctionSamples *Samples = Reader->getSamplesFor(MF.getFunction()); | 
|  | if (!Samples) | 
|  | return false; | 
|  |  | 
|  | bool Changed = false; | 
|  |  | 
|  | const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); | 
|  | SmallVector<PrefetchInfo, 4> Prefetches; | 
|  | for (auto &MBB : MF) { | 
|  | for (auto MI = MBB.instr_begin(); MI != MBB.instr_end();) { | 
|  | auto Current = MI; | 
|  | ++MI; | 
|  |  | 
|  | int Offset = X86II::getMemoryOperandNo(Current->getDesc().TSFlags); | 
|  | if (Offset < 0) | 
|  | continue; | 
|  | unsigned Bias = X86II::getOperandBias(Current->getDesc()); | 
|  | int MemOpOffset = Offset + Bias; | 
|  | // FIXME(mtrofin): ORE message when the recommendation cannot be taken. | 
|  | if (!IsMemOpCompatibleWithPrefetch(*Current, MemOpOffset)) | 
|  | continue; | 
|  | Prefetches.clear(); | 
|  | if (!findPrefetchInfo(Samples, *Current, Prefetches)) | 
|  | continue; | 
|  | assert(!Prefetches.empty() && | 
|  | "The Prefetches vector should contain at least a value if " | 
|  | "findPrefetchInfo returned true."); | 
|  | for (auto &PrefInfo : Prefetches) { | 
|  | unsigned PFetchInstrID = PrefInfo.InstructionID; | 
|  | int64_t Delta = PrefInfo.Delta; | 
|  | const MCInstrDesc &Desc = TII->get(PFetchInstrID); | 
|  | MachineInstr *PFetch = | 
|  | MF.CreateMachineInstr(Desc, Current->getDebugLoc(), true); | 
|  | MachineInstrBuilder MIB(MF, PFetch); | 
|  |  | 
|  | assert(X86::AddrBaseReg == 0 && X86::AddrScaleAmt == 1 && | 
|  | X86::AddrIndexReg == 2 && X86::AddrDisp == 3 && | 
|  | X86::AddrSegmentReg == 4 && | 
|  | "Unexpected change in X86 operand offset order."); | 
|  |  | 
|  | // This assumes X86::AddBaseReg = 0, {...}ScaleAmt = 1, etc. | 
|  | // FIXME(mtrofin): consider adding a: | 
|  | //     MachineInstrBuilder::set(unsigned offset, op). | 
|  | MIB.addReg(Current->getOperand(MemOpOffset + X86::AddrBaseReg).getReg()) | 
|  | .addImm( | 
|  | Current->getOperand(MemOpOffset + X86::AddrScaleAmt).getImm()) | 
|  | .addReg( | 
|  | Current->getOperand(MemOpOffset + X86::AddrIndexReg).getReg()) | 
|  | .addImm(Current->getOperand(MemOpOffset + X86::AddrDisp).getImm() + | 
|  | Delta) | 
|  | .addReg(Current->getOperand(MemOpOffset + X86::AddrSegmentReg) | 
|  | .getReg()); | 
|  |  | 
|  | if (!Current->memoperands_empty()) { | 
|  | MachineMemOperand *CurrentOp = *(Current->memoperands_begin()); | 
|  | MIB.addMemOperand(MF.getMachineMemOperand( | 
|  | CurrentOp, CurrentOp->getOffset() + Delta, CurrentOp->getSize())); | 
|  | } | 
|  |  | 
|  | // Insert before Current. This is because Current may clobber some of | 
|  | // the registers used to describe the input memory operand. | 
|  | MBB.insert(Current, PFetch); | 
|  | Changed = true; | 
|  | } | 
|  | } | 
|  | } | 
|  | return Changed; | 
|  | } | 
|  |  | 
|  | FunctionPass *llvm::createX86InsertPrefetchPass() { | 
|  | return new X86InsertPrefetch(PrefetchHintsFile); | 
|  | } |