diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -6728,7 +6728,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 @@ -6738,7 +6738,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: @@ -6806,6 +6806,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 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 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; + + /// Parameters use for a FunctionSummary. + std::vector getParamAccesses() const; }; class StackSafetyGlobalInfo { @@ -143,6 +147,8 @@ bool runOnModule(Module &M) override; }; +bool needsParamAccessSummary(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_PARAM_ACCESS = 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,34 @@ 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 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 @@ -567,7 +596,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 +621,9 @@ std::unique_ptr TIdInfo; + /// Uses for every parameter to this function. + std::vector ParamAccesses; + public: FunctionSummary(GVFlags Flags, unsigned NumInsts, FFlags FunFlags, uint64_t EntryCount, std::vector Refs, @@ -599,10 +632,12 @@ std::vector TypeTestAssumeVCalls, std::vector TypeCheckedLoadVCalls, std::vector TypeTestAssumeConstVCalls, - std::vector TypeCheckedLoadConstVCalls) + std::vector TypeCheckedLoadConstVCalls, + std::vector 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()) @@ -681,6 +716,14 @@ return {}; } + /// Returns the list of known uses of pointer parameters. + ArrayRef paramAccesses() const { return ParamAccesses; } + + /// Sets the list of known uses of pointer parameters. + void setParamAccesses(std::vector 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) { 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 ParamAccesses; + if (auto *SSI = GetSSICallback(F)) + ParamAccesses = SSI->getParamAccesses(); 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(ParamAccesses)); 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 = needsParamAccessSummary(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 = needsParamAccessSummary(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 @@ -665,6 +665,33 @@ return *Info; } +// Converts a StackSafetyFunctionInfo to the relevant FunctionSummary +// constructor fields +std::vector +StackSafetyInfo::getParamAccesses() const { + assert(needsParamAccessSummary(*F->getParent())); + + std::vector 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( @@ -785,6 +812,13 @@ 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, 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 ParseOptionalParamAccesses( + std::vector &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 &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]? [',' OptionalParamAccesses]? +/// [',' 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 ParamAccesses; 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 (ParseOptionalParamAccesses(ParamAccesses)) + 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(ParamAccesses)); FS->setModulePath(ModulePath); @@ -8616,13 +8626,133 @@ 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 &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 &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, PARAM_ACCESS) } 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,41 @@ parseWholeProgramDevirtResolution(Record, Strtab, Slot, TypeId); } +static std::vector +parseParamAccesses(ArrayRef 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 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 Record, size_t &Slot, TypeIdCompatibleVtableInfo &TypeId) { @@ -5881,6 +5916,7 @@ PendingTypeCheckedLoadVCalls; std::vector PendingTypeTestAssumeConstVCalls, PendingTypeCheckedLoadConstVCalls; + std::vector PendingParamAccesses; while (true) { Expected MaybeEntry = Stream.advanceSkippingSubblocks(); @@ -5979,7 +6015,8 @@ 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); @@ -6121,7 +6158,8 @@ 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]); @@ -6242,6 +6280,12 @@ case bitc::FS_BLOCK_COUNT: TheIndex.addBlockCount(Record[0]); + break; + + case bitc::FS_PARAM_ACCESS: { + PendingParamAccesses = parseParamAccesses(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::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. 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->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( 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::ParamAccess::RangeWidth; + FunctionSummary FunctionSummary::ExternalNode = FunctionSummary::makeDummyFunctionSummary({}); diff --git a/llvm/test/Bitcode/thinlto-function-summary-paramaccess.ll b/llvm/test/Bitcode/thinlto-function-summary-paramaccess.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Bitcode/thinlto-function-summary-paramaccess.ll @@ -0,0 +1,255 @@ +; 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-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: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: +; BC-NEXT: