diff options
author | Nikita Popov <npopov@redhat.com> | 2024-06-04 14:40:09 +0200 |
---|---|---|
committer | Nikita Popov <npopov@redhat.com> | 2024-06-11 15:03:10 +0200 |
commit | da5f45f5937d3cde4ff76aeeb208e72ee504baaf (patch) | |
tree | 8da19a5313af1090c99028103dfc3385bb5013c5 | |
parent | 1bae10879d9183c5edfb709c36b55086ebc772f0 (diff) | |
download | llvm-da5f45f5937d3cde4ff76aeeb208e72ee504baaf.zip llvm-da5f45f5937d3cde4ff76aeeb208e72ee504baaf.tar.gz llvm-da5f45f5937d3cde4ff76aeeb208e72ee504baaf.tar.bz2 |
[ConstantFolding] Preserve nowrap flags in gep of gep fold
A caveat here is that we can only preserve nusw if the offset
additions did not overflow.
Proofs: https://alive2.llvm.org/ce/z/u56z_u
-rw-r--r-- | llvm/lib/Analysis/ConstantFolding.cpp | 23 | ||||
-rw-r--r-- | llvm/test/Transforms/InstCombine/getelementptr.ll | 34 |
2 files changed, 47 insertions, 10 deletions
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp index 3ca3ae9..e0f5bf0 100644 --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -866,8 +866,6 @@ Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP, ArrayRef<Constant *> Ops, const DataLayout &DL, const TargetLibraryInfo *TLI) { - bool InBounds = GEP->isInBounds(); - Type *SrcElemTy = GEP->getSourceElementType(); Type *ResTy = GEP->getType(); if (!SrcElemTy->isSized() || isa<ScalableVectorType>(SrcElemTy)) @@ -898,8 +896,10 @@ Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP, InRange = InRange->sextOrTrunc(BitWidth); // If this is a GEP of a GEP, fold it all into a single GEP. + GEPNoWrapFlags NW = GEP->getNoWrapFlags(); + bool Overflow = false; while (auto *GEP = dyn_cast<GEPOperator>(Ptr)) { - InBounds &= GEP->isInBounds(); + NW &= GEP->getNoWrapFlags(); SmallVector<Value *, 4> NestedOps(llvm::drop_begin(GEP->operands())); @@ -923,9 +923,16 @@ Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP, Ptr = cast<Constant>(GEP->getOperand(0)); SrcElemTy = GEP->getSourceElementType(); - Offset += APInt(BitWidth, DL.getIndexedOffsetInType(SrcElemTy, NestedOps)); + Offset = Offset.sadd_ov( + APInt(BitWidth, DL.getIndexedOffsetInType(SrcElemTy, NestedOps)), + Overflow); } + // Preserving nusw (without inbounds) also requires that the offset + // additions did not overflow. + if (NW.hasNoUnsignedSignedWrap() && !NW.isInBounds() && Overflow) + NW = NW.withoutNoUnsignedSignedWrap(); + // If the base value for this address is a literal integer value, fold the // getelementptr to the resulting integer value casted to the pointer type. APInt BasePtr(BitWidth, 0); @@ -944,17 +951,19 @@ Constant *SymbolicallyEvaluateGEP(const GEPOperator *GEP, } // Try to infer inbounds for GEPs of globals. - if (!InBounds && Offset.isNonNegative()) { + // TODO(gep_nowrap): Also infer nuw flag. + if (!NW.isInBounds() && Offset.isNonNegative()) { bool CanBeNull, CanBeFreed; uint64_t DerefBytes = Ptr->getPointerDereferenceableBytes(DL, CanBeNull, CanBeFreed); - InBounds = DerefBytes != 0 && !CanBeNull && Offset.sle(DerefBytes); + if (DerefBytes != 0 && !CanBeNull && Offset.sle(DerefBytes)) + NW |= GEPNoWrapFlags::inBounds(); } // Otherwise canonicalize this to a single ptradd. LLVMContext &Ctx = Ptr->getContext(); return ConstantExpr::getGetElementPtr(Type::getInt8Ty(Ctx), Ptr, - ConstantInt::get(Ctx, Offset), InBounds, + ConstantInt::get(Ctx, Offset), NW, InRange); } diff --git a/llvm/test/Transforms/InstCombine/getelementptr.ll b/llvm/test/Transforms/InstCombine/getelementptr.ll index 8719136..f968fa6 100644 --- a/llvm/test/Transforms/InstCombine/getelementptr.ll +++ b/llvm/test/Transforms/InstCombine/getelementptr.ll @@ -419,20 +419,48 @@ define ptr @test_index_canon_nusw_nuw(ptr %X, i32 %Idx) { ret ptr %R } -define ptr @test_index_canon_const_expr_inbounds(ptr %X, i32 %Idx) { +define ptr @test_index_canon_const_expr_inbounds() { ; CHECK-LABEL: @test_index_canon_const_expr_inbounds( ; CHECK-NEXT: ret ptr getelementptr inbounds (i8, ptr @Global, i64 123) ; ret ptr getelementptr inbounds (i8, ptr @Global, i32 123) } -define ptr @test_index_canon_const_expr_nuw_nusw(ptr %X, i32 %Idx) { +define ptr @test_index_canon_const_expr_nuw_nusw() { ; CHECK-LABEL: @test_index_canon_const_expr_nuw_nusw( -; CHECK-NEXT: ret ptr getelementptr (i8, ptr @Global, i64 123) +; CHECK-NEXT: ret ptr getelementptr nusw nuw (i8, ptr @Global, i64 123) ; ret ptr getelementptr nusw nuw (i8, ptr @Global, i32 123) } +define ptr @test_const_gep_gep_nuw() { +; CHECK-LABEL: @test_const_gep_gep_nuw( +; CHECK-NEXT: ret ptr getelementptr nuw (i8, ptr @Global, i64 246) +; + ret ptr getelementptr nuw (i8, ptr getelementptr nuw (i8, ptr @Global, i64 123), i64 123) +} + +define ptr @test_const_gep_gep_nusw_no_overflow() { +; CHECK-LABEL: @test_const_gep_gep_nusw_no_overflow( +; CHECK-NEXT: ret ptr getelementptr nusw (i8, ptr @Global, i64 246) +; + ret ptr getelementptr nusw (i8, ptr getelementptr nusw (i8, ptr @Global, i64 123), i64 123) +} + +define ptr @test_const_gep_gep_nusw_no_overflow_neg() { +; CHECK-LABEL: @test_const_gep_gep_nusw_no_overflow_neg( +; CHECK-NEXT: ret ptr getelementptr nusw (i8, ptr @Global, i64 -246) +; + ret ptr getelementptr nusw (i8, ptr getelementptr nusw (i8, ptr @Global, i64 -123), i64 -123) +} + +define ptr @test_const_gep_gep_nusw_overflow() { +; CHECK-LABEL: @test_const_gep_gep_nusw_overflow( +; CHECK-NEXT: ret ptr getelementptr (i8, ptr @Global, i64 -2) +; + ret ptr getelementptr nusw (i8, ptr getelementptr nusw (i8, ptr @Global, i64 u0x7fffffffffffffff), i64 u0x7fffffffffffffff) +} + define i1 @test17(ptr %P, i32 %I, i32 %J) { ; CHECK-LABEL: @test17( ; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[I:%.*]], [[J:%.*]] |