diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -6715,7 +6715,7 @@ .. 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 @@ -6725,7 +6725,7 @@ The ``insts`` field contains the number of IR instructions in the function. Finally, there are several optional fields: :ref:`FuncFlags`, :ref:`Calls`, :ref:`TypeIdInfo`, -:ref:`Refs`. +:ref:`Params`, :ref:`Refs`. .. _variable_summary: @@ -6793,6 +6793,38 @@ 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 know 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 diff --git a/llvm/include/llvm/Analysis/ModuleSummaryAnalysis.h b/llvm/include/llvm/Analysis/ModuleSummaryAnalysis.h --- a/llvm/include/llvm/Analysis/ModuleSummaryAnalysis.h +++ b/llvm/include/llvm/Analysis/ModuleSummaryAnalysis.h @@ -25,6 +25,7 @@ class Function; class Module; class ProfileSummaryInfo; +class StackSafetyInfo; /// Direct function to compute a \c ModuleSummaryIndex from a given module. /// @@ -35,7 +36,9 @@ ModuleSummaryIndex buildModuleSummaryIndex( const Module &M, std::function GetBFICallback, - ProfileSummaryInfo *PSI); + ProfileSummaryInfo *PSI, + std::function GetSSICallback = + [](const Function &F) -> const StackSafetyInfo * { return nullptr; }); /// Analysis pass to provide the ModuleSummaryIndex object. class ModuleSummaryIndexAnalysis diff --git a/llvm/include/llvm/Analysis/StackSafetyAnalysis.h b/llvm/include/llvm/Analysis/StackSafetyAnalysis.h --- a/llvm/include/llvm/Analysis/StackSafetyAnalysis.h +++ b/llvm/include/llvm/Analysis/StackSafetyAnalysis.h @@ -13,6 +13,7 @@ #ifndef LLVM_ANALYSIS_STACKSAFETYANALYSIS_H #define LLVM_ANALYSIS_STACKSAFETYANALYSIS_H +#include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/IR/PassManager.h" #include "llvm/Pass.h" @@ -42,6 +43,9 @@ // TODO: Add useful for client methods. void print(raw_ostream &O) const; + + /// Argument usa for a FunctionSummary. + std::vector getStackSafetyParams() const; }; class StackSafetyGlobalInfo { @@ -145,6 +149,8 @@ ModulePass *createStackSafetyGlobalInfoWrapperPass(); +bool collectStackSafetySummary(const Module &M); + } // end namespace llvm #endif // LLVM_ANALYSIS_STACKSAFETYANALYSIS_H diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -292,6 +292,9 @@ 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_STACK_SAFETY_PARAMS = 25, }; enum MetadataCodes { diff --git a/llvm/include/llvm/IR/ModuleSummaryIndex.h b/llvm/include/llvm/IR/ModuleSummaryIndex.h --- a/llvm/include/llvm/IR/ModuleSummaryIndex.h +++ b/llvm/include/llvm/IR/ModuleSummaryIndex.h @@ -23,6 +23,7 @@ #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" @@ -552,6 +553,24 @@ unsigned AlwaysInline : 1; }; + static constexpr uint32_t StackSafetyRangeWidth = 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 StackSafetyCall { + uint64_t ParamNo = 0; + GlobalValue::GUID Callee = 0; + ConstantRange Offsets{StackSafetyRangeWidth, true}; + }; + + /// 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 StackSafetyParam { + uint64_t ParamNo = 0; + ConstantRange Use{StackSafetyRangeWidth, true}; + std::vector Calls; + }; + /// Create an empty FunctionSummary (with specified call edges). /// Used to represent external nodes and the dummy root node. static FunctionSummary @@ -567,7 +586,8 @@ std::vector(), std::vector(), std::vector(), - std::vector()); + std::vector(), + std::vector()); } /// A dummy node to reference external functions that aren't in the index @@ -591,6 +611,9 @@ std::unique_ptr TIdInfo; + /// Uses for every parameter to this function. + std::vector StackSafetyParams; + public: FunctionSummary(GVFlags Flags, unsigned NumInsts, FFlags FunFlags, uint64_t EntryCount, std::vector Refs, @@ -599,10 +622,12 @@ std::vector TypeTestAssumeVCalls, std::vector TypeCheckedLoadVCalls, std::vector TypeTestAssumeConstVCalls, - std::vector TypeCheckedLoadConstVCalls) + std::vector TypeCheckedLoadConstVCalls, + std::vector StackSafetyParams) : GlobalValueSummary(FunctionKind, Flags, std::move(Refs)), InstCount(NumInsts), FunFlags(FunFlags), EntryCount(EntryCount), - CallGraphEdgeList(std::move(CGEdges)) { + CallGraphEdgeList(std::move(CGEdges)), + StackSafetyParams(std::move(StackSafetyParams)) { if (!TypeTests.empty() || !TypeTestAssumeVCalls.empty() || !TypeCheckedLoadVCalls.empty() || !TypeTestAssumeConstVCalls.empty() || !TypeCheckedLoadConstVCalls.empty()) @@ -681,6 +706,18 @@ return {}; } + /// Returns the uses for every parameter in this function. + ArrayRef stackSafetyParams() const { + return StackSafetyParams; + } + + /// Set the uses for parameters in this function, these values are updated + /// after the StackSafetyGlobalAnalysis as only some of the information is + /// required by the backends. + void setStackSafetyParams(std::vector NewParams) { + StackSafetyParams = 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) { diff --git a/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h b/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h --- a/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h +++ b/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h @@ -223,13 +223,15 @@ Elem.SummaryList.push_back(std::make_unique( GlobalValueSummary::GVFlags( static_cast(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{}, 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{})); } } static void output(IO &io, GlobalValueSummaryMapTy &V) { diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp --- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp +++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -25,6 +25,7 @@ #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" @@ -238,12 +239,12 @@ return false; } -static void computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, - const Function &F, BlockFrequencyInfo *BFI, - ProfileSummaryInfo *PSI, DominatorTree &DT, - bool HasLocalsInUsedOrAsm, - DenseSet &CantBePromoted, - bool IsThinLTO) { +static void computeFunctionSummary( + ModuleSummaryIndex &Index, const Module &M, const Function &F, + BlockFrequencyInfo *BFI, ProfileSummaryInfo *PSI, DominatorTree &DT, + bool HasLocalsInUsedOrAsm, DenseSet &CantBePromoted, + bool IsThinLTO, + std::function GetSSICallback) { // Summary not currently supported for anonymous functions, they should // have been named. assert(F.hasName()); @@ -469,12 +470,15 @@ // Don't try to import functions with noinline attribute. F.getAttributes().hasFnAttribute(Attribute::NoInline), F.hasFnAttribute(Attribute::AlwaysInline)}; + std::vector StackSafetyParams; + if (auto *SSI = GetSSICallback(F)) + StackSafetyParams = SSI->getStackSafetyParams(); auto FuncSummary = std::make_unique( Flags, NumInsts, FunFlags, /*EntryCount=*/0, std::move(Refs), CallGraphEdges.takeVector(), TypeTests.takeVector(), TypeTestAssumeVCalls.takeVector(), TypeCheckedLoadVCalls.takeVector(), TypeTestAssumeConstVCalls.takeVector(), - TypeCheckedLoadConstVCalls.takeVector()); + TypeCheckedLoadConstVCalls.takeVector(), std::move(StackSafetyParams)); if (NonRenamableLocal) CantBePromoted.insert(F.getGUID()); Index.addGlobalValueSummary(F, std::move(FuncSummary)); @@ -643,7 +647,8 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex( const Module &M, std::function GetBFICallback, - ProfileSummaryInfo *PSI) { + ProfileSummaryInfo *PSI, + std::function GetSSICallback) { assert(PSI); bool EnableSplitLTOUnit = false; if (auto *MD = mdconst::extract_or_null( @@ -716,7 +721,8 @@ ArrayRef{}, ArrayRef{}, ArrayRef{}, - ArrayRef{}); + ArrayRef{}, + ArrayRef{}); Index.addGlobalValueSummary(*GV, std::move(Summary)); } else { std::unique_ptr Summary = @@ -756,7 +762,7 @@ 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 @@ -838,13 +844,19 @@ ModuleSummaryIndexAnalysis::run(Module &M, ModuleAnalysisManager &AM) { ProfileSummaryInfo &PSI = AM.getResult(M); auto &FAM = AM.getResult(M).getManager(); + bool NeedSSI = collectStackSafetySummary(M); return buildModuleSummaryIndex( M, [&FAM](const Function &F) { return &FAM.getResult( *const_cast(&F)); }, - &PSI); + &PSI, + [&FAM, NeedSSI](const Function &F) -> const StackSafetyInfo * { + return NeedSSI ? &FAM.getResult( + const_cast(F)) + : nullptr; + }); } char ModuleSummaryIndexWrapperPass::ID = 0; @@ -853,6 +865,7 @@ "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) @@ -867,6 +880,7 @@ bool ModuleSummaryIndexWrapperPass::runOnModule(Module &M) { auto *PSI = &getAnalysis().getPSI(); + bool NeedSSI = collectStackSafetySummary(M); Index.emplace(buildModuleSummaryIndex( M, [this](const Function &F) { @@ -874,7 +888,13 @@ *const_cast(&F)) .getBFI()); }, - PSI)); + PSI, + [&](const Function &F) -> const StackSafetyInfo * { + return NeedSSI ? &getAnalysis( + const_cast(F)) + .getResult() + : nullptr; + })); return false; } @@ -887,4 +907,5 @@ AU.setPreservesAll(); AU.addRequired(); AU.addRequired(); + AU.addRequired(); } diff --git a/llvm/lib/Analysis/StackSafetyAnalysis.cpp b/llvm/lib/Analysis/StackSafetyAnalysis.cpp --- a/llvm/lib/Analysis/StackSafetyAnalysis.cpp +++ b/llvm/lib/Analysis/StackSafetyAnalysis.cpp @@ -671,6 +671,40 @@ return *Info; } +// Converts a StackSafetyFunctionInfo to the relevant FunctionSummary +// constructor fields +std::vector +StackSafetyInfo::getStackSafetyParams() const { + assert(collectStackSafetySummary(*F->getParent())); + + std::vector StackSafetyParams; + for (size_t ParamNo = 0; ParamNo < getInfo().Info.Params.size(); ++ParamNo) { + auto &PS = getInfo().Info.Params[ParamNo]; + if (PS.Range.isFullSet()) + continue; + + StackSafetyParams.emplace_back(); + FunctionSummary::StackSafetyParam &Arg = StackSafetyParams.back(); + + Arg.ParamNo = ParamNo; + Arg.Use = PS.Range; + + Arg.Calls.reserve(PS.Calls.size()); + for (auto &C : PS.Calls) { + if (C.Offset.isFullSet()) { + StackSafetyParams.pop_back(); + break; + } + Arg.Calls.emplace_back(); + FunctionSummary::StackSafetyCall &Call = Arg.Calls.back(); + Call.ParamNo = C.ParamNo; + Call.Callee = C.Callee->getGUID(); + Call.Offsets = C.Offset; + } + } + return StackSafetyParams; +} + StackSafetyGlobalInfo::StackSafetyGlobalInfo() = default; StackSafetyGlobalInfo::StackSafetyGlobalInfo( @@ -795,6 +829,13 @@ return new StackSafetyGlobalInfoWrapperPass(); } +bool llvm::collectStackSafetySummary(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, diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -758,6 +758,8 @@ KEYWORD(alwaysInline); KEYWORD(calls); KEYWORD(callee); + KEYWORD(params); + KEYWORD(param); KEYWORD(hotness); KEYWORD(unknown); KEYWORD(hot); diff --git a/llvm/lib/AsmParser/LLParser.h b/llvm/lib/AsmParser/LLParser.h --- a/llvm/lib/AsmParser/LLParser.h +++ b/llvm/lib/AsmParser/LLParser.h @@ -365,6 +365,12 @@ bool ParseVFuncId(FunctionSummary::VFuncId &VFuncId, IdToIndexMapType &IdToIndexMap, unsigned Index); bool ParseOptionalVTableFuncs(VTableFuncList &VTableFuncs); + bool ParseOptionalStackSafetyParams( + std::vector &Params); + bool ParseStackSafetyParam(FunctionSummary::StackSafetyParam &Param); + bool ParseStackSafetyCall(FunctionSummary::StackSafetyCall &Call); + bool ParseStackSafetyParamNo(uint64_t &ParamNo); + bool ParseStackSafetyOffset(ConstantRange &range); bool ParseOptionalRefs(std::vector &Refs); bool ParseTypeIdEntry(unsigned ID); bool ParseTypeIdSummary(TypeIdSummary &TIS); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -11,6 +11,8 @@ //===----------------------------------------------------------------------===// #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" @@ -22,6 +24,7 @@ #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" @@ -8209,7 +8212,8 @@ /// FunctionSummary /// ::= 'function' ':' '(' 'module' ':' ModuleReference ',' GVFlags /// ',' 'insts' ':' UInt32 [',' OptionalFFlags]? [',' OptionalCalls]? -/// [',' OptionalTypeIdInfo]? [',' OptionalRefs]? ')' +/// [',' OptionalTypeIdInfo]? [',' OptionalStackSafetyParams]? +/// [',' OptionalRefs]? ')' bool LLParser::ParseFunctionSummary(std::string Name, GlobalValue::GUID GUID, unsigned ID) { assert(Lex.getKind() == lltok::kw_function); @@ -8222,6 +8226,7 @@ unsigned InstCount; std::vector Calls; FunctionSummary::TypeIdInfo TypeIdInfo; + std::vector StackSafetyParams; std::vector Refs; // Default is all-zeros (conservative values). FunctionSummary::FFlags FFlags = {}; @@ -8253,6 +8258,10 @@ if (ParseOptionalRefs(Refs)) return true; break; + case lltok::kw_params: + if (ParseOptionalStackSafetyParams(StackSafetyParams)) + return true; + break; default: return Error(Lex.getLoc(), "expected optional function summary field"); } @@ -8267,7 +8276,8 @@ std::move(TypeIdInfo.TypeTestAssumeVCalls), std::move(TypeIdInfo.TypeCheckedLoadVCalls), std::move(TypeIdInfo.TypeTestAssumeConstVCalls), - std::move(TypeIdInfo.TypeCheckedLoadConstVCalls)); + std::move(TypeIdInfo.TypeCheckedLoadConstVCalls), + std::move(StackSafetyParams)); FS->setModulePath(ModulePath); @@ -8616,13 +8626,135 @@ return false; } +/// ParamNo := 'param' ':' UInt64 +bool LLParser::ParseStackSafetyParamNo(uint64_t &ParamNo) { + if (ParseToken(lltok::kw_param, "expected 'param' here") || + ParseToken(lltok::colon, "expected ':' here") || ParseUInt64(ParamNo)) + return true; + return false; +} + +/// StackSafetyOffset := 'offset' ':' '[' APSINTVAL ',' APSINTVAL ']' +bool LLParser::ParseStackSafetyOffset(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::StackSafetyRangeWidth); + 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::StackSafetyRangeWidth) + : ConstantRange(Lower, Upper); + + return false; +} + +/// StackSafetyCall +/// := '(' 'callee' ':' GVReference ',' StackSafetyParamNo ',' +/// StackSafetyOffset ')' +bool LLParser::ParseStackSafetyCall(FunctionSummary::StackSafetyCall &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") || + ParseStackSafetyParamNo(Call.ParamNo) || + ParseToken(lltok::comma, "expected ',' here") || + ParseStackSafetyOffset(Call.Offsets)) + return true; + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + return false; +} + +/// StackSafetyParam +/// := '(' StackSafetyParamNo ',' StackSafetyOffset +/// [',' OptionalStackSafetyCalls]? ')' +/// OptionalStackSafetyCalls +/// := '(' StackSafetyCall [',' StackSafetyCall]* ')' +bool LLParser::ParseStackSafetyParam(FunctionSummary::StackSafetyParam &Param) { + if (ParseToken(lltok::lparen, "expected '(' here") || + ParseStackSafetyParamNo(Param.ParamNo) || + ParseToken(lltok::comma, "expected ',' here") || + ParseStackSafetyOffset(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::StackSafetyCall Call; + if (ParseStackSafetyCall(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; +} + +/// OptionalStackSafetyParams +/// := 'params' ':' '(' StackSafetyParam [',' StackSafetyParam]* ')' +bool LLParser::ParseOptionalStackSafetyParams( + std::vector &Params) { + assert(Lex.getKind() == lltok::kw_params); + Lex.Lex(); + + if (ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here")) + return true; + + do { + FunctionSummary::StackSafetyParam StackSafetyParam; + if (ParseStackSafetyParam(StackSafetyParam)) + return true; + Params.push_back(StackSafetyParam); + } while (EatIfPresent(lltok::comma)); + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + return false; +} + /// OptionalRefs /// := 'refs' ':' '(' GVReference [',' GVReference]* ')' bool LLParser::ParseOptionalRefs(std::vector &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; diff --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -391,6 +391,8 @@ kw_alwaysInline, kw_calls, kw_callee, + kw_params, + kw_param, kw_hotness, kw_unknown, kw_hot, diff --git a/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp b/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp @@ -306,6 +306,7 @@ STRINGIFY_CODE(FS, TYPE_ID) STRINGIFY_CODE(FS, TYPE_ID_METADATA) STRINGIFY_CODE(FS, BLOCK_COUNT) + STRINGIFY_CODE(FS, STACK_SAFETY_PARAMS) } case bitc::METADATA_ATTACHMENT_ID: switch (CodeID) { diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -5804,6 +5804,42 @@ parseWholeProgramDevirtResolution(Record, Strtab, Slot, TypeId); } +static std::vector +parseStackSafetyParams(ArrayRef Record) { + auto ReadRange = [&]() { + APInt Lower(FunctionSummary::StackSafetyRangeWidth, + BitcodeReader::decodeSignRotatedValue(Record.front())); + Record = Record.drop_front(); + APInt Upper(FunctionSummary::StackSafetyRangeWidth, + BitcodeReader::decodeSignRotatedValue(Record.front())); + Record = Record.drop_front(); + ConstantRange Range{Lower, Upper}; + assert(!Range.isFullSet()); + assert(!Range.isUpperSignWrapped()); + return Range; + }; + + std::vector PendingStackSafetyParams; + while (!Record.empty()) { + PendingStackSafetyParams.emplace_back(); + FunctionSummary::StackSafetyParam &StackSafetyParam = + PendingStackSafetyParams.back(); + StackSafetyParam.ParamNo = Record.front(); + Record = Record.drop_front(); + StackSafetyParam.Use = ReadRange(); + StackSafetyParam.Calls.resize(Record.front()); + Record = Record.drop_front(); + for (auto &Call : StackSafetyParam.Calls) { + Call.ParamNo = Record.front(); + Record = Record.drop_front(); + Call.Callee = Record.front(); + Record = Record.drop_front(); + Call.Offsets = ReadRange(); + } + } + return PendingStackSafetyParams; +} + void ModuleSummaryIndexBitcodeReader::parseTypeIdCompatibleVtableInfo( ArrayRef Record, size_t &Slot, TypeIdCompatibleVtableInfo &TypeId) { @@ -5881,6 +5917,7 @@ PendingTypeCheckedLoadVCalls; std::vector PendingTypeTestAssumeConstVCalls, PendingTypeCheckedLoadConstVCalls; + std::vector PendingStackSafetyParams; while (true) { Expected MaybeEntry = Stream.advanceSkippingSubblocks(); @@ -5979,7 +6016,8 @@ std::move(PendingTypeTestAssumeVCalls), std::move(PendingTypeCheckedLoadVCalls), std::move(PendingTypeTestAssumeConstVCalls), - std::move(PendingTypeCheckedLoadConstVCalls)); + std::move(PendingTypeCheckedLoadConstVCalls), + std::move(PendingStackSafetyParams)); auto VIAndOriginalGUID = getValueInfoFromValueId(ValueID); FS->setModulePath(getThisModule()->first()); FS->setOriginalName(VIAndOriginalGUID.second); @@ -6121,7 +6159,8 @@ std::move(PendingTypeTestAssumeVCalls), std::move(PendingTypeCheckedLoadVCalls), std::move(PendingTypeTestAssumeConstVCalls), - std::move(PendingTypeCheckedLoadConstVCalls)); + std::move(PendingTypeCheckedLoadConstVCalls), + std::move(PendingStackSafetyParams)); LastSeenSummary = FS.get(); LastSeenGUID = VI.getGUID(); FS->setModulePath(ModuleIdMap[ModuleId]); @@ -6242,6 +6281,12 @@ case bitc::FS_BLOCK_COUNT: TheIndex.addBlockCount(Record[0]); + break; + + case bitc::FS_STACK_SAFETY_PARAMS: { + PendingStackSafetyParams = parseStackSafetyParams(Record); + break; + } } } llvm_unreachable("Exit infinite loop"); diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -3576,6 +3576,29 @@ 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::StackSafetyRangeWidth); + assert(Range.getLower().getNumWords() == 1); + assert(Range.getUpper().getNumWords() == 1); + emitSignedInt64(Record, *Range.getLower().getRawData()); + emitSignedInt64(Record, *Range.getUpper().getRawData()); + }; + + if (!FS->stackSafetyParams().empty()) { + Record.clear(); + for (auto &Arg : FS->stackSafetyParams()) { + 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_STACK_SAFETY_PARAMS, Record); + } } /// Collect type IDs from type tests used by function. diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -3083,6 +3083,36 @@ if (const auto *TIdInfo = FS->getTypeIdInfo()) printTypeIdInfo(*TIdInfo); + + auto PrintRange = [&](const ConstantRange &Range) { + Out << "[" << Range.getLower() << ", " << Range.getSignedMax() << "]"; + }; + + if (!FS->stackSafetyParams().empty()) { + Out << ", params: ("; + FieldSeparator IFS; + for (auto &PS : FS->stackSafetyParams()) { + 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( diff --git a/llvm/lib/IR/ModuleSummaryIndex.cpp b/llvm/lib/IR/ModuleSummaryIndex.cpp --- a/llvm/lib/IR/ModuleSummaryIndex.cpp +++ b/llvm/lib/IR/ModuleSummaryIndex.cpp @@ -35,6 +35,8 @@ "import-constants-with-refs", cl::init(true), cl::Hidden, cl::desc("Import constant global variables with references")); +constexpr uint32_t FunctionSummary::StackSafetyRangeWidth; + FunctionSummary FunctionSummary::ExternalNode = FunctionSummary::makeDummyFunctionSummary({}); diff --git a/llvm/test/Bitcode/thinlto-function-summary-stacksafety.ll b/llvm/test/Bitcode/thinlto-function-summary-stacksafety.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Bitcode/thinlto-function-summary-stacksafety.ll @@ -0,0 +1,261 @@ +; REQUIRES: aarch64-registered-target + +; For convenience, to show what is being serialized. +; RUN: opt -S -passes="print" -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-stacksafety.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: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: