https://github.com/shiltian created https://github.com/llvm/llvm-project/pull/183209
Prepare the constant folding infrastructure for the `ConstantPointerNull` semantic change, where null may have a non-zero bit pattern. Thread `const DataLayout *DL = nullptr` through `ConstantFoldCastInstruction`, `ConstantFoldCompareInstruction`, and `ConstantFoldGetElementPtr`. When DL is present and the null pointer is not zero for the relevant address space, pointer-involving folds (e.g., ptrtoint null -> 0, icmp uge X null -> true) are deferred to the DL-aware folder instead of producing incorrect results. Without DL, behavior is unchanged. Fix `ConstantAggregateZero` element extraction to return `getZeroValue` (not `getNullValue`), ensuring CAZ always yields all-zero-bit elements regardless of the address space's null pointer value. Fix aggregate collapse checks to use `isZeroValue()` instead of `isNullValue()`. This correctly prevents collapsing aggregates of FP -0.0 (non-zero bit pattern) into `ConstantAggregateZero`, and will prevent incorrect collapse of non-zero-null `ConstantPointerNull` after the semantic change. >From a899485cd96fa2ba4b95663404df032a2e716628 Mon Sep 17 00:00:00 2001 From: Shilei Tian <[email protected]> Date: Sat, 14 Feb 2026 19:27:47 -0500 Subject: [PATCH] [NFCI][IR] Thread `DataLayout` through `ConstantFold`; fix CAZ extraction and aggregate collapse Prepare the constant folding infrastructure for the `ConstantPointerNull` semantic change, where null may have a non-zero bit pattern. Thread `const DataLayout *DL = nullptr` through `ConstantFoldCastInstruction`, `ConstantFoldCompareInstruction`, and `ConstantFoldGetElementPtr`. When DL is present and the null pointer is not zero for the relevant address space, pointer-involving folds (e.g., ptrtoint null -> 0, icmp uge X null -> true) are deferred to the DL-aware folder instead of producing incorrect results. Without DL, behavior is unchanged. Fix `ConstantAggregateZero` element extraction to return `getZeroValue` (not `getNullValue`), ensuring CAZ always yields all-zero-bit elements regardless of the address space's null pointer value. Fix aggregate collapse checks to use `isZeroValue()` instead of `isNullValue()`. This correctly prevents collapsing aggregates of FP -0.0 (non-zero bit pattern) into `ConstantAggregateZero`, and will prevent incorrect collapse of non-zero-null `ConstantPointerNull` after the semantic change. --- llvm/include/llvm/IR/ConstantFold.h | 15 +- llvm/lib/Analysis/ConstantFolding.cpp | 20 +- llvm/lib/Analysis/InstructionSimplify.cpp | 2 +- llvm/lib/IR/ConstantFold.cpp | 70 +++++-- llvm/lib/IR/Constants.cpp | 22 +-- .../RISCV/RISCVGatherScatterLowering.cpp | 6 +- llvm/unittests/IR/ConstantsTest.cpp | 187 +++++++++++++++++- 7 files changed, 278 insertions(+), 44 deletions(-) diff --git a/llvm/include/llvm/IR/ConstantFold.h b/llvm/include/llvm/IR/ConstantFold.h index 4056f1feb4dd3..3eb8c66d0aacf 100644 --- a/llvm/include/llvm/IR/ConstantFold.h +++ b/llvm/include/llvm/IR/ConstantFold.h @@ -29,14 +29,15 @@ namespace llvm { template <typename T> class ArrayRef; class Value; class Constant; +class DataLayout; class Type; // Constant fold various types of instruction... LLVM_ABI Constant * ConstantFoldCastInstruction(unsigned opcode, ///< The opcode of the cast Constant *V, ///< The source constant - Type *DestTy ///< The destination type -); + Type *DestTy, ///< The destination type + const DataLayout *DL = nullptr); /// Attempt to constant fold a select instruction with the specified /// operands. The constant result is returned if successful; if not, null is @@ -80,12 +81,12 @@ LLVM_ABI Constant *ConstantFoldInsertValueInstruction(Constant *Agg, LLVM_ABI Constant *ConstantFoldUnaryInstruction(unsigned Opcode, Constant *V); LLVM_ABI Constant *ConstantFoldBinaryInstruction(unsigned Opcode, Constant *V1, Constant *V2); -LLVM_ABI Constant *ConstantFoldCompareInstruction(CmpInst::Predicate Predicate, - Constant *C1, Constant *C2); LLVM_ABI Constant * -ConstantFoldGetElementPtr(Type *Ty, Constant *C, - std::optional<ConstantRange> InRange, - ArrayRef<Value *> Idxs); +ConstantFoldCompareInstruction(CmpInst::Predicate Predicate, Constant *C1, + Constant *C2, const DataLayout *DL = nullptr); +LLVM_ABI Constant *ConstantFoldGetElementPtr( + Type *Ty, Constant *C, std::optional<ConstantRange> InRange, + ArrayRef<Value *> Idxs, const DataLayout *DL = nullptr); } // namespace llvm #endif diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp index 7573afe423ec9..ae4554b0ab837 100644 --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -1308,7 +1308,7 @@ Constant *llvm::ConstantFoldCompareInstOperands( return nullptr; } - return ConstantFoldCompareInstruction(Predicate, Ops0, Ops1); + return ConstantFoldCompareInstruction(Predicate, Ops0, Ops1, &DL); } Constant *llvm::ConstantFoldUnaryOpOperand(unsigned Opcode, Constant *Op, @@ -1579,9 +1579,25 @@ Constant *llvm::ConstantFoldCastOperand(unsigned Opcode, Constant *C, return FoldBitCast(C, DestTy, DL); } + // DL-aware null folding for pointer casts. ConstantExpr::getCast below does + // not have DataLayout, so handle the null case here to ensure casts involving + // null pointers (e.g., inttoptr(0) -> null, ptrtoint(null) -> 0) still fold + // correctly when DataLayout confirms null is zero for the address space. + if (C->isNullValue() && !DestTy->isX86_AMXTy() && + Opcode != Instruction::AddrSpaceCast) { + bool SrcIsPtr = C->getType()->isPtrOrPtrVectorTy(); + bool DstIsPtr = DestTy->isPtrOrPtrVectorTy(); + if (SrcIsPtr || DstIsPtr) { + unsigned AS = SrcIsPtr ? C->getType()->getPointerAddressSpace() + : DestTy->getPointerAddressSpace(); + if (DL.isNullPointerAllZeroes(AS)) + return Constant::getNullValue(DestTy); + } + } + if (ConstantExpr::isDesirableCastOp(Opcode)) return ConstantExpr::getCast(Opcode, C, DestTy); - return ConstantFoldCastInstruction(Opcode, C, DestTy); + return ConstantFoldCastInstruction(Opcode, C, DestTy, &DL); } Constant *llvm::ConstantFoldIntegerCast(Constant *C, Type *DestTy, diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index 3d5ee74c0e2e8..94145f74d8531 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -5353,7 +5353,7 @@ static Value *simplifyGEPInst(Type *SrcTy, Value *Ptr, if (!ConstantExpr::isSupportedGetElementPtr(SrcTy)) return ConstantFoldGetElementPtr(SrcTy, cast<Constant>(Ptr), std::nullopt, - Indices); + Indices, &Q.DL); auto *CE = ConstantExpr::getGetElementPtr(SrcTy, cast<Constant>(Ptr), Indices, NW); diff --git a/llvm/lib/IR/ConstantFold.cpp b/llvm/lib/IR/ConstantFold.cpp index 87a70391fbec4..ba47600dcf0e1 100644 --- a/llvm/lib/IR/ConstantFold.cpp +++ b/llvm/lib/IR/ConstantFold.cpp @@ -21,6 +21,7 @@ #include "llvm/ADT/APSInt.h" #include "llvm/ADT/SmallVector.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalAlias.h" @@ -122,14 +123,16 @@ static Constant *FoldBitCast(Constant *V, Type *DestTy) { } static Constant *foldMaybeUndesirableCast(unsigned opc, Constant *V, - Type *DestTy) { + Type *DestTy, + const DataLayout *DL = nullptr) { return ConstantExpr::isDesirableCastOp(opc) ? ConstantExpr::getCast(opc, V, DestTy) - : ConstantFoldCastInstruction(opc, V, DestTy); + : ConstantFoldCastInstruction(opc, V, DestTy, DL); } Constant *llvm::ConstantFoldCastInstruction(unsigned opc, Constant *V, - Type *DestTy) { + Type *DestTy, + const DataLayout *DL) { if (isa<PoisonValue>(V)) return PoisonValue::get(DestTy); @@ -144,8 +147,22 @@ Constant *llvm::ConstantFoldCastInstruction(unsigned opc, Constant *V, } if (V->isNullValue() && !DestTy->isX86_AMXTy() && - opc != Instruction::AddrSpaceCast) + opc != Instruction::AddrSpaceCast) { + // If the source or destination involves pointers and DL tells us that + // null is not zero for the relevant address space, we cannot fold here. + // Defer to the DL-aware folding in Analysis/ConstantFolding.cpp. + if (DL) { + bool SrcIsPtr = V->getType()->isPtrOrPtrVectorTy(); + bool DstIsPtr = DestTy->isPtrOrPtrVectorTy(); + if (SrcIsPtr || DstIsPtr) { + unsigned AS = SrcIsPtr ? V->getType()->getPointerAddressSpace() + : DestTy->getPointerAddressSpace(); + if (!DL->isNullPointerAllZeroes(AS)) + return nullptr; + } + } return Constant::getNullValue(DestTy); + } // If the cast operand is a constant expression, there's a few things we can // do to try to simplify it. @@ -153,7 +170,7 @@ Constant *llvm::ConstantFoldCastInstruction(unsigned opc, Constant *V, if (CE->isCast()) { // Try hard to fold cast of cast because they are often eliminable. if (unsigned newOpc = foldConstantCastPair(opc, CE, DestTy)) - return foldMaybeUndesirableCast(newOpc, CE->getOperand(0), DestTy); + return foldMaybeUndesirableCast(newOpc, CE->getOperand(0), DestTy, DL); } } @@ -167,7 +184,7 @@ Constant *llvm::ConstantFoldCastInstruction(unsigned opc, Constant *V, Type *DstEltTy = DestVecTy->getElementType(); // Fast path for splatted constants. if (Constant *Splat = V->getSplatValue()) { - Constant *Res = foldMaybeUndesirableCast(opc, Splat, DstEltTy); + Constant *Res = foldMaybeUndesirableCast(opc, Splat, DstEltTy, DL); if (!Res) return nullptr; return ConstantVector::getSplat( @@ -181,7 +198,7 @@ Constant *llvm::ConstantFoldCastInstruction(unsigned opc, Constant *V, e = cast<FixedVectorType>(V->getType())->getNumElements(); i != e; ++i) { Constant *C = ConstantExpr::getExtractElement(V, ConstantInt::get(Ty, i)); - Constant *Casted = foldMaybeUndesirableCast(opc, C, DstEltTy); + Constant *Casted = foldMaybeUndesirableCast(opc, C, DstEltTy, DL); if (!Casted) return nullptr; res.push_back(Casted); @@ -1101,7 +1118,8 @@ static ICmpInst::Predicate evaluateICmpRelation(Constant *V1, Constant *V2) { } Constant *llvm::ConstantFoldCompareInstruction(CmpInst::Predicate Predicate, - Constant *C1, Constant *C2) { + Constant *C1, Constant *C2, + const DataLayout *DL) { Type *ResultTy; if (VectorType *VT = dyn_cast<VectorType>(C1->getType())) ResultTy = VectorType::get(Type::getInt1Ty(C1->getContext()), @@ -1139,14 +1157,25 @@ Constant *llvm::ConstantFoldCompareInstruction(CmpInst::Predicate Predicate, } if (C2->isNullValue()) { - // The caller is expected to commute the operands if the constant expression - // is C2. - // C1 >= 0 --> true - if (Predicate == ICmpInst::ICMP_UGE) - return Constant::getAllOnesValue(ResultTy); - // C1 < 0 --> false - if (Predicate == ICmpInst::ICMP_ULT) - return Constant::getNullValue(ResultTy); + // If DL tells us that null is not zero for this pointer's address space, + // we cannot rely on the null value being the unsigned minimum. Defer. + bool CanFoldNullCmp = true; + if (DL && C2->getType()->isPtrOrPtrVectorTy()) { + unsigned AS = C2->getType()->getPointerAddressSpace(); + if (!DL->isNullPointerAllZeroes(AS)) + CanFoldNullCmp = false; + } + + if (CanFoldNullCmp) { + // The caller is expected to commute the operands if the constant + // expression is C2. + // C1 >= 0 --> true + if (Predicate == ICmpInst::ICMP_UGE) + return Constant::getAllOnesValue(ResultTy); + // C1 < 0 --> false + if (Predicate == ICmpInst::ICMP_ULT) + return Constant::getNullValue(ResultTy); + } } // If the comparison is a comparison between two i1's, simplify it. @@ -1177,7 +1206,7 @@ Constant *llvm::ConstantFoldCompareInstruction(CmpInst::Predicate Predicate, if (Constant *C1Splat = C1->getSplatValue()) if (Constant *C2Splat = C2->getSplatValue()) if (Constant *Elt = - ConstantFoldCompareInstruction(Predicate, C1Splat, C2Splat)) + ConstantFoldCompareInstruction(Predicate, C1Splat, C2Splat, DL)) return ConstantVector::getSplat(C1VTy->getElementCount(), Elt); // Do not iterate on scalable vector. The number of elements is unknown at @@ -1196,7 +1225,7 @@ Constant *llvm::ConstantFoldCompareInstruction(CmpInst::Predicate Predicate, ConstantExpr::getExtractElement(C1, ConstantInt::get(Ty, I)); Constant *C2E = ConstantExpr::getExtractElement(C2, ConstantInt::get(Ty, I)); - Constant *Elt = ConstantFoldCompareInstruction(Predicate, C1E, C2E); + Constant *Elt = ConstantFoldCompareInstruction(Predicate, C1E, C2E, DL); if (!Elt) return nullptr; @@ -1308,7 +1337,7 @@ Constant *llvm::ConstantFoldCompareInstruction(CmpInst::Predicate Predicate, // other way if possible. // Also, if C1 is null and C2 isn't, flip them around. Predicate = ICmpInst::getSwappedPredicate(Predicate); - return ConstantFoldCompareInstruction(Predicate, C2, C1); + return ConstantFoldCompareInstruction(Predicate, C2, C1, DL); } } return nullptr; @@ -1316,7 +1345,8 @@ Constant *llvm::ConstantFoldCompareInstruction(CmpInst::Predicate Predicate, Constant *llvm::ConstantFoldGetElementPtr(Type *PointeeTy, Constant *C, std::optional<ConstantRange> InRange, - ArrayRef<Value *> Idxs) { + ArrayRef<Value *> Idxs, + const DataLayout *DL) { if (Idxs.empty()) return C; Type *GEPTy = GetElementPtrInst::getGEPReturnType( diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp index 5a011f50ef94b..53a627bd6a980 100644 --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -1163,12 +1163,12 @@ void ConstantFP::destroyConstantImpl() { Constant *ConstantAggregateZero::getSequentialElement() const { if (auto *AT = dyn_cast<ArrayType>(getType())) - return Constant::getNullValue(AT->getElementType()); - return Constant::getNullValue(cast<VectorType>(getType())->getElementType()); + return Constant::getZeroValue(AT->getElementType()); + return Constant::getZeroValue(cast<VectorType>(getType())->getElementType()); } Constant *ConstantAggregateZero::getStructElement(unsigned Elt) const { - return Constant::getNullValue(getType()->getStructElementType(Elt)); + return Constant::getZeroValue(getType()->getStructElementType(Elt)); } Constant *ConstantAggregateZero::getElementValue(Constant *C) const { @@ -1368,7 +1368,7 @@ Constant *ConstantArray::getImpl(ArrayType *Ty, ArrayRef<Constant*> V) { if (isa<UndefValue>(C) && rangeOnlyContains(V.begin(), V.end(), C)) return UndefValue::get(Ty); - if (C->isNullValue() && rangeOnlyContains(V.begin(), V.end(), C)) + if (C->isZeroValue() && rangeOnlyContains(V.begin(), V.end(), C)) return ConstantAggregateZero::get(Ty); // Check to see if all of the elements are ConstantFP or ConstantInt and if @@ -1419,11 +1419,11 @@ Constant *ConstantStruct::get(StructType *ST, ArrayRef<Constant*> V) { if (!V.empty()) { isUndef = isa<UndefValue>(V[0]); isPoison = isa<PoisonValue>(V[0]); - isZero = V[0]->isNullValue(); + isZero = V[0]->isZeroValue(); // PoisonValue inherits UndefValue, so its check is not necessary. if (isUndef || isZero) { for (Constant *C : V) { - if (!C->isNullValue()) + if (!C->isZeroValue()) isZero = false; if (!isa<PoisonValue>(C)) isPoison = false; @@ -1464,7 +1464,7 @@ Constant *ConstantVector::getImpl(ArrayRef<Constant*> V) { // If this is an all-undef or all-zero vector, return a // ConstantAggregateZero or UndefValue. Constant *C = V[0]; - bool isZero = C->isNullValue(); + bool isZero = C->isZeroValue(); bool isUndef = isa<UndefValue>(C); bool isPoison = isa<PoisonValue>(C); bool isSplatFP = UseConstantFPForFixedLengthSplat && isa<ConstantFP>(C); @@ -1535,7 +1535,7 @@ Constant *ConstantVector::getSplat(ElementCount EC, Constant *V) { Type *VTy = VectorType::get(V->getType(), EC); - if (V->isNullValue()) + if (V->isZeroValue()) return ConstantAggregateZero::get(VTy); if (isa<PoisonValue>(V)) return PoisonValue::get(VTy); @@ -1745,7 +1745,7 @@ Constant *Constant::getSplatValue(bool AllowPoison) const { if (isa<PoisonValue>(this)) return PoisonValue::get(cast<VectorType>(getType())->getElementType()); if (isa<ConstantAggregateZero>(this)) - return getNullValue(cast<VectorType>(getType())->getElementType()); + return getZeroValue(cast<VectorType>(getType())->getElementType()); if (auto *CI = dyn_cast<ConstantInt>(this)) return ConstantInt::get(getContext(), CI->getValue()); if (auto *CFP = dyn_cast<ConstantFP>(this)) @@ -3350,7 +3350,7 @@ Value *ConstantArray::handleOperandChangeImpl(Value *From, Value *To) { AllSame &= Val == ToC; } - if (AllSame && ToC->isNullValue()) + if (AllSame && ToC->isZeroValue()) return ConstantAggregateZero::get(getType()); if (AllSame && isa<UndefValue>(ToC)) @@ -3390,7 +3390,7 @@ Value *ConstantStruct::handleOperandChangeImpl(Value *From, Value *To) { AllSame &= Val == ToC; } - if (AllSame && ToC->isNullValue()) + if (AllSame && ToC->isZeroValue()) return ConstantAggregateZero::get(getType()); if (AllSame && isa<UndefValue>(ToC)) diff --git a/llvm/lib/Target/RISCV/RISCVGatherScatterLowering.cpp b/llvm/lib/Target/RISCV/RISCVGatherScatterLowering.cpp index 25b5af8324e64..36088fe96d3a9 100644 --- a/llvm/lib/Target/RISCV/RISCVGatherScatterLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVGatherScatterLowering.cpp @@ -421,9 +421,11 @@ RISCVGatherScatterLowering::determineBaseAndStride(Instruction *Ptr, if (!VecIndexC) return std::make_pair(nullptr, nullptr); if (VecIndex->getType()->getScalarSizeInBits() > VecIntPtrTy->getScalarSizeInBits()) - VecIndex = ConstantFoldCastInstruction(Instruction::Trunc, VecIndexC, VecIntPtrTy); + VecIndex = ConstantFoldCastInstruction(Instruction::Trunc, VecIndexC, + VecIntPtrTy, DL); else - VecIndex = ConstantFoldCastInstruction(Instruction::SExt, VecIndexC, VecIntPtrTy); + VecIndex = ConstantFoldCastInstruction(Instruction::SExt, VecIndexC, + VecIntPtrTy, DL); } // Handle the non-recursive case. This is what we see if the vectorizer diff --git a/llvm/unittests/IR/ConstantsTest.cpp b/llvm/unittests/IR/ConstantsTest.cpp index 96730dbf16758..dcf9d980c7413 100644 --- a/llvm/unittests/IR/ConstantsTest.cpp +++ b/llvm/unittests/IR/ConstantsTest.cpp @@ -989,7 +989,192 @@ TEST(ConstantsTest, ZeroValueAPIs) { Constant::getNullValue(StructTy)); // TODO: getNullValue slow path for aggregates with non-zero-null pointers is - // deferred to PR 3 testing (requires aggregate collapse fix). + // deferred to PR 4 testing (requires ConstantPointerNull semantic change). +} + +TEST(ConstantsTest, AggregateCollapseAndCAZExtraction) { + LLVMContext Context; + Type *Int32Ty = Type::getInt32Ty(Context); + Type *FloatTy = Type::getFloatTy(Context); + PointerType *PtrTy = PointerType::get(Context, 0); + + // --- ConstantAggregateZero element extraction returns getZeroValue --- + auto *ArrTy = ArrayType::get(Int32Ty, 3); + auto *CAZ = ConstantAggregateZero::get(ArrTy); + Constant *Elt = CAZ->getSequentialElement(); + EXPECT_EQ(Elt, Constant::getZeroValue(Int32Ty)); + // For pointer element types. + auto *PtrArrTy = ArrayType::get(PtrTy, 2); + auto *PtrCAZ = ConstantAggregateZero::get(PtrArrTy); + Constant *PtrElt = PtrCAZ->getSequentialElement(); + EXPECT_EQ(PtrElt, Constant::getZeroValue(PtrTy)); + + // Struct element extraction. + auto *StructTy = StructType::get(Int32Ty, PtrTy, FloatTy); + auto *StructCAZ = ConstantAggregateZero::get(StructTy); + EXPECT_EQ(StructCAZ->getStructElement(0), Constant::getZeroValue(Int32Ty)); + EXPECT_EQ(StructCAZ->getStructElement(1), Constant::getZeroValue(PtrTy)); + EXPECT_EQ(StructCAZ->getStructElement(2), Constant::getZeroValue(FloatTy)); + + // --- Zero-valued aggregates collapse to ConstantAggregateZero --- + Constant *ZeroI32 = Constant::getZeroValue(Int32Ty); + Constant *ZeroFloat = Constant::getZeroValue(FloatTy); + Constant *ZeroPtr = Constant::getZeroValue(PtrTy); + + // Array of zero ints collapses. + Constant *ZeroArr = ConstantArray::get(ArrTy, {ZeroI32, ZeroI32, ZeroI32}); + EXPECT_TRUE(isa<ConstantAggregateZero>(ZeroArr)); + + // Vector of zero ints collapses. + Constant *ZeroVec = ConstantVector::get({ZeroI32, ZeroI32, ZeroI32, ZeroI32}); + EXPECT_TRUE(isa<ConstantAggregateZero>(ZeroVec)); + + // Struct of zeros collapses. + Constant *ZeroStruct = + ConstantStruct::get(StructTy, {ZeroI32, ZeroPtr, ZeroFloat}); + EXPECT_TRUE(isa<ConstantAggregateZero>(ZeroStruct)); + + // Splat of zero collapses. + Constant *SplatZero = + ConstantVector::getSplat(ElementCount::getFixed(4), ZeroI32); + EXPECT_TRUE(isa<ConstantAggregateZero>(SplatZero)); + + // --- FP -0.0 does NOT collapse to ConstantAggregateZero --- + // -0.0 has a non-zero bit pattern (sign bit set), so it must not collapse. + Constant *NegZeroFP = ConstantFP::get( + FloatTy, APFloat::getZero(APFloat::IEEEsingle(), /*Negative=*/true)); + EXPECT_NE(NegZeroFP, Constant::getZeroValue(FloatTy)); + + auto *FloatArrTy = ArrayType::get(FloatTy, 2); + Constant *NegZeroArr = ConstantArray::get(FloatArrTy, {NegZeroFP, NegZeroFP}); + EXPECT_FALSE(isa<ConstantAggregateZero>(NegZeroArr)); + + Constant *NegZeroVec = ConstantVector::get({NegZeroFP, NegZeroFP}); + EXPECT_FALSE(isa<ConstantAggregateZero>(NegZeroVec)); + + auto *FloatStructTy = StructType::get(FloatTy, FloatTy); + Constant *NegZeroStruct = + ConstantStruct::get(FloatStructTy, {NegZeroFP, NegZeroFP}); + EXPECT_FALSE(isa<ConstantAggregateZero>(NegZeroStruct)); + + Constant *NegZeroSplat = + ConstantVector::getSplat(ElementCount::getFixed(4), NegZeroFP); + EXPECT_FALSE(isa<ConstantAggregateZero>(NegZeroSplat)); + + // --- getSplatValue for CAZ returns getZeroValue --- + auto *IntVecTy = FixedVectorType::get(Int32Ty, 4); + auto *IntVecCAZ = ConstantAggregateZero::get(IntVecTy); + Constant *SplatVal = IntVecCAZ->getSplatValue(); + EXPECT_EQ(SplatVal, Constant::getZeroValue(Int32Ty)); + + auto *PtrVecTy = FixedVectorType::get(PtrTy, 2); + auto *PtrVecCAZ = ConstantAggregateZero::get(PtrVecTy); + Constant *PtrSplatVal = PtrVecCAZ->getSplatValue(); + EXPECT_EQ(PtrSplatVal, Constant::getZeroValue(PtrTy)); +} + +TEST(ConstantsTest, ConstantFoldCastWithDL) { + LLVMContext Context; + // A DataLayout where AS 1 has all-ones null pointer. + DataLayout AllOnesDL("e-po1:64:64"); + // A DataLayout where all address spaces have zero null (the default). + DataLayout DefaultDL("e-p:64:64"); + + Type *Int64Ty = Type::getInt64Ty(Context); + PointerType *PtrTy0 = PointerType::get(Context, 0); + PointerType *PtrTy1 = PointerType::get(Context, 1); + + // --- Without DL, null pointer casts fold normally --- + Constant *NullPtr0 = ConstantPointerNull::get(PtrTy0); + Constant *NullPtr1 = ConstantPointerNull::get(PtrTy1); + + // ptrtoint(null AS0) -> 0 (no DL) + Constant *Result = + ConstantFoldCastInstruction(Instruction::PtrToInt, NullPtr0, Int64Ty); + ASSERT_NE(Result, nullptr); + EXPECT_TRUE(Result->isNullValue()); + + // ptrtoint(null AS1) -> 0 (no DL, backward compat) + Result = + ConstantFoldCastInstruction(Instruction::PtrToInt, NullPtr1, Int64Ty); + ASSERT_NE(Result, nullptr); + EXPECT_TRUE(Result->isNullValue()); + + // --- With DefaultDL, null pointer casts still fold (AS 0 is zero null) --- + Result = ConstantFoldCastInstruction(Instruction::PtrToInt, NullPtr0, Int64Ty, + &DefaultDL); + ASSERT_NE(Result, nullptr); + EXPECT_TRUE(Result->isNullValue()); + + // --- With AllOnesDL, AS 1 null cast is deferred --- + // ptrtoint(null AS1) should return nullptr (defer to DL-aware folder). + Result = ConstantFoldCastInstruction(Instruction::PtrToInt, NullPtr1, Int64Ty, + &AllOnesDL); + EXPECT_EQ(Result, nullptr); + + // inttoptr(0, AS1) should also be deferred. + Constant *ZeroI64 = ConstantInt::get(Int64Ty, 0); + Result = ConstantFoldCastInstruction(Instruction::IntToPtr, ZeroI64, PtrTy1, + &AllOnesDL); + EXPECT_EQ(Result, nullptr); + + // But AS 0 with AllOnesDL still folds fine. + Result = ConstantFoldCastInstruction(Instruction::PtrToInt, NullPtr0, Int64Ty, + &AllOnesDL); + ASSERT_NE(Result, nullptr); + EXPECT_TRUE(Result->isNullValue()); +} + +TEST(ConstantsTest, ConstantFoldCompareWithDL) { + LLVMContext Context; + DataLayout AllOnesDL("e-po1:64:64"); + DataLayout DefaultDL("e-p:64:64"); + + PointerType *PtrTy0 = PointerType::get(Context, 0); + PointerType *PtrTy1 = PointerType::get(Context, 1); + + Constant *NullPtr0 = ConstantPointerNull::get(PtrTy0); + Constant *NullPtr1 = ConstantPointerNull::get(PtrTy1); + + // Create a non-null pointer constant expression for comparison. + Type *Int64Ty = Type::getInt64Ty(Context); + Constant *One = ConstantInt::get(Int64Ty, 1); + Constant *NonNullPtr0 = ConstantExpr::getIntToPtr(One, PtrTy0); + Constant *NonNullPtr1 = ConstantExpr::getIntToPtr(One, PtrTy1); + + // --- Without DL, unsigned null comparisons fold --- + // ptr >= null -> true (always, since null is the unsigned minimum) + Constant *Result = + ConstantFoldCompareInstruction(CmpInst::ICMP_UGE, NonNullPtr0, NullPtr0); + ASSERT_NE(Result, nullptr); + EXPECT_TRUE(Result->isAllOnesValue()); + + // ptr < null -> false + Result = + ConstantFoldCompareInstruction(CmpInst::ICMP_ULT, NonNullPtr0, NullPtr0); + ASSERT_NE(Result, nullptr); + EXPECT_TRUE(Result->isNullValue()); + + // --- With AllOnesDL, AS 1 unsigned null comparisons are deferred --- + Result = ConstantFoldCompareInstruction(CmpInst::ICMP_UGE, NonNullPtr1, + NullPtr1, &AllOnesDL); + EXPECT_EQ(Result, nullptr); + + Result = ConstantFoldCompareInstruction(CmpInst::ICMP_ULT, NonNullPtr1, + NullPtr1, &AllOnesDL); + EXPECT_EQ(Result, nullptr); + + // --- With AllOnesDL, AS 0 still folds (zero null) --- + Result = ConstantFoldCompareInstruction(CmpInst::ICMP_UGE, NonNullPtr0, + NullPtr0, &AllOnesDL); + ASSERT_NE(Result, nullptr); + EXPECT_TRUE(Result->isAllOnesValue()); + + // --- With DefaultDL, everything folds normally --- + Result = ConstantFoldCompareInstruction(CmpInst::ICMP_UGE, NonNullPtr0, + NullPtr0, &DefaultDL); + ASSERT_NE(Result, nullptr); + EXPECT_TRUE(Result->isAllOnesValue()); } } // end anonymous namespace _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
