diff --git a/llvm/include/llvm/IR/Value.h b/llvm/include/llvm/IR/Value.h --- a/llvm/include/llvm/IR/Value.h +++ b/llvm/include/llvm/IR/Value.h @@ -643,6 +643,17 @@ static_cast(this)->stripPointerCasts()); } + /// Strip off pointer casts and local aliases to local aliasees. + /// + /// Returns the original uncasted value. If this is called on a non-pointer + /// value, it returns 'this'. This function should be used only in constant + /// folding loads. + const Value *stripPointerCastsForLoadOperand() const; + Value *stripPointerCastsForLoadOperand() { + return const_cast( + static_cast(this)->stripPointerCastsForLoadOperand()); + } + /// Strip off pointer casts, all-zero GEPs, address space casts, and aliases. /// /// Returns the original uncasted value. If this is called on a non-pointer 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,26 @@ GV->getInitializer(), CE, Ty, DL)) return V; } + } else { + // Try to simplify GEP if the pointer operand wasn't a GlobalVariable. + 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 (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 +860,14 @@ } /// 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) + Ptr = cast(Ptr->stripPointerCastsForLoadOperand()); + else + Ptr = cast(Ptr->stripPointerCasts()); auto *NewPtrTy = cast(Ptr->getType()); ElemTy = NewPtrTy->getPointerElementType(); @@ -855,7 +884,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 +933,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 +955,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 +1092,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/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp --- a/llvm/lib/IR/Value.cpp +++ b/llvm/lib/IR/Value.cpp @@ -568,6 +568,7 @@ PSK_ZeroIndicesAndAliases, PSK_ZeroIndicesSameRepresentation, PSK_ForAliasAnalysis, + PSK_ForLoadOperand, PSK_InBoundsConstantIndices, PSK_InBounds }; @@ -605,6 +606,8 @@ if (!GEP->isInBounds()) return V; break; + case PSK_ForLoadOperand: + return V; } V = GEP->getPointerOperand(); } else if (Operator::getOpcode(V) == Instruction::BitCast) { @@ -618,6 +621,10 @@ V = cast(V)->getOperand(0); } else if (StripKind == PSK_ZeroIndicesAndAliases && isa(V)) { V = cast(V)->getAliasee(); + } else if (StripKind == PSK_ForLoadOperand && isa(V) && + !cast(V)->isInterposable() && + !cast(V)->getBaseObject()->isInterposable()) { + V = cast(V)->getAliasee(); } else if (StripKind == PSK_ForAliasAnalysis && isa(V) && cast(V)->getNumIncomingValues() == 1) { V = cast(V)->getIncomingValue(0); @@ -650,6 +657,10 @@ return stripPointerCastsAndOffsets(this); } +const Value *Value::stripPointerCastsForLoadOperand() const { + return stripPointerCastsAndOffsets(this); +} + const Value *Value::stripPointerCastsAndAliases() const { return stripPointerCastsAndOffsets(this); } 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 +}