Files
UnrealEngine/Engine/Programs/UnrealInstrumentation/Compiler/CustomMemoryInstrumentation.h
2025-05-18 13:04:45 +08:00

253 lines
9.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#ifndef LLVM_TRANSFORMS_CUSTOMMEMORYINSTRUMENTATION_H
#define LLVM_TRANSFORMS_CUSTOMMEMORYINSTRUMENTATION_H
#include "llvm/IR/PassManager.h"
#include "llvm/Transforms/Instrumentation.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Regex.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/IntrinsicInst.h"
#include <intrin.h>
namespace llvm {
using RMWBinOp = AtomicRMWInst::BinOp;
Type *GetSretType(CallInst *Call);
enum AtomicCallSiteType {
CALL_SITE_LOAD,
CALL_SITE_STORE,
CALL_SITE_EXCHANGE,
CALL_SITE_COMPARE_EXCHANGE,
CALL_SITE_RMW,
};
struct AtomicCallSite {
AtomicCallSite(AtomicCallSiteType Type) : Type(Type) {}
static AtomicCallSite LoadSite(uint32_t PtrOperand,
std::optional<uint32_t> AtomicOrderOperand = std::nullopt) {
AtomicCallSite CallSite(CALL_SITE_LOAD);
CallSite.PtrOperand = PtrOperand;
CallSite.AtomicOrderOperand = AtomicOrderOperand;
return CallSite;
}
static AtomicCallSite StoreSite(uint32_t PtrOperand, uint32_t SizeTypeOperand,
uint32_t StoreValueOperand,
uint32_t AtomicOrderOperand) {
AtomicCallSite CallSite(CALL_SITE_STORE);
CallSite.PtrOperand = PtrOperand;
CallSite.SizeTypeOperand = SizeTypeOperand;
CallSite.StoreValueOperand = StoreValueOperand;
CallSite.AtomicOrderOperand = AtomicOrderOperand;
return CallSite;
}
static AtomicCallSite ExchangeSite(uint32_t PtrOperand,
uint32_t SizeTypeOperand,
uint32_t StoreValueOperand,
uint32_t AtomicOrderOperand) {
AtomicCallSite CallSite(CALL_SITE_EXCHANGE);
CallSite.PtrOperand = PtrOperand;
CallSite.SizeTypeOperand = SizeTypeOperand;
CallSite.StoreValueOperand = StoreValueOperand;
CallSite.AtomicOrderOperand = AtomicOrderOperand;
return CallSite;
}
static AtomicCallSite
CompareExchangeSite(uint32_t PtrOperand, uint32_t SizeTypeOperand,
uint32_t ExpectedOperand, uint32_t StoreValueOperand,
uint32_t SuccessAtomicOrderOperand,
std::optional<uint32_t> FailureAtomicOrderOperand) {
AtomicCallSite CallSite(CALL_SITE_COMPARE_EXCHANGE);
CallSite.PtrOperand = PtrOperand;
CallSite.ExpectedOperand = ExpectedOperand;
CallSite.SizeTypeOperand = SizeTypeOperand;
CallSite.StoreValueOperand = StoreValueOperand;
CallSite.AtomicOrderOperand = SuccessAtomicOrderOperand;
CallSite.FailureAtomicOrderOperand = FailureAtomicOrderOperand
? FailureAtomicOrderOperand
: SuccessAtomicOrderOperand;
return CallSite;
}
static AtomicCallSite RMWSite(RMWBinOp Op, uint32_t PtrOperand,
uint32_t SizeTypeOperand, uint32_t ValueOperand,
uint32_t AtomicOrderOperand,
bool RequiresPointerArithmetic = false) {
AtomicCallSite CallSite(CALL_SITE_RMW);
CallSite.PtrOperand = PtrOperand;
CallSite.RMWOp = Op;
CallSite.SizeTypeOperand = SizeTypeOperand;
CallSite.StoreValueOperand = ValueOperand;
CallSite.AtomicOrderOperand = AtomicOrderOperand;
CallSite.RequiresPointerArithmetic = RequiresPointerArithmetic;
return CallSite;
}
// Returns struct type.
Type *AdjustCallSiteForSret(CallInst *Inst) {
Function *Func = Inst->getCalledFunction();
if (!Func->hasStructRetAttr()) {
return nullptr;
}
SretOperand = Func->getParamStructRetType(0) != nullptr ? 0 : 1;
if (PtrOperand == 0 && SretOperand == 0) {
PtrOperand = 1;
}
if (AtomicOrderOperand)
AtomicOrderOperand = *AtomicOrderOperand + 1;
if (StoreValueOperand)
StoreValueOperand = *StoreValueOperand + 1;
if (ExpectedOperand)
ExpectedOperand = *ExpectedOperand + 1;
if (FailureAtomicOrderOperand)
FailureAtomicOrderOperand = *FailureAtomicOrderOperand + 1;
return GetSretType(Inst);
}
AtomicCallSiteType Type;
RMWBinOp RMWOp = RMWBinOp::BAD_BINOP;
std::optional<uint32_t> SretOperand;
uint32_t PtrOperand = 0;
int32_t SizeTypeOperand = -1; // -1 for return value.
std::optional<uint32_t> AtomicOrderOperand;
// Store, exchange, compare-exchange and RMW specific.
std::optional<uint32_t> StoreValueOperand;
// Compare-exchange specific.
std::optional<uint32_t> ExpectedOperand;
std::optional<uint32_t> FailureAtomicOrderOperand;
// Whether this operation requires pointer arithmetic (e.g. pointer fetch_add).
bool RequiresPointerArithmetic = false;
};
struct CustomMemoryInstrumentationOptions {
StringSet<> IncludedModulesRegexes;
StringSet<> FurtherExcludedModulesRegexes;
StringSet<> ExcludedFunctionNameRegexes;
bool MSVCStandardLibPrepass;
};
class CustomMemoryInstrumentationPass
: public PassInfoMixin<CustomMemoryInstrumentationPass> {
public:
CustomMemoryInstrumentationPass(bool MSVCStandardLibPrepass);
CustomMemoryInstrumentationPass(
const CustomMemoryInstrumentationOptions &Options,
bool MSVCStandardLibPrepass);
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
private:
bool instrumentModule(Module &M);
bool shouldInstrumentAddr(Value* Addr);
bool shouldInstrumentModule(Module &M);
bool shouldInstrumentFunction(Function &F);
bool instrumentMSVCStandardLib(Module &M);
void cacheInstrumentationFunctions(Module &M);
Value *CreateCast(IRBuilder<> &Builder, Value *Val, Type *DesiredType);
bool instrumentFunctionEntry(Function &F);
bool instrumentFunctionExit(Function &F);
FunctionCallee &
getInstrumentFunctionForMSVCAtomicCallSite(uint32_t Size,
AtomicCallSite &CallSite);
bool instrumentMSVCAtomicCallSite(CallInst *Inst, AtomicCallSite CallSite);
bool instrumentStore(StoreInst *Inst, bool SkipNonAtomics);
bool instrumentLoad(LoadInst *Inst, bool SkipNonAtomics);
bool instrumentCompareExchange(AtomicCmpXchgInst *Inst);
bool instrumentRMW(AtomicRMWInst *Inst);
bool instrumentMemoryInst(InstrumentationIRBuilder &Builder,
const DebugLoc &DebugLoc, Value *Ptr, uint32_t Size,
FunctionCallee &InstrumentFunction);
bool instrumentAtomicMemoryInst(InstrumentationIRBuilder &Builder,
Instruction *Inst, Value *Ptr,
Value *ValIfStore, Value *MemoryOrder,
FunctionCallee &InstrumentFunction,
Value *Sret);
bool instrumentAtomicCompareExchangeMemoryInst(
InstrumentationIRBuilder &Builder, Instruction *Inst, Value *Ptr,
Value *Expected, Value *Val, Value *SuccessMemoryOrder,
Value *FailureMemoryOrder, FunctionCallee &InstrumentFunction,
bool ReturnOnlyBool);
bool instrumentMemTransfer(MemTransferInst *Inst);
bool instrumentMemSet(MemSetInst *Inst);
bool instrumentMemoryInstRange(InstrumentationIRBuilder &Builder,
const DebugLoc &DebugLoc, Value *Ptr,
Value *Length,
FunctionCallee &InstrumentFunction);
CustomMemoryInstrumentationOptions Options;
SmallVector<Regex, 10> CachedExcludedFunctionRegexes;
bool MSVCStandardLibPrepass;
Module *CurrentModule = nullptr;
FunctionCallee FuncEntryInstrumentFunction;
FunctionCallee FuncExitInstrumentFunction;
FunctionCallee StoreInstrumentFunction;
FunctionCallee LoadInstrumentFunction;
FunctionCallee StoreVPtrInstrumentFunction;
FunctionCallee LoadVPtrInstrumentFunction;
FunctionCallee StoreRangeInstrumentFunction;
FunctionCallee LoadRangeInstrumentFunction;
static constexpr size_t MAX_ATOMIC_SIZE = 8;
static constexpr size_t NUM_ATOMIC_FUNCS = (MAX_ATOMIC_SIZE == 16) ? 5 : 4;
FunctionCallee AtomicStoreInstrumentFunctions[NUM_ATOMIC_FUNCS];
FunctionCallee AtomicLoadInstrumentFunctions[NUM_ATOMIC_FUNCS];
FunctionCallee AtomicExchangeInstrumentFunctions[NUM_ATOMIC_FUNCS];
FunctionCallee AtomicCompareExchangeInstrumentFunctions[NUM_ATOMIC_FUNCS];
FunctionCallee AtomicRMWInstrumentFunctions[RMWBinOp::LAST_BINOP]
[NUM_ATOMIC_FUNCS];
SmallVector<FunctionCallee *> InstrumentFunctions;
size_t FunctionIndexFromSize(size_t Size) {
switch (Size) {
case 1:
return 0;
case 2:
return 1;
case 4:
return 2;
case 8:
return 3;
}
return (size_t)-1;
}
// Atomic pointer's pointee size cache. Maps a MSVC std::atomic<T*>::fetch_add/fetch_sub function
// to the size of the underlying pointer type, i.e. sizeof(T).
std::unordered_map<Function *, uint64_t> AtomicPointeeSizeCache;
uint64_t CacheOrGetPointeeSizeForMSVCAtomicPointerRMW(CallInst *MSVCCall,
RMWBinOp RMWOp);
};
} // end namespace llvm
#endif // LLVM_TRANSFORMS_CUSTOMMEMORYINSTRUMENTATION_H