This is an archive of the discontinued LLVM Phabricator instance.

[BasicAA] Generalize base offset modulus handling
ClosedPublic

Authored by nikic on Nov 8 2020, 1:49 AM.

Details

Summary

The GEP aliasing implementation currently has two pieces of code that solve two different subsets of the same basic problem: If you have GEPs with offsets 4*x + 0 and 4*y + 1 (assuming access size 1), then they do not alias regardless of whether x and y are the same.

One implementation is in aliasSameBasePointerGEPs(), which looks at this in a limited structural way. It requires both GEP base pointers to be exactly the same, then (optionally) a number of equal indexes, then an unknown index, then a non-equal index into a struct. This set of limitations works, but it's overly restrictive and hides the core property we're trying to exploit.

The second implementation is part of aliasGEP() itself and tries to find a common modulus in the scales, so it can then check that the constant offset doesn't overlap under modular arithmetic. The second implementation has the right idea of what the general problem is, but effectively only considers power of two factors in the scales (while aliasSameBasePointerGEPs also works with non-pow2 struct sizes.)

What this patch does is to adjust the aliasGEP() implementation to instead find the largest common factor in all the scales (i.e. the GCD) and use that as the modulus.

Diff Detail

Event Timeline

nikic created this revision.Nov 8 2020, 1:49 AM
Herald added a project: Restricted Project. · View Herald TranscriptNov 8 2020, 1:49 AM
nikic requested review of this revision.Nov 8 2020, 1:49 AM
jdoerfert added inline comments.Nov 9 2020, 7:43 AM
llvm/lib/Analysis/BasicAliasAnalysis.cpp
1403

Maybe a comment above this to explain, I don't get the reasoning TBH.

Here is my idea of what is happening but it is not complete:

// X[0:N] is a vector of N+1 unknowns
GEP1 = GEP1BaseOffset + X[0:N] * GEP1.scale[0:N]
GCD = gcd(GEP1.scale[0], ..., GEP1.scale[N])
ModOffset = GEP1BaseOffset % GCD
// Every value of GEP1 looks like: GEP1 := Y * GCD + ModOffset
// and ModOffset < GCD.
Diff = GCD - ModOffset
if (ModOffset >=u V2Size  // I guess V2 fits in the size between Y * GCD and GEP1
 &&      Diff >=u V1Size  // this I don't understand.
  return NoAlias;
nikic updated this revision to Diff 303915.Nov 9 2020, 9:59 AM

Update comment.

nikic added inline comments.Nov 9 2020, 10:05 AM
llvm/lib/Analysis/BasicAliasAnalysis.cpp
1403

Does the new comment make sense to you? We have the second access at [0..V2Size) with the remaining [V2Size..GCD) not being accessed. We don't alias if the first access fits in there, i.e. ModOffset >= V2Size and ModOffset+V1Size <= GCD.

Yes, now I get it. I think I was also missing the "V2 has offset 0" precondition.

I like this, nice cleanup that adds more power. I think it is correct and good to go, would prefer a second opinion though.

asbirlea accepted this revision.Nov 17 2020, 5:10 PM

This makes sense to me too. The additional comment was helpful with the gcd/difference condition.

This revision is now accepted and ready to land.Nov 17 2020, 5:10 PM
This revision was automatically updated to reflect the committed changes.
fhahn added a comment.Mar 26 2021, 9:59 AM

Unfortunately we are seeing mis-compiles caused by this patch. I don't think think the use of modulo arithmetic here is safe, unless the index computation is guaranteed to not overflow.

For the example below, this patch determines no-alias for %gep.idx and %gep.6. But if %mul overflows (e.g. %idx == 52), %add == 6 and they are must alias. (https://godbolt.org/z/bWjz86K97)

I pushed a set of test cases in bcc8d80192f1 and put up a candidate fix D99424

; %gep.idx and %gep.6 must-alias if %mul overflows (e.g. %idx == 52).
define void @may_overflow_mul_add_i8([16 x i8]* %ptr, i8 %idx) {
  %mul = mul i8 %idx, 5
  %add = add i8 %mul, 2
  %gep.idx = getelementptr [16 x i8], [16 x i8]* %ptr, i32 0, i8 %add
  store i8 0, i8* %gep.idx, align 1
  %gep.6 = getelementptr [16 x i8], [16 x i8]* %ptr, i32 0, i32 6
  store i8 1, i8* %gep.6, align 1
  ret void
}