Files
swift-mirror/lib/IRGen/GenCoverage.cpp
Hamish Knight 6d3545f4d5 [IRGen] Don't emit multiple copies of coverage maps
Iterating over the IRGenModules and emitting every
SILCoverageMap in the SILModule meant that we
were emitting N copies of the coverage maps for
parallel IRGen, where N is the number of output
object files.

This has always been wrong, but was previously
saved by the fact that we would drop the coverage
map on the floor if we didn't have the name data
available. This would only be the case for the
IRGenModule that had the emitted entity to
profile, in addition to any IRGenModules that
had inlined the body of a profiled enitity. As
such, this prevented the duplication from being
too egregious. However with the recent change to
emit unused name data in the case where we don't
already have name data available, we're now fully
duplicating every coverage mapping, and emitting a
ton of redundant name data.

Fix things such that we only emit coverage mapping
records for the IRGenModule that corresponds to
the entity being profiled.

rdar://102905496
2022-12-06 14:28:35 +00:00

219 lines
8.6 KiB
C++

//===--- 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"
// This selects the coverage mapping format defined when `InstrProfData.inc`
// is textually included.
#define COVMAP_V3
using namespace swift;
using namespace irgen;
using llvm::coverage::CounterMappingRegion;
using llvm::coverage::CovMapVersion;
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());
}
std::vector<StringRef> Files;
for (const auto &M : Mappings)
if (std::find(Files.begin(), Files.end(), M->getFilename()) == Files.end())
Files.push_back(M->getFilename());
auto remapper = getOptions().CoveragePrefixMap;
llvm::SmallVector<std::string, 8> FilenameStrs;
for (StringRef Name : Files) {
llvm::SmallString<256> Path(Name);
llvm::sys::fs::make_absolute(Path);
FilenameStrs.push_back(remapper.remapPath(Path));
}
// 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);
unsigned FileID =
std::find(Files.begin(), Files.end(), M->getFilename()) - Files.begin();
std::vector<CounterMappingRegion> Regions;
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.
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, makeArrayRef(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, makeArrayRef(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, 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));
// Create the coverage data record
llvm::Type *CovDataTypes[] = {CovDataHeaderTy, FilenamesVal->getType()};
auto CovDataTy = llvm::StructType::get(Ctx, makeArrayRef(CovDataTypes));
llvm::Constant *TUDataVals[] = {CovDataHeaderVal, FilenamesVal};
auto CovDataVal =
llvm::ConstantStruct::get(CovDataTy, makeArrayRef(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]);
}
}