2020-07-26 17:11:30 +00:00
|
|
|
#include "LLVMWrapper.h"
|
2024-11-03 10:09:01 +00:00
|
|
|
|
2024-04-03 23:55:20 +00:00
|
|
|
#include "llvm/ADT/ArrayRef.h"
|
2024-11-03 10:09:01 +00:00
|
|
|
#include "llvm/ADT/SmallVector.h"
|
|
|
|
#include "llvm/ADT/StringRef.h"
|
|
|
|
#include "llvm/IR/Module.h"
|
2020-07-02 18:27:15 +00:00
|
|
|
#include "llvm/ProfileData/Coverage/CoverageMapping.h"
|
|
|
|
#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
|
|
|
|
#include "llvm/ProfileData/InstrProf.h"
|
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
|
2023-05-08 03:57:20 +00:00
|
|
|
// FFI equivalent of enum `llvm::coverage::Counter::CounterKind`
|
|
|
|
// https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L97-L99
|
|
|
|
enum class LLVMRustCounterKind {
|
|
|
|
Zero = 0,
|
|
|
|
CounterValueReference = 1,
|
|
|
|
Expression = 2,
|
|
|
|
};
|
|
|
|
|
|
|
|
// FFI equivalent of struct `llvm::coverage::Counter`
|
|
|
|
// https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L94-L149
|
|
|
|
struct LLVMRustCounter {
|
|
|
|
LLVMRustCounterKind CounterKind;
|
|
|
|
uint32_t ID;
|
|
|
|
};
|
|
|
|
|
|
|
|
static coverage::Counter fromRust(LLVMRustCounter Counter) {
|
|
|
|
switch (Counter.CounterKind) {
|
|
|
|
case LLVMRustCounterKind::Zero:
|
|
|
|
return coverage::Counter::getZero();
|
|
|
|
case LLVMRustCounterKind::CounterValueReference:
|
|
|
|
return coverage::Counter::getCounter(Counter.ID);
|
|
|
|
case LLVMRustCounterKind::Expression:
|
|
|
|
return coverage::Counter::getExpression(Counter.ID);
|
|
|
|
}
|
|
|
|
report_fatal_error("Bad LLVMRustCounterKind!");
|
|
|
|
}
|
|
|
|
|
2024-04-19 02:53:04 +00:00
|
|
|
struct LLVMRustMCDCDecisionParameters {
|
|
|
|
uint32_t BitmapIdx;
|
|
|
|
uint16_t NumConditions;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct LLVMRustMCDCBranchParameters {
|
|
|
|
int16_t ConditionID;
|
|
|
|
int16_t ConditionIDs[2];
|
|
|
|
};
|
|
|
|
|
2024-06-19 09:49:10 +00:00
|
|
|
#if LLVM_VERSION_GE(19, 0)
|
2024-10-19 11:22:43 +00:00
|
|
|
static coverage::mcdc::BranchParameters
|
|
|
|
fromRust(LLVMRustMCDCBranchParameters Params) {
|
|
|
|
return coverage::mcdc::BranchParameters(
|
|
|
|
Params.ConditionID, {Params.ConditionIDs[0], Params.ConditionIDs[1]});
|
|
|
|
}
|
|
|
|
|
|
|
|
static coverage::mcdc::DecisionParameters
|
|
|
|
fromRust(LLVMRustMCDCDecisionParameters Params) {
|
|
|
|
return coverage::mcdc::DecisionParameters(Params.BitmapIdx,
|
|
|
|
Params.NumConditions);
|
2024-04-19 02:53:04 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2024-10-19 11:22:43 +00:00
|
|
|
// Must match the layout of
|
|
|
|
// `rustc_codegen_llvm::coverageinfo::ffi::CoverageSpan`.
|
|
|
|
struct LLVMRustCoverageSpan {
|
2021-02-20 15:43:05 +00:00
|
|
|
uint32_t FileID;
|
|
|
|
uint32_t LineStart;
|
|
|
|
uint32_t ColumnStart;
|
|
|
|
uint32_t LineEnd;
|
|
|
|
uint32_t ColumnEnd;
|
2024-10-19 11:22:43 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Must match the layout of `rustc_codegen_llvm::coverageinfo::ffi::CodeRegion`.
|
|
|
|
struct LLVMRustCoverageCodeRegion {
|
|
|
|
LLVMRustCoverageSpan Span;
|
|
|
|
LLVMRustCounter Count;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Must match the layout of
|
|
|
|
// `rustc_codegen_llvm::coverageinfo::ffi::BranchRegion`.
|
|
|
|
struct LLVMRustCoverageBranchRegion {
|
|
|
|
LLVMRustCoverageSpan Span;
|
|
|
|
LLVMRustCounter TrueCount;
|
|
|
|
LLVMRustCounter FalseCount;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Must match the layout of
|
|
|
|
// `rustc_codegen_llvm::coverageinfo::ffi::MCDCBranchRegion`.
|
|
|
|
struct LLVMRustCoverageMCDCBranchRegion {
|
|
|
|
LLVMRustCoverageSpan Span;
|
|
|
|
LLVMRustCounter TrueCount;
|
|
|
|
LLVMRustCounter FalseCount;
|
|
|
|
LLVMRustMCDCBranchParameters MCDCBranchParams;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Must match the layout of
|
|
|
|
// `rustc_codegen_llvm::coverageinfo::ffi::MCDCDecisionRegion`.
|
|
|
|
struct LLVMRustCoverageMCDCDecisionRegion {
|
|
|
|
LLVMRustCoverageSpan Span;
|
|
|
|
LLVMRustMCDCDecisionParameters MCDCDecisionParams;
|
2023-05-08 03:57:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// FFI equivalent of enum `llvm::coverage::CounterExpression::ExprKind`
|
|
|
|
// https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L154
|
|
|
|
enum class LLVMRustCounterExprKind {
|
|
|
|
Subtract = 0,
|
|
|
|
Add = 1,
|
2021-02-20 15:43:05 +00:00
|
|
|
};
|
|
|
|
|
2023-05-08 03:57:20 +00:00
|
|
|
// FFI equivalent of struct `llvm::coverage::CounterExpression`
|
|
|
|
// https://github.com/rust-lang/llvm-project/blob/ea6fa9c2/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L151-L160
|
|
|
|
struct LLVMRustCounterExpression {
|
|
|
|
LLVMRustCounterExprKind Kind;
|
|
|
|
LLVMRustCounter LHS;
|
|
|
|
LLVMRustCounter RHS;
|
|
|
|
};
|
|
|
|
|
|
|
|
static coverage::CounterExpression::ExprKind
|
|
|
|
fromRust(LLVMRustCounterExprKind Kind) {
|
|
|
|
switch (Kind) {
|
|
|
|
case LLVMRustCounterExprKind::Subtract:
|
|
|
|
return coverage::CounterExpression::Subtract;
|
|
|
|
case LLVMRustCounterExprKind::Add:
|
|
|
|
return coverage::CounterExpression::Add;
|
|
|
|
}
|
|
|
|
report_fatal_error("Bad LLVMRustCounterExprKind!");
|
|
|
|
}
|
|
|
|
|
2024-11-01 10:29:09 +00:00
|
|
|
extern "C" void LLVMRustCoverageWriteFilenamesToBuffer(
|
2024-04-03 23:55:20 +00:00
|
|
|
const char *const Filenames[], size_t FilenamesLen, // String start pointers
|
|
|
|
const size_t *const Lengths, size_t LengthsLen, // Corresponding lengths
|
2020-07-02 18:27:15 +00:00
|
|
|
RustStringRef BufferOut) {
|
2023-07-24 07:27:29 +00:00
|
|
|
if (FilenamesLen != LengthsLen) {
|
|
|
|
report_fatal_error(
|
2024-11-01 10:29:09 +00:00
|
|
|
"Mismatched lengths in LLVMRustCoverageWriteFilenamesToBuffer");
|
2023-07-24 07:27:29 +00:00
|
|
|
}
|
|
|
|
|
2024-04-03 23:55:20 +00:00
|
|
|
SmallVector<std::string, 32> FilenameRefs;
|
2023-07-24 07:27:29 +00:00
|
|
|
FilenameRefs.reserve(FilenamesLen);
|
2021-03-16 20:32:00 +00:00
|
|
|
for (size_t i = 0; i < FilenamesLen; i++) {
|
2023-07-24 07:27:29 +00:00
|
|
|
FilenameRefs.emplace_back(Filenames[i], Lengths[i]);
|
2021-03-16 20:32:00 +00:00
|
|
|
}
|
2024-04-03 23:55:20 +00:00
|
|
|
auto FilenamesWriter = coverage::CoverageFilenamesSectionWriter(
|
|
|
|
ArrayRef<std::string>(FilenameRefs));
|
2024-03-06 04:04:24 +00:00
|
|
|
auto OS = RawRustStringOstream(BufferOut);
|
2020-11-23 20:56:07 +00:00
|
|
|
FilenamesWriter.write(OS);
|
2020-07-02 18:27:15 +00:00
|
|
|
}
|
|
|
|
|
2024-11-01 10:29:09 +00:00
|
|
|
extern "C" void LLVMRustCoverageWriteFunctionMappingsToBuffer(
|
|
|
|
const unsigned *VirtualFileMappingIDs, size_t NumVirtualFileMappingIDs,
|
|
|
|
const LLVMRustCounterExpression *RustExpressions, size_t NumExpressions,
|
|
|
|
const LLVMRustCoverageCodeRegion *CodeRegions, size_t NumCodeRegions,
|
|
|
|
const LLVMRustCoverageBranchRegion *BranchRegions, size_t NumBranchRegions,
|
2024-10-19 11:22:43 +00:00
|
|
|
const LLVMRustCoverageMCDCBranchRegion *MCDCBranchRegions,
|
2024-11-01 10:29:09 +00:00
|
|
|
size_t NumMCDCBranchRegions,
|
2024-10-19 11:22:43 +00:00
|
|
|
const LLVMRustCoverageMCDCDecisionRegion *MCDCDecisionRegions,
|
2024-11-01 10:29:09 +00:00
|
|
|
size_t NumMCDCDecisionRegions, RustStringRef BufferOut) {
|
2021-02-20 15:43:05 +00:00
|
|
|
// Convert from FFI representation to LLVM representation.
|
2023-05-08 03:57:20 +00:00
|
|
|
|
2024-10-19 11:22:43 +00:00
|
|
|
// Expressions:
|
2023-05-08 03:57:20 +00:00
|
|
|
std::vector<coverage::CounterExpression> Expressions;
|
|
|
|
Expressions.reserve(NumExpressions);
|
|
|
|
for (const auto &Expression :
|
|
|
|
ArrayRef<LLVMRustCounterExpression>(RustExpressions, NumExpressions)) {
|
|
|
|
Expressions.emplace_back(fromRust(Expression.Kind),
|
|
|
|
fromRust(Expression.LHS),
|
|
|
|
fromRust(Expression.RHS));
|
|
|
|
}
|
|
|
|
|
2024-10-19 11:22:43 +00:00
|
|
|
std::vector<coverage::CounterMappingRegion> MappingRegions;
|
|
|
|
MappingRegions.reserve(NumCodeRegions + NumBranchRegions +
|
|
|
|
NumMCDCBranchRegions + NumMCDCDecisionRegions);
|
|
|
|
|
|
|
|
// Code regions:
|
|
|
|
for (const auto &Region : ArrayRef(CodeRegions, NumCodeRegions)) {
|
|
|
|
MappingRegions.push_back(coverage::CounterMappingRegion::makeRegion(
|
|
|
|
fromRust(Region.Count), Region.Span.FileID, Region.Span.LineStart,
|
|
|
|
Region.Span.ColumnStart, Region.Span.LineEnd, Region.Span.ColumnEnd));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Branch regions:
|
|
|
|
for (const auto &Region : ArrayRef(BranchRegions, NumBranchRegions)) {
|
|
|
|
MappingRegions.push_back(coverage::CounterMappingRegion::makeBranchRegion(
|
|
|
|
fromRust(Region.TrueCount), fromRust(Region.FalseCount),
|
|
|
|
Region.Span.FileID, Region.Span.LineStart, Region.Span.ColumnStart,
|
|
|
|
Region.Span.LineEnd, Region.Span.ColumnEnd));
|
|
|
|
}
|
|
|
|
|
|
|
|
#if LLVM_VERSION_GE(19, 0)
|
|
|
|
// MC/DC branch regions:
|
|
|
|
for (const auto &Region : ArrayRef(MCDCBranchRegions, NumMCDCBranchRegions)) {
|
|
|
|
MappingRegions.push_back(coverage::CounterMappingRegion::makeBranchRegion(
|
|
|
|
fromRust(Region.TrueCount), fromRust(Region.FalseCount),
|
|
|
|
Region.Span.FileID, Region.Span.LineStart, Region.Span.ColumnStart,
|
|
|
|
Region.Span.LineEnd, Region.Span.ColumnEnd,
|
|
|
|
fromRust(Region.MCDCBranchParams)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// MC/DC decision regions:
|
|
|
|
for (const auto &Region :
|
|
|
|
ArrayRef(MCDCDecisionRegions, NumMCDCDecisionRegions)) {
|
|
|
|
MappingRegions.push_back(coverage::CounterMappingRegion::makeDecisionRegion(
|
|
|
|
fromRust(Region.MCDCDecisionParams), Region.Span.FileID,
|
|
|
|
Region.Span.LineStart, Region.Span.ColumnStart, Region.Span.LineEnd,
|
|
|
|
Region.Span.ColumnEnd));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Write the converted expressions and mappings to a byte buffer.
|
2020-07-02 18:27:15 +00:00
|
|
|
auto CoverageMappingWriter = coverage::CoverageMappingWriter(
|
2023-01-11 15:39:01 +00:00
|
|
|
ArrayRef<unsigned>(VirtualFileMappingIDs, NumVirtualFileMappingIDs),
|
2024-04-03 23:55:20 +00:00
|
|
|
Expressions, MappingRegions);
|
2024-03-06 04:04:24 +00:00
|
|
|
auto OS = RawRustStringOstream(BufferOut);
|
2020-07-02 18:27:15 +00:00
|
|
|
CoverageMappingWriter.write(OS);
|
|
|
|
}
|
|
|
|
|
2024-04-03 23:55:20 +00:00
|
|
|
extern "C" LLVMValueRef
|
|
|
|
LLVMRustCoverageCreatePGOFuncNameVar(LLVMValueRef F, const char *FuncName,
|
|
|
|
size_t FuncNameLen) {
|
2024-03-06 04:04:24 +00:00
|
|
|
auto FuncNameRef = StringRef(FuncName, FuncNameLen);
|
LLVM IR coverage encoding aligns closer to Clang's
I found some areas for improvement while attempting to debug the
SegFault issue when running rust programs compiled using MSVC, with
coverage instrumentation.
I discovered that LLVM's coverage writer was generating incomplete
function name variable names (that's not a typo: the name of the
variable that holds a function name).
The existing implementation used one-up numbers to distinguish
variables, and correcting the names did not fix the MSVC coverage bug,
but the fix in this PR makes the names and resulting LLVM IR easier to
follow and more consistent with Clang's implementation.
I also changed the way the `-Zinstrument-coverage` option is supported
in symbol_export.rs. The original implementation was incorrect, and the
corrected version matches the handling for `-Zprofile-generate`, as it
turns out.
(An argument could be made that maybe `-Zinstrument-coverage` should
automatically enable `-Cprofile-generate`. In fact, if
`-Cprofile-generate` is analagous to Clang's `-fprofile-generate`, as
some documentation implies, Clang always requires this flag for its
implementation of source-based code coverage. This would require a
little more validation, and if implemented, would probably require
updating some of the user-facing messages related to
`-Cprofile-generate` to not be so specific to the PGO use case.)
None of these changes fixed the MSVC coverage problems, but they should
still be welcome improvements.
Lastly, I added some additional FIXME comments in instrument_coverage.rs
describing issues I found with the generated LLVM IR that would be
resolved if the coverage instrumentation is injected with a `Statement`
instead of as a new `BasicBlock`. I describe seven advantages of this
change, but it requires some discussion before making a change like
this.
2020-08-06 05:53:11 +00:00
|
|
|
return wrap(createPGOFuncNameVar(*cast<Function>(unwrap(F)), FuncNameRef));
|
|
|
|
}
|
|
|
|
|
2024-11-01 10:29:09 +00:00
|
|
|
extern "C" uint64_t LLVMRustCoverageHashBytes(const char *Bytes,
|
|
|
|
size_t NumBytes) {
|
|
|
|
return IndexedInstrProf::ComputeHash(StringRef(Bytes, NumBytes));
|
2020-07-02 18:27:15 +00:00
|
|
|
}
|
|
|
|
|
2024-11-01 10:29:09 +00:00
|
|
|
// Private helper function for getting the covmap and covfun section names.
|
|
|
|
static void writeInstrProfSectionNameToString(LLVMModuleRef M,
|
|
|
|
InstrProfSectKind SectKind,
|
|
|
|
RustStringRef OutStr) {
|
2024-03-06 04:04:24 +00:00
|
|
|
auto TargetTriple = Triple(unwrap(M)->getTargetTriple());
|
2024-11-01 10:29:09 +00:00
|
|
|
auto name = getInstrProfSectionName(SectKind, TargetTriple.getObjectFormat());
|
|
|
|
auto OS = RawRustStringOstream(OutStr);
|
2020-07-02 18:27:15 +00:00
|
|
|
OS << name;
|
|
|
|
}
|
|
|
|
|
2024-11-01 10:29:09 +00:00
|
|
|
extern "C" void
|
|
|
|
LLVMRustCoverageWriteCovmapSectionNameToString(LLVMModuleRef M,
|
|
|
|
RustStringRef OutStr) {
|
|
|
|
writeInstrProfSectionNameToString(M, IPSK_covmap, OutStr);
|
2020-11-23 20:56:07 +00:00
|
|
|
}
|
|
|
|
|
2024-04-03 23:55:20 +00:00
|
|
|
extern "C" void
|
2024-11-01 10:29:09 +00:00
|
|
|
LLVMRustCoverageWriteCovfunSectionNameToString(LLVMModuleRef M,
|
|
|
|
RustStringRef OutStr) {
|
|
|
|
writeInstrProfSectionNameToString(M, IPSK_covfun, OutStr);
|
2020-11-23 20:56:07 +00:00
|
|
|
}
|
|
|
|
|
2024-11-01 10:29:09 +00:00
|
|
|
extern "C" void
|
|
|
|
LLVMRustCoverageWriteCovmapVarNameToString(RustStringRef OutStr) {
|
2020-07-02 18:27:15 +00:00
|
|
|
auto name = getCoverageMappingVarName();
|
2024-11-01 10:29:09 +00:00
|
|
|
auto OS = RawRustStringOstream(OutStr);
|
2020-07-02 18:27:15 +00:00
|
|
|
OS << name;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" uint32_t LLVMRustCoverageMappingVersion() {
|
2024-02-13 12:00:49 +00:00
|
|
|
// This should always be `CurrentVersion`, because that's the version LLVM
|
|
|
|
// will use when encoding the data we give it. If for some reason we ever
|
|
|
|
// want to override the version number we _emit_, do it on the Rust side.
|
|
|
|
return coverage::CovMapVersion::CurrentVersion;
|
2020-07-02 18:27:15 +00:00
|
|
|
}
|