diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp index 674099dd7e1f0..6bad9a93a3016 100644 --- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -67,9 +67,10 @@ class BindingKey { isa(r)) && "Not a base"); } -public: +public: bool isDirect() const { return P.getInt() & Direct; } + bool isDefault() const { return !isDirect(); } bool hasSymbolicOffset() const { return P.getInt() & Symbolic; } const MemRegion *getRegion() const { return P.getPointer(); } @@ -232,27 +233,86 @@ class RegionBindingsRef : public llvm::ImmutableMapRef StringifyCache; + auto ToString = [&StringifyCache](const MemRegion *R) { + auto [Place, Inserted] = StringifyCache.try_emplace(R); + if (!Inserted) + return Place->second; + std::string Res; + raw_string_ostream OS(Res); + OS << R; + Place->second = Res; + return Res; + }; + + using Cluster = + std::pair>; + using Binding = std::pair; + + const auto MemSpaceBeforeRegionName = [&ToString](const Cluster *L, + const Cluster *R) { + if (isa(L->first) && !isa(R->first)) + return true; + if (!isa(L->first) && isa(R->first)) + return false; + return ToString(L->first) < ToString(R->first); + }; + + const auto SymbolicBeforeOffset = [&ToString](const BindingKey &L, + const BindingKey &R) { + if (L.hasSymbolicOffset() && !R.hasSymbolicOffset()) + return true; + if (!L.hasSymbolicOffset() && R.hasSymbolicOffset()) + return false; + if (L.hasSymbolicOffset() && R.hasSymbolicOffset()) + return ToString(L.getRegion()) < ToString(R.getRegion()); + return L.getOffset() < R.getOffset(); + }; + + const auto DefaultBindingBeforeDirectBindings = + [&SymbolicBeforeOffset](const Binding *LPtr, const Binding *RPtr) { + const BindingKey &L = LPtr->first; + const BindingKey &R = RPtr->first; + if (L.isDefault() && !R.isDefault()) + return true; + if (!L.isDefault() && R.isDefault()) + return false; + assert(L.isDefault() == R.isDefault()); + return SymbolicBeforeOffset(L, R); + }; + + const auto AddrOf = [](const auto &Item) { return &Item; }; + + std::vector SortedClusters; + SortedClusters.reserve(std::distance(begin(), end())); + append_range(SortedClusters, map_range(*this, AddrOf)); + llvm::sort(SortedClusters, MemSpaceBeforeRegionName); + + for (auto [Idx, C] : llvm::enumerate(SortedClusters)) { + const auto &[BaseRegion, Bindings] = *C; Indent(Out, Space, IsDot) - << "{ \"cluster\": \"" << I.getKey() << "\", \"pointer\": \"" - << (const void *)I.getKey() << "\", \"items\": [" << NL; + << "{ \"cluster\": \"" << BaseRegion << "\", \"pointer\": \"" + << (const void *)BaseRegion << "\", \"items\": [" << NL; + + std::vector SortedBindings; + SortedBindings.reserve(std::distance(Bindings.begin(), Bindings.end())); + append_range(SortedBindings, map_range(Bindings, AddrOf)); + llvm::sort(SortedBindings, DefaultBindingBeforeDirectBindings); ++Space; - const ClusterBindings &CB = I.getData(); - for (ClusterBindings::iterator CI = CB.begin(), CE = CB.end(); CI != CE; - ++CI) { - Indent(Out, Space, IsDot) << "{ " << CI.getKey() << ", \"value\": "; - CI.getData().printJson(Out, /*AddQuotes=*/true); + for (auto [Idx, B] : llvm::enumerate(SortedBindings)) { + const auto &[Key, Value] = *B; + Indent(Out, Space, IsDot) << "{ " << Key << ", \"value\": "; + Value.printJson(Out, /*AddQuotes=*/true); Out << " }"; - if (std::next(CI) != CE) + if (Idx != SortedBindings.size() - 1) Out << ','; Out << NL; } - --Space; Indent(Out, Space, IsDot) << "]}"; - if (std::next(I) != E) + if (Idx != SortedClusters.size() - 1) Out << ','; Out << NL; } diff --git a/clang/test/Analysis/store-dump-orders.cpp b/clang/test/Analysis/store-dump-orders.cpp new file mode 100644 index 0000000000000..d99f581f00fe1 --- /dev/null +++ b/clang/test/Analysis/store-dump-orders.cpp @@ -0,0 +1,57 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s 2>&1 | FileCheck %s +// expected-no-diagnostics + +void clang_analyzer_printState(); + +struct Member { + int large[10]; +}; +Member getMember(); + +struct Class { + Member m; + int first; + int second; + int third; +}; + + +void test_output(int n) { + Class objsecond; + objsecond.m.large[n] = 20; + + Class objfirst; + + objfirst.m = getMember(); + objfirst.second = 2; + objfirst.third = 3; + objfirst.first = 1; + + clang_analyzer_printState(); + // Default binding is before any direct bindings. + // Direct bindings are increasing by offset. + // Global memory space clusters come before any other clusters. + // Otherwise, Clusters are in alphabetical order. + + // CHECK: "store": { "pointer": "0x{{[0-9a-f]+}}", "items": [ + // CHECK-NEXT: { "cluster": "GlobalInternalSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [ + // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "conj_$ + // CHECK-NEXT: ]}, + // CHECK-NEXT: { "cluster": "GlobalSystemSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [ + // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "conj_$ + // CHECK-NEXT: ]}, + // CHECK-NEXT: { "cluster": "objfirst", "pointer": "0x{{[0-9a-f]+}}", "items": [ + // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "lazyCompoundVal + // CHECK-NEXT: { "kind": "Direct", "offset": 320, "value": "1 S32b" }, + // CHECK-NEXT: { "kind": "Direct", "offset": 352, "value": "2 S32b" }, + // CHECK-NEXT: { "kind": "Direct", "offset": 384, "value": "3 S32b" } + // CHECK-NEXT: ]}, + // CHECK-NEXT: { "cluster": "objsecond", "pointer": "0x{{[0-9a-f]+}}", "items": [ + // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "Unknown" }, + // CHECK-NEXT: { "kind": "Direct", "offset": null, "value": "20 S32b" } + // CHECK-NEXT: ]} + // CHECK-NEXT: ]}, + + (void)objfirst; + (void)objsecond; +}