diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -63,6 +63,11 @@ using namespace llvm; namespace { +Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP, + ArrayRef Ops, + const DataLayout &DL, + const TargetLibraryInfo *TLI, + bool ForLoadOperand); //===----------------------------------------------------------------------===// // Constant Folding internal helper functions @@ -690,6 +695,33 @@ GV->getInitializer(), CE, Ty, DL)) return V; } + } else { + // Try to simplify GEP if the pointer operand wasn't a GlobalVariable. + // SymbolicallyEvaluateGEP() with `ForLoadOperand = true` can potentially + // simplify the GEP more than it normally would have been, but should only + // be used for const folding loads. + SmallVector Ops; + for (unsigned I = 0, E = CE->getNumOperands(); I != E; ++I) + Ops.push_back(cast(CE->getOperand(I))); + if (auto *Simplified = dyn_cast_or_null( + SymbolicallyEvaluateGEP(cast(CE), Ops, DL, nullptr, + /*ForLoadOperand*/ true))) { + // If the symbolically evaluated GEP is another GEP, we can only const + // fold it if the resulting pointer operand is a GlobalValue. Otherwise + // there is nothing else to simplify since the GEP is already in the + // most simplified form. + if (auto *SimplifiedGEP = dyn_cast(Simplified)) { + if (auto *GV = dyn_cast(Simplified->getOperand(0))) { + if (GV->isConstant() && GV->hasDefinitiveInitializer()) { + if (Constant *V = ConstantFoldLoadThroughGEPConstantExpr( + GV->getInitializer(), Simplified, Ty, DL)) + return V; + } + } + } else { + return ConstantFoldLoadFromConstPtr(Simplified, Ty, DL); + } + } } } @@ -835,10 +867,18 @@ } /// Strip the pointer casts, but preserve the address space information. -Constant *StripPtrCastKeepAS(Constant *Ptr, Type *&ElemTy) { +Constant *StripPtrCastKeepAS(Constant *Ptr, Type *&ElemTy, + bool ForLoadOperand) { assert(Ptr->getType()->isPointerTy() && "Not a pointer type"); auto *OldPtrTy = cast(Ptr->getType()); Ptr = cast(Ptr->stripPointerCasts()); + if (ForLoadOperand) { + while (isa(Ptr) && !cast(Ptr)->isInterposable() && + !cast(Ptr)->getBaseObject()->isInterposable()) { + Ptr = cast(Ptr)->getAliasee(); + } + } + auto *NewPtrTy = cast(Ptr->getType()); ElemTy = NewPtrTy->getPointerElementType(); @@ -855,7 +895,8 @@ Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP, ArrayRef Ops, const DataLayout &DL, - const TargetLibraryInfo *TLI) { + const TargetLibraryInfo *TLI, + bool ForLoadOperand) { const GEPOperator *InnermostGEP = GEP; bool InBounds = GEP->isInBounds(); @@ -903,7 +944,7 @@ DL.getIndexedOffsetInType( SrcElemTy, makeArrayRef((Value * const *)Ops.data() + 1, Ops.size() - 1))); - Ptr = StripPtrCastKeepAS(Ptr, SrcElemTy); + Ptr = StripPtrCastKeepAS(Ptr, SrcElemTy, ForLoadOperand); // If this is a GEP of a GEP, fold it all into a single GEP. while (auto *GEP = dyn_cast(Ptr)) { @@ -925,7 +966,7 @@ Ptr = cast(GEP->getOperand(0)); SrcElemTy = GEP->getSourceElementType(); Offset += APInt(BitWidth, DL.getIndexedOffsetInType(SrcElemTy, NestedOps)); - Ptr = StripPtrCastKeepAS(Ptr, SrcElemTy); + Ptr = StripPtrCastKeepAS(Ptr, SrcElemTy, ForLoadOperand); } // If the base value for this address is a literal integer value, fold the @@ -1062,7 +1103,8 @@ return ConstantFoldCastOperand(Opcode, Ops[0], DestTy, DL); if (auto *GEP = dyn_cast(InstOrCE)) { - if (Constant *C = SymbolicallyEvaluateGEP(GEP, Ops, DL, TLI)) + if (Constant *C = SymbolicallyEvaluateGEP(GEP, Ops, DL, TLI, + /*ForLoadOperand*/ false)) return C; return ConstantExpr::getGetElementPtr(GEP->getSourceElementType(), Ops[0], diff --git a/llvm/test/Transforms/InstSimplify/ConstProp/gep-alias-gep-load.ll b/llvm/test/Transforms/InstSimplify/ConstProp/gep-alias-gep-load.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstSimplify/ConstProp/gep-alias-gep-load.ll @@ -0,0 +1,35 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -passes=instsimplify -S < %s | FileCheck %s + +@a1 = internal alias i32, getelementptr ({[3 x i32]}, {[3 x i32]}* @b, i32 0, i32 0, i32 1) +@a2 = weak alias i32, getelementptr ({[3 x i32]}, {[3 x i32]}* @b, i32 0, i32 0, i32 1) +@b = internal constant {[3 x i32]} {[3 x i32] [i32 2, i32 3, i32 4]} + +@c = internal alias i32, getelementptr ({[3 x i32]}, {[3 x i32]}* @d, i32 0, i32 0, i32 1) +@d = weak constant {[3 x i32]} {[3 x i32] [i32 2, i32 3, i32 4]} + +define i32 @f() { +; CHECK-LABEL: @f( +; CHECK-NEXT: ret i32 4 +; + %a = load i32, i32* getelementptr (i32, i32* @a1, i64 1) + ret i32 %a +} + +define i32 @g() { +; CHECK-LABEL: @g( +; CHECK-NEXT: [[A:%.*]] = load i32, i32* getelementptr (i32, i32* @a2, i64 1), align 4 +; CHECK-NEXT: ret i32 [[A]] +; + %a = load i32, i32* getelementptr (i32, i32* @a2, i64 1) + ret i32 %a +} + +define i32 @h() { +; CHECK-LABEL: @h( +; CHECK-NEXT: [[A:%.*]] = load i32, i32* getelementptr (i32, i32* @c, i64 1), align 4 +; CHECK-NEXT: ret i32 [[A]] +; + %a = load i32, i32* getelementptr (i32, i32* @c, i64 1) + ret i32 %a +}