llvm-project/llvm/unittests/IR/ConstantsTest.cpp

588 lines
22 KiB
C++
Raw Normal View History

//===- 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/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 {
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* Undef = UndefValue::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) ; undefined
// @h = constant i1 undef
EXPECT_EQ(Undef, ConstantExpr::getShl(One, One));
// @i = constant i1 shl(i1 1 , i1 0)
// @i = constant i1 true
EXPECT_EQ(One, ConstantExpr::getShl(One, Zero));
// @j = constant i1 lshr(i1 1, i1 1) ; undefined
// @j = constant i1 undef
EXPECT_EQ(Undef, ConstantExpr::getLShr(One, One));
// @m = constant i1 ashr(i1 1, i1 1) ; undefined
// @m = constant i1 undef
EXPECT_EQ(Undef, ConstantExpr::getAShr(One, One));
// @n = constant i1 mul(i1 -1, i1 1)
// @n = constant i1 true
EXPECT_EQ(One, ConstantExpr::getMul(NegOne, One));
// @o = constant i1 sdiv(i1 -1, i1 1) ; overflow
// @o = constant i1 true
EXPECT_EQ(One, ConstantExpr::getSDiv(NegOne, One));
// @p = constant i1 sdiv(i1 1 , i1 -1); overflow
// @p = constant i1 true
EXPECT_EQ(One, ConstantExpr::getSDiv(One, NegOne));
// @q = constant i1 udiv(i1 -1, i1 1)
// @q = constant i1 true
EXPECT_EQ(One, ConstantExpr::getUDiv(NegOne, One));
// @r = constant i1 udiv(i1 1, i1 -1)
// @r = constant i1 true
EXPECT_EQ(One, ConstantExpr::getUDiv(One, NegOne));
// @s = constant i1 srem(i1 -1, i1 1) ; overflow
// @s = constant i1 false
EXPECT_EQ(Zero, ConstantExpr::getSRem(NegOne, One));
// @t = constant i1 urem(i1 -1, i1 1)
// @t = constant i1 false
EXPECT_EQ(Zero, ConstantExpr::getURem(NegOne, One));
// @u = constant i1 srem(i1 1, i1 -1) ; overflow
// @u = constant i1 false
EXPECT_EQ(Zero, ConstantExpr::getSRem(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, FP128Test) {
LLVMContext Context;
Type *FP128Ty = Type::getFP128Ty(Context);
IntegerType *Int128Ty = Type::getIntNTy(Context, 128);
Constant *Zero128 = Constant::getNullValue(Int128Ty);
Constant *X = ConstantExpr::getUIToFP(Zero128, FP128Ty);
EXPECT_TRUE(isa<ConstantFP>(X));
}
TEST(ConstantsTest, PointerCast) {
LLVMContext C;
Type *Int8PtrTy = Type::getInt8PtrTy(C);
Type *Int32PtrTy = Type::getInt32PtrTy(C);
Type *Int64Ty = Type::getInt64Ty(C);
VectorType *Int8PtrVecTy = VectorType::get(Int8PtrTy, 4);
VectorType *Int32PtrVecTy = VectorType::get(Int32PtrTy, 4);
VectorType *Int64VecTy = VectorType::get(Int64Ty, 4);
// ptrtoint i8* to i64
EXPECT_EQ(Constant::getNullValue(Int64Ty),
ConstantExpr::getPointerCast(
Constant::getNullValue(Int8PtrTy), Int64Ty));
// bitcast i8* to i32*
EXPECT_EQ(Constant::getNullValue(Int32PtrTy),
ConstantExpr::getPointerCast(
Constant::getNullValue(Int8PtrTy), Int32PtrTy));
// ptrtoint <4 x i8*> to <4 x i64>
EXPECT_EQ(Constant::getNullValue(Int64VecTy),
ConstantExpr::getPointerCast(
Constant::getNullValue(Int8PtrVecTy), Int64VecTy));
// bitcast <4 x i8*> to <4 x i32*>
EXPECT_EQ(Constant::getNullValue(Int32PtrVecTy),
ConstantExpr::getPointerCast(
Constant::getNullValue(Int8PtrVecTy), Int32PtrVecTy));
Type *Int32Ptr1Ty = Type::getInt32PtrTy(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, Int32PtrTy), Int32Ptr1Ty));
EXPECT_NE(K,
ConstantExpr::getAddrSpaceCast(
ConstantExpr::getIntToPtr(K, Int32Ptr1Ty), Int32PtrTy));
Constant *NullInt32Ptr0 = Constant::getNullValue(Int32PtrTy);
Constant *NullInt32Ptr1 = Constant::getNullValue(Int32Ptr1Ty);
// Make sure that addrspacecast of null is not folded away.
EXPECT_NE(Constant::getNullValue(Int32PtrTy),
ConstantExpr::getAddrSpaceCast(NullInt32Ptr0, Int32Ptr1Ty));
EXPECT_NE(Constant::getNullValue(Int32Ptr1Ty),
ConstantExpr::getAddrSpaceCast(NullInt32Ptr1, Int32PtrTy));
}
[IR] De-virtualize ~Value to save a vptr Summary: Implements PR889 Removing the virtual table pointer from Value saves 1% of RSS when doing LTO of llc on Linux. The impact on time was positive, but too noisy to conclusively say that performance improved. Here is a link to the spreadsheet with the original data: https://docs.google.com/spreadsheets/d/1F4FHir0qYnV0MEp2sYYp_BuvnJgWlWPhWOwZ6LbW7W4/edit?usp=sharing This change makes it invalid to directly delete a Value, User, or Instruction pointer. Instead, such code can be rewritten to a null check and a call Value::deleteValue(). Value objects tend to have their lifetimes managed through iplist, so for the most part, this isn't a big deal. However, there are some places where LLVM deletes values, and those places had to be migrated to deleteValue. I have also created llvm::unique_value, which has a custom deleter, so it can be used in place of std::unique_ptr<Value>. I had to add the "DerivedUser" Deleter escape hatch for MemorySSA, which derives from User outside of lib/IR. Code in IR cannot include MemorySSA headers or call the MemoryAccess object destructors without introducing a circular dependency, so we need some level of indirection. Unfortunately, no class derived from User may have any virtual methods, because adding a virtual method would break User::getHungOffOperands(), which assumes that it can find the use list immediately prior to the User object. I've added a static_assert to the appropriate OperandTraits templates to help people avoid this trap. Reviewers: chandlerc, mehdi_amini, pete, dberlin, george.burgess.iv Reviewed By: chandlerc Subscribers: krytarowski, eraman, george.burgess.iv, mzolotukhin, Prazek, nlewycky, hans, inglorion, pcc, tejohnson, dberlin, llvm-commits Differential Revision: https://reviews.llvm.org/D31261 llvm-svn: 303362
2017-05-19 01:24:10 +08:00
#define CHECK(x, y) \
{ \
std::string __s; \
raw_string_ostream __o(__s); \
Instruction *__I = cast<ConstantExpr>(x)->getAsInstruction(); \
__I->print(__o); \
__I->deleteValue(); \
__o.flush(); \
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);
Type *Int1Ty = Type::getInt1Ty(Context);
Type *FloatTy = Type::getFloatTy(Context);
Type *DoubleTy = Type::getDoubleTy(Context);
Constant *Global = M->getOrInsertGlobal("dummy",
PointerType::getUnqual(Int32Ty));
Constant *Global2 = M->getOrInsertGlobal("dummy2",
PointerType::getUnqual(Int32Ty));
Constant *P0 = ConstantExpr::getPtrToInt(Global, Int32Ty);
Constant *P1 = ConstantExpr::getUIToFP(P0, FloatTy);
Constant *P2 = ConstantExpr::getUIToFP(P0, DoubleTy);
Constant *P3 = ConstantExpr::getTrunc(P0, Int1Ty);
Constant *P4 = ConstantExpr::getPtrToInt(Global2, Int32Ty);
Constant *P5 = ConstantExpr::getUIToFP(P4, FloatTy);
Constant *P6 = ConstantExpr::getBitCast(P4, VectorType::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 *Undef16 = UndefValue::get(Int16Ty);
Constant *Undef64 = UndefValue::get(Int64Ty);
Constant *UndefV16 = UndefValue::get(P6->getType());
#define P0STR "ptrtoint (i32** @dummy to i32)"
#define P1STR "uitofp (i32 ptrtoint (i32** @dummy to i32) to float)"
#define P2STR "uitofp (i32 ptrtoint (i32** @dummy to i32) to double)"
#define P3STR "ptrtoint (i32** @dummy to i1)"
#define P4STR "ptrtoint (i32** @dummy2 to i32)"
#define P5STR "uitofp (i32 ptrtoint (i32** @dummy2 to i32) to float)"
#define P6STR "bitcast (i32 ptrtoint (i32** @dummy2 to i32) to <2 x i16>)"
CHECK(ConstantExpr::getNeg(P0), "sub i32 0, " P0STR);
CHECK(ConstantExpr::getFNeg(P1), "fneg float " P1STR);
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::getFAdd(P1, P1), "fadd float " P1STR ", " P1STR);
CHECK(ConstantExpr::getSub(P0, P0), "sub i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getFSub(P1, P1), "fsub float " P1STR ", " P1STR);
CHECK(ConstantExpr::getMul(P0, P0), "mul i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getFMul(P1, P1), "fmul float " P1STR ", " P1STR);
CHECK(ConstantExpr::getUDiv(P0, P0), "udiv i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getSDiv(P0, P0), "sdiv i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getFDiv(P1, P1), "fdiv float " P1STR ", " P1STR);
CHECK(ConstantExpr::getURem(P0, P0), "urem i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getSRem(P0, P0), "srem i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getFRem(P1, P1), "frem float " P1STR ", " P1STR);
CHECK(ConstantExpr::getAnd(P0, P0), "and i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getOr(P0, P0), "or i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getXor(P0, P0), "xor i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getShl(P0, P0), "shl i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getShl(P0, P0, true), "shl nuw i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getShl(P0, P0, false, true), "shl nsw i32 " P0STR ", "
P0STR);
CHECK(ConstantExpr::getLShr(P0, P0, false), "lshr i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getLShr(P0, P0, true), "lshr exact i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getAShr(P0, P0, false), "ashr i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getAShr(P0, P0, true), "ashr exact i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getSExt(P0, Int64Ty), "sext i32 " P0STR " to i64");
CHECK(ConstantExpr::getZExt(P0, Int64Ty), "zext i32 " P0STR " to i64");
CHECK(ConstantExpr::getFPTrunc(P2, FloatTy), "fptrunc double " P2STR
" to float");
CHECK(ConstantExpr::getFPExtend(P1, DoubleTy), "fpext float " P1STR
" to double");
CHECK(ConstantExpr::getExactUDiv(P0, P0), "udiv exact i32 " P0STR ", " P0STR);
CHECK(ConstantExpr::getSelect(P3, P0, P4), "select i1 " P3STR ", i32 " P0STR
", i32 " P4STR);
CHECK(ConstantExpr::getICmp(CmpInst::ICMP_EQ, P0, P4), "icmp eq i32 " P0STR
", " P4STR);
CHECK(ConstantExpr::getFCmp(CmpInst::FCMP_ULT, P1, P5), "fcmp ult float "
P1STR ", " P5STR);
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),
[opaque pointer type] Add textual IR support for explicit type parameter to getelementptr instruction One of several parallel first steps to remove the target type of pointers, replacing them with a single opaque pointer type. This adds an explicit type parameter to the gep instruction so that when the first parameter becomes an opaque pointer type, the type to gep through is still available to the instructions. * This doesn't modify gep operators, only instructions (operators will be handled separately) * Textual IR changes only. Bitcode (including upgrade) and changing the in-memory representation will be in separate changes. * geps of vectors are transformed as: getelementptr <4 x float*> %x, ... ->getelementptr float, <4 x float*> %x, ... Then, once the opaque pointer type is introduced, this will ultimately look like: getelementptr float, <4 x ptr> %x with the unambiguous interpretation that it is a vector of pointers to float. * address spaces remain on the pointer, not the type: getelementptr float addrspace(1)* %x ->getelementptr float, float addrspace(1)* %x Then, eventually: getelementptr float, ptr addrspace(1) %x Importantly, the massive amount of test case churn has been automated by same crappy python code. I had to manually update a few test cases that wouldn't fit the script's model (r228970,r229196,r229197,r229198). The python script just massages stdin and writes the result to stdout, I then wrapped that in a shell script to handle replacing files, then using the usual find+xargs to migrate all the files. update.py: import fileinput import sys import re ibrep = re.compile(r"(^.*?[^%\w]getelementptr inbounds )(((?:<\d* x )?)(.*?)(| addrspace\(\d\)) *\*(|>)(?:$| *(?:%|@|null|undef|blockaddress|getelementptr|addrspacecast|bitcast|inttoptr|\[\[[a-zA-Z]|\{\{).*$))") normrep = re.compile( r"(^.*?[^%\w]getelementptr )(((?:<\d* x )?)(.*?)(| addrspace\(\d\)) *\*(|>)(?:$| *(?:%|@|null|undef|blockaddress|getelementptr|addrspacecast|bitcast|inttoptr|\[\[[a-zA-Z]|\{\{).*$))") def conv(match, line): if not match: return line line = match.groups()[0] if len(match.groups()[5]) == 0: line += match.groups()[2] line += match.groups()[3] line += ", " line += match.groups()[1] line += "\n" return line for line in sys.stdin: if line.find("getelementptr ") == line.find("getelementptr inbounds"): if line.find("getelementptr inbounds") != line.find("getelementptr inbounds ("): line = conv(re.match(ibrep, line), line) elif line.find("getelementptr ") != line.find("getelementptr ("): line = conv(re.match(normrep, line), line) sys.stdout.write(line) apply.sh: for name in "$@" do python3 `dirname "$0"`/update.py < "$name" > "$name.tmp" && mv "$name.tmp" "$name" rm -f "$name.tmp" done The actual commands: From llvm/src: find test/ -name *.ll | xargs ./apply.sh From llvm/src/tools/clang: find test/ -name *.mm -o -name *.m -o -name *.cpp -o -name *.c | xargs -I '{}' ../../apply.sh "{}" From llvm/src/tools/polly: find test/ -name *.ll | xargs ./apply.sh After that, check-all (with llvm, clang, clang-tools-extra, lld, compiler-rt, and polly all checked out). The extra 'rm' in the apply.sh script is due to a few files in clang's test suite using interesting unicode stuff that my python script was throwing exceptions on. None of those files needed to be migrated, so it seemed sufficient to ignore those cases. Reviewers: rafael, dexonsmith, grosser Differential Revision: http://reviews.llvm.org/D7636 llvm-svn: 230786
2015-02-28 03:29:02 +08:00
// "getelementptr i32*, i32** @dummy, i32 1");
CHECK(ConstantExpr::getInBoundsGetElementPtr(PointerType::getUnqual(Int32Ty),
Global, V),
[opaque pointer type] Add textual IR support for explicit type parameter to getelementptr instruction One of several parallel first steps to remove the target type of pointers, replacing them with a single opaque pointer type. This adds an explicit type parameter to the gep instruction so that when the first parameter becomes an opaque pointer type, the type to gep through is still available to the instructions. * This doesn't modify gep operators, only instructions (operators will be handled separately) * Textual IR changes only. Bitcode (including upgrade) and changing the in-memory representation will be in separate changes. * geps of vectors are transformed as: getelementptr <4 x float*> %x, ... ->getelementptr float, <4 x float*> %x, ... Then, once the opaque pointer type is introduced, this will ultimately look like: getelementptr float, <4 x ptr> %x with the unambiguous interpretation that it is a vector of pointers to float. * address spaces remain on the pointer, not the type: getelementptr float addrspace(1)* %x ->getelementptr float, float addrspace(1)* %x Then, eventually: getelementptr float, ptr addrspace(1) %x Importantly, the massive amount of test case churn has been automated by same crappy python code. I had to manually update a few test cases that wouldn't fit the script's model (r228970,r229196,r229197,r229198). The python script just massages stdin and writes the result to stdout, I then wrapped that in a shell script to handle replacing files, then using the usual find+xargs to migrate all the files. update.py: import fileinput import sys import re ibrep = re.compile(r"(^.*?[^%\w]getelementptr inbounds )(((?:<\d* x )?)(.*?)(| addrspace\(\d\)) *\*(|>)(?:$| *(?:%|@|null|undef|blockaddress|getelementptr|addrspacecast|bitcast|inttoptr|\[\[[a-zA-Z]|\{\{).*$))") normrep = re.compile( r"(^.*?[^%\w]getelementptr )(((?:<\d* x )?)(.*?)(| addrspace\(\d\)) *\*(|>)(?:$| *(?:%|@|null|undef|blockaddress|getelementptr|addrspacecast|bitcast|inttoptr|\[\[[a-zA-Z]|\{\{).*$))") def conv(match, line): if not match: return line line = match.groups()[0] if len(match.groups()[5]) == 0: line += match.groups()[2] line += match.groups()[3] line += ", " line += match.groups()[1] line += "\n" return line for line in sys.stdin: if line.find("getelementptr ") == line.find("getelementptr inbounds"): if line.find("getelementptr inbounds") != line.find("getelementptr inbounds ("): line = conv(re.match(ibrep, line), line) elif line.find("getelementptr ") != line.find("getelementptr ("): line = conv(re.match(normrep, line), line) sys.stdout.write(line) apply.sh: for name in "$@" do python3 `dirname "$0"`/update.py < "$name" > "$name.tmp" && mv "$name.tmp" "$name" rm -f "$name.tmp" done The actual commands: From llvm/src: find test/ -name *.ll | xargs ./apply.sh From llvm/src/tools/clang: find test/ -name *.mm -o -name *.m -o -name *.cpp -o -name *.c | xargs -I '{}' ../../apply.sh "{}" From llvm/src/tools/polly: find test/ -name *.ll | xargs ./apply.sh After that, check-all (with llvm, clang, clang-tools-extra, lld, compiler-rt, and polly all checked out). The extra 'rm' in the apply.sh script is due to a few files in clang's test suite using interesting unicode stuff that my python script was throwing exceptions on. None of those files needed to be migrated, so it seemed sufficient to ignore those cases. Reviewers: rafael, dexonsmith, grosser Differential Revision: http://reviews.llvm.org/D7636 llvm-svn: 230786
2015-02-28 03:29:02 +08:00
"getelementptr inbounds i32*, i32** @dummy, i32 1");
CHECK(ConstantExpr::getExtractElement(P6, One), "extractelement <2 x i16> "
P6STR ", i32 1");
EXPECT_EQ(Undef16, ConstantExpr::getExtractElement(P6, Two));
EXPECT_EQ(Undef16, ConstantExpr::getExtractElement(P6, Big));
EXPECT_EQ(Undef16, ConstantExpr::getExtractElement(P6, Undef64));
EXPECT_EQ(Elt, ConstantExpr::getExtractElement(
ConstantExpr::getInsertElement(P6, Elt, One), One));
EXPECT_EQ(UndefV16, ConstantExpr::getInsertElement(P6, Elt, Two));
EXPECT_EQ(UndefV16, ConstantExpr::getInsertElement(P6, Elt, Big));
EXPECT_EQ(UndefV16, 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(Int32Ty));
Constant *GEP = ConstantExpr::getGetElementPtr(
PointerType::getUnqual(Int32Ty), 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(IntTy, 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, PtrTy, 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(I16Ty, 0);
Constant *Aliasee = ConstantExpr::getBitCast(G, I16PTy);
LLVMValueRef AliasRef =
LLVMAddAlias(wrap(M.get()), wrap(I16PTy), 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;
std::unique_ptr<Module> M(new Module("MyModule", 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 *CDV = ConstantArray::get(ArrayTy, Vals);
ASSERT_TRUE(dyn_cast<ConstantDataArray>(CDV) != nullptr)
<< " T = " << getNameOfType(T);
}
for (Type *T : {Type::getHalfTy(Context), Type::getFloatTy(Context),
Type::getDoubleTy(Context)}) {
ArrayType *ArrayTy = ArrayType::get(T, 2);
Constant *Vals[] = {ConstantFP::get(T, 0), ConstantFP::get(T, 1)};
Constant *CDV = ConstantArray::get(ArrayTy, Vals);
ASSERT_TRUE(dyn_cast<ConstantDataArray>(CDV) != nullptr)
<< " T = " << getNameOfType(T);
}
}
TEST(ConstantsTest, BuildConstantDataVectors) {
LLVMContext Context;
std::unique_ptr<Module> M(new Module("MyModule", 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 *CDV = ConstantVector::get(Vals);
ASSERT_TRUE(dyn_cast<ConstantDataVector>(CDV) != nullptr)
<< " T = " << getNameOfType(T);
}
for (Type *T : {Type::getHalfTy(Context), Type::getFloatTy(Context),
Type::getDoubleTy(Context)}) {
Constant *Vals[] = {ConstantFP::get(T, 0), ConstantFP::get(T, 1)};
Constant *CDV = ConstantVector::get(Vals);
ASSERT_TRUE(dyn_cast<ConstantDataVector>(CDV) != nullptr)
<< " 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(i32, 0);
auto *C = ConstantExpr::getBitCast(G, PtrTy);
ASSERT_EQ(cast<ConstantExpr>(C)->getOpcode(), Instruction::BitCast);
}
bool foldFuncPtrAndConstToNull(LLVMContext &Context, Module *TheModule,
uint64_t AndValue, unsigned FunctionAlign = 0) {
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));
bool result = ConstantExpr::get(Instruction::And, TheConstantExpr,
TheConstant)->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, 4));
}
TEST(ConstantsTest, DontFoldFunctionAlign4PtrAlignIndependent) {
LLVMContext Context;
Module TheModule("TestModule", Context);
TheModule.setDataLayout("Fi8");
ASSERT_FALSE(foldFuncPtrAndConstToNull(Context, &TheModule, 2, 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, 4));
}
TEST(ConstantsTest, FoldGlobalVariablePtr) {
LLVMContext Context;
IntegerType *IntType(Type::getInt32Ty(Context));
std::unique_ptr<GlobalVariable> Global(
new GlobalVariable(IntType, true, GlobalValue::ExternalLinkage));
Global->setAlignment(4);
ConstantInt *TheConstant(ConstantInt::get(IntType, 2));
Constant *TheConstantExpr(
ConstantExpr::getPtrToInt(Global.get(), IntType));
ASSERT_TRUE(ConstantExpr::get( \
Instruction::And, TheConstantExpr, TheConstant)->isNullValue());
}
} // end anonymous namespace
} // end namespace llvm