[Fortran Support] Add pattern match for Fortran Arrays that are parameters.

- This breaks the previous assumption that Fortran Arrays are `GlobalValue`.

- The names of functions were getting unwieldy. So, I renamed the
Fortran related functions.

Differential Revision: https://reviews.llvm.org/D33075

llvm-svn: 303040
This commit is contained in:
Siddharth Bhat 2017-05-15 08:41:30 +00:00
parent 9746f817ea
commit 0fe7231a2f
5 changed files with 197 additions and 100 deletions

View File

@ -57,28 +57,75 @@ class ScopBuilder {
// Methods for pattern matching against Fortran code generated by dragonegg.
// @{
/// Try to pattern match and find the array descriptor structure in case of a
/// fortran array accesss. succeeds on load/store into a fortran array that
/// has been allocated.
/// Try to match for the descriptor of a Fortran Array that has been declared
/// global, and is allocated in this module.
///
/// @see polly::FortranArrayDescriptor
/// "@globaldescriptor" is the descriptor of the Fortran Array.
///
/// @param Inst The load/store instruction that access the memory.
/// Pattern match for "@globaldescriptor":
/// 1. %mem = load double*, double** bitcast (%"struct.array1_real(kind=8)"*
/// @globaldescriptor to double**), align 32
///
/// 2. [%slot = getelementptr inbounds i8, i8* %mem, i64 <index>]
/// 2 is optional because if you are writing to the 0th index, you don't
/// need a GEP.
///
/// 3.1 store/load <memtype> <val>, <memtype>* %slot, align 8
/// 3.2 store/load <memtype> <val>, <memtype>* %mem, align 8
///
/// @see polly::MemoryAccess, polly::ScopArrayInfo
///
/// @note assumes -polly-canonicalize has been run.
GlobalValue *findFortranArrayDescriptorForAllocArrayAccess(MemAccInst Inst);
///
/// @param Inst The LoadInst/StoreInst that accesses the memory.
///
/// @returns Reference to @globaldescriptor on success, nullptr on failure.
Value *findFADGlobalAlloc(MemAccInst Inst);
/// Try to pattern match and find the array descriptor structure in case of a
/// fortran array accesss. succeeds on load/store into a fortran array that
/// has been allocated.
/// Try to match for the descriptor of a Fortran Array that has been declared
/// global, and is being accessed across modules.
///
/// @see polly::FortranArrayDescriptor
/// Pattern match for "@globaldescriptor":
/// 1. %mem = load double*, double** bitcast (%"struct.array1_real(kind=8)"*
/// @globaldescriptor to double**), align 32
///
/// @param Inst The load/store instruction that access the memory.
/// 2. [%slot = getelementptr inbounds i8, i8* %mem, i64 <index>]
/// 2 is optional because if you are writing to the 0th index, you don't
/// need a GEP.
///
/// 3.1 store/load <memtype> <val>, <memtype>* %slot, align 8
/// 3.2 store/load <memtype> <val>, <memtype>* %mem, align 8
///
/// @see polly::MemoryAccess, polly::ScopArrayInfo
///
/// @note assumes -polly-canonicalize has been run.
GlobalValue *
findFortranArrayDescriptorForNonAllocArrayAccess(MemAccInst Inst);
///
/// @param Inst The LoadInst/StoreInst that accesses the memory.
///
/// @returns Reference to @globaldescriptor on success, nullptr on failure.
Value *findFADGlobalNonAlloc(MemAccInst Inst);
/// Try to match for the descriptor of a Fortran array that is a parameter
/// to a function, and has not been allocated.
///
/// Pattern match for "%param":
/// 1. %mem = bitcast %"struct.array1_integer(kind=4)"* %param to i32**
///
/// 2. [%slot = getelementptr inbounds i8, i8* %mem, i64 <index>]
/// 2 is optional because if you are writing to the 0th index, you don't
/// need a GEP.
///
/// 3.1 store/load <memtype> <val>, <memtype>* %slot, align 8
/// 3.2 store/load <memtype> <val>, <memtype>* %mem, align 8
///
/// @see polly::MemoryAccess, polly::ScopArrayInfo
///
/// @note assumes -polly-canonicalize has been run.
///
/// @param Inst The LoadInst/StoreInst that accesses the memory.
///
/// @returns Reference to "%param" on success, nullptr on failure.
Value *findFADLocalNonAlloc(MemAccInst Inst);
// @}
// Build the SCoP for Region @p R.

View File

@ -616,12 +616,12 @@ private:
/// Updated access relation read from JSCOP file.
isl_map *NewAccessRelation;
/// Fortran arrays that are created using "Allocate" are stored in terms
/// Fortran arrays whose sizes are not statically known are stored in terms
/// of a descriptor struct. This maintains a raw pointer to the memory,
/// along with auxiliary fields with information such as dimensions.
/// We hold a reference to the descriptor corresponding to a MemoryAccess
/// into a Fortran array. FAD for "Fortran Array Descriptor"
AssertingVH<GlobalValue> FAD;
AssertingVH<Value> FAD;
// @}
__isl_give isl_basic_map *createBasicAccessMap(ScopStmt *Statement);
@ -1020,7 +1020,7 @@ public:
/// Set the array descriptor corresponding to the Array on which the
/// memory access is performed.
void setFortranArrayDescriptor(GlobalValue *FAD);
void setFortranArrayDescriptor(Value *FAD);
/// Update the original access relation.
///

View File

@ -123,28 +123,36 @@ void ScopBuilder::buildEscapingDependences(Instruction *Inst) {
}
}
/// Check that a global variable has a type resembling:
/// Check that a value is a Fortran Array descriptor.
///
/// We check if V has the following structure:
/// %"struct.array1_real(kind=8)" = type { i8*, i<zz>, i<zz>,
/// [<num> x %struct.descriptor_dimension] }
///
///
/// %struct.descriptor_dimension = type { i<zz>, i<zz>, i<zz> }
///
/// @global = unnamed_addr global %"struct.array1_real(kind=8)"
///
/// This function checks that:
/// 1. Global has a type name starting with "struct.array"
/// 2. Global type has layout as shown
/// 3. Final member of Global type has name "struct.descriptor_dimension"
/// 4. "struct.descriptor_dimension" has layout as shown
/// 5. Consistent use of i<zz> where <zz> is some fixed integer number
/// 1. V's type name starts with "struct.array"
/// 2. V's type has layout as shown.
/// 3. Final member of V's type has name "struct.descriptor_dimension",
/// 4. "struct.descriptor_dimension" has layout as shown.
/// 5. Consistent use of i<zz> where <zz> is some fixed integer number.
///
/// We are interested in such types since this is the code that dragonegg
/// generates for Fortran arrays.
/// generates for Fortran array descriptors.
///
/// @param Global the global variable believed to be a Fortran array
bool isGlobalFortranArray(GlobalValue *Global) {
auto StructArrTy = dyn_cast<StructType>(Global->getValueType());
/// @param V the Value to be checked.
///
/// @returns True if V is a Fortran array descriptor, False otherwise.
bool isFortranArrayDescriptor(Value *V) {
PointerType *PTy = dyn_cast<PointerType>(V->getType());
if (!PTy)
return false;
Type *Ty = PTy->getElementType();
assert(Ty && "Ty expected to be initialized");
auto *StructArrTy = dyn_cast<StructType>(Ty);
if (!(StructArrTy && StructArrTy->hasName()))
return false;
@ -158,7 +166,7 @@ bool isGlobalFortranArray(GlobalValue *Global) {
const ArrayRef<Type *> ArrMemberTys = StructArrTy->elements();
// i8* match
if (ArrMemberTys[0] != Type::getInt8PtrTy(Global->getContext()))
if (ArrMemberTys[0] != Type::getInt8PtrTy(V->getContext()))
return false;
// Get a reference to the int type and check that all the members
@ -193,31 +201,7 @@ bool isGlobalFortranArray(GlobalValue *Global) {
return true;
}
/// This is matching against code generated by dragonegg after simplifier
/// passes have been run.
///
/// This is trying to match against "@globaldescriptor", the descriptor
/// of the Fortran array that is being accessed at load/store. This style
/// of code is generated for arrays that have been allocated using "Allocate"
/// in the same module.
///
/// Pattern Match:
/// 1. %mallocmem = i8* @malloc(i64 40)
///
/// 5. store i8* %mallocmem, i8** getelementptr inbounds
/// (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"*
/// @globaldescriptor, i64 0, i32 0), align 32
///
/// 2. %typedmem = bitcast i8* %mallocmem to <memtype>*
///
/// 3. [%slot = getelementptr inbounds i8, i8* %typedmem, i64 <index>]
/// 3 is optional because if you are writing to the 0th index, you don't
/// need a GEP.
///
/// 4.1 store/load <memtype> <val>, <memtype>* %typedmem, align 8
/// 4.2 store/load <memtype> <val>, <memtype>* %slot, align 8
GlobalValue *
ScopBuilder::findFortranArrayDescriptorForAllocArrayAccess(MemAccInst Inst) {
Value *ScopBuilder::findFADGlobalNonAlloc(MemAccInst Inst) {
// match: 4.1 & 4.2 store/load
if (!isa<LoadInst>(Inst) && !isa<StoreInst>(Inst))
return nullptr;
@ -274,13 +258,12 @@ ScopBuilder::findFortranArrayDescriptorForAllocArrayAccess(MemAccInst Inst) {
if (!(DescriptorType && DescriptorType->hasName()))
continue;
GlobalValue *Descriptor =
dyn_cast<GlobalValue>(DescriptorGEP->getPointerOperand());
Value *Descriptor = dyn_cast<Value>(DescriptorGEP->getPointerOperand());
if (!Descriptor)
continue;
if (!isGlobalFortranArray(Descriptor))
if (!isFortranArrayDescriptor(Descriptor))
continue;
return Descriptor;
@ -289,26 +272,7 @@ ScopBuilder::findFortranArrayDescriptorForAllocArrayAccess(MemAccInst Inst) {
return nullptr;
}
/// This is matching against code generated by dragonegg after simplifier
/// passes have been run.
///
/// This is trying to match against "@globaldescriptor", the descriptor
/// of the Fortran array that is being accessed at load/store. This style
/// of code is generated for arrays that have been declared global, and
/// are being accessed across modules.
///
/// Pattern Match:
/// 1. %mem = load double*, double** bitcast (%"struct.array1_real(kind=8)"*
/// @globaldescriptor to double**), align 32
///
/// 2. [%slot = getelementptr inbounds i8, i8* %mem, i64 <index>]
/// 2 is optional because if you are writing to the 0th index, you don't
/// need a GEP.
///
/// 3.1 store/load <memtype> <val>, <memtype>* %slot, align 8
/// 3.2 store/load <memtype> <val>, <memtype>* %mem, align 8
GlobalValue *
ScopBuilder::findFortranArrayDescriptorForNonAllocArrayAccess(MemAccInst Inst) {
Value *ScopBuilder::findFADGlobalAlloc(MemAccInst Inst) {
// match: 3
if (!isa<LoadInst>(Inst) && !isa<StoreInst>(Inst))
return nullptr;
@ -337,12 +301,45 @@ ScopBuilder::findFortranArrayDescriptorForNonAllocArrayAccess(MemAccInst Inst) {
if (!BitcastOperator)
return nullptr;
GlobalValue *Descriptor =
dyn_cast<GlobalValue>(BitcastOperator->getOperand(0));
Value *Descriptor = dyn_cast<Value>(BitcastOperator->getOperand(0));
if (!Descriptor)
return nullptr;
if (!isGlobalFortranArray(Descriptor))
if (!isFortranArrayDescriptor(Descriptor))
return nullptr;
return Descriptor;
}
Value *ScopBuilder::findFADLocalNonAlloc(MemAccInst Inst) {
// match: 3
if (!isa<LoadInst>(Inst) && !isa<StoreInst>(Inst))
return nullptr;
// match: 3
if (Inst.getAlignment() != 8)
return nullptr;
Value *Slot = Inst.getPointerOperand();
BitCastOperator *MemBitcast = nullptr;
// [match: 2]
if (auto *SlotGEP = dyn_cast<GetElementPtrInst>(Slot)) {
// match: 1
MemBitcast = dyn_cast<BitCastOperator>(SlotGEP->getPointerOperand());
} else {
// match: 1
MemBitcast = dyn_cast<BitCastOperator>(Slot);
}
if (!MemBitcast)
return nullptr;
Value *Descriptor = dyn_cast<Value>(MemBitcast->getOperand(0));
if (!Descriptor)
return nullptr;
if (!isFortranArrayDescriptor(Descriptor))
return nullptr;
return Descriptor;
@ -774,11 +771,11 @@ void ScopBuilder::addArrayAccess(
if (!DetectFortranArrays)
return;
if (GlobalValue *FAD =
findFortranArrayDescriptorForAllocArrayAccess(MemAccInst))
if (Value *FAD = findFADGlobalNonAlloc(MemAccInst))
MemAccess->setFortranArrayDescriptor(FAD);
else if (GlobalValue *FAD =
findFortranArrayDescriptorForNonAllocArrayAccess(MemAccInst))
else if (Value *FAD = findFADGlobalAlloc(MemAccInst))
MemAccess->setFortranArrayDescriptor(FAD);
else if (Value *FAD = findFADLocalNonAlloc(MemAccInst))
MemAccess->setFortranArrayDescriptor(FAD);
}

View File

@ -1034,21 +1034,7 @@ raw_ostream &polly::operator<<(raw_ostream &OS,
return OS;
}
void MemoryAccess::setFortranArrayDescriptor(GlobalValue *FAD) {
this->FAD = FAD;
// TODO: write checks to make sure it looks _exactly_ like a Fortran array
// descriptor
#ifndef NDEBUG
StructType *ty = dyn_cast<StructType>(FAD->getValueType());
assert(ty && "expected value of type Fortran array descriptor");
assert(ty->hasName() && ty->getName().startswith("struct.array") &&
"expected global to follow Fortran array descriptor type naming "
"convention");
assert(ty->getNumElements() == 4 &&
"expected layout to be like Fortran array descriptor type");
#endif
}
void MemoryAccess::setFortranArrayDescriptor(Value *FAD) { this->FAD = FAD; }
void MemoryAccess::print(raw_ostream &OS) const {
switch (AccType) {

View File

@ -0,0 +1,67 @@
; RUN: opt %loadPolly -analyze -polly-detect-fortran-arrays \
; RUN: -polly-scops -polly-allow-nonaffine -polly-invariant-load-hoisting < %s | FileCheck %s
; This testcase is the corresponding LLVM for testfunc:
; PROGRAM main
; INTEGER, DIMENSION(1) :: xs
;
; CALL testfunc(xs, 10)
; CONTAINS
; SUBROUTINE func(xs, n)
; IMPLICIT NONE
; INTEGER, DIMENSION(:), INTENT(INOUT) :: xs
; INTEGER, INTENT(IN) :: n
; INTEGER :: i
; DO i = 1, n
; xs(i) = 1
; END DO
;
; END SUBROUTINE func
; END PROGRAM
target datalayout = "e-p:64:64:64-S128-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f16:16:16-f32:32:32-f64:64:64-f128:128:128-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-unknown-linux-gnu"
module asm "\09.ident\09\22GCC: (GNU) 4.6.4 LLVM: 3.3.1\22"
%"struct.array1_integer(kind=4)" = type { i8*, i64, i64, [1 x %struct.descriptor_dimension] }
%struct.descriptor_dimension = type { i64, i64, i64 }
define internal void @testfunc(%"struct.array1_integer(kind=4)"* noalias %xs, i32* noalias %n) {
entry:
br label %entry.split
entry.split: ; preds = %entry
%tmp = getelementptr inbounds %"struct.array1_integer(kind=4)", %"struct.array1_integer(kind=4)"* %xs, i64 0, i32 3, i64 0, i32 0
%tmp1 = load i64, i64* %tmp, align 8
%tmp2 = icmp eq i64 %tmp1, 0
%tmp3 = select i1 %tmp2, i64 1, i64 %tmp1
%tmp4 = bitcast %"struct.array1_integer(kind=4)"* %xs to i32**
%tmp5 = load i32*, i32** %tmp4, align 8
%tmp6 = load i32, i32* %n, align 4
%tmp7 = icmp sgt i32 %tmp6, 0
br i1 %tmp7, label %"6.preheader", label %return
"6.preheader": ; preds = %entry.split
br label %"6"
"6": ; preds = %"6", %"6.preheader"
%tmp8 = phi i32 [ %tmp14, %"6" ], [ 1, %"6.preheader" ]
%tmp9 = sext i32 %tmp8 to i64
%tmp10 = mul i64 %tmp3, %tmp9
%tmp11 = sub i64 %tmp10, %tmp3
%tmp12 = getelementptr i32, i32* %tmp5, i64 %tmp11
store i32 1, i32* %tmp12, align 4
%tmp13 = icmp eq i32 %tmp8, %tmp6
%tmp14 = add i32 %tmp8, 1
br i1 %tmp13, label %return.loopexit, label %"6"
return.loopexit: ; preds = %"6"
br label %return
return: ; preds = %return.loopexit, %entry.split
ret void
}
; CHECK: ReadAccess := [Reduction Type: NONE] [Fortran array descriptor: xs] [Scalar: 0]