diff --git a/llvm/include/llvm/Analysis/Delinearization.h b/llvm/include/llvm/Analysis/Delinearization.h --- a/llvm/include/llvm/Analysis/Delinearization.h +++ b/llvm/include/llvm/Analysis/Delinearization.h @@ -23,6 +23,7 @@ namespace llvm { class ScalarEvolution; class SCEV; +class Loop; /// Compute the array dimensions Sizes from the set of Terms extracted from /// the memory access function of this SCEVAddRecExpr (second step of @@ -115,6 +116,10 @@ SmallVectorImpl &Subscripts, SmallVectorImpl &Sizes, const SCEV *ElementSize); +void delinearizeAccess(ScalarEvolution &SE, Value *PtrVal, Loop *Scope, + SmallVectorImpl &Subscripts, + SmallVectorImpl &Sizes); + struct DelinearizationPrinterPass : public PassInfoMixin { explicit DelinearizationPrinterPass(raw_ostream &OS); diff --git a/llvm/lib/Analysis/Delinearization.cpp b/llvm/lib/Analysis/Delinearization.cpp --- a/llvm/lib/Analysis/Delinearization.cpp +++ b/llvm/lib/Analysis/Delinearization.cpp @@ -471,6 +471,68 @@ }); } +void llvm::delinearizeAccess(ScalarEvolution &SE, Value *PtrVal, Loop *Scope, + SmallVectorImpl &Subscripts, + SmallVectorImpl &Sizes) { + + Type *AssumedEltType; + auto PtrExpr = SE.getSCEVAtScope(PtrVal, Scope); + + const SCEVUnknown *BasePointer = + dyn_cast(SE.getPointerBase(PtrExpr)); + // Do not delinearize if we cannot find the base pointer. + if (!BasePointer) + return; + auto AccessFn = SE.getMinusSCEV(PtrExpr, BasePointer); + + if (auto GEP = dyn_cast(PtrVal)) { + auto SrcType = GEP->getSourceElementType(); + auto LastLvlTy = SrcType; + auto NumDerefs = GEP->getNumOperands(); + // TODO: Do this at most NumDerefs times. + while (LastLvlTy->isPointerTy()) { + LastLvlTy = LastLvlTy->getPointerElementType(); + } + AssumedEltType = LastLvlTy; + } else { + AssumedEltType = PtrVal->getType()->getPointerElementType(); + } + Type *PtrTy = PtrVal->getType(); + Type *ETy = SE.getEffectiveSCEVType(PointerType::getUnqual(PtrTy)); + const SCEV *ElementSize = SE.getSizeOfExpr(ETy, AssumedEltType); + + // First step: collect parametric terms. + SmallVector Terms; + collectParametricTerms(SE, AccessFn, Terms); + + if (Terms.empty()) + return; + + // Second step: find subscript sizes. + findArrayDimensions(SE, Terms, Sizes, ElementSize); + + if (Sizes.empty()) + return; + + // Third step: compute the access functions for each subscript. + computeAccessFunctions(SE, AccessFn, Subscripts, Sizes); + + if (Subscripts.empty()) + return; + + LLVM_DEBUG({ + dbgs() << "succeeded to delinearize " << *AccessFn << "\n"; + dbgs() << "ArrayDecl[UnknownSize]"; + for (const SCEV *S : Sizes) + dbgs() << "[" << *S << "]"; + + dbgs() << "\nArrayRef"; + for (const SCEV *S : Subscripts) + dbgs() << "[" << *S << "]"; + dbgs() << "\n"; + }); +} + namespace { class Delinearization : public FunctionPass { @@ -496,8 +558,7 @@ O << "Delinearization on function " << F->getName() << ":\n"; for (Instruction &Inst : instructions(F)) { // Only analyze loads and stores. - if (!isa(&Inst) && !isa(&Inst) && - !isa(&Inst)) + if (!isa(&Inst) && !isa(&Inst)) continue; const BasicBlock *BB = Inst.getParent(); @@ -506,6 +567,8 @@ for (Loop *L = LI->getLoopFor(BB); L != nullptr; L = L->getParentLoop()) { const SCEV *AccessFn = SE->getSCEVAtScope(getPointerOperand(&Inst), L); + auto PtrVal = getPointerOperand(&Inst); + const SCEVUnknown *BasePointer = dyn_cast(SE->getPointerBase(AccessFn)); // Do not delinearize if we cannot find the base pointer. @@ -519,7 +582,7 @@ O << "AccessFunction: " << *AccessFn << "\n"; SmallVector Subscripts, Sizes; - delinearize(*SE, AccessFn, Subscripts, Sizes, SE->getElementSize(&Inst)); + delinearizeAccess(*SE, PtrVal, L, Subscripts, Sizes); if (Subscripts.size() == 0 || Sizes.size() == 0 || Subscripts.size() != Sizes.size() + 1) { O << "failed to delinearize\n"; diff --git a/llvm/test/Analysis/Delinearization/array_of_struct.ll b/llvm/test/Analysis/Delinearization/array_of_struct.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Analysis/Delinearization/array_of_struct.ll @@ -0,0 +1,70 @@ +; RUN: opt < %s -analyze -enable-new-pm=0 -delinearize | FileCheck %s +; RUN: opt < %s -passes='print' -disable-output 2>&1 | FileCheck %s + +; CHECK: Inst: store i64 0, i64* %y, align 1, !tbaa !3 +; CHECK: In Loop with Header: for.cond +; CHECK: AccessFunction: {4,+,(12 + (12 * %N))}<%for.cond> +; CHECK: Base offset: %A +; CHECK: ArrayDecl[UnknownSize][%N][12] +; CHECK: ArrayRef[{0,+,1}<%for.cond>][{0,+,1}<%for.cond>][4] + +; struct __attribute__((packed)) Pair { int x; long y; }; +; void foo(unsigned long N, struct Pair A[][N]) { +; for (unsigned long i = 0; i < N; i+=1) +; A[i][i].y = 0; +; } + + + +source_filename = "byte_offset.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.Pair = type <{ i32, i64 }> + +; Function Attrs: nounwind uwtable +define dso_local void @foo(i64 %N, %struct.Pair* %A) #0 { +entry: + br label %for.cond + +for.cond: ; preds = %for.body, %entry + %i.0 = phi i64 [ 0, %entry ], [ %add, %for.body ] + %cmp = icmp ult i64 %i.0, %N + br i1 %cmp, label %for.body, label %for.end + +for.body: ; preds = %for.cond + %0 = mul nsw i64 %i.0, %N + %arrayidx = getelementptr inbounds %struct.Pair, %struct.Pair* %A, i64 %0 + %arrayidx1 = getelementptr inbounds %struct.Pair, %struct.Pair* %arrayidx, i64 %i.0 + %y = getelementptr inbounds %struct.Pair, %struct.Pair* %arrayidx1, i64 0, i32 1 + store i64 0, i64* %y, align 1, !tbaa !3 + %add = add i64 %i.0, 1 + br label %for.cond, !llvm.loop !9 + +for.end: ; preds = %for.cond + ret void +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +attributes #0 = { nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { argmemonly nofree nosync nounwind willreturn } + +!llvm.module.flags = !{!0, !1} +!llvm.ident = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"uwtable", i32 1} +!2 = !{!"clang version 14.0.0 (/home/meinersbur/src/llvm-project/clang 164631ce80ecbcc0af069cb6ddd29fef726f222a)"} +!3 = !{!4, !8, i64 4} +!4 = !{!"Pair", !5, i64 0, !8, i64 4} +!5 = !{!"int", !6, i64 0} +!6 = !{!"omnipotent char", !7, i64 0} +!7 = !{!"Simple C/C++ TBAA"} +!8 = !{!"long", !6, i64 0} +!9 = distinct !{!9, !10} +!10 = !{!"llvm.loop.mustprogress"} \ No newline at end of file