Index: include/polly/ScopBuilder.h =================================================================== --- include/polly/ScopBuilder.h +++ include/polly/ScopBuilder.h @@ -278,7 +278,7 @@ Value *BaseAddress, Type *ElemType, bool Affine, Value *AccessValue, ArrayRef Subscripts, - ArrayRef Sizes, MemoryKind Kind); + ShapeInfo Shape, MemoryKind Kind); /// Create a MemoryAccess that represents either a LoadInst or /// StoreInst. @@ -298,7 +298,7 @@ MemoryAccess::AccessType AccType, Value *BaseAddress, Type *ElemType, bool IsAffine, ArrayRef Subscripts, - ArrayRef Sizes, Value *AccessValue); + ShapeInfo Shape, Value *AccessValue); /// Create a MemoryAccess for writing an llvm::Instruction. /// Index: include/polly/ScopInfo.h =================================================================== --- include/polly/ScopInfo.h +++ include/polly/ScopInfo.h @@ -108,6 +108,195 @@ DELINEARIZATION, }; +// Abstract over a notion of the shape of an array: +// Once can compute indeces using both sizes and strides. +class ShapeInfo { +private: + using SCEVArrayTy = SmallVector; + using SCEVArrayRefTy = ArrayRef; + + using OptionalSCEVArrayTy = Optional; + using OptionalSCEVArrayRefTy = Optional; + + llvm::Optional> Sizes; + llvm::Optional> Strides; + llvm::Optional Offset; + + llvm::OptionalHackFAD; + + ShapeInfo(Optional> SizesRef, + Optional> StridesRef, + llvm::Optional Offset, + llvm::OptionalHackFAD) + : Offset(Offset), HackFAD(HackFAD) { + // Can check for XOR + assert(bool(SizesRef) || bool(StridesRef)); + assert(!(bool(SizesRef) && bool(StridesRef))); + + if (StridesRef || Offset) { + assert(Offset); + assert(StridesRef); + } + + if (SizesRef) + Sizes = + OptionalSCEVArrayTy(SCEVArrayTy(SizesRef->begin(), SizesRef->end())); + + if (StridesRef) + Strides = OptionalSCEVArrayTy( + SCEVArrayTy(StridesRef->begin(), StridesRef->end())); + } + + ShapeInfo(NoneType) : Sizes(None), Strides(None), Offset(None), HackFAD(None) {} + +public: + static ShapeInfo fromSizes(ArrayRef Sizes) { + return ShapeInfo(OptionalSCEVArrayRefTy(Sizes), None, None, None); + } + + // We have this anti-pattern in polly which does this: + // Shape(ShapeInfo::fromSizes({nullptr}) + // Consider providing a separate constructor for this, which we then + // kill in some cleanup. + + ShapeInfo(const ShapeInfo &other) { + Sizes = other.Sizes; + Strides = other.Strides; + Offset = other.Offset; + HackFAD = other.HackFAD; + } + + ShapeInfo &operator=(const ShapeInfo &other) { + Sizes = other.Sizes; + Strides = other.Strides; + Offset = other.Offset; + HackFAD = other.HackFAD; + return *this; + } + + static ShapeInfo fromStrides(ArrayRef Strides, + const SCEV *Offset, + GlobalValue *FAD) { + assert(Offset && "offset is null"); + return ShapeInfo(None, OptionalSCEVArrayRefTy(Strides), + Optional(Offset), + Optional(FAD)); + } + + static ShapeInfo none() { return ShapeInfo(None); } + + unsigned getNumberOfDimensions() const { + // assert(isInitialized()); + if (Sizes) + return Sizes->size(); + + if (Strides) + return Strides->size(); + + return 0; + } + + /// Set the sizes of the Shape. It checks the invariant + /// That this shape does not have strides. + void setSizes(SmallVector NewSizes) { + assert(!bool(Strides)); + + if (!bool(Sizes)) { + Sizes = Optional>( + SmallVector()); + } + + Sizes = NewSizes; + } + + /// Set the strides of the Shape. It checks the invariant + /// That this shape does not have sizes. + void setStrides(ArrayRef NewStrides, const SCEV *NewOffset, GlobalValue *NewHackFAD) { + Offset = NewOffset; + assert(!bool(Sizes)); + + // Be explicit because GCC(5.3.0) is unable to deduce this. + if (!Strides) + Strides = Optional>( + SmallVector()); + + Strides->clear(); + Strides->insert(Strides->begin(), NewStrides.begin(), NewStrides.end()); + + HackFAD = NewHackFAD; + + assert(Offset && "offset is null"); + } + + const SmallVector &sizes() const { + assert(!bool(Strides)); + return Sizes.getValue(); + } + + const SCEV *offset() const { return Offset.getValue(); } + + + GlobalValue *hackFAD() const { return HackFAD.getValue(); } + + SmallVector &sizes_mut() { + assert(!bool(Strides)); + return Sizes.getValue(); + } + + bool isInitialized() const { return bool(Sizes) || bool(Strides); } + + const SmallVector &strides() const { + assert(!bool(Sizes)); + return Strides.getValue(); + } + + bool hasSizes() const { return bool(Sizes); } + bool hasStrides() const { return bool(Strides); } + + template + Ret mapSizes(std::function &)> func, + Ret otherwise) { + if (Sizes) + return func(*Sizes); + + return otherwise; + } + + void mapSizes(std::function &)> func) { + if (Sizes) + func(*Sizes); + } + + raw_ostream &print(raw_ostream &OS) const { + if (Sizes) { + OS << "Sizes: "; + for (auto Size : *Sizes) { + if (Size) + OS << *Size << ", "; + else + OS << "null" + << ", "; + } + return OS; + } else if (Strides) { + OS << "Strides: "; + for (auto Stride : *Strides) { + if (Stride) + OS << *Stride << ", "; + else + OS << "null" + << ", "; + } + return OS; + } + OS << "Uninitialized.\n"; + return OS; + } +}; + +raw_ostream &operator<<(raw_ostream &OS, const ShapeInfo &Shape); + + /// Enum to distinguish between assumptions and restrictions. enum AssumptionSign { AS_ASSUMPTION, AS_RESTRICTION }; @@ -256,13 +445,14 @@ /// @param BasePtr The array base pointer. /// @param ElementType The type of the elements stored in the array. /// @param IslCtx The isl context used to create the base pointer id. + /// @param ShapeInfo /// @param DimensionSizes A vector containing the size of each dimension. /// @param Kind The kind of the array object. /// @param DL The data layout of the module. /// @param S The scop this array object belongs to. /// @param BaseName The optional name of this memory reference. ScopArrayInfo(Value *BasePtr, Type *ElementType, isl::ctx IslCtx, - ArrayRef DimensionSizes, MemoryKind Kind, + ShapeInfo Shape, MemoryKind Kind, const DataLayout &DL, Scop *S, const char *BaseName = nullptr); /// Destructor to free the isl id of the base pointer. @@ -292,6 +482,29 @@ /// with old sizes bool updateSizes(ArrayRef Sizes, bool CheckConsistency = true); + /// Update the strides of a ScopArrayInfo object, when we had + /// initially believed it to be a size based representation, we later + /// get a more refined view of the array in terms of strides. + /// TODO: Think if this is really necessary. We needed this in COSMO for reasons, + /// (IIRC, they would have be a i8*, which they would index "correctly" + /// to get to the offset and stride info.) + /// Roughly, they had code like this: + /// struct ARRTY { int64 offset; int64 stride_1; int64 stride_2; void *memory; } + /// void * arr; + /// offset = ((int64 *)arr)[0]; + /// stride_1 = ((int64 *)arr)[1]; + /// stride_2 = ((int64 *)arr)[1]; + /// memory = fortran_index(((char*)arr+)), offset, stride_1, stride_2, ix1, ix2); + /// In this type of code, we would first see the naked access of "arr" which we would represent in a + /// size based repr, and we would change our view later into the strided version. + /// We should probably disallow this for future clients. + void overwriteSizeWithStrides(ArrayRef Strides, + const SCEV *Offset, GlobalValue *HackFAD); + + /// Update the strides of a ScopArrayInfo object. + bool updateStrides(ArrayRef Strides, const SCEV *Offset, GlobalValue *HackFAD); + + /// Make the ScopArrayInfo model a Fortran array. /// It receives the Fortran array descriptor and stores this. /// It also adds a piecewise expression for the outermost dimension @@ -423,6 +636,7 @@ /// @returns True, if the arrays are compatible, False otherwise. bool isCompatibleWith(const ScopArrayInfo *Array) const; + bool hasStrides() const { return Shape.hasStrides(); } private: void addDerivedSAI(ScopArrayInfo *DerivedSAI) { DerivedSAIs.insert(DerivedSAI); @@ -466,6 +680,9 @@ /// The data layout of the module. const DataLayout &DL; + /// The sizes of each dimension as SCEV*. + ShapeInfo Shape; + /// The scop this SAI object belongs to. Scop &S; @@ -588,6 +805,9 @@ Type *ElementType; /// Size of each dimension of the accessed array. + ShapeInfo Shape; + + /// Size of each dimension of the accessed array. SmallVector Sizes; // @} @@ -756,10 +976,11 @@ /// @param IsAffine Whether the subscripts are affine expressions. /// @param Kind The kind of memory accessed. /// @param Subscripts Subscript expressions - /// @param Sizes Dimension lengths of the accessed array. + /// @param Shape Shape of the accessed array. + MemoryAccess(ScopStmt *Stmt, Instruction *AccessInst, AccessType AccType, Value *BaseAddress, Type *ElemType, bool Affine, - ArrayRef Subscripts, ArrayRef Sizes, + ArrayRef Subscripts, ShapeInfo Shape, Value *AccessValue, MemoryKind Kind); /// Create a new MemoryAccess that corresponds to @p AccRel. @@ -2841,14 +3062,15 @@ const MapInsnToMemAcc &getInsnToMemAccMap() const { return DC.InsnToMemAcc; } /// Return the (possibly new) ScopArrayInfo object for @p Access. - /// + /// @param BasePtr The base pointer of the SAI to add /// @param ElementType The type of the elements stored in this array. + /// @param Shape The shape of the SAI. /// @param Kind The kind of the array info object. /// @param BaseName The optional name of this memory reference. ScopArrayInfo *getOrCreateScopArrayInfo(Value *BasePtr, Type *ElementType, - ArrayRef Sizes, - MemoryKind Kind, - const char *BaseName = nullptr); + ShapeInfo Shape, + MemoryKind Kind, + const char *BaseName = nullptr); /// Create an array and return the corresponding ScopArrayInfo object. /// Index: lib/Analysis/ScopBuilder.cpp =================================================================== --- lib/Analysis/ScopBuilder.cpp +++ lib/Analysis/ScopBuilder.cpp @@ -429,7 +429,7 @@ ConstantInt::get(IntegerType::getInt64Ty(BasePtr->getContext()), V))); addArrayAccess(Stmt, Inst, AccType, BasePointer->getValue(), ElementType, - true, Subscripts, SizesSCEV, Val); + true, Subscripts, ShapeInfo::fromSizes(SizesSCEV), Val); return true; } @@ -480,7 +480,7 @@ scop->invalidate(DELINEARIZATION, Inst->getDebugLoc(), Inst->getParent()); addArrayAccess(Stmt, Inst, AccType, BasePointer->getValue(), ElementType, - true, AccItr->second.DelinearizedSubscripts, Sizes, Val); + true, AccItr->second.DelinearizedSubscripts, ShapeInfo::fromSizes(Sizes), Val); return true; } @@ -522,10 +522,13 @@ auto *DestPtrSCEV = dyn_cast(SE.getPointerBase(DestAccFunc)); assert(DestPtrSCEV); + // TODO: code smell, why doe we initialize an empty shape? I don't recall the details + // anymore. DestAccFunc = SE.getMinusSCEV(DestAccFunc, DestPtrSCEV); addArrayAccess(Stmt, Inst, MemoryAccess::MUST_WRITE, DestPtrSCEV->getValue(), IntegerType::getInt8Ty(DestPtrVal->getContext()), - LengthIsAffine, {DestAccFunc, LengthVal}, {nullptr}, + LengthIsAffine, {DestAccFunc, LengthVal}, + ShapeInfo::fromSizes({nullptr}), Inst.getValueOperand()); auto *MemTrans = dyn_cast(MemIntr); @@ -547,7 +550,8 @@ SrcAccFunc = SE.getMinusSCEV(SrcAccFunc, SrcPtrSCEV); addArrayAccess(Stmt, Inst, MemoryAccess::READ, SrcPtrSCEV->getValue(), IntegerType::getInt8Ty(SrcPtrVal->getContext()), - LengthIsAffine, {SrcAccFunc, LengthVal}, {nullptr}, + LengthIsAffine, {SrcAccFunc, LengthVal}, + ShapeInfo::fromSizes({nullptr}), Inst.getValueOperand()); return true; @@ -591,9 +595,10 @@ if (ArgSCEV->isZero()) continue; + // TODO: again, why do we create a shape with a nullptr shape? auto *ArgBasePtr = cast(SE.getPointerBase(ArgSCEV)); addArrayAccess(Stmt, Inst, AccType, ArgBasePtr->getValue(), - ArgBasePtr->getType(), false, {AF}, {nullptr}, CI); + ArgBasePtr->getType(), false, {AF}, ShapeInfo::fromSizes({nullptr}), CI); } return true; } @@ -643,7 +648,8 @@ AccType = MemoryAccess::MAY_WRITE; addArrayAccess(Stmt, Inst, AccType, BasePointer->getValue(), ElementType, - IsAffine, {AccessFunction}, {nullptr}, Val); + IsAffine, {AccessFunction}, + ShapeInfo::fromSizes({nullptr}), Val); } void ScopBuilder::buildMemoryAccess(MemAccInst Inst, ScopStmt *Stmt) { @@ -1011,7 +1017,7 @@ MemoryAccess *ScopBuilder::addMemoryAccess( ScopStmt *Stmt, Instruction *Inst, MemoryAccess::AccessType AccType, Value *BaseAddress, Type *ElementType, bool Affine, Value *AccessValue, - ArrayRef Subscripts, ArrayRef Sizes, + ArrayRef Subscripts, ShapeInfo Shape, MemoryKind Kind) { bool isKnownMustAccess = false; @@ -1039,7 +1045,7 @@ AccType = MemoryAccess::MAY_WRITE; auto *Access = new MemoryAccess(Stmt, Inst, AccType, BaseAddress, ElementType, - Affine, Subscripts, Sizes, AccessValue, Kind); + Affine, Subscripts, Shape, AccessValue, Kind); scop->addAccessFunction(Access); Stmt->addAccess(Access); @@ -1051,12 +1057,12 @@ Value *BaseAddress, Type *ElementType, bool IsAffine, ArrayRef Subscripts, - ArrayRef Sizes, + ShapeInfo Shape, Value *AccessValue) { ArrayBasePointers.insert(BaseAddress); auto *MemAccess = addMemoryAccess(Stmt, MemAccInst, AccType, BaseAddress, ElementType, IsAffine, AccessValue, - Subscripts, Sizes, MemoryKind::Array); + Subscripts, Shape, MemoryKind::Array); if (!DetectFortranArrays) return; @@ -1090,7 +1096,7 @@ addMemoryAccess(Stmt, Inst, MemoryAccess::MUST_WRITE, Inst, Inst->getType(), true, Inst, ArrayRef(), - ArrayRef(), MemoryKind::Value); + ShapeInfo::fromSizes(ArrayRef()), MemoryKind::Value); } void ScopBuilder::ensureValueRead(Value *V, ScopStmt *UserStmt) { @@ -1128,7 +1134,7 @@ break; addMemoryAccess(UserStmt, nullptr, MemoryAccess::READ, V, V->getType(), - true, V, ArrayRef(), ArrayRef(), + true, V, ArrayRef(), ShapeInfo::fromSizes(ArrayRef()), MemoryKind::Value); // Inter-statement uses need to write the value in their defining statement. @@ -1145,7 +1151,7 @@ // will create an exit PHI SAI object. It is needed during code generation // and would be created later anyway. if (IsExitBlock) - scop->getOrCreateScopArrayInfo(PHI, PHI->getType(), {}, + scop->getOrCreateScopArrayInfo(PHI, PHI->getType(), ShapeInfo::fromSizes({}), MemoryKind::ExitPHI); // This is possible if PHI is in the SCoP's entry block. The incoming blocks @@ -1169,7 +1175,7 @@ MemoryAccess *Acc = addMemoryAccess( IncomingStmt, PHI, MemoryAccess::MUST_WRITE, PHI, PHI->getType(), true, - PHI, ArrayRef(), ArrayRef(), + PHI, ArrayRef(), ShapeInfo::fromSizes(ArrayRef()), IsExitBlock ? MemoryKind::ExitPHI : MemoryKind::PHI); assert(Acc); Acc->addIncoming(IncomingBlock, IncomingValue); @@ -1177,7 +1183,7 @@ void ScopBuilder::addPHIReadAccess(ScopStmt *PHIStmt, PHINode *PHI) { addMemoryAccess(PHIStmt, PHI, MemoryAccess::READ, PHI, PHI->getType(), true, - PHI, ArrayRef(), ArrayRef(), + PHI, ArrayRef(), ShapeInfo::fromSizes(ArrayRef()), MemoryKind::PHI); } @@ -1360,7 +1366,7 @@ Ty = MemoryKind::Array; auto *SAI = scop->getOrCreateScopArrayInfo(Access->getOriginalBaseAddr(), - ElementType, Access->Sizes, Ty); + ElementType, Access->Shape, Ty); Access->buildAccessRelation(SAI); scop->addAccessData(Access); } @@ -1502,7 +1508,7 @@ Instruction *GlobalRead = GlobalReadPair.second; for (auto *BP : ArrayBasePointers) addArrayAccess(GlobalReadStmt, MemAccInst(GlobalRead), MemoryAccess::READ, - BP, BP->getType(), false, {AF}, {nullptr}, GlobalRead); + BP, BP->getType(), false, {AF}, ShapeInfo::fromSizes({nullptr}), GlobalRead); } scop->buildInvariantEquivalenceClasses(); Index: lib/Analysis/ScopInfo.cpp =================================================================== --- lib/Analysis/ScopInfo.cpp +++ lib/Analysis/ScopInfo.cpp @@ -248,6 +248,10 @@ //===----------------------------------------------------------------------===// +raw_ostream &polly::operator<<(raw_ostream &OS, const ShapeInfo &Shape) { + return Shape.print(OS); +} + // Create a sequence of two schedules. Either argument may be null and is // interpreted as the empty schedule. Can also return null if both schedules are // empty. @@ -318,10 +322,11 @@ } ScopArrayInfo::ScopArrayInfo(Value *BasePtr, Type *ElementType, isl::ctx Ctx, - ArrayRef Sizes, MemoryKind Kind, + ShapeInfo Shape, 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), + Shape(ShapeInfo::none()), S(*S) { std::string BasePtrName = BaseName ? BaseName : getIslCompatibleName("MemRef", BasePtr, S->getNextArrayIdx(), @@ -329,7 +334,12 @@ UseInstructionNames); Id = isl::id::alloc(Ctx, BasePtrName, this); - updateSizes(Sizes); + // TODO: why do we need updateSizes() ? Why don't we set this up in the + // ctor? + if (Shape.hasSizes()) + updateSizes(Shape.sizes()); + else + updateStrides(Shape.strides(), Shape.offset(), Shape.hackFAD()); if (!BasePtr || Kind != MemoryKind::Array) { BasePtrOriginSAI = nullptr; @@ -415,6 +425,36 @@ DimensionSizesPw[0] = PwAff; } +void ScopArrayInfo::overwriteSizeWithStrides(ArrayRef Strides, + const SCEV *Offset, + GlobalValue *FAD) { + + // HACK: first set our shape to a stride based shape so that we don't + // assert within updateStrides. Move this into a bool parameter of + // updateStrides + Shape = ShapeInfo::fromStrides(Strides, Offset, FAD); + updateStrides(Strides, Offset, FAD); +} +bool ScopArrayInfo::updateStrides(ArrayRef Strides, + const SCEV *Offset, GlobalValue *FAD) { + Shape.setStrides(Strides, Offset, FAD); + DimensionSizesPw.clear(); + for (size_t i = 0; i < Shape.getNumberOfDimensions(); i++) { + isl::space Space(S.getIslCtx(), 1, 0); + + std::string param_name = getIslCompatibleName( + "stride_" + std::to_string(i) + "__", getName(), ""); + isl::id IdPwAff = isl::id::alloc(S.getIslCtx(), param_name, this); + + Space = Space.set_dim_id(isl::dim::param, 0, IdPwAff); + isl::pw_aff PwAff = + isl::aff::var_on_domain(isl::local_space(Space), isl::dim::param, 0); + + DimensionSizesPw.push_back(PwAff); + } + return true; +} + bool ScopArrayInfo::updateSizes(ArrayRef NewSizes, bool CheckConsistency) { int SharedDims = std::min(NewSizes.size(), DimensionSizes.size()); @@ -1012,11 +1052,11 @@ AccessType AccType, Value *BaseAddress, Type *ElementType, bool Affine, ArrayRef Subscripts, - ArrayRef Sizes, Value *AccessValue, + ShapeInfo Shape, Value *AccessValue, MemoryKind Kind) : Kind(Kind), AccType(AccType), Statement(Stmt), InvalidDomain(nullptr), BaseAddr(BaseAddress), ElementType(ElementType), - Sizes(Sizes.begin(), Sizes.end()), AccessInstruction(AccessInst), + Shape(Shape), AccessInstruction(AccessInst), AccessValue(AccessValue), IsAffine(Affine), Subscripts(Subscripts.begin(), Subscripts.end()), AccessRelation(nullptr), NewAccessRelation(nullptr), FAD(nullptr) { @@ -1030,6 +1070,7 @@ MemoryAccess::MemoryAccess(ScopStmt *Stmt, AccessType AccType, isl::map AccRel) : Kind(MemoryKind::Array), AccType(AccType), Statement(Stmt), InvalidDomain(nullptr), AccessRelation(nullptr), + Shape(ShapeInfo::fromSizes({nullptr})), NewAccessRelation(AccRel), FAD(nullptr) { isl::id ArrayInfoId = NewAccessRelation.get_tuple_id(isl::dim::out); auto *SAI = ScopArrayInfo::getFromId(ArrayInfoId); @@ -1840,10 +1881,11 @@ if (Access) return Access; + // TODO: again, why do we have an _empty_ list here for size? ScopArrayInfo *SAI = - Parent.getOrCreateScopArrayInfo(V, V->getType(), {}, MemoryKind::Value); + Parent.getOrCreateScopArrayInfo(V, V->getType(), ShapeInfo::fromSizes({}), MemoryKind::Value); Access = new MemoryAccess(this, nullptr, MemoryAccess::READ, V, V->getType(), - true, {}, {}, V, MemoryKind::Value); + true, {}, ShapeInfo::fromSizes({}), V, MemoryKind::Value); Parent.addAccessFunction(Access); Access->buildAccessRelation(SAI); addAccess(Access); @@ -3964,27 +4006,94 @@ } } +Value *getPointerFromLoadOrStore(Value *V) { + if (LoadInst *LI = dyn_cast(V)) + return LI->getPointerOperand(); + + if (StoreInst *SI = dyn_cast(V)) + return SI->getPointerOperand(); + return nullptr; +} + + ScopArrayInfo *Scop::getOrCreateScopArrayInfo(Value *BasePtr, Type *ElementType, - ArrayRef Sizes, + ShapeInfo Shape, MemoryKind Kind, const char *BaseName) { assert((BasePtr || BaseName) && "BasePtr and BaseName can not be nullptr at the same time."); assert(!(BasePtr && BaseName) && "BaseName is redundant."); + + // We assume that arrays with the strided representation can unify. + // Yes this is nuts. Yes I stil want to do this. + auto unifyStridedArrayBasePtrs = [&]() -> Value * { + if (Shape.hasSizes()) return BasePtr; + + const Value *CurBase = getPointerFromLoadOrStore(BasePtr); + if (!CurBase) + return BasePtr; + + for (ScopArrayInfo *SAI : arrays()) { + Value *SAIBase = getPointerFromLoadOrStore(SAI->getBasePtr()); + if (!SAIBase) + continue; + + if (SAIBase == CurBase && SAI->hasStrides()) + return SAI->getBasePtr(); + } + return BasePtr; + }; + + BasePtr = unifyStridedArrayBasePtrs(); + auto &SAI = BasePtr ? ScopArrayInfoMap[std::make_pair(BasePtr, Kind)] : ScopArrayNameMap[BaseName]; + + + // errs() << "Creating: " << (int)Kind << "\n"; + // BasePtr->dump(); + if (!SAI) { auto &DL = getFunction().getParent()->getDataLayout(); - SAI.reset(new ScopArrayInfo(BasePtr, ElementType, getIslCtx(), Sizes, Kind, + SAI.reset(new ScopArrayInfo(BasePtr, ElementType, getIslCtx(), Shape, Kind, DL, this, BaseName)); ScopArrayInfoSet.insert(SAI.get()); } else { SAI->updateElementType(ElementType); // In case of mismatching array sizes, we bail out by setting the run-time // context to false. - if (!SAI->updateSizes(Sizes)) - invalidate(DELINEARIZATION, DebugLoc()); + if (SAI->hasStrides() != Shape.hasStrides()) { + LLVM_DEBUG(dbgs() << "SAI and new shape do not agree:\n"); + LLVM_DEBUG(dbgs() << "SAI: "; SAI->print(dbgs(), true); dbgs() << "\n"); + LLVM_DEBUG(dbgs() << "Shape: " << Shape << "\n"); + + if (Shape.hasStrides()) { + LLVM_DEBUG(dbgs() << "Shape has strides, SAI had size. Overwriting size " + "with strides"); + SAI->overwriteSizeWithStrides(Shape.strides(), Shape.offset(), + Shape.hackFAD()); + } else { + + errs() << __PRETTY_FUNCTION__ << "\n" << "SAI has strides, Shape is size based. This should not " + "happen. Ignoring new data for now."; + errs() << " SAI:\n"; + SAI->print(errs(), false); + errs() << "Shape:\n"; + errs() << Shape << "\n"; + errs() << "---\n"; + // report_fatal_error("SAI was given sizes when it had strides"); + return SAI.get(); + } + } + + if (SAI->hasStrides()) { + SAI->updateStrides(Shape.strides(), Shape.offset(), Shape.hackFAD()); + } else { + if (!SAI->updateSizes(Shape.sizes())) + invalidate(DELINEARIZATION, DebugLoc()); + } } + return SAI.get(); } @@ -4000,7 +4109,7 @@ else SCEVSizes.push_back(nullptr); - auto *SAI = getOrCreateScopArrayInfo(nullptr, ElementType, SCEVSizes, + auto *SAI = getOrCreateScopArrayInfo(nullptr, ElementType, ShapeInfo::fromSizes(SCEVSizes), MemoryKind::Array, BaseName.c_str()); return SAI; } Index: lib/Transform/ForwardOpTree.cpp =================================================================== --- lib/Transform/ForwardOpTree.cpp +++ lib/Transform/ForwardOpTree.cpp @@ -362,7 +362,7 @@ MemoryAccess *Access = new MemoryAccess(Stmt, LI, MemoryAccess::READ, SAI->getBasePtr(), - LI->getType(), true, {}, Sizes, LI, MemoryKind::Array); + LI->getType(), true, {}, ShapeInfo::fromSizes(Sizes), LI, MemoryKind::Array); S->addAccessFunction(Access); Stmt->addAccess(Access, true);