Skip to content

Never destroy the DialectContext map - #146

Merged
dstutt merged 1 commit into
GPUOpen-Drivers:devfrom
dstutt:immortal-context-map
Oct 2, 2026
Merged

dstutt merged 1 commit into
GPUOpen-Drivers:devfrom
dstutt:immortal-context-map

Conversation

@dstutt

@dstutt dstutt commented Oct 2, 2026

Copy link
Copy Markdown
Member

ContextMap::get() kept the map in a function-local static. A client that destroys its last DialectContext from another static destructor can run after the map has been destroyed, because statics are destroyed in reverse order of construction and the map is created lazily by the first DialectContext. ~DialectContext then calls ContextMap::remove() on a destroyed DenseMap, which probes freed buckets: it can loop forever or write into freed memory.

Allocate the map once and never destroy it using a standard idiomatic approach to avoid "static destruction order fiasco". This change avoids using a heap allocation primarily to avoid analysis tools flagging a memory leak.

ContextMap::get() kept the map in a function-local static. A client that
destroys its last DialectContext from another static destructor can run
after the map has been destroyed, because statics are destroyed in reverse
order of construction and the map is created lazily by the first
DialectContext. ~DialectContext then calls ContextMap::remove() on a
destroyed DenseMap, which probes freed buckets: it can loop forever or
write into freed memory.

Allocate the map once and never destroy it using a standard idiomatic
approach to avoid "static destruction order fiasco".
This change avoids using a heap allocation primarily to avoid analysis
tools flagging a memory leak.
@dstutt
dstutt requested review from Flakebi and trenouf October 2, 2026 11:03
@Flakebi

Flakebi commented Oct 2, 2026

Copy link
Copy Markdown
Member

This looks quite dirty. A DialectContext creates the ContextMap in its constructor, so, just from that the destruction order should be guaranteed to be the correct one.
(That seems to match a pattern described here: https://stackoverflow.com/questions/335369/finding-c-static-initialization-order-problems#335746)

Before we merge this, I’d like to understand why this is the best solution.

@Flakebi Flakebi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so roughly what goes wrong is:

  1. A compiler is created in a static variable A
  2. At some point, the compiler is requested to compile something, creating a dialect (held in a member variable of the compiler) and the ContextMap in static B

At cleanup time, static B with the ContextMap is deleted first (as it destroys in reverse order),
then static A with the compiler is deleted, deleting the Dialect which then tries to access the already deleted ContextMap.

In short, the combination of multiple statics and lazy initialization causes the problem.
That seems like a pattern we should support, so LGTM.

@dstutt
dstutt merged commit c6d2102 into GPUOpen-Drivers:dev Oct 2, 2026
8 checks passed
@dstutt
dstutt deleted the immortal-context-map branch October 2, 2026 13:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants