[llvm-dev] Using full restrict to map sets of variables to a universe

Jeroen Dobbelaere via llvm-dev llvm-dev at lists.llvm.org
Wed Jul 15 03:42:43 PDT 2020


Hi Kelvin, Tarique,

Based on what I understood from your explanation yesterday in the LLVM AA Technical call,
you can probably use following technique to map 'sets of global variables' to their own 'alias scope':

The (rough) c equivalent code looks like:

---
// Conceptual example of mapping an access to a generic set:
// set A
int tgtA01, tgtA02, tgtA03;
int * __restrict pSetA; // restrict to setA

// set B
int tgtB01, tgtB02, tgtB03;
int * __restrict pSetB; // restrict to setB


int* addSingleRestrictInfo(int* p, int * __restrict* pSet) {
  *pSet = p;
  return *pSet;
}

extern int _g2RestrictI; // must not be 'known'
int* addTwoRestrictInfo(int *p, int *__restrict* pSet0, int *__restrict* pSet1) {
  int * retP;
  if (_g2RestrictI == 0) {
    *pSet0 = p;
    retP = *pSet0;
  } else {
    *pSet1 = p;
    retP = *pSet1;
  }
  return retP;
}

void test(int *pA, int *pB, int *pAB) {
  *addSingleRestrictInfo(pA, &pSetA) = 42;
  *addSingleRestrictInfo(pB, &pSetB) = 43;
  *addTwoRestrictInfo(pAB, &pSetA, &pSetB) = 44;
}
----

It is based on:
- pSetA and pSetB representing two different 'object P', each specifying its own set of objects
- they work at global level (depending on the "unknown function" scope) -> after inlining, its information will still apply.
- addSingleRestrictInfo / addTwoRestrictInfo are used to introduce restrict information to a pointer


Of course, this conceptual example will introduce unnecessary stores to 'pSetA' and 'pSetB', so the actual llvm-ir you would want to produce for your fortran mapping should look something like:

----
; Function Attrs: nofree nounwind uwtable
define dso_local void @test(i32* %pA, i32* %pB, i32* %pAB) local_unnamed_addr #1 !noalias !14 {
entry:
  %0 = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i64(i32* %pA, i8* null, i32** nonnull @pSetA, i32** undef, i64 0, metadata !14) #3, !tbaa !5, !noalias !14
  store i32 42, i32* %pA, ptr_provenance i32* %0, align 4, !tbaa !12, !noalias !14
  %1 = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i64(i32* %pB, i8* null, i32** nonnull @pSetB, i32** undef, i64 0, metadata !14) #3, !tbaa !5, !noalias !14
  store i32 43, i32* %pB, ptr_provenance i32* %1, align 4, !tbaa !12, !noalias !14
  %2 = load i32, i32* @_g2RestrictI, align 4, !tbaa !12, !noalias !14                                           ;; NOTE: unnecessary load
  %cmp.i = icmp eq i32 %2, 0
  %3 = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i64(i32* %pAB, i8* null, i32** nonnull @pSetA, i32** undef, i64 0, metadata !14) #4, !tbaa !5, !noalias !14
  %4 = tail call i32* @llvm.provenance.noalias.p0i32.p0i8.p0p0i32.p0p0i32.i64(i32* %pAB, i8* null, i32** nonnull @pSetB, i32** undef, i64 0, metadata !14) #4, !tbaa !5, !noalias !14
  %prov.retP.0.i = select i1 %cmp.i,  i32* %3, i32*  %4
  store i32 44, i32* %pAB, ptr_provenance i32* %prov.retP.0.i, align 4, !tbaa !12, !noalias !14
  ret void
}
---- 

This still contains an unnecessary load from '_g2RestrictI' (which should be optimized away at MIR level).

For these kind of use cases, it makes sense to introduce a new intrinsic that joins the possible @llvm.provenance.noalias paths.
It should replace the load/cmp/select; something like:
    %prov.retP.0.i = call i32* @llvm.provenance.depends i32* %3, i32* %4

You also do not need to map 'pSetA' and 'pSetB' to real memory locations. After lowering to MIR, those dependencies should be gone.

Greetings,

Jeroen Dobbelaere



More information about the llvm-dev mailing list