-
Notifications
You must be signed in to change notification settings - Fork 15k
[delinearize] Extract array dimensions from alloca and global declarations #156342
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-llvm-analysis @llvm/pr-subscribers-llvm-transforms Author: Sebastian Pop (sebpop) ChangesThis implements an alternative to extract multi-dimensional array information The functionality for GEP delinearization remains in this patch such that we can Next patch removes the code for GEP delinearization. Patch is 227.84 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/156342.diff 92 Files Affected:
diff --git a/llvm/include/llvm/Analysis/Delinearization.h b/llvm/include/llvm/Analysis/Delinearization.h
index 434cfb61699d6..0dafac9102247 100644
--- a/llvm/include/llvm/Analysis/Delinearization.h
+++ b/llvm/include/llvm/Analysis/Delinearization.h
@@ -112,6 +112,13 @@ void delinearize(ScalarEvolution &SE, const SCEV *Expr,
SmallVectorImpl<const SCEV *> &Subscripts,
SmallVectorImpl<const SCEV *> &Sizes, const SCEV *ElementSize);
+/// Same as delinearize. TODO: only use delinearize() instead of other internal
+/// functions.
+bool delinearizeUsingArrayInfo(ScalarEvolution &SE, const SCEV *AccessFn,
+ SmallVectorImpl<const SCEV *> &Subscripts,
+ SmallVectorImpl<const SCEV *> &Sizes,
+ const SCEV *ElementSize);
+
/// Compute the dimensions of fixed size array from \Expr and save the results
/// in \p Sizes.
bool findFixedSizeArrayDimensions(ScalarEvolution &SE, const SCEV *Expr,
@@ -155,6 +162,17 @@ bool getIndexExpressionsFromGEP(ScalarEvolution &SE,
SmallVectorImpl<const SCEV *> &Subscripts,
SmallVectorImpl<int> &Sizes);
+/// Compute access functions for each subscript in a delinearized array access.
+void computeAccessFunctions(ScalarEvolution &SE, const SCEV *Expr,
+ SmallVectorImpl<const SCEV *> &Subscripts,
+ SmallVectorImpl<const SCEV *> &Sizes,
+ const SCEV *ElementSize);
+
+/// Backward compatibility wrapper for computeAccessFunctions.
+void computeAccessFunctions(ScalarEvolution &SE, const SCEV *Expr,
+ SmallVectorImpl<const SCEV *> &Subscripts,
+ SmallVectorImpl<const SCEV *> &Sizes);
+
/// Implementation of fixed size array delinearization. Try to delinearize
/// access function for a fixed size multi-dimensional array, by deriving
/// subscripts from GEP instructions. Returns true upon success and false
diff --git a/llvm/lib/Analysis/Delinearization.cpp b/llvm/lib/Analysis/Delinearization.cpp
index 762d9191aab1e..1d07d632fe787 100644
--- a/llvm/lib/Analysis/Delinearization.cpp
+++ b/llvm/lib/Analysis/Delinearization.cpp
@@ -14,15 +14,18 @@
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/Delinearization.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Analysis/ScalarEvolutionDivision.h"
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
@@ -38,6 +41,10 @@ static cl::opt<bool> UseFixedSizeArrayHeuristic(
cl::desc("When printing analysis, use the heuristic for fixed-size arrays "
"if the default delinearizetion fails."));
+static cl::opt<bool> useGEPToDelinearize(
+ "use-gep-to-delinearize", cl::init(true), cl::Hidden,
+ cl::desc("validate both delinearization methods match."));
+
// Return true when S contains at least an undef value.
static inline bool containsUndefs(const SCEV *S) {
return SCEVExprContains(S, [](const SCEV *S) {
@@ -182,7 +189,7 @@ void llvm::collectParametricTerms(ScalarEvolution &SE, const SCEV *Expr,
LLVM_DEBUG({
dbgs() << "Strides:\n";
for (const SCEV *S : Strides)
- dbgs() << *S << "\n";
+ dbgs() << " " << *S << "\n";
});
for (const SCEV *S : Strides) {
@@ -193,7 +200,7 @@ void llvm::collectParametricTerms(ScalarEvolution &SE, const SCEV *Expr,
LLVM_DEBUG({
dbgs() << "Terms:\n";
for (const SCEV *T : Terms)
- dbgs() << *T << "\n";
+ dbgs() << " " << *T << "\n";
});
SCEVCollectAddRecMultiplies MulCollector(Terms, SE);
@@ -294,7 +301,7 @@ void llvm::findArrayDimensions(ScalarEvolution &SE,
LLVM_DEBUG({
dbgs() << "Terms:\n";
for (const SCEV *T : Terms)
- dbgs() << *T << "\n";
+ dbgs() << " " << *T << "\n";
});
// Remove duplicates.
@@ -325,7 +332,7 @@ void llvm::findArrayDimensions(ScalarEvolution &SE,
LLVM_DEBUG({
dbgs() << "Terms after sorting:\n";
for (const SCEV *T : NewTerms)
- dbgs() << *T << "\n";
+ dbgs() << " " << *T << "\n";
});
if (NewTerms.empty() || !findArrayDimensionsRec(SE, NewTerms, Sizes)) {
@@ -339,13 +346,14 @@ void llvm::findArrayDimensions(ScalarEvolution &SE,
LLVM_DEBUG({
dbgs() << "Sizes:\n";
for (const SCEV *S : Sizes)
- dbgs() << *S << "\n";
+ dbgs() << " " << *S << "\n";
});
}
void llvm::computeAccessFunctions(ScalarEvolution &SE, const SCEV *Expr,
SmallVectorImpl<const SCEV *> &Subscripts,
- SmallVectorImpl<const SCEV *> &Sizes) {
+ SmallVectorImpl<const SCEV *> &Sizes,
+ const SCEV *ElementSize) {
// Early exit in case this SCEV is not an affine multivariate function.
if (Sizes.empty())
return;
@@ -354,20 +362,32 @@ void llvm::computeAccessFunctions(ScalarEvolution &SE, const SCEV *Expr,
if (!AR->isAffine())
return;
+ if (ElementSize->isZero())
+ return;
+
+ // Clear output vector.
+ Subscripts.clear();
+
+ LLVM_DEBUG(dbgs() << "\ncomputeAccessFunctions\n"
+ << "Linearized Memory Access Function: " << *Expr << "\n");
+
const SCEV *Res = Expr;
int Last = Sizes.size() - 1;
+
for (int i = Last; i >= 0; i--) {
- const SCEV *Q, *R;
- SCEVDivision::divide(SE, Res, Sizes[i], &Q, &R);
+ const SCEV *Size = Sizes[i];
+ if (Size->isZero())
+ continue;
+ const SCEV *Q, *R;
+ SCEVDivision::divide(SE, Res, Size, &Q, &R);
LLVM_DEBUG({
- dbgs() << "Res: " << *Res << "\n";
- dbgs() << "Sizes[i]: " << *Sizes[i] << "\n";
- dbgs() << "Res divided by Sizes[i]:\n";
- dbgs() << "Quotient: " << *Q << "\n";
- dbgs() << "Remainder: " << *R << "\n";
+ dbgs() << "Computing 'MemAccFn / Sizes[" << i << "]':\n";
+ dbgs() << " MemAccFn: " << *Res << "\n";
+ dbgs() << " Sizes[" << i << "]: " << *Size << "\n";
+ dbgs() << " Quotient (Leftover): " << *Q << "\n";
+ dbgs() << " Remainder (Subscript Access Function): " << *R << "\n";
});
-
Res = Q;
// Do not record the last subscript corresponding to the size of elements in
@@ -385,22 +405,169 @@ void llvm::computeAccessFunctions(ScalarEvolution &SE, const SCEV *Expr,
}
// Record the access function for the current subscript.
+ LLVM_DEBUG(dbgs() << "Subscripts push_back Remainder: " << *R << "\n");
Subscripts.push_back(R);
}
- // Also push in last position the remainder of the last division: it will be
- // the access function of the innermost dimension.
- Subscripts.push_back(Res);
+ // Also push in last position the quotient "Res = Q" of the last division: it
+ // will be the access function of the outermost array dimension.
+ if (!Res->isZero()) {
+ // This is only needed when the outermost array size is not known. Res = 0
+ // when the outermost array dimension is known, as for example when reading
+ // array sizes from a local or global declaration.
+ Subscripts.push_back(Res);
+ LLVM_DEBUG(dbgs() << "Subscripts push_back Res: " << *Res << "\n");
+ }
std::reverse(Subscripts.begin(), Subscripts.end());
LLVM_DEBUG({
dbgs() << "Subscripts:\n";
for (const SCEV *S : Subscripts)
- dbgs() << *S << "\n";
+ dbgs() << " " << *S << "\n";
+ dbgs() << "\n";
});
}
+/// Backward compatibility wrapper for the old 4-parameter version.
+void llvm::computeAccessFunctions(ScalarEvolution &SE, const SCEV *Expr,
+ SmallVectorImpl<const SCEV *> &Subscripts,
+ SmallVectorImpl<const SCEV *> &Sizes) {
+ // Use the element size from the last element in Sizes array (legacy behavior)
+ if (Sizes.empty()) {
+ Subscripts.clear();
+ return;
+ }
+ const SCEV *ElementSize = Sizes.back();
+ computeAccessFunctions(SE, Expr, Subscripts, Sizes, ElementSize);
+}
+
+/// Extract array dimensions from alloca or global variable declarations.
+/// Returns true if array dimensions were successfully extracted.
+static bool
+extractArrayInfoFromAllocaOrGlobal(ScalarEvolution &SE, Value *BasePtr,
+ SmallVectorImpl<const SCEV *> &Sizes,
+ const SCEV *ElementSize) {
+ // Clear output vector.
+ Sizes.clear();
+
+ LLVM_DEBUG(
+ dbgs() << "extractArrayInfoFromAllocaOrGlobal called with BasePtr: "
+ << *BasePtr << "\n");
+
+ // Distinguish between simple array accesses and complex pointer arithmetic.
+ // Only apply array_info extraction to direct array accesses to avoid
+ // incorrect delinearization of complex pointer arithmetic patterns.
+ if (auto *GEP = dyn_cast<GetElementPtrInst>(BasePtr)) {
+ // Check if this is a simple array access pattern: GEP [N x T]* @array, 0,
+ // idx This represents direct indexing like array[i], which should use array
+ // dimensions.
+ if (GEP->getNumIndices() == 2) {
+ auto *FirstIdx = dyn_cast<ConstantInt>(GEP->getOperand(1));
+ if (FirstIdx && FirstIdx->isZero()) {
+ // Simple array access: extract dimensions from the underlying array
+ // type
+ Value *Source = GEP->getPointerOperand()->stripPointerCasts();
+ return extractArrayInfoFromAllocaOrGlobal(SE, Source, Sizes,
+ ElementSize);
+ }
+ }
+ // Complex GEPs like (&array[offset])[index] represent pointer arithmetic,
+ // not simple array indexing. These should be handled by parametric
+ // delinearization to preserve the linearized byte-offset semantics rather
+ // than treating them as multidimensional array accesses.
+ return false;
+ }
+
+ // Check if BasePtr is from an alloca instruction.
+ Type *ElementType = nullptr;
+ if (auto *AI = dyn_cast<AllocaInst>(BasePtr)) {
+ ElementType = AI->getAllocatedType();
+ LLVM_DEBUG(dbgs() << "Found alloca with type: " << *ElementType << "\n");
+ } else if (auto *GV = dyn_cast<GlobalVariable>(BasePtr)) {
+ ElementType = GV->getValueType();
+ LLVM_DEBUG(dbgs() << "Found global variable with type: " << *ElementType
+ << "\n");
+ } else {
+ LLVM_DEBUG(dbgs() << "No alloca or global found for base pointer\n");
+ return false;
+ }
+
+ // Extract dimensions from nested array types.
+ Type *I64Ty = Type::getInt64Ty(SE.getContext());
+
+ while (auto *ArrayTy = dyn_cast<ArrayType>(ElementType)) {
+ uint64_t Size = ArrayTy->getNumElements();
+ const SCEV *SizeSCEV = SE.getConstant(I64Ty, Size);
+ Sizes.push_back(SizeSCEV);
+ ElementType = ArrayTy->getElementType();
+ LLVM_DEBUG(dbgs() << " Found array dimension: " << Size << "\n");
+ }
+
+ if (Sizes.empty()) {
+ LLVM_DEBUG(dbgs() << "No array dimensions found in type\n");
+ return false;
+ }
+
+ // Add element size as the last element for computeAccessFunctions algorithm.
+ Sizes.push_back(ElementSize);
+
+ LLVM_DEBUG({
+ dbgs() << "Extracted array info from alloca/global for base pointer "
+ << *BasePtr << "\n";
+ dbgs() << "Dimensions: ";
+ for (const SCEV *Size : Sizes)
+ dbgs() << *Size << " ";
+ dbgs() << "\n";
+ });
+
+ return true;
+}
+
+bool llvm::delinearizeUsingArrayInfo(ScalarEvolution &SE, const SCEV *AccessFn,
+ SmallVectorImpl<const SCEV *> &Subscripts,
+ SmallVectorImpl<const SCEV *> &Sizes,
+ const SCEV *ElementSize) {
+ // Clear output vectors.
+ Subscripts.clear();
+ Sizes.clear();
+
+ const SCEVUnknown *BasePointer =
+ dyn_cast<SCEVUnknown>(SE.getPointerBase(AccessFn));
+ if (!BasePointer) {
+ LLVM_DEBUG(dbgs() << "no BasePointer for AccessFn: " << *AccessFn << "\n");
+ return false;
+ }
+
+ Value *BasePtr = BasePointer->getValue();
+
+ // Extract array dimensions from alloca or global declarations.
+ if (!extractArrayInfoFromAllocaOrGlobal(SE, BasePtr, Sizes, ElementSize))
+ return false;
+
+ // Get the full SCEV expression and subtract the base pointer to get
+ // offset-only expression.
+ const SCEV *Expr = SE.getMinusSCEV(AccessFn, BasePointer);
+
+ computeAccessFunctions(SE, Expr, Subscripts, Sizes, ElementSize);
+ if (Sizes.empty() || Subscripts.empty())
+ return false;
+
+ // Validate dimension consistency: subscripts should match array dimensions
+ // (Sizes includes element size as last element, so array dimensions =
+ // Sizes.size() - 1)
+ unsigned ArrayDims = Sizes.size() - 1;
+ if (Subscripts.size() != ArrayDims) {
+ LLVM_DEBUG(
+ dbgs() << "delinearizeUsingArrayInfo: Dimension mismatch - "
+ << Subscripts.size() << " subscripts for " << ArrayDims
+ << " array dimensions. Falling back to parametric method.\n");
+ return false;
+ }
+
+ return true;
+}
+
/// Splits the SCEV into two vectors of SCEVs representing the subscripts and
/// sizes of an array access. Returns the remainder of the delinearization that
/// is the offset start of the array. The SCEV->delinearize algorithm computes
@@ -454,36 +621,35 @@ void llvm::delinearize(ScalarEvolution &SE, const SCEV *Expr,
SmallVectorImpl<const SCEV *> &Subscripts,
SmallVectorImpl<const SCEV *> &Sizes,
const SCEV *ElementSize) {
- // First step: collect parametric terms.
+ // Clear output vectors.
+ Subscripts.clear();
+ Sizes.clear();
+
+ // Try array_info extraction.
+ if (delinearizeUsingArrayInfo(SE, Expr, Subscripts, Sizes, ElementSize))
+ return;
+ LLVM_DEBUG(dbgs() << "delinearize falling back to parametric method\n");
+
+ // Fall back to parametric delinearization.
+ const SCEVUnknown *BasePointer =
+ dyn_cast<SCEVUnknown>(SE.getPointerBase(Expr));
+ if (BasePointer)
+ Expr = SE.getMinusSCEV(Expr, BasePointer);
+
SmallVector<const SCEV *, 4> Terms;
collectParametricTerms(SE, Expr, Terms);
if (Terms.empty())
return;
- // Second step: find subscript sizes.
+ // Find subscript sizes.
findArrayDimensions(SE, Terms, Sizes, ElementSize);
if (Sizes.empty())
return;
- // Third step: compute the access functions for each subscript.
- computeAccessFunctions(SE, Expr, Subscripts, Sizes);
-
- if (Subscripts.empty())
- return;
-
- LLVM_DEBUG({
- dbgs() << "succeeded to delinearize " << *Expr << "\n";
- dbgs() << "ArrayDecl[UnknownSize]";
- for (const SCEV *S : Sizes)
- dbgs() << "[" << *S << "]";
-
- dbgs() << "\nArrayRef";
- for (const SCEV *S : Subscripts)
- dbgs() << "[" << *S << "]";
- dbgs() << "\n";
- });
+ // Compute the access functions for each subscript.
+ computeAccessFunctions(SE, Expr, Subscripts, Sizes, ElementSize);
}
static std::optional<APInt> tryIntoAPInt(const SCEV *S) {
@@ -492,6 +658,21 @@ static std::optional<APInt> tryIntoAPInt(const SCEV *S) {
return std::nullopt;
}
+/// Convert cached SCEV sizes to int sizes for compatibility.
+/// TODO: Remove this after we remove GEP delinearization.
+static void convertSCEVSizesToIntSizes(ArrayRef<const SCEV *> SCEVSizes,
+ SmallVectorImpl<int> &Sizes) {
+ for (const SCEV *S : SCEVSizes) {
+ if (auto *Const = dyn_cast<SCEVConstant>(S)) {
+ const APInt &APVal = Const->getAPInt();
+ if (APVal.isSignedIntN(32)) {
+ int intValue = APVal.getSExtValue();
+ Sizes.push_back(intValue);
+ }
+ }
+ }
+}
+
/// Collects the absolute values of constant steps for all induction variables.
/// Returns true if we can prove that all step recurrences are constants and \p
/// Expr is divisible by \p ElementSize. Each step recurrence is stored in \p
@@ -646,6 +827,9 @@ bool llvm::delinearizeFixedSizeArray(ScalarEvolution &SE, const SCEV *Expr,
SmallVectorImpl<const SCEV *> &Subscripts,
SmallVectorImpl<const SCEV *> &Sizes,
const SCEV *ElementSize) {
+ // Clear output vectors.
+ Subscripts.clear();
+ Sizes.clear();
// First step: find the fixed array size.
SmallVector<uint64_t, 4> ConstSizes;
@@ -659,7 +843,7 @@ bool llvm::delinearizeFixedSizeArray(ScalarEvolution &SE, const SCEV *Expr,
Sizes.push_back(SE.getConstant(Expr->getType(), Size));
// Second step: compute the access functions for each subscript.
- computeAccessFunctions(SE, Expr, Subscripts, Sizes);
+ computeAccessFunctions(SE, Expr, Subscripts, Sizes, ElementSize);
return !Subscripts.empty();
}
@@ -671,6 +855,7 @@ bool llvm::getIndexExpressionsFromGEP(ScalarEvolution &SE,
assert(Subscripts.empty() && Sizes.empty() &&
"Expected output lists to be empty on entry to this function.");
assert(GEP && "getIndexExpressionsFromGEP called with a null GEP");
+ LLVM_DEBUG(dbgs() << "\nGEP to delinearize: " << *GEP << "\n");
Type *Ty = nullptr;
bool DroppedFirstDim = false;
for (unsigned i = 1; i < GEP->getNumOperands(); i++) {
@@ -683,28 +868,43 @@ bool llvm::getIndexExpressionsFromGEP(ScalarEvolution &SE,
continue;
}
Subscripts.push_back(Expr);
+ LLVM_DEBUG(dbgs() << "Subscripts push_back: " << *Expr << "\n");
continue;
}
auto *ArrayTy = dyn_cast<ArrayType>(Ty);
if (!ArrayTy) {
+ LLVM_DEBUG(dbgs() << "GEP delinearize failed: " << *Ty
+ << " is not an array type.\n");
Subscripts.clear();
Sizes.clear();
return false;
}
Subscripts.push_back(Expr);
+ LLVM_DEBUG(dbgs() << "Subscripts push_back: " << *Expr << "\n");
if (!(DroppedFirstDim && i == 2))
Sizes.push_back(ArrayTy->getNumElements());
Ty = ArrayTy->getElementType();
}
+ LLVM_DEBUG({
+ dbgs() << "Subscripts:\n";
+ for (const SCEV *S : Subscripts)
+ dbgs() << *S << "\n";
+ dbgs() << "\n";
+ });
+
return !Subscripts.empty();
}
bool llvm::tryDelinearizeFixedSizeImpl(
ScalarEvolution *SE, Instruction *Inst, const SCEV *AccessFn,
SmallVectorImpl<const SCEV *> &Subscripts, SmallVectorImpl<int> &Sizes) {
+ // Clear output vectors.
+ Subscripts.clear();
+ Sizes.clear();
+
Value *SrcPtr = getLoadStorePointerOperand(Inst);
// Check the simple case where the array dimensions are fixed size.
@@ -712,7 +912,140 @@ bool llvm::tryDelinearizeFixedSizeImpl(
if (!SrcGEP)
return false;
- getIndexExpressionsFromGEP(*SE, SrcGEP, Subscripts, Sizes);
+ // When flag useGEPToDelinearize is false, delinearize only using array_info.
+ if (!useGEPToDelinearize) {
+ SmallVector<const SCEV *, 4> SCEVSizes;
+ const SCEV *ElementSize = SE->getElementSize(Inst);
+ if (!delinearizeUsingArrayInfo(*SE, AccessFn, Subscripts, SCEVSizes,
+ ElementSize))
+ return false;
+
+ // TODO: Remove the following code. Convert SCEV sizes to int sizes. This
+ // conversion is only needed as long as getIndexExpressionsFromGEP is still
+ // around. Remove this code and change the interface of
+ // tryDelinearizeFixedSizeImpl to take a SmallVectorImpl<const SCEV *>
+ // &Sizes.
+ convertSCEVSizesToIntSizes(SCEVSizes, Sizes);
+ return true;
+ }
+
+ // TODO: Remove all the following code once we are satisfied with array_info.
+ // Run both methods when useGEPToDelinearize is true: validation is enabled.
+
+ // Store results from both methods.
+ SmallVector<const SCEV *, 4> GEPSubscripts, ArrayInfoSubscripts;
+ SmallVector<int, 4> GEPSizes, ArrayInfoSizes;
+
+ // GEP-based delinearization.
+ bool GEPSuccess =
+ getIndexExpressionsFromGEP(*SE, SrcGEP, GEPSubscripts, GEPSizes);
+
+ // Array_info delinearization.
+ SmallVector<const SCEV *, 4> SCEVSizes;
+ const SCEV *ElementSize = SE->getElementSize(Ins...
[truncated]
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've not checked the details of this change, but I have a high-level question: Is it allowed to use the type information of alloca
s and/or global variable to guide optimization heuristics?
Why not? I find this solution more suited than other approaches because we don't have to change LLVM's IR. There's an alternative solution that requires an RFC to change LLVM IR: see comment on the first patch of the series.
|
It would be best to consult an LLVM IR expert. I'm not an expert, but from my perspective, using the type arguments of allocas and/or global variables doesn't seem to significantly different from driving heuristics based on those of GEPs. |
…tions This implements an alternative to extract multi-dimensional array information directly from alloca instructions and global variable declarations instead of relying on parsing the information from GEP instructions. The functionality for GEP delinearization remains in this patch such that we can evaluate regressions of the array_info extraction. Next patch removes the code for GEP delinearization.
65e9cfc
to
62ac07f
Compare
This implements an alternative to extract multi-dimensional array information
directly from alloca instructions and global variable declarations instead of
relying on parsing the information from GEP instructions.
The functionality for GEP delinearization remains in this patch such that we can
evaluate regressions of the array_info extraction.
Next patch removes the code for GEP delinearization.