blob: acfb5ca0a440f1ced65d1bb248a63909ea5b9d76 [file] [log] [blame]
//===--- GenCoverage.cpp - IR Generation for coverage ---------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements IR generation for the initialization of
// coverage related variables.
//
//===----------------------------------------------------------------------===//
#include "IRGenModule.h"
#include "swift/SIL/SILModule.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/ProfileData/CoverageMappingWriter.h"
#include "llvm/Support/FileSystem.h"
using namespace swift;
using namespace irgen;
using llvm::coverage::CounterMappingRegion;
void IRGenModule::emitCoverageMapping() {
const auto &Mappings = SILMod->getCoverageMapList();
// If there aren't any coverage maps, there's nothing to emit.
if (Mappings.empty())
return;
std::vector<StringRef> Files;
for (const auto &M : Mappings)
if (std::find(Files.begin(), Files.end(), M.getFile()) == Files.end())
Files.push_back(M.getFile());
// Awkwardly munge absolute filenames into a vector of StringRefs.
// TODO: This is heinous - the same thing is happening in clang, but the API
// really needs to be cleaned up for both.
llvm::SmallVector<std::string, 8> FilenameStrs;
llvm::SmallVector<StringRef, 8> FilenameRefs;
for (StringRef Name : Files) {
llvm::SmallString<256> Path(Name);
llvm::sys::fs::make_absolute(Path);
FilenameStrs.push_back(std::string(Path.begin(), Path.end()));
FilenameRefs.push_back(FilenameStrs.back());
}
// Encode the filenames first.
std::string EncodedDataBuf;
llvm::raw_string_ostream OS(EncodedDataBuf);
llvm::coverage::CoverageFilenamesSectionWriter(FilenameRefs).write(OS);
size_t FilenamesSize = OS.str().size();
size_t CurrentSize, PrevSize = FilenamesSize;
// Now we need to build up the list of function records.
auto *Int32Ty = llvm::Type::getInt32Ty(LLVMContext);
auto *Int64Ty = llvm::Type::getInt64Ty(LLVMContext);
auto *Int8PtrTy = llvm::Type::getInt8PtrTy(LLVMContext);
auto *FunctionRecordTy = llvm::StructType::get(
LLVMContext, {Int8PtrTy, Int32Ty, Int32Ty, Int64Ty}, /*isPacked=*/true);
std::vector<llvm::Constant *> FunctionRecords;
std::vector<CounterMappingRegion> Regions;
for (const auto &M : Mappings) {
unsigned FileID =
std::find(Files.begin(), Files.end(), M.getFile()) - Files.begin();
Regions.clear();
for (const auto &MR : M.getMappedRegions())
Regions.emplace_back(CounterMappingRegion::makeRegion(
MR.Counter, /*FileID=*/0, MR.StartLine, MR.StartCol, MR.EndLine,
MR.EndCol));
// Append each function's regions into the encoded buffer.
llvm::coverage::CoverageMappingWriter W({FileID}, M.getExpressions(),
Regions);
W.write(OS);
auto *NameVal =
llvm::ConstantDataArray::getString(LLVMContext, M.getName(), false);
auto *NameVar =
new llvm::GlobalVariable(*getModule(), NameVal->getType(), true,
llvm::GlobalValue::LinkOnceAnyLinkage, NameVal,
"__llvm_profile_name_" + M.getName());
CurrentSize = OS.str().size();
// Create a record for this function.
llvm::Constant *FunctionRecordVals[] = {
llvm::ConstantExpr::getBitCast(NameVar, Int8PtrTy),
// TODO: We're including the null to match the profile, but we should
// really skip the null in the profile instead.
llvm::ConstantInt::get(Int32Ty, M.getName().size()),
llvm::ConstantInt::get(Int32Ty, CurrentSize - PrevSize),
llvm::ConstantInt::get(Int64Ty, M.getHash())};
FunctionRecords.push_back(llvm::ConstantStruct::get(
FunctionRecordTy, makeArrayRef(FunctionRecordVals)));
PrevSize = CurrentSize;
}
size_t CoverageMappingSize = PrevSize - FilenamesSize;
// Append extra zeroes if necessary to ensure that the size of the filenames
// and coverage mappings is a multiple of 8.
if (size_t Rem = OS.str().size() % 8) {
CoverageMappingSize += 8 - Rem;
for (size_t I = 0, S = 8 - Rem; I < S; ++I)
OS << '\0';
}
auto *EncodedData =
llvm::ConstantDataArray::getString(LLVMContext, OS.str(), false);
auto *RecordsTy =
llvm::ArrayType::get(FunctionRecordTy, FunctionRecords.size());
auto *RecordsVal = llvm::ConstantArray::get(RecordsTy, FunctionRecords);
// Now we embed everything into a constant with a well-known name.
auto *CovDataTy =
llvm::StructType::get(LLVMContext, {Int32Ty, Int32Ty, Int32Ty, Int32Ty,
RecordsTy, EncodedData->getType()});
llvm::Constant *TUDataVals[] = {
llvm::ConstantInt::get(Int32Ty, FunctionRecords.size()),
llvm::ConstantInt::get(Int32Ty, FilenamesSize),
llvm::ConstantInt::get(Int32Ty, CoverageMappingSize),
llvm::ConstantInt::get(Int32Ty, llvm::coverage::CoverageMappingVersion1),
RecordsVal, EncodedData};
auto CovDataVal =
llvm::ConstantStruct::get(CovDataTy, makeArrayRef(TUDataVals));
auto CovData = new llvm::GlobalVariable(
*getModule(), CovDataTy, true, llvm::GlobalValue::InternalLinkage,
CovDataVal, "__llvm_coverage_mapping");
addUsedGlobal(CovData);
}