mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Changes to the coverage mapping API in upstream llvm have caused merge
conflicts and breakage in swift. This commit phases in bits of the new
API in a way that can be safely cherry-picked to swift-{2.2,3.0}-branch.
This commit is NFC for master-next and swift-2.2-branch, but constitutes
an actual bugfix for 3.0. That's because 3.0 is paired with a version of
llvm that takes advantage of the new coverage API to reduce the size of
instrumented binaries.
This effectively closes Swift PR-1053.
(cherry picked from commit a079d313fe)
156 lines
5.9 KiB
C++
156 lines
5.9 KiB
C++
//===--- 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 "SwiftTargetInfo.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"
|
|
#include "llvm/ProfileData/InstrProf.h"
|
|
|
|
using namespace swift;
|
|
using namespace irgen;
|
|
|
|
using llvm::coverage::CounterMappingRegion;
|
|
|
|
static bool isMachO(IRGenModule &IGM) {
|
|
return SwiftTargetInfo::get(IGM).OutputObjectFormat == llvm::Triple::MachO;
|
|
}
|
|
|
|
static StringRef getCoverageSection(IRGenModule &IGM) {
|
|
return llvm::getInstrProfCoverageSectionName(isMachO(IGM));
|
|
}
|
|
|
|
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.
|
|
llvm::LLVMContext &Ctx = LLVMContext;
|
|
auto *Int32Ty = llvm::Type::getInt32Ty(Ctx);
|
|
auto *Int64Ty = llvm::Type::getInt64Ty(Ctx);
|
|
auto *Int8PtrTy = llvm::Type::getInt8PtrTy(Ctx);
|
|
|
|
#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) LLVMType,
|
|
llvm::Type *FunctionRecordTypes[] = {
|
|
#include "llvm/ProfileData/InstrProfData.inc"
|
|
};
|
|
|
|
auto FunctionRecordTy =
|
|
llvm::StructType::get(Ctx, llvm::makeArrayRef(FunctionRecordTypes),
|
|
/*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(Ctx, M.getName(), false);
|
|
auto *NameVar = new llvm::GlobalVariable(
|
|
*getModule(), NameVal->getType(), true,
|
|
llvm::GlobalValue::LinkOnceAnyLinkage, NameVal,
|
|
llvm::getInstrProfNameVarPrefix() + M.getName());
|
|
|
|
CurrentSize = OS.str().size();
|
|
// Create a record for this function.
|
|
llvm::Constant *FunctionRecordVals[] = {
|
|
llvm::ConstantExpr::getBitCast(NameVar, Int8PtrTy),
|
|
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(Ctx, 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(Ctx, {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::getCoverageMappingVarName());
|
|
CovData->setSection(getCoverageSection(*this));
|
|
CovData->setAlignment(8);
|
|
|
|
addUsedGlobal(CovData);
|
|
}
|