Skip to content

Commit

Permalink
[PGO] Value profile for size of memory intrinsic calls
Browse files Browse the repository at this point in the history
This patch adds the value profile support to profile the size parameter of
memory intrinsic calls: memcpy, memcmp, and memmov.

Differential Revision: http://reviews.llvm.org/D28965

llvm-svn: 297897
  • Loading branch information
xur-llvm committed Mar 15, 2017
1 parent ce450a6 commit 4ed5279
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 28 deletions.
4 changes: 4 additions & 0 deletions llvm/docs/CommandGuide/llvm-profdata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ OPTIONS

Specify that the input profile is a sample-based profile.

.. option:: -memop-sizes

Show the profiled sizes of the memory intrinsic calls for shown functions.

EXIT STATUS
-----------

Expand Down
8 changes: 8 additions & 0 deletions llvm/include/llvm/ProfileData/InstrProf.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ inline StringRef getInstrProfValueProfFuncName() {
return INSTR_PROF_VALUE_PROF_FUNC_STR;
}

/// Return the name profile runtime entry point to do value range profiling.
inline StringRef getInstrProfValueRangeProfFuncName() {
return INSTR_PROF_VALUE_RANGE_PROF_FUNC_STR;
}

/// Return the name of the section containing function coverage mapping
/// data.
inline StringRef getInstrProfCoverageSectionName(bool AddSegment) {
Expand Down Expand Up @@ -689,12 +694,15 @@ struct InstrProfRecord {

private:
std::vector<InstrProfValueSiteRecord> IndirectCallSites;
std::vector<InstrProfValueSiteRecord> MemOPSizes;
const std::vector<InstrProfValueSiteRecord> &

getValueSitesForKind(uint32_t ValueKind) const {
switch (ValueKind) {
case IPVK_IndirectCallTarget:
return IndirectCallSites;
case IPVK_MemOPSize:
return MemOPSizes;
default:
llvm_unreachable("Unknown value kind!");
}
Expand Down
17 changes: 16 additions & 1 deletion llvm/include/llvm/ProfileData/InstrProfData.inc
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,17 @@ INSTR_PROF_RAW_HEADER(uint64_t, ValueKindLast, IPVK_Last)
VALUE_PROF_FUNC_PARAM(uint64_t, TargetValue, Type::getInt64Ty(Ctx)) \
INSTR_PROF_COMMA
VALUE_PROF_FUNC_PARAM(void *, Data, Type::getInt8PtrTy(Ctx)) INSTR_PROF_COMMA
#ifndef VALUE_RANGE_PROF
VALUE_PROF_FUNC_PARAM(uint32_t, CounterIndex, Type::getInt32Ty(Ctx))
#else /* VALUE_RANGE_PROF */
VALUE_PROF_FUNC_PARAM(uint32_t, CounterIndex, Type::getInt32Ty(Ctx)) \
INSTR_PROF_COMMA
VALUE_PROF_FUNC_PARAM(uint64_t, PreciseRangeStart, Type::getInt64Ty(Ctx)) \
INSTR_PROF_COMMA
VALUE_PROF_FUNC_PARAM(uint64_t, PreciseRangeLast, Type::getInt64Ty(Ctx)) \
INSTR_PROF_COMMA
VALUE_PROF_FUNC_PARAM(uint64_t, LargeValue, Type::getInt64Ty(Ctx))
#endif /*VALUE_RANGE_PROF */
#undef VALUE_PROF_FUNC_PARAM
#undef INSTR_PROF_COMMA
/* VALUE_PROF_FUNC_PARAM end */
Expand All @@ -174,13 +184,15 @@ VALUE_PROF_FUNC_PARAM(uint32_t, CounterIndex, Type::getInt32Ty(Ctx))
* name hash and the function address.
*/
VALUE_PROF_KIND(IPVK_IndirectCallTarget, 0)
/* For memory intrinsic functions size profiling. */
VALUE_PROF_KIND(IPVK_MemOPSize, 1)
/* These two kinds must be the last to be
* declared. This is to make sure the string
* array created with the template can be
* indexed with the kind value.
*/
VALUE_PROF_KIND(IPVK_First, IPVK_IndirectCallTarget)
VALUE_PROF_KIND(IPVK_Last, IPVK_IndirectCallTarget)
VALUE_PROF_KIND(IPVK_Last, IPVK_MemOPSize)

#undef VALUE_PROF_KIND
/* VALUE_PROF_KIND end */
Expand Down Expand Up @@ -649,6 +661,9 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
#define INSTR_PROF_VALUE_PROF_FUNC __llvm_profile_instrument_target
#define INSTR_PROF_VALUE_PROF_FUNC_STR \
INSTR_PROF_QUOTE(INSTR_PROF_VALUE_PROF_FUNC)
#define INSTR_PROF_VALUE_RANGE_PROF_FUNC __llvm_profile_instrument_range
#define INSTR_PROF_VALUE_RANGE_PROF_FUNC_STR \
INSTR_PROF_QUOTE(INSTR_PROF_VALUE_RANGE_PROF_FUNC)

/* InstrProfile per-function control data alignment. */
#define INSTR_PROF_DATA_ALIGNMENT 8
Expand Down
11 changes: 11 additions & 0 deletions llvm/include/llvm/Transforms/InstrProfiling.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,14 @@ class InstrProfiling : public PassInfoMixin<InstrProfiling> {
GlobalVariable *NamesVar;
size_t NamesSize;

// The start value of precise value profile range for memory intrinsic sizes.
const int64_t DefaultMemOPSizeRangeStart = 0;
int64_t MemOPSizeRangeStart;
// The end value of precise value profile range for memory intrinsic sizes.
const int64_t DefaultMemOPSizeRangeLast = 8;
int64_t MemOPSizeRangeLast;
int64_t MemOPSizeLargeVal;

bool isMachO() const;

/// Get the section name for the counter variables.
Expand Down Expand Up @@ -109,6 +117,9 @@ class InstrProfiling : public PassInfoMixin<InstrProfiling> {
/// Create a static initializer for our data, on platforms that need it,
/// and for any profile output file that was specified.
void emitInitialization();

/// Helper funtion that parsing the MemOPSize value profile options
void getMemOPSizeOptions();
};

} // end namespace llvm
Expand Down
87 changes: 75 additions & 12 deletions llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ cl::opt<double> NumCountersPerValueSite(
// is usually smaller than 2.
cl::init(1.0));

cl::opt<std::string> MemOPSizeRange(
"memop-size-range",
cl::desc("Set the range of size in memory intrinsic calls to be profiled "
"precisely, in a format of <start_val>:<end_val>"),
cl::init(""));
cl::opt<unsigned> MemOPSizeLarge(
"memop-size-large",
cl::desc("Set large value thresthold in memory intrinsic size profiling. "
"Value of 0 disables the large value profiling."),
cl::init(8192));

class InstrProfilingLegacyPass : public ModulePass {
InstrProfiling InstrProf;

Expand Down Expand Up @@ -165,6 +176,7 @@ bool InstrProfiling::run(Module &M, const TargetLibraryInfo &TLI) {
NamesSize = 0;
ProfileDataMap.clear();
UsedVars.clear();
getMemOPSizeOptions();

// We did not know how many value sites there would be inside
// the instrumented function. This is counting the number of instrumented
Expand Down Expand Up @@ -217,17 +229,34 @@ bool InstrProfiling::run(Module &M, const TargetLibraryInfo &TLI) {
}

static Constant *getOrInsertValueProfilingCall(Module &M,
const TargetLibraryInfo &TLI) {
const TargetLibraryInfo &TLI,
bool IsRange = false) {
LLVMContext &Ctx = M.getContext();
auto *ReturnTy = Type::getVoidTy(M.getContext());
Type *ParamTypes[] = {

Constant *Res;
if (!IsRange) {
Type *ParamTypes[] = {
#define VALUE_PROF_FUNC_PARAM(ParamType, ParamName, ParamLLVMType) ParamLLVMType
#include "llvm/ProfileData/InstrProfData.inc"
};
auto *ValueProfilingCallTy =
FunctionType::get(ReturnTy, makeArrayRef(ParamTypes), false);
Constant *Res = M.getOrInsertFunction(getInstrProfValueProfFuncName(),
ValueProfilingCallTy);
};
auto *ValueProfilingCallTy =
FunctionType::get(ReturnTy, makeArrayRef(ParamTypes), false);
Res = M.getOrInsertFunction(getInstrProfValueProfFuncName(),
ValueProfilingCallTy);
} else {
Type *RangeParamTypes[] = {
#define VALUE_RANGE_PROF 1
#define VALUE_PROF_FUNC_PARAM(ParamType, ParamName, ParamLLVMType) ParamLLVMType
#include "llvm/ProfileData/InstrProfData.inc"
#undef VALUE_RANGE_PROF
};
auto *ValueRangeProfilingCallTy =
FunctionType::get(ReturnTy, makeArrayRef(RangeParamTypes), false);
Res = M.getOrInsertFunction(getInstrProfValueRangeProfFuncName(),
ValueRangeProfilingCallTy);
}

if (Function *FunRes = dyn_cast<Function>(Res)) {
if (auto AK = TLI.getExtAttrForI32Param(false))
FunRes->addAttribute(3, AK);
Expand Down Expand Up @@ -261,11 +290,24 @@ void InstrProfiling::lowerValueProfileInst(InstrProfValueProfileInst *Ind) {
Index += It->second.NumValueSites[Kind];

IRBuilder<> Builder(Ind);
Value *Args[3] = {Ind->getTargetValue(),
Builder.CreateBitCast(DataVar, Builder.getInt8PtrTy()),
Builder.getInt32(Index)};
CallInst *Call = Builder.CreateCall(getOrInsertValueProfilingCall(*M, *TLI),
Args);
bool IsRange = (Ind->getValueKind()->getZExtValue() ==
llvm::InstrProfValueKind::IPVK_MemOPSize);
CallInst *Call = nullptr;
if (!IsRange) {
Value *Args[3] = {Ind->getTargetValue(),
Builder.CreateBitCast(DataVar, Builder.getInt8PtrTy()),
Builder.getInt32(Index)};
Call = Builder.CreateCall(getOrInsertValueProfilingCall(*M, *TLI), Args);
} else {
Value *Args[6] = {Ind->getTargetValue(),
Builder.CreateBitCast(DataVar, Builder.getInt8PtrTy()),
Builder.getInt32(Index),
Builder.getInt64(MemOPSizeRangeStart),
Builder.getInt64(MemOPSizeRangeLast),
Builder.getInt64(MemOPSizeLargeVal)};
Call =
Builder.CreateCall(getOrInsertValueProfilingCall(*M, *TLI, true), Args);
}
if (auto AK = TLI->getExtAttrForI32Param(false))
Call->addAttribute(3, AK);
Ind->replaceAllUsesWith(Call);
Expand Down Expand Up @@ -658,3 +700,24 @@ void InstrProfiling::emitInitialization() {

appendToGlobalCtors(*M, F, 0);
}

void InstrProfiling::getMemOPSizeOptions() {
// Parse the value profile options.
MemOPSizeRangeStart = DefaultMemOPSizeRangeStart;
MemOPSizeRangeLast = DefaultMemOPSizeRangeLast;
if (!MemOPSizeRange.empty()) {
auto Pos = MemOPSizeRange.find(":");
if (Pos != std::string::npos) {
if (Pos > 0)
MemOPSizeRangeStart = std::stoi(MemOPSizeRange.substr(0, Pos));
if (Pos < MemOPSizeRange.size() - 1)
MemOPSizeRangeLast = std::stoi(MemOPSizeRange.substr(Pos + 1));
} else
MemOPSizeRangeLast = std::stoi(MemOPSizeRange);
}
assert(MemOPSizeRangeLast >= MemOPSizeRangeStart);

MemOPSizeLargeVal = MemOPSizeLarge;
if (MemOPSizeLargeVal == 0)
MemOPSizeLargeVal = INT64_MIN;
}
84 changes: 83 additions & 1 deletion llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ using namespace llvm;

STATISTIC(NumOfPGOInstrument, "Number of edges instrumented.");
STATISTIC(NumOfPGOSelectInsts, "Number of select instruction instrumented.");
STATISTIC(NumOfPGOMemIntrinsics, "Number of mem intrinsics instrumented.");
STATISTIC(NumOfPGOEdge, "Number of edges.");
STATISTIC(NumOfPGOBB, "Number of basic-blocks.");
STATISTIC(NumOfPGOSplit, "Number of critical edge splits.");
Expand Down Expand Up @@ -168,6 +169,10 @@ static cl::opt<bool>
"display to only one function, use "
"filtering option -view-bfi-func-name."));

// Command line option to enable/disable memop intrinsic calls..
static cl::opt<bool> PGOInstrMemOP("pgo-instr-memop", cl::init(true),
cl::Hidden);

// Command line option to turn on CFG dot dump after profile annotation.
// Defined in Analysis/BlockFrequencyInfo.cpp: -pgo-view-counts
extern cl::opt<bool> PGOViewCounts;
Expand Down Expand Up @@ -235,6 +240,40 @@ struct SelectInstVisitor : public InstVisitor<SelectInstVisitor> {
unsigned getNumOfSelectInsts() const { return NSIs; }
};

/// Instruction Visitor class to visit memory intrinsic calls.
struct MemIntrinsicVisitor : public InstVisitor<MemIntrinsicVisitor> {
Function &F;
unsigned NMemIs = 0; // Number of memIntrinsics instrumented.
VisitMode Mode = VM_counting; // Visiting mode.
unsigned CurCtrId = 0; // Current counter index.
unsigned TotalNumCtrs = 0; // Total number of counters
GlobalVariable *FuncNameVar = nullptr;
uint64_t FuncHash = 0;
PGOUseFunc *UseFunc = nullptr;

MemIntrinsicVisitor(Function &Func) : F(Func) {}

void countMemIntrinsics(Function &Func) {
NMemIs = 0;
Mode = VM_counting;
visit(Func);
}
void instrumentMemIntrinsics(Function &Func, unsigned TotalNC,
GlobalVariable *FNV, uint64_t FHash) {
Mode = VM_instrument;
TotalNumCtrs = TotalNC;
FuncHash = FHash;
FuncNameVar = FNV;
visit(Func);
}

// Visit the IR stream and annotate all mem intrinsic call instructions.
void instrumentOneMemIntrinsic(MemIntrinsic &MI);
// Visit \p MI instruction and perform tasks according to visit mode.
void visitMemIntrinsic(MemIntrinsic &SI);
unsigned getNumOfMemIntrinsics() const { return NMemIs; }
};

class PGOInstrumentationGenLegacyPass : public ModulePass {
public:
static char ID;
Expand Down Expand Up @@ -354,6 +393,7 @@ template <class Edge, class BBInfo> class FuncPGOInstrumentation {
public:
std::vector<std::vector<Instruction *>> ValueSites;
SelectInstVisitor SIVisitor;
MemIntrinsicVisitor MIVisitor;
std::string FuncName;
GlobalVariable *FuncNameVar;
// CFG hash value for this function.
Expand Down Expand Up @@ -384,11 +424,13 @@ template <class Edge, class BBInfo> class FuncPGOInstrumentation {
bool CreateGlobalVar = false, BranchProbabilityInfo *BPI = nullptr,
BlockFrequencyInfo *BFI = nullptr)
: F(Func), ComdatMembers(ComdatMembers), ValueSites(IPVK_Last + 1),
SIVisitor(Func), FunctionHash(0), MST(F, BPI, BFI) {
SIVisitor(Func), MIVisitor(Func), FunctionHash(0), MST(F, BPI, BFI) {

// This should be done before CFG hash computation.
SIVisitor.countSelects(Func);
MIVisitor.countMemIntrinsics(Func);
NumOfPGOSelectInsts += SIVisitor.getNumOfSelectInsts();
NumOfPGOMemIntrinsics += MIVisitor.getNumOfMemIntrinsics();
ValueSites[IPVK_IndirectCallTarget] = findIndirectCallSites(Func);

FuncName = getPGOFuncName(F);
Expand Down Expand Up @@ -605,6 +647,10 @@ static void instrumentOneFunc(
Builder.getInt32(NumIndirectCallSites++)});
}
NumOfPGOICall += NumIndirectCallSites;

// Now instrument memop intrinsic calls.
FuncInfo.MIVisitor.instrumentMemIntrinsics(
F, NumCounters, FuncInfo.FuncNameVar, FuncInfo.FunctionHash);
}

// This class represents a CFG edge in profile use compilation.
Expand Down Expand Up @@ -1076,6 +1122,42 @@ void SelectInstVisitor::visitSelectInst(SelectInst &SI) {
llvm_unreachable("Unknown visiting mode");
}

void MemIntrinsicVisitor::instrumentOneMemIntrinsic(MemIntrinsic &MI) {
Module *M = F.getParent();
IRBuilder<> Builder(&MI);
Type *Int64Ty = Builder.getInt64Ty();
Type *I8PtrTy = Builder.getInt8PtrTy();
Value *Length = MI.getLength();
assert(!dyn_cast<ConstantInt>(Length));
Builder.CreateCall(
Intrinsic::getDeclaration(M, Intrinsic::instrprof_value_profile),
{llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy),
Builder.getInt64(FuncHash), Builder.CreatePtrToInt(Length, Int64Ty),
Builder.getInt32(IPVK_MemOPSize), Builder.getInt32(CurCtrId)});
++CurCtrId;
}

void MemIntrinsicVisitor::visitMemIntrinsic(MemIntrinsic &MI) {
if (!PGOInstrMemOP)
return;
Value *Length = MI.getLength();
// Not instrument constant length calls.
if (dyn_cast<ConstantInt>(Length))
return;

NMemIs++;
switch (Mode) {
case VM_counting:
return;
case VM_instrument:
instrumentOneMemIntrinsic(MI);
return;
case VM_annotate:
break;
}
llvm_unreachable("Unknown visiting mode");
}

// Traverse all valuesites and annotate the instructions for all value kind.
void PGOUseFunc::annotateValueSites() {
if (DisableValueProfiling)
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/Instrumentation/InstrProfiling/PR23499.ll
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ $_Z3barIvEvv = comdat any

; CHECK-NOT: __profn__Z3barIvEvv
; CHECK: @__profc__Z3barIvEvv = linkonce_odr hidden global [1 x i64] zeroinitializer, section "{{.*}}__llvm_prf_cnts", comdat($__profv__Z3barIvEvv), align 8
; CHECK: @__profd__Z3barIvEvv = linkonce_odr hidden global { i64, i64, i64*, i8*, i8*, i32, [1 x i16] } { i64 4947693190065689389, i64 0, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc__Z3barIvEvv, i32 0, i32 0), i8*{{.*}}, i8* null, i32 1, [1 x i16] zeroinitializer }, section "{{.*}}__llvm_prf_data{{.*}}", comdat($__profv__Z3barIvEvv), align 8
; CHECK: @__profd__Z3barIvEvv = linkonce_odr hidden global { i64, i64, i64*, i8*, i8*, i32, [2 x i16] } { i64 4947693190065689389, i64 0, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc__Z3barIvEvv, i32 0, i32 0), i8*{{.*}}, i8* null, i32 1, [2 x i16] zeroinitializer }, section "{{.*}}__llvm_prf_data{{.*}}", comdat($__profv__Z3barIvEvv), align 8
; CHECK: @__llvm_prf_nm = private constant [{{.*}} x i8] c"{{.*}}", section "{{.*}}__llvm_prf_names"


; COFF-NOT: __profn__Z3barIvEvv
; COFF: @__profc__Z3barIvEvv = linkonce_odr hidden global [1 x i64] zeroinitializer, section "{{.*}}__llvm_prf_cnts", comdat, align 8
; COFF: @__profd__Z3barIvEvv = linkonce_odr hidden global { i64, i64, i64*, i8*, i8*, i32, [1 x i16] } { i64 4947693190065689389, i64 0, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc__Z3barIvEvv, i32 0, i32 0), i8*{{.*}}, i8* null, i32 1, [1 x i16] zeroinitializer }, section "{{.*}}__llvm_prf_data{{.*}}", comdat($__profc__Z3barIvEvv), align 8
; COFF: @__profd__Z3barIvEvv = linkonce_odr hidden global { i64, i64, i64*, i8*, i8*, i32, [2 x i16] } { i64 4947693190065689389, i64 0, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc__Z3barIvEvv, i32 0, i32 0), i8*{{.*}}, i8* null, i32 1, [2 x i16] zeroinitializer }, section "{{.*}}__llvm_prf_data{{.*}}", comdat($__profc__Z3barIvEvv), align 8


declare void @llvm.instrprof.increment(i8*, i64, i32, i32) #1
Expand Down
6 changes: 3 additions & 3 deletions llvm/test/Instrumentation/InstrProfiling/icall.ll
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ attributes #0 = { nounwind }
; DYN-NOT: @__profvp_foo
; DYN-NOT: @__llvm_prf_vnodes

; STATIC: call void @__llvm_profile_instrument_target(i64 %3, i8* bitcast ({ i64, i64, i64*, i8*, i8*, i32, [1 x i16] }* @__profd_foo to i8*), i32 0)
; STATIC-EXT: call void @__llvm_profile_instrument_target(i64 %3, i8* bitcast ({ i64, i64, i64*, i8*, i8*, i32, [1 x i16] }* @__profd_foo to i8*), i32 zeroext 0)
; STATIC-SEXT: call void @__llvm_profile_instrument_target(i64 %3, i8* bitcast ({ i64, i64, i64*, i8*, i8*, i32, [1 x i16] }* @__profd_foo to i8*), i32 signext 0)
; STATIC: call void @__llvm_profile_instrument_target(i64 %3, i8* bitcast ({ i64, i64, i64*, i8*, i8*, i32, [2 x i16] }* @__profd_foo to i8*), i32 0)
; STATIC-EXT: call void @__llvm_profile_instrument_target(i64 %3, i8* bitcast ({ i64, i64, i64*, i8*, i8*, i32, [2 x i16] }* @__profd_foo to i8*), i32 zeroext 0)
; STATIC-SEXT: call void @__llvm_profile_instrument_target(i64 %3, i8* bitcast ({ i64, i64, i64*, i8*, i8*, i32, [2 x i16] }* @__profd_foo to i8*), i32 signext 0)

; STATIC: declare void @__llvm_profile_instrument_target(i64, i8*, i32)
; STATIC-EXT: declare void @__llvm_profile_instrument_target(i64, i8*, i32 zeroext)
Expand Down
Loading

0 comments on commit 4ed5279

Please sign in to comment.