Skip to content

Commit

Permalink
[gcov] Support big-endian .gcno and simplify version handling in .gcda
Browse files Browse the repository at this point in the history
  • Loading branch information
MaskRay committed Jun 6, 2020
1 parent 515bfc6 commit cdd683b
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 74 deletions.
11 changes: 8 additions & 3 deletions clang/test/CodeGen/code-coverage.c
Expand Up @@ -35,13 +35,18 @@ int test2(int b) {
return b * 2;
}

// 402: private unnamed_addr constant [5 x i8] c"*204\00"
// 407: private unnamed_addr constant [5 x i8] c"*704\00"
// 408: private unnamed_addr constant [5 x i8] c"*804\00"

// CHECK: @__llvm_internal_gcov_emit_function_args.0 = internal unnamed_addr constant [2 x %0]
// CHECK-SAME: [%0 zeroinitializer, %0 { i32 1, i32 0, i32 0 }]

// CHECK: @__llvm_internal_gcov_emit_file_info = internal unnamed_addr constant [1 x %2]
/// 0x3430322a '4' '0' '2' '*'
// 402-SAME: i32 875573802
/// 0x3430372a '4' '0' '7' '*'
// 407-SAME: i32 875575082
/// 0x3430382a '4' '0' '8' '*'
// 408-SAME: i32 875575338

// Check that the noredzone flag is set on the generated functions.

// CHECK: void @__llvm_gcov_writeout() unnamed_addr [[NRZ:#[0-9]+]]
Expand Down
23 changes: 9 additions & 14 deletions compiler-rt/lib/profile/GCDAProfiling.c
Expand Up @@ -349,7 +349,7 @@ static void unmap_file() {
* started at a time.
*/
COMPILER_RT_VISIBILITY
void llvm_gcda_start_file(const char *orig_filename, const char version[4],
void llvm_gcda_start_file(const char *orig_filename, uint32_t version,
uint32_t checksum) {
const char *mode = "r+b";
filename = mangle_filename(orig_filename);
Expand Down Expand Up @@ -406,20 +406,15 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4],
}

/* gcda file, version, stamp checksum. */
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
gcov_version = version[3] >= 'A'
? (version[3] - 'A') * 100 + (version[2] - '0') * 10 +
version[1] - '0'
: (version[3] - '0') * 10 + version[1] - '0';
#else
gcov_version = version[0] >= 'A'
? (version[0] - 'A') * 100 + (version[1] - '0') * 10 +
version[2] - '0'
: (version[0] - '0') * 10 + version[2] - '0';
#endif

{
uint8_t c3 = version >> 24;
uint8_t c2 = (version >> 16) & 255;
uint8_t c1 = (version >> 8) & 255;
gcov_version = c3 >= 'A' ? (c3 - 'A') * 100 + (c2 - '0') * 10 + c1 - '0'
: (c3 - '0') * 10 + c1 - '0';
}
write_32bit_value(GCOV_DATA_MAGIC);
write_bytes(version, 4);
write_32bit_value(version);
write_32bit_value(checksum);

#ifdef DEBUG_GCDAPROFILING
Expand Down
115 changes: 62 additions & 53 deletions llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
Expand Up @@ -45,10 +45,19 @@
#include <memory>
#include <string>
#include <utility>

using namespace llvm;
namespace endian = llvm::support::endian;

#define DEBUG_TYPE "insert-gcov-profiling"

enum : uint32_t {
GCOV_TAG_FUNCTION = 0x01000000,
GCOV_TAG_BLOCKS = 0x01410000,
GCOV_TAG_ARCS = 0x01430000,
GCOV_TAG_LINES = 0x01450000,
};

static cl::opt<std::string> DefaultGCOVVersion("default-gcov-version",
cl::init("408*"), cl::Hidden,
cl::ValueRequired);
Expand All @@ -73,15 +82,7 @@ class GCOVFunction;
class GCOVProfiler {
public:
GCOVProfiler() : GCOVProfiler(GCOVOptions::getDefault()) {}
GCOVProfiler(const GCOVOptions &Opts) : Options(Opts) {
assert((Options.EmitNotes || Options.EmitData) &&
"GCOVProfiler asked to do nothing?");
ReversedVersion[0] = Options.Version[3];
ReversedVersion[1] = Options.Version[2];
ReversedVersion[2] = Options.Version[1];
ReversedVersion[3] = Options.Version[0];
ReversedVersion[4] = '\0';
}
GCOVProfiler(const GCOVOptions &Opts) : Options(Opts) {}
bool
runOnModule(Module &M,
std::function<const TargetLibraryInfo &(Function &F)> GetTLI);
Expand Down Expand Up @@ -120,8 +121,6 @@ class GCOVProfiler {

GCOVOptions Options;

// Reversed, NUL-terminated copy of Options.Version.
char ReversedVersion[5];
// Checksum, produced by hash of EdgeDestinations
SmallVector<uint32_t, 4> FileChecksums;

Expand Down Expand Up @@ -196,20 +195,17 @@ static SmallString<128> getFilename(const DISubprogram *SP) {

namespace {
class GCOVRecord {
protected:
static const char *const LinesTag;
static const char *const FunctionTag;
static const char *const BlockTag;
static const char *const EdgeTag;
protected:
support::endianness Endian;

GCOVRecord() = default;
GCOVRecord(support::endianness Endian) : Endian(Endian) {}

void writeBytes(const char *Bytes, int Size) {
os->write(Bytes, Size);
}
void writeBytes(const char *Bytes, int Size) { os->write(Bytes, Size); }

void write(uint32_t i) {
writeBytes(reinterpret_cast<char*>(&i), 4);
char Bytes[4];
endian::write32(Bytes, i, Endian);
os->write(Bytes, 4);
}

// Returns the length measured in 4-byte blocks that will be used to
Expand All @@ -234,10 +230,6 @@ namespace {

raw_ostream *os;
};
const char *const GCOVRecord::LinesTag = "\0\0\x45\x01";
const char *const GCOVRecord::FunctionTag = "\0\0\0\1";
const char *const GCOVRecord::BlockTag = "\0\0\x41\x01";
const char *const GCOVRecord::EdgeTag = "\0\0\x43\x01";

class GCOVFunction;
class GCOVBlock;
Expand All @@ -264,7 +256,8 @@ namespace {
write(Lines[i]);
}

GCOVLines(StringRef F, raw_ostream *os) : Filename(std::string(F)) {
GCOVLines(StringRef F, raw_ostream *os, support::endianness Endian)
: GCOVRecord(Endian), Filename(std::string(F)) {
this->os = os;
}

Expand All @@ -280,7 +273,8 @@ namespace {
class GCOVBlock : public GCOVRecord {
public:
GCOVLines &getFile(StringRef Filename) {
return LinesByFile.try_emplace(Filename, Filename, os).first->second;
return LinesByFile.try_emplace(Filename, Filename, os, Endian)
.first->second;
}

void addEdge(GCOVBlock &Successor) {
Expand All @@ -295,7 +289,7 @@ namespace {
SortedLinesByFile.push_back(&I);
}

writeBytes(LinesTag, 4);
write(GCOV_TAG_LINES);
write(Len);
write(Number);

Expand All @@ -320,8 +314,8 @@ namespace {
private:
friend class GCOVFunction;

GCOVBlock(uint32_t Number, raw_ostream *os)
: Number(Number) {
GCOVBlock(uint32_t Number, raw_ostream *os, support::endianness Endian)
: GCOVRecord(Endian), Number(Number) {
this->os = os;
}

Expand All @@ -334,11 +328,13 @@ namespace {
// set of blocks and a map of edges between blocks. This is the only GCOV
// object users can construct, the blocks and lines will be rooted here.
class GCOVFunction : public GCOVRecord {
public:
GCOVFunction(const DISubprogram *SP, Function *F, raw_ostream *os,
uint32_t Ident, bool UseCfgChecksum, bool ExitBlockBeforeBody)
: SP(SP), Ident(Ident), UseCfgChecksum(UseCfgChecksum), CfgChecksum(0),
ReturnBlock(1, os) {
public:
GCOVFunction(const DISubprogram *SP, Function *F, raw_ostream *os,
support::endianness Endian, uint32_t Ident,
bool UseCfgChecksum, bool ExitBlockBeforeBody)
: GCOVRecord(Endian), SP(SP), Ident(Ident),
UseCfgChecksum(UseCfgChecksum), CfgChecksum(0),
ReturnBlock(1, os, Endian) {
this->os = os;

LLVM_DEBUG(dbgs() << "Function: " << getFunctionName(SP) << "\n");
Expand All @@ -348,7 +344,7 @@ namespace {
// Skip index 1 if it's assigned to the ReturnBlock.
if (i == 1 && ExitBlockBeforeBody)
++i;
Blocks.insert(std::make_pair(&BB, GCOVBlock(i++, os)));
Blocks.insert(std::make_pair(&BB, GCOVBlock(i++, os, Endian)));
}
if (!ExitBlockBeforeBody)
ReturnBlock.Number = i;
Expand Down Expand Up @@ -389,7 +385,7 @@ namespace {
}

void writeOut() {
writeBytes(FunctionTag, 4);
write(GCOV_TAG_FUNCTION);
SmallString<128> Filename = getFilename(SP);
uint32_t BlockLen = 1 + 1 + 1 + lengthOfGCOVString(getFunctionName(SP)) +
1 + lengthOfGCOVString(Filename) + 1;
Expand All @@ -405,7 +401,7 @@ namespace {
write(SP->getLine());

// Emit count of blocks.
writeBytes(BlockTag, 4);
write(GCOV_TAG_BLOCKS);
write(Blocks.size() + 1);
for (int i = 0, e = Blocks.size() + 1; i != e; ++i) {
write(0); // No flags on our blocks.
Expand All @@ -419,7 +415,7 @@ namespace {
GCOVBlock &Block = getBlock(&I);
if (Block.OutEdges.empty()) continue;

writeBytes(EdgeTag, 4);
write(GCOV_TAG_ARCS);
write(Block.OutEdges.size() * 2 + 1);
write(Block.Number);
for (int i = 0, e = Block.OutEdges.size(); i != e; ++i) {
Expand All @@ -435,8 +431,8 @@ namespace {
getBlock(&I).writeOut();
}

private:
const DISubprogram *SP;
private:
const DISubprogram *SP;
uint32_t Ident;
uint32_t FuncChecksum;
bool UseCfgChecksum;
Expand Down Expand Up @@ -726,6 +722,9 @@ void GCOVProfiler::emitProfileNotes() {

std::string EdgeDestinations;

auto Endian = M->getDataLayout().isLittleEndian()
? support::endianness::little
: support::endianness::big;
unsigned FunctionIdent = 0;
for (auto &F : M->functions()) {
DISubprogram *SP = F.getSubprogram();
Expand All @@ -745,8 +744,9 @@ void GCOVProfiler::emitProfileNotes() {

bool UseCfgChecksum = strncmp(Options.Version, "407", 3) >= 0;
bool ExitBlockBeforeBody = strncmp(Options.Version, "408", 3) >= 0;
Funcs.push_back(std::make_unique<GCOVFunction>(
SP, &F, &out, FunctionIdent++, UseCfgChecksum, ExitBlockBeforeBody));
Funcs.push_back(
std::make_unique<GCOVFunction>(SP, &F, &out, Endian, FunctionIdent++,
UseCfgChecksum, ExitBlockBeforeBody));
GCOVFunction &Func = *Funcs.back();

// Add the function line number to the lines of the entry block
Expand Down Expand Up @@ -795,10 +795,18 @@ void GCOVProfiler::emitProfileNotes() {
EdgeDestinations += Func.getEdgeDestinations();
}

char Tmp[4];
FileChecksums.push_back(hash_value(EdgeDestinations));
out.write("oncg", 4);
out.write(ReversedVersion, 4);
out.write(reinterpret_cast<char*>(&FileChecksums.back()), 4);
if (Endian == support::endianness::big) {
out.write("gcno", 4);
out.write(Options.Version, 4);
} else {
out.write("oncg", 4);
std::reverse_copy(Options.Version, Options.Version + 4, Tmp);
out.write(Tmp, 4);
}
endian::write32(Tmp, FileChecksums.back(), Endian);
out.write(Tmp, 4);

for (auto &Func : Funcs) {
Func->setCfgChecksum(FileChecksums.back());
Expand Down Expand Up @@ -926,9 +934,9 @@ bool GCOVProfiler::emitProfileArcs() {

FunctionCallee GCOVProfiler::getStartFileFunc(const TargetLibraryInfo *TLI) {
Type *Args[] = {
Type::getInt8PtrTy(*Ctx), // const char *orig_filename
Type::getInt8PtrTy(*Ctx), // const char version[4]
Type::getInt32Ty(*Ctx), // uint32_t checksum
Type::getInt8PtrTy(*Ctx), // const char *orig_filename
Type::getInt32Ty(*Ctx), // uint32_t version
Type::getInt32Ty(*Ctx), // uint32_t checksum
};
FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), Args, false);
AttributeList AL;
Expand Down Expand Up @@ -1008,7 +1016,7 @@ Function *GCOVProfiler::insertCounterWriteout(
// Collect the relevant data into a large constant data structure that we can
// walk to write out everything.
StructType *StartFileCallArgsTy = StructType::create(
{Builder.getInt8PtrTy(), Builder.getInt8PtrTy(), Builder.getInt32Ty()});
{Builder.getInt8PtrTy(), Builder.getInt32Ty(), Builder.getInt32Ty()});
StructType *EmitFunctionCallArgsTy = StructType::create(
{Builder.getInt32Ty(), Builder.getInt32Ty(), Builder.getInt32Ty()});
StructType *EmitArcsCallArgsTy = StructType::create(
Expand All @@ -1033,9 +1041,10 @@ Function *GCOVProfiler::insertCounterWriteout(
std::string FilenameGcda = mangleName(CU, GCovFileType::GCDA);
uint32_t CfgChecksum = FileChecksums.empty() ? 0 : FileChecksums[i];
auto *StartFileCallArgs = ConstantStruct::get(
StartFileCallArgsTy, {Builder.CreateGlobalStringPtr(FilenameGcda),
Builder.CreateGlobalStringPtr(ReversedVersion),
Builder.getInt32(CfgChecksum)});
StartFileCallArgsTy,
{Builder.CreateGlobalStringPtr(FilenameGcda),
Builder.getInt32(endian::read32be(Options.Version)),
Builder.getInt32(CfgChecksum)});

SmallVector<Constant *, 8> EmitFunctionCallArgsArray;
SmallVector<Constant *, 8> EmitArcsCallArgsArray;
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/Transforms/GCOVProfiling/function-numbering.ll
Expand Up @@ -32,10 +32,10 @@ target triple = "x86_64-apple-macosx10.10.0"
; GCDA-NEXT: %[[START_FILE_ARG_0_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[START_FILE_ARGS]], i32 0, i32 0
; GCDA-NEXT: %[[START_FILE_ARG_0:.*]] = load i8*, i8** %[[START_FILE_ARG_0_PTR]]
; GCDA-NEXT: %[[START_FILE_ARG_1_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[START_FILE_ARGS]], i32 0, i32 1
; GCDA-NEXT: %[[START_FILE_ARG_1:.*]] = load i8*, i8** %[[START_FILE_ARG_1_PTR]]
; GCDA-NEXT: %[[START_FILE_ARG_1:.*]] = load i32, i32* %[[START_FILE_ARG_1_PTR]]
; GCDA-NEXT: %[[START_FILE_ARG_2_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[START_FILE_ARGS]], i32 0, i32 2
; GCDA-NEXT: %[[START_FILE_ARG_2:.*]] = load i32, i32* %[[START_FILE_ARG_2_PTR]]
; GCDA-NEXT: call void @llvm_gcda_start_file(i8* %[[START_FILE_ARG_0]], i8* %[[START_FILE_ARG_1]], i32 %[[START_FILE_ARG_2]])
; GCDA-NEXT: call void @llvm_gcda_start_file(i8* %[[START_FILE_ARG_0]], i32 %[[START_FILE_ARG_1]], i32 %[[START_FILE_ARG_2]])
; GCDA-NEXT: %[[NUM_COUNTERS_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[FILE_INFO]], i32 0, i32 1
; GCDA-NEXT: %[[NUM_COUNTERS:.*]] = load i32, i32* %[[NUM_COUNTERS_PTR]]
; GCDA-NEXT: %[[EMIT_FUN_ARGS_ARRAY_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[FILE_INFO]], i32 0, i32 2
Expand Down
11 changes: 9 additions & 2 deletions llvm/test/Transforms/GCOVProfiling/version.ll
@@ -1,6 +1,9 @@
; RUN: rm -rf %t && mkdir -p %t
; RUN: echo '!9 = !{!"%/t/version.ll", !0}' > %t/1
; RUN: cat %s %t/1 > %t/2
; RUN: echo 'target datalayout = "e"' > %t/little.txt
; RUN: echo 'target datalayout = "E"' > %t/big.txt
; RUN: echo '!9 = !{!"%/t/version.ll", !0}' > %t/version.txt
; RUN: cat %t/little.txt %s %t/version.txt > %t/2

; RUN: opt -insert-gcov-profiling -disable-output < %t/2
; RUN: head -c8 %t/version.gcno | grep '^oncg.804'
; RUN: rm %t/version.gcno
Expand All @@ -17,6 +20,10 @@
; RUN: head -c8 %t/version.gcno | grep '^oncg.204'
; RUN: rm %t/version.gcno

; RUN: cat %t/big.txt %s %t/version.txt > %t/big.ll
; RUN: opt -insert-gcov-profiling -disable-output < %t/big.ll
; RUN: head -c8 %t/version.gcno | grep '^gcno408.'

define void @test() !dbg !5 {
ret void, !dbg !8
}
Expand Down

0 comments on commit cdd683b

Please sign in to comment.