[PATCH] D36647: [Polly][WIP] Scalar fully indexed expansion
Andreas Simbuerger via Phabricator via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 13 17:56:42 PDT 2017
simbuerg added a comment.
We talked about it on Friday but I think I can help out more with a small writeup.
First to your SCoP. The accesses to 'add_lcssa_phi' result from LLVM transforming the loop into "loop closed static single assignment" form, i.e.,
providing a single definition for values that live accross the loop boundaries outside of the loop. LLVM does this by inserting redundant PHI nodes for these values.
That's why you see a PHI node with a single operand in the LLVM IR right after the innermost loop.
You got (roughly) this after LCSSA (actually only tmp_lcssa):
#define Ni 10000
#define Nj 10000
void mse(double A[Ni], double B[Nj]) {
int i,j;
double tmp = 6;
for (i = 0; i < Ni; i++) {
// Either outside, or previous
tmp_0 = PHI(tmp, tmp_lcssa);
for (int j = 0; j<Nj; j++) {
// Either outside, or previous
tmp_1 = PHI(tmp_0, tmp_2);
tmp_2 = tmp_1 + 2;
}
// tmp_2's lifetime exceeds the innermost loop
tmp_lcssa = PHI(tmp_2);
B[i] = tmp_lcssa;
}
}
Polly then models each PHI node as a 'load' of new memory that was written at the end of the incoming block (numbering is differnt, sorry).
Statements {
Stmt_for_body
ReadAccess := [Reduction Type: NONE] [Scalar: 1] // Load current value of 'tmp' (PHI_0)
{ Stmt_for_body[i0] -> MemRef_tmp_04__phi[] };
MustWriteAccess := [Reduction Type: NONE] [Scalar: 1] // Write for PHI_1
{ Stmt_for_body[i0] -> MemRef_tmp_11__phi[] };
Stmt_for_inc
MustWriteAccess := [Reduction Type: NONE] [Scalar: 1] // Write for PHI_1
{ Stmt_for_inc[i0, i1] -> MemRef_tmp_11__phi[] };
ReadAccess := [Reduction Type: NONE] [Scalar: 1] // Load current value of 'tmp' (PHI_1)
{ Stmt_for_inc[i0, i1] -> MemRef_tmp_11__phi[] };
MustWriteAccess := [Reduction Type: NONE] [Scalar: 1] // Write for PHI_2
{ Stmt_for_inc[i0, i1] -> MemRef_add_lcssa__phi[] };
Stmt_for_end
MustWriteAccess := [Reduction Type: NONE] [Scalar: 1] // Write for PHI_0
{ Stmt_for_end[i0] -> MemRef_tmp_04__phi[] };
ReadAccess := [Reduction Type: NONE] [Scalar: 1] // Load current value of 'tmp_lcssa' (PHI_2)
{ Stmt_for_end[i0] -> MemRef_add_lcssa__phi[] };
MustWriteAccess := [Reduction Type: NONE] [Scalar: 0]
{ Stmt_for_end[i0] -> MemRef_B[i0] };
}
Now when you switch the roles of Reads and Writes for PHI nodes you should always get a single read and one or more writes (see above). After PHI expansion it should virtually look like this:
void mse(double A[Ni], double B[Nj]) {
int i,j;
double tmp = 6;
for (i = 0; i < Ni; i++) {
// Either outside, or previous
tmp_0 = PHI(tmp, tmp_lcssa); // READ: tmp_0[i] (Problem: Nobody is writing this value
(Copy in required), you should actually bail out on this example).
// WRITE: tmp_1[i][0] (Initial value).
for (int j = 0; j<Nj; j++) {
// Either outside, or previous
tmp_1 = PHI(tmp_0, tmp_2); // READ: tmp_1[i][j]
tmp_2 = tmp_1 + 2;
// WRITE: tmp_1[i][j+1] (We need to write the current value into the 'next' iteration because we will read the value from there).
// WRITE: tmp_lcssa[i] (Got this one from LCSSA)
}
tmp_lcssa = PHI(tmp_2); // READ: tmp_lcssa[i]
B[i] = tmp_lcssa;
// WRITE: tmp_0[i+1] (We need to write the current value into the 'next' iteration because we will read the value from there).
}
}
I can't see yet why your code specifically does the wrong thing, but I hope this helps anyway. @everyone correct me if i missed something, it was late :-)
================
Comment at: lib/Transform/MaximalStaticExpansion.cpp:398
- assert(AllWrites.size() == 1);
+ // If MemoryKind::Value of MemoryKind::Array
+ if (SAI->isValueKind() || SAI->isArrayKind()) {
----------------
or not of.
Besides, no need to verbally describe the if statement. Just remove the comment.
================
Comment at: lib/Transform/MaximalStaticExpansion.cpp:406
+ for (MemoryAccess *MA : AllReads)
+ expandAccordingToDependences(S, MA, Dependences, ExpandedArray, true);
+ }
----------------
Why 'expandAccordingToDependences'?
Style: I would handle that loop inside expandRead(s) and pass the Reads as parameter (You have to handle them all anyway inside the function, might as well iterate in there).
I think what would make everything clearer (to me at least) would be a naming as follows:
expandAccess (Read/Write): Your expandAccordingToDomain.
redirect/mapAccess (Read/Write): Your expandAccordingToDependences.
The first does the array allocation, the second redirects/maps the access(es) to the new array.
https://reviews.llvm.org/D36647
More information about the llvm-commits
mailing list