This is an archive of the discontinued LLVM Phabricator instance.

Use memory region declaration intrinsic when generating code for array subscripts
Needs ReviewPublic

Authored by simeon on Jun 6 2023, 7:51 AM.

Details

Summary

As an alternative to https://reviews.llvm.org/D150192, we consider using the memory region declaration intrinsic, introduced in https://reviews.llvm.org/D115274, as another way to propagate the semantics of C/C++ array-indexing rules to LLVM IR.

For example, given the following C source

int f(long i) {
  int A[100];
  return A[i];
}

where we would previously emit the following for the indexed array access

%0 = load i64, ptr %X.addr, align 8
%arrayidx = getelementptr inbounds [100 x i32], ptr %A, i64 0, i64 %0
%1 = load i32, ptr %arrayidx, align 4

we now emit

%0 = load i64, ptr %X.addr, align 8
%arrayidx = getelementptr inbounds [100 x i32], ptr %A, i64 0, i64 0
%arrayidx.bounded = call ptr @llvm.memory.region.decl.p0(ptr %arrayidx, i64 0, i64 6400)
%arrayidx1 = getelementptr inbounds i32, ptr %arrayidx.bounded, i64 %0
%1 = load i32, ptr %arrayidx1, align 4

This patch is only only a proof-of-concept at this point, intended to gather feedback and bring https://reviews.llvm.org/D115274 back into focus. As was mentioned there (and as seen from test results), completing this patch would require patching a lot of optimizations. For example, InstructionCombiningPass works with GetElementPtrInsts directly and will not recognize an intrinsic call as such. Many other passes such as IndVarSimplifyPass, EarlyCSE, etc., routinely use a switch case or call isa<GetElementPtrInst>() to detect GEPs.

We'd be fine with doing that sort of work, if we get some initial thumbs up from the community that this is the way to go. Of course, we're also open to trying a different approach if it avoids the apparently unavoidable complexity of this one.

An example of alias analysis based on the intrinsic can be seen in this independent diff: https://reviews.llvm.org/differential/diff/528871/.

Depends on D115274

Diff Detail

Event Timeline

simeon created this revision.Jun 6 2023, 7:51 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 6 2023, 7:51 AM
Herald added a subscriber: StephenFan. · View Herald Transcript
simeon requested review of this revision.Jun 6 2023, 7:51 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 6 2023, 7:51 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
simeon edited the summary of this revision. (Show Details)Jun 6 2023, 8:21 AM
simeon edited the summary of this revision. (Show Details)Jun 13 2023, 1:26 AM

If we are going to do this at all, I think this is roughly what it should look like. Potential issues you might run into:

  • The compile-time overhead of creating a bunch of extra intrinsics might be significant. Maybe we can mitigate to some extent by avoiding emitting the intrinsic in simple cases where it doesn't actually help (constant indexes?).
  • User code might not actually obey the language rules; do we have any sanitizer that checks if user code trips over this?
  • Not sure how this interacts with full restrict and related proposals.
  • The compile-time overhead of creating a bunch of extra intrinsics might be significant. Maybe we can mitigate to some extent by avoiding emitting the intrinsic in simple cases where it doesn't actually help (constant indexes?).

Yes, that would make sense.

  • User code might not actually obey the language rules; do we have any sanitizer that checks if user code trips over this?

I believe AddressSanitizer should be able to detect out-of-bounds accesses.

  • Not sure how this interacts with full restrict and related proposals.

So far as alias analysis goes, the two approaches should be orthogonal, but nevertheless we'd still have to go through the usual procedure of modifying the direct GetElementPtrInst checks and casts used by such patches.

  • Not sure how this interacts with full restrict and related proposals.

the full restrict PropagateAndConvertNoAlias pass will need to learn about it, but that should be trivial.

  • User code might not actually obey the language rules; do we have any sanitizer that checks if user code trips over this?

I believe AddressSanitizer should be able to detect out-of-bounds accesses.

asan will detect if you index completely outside a memory allocation, but it won't detect if you do something like struct S { int x[10]; int y; } s = {}; return s->x[10];.

simeon updated this revision to Diff 547763.Aug 7 2023, 6:58 AM

The patch now includes the changes that need to be made to the optimization passes we observed to be most negatively affected by the introduction of the intrinsic, primarily InstCombine and SROA. However, it is not comprehensive: we also observed missed optimization opportunities in other passes such as GlobalOpt and MergedLoadStoreMotionPass.

A large number of hand-written unit tests need to be updated by hand but as they are very sensitive to the smallest change in the definition of the intrinsic, I am postponing this until we have some initial approval.