diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp --- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp +++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp @@ -128,6 +128,11 @@ // of the argument that are accessed. std::map, LoadInst *> OriginalLoads; + // Keeps track of inbounds information of GEP instruction used to load + // promoted arguments present in callee function. This is used to preserve + // this inbound information in callers as well. + std::map, bool> IsArgGepInBound; + // Attribute - Keep track of the parameter attributes for the arguments // that we are *not* promoting. For the ones that we do promote, the parameter // attributes are lost @@ -191,6 +196,14 @@ // Take any load, we will use it only to update Alias Analysis OrigLoad = cast(UI->user_back()); OriginalLoads[std::make_pair(&*I, Indices)] = OrigLoad; + // Preserve inbounds information of GEP so that it can be used in + // callers of current function + const GEPOperator *GEPOp = dyn_cast_or_null(UI); + if (GEPOp) { + // Indices are unique in GEP since all the dead GEPs are already + // removed + IsArgGepInBound[std::make_pair(&*I, Indices)] = GEPOp->isInBounds(); + } } // Add a parameter to the function for each element passed in. @@ -267,8 +280,9 @@ Align StructAlign = *I->getParamAlign(); for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) { Idxs[1] = ConstantInt::get(Type::getInt32Ty(F->getContext()), i); - auto *Idx = - IRB.CreateGEP(STy, *AI, Idxs, (*AI)->getName() + "." + Twine(i)); + // It is safe to create inbounds GEP since source type is struct + auto *Idx = IRB.CreateInBoundsGEP(STy, *AI, Idxs, + (*AI)->getName() + "." + Twine(i)); // TODO: Tell AA about the new values? Align Alignment = commonAlignment(StructAlign, SL->getElementOffset(i)); @@ -303,7 +317,16 @@ ElTy = GetElementPtrInst::getTypeAtIndex(ElTy, II); } // And create a GEP to extract those indices. - V = IRB.CreateGEP(ArgIndex.first, V, Ops, V->getName() + ".idx"); + bool IsCalleeArgGepInBound = + IsArgGepInBound[std::make_pair(&*I, ArgIndex.second)]; + if (IsCalleeArgGepInBound) { + // Using preserved inbounds information of GEP in callee while + // creating GEP in caller. + V = IRB.CreateInBoundsGEP(ArgIndex.first, V, Ops, + V->getName() + ".idx"); + } else { + V = IRB.CreateGEP(ArgIndex.first, V, Ops, V->getName() + ".idx"); + } Ops.clear(); } // Since we're replacing a load make sure we take the alignment diff --git a/llvm/test/Transforms/ArgumentPromotion/preserve-inbounds-info-of-gep.ll b/llvm/test/Transforms/ArgumentPromotion/preserve-inbounds-info-of-gep.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/ArgumentPromotion/preserve-inbounds-info-of-gep.ll @@ -0,0 +1,36 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes +; RUN: opt < %s -passes=argpromotion -S | FileCheck %s + +%T = type { i32, i32, i32, i32 } +@G = constant %T { i32 0, i32 0, i32 17, i32 25 } + +define internal i32 @test(%T* %p) { +; CHECK-LABEL: define {{[^@]+}}@test +; CHECK-SAME: (i32 [[P_0_2_VAL:%.*]], i32 [[P_0_3_VAL:%.*]]) +; CHECK-NEXT: entry: +; CHECK-NEXT: [[V:%.*]] = add i32 [[P_0_3_VAL]], [[P_0_2_VAL]] +; CHECK-NEXT: ret i32 [[V]] +; +entry: + %a.gep = getelementptr inbounds %T, %T* %p, i64 0, i32 3 + %b.gep = getelementptr inbounds %T, %T* %p, i64 0, i32 2 + %a = load i32, i32* %a.gep + %b = load i32, i32* %b.gep + %v = add i32 %a, %b + ret i32 %v +} + +define i32 @caller() { +; CHECK-LABEL: define {{[^@]+}}@caller() +; CHECK-NEXT: entry: +; CHECK-NEXT: [[G_IDX:%.*]] = getelementptr inbounds [[T:%.*]], %T* @G, i64 0, i32 2 +; CHECK-NEXT: [[G_IDX_VAL:%.*]] = load i32, i32* [[G_IDX]] +; CHECK-NEXT: [[G_IDX1:%.*]] = getelementptr inbounds [[T]], %T* @G, i64 0, i32 3 +; CHECK-NEXT: [[G_IDX1_VAL:%.*]] = load i32, i32* [[G_IDX1]] +; CHECK-NEXT: [[V:%.*]] = call i32 @test(i32 [[G_IDX_VAL]], i32 [[G_IDX1_VAL]]) +; CHECK-NEXT: ret i32 [[V]] +; +entry: + %v = call i32 @test(%T* @G) + ret i32 %v +}