This is an archive of the discontinued LLVM Phabricator instance.

[ValueTracking] Don't assume readonly function will return
ClosedPublic

Authored by nikic on Jan 23 2021, 8:54 AM.

Details

Summary

This is similar to D94106, but for the isGuaranteedToTransferExecutionToSuccessor() helper. We should not assume that readonly functions will return, as this is only true for mustprogress functions (in which case we already infer willreturn). As with the DCE change, for now continue assuming that readonly intrinsics will return, as not all target intrinsics have been annotated yet.

Diff Detail

Event Timeline

nikic created this revision.Jan 23 2021, 8:54 AM
nikic requested review of this revision.Jan 23 2021, 8:54 AM
Herald added a reviewer: baziotis. · View Herald Transcript
Herald added a project: Restricted Project. · View Herald Transcript
fhahn accepted this revision.Jan 23 2021, 11:04 AM

LGTM, thanks

lib/Analysis/ValueTracking.cpp
5059 ↗(On Diff #318761)

I guess we could incorporate the argmemonly reasoning in 'FunctionAttrs`, using something like argmemonly + nosync => willreturn, assuming that IO can only happen through inaccessible memory? Just argmemonly would not be enough I think, because we still need to rule out volatile accesses/synchronization via. the arguments.

unittests/Analysis/ValueTrackingTest.cpp
654 ↗(On Diff #318761)

also add // call void @unknown(i32* %p) willreturn?

This revision is now accepted and ready to land.Jan 23 2021, 11:04 AM
nikic added inline comments.Jan 23 2021, 11:45 AM
lib/Analysis/ValueTracking.cpp
5059 ↗(On Diff #318761)

Yeah, I agree that mustprogress, argmemonly and nosync should imply willreturn. However, FuncAttrs currently infers neither nosync nor argmemonly, so it would need those first. I think that the argmemonly check here was there to cover some common intrinsics like llvm.memcpy, but those are explicitly willreturn now.

This revision was landed with ongoing or failed builds.Jan 24 2021, 1:41 AM
This revision was automatically updated to reflect the committed changes.
SjoerdMeijer added inline comments.
llvm/lib/Analysis/ValueTracking.cpp
5046

Drive by comment: if this reads volatile memory, it is not side-effect free? If this is a check for "side-effect free intrinsics", does this check cover that?

fhahn added inline comments.Jan 25 2021, 2:46 AM
llvm/lib/Analysis/ValueTracking.cpp
5046

Unfortunately this is not completely clear from the readonly definition, but I don't think to would currently be safe to treat functions with volatile or atomic loads as readonly. They are currently treated as may-write-to-memory, see https://llvm.org/docs/LangRef.html#volatile-memory-accesses

We have nosync to model some of those aspects separately, but it is not really used at the moment.

SjoerdMeijer added inline comments.
llvm/lib/Analysis/ValueTracking.cpp
5046

but I don't think to would currently be safe to treat functions with volatile or atomic loads as readonly.

Yes, that's what I think (also from reading langref).

@samparker remarked that we could perhaps also check the IntrHasSideEffects intrinsic property?

fhahn added inline comments.Jan 25 2021, 3:21 AM
llvm/lib/Analysis/ValueTracking.cpp
5046

@samparker remarked that we could perhaps also check the IntrHasSideEffects intrinsic property?

I think that's only accessible in the backend at the moment?

jdoerfert added inline comments.Jan 25 2021, 7:55 AM
llvm/lib/Analysis/ValueTracking.cpp
5046

> @samparker remarked that we could perhaps also check the IntrHasSideEffects intrinsic property?

I think that's only accessible in the backend at the moment?

And, IMHO, it is conceptually broken to have such "hidden side effects" in the first place. If you don't model you behavior in the IR properly how is any of this supposed to work. We might need more "side effect categories" but that is a different conversation.

Yeah, I agree that mustprogress, argmemonly and nosync should imply willreturn. However, FuncAttrs currently infers neither nosync nor argmemonly, so it would need those first. I think that the argmemonly check here was there to cover some common intrinsics like llvm.memcpy, but those are explicitly willreturn now.

Post release branch we should start running a light Attributor, to get things like nosync, argmemonly, ... one by one.

Yeah, I agree that mustprogress, argmemonly and nosync should imply willreturn. However, FuncAttrs currently infers neither nosync nor argmemonly, so it would need those first. I think that the argmemonly check here was there to cover some common intrinsics like llvm.memcpy, but those are explicitly willreturn now.

Post release branch we should start running a light Attributor, to get things like nosync, argmemonly, ... one by one.

But just for my understanding, does this mean there’s a problem that we intend to fix after the release?

fhahn added a comment.Jan 27 2021, 2:22 AM

Yeah, I agree that mustprogress, argmemonly and nosync should imply willreturn. However, FuncAttrs currently infers neither nosync nor argmemonly, so it would need those first. I think that the argmemonly check here was there to cover some common intrinsics like llvm.memcpy, but those are explicitly willreturn now.

Post release branch we should start running a light Attributor, to get things like nosync, argmemonly, ... one by one.

But just for my understanding, does this mean there’s a problem that we intend to fix after the release?

I don't think there's a problem per-se. There will be a few cases where we now fail to remove dead calls, because willreturn is not being inferred, even though the function is actually willreturn. So far, we do not have any indication that this is causing performance problems in practice, but we should keep a close eye on the impact. (And there are still places in tree that assume functions will return, without willreturn, which potentially still hides some of the negative impact).

Going forward, we now have greater motivation to improve attribute inference, because there's a tangible benefit, where none was before ('as long as willreturn is not useful, why infer it?'). I think what Johannes is eluding to is that now there is a bigger motivation to move towards using parts of the attributor to improve the inference.

Yeah, I agree that mustprogress, argmemonly and nosync should imply willreturn. However, FuncAttrs currently infers neither nosync nor argmemonly, so it would need those first. I think that the argmemonly check here was there to cover some common intrinsics like llvm.memcpy, but those are explicitly willreturn now.

Post release branch we should start running a light Attributor, to get things like nosync, argmemonly, ... one by one.

But just for my understanding, does this mean there’s a problem that we intend to fix after the release?

I don't think there's a problem per-se. There will be a few cases where we now fail to remove dead calls, because willreturn is not being inferred, even though the function is actually willreturn. So far, we do not have any indication that this is causing performance problems in practice, but we should keep a close eye on the impact. (And there are still places in tree that assume functions will return, without willreturn, which potentially still hides some of the negative impact).

Going forward, we now have greater motivation to improve attribute inference, because there's a tangible benefit, where none was before ('as long as willreturn is not useful, why infer it?'). I think what Johannes is eluding to is that now there is a bigger motivation to move towards using parts of the attributor to improve the inference.

Ok, makes sense, got it. Cheers.