This is an archive of the discontinued LLVM Phabricator instance.

[BasicAA] Don't short-circuit non-capturing arguments
ClosedPublic

Authored by nikic on Jun 22 2023, 1:45 AM.

Details

Summary

This is a possible alternative to D153464. BasicAA currently assumes that an unescaped alloca cannot be read through non-nocapture arguments of a call, based on the argument that if the argument were based on the alloca, it would not be unescaped.

This currently fails in the case where the call is an ephemeral value and as such does not count as a capture. It could also happen if CaptureTracking were slightly smarter and continued following captures on call return values.

Diff Detail

Event Timeline

nikic created this revision.Jun 22 2023, 1:45 AM
nikic requested review of this revision.Jun 22 2023, 1:45 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 22 2023, 1:45 AM

Hmm, I am a bit worried that fixing this here rather than marking them as capturing in the CaptureTracker leaves a potential door for mis-use of the general API. Perhaps it would be possible to just consider those calls as capturing, as in
D153577

Hmm, I am a bit worried that fixing this here rather than marking them as capturing in the CaptureTracker leaves a potential door for mis-use of the general API. Perhaps it would be possible to just consider those calls as capturing, as in
D153577

Those calls aren't capturing the pointer though.

Do you mean you're worried about mis-use of the capture tracking API?

I think this makes sense, using capture tracking to infer mod/ref seems backwards. IIUC this analysis depends on AAQI.CI->isNotCapturedBeforeOrAt() agreeing with Call->doesNotCapture(), which seems brittle.

Hmm, I am a bit worried that fixing this here rather than marking them as capturing in the CaptureTracker leaves a potential door for mis-use of the general API. Perhaps it would be possible to just consider those calls as capturing, as in
D153577

Those calls aren't capturing the pointer though.

Do you mean you're worried about mis-use of the capture tracking API?

I think this makes sense, using capture tracking to infer mod/ref seems backwards. IIUC this analysis depends on AAQI.CI->isNotCapturedBeforeOrAt() agreeing with Call->doesNotCapture(), which seems brittle.

IIUC the capture tracking here is used more to check whether the pointer escapes. We also have the following code which considers calls as capturing: https://github.com/llvm/llvm-project/blob/88f07a311947f88de82ad2de9b2d6a26eba21343/llvm/lib/Analysis/CaptureTracking.cpp#L314 . For the test case, if the result isn't used in an assume, it is considered capturing the pointer.

You don't really need an assume to show the issue, this simpler variant exhibits the same problem: https://llvm.godbolt.org/z/M3K5fG97G The store is removed even though the call reads it.

This just happens to mostly work out fine right now because such calls get DCEd, so we don't observe end-to-end miscompiles. If capture tracking were slightly smarter and followed the return value (as we effectively do in the assume case) we would get observable miscompiles even without ephemeral value interactions.

You don't really need an assume to show the issue, this simpler variant exhibits the same problem: https://llvm.godbolt.org/z/M3K5fG97G The store is removed even though the call reads it.

This just happens to mostly work out fine right now because such calls get DCEd, so we don't observe end-to-end miscompiles. If capture tracking were slightly smarter and followed the return value (as we effectively do in the assume case) we would get observable miscompiles even without ephemeral value interactions.

Yeah, one issue seems to be that AA uses CaptureTracking as proxy for escaping (e.g. EarliestEscapeInfo by DSE), but in some cases escaping doesn't imply capturing and things go wrong. I *think* tracking escapes (instead of captures) would make sense in general, as this should help to avoid most code-size changes seen by this patch I suspect.

nikic added a comment.Jun 22 2023, 1:45 PM

You don't really need an assume to show the issue, this simpler variant exhibits the same problem: https://llvm.godbolt.org/z/M3K5fG97G The store is removed even though the call reads it.

This just happens to mostly work out fine right now because such calls get DCEd, so we don't observe end-to-end miscompiles. If capture tracking were slightly smarter and followed the return value (as we effectively do in the assume case) we would get observable miscompiles even without ephemeral value interactions.

Yeah, one issue seems to be that AA uses CaptureTracking as proxy for escaping (e.g. EarliestEscapeInfo by DSE), but in some cases escaping doesn't imply capturing and things go wrong. I *think* tracking escapes (instead of captures) would make sense in general, as this should help to avoid most code-size changes seen by this patch I suspect.

CaptureTracking handles both captures and escapes. There is no escape in this case. No escape does not imply that memory is not accessed, just that provenance is not retained after the call.

BasicAA is making assumptions about how the CaptureTracking implementation works here, which goes beyond the actual capture/escape properties. Those assumptions are close, but not quite correct.

fhahn added a comment.Jun 22 2023, 2:18 PM

CaptureTracking handles both captures and escapes. There is no escape in this case. No escape does not imply that memory is not accessed, just that provenance is not retained after the call.

BasicAA is making assumptions about how the CaptureTracking implementation works here, which goes beyond the actual capture/escape properties. Those assumptions are close, but not quite correct.

Right, IIUC the code in BasicAA assumes escaping pointers to not be visible outside the current function, which includes not being passed to readonly functions. I was initially (incorrectly) assuming this is what our APIs would consider an escape. It might be worth to have an option to track this property for AA.

nikic added a comment.Jun 22 2023, 2:43 PM

CaptureTracking handles both captures and escapes. There is no escape in this case. No escape does not imply that memory is not accessed, just that provenance is not retained after the call.

BasicAA is making assumptions about how the CaptureTracking implementation works here, which goes beyond the actual capture/escape properties. Those assumptions are close, but not quite correct.

Right, IIUC the code in BasicAA assumes escaping pointers to not be visible outside the current function, which includes not being passed to readonly functions.

Not really. A call can access an alloca in one of two ways: Either by the pointer first escaping and the call accessing the escaped pointer, or by the pointer being passed as an argument to the call. BasicAA performs checks for both of these. The first is based on CaptureTracking, and the second based on alias checks on the call arguments. This is the correct way to use the CaptureTracking API.

The only problem is that BasicAA, as an optimization, does not perform the argument check for non-nocapture arguments, on the premise that CaptureTracking will have treated these as an escape and thus failed the first check already.

I was initially (incorrectly) assuming this is what our APIs would consider an escape. It might be worth to have an option to track this property for AA.

I don't think this is really possible. For the "escaped before call" check, we are only interested in escapes, not in accesses by calls. It's okay if the pointer gets accessed by previous calls, as long as it does not escape. For accessed, we only care about the current call. So this would require a call-specific result which is not really compatible with EarliestEscapeInfo.

(As a side note, with the change I'm proposing here, we could replace isNotCapturedBeforeOrAt with isNotCaptureBefore, dropping the "OrAt" part. Not sure whether this would make any practical difference.)

aeubanks accepted this revision.Jun 22 2023, 3:06 PM

makes sense to me

llvm/lib/Analysis/BasicAliasAnalysis.cpp
866–871

can you update this comment with your explanation so it's clearer?

llvm/test/Transforms/PhaseOrdering/dse-ephemeral-value-captures.ll
24–25

remove FIXME

This revision is now accepted and ready to land.Jun 22 2023, 3:06 PM
nikic updated this revision to Diff 533891.Jun 23 2023, 1:17 AM

Update comment, remove FIXME.

This revision was automatically updated to reflect the committed changes.