This is an archive of the discontinued LLVM Phabricator instance.

[OPENMP]Add support for analysis of if clauses.
ClosedPublic

Authored by ABataev on Jul 12 2019, 10:10 AM.

Diff Detail

Repository
rL LLVM

Event Timeline

ABataev created this revision.Jul 12 2019, 10:10 AM
Herald added a project: Restricted Project. · View Herald TranscriptJul 12 2019, 10:10 AM
NoQ accepted this revision.Jul 12 2019, 3:56 PM

Looks great! Thank you for improving the tests even further.

test/Analysis/cfg-openmp.cpp
58–67 ↗(On Diff #209520)

I'm slowly updating my mental model of these CFGs. Just to confirm my understanding - tried the following example:

int main(int argc, char **argv) {
  int x = 0;
#pragma omp for
  for (int i = 0; i < 10; ++i)
    x += argv[i];
}

The CFG was as follows:

 1: 0
 2: int x = 0;
 3: x
 4: argv
 5: [B1.4] (ImplicitCastExpr, LValueToRValue, char **)
 6: i
 7: [B1.6] (ImplicitCastExpr, LValueToRValue, int)
 8: [B1.5][[B1.7]]
 9: [B1.8] (ImplicitCastExpr, LValueToRValue, char *)
10: [B1.3] += [B1.9]
11: #pragma omp for
  for (int i = 0; i < 10; ++i)
      [B1.10];

Do i understand correctly that [B1.10] aka argv[0] is going to act like an "argument" to the "outlined function" and then it's going to be re-used (as if it was a "local" "variable") on subsequent iterations of the loop (i.e., assigned values argv[1], ..., argv[9])? I.e., the "function" is going to be responsible for computing argv[1] and storing it in the "parameter variable" (OMPCapturedExprDecl which is a sub-class of VarDecl) that previously contained argv[0], but it's not responsible for computing argv[0] itself, right?

This revision is now accepted and ready to land.Jul 12 2019, 3:56 PM
ABataev marked an inline comment as done.Jul 15 2019, 7:11 AM

Thanks for the review!

test/Analysis/cfg-openmp.cpp
58–67 ↗(On Diff #209520)

argv here is the shared variable, so it is passed by reference to the "outlined function". We do not create local copy of this variable here, we use the original argv in the "outlined function".
you can consider this code as something like this:

char **argv;
int x = 0;
outlined(x, argv);
...

void outlined(char **&argv, int &x) {
  for(int i =0; i < 10; ++i)
  x += argv[i];
}

This is very schematic but good enough to understand how it works.

OMPCapturedExprDecl is used only in some rare cases, when we need to pass the expression to the outlined region. For example:

#pragma omp target parallel if(a+b)
<body>

After codegen it must look like this:

<OMPCapturedExprDecl> int .captured_expr. = a+b;
if (.captured_expr.) {
  offload target_outlined(.captured_expr.)
} else {
  call on host target_outlined(.captured_expr.)
}
...
void target_outlined(int .captured_expr.) {
  if (.captured_expr.)
    parallel outlined();
  else
    serialized outlined();
}
...
void outined() {
<body>;
}

In this case if clause is applied to both, target and parallel constructs. We could capture variables a and b and calculate a+b inside of the target region but this is not effective. Instead, it is better to capture the result of a+b condition into the special variable and capture only this single variable by value (the value of clauses in most cases must be pre-evaluated before the real execution of the OpenMP construct). Does it make it a little bit clearer?
OMPCapturedExprDecl is used only in some rare cases, only for the complex combined constructs (which consists of several simple constructs) and only for the clauses, which require expression evaluation before entering the construct.

This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptJul 15 2019, 7:48 AM
NoQ added inline comments.Jul 19 2019, 2:34 PM
test/Analysis/cfg-openmp.cpp
58–67 ↗(On Diff #209520)

I think i managed to consume this, thanks!! I'll ask more questions when i have them.