Skip to content

Commit 26f0cfa

Browse files
committed
[analyzer][NFC] Make RegionStore dumps deterministic
Dump the memory space clusters before the other clusters, in alphabetical order. Then default bindings over direct bindings, and if any has symbolic offset, then those should come before the ones with concrete offsets. In theory, we should either have a symbolic offset OR concrete offsets, but never both at the same time.
1 parent 1d41543 commit 26f0cfa

File tree

1 file changed

+73
-13
lines changed

1 file changed

+73
-13
lines changed

clang/lib/StaticAnalyzer/Core/RegionStore.cpp

Lines changed: 73 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,10 @@ class BindingKey {
6767
isa<ObjCIvarRegion, CXXDerivedObjectRegion>(r)) &&
6868
"Not a base");
6969
}
70-
public:
7170

71+
public:
7272
bool isDirect() const { return P.getInt() & Direct; }
73+
bool isDefault() const { return !isDirect(); }
7374
bool hasSymbolicOffset() const { return P.getInt() & Symbolic; }
7475

7576
const MemRegion *getRegion() const { return P.getPointer(); }
@@ -232,27 +233,86 @@ class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
232233

233234
void printJson(raw_ostream &Out, const char *NL = "\n",
234235
unsigned int Space = 0, bool IsDot = false) const {
235-
for (iterator I = begin(), E = end(); I != E; ++I) {
236-
// TODO: We might need a .printJson for I.getKey() as well.
236+
using namespace llvm;
237+
DenseMap<const MemRegion *, std::string> StringifyCache;
238+
auto ToString = [&StringifyCache](const MemRegion *R) {
239+
auto [Place, Inserted] = StringifyCache.try_emplace(R);
240+
if (!Inserted)
241+
return Place->second;
242+
std::string Res;
243+
raw_string_ostream OS(Res);
244+
OS << R;
245+
Place->second = Res;
246+
return Res;
247+
};
248+
249+
using Cluster =
250+
std::pair<const MemRegion *, ImmutableMap<BindingKey, SVal>>;
251+
using Binding = std::pair<BindingKey, SVal>;
252+
253+
const auto MemSpaceBeforeRegionName = [&ToString](const Cluster *L,
254+
const Cluster *R) {
255+
if (isa<MemSpaceRegion>(L->first) && !isa<MemSpaceRegion>(R->first))
256+
return true;
257+
if (!isa<MemSpaceRegion>(L->first) && isa<MemSpaceRegion>(R->first))
258+
return false;
259+
return ToString(L->first) < ToString(R->first);
260+
};
261+
262+
const auto SymbolicBeforeOffset = [&ToString](const BindingKey &L,
263+
const BindingKey &R) {
264+
if (L.hasSymbolicOffset() && !R.hasSymbolicOffset())
265+
return true;
266+
if (!L.hasSymbolicOffset() && R.hasSymbolicOffset())
267+
return false;
268+
if (L.hasSymbolicOffset() && R.hasSymbolicOffset())
269+
return ToString(L.getRegion()) < ToString(R.getRegion());
270+
return L.getOffset() < R.getOffset();
271+
};
272+
273+
const auto DefaultBindingBeforeDirectBindings =
274+
[&SymbolicBeforeOffset](const Binding *LPtr, const Binding *RPtr) {
275+
const BindingKey &L = LPtr->first;
276+
const BindingKey &R = RPtr->first;
277+
if (L.isDefault() && !R.isDefault())
278+
return true;
279+
if (!L.isDefault() && R.isDefault())
280+
return false;
281+
assert(L.isDefault() == R.isDefault());
282+
return SymbolicBeforeOffset(L, R);
283+
};
284+
285+
const auto AddrOf = [](const auto &Item) { return &Item; };
286+
287+
std::vector<const Cluster *> SortedClusters;
288+
SortedClusters.reserve(std::distance(begin(), end()));
289+
append_range(SortedClusters, map_range(*this, AddrOf));
290+
llvm::sort(SortedClusters, MemSpaceBeforeRegionName);
291+
292+
for (auto [Idx, C] : llvm::enumerate(SortedClusters)) {
293+
const auto &[BaseRegion, Bindings] = *C;
237294
Indent(Out, Space, IsDot)
238-
<< "{ \"cluster\": \"" << I.getKey() << "\", \"pointer\": \""
239-
<< (const void *)I.getKey() << "\", \"items\": [" << NL;
295+
<< "{ \"cluster\": \"" << BaseRegion << "\", \"pointer\": \""
296+
<< (const void *)BaseRegion << "\", \"items\": [" << NL;
297+
298+
std::vector<const Binding *> SortedBindings;
299+
SortedBindings.reserve(std::distance(Bindings.begin(), Bindings.end()));
300+
append_range(SortedBindings, map_range(Bindings, AddrOf));
301+
llvm::sort(SortedBindings, DefaultBindingBeforeDirectBindings);
240302

241303
++Space;
242-
const ClusterBindings &CB = I.getData();
243-
for (ClusterBindings::iterator CI = CB.begin(), CE = CB.end(); CI != CE;
244-
++CI) {
245-
Indent(Out, Space, IsDot) << "{ " << CI.getKey() << ", \"value\": ";
246-
CI.getData().printJson(Out, /*AddQuotes=*/true);
304+
for (auto [Idx, B] : llvm::enumerate(SortedBindings)) {
305+
const auto &[Key, Value] = *B;
306+
Indent(Out, Space, IsDot) << "{ " << Key << ", \"value\": ";
307+
Value.printJson(Out, /*AddQuotes=*/true);
247308
Out << " }";
248-
if (std::next(CI) != CE)
309+
if (Idx != SortedBindings.size() - 1)
249310
Out << ',';
250311
Out << NL;
251312
}
252-
253313
--Space;
254314
Indent(Out, Space, IsDot) << "]}";
255-
if (std::next(I) != E)
315+
if (Idx != SortedClusters.size() - 1)
256316
Out << ',';
257317
Out << NL;
258318
}

0 commit comments

Comments
 (0)