Skip to content

Commit 54abcfe

Browse files
authored
Fix CFP to avoid removing synchronization (#7311)
ConstantFieldPropagation previously reasoned that if a field has a constant value, then it is not possible for release-acquire operations to use it for synchronization, since the read can be assumed to be from a previous write. This logic was incorrect because reads must read the value written by the last write in the global modification order for the accessed location. In principle it would be possible for CFP to detect fields that have both constant values and also either no acquire reads or no release writes. Such fields cannot possibly be part of synchronization edges and their gets could be optimized normally. For now though, simply do not optimize any gets with acquire or seqcst ordering. Similarly, stop optimizing RMW operations because they can also form synchronization edges.
1 parent bf7ff3e commit 54abcfe

File tree

3 files changed

+66
-709
lines changed

3 files changed

+66
-709
lines changed

src/passes/ConstantFieldPropagation.cpp

Lines changed: 12 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -133,27 +133,6 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
133133
return PossibleConstantValues{};
134134
}
135135

136-
// Returns a block dropping the `ref` operand of the argument.
137-
template<typename T> Block* makeRefDroppingBlock(T* curr) {
138-
Builder builder(*getModule());
139-
return builder.blockify(
140-
builder.makeDrop(builder.makeRefAs(RefAsNonNull, curr->ref)));
141-
}
142-
143-
// If an optimized access is sequentially consistent, then it synchronizes
144-
// with other threads at least by participating in the global order of
145-
// sequentially consistent operations. Preserve that effect by replacing the
146-
// access with a fence. On the other hand, if we're optimizing an
147-
// acquire-release operation, then we know the accessed field is constant and
148-
// will not be modified, so the operation does not necessarily synchronize
149-
// with other threads and no fence is required.
150-
Block* maybeAddFence(Block* block, MemoryOrder order) {
151-
if (order == MemoryOrder::SeqCst) {
152-
block->list.push_back(Builder(*getModule()).makeAtomicFence());
153-
}
154-
return block;
155-
}
156-
157136
// Given information about a constant value, and the struct type and
158137
// StructGet/RMW/Cmpxchg that reads it, create an expression for that value.
159138
template<typename T>
@@ -216,6 +195,16 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
216195
return;
217196
}
218197

198+
if (curr->order != MemoryOrder::Unordered) {
199+
// The analysis we're basing the optimization on is not precise enough to
200+
// rule out the field being used to synchronize between a write of the
201+
// constant value and a subsequent read on another thread. This
202+
// synchronization is possible even when the write does not change the
203+
// value of the field. For now, simply avoid optimizing this case.
204+
// TODO: Track release and acquire operations in the analysis.
205+
return;
206+
}
207+
219208
// If the value is not a constant, then it is unknown and we must give up
220209
// on simply applying a constant. However, we can try to use a ref.test, if
221210
// that is allowed.
@@ -231,69 +220,8 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
231220
// constant value. (Leave it to further optimizations to get rid of the
232221
// ref.)
233222
auto* value = makeExpression(info, heapType, curr);
234-
auto* replacement = makeRefDroppingBlock(curr);
235-
replacement = maybeAddFence(replacement, curr->order);
236-
replacement->list.push_back(value);
237-
replacement->type = value->type;
238-
replaceCurrent(replacement);
239-
changed = true;
240-
}
241-
242-
template<typename T>
243-
std::optional<std::pair<HeapType, PossibleConstantValues>>
244-
shouldOptimizeRMW(T* curr) {
245-
auto type = getRelevantHeapType(curr);
246-
if (!type) {
247-
return std::nullopt;
248-
}
249-
auto heapType = *type;
250-
251-
// Get the info about the field. Since RMWs can only copy or mutate the
252-
// value, we always have something recorded.
253-
PossibleConstantValues info = getInfo(heapType, curr->index);
254-
assert(info.hasNoted() && "unexpected lack of info for RMW");
255-
256-
if (!info.isConstant()) {
257-
// Optimizing using ref.test is not an option here because that only works
258-
// on immutable fields, but RMW operations always access mutable fields.
259-
return std::nullopt;
260-
}
261-
262-
// We can optimize.
263-
return std::pair(heapType, info);
264-
}
265-
266-
void visitStructRMW(StructRMW* curr) {
267-
auto typeAndInfo = shouldOptimizeRMW(curr);
268-
if (!typeAndInfo) {
269-
return;
270-
}
271-
// Only xchg allows the field to have a constant value.
272-
assert(curr->op == RMWXchg && "unexpected op");
273-
auto& [type, info] = *typeAndInfo;
274-
Builder builder(*getModule());
275-
auto* value = makeExpression(info, type, curr);
276-
auto* replacement = makeRefDroppingBlock(curr);
277-
replacement->list.push_back(builder.makeDrop(curr->value));
278-
replacement = maybeAddFence(replacement, curr->order);
279-
replacement->list.push_back(value);
280-
replacement->type = value->type;
281-
replaceCurrent(replacement);
282-
changed = true;
283-
}
284-
285-
void visitStructCmpxchg(StructCmpxchg* curr) {
286-
auto typeAndInfo = shouldOptimizeRMW(curr);
287-
if (!typeAndInfo) {
288-
return;
289-
}
290-
auto& [type, info] = *typeAndInfo;
291-
Builder builder(*getModule());
292-
auto* value = makeExpression(info, type, curr);
293-
auto* replacement = makeRefDroppingBlock(curr);
294-
replacement->list.push_back(builder.makeDrop(curr->expected));
295-
replacement->list.push_back(builder.makeDrop(curr->replacement));
296-
replacement = maybeAddFence(replacement, curr->order);
223+
auto* replacement = builder.blockify(
224+
builder.makeDrop(builder.makeRefAs(RefAsNonNull, curr->ref)));
297225
replacement->list.push_back(value);
298226
replacement->type = value->type;
299227
replaceCurrent(replacement);

0 commit comments

Comments
 (0)