cb424d7448
verify-patch-sanity.py validates every active recipe .patch has internally- consistent hunk line counts — catching the 'malformed patch at line N' failure at commit/CI/preflight time instead of hours into a cook. This cycle hit that class three times (qtwaylandscanner, sddm, xwayland), each only discovered when cookbook tried to apply the patch. Running it across the repo found 29 latent malformed patches (validated against GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22'). They were harmless only because they sit in vendored recipes (baked, not re- applied) — but would fail on any version-bump re-derivation. --fix recounts the hunk headers (body untouched) and repaired all 29. Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test (test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats (empty placeholders, bare-@@ git hunks).
270 lines
11 KiB
C++
270 lines
11 KiB
C++
//=-- Profilesummary.cpp - Profile summary support --------------------------=//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file contains support for converting profile summary data from/to
|
|
// metadata.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/IR/ProfileSummary.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/DerivedTypes.h"
|
|
#include "llvm/IR/Metadata.h"
|
|
#include "llvm/IR/Type.h"
|
|
#include "llvm/Support/Casting.h"
|
|
#include "llvm/Support/Format.h"
|
|
|
|
using namespace llvm;
|
|
|
|
// Return an MDTuple with two elements. The first element is a string Key and
|
|
// the second is a uint64_t Value.
|
|
static Metadata *getKeyValMD(LLVMContext &Context, const char *Key,
|
|
uint64_t Val) {
|
|
Type *Int64Ty = Type::getInt64Ty(Context);
|
|
Metadata *Ops[2] = {MDString::get(Context, Key),
|
|
ConstantAsMetadata::get(ConstantInt::get(Int64Ty, Val))};
|
|
return MDTuple::get(Context, Ops);
|
|
}
|
|
|
|
static Metadata *getKeyFPValMD(LLVMContext &Context, const char *Key,
|
|
double Val) {
|
|
Type *DoubleTy = Type::getDoubleTy(Context);
|
|
Metadata *Ops[2] = {MDString::get(Context, Key),
|
|
ConstantAsMetadata::get(ConstantFP::get(DoubleTy, Val))};
|
|
return MDTuple::get(Context, Ops);
|
|
}
|
|
|
|
// Return an MDTuple with two elements. The first element is a string Key and
|
|
// the second is a string Value.
|
|
static Metadata *getKeyValMD(LLVMContext &Context, const char *Key,
|
|
const char *Val) {
|
|
Metadata *Ops[2] = {MDString::get(Context, Key), MDString::get(Context, Val)};
|
|
return MDTuple::get(Context, Ops);
|
|
}
|
|
|
|
// This returns an MDTuple representing the detiled summary. The tuple has two
|
|
// elements: a string "DetailedSummary" and an MDTuple representing the value
|
|
// of the detailed summary. Each element of this tuple is again an MDTuple whose
|
|
// elements are the (Cutoff, MinCount, NumCounts) triplet of the
|
|
// DetailedSummaryEntry.
|
|
Metadata *ProfileSummary::getDetailedSummaryMD(LLVMContext &Context) {
|
|
std::vector<Metadata *> Entries;
|
|
Type *Int32Ty = Type::getInt32Ty(Context);
|
|
Type *Int64Ty = Type::getInt64Ty(Context);
|
|
for (auto &Entry : DetailedSummary) {
|
|
Metadata *EntryMD[3] = {
|
|
ConstantAsMetadata::get(ConstantInt::get(Int32Ty, Entry.Cutoff)),
|
|
ConstantAsMetadata::get(ConstantInt::get(Int64Ty, Entry.MinCount)),
|
|
ConstantAsMetadata::get(ConstantInt::get(Int32Ty, Entry.NumCounts))};
|
|
Entries.push_back(MDTuple::get(Context, EntryMD));
|
|
}
|
|
Metadata *Ops[2] = {MDString::get(Context, "DetailedSummary"),
|
|
MDTuple::get(Context, Entries)};
|
|
return MDTuple::get(Context, Ops);
|
|
}
|
|
|
|
// This returns an MDTuple representing this ProfileSummary object. The first
|
|
// entry of this tuple is another MDTuple of two elements: a string
|
|
// "ProfileFormat" and a string representing the format ("InstrProf" or
|
|
// "SampleProfile"). The rest of the elements of the outer MDTuple are specific
|
|
// to the kind of profile summary as returned by getFormatSpecificMD.
|
|
// IsPartialProfile is an optional field and \p AddPartialField will decide
|
|
// whether to add a field for it.
|
|
// PartialProfileRatio is an optional field and \p AddPartialProfileRatioField
|
|
// will decide whether to add a field for it.
|
|
Metadata *ProfileSummary::getMD(LLVMContext &Context, bool AddPartialField,
|
|
bool AddPartialProfileRatioField) {
|
|
const char *KindStr[3] = {"InstrProf", "CSInstrProf", "SampleProfile"};
|
|
SmallVector<Metadata *, 16> Components;
|
|
Components.push_back(getKeyValMD(Context, "ProfileFormat", KindStr[PSK]));
|
|
Components.push_back(getKeyValMD(Context, "TotalCount", getTotalCount()));
|
|
Components.push_back(getKeyValMD(Context, "MaxCount", getMaxCount()));
|
|
Components.push_back(
|
|
getKeyValMD(Context, "MaxInternalCount", getMaxInternalCount()));
|
|
Components.push_back(
|
|
getKeyValMD(Context, "MaxFunctionCount", getMaxFunctionCount()));
|
|
Components.push_back(getKeyValMD(Context, "NumCounts", getNumCounts()));
|
|
Components.push_back(getKeyValMD(Context, "NumFunctions", getNumFunctions()));
|
|
if (AddPartialField)
|
|
Components.push_back(
|
|
getKeyValMD(Context, "IsPartialProfile", isPartialProfile()));
|
|
if (AddPartialProfileRatioField)
|
|
Components.push_back(getKeyFPValMD(Context, "PartialProfileRatio",
|
|
getPartialProfileRatio()));
|
|
Components.push_back(getDetailedSummaryMD(Context));
|
|
return MDTuple::get(Context, Components);
|
|
}
|
|
|
|
// Get the value metadata for the input MD/Key.
|
|
static ConstantAsMetadata *getValMD(MDTuple *MD, const char *Key) {
|
|
if (!MD)
|
|
return nullptr;
|
|
if (MD->getNumOperands() != 2)
|
|
return nullptr;
|
|
MDString *KeyMD = dyn_cast<MDString>(MD->getOperand(0));
|
|
ConstantAsMetadata *ValMD = dyn_cast<ConstantAsMetadata>(MD->getOperand(1));
|
|
if (!KeyMD || !ValMD)
|
|
return nullptr;
|
|
if (KeyMD->getString() != Key)
|
|
return nullptr;
|
|
return ValMD;
|
|
}
|
|
|
|
// Parse an MDTuple representing (Key, Val) pair.
|
|
static bool getVal(MDTuple *MD, const char *Key, uint64_t &Val) {
|
|
if (auto *ValMD = getValMD(MD, Key)) {
|
|
Val = cast<ConstantInt>(ValMD->getValue())->getZExtValue();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool getVal(MDTuple *MD, const char *Key, double &Val) {
|
|
if (auto *ValMD = getValMD(MD, Key)) {
|
|
Val = cast<ConstantFP>(ValMD->getValue())->getValueAPF().convertToDouble();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Check if an MDTuple represents a (Key, Val) pair.
|
|
static bool isKeyValuePair(MDTuple *MD, const char *Key, const char *Val) {
|
|
if (!MD || MD->getNumOperands() != 2)
|
|
return false;
|
|
MDString *KeyMD = dyn_cast<MDString>(MD->getOperand(0));
|
|
MDString *ValMD = dyn_cast<MDString>(MD->getOperand(1));
|
|
if (!KeyMD || !ValMD)
|
|
return false;
|
|
if (KeyMD->getString() != Key || ValMD->getString() != Val)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// Parse an MDTuple representing detailed summary.
|
|
static bool getSummaryFromMD(MDTuple *MD, SummaryEntryVector &Summary) {
|
|
if (!MD || MD->getNumOperands() != 2)
|
|
return false;
|
|
MDString *KeyMD = dyn_cast<MDString>(MD->getOperand(0));
|
|
if (!KeyMD || KeyMD->getString() != "DetailedSummary")
|
|
return false;
|
|
MDTuple *EntriesMD = dyn_cast<MDTuple>(MD->getOperand(1));
|
|
if (!EntriesMD)
|
|
return false;
|
|
for (auto &&MDOp : EntriesMD->operands()) {
|
|
MDTuple *EntryMD = dyn_cast<MDTuple>(MDOp);
|
|
if (!EntryMD || EntryMD->getNumOperands() != 3)
|
|
return false;
|
|
ConstantAsMetadata *Op0 =
|
|
dyn_cast<ConstantAsMetadata>(EntryMD->getOperand(0));
|
|
ConstantAsMetadata *Op1 =
|
|
dyn_cast<ConstantAsMetadata>(EntryMD->getOperand(1));
|
|
ConstantAsMetadata *Op2 =
|
|
dyn_cast<ConstantAsMetadata>(EntryMD->getOperand(2));
|
|
|
|
if (!Op0 || !Op1 || !Op2)
|
|
return false;
|
|
Summary.emplace_back(cast<ConstantInt>(Op0->getValue())->getZExtValue(),
|
|
cast<ConstantInt>(Op1->getValue())->getZExtValue(),
|
|
cast<ConstantInt>(Op2->getValue())->getZExtValue());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Get the value of an optional field. Increment 'Idx' if it was present. Return
|
|
// true if we can move onto the next field.
|
|
template <typename ValueType>
|
|
static bool getOptionalVal(MDTuple *Tuple, unsigned &Idx, const char *Key,
|
|
ValueType &Value) {
|
|
if (getVal(dyn_cast<MDTuple>(Tuple->getOperand(Idx)), Key, Value)) {
|
|
Idx++;
|
|
// Need to make sure when the key is present, we won't step over the bound
|
|
// of Tuple operand array. Since (non-optional) DetailedSummary always comes
|
|
// last, the next entry in the tuple operand array must exist.
|
|
return Idx < Tuple->getNumOperands();
|
|
}
|
|
// It was absent, keep going.
|
|
return true;
|
|
}
|
|
|
|
ProfileSummary *ProfileSummary::getFromMD(Metadata *MD) {
|
|
MDTuple *Tuple = dyn_cast_or_null<MDTuple>(MD);
|
|
if (!Tuple || Tuple->getNumOperands() < 8 || Tuple->getNumOperands() > 10)
|
|
return nullptr;
|
|
|
|
unsigned I = 0;
|
|
auto &FormatMD = Tuple->getOperand(I++);
|
|
ProfileSummary::Kind SummaryKind;
|
|
if (isKeyValuePair(dyn_cast_or_null<MDTuple>(FormatMD), "ProfileFormat",
|
|
"SampleProfile"))
|
|
SummaryKind = PSK_Sample;
|
|
else if (isKeyValuePair(dyn_cast_or_null<MDTuple>(FormatMD), "ProfileFormat",
|
|
"InstrProf"))
|
|
SummaryKind = PSK_Instr;
|
|
else if (isKeyValuePair(dyn_cast_or_null<MDTuple>(FormatMD), "ProfileFormat",
|
|
"CSInstrProf"))
|
|
SummaryKind = PSK_CSInstr;
|
|
else
|
|
return nullptr;
|
|
|
|
uint64_t NumCounts, TotalCount, NumFunctions, MaxFunctionCount, MaxCount,
|
|
MaxInternalCount;
|
|
if (!getVal(dyn_cast<MDTuple>(Tuple->getOperand(I++)), "TotalCount",
|
|
TotalCount))
|
|
return nullptr;
|
|
if (!getVal(dyn_cast<MDTuple>(Tuple->getOperand(I++)), "MaxCount", MaxCount))
|
|
return nullptr;
|
|
if (!getVal(dyn_cast<MDTuple>(Tuple->getOperand(I++)), "MaxInternalCount",
|
|
MaxInternalCount))
|
|
return nullptr;
|
|
if (!getVal(dyn_cast<MDTuple>(Tuple->getOperand(I++)), "MaxFunctionCount",
|
|
MaxFunctionCount))
|
|
return nullptr;
|
|
if (!getVal(dyn_cast<MDTuple>(Tuple->getOperand(I++)), "NumCounts",
|
|
NumCounts))
|
|
return nullptr;
|
|
if (!getVal(dyn_cast<MDTuple>(Tuple->getOperand(I++)), "NumFunctions",
|
|
NumFunctions))
|
|
return nullptr;
|
|
|
|
// Optional fields. Need to initialize because the fields are optional.
|
|
uint64_t IsPartialProfile = 0;
|
|
if (!getOptionalVal(Tuple, I, "IsPartialProfile", IsPartialProfile))
|
|
return nullptr;
|
|
double PartialProfileRatio = 0;
|
|
if (!getOptionalVal(Tuple, I, "PartialProfileRatio", PartialProfileRatio))
|
|
return nullptr;
|
|
|
|
SummaryEntryVector Summary;
|
|
if (!getSummaryFromMD(dyn_cast<MDTuple>(Tuple->getOperand(I++)), Summary))
|
|
return nullptr;
|
|
return new ProfileSummary(SummaryKind, std::move(Summary), TotalCount,
|
|
MaxCount, MaxInternalCount, MaxFunctionCount,
|
|
NumCounts, NumFunctions, IsPartialProfile,
|
|
PartialProfileRatio);
|
|
}
|
|
|
|
void ProfileSummary::printSummary(raw_ostream &OS) const {
|
|
OS << "Total functions: " << NumFunctions << "\n";
|
|
OS << "Maximum function count: " << MaxFunctionCount << "\n";
|
|
OS << "Maximum internal block count: " << MaxInternalCount << "\n";
|
|
OS << "Total number of blocks: " << NumCounts << "\n";
|
|
OS << "Total count: " << TotalCount << "\n";
|
|
}
|
|
|
|
void ProfileSummary::printDetailedSummary(raw_ostream &OS) const {
|
|
OS << "Detailed summary:\n";
|
|
for (const auto &Entry : DetailedSummary) {
|
|
OS << Entry.NumCounts << " blocks "
|
|
<< format("(%.2f%%)",
|
|
NumCounts ? (100.f * Entry.NumCounts / NumCounts) : 0)
|
|
<< " with count >= " << Entry.MinCount << " account for "
|
|
<< format("%0.6g", 100.f * Entry.Cutoff / Scale)
|
|
<< "% of the total counts.\n";
|
|
}
|
|
}
|