// The Scop
std::unique_ptr<Scop> scop;
+ // 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.
+ ///
+ /// @see polly::FortranArrayDescriptor
+ ///
+ /// @param Inst The load/store instruction that access the memory.
+ ///
+ /// @note assumes -polly-canonicalize has been run.
+ GlobalValue *findFortranArrayDescriptorForAllocArrayAccess(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.
+ ///
+ /// @see polly::FortranArrayDescriptor
+ ///
+ /// @param Inst The load/store instruction that access the memory.
+ ///
+ /// @note assumes -polly-canonicalize has been run.
+ GlobalValue *
+ findFortranArrayDescriptorForNonAllocArrayAccess(MemAccInst Inst);
+ // @}
+
// Build the SCoP for Region @p R.
void buildScop(Region &R, AssumptionCache &AC);
/// Updated access relation read from JSCOP file.
isl_map *NewAccessRelation;
+
+ /// Fortran arrays that are created using "Allocate" 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;
// @}
__isl_give isl_basic_map *createBasicAccessMap(ScopStmt *Statement);
/// Get the reduction type of this access
ReductionType getReductionType() const { return RedType; }
+ /// Set the array descriptor corresponding to the Array on which the
+ /// memory access is performed.
+ void setFortranArrayDescriptor(GlobalValue *FAD);
+
/// Update the original access relation.
///
/// We need to update the original access relation during scop construction,
cl::desc("Count statements with scalar accesses as not optimizable"),
cl::Hidden, cl::init(false), cl::cat(PollyCategory));
+static cl::opt<bool> DetectFortranArrays(
+ "polly-detect-fortran-arrays",
+ cl::desc("Detect Fortran arrays and use this for code generation"),
+ cl::Hidden, cl::init(false), cl::cat(PollyCategory));
+
void ScopBuilder::buildPHIAccesses(PHINode *PHI, Region *NonAffineSubRegion,
bool IsExitBlock) {
}
}
+/// Check that a global variable has a type resembling:
+/// %"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
+///
+/// We are interested in such types since this is the code that dragonegg
+/// generates for Fortran arrays.
+///
+/// @param Global the global variable believed to be a Fortran array
+bool isGlobalFortranArray(GlobalValue *Global) {
+ auto StructArrTy = dyn_cast<StructType>(Global->getValueType());
+
+ if (!(StructArrTy && StructArrTy->hasName()))
+ return false;
+
+ if (!StructArrTy->getName().startswith("struct.array"))
+ return false;
+
+ if (StructArrTy->getNumElements() != 4)
+ return false;
+
+ const ArrayRef<Type *> ArrMemberTys = StructArrTy->elements();
+
+ // i8* match
+ if (ArrMemberTys[0] != Type::getInt8PtrTy(Global->getContext()))
+ return false;
+
+ // Get a reference to the int type and check that all the members
+ // share the same int type
+ Type *IntTy = ArrMemberTys[1];
+ if (ArrMemberTys[2] != IntTy)
+ return false;
+
+ // type: [<num> x %struct.descriptor_dimension]
+ ArrayType *DescriptorDimArrayTy = dyn_cast<ArrayType>(ArrMemberTys[3]);
+ if (!DescriptorDimArrayTy)
+ return false;
+
+ // type: %struct.descriptor_dimension := type { ixx, ixx, ixx }
+ StructType *DescriptorDimTy =
+ dyn_cast<StructType>(DescriptorDimArrayTy->getElementType());
+
+ if (!(DescriptorDimTy && DescriptorDimTy->hasName()))
+ return false;
+
+ if (DescriptorDimTy->getName() != "struct.descriptor_dimension")
+ return false;
+
+ if (DescriptorDimTy->getNumElements() != 3)
+ return false;
+
+ for (auto MemberTy : DescriptorDimTy->elements()) {
+ if (MemberTy != IntTy)
+ return false;
+ }
+
+ 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) {
+ // match: 4.1 & 4.2 store/load
+ if (!isa<LoadInst>(Inst) && !isa<StoreInst>(Inst))
+ return nullptr;
+
+ // match: 4
+ if (Inst.getAlignment() != 8)
+ return nullptr;
+
+ Value *Address = Inst.getPointerOperand();
+
+ const BitCastInst *Bitcast = nullptr;
+ // [match: 3]
+ if (auto *Slot = dyn_cast<GetElementPtrInst>(Address)) {
+ Value *TypedMem = Slot->getPointerOperand();
+ // match: 2
+ Bitcast = dyn_cast<BitCastInst>(TypedMem);
+ } else {
+ // match: 2
+ Bitcast = dyn_cast<BitCastInst>(Address);
+ }
+
+ if (!Bitcast)
+ return nullptr;
+
+ auto *MallocMem = Bitcast->getOperand(0);
+
+ // match: 1
+ auto *MallocCall = dyn_cast<CallInst>(MallocMem);
+ if (!MallocCall)
+ return nullptr;
+
+ Function *MallocFn = MallocCall->getCalledFunction();
+ if (!(MallocFn && MallocFn->hasName() && MallocFn->getName() == "malloc"))
+ return nullptr;
+
+ // Find all uses the malloc'd memory.
+ // We are looking for a "store" into a struct with the type being the Fortran
+ // descriptor type
+ for (auto user : MallocMem->users()) {
+
+ /// match: 5
+ auto *MallocStore = dyn_cast<StoreInst>(user);
+ if (!MallocStore)
+ continue;
+
+ auto *DescriptorGEP =
+ dyn_cast<GEPOperator>(MallocStore->getPointerOperand());
+ if (!DescriptorGEP)
+ continue;
+
+ // match: 5
+ auto DescriptorType =
+ dyn_cast<StructType>(DescriptorGEP->getSourceElementType());
+ if (!(DescriptorType && DescriptorType->hasName()))
+ continue;
+
+ GlobalValue *Descriptor =
+ dyn_cast<GlobalValue>(DescriptorGEP->getPointerOperand());
+
+ if (!Descriptor)
+ continue;
+
+ if (!isGlobalFortranArray(Descriptor))
+ continue;
+
+ return Descriptor;
+ }
+
+ 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) {
+ // match: 3
+ if (!isa<LoadInst>(Inst) && !isa<StoreInst>(Inst))
+ return nullptr;
+
+ // match: 3
+ if (Inst.getAlignment() != 8)
+ return nullptr;
+
+ Value *Slot = Inst.getPointerOperand();
+
+ LoadInst *MemLoad = nullptr;
+ // [match: 2]
+ if (auto *SlotGEP = dyn_cast<GetElementPtrInst>(Slot)) {
+ // match: 1
+ MemLoad = dyn_cast<LoadInst>(SlotGEP->getPointerOperand());
+ } else {
+ // match: 1
+ MemLoad = dyn_cast<LoadInst>(Slot);
+ }
+
+ if (!MemLoad)
+ return nullptr;
+
+ auto *BitcastOperator =
+ dyn_cast<BitCastOperator>(MemLoad->getPointerOperand());
+ if (!BitcastOperator)
+ return nullptr;
+
+ GlobalValue *Descriptor =
+ dyn_cast<GlobalValue>(BitcastOperator->getOperand(0));
+ if (!Descriptor)
+ return nullptr;
+
+ if (!isGlobalFortranArray(Descriptor))
+ return nullptr;
+
+ return Descriptor;
+}
+
bool ScopBuilder::buildAccessMultiDimFixed(MemAccInst Inst, ScopStmt *Stmt) {
Value *Val = Inst.getValueOperand();
Type *ElementType = Val->getType();
Type *ElementType, bool IsAffine, ArrayRef<const SCEV *> Subscripts,
ArrayRef<const SCEV *> Sizes, Value *AccessValue) {
ArrayBasePointers.insert(BaseAddress);
- addMemoryAccess(MemAccInst->getParent(), MemAccInst, AccType, BaseAddress,
- ElementType, IsAffine, AccessValue, Subscripts, Sizes,
- MemoryKind::Array);
+ auto *MemAccess = addMemoryAccess(
+ MemAccInst->getParent(), MemAccInst, AccType, BaseAddress, ElementType,
+ IsAffine, AccessValue, Subscripts, Sizes, MemoryKind::Array);
+
+ if (!DetectFortranArrays)
+ return;
+
+ if (GlobalValue *FAD =
+ findFortranArrayDescriptorForAllocArrayAccess(MemAccInst))
+ MemAccess->setFortranArrayDescriptor(FAD);
+ else if (GlobalValue *FAD =
+ findFortranArrayDescriptorForNonAllocArrayAccess(MemAccInst))
+ MemAccess->setFortranArrayDescriptor(FAD);
}
void ScopBuilder::ensureValueWrite(Instruction *Inst) {
Sizes(Sizes.begin(), Sizes.end()), AccessInstruction(AccessInst),
AccessValue(AccessValue), IsAffine(Affine),
Subscripts(Subscripts.begin(), Subscripts.end()), AccessRelation(nullptr),
- NewAccessRelation(nullptr) {
+ NewAccessRelation(nullptr), FAD(nullptr) {
static const std::string TypeStrings[] = {"", "_Read", "_Write", "_MayWrite"};
const std::string Access = TypeStrings[AccType] + utostr(Stmt->size());
__isl_take isl_map *AccRel)
: Kind(MemoryKind::Array), AccType(AccType), RedType(RT_NONE),
Statement(Stmt), InvalidDomain(nullptr), AccessInstruction(nullptr),
- IsAffine(true), AccessRelation(nullptr), NewAccessRelation(AccRel) {
+ IsAffine(true), AccessRelation(nullptr), NewAccessRelation(AccRel),
+ FAD(nullptr) {
auto *ArrayInfoId = isl_map_get_tuple_id(NewAccessRelation, isl_dim_out);
auto *SAI = ScopArrayInfo::getFromId(ArrayInfoId);
Sizes.push_back(nullptr);
return OS;
}
+void MemoryAccess::setFortranArrayDescriptor(GlobalValue *FAD) {
+ this->FAD = FAD;
+
+// TODO: write checks to make sure it looks _exactly_ like a Fortran array
+// descriptor
+#ifdef NDEBUG
+ StructType *ty = dyn_cast<StructType>(Descriptor->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::print(raw_ostream &OS) const {
switch (AccType) {
case READ:
OS.indent(12) << "MayWriteAccess :=\t";
break;
}
+
OS << "[Reduction Type: " << getReductionType() << "] ";
+
+ if (FAD) {
+ OS << "[Fortran array descriptor: " << FAD->getName();
+ OS << "] ";
+ };
+
OS << "[Scalar: " << isScalarKind() << "]\n";
OS.indent(16) << getOriginalAccessRelationStr() << ";\n";
if (hasNewAccessRelation())
--- /dev/null
+; RUN: opt -S -polly-detect-fortran-arrays -analyze -polly-process-unprofitable \
+; RUN: -polly-remarks-minimal -polly-canonicalize -polly-scops \
+; RUN: -polly-dependences -debug-only=polly-dependence -polly-canonicalize \
+; RUN: -polly-allow-nonaffine -polly-ignore-aliasing \
+; RUN: -polly-invariant-load-hoisting < %s| FileCheck %s
+;
+; MODULE src_soil
+; USE data_parameters, ONLY : &
+; wp, & ! KIND-type parameter for real variables
+; iintegers ! KIND-type parameter for standard integer variables
+; IMPLICIT NONE
+; REAL (KIND = wp), ALLOCATABLE, PRIVATE :: &
+; xdzs (:)
+; CONTAINS
+; SUBROUTINE terra1(n)
+; INTEGER, intent(in) :: n
+; INTEGER (KIND=iintegers) :: &
+; j
+; Allocate(xdzs(n));
+; DO j = 2, n
+; xdzs(j) = xdzs(j) * xdzs(j) + xdzs(j - 1)
+; END DO
+; END SUBROUTINE terra1
+; END MODULE src_soil
+
+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_real(kind=8)" = type { i8*, i64, i64, [1 x %struct.descriptor_dimension] }
+%struct.descriptor_dimension = type { i64, i64, i64 }
+
+@__src_soil_MOD_xdzs = unnamed_addr global %"struct.array1_real(kind=8)" zeroinitializer, align 32
+@.cst = private unnamed_addr constant [67 x i8] c"Integer overflow when calculating the amount of memory to allocate\00", align 64
+@.cst1 = private unnamed_addr constant [37 x i8] c"Allocation would exceed memory limit\00", align 64
+@.cst2 = private unnamed_addr constant [93 x i8] c"At line 23 of file /home/siddhart/cosmo-self-installation/cosmo-pompa/cosmo/src/src_soil.f90\00", align 64
+@.cst3 = private unnamed_addr constant [55 x i8] c"Attempting to allocate already allocated variable '%s'\00", align 64
+@.cst4 = private unnamed_addr constant [5 x i8] c"xdzs\00", align 8
+
+; Function Attrs: nounwind uwtable
+define void @__src_soil_MOD_terra1(i32* noalias nocapture %n) unnamed_addr #0 {
+entry:
+ store i64 537, i64* getelementptr inbounds (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"* @__src_soil_MOD_xdzs, i64 0, i32 2), align 16, !tbaa !0
+ store i64 1, i64* getelementptr inbounds (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"* @__src_soil_MOD_xdzs, i64 0, i32 3, i64 0, i32 1), align 8, !tbaa !0
+ %0 = load i32, i32* %n, align 4, !tbaa !3
+ %1 = sext i32 %0 to i64
+ store i64 %1, i64* getelementptr inbounds (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"* @__src_soil_MOD_xdzs, i64 0, i32 3, i64 0, i32 2), align 8, !tbaa !0
+ store i64 1, i64* getelementptr inbounds (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"* @__src_soil_MOD_xdzs, i64 0, i32 3, i64 0, i32 0), align 8, !tbaa !0
+ %2 = icmp slt i32 %0, 0
+ %3 = select i1 %2, i64 0, i64 %1
+ %4 = icmp eq i64 %3, 0
+ br i1 %4, label %"16", label %"8"
+
+"8": ; preds = %entry
+ %5 = sdiv i64 9223372036854775807, %1
+ %6 = icmp slt i64 %5, 1
+ %7 = icmp slt i32 %0, 1
+ %8 = shl nsw i64 %3, 3
+ %.2 = select i1 %7, i64 0, i64 %8
+ br i1 %6, label %"15", label %"16"
+
+"15": ; preds = %"8"
+
+ unreachable
+
+"16": ; preds = %"8", %entry
+ %.24 = phi i64 [ %.2, %"8" ], [ 0, %entry ]
+ %9 = load i8*, i8** getelementptr inbounds (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"* @__src_soil_MOD_xdzs, i64 0, i32 0), align 32, !tbaa !5
+ %10 = icmp eq i8* %9, null
+ br i1 %10, label %"17", label %"20"
+
+"17": ; preds = %"16"
+ %11 = icmp ne i64 %.24, 0
+ %12 = select i1 %11, i64 %.24, i64 1
+ %13 = tail call noalias i8* @malloc(i64 %12) #2 ;<= 1. malloc
+ %14 = icmp eq i8* %13, null
+ br i1 %14, label %"18", label %"19"
+
+"18": ; preds = %"17"
+ unreachable
+
+"19": ; preds = %"17"
+ store i8* %13, i8** getelementptr inbounds (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"* @__src_soil_MOD_xdzs, i64 0, i32 0), align 32, !tbaa !5
+ store i64 -1, i64* getelementptr inbounds (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"* @__src_soil_MOD_xdzs, i64 0, i32 1), align 8, !tbaa !0
+ %15 = icmp sgt i32 %0, 1
+ br i1 %15, label %"21.preheader", label %return
+
+"21.preheader": ; preds = %"19"
+ %16 = bitcast i8* %13 to double* ;<= 2. bitcast to double*
+ %17 = add i32 %0, 1
+ br label %"21"
+
+"20": ; preds = %"16"
+ unreachable
+
+"21": ; preds = %"21", %"21.preheader"
+ %18 = phi double [ undef, %"21.preheader" ], [ %23, %"21" ]
+ %indvars.iv = phi i64 [ 2, %"21.preheader" ], [ %indvars.iv.next, %"21" ]
+ %19 = add nsw i64 %indvars.iv, -1
+ %20 = getelementptr inbounds double, double* %16, i64 %19 ;<= 3. GEP
+ %21 = load double, double* %20, align 8, !tbaa !7
+ %22 = fmul double %21, %21
+ %23 = fadd double %22, %18
+ store double %23, double* %20, align 8, !tbaa !7 ;<= 4. store
+ %indvars.iv.next = add i64 %indvars.iv, 1
+ %lftr.wideiv = trunc i64 %indvars.iv.next to i32
+ %exitcond = icmp eq i32 %lftr.wideiv, %17
+ br i1 %exitcond, label %return, label %"21"
+
+return: ; preds = %"21", %"19"
+ ret void
+}
+
+; Function Attrs: noreturn
+declare void @_gfortran_runtime_error(i8*, ...) #1
+
+; Function Attrs: nounwind
+declare noalias i8* @malloc(i64) #2
+
+; Function Attrs: noreturn
+declare void @_gfortran_os_error(i8*) #1
+
+; Function Attrs: noreturn
+declare void @_gfortran_runtime_error_at(i8*, i8*, ...) #1
+
+attributes #0 = { nounwind uwtable }
+attributes #1 = { noreturn }
+attributes #2 = { nounwind }
+attributes #3 = { noreturn nounwind }
+
+!0 = !{!1, !1, i64 0}
+!1 = !{!"alias set 4: integer(kind=8)", !2}
+!2 = distinct !{!2}
+!3 = !{!4, !4, i64 0}
+!4 = !{!"alias set 11: integer(kind=4)", !2}
+!5 = !{!6, !6, i64 0}
+!6 = !{!"alias set 3: void*", !2}
+!7 = !{!8, !8, i64 0}
+!8 = !{!"alias set 18: real(kind=8)", !2}
+
+; CHECK: ReadAccess := [Reduction Type: NONE] [Fortran array descriptor: __src_soil_MOD_xdzs] [Scalar: 0]
+; CHECK: MustWriteAccess := [Reduction Type: NONE] [Fortran array descriptor: __src_soil_MOD_xdzs] [Scalar: 0]
--- /dev/null
+; RUN: opt -S -polly-detect-fortran-arrays -analyze -polly-process-unprofitable \
+; RUN: -polly-remarks-minimal -polly-canonicalize -polly-scops \
+; RUN: -polly-dependences -debug-only=polly-dependence -polly-canonicalize \
+; RUN: -polly-allow-nonaffine -polly-ignore-aliasing \
+; RUN: -polly-invariant-load-hoisting < %s| FileCheck %s
+;
+; MODULE src_soil
+; USE data_parameters, ONLY : &
+; wp, & ! KIND-type parameter for real variables
+; iintegers ! KIND-type parameter for standard integer variables
+; IMPLICIT NONE
+; REAL (KIND = wp), ALLOCATABLE, PRIVATE :: &
+; xdzs (:)
+; CONTAINS
+;
+; SUBROUTINE terra1(n)
+; INTEGER, intent(in) :: n
+;
+; INTEGER (KIND=iintegers) :: &
+; j
+;
+; DO j = 22, n
+; xdzs(j) = xdzs(j) * xdzs(j) + xdzs(j - 1)
+; END DO
+; END SUBROUTINE terra1
+; END MODULE src_soil
+
+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_real(kind=8)" = type { i8*, i64, i64, [1 x %struct.descriptor_dimension] }
+%struct.descriptor_dimension = type { i64, i64, i64 }
+
+@__src_soil_MOD_xdzs = unnamed_addr global %"struct.array1_real(kind=8)" zeroinitializer, align 32
+
+; Function Attrs: nounwind uwtable
+define void @__src_soil_MOD_terra1(i32* noalias nocapture %n) unnamed_addr #0 {
+entry:
+ %0 = load i32, i32* %n, align 4, !tbaa !0
+ %1 = icmp sgt i32 %0, 21
+ br i1 %1, label %"3.preheader", label %return
+
+"3.preheader": ; preds = %entry
+ %2 = load i64, i64* getelementptr inbounds (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"* @__src_soil_MOD_xdzs, i64 0, i32 1), align 8, !tbaa !3
+ %3 = load i8*, i8** getelementptr inbounds (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"* @__src_soil_MOD_xdzs, i64 0, i32 0), align 32, !tbaa !5
+ %4 = bitcast i8* %3 to double*
+ %5 = add i32 %0, 1
+ br label %"3"
+
+"3": ; preds = %"3", %"3.preheader"
+ %indvars.iv = phi i64 [ 22, %"3.preheader" ], [ %indvars.iv.next, %"3" ]
+ %6 = add nsw i64 %indvars.iv, %2
+ %7 = getelementptr inbounds double, double* %4, i64 %6
+ %8 = load double, double* %7, align 8, !tbaa !7
+ %9 = fmul double %8, %8
+ %10 = add nsw i64 %indvars.iv, -1
+ %11 = add nsw i64 %10, %2
+ %12 = getelementptr inbounds double, double* %4, i64 %11
+ %13 = load double, double* %12, align 8, !tbaa !7
+ %14 = fadd double %9, %13
+ store double %14, double* %7, align 8, !tbaa !7
+ %indvars.iv.next = add i64 %indvars.iv, 1
+ %lftr.wideiv = trunc i64 %indvars.iv.next to i32
+ %exitcond = icmp eq i32 %lftr.wideiv, %5
+ br i1 %exitcond, label %return, label %"3"
+
+return: ; preds = %"3", %entry
+ ret void
+}
+
+attributes #0 = { nounwind uwtable }
+
+!0 = !{!1, !1, i64 0}
+!1 = !{!"alias set 11: integer(kind=4)", !2}
+!2 = distinct !{!2}
+!3 = !{!4, !4, i64 0}
+!4 = !{!"alias set 4: integer(kind=8)", !2}
+!5 = !{!6, !6, i64 0}
+!6 = !{!"alias set 3: void*", !2}
+!7 = !{!8, !8, i64 0}
+!8 = !{!"alias set 18: real(kind=8)", !2}
+
+; CHECK: ReadAccess := [Reduction Type: NONE] [Fortran array descriptor: __src_soil_MOD_xdzs] [Scalar: 0]
+; CHECK: ReadAccess := [Reduction Type: NONE] [Fortran array descriptor: __src_soil_MOD_xdzs] [Scalar: 0]
+; CHECK: MustWriteAccess := [Reduction Type: NONE] [Fortran array descriptor: __src_soil_MOD_xdzs] [Scalar: 0]