278 changes: 269 additions & 9 deletions llvm/lib/ProfileData/SampleProfReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ std::error_code SampleProfileReaderText::read() {
// should not begin with a number.
SmallVector<StringRef, 4> Matches;
if (!HeadRE.match(*LineIt, &Matches)) {
reportParseError(LineIt.line_number(),
"Expected 'mangled_name:NUM:NUM', found " + *LineIt);
reportError(LineIt.line_number(),
"Expected 'mangled_name:NUM:NUM', found " + *LineIt);
return sampleprof_error::malformed;
}
assert(Matches.size() == 4);
Expand All @@ -192,9 +192,9 @@ std::error_code SampleProfileReaderText::read() {
// EOF or when we see the start of the next function.
while (!LineIt.is_at_eof() && isdigit((*LineIt)[0])) {
if (!LineSampleRE.match(*LineIt, &Matches)) {
reportParseError(
LineIt.line_number(),
"Expected 'NUM[.NUM]: NUM[ mangled_name:NUM]*', found " + *LineIt);
reportError(LineIt.line_number(),
"Expected 'NUM[.NUM]: NUM[ mangled_name:NUM]*', found " +
*LineIt);
return sampleprof_error::malformed;
}
assert(Matches.size() == 5);
Expand All @@ -210,8 +210,8 @@ std::error_code SampleProfileReaderText::read() {
while (CallsLine != "") {
SmallVector<StringRef, 3> CallSample;
if (!CallSampleRE.match(CallsLine, &CallSample)) {
reportParseError(LineIt.line_number(),
"Expected 'mangled_name:NUM', found " + CallsLine);
reportError(LineIt.line_number(),
"Expected 'mangled_name:NUM', found " + CallsLine);
return sampleprof_error::malformed;
}
StringRef CalledFunction = CallSample[1];
Expand Down Expand Up @@ -243,7 +243,7 @@ template <typename T> ErrorOr<T> SampleProfileReaderBinary::readNumber() {
EC = sampleprof_error::success;

if (EC) {
reportParseError(0, EC.message());
reportError(0, EC.message());
return EC;
}

Expand All @@ -256,7 +256,7 @@ ErrorOr<StringRef> SampleProfileReaderBinary::readString() {
StringRef Str(reinterpret_cast<const char *>(Data));
if (Data + Str.size() + 1 > End) {
EC = sampleprof_error::truncated;
reportParseError(0, EC.message());
reportError(0, EC.message());
return EC;
}

Expand Down Expand Up @@ -353,6 +353,264 @@ bool SampleProfileReaderBinary::hasFormat(const MemoryBuffer &Buffer) {
return Magic == SPMagic();
}

bool SourceInfo::operator<(const SourceInfo &P) const {
if (Line != P.Line)
return Line < P.Line;
if (StartLine != P.StartLine)
return StartLine < P.StartLine;
if (Discriminator != P.Discriminator)
return Discriminator < P.Discriminator;
return FuncName < P.FuncName;
}

std::error_code SampleProfileReaderGCC::skipNextWord() {
uint32_t dummy;
if (!GcovBuffer.readInt(dummy))
return sampleprof_error::truncated;
return sampleprof_error::success;
}

template <typename T> ErrorOr<T> SampleProfileReaderGCC::readNumber() {
if (sizeof(T) <= sizeof(uint32_t)) {
uint32_t Val;
if (GcovBuffer.readInt(Val) && Val <= std::numeric_limits<T>::max())
return static_cast<T>(Val);
} else if (sizeof(T) <= sizeof(uint64_t)) {
uint64_t Val;
if (GcovBuffer.readInt64(Val) && Val <= std::numeric_limits<T>::max())
return static_cast<T>(Val);
}

std::error_code EC = sampleprof_error::malformed;
reportError(0, EC.message());
return EC;
}

ErrorOr<StringRef> SampleProfileReaderGCC::readString() {
StringRef Str;
if (!GcovBuffer.readString(Str))
return sampleprof_error::truncated;
return Str;
}

std::error_code SampleProfileReaderGCC::readHeader() {
// Read the magic identifier.
if (!GcovBuffer.readGCDAFormat())
return sampleprof_error::unrecognized_format;

// Read the version number. Note - the GCC reader does not validate this
// version, but the profile creator generates v704.
GCOV::GCOVVersion version;
if (!GcovBuffer.readGCOVVersion(version))
return sampleprof_error::unrecognized_format;

if (version != GCOV::V704)
return sampleprof_error::unsupported_version;

// Skip the empty integer.
if (std::error_code EC = skipNextWord())
return EC;

return sampleprof_error::success;
}

std::error_code SampleProfileReaderGCC::readSectionTag(uint32_t Expected) {
uint32_t Tag;
if (!GcovBuffer.readInt(Tag))
return sampleprof_error::truncated;

if (Tag != Expected)
return sampleprof_error::malformed;

if (std::error_code EC = skipNextWord())
return EC;

return sampleprof_error::success;
}

std::error_code SampleProfileReaderGCC::readNameTable() {
if (std::error_code EC = readSectionTag(GCOVTagAFDOFileNames))
return EC;

uint32_t Size;
if (!GcovBuffer.readInt(Size))
return sampleprof_error::truncated;

for (uint32_t I = 0; I < Size; ++I) {
StringRef Str;
if (!GcovBuffer.readString(Str))
return sampleprof_error::truncated;
Names.push_back(Str);
}

return sampleprof_error::success;
}

std::error_code SampleProfileReaderGCC::readFunctionProfiles() {
if (std::error_code EC = readSectionTag(GCOVTagAFDOFunction))
return EC;

uint32_t NumFunctions;
if (!GcovBuffer.readInt(NumFunctions))
return sampleprof_error::truncated;

SourceStack Stack;
for (uint32_t I = 0; I < NumFunctions; ++I)
if (std::error_code EC = readOneFunctionProfile(Stack, true))
return EC;

return sampleprof_error::success;
}

std::error_code SampleProfileReaderGCC::addSourceCount(StringRef Name,
const SourceStack &Src,
uint64_t Count) {
if (Src.size() == 0 || Src[0].Malformed())
return sampleprof_error::malformed;
FunctionSamples &FProfile = Profiles[Name];
FProfile.addTotalSamples(Count);
// FIXME(dnovillo) - Properly update inline stack for FnName.
FProfile.addBodySamples(Src[0].Line, Src[0].Discriminator, Count);
return sampleprof_error::success;
}


std::error_code
SampleProfileReaderGCC::readOneFunctionProfile(const SourceStack &Stack,
bool Update) {
uint64_t HeadCount = 0;
if (Stack.size() == 0)
if (!GcovBuffer.readInt64(HeadCount))
return sampleprof_error::truncated;

uint32_t NameIdx;
if (!GcovBuffer.readInt(NameIdx))
return sampleprof_error::truncated;

StringRef Name(Names[NameIdx]);

uint32_t NumPosCounts;
if (!GcovBuffer.readInt(NumPosCounts))
return sampleprof_error::truncated;

uint32_t NumCallSites;
if (!GcovBuffer.readInt(NumCallSites))
return sampleprof_error::truncated;

if (Stack.size() == 0) {
FunctionSamples &FProfile = Profiles[Name];
FProfile.addHeadSamples(HeadCount);
if (FProfile.getTotalSamples() > 0)
Update = false;
}

for (uint32_t I = 0; I < NumPosCounts; ++I) {
uint32_t Offset;
if (!GcovBuffer.readInt(Offset))
return sampleprof_error::truncated;

uint32_t NumTargets;
if (!GcovBuffer.readInt(NumTargets))
return sampleprof_error::truncated;

uint64_t Count;
if (!GcovBuffer.readInt64(Count))
return sampleprof_error::truncated;

SourceInfo Info(Name, "", "", 0, Offset >> 16, Offset & 0xffff);
SourceStack NewStack;
NewStack.push_back(Info);
NewStack.insert(NewStack.end(), Stack.begin(), Stack.end());
if (Update)
addSourceCount(NewStack[NewStack.size() - 1].FuncName, NewStack, Count);

for (uint32_t J = 0; J < NumTargets; J++) {
uint32_t HistVal;
if (!GcovBuffer.readInt(HistVal))
return sampleprof_error::truncated;

if (HistVal != HIST_TYPE_INDIR_CALL_TOPN)
return sampleprof_error::malformed;

uint64_t TargetIdx;
if (!GcovBuffer.readInt64(TargetIdx))
return sampleprof_error::truncated;
StringRef TargetName(Names[TargetIdx]);

uint64_t TargetCount;
if (!GcovBuffer.readInt64(TargetCount))
return sampleprof_error::truncated;

if (Update) {
FunctionSamples &TargetProfile = Profiles[TargetName];
TargetProfile.addBodySamples(NewStack[0].Line,
NewStack[0].Discriminator, TargetCount);
}
}
}

for (uint32_t I = 0; I < NumCallSites; I++) {
// The offset is encoded as:
// high 16 bits: line offset to the start of the function.
// low 16 bits: discriminator.
uint32_t Offset;
if (!GcovBuffer.readInt(Offset))
return sampleprof_error::truncated;
SourceInfo Info(Name, "", "", 0, Offset >> 16, Offset & 0xffff);
SourceStack NewStack;
NewStack.push_back(Info);
NewStack.insert(NewStack.end(), Stack.begin(), Stack.end());
if (std::error_code EC = readOneFunctionProfile(NewStack, Update))
return EC;
}

return sampleprof_error::success;
}

std::error_code SampleProfileReaderGCC::readModuleGroup() {
// FIXME(dnovillo) - Module support still not implemented.
return sampleprof_error::not_implemented;
}

std::error_code SampleProfileReaderGCC::readWorkingSet() {
// FIXME(dnovillo) - Working sets still not implemented.
return sampleprof_error::not_implemented;
}


/// \brief Read a GCC AutoFDO profile.
///
/// This format is generated by the Linux Perf conversion tool at
/// https://github.com/google/autofdo.
std::error_code SampleProfileReaderGCC::read() {
// Read the string table.
if (std::error_code EC = readNameTable())
return EC;

// Read the source profile.
if (std::error_code EC = readFunctionProfiles())
return EC;

// FIXME(dnovillo) - Module groups and working set support are not
// yet implemented.
#if 0
// Read the module group file.
if (std::error_code EC = readModuleGroup())
return EC;

// Read the working set.
if (std::error_code EC = readWorkingSet())
return EC;
#endif

return sampleprof_error::success;
}

bool SampleProfileReaderGCC::hasFormat(const MemoryBuffer &Buffer) {
StringRef Magic(reinterpret_cast<const char *>(Buffer.getBufferStart()));
return Magic == "adcg*704";
}

/// \brief Prepare a memory buffer for the contents of \p Filename.
///
/// \returns an error code indicating the status of the buffer.
Expand Down Expand Up @@ -389,6 +647,8 @@ SampleProfileReader::create(StringRef Filename, LLVMContext &C) {
std::unique_ptr<SampleProfileReader> Reader;
if (SampleProfileReaderBinary::hasFormat(*Buffer))
Reader.reset(new SampleProfileReaderBinary(std::move(Buffer), C));
else if (SampleProfileReaderGCC::hasFormat(*Buffer))
Reader.reset(new SampleProfileReaderGCC(std::move(Buffer), C));
else
Reader.reset(new SampleProfileReaderText(std::move(Buffer), C));

Expand Down
Binary file not shown.
218 changes: 218 additions & 0 deletions llvm/test/Transforms/SampleProfile/gcc-simple.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
; RUN: opt < %s -sample-profile -sample-profile-file=%S/Inputs/gcc-simple.afdo -S | FileCheck %s
;
; Original code:
;
; #include <stdlib.h>
;
; long long int foo(long i) {
; if (rand() < 500) return 2; else if (rand() > 5000) return 10; else return 90;
; }
;
; int main() {
; long long int sum = 0;
; for (int k = 0; k < 3000; k++)
; for (int i = 0; i < 200000; i++) sum += foo(i);
; return sum > 0 ? 0 : 1;
; }
;
; This test was compiled down to bytecode at -O0 to avoid inlining foo() into
; main(). The profile was generated using a GCC-generated binary (also compiled
; at -O0). The conversion from the Linux Perf profile to the GCC autofdo
; profile used the converter at https://github.com/google/autofdo
;
; $ gcc -g -O0 gcc-simple.cc -o gcc-simple
; $ perf record -b ./gcc-simple
; $ create_gcov --binary=gcc-simple --gcov=gcc-simple.afdo

define i64 @_Z3fool(i64 %i) #0 {
; CHECK: !prof ![[EC1:[0-9]+]]
entry:
%retval = alloca i64, align 8
%i.addr = alloca i64, align 8
store i64 %i, i64* %i.addr, align 8
call void @llvm.dbg.declare(metadata i64* %i.addr, metadata !16, metadata !17), !dbg !18
%call = call i32 @rand() #3, !dbg !19
%cmp = icmp slt i32 %call, 500, !dbg !21
br i1 %cmp, label %if.then, label %if.else, !dbg !22
; CHECK: !prof ![[PROF1:[0-9]+]]

if.then: ; preds = %entry
store i64 2, i64* %retval, align 8, !dbg !23
br label %return, !dbg !23

if.else: ; preds = %entry
%call1 = call i32 @rand() #3, !dbg !25
%cmp2 = icmp sgt i32 %call1, 5000, !dbg !28
br i1 %cmp2, label %if.then.3, label %if.else.4, !dbg !29
; CHECK: !prof ![[PROF2:[0-9]+]]

if.then.3: ; preds = %if.else
store i64 10, i64* %retval, align 8, !dbg !30
br label %return, !dbg !30

if.else.4: ; preds = %if.else
store i64 90, i64* %retval, align 8, !dbg !32
br label %return, !dbg !32

return: ; preds = %if.else.4, %if.then.3, %if.then
%0 = load i64, i64* %retval, align 8, !dbg !34
ret i64 %0, !dbg !34
}

; Function Attrs: nounwind readnone
declare void @llvm.dbg.declare(metadata, metadata, metadata) #1

; Function Attrs: nounwind
declare i32 @rand() #2

; Function Attrs: nounwind uwtable
define i32 @main() #0 {
; CHECK: !prof ![[EC2:[0-9]+]]
entry:
%retval = alloca i32, align 4
%sum = alloca i64, align 8
%k = alloca i32, align 4
%i = alloca i32, align 4
store i32 0, i32* %retval, align 4
call void @llvm.dbg.declare(metadata i64* %sum, metadata !35, metadata !17), !dbg !36
store i64 0, i64* %sum, align 8, !dbg !36
call void @llvm.dbg.declare(metadata i32* %k, metadata !37, metadata !17), !dbg !39
store i32 0, i32* %k, align 4, !dbg !39
br label %for.cond, !dbg !40

for.cond: ; preds = %for.inc.4, %entry
%0 = load i32, i32* %k, align 4, !dbg !41
%cmp = icmp slt i32 %0, 3000, !dbg !45
br i1 %cmp, label %for.body, label %for.end.6, !dbg !46
; CHECK: !prof ![[PROF3:[0-9]+]]

for.body: ; preds = %for.cond
call void @llvm.dbg.declare(metadata i32* %i, metadata !47, metadata !17), !dbg !49
store i32 0, i32* %i, align 4, !dbg !49
br label %for.cond.1, !dbg !50

for.cond.1: ; preds = %for.inc, %for.body
%1 = load i32, i32* %i, align 4, !dbg !51
%cmp2 = icmp slt i32 %1, 200000, !dbg !55
br i1 %cmp2, label %for.body.3, label %for.end, !dbg !56
; CHECK: !prof ![[PROF4:[0-9]+]]

for.body.3: ; preds = %for.cond.1
%2 = load i32, i32* %i, align 4, !dbg !57
%conv = sext i32 %2 to i64, !dbg !57
%call = call i64 @_Z3fool(i64 %conv), !dbg !59
%3 = load i64, i64* %sum, align 8, !dbg !60
%add = add nsw i64 %3, %call, !dbg !60
store i64 %add, i64* %sum, align 8, !dbg !60
br label %for.inc, !dbg !61

for.inc: ; preds = %for.body.3
%4 = load i32, i32* %i, align 4, !dbg !62
%inc = add nsw i32 %4, 1, !dbg !62
store i32 %inc, i32* %i, align 4, !dbg !62
br label %for.cond.1, !dbg !64

for.end: ; preds = %for.cond.1
br label %for.inc.4, !dbg !65

for.inc.4: ; preds = %for.end
%5 = load i32, i32* %k, align 4, !dbg !67
%inc5 = add nsw i32 %5, 1, !dbg !67
store i32 %inc5, i32* %k, align 4, !dbg !67
br label %for.cond, !dbg !68

for.end.6: ; preds = %for.cond
%6 = load i64, i64* %sum, align 8, !dbg !69
%cmp7 = icmp sgt i64 %6, 0, !dbg !70
%cond = select i1 %cmp7, i32 0, i32 1, !dbg !69
ret i32 %cond, !dbg !71
}

; CHECK ![[EC1]] = !{!"function_entry_count", i64 24108}
; CHECK ![[PROF1]] = !{!"branch_weights", i32 1, i32 30124}
; CHECK ![[PROF2]] = !{!"branch_weights", i32 30177, i32 29579}
; CHECK ![[EC2]] = !{!"function_entry_count", i64 0}
; CHECK ![[PROF3]] = !{!"branch_weights", i32 1, i32 1}
; CHECK ![[PROF4]] = !{!"branch_weights", i32 1, i32 20238}

attributes #0 = { nounwind uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind readnone }
attributes #2 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #3 = { nounwind }

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!13, !14}
!llvm.ident = !{!15}

!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.8.0 (trunk 247554) (llvm/trunk 247557)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, subprograms: !3)
!1 = !DIFile(filename: "discriminator.cc", directory: "/usr/local/google/home/dnovillo/llvm/test/autofdo")
!2 = !{}
!3 = !{!4, !9}
!4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fool", scope: !1, file: !1, line: 3, type: !5, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, function: i64 (i64)* @_Z3fool, variables: !2)
!5 = !DISubroutineType(types: !6)
!6 = !{!7, !8}
!7 = !DIBasicType(name: "long long int", size: 64, align: 64, encoding: DW_ATE_signed)
!8 = !DIBasicType(name: "long int", size: 64, align: 64, encoding: DW_ATE_signed)
!9 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 7, type: !10, isLocal: false, isDefinition: true, scopeLine: 7, flags: DIFlagPrototyped, isOptimized: false, function: i32 ()* @main, variables: !2)
!10 = !DISubroutineType(types: !11)
!11 = !{!12}
!12 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed)
!13 = !{i32 2, !"Dwarf Version", i32 4}
!14 = !{i32 2, !"Debug Info Version", i32 3}
!15 = !{!"clang version 3.8.0 (trunk 247554) (llvm/trunk 247557)"}
!16 = !DILocalVariable(name: "i", arg: 1, scope: !4, file: !1, line: 3, type: !8)
!17 = !DIExpression()
!18 = !DILocation(line: 3, column: 24, scope: !4)
!19 = !DILocation(line: 4, column: 7, scope: !20)
!20 = distinct !DILexicalBlock(scope: !4, file: !1, line: 4, column: 7)
!21 = !DILocation(line: 4, column: 14, scope: !20)
!22 = !DILocation(line: 4, column: 7, scope: !4)
!23 = !DILocation(line: 4, column: 21, scope: !24)
!24 = !DILexicalBlockFile(scope: !20, file: !1, discriminator: 1)
!25 = !DILocation(line: 4, column: 40, scope: !26)
!26 = !DILexicalBlockFile(scope: !27, file: !1, discriminator: 2)
!27 = distinct !DILexicalBlock(scope: !20, file: !1, line: 4, column: 40)
!28 = !DILocation(line: 4, column: 47, scope: !27)
!29 = !DILocation(line: 4, column: 40, scope: !20)
!30 = !DILocation(line: 4, column: 55, scope: !31)
!31 = !DILexicalBlockFile(scope: !27, file: !1, discriminator: 3)
!32 = !DILocation(line: 4, column: 71, scope: !33)
!33 = !DILexicalBlockFile(scope: !27, file: !1, discriminator: 4)
!34 = !DILocation(line: 5, column: 1, scope: !4)
!35 = !DILocalVariable(name: "sum", scope: !9, file: !1, line: 8, type: !7)
!36 = !DILocation(line: 8, column: 17, scope: !9)
!37 = !DILocalVariable(name: "k", scope: !38, file: !1, line: 9, type: !12)
!38 = distinct !DILexicalBlock(scope: !9, file: !1, line: 9, column: 3)
!39 = !DILocation(line: 9, column: 12, scope: !38)
!40 = !DILocation(line: 9, column: 8, scope: !38)
!41 = !DILocation(line: 9, column: 19, scope: !42)
!42 = !DILexicalBlockFile(scope: !43, file: !1, discriminator: 2)
!43 = !DILexicalBlockFile(scope: !44, file: !1, discriminator: 1)
!44 = distinct !DILexicalBlock(scope: !38, file: !1, line: 9, column: 3)
!45 = !DILocation(line: 9, column: 21, scope: !44)
!46 = !DILocation(line: 9, column: 3, scope: !38)
!47 = !DILocalVariable(name: "i", scope: !48, file: !1, line: 10, type: !12)
!48 = distinct !DILexicalBlock(scope: !44, file: !1, line: 10, column: 5)
!49 = !DILocation(line: 10, column: 14, scope: !48)
!50 = !DILocation(line: 10, column: 10, scope: !48)
!51 = !DILocation(line: 10, column: 21, scope: !52)
!52 = !DILexicalBlockFile(scope: !53, file: !1, discriminator: 5)
!53 = !DILexicalBlockFile(scope: !54, file: !1, discriminator: 1)
!54 = distinct !DILexicalBlock(scope: !48, file: !1, line: 10, column: 5)
!55 = !DILocation(line: 10, column: 23, scope: !54)
!56 = !DILocation(line: 10, column: 5, scope: !48)
!57 = !DILocation(line: 10, column: 49, scope: !58)
!58 = !DILexicalBlockFile(scope: !54, file: !1, discriminator: 2)
!59 = !DILocation(line: 10, column: 45, scope: !54)
!60 = !DILocation(line: 10, column: 42, scope: !54)
!61 = !DILocation(line: 10, column: 38, scope: !54)
!62 = !DILocation(line: 10, column: 34, scope: !63)
!63 = !DILexicalBlockFile(scope: !54, file: !1, discriminator: 4)
!64 = !DILocation(line: 10, column: 5, scope: !54)
!65 = !DILocation(line: 10, column: 50, scope: !66)
!66 = !DILexicalBlockFile(scope: !48, file: !1, discriminator: 3)
!67 = !DILocation(line: 9, column: 30, scope: !44)
!68 = !DILocation(line: 9, column: 3, scope: !44)
!69 = !DILocation(line: 11, column: 10, scope: !9)
!70 = !DILocation(line: 11, column: 14, scope: !9)
!71 = !DILocation(line: 11, column: 3, scope: !9)