Skip to content

Commit 7ccb5c0

Browse files
authored
Thread Safety Analysis: Optimize LocalVariableMap's canonical reference computation (#161600)
We observed slowdowns in auto-generated million+ line C++ source files due to recent fixes to LocalVariableMap. Rather than recompute the canonical underlying non-reference VarDefinition every time we need to perform variable definition intersection at CFG merge points, pre-compute the canonical references once on construction. Ensure to maintain the invariant that if both the direct and canonical reference are being invalidated, both become 0. Reported-by: Bogdan Graur <[email protected]>
1 parent 3c39187 commit 7ccb5c0

File tree

1 file changed

+21
-15
lines changed

1 file changed

+21
-15
lines changed

clang/lib/Analysis/ThreadSafety.cpp

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -419,22 +419,28 @@ class LocalVariableMap {
419419
// The expression for this variable, OR
420420
const Expr *Exp = nullptr;
421421

422-
// Reference to another VarDefinition
423-
unsigned Ref = 0;
422+
// Direct reference to another VarDefinition
423+
unsigned DirectRef = 0;
424+
425+
// Reference to underlying canonical non-reference VarDefinition.
426+
unsigned CanonicalRef = 0;
424427

425428
// The map with which Exp should be interpreted.
426429
Context Ctx;
427430

428431
bool isReference() const { return !Exp; }
429432

433+
void invalidateRef() { DirectRef = CanonicalRef = 0; }
434+
430435
private:
431436
// Create ordinary variable definition
432437
VarDefinition(const NamedDecl *D, const Expr *E, Context C)
433438
: Dec(D), Exp(E), Ctx(C) {}
434439

435440
// Create reference to previous definition
436-
VarDefinition(const NamedDecl *D, unsigned R, Context C)
437-
: Dec(D), Ref(R), Ctx(C) {}
441+
VarDefinition(const NamedDecl *D, unsigned DirectRef, unsigned CanonicalRef,
442+
Context C)
443+
: Dec(D), DirectRef(DirectRef), CanonicalRef(CanonicalRef), Ctx(C) {}
438444
};
439445

440446
private:
@@ -445,7 +451,7 @@ class LocalVariableMap {
445451
public:
446452
LocalVariableMap() {
447453
// index 0 is a placeholder for undefined variables (aka phi-nodes).
448-
VarDefinitions.push_back(VarDefinition(nullptr, 0u, getEmptyContext()));
454+
VarDefinitions.push_back(VarDefinition(nullptr, 0, 0, getEmptyContext()));
449455
}
450456

451457
/// Look up a definition, within the given context.
@@ -471,7 +477,7 @@ class LocalVariableMap {
471477
Ctx = VarDefinitions[i].Ctx;
472478
return VarDefinitions[i].Exp;
473479
}
474-
i = VarDefinitions[i].Ref;
480+
i = VarDefinitions[i].DirectRef;
475481
}
476482
return nullptr;
477483
}
@@ -508,7 +514,7 @@ class LocalVariableMap {
508514
void dump() {
509515
for (unsigned i = 1, e = VarDefinitions.size(); i < e; ++i) {
510516
const Expr *Exp = VarDefinitions[i].Exp;
511-
unsigned Ref = VarDefinitions[i].Ref;
517+
unsigned Ref = VarDefinitions[i].DirectRef;
512518

513519
dumpVarDefinitionName(i);
514520
llvm::errs() << " = ";
@@ -539,9 +545,9 @@ class LocalVariableMap {
539545
friend class VarMapBuilder;
540546

541547
// Resolve any definition ID down to its non-reference base ID.
542-
unsigned getCanonicalDefinitionID(unsigned ID) {
548+
unsigned getCanonicalDefinitionID(unsigned ID) const {
543549
while (ID > 0 && VarDefinitions[ID].isReference())
544-
ID = VarDefinitions[ID].Ref;
550+
ID = VarDefinitions[ID].CanonicalRef;
545551
return ID;
546552
}
547553

@@ -564,10 +570,11 @@ class LocalVariableMap {
564570
}
565571

566572
// Add a new reference to an existing definition.
567-
Context addReference(const NamedDecl *D, unsigned i, Context Ctx) {
573+
Context addReference(const NamedDecl *D, unsigned Ref, Context Ctx) {
568574
unsigned newID = VarDefinitions.size();
569575
Context NewCtx = ContextFactory.add(Ctx, D, newID);
570-
VarDefinitions.push_back(VarDefinition(D, i, Ctx));
576+
VarDefinitions.push_back(
577+
VarDefinition(D, Ref, getCanonicalDefinitionID(Ref), Ctx));
571578
return NewCtx;
572579
}
573580

@@ -769,15 +776,14 @@ void LocalVariableMap::intersectBackEdge(Context C1, Context C2) {
769776
const unsigned *I2 = C2.lookup(P.first);
770777
if (!I2) {
771778
// Variable does not exist at the end of the loop, invalidate.
772-
VDef->Ref = 0;
779+
VDef->invalidateRef();
773780
continue;
774781
}
775782

776783
// Compare the canonical IDs. This correctly handles chains of references
777784
// and determines if the variable is truly loop-invariant.
778-
if (getCanonicalDefinitionID(VDef->Ref) != getCanonicalDefinitionID(*I2)) {
779-
VDef->Ref = 0; // Mark this variable as undefined
780-
}
785+
if (VDef->CanonicalRef != getCanonicalDefinitionID(*I2))
786+
VDef->invalidateRef(); // Mark this variable as undefined
781787
}
782788
}
783789

0 commit comments

Comments
 (0)