.. code-block:: text
- function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 2[, FuncFlags]?[, Calls]?[, TypeIdInfo]?[, Refs]?
+ function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 2[, FuncFlags]?[, Calls]?[, TypeIdInfo]?[, Params]?[, Refs]?
The ``module`` field includes the summary entry id for the module containing
this definition, and the ``flags`` field contains information such as
The ``insts`` field contains the number of IR instructions in the function.
Finally, there are several optional fields: :ref:`FuncFlags<funcflags_summary>`,
:ref:`Calls<calls_summary>`, :ref:`TypeIdInfo<typeidinfo_summary>`,
-:ref:`Refs<refs_summary>`.
+:ref:`Params<params_summary>`, :ref:`Refs<refs_summary>`.
.. _variable_summary:
branch frequency relative to the entry frequency, scaled down by 2^8)
may be specified. The defaults are ``Unknown`` and ``0``, respectively.
+.. _stacksafety_summary:
+
+Params
+^^^^^^
+
+The optional ``Params`` is used by ``StackSafety`` and looks like:
+
+.. code-block:: text
+
+ Params: ((Param)[, (Param)]*)
+
+where each ``Param`` describes pointer parameter access inside of the
+function and looks like:
+
+.. code-block:: text
+
+ param: 4, offset: [0, 5][, calls: ((Callee)[, (Callee)]*)]?
+
+where the first ``param`` is the number of the parameter it describes,
+``offset`` is the known access range of the paramenter inside of the function.
+
+where each ``Callee`` decribes how parameter is forwared into other
+functions and looks like:
+
+.. code-block:: text
+
+ callee: ^3, param: 5, offset: [-3, 3]
+
+The ``callee`` refers to the summary entry id of the callee, ``param`` is
+the number of the callee parameter which points into the callers parameter
+with offset known to be inside of the ``offset`` range.
+
.. _refs_summary:
Refs
class Function;
class Module;
class ProfileSummaryInfo;
+class StackSafetyInfo;
/// Direct function to compute a \c ModuleSummaryIndex from a given module.
///
ModuleSummaryIndex buildModuleSummaryIndex(
const Module &M,
std::function<BlockFrequencyInfo *(const Function &F)> GetBFICallback,
- ProfileSummaryInfo *PSI);
+ ProfileSummaryInfo *PSI,
+ std::function<const StackSafetyInfo *(const Function &F)> GetSSICallback =
+ [](const Function &F) -> const StackSafetyInfo * { return nullptr; });
/// Analysis pass to provide the ModuleSummaryIndex object.
class ModuleSummaryIndexAnalysis
#ifndef LLVM_ANALYSIS_STACKSAFETYANALYSIS_H
#define LLVM_ANALYSIS_STACKSAFETYANALYSIS_H
+#include "llvm/IR/ModuleSummaryIndex.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
// TODO: Add useful for client methods.
void print(raw_ostream &O) const;
+
+ /// Parameters use for a FunctionSummary.
+ std::vector<FunctionSummary::ParamAccess> getParamAccesses() const;
};
class StackSafetyGlobalInfo {
bool runOnModule(Module &M) override;
};
+bool needsParamAccessSummary(const Module &M);
+
} // end namespace llvm
#endif // LLVM_ANALYSIS_STACKSAFETYANALYSIS_H
FS_PERMODULE_VTABLE_GLOBALVAR_INIT_REFS = 23,
// The total number of basic blocks in the module.
FS_BLOCK_COUNT = 24,
+ // Range information for accessed offsets for every argument.
+ // [n x (paramno, range, numcalls, numcalls x (callee_guid, paramno, range))]
+ FS_PARAM_ACCESS = 25,
};
enum MetadataCodes {
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/TinyPtrVector.h"
+#include "llvm/IR/ConstantRange.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Allocator.h"
unsigned AlwaysInline : 1;
};
+ /// Describes the uses of a parameter by the range of offsets accessed in the
+ /// function and all of the call targets it is passed to.
+ struct ParamAccess {
+ static constexpr uint32_t RangeWidth = 64;
+
+ /// Describes the use of a value in a call instruction, specifying the
+ /// call's target, the value's parameter number, and the possible range of
+ /// offsets from the beginning of the value that are passed.
+ struct Call {
+ uint64_t ParamNo = 0;
+ GlobalValue::GUID Callee = 0;
+ ConstantRange Offsets{RangeWidth, true};
+
+ Call() = default;
+ Call(uint64_t ParamNo, GlobalValue::GUID Callee,
+ const ConstantRange &Offsets)
+ : ParamNo(ParamNo), Callee(Callee), Offsets(Offsets) {}
+ };
+
+ uint64_t ParamNo = 0;
+ ConstantRange Use{RangeWidth, true};
+ std::vector<Call> Calls;
+
+ ParamAccess() = default;
+ ParamAccess(uint64_t ParamNo, const ConstantRange &Use)
+ : ParamNo(ParamNo), Use(Use) {}
+ };
+
/// Create an empty FunctionSummary (with specified call edges).
/// Used to represent external nodes and the dummy root node.
static FunctionSummary
std::vector<FunctionSummary::VFuncId>(),
std::vector<FunctionSummary::VFuncId>(),
std::vector<FunctionSummary::ConstVCall>(),
- std::vector<FunctionSummary::ConstVCall>());
+ std::vector<FunctionSummary::ConstVCall>(),
+ std::vector<FunctionSummary::ParamAccess>());
}
/// A dummy node to reference external functions that aren't in the index
std::unique_ptr<TypeIdInfo> TIdInfo;
+ /// Uses for every parameter to this function.
+ std::vector<ParamAccess> ParamAccesses;
+
public:
FunctionSummary(GVFlags Flags, unsigned NumInsts, FFlags FunFlags,
uint64_t EntryCount, std::vector<ValueInfo> Refs,
std::vector<VFuncId> TypeTestAssumeVCalls,
std::vector<VFuncId> TypeCheckedLoadVCalls,
std::vector<ConstVCall> TypeTestAssumeConstVCalls,
- std::vector<ConstVCall> TypeCheckedLoadConstVCalls)
+ std::vector<ConstVCall> TypeCheckedLoadConstVCalls,
+ std::vector<ParamAccess> ParamAccesses)
: GlobalValueSummary(FunctionKind, Flags, std::move(Refs)),
InstCount(NumInsts), FunFlags(FunFlags), EntryCount(EntryCount),
- CallGraphEdgeList(std::move(CGEdges)) {
+ CallGraphEdgeList(std::move(CGEdges)),
+ ParamAccesses(std::move(ParamAccesses)) {
if (!TypeTests.empty() || !TypeTestAssumeVCalls.empty() ||
!TypeCheckedLoadVCalls.empty() || !TypeTestAssumeConstVCalls.empty() ||
!TypeCheckedLoadConstVCalls.empty())
return {};
}
+ /// Returns the list of known uses of pointer parameters.
+ ArrayRef<ParamAccess> paramAccesses() const { return ParamAccesses; }
+
+ /// Sets the list of known uses of pointer parameters.
+ void setParamAccesses(std::vector<ParamAccess> NewParams) {
+ ParamAccesses = std::move(NewParams);
+ }
+
/// Add a type test to the summary. This is used by WholeProgramDevirt if we
/// were unable to devirtualize a checked call.
void addTypeTest(GlobalValue::GUID Guid) {
Elem.SummaryList.push_back(std::make_unique<FunctionSummary>(
GlobalValueSummary::GVFlags(
static_cast<GlobalValue::LinkageTypes>(FSum.Linkage),
- FSum.NotEligibleToImport, FSum.Live, FSum.IsLocal, FSum.CanAutoHide),
+ FSum.NotEligibleToImport, FSum.Live, FSum.IsLocal,
+ FSum.CanAutoHide),
/*NumInsts=*/0, FunctionSummary::FFlags{}, /*EntryCount=*/0, Refs,
ArrayRef<FunctionSummary::EdgeTy>{}, std::move(FSum.TypeTests),
std::move(FSum.TypeTestAssumeVCalls),
std::move(FSum.TypeCheckedLoadVCalls),
std::move(FSum.TypeTestAssumeConstVCalls),
- std::move(FSum.TypeCheckedLoadConstVCalls)));
+ std::move(FSum.TypeCheckedLoadConstVCalls),
+ ArrayRef<FunctionSummary::ParamAccess>{}));
}
}
static void output(IO &io, GlobalValueSummaryMapTy &V) {
#include "llvm/Analysis/IndirectCallPromotionAnalysis.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/ProfileSummaryInfo.h"
+#include "llvm/Analysis/StackSafetyAnalysis.h"
#include "llvm/Analysis/TypeMetadataUtils.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/BasicBlock.h"
return false;
}
-static void computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M,
- const Function &F, BlockFrequencyInfo *BFI,
- ProfileSummaryInfo *PSI, DominatorTree &DT,
- bool HasLocalsInUsedOrAsm,
- DenseSet<GlobalValue::GUID> &CantBePromoted,
- bool IsThinLTO) {
+static void computeFunctionSummary(
+ ModuleSummaryIndex &Index, const Module &M, const Function &F,
+ BlockFrequencyInfo *BFI, ProfileSummaryInfo *PSI, DominatorTree &DT,
+ bool HasLocalsInUsedOrAsm, DenseSet<GlobalValue::GUID> &CantBePromoted,
+ bool IsThinLTO,
+ std::function<const StackSafetyInfo *(const Function &F)> GetSSICallback) {
// Summary not currently supported for anonymous functions, they should
// have been named.
assert(F.hasName());
// Don't try to import functions with noinline attribute.
F.getAttributes().hasFnAttribute(Attribute::NoInline),
F.hasFnAttribute(Attribute::AlwaysInline)};
+ std::vector<FunctionSummary::ParamAccess> ParamAccesses;
+ if (auto *SSI = GetSSICallback(F))
+ ParamAccesses = SSI->getParamAccesses();
auto FuncSummary = std::make_unique<FunctionSummary>(
Flags, NumInsts, FunFlags, /*EntryCount=*/0, std::move(Refs),
CallGraphEdges.takeVector(), TypeTests.takeVector(),
TypeTestAssumeVCalls.takeVector(), TypeCheckedLoadVCalls.takeVector(),
TypeTestAssumeConstVCalls.takeVector(),
- TypeCheckedLoadConstVCalls.takeVector());
+ TypeCheckedLoadConstVCalls.takeVector(), std::move(ParamAccesses));
if (NonRenamableLocal)
CantBePromoted.insert(F.getGUID());
Index.addGlobalValueSummary(F, std::move(FuncSummary));
ModuleSummaryIndex llvm::buildModuleSummaryIndex(
const Module &M,
std::function<BlockFrequencyInfo *(const Function &F)> GetBFICallback,
- ProfileSummaryInfo *PSI) {
+ ProfileSummaryInfo *PSI,
+ std::function<const StackSafetyInfo *(const Function &F)> GetSSICallback) {
assert(PSI);
bool EnableSplitLTOUnit = false;
if (auto *MD = mdconst::extract_or_null<ConstantInt>(
ArrayRef<FunctionSummary::VFuncId>{},
ArrayRef<FunctionSummary::VFuncId>{},
ArrayRef<FunctionSummary::ConstVCall>{},
- ArrayRef<FunctionSummary::ConstVCall>{});
+ ArrayRef<FunctionSummary::ConstVCall>{},
+ ArrayRef<FunctionSummary::ParamAccess>{});
Index.addGlobalValueSummary(*GV, std::move(Summary));
} else {
std::unique_ptr<GlobalVarSummary> Summary =
computeFunctionSummary(Index, M, F, BFI, PSI, DT,
!LocalsUsed.empty() || HasLocalInlineAsmSymbol,
- CantBePromoted, IsThinLTO);
+ CantBePromoted, IsThinLTO, GetSSICallback);
}
// Compute summaries for all variables defined in module, and save in the
ModuleSummaryIndexAnalysis::run(Module &M, ModuleAnalysisManager &AM) {
ProfileSummaryInfo &PSI = AM.getResult<ProfileSummaryAnalysis>(M);
auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
+ bool NeedSSI = needsParamAccessSummary(M);
return buildModuleSummaryIndex(
M,
[&FAM](const Function &F) {
return &FAM.getResult<BlockFrequencyAnalysis>(
*const_cast<Function *>(&F));
},
- &PSI);
+ &PSI,
+ [&FAM, NeedSSI](const Function &F) -> const StackSafetyInfo * {
+ return NeedSSI ? &FAM.getResult<StackSafetyAnalysis>(
+ const_cast<Function &>(F))
+ : nullptr;
+ });
}
char ModuleSummaryIndexWrapperPass::ID = 0;
"Module Summary Analysis", false, true)
INITIALIZE_PASS_DEPENDENCY(BlockFrequencyInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(StackSafetyInfoWrapperPass)
INITIALIZE_PASS_END(ModuleSummaryIndexWrapperPass, "module-summary-analysis",
"Module Summary Analysis", false, true)
bool ModuleSummaryIndexWrapperPass::runOnModule(Module &M) {
auto *PSI = &getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI();
+ bool NeedSSI = needsParamAccessSummary(M);
Index.emplace(buildModuleSummaryIndex(
M,
[this](const Function &F) {
*const_cast<Function *>(&F))
.getBFI());
},
- PSI));
+ PSI,
+ [&](const Function &F) -> const StackSafetyInfo * {
+ return NeedSSI ? &getAnalysis<StackSafetyInfoWrapperPass>(
+ const_cast<Function &>(F))
+ .getResult()
+ : nullptr;
+ }));
return false;
}
AU.setPreservesAll();
AU.addRequired<BlockFrequencyInfoWrapperPass>();
AU.addRequired<ProfileSummaryInfoWrapperPass>();
+ AU.addRequired<StackSafetyInfoWrapperPass>();
}
return *Info;
}
+// Converts a StackSafetyFunctionInfo to the relevant FunctionSummary
+// constructor fields
+std::vector<FunctionSummary::ParamAccess>
+StackSafetyInfo::getParamAccesses() const {
+ assert(needsParamAccessSummary(*F->getParent()));
+
+ std::vector<FunctionSummary::ParamAccess> ParamAccesses;
+ for (const auto &KV : getInfo().Info.Params) {
+ auto &PS = KV.second;
+ if (PS.Range.isFullSet())
+ continue;
+
+ ParamAccesses.emplace_back(KV.first, PS.Range);
+ FunctionSummary::ParamAccess &Param = ParamAccesses.back();
+
+ Param.Calls.reserve(PS.Calls.size());
+ for (auto &C : PS.Calls) {
+ if (C.Offset.isFullSet()) {
+ ParamAccesses.pop_back();
+ break;
+ }
+ Param.Calls.emplace_back(C.ParamNo, C.Callee->getGUID(), C.Offset);
+ }
+ }
+ return ParamAccesses;
+}
+
StackSafetyGlobalInfo::StackSafetyGlobalInfo() = default;
StackSafetyGlobalInfo::StackSafetyGlobalInfo(
return false;
}
+bool llvm::needsParamAccessSummary(const Module &M) {
+ for (auto &F : M.functions())
+ if (F.hasFnAttribute(Attribute::SanitizeMemTag))
+ return true;
+ return false;
+}
+
static const char LocalPassArg[] = "stack-safety-local";
static const char LocalPassName[] = "Stack Safety Local Analysis";
INITIALIZE_PASS_BEGIN(StackSafetyInfoWrapperPass, LocalPassArg, LocalPassName,
KEYWORD(alwaysInline);
KEYWORD(calls);
KEYWORD(callee);
+ KEYWORD(params);
+ KEYWORD(param);
KEYWORD(hotness);
KEYWORD(unknown);
KEYWORD(hot);
//===----------------------------------------------------------------------===//
#include "LLParser.h"
+#include "LLToken.h"
+#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/Comdat.h"
+#include "llvm/IR/ConstantRange.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DerivedTypes.h"
/// FunctionSummary
/// ::= 'function' ':' '(' 'module' ':' ModuleReference ',' GVFlags
/// ',' 'insts' ':' UInt32 [',' OptionalFFlags]? [',' OptionalCalls]?
-/// [',' OptionalTypeIdInfo]? [',' OptionalRefs]? ')'
+/// [',' OptionalTypeIdInfo]? [',' OptionalParamAccesses]?
+/// [',' OptionalRefs]? ')'
bool LLParser::ParseFunctionSummary(std::string Name, GlobalValue::GUID GUID,
unsigned ID) {
assert(Lex.getKind() == lltok::kw_function);
unsigned InstCount;
std::vector<FunctionSummary::EdgeTy> Calls;
FunctionSummary::TypeIdInfo TypeIdInfo;
+ std::vector<FunctionSummary::ParamAccess> ParamAccesses;
std::vector<ValueInfo> Refs;
// Default is all-zeros (conservative values).
FunctionSummary::FFlags FFlags = {};
if (ParseOptionalRefs(Refs))
return true;
break;
+ case lltok::kw_params:
+ if (ParseOptionalParamAccesses(ParamAccesses))
+ return true;
+ break;
default:
return Error(Lex.getLoc(), "expected optional function summary field");
}
std::move(TypeIdInfo.TypeTestAssumeVCalls),
std::move(TypeIdInfo.TypeCheckedLoadVCalls),
std::move(TypeIdInfo.TypeTestAssumeConstVCalls),
- std::move(TypeIdInfo.TypeCheckedLoadConstVCalls));
+ std::move(TypeIdInfo.TypeCheckedLoadConstVCalls),
+ std::move(ParamAccesses));
FS->setModulePath(ModulePath);
return false;
}
+/// ParamNo := 'param' ':' UInt64
+bool LLParser::ParseParamNo(uint64_t &ParamNo) {
+ if (ParseToken(lltok::kw_param, "expected 'param' here") ||
+ ParseToken(lltok::colon, "expected ':' here") || ParseUInt64(ParamNo))
+ return true;
+ return false;
+}
+
+/// ParamAccessOffset := 'offset' ':' '[' APSINTVAL ',' APSINTVAL ']'
+bool LLParser::ParseParamAccessOffset(ConstantRange &Range) {
+ APSInt Lower;
+ APSInt Upper;
+ auto ParseAPSInt = [&](APSInt &Val) {
+ if (Lex.getKind() != lltok::APSInt)
+ return TokError("expected integer");
+ Val = Lex.getAPSIntVal();
+ Val = Val.extOrTrunc(FunctionSummary::ParamAccess::RangeWidth);
+ Val.setIsSigned(true);
+ Lex.Lex();
+ return false;
+ };
+ if (ParseToken(lltok::kw_offset, "expected 'offset' here") ||
+ ParseToken(lltok::colon, "expected ':' here") ||
+ ParseToken(lltok::lsquare, "expected '[' here") || ParseAPSInt(Lower) ||
+ ParseToken(lltok::comma, "expected ',' here") || ParseAPSInt(Upper) ||
+ ParseToken(lltok::rsquare, "expected ']' here"))
+ return true;
+
+ ++Upper;
+ Range =
+ (Lower == Upper && !Lower.isMaxValue())
+ ? ConstantRange::getEmpty(FunctionSummary::ParamAccess::RangeWidth)
+ : ConstantRange(Lower, Upper);
+
+ return false;
+}
+
+/// ParamAccessCall
+/// := '(' 'callee' ':' GVReference ',' ParamNo ',' ParamAccessOffset ')'
+bool LLParser::ParseParamAccessCall(FunctionSummary::ParamAccess::Call &Call) {
+ if (ParseToken(lltok::lparen, "expected '(' here") ||
+ ParseToken(lltok::kw_callee, "expected 'callee' here") ||
+ ParseToken(lltok::colon, "expected ':' here"))
+ return true;
+
+ unsigned GVId;
+ ValueInfo VI;
+ if (ParseGVReference(VI, GVId))
+ return true;
+
+ Call.Callee = VI.getGUID();
+
+ if (ParseToken(lltok::comma, "expected ',' here") ||
+ ParseParamNo(Call.ParamNo) ||
+ ParseToken(lltok::comma, "expected ',' here") ||
+ ParseParamAccessOffset(Call.Offsets))
+ return true;
+
+ if (ParseToken(lltok::rparen, "expected ')' here"))
+ return true;
+
+ return false;
+}
+
+/// ParamAccess
+/// := '(' ParamNo ',' ParamAccessOffset [',' OptionalParamAccessCalls]? ')'
+/// OptionalParamAccessCalls := '(' Call [',' Call]* ')'
+bool LLParser::ParseParamAccess(FunctionSummary::ParamAccess &Param) {
+ if (ParseToken(lltok::lparen, "expected '(' here") ||
+ ParseParamNo(Param.ParamNo) ||
+ ParseToken(lltok::comma, "expected ',' here") ||
+ ParseParamAccessOffset(Param.Use))
+ return true;
+
+ if (EatIfPresent(lltok::comma)) {
+ if (ParseToken(lltok::kw_calls, "expected 'calls' here") ||
+ ParseToken(lltok::colon, "expected ':' here") ||
+ ParseToken(lltok::lparen, "expected '(' here"))
+ return true;
+ do {
+ FunctionSummary::ParamAccess::Call Call;
+ if (ParseParamAccessCall(Call))
+ return true;
+ Param.Calls.push_back(Call);
+ } while (EatIfPresent(lltok::comma));
+
+ if (ParseToken(lltok::rparen, "expected ')' here"))
+ return true;
+ }
+
+ if (ParseToken(lltok::rparen, "expected ')' here"))
+ return true;
+
+ return false;
+}
+
+/// OptionalParamAccesses
+/// := 'params' ':' '(' ParamAccess [',' ParamAccess]* ')'
+bool LLParser::ParseOptionalParamAccesses(
+ std::vector<FunctionSummary::ParamAccess> &Params) {
+ assert(Lex.getKind() == lltok::kw_params);
+ Lex.Lex();
+
+ if (ParseToken(lltok::colon, "expected ':' here") ||
+ ParseToken(lltok::lparen, "expected '(' here"))
+ return true;
+
+ do {
+ FunctionSummary::ParamAccess ParamAccess;
+ if (ParseParamAccess(ParamAccess))
+ return true;
+ Params.push_back(ParamAccess);
+ } while (EatIfPresent(lltok::comma));
+
+ if (ParseToken(lltok::rparen, "expected ')' here"))
+ return true;
+
+ return false;
+}
+
/// OptionalRefs
/// := 'refs' ':' '(' GVReference [',' GVReference]* ')'
bool LLParser::ParseOptionalRefs(std::vector<ValueInfo> &Refs) {
assert(Lex.getKind() == lltok::kw_refs);
Lex.Lex();
- if (ParseToken(lltok::colon, "expected ':' in refs") |
+ if (ParseToken(lltok::colon, "expected ':' in refs") ||
ParseToken(lltok::lparen, "expected '(' in refs"))
return true;
bool ParseVFuncId(FunctionSummary::VFuncId &VFuncId,
IdToIndexMapType &IdToIndexMap, unsigned Index);
bool ParseOptionalVTableFuncs(VTableFuncList &VTableFuncs);
+ bool ParseOptionalParamAccesses(
+ std::vector<FunctionSummary::ParamAccess> &Params);
+ bool ParseParamNo(uint64_t &ParamNo);
+ bool ParseParamAccess(FunctionSummary::ParamAccess &Param);
+ bool ParseParamAccessCall(FunctionSummary::ParamAccess::Call &Call);
+ bool ParseParamAccessOffset(ConstantRange &range);
bool ParseOptionalRefs(std::vector<ValueInfo> &Refs);
bool ParseTypeIdEntry(unsigned ID);
bool ParseTypeIdSummary(TypeIdSummary &TIS);
kw_alwaysInline,
kw_calls,
kw_callee,
+ kw_params,
+ kw_param,
kw_hotness,
kw_unknown,
kw_hot,
STRINGIFY_CODE(FS, TYPE_ID)
STRINGIFY_CODE(FS, TYPE_ID_METADATA)
STRINGIFY_CODE(FS, BLOCK_COUNT)
+ STRINGIFY_CODE(FS, PARAM_ACCESS)
}
case bitc::METADATA_ATTACHMENT_ID:
switch (CodeID) {
parseWholeProgramDevirtResolution(Record, Strtab, Slot, TypeId);
}
+static std::vector<FunctionSummary::ParamAccess>
+parseParamAccesses(ArrayRef<uint64_t> Record) {
+ auto ReadRange = [&]() {
+ APInt Lower(FunctionSummary::ParamAccess::RangeWidth,
+ BitcodeReader::decodeSignRotatedValue(Record.front()));
+ Record = Record.drop_front();
+ APInt Upper(FunctionSummary::ParamAccess::RangeWidth,
+ BitcodeReader::decodeSignRotatedValue(Record.front()));
+ Record = Record.drop_front();
+ ConstantRange Range{Lower, Upper};
+ assert(!Range.isFullSet());
+ assert(!Range.isUpperSignWrapped());
+ return Range;
+ };
+
+ std::vector<FunctionSummary::ParamAccess> PendingParamAccesses;
+ while (!Record.empty()) {
+ PendingParamAccesses.emplace_back();
+ FunctionSummary::ParamAccess &ParamAccess = PendingParamAccesses.back();
+ ParamAccess.ParamNo = Record.front();
+ Record = Record.drop_front();
+ ParamAccess.Use = ReadRange();
+ ParamAccess.Calls.resize(Record.front());
+ Record = Record.drop_front();
+ for (auto &Call : ParamAccess.Calls) {
+ Call.ParamNo = Record.front();
+ Record = Record.drop_front();
+ Call.Callee = Record.front();
+ Record = Record.drop_front();
+ Call.Offsets = ReadRange();
+ }
+ }
+ return PendingParamAccesses;
+}
+
void ModuleSummaryIndexBitcodeReader::parseTypeIdCompatibleVtableInfo(
ArrayRef<uint64_t> Record, size_t &Slot,
TypeIdCompatibleVtableInfo &TypeId) {
PendingTypeCheckedLoadVCalls;
std::vector<FunctionSummary::ConstVCall> PendingTypeTestAssumeConstVCalls,
PendingTypeCheckedLoadConstVCalls;
+ std::vector<FunctionSummary::ParamAccess> PendingParamAccesses;
while (true) {
Expected<BitstreamEntry> MaybeEntry = Stream.advanceSkippingSubblocks();
std::move(PendingTypeTestAssumeVCalls),
std::move(PendingTypeCheckedLoadVCalls),
std::move(PendingTypeTestAssumeConstVCalls),
- std::move(PendingTypeCheckedLoadConstVCalls));
+ std::move(PendingTypeCheckedLoadConstVCalls),
+ std::move(PendingParamAccesses));
auto VIAndOriginalGUID = getValueInfoFromValueId(ValueID);
FS->setModulePath(getThisModule()->first());
FS->setOriginalName(VIAndOriginalGUID.second);
std::move(PendingTypeTestAssumeVCalls),
std::move(PendingTypeCheckedLoadVCalls),
std::move(PendingTypeTestAssumeConstVCalls),
- std::move(PendingTypeCheckedLoadConstVCalls));
+ std::move(PendingTypeCheckedLoadConstVCalls),
+ std::move(PendingParamAccesses));
LastSeenSummary = FS.get();
LastSeenGUID = VI.getGUID();
FS->setModulePath(ModuleIdMap[ModuleId]);
case bitc::FS_BLOCK_COUNT:
TheIndex.addBlockCount(Record[0]);
+ break;
+
+ case bitc::FS_PARAM_ACCESS: {
+ PendingParamAccesses = parseParamAccesses(Record);
+ break;
+ }
}
}
llvm_unreachable("Exit infinite loop");
FS->type_test_assume_const_vcalls());
WriteConstVCallVec(bitc::FS_TYPE_CHECKED_LOAD_CONST_VCALL,
FS->type_checked_load_const_vcalls());
+
+ auto WriteRange = [&](ConstantRange Range) {
+ Range = Range.sextOrTrunc(FunctionSummary::ParamAccess::RangeWidth);
+ assert(Range.getLower().getNumWords() == 1);
+ assert(Range.getUpper().getNumWords() == 1);
+ emitSignedInt64(Record, *Range.getLower().getRawData());
+ emitSignedInt64(Record, *Range.getUpper().getRawData());
+ };
+
+ if (!FS->paramAccesses().empty()) {
+ Record.clear();
+ for (auto &Arg : FS->paramAccesses()) {
+ Record.push_back(Arg.ParamNo);
+ WriteRange(Arg.Use);
+ Record.push_back(Arg.Calls.size());
+ for (auto &Call : Arg.Calls) {
+ Record.push_back(Call.ParamNo);
+ Record.push_back(Call.Callee);
+ WriteRange(Call.Offsets);
+ }
+ }
+ Stream.EmitRecord(bitc::FS_PARAM_ACCESS, Record);
+ }
}
/// Collect type IDs from type tests used by function.
if (const auto *TIdInfo = FS->getTypeIdInfo())
printTypeIdInfo(*TIdInfo);
+
+ auto PrintRange = [&](const ConstantRange &Range) {
+ Out << "[" << Range.getLower() << ", " << Range.getSignedMax() << "]";
+ };
+
+ if (!FS->paramAccesses().empty()) {
+ Out << ", params: (";
+ FieldSeparator IFS;
+ for (auto &PS : FS->paramAccesses()) {
+ Out << IFS;
+ Out << "(param: " << PS.ParamNo;
+ Out << ", offset: ";
+ PrintRange(PS.Use);
+ if (!PS.Calls.empty()) {
+ Out << ", calls: (";
+ FieldSeparator IFS;
+ for (auto &Call : PS.Calls) {
+ Out << IFS;
+ Out << "(callee: ^" << Machine.getGUIDSlot(Call.Callee);
+ Out << ", param: " << Call.ParamNo;
+ Out << ", offset: ";
+ PrintRange(Call.Offsets);
+ Out << ")";
+ }
+ Out << ")";
+ }
+ Out << ")";
+ }
+ Out << ")";
+ }
}
void AssemblyWriter::printTypeIdInfo(
"import-constants-with-refs", cl::init(true), cl::Hidden,
cl::desc("Import constant global variables with references"));
+constexpr uint32_t FunctionSummary::ParamAccess::RangeWidth;
+
FunctionSummary FunctionSummary::ExternalNode =
FunctionSummary::makeDummyFunctionSummary({});
--- /dev/null
+; REQUIRES: aarch64-registered-target
+
+; For convenience, to show what is being serialized.
+; RUN: opt -S -passes="print<stack-safety-local>" -disable-output < %s 2>&1 | FileCheck %s --check-prefixes=SSI
+
+; RUN: opt -module-summary %s -o %t.bc
+; RUN: llvm-bcanalyzer -dump %t.bc | FileCheck %s -check-prefixes=BC
+
+; RUN: llvm-dis -o - %t.bc | FileCheck %s --check-prefix=DIS
+; Round trip it through llvm-as
+; RUN: llvm-dis -o - %t.bc | llvm-as -o - | llvm-dis -o - | FileCheck %s --check-prefix=DIS
+
+; RUN: opt -thinlto-bc %s -o %t.bc
+; RUN: llvm-bcanalyzer -dump %t.bc | FileCheck %s -check-prefixes=BC
+
+; RUN: llvm-dis -o - %t.bc | FileCheck %s --check-prefix=DIS
+; Round trip it through llvm-as
+; RUN: llvm-dis -o - %t.bc | llvm-as -o - | llvm-dis -o - | FileCheck %s --check-prefix=DIS
+
+; DIS: ^0 = module: (path: "{{.*}}", hash: ({{.*}}))
+; ModuleID = 'thinlto-function-summary-paramaccess.ll'
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64-unknown-linux"
+
+attributes #0 = { noinline sanitize_memtag "target-features"="+mte,+neon" }
+
+; BC-LABEL: <GLOBALVAL_SUMMARY_BLOCK
+; BC-NEXT: <VERSION
+; BC-NEXT: <FLAGS
+
+; DIS-DAG: = gv: (name: "Callee") ; guid = 900789920918863816
+declare void @Callee(i8* %p)
+
+; DIS-DAG: = gv: (name: "Callee2") ; guid = 72710208629861106
+declare void @Callee2(i32 %x, i8* %p)
+
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "NoParam", summaries: {{.*}} guid = 10287433468618421703
+define void @NoParam() #0 {
+entry:
+ ret void
+}
+
+; SSI-LABEL: function 'IntParam'
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "IntParam", summaries: {{.*}} guid = 13164714711077064397
+define void @IntParam(i32 %x) #0 {
+entry:
+ ret void
+}
+
+; SSI-LABEL: for function 'WriteNone'
+; SSI: p[]: empty-set
+; BC-NEXT: <PARAM_ACCESS op0=0 op1=0 op2=0 op3=0/>
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "WriteNone", summaries: {{.*}} params: ((param: 0, offset: [0, -1]))))) ; guid = 15261848357689602442
+define void @WriteNone(i8* %p) #0 {
+entry:
+ ret void
+}
+
+; SSI-LABEL: for function 'Write0'
+; SSI: p[]: [0,1)
+; BC-NEXT: <PARAM_ACCESS op0=0 op1=0 op2=2 op3=0/>
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "Write0", summaries: {{.*}} params: ((param: 0, offset: [0, 0]))))) ; guid = 5540766144860458461
+define void @Write0(i8* %p) #0 {
+entry:
+ store i8 0, i8* %p
+ ret void
+}
+
+; SSI-LABEL: for function 'WriteOffset'
+; SSI: p[]: [12,16)
+; BC-NEXT: <PARAM_ACCESS op0=0 op1=24 op2=32 op3=0/>
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "WriteOffset", summaries: {{.*}} params: ((param: 0, offset: [12, 15]))))) ; guid = 1417835201204712148
+define void @WriteOffset(i8* %p) #0 {
+entry:
+ %0 = bitcast i8* %p to i32*
+ %1 = getelementptr i32, i32* %0, i64 3
+ store i32 0, i32* %1
+ ret void
+}
+
+; SSI-LABEL: for function 'WriteNegOffset'
+; SSI: p[]: [-56,-48)
+; BC-NEXT: <PARAM_ACCESS op0=0 op1=113 op2=97 op3=0/>
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "WriteNegOffset", summaries: {{.*}} params: ((param: 0, offset: [-56, -49]))))) ; guid = 11847411556962310546
+define void @WriteNegOffset(i8* %p) #0 {
+entry:
+ %0 = bitcast i8* %p to i64*
+ %1 = getelementptr i64, i64* %0, i64 -7
+ store i64 0, i64* %1
+ ret void
+}
+
+; SSI-LABEL: for function 'WriteAnyOffset'
+; SSI: p[]: [-9223372036854775808,9223372036854775807)
+; BC-NEXT: <PARAM_ACCESS op0=0 op1=1 op2=-2 op3=0/>
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "WriteAnyOffset", summaries: {{.*}} params: ((param: 0, offset: [-9223372036854775808, 9223372036854775806]))))) ; guid = 16159595372881907190
+define void @WriteAnyOffset(i8* %p, i64 %i) #0 {
+entry:
+ %0 = bitcast i8* %p to i24*
+ %1 = getelementptr i24, i24* %0, i64 %i
+ store i24 0, i24* %1
+ ret void
+}
+
+; SSI-LABEL: for function 'WritePQ'
+; SSI: p[]: [0,1)
+; SSI: q[]: [0,4)
+; BC-NEXT: <PARAM_ACCESS op0=0 op1=0 op2=2 op3=0 op4=1 op5=0 op6=8 op7=0/>
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "WritePQ", summaries: {{.*}} params: ((param: 0, offset: [0, 0]), (param: 1, offset: [0, 3]))))) ; guid = 6187077497926519485
+define void @WritePQ(i8* %p, i32* %q) #0 {
+entry:
+ store i8 5, i8* %p
+ store i32 6, i32* %q
+ ret void
+}
+
+; SSI-LABEL: for function 'WriteTwoPIQ'
+; SSI: p[]: [0,1)
+; SSI: q[]: [0,4)
+; BC-NEXT: <PARAM_ACCESS op0=0 op1=0 op2=2 op3=0 op4=2 op5=0 op6=8 op7=0/>
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "WriteTwoPIQ", summaries: {{.*}} params: ((param: 0, offset: [0, 0]), (param: 2, offset: [0, 3]))))) ; guid = 2949024673554120799
+define void @WriteTwoPIQ(i8* %p, i32 %i, i32* %q) #0 {
+entry:
+ store i8 7, i8* %p
+ store i32 %i, i32* %q
+ ret void
+}
+
+; SSI-LABEL: for function 'Call'
+; SSI: p[]: empty-set, @Callee(arg0, [0,1))
+; BC-NEXT: <PARAM_ACCESS op0=0 op1=0 op2=0 op3=1 op4=0 op5=[[CALLEE:-?[0-9]+]] op6=0 op7=2/>
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "Call", summaries: {{.*}} calls: ((callee: ^{{.*}})), params: ((param: 0, offset: [0, -1], calls: ((callee: ^{{.*}}, param: 0, offset: [0, 0]))))))) ; guid = 8411925997558855107
+define void @Call(i8* %p) #0 {
+entry:
+ call void @Callee(i8* %p)
+ ret void
+}
+
+; SSI-LABEL: for function 'CallOffset'
+; SSI: p[]: empty-set, @Callee(arg0, [2,3))
+; BC-NEXT: <PARAM_ACCESS op0=0 op1=0 op2=0 op3=1 op4=0 op5=[[CALLEE]] op6=4 op7=6/>
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "CallOffset", summaries: {{.*}} calls: ((callee: ^{{.*}})), params: ((param: 0, offset: [0, -1], calls: ((callee: ^{{.*}}, param: 0, offset: [2, 2]))))))) ; guid = 1075564720951610524
+define void @CallOffset(i8* %p) #0 {
+entry:
+ %p1 = getelementptr i8, i8* %p, i64 2
+ call void @Callee(i8* %p1)
+ ret void
+}
+
+; SSI-LABEL: for function 'CallNegOffset'
+; SSI: p[]: empty-set, @Callee(arg0, [-715,-714))
+; BC-NEXT: <PARAM_ACCESS op0=0 op1=0 op2=0 op3=1 op4=0 op5=[[CALLEE]] op6=1431 op7=1429/>
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "CallNegOffset", summaries: {{.*}} calls: ((callee: ^{{.*}})), params: ((param: 0, offset: [0, -1], calls: ((callee: ^{{.*}}, param: 0, offset: [-715, -715]))))))) ; guid = 16532891468562335146
+define void @CallNegOffset(i8* %p) #0 {
+entry:
+ %p1 = getelementptr i8, i8* %p, i64 -715
+ call void @Callee(i8* %p1)
+ ret void
+}
+
+; BC-NEXT: <PERMODULE
+; SSI-LABEL: for function 'CallAnyOffset'
+; SSI: p[]: empty-set, @Callee(arg0, full-set)
+; DIS-DAG: = gv: (name: "CallAnyOffset", summaries: {{.*}} calls: ((callee: ^{{.*}}))))) ; guid = 4179978066780831873
+define void @CallAnyOffset(i8* %p, i64 %i) #0 {
+entry:
+ %p1 = getelementptr i8, i8* %p, i64 %i
+ call void @Callee(i8* %p1)
+ ret void
+}
+
+; SSI-LABEL: for function 'CallMany'
+; SSI: p[]: empty-set, @Callee(arg0, [-715,-714)), @Callee(arg0, [-33,-32)), @Callee(arg0, [124,125))
+; BC-NEXT: <PARAM_ACCESS op0=0 op1=0 op2=0 op3=3 op4=0 op5=[[CALLEE]] op6=1431 op7=1429 op8=0 op9=[[CALLEE]] op10=67 op11=65 op12=0 op13=[[CALLEE]] op14=248 op15=250/>
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "CallMany", summaries: {{.*}} calls: ((callee: ^{{.*}})), params: ((param: 0, offset: [0, -1], calls: ((callee: ^{{.*}}, param: 0, offset: [-715, -715]), (callee: ^{{.*}}, param: 0, offset: [-33, -33]), (callee: ^{{.*}}, param: 0, offset: [124, 124]))))))) ; guid = 17150418543861409076
+define void @CallMany(i8* %p) #0 {
+entry:
+ %p0 = getelementptr i8, i8* %p, i64 -715
+ call void @Callee(i8* %p0)
+
+ %p1 = getelementptr i8, i8* %p, i64 -33
+ call void @Callee(i8* %p1)
+
+ %p2 = getelementptr i8, i8* %p, i64 124
+ call void @Callee(i8* %p2)
+
+ ret void
+}
+
+; SSI-LABEL: for function 'CallMany2'
+; SSI: p[]: empty-set, @Callee(arg0, [-715,-714)), @Callee2(arg1, [-33,-32)), @Callee(arg0, [124,125))
+; BC-NEXT: <PARAM_ACCESS op0=0 op1=0 op2=0 op3=3 op4=0 op5=[[CALLEE]] op6=1431 op7=1429 op8=1 op9=[[CALLEE2:-?[0-9]+]] op10=67 op11=65 op12=0 op13=[[CALLEE]] op14=248 op15=250/>
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "CallMany2", summaries: {{.*}} calls: ((callee: ^{{.*}}), (callee: ^{{.*}})), params: ((param: 0, offset: [0, -1], calls: ((callee: ^{{.*}}, param: 0, offset: [-715, -715]), (callee: ^{{.*}}, param: 1, offset: [-33, -33]), (callee: ^{{.*}}, param: 0, offset: [124, 124]))))))) ; guid = 16654048340802466690
+define void @CallMany2(i8* %p) #0 {
+entry:
+ %p0 = getelementptr i8, i8* %p, i64 -715
+ call void @Callee(i8* %p0)
+
+ %p1 = getelementptr i8, i8* %p, i64 -33
+ call void @Callee2(i32 6, i8* %p1)
+
+ %p2 = getelementptr i8, i8* %p, i64 124
+ call void @Callee(i8* %p2)
+
+ ret void
+}
+
+; SSI-LABEL: for function 'CallManyUnsafe'
+; SSI: p[]: full-set, @Callee(arg0, [-715,-714)), @Callee(arg0, [-33,-32)), @Callee(arg0, [124,125))
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "CallManyUnsafe", summaries: {{.*}} calls: ((callee: ^{{.*}}))))) ; guid = 15696680128757863301
+define void @CallManyUnsafe(i8* %p, i64 %i) #0 {
+entry:
+ %pi = getelementptr i8, i8* %p, i64 %i
+ store i8 5, i8* %pi
+
+ %p0 = getelementptr i8, i8* %p, i64 -715
+ call void @Callee(i8* %p0)
+
+ %p1 = getelementptr i8, i8* %p, i64 -33
+ call void @Callee(i8* %p1)
+
+ %p2 = getelementptr i8, i8* %p, i64 124
+ call void @Callee(i8* %p2)
+
+ ret void
+}
+
+; SSI-LABEL: for function 'Ret'
+; SSI: p[]: full-set
+; BC-NEXT: <PERMODULE
+; DIS-DAG: = gv: (name: "Ret", summaries: {{.*}} ; guid = 6707380319572075172
+define i8* @Ret(i8* %p) #0 {
+entry:
+ ret i8* %p
+}
+
+; BC-NOT: <PERMODULE
+; BC-NOT: <PARAM_ACCESS1
+
+