Index: include/polly/CodeGen/IslNodeBuilder.h =================================================================== --- include/polly/CodeGen/IslNodeBuilder.h +++ include/polly/CodeGen/IslNodeBuilder.h @@ -75,6 +75,12 @@ void addParameters(__isl_take isl_set *Context); + /// Materialize all fortran array parameters in the current scop. + /// + /// @returns False, iff a problem occurred and a fortran array was not + /// materialized. + bool materializeFortranArrayOutermostDimensionParameters(); + /// Generate code that evaluates @p Condition at run-time. /// /// This function is typically called to generate the LLVM-IR for the Index: include/polly/ScopBuilder.h =================================================================== --- include/polly/ScopBuilder.h +++ include/polly/ScopBuilder.h @@ -54,6 +54,33 @@ // The Scop std::unique_ptr 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); Index: include/polly/ScopInfo.h =================================================================== --- include/polly/ScopInfo.h +++ include/polly/ScopInfo.h @@ -263,6 +263,9 @@ /// with old sizes bool updateSizes(ArrayRef Sizes, bool CheckConsistency = true); + /// Make the ScopArrayInfo model a Fortran Array + void makeFortranArray(GlobalValue *FAD); + /// Destructor to free the isl id of the base pointer. ~ScopArrayInfo(); @@ -409,6 +412,10 @@ /// The scop this SAI object belongs to. Scop &S; + + /// If this arrays models a Fortran array, contains a pointer + /// to the Fortran array descriptor + const GlobalValue *FAD; }; /// Represent memory accesses in statements. @@ -604,6 +611,13 @@ /// 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 FAD; // @} __isl_give isl_basic_map *createBasicAccessMap(ScopStmt *Statement); @@ -886,6 +900,10 @@ /// the dimension of the innermost loop containing the statement. __isl_give isl_set *getStride(__isl_take const isl_map *Schedule) const; + /// Get the FortranArrayDescriptor corresponding to this memory access if + /// it exists, and nullptr otherwise. + GlobalValue *getFortranArrayDescriptor() const { return this->FAD; }; + /// Is the stride of the access equal to a certain width? Schedule is a map /// from the statement to a schedule where the innermost dimension is the /// dimension of the innermost loop containing the statement. @@ -1008,6 +1026,10 @@ /// 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, @@ -1994,6 +2016,10 @@ /// all memory accesses have been modeled and canonicalized. void assumeNoOutOfBounds(); + /// Mark Arrays that have memory accesses with FortranArrayDescriptor + /// as fortran arrays + void markFortranArrays(); + /// Finalize all access relations. /// /// When building up access relations, temporary access relations that Index: lib/Analysis/ScopBuilder.cpp =================================================================== --- lib/Analysis/ScopBuilder.cpp +++ lib/Analysis/ScopBuilder.cpp @@ -118,6 +118,231 @@ } } +/// Check that a global variable has a type resembling: +/// %"struct.array1_real(kind=8)" = type { i8*, i, i, +/// [ x %struct.descriptor_dimension] } +/// +/// +/// %struct.descriptor_dimension = type { i, i, i } +/// +/// @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 where 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(Global->getValueType()); + + if (!(StructArrTy && StructArrTy->hasName())) + return false; + + if (!StructArrTy->getName().startswith("struct.array")) + return false; + + if (StructArrTy->getNumElements() != 4) + return false; + + const ArrayRef 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: [ x %struct.descriptor_dimension] + ArrayType *DescriptorDimArrayTy = dyn_cast(ArrMemberTys[3]); + if (!DescriptorDimArrayTy) + return false; + + // type: %struct.descriptor_dimension := type { ixx, ixx, ixx } + StructType *DescriptorDimTy = + dyn_cast(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 * +/// +/// 3. [%slot = getelementptr inbounds i8, i8* %typedmem, i64 ] +/// 3 is optional because if you are writing to the 0th index, you don't +/// need a GEP. +/// +/// 4.1 store/load , * %typedmem, align 8 +/// 4.2 store/load , * %slot, align 8 +GlobalValue * +ScopBuilder::findFortranArrayDescriptorForAllocArrayAccess(MemAccInst Inst) { + // match: 4.1 & 4.2 store/load + if (!isa(Inst) && !isa(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(Address)) { + Value *TypedMem = Slot->getPointerOperand(); + // match: 2 + Bitcast = dyn_cast(TypedMem); + } else { + // match: 2 + Bitcast = dyn_cast(Address); + } + + if (!Bitcast) + return nullptr; + + auto *MallocMem = Bitcast->getOperand(0); + + // match: 1 + auto *MallocCall = dyn_cast(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(user); + if (!MallocStore) + continue; + + auto *DescriptorGEP = + dyn_cast(MallocStore->getPointerOperand()); + if (!DescriptorGEP) + continue; + + // match: 5 + auto DescriptorType = + dyn_cast(DescriptorGEP->getSourceElementType()); + if (!(DescriptorType && DescriptorType->hasName())) + continue; + + GlobalValue *Descriptor = + dyn_cast(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 ] +/// 2 is optional because if you are writing to the 0th index, you don't +/// need a GEP. +/// +/// 3.1 store/load , * %slot, align 8 +/// 3.2 store/load , * %mem, align 8 +GlobalValue * +ScopBuilder::findFortranArrayDescriptorForNonAllocArrayAccess(MemAccInst Inst) { + // match: 3 + if (!isa(Inst) && !isa(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(Slot)) { + // match: 1 + MemLoad = dyn_cast(SlotGEP->getPointerOperand()); + } else { + // match: 1 + MemLoad = dyn_cast(Slot); + } + + if (!MemLoad) + return nullptr; + + auto *BitcastOperator = + dyn_cast(MemLoad->getPointerOperand()); + if (!BitcastOperator) + return nullptr; + + GlobalValue *Descriptor = + dyn_cast(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(); @@ -537,9 +762,17 @@ Type *ElementType, bool IsAffine, ArrayRef Subscripts, ArrayRef 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); + + // TODO: change to loop of function pointers? + if (GlobalValue *FAD = + findFortranArrayDescriptorForAllocArrayAccess(MemAccInst)) + MemAccess->setFortranArrayDescriptor(FAD); + else if (GlobalValue *FAD = + findFortranArrayDescriptorForNonAllocArrayAccess(MemAccInst)) + MemAccess->setFortranArrayDescriptor(FAD); } void ScopBuilder::ensureValueWrite(Instruction *Inst) { Index: lib/Analysis/ScopInfo.cpp =================================================================== --- lib/Analysis/ScopInfo.cpp +++ lib/Analysis/ScopInfo.cpp @@ -240,7 +240,8 @@ ArrayRef Sizes, MemoryKind Kind, const DataLayout &DL, Scop *S, const char *BaseName) - : BasePtr(BasePtr), ElementType(ElementType), Kind(Kind), DL(DL), S(*S) { + : BasePtr(BasePtr), ElementType(ElementType), Kind(Kind), DL(DL), S(*S), + FAD(nullptr) { std::string BasePtrName = BaseName ? BaseName : getIslCompatibleName("MemRef", BasePtr, S->getNextArrayIdx(), @@ -297,6 +298,36 @@ } } +/// Make the ScopArrayInfo model a Fortran Array +void ScopArrayInfo::makeFortranArray(GlobalValue *FAD) { + assert(FAD != nullptr && "got invalid FortranArraydescriptor"); + if (this->FAD != nullptr) { + assert(this->FAD == FAD && + "receiving different array descriptors for same array"); + return; + } + + assert(DimensionSizesPw.size() > 0 && DimensionSizesPw[0] == nullptr); + assert(this->FAD == nullptr); + this->FAD = FAD; + + isl_space *space = isl_space_set_alloc(S.getIslCtx(), 1, 0); + + std::string param_name = this->getName(); + param_name += "_fortranarr_size"; + isl_id *id_for_pa = isl_id_alloc(S.getIslCtx(), param_name.c_str(), nullptr); + + space = isl_space_set_dim_id(space, isl_dim_param, 0, id_for_pa); + isl_basic_set *identity = isl_basic_set_universe(space); + isl_local_space *ls = isl_basic_set_get_local_space(identity); + isl_basic_set_free(identity); + + isl_aff *aff = isl_aff_var_on_domain(ls, isl_dim_param, 0); + isl_pw_aff *pa = isl_pw_aff_from_aff(aff); + + DimensionSizesPw[0] = pa; +} + bool ScopArrayInfo::updateSizes(ArrayRef NewSizes, bool CheckConsistency) { int SharedDims = std::min(NewSizes.size(), DimensionSizes.size()); @@ -353,7 +384,12 @@ void ScopArrayInfo::print(raw_ostream &OS, bool SizeAsPwAff) const { OS.indent(8) << *getElementType() << " " << getName(); unsigned u = 0; - if (getNumberOfDimensions() > 0 && !getDimensionSize(0)) { + // If this is a Fortran array, then we can print the outermost dimension + // as a pwAff even though there is no SCEV information. + const bool shouldPrintOutermostDim = SizeAsPwAff && FAD != nullptr; + + if (!shouldPrintOutermostDim && getNumberOfDimensions() > 0 && + !getDimensionSize(0)) { OS << "[*]"; u++; } @@ -971,7 +1007,7 @@ 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()); @@ -983,7 +1019,8 @@ __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); @@ -1019,6 +1056,22 @@ 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(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: @@ -1031,7 +1084,14 @@ 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()) @@ -2130,7 +2190,30 @@ Stmt.realignParams(); // Simplify the schedule according to the context too. - Schedule = isl_schedule_gist_domain_params(Schedule, getContext()); + // Add fortran arrays into the context + auto Context = getContext(); + int ContextNParams = isl_set_dim(Context, isl_dim_param); + for (auto arr : arrays()) { + // is a fortran array + // HACK: actually need to check if it has a FAD, but for now this works + if (arr->getNumberOfDimensions() > 0) { + isl_pw_aff *pwaff = arr->getDimensionSizePw(0); + if (pwaff == nullptr) + continue; + isl_id *id_for_pa = isl_pw_aff_get_dim_id(pwaff, isl_dim_param, 0); + + assert(id_for_pa != nullptr); + + Context = isl_set_add_dims(Context, isl_dim_param, 1); + Context = + isl_set_set_dim_id(Context, isl_dim_param, ContextNParams, id_for_pa); + ContextNParams++; + + isl_pw_aff_free(pwaff); + } + } + + Schedule = isl_schedule_gist_domain_params(Schedule, Context); } static __isl_give isl_set * @@ -3355,11 +3438,28 @@ return; } +void Scop::markFortranArrays() { + for (ScopStmt &Stmt : this->Stmts) { + for (MemoryAccess *memAccess : Stmt) { + GlobalValue *FAD = memAccess->getFortranArrayDescriptor(); + if (!FAD) + continue; + // HACK: const_cast ing to edit + ScopArrayInfo *SAI = + const_cast(memAccess->getLatestScopArrayInfo()); + assert(SAI != nullptr && "memory access into a fortran array does not " + "have an associated ScopArrayInfo"); + SAI->makeFortranArray(FAD); + } + } +} + void Scop::finalizeAccesses() { updateAccessDimensionality(); foldSizeConstantsToRight(); foldAccessRelations(); assumeNoOutOfBounds(); + markFortranArrays(); } Scop::~Scop() { Index: lib/CodeGen/IslNodeBuilder.cpp =================================================================== --- lib/CodeGen/IslNodeBuilder.cpp +++ lib/CodeGen/IslNodeBuilder.cpp @@ -995,6 +995,90 @@ return true; } +/// %"struct.array3_integer(kind=4)" = type { i8*, i64, i64, [3 x +/// %struct.descriptor_dimension] } %struct.descriptor_dimension = type { i64, +/// i64, i64 } +/// @__src_soil_MOD_arr = global %"struct.array3_integer(kind=4)" +/// zeroinitializer, align 32 +/// ... +/// %0 = load i64, i64* getelementptr inbounds +/// (%"struct.array3_integer(kind=4)", %"struct.array3_integer(kind=4)"* +/// @__src_soil_MOD_arr, i64 0, i32 3, i64 0, i32 2), align 8, !tbaa !0 %1 = +/// load i64, i64* getelementptr inbounds (%"struct.array3_integer(kind=4)", +/// %"struct.array3_integer(kind=4)"* @__src_soil_MOD_arr, i64 0, i32 3, i64 0, +/// i32 1), align 8, !tbaa !0 %2 = sub nsw i64 %0, %1 %3 = add nsw i64 %2, 1 +Value *buildFortranArrayDescriptorOutermostDimensionLoad( + Value *GlobalDescriptor, PollyIRBuilder &Builder, std::string ArrayName) { + assert(GlobalDescriptor != nullptr && "invalid global descriptor given"); + + Value *endIdx[4] = {Builder.getInt64(0), Builder.getInt32(3), + Builder.getInt64(0), Builder.getInt32(2)}; + Value *endPtr = Builder.CreateInBoundsGEP(GlobalDescriptor, endIdx, + ArrayName + "_end_ptr"); + Value *end = Builder.CreateLoad(endPtr, ArrayName + "_end"); + + Value *beginIdx[4] = {Builder.getInt64(0), Builder.getInt32(3), + Builder.getInt64(0), Builder.getInt32(1)}; + Value *beginPtr = Builder.CreateInBoundsGEP(GlobalDescriptor, beginIdx, + ArrayName + "_begin_ptr"); + Value *begin = Builder.CreateLoad(beginPtr, ArrayName + "_begin"); + + Value *size = + Builder.CreateNSWSub(end, begin, ArrayName + "_end_begin_delta"); + Type *endType = dyn_cast(end->getType()); + assert(endType != nullptr && "expected type of end to be integral"); + + size = Builder.CreateNSWAdd(end, + ConstantInt::get(endType, 1, /* signed = */ true), + ArrayName + "_size"); + + return size; +} + +bool IslNodeBuilder::materializeFortranArrayOutermostDimensionParameters() { + for (const ScopStmt &Stmt : S) { + for (const MemoryAccess *Access : Stmt) { + if (!Access->isArrayKind()) + continue; + + const ScopArrayInfo *Array = Access->getScopArrayInfo(); + if (!Array) + continue; + + if (Array->getNumberOfDimensions() == 0) + continue; + + GlobalValue *FAD = + const_cast(Access->getFortranArrayDescriptor()); + if (FAD == nullptr) + continue; + + isl_pw_aff *parametric_pw_aff = Array->getDimensionSizePw(0); + assert(parametric_pw_aff != nullptr && "parameteric pw_aff corresponding " + "to outermost dimension does not " + "exist"); + + isl_id *Id = isl_pw_aff_get_dim_id(parametric_pw_aff, isl_dim_param, 0); + isl_pw_aff_free(parametric_pw_aff); + + assert(Id != nullptr && "pw_aff is not parametric"); + + if (IDToValue.count(Id)) { + isl_id_free(Id); + continue; + } + + Value *finalValue = buildFortranArrayDescriptorOutermostDimensionLoad( + dyn_cast(FAD), Builder, Array->getName()); + assert(finalValue != nullptr && "unable to build fortran array " + "descriptor load of outermost dimension"); + IDToValue[Id] = finalValue; + isl_id_free(Id); + } + } + return true; +} + /// Add the number of dimensions in @p BS to @p U. static isl_stat countTotalDims(__isl_take isl_basic_set *BS, void *U) { unsigned *NumTotalDim = static_cast(U); @@ -1313,6 +1397,12 @@ // Materialize values for the parameters of the SCoP. materializeParameters(); + // materialize the outermost dimension parameters for a fortran array. + // NOTE: materializeParameters() does not work since it looks through + // the SCEVs. We don't have a corresponding SCEV for the array size + // parameter + materializeFortranArrayOutermostDimensionParameters(); + // Generate values for the current loop iteration for all surrounding loops. // // We may also reference loops outside of the scop which do not contain the Index: lib/CodeGen/PPCGCodeGeneration.cpp =================================================================== --- lib/CodeGen/PPCGCodeGeneration.cpp +++ lib/CodeGen/PPCGCodeGeneration.cpp @@ -2126,9 +2126,18 @@ for (unsigned i = 1; i < NumDims; ++i) Extent = isl_set_lower_bound_si(Extent, isl_dim_set, i, 0); - for (unsigned i = 1; i < NumDims; ++i) { + for (unsigned i = 0; i < NumDims; ++i) { isl_pw_aff *PwAff = const_cast(Array->getDimensionSizePw(i)); + + // pwAff can be NULL for zero dimension. only in the case of a fortran + // array will we have a legitimate dimension + if (!PwAff) { + if (i != 0) + assert(false && "invalid dimension pwAff for nonzero dimension"); + continue; + } + isl_pw_aff *Val = isl_pw_aff_from_aff(isl_aff_var_on_domain( isl_local_space_from_space(Array->getSpace()), isl_dim_set, i)); PwAff = isl_pw_aff_add_dims(PwAff, isl_dim_in, Index: test/FortranDetection/global-malloc-nonvectored.ll =================================================================== --- /dev/null +++ test/FortranDetection/global-malloc-nonvectored.ll @@ -0,0 +1,143 @@ +; RUN: opt -S -analyze -polly-process-unprofitable -polly-remarks-minimal \ +; RUN: -polly-canonicalize -polly-scops -polly-dependences \ +; RUN: -debug-only=polly-dependence -polly-canonicalize -polly-allow-nonaffine \ +; RUN: -polly-ignore-aliasing -polly-invariant-load-hoisting \ +; RUN: < %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] Index: test/FortranDetection/global-nonmalloc-nonvectored.ll =================================================================== --- /dev/null +++ test/FortranDetection/global-nonmalloc-nonvectored.ll @@ -0,0 +1,87 @@ +; RUN: opt -S -analyze -polly-process-unprofitable -polly-remarks-minimal \ +; RUN: -polly-canonicalize -polly-scops -polly-dependences \ +; RUN: -debug-only=polly-dependence -polly-canonicalize -polly-allow-nonaffine \ +; RUN: -polly-ignore-aliasing -polly-invariant-load-hoisting \ +; RUN: < %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] Index: test/Isl/CodeGen/fortran_array_runtime_size_generation.ll =================================================================== --- /dev/null +++ test/Isl/CodeGen/fortran_array_runtime_size_generation.ll @@ -0,0 +1,100 @@ +; Check that the runtime size computation is generation for fortran arrays +; PPCG code generation backend: +; RUN: opt %loadPolly -S -polly-process-unprofitable -polly-allow-nonaffine \ +; RUN: -polly-ignore-aliasing -polly-invariant-load-hoisting \ +; RUN: -polly-canonicalize -polly-target=gpu -polly-acc-dump-kernel-ir \ +; RUN: -polly-acc-mincompute=0 -polly-codegen-ppcg \ +; RUN: < %s | FileCheck %s + +; Reguar code generation backend: +; RUN: opt %loadPolly -S -polly-process-unprofitable -polly-allow-nonaffine \ +; RUN: -polly-ignore-aliasing -polly-invariant-load-hoisting \ +; RUN: -polly-canonicalize -polly-codegen < %s | FileCheck %s + +; What the input fortran code should look like. NOTE: this is fake, the +; .ll file was hand-written. +; +; MODULE testmod +; USE data_parameters, ONLY : & +; IMPLICIT NONE +; +; INTEGER (KIND=iintegers), ALLOCATABLE, PRIVATE :: & +; arrin(:), arrout(:) +; CONTAINS +; +; SUBROUTINE test() +; INTEGER (KIND=iintegers) :: i +; +; DO i = 1, 100 +; arrout(i) = arrin(i) * arrin(i) +; END DO +; END SUBROUTINE test +; END MODULE testmod + +target datalayout = "e-p:64:64:64-S128-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i32: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*, i32, i32, [1 x %struct.descriptor_dimension] } +%struct.descriptor_dimension = type { i32, i32, i32 } + +@arrin = unnamed_addr global %"struct.array1_real(kind=8)" zeroinitializer, align 32 +@arrout = unnamed_addr global %"struct.array1_real(kind=8)" zeroinitializer, align 32 + +; Function Attrs: nounwind uwtable +define void @__src_soil_MOD_terra1() unnamed_addr #0 { +entry: + %rawmemin = load i8*, i8** getelementptr inbounds (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"* @arrin, i32 0, i32 0), align 32, !tbaa !5 + %typedmemin = bitcast i8* %rawmemin to i32* + + + %rawmemout = load i8*, i8** getelementptr inbounds (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"* @arrout, i32 0, i32 0), align 32, !tbaa !5 + %typedmemout = bitcast i8* %rawmemout to i32* + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %indvars.iv = phi i32 [ %indvars.iv.next, %for.inc ], [ 1, %entry ] + %exitcond = icmp ne i32 %indvars.iv, 100 + br i1 %exitcond, label %for.body, label %return + +for.body: ; preds = %"3", %"3.preheader" + ; %6 = add nsw i32 %indvars.iv, %0 + %inslot = getelementptr inbounds i32, i32* %typedmemin, i32 %indvars.iv + %inval = load i32, i32* %inslot, align 8 + + %outslot = getelementptr inbounds i32, i32* %typedmemout, i32 %indvars.iv + + %out = mul nsw i32 %inval, %inval + store i32 %out, i32* %outslot, align 8 + + br label %for.inc + +for.inc: + %indvars.iv.next = add i32 %indvars.iv, 1 + br label %for.cond + +return: ; preds = %"3", %entry + ret void +} + +attributes #0 = { nounwind uwtable } + +!0 = !{!1, !1, i32 0} +!1 = !{!"alias set 11: integer(kind=4)", !2} +!2 = distinct !{!2} +!3 = !{!4, !4, i32 0} +!4 = !{!"alias set 4: integer(kind=8)", !2} +!5 = !{!6, !6, i32 0} +!6 = !{!"alias set 3: void*", !2} +!7 = !{!8, !8, i32 0} +!8 = !{!"alias set 18: real(kind=8)", !2} + +; CHECK: %MemRef_rawmemin1_end = load i32, i32* getelementptr inbounds (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"* @arrin, i64 0, i32 3, i64 0, i32 2) +; CHECK-NEXT: %MemRef_rawmemin1_begin = load i32, i32* getelementptr inbounds (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"* @arrin, i64 0, i32 3, i64 0, i32 1) +; CHECK-NEXT: %MemRef_rawmemin1_end_begin_delta = sub nsw i32 %MemRef_rawmemin1_end, %MemRef_rawmemin1_begin +; CHECK-NEXT: %MemRef_rawmemin1_size = add nsw i32 %MemRef_rawmemin1_end, 1 +; CHECK-NEXT: %MemRef_rawmemout2_end = load i32, i32* getelementptr inbounds (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"* @arrout, i64 0, i32 3, i64 0, i32 2) +; CHECK-NEXT: %MemRef_rawmemout2_begin = load i32, i32* getelementptr inbounds (%"struct.array1_real(kind=8)", %"struct.array1_real(kind=8)"* @arrout, i64 0, i32 3, i64 0, i32 1) +; CHECK-NEXT: %MemRef_rawmemout2_end_begin_delta = sub nsw i32 %MemRef_rawmemout2_end, %MemRef_rawmemout2_begin +; CHECK-NEXT: %MemRef_rawmemout2_size = add nsw i32 %MemRef_rawmemout2_end, 1