llvm-project/llvm/unittests/IR/ConstantsTest.cpp
Matt Arsenault 51a3bd919d
IR: Remove reference counts from ConstantData (#137314)
This is a follow up change to eliminating uselists for ConstantData.
In the previous revision, ConstantData had a replacement reference count
instead of a uselist. This reference count was misleading, and not useful
in the same way as it would be for another value. The references may not
have even been in the current module, since these are shared throughout
the LLVMContext.

This doesn't space leak any more than we previously did; nothing was
attempting to garbage collect unused constants.

Previously the use_empty, and hasNUses type of APIs were supported through
the reference count. These now behave as if the uses are always empty.
Ideally it would be illegal to inspect these, but this forces API complexity
into quite a few places. It may be doable to make it illegal to check these
counts, but I would like there to be a targeted fuzzing effort to make sure
every transform properly deals with a constant in every operand position.

All tests pass if I turn the hasNUses* and getNumUses queries into assertions,
only hasOneUse in particular appears to hit in some set of contexts. I've
added unit tests to ensure logical consistency between these cases
2025-05-06 17:23:59 +02:00

836 lines
31 KiB
C++

//===- llvm/unittest/IR/ConstantsTest.cpp - Constants unit tests ----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/IR/Constants.h"
#include "llvm-c/Core.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/ConstantFold.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/SourceMgr.h"
#include "gtest/gtest.h"
namespace llvm {
namespace {
// Check that use count checks treat ConstantData like they have no uses.
TEST(ConstantsTest, UseCounts) {
LLVMContext Context;
Type *Int32Ty = Type::getInt32Ty(Context);
Constant *Zero = ConstantInt::get(Int32Ty, 0);
EXPECT_TRUE(Zero->use_empty());
EXPECT_EQ(Zero->getNumUses(), 0u);
EXPECT_TRUE(Zero->hasNUses(0));
EXPECT_FALSE(Zero->hasOneUse());
EXPECT_FALSE(Zero->hasOneUser());
EXPECT_FALSE(Zero->hasNUses(1));
EXPECT_FALSE(Zero->hasNUsesOrMore(1));
EXPECT_FALSE(Zero->hasNUses(2));
EXPECT_FALSE(Zero->hasNUsesOrMore(2));
std::unique_ptr<Module> M(new Module("MyModule", Context));
// Introduce some uses
new GlobalVariable(*M, Int32Ty, /*isConstant=*/false,
GlobalValue::ExternalLinkage, /*Initializer=*/Zero,
"gv_user0");
new GlobalVariable(*M, Int32Ty, /*isConstant=*/false,
GlobalValue::ExternalLinkage, /*Initializer=*/Zero,
"gv_user1");
// Still looks like use_empty with uses.
EXPECT_TRUE(Zero->use_empty());
EXPECT_EQ(Zero->getNumUses(), 0u);
EXPECT_TRUE(Zero->hasNUses(0));
EXPECT_FALSE(Zero->hasOneUse());
EXPECT_FALSE(Zero->hasOneUser());
EXPECT_FALSE(Zero->hasNUses(1));
EXPECT_FALSE(Zero->hasNUsesOrMore(1));
EXPECT_FALSE(Zero->hasNUses(2));
EXPECT_FALSE(Zero->hasNUsesOrMore(2));
}
TEST(ConstantsTest, Integer_i1) {
LLVMContext Context;
IntegerType *Int1 = IntegerType::get(Context, 1);
Constant *One = ConstantInt::get(Int1, 1, true);
Constant *Zero = ConstantInt::get(Int1, 0);
Constant *NegOne = ConstantInt::get(Int1, static_cast<uint64_t>(-1), true);
EXPECT_EQ(NegOne, ConstantInt::getSigned(Int1, -1));
Constant *Poison = PoisonValue::get(Int1);
// Input: @b = constant i1 add(i1 1 , i1 1)
// Output: @b = constant i1 false
EXPECT_EQ(Zero, ConstantExpr::getAdd(One, One));
// @c = constant i1 add(i1 -1, i1 1)
// @c = constant i1 false
EXPECT_EQ(Zero, ConstantExpr::getAdd(NegOne, One));
// @d = constant i1 add(i1 -1, i1 -1)
// @d = constant i1 false
EXPECT_EQ(Zero, ConstantExpr::getAdd(NegOne, NegOne));
// @e = constant i1 sub(i1 -1, i1 1)
// @e = constant i1 false
EXPECT_EQ(Zero, ConstantExpr::getSub(NegOne, One));
// @f = constant i1 sub(i1 1 , i1 -1)
// @f = constant i1 false
EXPECT_EQ(Zero, ConstantExpr::getSub(One, NegOne));
// @g = constant i1 sub(i1 1 , i1 1)
// @g = constant i1 false
EXPECT_EQ(Zero, ConstantExpr::getSub(One, One));
// @h = constant i1 shl(i1 1 , i1 1) ; poison
// @h = constant i1 poison
EXPECT_EQ(Poison, ConstantFoldBinaryInstruction(Instruction::Shl, One, One));
// @i = constant i1 shl(i1 1 , i1 0)
// @i = constant i1 true
EXPECT_EQ(One, ConstantFoldBinaryInstruction(Instruction::Shl, One, Zero));
// @n = constant i1 mul(i1 -1, i1 1)
// @n = constant i1 true
EXPECT_EQ(One, ConstantFoldBinaryInstruction(Instruction::Mul, NegOne, One));
// @o = constant i1 sdiv(i1 -1, i1 1) ; overflow
// @o = constant i1 true
EXPECT_EQ(One, ConstantFoldBinaryInstruction(Instruction::SDiv, NegOne, One));
// @p = constant i1 sdiv(i1 1 , i1 -1); overflow
// @p = constant i1 true
EXPECT_EQ(One, ConstantFoldBinaryInstruction(Instruction::SDiv, One, NegOne));
// @q = constant i1 udiv(i1 -1, i1 1)
// @q = constant i1 true
EXPECT_EQ(One, ConstantFoldBinaryInstruction(Instruction::UDiv, NegOne, One));
// @r = constant i1 udiv(i1 1, i1 -1)
// @r = constant i1 true
EXPECT_EQ(One, ConstantFoldBinaryInstruction(Instruction::UDiv, One, NegOne));
// @s = constant i1 srem(i1 -1, i1 1) ; overflow
// @s = constant i1 false
EXPECT_EQ(Zero,
ConstantFoldBinaryInstruction(Instruction::SRem, NegOne, One));
// @u = constant i1 srem(i1 1, i1 -1) ; overflow
// @u = constant i1 false
EXPECT_EQ(Zero,
ConstantFoldBinaryInstruction(Instruction::SRem, One, NegOne));
}
TEST(ConstantsTest, IntSigns) {
LLVMContext Context;
IntegerType *Int8Ty = Type::getInt8Ty(Context);
EXPECT_EQ(100, ConstantInt::get(Int8Ty, 100, false)->getSExtValue());
EXPECT_EQ(100, ConstantInt::get(Int8Ty, 100, true)->getSExtValue());
EXPECT_EQ(100, ConstantInt::getSigned(Int8Ty, 100)->getSExtValue());
EXPECT_EQ(-50, ConstantInt::get(Int8Ty, 206)->getSExtValue());
EXPECT_EQ(-50, ConstantInt::getSigned(Int8Ty, -50)->getSExtValue());
EXPECT_EQ(206U, ConstantInt::getSigned(Int8Ty, -50)->getZExtValue());
// Overflow is handled by truncation.
EXPECT_EQ(0x3b, ConstantInt::get(Int8Ty, 0x13b)->getSExtValue());
}
TEST(ConstantsTest, PointerCast) {
LLVMContext C;
Type *PtrTy = PointerType::get(C, 0);
Type *Int64Ty = Type::getInt64Ty(C);
VectorType *PtrVecTy = FixedVectorType::get(PtrTy, 4);
VectorType *Int64VecTy = FixedVectorType::get(Int64Ty, 4);
VectorType *PtrScalableVecTy = ScalableVectorType::get(PtrTy, 4);
VectorType *Int64ScalableVecTy = ScalableVectorType::get(Int64Ty, 4);
// ptrtoint ptr to i64
EXPECT_EQ(
Constant::getNullValue(Int64Ty),
ConstantExpr::getPointerCast(Constant::getNullValue(PtrTy), Int64Ty));
// bitcast ptr to ptr
EXPECT_EQ(Constant::getNullValue(PtrTy),
ConstantExpr::getPointerCast(Constant::getNullValue(PtrTy), PtrTy));
// ptrtoint <4 x ptr> to <4 x i64>
EXPECT_EQ(Constant::getNullValue(Int64VecTy),
ConstantExpr::getPointerCast(Constant::getNullValue(PtrVecTy),
Int64VecTy));
// ptrtoint <vscale x 4 x ptr> to <vscale x 4 x i64>
EXPECT_EQ(Constant::getNullValue(Int64ScalableVecTy),
ConstantExpr::getPointerCast(
Constant::getNullValue(PtrScalableVecTy), Int64ScalableVecTy));
// bitcast <4 x ptr> to <4 x ptr>
EXPECT_EQ(
Constant::getNullValue(PtrVecTy),
ConstantExpr::getPointerCast(Constant::getNullValue(PtrVecTy), PtrVecTy));
// bitcast <vscale x 4 x ptr> to <vscale x 4 x ptr>
EXPECT_EQ(Constant::getNullValue(PtrScalableVecTy),
ConstantExpr::getPointerCast(
Constant::getNullValue(PtrScalableVecTy), PtrScalableVecTy));
Type *Ptr1Ty = PointerType::get(C, 1);
ConstantInt *K = ConstantInt::get(Type::getInt64Ty(C), 1234);
// Make sure that addrspacecast of inttoptr is not folded away.
EXPECT_NE(K, ConstantExpr::getAddrSpaceCast(
ConstantExpr::getIntToPtr(K, PtrTy), Ptr1Ty));
EXPECT_NE(K, ConstantExpr::getAddrSpaceCast(
ConstantExpr::getIntToPtr(K, Ptr1Ty), PtrTy));
Constant *NullPtr0 = Constant::getNullValue(PtrTy);
Constant *NullPtr1 = Constant::getNullValue(Ptr1Ty);
// Make sure that addrspacecast of null is not folded away.
EXPECT_NE(Constant::getNullValue(PtrTy),
ConstantExpr::getAddrSpaceCast(NullPtr0, Ptr1Ty));
EXPECT_NE(Constant::getNullValue(Ptr1Ty),
ConstantExpr::getAddrSpaceCast(NullPtr1, PtrTy));
}
#define CHECK(x, y) \
{ \
std::string __s; \
raw_string_ostream __o(__s); \
Instruction *__I = cast<ConstantExpr>(x)->getAsInstruction(); \
__I->print(__o); \
__I->deleteValue(); \
EXPECT_EQ(std::string(" <badref> = " y), __s); \
}
TEST(ConstantsTest, AsInstructionsTest) {
LLVMContext Context;
std::unique_ptr<Module> M(new Module("MyModule", Context));
Type *Int64Ty = Type::getInt64Ty(Context);
Type *Int32Ty = Type::getInt32Ty(Context);
Type *Int16Ty = Type::getInt16Ty(Context);
Constant *Global =
M->getOrInsertGlobal("dummy", PointerType::getUnqual(Context));
Constant *Global2 =
M->getOrInsertGlobal("dummy2", PointerType::getUnqual(Context));
Constant *P0 = ConstantExpr::getPtrToInt(Global, Int32Ty);
Constant *P4 = ConstantExpr::getPtrToInt(Global2, Int32Ty);
Constant *P6 = ConstantExpr::getBitCast(P4, FixedVectorType::get(Int16Ty, 2));
Constant *One = ConstantInt::get(Int32Ty, 1);
Constant *Two = ConstantInt::get(Int64Ty, 2);
Constant *Big = ConstantInt::get(Context, APInt{256, uint64_t(-1), true});
Constant *Elt = ConstantInt::get(Int16Ty, 2015);
Constant *Poison16 = PoisonValue::get(Int16Ty);
Constant *Undef64 = UndefValue::get(Int64Ty);
Constant *PoisonV16 = PoisonValue::get(P6->getType());
#define P0STR "ptrtoint (ptr @dummy to i32)"
#define P3STR "ptrtoint (ptr @dummy to i1)"
#define P4STR "ptrtoint (ptr @dummy2 to i32)"
#define P6STR "bitcast (i32 ptrtoint (ptr @dummy2 to i32) to <2 x i16>)"
CHECK(ConstantExpr::getNeg(P0), "sub i32 0, " P0STR);
CHECK(ConstantExpr::getNot(P0), "xor i32 " P0STR ", -1");
CHECK(ConstantExpr::getAdd(P0, P0), "add i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getAdd(P0, P0, false, true),
"add nsw i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getAdd(P0, P0, true, true),
"add nuw nsw i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getSub(P0, P0), "sub i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getXor(P0, P0), "xor i32 " P0STR ", " P0STR);
std::vector<Constant *> V;
V.push_back(One);
// FIXME: getGetElementPtr() actually creates an inbounds ConstantGEP,
// not a normal one!
// CHECK(ConstantExpr::getGetElementPtr(Global, V, false),
// "getelementptr i32*, i32** @dummy, i32 1");
CHECK(ConstantExpr::getInBoundsGetElementPtr(PointerType::getUnqual(Context),
Global, V),
"getelementptr inbounds ptr, ptr @dummy, i32 1");
CHECK(ConstantExpr::getExtractElement(P6, One),
"extractelement <2 x i16> " P6STR ", i32 1");
EXPECT_EQ(Poison16, ConstantExpr::getExtractElement(P6, Two));
EXPECT_EQ(Poison16, ConstantExpr::getExtractElement(P6, Big));
EXPECT_EQ(Poison16, ConstantExpr::getExtractElement(P6, Undef64));
EXPECT_EQ(Elt, ConstantExpr::getExtractElement(
ConstantExpr::getInsertElement(P6, Elt, One), One));
EXPECT_EQ(PoisonV16, ConstantExpr::getInsertElement(P6, Elt, Two));
EXPECT_EQ(PoisonV16, ConstantExpr::getInsertElement(P6, Elt, Big));
EXPECT_EQ(PoisonV16, ConstantExpr::getInsertElement(P6, Elt, Undef64));
}
#ifdef GTEST_HAS_DEATH_TEST
#ifndef NDEBUG
TEST(ConstantsTest, ReplaceWithConstantTest) {
LLVMContext Context;
std::unique_ptr<Module> M(new Module("MyModule", Context));
Type *Int32Ty = Type::getInt32Ty(Context);
Constant *One = ConstantInt::get(Int32Ty, 1);
Constant *Global =
M->getOrInsertGlobal("dummy", PointerType::getUnqual(Context));
Constant *GEP = ConstantExpr::getGetElementPtr(
PointerType::getUnqual(Context), Global, One);
EXPECT_DEATH(Global->replaceAllUsesWith(GEP),
"this->replaceAllUsesWith\\(expr\\(this\\)\\) is NOT valid!");
}
#endif
#endif
#undef CHECK
TEST(ConstantsTest, ConstantArrayReplaceWithConstant) {
LLVMContext Context;
std::unique_ptr<Module> M(new Module("MyModule", Context));
Type *IntTy = Type::getInt8Ty(Context);
ArrayType *ArrayTy = ArrayType::get(IntTy, 2);
Constant *A01Vals[2] = {ConstantInt::get(IntTy, 0),
ConstantInt::get(IntTy, 1)};
Constant *A01 = ConstantArray::get(ArrayTy, A01Vals);
Constant *Global = new GlobalVariable(*M, IntTy, false,
GlobalValue::ExternalLinkage, nullptr);
Constant *GlobalInt = ConstantExpr::getPtrToInt(Global, IntTy);
Constant *A0GVals[2] = {ConstantInt::get(IntTy, 0), GlobalInt};
Constant *A0G = ConstantArray::get(ArrayTy, A0GVals);
ASSERT_NE(A01, A0G);
GlobalVariable *RefArray =
new GlobalVariable(*M, ArrayTy, false, GlobalValue::ExternalLinkage, A0G);
ASSERT_EQ(A0G, RefArray->getInitializer());
GlobalInt->replaceAllUsesWith(ConstantInt::get(IntTy, 1));
ASSERT_EQ(A01, RefArray->getInitializer());
}
TEST(ConstantsTest, ConstantExprReplaceWithConstant) {
LLVMContext Context;
std::unique_ptr<Module> M(new Module("MyModule", Context));
Type *IntTy = Type::getInt8Ty(Context);
Constant *G1 = new GlobalVariable(*M, IntTy, false,
GlobalValue::ExternalLinkage, nullptr);
Constant *G2 = new GlobalVariable(*M, IntTy, false,
GlobalValue::ExternalLinkage, nullptr);
ASSERT_NE(G1, G2);
Constant *Int1 = ConstantExpr::getPtrToInt(G1, IntTy);
Constant *Int2 = ConstantExpr::getPtrToInt(G2, IntTy);
ASSERT_NE(Int1, Int2);
GlobalVariable *Ref =
new GlobalVariable(*M, IntTy, false, GlobalValue::ExternalLinkage, Int1);
ASSERT_EQ(Int1, Ref->getInitializer());
G1->replaceAllUsesWith(G2);
ASSERT_EQ(Int2, Ref->getInitializer());
}
TEST(ConstantsTest, GEPReplaceWithConstant) {
LLVMContext Context;
std::unique_ptr<Module> M(new Module("MyModule", Context));
Type *IntTy = Type::getInt32Ty(Context);
Type *PtrTy = PointerType::get(Context, 0);
auto *C1 = ConstantInt::get(IntTy, 1);
auto *Placeholder = new GlobalVariable(
*M, IntTy, false, GlobalValue::ExternalWeakLinkage, nullptr);
auto *GEP = ConstantExpr::getGetElementPtr(IntTy, Placeholder, C1);
ASSERT_EQ(GEP->getOperand(0), Placeholder);
auto *Ref =
new GlobalVariable(*M, PtrTy, false, GlobalValue::ExternalLinkage, GEP);
ASSERT_EQ(GEP, Ref->getInitializer());
auto *Global = new GlobalVariable(*M, IntTy, false,
GlobalValue::ExternalLinkage, nullptr);
auto *Alias = GlobalAlias::create(IntTy, 0, GlobalValue::ExternalLinkage,
"alias", Global, M.get());
Placeholder->replaceAllUsesWith(Alias);
ASSERT_EQ(GEP, Ref->getInitializer());
ASSERT_EQ(GEP->getOperand(0), Alias);
}
TEST(ConstantsTest, AliasCAPI) {
LLVMContext Context;
SMDiagnostic Error;
std::unique_ptr<Module> M =
parseAssemblyString("@g = global i32 42", Error, Context);
GlobalVariable *G = M->getGlobalVariable("g");
Type *I16Ty = Type::getInt16Ty(Context);
Type *I16PTy = PointerType::get(Context, 0);
Constant *Aliasee = ConstantExpr::getBitCast(G, I16PTy);
LLVMValueRef AliasRef =
LLVMAddAlias2(wrap(M.get()), wrap(I16Ty), 0, wrap(Aliasee), "a");
ASSERT_EQ(unwrap<GlobalAlias>(AliasRef)->getAliasee(), Aliasee);
}
static std::string getNameOfType(Type *T) {
std::string S;
raw_string_ostream RSOS(S);
T->print(RSOS);
return S;
}
TEST(ConstantsTest, BuildConstantDataArrays) {
LLVMContext Context;
for (Type *T : {Type::getInt8Ty(Context), Type::getInt16Ty(Context),
Type::getInt32Ty(Context), Type::getInt64Ty(Context)}) {
ArrayType *ArrayTy = ArrayType::get(T, 2);
Constant *Vals[] = {ConstantInt::get(T, 0), ConstantInt::get(T, 1)};
Constant *CA = ConstantArray::get(ArrayTy, Vals);
ASSERT_TRUE(isa<ConstantDataArray>(CA)) << " T = " << getNameOfType(T);
auto *CDA = cast<ConstantDataArray>(CA);
Constant *CA2 = ConstantDataArray::getRaw(
CDA->getRawDataValues(), CDA->getNumElements(), CDA->getElementType());
ASSERT_TRUE(CA == CA2) << " T = " << getNameOfType(T);
}
for (Type *T : {Type::getHalfTy(Context), Type::getBFloatTy(Context),
Type::getFloatTy(Context), Type::getDoubleTy(Context)}) {
ArrayType *ArrayTy = ArrayType::get(T, 2);
Constant *Vals[] = {ConstantFP::get(T, 0), ConstantFP::get(T, 1)};
Constant *CA = ConstantArray::get(ArrayTy, Vals);
ASSERT_TRUE(isa<ConstantDataArray>(CA)) << " T = " << getNameOfType(T);
auto *CDA = cast<ConstantDataArray>(CA);
Constant *CA2 = ConstantDataArray::getRaw(
CDA->getRawDataValues(), CDA->getNumElements(), CDA->getElementType());
ASSERT_TRUE(CA == CA2) << " T = " << getNameOfType(T);
}
}
TEST(ConstantsTest, BuildConstantDataVectors) {
LLVMContext Context;
for (Type *T : {Type::getInt8Ty(Context), Type::getInt16Ty(Context),
Type::getInt32Ty(Context), Type::getInt64Ty(Context)}) {
Constant *Vals[] = {ConstantInt::get(T, 0), ConstantInt::get(T, 1)};
Constant *CV = ConstantVector::get(Vals);
ASSERT_TRUE(isa<ConstantDataVector>(CV)) << " T = " << getNameOfType(T);
auto *CDV = cast<ConstantDataVector>(CV);
Constant *CV2 = ConstantDataVector::getRaw(
CDV->getRawDataValues(), CDV->getNumElements(), CDV->getElementType());
ASSERT_TRUE(CV == CV2) << " T = " << getNameOfType(T);
}
for (Type *T : {Type::getHalfTy(Context), Type::getBFloatTy(Context),
Type::getFloatTy(Context), Type::getDoubleTy(Context)}) {
Constant *Vals[] = {ConstantFP::get(T, 0), ConstantFP::get(T, 1)};
Constant *CV = ConstantVector::get(Vals);
ASSERT_TRUE(isa<ConstantDataVector>(CV)) << " T = " << getNameOfType(T);
auto *CDV = cast<ConstantDataVector>(CV);
Constant *CV2 = ConstantDataVector::getRaw(
CDV->getRawDataValues(), CDV->getNumElements(), CDV->getElementType());
ASSERT_TRUE(CV == CV2) << " T = " << getNameOfType(T);
}
}
TEST(ConstantsTest, BitcastToGEP) {
LLVMContext Context;
std::unique_ptr<Module> M(new Module("MyModule", Context));
auto *i32 = Type::getInt32Ty(Context);
auto *U = StructType::create(Context, "Unsized");
Type *EltTys[] = {i32, U};
auto *S = StructType::create(EltTys);
auto *G =
new GlobalVariable(*M, S, false, GlobalValue::ExternalLinkage, nullptr);
auto *PtrTy = PointerType::get(Context, 0);
auto *C = ConstantExpr::getBitCast(G, PtrTy);
/* With opaque pointers, no cast is necessary. */
EXPECT_EQ(C, G);
}
bool foldFuncPtrAndConstToNull(LLVMContext &Context, Module *TheModule,
uint64_t AndValue,
MaybeAlign FunctionAlign = std::nullopt) {
Type *VoidType(Type::getVoidTy(Context));
FunctionType *FuncType(FunctionType::get(VoidType, false));
Function *Func(
Function::Create(FuncType, GlobalValue::ExternalLinkage, "", TheModule));
if (FunctionAlign)
Func->setAlignment(*FunctionAlign);
IntegerType *ConstantIntType(Type::getInt32Ty(Context));
ConstantInt *TheConstant(ConstantInt::get(ConstantIntType, AndValue));
Constant *TheConstantExpr(ConstantExpr::getPtrToInt(Func, ConstantIntType));
Constant *C = ConstantFoldBinaryInstruction(Instruction::And, TheConstantExpr,
TheConstant);
bool Result = C && C->isNullValue();
if (!TheModule) {
// If the Module exists then it will delete the Function.
delete Func;
}
return Result;
}
TEST(ConstantsTest, FoldFunctionPtrAlignUnknownAnd2) {
LLVMContext Context;
Module TheModule("TestModule", Context);
// When the DataLayout doesn't specify a function pointer alignment we
// assume in this case that it is 4 byte aligned. This is a bug but we can't
// fix it directly because it causes a code size regression on X86.
// FIXME: This test should be changed once existing targets have
// appropriate defaults. See associated FIXME in ConstantFoldBinaryInstruction
ASSERT_TRUE(foldFuncPtrAndConstToNull(Context, &TheModule, 2));
}
TEST(ConstantsTest, DontFoldFunctionPtrAlignUnknownAnd4) {
LLVMContext Context;
Module TheModule("TestModule", Context);
ASSERT_FALSE(foldFuncPtrAndConstToNull(Context, &TheModule, 4));
}
TEST(ConstantsTest, FoldFunctionPtrAlign4) {
LLVMContext Context;
Module TheModule("TestModule", Context);
const char *AlignmentStrings[] = {"Fi32", "Fn32"};
for (unsigned AndValue = 1; AndValue <= 2; ++AndValue) {
for (const char *AlignmentString : AlignmentStrings) {
TheModule.setDataLayout(AlignmentString);
ASSERT_TRUE(foldFuncPtrAndConstToNull(Context, &TheModule, AndValue));
}
}
}
TEST(ConstantsTest, DontFoldFunctionPtrAlign1) {
LLVMContext Context;
Module TheModule("TestModule", Context);
const char *AlignmentStrings[] = {"Fi8", "Fn8"};
for (const char *AlignmentString : AlignmentStrings) {
TheModule.setDataLayout(AlignmentString);
ASSERT_FALSE(foldFuncPtrAndConstToNull(Context, &TheModule, 2));
}
}
TEST(ConstantsTest, FoldFunctionAlign4PtrAlignMultiple) {
LLVMContext Context;
Module TheModule("TestModule", Context);
TheModule.setDataLayout("Fn8");
ASSERT_TRUE(foldFuncPtrAndConstToNull(Context, &TheModule, 2, Align(4)));
}
TEST(ConstantsTest, DontFoldFunctionAlign4PtrAlignIndependent) {
LLVMContext Context;
Module TheModule("TestModule", Context);
TheModule.setDataLayout("Fi8");
ASSERT_FALSE(foldFuncPtrAndConstToNull(Context, &TheModule, 2, Align(4)));
}
TEST(ConstantsTest, DontFoldFunctionPtrIfNoModule) {
LLVMContext Context;
// Even though the function is explicitly 4 byte aligned, in the absence of a
// DataLayout we can't assume that the function pointer is aligned.
ASSERT_FALSE(foldFuncPtrAndConstToNull(Context, nullptr, 2, Align(4)));
}
TEST(ConstantsTest, FoldGlobalVariablePtr) {
LLVMContext Context;
IntegerType *IntType(Type::getInt32Ty(Context));
std::unique_ptr<GlobalVariable> Global(
new GlobalVariable(IntType, true, GlobalValue::ExternalLinkage));
Global->setAlignment(Align(4));
ConstantInt *TheConstant(ConstantInt::get(IntType, 2));
Constant *TheConstantExpr(ConstantExpr::getPtrToInt(Global.get(), IntType));
ASSERT_TRUE(ConstantFoldBinaryInstruction(Instruction::And, TheConstantExpr,
TheConstant)
->isNullValue());
}
// Check that containsUndefOrPoisonElement and containsPoisonElement is working
// great
TEST(ConstantsTest, containsUndefElemTest) {
LLVMContext Context;
Type *Int32Ty = Type::getInt32Ty(Context);
Constant *CU = UndefValue::get(Int32Ty);
Constant *CP = PoisonValue::get(Int32Ty);
Constant *C1 = ConstantInt::get(Int32Ty, 1);
Constant *C2 = ConstantInt::get(Int32Ty, 2);
{
Constant *V1 = ConstantVector::get({C1, C2});
EXPECT_FALSE(V1->containsUndefOrPoisonElement());
EXPECT_FALSE(V1->containsPoisonElement());
}
{
Constant *V2 = ConstantVector::get({C1, CU});
EXPECT_TRUE(V2->containsUndefOrPoisonElement());
EXPECT_FALSE(V2->containsPoisonElement());
}
{
Constant *V3 = ConstantVector::get({C1, CP});
EXPECT_TRUE(V3->containsUndefOrPoisonElement());
EXPECT_TRUE(V3->containsPoisonElement());
}
{
Constant *V4 = ConstantVector::get({CU, CP});
EXPECT_TRUE(V4->containsUndefOrPoisonElement());
EXPECT_TRUE(V4->containsPoisonElement());
}
}
// Check that poison elements in vector constants are matched
// correctly for both integer and floating-point types. Just don't
// crash on vectors of pointers (could be handled?).
TEST(ConstantsTest, isElementWiseEqual) {
LLVMContext Context;
Type *Int32Ty = Type::getInt32Ty(Context);
Constant *CU = UndefValue::get(Int32Ty);
Constant *CP = PoisonValue::get(Int32Ty);
Constant *C1 = ConstantInt::get(Int32Ty, 1);
Constant *C2 = ConstantInt::get(Int32Ty, 2);
Constant *C1211 = ConstantVector::get({C1, C2, C1, C1});
Constant *C12U1 = ConstantVector::get({C1, C2, CU, C1});
Constant *C12U2 = ConstantVector::get({C1, C2, CU, C2});
Constant *C12U21 = ConstantVector::get({C1, C2, CU, C2, C1});
Constant *C12P1 = ConstantVector::get({C1, C2, CP, C1});
Constant *C12P2 = ConstantVector::get({C1, C2, CP, C2});
Constant *C12P21 = ConstantVector::get({C1, C2, CP, C2, C1});
EXPECT_FALSE(C1211->isElementWiseEqual(C12U1));
EXPECT_FALSE(C12U1->isElementWiseEqual(C1211));
EXPECT_FALSE(C12U2->isElementWiseEqual(C12U1));
EXPECT_FALSE(C12U1->isElementWiseEqual(C12U2));
EXPECT_FALSE(C12U21->isElementWiseEqual(C12U2));
EXPECT_TRUE(C1211->isElementWiseEqual(C12P1));
EXPECT_TRUE(C12P1->isElementWiseEqual(C1211));
EXPECT_FALSE(C12P2->isElementWiseEqual(C12P1));
EXPECT_FALSE(C12P1->isElementWiseEqual(C12P2));
EXPECT_FALSE(C12P21->isElementWiseEqual(C12P2));
Type *FltTy = Type::getFloatTy(Context);
Constant *CFU = UndefValue::get(FltTy);
Constant *CFP = PoisonValue::get(FltTy);
Constant *CF1 = ConstantFP::get(FltTy, 1.0);
Constant *CF2 = ConstantFP::get(FltTy, 2.0);
Constant *CF1211 = ConstantVector::get({CF1, CF2, CF1, CF1});
Constant *CF12U1 = ConstantVector::get({CF1, CF2, CFU, CF1});
Constant *CF12U2 = ConstantVector::get({CF1, CF2, CFU, CF2});
Constant *CFUU1U = ConstantVector::get({CFU, CFU, CF1, CFU});
Constant *CF12P1 = ConstantVector::get({CF1, CF2, CFP, CF1});
Constant *CF12P2 = ConstantVector::get({CF1, CF2, CFP, CF2});
Constant *CFPP1P = ConstantVector::get({CFP, CFP, CF1, CFP});
EXPECT_FALSE(CF1211->isElementWiseEqual(CF12U1));
EXPECT_FALSE(CF12U1->isElementWiseEqual(CF1211));
EXPECT_FALSE(CFUU1U->isElementWiseEqual(CF12U1));
EXPECT_FALSE(CF12U2->isElementWiseEqual(CF12U1));
EXPECT_FALSE(CF12U1->isElementWiseEqual(CF12U2));
EXPECT_TRUE(CF1211->isElementWiseEqual(CF12P1));
EXPECT_TRUE(CF12P1->isElementWiseEqual(CF1211));
EXPECT_TRUE(CFPP1P->isElementWiseEqual(CF12P1));
EXPECT_FALSE(CF12P2->isElementWiseEqual(CF12P1));
EXPECT_FALSE(CF12P1->isElementWiseEqual(CF12P2));
PointerType *PtrTy = PointerType::get(Context, 0);
Constant *CPU = UndefValue::get(PtrTy);
Constant *CPP = PoisonValue::get(PtrTy);
Constant *CP0 = ConstantPointerNull::get(PtrTy);
Constant *CP0000 = ConstantVector::get({CP0, CP0, CP0, CP0});
Constant *CP00U0 = ConstantVector::get({CP0, CP0, CPU, CP0});
Constant *CP00U = ConstantVector::get({CP0, CP0, CPU});
Constant *CP00P0 = ConstantVector::get({CP0, CP0, CPP, CP0});
Constant *CP00P = ConstantVector::get({CP0, CP0, CPP});
EXPECT_FALSE(CP0000->isElementWiseEqual(CP00U0));
EXPECT_FALSE(CP00U0->isElementWiseEqual(CP0000));
EXPECT_FALSE(CP0000->isElementWiseEqual(CP00U));
EXPECT_FALSE(CP00U->isElementWiseEqual(CP00U0));
EXPECT_FALSE(CP0000->isElementWiseEqual(CP00P0));
EXPECT_FALSE(CP00P0->isElementWiseEqual(CP0000));
EXPECT_FALSE(CP0000->isElementWiseEqual(CP00P));
EXPECT_FALSE(CP00P->isElementWiseEqual(CP00P0));
}
// Check that vector/aggregate constants correctly store undef and poison
// elements.
TEST(ConstantsTest, CheckElementWiseUndefPoison) {
LLVMContext Context;
Type *Int32Ty = Type::getInt32Ty(Context);
StructType *STy = StructType::get(Int32Ty, Int32Ty);
ArrayType *ATy = ArrayType::get(Int32Ty, 2);
Constant *CU = UndefValue::get(Int32Ty);
Constant *CP = PoisonValue::get(Int32Ty);
{
Constant *CUU = ConstantVector::get({CU, CU});
Constant *CPP = ConstantVector::get({CP, CP});
Constant *CUP = ConstantVector::get({CU, CP});
Constant *CPU = ConstantVector::get({CP, CU});
EXPECT_EQ(CUU, UndefValue::get(CUU->getType()));
EXPECT_EQ(CPP, PoisonValue::get(CPP->getType()));
EXPECT_NE(CUP, UndefValue::get(CUP->getType()));
EXPECT_NE(CPU, UndefValue::get(CPU->getType()));
}
{
Constant *CUU = ConstantStruct::get(STy, {CU, CU});
Constant *CPP = ConstantStruct::get(STy, {CP, CP});
Constant *CUP = ConstantStruct::get(STy, {CU, CP});
Constant *CPU = ConstantStruct::get(STy, {CP, CU});
EXPECT_EQ(CUU, UndefValue::get(CUU->getType()));
EXPECT_EQ(CPP, PoisonValue::get(CPP->getType()));
EXPECT_NE(CUP, UndefValue::get(CUP->getType()));
EXPECT_NE(CPU, UndefValue::get(CPU->getType()));
}
{
Constant *CUU = ConstantArray::get(ATy, {CU, CU});
Constant *CPP = ConstantArray::get(ATy, {CP, CP});
Constant *CUP = ConstantArray::get(ATy, {CU, CP});
Constant *CPU = ConstantArray::get(ATy, {CP, CU});
EXPECT_EQ(CUU, UndefValue::get(CUU->getType()));
EXPECT_EQ(CPP, PoisonValue::get(CPP->getType()));
EXPECT_NE(CUP, UndefValue::get(CUP->getType()));
EXPECT_NE(CPU, UndefValue::get(CPU->getType()));
}
}
TEST(ConstantsTest, GetSplatValueRoundTrip) {
LLVMContext Context;
Type *FloatTy = Type::getFloatTy(Context);
Type *Int32Ty = Type::getInt32Ty(Context);
Type *Int8Ty = Type::getInt8Ty(Context);
for (unsigned Min : {1, 2, 8}) {
auto ScalableEC = ElementCount::getScalable(Min);
auto FixedEC = ElementCount::getFixed(Min);
for (auto EC : {ScalableEC, FixedEC}) {
for (auto *Ty : {FloatTy, Int32Ty, Int8Ty}) {
Constant *Zero = Constant::getNullValue(Ty);
Constant *One = Constant::getAllOnesValue(Ty);
for (auto *C : {Zero, One}) {
Constant *Splat = ConstantVector::getSplat(EC, C);
ASSERT_NE(nullptr, Splat);
Constant *SplatVal = Splat->getSplatValue();
EXPECT_NE(nullptr, SplatVal);
EXPECT_EQ(SplatVal, C);
}
}
}
}
}
TEST(ConstantsTest, ComdatUserTracking) {
LLVMContext Context;
Module M("MyModule", Context);
Comdat *C = M.getOrInsertComdat("comdat");
const SmallPtrSetImpl<GlobalObject *> &Users = C->getUsers();
EXPECT_TRUE(Users.size() == 0);
Type *Ty = Type::getInt8Ty(Context);
GlobalVariable *GV1 = cast<GlobalVariable>(M.getOrInsertGlobal("gv1", Ty));
GV1->setComdat(C);
EXPECT_TRUE(Users.size() == 1);
EXPECT_TRUE(Users.contains(GV1));
GlobalVariable *GV2 = cast<GlobalVariable>(M.getOrInsertGlobal("gv2", Ty));
GV2->setComdat(C);
EXPECT_TRUE(Users.size() == 2);
EXPECT_TRUE(Users.contains(GV2));
GV1->eraseFromParent();
EXPECT_TRUE(Users.size() == 1);
EXPECT_TRUE(Users.contains(GV2));
GV2->eraseFromParent();
EXPECT_TRUE(Users.size() == 0);
}
// Verify that the C API getters for BlockAddress work
TEST(ConstantsTest, BlockAddressCAPITest) {
const char *BlockAddressIR = R"(
define void @test_block_address_func() {
entry:
br label %block_bb_0
block_bb_0:
ret void
}
)";
LLVMContext Context;
SMDiagnostic Error;
std::unique_ptr<Module> M =
parseAssemblyString(BlockAddressIR, Error, Context);
EXPECT_TRUE(M.get() != nullptr);
// Get the function
auto *Func = M->getFunction("test_block_address_func");
EXPECT_TRUE(Func != nullptr);
// Get the second basic block, since we can't use the entry one
const BasicBlock &BB = *(++Func->begin());
EXPECT_EQ(BB.getName(), "block_bb_0");
// Construct the C API values
LLVMValueRef BlockAddr = LLVMBlockAddress(wrap(Func), wrap(&BB));
EXPECT_TRUE(LLVMIsABlockAddress(BlockAddr));
// Get the Function/BasicBlock values back out
auto *OutFunc = unwrap(LLVMGetBlockAddressFunction(BlockAddr));
auto *OutBB = unwrap(LLVMGetBlockAddressBasicBlock(BlockAddr));
// Verify that they round-tripped properly
EXPECT_EQ(Func, OutFunc);
EXPECT_EQ(&BB, OutBB);
}
} // end anonymous namespace
} // end namespace llvm