Path: blob/main/contrib/llvm-project/llvm/lib/Target/X86/X86InsertPrefetch.cpp
35266 views
//===------- X86InsertPrefetch.cpp - Insert cache prefetch hints ----------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This pass applies cache prefetch instructions based on a profile. The pass9// assumes DiscriminateMemOps ran immediately before, to ensure debug info10// matches the one used at profile generation time. The profile is encoded in11// afdo format (text or binary). It contains prefetch hints recommendations.12// Each recommendation is made in terms of debug info locations, a type (i.e.13// nta, t{0|1|2}) and a delta. The debug info identifies an instruction with a14// memory operand (see X86DiscriminateMemOps). The prefetch will be made for15// a location at that memory operand + the delta specified in the16// recommendation.17//18//===----------------------------------------------------------------------===//1920#include "X86.h"21#include "X86InstrBuilder.h"22#include "X86InstrInfo.h"23#include "X86MachineFunctionInfo.h"24#include "X86Subtarget.h"25#include "llvm/CodeGen/MachineFunctionPass.h"26#include "llvm/CodeGen/MachineModuleInfo.h"27#include "llvm/IR/DebugInfoMetadata.h"28#include "llvm/IR/Module.h"29#include "llvm/ProfileData/SampleProf.h"30#include "llvm/ProfileData/SampleProfReader.h"31#include "llvm/Support/VirtualFileSystem.h"32#include "llvm/Transforms/IPO/SampleProfile.h"33using namespace llvm;34using namespace sampleprof;3536static cl::opt<std::string>37PrefetchHintsFile("prefetch-hints-file",38cl::desc("Path to the prefetch hints profile. See also "39"-x86-discriminate-memops"),40cl::Hidden);41namespace {4243class X86InsertPrefetch : public MachineFunctionPass {44void getAnalysisUsage(AnalysisUsage &AU) const override;45bool doInitialization(Module &) override;4647bool runOnMachineFunction(MachineFunction &MF) override;48struct PrefetchInfo {49unsigned InstructionID;50int64_t Delta;51};52typedef SmallVectorImpl<PrefetchInfo> Prefetches;53bool findPrefetchInfo(const FunctionSamples *Samples, const MachineInstr &MI,54Prefetches &prefetches) const;5556public:57static char ID;58X86InsertPrefetch(const std::string &PrefetchHintsFilename);59StringRef getPassName() const override {60return "X86 Insert Cache Prefetches";61}6263private:64std::string Filename;65std::unique_ptr<SampleProfileReader> Reader;66};6768using PrefetchHints = SampleRecord::CallTargetMap;6970// Return any prefetching hints for the specified MachineInstruction. The hints71// are returned as pairs (name, delta).72ErrorOr<const PrefetchHints &>73getPrefetchHints(const FunctionSamples *TopSamples, const MachineInstr &MI) {74if (const auto &Loc = MI.getDebugLoc())75if (const auto *Samples = TopSamples->findFunctionSamples(Loc))76return Samples->findCallTargetMapAt(FunctionSamples::getOffset(Loc),77Loc->getBaseDiscriminator());78return std::error_code();79}8081// The prefetch instruction can't take memory operands involving vector82// registers.83bool IsMemOpCompatibleWithPrefetch(const MachineInstr &MI, int Op) {84Register BaseReg = MI.getOperand(Op + X86::AddrBaseReg).getReg();85Register IndexReg = MI.getOperand(Op + X86::AddrIndexReg).getReg();86return (BaseReg == 0 ||87X86MCRegisterClasses[X86::GR64RegClassID].contains(BaseReg) ||88X86MCRegisterClasses[X86::GR32RegClassID].contains(BaseReg)) &&89(IndexReg == 0 ||90X86MCRegisterClasses[X86::GR64RegClassID].contains(IndexReg) ||91X86MCRegisterClasses[X86::GR32RegClassID].contains(IndexReg));92}9394} // end anonymous namespace9596//===----------------------------------------------------------------------===//97// Implementation98//===----------------------------------------------------------------------===//99100char X86InsertPrefetch::ID = 0;101102X86InsertPrefetch::X86InsertPrefetch(const std::string &PrefetchHintsFilename)103: MachineFunctionPass(ID), Filename(PrefetchHintsFilename) {}104105/// Return true if the provided MachineInstruction has cache prefetch hints. In106/// that case, the prefetch hints are stored, in order, in the Prefetches107/// vector.108bool X86InsertPrefetch::findPrefetchInfo(const FunctionSamples *TopSamples,109const MachineInstr &MI,110Prefetches &Prefetches) const {111assert(Prefetches.empty() &&112"Expected caller passed empty PrefetchInfo vector.");113114// There is no point to match prefetch hints if the profile is using MD5.115if (FunctionSamples::UseMD5)116return false;117118static constexpr std::pair<StringLiteral, unsigned> HintTypes[] = {119{"_nta_", X86::PREFETCHNTA},120{"_t0_", X86::PREFETCHT0},121{"_t1_", X86::PREFETCHT1},122{"_t2_", X86::PREFETCHT2},123};124static const char *SerializedPrefetchPrefix = "__prefetch";125126auto T = getPrefetchHints(TopSamples, MI);127if (!T)128return false;129int16_t max_index = -1;130// Convert serialized prefetch hints into PrefetchInfo objects, and populate131// the Prefetches vector.132for (const auto &S_V : *T) {133StringRef Name = S_V.first.stringRef();134if (Name.consume_front(SerializedPrefetchPrefix)) {135int64_t D = static_cast<int64_t>(S_V.second);136unsigned IID = 0;137for (const auto &HintType : HintTypes) {138if (Name.consume_front(HintType.first)) {139IID = HintType.second;140break;141}142}143if (IID == 0)144return false;145uint8_t index = 0;146Name.consumeInteger(10, index);147148if (index >= Prefetches.size())149Prefetches.resize(index + 1);150Prefetches[index] = {IID, D};151max_index = std::max(max_index, static_cast<int16_t>(index));152}153}154assert(max_index + 1 >= 0 &&155"Possible overflow: max_index + 1 should be positive.");156assert(static_cast<size_t>(max_index + 1) == Prefetches.size() &&157"The number of prefetch hints received should match the number of "158"PrefetchInfo objects returned");159return !Prefetches.empty();160}161162bool X86InsertPrefetch::doInitialization(Module &M) {163if (Filename.empty())164return false;165166LLVMContext &Ctx = M.getContext();167// TODO: Propagate virtual file system into LLVM targets.168auto FS = vfs::getRealFileSystem();169ErrorOr<std::unique_ptr<SampleProfileReader>> ReaderOrErr =170SampleProfileReader::create(Filename, Ctx, *FS);171if (std::error_code EC = ReaderOrErr.getError()) {172std::string Msg = "Could not open profile: " + EC.message();173Ctx.diagnose(DiagnosticInfoSampleProfile(Filename, Msg,174DiagnosticSeverity::DS_Warning));175return false;176}177Reader = std::move(ReaderOrErr.get());178Reader->read();179return true;180}181182void X86InsertPrefetch::getAnalysisUsage(AnalysisUsage &AU) const {183AU.setPreservesAll();184MachineFunctionPass::getAnalysisUsage(AU);185}186187bool X86InsertPrefetch::runOnMachineFunction(MachineFunction &MF) {188if (!Reader)189return false;190const FunctionSamples *Samples = Reader->getSamplesFor(MF.getFunction());191if (!Samples)192return false;193194bool Changed = false;195196const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();197SmallVector<PrefetchInfo, 4> Prefetches;198for (auto &MBB : MF) {199for (auto MI = MBB.instr_begin(); MI != MBB.instr_end();) {200auto Current = MI;201++MI;202203int Offset = X86II::getMemoryOperandNo(Current->getDesc().TSFlags);204if (Offset < 0)205continue;206unsigned Bias = X86II::getOperandBias(Current->getDesc());207int MemOpOffset = Offset + Bias;208// FIXME(mtrofin): ORE message when the recommendation cannot be taken.209if (!IsMemOpCompatibleWithPrefetch(*Current, MemOpOffset))210continue;211Prefetches.clear();212if (!findPrefetchInfo(Samples, *Current, Prefetches))213continue;214assert(!Prefetches.empty() &&215"The Prefetches vector should contain at least a value if "216"findPrefetchInfo returned true.");217for (auto &PrefInfo : Prefetches) {218unsigned PFetchInstrID = PrefInfo.InstructionID;219int64_t Delta = PrefInfo.Delta;220const MCInstrDesc &Desc = TII->get(PFetchInstrID);221MachineInstr *PFetch =222MF.CreateMachineInstr(Desc, Current->getDebugLoc(), true);223MachineInstrBuilder MIB(MF, PFetch);224225static_assert(X86::AddrBaseReg == 0 && X86::AddrScaleAmt == 1 &&226X86::AddrIndexReg == 2 && X86::AddrDisp == 3 &&227X86::AddrSegmentReg == 4,228"Unexpected change in X86 operand offset order.");229230// This assumes X86::AddBaseReg = 0, {...}ScaleAmt = 1, etc.231// FIXME(mtrofin): consider adding a:232// MachineInstrBuilder::set(unsigned offset, op).233MIB.addReg(Current->getOperand(MemOpOffset + X86::AddrBaseReg).getReg())234.addImm(235Current->getOperand(MemOpOffset + X86::AddrScaleAmt).getImm())236.addReg(237Current->getOperand(MemOpOffset + X86::AddrIndexReg).getReg())238.addImm(Current->getOperand(MemOpOffset + X86::AddrDisp).getImm() +239Delta)240.addReg(Current->getOperand(MemOpOffset + X86::AddrSegmentReg)241.getReg());242243if (!Current->memoperands_empty()) {244MachineMemOperand *CurrentOp = *(Current->memoperands_begin());245MIB.addMemOperand(MF.getMachineMemOperand(246CurrentOp, CurrentOp->getOffset() + Delta, CurrentOp->getSize()));247}248249// Insert before Current. This is because Current may clobber some of250// the registers used to describe the input memory operand.251MBB.insert(Current, PFetch);252Changed = true;253}254}255}256return Changed;257}258259FunctionPass *llvm::createX86InsertPrefetchPass() {260return new X86InsertPrefetch(PrefetchHintsFile);261}262263264