This is an archive of the discontinued LLVM Phabricator instance.

[InstCombine] Add !noundef if is guaranteed do not violate !range
Needs RevisionPublic

Authored by StephenFan on Apr 24 2023, 9:54 AM.

Details

Reviewers
nikic
Summary

Since D141386, violating !range would return poison. But folding select
to and/or i1 isn't poison safe in general. If we can prove that the
!range can never be violated, adding a !noundef can assume it is
guaranteed not to be posion value.

Even though violating !noundef would cause immediate undefine behavior,
in other words this instruction would not be speculatable, but metadatas
that may raise IUB is not considered in isSafeToSpeculativelyExecute
since these metadatas can be dropped.

Therefore, adding !noundef can improve folding of select and there is no
foreseeable side-effect at the same time.

Diff Detail

Event Timeline

StephenFan created this revision.Apr 24 2023, 9:54 AM
Herald added a project: Restricted Project. · View Herald TranscriptApr 24 2023, 9:54 AM
Herald added a subscriber: hiraditya. · View Herald Transcript
StephenFan requested review of this revision.Apr 24 2023, 9:54 AM
Herald added a project: Restricted Project. · View Herald TranscriptApr 24 2023, 9:54 AM

If the instruction self can create poison or undef, it is not guaranteed do not violate the !range metadata

nikic added a comment.Apr 25 2023, 1:13 AM

Can you please explain what the larger motivation for this is? Can you show the whole pattern you're trying to optimize? I really don't want to do this without strong motivation.

Also, the !noundef metadata should be a noundef attribute. Actually, this should be enforced by the verifier the same way it is for !nonull and !align, it looks like we're missing a check.

Can you please explain what the larger motivation for this is? Can you show the whole pattern you're trying to optimize? I really don't want to do this without strong motivation.

Also, the !noundef metadata should be a noundef attribute. Actually, this should be enforced by the verifier the same way it is for !nonull and !align, it looks like we're missing a check.

The larger motivation is we can't instcombine %r = select i1 %b, i1 %cmp, i1 false to %r = and i1 %b, %cmp in the following example. And it caused code size regressions on lzbench https://github.com/dtcxzyw/llvm-ci/issues/135 .

define i1 @poison_metadata(i32 noundef %a, i1 %b) {
  %c = call i32 @llvm.ctpop.i32(i32 %a), !range !0
  %cmp = icmp ugt i32 %c, 2
  %r = select i1 %b, i1 %cmp, i1 false
  ret i1 %r
}

!0 = !{i32 0, i32 4}
!1 = !{}
declare i32 @llvm.ctpop.i32(i32)
nikic added a comment.Apr 25 2023, 5:01 AM

I think the right way to fix that would be to support poison flag/metadata drop in impliesPoison() transform. Even if we can't prove that the metadata does not produce poison, we can still drop it.

nikic requested changes to this revision.Apr 26 2023, 1:09 AM

I think the right way to fix that would be to support poison flag/metadata drop in impliesPoison() transform. Even if we can't prove that the metadata does not produce poison, we can still drop it.

To expand on this: The general policy is that presence of poison flags/metadata should not block transforms, instead we should drop the flags/metadata to allow the transform. For example, when we push freeze into operands we call canCreateUndefOrPoison with ConsiderFlagsAndMetadata=false and drop those flags. The impliesPoison() based select -> and/or transform currently doesn't do this, so we may fail the transform in cases where it would succeed if we dropped flags. Supporting this will need some API changes, in particular impliesPoison() will have to collect instructions on which flags need to be dropped.

I believe this is the proper way to fix this, which will also address other uses of flags/metadata.

This revision now requires changes to proceed.Apr 26 2023, 1:09 AM

I think the right way to fix that would be to support poison flag/metadata drop in impliesPoison() transform. Even if we can't prove that the metadata does not produce poison, we can still drop it.

To expand on this: The general policy is that presence of poison flags/metadata should not block transforms, instead we should drop the flags/metadata to allow the transform. For example, when we push freeze into operands we call canCreateUndefOrPoison with ConsiderFlagsAndMetadata=false and drop those flags. The impliesPoison() based select -> and/or transform currently doesn't do this, so we may fail the transform in cases where it would succeed if we dropped flags. Supporting this will need some API changes, in particular impliesPoison() will have to collect instructions on which flags need to be dropped.

I believe this is the proper way to fix this, which will also address other uses of flags/metadata.

Thanks for the detailed explanation! @nikic