Skip to content

[WIP] Update effects for acquire and release operations#8399

Open
tlively wants to merge 4 commits intomainfrom
acqrel-effects-and-more
Open

[WIP] Update effects for acquire and release operations#8399
tlively wants to merge 4 commits intomainfrom
acqrel-effects-and-more

Conversation

@tlively
Copy link
Member

@tlively tlively commented Feb 27, 2026

One of the primary use cases of EffectAnalyzer is to determine whether
one expression (or a sequence of expressions) can be reordered before or
after another expression (or sequence of expressions). Until now, the
check whether effects "invalidate" each other and would prevent
reordering has been symmetrical, checking read-write, write-write, and
write-read conflicts as well as symmetric conditions that would prevent
reordering. But acquire load and release store operations break this
symmetry.

Consider an expression A containing an acquire load (and no other
effects) and an expression B containing a release store (and no other
effects), where we know the accesses do not alias. Then if A is before
B, it is not valid to reorder the expressions to have B before A because
the acquire load cannot move forward past the release store and the
release store cannot move back past the acquire load. However, if B is
before A, it is valid to reorder them, since memory accesses are
generally allowed to move forward past acquire loads and back past
release stores.

Update EffectAnalyzer to take these allowed reorderings into account
by tracking the strictest memory orders used to read or write to shared
locations in the analyzed expressions. Also update it to differentiate
between shared and unshared memories, structs, and arrays to account for
both the fact that accesses to shared and unshared locations can never
alias and the fact that accesses to unshared locations can never
participate in synchronization. While we're at it, treat effects due to
e.g. nullability of reference parameters and mutability of fields more
uniformly across expressions.

Now that the effect comparison to see whether reordering is prevented is
no longer symmetric, rename it from invalidates to the more
descriptive orderedBefore, where A.orderedBefore(B) returns true iff
A ocurring before B implies that there is an ordering constraint
that would prevent reordering A and B past each other. To accomodate
users trying to move expressions in either direction, also add an
A.orderedAfter(B) method for the case where A occurs after B.

WIP because I need to add more testing for the effect analysis itself
and for the passes to ensure they are performing effect comparisons in
the correct directions.

One of the primary use cases of `EffectAnalyzer` is to determine whether
one expression (or a sequence of expressions) can be reordered before or
after another expression (or sequence of expressions). Until now, the
check whether effects "invalidate" each other and would prevent
reordering has been symmetrical, checking read-write, write-write, and
write-read conflicts as well as symmetric conditions that would prevent
reordering. But acquire load and release store operations break this
symmetry.

Consider an expression A containing and an acquire load (and no other
effects) and an expression B containing a release store (and no other
effects), where we know the accesses do not alias. Then if A is before
B, it is not valid to reorder the expressions to have B before A because
the acquire load cannot move forward past the release store and the
release store cannot move back past the acquire load. However, if B is
before A, it _is_ valid to reorder them, since memory accesses are
generally allowed to move forward past acquire loads and back past
release stores.

Update `EffectAnalyzer` to take these allowed reorderings into account
by tracking the strictest memory orders used to read or write to shared
locations in the analyzed expressions. Also update it to differentiate
between shared and unshared memories, structs, and arrays to account for
both the fact that accesses to shared and unshared locations can never
alias and the fact that accesses to unshared locations can never
participate in synchronization. While we're at it, treat effects due to
e.g. nullability of reference parameters and mutability of fields more
uniformly across expressions.

Now that the effect comparison to see whether reordering is prevented is
no longer symmetric, rename it from `invalidates` to the more
descriptive `orderedBefore`, where `A.orderedBefore(B)` returns true iff
`A` ocurring before `B` implies that there is an ordering constraint
that would prevent reordering `A` and `B` past each other. To accomodate
users trying to move expressions in either direction, also add an
`A.orderedAfter(B)` method for the case where `A` occurs after `B`.

WIP because I need to add more testing for the effect analysis itself
and for the passes to ensure they are performing effect comparisons in
the correct directions.
@tlively
Copy link
Member Author

tlively commented Feb 27, 2026

Subsumes #8384 and #8393.

cc @rmahdav

Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

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

some initial comments

if (effects.hasExternalBreakTargets()) {
o << "hasExternalBreakTargets\n";
}
o << "}";
Copy link
Member

Choose a reason for hiding this comment

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

The { on line 23 will be lonely after this

// The strongest memory orders used to read and write to any shared location
// (e.g. shared memories, shared GC structs, etc), if any.
MemoryOrder readOrder = MemoryOrder::Unordered;
MemoryOrder writeOrder = MemoryOrder::Unordered;
Copy link
Member

Choose a reason for hiding this comment

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

This adds a lot of fields to this core data structure - please test if it does not have a performance cost. One thing we might do is use : 1 for all the bools above, to avoid growing this structure by a lot (several passes store many copies).

// Changes something in globally-stored state.
bool writesGlobalState() const {
return globalsWritten.size() || writesMemory || writesTable ||
writesStruct || writesArray || isAtomic || calls;
Copy link
Member

Choose a reason for hiding this comment

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

Was isAtomic removed intentionally? I guess it moved into hasSynchronization?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, isAtomic is intentionally removed.

parent.writeOrder = std::max(parent.writeOrder, curr->order);
} else {
parent.writesMemory = true;
}
Copy link
Member

Choose a reason for hiding this comment

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

can perhaps use a helper for this repeating pattern? void writesMemory(Name memory, Order order); etc.

// We don't want this to be moved out of loops, but it doesn't otherwises
// matter much how it gets reordered. Say we transfer control as a coarse
// approximation of this.
parent.branchesOut = true;
Copy link
Member

Choose a reason for hiding this comment

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

This could be in an earlier PR perhaps? Just for bisection purposes later.

parent.readsSharedMemory = true;
} else {
parent.readsMemory = true;
}
Copy link
Member

Choose a reason for hiding this comment

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

This pattern also repeats a lot

EffectAnalyzer aEffects(passOptions, module, a);
EffectAnalyzer bEffects(passOptions, module, b);
return !aEffects.invalidates(bEffects);
return !aEffects.orderedBefore(bEffects);
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this check the other direction too? orderedBefore assumes a is before b, but this function doesn't. Or is the intention for this to assume that ordering as well? The comment could be more explicit about that, then?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, this should also assume an initial ordering.

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