1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
|
//===--- GenCoverage.cpp - IR Generation for coverage ---------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://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/AST/IRGenOptions.h"
#include "swift/SIL/SILModule.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/FileSystem.h"
using namespace swift;
using namespace irgen;
using llvm::coverage::CounterMappingRegion;
using llvm::coverage::CovMapVersion;
// This affects the coverage mapping format defined when `InstrProfData.inc`
// is textually included. Note that it means 'version >= 3', not 'version == 3'.
#define COVMAP_V3
/// This assert is here to make sure we make all the necessary code generation
/// changes that are needed to support the new coverage mapping format. Note we
/// cannot pin our version, as it must remain in sync with the version Clang is
/// using.
/// Do not bump without at least filing a bug and pinging a coverage maintainer.
static_assert(CovMapVersion::CurrentVersion == CovMapVersion::Version6,
"Coverage mapping emission needs updating");
static std::string getInstrProfSection(IRGenModule &IGM,
llvm::InstrProfSectKind SK) {
return llvm::getInstrProfSectionName(SK, IGM.Triple.getObjectFormat());
}
void IRGenModule::emitCoverageMaps(ArrayRef<const SILCoverageMap *> Mappings) {
// If there aren't any coverage maps, there's nothing to emit.
if (Mappings.empty())
return;
SmallVector<llvm::Constant *, 4> UnusedFuncNames;
for (const auto *Mapping : Mappings) {
auto FuncName = Mapping->getPGOFuncName();
auto VarLinkage = llvm::GlobalValue::LinkOnceAnyLinkage;
auto FuncNameVarName = llvm::getPGOFuncNameVarName(FuncName, VarLinkage);
// Check whether this coverage mapping can reference its name data within
// the profile symbol table. If the name global isn't there, this function
// has been optimized out. We need to tell LLVM about it by emitting the
// name data separately.
if (!Module.getNamedGlobal(FuncNameVarName)) {
auto *Var = llvm::createPGOFuncNameVar(Module, VarLinkage, FuncName);
UnusedFuncNames.push_back(llvm::ConstantExpr::getBitCast(Var, Int8PtrTy));
}
}
// Emit the name data for any unused functions.
if (!UnusedFuncNames.empty()) {
auto NamePtrsTy = llvm::ArrayType::get(Int8PtrTy, UnusedFuncNames.size());
auto NamePtrs = llvm::ConstantArray::get(NamePtrsTy, UnusedFuncNames);
// Note we don't mark this variable as used, as it doesn't need to be
// present in the object file, it gets picked up by the LLVM instrumentation
// lowering pass.
new llvm::GlobalVariable(Module, NamePtrsTy, /*IsConstant*/ true,
llvm::GlobalValue::InternalLinkage, NamePtrs,
llvm::getCoverageUnusedNamesVarName());
}
llvm::DenseMap<StringRef, unsigned> RawFileIndices;
llvm::SmallVector<StringRef, 8> RawFiles;
for (const auto &M : Mappings) {
auto Filename = M->getFilename();
auto Inserted = RawFileIndices.insert({Filename, RawFiles.size()}).second;
if (!Inserted)
continue;
RawFiles.push_back(Filename);
}
const auto &Remapper = getOptions().CoveragePrefixMap;
llvm::SmallVector<std::string, 8> FilenameStrs;
FilenameStrs.reserve(RawFiles.size() + 1);
// First element needs to be the current working directory. Note if this
// scheme ever changes, the FileID computation below will need updating.
SmallString<256> WorkingDirectory;
llvm::sys::fs::current_path(WorkingDirectory);
FilenameStrs.emplace_back(Remapper.remapPath(WorkingDirectory));
// Following elements are the filenames present. We use their relative path,
// which llvm-cov will turn back into absolute paths using the working
// directory element.
for (auto Name : RawFiles)
FilenameStrs.emplace_back(Remapper.remapPath(Name));
// Encode the filenames.
std::string Filenames;
llvm::LLVMContext &Ctx = getLLVMContext();
{
llvm::raw_string_ostream OS(Filenames);
llvm::coverage::CoverageFilenamesSectionWriter(FilenameStrs).write(OS);
}
auto *FilenamesVal =
llvm::ConstantDataArray::getString(Ctx, Filenames, false);
const int64_t FilenamesRef = llvm::IndexedInstrProf::ComputeHash(Filenames);
const size_t FilenamesSize = Filenames.size();
// Emit the function records.
auto *Int32Ty = llvm::Type::getInt32Ty(Ctx);
for (const auto &M : Mappings) {
StringRef NameValue = M->getPGOFuncName();
assert(!NameValue.empty() && "Expected a named record");
uint64_t FuncHash = M->getHash();
const uint64_t NameHash = llvm::IndexedInstrProf::ComputeHash(NameValue);
std::string FuncRecordName = "__covrec_" + llvm::utohexstr(NameHash);
// The file ID needs to be bumped by 1 to account for the working directory
// as the first element.
unsigned FileID = [&]() {
auto Result = RawFileIndices.find(M->getFilename());
assert(Result != RawFileIndices.end());
return Result->second + 1;
}();
assert(FileID < FilenameStrs.size());
std::vector<CounterMappingRegion> Regions;
for (const auto &MR : M->getMappedRegions()) {
// The FileID here is 0, because it's an index into VirtualFileMapping,
// and we only ever have a single file associated for a function.
Regions.push_back(MR.getLLVMRegion(/*FileID*/ 0));
}
// Append each function's regions into the encoded buffer.
ArrayRef<unsigned> VirtualFileMapping(FileID);
llvm::coverage::CoverageMappingWriter W(VirtualFileMapping,
M->getExpressions(), Regions);
std::string CoverageMapping;
{
llvm::raw_string_ostream OS(CoverageMapping);
W.write(OS);
}
#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) LLVMType,
llvm::Type *FunctionRecordTypes[] = {
#include "llvm/ProfileData/InstrProfData.inc"
};
auto *FunctionRecordTy =
llvm::StructType::get(Ctx, llvm::ArrayRef(FunctionRecordTypes),
/*isPacked=*/true);
// Create the function record constant.
#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Init,
llvm::Constant *FunctionRecordVals[] = {
#include "llvm/ProfileData/InstrProfData.inc"
};
auto *FuncRecordConstant = llvm::ConstantStruct::get(
FunctionRecordTy, llvm::ArrayRef(FunctionRecordVals));
// Create the function record global.
auto *FuncRecord = new llvm::GlobalVariable(
*getModule(), FunctionRecordTy, /*isConstant=*/true,
llvm::GlobalValue::LinkOnceODRLinkage, FuncRecordConstant,
FuncRecordName);
FuncRecord->setVisibility(llvm::GlobalValue::HiddenVisibility);
FuncRecord->setSection(getInstrProfSection(*this, llvm::IPSK_covfun));
FuncRecord->setAlignment(llvm::Align(8));
if (Triple.supportsCOMDAT())
FuncRecord->setComdat(getModule()->getOrInsertComdat(FuncRecordName));
// Make sure the data doesn't get deleted.
addUsedGlobal(FuncRecord);
}
// Create the coverage data header.
const unsigned NRecords = 0;
const unsigned CoverageMappingSize = 0;
llvm::Type *CovDataHeaderTypes[] = {
#define COVMAP_HEADER(Type, LLVMType, Name, Init) LLVMType,
#include "llvm/ProfileData/InstrProfData.inc"
};
auto CovDataHeaderTy =
llvm::StructType::get(Ctx, llvm::ArrayRef(CovDataHeaderTypes));
llvm::Constant *CovDataHeaderVals[] = {
#define COVMAP_HEADER(Type, LLVMType, Name, Init) Init,
#include "llvm/ProfileData/InstrProfData.inc"
};
auto CovDataHeaderVal = llvm::ConstantStruct::get(
CovDataHeaderTy, llvm::ArrayRef(CovDataHeaderVals));
// Create the coverage data record
llvm::Type *CovDataTypes[] = {CovDataHeaderTy, FilenamesVal->getType()};
auto CovDataTy = llvm::StructType::get(Ctx, llvm::ArrayRef(CovDataTypes));
llvm::Constant *TUDataVals[] = {CovDataHeaderVal, FilenamesVal};
auto CovDataVal =
llvm::ConstantStruct::get(CovDataTy, llvm::ArrayRef(TUDataVals));
auto CovData = new llvm::GlobalVariable(
*getModule(), CovDataTy, true, llvm::GlobalValue::PrivateLinkage,
CovDataVal, llvm::getCoverageMappingVarName());
CovData->setSection(getInstrProfSection(*this, llvm::IPSK_covmap));
CovData->setAlignment(llvm::Align(8));
addUsedGlobal(CovData);
}
void IRGenerator::emitCoverageMapping() {
if (SIL.getCoverageMaps().empty())
return;
// Shard the coverage maps across their designated IRGenModules. This is
// necessary to ensure we don't output N copies of a coverage map when doing
// parallel IRGen, where N is the number of output object files.
//
// Note we don't just dump all the coverage maps into the primary IGM as
// that would require creating unecessary name data entries, since the name
// data is likely to already be present in the IGM that contains the entity
// being profiled (unless it has been optimized out). Matching the coverage
// map to its originating SourceFile also matches the behavior of a debug
// build where the files are compiled separately.
llvm::DenseMap<IRGenModule *, std::vector<const SILCoverageMap *>> MapsToEmit;
for (const auto &M : SIL.getCoverageMaps()) {
auto &Mapping = M.second;
auto *SF = Mapping->getParentSourceFile();
MapsToEmit[getGenModule(SF)].push_back(Mapping);
}
for (auto &IGMPair : *this) {
auto *IGM = IGMPair.second;
IGM->emitCoverageMaps(MapsToEmit[IGM]);
}
}
|