This is an archive of the discontinued LLVM Phabricator instance.

[SCCP] Add initial support for propagation through freeze.
Needs RevisionPublic

Authored by fhahn on Apr 23 2022, 4:19 AM.

Details

Summary

This patch implements initial support for freeze instructions in
[IP]SCCP.

If the operand is known to be undef, exit early and wait until it
changes. If it stays undef until the end of solving, resolvedUndefsIn
will take care of handling it. At the moment, it will be marked as
overdefined.

Otherwise just merge in the value of the operand.

Diff Detail

Event Timeline

fhahn created this revision.Apr 23 2022, 4:19 AM
Herald added a project: Restricted Project. · View Herald TranscriptApr 23 2022, 4:19 AM
Herald added a subscriber: hiraditya. · View Herald Transcript
fhahn requested review of this revision.Apr 23 2022, 4:19 AM
Herald added a project: Restricted Project. · View Herald TranscriptApr 23 2022, 4:19 AM
nikic added a comment.Apr 23 2022, 4:22 AM

What about constant lattice element with a constant expression that may be undef/poison? What about constantrange_including_undef lattice element?

fhahn added a comment.Apr 23 2022, 4:43 AM

What about constant lattice element with a constant expression that may be undef/poison? What about constantrange_including_undef lattice element?

I *think* the handling in the patch should be conservatively correct, as the it should be a valid over-approximation to have the freeze result may include undef.

If we use the range to replace with a constant, the assumption is that we choose a value in the range and which particular one doesn't matter. As users of the range have to assume the value could be any in the range, I don't think we can get in a situation where 2 different users would pick different values due to undef in the range. This should effectively delay committing to a constant value for the freeze until we use the range to replace with a constant. The _including_undef part of the name is probably not entirely accurate. The important property is that we cannot use such ranges to perform simplifications *without* replacing the value with a constant ( (see test case`@test1_sext_op_can_be_undef_but_frozen` in rGb2a885a290be)

If we would need to commit to eliminating the undef part of a range at the point we mark the result of the freeze we would have to mark it as overdefined.

nikic requested changes to this revision.Apr 25 2022, 7:57 AM

I tested this patch, and it folds

@g = external global i8

define i64 @test() {
  %x = freeze i64 add nuw (i64 ptrtoint (ptr @g to i64), i64 1)
  ret i64 %x
}

to

@g = external global i8

define i64 @test() {
  ret i64 add nuw (i64 ptrtoint (ptr @g to i64), i64 1)
}

which is clearly not right due to the presence of the nuw flag. This needs an isGuaranteedNotToBeUndefOrPoison check for the constant value.

This revision now requires changes to proceed.Apr 25 2022, 7:57 AM
nikic added inline comments.Apr 25 2022, 8:10 AM
llvm/test/Transforms/SCCP/freeze.ll
48

Let's assume %x is poison here, then %and is poison and %and.fr can take any value, while you assume that it must be in [0,3].

Here's an example of how this goes wrong across multiple passes:

declare void @use(i64) 
 
define internal i1 @test(i64 %a) {
  %b = or i64 %a, 2 
  %x = freeze i64 %b
  call void @use(i64 %x) 
  %c = icmp eq i64 %x, 0 
  ret i1 %c
} 
 
define i1 @test2() {
  %c = call i1 @test(i64 poison)
  ret i1 %c
}

; opt -S -sccp -inline -instcombine

define i1 @test2() {
  call void @use(i64 0)
  ret i1 false
}

As you can see, we both claim that %x != 0 and pass %x == 0 to @use(). This example uses -sccp to make sure SCCP doesn't have a global view.