[llvm-branch-commits] [clang] Fix lifetimebound for field access (#100197) (PR #100725)
https://github.com/usx95 created https://github.com/llvm/llvm-project/pull/100725 Fixes: https://github.com/llvm/llvm-project/issues/81589 There is no way to switch this off without `-Wno-dangling`. >From f9e213895548bfaba21399cb337688e8f3839a3a Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Wed, 24 Jul 2024 15:58:52 +0200 Subject: [PATCH] Fix lifetimebound for field access (#100197) Fixes: https://github.com/llvm/llvm-project/issues/81589 There is no way to switch this off without `-Wno-dangling`. --- clang/docs/ReleaseNotes.rst | 3 +++ clang/lib/Sema/CheckExprLifetime.cpp | 9 clang/test/SemaCXX/attr-lifetimebound.cpp | 26 +++ 3 files changed, 38 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 549da6812740f..71d615553c613 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -767,6 +767,9 @@ Improvements to Clang's diagnostics - Clang now diagnoses undefined behavior in constant expressions more consistently. This includes invalid shifts, and signed overflow in arithmetic. +- Clang now diagnoses dangling references to fields of temporary objects. Fixes #GH81589. + + Improvements to Clang's time-trace -- diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp index 5c8ef564f30aa..112cf3d081822 100644 --- a/clang/lib/Sema/CheckExprLifetime.cpp +++ b/clang/lib/Sema/CheckExprLifetime.cpp @@ -7,6 +7,7 @@ //===--===// #include "CheckExprLifetime.h" +#include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/Basic/DiagnosticSema.h" #include "clang/Sema/Initialization.h" @@ -548,6 +549,14 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, EnableLifetimeWarnings); } + if (auto *M = dyn_cast(Init)) { +// Lifetime of a non-reference type field is same as base object. +if (auto *F = dyn_cast(M->getMemberDecl()); +F && !F->getType()->isReferenceType()) + visitLocalsRetainedByInitializer(Path, M->getBase(), Visit, true, + EnableLifetimeWarnings); + } + if (isa(Init)) { if (EnableLifetimeWarnings) handleGslAnnotatedTypes(Path, Init, Visit); diff --git a/clang/test/SemaCXX/attr-lifetimebound.cpp b/clang/test/SemaCXX/attr-lifetimebound.cpp index 70bc545c07bd9..7db0a4d64d259 100644 --- a/clang/test/SemaCXX/attr-lifetimebound.cpp +++ b/clang/test/SemaCXX/attr-lifetimebound.cpp @@ -47,6 +47,31 @@ namespace usage_ok { q = A(); // expected-warning {{object backing the pointer q will be destroyed at the end of the full-expression}} r = A(1); // expected-warning {{object backing the pointer r will be destroyed at the end of the full-expression}} } + + struct FieldCheck { +struct Set { + int a; +}; +struct Pair { + const int& a; + int b; + Set c; + int * d; +}; +Pair p; +FieldCheck(const int& a): p(a){} +Pair& getR() [[clang::lifetimebound]] { return p; } +Pair* getP() [[clang::lifetimebound]] { return &p; } +Pair* getNoLB() { return &p; } + }; + void test_field_access() { +int x = 0; +const int& a = FieldCheck{x}.getR().a; +const int& b = FieldCheck{x}.getP()->b; // expected-warning {{temporary bound to local reference 'b' will be destroyed at the end of the full-expression}} +const int& c = FieldCheck{x}.getP()->c.a; // expected-warning {{temporary bound to local reference 'c' will be destroyed at the end of the full-expression}} +const int& d = FieldCheck{x}.getNoLB()->c.a; +const int* e = FieldCheck{x}.getR().d; + } } # 1 "" 1 3 @@ -239,3 +264,4 @@ namespace move_forward_et_al_examples { S X; S *AddressOfOk = std::addressof(X); } // namespace move_forward_et_al_examples + ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] Fix lifetimebound for field access (#100197) (PR #100725)
https://github.com/usx95 milestoned https://github.com/llvm/llvm-project/pull/100725 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [codegen] Emit missing cleanups for stmt-expr and coro suspensions [take-2] (PR #85398)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/85398 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang-tools-extra] a4f3866 - [clangd] Remove "decision-forest-base" experimental flag.
Author: Utkarsh Saxena Date: 2021-01-13T17:54:38+01:00 New Revision: a4f386688239b06e09f28fd31f93bf761aa9c76f URL: https://github.com/llvm/llvm-project/commit/a4f386688239b06e09f28fd31f93bf761aa9c76f DIFF: https://github.com/llvm/llvm-project/commit/a4f386688239b06e09f28fd31f93bf761aa9c76f.diff LOG: [clangd] Remove "decision-forest-base" experimental flag. The value of this flag can only be fine tuned by using A/B testing on large user base. We do not expect individual users to use and fine tune this flag. Differential Revision: https://reviews.llvm.org/D94513 Added: Modified: clang-tools-extra/clangd/tool/ClangdMain.cpp Removed: diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp index d2c52cf61c53..9c75cafdb08e 100644 --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -194,14 +194,6 @@ opt RankingModel{ Hidden, }; -opt DecisionForestBase{ -"decision-forest-base", -cat(Features), -desc("Base for exponentiating the prediction from DecisionForest."), -init(CodeCompleteOptions().DecisionForestBase), -Hidden, -}; - // FIXME: also support "plain" style where signatures are always omitted. enum CompletionStyleFlag { Detailed, Bundled }; opt CompletionStyle{ @@ -841,7 +833,6 @@ clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment var Opts.CodeComplete.AllScopes = AllScopesCompletion; Opts.CodeComplete.RunParser = CodeCompletionParse; Opts.CodeComplete.RankingModel = RankingModel; - Opts.CodeComplete.DecisionForestBase = DecisionForestBase; RealThreadsafeFS TFS; std::vector> ProviderStack; ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang-tools-extra] 2f395b7 - [clangd] Make AST-based signals available to runWithPreamble.
Author: Utkarsh Saxena Date: 2021-01-14T18:34:50+01:00 New Revision: 2f395b7092bdac0e39bb4e2bb5e6b03e521a45dd URL: https://github.com/llvm/llvm-project/commit/2f395b7092bdac0e39bb4e2bb5e6b03e521a45dd DIFF: https://github.com/llvm/llvm-project/commit/2f395b7092bdac0e39bb4e2bb5e6b03e521a45dd.diff LOG: [clangd] Make AST-based signals available to runWithPreamble. Many useful signals can be derived from a valid AST which is regularly updated by the ASTWorker. `runWithPreamble` does not have access to the ParsedAST but it can be provided access to some signals derived from a (possibly stale) AST. Differential Revision: https://reviews.llvm.org/D94424 Added: clang-tools-extra/clangd/ASTSignals.cpp clang-tools-extra/clangd/ASTSignals.h clang-tools-extra/clangd/unittests/ASTSignalsTests.cpp Modified: clang-tools-extra/clangd/CMakeLists.txt clang-tools-extra/clangd/TUScheduler.cpp clang-tools-extra/clangd/TUScheduler.h clang-tools-extra/clangd/unittests/CMakeLists.txt clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp Removed: diff --git a/clang-tools-extra/clangd/ASTSignals.cpp b/clang-tools-extra/clangd/ASTSignals.cpp new file mode 100644 index ..da849287bbf6 --- /dev/null +++ b/clang-tools-extra/clangd/ASTSignals.cpp @@ -0,0 +1,42 @@ +//===--- ASTSignals.cpp - LSP server -*- C++-*-===// +// +// 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 +// +//===--===// + +#include "ASTSignals.h" +#include "AST.h" +#include "FindTarget.h" + +namespace clang { +namespace clangd { +ASTSignals ASTSignals::derive(const ParsedAST &AST) { + ASTSignals Signals; + const SourceManager &SM = AST.getSourceManager(); + findExplicitReferences(AST.getASTContext(), [&](ReferenceLoc Ref) { +for (const NamedDecl *ND : Ref.Targets) { + if (!isInsideMainFile(Ref.NameLoc, SM)) +continue; + SymbolID ID = getSymbolID(ND); + if (!ID) +continue; + unsigned &SymbolCount = Signals.ReferencedSymbols[ID]; + SymbolCount++; + // Process namespace only when we see the symbol for the first time. + if (SymbolCount != 1) +continue; + if (const auto *NSD = dyn_cast(ND->getDeclContext())) { +if (NSD->isAnonymousNamespace()) + continue; +std::string NS = printNamespaceScope(*NSD); +if (!NS.empty()) + Signals.RelatedNamespaces[NS]++; + } +} + }); + return Signals; +} +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/ASTSignals.h b/clang-tools-extra/clangd/ASTSignals.h new file mode 100644 index ..bc70cd17310a --- /dev/null +++ b/clang-tools-extra/clangd/ASTSignals.h @@ -0,0 +1,39 @@ +//===--- ASTSignals.h - LSP server ---*- C++-*-===// +// +// 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 +// +//===--===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_ASTSIGNALS_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_ASTSIGNALS_H + +#include "ParsedAST.h" +#include "index/SymbolID.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringMap.h" + +namespace clang { +namespace clangd { + +/// Signals derived from a valid AST of a file. +/// Provides information that can only be extracted from the AST to actions that +/// can't access an AST. The signals are computed and updated asynchronously by +/// the ASTWorker and thus they are always stale and also can be absent. +/// Example usage: Information about the declarations used in a file affects +/// code-completion ranking in that file. +struct ASTSignals { + /// Number of occurrences of each symbol present in the file. + llvm::DenseMap ReferencedSymbols; + /// Namespaces whose symbols are used in the file, and the number of such + /// distinct symbols. + llvm::StringMap RelatedNamespaces; + + static ASTSignals derive(const ParsedAST &AST); +}; + +} // namespace clangd +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_ASTSIGNALS_H diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt index 9e62e0948027..1d12e7e2355d 100644 --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/clang-tools-extra/clangd/CMakeLists.txt @@ -46,6 +46,7 @@ include_directories(BEFORE "${CMAKE_CURRENT_BINARY_DIR}/../clang-tidy") add_clang_library(clangDaemon AST.cpp + ASTSignals.cpp ClangdLSPServer.cpp ClangdServer.cpp CodeComplete.cpp diff --git a/clang-tools-
[llvm-branch-commits] [clang-tools-extra] 8b09cf7 - [clangd] Trivial: Documentation fix in ASTSignals.
Author: Utkarsh Saxena Date: 2021-01-14T18:38:42+01:00 New Revision: 8b09cf7956d8abc722fa736874e4cea667a9d3cb URL: https://github.com/llvm/llvm-project/commit/8b09cf7956d8abc722fa736874e4cea667a9d3cb DIFF: https://github.com/llvm/llvm-project/commit/8b09cf7956d8abc722fa736874e4cea667a9d3cb.diff LOG: [clangd] Trivial: Documentation fix in ASTSignals. Added: Modified: clang-tools-extra/clangd/ASTSignals.cpp clang-tools-extra/clangd/ASTSignals.h Removed: diff --git a/clang-tools-extra/clangd/ASTSignals.cpp b/clang-tools-extra/clangd/ASTSignals.cpp index da849287bbf6..b8cc7f05927a 100644 --- a/clang-tools-extra/clangd/ASTSignals.cpp +++ b/clang-tools-extra/clangd/ASTSignals.cpp @@ -1,4 +1,4 @@ -//===--- ASTSignals.cpp - LSP server -*- C++-*-===// +//===--- ASTSignals.cpp --*- C++-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/clang-tools-extra/clangd/ASTSignals.h b/clang-tools-extra/clangd/ASTSignals.h index bc70cd17310a..fd31be38ce8b 100644 --- a/clang-tools-extra/clangd/ASTSignals.h +++ b/clang-tools-extra/clangd/ASTSignals.h @@ -1,4 +1,4 @@ -//===--- ASTSignals.h - LSP server ---*- C++-*-===// +//===--- ASTSignals.h *- C++-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang-tools-extra] d5047d7 - [clangd] Update CC Ranking model with better sampling.
Author: Utkarsh Saxena Date: 2021-01-15T18:13:24+01:00 New Revision: d5047d762f391c94939d67fc84cae25b24125694 URL: https://github.com/llvm/llvm-project/commit/d5047d762f391c94939d67fc84cae25b24125694 DIFF: https://github.com/llvm/llvm-project/commit/d5047d762f391c94939d67fc84cae25b24125694.diff LOG: [clangd] Update CC Ranking model with better sampling. A better sampling strategy was used to generate the dataset for this model. New signals introduced in this model: - NumNameInContext: Number of words in the context that matches the name of the candidate. - FractionNameInContext: Fraction of the words in context matching the name of the candidate. We remove the signal `IsForbidden` from the model and down rank forbidden signals aggresively. Differential Revision: https://reviews.llvm.org/D94697 Added: Modified: clang-tools-extra/clangd/Quality.cpp clang-tools-extra/clangd/quality/model/features.json clang-tools-extra/clangd/quality/model/forest.json Removed: error: too big or took too long to generate ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang-tools-extra] 0f9908a - [clangd] Use empty() instead of size()>0
Author: Utkarsh Saxena Date: 2021-01-17T15:13:01+01:00 New Revision: 0f9908a7c9c547f2675e00f88cc11ec02ca28e8d URL: https://github.com/llvm/llvm-project/commit/0f9908a7c9c547f2675e00f88cc11ec02ca28e8d DIFF: https://github.com/llvm/llvm-project/commit/0f9908a7c9c547f2675e00f88cc11ec02ca28e8d.diff LOG: [clangd] Use empty() instead of size()>0 Added: Modified: clang-tools-extra/clangd/Quality.cpp Removed: diff --git a/clang-tools-extra/clangd/Quality.cpp b/clang-tools-extra/clangd/Quality.cpp index 7b6a76584778..f076b44f5743 100644 --- a/clang-tools-extra/clangd/Quality.cpp +++ b/clang-tools-extra/clangd/Quality.cpp @@ -512,7 +512,7 @@ evaluateDecisionForest(const SymbolQualitySignals &Quality, E.setIsNameInContext(NumMatch > 0); E.setNumNameInContext(NumMatch); E.setFractionNameInContext( - Relevance.ContextWords && Relevance.ContextWords->size() > 0 + Relevance.ContextWords && Relevance.ContextWords->empty() ? NumMatch * 1.0 / Relevance.ContextWords->size() : 0); E.setIsInBaseClass(Relevance.InBaseClass); ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang-tools-extra] 9abbc05 - [clangd] Use !empty() instead of size()>0
Author: Utkarsh Saxena Date: 2021-01-17T15:26:40+01:00 New Revision: 9abbc050974ff117b79e8e049c52c56db3f49aec URL: https://github.com/llvm/llvm-project/commit/9abbc050974ff117b79e8e049c52c56db3f49aec DIFF: https://github.com/llvm/llvm-project/commit/9abbc050974ff117b79e8e049c52c56db3f49aec.diff LOG: [clangd] Use !empty() instead of size()>0 Added: Modified: clang-tools-extra/clangd/Quality.cpp Removed: diff --git a/clang-tools-extra/clangd/Quality.cpp b/clang-tools-extra/clangd/Quality.cpp index f076b44f5743..27b959ecacb3 100644 --- a/clang-tools-extra/clangd/Quality.cpp +++ b/clang-tools-extra/clangd/Quality.cpp @@ -512,7 +512,7 @@ evaluateDecisionForest(const SymbolQualitySignals &Quality, E.setIsNameInContext(NumMatch > 0); E.setNumNameInContext(NumMatch); E.setFractionNameInContext( - Relevance.ContextWords && Relevance.ContextWords->empty() + Relevance.ContextWords && !Relevance.ContextWords->empty() ? NumMatch * 1.0 / Relevance.ContextWords->size() : 0); E.setIsInBaseClass(Relevance.InBaseClass); ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang-tools-extra] 275716d - [clangd] Derive new signals in CC from ASTSignals.
Author: Utkarsh Saxena Date: 2021-01-18T17:37:27+01:00 New Revision: 275716d6db79a6da3d5cee12139dd0c0abf8fd07 URL: https://github.com/llvm/llvm-project/commit/275716d6db79a6da3d5cee12139dd0c0abf8fd07 DIFF: https://github.com/llvm/llvm-project/commit/275716d6db79a6da3d5cee12139dd0c0abf8fd07.diff LOG: [clangd] Derive new signals in CC from ASTSignals. This patch only introduces new signals but does not use their value in scoring a CC candidate. Usage of these signals in CC ranking in both heiristics and ML model will be introduced in later patches. Differential Revision: https://reviews.llvm.org/D94473 Added: Modified: clang-tools-extra/clangd/ClangdServer.cpp clang-tools-extra/clangd/CodeComplete.cpp clang-tools-extra/clangd/CodeComplete.h clang-tools-extra/clangd/Quality.cpp clang-tools-extra/clangd/Quality.h clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp Removed: diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index 4f3a47dff05d..a76250fa168e 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -255,6 +255,7 @@ void ClangdServer::codeComplete(PathRef File, Position Pos, ParseInput.Opts.BuildRecoveryAST = BuildRecoveryAST; ParseInput.Opts.PreserveRecoveryASTType = PreserveRecoveryASTType; +CodeCompleteOpts.MainFileSignals = IP->Signals; // FIXME(ibiryukov): even if Preamble is non-null, we may want to check // both the old and the new version in case only one of them matches. CodeCompleteResult Result = clangd::codeComplete( diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp index b3b40022fbb2..9cc18ae789d5 100644 --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -1685,6 +1685,7 @@ class CodeCompleteFlow { if (PreferredType) Relevance.HadContextType = true; Relevance.ContextWords = &ContextWords; +Relevance.MainFileSignals = Opts.MainFileSignals; auto &First = Bundle.front(); if (auto FuzzyScore = fuzzyScore(First)) diff --git a/clang-tools-extra/clangd/CodeComplete.h b/clang-tools-extra/clangd/CodeComplete.h index ddcbd487ecc6..debf71d4117c 100644 --- a/clang-tools-extra/clangd/CodeComplete.h +++ b/clang-tools-extra/clangd/CodeComplete.h @@ -15,6 +15,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETE_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETE_H +#include "ASTSignals.h" #include "Compiler.h" #include "Headers.h" #include "Protocol.h" @@ -89,6 +90,7 @@ struct CodeCompleteOptions { /// clangd. const SymbolIndex *Index = nullptr; + const ASTSignals *MainFileSignals = nullptr; /// Include completions that require small corrections, e.g. change '.' to /// '->' on member access etc. bool IncludeFixIts = false; diff --git a/clang-tools-extra/clangd/Quality.cpp b/clang-tools-extra/clangd/Quality.cpp index 27b959ecacb3..1c41b7c7661f 100644 --- a/clang-tools-extra/clangd/Quality.cpp +++ b/clang-tools-extra/clangd/Quality.cpp @@ -294,6 +294,38 @@ void SymbolRelevanceSignals::merge(const Symbol &IndexResult) { if (!(IndexResult.Flags & Symbol::VisibleOutsideFile)) { Scope = AccessibleScope::FileScope; } + if (MainFileSignals) { +MainFileRefs = +std::max(MainFileRefs, + MainFileSignals->ReferencedSymbols.lookup(IndexResult.ID)); +ScopeRefsInFile = +std::max(ScopeRefsInFile, + MainFileSignals->RelatedNamespaces.lookup(IndexResult.Scope)); + } +} + +void SymbolRelevanceSignals::computeASTSignals( +const CodeCompletionResult &SemaResult) { + if (!MainFileSignals) +return; + if ((SemaResult.Kind != CodeCompletionResult::RK_Declaration) && + (SemaResult.Kind != CodeCompletionResult::RK_Pattern)) +return; + if (const NamedDecl *ND = SemaResult.getDeclaration()) { +auto ID = getSymbolID(ND); +if (!ID) + return; +MainFileRefs = +std::max(MainFileRefs, MainFileSignals->ReferencedSymbols.lookup(ID)); +if (const auto *NSD = dyn_cast(ND->getDeclContext())) { + if (NSD->isAnonymousNamespace()) +return; + std::string Scope = printNamespaceScope(*NSD); + if (!Scope.empty()) +ScopeRefsInFile = std::max( +ScopeRefsInFile, MainFileSignals->RelatedNamespaces.lookup(Scope)); +} + } } void SymbolRelevanceSignals::merge(const CodeCompletionResult &SemaCCResult) { @@ -315,6 +347,7 @@ void SymbolRelevanceSignals::merge(const CodeCompletionResult &SemaCCResult) { InBaseClass |= SemaCCResult.InBaseClass; } + computeASTSignals(SemaCCResult); // Declarations are scoped, others (like macros) are assumed global. if (SemaCCResult.Declaration) Scope = std::min(Scope, computeScope(SemaCCResult.Declaration)); diff
[llvm-branch-commits] [clang-tools-extra] 8bf7116 - [clangd] Index local classes, virtual and overriding methods.
Author: Utkarsh Saxena Date: 2021-01-19T16:18:48+01:00 New Revision: 8bf7116d50bfe8cb881273798ff384ed965c05e9 URL: https://github.com/llvm/llvm-project/commit/8bf7116d50bfe8cb881273798ff384ed965c05e9 DIFF: https://github.com/llvm/llvm-project/commit/8bf7116d50bfe8cb881273798ff384ed965c05e9.diff LOG: [clangd] Index local classes, virtual and overriding methods. Previously we did not record local class declarations. Now with features like findImplementation and typeHierarchy, we have a need to index such local classes to accurately report subclasses and implementations of methods. Performance testing results: - No changes in indexing timing. - No significant change in memory usage. - **1%** increase in #relations. - **0.17%** increase in #refs. - **0.22%** increase #symbols. **New index stats** Time to index: **4:13 min** memory usage **543MB** number of symbols: **521.5K** number of refs: **8679K** number of relations: **49K** **Base Index stats** Time to index: **4:15 min** memory usage **542MB** number of symbols: **520K** number of refs: **8664K** number of relations: **48.5K** Fixes: https://github.com/clangd/clangd/issues/644 Differential Revision: https://reviews.llvm.org/D94785 Added: Modified: clang-tools-extra/clangd/index/FileIndex.cpp clang-tools-extra/clangd/index/IndexAction.cpp clang-tools-extra/clangd/index/Serialization.cpp clang-tools-extra/clangd/index/SymbolCollector.cpp clang-tools-extra/clangd/index/SymbolCollector.h clang-tools-extra/clangd/test/index-serialization/Inputs/sample.idx clang-tools-extra/clangd/unittests/FindSymbolsTests.cpp clang-tools-extra/clangd/unittests/SymbolCollectorTests.cpp clang-tools-extra/clangd/unittests/XRefsTests.cpp Removed: diff --git a/clang-tools-extra/clangd/index/FileIndex.cpp b/clang-tools-extra/clangd/index/FileIndex.cpp index 143e76863777..26084c288674 100644 --- a/clang-tools-extra/clangd/index/FileIndex.cpp +++ b/clang-tools-extra/clangd/index/FileIndex.cpp @@ -61,7 +61,8 @@ SlabTuple indexSymbols(ASTContext &AST, std::shared_ptr PP, // We only need declarations, because we don't count references. IndexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly; - IndexOpts.IndexFunctionLocals = false; + // We index function-local classes and its member functions only. + IndexOpts.IndexFunctionLocals = true; if (IsIndexMainAST) { // We only collect refs when indexing main AST. CollectorOpts.RefFilter = RefKind::All; diff --git a/clang-tools-extra/clangd/index/IndexAction.cpp b/clang-tools-extra/clangd/index/IndexAction.cpp index aa65008b51c0..e5a48df90b4d 100644 --- a/clang-tools-extra/clangd/index/IndexAction.cpp +++ b/clang-tools-extra/clangd/index/IndexAction.cpp @@ -212,6 +212,8 @@ std::unique_ptr createStaticIndexingAction( index::IndexingOptions IndexOpts; IndexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::All; + // We index function-local classes and its member functions only. + IndexOpts.IndexFunctionLocals = true; Opts.CollectIncludePath = true; if (Opts.Origin == SymbolOrigin::Unknown) Opts.Origin = SymbolOrigin::Static; diff --git a/clang-tools-extra/clangd/index/Serialization.cpp b/clang-tools-extra/clangd/index/Serialization.cpp index bba5eaa36754..ad1299aa1445 100644 --- a/clang-tools-extra/clangd/index/Serialization.cpp +++ b/clang-tools-extra/clangd/index/Serialization.cpp @@ -452,7 +452,7 @@ readCompileCommand(Reader CmdReader, llvm::ArrayRef Strings) { // The current versioning scheme is simple - non-current versions are rejected. // If you make a breaking change, bump this version number to invalidate stored // data. Later we may want to support some backward compatibility. -constexpr static uint32_t Version = 15; +constexpr static uint32_t Version = 16; llvm::Expected readRIFF(llvm::StringRef Data) { auto RIFF = riff::readFile(Data); diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp index 20f2eacafb27..b1363c1f9cef 100644 --- a/clang-tools-extra/clangd/index/SymbolCollector.cpp +++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp @@ -223,6 +223,11 @@ bool SymbolCollector::shouldCollectSymbol(const NamedDecl &ND, if (!IsMainFileOnly && ND.isInAnonymousNamespace()) return false; + // For function local symbols, index only classes and its member functions. + if (index::isFunctionLocalSymbol(&ND)) +return isa(ND) || + (ND.isCXXInstanceMember() && ND.isFunctionOrFunctionTemplate()); + // We want most things but not "local" symbols such as symbols inside // FunctionDecl, BlockDecl, ObjCMethodDecl and OMPDeclareReductionDecl. // FIXME: Need a matcher for ExportDecl in order to include symbols declared diff --git a/clang-tools-extra/clangd/index/Symbo
[llvm-branch-commits] [clang-tools-extra] 17846ed - [clangd] Use ASTSignals in Heuristics CC Ranking.
Author: Utkarsh Saxena Date: 2021-01-19T19:48:42+01:00 New Revision: 17846ed5af4a83334ef7d07f0b4a9d525e6ec0db URL: https://github.com/llvm/llvm-project/commit/17846ed5af4a83334ef7d07f0b4a9d525e6ec0db DIFF: https://github.com/llvm/llvm-project/commit/17846ed5af4a83334ef7d07f0b4a9d525e6ec0db.diff LOG: [clangd] Use ASTSignals in Heuristics CC Ranking. Differential Revision: https://reviews.llvm.org/D94927 Added: Modified: clang-tools-extra/clangd/Quality.cpp Removed: diff --git a/clang-tools-extra/clangd/Quality.cpp b/clang-tools-extra/clangd/Quality.cpp index 1c41b7c7661f..b49392bc7d04 100644 --- a/clang-tools-extra/clangd/Quality.cpp +++ b/clang-tools-extra/clangd/Quality.cpp @@ -474,6 +474,21 @@ float SymbolRelevanceSignals::evaluateHeuristics() const { if (NeedsFixIts) Score *= 0.5f; + // Use a sigmoid style boosting function similar to `References`, which flats + // out nicely for large values. This avoids a sharp gradient for heavily + // referenced symbols. Use smaller gradient for ScopeRefsInFile since ideally + // MainFileRefs <= ScopeRefsInFile. + if (MainFileRefs >= 2) { +// E.g.: (2, 1.12), (9, 2.0), (48, 3.0). +float S = std::pow(MainFileRefs, -0.11); +Score *= 11.0 * (1 - S) / (1 + S) + 0.7; + } + if (ScopeRefsInFile >= 2) { +// E.g.: (2, 1.04), (14, 2.0), (109, 3.0), (400, 3.6). +float S = std::pow(ScopeRefsInFile, -0.10); +Score *= 10.0 * (1 - S) / (1 + S) + 0.7; + } + return Score; } ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang-tools-extra] 85c1c6a - [clangd] Add Random Forest runtime for code completion.
Author: Utkarsh Saxena Date: 2020-09-18T18:27:42+02:00 New Revision: 85c1c6a4ba4eebbd3f5cefb1512498b9f8a5bb7a URL: https://github.com/llvm/llvm-project/commit/85c1c6a4ba4eebbd3f5cefb1512498b9f8a5bb7a DIFF: https://github.com/llvm/llvm-project/commit/85c1c6a4ba4eebbd3f5cefb1512498b9f8a5bb7a.diff LOG: [clangd] Add Random Forest runtime for code completion. Summary: [WIP] - Proposes a json format for representing Random Forest model. - Proposes a way to test the generated runtime using a test model. TODO: - Add generated source code snippet for easier review. - Fix unused label warning. - Figure out required using declarations for CATEGORICAL columns from Features.json. - Necessary Google3 internal modifications for blaze before landing. - Add documentation for format of the model. - Document more. Subscribers: mgorny, ilya-biryukov, MaskRay, jkorous, arphaman, kadircet, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D83814 Added: clang-tools-extra/clangd/quality/CompletionModel.cmake clang-tools-extra/clangd/quality/CompletionModelCodegen.py clang-tools-extra/clangd/quality/README.md clang-tools-extra/clangd/quality/model/features.json clang-tools-extra/clangd/quality/model/forest.json clang-tools-extra/clangd/unittests/DecisionForestTests.cpp clang-tools-extra/clangd/unittests/decision_forest_model/CategoricalFeature.h clang-tools-extra/clangd/unittests/decision_forest_model/features.json clang-tools-extra/clangd/unittests/decision_forest_model/forest.json Modified: clang-tools-extra/clangd/CMakeLists.txt clang-tools-extra/clangd/unittests/CMakeLists.txt clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp Removed: diff --git a/clang-tools-extra/clangd/CMakeLists.txt b/clang-tools-extra/clangd/CMakeLists.txt index 3a1a034ed17b..9d2ab5be222a 100644 --- a/clang-tools-extra/clangd/CMakeLists.txt +++ b/clang-tools-extra/clangd/CMakeLists.txt @@ -28,6 +28,9 @@ set(LLVM_LINK_COMPONENTS FrontendOpenMP Option ) + +include(${CMAKE_CURRENT_SOURCE_DIR}/quality/CompletionModel.cmake) +gen_decision_forest(${CMAKE_CURRENT_SOURCE_DIR}/quality/model CompletionModel clang::clangd::Example) if(MSVC AND NOT CLANG_CL) set_source_files_properties(CompileCommands.cpp PROPERTIES COMPILE_FLAGS -wd4130) # disables C4130: logical operation on address of string constant @@ -77,6 +80,7 @@ add_clang_library(clangDaemon TUScheduler.cpp URI.cpp XRefs.cpp + ${CMAKE_CURRENT_BINARY_DIR}/CompletionModel.cpp index/Background.cpp index/BackgroundIndexLoader.cpp @@ -117,6 +121,11 @@ add_clang_library(clangDaemon omp_gen ) +# Include generated CompletionModel headers. +target_include_directories(clangDaemon PUBLIC + $ +) + clang_target_link_libraries(clangDaemon PRIVATE clangAST diff --git a/clang-tools-extra/clangd/quality/CompletionModel.cmake b/clang-tools-extra/clangd/quality/CompletionModel.cmake new file mode 100644 index ..60c6d2aa8433 --- /dev/null +++ b/clang-tools-extra/clangd/quality/CompletionModel.cmake @@ -0,0 +1,37 @@ +# Run the Completion Model Codegenerator on the model present in the +# ${model} directory. +# Produces a pair of files called ${filename}.h and ${filename}.cpp in the +# ${CMAKE_CURRENT_BINARY_DIR}. The generated header +# will define a C++ class called ${cpp_class} - which may be a +# namespace-qualified class name. +function(gen_decision_forest model filename cpp_class) + set(model_compiler ${CMAKE_SOURCE_DIR}/../clang-tools-extra/clangd/quality/CompletionModelCodegen.py) + + set(output_dir ${CMAKE_CURRENT_BINARY_DIR}) + set(header_file ${output_dir}/${filename}.h) + set(cpp_file ${output_dir}/${filename}.cpp) + + add_custom_command(OUTPUT ${header_file} ${cpp_file} +COMMAND "${Python3_EXECUTABLE}" ${model_compiler} + --model ${model} + --output_dir ${output_dir} + --filename ${filename} + --cpp_class ${cpp_class} +COMMENT "Generating code completion model runtime..." +DEPENDS ${model_compiler} ${model}/forest.json ${model}/features.json +VERBATIM ) + + set_source_files_properties(${header_file} PROPERTIES +GENERATED 1) + set_source_files_properties(${cpp_file} PROPERTIES +GENERATED 1) + + # Disable unused label warning for generated files. + if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") +set_source_files_properties(${cpp_file} PROPERTIES + COMPILE_FLAGS /wd4102) + else() +set_source_files_properties(${cpp_file} PROPERTIES + COMPILE_FLAGS -Wno-unused) + endif() +endfunction() diff --git a/clang-tools-extra/clangd/quality/CompletionModelCodegen.py b/clang-tools-extra/clangd/quality/CompletionModelCodegen.py new file mode 100644 index ..8f8234f6ebbc --- /dev/null +++ b/clang-tools-extra/clangd/quality/CompletionModelCodegen.py @@ -0,0 +1,283 @@ +"""Code generator for Code Comp
[llvm-branch-commits] [clang-tools-extra] b31486a - [clangd] textDocument/implementation (LSP layer)
Author: Utkarsh Saxena Date: 2020-11-23T13:50:44+01:00 New Revision: b31486ad971774c859e3e031fc0d8d9b77e3b083 URL: https://github.com/llvm/llvm-project/commit/b31486ad971774c859e3e031fc0d8d9b77e3b083 DIFF: https://github.com/llvm/llvm-project/commit/b31486ad971774c859e3e031fc0d8d9b77e3b083.diff LOG: [clangd] textDocument/implementation (LSP layer) Differential Revision: https://reviews.llvm.org/D91721 Added: clang-tools-extra/clangd/test/implementations.test Modified: clang-tools-extra/clangd/ClangdLSPServer.cpp clang-tools-extra/clangd/ClangdLSPServer.h clang-tools-extra/clangd/ClangdServer.cpp clang-tools-extra/clangd/ClangdServer.h clang-tools-extra/clangd/test/initialize-params.test Removed: diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp index e726271fe7cbe..335a6fc9ad94e 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -604,6 +604,7 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params, }}, {"declarationProvider", true}, {"definitionProvider", true}, +{"implementationProvider", true}, {"documentHighlightProvider", true}, {"documentLinkProvider", llvm::json::Object{ @@ -1291,6 +1292,22 @@ void ClangdLSPServer::onReference(const ReferenceParams &Params, }); } +void ClangdLSPServer::onGoToImplementation( +const TextDocumentPositionParams &Params, +Callback> Reply) { + Server->findImplementations( + Params.textDocument.uri.file(), Params.position, + [Reply = std::move(Reply)]( + llvm::Expected> Overrides) mutable { +if (!Overrides) + return Reply(Overrides.takeError()); +std::vector Impls; +for (const LocatedSymbol &Sym : *Overrides) + Impls.push_back(Sym.PreferredDeclaration); +return Reply(std::move(Impls)); + }); +} + void ClangdLSPServer::onSymbolInfo(const TextDocumentPositionParams &Params, Callback> Reply) { Server->symbolInfo(Params.textDocument.uri.file(), Params.position, @@ -1431,6 +1448,7 @@ ClangdLSPServer::ClangdLSPServer(class Transport &Transp, MsgHandler->bind("textDocument/signatureHelp", &ClangdLSPServer::onSignatureHelp); MsgHandler->bind("textDocument/definition", &ClangdLSPServer::onGoToDefinition); MsgHandler->bind("textDocument/declaration", &ClangdLSPServer::onGoToDeclaration); + MsgHandler->bind("textDocument/implementation", &ClangdLSPServer::onGoToImplementation); MsgHandler->bind("textDocument/references", &ClangdLSPServer::onReference); MsgHandler->bind("textDocument/switchSourceHeader", &ClangdLSPServer::onSwitchSourceHeader); MsgHandler->bind("textDocument/prepareRename", &ClangdLSPServer::onPrepareRename); diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h index b9200f6a2e1b8..4d568bc13d8bf 100644 --- a/clang-tools-extra/clangd/ClangdLSPServer.h +++ b/clang-tools-extra/clangd/ClangdLSPServer.h @@ -115,6 +115,8 @@ class ClangdLSPServer : private ClangdServer::Callbacks { Callback>); void onGoToDefinition(const TextDocumentPositionParams &, Callback>); + void onGoToImplementation(const TextDocumentPositionParams &, +Callback>); void onReference(const ReferenceParams &, Callback>); void onSwitchSourceHeader(const TextDocumentIdentifier &, Callback>); diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp index b6f9fcfd23da8..889d2cbcf2807 100644 --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -718,6 +718,18 @@ void ClangdServer::foldingRanges(llvm::StringRef File, TUScheduler::InvalidateOnUpdate); } +void ClangdServer::findImplementations( +PathRef File, Position Pos, Callback> CB) { + auto Action = [Pos, CB = std::move(CB), + this](llvm::Expected InpAST) mutable { +if (!InpAST) + return CB(InpAST.takeError()); +CB(clangd::findImplementations(InpAST->AST, Pos, Index)); + }; + + WorkScheduler.runWithAST("Implementations", File, std::move(Action)); +} + void ClangdServer::findReferences(PathRef File, Position Pos, uint32_t Limit, Callback CB) { auto Action = [Pos, Limit, CB = std::move(CB), diff --git a/clang-tools-extra/clangd/ClangdServer.h b/clang-tools-extra/clangd/ClangdServer.h index 0056f5072cca3..1ccb4c5899f81 100644 --- a/clang-tools-extra/clangd/ClangdServer.h +++ b/clang-tools-extra/clangd/ClangdServer.h @@ -253,6 +253,10 @@ class ClangdServer { /// Retrieve ranges that
[llvm-branch-commits] [clang-tools-extra] 4ce242a - [clangd] Find relations in Dex exploration tool.
Author: Utkarsh Saxena Date: 2020-12-10T16:54:03+01:00 New Revision: 4ce242a163c3b98385a5cb949a7e6b1e1ae7eb83 URL: https://github.com/llvm/llvm-project/commit/4ce242a163c3b98385a5cb949a7e6b1e1ae7eb83 DIFF: https://github.com/llvm/llvm-project/commit/4ce242a163c3b98385a5cb949a7e6b1e1ae7eb83.diff LOG: [clangd] Find relations in Dex exploration tool. Reviewed By: hokein Differential Revision: https://reviews.llvm.org/D93029 Added: Modified: clang-tools-extra/clangd/index/dex/dexp/Dexp.cpp Removed: diff --git a/clang-tools-extra/clangd/index/dex/dexp/Dexp.cpp b/clang-tools-extra/clangd/index/dex/dexp/Dexp.cpp index d7da330e2ed0..49f16e72be92 100644 --- a/clang-tools-extra/clangd/index/dex/dexp/Dexp.cpp +++ b/clang-tools-extra/clangd/index/dex/dexp/Dexp.cpp @@ -11,6 +11,8 @@ // //===--===// +#include "index/Index.h" +#include "index/Relation.h" #include "index/Serialization.h" #include "index/dex/Dex.h" #include "index/remote/Client.h" @@ -267,6 +269,43 @@ class Refs : public Command { } }; +class Relations : public Command { + llvm::cl::opt ID{ + "id", + llvm::cl::Positional, + llvm::cl::desc("Symbol ID of the symbol being queried (hex)."), + }; + llvm::cl::opt Relation{ + "relation", + llvm::cl::desc("Relation kind for the predicate."), + values(clEnumValN(RelationKind::BaseOf, "base_of", +"Find subclasses of a class."), + clEnumValN(RelationKind::OverriddenBy, "overridden_by", +"Find methods that overrides a virtual method.")), + }; + + void run() override { +if (ID.getNumOccurrences() == 0 || Relation.getNumOccurrences() == 0) { + llvm::errs() + << "Missing required argument: please provide id and -relation.\n"; + return; +} +RelationsRequest Req; +if (ID.getNumOccurrences()) { + auto SID = SymbolID::fromStr(ID); + if (!SID) { +llvm::errs() << llvm::toString(SID.takeError()) << "\n"; +return; + } + Req.Subjects.insert(*SID); +} +Req.Predicate = Relation.getValue(); +Index->relations(Req, [](const SymbolID &SID, const Symbol &S) { + llvm::outs() << toYAML(S); +}); + } +}; + class Export : public Command { llvm::cl::opt Format{ "format", @@ -326,6 +365,8 @@ struct { {"lookup", "Dump symbol details by ID or qualified name", std::make_unique}, {"refs", "Find references by ID or qualified name", std::make_unique}, +{"relations", "Find relations by ID and relation kind", + std::make_unique}, {"export", "Export index", std::make_unique}, }; ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [Serialization] Code cleanups and polish 83233 (PR #83237)
usx95 wrote: > I tried to take a look at eigen and it looks like the declaration looks well > and I had no clue how that happens. A reproducer may be necessary here to > proceed. Thanks in advance. I can reproduce using the following sources and invocations outlined in `run.sh` https://github.com/usx95/llvm-project/commit/363d877bd317638b197f57c3591860e1688950d5 ```sh > module-reproducer/run.sh Building sensor_data.cc Building tensor.cc Building base.cc In module 'sensor_data': ../../eigen/Eigen/src/Core/../plugins/CommonCwiseBinaryOps.inc:47:29: warning: inline function 'Eigen::operator*' is not defined [-Wundefined-inline] 47 | EIGEN_MAKE_SCALAR_BINARY_OP(operator*, product) | ^ ../../eigen/Eigen/src/Geometry/AngleAxis.h:221:35: note: used here 221 | Vector3 sin_axis = sin(m_angle) * m_axis; | ^ 1 warning generated. ``` This warning is a new breakage and does not happed without this change (ignore the linker failure). https://github.com/llvm/llvm-project/pull/83237 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] release/20.x: [clang] Fix false positive regression for lifetime analysis warning. (#127460) (PR #127618)
https://github.com/usx95 approved this pull request. https://github.com/llvm/llvm-project/pull/127618 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/148712 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
usx95 wrote: > Looks good, but what's the plan for tests? Good question :) I think adding tests for each analyses to the current [llvm-lit tests](https://github.com/llvm/llvm-project/blob/main/clang/test/Sema/warn-lifetime-safety-dataflow.cpp) is quite a pain for the eyes at this point. Also I want to test things at specific program points (is loan expired here, does origin contain loan here, etc). I am exploring a couple of options, primarily https://github.com/llvm/llvm-project/blob/main/clang/include/clang/Testing/TestAST.h, https://github.com/llvm/llvm-project/blob/main/clang/unittests/Analysis/FlowSensitive/TestingSupport.h and something like https://github.com/llvm/llvm-project/blob/main/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp#L874-L900 This will likely take some more time. Happy to hold this off until I add something like this for unit tests! I would still rely on llvm-lit tests for final diagnostic reporting (or maybe that also can be done through unit testing) https://github.com/llvm/llvm-project/pull/148712 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add per-program-point state tracking and query methodscg (PR #149069)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149069 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add lattice tracking per program point (PR #149069)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149069 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add lattice tracking per program point (PR #149069)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149069 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
usx95 wrote: >Once the testing story is figured out this looks good to me. Alright. I will add some unit test infra separately before this PR then! https://github.com/llvm/llvm-project/pull/148712 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Implement LiveOrigins analysis (PR #148976)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148976 >From 9834dbd9e095cad2bcafbeef763ff21bf223b26e Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Tue, 15 Jul 2025 22:19:48 + Subject: [PATCH] add-liveness-finally --- clang/lib/Analysis/LifetimeSafety.cpp | 78 +++ 1 file changed, 78 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 3a492b2e6707d..e12f4aaf1c4b0 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -675,12 +675,14 @@ join(llvm::ImmutableMap A, llvm::ImmutableMap B, // TODO(opt): Consider using a bitset to represent the set of loans. using LoanSet = llvm::ImmutableSet; using OriginLoanMap = llvm::ImmutableMap; +using OriginSet = llvm::ImmutableSet; /// An object to hold the factories for immutable collections, ensuring /// that all created states share the same underlying memory management. struct LifetimeFactory { OriginLoanMap::Factory OriginMapFactory; LoanSet::Factory LoanSetFactory; + OriginSet::Factory OriginSetFactory; /// Creates a singleton set containing only the given loan ID. LoanSet createLoanSet(LoanID LID) { @@ -777,6 +779,78 @@ class LoanPropagationAnalysis } }; +// = // +// Live Origins Analysis +// = // + +/// The dataflow lattice for origin liveness analysis. +/// It tracks the set of origins that are live at a given program point. +struct LivenessLattice { + OriginSet LiveOrigins; + + LivenessLattice() : LiveOrigins(nullptr) {}; + explicit LivenessLattice(OriginSet S) : LiveOrigins(S) {} + + bool operator==(const LivenessLattice &Other) const { +return LiveOrigins == Other.LiveOrigins; + } + bool operator!=(const LivenessLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "LivenessLattice State:\n"; +if (LiveOrigins.isEmpty()) + OS << " \n"; +for (const OriginID &OID : LiveOrigins) + OS << " Origin " << OID << " is live\n"; + } +}; + +/// The analysis that tracks which origins are live. This is a backward +/// analysis. +class LiveOriginAnalysis +: public DataflowAnalysis { + + OriginSet::Factory &SetFactory; + +public: + LiveOriginAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + OriginSet::Factory &SF) + : DataflowAnalysis(C, AC, F), SetFactory(SF) {} + + using DataflowAnalysis::transfer; + + StringRef getAnalysisName() const { return "LiveOrigins"; } + + Lattice getInitialState() { return Lattice(SetFactory.getEmptySet()); } + + /// Merges two lattices by taking the union of the live origin sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.LiveOrigins, L2.LiveOrigins, SetFactory)); + } + + /// An assignment `p = q` kills the liveness of `p` and generates liveness + /// for `q`. + Lattice transfer(Lattice In, const AssignOriginFact &F) { +OriginSet S = SetFactory.remove(In.LiveOrigins, F.getDestOriginID()); +S = SetFactory.add(S, F.getSrcOriginID()); +return Lattice(S); + } + + /// Issuing a new loan to an origin kills its liveness. + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(SetFactory.remove(In.LiveOrigins, F.getOriginID())); + } + + /// A return statement generates liveness for the returned origin. + Lattice transfer(Lattice In, const ReturnOfOriginFact &F) { +return Lattice(SetFactory.add(In.LiveOrigins, F.getReturnedOriginID())); + } +}; + // = // // Expired Loans Analysis // = // @@ -872,5 +946,9 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, ExpiredLoansAnalysis ExpiredLoans(Cfg, AC, FactMgr, Factory); ExpiredLoans.run(); DEBUG_WITH_TYPE("LifetimeExpiredLoans", ExpiredLoans.dump()); + + LiveOriginAnalysis Liveness(Cfg, AC, FactMgr, Factory.OriginSetFactory); + Liveness.run(); + DEBUG_WITH_TYPE("LifetimeLiveOrigins", Liveness.dump()); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Implement LiveOrigins analysis (PR #148976)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148976 >From b51604daf9c2f17e2e0d2e73784e22bf573a986a Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Tue, 15 Jul 2025 22:19:48 + Subject: [PATCH] add-liveness-finally --- clang/lib/Analysis/LifetimeSafety.cpp | 78 +++ 1 file changed, 78 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 3a492b2e6707d..e12f4aaf1c4b0 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -675,12 +675,14 @@ join(llvm::ImmutableMap A, llvm::ImmutableMap B, // TODO(opt): Consider using a bitset to represent the set of loans. using LoanSet = llvm::ImmutableSet; using OriginLoanMap = llvm::ImmutableMap; +using OriginSet = llvm::ImmutableSet; /// An object to hold the factories for immutable collections, ensuring /// that all created states share the same underlying memory management. struct LifetimeFactory { OriginLoanMap::Factory OriginMapFactory; LoanSet::Factory LoanSetFactory; + OriginSet::Factory OriginSetFactory; /// Creates a singleton set containing only the given loan ID. LoanSet createLoanSet(LoanID LID) { @@ -777,6 +779,78 @@ class LoanPropagationAnalysis } }; +// = // +// Live Origins Analysis +// = // + +/// The dataflow lattice for origin liveness analysis. +/// It tracks the set of origins that are live at a given program point. +struct LivenessLattice { + OriginSet LiveOrigins; + + LivenessLattice() : LiveOrigins(nullptr) {}; + explicit LivenessLattice(OriginSet S) : LiveOrigins(S) {} + + bool operator==(const LivenessLattice &Other) const { +return LiveOrigins == Other.LiveOrigins; + } + bool operator!=(const LivenessLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "LivenessLattice State:\n"; +if (LiveOrigins.isEmpty()) + OS << " \n"; +for (const OriginID &OID : LiveOrigins) + OS << " Origin " << OID << " is live\n"; + } +}; + +/// The analysis that tracks which origins are live. This is a backward +/// analysis. +class LiveOriginAnalysis +: public DataflowAnalysis { + + OriginSet::Factory &SetFactory; + +public: + LiveOriginAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + OriginSet::Factory &SF) + : DataflowAnalysis(C, AC, F), SetFactory(SF) {} + + using DataflowAnalysis::transfer; + + StringRef getAnalysisName() const { return "LiveOrigins"; } + + Lattice getInitialState() { return Lattice(SetFactory.getEmptySet()); } + + /// Merges two lattices by taking the union of the live origin sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.LiveOrigins, L2.LiveOrigins, SetFactory)); + } + + /// An assignment `p = q` kills the liveness of `p` and generates liveness + /// for `q`. + Lattice transfer(Lattice In, const AssignOriginFact &F) { +OriginSet S = SetFactory.remove(In.LiveOrigins, F.getDestOriginID()); +S = SetFactory.add(S, F.getSrcOriginID()); +return Lattice(S); + } + + /// Issuing a new loan to an origin kills its liveness. + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(SetFactory.remove(In.LiveOrigins, F.getOriginID())); + } + + /// A return statement generates liveness for the returned origin. + Lattice transfer(Lattice In, const ReturnOfOriginFact &F) { +return Lattice(SetFactory.add(In.LiveOrigins, F.getReturnedOriginID())); + } +}; + // = // // Expired Loans Analysis // = // @@ -872,5 +946,9 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, ExpiredLoansAnalysis ExpiredLoans(Cfg, AC, FactMgr, Factory); ExpiredLoans.run(); DEBUG_WITH_TYPE("LifetimeExpiredLoans", ExpiredLoans.dump()); + + LiveOriginAnalysis Liveness(Cfg, AC, FactMgr, Factory.OriginSetFactory); + Liveness.run(); + DEBUG_WITH_TYPE("LifetimeLiveOrigins", Liveness.dump()); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148712 >From 8dbb9e00758784a4be5e7c3d182ff246291eaef3 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:32:35 + Subject: [PATCH 1/2] users/usx95/lifetime-safety-add-loan-expiry >From a4cba2006dc168e59c14e870a993c37c0fff9f61 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:37:49 + Subject: [PATCH 2/2] [LifetimeSafety] Add loan expiry analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 63 +++ 1 file changed, 63 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index e3a03cf93880e..3a492b2e6707d 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -777,6 +777,65 @@ class LoanPropagationAnalysis } }; +// = // +// Expired Loans Analysis +// = // + +/// The dataflow lattice for tracking the set of expired loans. +struct ExpiredLattice { + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { +return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "ExpiredLattice State:\n"; +if (Expired.isEmpty()) + OS << " \n"; +for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +/// The analysis that tracks which loans have expired. +class ExpiredLoansAnalysis +: public DataflowAnalysis { + + LoanSet::Factory &Factory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LifetimeFactory &Factory) + : DataflowAnalysis(C, AC, F), Factory(Factory.LoanSetFactory) {} + + using Base::transfer; + + StringRef getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(Factory.getEmptySet()); } + + /// Merges two lattices by taking the union of the expired loan sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.Expired, L2.Expired, Factory)); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { +return Lattice(Factory.add(In.Expired, F.getLoanID())); + } + + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(Factory.remove(In.Expired, F.getLoanID())); + } +}; + // = // // TODO: // - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` @@ -809,5 +868,9 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, Factory); LoanPropagation.run(); DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + + ExpiredLoansAnalysis ExpiredLoans(Cfg, AC, FactMgr, Factory); + ExpiredLoans.run(); + DEBUG_WITH_TYPE("LifetimeExpiredLoans", ExpiredLoans.dump()); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add lattice tracking per program point (PR #149069)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/149069 >From 266cf01d3afeac7d9d675e4c605f212c758689c5 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Wed, 16 Jul 2025 10:44:37 + Subject: [PATCH] add-point-level-granularity-to-analyses --- clang/lib/Analysis/LifetimeSafety.cpp | 43 --- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index e12f4aaf1c4b0..a740149a2734c 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -500,6 +500,9 @@ class FactGenerator : public ConstStmtVisitor { // Generic Dataflow Analysis // = // +// DO NOT SUBMIT: TODO: Document notion of before or after in the analyses. +using ProgramPoint = std::pair; + enum class Direction { Forward, Backward }; /// A generic, policy-based driver for dataflow analyses. It combines @@ -532,6 +535,7 @@ class DataflowAnalysis { llvm::DenseMap InStates; llvm::DenseMap OutStates; + llvm::DenseMap PerPointStates; static constexpr bool isForward() { return Dir == Direction::Forward; } @@ -577,6 +581,8 @@ class DataflowAnalysis { } } + Lattice getState(ProgramPoint P) const { return PerPointStates.lookup(P); } + Lattice getInState(const CFGBlock *B) const { return InStates.lookup(B); } Lattice getOutState(const CFGBlock *B) const { return OutStates.lookup(B); } @@ -590,18 +596,22 @@ class DataflowAnalysis { getOutState(&B).dump(llvm::dbgs()); } +private: /// Computes the state at one end of a block by applying all its facts /// sequentially to a given state from the other end. - /// TODO: We might need to store intermediate states per-fact in the block for - /// later analysis. Lattice transferBlock(const CFGBlock *Block, Lattice State) { auto Facts = AllFacts.getFacts(Block); -if constexpr (isForward()) - for (const Fact *F : Facts) +if constexpr (isForward()) { + for (const Fact *F : Facts) { State = transferFact(State, F); -else - for (const Fact *F : llvm::reverse(Facts)) +PerPointStates[{Block, F}] = State; + } +} else { + for (const Fact *F : llvm::reverse(Facts)) { State = transferFact(State, F); +PerPointStates[{Block, F}] = State; + } +} return State; } @@ -771,6 +781,10 @@ class LoanPropagationAnalysis Factory.OriginMapFactory.add(In.Origins, DestOID, SrcLoans)); } + LoanSet getLoans(OriginID OID, ProgramPoint P) { +return getLoans(getState(P), OID); + } + private: LoanSet getLoans(Lattice L, OriginID OID) { if (auto *Loans = L.Origins.lookup(OID)) @@ -849,6 +863,14 @@ class LiveOriginAnalysis Lattice transfer(Lattice In, const ReturnOfOriginFact &F) { return Lattice(SetFactory.add(In.LiveOrigins, F.getReturnedOriginID())); } + + bool isLive(OriginID OID, ProgramPoint P) const { +return getState(P).LiveOrigins.contains(OID); + } + + OriginSet getLiveOrigins(ProgramPoint P) const { +return getState(P).LiveOrigins; + } }; // = // @@ -908,14 +930,15 @@ class ExpiredLoansAnalysis Lattice transfer(Lattice In, const IssueFact &F) { return Lattice(Factory.remove(In.Expired, F.getLoanID())); } + + bool isExpired(LoanID LID, ProgramPoint P) const { +return getState(P).Expired.contains(LID); + } }; // = // // TODO: -// - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` -// - Modify loan expiry analysis to answer `bool isExpired(Loan L, Point P)` -// - Modify origin liveness analysis to answer `bool isLive(Origin O, Point P)` -// - Using the above three to perform the final error reporting. +// - Add error reporting Add how would it work. // = // } // anonymous namespace ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148712 >From 8dbb9e00758784a4be5e7c3d182ff246291eaef3 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:32:35 + Subject: [PATCH 1/2] users/usx95/lifetime-safety-add-loan-expiry >From a4cba2006dc168e59c14e870a993c37c0fff9f61 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:37:49 + Subject: [PATCH 2/2] [LifetimeSafety] Add loan expiry analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 63 +++ 1 file changed, 63 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index e3a03cf93880e..3a492b2e6707d 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -777,6 +777,65 @@ class LoanPropagationAnalysis } }; +// = // +// Expired Loans Analysis +// = // + +/// The dataflow lattice for tracking the set of expired loans. +struct ExpiredLattice { + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { +return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "ExpiredLattice State:\n"; +if (Expired.isEmpty()) + OS << " \n"; +for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +/// The analysis that tracks which loans have expired. +class ExpiredLoansAnalysis +: public DataflowAnalysis { + + LoanSet::Factory &Factory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LifetimeFactory &Factory) + : DataflowAnalysis(C, AC, F), Factory(Factory.LoanSetFactory) {} + + using Base::transfer; + + StringRef getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(Factory.getEmptySet()); } + + /// Merges two lattices by taking the union of the expired loan sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.Expired, L2.Expired, Factory)); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { +return Lattice(Factory.add(In.Expired, F.getLoanID())); + } + + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(Factory.remove(In.Expired, F.getLoanID())); + } +}; + // = // // TODO: // - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` @@ -809,5 +868,9 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, Factory); LoanPropagation.run(); DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + + ExpiredLoansAnalysis ExpiredLoans(Cfg, AC, FactMgr, Factory); + ExpiredLoans.run(); + DEBUG_WITH_TYPE("LifetimeExpiredLoans", ExpiredLoans.dump()); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Implement LiveOrigins analysis (PR #148976)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148976 >From b51604daf9c2f17e2e0d2e73784e22bf573a986a Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Tue, 15 Jul 2025 22:19:48 + Subject: [PATCH] add-liveness-finally --- clang/lib/Analysis/LifetimeSafety.cpp | 78 +++ 1 file changed, 78 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 3a492b2e6707d..e12f4aaf1c4b0 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -675,12 +675,14 @@ join(llvm::ImmutableMap A, llvm::ImmutableMap B, // TODO(opt): Consider using a bitset to represent the set of loans. using LoanSet = llvm::ImmutableSet; using OriginLoanMap = llvm::ImmutableMap; +using OriginSet = llvm::ImmutableSet; /// An object to hold the factories for immutable collections, ensuring /// that all created states share the same underlying memory management. struct LifetimeFactory { OriginLoanMap::Factory OriginMapFactory; LoanSet::Factory LoanSetFactory; + OriginSet::Factory OriginSetFactory; /// Creates a singleton set containing only the given loan ID. LoanSet createLoanSet(LoanID LID) { @@ -777,6 +779,78 @@ class LoanPropagationAnalysis } }; +// = // +// Live Origins Analysis +// = // + +/// The dataflow lattice for origin liveness analysis. +/// It tracks the set of origins that are live at a given program point. +struct LivenessLattice { + OriginSet LiveOrigins; + + LivenessLattice() : LiveOrigins(nullptr) {}; + explicit LivenessLattice(OriginSet S) : LiveOrigins(S) {} + + bool operator==(const LivenessLattice &Other) const { +return LiveOrigins == Other.LiveOrigins; + } + bool operator!=(const LivenessLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "LivenessLattice State:\n"; +if (LiveOrigins.isEmpty()) + OS << " \n"; +for (const OriginID &OID : LiveOrigins) + OS << " Origin " << OID << " is live\n"; + } +}; + +/// The analysis that tracks which origins are live. This is a backward +/// analysis. +class LiveOriginAnalysis +: public DataflowAnalysis { + + OriginSet::Factory &SetFactory; + +public: + LiveOriginAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + OriginSet::Factory &SF) + : DataflowAnalysis(C, AC, F), SetFactory(SF) {} + + using DataflowAnalysis::transfer; + + StringRef getAnalysisName() const { return "LiveOrigins"; } + + Lattice getInitialState() { return Lattice(SetFactory.getEmptySet()); } + + /// Merges two lattices by taking the union of the live origin sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.LiveOrigins, L2.LiveOrigins, SetFactory)); + } + + /// An assignment `p = q` kills the liveness of `p` and generates liveness + /// for `q`. + Lattice transfer(Lattice In, const AssignOriginFact &F) { +OriginSet S = SetFactory.remove(In.LiveOrigins, F.getDestOriginID()); +S = SetFactory.add(S, F.getSrcOriginID()); +return Lattice(S); + } + + /// Issuing a new loan to an origin kills its liveness. + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(SetFactory.remove(In.LiveOrigins, F.getOriginID())); + } + + /// A return statement generates liveness for the returned origin. + Lattice transfer(Lattice In, const ReturnOfOriginFact &F) { +return Lattice(SetFactory.add(In.LiveOrigins, F.getReturnedOriginID())); + } +}; + // = // // Expired Loans Analysis // = // @@ -872,5 +946,9 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, ExpiredLoansAnalysis ExpiredLoans(Cfg, AC, FactMgr, Factory); ExpiredLoans.run(); DEBUG_WITH_TYPE("LifetimeExpiredLoans", ExpiredLoans.dump()); + + LiveOriginAnalysis Liveness(Cfg, AC, FactMgr, Factory.OriginSetFactory); + Liveness.run(); + DEBUG_WITH_TYPE("LifetimeLiveOrigins", Liveness.dump()); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Support bidirectional dataflow analysis (PR #148967)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148967 >From 7762a38fb70176b2b4083cc37334713fb4f70f2c Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Tue, 15 Jul 2025 21:24:11 + Subject: [PATCH] add-backward-analysis-capability --- clang/lib/Analysis/LifetimeSafety.cpp | 108 ++ 1 file changed, 59 insertions(+), 49 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 9c623e3a5693b..e3a03cf93880e 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -499,13 +499,16 @@ class FactGenerator : public ConstStmtVisitor { // = // // Generic Dataflow Analysis // = // -/// A generic, policy-based driver for forward dataflow analyses. It combines + +enum class Direction { Forward, Backward }; + +/// A generic, policy-based driver for dataflow analyses. It combines /// the dataflow runner and the transferer logic into a single class hierarchy. /// /// The derived class is expected to provide: /// - A `Lattice` type. /// - `StringRef getAnalysisName() const` -/// - `Lattice getInitialState();` The initial state at the function entry. +/// - `Lattice getInitialState();` The initial state of the analysis. /// - `Lattice join(Lattice, Lattice);` Merges states from multiple CFG paths. /// - `Lattice transfer(Lattice, const FactType&);` Defines how a single /// lifetime-relevant `Fact` transforms the lattice state. Only overloads @@ -514,18 +517,23 @@ class FactGenerator : public ConstStmtVisitor { /// \tparam Derived The CRTP derived class that implements the specific /// analysis. /// \tparam LatticeType The dataflow lattice used by the analysis. +/// \tparam Dir The direction of the analysis (Forward or Backward). /// TODO: Maybe use the dataflow framework! The framework might need changes /// to support the current comparison done at block-entry. -template class DataflowAnalysis { +template +class DataflowAnalysis { public: using Lattice = LatticeType; + using Base = DataflowAnalysis; private: const CFG &Cfg; AnalysisDeclContext &AC; - llvm::DenseMap BlockEntryStates; - llvm::DenseMap BlockExitStates; + llvm::DenseMap InStates; + llvm::DenseMap OutStates; + + static constexpr bool isForward() { return Dir == Direction::Forward; } protected: FactManager &AllFacts; @@ -539,75 +547,76 @@ template class DataflowAnalysis { Derived &D = static_cast(*this); llvm::TimeTraceScope Time(D.getAnalysisName()); -ForwardDataflowWorklist Worklist(Cfg, AC); -const CFGBlock *Entry = &Cfg.getEntry(); -BlockEntryStates[Entry] = D.getInitialState(); -Worklist.enqueueBlock(Entry); -llvm::SmallBitVector Visited; -Visited.resize(Cfg.getNumBlockIDs() + 1); - -while (const CFGBlock *B = Worklist.dequeue()) { - Lattice EntryState = getEntryState(B); - Lattice ExitState = transferBlock(B, EntryState); - BlockExitStates[B] = ExitState; - Visited.set(B->getBlockID()); +using Worklist = +std::conditional_t; +Worklist W(Cfg, AC); + +const CFGBlock *Start = isForward() ? &Cfg.getEntry() : &Cfg.getExit(); +InStates[Start] = D.getInitialState(); +W.enqueueBlock(Start); - for (const CFGBlock *Successor : B->succs()) { -Lattice OldSuccEntryState = getEntryState(Successor); -Lattice NewSuccEntryState = D.join(OldSuccEntryState, ExitState); +llvm::SmallBitVector Visited(Cfg.getNumBlockIDs() + 1); -// Enqueue the successor if its entry state has changed or if we have +while (const CFGBlock *B = W.dequeue()) { + Lattice StateIn = getInState(B); + Lattice StateOut = transferBlock(B, StateIn); + OutStates[B] = StateOut; + Visited.set(B->getBlockID()); + for (const CFGBlock *AdjacentB : isForward() ? B->succs() : B->preds()) { +Lattice OldInState = getInState(AdjacentB); +Lattice NewInState = D.join(OldInState, StateOut); +// Enqueue the adjacent block if its in-state has changed or if we have // never visited it. -if (!Visited.test(Successor->getBlockID()) || -NewSuccEntryState != OldSuccEntryState) { - BlockEntryStates[Successor] = NewSuccEntryState; - Worklist.enqueueBlock(Successor); +if (!Visited.test(AdjacentB->getBlockID()) || +NewInState != OldInState) { + InStates[AdjacentB] = NewInState; + W.enqueueBlock(AdjacentB); } } } } - Lattice getEntryState(const CFGBlock *B) const { -return BlockEntryStates.lookup(B); - } + Lattice getInState(const CFGBlock *B) const { return InStates.lookup(B); } - Lattice getExitState(const CFGBlock *B) const { -return BlockExitStates.lookup(B); - }
[llvm-branch-commits] [clang] [LifetimeSafety] Add lattice tracking per program point (PR #149069)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/149069 >From 266cf01d3afeac7d9d675e4c605f212c758689c5 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Wed, 16 Jul 2025 10:44:37 + Subject: [PATCH] add-point-level-granularity-to-analyses --- clang/lib/Analysis/LifetimeSafety.cpp | 43 --- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index e12f4aaf1c4b0..a740149a2734c 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -500,6 +500,9 @@ class FactGenerator : public ConstStmtVisitor { // Generic Dataflow Analysis // = // +// DO NOT SUBMIT: TODO: Document notion of before or after in the analyses. +using ProgramPoint = std::pair; + enum class Direction { Forward, Backward }; /// A generic, policy-based driver for dataflow analyses. It combines @@ -532,6 +535,7 @@ class DataflowAnalysis { llvm::DenseMap InStates; llvm::DenseMap OutStates; + llvm::DenseMap PerPointStates; static constexpr bool isForward() { return Dir == Direction::Forward; } @@ -577,6 +581,8 @@ class DataflowAnalysis { } } + Lattice getState(ProgramPoint P) const { return PerPointStates.lookup(P); } + Lattice getInState(const CFGBlock *B) const { return InStates.lookup(B); } Lattice getOutState(const CFGBlock *B) const { return OutStates.lookup(B); } @@ -590,18 +596,22 @@ class DataflowAnalysis { getOutState(&B).dump(llvm::dbgs()); } +private: /// Computes the state at one end of a block by applying all its facts /// sequentially to a given state from the other end. - /// TODO: We might need to store intermediate states per-fact in the block for - /// later analysis. Lattice transferBlock(const CFGBlock *Block, Lattice State) { auto Facts = AllFacts.getFacts(Block); -if constexpr (isForward()) - for (const Fact *F : Facts) +if constexpr (isForward()) { + for (const Fact *F : Facts) { State = transferFact(State, F); -else - for (const Fact *F : llvm::reverse(Facts)) +PerPointStates[{Block, F}] = State; + } +} else { + for (const Fact *F : llvm::reverse(Facts)) { State = transferFact(State, F); +PerPointStates[{Block, F}] = State; + } +} return State; } @@ -771,6 +781,10 @@ class LoanPropagationAnalysis Factory.OriginMapFactory.add(In.Origins, DestOID, SrcLoans)); } + LoanSet getLoans(OriginID OID, ProgramPoint P) { +return getLoans(getState(P), OID); + } + private: LoanSet getLoans(Lattice L, OriginID OID) { if (auto *Loans = L.Origins.lookup(OID)) @@ -849,6 +863,14 @@ class LiveOriginAnalysis Lattice transfer(Lattice In, const ReturnOfOriginFact &F) { return Lattice(SetFactory.add(In.LiveOrigins, F.getReturnedOriginID())); } + + bool isLive(OriginID OID, ProgramPoint P) const { +return getState(P).LiveOrigins.contains(OID); + } + + OriginSet getLiveOrigins(ProgramPoint P) const { +return getState(P).LiveOrigins; + } }; // = // @@ -908,14 +930,15 @@ class ExpiredLoansAnalysis Lattice transfer(Lattice In, const IssueFact &F) { return Lattice(Factory.remove(In.Expired, F.getLoanID())); } + + bool isExpired(LoanID LID, ProgramPoint P) const { +return getState(P).Expired.contains(LID); + } }; // = // // TODO: -// - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` -// - Modify loan expiry analysis to answer `bool isExpired(Loan L, Point P)` -// - Modify origin liveness analysis to answer `bool isLive(Origin O, Point P)` -// - Using the above three to perform the final error reporting. +// - Add error reporting Add how would it work. // = // } // anonymous namespace ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Support bidirectional dataflow analysis (PR #148967)
usx95 wrote: ### Merge activity * **Jul 16, 2:12 PM UTC**: A user started a stack merge that includes this pull request via [Graphite](https://app.graphite.dev/github/pr/llvm/llvm-project/148967). https://github.com/llvm/llvm-project/pull/148967 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add lattice tracking per program point (PR #149069)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/149069 >From 6d741458716167db24a5ef00df1d35f4cd28ab02 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Wed, 16 Jul 2025 10:44:37 + Subject: [PATCH] add-point-level-granularity-to-analyses --- clang/lib/Analysis/LifetimeSafety.cpp | 43 --- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index e12f4aaf1c4b0..a740149a2734c 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -500,6 +500,9 @@ class FactGenerator : public ConstStmtVisitor { // Generic Dataflow Analysis // = // +// DO NOT SUBMIT: TODO: Document notion of before or after in the analyses. +using ProgramPoint = std::pair; + enum class Direction { Forward, Backward }; /// A generic, policy-based driver for dataflow analyses. It combines @@ -532,6 +535,7 @@ class DataflowAnalysis { llvm::DenseMap InStates; llvm::DenseMap OutStates; + llvm::DenseMap PerPointStates; static constexpr bool isForward() { return Dir == Direction::Forward; } @@ -577,6 +581,8 @@ class DataflowAnalysis { } } + Lattice getState(ProgramPoint P) const { return PerPointStates.lookup(P); } + Lattice getInState(const CFGBlock *B) const { return InStates.lookup(B); } Lattice getOutState(const CFGBlock *B) const { return OutStates.lookup(B); } @@ -590,18 +596,22 @@ class DataflowAnalysis { getOutState(&B).dump(llvm::dbgs()); } +private: /// Computes the state at one end of a block by applying all its facts /// sequentially to a given state from the other end. - /// TODO: We might need to store intermediate states per-fact in the block for - /// later analysis. Lattice transferBlock(const CFGBlock *Block, Lattice State) { auto Facts = AllFacts.getFacts(Block); -if constexpr (isForward()) - for (const Fact *F : Facts) +if constexpr (isForward()) { + for (const Fact *F : Facts) { State = transferFact(State, F); -else - for (const Fact *F : llvm::reverse(Facts)) +PerPointStates[{Block, F}] = State; + } +} else { + for (const Fact *F : llvm::reverse(Facts)) { State = transferFact(State, F); +PerPointStates[{Block, F}] = State; + } +} return State; } @@ -771,6 +781,10 @@ class LoanPropagationAnalysis Factory.OriginMapFactory.add(In.Origins, DestOID, SrcLoans)); } + LoanSet getLoans(OriginID OID, ProgramPoint P) { +return getLoans(getState(P), OID); + } + private: LoanSet getLoans(Lattice L, OriginID OID) { if (auto *Loans = L.Origins.lookup(OID)) @@ -849,6 +863,14 @@ class LiveOriginAnalysis Lattice transfer(Lattice In, const ReturnOfOriginFact &F) { return Lattice(SetFactory.add(In.LiveOrigins, F.getReturnedOriginID())); } + + bool isLive(OriginID OID, ProgramPoint P) const { +return getState(P).LiveOrigins.contains(OID); + } + + OriginSet getLiveOrigins(ProgramPoint P) const { +return getState(P).LiveOrigins; + } }; // = // @@ -908,14 +930,15 @@ class ExpiredLoansAnalysis Lattice transfer(Lattice In, const IssueFact &F) { return Lattice(Factory.remove(In.Expired, F.getLoanID())); } + + bool isExpired(LoanID LID, ProgramPoint P) const { +return getState(P).Expired.contains(LID); + } }; // = // // TODO: -// - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` -// - Modify loan expiry analysis to answer `bool isExpired(Loan L, Point P)` -// - Modify origin liveness analysis to answer `bool isLive(Origin O, Point P)` -// - Using the above three to perform the final error reporting. +// - Add error reporting Add how would it work. // = // } // anonymous namespace ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148712 >From 7c2838035392804a2cd9bda1d0751e3633d2a31f Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:32:35 + Subject: [PATCH 1/2] users/usx95/lifetime-safety-add-loan-expiry >From ed73e3b1914a25c525bbb8f06fa0112cc4eb4bd0 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:37:49 + Subject: [PATCH 2/2] [LifetimeSafety] Add loan expiry analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 62 +++ 1 file changed, 62 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index c1bd4e82f4327..1aba655a6ead1 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -746,6 +746,65 @@ class LoanPropagationAnalysis return Factory.LoanSetFact.getEmptySet(); } }; +// = // +// Expired Loans Analysis +// = // + +/// The lattice for tracking expired loans. It is a set of loan IDs. +struct ExpiredLattice { + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { +return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "ExpiredLattice State:\n"; +if (Expired.isEmpty()) + OS << " \n"; +for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +class ExpiredLoansAnalysis +: public DataflowAnalysis { + + LoanSet::Factory &SetFactory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LoanSet::Factory &SF) + : DataflowAnalysis(C, AC, F), SetFactory(SF) {} + + using DataflowAnalysis::transfer; + + const char *getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(SetFactory.getEmptySet()); } + + Lattice join(Lattice L1, Lattice L2) const { +LoanSet JoinedSet = L1.Expired; +for (LoanID LID : L2.Expired) + JoinedSet = SetFactory.add(JoinedSet, LID); +return Lattice(JoinedSet); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { +return Lattice(SetFactory.add(In.Expired, F.getLoanID())); + } + + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(SetFactory.remove(In.Expired, F.getLoanID())); + } +}; + // = // // TODO: // - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` @@ -778,5 +837,8 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, LifetimeFact); LoanPropagation.run(); DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + + ExpiredLoansAnalysis ExpiredAnalysis(Cfg, AC, FactMgr, + LifetimeFact.LoanSetFact); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148712 >From 7c2838035392804a2cd9bda1d0751e3633d2a31f Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:32:35 + Subject: [PATCH 1/2] users/usx95/lifetime-safety-add-loan-expiry >From ed73e3b1914a25c525bbb8f06fa0112cc4eb4bd0 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:37:49 + Subject: [PATCH 2/2] [LifetimeSafety] Add loan expiry analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 62 +++ 1 file changed, 62 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index c1bd4e82f4327..1aba655a6ead1 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -746,6 +746,65 @@ class LoanPropagationAnalysis return Factory.LoanSetFact.getEmptySet(); } }; +// = // +// Expired Loans Analysis +// = // + +/// The lattice for tracking expired loans. It is a set of loan IDs. +struct ExpiredLattice { + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { +return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "ExpiredLattice State:\n"; +if (Expired.isEmpty()) + OS << " \n"; +for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +class ExpiredLoansAnalysis +: public DataflowAnalysis { + + LoanSet::Factory &SetFactory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LoanSet::Factory &SF) + : DataflowAnalysis(C, AC, F), SetFactory(SF) {} + + using DataflowAnalysis::transfer; + + const char *getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(SetFactory.getEmptySet()); } + + Lattice join(Lattice L1, Lattice L2) const { +LoanSet JoinedSet = L1.Expired; +for (LoanID LID : L2.Expired) + JoinedSet = SetFactory.add(JoinedSet, LID); +return Lattice(JoinedSet); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { +return Lattice(SetFactory.add(In.Expired, F.getLoanID())); + } + + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(SetFactory.remove(In.Expired, F.getLoanID())); + } +}; + // = // // TODO: // - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` @@ -778,5 +837,8 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, LifetimeFact); LoanPropagation.run(); DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + + ExpiredLoansAnalysis ExpiredAnalysis(Cfg, AC, FactMgr, + LifetimeFact.LoanSetFact); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148712 >From a9cb32152b8ba94a6a93c7923cce1abac43c9022 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:32:35 + Subject: [PATCH 1/2] users/usx95/lifetime-safety-add-loan-expiry >From a50b00e1b5394dacd635fd1da3aef83eb54348e5 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:37:49 + Subject: [PATCH 2/2] [LifetimeSafety] Add loan expiry analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 62 +++ 1 file changed, 62 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 99bd7bc36faf5..1ac8f42318b8d 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -747,6 +747,65 @@ class LoanPropagationAnalysis } }; +// = // +// Expired Loans Analysis +// = // + +/// The lattice for tracking expired loans. It is a set of loan IDs. +struct ExpiredLattice { + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { +return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "ExpiredLattice State:\n"; +if (Expired.isEmpty()) + OS << " \n"; +for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +class ExpiredLoansAnalysis +: public DataflowAnalysis { + + LoanSet::Factory &SetFactory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LoanSet::Factory &SF) + : DataflowAnalysis(C, AC, F), SetFactory(SF) {} + + using DataflowAnalysis::transfer; + + const char *getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(SetFactory.getEmptySet()); } + + Lattice join(Lattice L1, Lattice L2) const { +LoanSet JoinedSet = L1.Expired; +for (LoanID LID : L2.Expired) + JoinedSet = SetFactory.add(JoinedSet, LID); +return Lattice(JoinedSet); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { +return Lattice(SetFactory.add(In.Expired, F.getLoanID())); + } + + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(SetFactory.remove(In.Expired, F.getLoanID())); + } +}; + // = // // TODO: // - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` @@ -779,5 +838,8 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, LifetimeFact); LoanPropagation.run(); DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + + ExpiredLoansAnalysis ExpiredAnalysis(Cfg, AC, FactMgr, + LifetimeFact.LoanSetFact); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148712 >From a9cb32152b8ba94a6a93c7923cce1abac43c9022 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:32:35 + Subject: [PATCH 1/2] users/usx95/lifetime-safety-add-loan-expiry >From a50b00e1b5394dacd635fd1da3aef83eb54348e5 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:37:49 + Subject: [PATCH 2/2] [LifetimeSafety] Add loan expiry analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 62 +++ 1 file changed, 62 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 99bd7bc36faf5..1ac8f42318b8d 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -747,6 +747,65 @@ class LoanPropagationAnalysis } }; +// = // +// Expired Loans Analysis +// = // + +/// The lattice for tracking expired loans. It is a set of loan IDs. +struct ExpiredLattice { + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { +return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "ExpiredLattice State:\n"; +if (Expired.isEmpty()) + OS << " \n"; +for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +class ExpiredLoansAnalysis +: public DataflowAnalysis { + + LoanSet::Factory &SetFactory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LoanSet::Factory &SF) + : DataflowAnalysis(C, AC, F), SetFactory(SF) {} + + using DataflowAnalysis::transfer; + + const char *getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(SetFactory.getEmptySet()); } + + Lattice join(Lattice L1, Lattice L2) const { +LoanSet JoinedSet = L1.Expired; +for (LoanID LID : L2.Expired) + JoinedSet = SetFactory.add(JoinedSet, LID); +return Lattice(JoinedSet); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { +return Lattice(SetFactory.add(In.Expired, F.getLoanID())); + } + + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(SetFactory.remove(In.Expired, F.getLoanID())); + } +}; + // = // // TODO: // - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` @@ -779,5 +838,8 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, LifetimeFact); LoanPropagation.run(); DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + + ExpiredLoansAnalysis ExpiredAnalysis(Cfg, AC, FactMgr, + LifetimeFact.LoanSetFact); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Implement LiveOrigins analysis (PR #148976)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148976 >From 76e0954a5718990e8f1ebb6e4480c2cb32a56909 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Tue, 15 Jul 2025 22:19:48 + Subject: [PATCH] add-liveness-finally --- clang/lib/Analysis/LifetimeSafety.cpp | 78 +++ 1 file changed, 78 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index e44c3410161f0..b1ba171b3a8e2 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -675,12 +675,14 @@ join(llvm::ImmutableMap A, llvm::ImmutableMap B, // TODO(opt): Consider using a bitset to represent the set of loans. using LoanSet = llvm::ImmutableSet; using OriginLoanMap = llvm::ImmutableMap; +using OriginSet = llvm::ImmutableSet; /// An object to hold the factories for immutable collections, ensuring /// that all created states share the same underlying memory management. struct LifetimeFactory { OriginLoanMap::Factory OriginMapFactory; LoanSet::Factory LoanSetFactory; + OriginSet::Factory OriginSetFactory; /// Creates a singleton set containing only the given loan ID. LoanSet createLoanSet(LoanID LID) { @@ -778,6 +780,78 @@ class LoanPropagationAnalysis } }; +// = // +// Live Origins Analysis +// = // + +/// The dataflow lattice for origin liveness analysis. +/// It tracks the set of origins that are live at a given program point. +struct LivenessLattice { + OriginSet LiveOrigins; + + LivenessLattice() : LiveOrigins(nullptr) {}; + explicit LivenessLattice(OriginSet S) : LiveOrigins(S) {} + + bool operator==(const LivenessLattice &Other) const { +return LiveOrigins == Other.LiveOrigins; + } + bool operator!=(const LivenessLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "LivenessLattice State:\n"; +if (LiveOrigins.isEmpty()) + OS << " \n"; +for (const OriginID &OID : LiveOrigins) + OS << " Origin " << OID << " is live\n"; + } +}; + +/// The analysis that tracks which origins are live. This is a backward +/// analysis. +class LiveOriginAnalysis +: public DataflowAnalysis { + + OriginSet::Factory &SetFactory; + +public: + LiveOriginAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + OriginSet::Factory &SF) + : DataflowAnalysis(C, AC, F), SetFactory(SF) {} + + using DataflowAnalysis::transfer; + + StringRef getAnalysisName() const { return "LiveOrigins"; } + + Lattice getInitialState() { return Lattice(SetFactory.getEmptySet()); } + + /// Merges two lattices by taking the union of the live origin sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.LiveOrigins, L2.LiveOrigins, SetFactory)); + } + + /// An assignment `p = q` kills the liveness of `p` and generates liveness + /// for `q`. + Lattice transfer(Lattice In, const AssignOriginFact &F) { +OriginSet S = SetFactory.remove(In.LiveOrigins, F.getDestOriginID()); +S = SetFactory.add(S, F.getSrcOriginID()); +return Lattice(S); + } + + /// Issuing a new loan to an origin kills its liveness. + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(SetFactory.remove(In.LiveOrigins, F.getOriginID())); + } + + /// A return statement generates liveness for the returned origin. + Lattice transfer(Lattice In, const ReturnOfOriginFact &F) { +return Lattice(SetFactory.add(In.LiveOrigins, F.getReturnedOriginID())); + } +}; + // = // // Expired Loans Analysis // = // @@ -873,5 +947,9 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, ExpiredLoansAnalysis ExpiredLoans(Cfg, AC, FactMgr, Factory); ExpiredLoans.run(); DEBUG_WITH_TYPE("LifetimeExpiredLoans", ExpiredLoans.dump()); + + LiveOriginAnalysis Liveness(Cfg, AC, FactMgr, Factory.OriginSetFactory); + Liveness.run(); + DEBUG_WITH_TYPE("LifetimeLiveOrigins", Liveness.dump()); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Implement LiveOrigins analysis (PR #148976)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148976 >From 76e0954a5718990e8f1ebb6e4480c2cb32a56909 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Tue, 15 Jul 2025 22:19:48 + Subject: [PATCH] add-liveness-finally --- clang/lib/Analysis/LifetimeSafety.cpp | 78 +++ 1 file changed, 78 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index e44c3410161f0..b1ba171b3a8e2 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -675,12 +675,14 @@ join(llvm::ImmutableMap A, llvm::ImmutableMap B, // TODO(opt): Consider using a bitset to represent the set of loans. using LoanSet = llvm::ImmutableSet; using OriginLoanMap = llvm::ImmutableMap; +using OriginSet = llvm::ImmutableSet; /// An object to hold the factories for immutable collections, ensuring /// that all created states share the same underlying memory management. struct LifetimeFactory { OriginLoanMap::Factory OriginMapFactory; LoanSet::Factory LoanSetFactory; + OriginSet::Factory OriginSetFactory; /// Creates a singleton set containing only the given loan ID. LoanSet createLoanSet(LoanID LID) { @@ -778,6 +780,78 @@ class LoanPropagationAnalysis } }; +// = // +// Live Origins Analysis +// = // + +/// The dataflow lattice for origin liveness analysis. +/// It tracks the set of origins that are live at a given program point. +struct LivenessLattice { + OriginSet LiveOrigins; + + LivenessLattice() : LiveOrigins(nullptr) {}; + explicit LivenessLattice(OriginSet S) : LiveOrigins(S) {} + + bool operator==(const LivenessLattice &Other) const { +return LiveOrigins == Other.LiveOrigins; + } + bool operator!=(const LivenessLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "LivenessLattice State:\n"; +if (LiveOrigins.isEmpty()) + OS << " \n"; +for (const OriginID &OID : LiveOrigins) + OS << " Origin " << OID << " is live\n"; + } +}; + +/// The analysis that tracks which origins are live. This is a backward +/// analysis. +class LiveOriginAnalysis +: public DataflowAnalysis { + + OriginSet::Factory &SetFactory; + +public: + LiveOriginAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + OriginSet::Factory &SF) + : DataflowAnalysis(C, AC, F), SetFactory(SF) {} + + using DataflowAnalysis::transfer; + + StringRef getAnalysisName() const { return "LiveOrigins"; } + + Lattice getInitialState() { return Lattice(SetFactory.getEmptySet()); } + + /// Merges two lattices by taking the union of the live origin sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.LiveOrigins, L2.LiveOrigins, SetFactory)); + } + + /// An assignment `p = q` kills the liveness of `p` and generates liveness + /// for `q`. + Lattice transfer(Lattice In, const AssignOriginFact &F) { +OriginSet S = SetFactory.remove(In.LiveOrigins, F.getDestOriginID()); +S = SetFactory.add(S, F.getSrcOriginID()); +return Lattice(S); + } + + /// Issuing a new loan to an origin kills its liveness. + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(SetFactory.remove(In.LiveOrigins, F.getOriginID())); + } + + /// A return statement generates liveness for the returned origin. + Lattice transfer(Lattice In, const ReturnOfOriginFact &F) { +return Lattice(SetFactory.add(In.LiveOrigins, F.getReturnedOriginID())); + } +}; + // = // // Expired Loans Analysis // = // @@ -873,5 +947,9 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, ExpiredLoansAnalysis ExpiredLoans(Cfg, AC, FactMgr, Factory); ExpiredLoans.run(); DEBUG_WITH_TYPE("LifetimeExpiredLoans", ExpiredLoans.dump()); + + LiveOriginAnalysis Liveness(Cfg, AC, FactMgr, Factory.OriginSetFactory); + Liveness.run(); + DEBUG_WITH_TYPE("LifetimeLiveOrigins", Liveness.dump()); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148712 >From 615b4d0d262c742d7fd09f2afd8228d3d24f1513 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:32:35 + Subject: [PATCH 1/2] users/usx95/lifetime-safety-add-loan-expiry >From 037a7ec48c64b334be7f6abf6c54a39e10529b64 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:37:49 + Subject: [PATCH 2/2] [LifetimeSafety] Add loan expiry analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 63 +++ 1 file changed, 63 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index d37246aea712f..e44c3410161f0 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -778,6 +778,65 @@ class LoanPropagationAnalysis } }; +// = // +// Expired Loans Analysis +// = // + +/// The dataflow lattice for tracking the set of expired loans. +struct ExpiredLattice { + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { +return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "ExpiredLattice State:\n"; +if (Expired.isEmpty()) + OS << " \n"; +for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +/// The analysis that tracks which loans have expired. +class ExpiredLoansAnalysis +: public DataflowAnalysis { + + LoanSet::Factory &Factory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LifetimeFactory &Factory) + : DataflowAnalysis(C, AC, F), Factory(Factory.LoanSetFactory) {} + + using Base::transfer; + + StringRef getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(Factory.getEmptySet()); } + + /// Merges two lattices by taking the union of the expired loan sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.Expired, L2.Expired, Factory)); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { +return Lattice(Factory.add(In.Expired, F.getLoanID())); + } + + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(Factory.remove(In.Expired, F.getLoanID())); + } +}; + // = // // TODO: // - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` @@ -810,5 +869,9 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, Factory); LoanPropagation.run(); DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + + ExpiredLoansAnalysis ExpiredLoans(Cfg, AC, FactMgr, Factory); + ExpiredLoans.run(); + DEBUG_WITH_TYPE("LifetimeExpiredLoans", ExpiredLoans.dump()); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] add-point-level-granularity-to-analyses (PR #149069)
https://github.com/usx95 created https://github.com/llvm/llvm-project/pull/149069 None >From 4956a92749091ee811784d1928d47e95f9646640 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Wed, 16 Jul 2025 10:44:37 + Subject: [PATCH] add-point-level-granularity-to-analyses --- clang/lib/Analysis/LifetimeSafety.cpp | 43 --- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index b1ba171b3a8e2..9d5e73e48cc65 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -500,6 +500,9 @@ class FactGenerator : public ConstStmtVisitor { // Generic Dataflow Analysis // = // +// DO NOT SUBMIT: TODO: Document notion of before or after in the analyses. +using ProgramPoint = std::pair; + enum class Direction { Forward, Backward }; /// A generic, policy-based driver for dataflow analyses. It combines @@ -532,6 +535,7 @@ class DataflowAnalysis { llvm::DenseMap InStates; llvm::DenseMap OutStates; + llvm::DenseMap PerPointStates; static constexpr bool isForward() { return Dir == Direction::Forward; } @@ -577,6 +581,8 @@ class DataflowAnalysis { } } + Lattice getState(ProgramPoint P) const { return PerPointStates.lookup(P); } + Lattice getInState(const CFGBlock *B) const { return InStates.lookup(B); } Lattice getOutState(const CFGBlock *B) const { return OutStates.lookup(B); } @@ -590,18 +596,22 @@ class DataflowAnalysis { getOutState(&B).dump(llvm::dbgs()); } +private: /// Computes the state at one end of a block by applying all its facts /// sequentially to a given state from the other end. - /// TODO: We might need to store intermediate states per-fact in the block for - /// later analysis. Lattice transferBlock(const CFGBlock *Block, Lattice State) { auto Facts = AllFacts.getFacts(Block); -if constexpr (isForward()) - for (const Fact *F : Facts) +if constexpr (isForward()) { + for (const Fact *F : Facts) { State = transferFact(State, F); -else - for (const Fact *F : llvm::reverse(Facts)) +PerPointStates[{Block, F}] = State; + } +} else { + for (const Fact *F : llvm::reverse(Facts)) { State = transferFact(State, F); +PerPointStates[{Block, F}] = State; + } +} return State; } @@ -772,6 +782,10 @@ class LoanPropagationAnalysis Factory.OriginMapFactory.add(In.Origins, DestOID, SrcLoans)); } + LoanSet getLoans(OriginID OID, ProgramPoint P) { +return getLoans(getState(P), OID); + } + private: LoanSet getLoans(Lattice L, OriginID OID) { if (auto *Loans = L.Origins.lookup(OID)) @@ -850,6 +864,14 @@ class LiveOriginAnalysis Lattice transfer(Lattice In, const ReturnOfOriginFact &F) { return Lattice(SetFactory.add(In.LiveOrigins, F.getReturnedOriginID())); } + + bool isLive(OriginID OID, ProgramPoint P) const { +return getState(P).LiveOrigins.contains(OID); + } + + OriginSet getLiveOrigins(ProgramPoint P) const { +return getState(P).LiveOrigins; + } }; // = // @@ -909,14 +931,15 @@ class ExpiredLoansAnalysis Lattice transfer(Lattice In, const IssueFact &F) { return Lattice(Factory.remove(In.Expired, F.getLoanID())); } + + bool isExpired(LoanID LID, ProgramPoint P) const { +return getState(P).Expired.contains(LID); + } }; // = // // TODO: -// - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` -// - Modify loan expiry analysis to answer `bool isExpired(Loan L, Point P)` -// - Modify origin liveness analysis to answer `bool isLive(Origin O, Point P)` -// - Using the above three to perform the final error reporting. +// - Add error reporting Add how would it work. // = // } // anonymous namespace ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] add-point-level-granularity-to-analyses (PR #149069)
usx95 wrote: > [!WARNING] > This pull request is not mergeable via GitHub because a downstack PR is > open. Once all requirements are satisfied, merge this PR as a stack href="https://app.graphite.dev/github/pr/llvm/llvm-project/149069?utm_source=stack-comment-downstack-mergeability-warning"; > >on Graphite. > https://graphite.dev/docs/merge-pull-requests";>Learn more * **#149069** https://app.graphite.dev/github/pr/llvm/llvm-project/149069?utm_source=stack-comment-icon"; target="_blank">https://static.graphite.dev/graphite-32x32-black.png"; alt="Graphite" width="10px" height="10px"/> 👈 https://app.graphite.dev/github/pr/llvm/llvm-project/149069?utm_source=stack-comment-view-in-graphite"; target="_blank">(View in Graphite) * **#148976** https://app.graphite.dev/github/pr/llvm/llvm-project/148976?utm_source=stack-comment-icon"; target="_blank">https://static.graphite.dev/graphite-32x32-black.png"; alt="Graphite" width="10px" height="10px"/> * **#148712** https://app.graphite.dev/github/pr/llvm/llvm-project/148712?utm_source=stack-comment-icon"; target="_blank">https://static.graphite.dev/graphite-32x32-black.png"; alt="Graphite" width="10px" height="10px"/> * **#148967** https://app.graphite.dev/github/pr/llvm/llvm-project/148967?utm_source=stack-comment-icon"; target="_blank">https://static.graphite.dev/graphite-32x32-black.png"; alt="Graphite" width="10px" height="10px"/> * **#148222** https://app.graphite.dev/github/pr/llvm/llvm-project/148222?utm_source=stack-comment-icon"; target="_blank">https://static.graphite.dev/graphite-32x32-black.png"; alt="Graphite" width="10px" height="10px"/> * **#148065** https://app.graphite.dev/github/pr/llvm/llvm-project/148065?utm_source=stack-comment-icon"; target="_blank">https://static.graphite.dev/graphite-32x32-black.png"; alt="Graphite" width="10px" height="10px"/>: 1 other dependent PR ([#147315](https://github.com/llvm/llvm-project/pull/147315) https://app.graphite.dev/github/pr/llvm/llvm-project/147315?utm_source=stack-comment-icon"; target="_blank">https://static.graphite.dev/graphite-32x32-black.png"; alt="Graphite" width="10px" height="10px"/>) * `main` This stack of pull requests is managed by https://graphite.dev?utm-source=stack-comment";>Graphite. Learn more about https://stacking.dev/?utm_source=stack-comment";>stacking. https://github.com/llvm/llvm-project/pull/149069 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148712 >From 8f6394b2ab6183fd06f6e216cbaa70b823809e5d Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:32:35 + Subject: [PATCH 1/2] users/usx95/lifetime-safety-add-loan-expiry >From 12e5b9a2dc200a9d8e710ef509bb39021fc92de0 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:37:49 + Subject: [PATCH 2/2] [LifetimeSafety] Add loan expiry analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 61 +++ 1 file changed, 61 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 0b4241cdf404c..300cc6ddd66af 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -769,6 +769,65 @@ class LoanPropagationAnalysis } }; +// = // +// Expired Loans Analysis +// = // + +/// The dataflow lattice for tracking the set of expired loans. +class ExpiredLattice { +public: + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { +return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "ExpiredLattice State:\n"; +if (Expired.isEmpty()) + OS << " \n"; +for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +/// The analysis that tracks which loans have expired. +class ExpiredLoansAnalysis +: public DataflowAnalysis { + + LoanSet::Factory &Factory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LifetimeFactory &SF) + : DataflowAnalysis(C, AC, F), Factory(SF.LoanSetFactory) {} + + using DataflowAnalysis::transfer; + + const char *getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(Factory.getEmptySet()); } + + /// Merges two lattices by taking the union of the expired loan sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.Expired, L2.Expired, Factory)); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { +return Lattice(Factory.add(In.Expired, F.getLoanID())); + } + + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(Factory.remove(In.Expired, F.getLoanID())); + } +}; + // = // // TODO: // - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` @@ -801,5 +860,7 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, Factory); LoanPropagation.run(); DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + + ExpiredLoansAnalysis ExpiredAnalysis(Cfg, AC, FactMgr, Factory); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148712 >From 3c30a13f6f796fd9483f7d6aeea28c12b8ad6d64 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:32:35 + Subject: [PATCH 1/2] users/usx95/lifetime-safety-add-loan-expiry >From 9da8f3ab473378b2f5191c88b60a847e777ef4ec Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:37:49 + Subject: [PATCH 2/2] [LifetimeSafety] Add loan expiry analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 61 +++ 1 file changed, 61 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 8be93bbba7131..e30e979bf1120 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -762,6 +762,64 @@ class LoanPropagationAnalysis } }; +// = // +// Expired Loans Analysis +// = // + +/// The dataflow lattice for tracking the set of expired loans. +struct ExpiredLattice { + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { +return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "ExpiredLattice State:\n"; +if (Expired.isEmpty()) + OS << " \n"; +for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +/// The analysis that tracks which loans have expired. +class ExpiredLoansAnalysis +: public DataflowAnalysis { + + LoanSet::Factory &Factory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LifetimeFactory &SF) + : DataflowAnalysis(C, AC, F), Factory(SF.LoanSetFactory) {} + + using DataflowAnalysis::transfer; + + StringRef getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(Factory.getEmptySet()); } + + /// Merges two lattices by taking the union of the expired loan sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.Expired, L2.Expired, Factory)); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { +return Lattice(Factory.add(In.Expired, F.getLoanID())); + } + + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(Factory.remove(In.Expired, F.getLoanID())); + } +}; + // = // // TODO: // - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` @@ -794,5 +852,8 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, Factory); LoanPropagation.run(); DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + + ExpiredLoansAnalysis ExpiredLoans(Cfg, AC, FactMgr, Factory); + ExpiredLoans.run(); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148712 >From 3c30a13f6f796fd9483f7d6aeea28c12b8ad6d64 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:32:35 + Subject: [PATCH 1/2] users/usx95/lifetime-safety-add-loan-expiry >From 9da8f3ab473378b2f5191c88b60a847e777ef4ec Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:37:49 + Subject: [PATCH 2/2] [LifetimeSafety] Add loan expiry analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 61 +++ 1 file changed, 61 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 8be93bbba7131..e30e979bf1120 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -762,6 +762,64 @@ class LoanPropagationAnalysis } }; +// = // +// Expired Loans Analysis +// = // + +/// The dataflow lattice for tracking the set of expired loans. +struct ExpiredLattice { + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { +return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "ExpiredLattice State:\n"; +if (Expired.isEmpty()) + OS << " \n"; +for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +/// The analysis that tracks which loans have expired. +class ExpiredLoansAnalysis +: public DataflowAnalysis { + + LoanSet::Factory &Factory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LifetimeFactory &SF) + : DataflowAnalysis(C, AC, F), Factory(SF.LoanSetFactory) {} + + using DataflowAnalysis::transfer; + + StringRef getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(Factory.getEmptySet()); } + + /// Merges two lattices by taking the union of the expired loan sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.Expired, L2.Expired, Factory)); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { +return Lattice(Factory.add(In.Expired, F.getLoanID())); + } + + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(Factory.remove(In.Expired, F.getLoanID())); + } +}; + // = // // TODO: // - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` @@ -794,5 +852,8 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, Factory); LoanPropagation.run(); DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + + ExpiredLoansAnalysis ExpiredLoans(Cfg, AC, FactMgr, Factory); + ExpiredLoans.run(); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148712 >From 542bc99c1f5fb5fc28a3374f61b2fa3d059d9d75 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:32:35 + Subject: [PATCH 1/2] users/usx95/lifetime-safety-add-loan-expiry >From 4b70fcf7f20083a34a18bf0f84dfc29cce696db4 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:37:49 + Subject: [PATCH 2/2] [LifetimeSafety] Add loan expiry analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 61 +++ 1 file changed, 61 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 9d1b662e3c589..5e54e8dadcfd0 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -763,6 +763,65 @@ class LoanPropagationAnalysis } }; +// = // +// Expired Loans Analysis +// = // + +/// The dataflow lattice for tracking the set of expired loans. +class ExpiredLattice { +public: + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { +return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "ExpiredLattice State:\n"; +if (Expired.isEmpty()) + OS << " \n"; +for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +/// The analysis that tracks which loans have expired. +class ExpiredLoansAnalysis +: public DataflowAnalysis { + + LoanSet::Factory &Factory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LifetimeFactory &SF) + : DataflowAnalysis(C, AC, F), Factory(SF.LoanSetFactory) {} + + using DataflowAnalysis::transfer; + + const char *getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(Factory.getEmptySet()); } + + /// Merges two lattices by taking the union of the expired loan sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.Expired, L2.Expired, Factory)); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { +return Lattice(Factory.add(In.Expired, F.getLoanID())); + } + + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(Factory.remove(In.Expired, F.getLoanID())); + } +}; + // = // // TODO: // - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` @@ -795,5 +854,7 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, Factory); LoanPropagation.run(); DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + + ExpiredLoansAnalysis ExpiredAnalysis(Cfg, AC, FactMgr, Factory); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148712 >From 542bc99c1f5fb5fc28a3374f61b2fa3d059d9d75 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:32:35 + Subject: [PATCH 1/2] users/usx95/lifetime-safety-add-loan-expiry >From 4b70fcf7f20083a34a18bf0f84dfc29cce696db4 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:37:49 + Subject: [PATCH 2/2] [LifetimeSafety] Add loan expiry analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 61 +++ 1 file changed, 61 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 9d1b662e3c589..5e54e8dadcfd0 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -763,6 +763,65 @@ class LoanPropagationAnalysis } }; +// = // +// Expired Loans Analysis +// = // + +/// The dataflow lattice for tracking the set of expired loans. +class ExpiredLattice { +public: + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { +return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "ExpiredLattice State:\n"; +if (Expired.isEmpty()) + OS << " \n"; +for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +/// The analysis that tracks which loans have expired. +class ExpiredLoansAnalysis +: public DataflowAnalysis { + + LoanSet::Factory &Factory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LifetimeFactory &SF) + : DataflowAnalysis(C, AC, F), Factory(SF.LoanSetFactory) {} + + using DataflowAnalysis::transfer; + + const char *getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(Factory.getEmptySet()); } + + /// Merges two lattices by taking the union of the expired loan sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.Expired, L2.Expired, Factory)); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { +return Lattice(Factory.add(In.Expired, F.getLoanID())); + } + + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(Factory.remove(In.Expired, F.getLoanID())); + } +}; + // = // // TODO: // - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` @@ -795,5 +854,7 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, Factory); LoanPropagation.run(); DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + + ExpiredLoansAnalysis ExpiredAnalysis(Cfg, AC, FactMgr, Factory); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148712 >From 3696b6b7a6167c63d0fd5cbef607e44129feb2e2 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:32:35 + Subject: [PATCH 1/2] users/usx95/lifetime-safety-add-loan-expiry >From 3e064c102babd4abc403fa2ea4a41ce018f1352b Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:37:49 + Subject: [PATCH 2/2] [LifetimeSafety] Add loan expiry analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 61 +++ 1 file changed, 61 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index a9c0ee08950b8..8995b5a8ecc90 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -761,6 +761,65 @@ class LoanPropagationAnalysis } }; +// = // +// Expired Loans Analysis +// = // + +/// The dataflow lattice for tracking the set of expired loans. +class ExpiredLattice { +public: + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { +return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "ExpiredLattice State:\n"; +if (Expired.isEmpty()) + OS << " \n"; +for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +/// The analysis that tracks which loans have expired. +class ExpiredLoansAnalysis +: public DataflowAnalysis { + + LoanSet::Factory &Factory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LifetimeFactory &SF) + : DataflowAnalysis(C, AC, F), Factory(SF.LoanSetFactory) {} + + using DataflowAnalysis::transfer; + + const char *getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(Factory.getEmptySet()); } + + /// Merges two lattices by taking the union of the expired loan sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.Expired, L2.Expired, Factory)); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { +return Lattice(Factory.add(In.Expired, F.getLoanID())); + } + + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(Factory.remove(In.Expired, F.getLoanID())); + } +}; + // = // // TODO: // - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` @@ -793,5 +852,7 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, Factory); LoanPropagation.run(); DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + + ExpiredLoansAnalysis ExpiredAnalysis(Cfg, AC, FactMgr, Factory); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148712 >From 3696b6b7a6167c63d0fd5cbef607e44129feb2e2 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:32:35 + Subject: [PATCH 1/2] users/usx95/lifetime-safety-add-loan-expiry >From 3e064c102babd4abc403fa2ea4a41ce018f1352b Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:37:49 + Subject: [PATCH 2/2] [LifetimeSafety] Add loan expiry analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 61 +++ 1 file changed, 61 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index a9c0ee08950b8..8995b5a8ecc90 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -761,6 +761,65 @@ class LoanPropagationAnalysis } }; +// = // +// Expired Loans Analysis +// = // + +/// The dataflow lattice for tracking the set of expired loans. +class ExpiredLattice { +public: + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { +return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "ExpiredLattice State:\n"; +if (Expired.isEmpty()) + OS << " \n"; +for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +/// The analysis that tracks which loans have expired. +class ExpiredLoansAnalysis +: public DataflowAnalysis { + + LoanSet::Factory &Factory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LifetimeFactory &SF) + : DataflowAnalysis(C, AC, F), Factory(SF.LoanSetFactory) {} + + using DataflowAnalysis::transfer; + + const char *getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(Factory.getEmptySet()); } + + /// Merges two lattices by taking the union of the expired loan sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.Expired, L2.Expired, Factory)); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { +return Lattice(Factory.add(In.Expired, F.getLoanID())); + } + + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(Factory.remove(In.Expired, F.getLoanID())); + } +}; + // = // // TODO: // - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` @@ -793,5 +852,7 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, Factory); LoanPropagation.run(); DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + + ExpiredLoansAnalysis ExpiredAnalysis(Cfg, AC, FactMgr, Factory); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/148712 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add loan expiry analysis (PR #148712)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148712 >From 542bc99c1f5fb5fc28a3374f61b2fa3d059d9d75 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:32:35 + Subject: [PATCH 1/2] users/usx95/lifetime-safety-add-loan-expiry >From 6ce317303d599371fc61529eb1d3e7a1624aa8fb Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 14 Jul 2025 19:37:49 + Subject: [PATCH 2/2] [LifetimeSafety] Add loan expiry analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 62 +++ 1 file changed, 62 insertions(+) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 9d1b662e3c589..52c6b18021d7a 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -763,6 +763,65 @@ class LoanPropagationAnalysis } }; +// = // +// Expired Loans Analysis +// = // + +/// The dataflow lattice for tracking the set of expired loans. +class ExpiredLattice { +public: + LoanSet Expired; + + ExpiredLattice() : Expired(nullptr) {}; + explicit ExpiredLattice(LoanSet S) : Expired(S) {} + + bool operator==(const ExpiredLattice &Other) const { +return Expired == Other.Expired; + } + bool operator!=(const ExpiredLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "ExpiredLattice State:\n"; +if (Expired.isEmpty()) + OS << " \n"; +for (const LoanID &LID : Expired) + OS << " Loan " << LID << " is expired\n"; + } +}; + +/// The analysis that tracks which loans have expired. +class ExpiredLoansAnalysis +: public DataflowAnalysis { + + LoanSet::Factory &Factory; + +public: + ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + LifetimeFactory &SF) + : DataflowAnalysis(C, AC, F), Factory(SF.LoanSetFactory) {} + + using DataflowAnalysis::transfer; + + const char *getAnalysisName() const { return "ExpiredLoans"; } + + Lattice getInitialState() { return Lattice(Factory.getEmptySet()); } + + /// Merges two lattices by taking the union of the expired loan sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.Expired, L2.Expired, Factory)); + } + + Lattice transfer(Lattice In, const ExpireFact &F) { +return Lattice(Factory.add(In.Expired, F.getLoanID())); + } + + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(Factory.remove(In.Expired, F.getLoanID())); + } +}; + // = // // TODO: // - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)` @@ -795,5 +854,8 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, Factory); LoanPropagation.run(); DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + + ExpiredLoansAnalysis ExpiredLoans(Cfg, AC, FactMgr, Factory); + ExpiredLoans.run(); } } // namespace clang ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Revamp test suite using unittests (PR #149158)
https://github.com/usx95 ready_for_review https://github.com/llvm/llvm-project/pull/149158 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Revamp test suite using unittests (PR #149158)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/149158 >From 1b945d05ac984b6a62dd07f64eaafd8f364fbbfb Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Wed, 16 Jul 2025 18:22:39 + Subject: [PATCH] [LifetimeSafety] Revamp test suite using unittests --- .../clang/Analysis/Analyses/LifetimeSafety.h | 83 +++- clang/lib/Analysis/LifetimeSafety.cpp | 173 +-- clang/lib/Sema/AnalysisBasedWarnings.cpp | 4 +- .../Sema/warn-lifetime-safety-dataflow.cpp| 87 +--- clang/unittests/Analysis/CMakeLists.txt | 1 + .../unittests/Analysis/LifetimeSafetyTest.cpp | 431 ++ 6 files changed, 640 insertions(+), 139 deletions(-) create mode 100644 clang/unittests/Analysis/LifetimeSafetyTest.cpp diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h index 9998702a41cab..87627d4388d79 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h @@ -17,14 +17,87 @@ //===--===// #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H -#include "clang/AST/DeclBase.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" -namespace clang { +#include "llvm/ADT/ImmutableSet.h" +#include "llvm/ADT/StringMap.h" +#include -void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, - AnalysisDeclContext &AC); +namespace clang::lifetimes { +namespace internal { +// Forward declarations of internal types. +class Fact; +class FactManager; +class LoanPropagationAnalysis; +struct LifetimeFactory; -} // namespace clang +/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type. +/// Used for giving ID to loans and origins. +template struct ID { + uint32_t Value = 0; + + bool operator==(const ID &Other) const { return Value == Other.Value; } + bool operator!=(const ID &Other) const { return !(*this == Other); } + bool operator<(const ID &Other) const { return Value < Other.Value; } + ID operator++(int) { +ID Tmp = *this; +++Value; +return Tmp; + } + void Profile(llvm::FoldingSetNodeID &IDBuilder) const { +IDBuilder.AddInteger(Value); + } +}; + +using LoanID = ID; +using OriginID = ID; + +// Using LLVM's immutable collections is efficient for dataflow analysis +// as it avoids deep copies during state transitions. +// TODO(opt): Consider using a bitset to represent the set of loans. +using LoanSet = llvm::ImmutableSet; +using OriginSet = llvm::ImmutableSet; + +/// A `ProgramPoint` identifies a location in the CFG by pointing to a specific +/// `Fact`. identified by a lifetime-related event (`Fact`). +/// +/// A `ProgramPoint` has "after" semantics: it represents the location +/// immediately after its corresponding `Fact`. +using ProgramPoint = const Fact *; + +/// Running the lifetime safety analysis and querying its results. It +/// encapsulates the various dataflow analyses. +class LifetimeSafetyAnalysis { +public: + LifetimeSafetyAnalysis(AnalysisDeclContext &AC); + ~LifetimeSafetyAnalysis(); + + void run(); + + /// Returns the set of loans an origin holds at a specific program point. + LoanSet getLoansAtPoint(OriginID OID, ProgramPoint PP) const; + + /// Finds the OriginID for a given declaration. + /// Returns a null optional if not found. + std::optional getOriginIDForDecl(const ValueDecl *D) const; + + /// Finds the LoanID's for the loan created with the specific variable as + /// their Path. + std::vector getLoanIDForVar(const VarDecl *VD) const; + + llvm::StringMap getTestPoints() const; + +private: + AnalysisDeclContext &AC; + std::unique_ptr Factory; + std::unique_ptr FactMgr; + std::unique_ptr LoanPropagation; +}; +} // namespace internal + +/// The main entry point for the analysis. +void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC); + +} // namespace clang::lifetimes #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index a95db6d8013bd..3f56c20820e08 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -24,8 +24,14 @@ #include "llvm/Support/TimeProfiler.h" #include -namespace clang { +namespace clang::lifetimes { +namespace internal { namespace { +template +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID ID) { + return OS << ID.Value; +} +} // namespace /// Represents the storage location being borrowed, e.g., a specific stack /// variable. @@ -36,32 +42,6 @@ struct AccessPath { AccessPath(const clang::ValueDecl *D) : D(D) {} }; -/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type. -/// Used for giving ID to loans and origins. -template struct ID {
[llvm-branch-commits] [clang] [LifetimeSafety] Revamp test suite using unittests (PR #149158)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/149158 >From 16fccbde6b871bdbc5103814bd2d31068fc75718 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Wed, 16 Jul 2025 18:22:39 + Subject: [PATCH] address comment --- .../clang/Analysis/Analyses/LifetimeSafety.h | 83 +++- clang/lib/Analysis/LifetimeSafety.cpp | 180 +-- clang/lib/Sema/AnalysisBasedWarnings.cpp | 4 +- .../Sema/warn-lifetime-safety-dataflow.cpp| 87 +--- clang/unittests/Analysis/CMakeLists.txt | 1 + .../unittests/Analysis/LifetimeSafetyTest.cpp | 439 ++ 6 files changed, 655 insertions(+), 139 deletions(-) create mode 100644 clang/unittests/Analysis/LifetimeSafetyTest.cpp diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h index 9998702a41cab..f5ce23848448f 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h @@ -17,14 +17,87 @@ //===--===// #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H -#include "clang/AST/DeclBase.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" -namespace clang { +#include "llvm/ADT/ImmutableSet.h" +#include "llvm/ADT/StringMap.h" +#include -void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, - AnalysisDeclContext &AC); +namespace clang::lifetimes { -} // namespace clang +/// The main entry point for the analysis. +void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC); + +namespace internal { +// Forward declarations of internal types. +class Fact; +class FactManager; +class LoanPropagationAnalysis; +struct LifetimeFactory; + +/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type. +/// Used for giving ID to loans and origins. +template struct ID { + uint32_t Value = 0; + + bool operator==(const ID &Other) const { return Value == Other.Value; } + bool operator!=(const ID &Other) const { return !(*this == Other); } + bool operator<(const ID &Other) const { return Value < Other.Value; } + ID operator++(int) { +ID Tmp = *this; +++Value; +return Tmp; + } + void Profile(llvm::FoldingSetNodeID &IDBuilder) const { +IDBuilder.AddInteger(Value); + } +}; + +using LoanID = ID; +using OriginID = ID; + +// Using LLVM's immutable collections is efficient for dataflow analysis +// as it avoids deep copies during state transitions. +// TODO(opt): Consider using a bitset to represent the set of loans. +using LoanSet = llvm::ImmutableSet; +using OriginSet = llvm::ImmutableSet; + +/// A `ProgramPoint` identifies a location in the CFG by pointing to a specific +/// `Fact`. identified by a lifetime-related event (`Fact`). +/// +/// A `ProgramPoint` has "after" semantics: it represents the location +/// immediately after its corresponding `Fact`. +using ProgramPoint = const Fact *; + +/// Running the lifetime safety analysis and querying its results. It +/// encapsulates the various dataflow analyses. +class LifetimeSafetyAnalysis { +public: + LifetimeSafetyAnalysis(AnalysisDeclContext &AC); + ~LifetimeSafetyAnalysis(); + + void run(); + + /// Returns the set of loans an origin holds at a specific program point. + LoanSet getLoansAtPoint(OriginID OID, ProgramPoint PP) const; + + /// Finds the OriginID for a given declaration. + /// Returns a null optional if not found. + std::optional getOriginIDForDecl(const ValueDecl *D) const; + + /// Finds the LoanID's for the loan created with the specific variable as + /// their Path. + std::vector getLoanIDForVar(const VarDecl *VD) const; + + llvm::StringMap getTestPoints() const; + +private: + AnalysisDeclContext &AC; + std::unique_ptr Factory; + std::unique_ptr FactMgr; + std::unique_ptr LoanPropagation; +}; +} // namespace internal +} // namespace clang::lifetimes #endif // LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index a95db6d8013bd..22c724dbd6ed9 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -24,8 +24,14 @@ #include "llvm/Support/TimeProfiler.h" #include -namespace clang { +namespace clang::lifetimes { +namespace internal { namespace { +template +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID ID) { + return OS << ID.Value; +} +} // namespace /// Represents the storage location being borrowed, e.g., a specific stack /// variable. @@ -36,32 +42,6 @@ struct AccessPath { AccessPath(const clang::ValueDecl *D) : D(D) {} }; -/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type. -/// Used for giving ID to loans and origins. -template struct ID { - uint32_t Value = 0; - - bool op
[llvm-branch-commits] [clang] [LifetimeSafety] Revamp test suite using unittests (PR #149158)
@@ -17,14 +17,87 @@ //===--===// #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMESAFETY_H -#include "clang/AST/DeclBase.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" -namespace clang { +#include "llvm/ADT/ImmutableSet.h" +#include "llvm/ADT/StringMap.h" +#include -void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, - AnalysisDeclContext &AC); +namespace clang::lifetimes { +namespace internal { +// Forward declarations of internal types. +class Fact; +class FactManager; +class LoanPropagationAnalysis; +struct LifetimeFactory; -} // namespace clang +/// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type. +/// Used for giving ID to loans and origins. +template struct ID { + uint32_t Value = 0; + + bool operator==(const ID &Other) const { return Value == Other.Value; } + bool operator!=(const ID &Other) const { return !(*this == Other); } + bool operator<(const ID &Other) const { return Value < Other.Value; } + ID operator++(int) { +ID Tmp = *this; +++Value; +return Tmp; + } + void Profile(llvm::FoldingSetNodeID &IDBuilder) const { +IDBuilder.AddInteger(Value); + } +}; + +using LoanID = ID; +using OriginID = ID; + +// Using LLVM's immutable collections is efficient for dataflow analysis +// as it avoids deep copies during state transitions. +// TODO(opt): Consider using a bitset to represent the set of loans. +using LoanSet = llvm::ImmutableSet; +using OriginSet = llvm::ImmutableSet; + +/// A `ProgramPoint` identifies a location in the CFG by pointing to a specific +/// `Fact`. identified by a lifetime-related event (`Fact`). +/// +/// A `ProgramPoint` has "after" semantics: it represents the location +/// immediately after its corresponding `Fact`. +using ProgramPoint = const Fact *; + +/// Running the lifetime safety analysis and querying its results. It +/// encapsulates the various dataflow analyses. +class LifetimeSafetyAnalysis { +public: + LifetimeSafetyAnalysis(AnalysisDeclContext &AC); + ~LifetimeSafetyAnalysis(); + + void run(); + + /// Returns the set of loans an origin holds at a specific program point. + LoanSet getLoansAtPoint(OriginID OID, ProgramPoint PP) const; + + /// Finds the OriginID for a given declaration. + /// Returns a null optional if not found. + std::optional getOriginIDForDecl(const ValueDecl *D) const; + + /// Finds the LoanID's for the loan created with the specific variable as + /// their Path. + std::vector getLoanIDForVar(const VarDecl *VD) const; + + llvm::StringMap getTestPoints() const; + +private: + AnalysisDeclContext &AC; + std::unique_ptr Factory; + std::unique_ptr FactMgr; + std::unique_ptr LoanPropagation; +}; +} // namespace internal + +/// The main entry point for the analysis. +void runLifetimeSafetyAnalysis(AnalysisDeclContext &AC); usx95 wrote: Makes sense. Done. https://github.com/llvm/llvm-project/pull/149158 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Revamp test suite using unittests (PR #149158)
@@ -828,9 +869,49 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, ///blocks; only Decls are visible. Therefore, loans in a block that ///never reach an Origin associated with a Decl can be safely dropped by ///the analysis. - LifetimeFactory Factory; - LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, Factory); - LoanPropagation.run(); - DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + LoanPropagation = + std::make_unique(Cfg, AC, *FactMgr, *Factory); + LoanPropagation->run(); +} + +LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID, +ProgramPoint PP) const { + assert(LoanPropagation && "Analysis has not been run."); + return LoanPropagation->getLoans(OID, PP); +} + +std::optional +LifetimeSafetyAnalysis::getOriginIDForDecl(const ValueDecl *D) const { + assert(FactMgr && "FactManager not initialized"); + // This assumes the OriginManager's `get` can find an existing origin. + // We might need a `find` method on OriginManager to avoid `getOrCreate` logic + // in a const-query context if that becomes an issue. + return FactMgr->getOriginMgr().get(*D); +} + +std::vector +LifetimeSafetyAnalysis::getLoanIDForVar(const VarDecl *VD) const { + assert(FactMgr && "FactManager not initialized"); + std::vector Result; + for (const Loan &L : FactMgr->getLoanMgr().getLoans()) +if (L.Path.D == VD) + Result.push_back(L.ID); + return Result; +} + +llvm::StringMap LifetimeSafetyAnalysis::getTestPoints() const { + assert(FactMgr && "FactManager not initialized"); + llvm::StringMap AnnotationToPointMap; + for (const CFGBlock *Block : *AC.getCFG()) +for (const Fact *F : FactMgr->getFacts(Block)) + if (const auto *TPF = F->getAs()) +AnnotationToPointMap[TPF->getAnnotation()] = F; usx95 wrote: Oops. Missed it. Thanks! https://github.com/llvm/llvm-project/pull/149158 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Revamp test suite using unittests (PR #149158)
@@ -807,17 +838,27 @@ class LoanPropagationAnalysis // - Modify origin liveness analysis to answer `bool isLive(Origin O, Point P)` // - Using the above three to perform the final error reporting. // = // -} // anonymous namespace -void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, - AnalysisDeclContext &AC) { +// = // +// LifetimeSafetyAnalysis Class Implementation +// = // + +LifetimeSafetyAnalysis::~LifetimeSafetyAnalysis() = default; usx95 wrote: In general yes. But we have some forward decls with members as unique_ptr to them: ``` std::unique_ptr Factory; std::unique_ptr FactMgr; std::unique_ptr LoanPropagation; ``` By defining the destructor here, we are delaying the need for the class `LifetimeSafetyAnalysis` to be a complete type (specially while parsing the `LifetimeSafetyTest.cpp` TU). https://github.com/llvm/llvm-project/pull/149158 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Revamp test suite using unittests (PR #149158)
@@ -828,9 +869,49 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg, ///blocks; only Decls are visible. Therefore, loans in a block that ///never reach an Origin associated with a Decl can be safely dropped by ///the analysis. - LifetimeFactory Factory; - LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, Factory); - LoanPropagation.run(); - DEBUG_WITH_TYPE("LifetimeLoanPropagation", LoanPropagation.dump()); + LoanPropagation = + std::make_unique(Cfg, AC, *FactMgr, *Factory); + LoanPropagation->run(); +} + +LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID, +ProgramPoint PP) const { + assert(LoanPropagation && "Analysis has not been run."); + return LoanPropagation->getLoans(OID, PP); +} + +std::optional +LifetimeSafetyAnalysis::getOriginIDForDecl(const ValueDecl *D) const { + assert(FactMgr && "FactManager not initialized"); + // This assumes the OriginManager's `get` can find an existing origin. + // We might need a `find` method on OriginManager to avoid `getOrCreate` logic + // in a const-query context if that becomes an issue. + return FactMgr->getOriginMgr().get(*D); +} + +std::vector +LifetimeSafetyAnalysis::getLoanIDForVar(const VarDecl *VD) const { + assert(FactMgr && "FactManager not initialized"); + std::vector Result; + for (const Loan &L : FactMgr->getLoanMgr().getLoans()) +if (L.Path.D == VD) + Result.push_back(L.ID); + return Result; +} + +llvm::StringMap LifetimeSafetyAnalysis::getTestPoints() const { + assert(FactMgr && "FactManager not initialized"); usx95 wrote: Do you mean the assert statement (it is already available only in assert builds). Maybe you are talking about the getTestPoints() function? https://github.com/llvm/llvm-project/pull/149158 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] add-loan-analysis-to-benchmark (PR #149577)
https://github.com/usx95 created https://github.com/llvm/llvm-project/pull/149577 None >From cb9a52142e992ee632dc669d80b02443af84ae5e Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Fri, 18 Jul 2025 19:16:43 + Subject: [PATCH] add-loan-analysis-to-benchmark --- .../test/Analysis/LifetimeSafety/benchmark.py | 238 -- 1 file changed, 157 insertions(+), 81 deletions(-) diff --git a/clang/test/Analysis/LifetimeSafety/benchmark.py b/clang/test/Analysis/LifetimeSafety/benchmark.py index 9d5f36c51b9ee..229f58ba019e4 100644 --- a/clang/test/Analysis/LifetimeSafety/benchmark.py +++ b/clang/test/Analysis/LifetimeSafety/benchmark.py @@ -21,22 +21,12 @@ def generate_cpp_cycle_test(n: int) -> str: struct MyObj { int id; ~MyObj() {} }; void long_cycle_4(bool condition) { -MyObj v1{1}; -MyObj v2{1}; -MyObj v3{1}; -MyObj v4{1}; - -MyObj* p1 = &v1; -MyObj* p2 = &v2; -MyObj* p3 = &v3; -MyObj* p4 = &v4; +MyObj v1{1}; MyObj v2{1}; MyObj v3{1}; MyObj v4{1}; +MyObj* p1 = &v1; MyObj* p2 = &v2; MyObj* p3 = &v3; MyObj* p4 = &v4; while (condition) { MyObj* temp = p1; -p1 = p2; -p2 = p3; -p3 = p4; -p4 = temp; +p1 = p2; p2 = p3; p3 = p4; p4 = temp; } } """ @@ -99,28 +89,81 @@ def generate_cpp_merge_test(n: int) -> str: return cpp_code -def analyze_trace_file(trace_path: str) -> tuple[float, float]: +def generate_cpp_nested_loop_test(n: int) -> str: """ -Parses the -ftime-trace JSON output to find durations. +Generates C++ code with N levels of nested loops. +This pattern tests how analysis performance scales with loop nesting depth, +which is a key factor in the complexity of dataflow analyses on structured +control flow. + +Example (n=3): +struct MyObj { int id; ~MyObj() {} }; +void nested_loops_3() { +MyObj* p = nullptr; +for(int i0=0; i0<2; ++i0) { +MyObj s0; p = &s0; +for(int i1=0; i1<2; ++i1) { +MyObj s1; p = &s1; +for(int i2=0; i2<2; ++i2) { +MyObj s2; p = &s2; +} +} +} +} +""" +if n <= 0: +return "// Nesting depth must be positive." + +cpp_code = "struct MyObj { int id; ~MyObj() {} };\n\n" +cpp_code += f"void nested_loops_{n}() {{\n" +cpp_code += "MyObj* p = nullptr;\n" + +for i in range(n): +indent = "" * (i + 1) +cpp_code += f"{indent}for(int i{i}=0; i{i}<2; ++i{i}) {{\n" +cpp_code += f"{indent}MyObj s{i}; p = &s{i};\n" + +for i in range(n - 1, -1, -1): +indent = "" * (i + 1) +cpp_code += f"{indent}}}\n" + +cpp_code += "}\n" +cpp_code += f"\nint main() {{ nested_loops_{n}(); return 0; }}\n" +return cpp_code + -Returns: -A tuple of (lifetime_analysis_duration_us, total_clang_duration_us). +def analyze_trace_file(trace_path: str) -> dict: """ -lifetime_duration = 0.0 -total_duration = 0.0 +Parses the -ftime-trace JSON output to find durations for the lifetime +analysis and its sub-phases. +Returns a dictionary of durations in microseconds. +""" +durations = { +"lifetime_us": 0.0, +"total_us": 0.0, +"fact_gen_us": 0.0, +"loan_prop_us": 0.0, +"expired_loans_us": 0.0, +} +event_name_map = { +"LifetimeSafetyAnalysis": "lifetime_us", +"ExecuteCompiler": "total_us", +"FactGenerator": "fact_gen_us", +"LoanPropagation": "loan_prop_us", +"ExpiredLoans": "expired_loans_us", +} try: with open(trace_path, "r") as f: trace_data = json.load(f) for event in trace_data.get("traceEvents", []): -if event.get("name") == "LifetimeSafetyAnalysis": -lifetime_duration += float(event.get("dur", 0)) -if event.get("name") == "ExecuteCompiler": -total_duration += float(event.get("dur", 0)) - +event_name = event.get("name") +if event_name in event_name_map: +key = event_name_map[event_name] +durations[key] += float(event.get("dur", 0)) except (IOError, json.JSONDecodeError) as e: print(f"Error reading or parsing trace file {trace_path}: {e}", file=sys.stderr) -return 0.0, 0.0 -return lifetime_duration, total_duration +return {key: 0.0 for key in durations} +return durations def power_law(n, c, k): @@ -135,8 +178,29 @@ def human_readable_time(ms: float) -> str: return f"{ms:.2f} ms" +def calculate_complexity(n_data, y_data) -> tuple[float |
[llvm-branch-commits] [clang] [LifetimeSafety] Enhance benchmark script for ExpiredLoans analysis (PR #149577)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149577 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Enhance benchmark script for ExpiredLoans analysis (PR #149577)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149577 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Enhance benchmark script for ExpiredLoans analysis (PR #149577)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/149577 >From 9e6a675838d87e79b39c74af81c359fab1190473 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Fri, 18 Jul 2025 19:16:43 + Subject: [PATCH] add-loan-analysis-to-benchmark --- .../test/Analysis/LifetimeSafety/benchmark.py | 239 -- 1 file changed, 158 insertions(+), 81 deletions(-) diff --git a/clang/test/Analysis/LifetimeSafety/benchmark.py b/clang/test/Analysis/LifetimeSafety/benchmark.py index 9d5f36c51b9ee..2ade1716caff3 100644 --- a/clang/test/Analysis/LifetimeSafety/benchmark.py +++ b/clang/test/Analysis/LifetimeSafety/benchmark.py @@ -21,22 +21,12 @@ def generate_cpp_cycle_test(n: int) -> str: struct MyObj { int id; ~MyObj() {} }; void long_cycle_4(bool condition) { -MyObj v1{1}; -MyObj v2{1}; -MyObj v3{1}; -MyObj v4{1}; - -MyObj* p1 = &v1; -MyObj* p2 = &v2; -MyObj* p3 = &v3; -MyObj* p4 = &v4; +MyObj v1{1}; MyObj v2{1}; MyObj v3{1}; MyObj v4{1}; +MyObj* p1 = &v1; MyObj* p2 = &v2; MyObj* p3 = &v3; MyObj* p4 = &v4; while (condition) { MyObj* temp = p1; -p1 = p2; -p2 = p3; -p3 = p4; -p4 = temp; +p1 = p2; p2 = p3; p3 = p4; p4 = temp; } } """ @@ -99,28 +89,81 @@ def generate_cpp_merge_test(n: int) -> str: return cpp_code -def analyze_trace_file(trace_path: str) -> tuple[float, float]: +def generate_cpp_nested_loop_test(n: int) -> str: """ -Parses the -ftime-trace JSON output to find durations. +Generates C++ code with N levels of nested loops. +This pattern tests how analysis performance scales with loop nesting depth, +which is a key factor in the complexity of dataflow analyses on structured +control flow. + +Example (n=3): +struct MyObj { int id; ~MyObj() {} }; +void nested_loops_3() { +MyObj* p = nullptr; +for(int i0=0; i0<2; ++i0) { +MyObj s0; p = &s0; +for(int i1=0; i1<2; ++i1) { +MyObj s1; p = &s1; +for(int i2=0; i2<2; ++i2) { +MyObj s2; p = &s2; +} +} +} +} +""" +if n <= 0: +return "// Nesting depth must be positive." + +cpp_code = "struct MyObj { int id; ~MyObj() {} };\n\n" +cpp_code += f"void nested_loops_{n}() {{\n" +cpp_code += "MyObj* p = nullptr;\n" + +for i in range(n): +indent = "" * (i + 1) +cpp_code += f"{indent}for(int i{i}=0; i{i}<2; ++i{i}) {{\n" +cpp_code += f"{indent}MyObj s{i}; p = &s{i};\n" + +for i in range(n - 1, -1, -1): +indent = "" * (i + 1) +cpp_code += f"{indent}}}\n" + +cpp_code += "}\n" +cpp_code += f"\nint main() {{ nested_loops_{n}(); return 0; }}\n" +return cpp_code + -Returns: -A tuple of (lifetime_analysis_duration_us, total_clang_duration_us). +def analyze_trace_file(trace_path: str) -> dict: """ -lifetime_duration = 0.0 -total_duration = 0.0 +Parses the -ftime-trace JSON output to find durations for the lifetime +analysis and its sub-phases. +Returns a dictionary of durations in microseconds. +""" +durations = { +"lifetime_us": 0.0, +"total_us": 0.0, +"fact_gen_us": 0.0, +"loan_prop_us": 0.0, +"expired_loans_us": 0.0, +} +event_name_map = { +"LifetimeSafetyAnalysis": "lifetime_us", +"ExecuteCompiler": "total_us", +"FactGenerator": "fact_gen_us", +"LoanPropagation": "loan_prop_us", +"ExpiredLoans": "expired_loans_us", +} try: with open(trace_path, "r") as f: trace_data = json.load(f) for event in trace_data.get("traceEvents", []): -if event.get("name") == "LifetimeSafetyAnalysis": -lifetime_duration += float(event.get("dur", 0)) -if event.get("name") == "ExecuteCompiler": -total_duration += float(event.get("dur", 0)) - +event_name = event.get("name") +if event_name in event_name_map: +key = event_name_map[event_name] +durations[key] += float(event.get("dur", 0)) except (IOError, json.JSONDecodeError) as e: print(f"Error reading or parsing trace file {trace_path}: {e}", file=sys.stderr) -return 0.0, 0.0 -return lifetime_duration, total_duration +return {key: 0.0 for key in durations} +return durations def power_law(n, c, k): @@ -135,8 +178,29 @@ def human_readable_time(ms: float) -> str: return f"{ms:.2f} ms" +def calculate_complexity(n_data, y_data) -> tuple[float | None,
[llvm-branch-commits] [clang] [LifetimeSafety] Enhance benchmark script for ExpiredLoans analysis (PR #149577)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149577 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Enhance benchmark script for ExpiredLoans analysis (PR #149577)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149577 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Enhance benchmark script for ExpiredLoans analysis (PR #149577)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149577 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Enhance benchmark script for ExpiredLoans analysis (PR #149577)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/149577 >From d4f54571c0c795d5296ef12cd055599490d14259 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Fri, 18 Jul 2025 19:16:43 + Subject: [PATCH] add-loan-analysis-to-benchmark --- .../test/Analysis/LifetimeSafety/benchmark.py | 239 -- 1 file changed, 158 insertions(+), 81 deletions(-) diff --git a/clang/test/Analysis/LifetimeSafety/benchmark.py b/clang/test/Analysis/LifetimeSafety/benchmark.py index 9d5f36c51b9ee..2ade1716caff3 100644 --- a/clang/test/Analysis/LifetimeSafety/benchmark.py +++ b/clang/test/Analysis/LifetimeSafety/benchmark.py @@ -21,22 +21,12 @@ def generate_cpp_cycle_test(n: int) -> str: struct MyObj { int id; ~MyObj() {} }; void long_cycle_4(bool condition) { -MyObj v1{1}; -MyObj v2{1}; -MyObj v3{1}; -MyObj v4{1}; - -MyObj* p1 = &v1; -MyObj* p2 = &v2; -MyObj* p3 = &v3; -MyObj* p4 = &v4; +MyObj v1{1}; MyObj v2{1}; MyObj v3{1}; MyObj v4{1}; +MyObj* p1 = &v1; MyObj* p2 = &v2; MyObj* p3 = &v3; MyObj* p4 = &v4; while (condition) { MyObj* temp = p1; -p1 = p2; -p2 = p3; -p3 = p4; -p4 = temp; +p1 = p2; p2 = p3; p3 = p4; p4 = temp; } } """ @@ -99,28 +89,81 @@ def generate_cpp_merge_test(n: int) -> str: return cpp_code -def analyze_trace_file(trace_path: str) -> tuple[float, float]: +def generate_cpp_nested_loop_test(n: int) -> str: """ -Parses the -ftime-trace JSON output to find durations. +Generates C++ code with N levels of nested loops. +This pattern tests how analysis performance scales with loop nesting depth, +which is a key factor in the complexity of dataflow analyses on structured +control flow. + +Example (n=3): +struct MyObj { int id; ~MyObj() {} }; +void nested_loops_3() { +MyObj* p = nullptr; +for(int i0=0; i0<2; ++i0) { +MyObj s0; p = &s0; +for(int i1=0; i1<2; ++i1) { +MyObj s1; p = &s1; +for(int i2=0; i2<2; ++i2) { +MyObj s2; p = &s2; +} +} +} +} +""" +if n <= 0: +return "// Nesting depth must be positive." + +cpp_code = "struct MyObj { int id; ~MyObj() {} };\n\n" +cpp_code += f"void nested_loops_{n}() {{\n" +cpp_code += "MyObj* p = nullptr;\n" + +for i in range(n): +indent = "" * (i + 1) +cpp_code += f"{indent}for(int i{i}=0; i{i}<2; ++i{i}) {{\n" +cpp_code += f"{indent}MyObj s{i}; p = &s{i};\n" + +for i in range(n - 1, -1, -1): +indent = "" * (i + 1) +cpp_code += f"{indent}}}\n" + +cpp_code += "}\n" +cpp_code += f"\nint main() {{ nested_loops_{n}(); return 0; }}\n" +return cpp_code + -Returns: -A tuple of (lifetime_analysis_duration_us, total_clang_duration_us). +def analyze_trace_file(trace_path: str) -> dict: """ -lifetime_duration = 0.0 -total_duration = 0.0 +Parses the -ftime-trace JSON output to find durations for the lifetime +analysis and its sub-phases. +Returns a dictionary of durations in microseconds. +""" +durations = { +"lifetime_us": 0.0, +"total_us": 0.0, +"fact_gen_us": 0.0, +"loan_prop_us": 0.0, +"expired_loans_us": 0.0, +} +event_name_map = { +"LifetimeSafetyAnalysis": "lifetime_us", +"ExecuteCompiler": "total_us", +"FactGenerator": "fact_gen_us", +"LoanPropagation": "loan_prop_us", +"ExpiredLoans": "expired_loans_us", +} try: with open(trace_path, "r") as f: trace_data = json.load(f) for event in trace_data.get("traceEvents", []): -if event.get("name") == "LifetimeSafetyAnalysis": -lifetime_duration += float(event.get("dur", 0)) -if event.get("name") == "ExecuteCompiler": -total_duration += float(event.get("dur", 0)) - +event_name = event.get("name") +if event_name in event_name_map: +key = event_name_map[event_name] +durations[key] += float(event.get("dur", 0)) except (IOError, json.JSONDecodeError) as e: print(f"Error reading or parsing trace file {trace_path}: {e}", file=sys.stderr) -return 0.0, 0.0 -return lifetime_duration, total_duration +return {key: 0.0 for key in durations} +return durations def power_law(n, c, k): @@ -135,8 +178,29 @@ def human_readable_time(ms: float) -> str: return f"{ms:.2f} ms" +def calculate_complexity(n_data, y_data) -> tuple[float | None,
[llvm-branch-commits] [clang] [LifetimeSafety] Implement LiveOrigins analysis (PR #148976)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148976 >From 01b019da96555128bef927d51173226a03f69a88 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Tue, 15 Jul 2025 22:19:48 + Subject: [PATCH] add-liveness-finally --- .../clang/Analysis/Analyses/LifetimeSafety.h | 5 + clang/lib/Analysis/LifetimeSafety.cpp | 83 +++ .../unittests/Analysis/LifetimeSafetyTest.cpp | 136 ++ 3 files changed, 224 insertions(+) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h index 1c00558d32f63..1b7cb82fbe891 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h @@ -34,6 +34,7 @@ class Fact; class FactManager; class LoanPropagationAnalysis; class ExpiredLoansAnalysis; +class LiveOriginAnalysis; struct LifetimeFactory; /// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type. @@ -97,6 +98,9 @@ class LifetimeSafetyAnalysis { /// their Path. std::vector getLoanIDForVar(const VarDecl *VD) const; + /// Returns the set of live origins at a specific program point. + OriginSet getLiveOriginsAtPoint(ProgramPoint PP) const; + /// Retrieves program points that were specially marked in the source code /// for testing. /// @@ -114,6 +118,7 @@ class LifetimeSafetyAnalysis { std::unique_ptr FactMgr; std::unique_ptr LoanPropagation; std::unique_ptr ExpiredLoans; + std::unique_ptr LiveOrigins; }; } // namespace internal } // namespace clang::lifetimes diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 815a36e13412c..a717681c4ea44 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -727,12 +727,14 @@ join(llvm::ImmutableMap A, llvm::ImmutableMap B, // = // using OriginLoanMap = llvm::ImmutableMap; +using OriginSet = llvm::ImmutableSet; /// An object to hold the factories for immutable collections, ensuring /// that all created states share the same underlying memory management. struct LifetimeFactory { OriginLoanMap::Factory OriginMapFactory; LoanSet::Factory LoanSetFactory; + OriginSet::Factory OriginSetFactory; /// Creates a singleton set containing only the given loan ID. LoanSet createLoanSet(LoanID LID) { @@ -833,6 +835,78 @@ class LoanPropagationAnalysis } }; +// = // +// Live Origins Analysis +// = // + +/// The dataflow lattice for origin liveness analysis. +/// It tracks the set of origins that are live at a given program point. +struct LivenessLattice { + OriginSet LiveOrigins; + + LivenessLattice() : LiveOrigins(nullptr) {}; + explicit LivenessLattice(OriginSet S) : LiveOrigins(S) {} + + bool operator==(const LivenessLattice &Other) const { +return LiveOrigins == Other.LiveOrigins; + } + bool operator!=(const LivenessLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "LivenessLattice State:\n"; +if (LiveOrigins.isEmpty()) + OS << " \n"; +for (const OriginID &OID : LiveOrigins) + OS << " Origin " << OID << " is live\n"; + } +}; + +/// The analysis that tracks which origins are live. This is a backward +/// analysis. +class LiveOriginAnalysis +: public DataflowAnalysis { + + OriginSet::Factory &SetFactory; + +public: + LiveOriginAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + OriginSet::Factory &SF) + : DataflowAnalysis(C, AC, F), SetFactory(SF) {} + + using DataflowAnalysis::transfer; + + StringRef getAnalysisName() const { return "LiveOrigins"; } + + Lattice getInitialState() { return Lattice(SetFactory.getEmptySet()); } + + /// Merges two lattices by taking the union of the live origin sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.LiveOrigins, L2.LiveOrigins, SetFactory)); + } + + /// An assignment `p = q` kills the liveness of `p` and generates liveness + /// for `q`. + Lattice transfer(Lattice In, const AssignOriginFact &F) { +OriginSet S = SetFactory.remove(In.LiveOrigins, F.getDestOriginID()); +S = SetFactory.add(S, F.getSrcOriginID()); +return Lattice(S); + } + + /// Issuing a new loan to an origin kills its liveness. + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(SetFactory.remove(In.LiveOrigins, F.getOriginID())); + } + + /// A return statement generates liveness for the returned origin. + Lattice transfer(Lattice In, const ReturnOfOriginFact &F) { +return Lattice(SetFactory.add(In.LiveOrigins, F.getReturnedOriginID())); + } +}; + // =
[llvm-branch-commits] [clang] [LifetimeSafety] Implement LiveOrigins analysis (PR #148976)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148976 >From e1327406119cc0887f473ebd93a02d6eb383b234 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Tue, 15 Jul 2025 22:19:48 + Subject: [PATCH] add-liveness-finally --- .../clang/Analysis/Analyses/LifetimeSafety.h | 5 + clang/lib/Analysis/LifetimeSafety.cpp | 83 +++ .../unittests/Analysis/LifetimeSafetyTest.cpp | 136 ++ 3 files changed, 224 insertions(+) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h index 1c00558d32f63..1b7cb82fbe891 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h @@ -34,6 +34,7 @@ class Fact; class FactManager; class LoanPropagationAnalysis; class ExpiredLoansAnalysis; +class LiveOriginAnalysis; struct LifetimeFactory; /// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type. @@ -97,6 +98,9 @@ class LifetimeSafetyAnalysis { /// their Path. std::vector getLoanIDForVar(const VarDecl *VD) const; + /// Returns the set of live origins at a specific program point. + OriginSet getLiveOriginsAtPoint(ProgramPoint PP) const; + /// Retrieves program points that were specially marked in the source code /// for testing. /// @@ -114,6 +118,7 @@ class LifetimeSafetyAnalysis { std::unique_ptr FactMgr; std::unique_ptr LoanPropagation; std::unique_ptr ExpiredLoans; + std::unique_ptr LiveOrigins; }; } // namespace internal } // namespace clang::lifetimes diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 815a36e13412c..a717681c4ea44 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -727,12 +727,14 @@ join(llvm::ImmutableMap A, llvm::ImmutableMap B, // = // using OriginLoanMap = llvm::ImmutableMap; +using OriginSet = llvm::ImmutableSet; /// An object to hold the factories for immutable collections, ensuring /// that all created states share the same underlying memory management. struct LifetimeFactory { OriginLoanMap::Factory OriginMapFactory; LoanSet::Factory LoanSetFactory; + OriginSet::Factory OriginSetFactory; /// Creates a singleton set containing only the given loan ID. LoanSet createLoanSet(LoanID LID) { @@ -833,6 +835,78 @@ class LoanPropagationAnalysis } }; +// = // +// Live Origins Analysis +// = // + +/// The dataflow lattice for origin liveness analysis. +/// It tracks the set of origins that are live at a given program point. +struct LivenessLattice { + OriginSet LiveOrigins; + + LivenessLattice() : LiveOrigins(nullptr) {}; + explicit LivenessLattice(OriginSet S) : LiveOrigins(S) {} + + bool operator==(const LivenessLattice &Other) const { +return LiveOrigins == Other.LiveOrigins; + } + bool operator!=(const LivenessLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "LivenessLattice State:\n"; +if (LiveOrigins.isEmpty()) + OS << " \n"; +for (const OriginID &OID : LiveOrigins) + OS << " Origin " << OID << " is live\n"; + } +}; + +/// The analysis that tracks which origins are live. This is a backward +/// analysis. +class LiveOriginAnalysis +: public DataflowAnalysis { + + OriginSet::Factory &SetFactory; + +public: + LiveOriginAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + OriginSet::Factory &SF) + : DataflowAnalysis(C, AC, F), SetFactory(SF) {} + + using DataflowAnalysis::transfer; + + StringRef getAnalysisName() const { return "LiveOrigins"; } + + Lattice getInitialState() { return Lattice(SetFactory.getEmptySet()); } + + /// Merges two lattices by taking the union of the live origin sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.LiveOrigins, L2.LiveOrigins, SetFactory)); + } + + /// An assignment `p = q` kills the liveness of `p` and generates liveness + /// for `q`. + Lattice transfer(Lattice In, const AssignOriginFact &F) { +OriginSet S = SetFactory.remove(In.LiveOrigins, F.getDestOriginID()); +S = SetFactory.add(S, F.getSrcOriginID()); +return Lattice(S); + } + + /// Issuing a new loan to an origin kills its liveness. + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(SetFactory.remove(In.LiveOrigins, F.getOriginID())); + } + + /// A return statement generates liveness for the returned origin. + Lattice transfer(Lattice In, const ReturnOfOriginFact &F) { +return Lattice(SetFactory.add(In.LiveOrigins, F.getReturnedOriginID())); + } +}; + // =
[llvm-branch-commits] [clang] [LifetimeSafety] Enhance benchmark script for ExpiredLoans analysis (PR #149577)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149577 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Enhance benchmark script for new sub analyses (PR #149577)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149577 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] add-loan-analysis-to-benchmark (PR #149577)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149577 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] add-loan-analysis-to-benchmark (PR #149577)
usx95 wrote: > [!WARNING] > This pull request is not mergeable via GitHub because a downstack PR is > open. Once all requirements are satisfied, merge this PR as a stack href="https://app.graphite.dev/github/pr/llvm/llvm-project/149577?utm_source=stack-comment-downstack-mergeability-warning"; > >on Graphite. > https://graphite.dev/docs/merge-pull-requests";>Learn more * **#149577** https://app.graphite.dev/github/pr/llvm/llvm-project/149577?utm_source=stack-comment-icon"; target="_blank">https://static.graphite.dev/graphite-32x32-black.png"; alt="Graphite" width="10px" height="10px"/> 👈 https://app.graphite.dev/github/pr/llvm/llvm-project/149577?utm_source=stack-comment-view-in-graphite"; target="_blank">(View in Graphite) * **#148712** https://app.graphite.dev/github/pr/llvm/llvm-project/148712?utm_source=stack-comment-icon"; target="_blank">https://static.graphite.dev/graphite-32x32-black.png"; alt="Graphite" width="10px" height="10px"/>: 1 other dependent PR ([#148976](https://github.com/llvm/llvm-project/pull/148976) https://app.graphite.dev/github/pr/llvm/llvm-project/148976?utm_source=stack-comment-icon"; target="_blank">https://static.graphite.dev/graphite-32x32-black.png"; alt="Graphite" width="10px" height="10px"/>) * **#149158** https://app.graphite.dev/github/pr/llvm/llvm-project/149158?utm_source=stack-comment-icon"; target="_blank">https://static.graphite.dev/graphite-32x32-black.png"; alt="Graphite" width="10px" height="10px"/> * **#149199** https://app.graphite.dev/github/pr/llvm/llvm-project/149199?utm_source=stack-comment-icon"; target="_blank">https://static.graphite.dev/graphite-32x32-black.png"; alt="Graphite" width="10px" height="10px"/> * `main` This stack of pull requests is managed by https://graphite.dev?utm-source=stack-comment";>Graphite. Learn more about https://stacking.dev/?utm_source=stack-comment";>stacking. https://github.com/llvm/llvm-project/pull/149577 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Enhance benchmark script for end timing (PR #149577)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149577 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Enhance benchmark script for ExpiredLoans analysis (PR #149577)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149577 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Enhance benchmark script for ExpiredLoans analysis (PR #149577)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149577 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Enhance benchmark script for ExpiredLoans analysis (PR #149577)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149577 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Implement LiveOrigins analysis (PR #148976)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/148976 >From 5eadf7b9b70e2a1f839939806de9794313b6f8f0 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Tue, 15 Jul 2025 22:19:48 + Subject: [PATCH] add-liveness-finally --- .../clang/Analysis/Analyses/LifetimeSafety.h | 5 + clang/lib/Analysis/LifetimeSafety.cpp | 83 +++ .../unittests/Analysis/LifetimeSafetyTest.cpp | 134 ++ 3 files changed, 222 insertions(+) diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h index 1c00558d32f63..1b7cb82fbe891 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h @@ -34,6 +34,7 @@ class Fact; class FactManager; class LoanPropagationAnalysis; class ExpiredLoansAnalysis; +class LiveOriginAnalysis; struct LifetimeFactory; /// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type. @@ -97,6 +98,9 @@ class LifetimeSafetyAnalysis { /// their Path. std::vector getLoanIDForVar(const VarDecl *VD) const; + /// Returns the set of live origins at a specific program point. + OriginSet getLiveOriginsAtPoint(ProgramPoint PP) const; + /// Retrieves program points that were specially marked in the source code /// for testing. /// @@ -114,6 +118,7 @@ class LifetimeSafetyAnalysis { std::unique_ptr FactMgr; std::unique_ptr LoanPropagation; std::unique_ptr ExpiredLoans; + std::unique_ptr LiveOrigins; }; } // namespace internal } // namespace clang::lifetimes diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 815a36e13412c..a717681c4ea44 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -727,12 +727,14 @@ join(llvm::ImmutableMap A, llvm::ImmutableMap B, // = // using OriginLoanMap = llvm::ImmutableMap; +using OriginSet = llvm::ImmutableSet; /// An object to hold the factories for immutable collections, ensuring /// that all created states share the same underlying memory management. struct LifetimeFactory { OriginLoanMap::Factory OriginMapFactory; LoanSet::Factory LoanSetFactory; + OriginSet::Factory OriginSetFactory; /// Creates a singleton set containing only the given loan ID. LoanSet createLoanSet(LoanID LID) { @@ -833,6 +835,78 @@ class LoanPropagationAnalysis } }; +// = // +// Live Origins Analysis +// = // + +/// The dataflow lattice for origin liveness analysis. +/// It tracks the set of origins that are live at a given program point. +struct LivenessLattice { + OriginSet LiveOrigins; + + LivenessLattice() : LiveOrigins(nullptr) {}; + explicit LivenessLattice(OriginSet S) : LiveOrigins(S) {} + + bool operator==(const LivenessLattice &Other) const { +return LiveOrigins == Other.LiveOrigins; + } + bool operator!=(const LivenessLattice &Other) const { +return !(*this == Other); + } + + void dump(llvm::raw_ostream &OS) const { +OS << "LivenessLattice State:\n"; +if (LiveOrigins.isEmpty()) + OS << " \n"; +for (const OriginID &OID : LiveOrigins) + OS << " Origin " << OID << " is live\n"; + } +}; + +/// The analysis that tracks which origins are live. This is a backward +/// analysis. +class LiveOriginAnalysis +: public DataflowAnalysis { + + OriginSet::Factory &SetFactory; + +public: + LiveOriginAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, + OriginSet::Factory &SF) + : DataflowAnalysis(C, AC, F), SetFactory(SF) {} + + using DataflowAnalysis::transfer; + + StringRef getAnalysisName() const { return "LiveOrigins"; } + + Lattice getInitialState() { return Lattice(SetFactory.getEmptySet()); } + + /// Merges two lattices by taking the union of the live origin sets. + Lattice join(Lattice L1, Lattice L2) const { +return Lattice(utils::join(L1.LiveOrigins, L2.LiveOrigins, SetFactory)); + } + + /// An assignment `p = q` kills the liveness of `p` and generates liveness + /// for `q`. + Lattice transfer(Lattice In, const AssignOriginFact &F) { +OriginSet S = SetFactory.remove(In.LiveOrigins, F.getDestOriginID()); +S = SetFactory.add(S, F.getSrcOriginID()); +return Lattice(S); + } + + /// Issuing a new loan to an origin kills its liveness. + Lattice transfer(Lattice In, const IssueFact &F) { +return Lattice(SetFactory.remove(In.LiveOrigins, F.getOriginID())); + } + + /// A return statement generates liveness for the returned origin. + Lattice transfer(Lattice In, const ReturnOfOriginFact &F) { +return Lattice(SetFactory.add(In.LiveOrigins, F.getReturnedOriginID())); + } +}; + // =
[llvm-branch-commits] [clang] [LifetimeSafety] Enhance benchmark script for new sub analyses (PR #149577)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/149577 >From 6cb36540be595794914faa992e59be07e7cede9a Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sat, 19 Jul 2025 15:40:36 + Subject: [PATCH] add live origins --- .../test/Analysis/LifetimeSafety/benchmark.py | 230 +- 1 file changed, 165 insertions(+), 65 deletions(-) diff --git a/clang/test/Analysis/LifetimeSafety/benchmark.py b/clang/test/Analysis/LifetimeSafety/benchmark.py index 9d5f36c51b9ee..1ab89d107a238 100644 --- a/clang/test/Analysis/LifetimeSafety/benchmark.py +++ b/clang/test/Analysis/LifetimeSafety/benchmark.py @@ -99,28 +99,86 @@ def generate_cpp_merge_test(n: int) -> str: return cpp_code -def analyze_trace_file(trace_path: str) -> tuple[float, float]: +def generate_cpp_nested_loop_test(n: int) -> str: """ -Parses the -ftime-trace JSON output to find durations. +Generates C++ code with N levels of nested loops. +This pattern tests how analysis performance scales with loop nesting depth, +which is a key factor in the complexity of dataflow analyses on structured +control flow. -Returns: -A tuple of (lifetime_analysis_duration_us, total_clang_duration_us). +Example (n=3): +struct MyObj { int id; ~MyObj() {} }; +void nested_loops_3() { +MyObj* p = nullptr; +for(int i0=0; i0<2; ++i0) { +MyObj s0; +p = &s0; +for(int i1=0; i1<2; ++i1) { +MyObj s1; +p = &s1; +for(int i2=0; i2<2; ++i2) { +MyObj s2; +p = &s2; +} +} +} +} +""" +if n <= 0: +return "// Nesting depth must be positive." + +cpp_code = "struct MyObj { int id; ~MyObj() {} };\n\n" +cpp_code += f"void nested_loops_{n}() {{\n" +cpp_code += "MyObj* p = nullptr;\n" + +for i in range(n): +indent = "" * (i + 1) +cpp_code += f"{indent}for(int i{i}=0; i{i}<2; ++i{i}) {{\n" +cpp_code += f"{indent}MyObj s{i}; p = &s{i};\n" + +for i in range(n - 1, -1, -1): +indent = "" * (i + 1) +cpp_code += f"{indent}}}\n" + +cpp_code += "}\n" +cpp_code += f"\nint main() {{ nested_loops_{n}(); return 0; }}\n" +return cpp_code + + +def analyze_trace_file(trace_path: str) -> dict: """ -lifetime_duration = 0.0 -total_duration = 0.0 +Parses the -ftime-trace JSON output to find durations for the lifetime +analysis and its sub-phases. +Returns a dictionary of durations in microseconds. +""" +durations = { +"lifetime_us": 0.0, +"total_us": 0.0, +"fact_gen_us": 0.0, +"loan_prop_us": 0.0, +"expired_loans_us": 0.0, +"live_origins_us": 0.0, +} +event_name_map = { +"LifetimeSafetyAnalysis": "lifetime_us", +"ExecuteCompiler": "total_us", +"FactGenerator": "fact_gen_us", +"LoanPropagation": "loan_prop_us", +"ExpiredLoans": "expired_loans_us", +"LiveOrigins": "live_origins_us", +} try: with open(trace_path, "r") as f: trace_data = json.load(f) for event in trace_data.get("traceEvents", []): -if event.get("name") == "LifetimeSafetyAnalysis": -lifetime_duration += float(event.get("dur", 0)) -if event.get("name") == "ExecuteCompiler": -total_duration += float(event.get("dur", 0)) - +event_name = event.get("name") +if event_name in event_name_map: +key = event_name_map[event_name] +durations[key] += float(event.get("dur", 0)) except (IOError, json.JSONDecodeError) as e: print(f"Error reading or parsing trace file {trace_path}: {e}", file=sys.stderr) -return 0.0, 0.0 -return lifetime_duration, total_duration +return {key: 0.0 for key in durations} +return durations def power_law(n, c, k): @@ -135,8 +193,29 @@ def human_readable_time(ms: float) -> str: return f"{ms:.2f} ms" +def calculate_complexity(n_data, y_data) -> tuple[float | None, float | None]: +""" +Calculates the exponent 'k' for the power law fit y = c * n^k. +Returns a tuple of (k, k_standard_error). +""" +try: +if len(n_data) < 3 or np.all(y_data < 1e-6) or np.var(y_data) < 1e-6: +return None, None + +non_zero_indices = y_data > 0 +if np.sum(non_zero_indices) < 3: +return None, None + +n_fit, y_fit = n_data[non_zero_indices], y_data[non_zero_indices] +popt, pcov = curve_fit(power_law, n_fit, y_fit, p0=[0, 1], maxfev=5000) +k_stderr = np.sqrt(np.diag(pcov))[1] +return popt[1], k_stderr +except (RuntimeError, ValueError): +return None, N
[llvm-branch-commits] [clang] [LifetimeSafety] Enhance benchmark script for ExpiredLoans analysis (PR #149577)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/149577 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Propagate loans using dataflow analysis (PR #147295)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/147295 >From dd2dd838f44ebeb6d45f75af2934159ee61b385b Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 6 Jul 2025 19:12:55 + Subject: [PATCH] [LifetimeSafety] Propagate loans using dataflow analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 255 +- .../Sema/warn-lifetime-safety-dataflow.cpp| 186 + 2 files changed, 440 insertions(+), 1 deletion(-) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 2c2309de90e26..cdbab31ac7a9c 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -482,7 +482,247 @@ class FactGenerator : public ConstStmtVisitor { }; // = // -// TODO: Run dataflow analysis to propagate loans, analyse and error reporting. +// The Dataflow Lattice +// = // + +// Using LLVM's immutable collections is efficient for dataflow analysis +// as it avoids deep copies during state transitions. +// TODO(opt): Consider using a bitset to represent the set of loans. +using LoanSet = llvm::ImmutableSet; +using OriginLoanMap = llvm::ImmutableMap; + +/// An object to hold the factories for immutable collections, ensuring +/// that all created states share the same underlying memory management. +struct LifetimeFactory { + OriginLoanMap::Factory OriginMapFact; + LoanSet::Factory LoanSetFact; + + LoanSet createLoanSet(LoanID LID) { +return LoanSetFact.add(LoanSetFact.getEmptySet(), LID); + } +}; + +/// LifetimeLattice represents the state of our analysis at a given program +/// point. It is an immutable object, and all operations produce a new +/// instance rather than modifying the existing one. +struct LifetimeLattice { + /// The map from an origin to the set of loans it contains. + /// TODO(opt): To reduce the lattice size, propagate origins of declarations, + /// not expressions, because expressions are not visible across blocks. + OriginLoanMap Origins = OriginLoanMap(nullptr); + + explicit LifetimeLattice(const OriginLoanMap &S) : Origins(S) {} + LifetimeLattice() = default; + + bool operator==(const LifetimeLattice &Other) const { +return Origins == Other.Origins; + } + bool operator!=(const LifetimeLattice &Other) const { +return !(*this == Other); + } + + LoanSet getLoans(OriginID OID, LifetimeFactory &Factory) const { +if (auto *Loans = Origins.lookup(OID)) + return *Loans; +return Factory.LoanSetFact.getEmptySet(); + } + + /// Computes the union of two lattices by performing a key-wise join of + /// their OriginLoanMaps. + // TODO(opt): This key-wise join is a performance bottleneck. A more + // efficient merge could be implemented using a Patricia Trie or HAMT + // instead of the current AVL-tree-based ImmutableMap. + LifetimeLattice join(const LifetimeLattice &Other, + LifetimeFactory &Factory) const { +/// Merge the smaller map into the larger one ensuring we iterate over the +/// smaller map. +if (Origins.getHeight() < Other.Origins.getHeight()) + return Other.join(*this, Factory); + +OriginLoanMap JoinedState = Origins; +// For each origin in the other map, union its loan set with ours. +for (const auto &Entry : Other.Origins) { + OriginID OID = Entry.first; + LoanSet OtherLoanSet = Entry.second; + JoinedState = Factory.OriginMapFact.add( + JoinedState, OID, + join(getLoans(OID, Factory), OtherLoanSet, Factory)); +} +return LifetimeLattice(JoinedState); + } + + LoanSet join(LoanSet a, LoanSet b, LifetimeFactory &Factory) const { +/// Merge the smaller set into the larger one ensuring we iterate over the +/// smaller set. +if (a.getHeight() < b.getHeight()) + std::swap(a, b); +LoanSet Result = a; +for (LoanID LID : b) { + /// TODO(opt): Profiling shows that this loop is a major performance + /// bottleneck. Investigate using a BitVector to represent the set of + /// loans for improved join performance. + Result = Factory.LoanSetFact.add(Result, LID); +} +return Result; + } + + void dump(llvm::raw_ostream &OS) const { +OS << "LifetimeLattice State:\n"; +if (Origins.isEmpty()) + OS << " \n"; +for (const auto &Entry : Origins) { + if (Entry.second.isEmpty()) +OS << " Origin " << Entry.first << " contains no loans\n"; + for (const LoanID &LID : Entry.second) +OS << " Origin " << Entry.first << " contains Loan " << LID << "\n"; +} + } +}; + +// = // +// The Transfer Function +// = // +class T
[llvm-branch-commits] [clang] [LifetimeSafety] Implement dataflow analysis for loan propagation (PR #147295)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/147295 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Propagate loans using dataflow analysis (PR #147295)
https://github.com/usx95 created https://github.com/llvm/llvm-project/pull/147295 None >From 2e4261b02b6230a8c79f01a673cc3030cfff3ea7 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 6 Jul 2025 19:12:55 + Subject: [PATCH 1/6] [LifetimeSafety] Propagate loans using dataflow analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 255 +- .../Sema/warn-lifetime-safety-dataflow.cpp| 186 + 2 files changed, 440 insertions(+), 1 deletion(-) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 3fe30e36ebd0f..7870352f0287a 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -491,7 +491,247 @@ class FactGenerator : public ConstStmtVisitor { }; // = // -// TODO: Run dataflow analysis to propagate loans, analyse and error reporting. +// The Dataflow Lattice +// = // + +// Using LLVM's immutable collections is efficient for dataflow analysis +// as it avoids deep copies during state transitions. +// TODO(opt): Consider using a bitset to represent the set of loans. +using LoanSet = llvm::ImmutableSet; +using OriginLoanMap = llvm::ImmutableMap; + +/// An object to hold the factories for immutable collections, ensuring +/// that all created states share the same underlying memory management. +struct LifetimeFactory { + OriginLoanMap::Factory OriginMapFact; + LoanSet::Factory LoanSetFact; + + LoanSet createLoanSet(LoanID LID) { +return LoanSetFact.add(LoanSetFact.getEmptySet(), LID); + } +}; + +/// LifetimeLattice represents the state of our analysis at a given program +/// point. It is an immutable object, and all operations produce a new +/// instance rather than modifying the existing one. +struct LifetimeLattice { + /// The map from an origin to the set of loans it contains. + /// TODO(opt): To reduce the lattice size, propagate origins of declarations, + /// not expressions, because expressions are not visible across blocks. + OriginLoanMap Origins = OriginLoanMap(nullptr); + + explicit LifetimeLattice(const OriginLoanMap &S) : Origins(S) {} + LifetimeLattice() = default; + + bool operator==(const LifetimeLattice &Other) const { +return Origins == Other.Origins; + } + bool operator!=(const LifetimeLattice &Other) const { +return !(*this == Other); + } + + LoanSet getLoans(OriginID OID, LifetimeFactory &Factory) const { +if (auto *Loans = Origins.lookup(OID)) + return *Loans; +return Factory.LoanSetFact.getEmptySet(); + } + + /// Computes the union of two lattices by performing a key-wise join of + /// their OriginLoanMaps. + // TODO(opt): This key-wise join is a performance bottleneck. A more + // efficient merge could be implemented using a Patricia Trie or HAMT + // instead of the current AVL-tree-based ImmutableMap. + LifetimeLattice join(const LifetimeLattice &Other, + LifetimeFactory &Factory) const { +/// Merge the smaller map into the larger one ensuring we iterate over the +/// smaller map. +if (Origins.getHeight() < Other.Origins.getHeight()) + return Other.join(*this, Factory); + +OriginLoanMap JoinedState = Origins; +// For each origin in the other map, union its loan set with ours. +for (const auto &Entry : Other.Origins) { + OriginID OID = Entry.first; + LoanSet OtherLoanSet = Entry.second; + JoinedState = Factory.OriginMapFact.add( + JoinedState, OID, + join(getLoans(OID, Factory), OtherLoanSet, Factory)); +} +return LifetimeLattice(JoinedState); + } + + LoanSet join(LoanSet a, LoanSet b, LifetimeFactory &Factory) const { +/// Merge the smaller set into the larger one ensuring we iterate over the +/// smaller set. +if (a.getHeight() < b.getHeight()) + std::swap(a, b); +LoanSet Result = a; +for (LoanID LID : b) { + /// TODO(opt): Profiling shows that this loop is a major performance + /// bottleneck. Investigate using a BitVector to represent the set of + /// loans for improved join performance. + Result = Factory.LoanSetFact.add(Result, LID); +} +return Result; + } + + void dump(llvm::raw_ostream &OS) const { +OS << "LifetimeLattice State:\n"; +if (Origins.isEmpty()) + OS << " \n"; +for (const auto &Entry : Origins) { + if (Entry.second.isEmpty()) +OS << " Origin " << Entry.first << " contains no loans\n"; + for (const LoanID &LID : Entry.second) +OS << " Origin " << Entry.first << " contains Loan " << LID << "\n"; +} + } +}; + +// = // +// The Transfer Function +// = /
[llvm-branch-commits] [clang] [LifetimeSafety] Propagate loans using dataflow analysis (PR #147295)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/147295 >From 2e4261b02b6230a8c79f01a673cc3030cfff3ea7 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 6 Jul 2025 19:12:55 + Subject: [PATCH 1/6] [LifetimeSafety] Propagate loans using dataflow analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 255 +- .../Sema/warn-lifetime-safety-dataflow.cpp| 186 + 2 files changed, 440 insertions(+), 1 deletion(-) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 3fe30e36ebd0f..7870352f0287a 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -491,7 +491,247 @@ class FactGenerator : public ConstStmtVisitor { }; // = // -// TODO: Run dataflow analysis to propagate loans, analyse and error reporting. +// The Dataflow Lattice +// = // + +// Using LLVM's immutable collections is efficient for dataflow analysis +// as it avoids deep copies during state transitions. +// TODO(opt): Consider using a bitset to represent the set of loans. +using LoanSet = llvm::ImmutableSet; +using OriginLoanMap = llvm::ImmutableMap; + +/// An object to hold the factories for immutable collections, ensuring +/// that all created states share the same underlying memory management. +struct LifetimeFactory { + OriginLoanMap::Factory OriginMapFact; + LoanSet::Factory LoanSetFact; + + LoanSet createLoanSet(LoanID LID) { +return LoanSetFact.add(LoanSetFact.getEmptySet(), LID); + } +}; + +/// LifetimeLattice represents the state of our analysis at a given program +/// point. It is an immutable object, and all operations produce a new +/// instance rather than modifying the existing one. +struct LifetimeLattice { + /// The map from an origin to the set of loans it contains. + /// TODO(opt): To reduce the lattice size, propagate origins of declarations, + /// not expressions, because expressions are not visible across blocks. + OriginLoanMap Origins = OriginLoanMap(nullptr); + + explicit LifetimeLattice(const OriginLoanMap &S) : Origins(S) {} + LifetimeLattice() = default; + + bool operator==(const LifetimeLattice &Other) const { +return Origins == Other.Origins; + } + bool operator!=(const LifetimeLattice &Other) const { +return !(*this == Other); + } + + LoanSet getLoans(OriginID OID, LifetimeFactory &Factory) const { +if (auto *Loans = Origins.lookup(OID)) + return *Loans; +return Factory.LoanSetFact.getEmptySet(); + } + + /// Computes the union of two lattices by performing a key-wise join of + /// their OriginLoanMaps. + // TODO(opt): This key-wise join is a performance bottleneck. A more + // efficient merge could be implemented using a Patricia Trie or HAMT + // instead of the current AVL-tree-based ImmutableMap. + LifetimeLattice join(const LifetimeLattice &Other, + LifetimeFactory &Factory) const { +/// Merge the smaller map into the larger one ensuring we iterate over the +/// smaller map. +if (Origins.getHeight() < Other.Origins.getHeight()) + return Other.join(*this, Factory); + +OriginLoanMap JoinedState = Origins; +// For each origin in the other map, union its loan set with ours. +for (const auto &Entry : Other.Origins) { + OriginID OID = Entry.first; + LoanSet OtherLoanSet = Entry.second; + JoinedState = Factory.OriginMapFact.add( + JoinedState, OID, + join(getLoans(OID, Factory), OtherLoanSet, Factory)); +} +return LifetimeLattice(JoinedState); + } + + LoanSet join(LoanSet a, LoanSet b, LifetimeFactory &Factory) const { +/// Merge the smaller set into the larger one ensuring we iterate over the +/// smaller set. +if (a.getHeight() < b.getHeight()) + std::swap(a, b); +LoanSet Result = a; +for (LoanID LID : b) { + /// TODO(opt): Profiling shows that this loop is a major performance + /// bottleneck. Investigate using a BitVector to represent the set of + /// loans for improved join performance. + Result = Factory.LoanSetFact.add(Result, LID); +} +return Result; + } + + void dump(llvm::raw_ostream &OS) const { +OS << "LifetimeLattice State:\n"; +if (Origins.isEmpty()) + OS << " \n"; +for (const auto &Entry : Origins) { + if (Entry.second.isEmpty()) +OS << " Origin " << Entry.first << " contains no loans\n"; + for (const LoanID &LID : Entry.second) +OS << " Origin " << Entry.first << " contains Loan " << LID << "\n"; +} + } +}; + +// = // +// The Transfer Function +// = // +cla
[llvm-branch-commits] [clang] [LifetimeSafety] Implement dataflow analysis for loan propagation (PR #147295)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/147295 >From e870b040c4ef29b7ca2e50c1fc0ab5a2446f5cf6 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 6 Jul 2025 19:12:55 + Subject: [PATCH] [LifetimeSafety] Propagate loans using dataflow analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 258 +- .../Sema/warn-lifetime-safety-dataflow.cpp| 186 + 2 files changed, 443 insertions(+), 1 deletion(-) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 2c2309de90e26..e881e592ef59f 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -13,7 +13,10 @@ #include "clang/Analysis/Analyses/PostOrderCFGView.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" +#include "clang/Analysis/FlowSensitive/DataflowWorklist.h" #include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/ImmutableSet.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Debug.h" @@ -482,7 +485,247 @@ class FactGenerator : public ConstStmtVisitor { }; // = // -// TODO: Run dataflow analysis to propagate loans, analyse and error reporting. +// The Dataflow Lattice +// = // + +// Using LLVM's immutable collections is efficient for dataflow analysis +// as it avoids deep copies during state transitions. +// TODO(opt): Consider using a bitset to represent the set of loans. +using LoanSet = llvm::ImmutableSet; +using OriginLoanMap = llvm::ImmutableMap; + +/// An object to hold the factories for immutable collections, ensuring +/// that all created states share the same underlying memory management. +struct LifetimeFactory { + OriginLoanMap::Factory OriginMapFact; + LoanSet::Factory LoanSetFact; + + LoanSet createLoanSet(LoanID LID) { +return LoanSetFact.add(LoanSetFact.getEmptySet(), LID); + } +}; + +/// LifetimeLattice represents the state of our analysis at a given program +/// point. It is an immutable object, and all operations produce a new +/// instance rather than modifying the existing one. +struct LifetimeLattice { + /// The map from an origin to the set of loans it contains. + /// TODO(opt): To reduce the lattice size, propagate origins of declarations, + /// not expressions, because expressions are not visible across blocks. + OriginLoanMap Origins = OriginLoanMap(nullptr); + + explicit LifetimeLattice(const OriginLoanMap &S) : Origins(S) {} + LifetimeLattice() = default; + + bool operator==(const LifetimeLattice &Other) const { +return Origins == Other.Origins; + } + bool operator!=(const LifetimeLattice &Other) const { +return !(*this == Other); + } + + LoanSet getLoans(OriginID OID, LifetimeFactory &Factory) const { +if (auto *Loans = Origins.lookup(OID)) + return *Loans; +return Factory.LoanSetFact.getEmptySet(); + } + + /// Computes the union of two lattices by performing a key-wise join of + /// their OriginLoanMaps. + // TODO(opt): This key-wise join is a performance bottleneck. A more + // efficient merge could be implemented using a Patricia Trie or HAMT + // instead of the current AVL-tree-based ImmutableMap. + LifetimeLattice join(const LifetimeLattice &Other, + LifetimeFactory &Factory) const { +/// Merge the smaller map into the larger one ensuring we iterate over the +/// smaller map. +if (Origins.getHeight() < Other.Origins.getHeight()) + return Other.join(*this, Factory); + +OriginLoanMap JoinedState = Origins; +// For each origin in the other map, union its loan set with ours. +for (const auto &Entry : Other.Origins) { + OriginID OID = Entry.first; + LoanSet OtherLoanSet = Entry.second; + JoinedState = Factory.OriginMapFact.add( + JoinedState, OID, + join(getLoans(OID, Factory), OtherLoanSet, Factory)); +} +return LifetimeLattice(JoinedState); + } + + LoanSet join(LoanSet a, LoanSet b, LifetimeFactory &Factory) const { +/// Merge the smaller set into the larger one ensuring we iterate over the +/// smaller set. +if (a.getHeight() < b.getHeight()) + std::swap(a, b); +LoanSet Result = a; +for (LoanID LID : b) { + /// TODO(opt): Profiling shows that this loop is a major performance + /// bottleneck. Investigate using a BitVector to represent the set of + /// loans for improved join performance. + Result = Factory.LoanSetFact.add(Result, LID); +} +return Result; + } + + void dump(llvm::raw_ostream &OS) const { +OS << "LifetimeLattice State:\n"; +if (Origins.isEmpty()) + OS << " \n"; +for (const auto &Entry : Origins) { + if (Entry.second.isEmpty()) +OS
[llvm-branch-commits] [clang] [LifetimeSafety] Implement dataflow analysis for loan propagation (PR #147295)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/147295 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Implement dataflow analysis for loan propagation (PR #147295)
https://github.com/usx95 ready_for_review https://github.com/llvm/llvm-project/pull/147295 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add script performance benchmarking (PR #147315)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/147315 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] Users/usx95/lifetime safety benchmarking (PR #147315)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/147315 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add script performance benchmarking (PR #147315)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/147315 >From 0fbfd74d23b6cd26ef0480f7b9061b2f4a745338 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 7 Jul 2025 15:13:00 + Subject: [PATCH 1/2] [LifetimeSafety] Add script performance benchmarking --- clang/lib/Analysis/LifetimeSafety.cpp | 7 +- .../Analysis/lifetime_safety/benchmark.py | 215 ++ 2 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 clang/test/Analysis/lifetime_safety/benchmark.py diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index e881e592ef59f..1c83b5051bad1 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -151,7 +151,12 @@ class OriginManager { OriginID get(const ValueDecl &D) { auto It = DeclToOriginID.find(&D); -assert(It != DeclToOriginID.end()); +// TODO: This should be an assert(It != ExprToOriginID.end()). The current +// implementation falls back to getOrCreate to avoid crashing on +// yet-unhandled pointer expressions, creating an empty origin for them. +if (It == DeclToOriginID.end()) + return getOrCreate(D); + return It->second; } diff --git a/clang/test/Analysis/lifetime_safety/benchmark.py b/clang/test/Analysis/lifetime_safety/benchmark.py new file mode 100644 index 0..ddf32e192de17 --- /dev/null +++ b/clang/test/Analysis/lifetime_safety/benchmark.py @@ -0,0 +1,215 @@ +import sys +import argparse +import subprocess +import tempfile +import json +import os +from datetime import datetime +import numpy as np +from scipy.optimize import curve_fit +from scipy.stats import t + +def generate_cpp_cycle_test(n: int) -> str: +""" +Generates a C++ code snippet with a specified number of pointers in a cycle. +""" +if n <= 0: +return "// Number of variables must be positive." + +cpp_code = "struct MyObj { int id; ~MyObj() {} };\n\n" +cpp_code += f"void long_cycle_{n}(bool condition) {{\n" +for i in range(1, n + 1): +cpp_code += f" MyObj v{i}{{1}};\n" +cpp_code += "\n" +for i in range(1, n + 1): +cpp_code += f" MyObj* p{i} = &v{i};\n" + +cpp_code += "\n while (condition) {\n" +if n > 0: +cpp_code += f"MyObj* temp = p1;\n" +for i in range(1, n): +cpp_code += f"p{i} = p{i+1};\n" +cpp_code += f"p{n} = temp;\n" +cpp_code += " }\n}\n" +cpp_code += f"\nint main() {{ long_cycle_{n}(false); return 0; }}\n" +return cpp_code + +def generate_cpp_merge_test(n: int) -> str: +""" +Generates a C++ code snippet with N independent conditional assignments. +""" +if n <= 0: +return "// Number of variables must be positive." + +cpp_code = "struct MyObj { int id; ~MyObj() {} };\n\n" +cpp_code += f"void conditional_merges_{n}(bool condition) {{\n" +decls = [f"v{i}" for i in range(1, n + 1)] +cpp_code += f" MyObj {', '.join(decls)};\n" +ptr_decls = [f"*p{i} = nullptr" for i in range(1, n + 1)] +cpp_code += f" MyObj {', '.join(ptr_decls)};\n\n" + +for i in range(1, n + 1): +cpp_code += f" if(condition) {{ p{i} = &v{i}; }}\n" + +cpp_code += "}\n" +cpp_code += f"\nint main() {{ conditional_merges_{n}(false); return 0; }}\n" +return cpp_code + +def analyze_trace_file(trace_path: str) -> tuple[float, float]: +""" +Parses the -ftime-trace JSON output to find durations. + +Returns: +A tuple of (lifetime_analysis_duration_us, total_clang_duration_us). +""" +lifetime_duration = 0.0 +total_duration = 0.0 +try: +with open(trace_path, 'r') as f: +trace_data = json.load(f) +for event in trace_data.get('traceEvents', []): +if event.get('name') == 'LifetimeAnalysis': +lifetime_duration += float(event.get('dur', 0)) +if event.get('name') == 'ExecuteCompiler': +total_duration += float(event.get('dur', 0)) + +except (IOError, json.JSONDecodeError) as e: +print(f"Error reading or parsing trace file {trace_path}: {e}", file=sys.stderr) +return 0.0, 0.0 +return lifetime_duration, total_duration + +def power_law(n, c, k): +"""Represents the power law function: y = c * n^k""" +return c * np.power(n, k) + +def human_readable_time(ms: float) -> str: +"""Converts milliseconds to a human-readable string (ms or s).""" +if ms >= 1000: +return f"{ms / 1000:.2f} s" +return f"{ms:.2f} ms" + +def generate_markdown_report(results: dict) -> str: +"""Generates a Markdown-formatted report from the benchmark results.""" +report = [] +timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S %Z") +report.append(f"# Lifetime Analysis Performance Report") +report.append(f"> Generated on: {timestamp}") +report.append("\n---\n") + +for test_typ
[llvm-branch-commits] [clang] [LifetimeSafety] Add script for performance benchmarking (PR #147315)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/147315 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add script for performance benchmarking (PR #147315)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/147315 >From 014d81d9da31df3cf46bd8fc5f7cb470b3271b8e Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 7 Jul 2025 15:13:00 + Subject: [PATCH] [LifetimeSafety] Add script performance benchmarking --- clang/lib/Analysis/LifetimeSafety.cpp | 7 +- .../Analysis/LifetimeSafety/CMakeLists.txt| 49 +++ .../test/Analysis/LifetimeSafety/benchmark.py | 308 ++ .../Analysis/LifetimeSafety/requirements.txt | 2 + clang/test/CMakeLists.txt | 2 + 5 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 clang/test/Analysis/LifetimeSafety/CMakeLists.txt create mode 100644 clang/test/Analysis/LifetimeSafety/benchmark.py create mode 100644 clang/test/Analysis/LifetimeSafety/requirements.txt diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index e881e592ef59f..1c83b5051bad1 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -151,7 +151,12 @@ class OriginManager { OriginID get(const ValueDecl &D) { auto It = DeclToOriginID.find(&D); -assert(It != DeclToOriginID.end()); +// TODO: This should be an assert(It != ExprToOriginID.end()). The current +// implementation falls back to getOrCreate to avoid crashing on +// yet-unhandled pointer expressions, creating an empty origin for them. +if (It == DeclToOriginID.end()) + return getOrCreate(D); + return It->second; } diff --git a/clang/test/Analysis/LifetimeSafety/CMakeLists.txt b/clang/test/Analysis/LifetimeSafety/CMakeLists.txt new file mode 100644 index 0..ce37a29655668 --- /dev/null +++ b/clang/test/Analysis/LifetimeSafety/CMakeLists.txt @@ -0,0 +1,49 @@ +# = +# Lifetime Analysis Benchmarking Target +# = +# This target allows running performance benchmarks for the clang lifetime analysis +# using a Python script (with managed dependencies). + +find_package(Python3 COMPONENTS Interpreter REQUIRED) + +# Define paths for the virtual environment and requirements file. +set(LIFETIME_BENCHMARK_SCRIPT + "${CMAKE_CURRENT_SOURCE_DIR}/benchmark.py") +set(LIFETIME_BENCHMARK_VENV_DIR "${CMAKE_CURRENT_BINARY_DIR}/benchmark-venv") +set(LIFETIME_BENCHMARK_REQUIREMENTS + "${CMAKE_CURRENT_SOURCE_DIR}/requirements.txt") +set(LIFETIME_BENCHMARK_OUTPUT_DIR + "${CMAKE_CURRENT_BINARY_DIR}/benchmark_results") + + +if(EXISTS ${LIFETIME_BENCHMARK_SCRIPT} AND EXISTS ${LIFETIME_BENCHMARK_REQUIREMENTS}) + + # Set up the virtual environment and install packages + add_custom_command( +OUTPUT ${LIFETIME_BENCHMARK_VENV_DIR}/pyvenv.cfg +COMMAND ${Python3_EXECUTABLE} -m venv ${LIFETIME_BENCHMARK_VENV_DIR} +COMMAND ${LIFETIME_BENCHMARK_VENV_DIR}/bin/python -m pip install -r ${LIFETIME_BENCHMARK_REQUIREMENTS} +DEPENDS ${LIFETIME_BENCHMARK_REQUIREMENTS} +COMMENT "Creating Python virtual environment and installing dependencies for benchmark..." + ) + add_custom_target(benchmark_venv_setup +DEPENDS ${LIFETIME_BENCHMARK_VENV_DIR}/pyvenv.cfg + ) + + # Main benchmark target + add_custom_target(benchmark_lifetime_safety_analysis +COMMAND ${LIFETIME_BENCHMARK_VENV_DIR}/bin/python ${LIFETIME_BENCHMARK_SCRIPT} +--clang-binary ${LLVM_BINARY_DIR}/bin/clang +--output-dir ${LIFETIME_BENCHMARK_OUTPUT_DIR} + +DEPENDS clang benchmark_venv_setup + +# Display the output directly in the console. +USES_TERMINAL + +COMMENT "Running Lifetime Analysis performance benchmarks..." + ) + + set_target_properties(benchmark_lifetime_safety_analysis +PROPERTIES FOLDER "Clang/Benchmarks") +endif() diff --git a/clang/test/Analysis/LifetimeSafety/benchmark.py b/clang/test/Analysis/LifetimeSafety/benchmark.py new file mode 100644 index 0..10ffa6d7dc2be --- /dev/null +++ b/clang/test/Analysis/LifetimeSafety/benchmark.py @@ -0,0 +1,308 @@ +import sys +import argparse +import subprocess +import tempfile +import json +import os +from datetime import datetime +import numpy as np +from scipy.optimize import curve_fit +from scipy.stats import t + + +def generate_cpp_cycle_test(n: int) -> str: +""" +Generates a C++ code snippet with a specified number of pointers in a cycle. +Creates a while loop that rotates N pointers. +This pattern tests the convergence speed of the dataflow analysis when +reaching its fixed point. + +Example: +struct MyObj { int id; ~MyObj() {} }; + +void long_cycle_4(bool condition) { +MyObj v1{1}; +MyObj v2{1}; +MyObj v3{1}; +MyObj v4{1}; + +MyObj* p1 = &v1; +MyObj* p2 = &v2; +MyObj* p3 = &v3; +MyObj* p4 = &v4; + +w
[llvm-branch-commits] [clang] [LifetimeSafety] Add script for performance benchmarking (PR #147315)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/147315 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add script for performance benchmarking (PR #147315)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/147315 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add script for performance benchmarking (PR #147315)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/147315 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add script for performance benchmarking (PR #147315)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/147315 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add script for performance benchmarking (PR #147315)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/147315 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Implement dataflow analysis for loan propagation (PR #147295)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/147295 >From 8cc690f5cae252e744dc7244dc701929a44a8799 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 6 Jul 2025 19:12:55 + Subject: [PATCH] [LifetimeSafety] Propagate loans using dataflow analysis --- clang/lib/Analysis/LifetimeSafety.cpp | 258 +- .../Sema/warn-lifetime-safety-dataflow.cpp| 186 + 2 files changed, 443 insertions(+), 1 deletion(-) diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp index 366e73e803146..1c83b5051bad1 100644 --- a/clang/lib/Analysis/LifetimeSafety.cpp +++ b/clang/lib/Analysis/LifetimeSafety.cpp @@ -13,7 +13,10 @@ #include "clang/Analysis/Analyses/PostOrderCFGView.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" +#include "clang/Analysis/FlowSensitive/DataflowWorklist.h" #include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/ImmutableSet.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Debug.h" @@ -487,7 +490,247 @@ class FactGenerator : public ConstStmtVisitor { }; // = // -// TODO: Run dataflow analysis to propagate loans, analyse and error reporting. +// The Dataflow Lattice +// = // + +// Using LLVM's immutable collections is efficient for dataflow analysis +// as it avoids deep copies during state transitions. +// TODO(opt): Consider using a bitset to represent the set of loans. +using LoanSet = llvm::ImmutableSet; +using OriginLoanMap = llvm::ImmutableMap; + +/// An object to hold the factories for immutable collections, ensuring +/// that all created states share the same underlying memory management. +struct LifetimeFactory { + OriginLoanMap::Factory OriginMapFact; + LoanSet::Factory LoanSetFact; + + LoanSet createLoanSet(LoanID LID) { +return LoanSetFact.add(LoanSetFact.getEmptySet(), LID); + } +}; + +/// LifetimeLattice represents the state of our analysis at a given program +/// point. It is an immutable object, and all operations produce a new +/// instance rather than modifying the existing one. +struct LifetimeLattice { + /// The map from an origin to the set of loans it contains. + /// TODO(opt): To reduce the lattice size, propagate origins of declarations, + /// not expressions, because expressions are not visible across blocks. + OriginLoanMap Origins = OriginLoanMap(nullptr); + + explicit LifetimeLattice(const OriginLoanMap &S) : Origins(S) {} + LifetimeLattice() = default; + + bool operator==(const LifetimeLattice &Other) const { +return Origins == Other.Origins; + } + bool operator!=(const LifetimeLattice &Other) const { +return !(*this == Other); + } + + LoanSet getLoans(OriginID OID, LifetimeFactory &Factory) const { +if (auto *Loans = Origins.lookup(OID)) + return *Loans; +return Factory.LoanSetFact.getEmptySet(); + } + + /// Computes the union of two lattices by performing a key-wise join of + /// their OriginLoanMaps. + // TODO(opt): This key-wise join is a performance bottleneck. A more + // efficient merge could be implemented using a Patricia Trie or HAMT + // instead of the current AVL-tree-based ImmutableMap. + LifetimeLattice join(const LifetimeLattice &Other, + LifetimeFactory &Factory) const { +/// Merge the smaller map into the larger one ensuring we iterate over the +/// smaller map. +if (Origins.getHeight() < Other.Origins.getHeight()) + return Other.join(*this, Factory); + +OriginLoanMap JoinedState = Origins; +// For each origin in the other map, union its loan set with ours. +for (const auto &Entry : Other.Origins) { + OriginID OID = Entry.first; + LoanSet OtherLoanSet = Entry.second; + JoinedState = Factory.OriginMapFact.add( + JoinedState, OID, + join(getLoans(OID, Factory), OtherLoanSet, Factory)); +} +return LifetimeLattice(JoinedState); + } + + LoanSet join(LoanSet a, LoanSet b, LifetimeFactory &Factory) const { +/// Merge the smaller set into the larger one ensuring we iterate over the +/// smaller set. +if (a.getHeight() < b.getHeight()) + std::swap(a, b); +LoanSet Result = a; +for (LoanID LID : b) { + /// TODO(opt): Profiling shows that this loop is a major performance + /// bottleneck. Investigate using a BitVector to represent the set of + /// loans for improved join performance. + Result = Factory.LoanSetFact.add(Result, LID); +} +return Result; + } + + void dump(llvm::raw_ostream &OS) const { +OS << "LifetimeLattice State:\n"; +if (Origins.isEmpty()) + OS << " \n"; +for (const auto &Entry : Origins) { + if (Entry.second.isEmpty()) +OS
[llvm-branch-commits] [clang] [LifetimeSafety] Add script for performance benchmarking (PR #147315)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/147315 >From e8687c3d58a9da0874814846a1dbbaf173cdbf34 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 7 Jul 2025 15:13:00 + Subject: [PATCH] [LifetimeSafety] Add script performance benchmarking --- .../Analysis/LifetimeSafety/CMakeLists.txt| 49 +++ .../test/Analysis/LifetimeSafety/benchmark.py | 307 ++ .../Analysis/LifetimeSafety/requirements.txt | 2 + clang/test/CMakeLists.txt | 2 + 4 files changed, 360 insertions(+) create mode 100644 clang/test/Analysis/LifetimeSafety/CMakeLists.txt create mode 100644 clang/test/Analysis/LifetimeSafety/benchmark.py create mode 100644 clang/test/Analysis/LifetimeSafety/requirements.txt diff --git a/clang/test/Analysis/LifetimeSafety/CMakeLists.txt b/clang/test/Analysis/LifetimeSafety/CMakeLists.txt new file mode 100644 index 0..ce37a29655668 --- /dev/null +++ b/clang/test/Analysis/LifetimeSafety/CMakeLists.txt @@ -0,0 +1,49 @@ +# = +# Lifetime Analysis Benchmarking Target +# = +# This target allows running performance benchmarks for the clang lifetime analysis +# using a Python script (with managed dependencies). + +find_package(Python3 COMPONENTS Interpreter REQUIRED) + +# Define paths for the virtual environment and requirements file. +set(LIFETIME_BENCHMARK_SCRIPT + "${CMAKE_CURRENT_SOURCE_DIR}/benchmark.py") +set(LIFETIME_BENCHMARK_VENV_DIR "${CMAKE_CURRENT_BINARY_DIR}/benchmark-venv") +set(LIFETIME_BENCHMARK_REQUIREMENTS + "${CMAKE_CURRENT_SOURCE_DIR}/requirements.txt") +set(LIFETIME_BENCHMARK_OUTPUT_DIR + "${CMAKE_CURRENT_BINARY_DIR}/benchmark_results") + + +if(EXISTS ${LIFETIME_BENCHMARK_SCRIPT} AND EXISTS ${LIFETIME_BENCHMARK_REQUIREMENTS}) + + # Set up the virtual environment and install packages + add_custom_command( +OUTPUT ${LIFETIME_BENCHMARK_VENV_DIR}/pyvenv.cfg +COMMAND ${Python3_EXECUTABLE} -m venv ${LIFETIME_BENCHMARK_VENV_DIR} +COMMAND ${LIFETIME_BENCHMARK_VENV_DIR}/bin/python -m pip install -r ${LIFETIME_BENCHMARK_REQUIREMENTS} +DEPENDS ${LIFETIME_BENCHMARK_REQUIREMENTS} +COMMENT "Creating Python virtual environment and installing dependencies for benchmark..." + ) + add_custom_target(benchmark_venv_setup +DEPENDS ${LIFETIME_BENCHMARK_VENV_DIR}/pyvenv.cfg + ) + + # Main benchmark target + add_custom_target(benchmark_lifetime_safety_analysis +COMMAND ${LIFETIME_BENCHMARK_VENV_DIR}/bin/python ${LIFETIME_BENCHMARK_SCRIPT} +--clang-binary ${LLVM_BINARY_DIR}/bin/clang +--output-dir ${LIFETIME_BENCHMARK_OUTPUT_DIR} + +DEPENDS clang benchmark_venv_setup + +# Display the output directly in the console. +USES_TERMINAL + +COMMENT "Running Lifetime Analysis performance benchmarks..." + ) + + set_target_properties(benchmark_lifetime_safety_analysis +PROPERTIES FOLDER "Clang/Benchmarks") +endif() diff --git a/clang/test/Analysis/LifetimeSafety/benchmark.py b/clang/test/Analysis/LifetimeSafety/benchmark.py new file mode 100644 index 0..9d5f36c51b9ee --- /dev/null +++ b/clang/test/Analysis/LifetimeSafety/benchmark.py @@ -0,0 +1,307 @@ +import sys +import argparse +import subprocess +import tempfile +import json +import os +from datetime import datetime +import numpy as np +from scipy.optimize import curve_fit +from scipy.stats import t + + +def generate_cpp_cycle_test(n: int) -> str: +""" +Generates a C++ code snippet with a specified number of pointers in a cycle. +Creates a while loop that rotates N pointers. +This pattern tests the convergence speed of the dataflow analysis when +reaching its fixed point. + +Example: +struct MyObj { int id; ~MyObj() {} }; + +void long_cycle_4(bool condition) { +MyObj v1{1}; +MyObj v2{1}; +MyObj v3{1}; +MyObj v4{1}; + +MyObj* p1 = &v1; +MyObj* p2 = &v2; +MyObj* p3 = &v3; +MyObj* p4 = &v4; + +while (condition) { +MyObj* temp = p1; +p1 = p2; +p2 = p3; +p3 = p4; +p4 = temp; +} +} +""" +if n <= 0: +return "// Number of variables must be positive." + +cpp_code = "struct MyObj { int id; ~MyObj() {} };\n\n" +cpp_code += f"void long_cycle_{n}(bool condition) {{\n" +for i in range(1, n + 1): +cpp_code += f" MyObj v{i}{{1}};\n" +cpp_code += "\n" +for i in range(1, n + 1): +cpp_code += f" MyObj* p{i} = &v{i};\n" + +cpp_code += "\n while (condition) {\n" +if n > 0: +cpp_code += f"MyObj* temp = p1;\n" +for i in range(1, n): +cpp_code += f"p{i} = p{i+1};\n" +cpp_code += f"
[llvm-branch-commits] [clang] [LifetimeSafety] Add script for performance benchmarking (PR #147315)
https://github.com/usx95 ready_for_review https://github.com/llvm/llvm-project/pull/147315 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
[llvm-branch-commits] [clang] [LifetimeSafety] Add script for performance benchmarking (PR #147315)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/147315 ___ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits