blob: 52663cb8b3293a051910fea1c7bb46aeef73247e [file]
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "CoverageMappingC.h"
#include <llvm/ProfileData/Coverage/CoverageMapping.h>
#include <llvm/ProfileData/Coverage/CoverageMappingWriter.h>
#include <llvm/IR/GlobalVariable.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/ADT/Triple.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Type.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/Intrinsics.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/Analysis/TargetLibraryInfo.h>
#include <llvm/Transforms/Instrumentation.h>
#include <llvm/Transforms/ObjCARC.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/Path.h>
#include <utility>
#include <string>
#include <vector>
#include <iostream>
#include <iomanip>
using namespace llvm;
using namespace llvm::coverage;
namespace llvm {
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(TargetLibraryInfoImpl, LLVMTargetLibraryInfoRef)
}
struct LLVMFunctionCoverage {
explicit LLVMFunctionCoverage(std::string coverageData) : coverageData(std::move(coverageData)) {}
std::string coverageData;
};
static coverage::CounterMappingRegion::RegionKind determineRegionKind(const struct LLVMCoverageRegion& region) {
switch (region.kind) {
case LLVMCoverageRegionKind::CODE:
return coverage::CounterMappingRegion::RegionKind::CodeRegion;
case LLVMCoverageRegionKind::GAP:
return coverage::CounterMappingRegion::RegionKind::GapRegion;
case LLVMCoverageRegionKind::EXPANSION:
return coverage::CounterMappingRegion::RegionKind::ExpansionRegion;
}
}
static coverage::CounterMappingRegion createCounterMappingRegion(struct LLVMCoverageRegion& region) {
auto regionKind = determineRegionKind(region);
int expandedFileId = 0;
if (regionKind == coverage::CounterMappingRegion::RegionKind::ExpansionRegion) {
expandedFileId = region.expandedFileId;
}
const Counter &counter = coverage::Counter::getCounter(region.counterId);
return coverage::CounterMappingRegion(counter, region.fileId, expandedFileId, region.lineStart,
region.columnStart, region.lineEnd, region.columnEnd, regionKind);
}
LLVMFunctionCoverage* LLVMWriteCoverageRegionMapping(unsigned int *fileIdMapping, size_t fileIdMappingSize,
struct LLVMCoverageRegion **mappingRegions, size_t mappingRegionsSize) {
std::vector<coverage::CounterMappingRegion> counterMappingRegions;
for (size_t i = 0; i < mappingRegionsSize; ++i) {
struct LLVMCoverageRegion region = *mappingRegions[i];
counterMappingRegions.emplace_back(createCounterMappingRegion(region));
}
CoverageMappingWriter writer(ArrayRef<unsigned int>(fileIdMapping, fileIdMappingSize), None, counterMappingRegions);
std::string CoverageMapping;
raw_string_ostream OS(CoverageMapping);
writer.write(OS);
OS.flush();
// Should be disposed with `LLVMFunctionCoverageDispose`.
return new LLVMFunctionCoverage(CoverageMapping);
}
void LLVMFunctionCoverageDispose(struct LLVMFunctionCoverage* functionCoverage) {
delete functionCoverage;
}
static StructType *getFunctionRecordTy(LLVMContext &Ctx) {
#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) LLVMType,
Type *FunctionRecordTypes[] = {
#include "llvm/ProfileData/InstrProfData.inc"
};
StructType *FunctionRecordTy = StructType::get(Ctx, makeArrayRef(FunctionRecordTypes), true);
return FunctionRecordTy;
}
static llvm::Constant *addFunctionMappingRecord(llvm::LLVMContext &Ctx, StringRef NameValue, uint64_t FuncHash,
const std::string &CoverageMapping) {
llvm::StructType *FunctionRecordTy = getFunctionRecordTy(Ctx);
#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Init,
llvm::Constant *FunctionRecordVals[] = {
#include "llvm/ProfileData/InstrProfData.inc"
};
return llvm::ConstantStruct::get(FunctionRecordTy, makeArrayRef(FunctionRecordVals));
}
// See https://github.com/llvm/llvm-project/blob/fa8fa044ec46b94e64971efa8852df0d58114062/clang/lib/CodeGen/CoverageMappingGen.cpp#L1284.
LLVMValueRef LLVMAddFunctionMappingRecord(LLVMContextRef context, const char *name, uint64_t hash,
struct LLVMFunctionCoverage *coverageMapping) {
return llvm::wrap(addFunctionMappingRecord(*llvm::unwrap(context), name, hash, coverageMapping->coverageData));
}
// See https://github.com/llvm/llvm-project/blob/fa8fa044ec46b94e64971efa8852df0d58114062/clang/lib/CodeGen/CoverageMappingGen.cpp#L1335.
// Please note that llvm/ProfileData/InstrProfData.inc refers to variable names of the function that includes it. So be careful with renaming.
static llvm::GlobalVariable* emitCoverageGlobal(
llvm::LLVMContext &Ctx,
llvm::Module &module,
std::vector<llvm::Constant *> &FunctionRecords,
const llvm::SmallVector<StringRef, 16> &FilenameRefs,
const std::string &RawCoverageMappings) {
auto *Int32Ty = llvm::Type::getInt32Ty(Ctx);
std::string FilenamesAndCoverageMappings;
llvm::raw_string_ostream outputStream(FilenamesAndCoverageMappings);
CoverageFilenamesSectionWriter(FilenameRefs).write(outputStream);
outputStream << RawCoverageMappings;
size_t CoverageMappingSize = RawCoverageMappings.size();
size_t FilenamesSize = outputStream.str().size() - CoverageMappingSize;
// See https://llvm.org/docs/CoverageMappingFormat.html#llvm-ir-representation
//
// > Coverage mapping data which is an array of bytes. Zero paddings are added at the end to force 8 byte alignment.
//
if (size_t rem = outputStream.str().size() % 8) {
CoverageMappingSize += 8 - rem;
for (size_t i = 0; i < 8 - rem; ++i) {
outputStream << '\0';
}
}
StructType *functionRecordTy = getFunctionRecordTy(Ctx);
// Create the deferred function records array
auto functionRecordsTy = llvm::ArrayType::get(functionRecordTy, FunctionRecords.size());
auto functionRecordsVal = llvm::ConstantArray::get(functionRecordsTy, FunctionRecords);
const unsigned NRecords = 0;
llvm::Type *CovDataHeaderTypes[] = {
#define COVMAP_HEADER(Type, LLVMType, Name, Init) LLVMType,
#include "llvm/ProfileData/InstrProfData.inc"
};
auto CovDataHeaderTy = llvm::StructType::get(Ctx, makeArrayRef(CovDataHeaderTypes));
llvm::Constant *CovDataHeaderVals[] = {
#define COVMAP_HEADER(Type, LLVMType, Name, Init) Init,
#include "llvm/ProfileData/InstrProfData.inc"
};
auto covDataHeaderVal = llvm::ConstantStruct::get(CovDataHeaderTy, makeArrayRef(CovDataHeaderVals));
auto *filenamesAndMappingsVal = llvm::ConstantDataArray::getString(Ctx, outputStream.str(), false);
// Create the coverage data record
llvm::Type *covDataTypes[] = {CovDataHeaderTy, functionRecordsTy, filenamesAndMappingsVal->getType()};
auto covDataTy = llvm::StructType::get(Ctx, makeArrayRef(covDataTypes));
llvm::Constant *TUDataVals[] = {covDataHeaderVal, functionRecordsVal, filenamesAndMappingsVal};
auto covDataVal = llvm::ConstantStruct::get(covDataTy, makeArrayRef(TUDataVals));
// Will be deleted when module is disposed.
return new llvm::GlobalVariable(module, covDataTy, true, llvm::GlobalValue::InternalLinkage,
covDataVal, llvm::getCoverageMappingVarName());
}
static std::string createRawCoverageMapping(struct LLVMFunctionCoverage **functionCoverages, size_t functionCoveragesSize) {
std::vector<std::string> coverageMappings;
for (size_t i = 0; i < functionCoveragesSize; ++i) {
coverageMappings.emplace_back(functionCoverages[i]->coverageData);
}
return llvm::join(coverageMappings.begin(), coverageMappings.end(), "");
}
LLVMValueRef LLVMCoverageEmit(LLVMModuleRef moduleRef,
LLVMValueRef *records, size_t recordsSize,
const char **filenames, int *filenamesIndices, size_t filenamesSize,
struct LLVMFunctionCoverage **functionCoverages, size_t functionCoveragesSize) {
LLVMContext &ctx = *unwrap(LLVMGetModuleContext(moduleRef));
Module &module = *unwrap(moduleRef);
std::vector<Constant *> functionRecords;
for (size_t i = 0; i < recordsSize; ++i) {
functionRecords.push_back(dyn_cast<Constant>(unwrap(records[i])));
}
llvm::SmallVector<StringRef, 16> filenameRefs;
filenameRefs.resize(filenamesSize);
for (size_t i = 0; i < filenamesSize; ++i) {
if (sys::path::is_absolute(filenames[i])) {
filenameRefs[filenamesIndices[i]] = filenames[i];
} else {
SmallString<256> path(filenames[i]);
sys::fs::make_absolute(path);
sys::path::remove_dots(path, true);
filenameRefs[filenamesIndices[i]] = path;
}
}
const std::string &rawCoverageMappings = createRawCoverageMapping(functionCoverages, functionCoveragesSize);
GlobalVariable *coverageGlobal = emitCoverageGlobal(ctx, module, functionRecords, filenameRefs, rawCoverageMappings);
const std::string &section = getInstrProfSectionName(IPSK_covmap, Triple(module.getTargetTriple()).getObjectFormat());
coverageGlobal->setSection(section);
coverageGlobal->setAlignment(llvm::Align(8));
return wrap(coverageGlobal);
}
LLVMValueRef LLVMInstrProfIncrement(LLVMModuleRef moduleRef) {
Module &module = *unwrap(moduleRef);
return wrap(Intrinsic::getDeclaration(&module, Intrinsic::instrprof_increment, None));
}
LLVMValueRef LLVMCreatePGOFunctionNameVar(LLVMValueRef llvmFunction, const char *pgoFunctionName) {
auto *fnPtr = cast<llvm::Function>(unwrap(llvmFunction));
return wrap(createPGOFuncNameVar(*fnPtr, pgoFunctionName));
}
void LLVMAddInstrProfPass(LLVMPassManagerRef passManagerRef, const char* outputFileName) {
legacy::PassManagerBase *passManager = unwrap(passManagerRef);
InstrProfOptions options;
options.InstrProfileOutput = outputFileName;
passManager->add(createInstrProfilingLegacyPass(options));
}
void LLVMKotlinAddTargetLibraryInfoWrapperPass(LLVMPassManagerRef passManagerRef, const char* targetTriple) {
legacy::PassManagerBase *passManager = unwrap(passManagerRef);
passManager->add(new TargetLibraryInfoWrapperPass(Triple(targetTriple)));
}
void LLVMAddObjCARCContractPass(LLVMPassManagerRef passManagerRef) {
legacy::PassManagerBase *passManager = unwrap(passManagerRef);
passManager->add(createObjCARCContractPass());
}
void LLVMKotlinInitializeTargets() {
#define INIT_LLVM_TARGET(TargetName) \
LLVMInitialize##TargetName##TargetInfo();\
LLVMInitialize##TargetName##Target();\
LLVMInitialize##TargetName##TargetMC();
#if KONAN_MACOS
INIT_LLVM_TARGET(AArch64)
INIT_LLVM_TARGET(ARM)
INIT_LLVM_TARGET(Mips)
INIT_LLVM_TARGET(X86)
INIT_LLVM_TARGET(WebAssembly)
#elif KONAN_LINUX
INIT_LLVM_TARGET(AArch64)
INIT_LLVM_TARGET(ARM)
INIT_LLVM_TARGET(Mips)
INIT_LLVM_TARGET(X86)
INIT_LLVM_TARGET(WebAssembly)
#elif KONAN_WINDOWS
INIT_LLVM_TARGET(AArch64)
INIT_LLVM_TARGET(ARM)
INIT_LLVM_TARGET(X86)
INIT_LLVM_TARGET(WebAssembly)
#endif
#undef INIT_LLVM_TARGET
}