Add support for non-zero null pointer for C and OpenCL
In amdgcn target, null pointers in global, constant, and generic address space take value 0 but null pointers in private and local address space take value -1. Currently LLVM assumes all null pointers take value 0, which results in incorrectly translated IR. To workaround this issue, instead of emit null pointers in local and private address space, a null pointer in generic address space is emitted and casted to local and private address space.
Tentative definition of global variables with non-zero initializer will have weak linkage instead of common linkage since common linkage requires zero initializer and does not have explicit section to hold the non-zero value.
Virtual member functions getNullPointer and performAddrSpaceCast are added to TargetCodeGenInfo which by default returns ConstantPointerNull and emitting addrspacecast instruction. A virtual member function getNullPointerValue is added to TargetInfo which by default returns 0. Each target can override these virtual functions to get target specific null pointer and the null pointer value for specific address space, and perform specific translations for addrspacecast.
Wrapper functions getNullPointer is added to CodegenModule and getTargetNullPointerValue is added to ASTContext to facilitate getting the target specific null pointers and their values.
This change has no effect on other targets except amdgcn target. Other targets can provide support of non-zero null pointer in a similar way.
This change only provides support for non-zero null pointer for C and OpenCL. Supporting for other languages will be added later incrementally.
Differential Revision: https://reviews.llvm.org/D26196
llvm-svn: 289252
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 695e92e..4657347 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -708,7 +708,7 @@
}
auto ty = cast<llvm::PointerType>(tempLV.getAddress().getElementType());
- llvm::Value *zero = llvm::ConstantPointerNull::get(ty);
+ llvm::Value *zero = CGM.getNullPointer(ty, tempLV.getType());
// If __weak, we want to use a barrier under certain conditions.
if (lifetime == Qualifiers::OCL_Weak)
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 98f476e..c098988 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -1050,7 +1050,8 @@
return true;
// (int*)0 - Null pointer expressions.
if (const CastExpr *ICE = dyn_cast<CastExpr>(E))
- return ICE->getCastKind() == CK_NullToPointer;
+ return ICE->getCastKind() == CK_NullToPointer &&
+ CGF.getTypes().isPointerZeroInitializable(E->getType());
// '\0'
if (const CharacterLiteral *CL = dyn_cast<CharacterLiteral>(E))
return CL->getValue() == 0;
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 27c8386..f657309 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -16,6 +16,7 @@
#include "CGObjCRuntime.h"
#include "CGRecordLayout.h"
#include "CodeGenModule.h"
+#include "TargetInfo.h"
#include "clang/AST/APValue.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecordLayout.h"
@@ -1262,6 +1263,10 @@
return C;
}
+llvm::Constant *CodeGenModule::getNullPointer(llvm::PointerType *T, QualType QT) {
+ return getTargetCodeGenInfo().getNullPointer(*this, T, QT);
+}
+
llvm::Constant *CodeGenModule::EmitConstantValue(const APValue &Value,
QualType DestType,
CodeGenFunction *CGF) {
@@ -1293,6 +1298,7 @@
llvm::ConstantInt::get(Int64Ty, Value.getLValueOffset().getQuantity());
llvm::Constant *C = nullptr;
+
if (APValue::LValueBase LVBase = Value.getLValueBase()) {
// An array can be represented as an lvalue referring to the base.
if (isa<llvm::ArrayType>(DestTy)) {
@@ -1323,7 +1329,9 @@
// Convert to the appropriate type; this could be an lvalue for
// an integer.
- if (isa<llvm::PointerType>(DestTy)) {
+ if (auto PT = dyn_cast<llvm::PointerType>(DestTy)) {
+ if (Value.isNullPointer())
+ return getNullPointer(PT, DestType);
// Convert the integer to a pointer-sized integer before converting it
// to a pointer.
C = llvm::ConstantExpr::getIntegerCast(
@@ -1510,7 +1518,7 @@
const CXXRecordDecl *base);
static llvm::Constant *EmitNullConstant(CodeGenModule &CGM,
- const CXXRecordDecl *record,
+ const RecordDecl *record,
bool asCompleteObject) {
const CGRecordLayout &layout = CGM.getTypes().getCGRecordLayout(record);
llvm::StructType *structure =
@@ -1520,25 +1528,29 @@
unsigned numElements = structure->getNumElements();
std::vector<llvm::Constant *> elements(numElements);
+ auto CXXR = dyn_cast<CXXRecordDecl>(record);
// Fill in all the bases.
- for (const auto &I : record->bases()) {
- if (I.isVirtual()) {
- // Ignore virtual bases; if we're laying out for a complete
- // object, we'll lay these out later.
- continue;
+ if (CXXR) {
+ for (const auto &I : CXXR->bases()) {
+ if (I.isVirtual()) {
+ // Ignore virtual bases; if we're laying out for a complete
+ // object, we'll lay these out later.
+ continue;
+ }
+
+ const CXXRecordDecl *base =
+ cast<CXXRecordDecl>(I.getType()->castAs<RecordType>()->getDecl());
+
+ // Ignore empty bases.
+ if (base->isEmpty() ||
+ CGM.getContext().getASTRecordLayout(base).getNonVirtualSize()
+ .isZero())
+ continue;
+
+ unsigned fieldIndex = layout.getNonVirtualBaseLLVMFieldNo(base);
+ llvm::Type *baseType = structure->getElementType(fieldIndex);
+ elements[fieldIndex] = EmitNullConstantForBase(CGM, baseType, base);
}
-
- const CXXRecordDecl *base =
- cast<CXXRecordDecl>(I.getType()->castAs<RecordType>()->getDecl());
-
- // Ignore empty bases.
- if (base->isEmpty() ||
- CGM.getContext().getASTRecordLayout(base).getNonVirtualSize().isZero())
- continue;
-
- unsigned fieldIndex = layout.getNonVirtualBaseLLVMFieldNo(base);
- llvm::Type *baseType = structure->getElementType(fieldIndex);
- elements[fieldIndex] = EmitNullConstantForBase(CGM, baseType, base);
}
// Fill in all the fields.
@@ -1562,8 +1574,8 @@
}
// Fill in the virtual bases, if we're working with the complete object.
- if (asCompleteObject) {
- for (const auto &I : record->vbases()) {
+ if (CXXR && asCompleteObject) {
+ for (const auto &I : CXXR->vbases()) {
const CXXRecordDecl *base =
cast<CXXRecordDecl>(I.getType()->castAs<RecordType>()->getDecl());
@@ -1605,6 +1617,10 @@
}
llvm::Constant *CodeGenModule::EmitNullConstant(QualType T) {
+ if (auto PT = T->getAs<PointerType>())
+ return getNullPointer(cast<llvm::PointerType>(getTypes().ConvertTypeForMem(T)),
+ T);
+
if (getTypes().isZeroInitializable(T))
return llvm::Constant::getNullValue(getTypes().ConvertTypeForMem(T));
@@ -1620,10 +1636,8 @@
return llvm::ConstantArray::get(ATy, Array);
}
- if (const RecordType *RT = T->getAs<RecordType>()) {
- const CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
- return ::EmitNullConstant(*this, RD, /*complete object*/ true);
- }
+ if (const RecordType *RT = T->getAs<RecordType>())
+ return ::EmitNullConstant(*this, RT->getDecl(), /*complete object*/ true);
assert(T->isMemberDataPointerType() &&
"Should only see pointers to data members here!");
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index dc81865..2b54370 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -19,6 +19,7 @@
#include "TargetInfo.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/TargetInfo.h"
@@ -171,9 +172,9 @@
}
/// EmitPointerToBoolConversion - Perform a pointer to boolean conversion.
- Value *EmitPointerToBoolConversion(Value *V) {
- Value *Zero = llvm::ConstantPointerNull::get(
- cast<llvm::PointerType>(V->getType()));
+ Value *EmitPointerToBoolConversion(Value *V, QualType QT) {
+ Value *Zero = CGF.CGM.getNullPointer(cast<llvm::PointerType>(V->getType()), QT);
+
return Builder.CreateICmpNE(V, Zero, "tobool");
}
@@ -591,7 +592,7 @@
return EmitIntToBoolConversion(Src);
assert(isa<llvm::PointerType>(Src->getType()));
- return EmitPointerToBoolConversion(Src);
+ return EmitPointerToBoolConversion(Src, SrcType);
}
void ScalarExprEmitter::EmitFloatConversionCheck(
@@ -1394,11 +1395,23 @@
return Builder.CreateBitCast(Src, DstTy);
}
case CK_AddressSpaceConversion: {
- Value *Src = Visit(const_cast<Expr*>(E));
+ Expr::EvalResult Result;
+ if (E->EvaluateAsRValue(Result, CGF.getContext()) &&
+ Result.Val.isNullPointer()) {
+ // If E has side effect, it is emitted even if its final result is a
+ // null pointer. In that case, a DCE pass should be able to
+ // eliminate the useless instructions emitted during translating E.
+ if (Result.HasSideEffects)
+ Visit(E);
+ return CGF.CGM.getNullPointer(cast<llvm::PointerType>(
+ ConvertType(DestTy)), DestTy);
+ }
// Since target may map different address spaces in AST to the same address
// space, an address space conversion may end up as a bitcast.
- return Builder.CreatePointerBitCastOrAddrSpaceCast(Src,
- ConvertType(DestTy));
+ auto *Src = Visit(E);
+ return CGF.CGM.getTargetCodeGenInfo().performAddrSpaceCast(CGF, Src,
+ E->getType(),
+ DestTy);
}
case CK_AtomicToNonAtomic:
case CK_NonAtomicToAtomic:
@@ -1453,8 +1466,8 @@
if (MustVisitNullValue(E))
(void) Visit(E);
- return llvm::ConstantPointerNull::get(
- cast<llvm::PointerType>(ConvertType(DestTy)));
+ return CGF.CGM.getNullPointer(cast<llvm::PointerType>(ConvertType(DestTy)),
+ DestTy);
case CK_NullToMemberPointer: {
if (MustVisitNullValue(E))
@@ -1547,7 +1560,7 @@
case CK_IntegralToBoolean:
return EmitIntToBoolConversion(Visit(E));
case CK_PointerToBoolean:
- return EmitPointerToBoolConversion(Visit(E));
+ return EmitPointerToBoolConversion(Visit(E), E->getType());
case CK_FloatingToBoolean:
return EmitFloatToBoolConversion(Visit(E));
case CK_MemberPointerToBoolean: {
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 78f43bd..98b6215 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -2611,9 +2611,16 @@
else
GV->setDLLStorageClass(llvm::GlobalVariable::DefaultStorageClass);
- if (Linkage == llvm::GlobalVariable::CommonLinkage)
+ if (Linkage == llvm::GlobalVariable::CommonLinkage) {
// common vars aren't constant even if declared const.
GV->setConstant(false);
+ // Tentative definition of global variables may be initialized with
+ // non-zero null pointers. In this case they should have weak linkage
+ // since common linkage must have zero initializer and must not have
+ // explicit section therefore cannot have non-zero initial value.
+ if (!GV->getInitializer()->isNullValue())
+ GV->setLinkage(llvm::GlobalVariable::WeakAnyLinkage);
+ }
setNonAliasAttributes(D, GV);
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index be45bb8..f014be9 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1155,6 +1155,11 @@
llvm::Value *
createOpenCLIntToSamplerConversion(const Expr *E, CodeGenFunction &CGF);
+ /// Get target specific null pointer.
+ /// \param T is the LLVM type of the null pointer.
+ /// \param QT is the clang QualType of the null pointer.
+ llvm::Constant *getNullPointer(llvm::PointerType *T, QualType QT);
+
private:
llvm::Constant *
GetOrCreateLLVMFunction(StringRef MangledName, llvm::Type *Ty, GlobalDecl D,
diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp
index ebe55c7..1ec6607 100644
--- a/clang/lib/CodeGen/CodeGenTypes.cpp
+++ b/clang/lib/CodeGen/CodeGenTypes.cpp
@@ -736,10 +736,14 @@
return *Layout;
}
+bool CodeGenTypes::isPointerZeroInitializable(QualType T) {
+ assert (T->getAs<PointerType>() && "Invalid type");
+ return isZeroInitializable(T);
+}
+
bool CodeGenTypes::isZeroInitializable(QualType T) {
- // No need to check for member pointers when not compiling C++.
- if (!Context.getLangOpts().CPlusPlus)
- return true;
+ if (auto PT = T->getAs<PointerType>())
+ return Context.getTargetNullPointerValue(T) == 0;
if (const auto *AT = Context.getAsArrayType(T)) {
if (isa<IncompleteArrayType>(AT))
@@ -753,7 +757,7 @@
// Records are non-zero-initializable if they contain any
// non-zero-initializable subobjects.
if (const RecordType *RT = T->getAs<RecordType>()) {
- const CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
+ auto RD = cast<RecordDecl>(RT->getDecl());
return isZeroInitializable(RD);
}
diff --git a/clang/lib/CodeGen/CodeGenTypes.h b/clang/lib/CodeGen/CodeGenTypes.h
index 00df10d..2ce6591 100644
--- a/clang/lib/CodeGen/CodeGenTypes.h
+++ b/clang/lib/CodeGen/CodeGenTypes.h
@@ -352,6 +352,10 @@
/// zero-initialized (in the C++ sense) with an LLVM zeroinitializer.
bool isZeroInitializable(QualType T);
+ /// Check if the pointer type can be zero-initialized (in the C++ sense)
+ /// with an LLVM zeroinitializer.
+ bool isPointerZeroInitializable(QualType T);
+
/// IsZeroInitializable - Return whether a record type can be
/// zero-initialized (in the C++ sense) with an LLVM zeroinitializer.
bool isZeroInitializable(const RecordDecl *RD);
diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp
index 79d4815..f70c6cb 100644
--- a/clang/lib/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CodeGen/TargetInfo.cpp
@@ -401,6 +401,20 @@
return llvm::CallingConv::C;
}
+llvm::Constant *TargetCodeGenInfo::getNullPointer(const CodeGen::CodeGenModule &CGM,
+ llvm::PointerType *T, QualType QT) const {
+ return llvm::ConstantPointerNull::get(T);
+}
+
+llvm::Value *TargetCodeGenInfo::performAddrSpaceCast(
+ CodeGen::CodeGenFunction &CGF, llvm::Value *Src, QualType SrcTy,
+ QualType DestTy) const {
+ // Since target may map different address spaces in AST to the same address
+ // space, an address space conversion may end up as a bitcast.
+ return CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(Src,
+ CGF.ConvertType(DestTy));
+}
+
static bool isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays);
/// isEmptyField - Return true iff a the field is "empty", that is it
@@ -7075,8 +7089,10 @@
void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
CodeGen::CodeGenModule &M) const override;
unsigned getOpenCLKernelCallingConv() const override;
-};
+ llvm::Constant *getNullPointer(const CodeGen::CodeGenModule &CGM,
+ llvm::PointerType *T, QualType QT) const override;
+};
}
static void appendOpenCLVersionMD (CodeGen::CodeGenModule &CGM);
@@ -7140,6 +7156,24 @@
return llvm::CallingConv::AMDGPU_KERNEL;
}
+// Currently LLVM assumes null pointers always have value 0,
+// which results in incorrectly transformed IR. Therefore, instead of
+// emitting null pointers in private and local address spaces, a null
+// pointer in generic address space is emitted which is casted to a
+// pointer in local or private address space.
+llvm::Constant *AMDGPUTargetCodeGenInfo::getNullPointer(
+ const CodeGen::CodeGenModule &CGM, llvm::PointerType *PT,
+ QualType QT) const {
+ if (CGM.getContext().getTargetNullPointerValue(QT) == 0)
+ return llvm::ConstantPointerNull::get(PT);
+
+ auto &Ctx = CGM.getContext();
+ auto NPT = llvm::PointerType::get(PT->getElementType(),
+ Ctx.getTargetAddressSpace(LangAS::opencl_generic));
+ return llvm::ConstantExpr::getAddrSpaceCast(
+ llvm::ConstantPointerNull::get(NPT), PT);
+}
+
//===----------------------------------------------------------------------===//
// SPARC v8 ABI Implementation.
// Based on the SPARC Compliance Definition version 2.4.1.
diff --git a/clang/lib/CodeGen/TargetInfo.h b/clang/lib/CodeGen/TargetInfo.h
index e463825..223d6d0 100644
--- a/clang/lib/CodeGen/TargetInfo.h
+++ b/clang/lib/CodeGen/TargetInfo.h
@@ -220,6 +220,22 @@
/// Get LLVM calling convention for OpenCL kernel.
virtual unsigned getOpenCLKernelCallingConv() const;
+
+ /// Get target specific null pointer.
+ /// \param T is the LLVM type of the null pointer.
+ /// \param QT is the clang QualType of the null pointer.
+ /// \return ConstantPointerNull with the given type \p T.
+ /// Each target can override it to return its own desired constant value.
+ virtual llvm::Constant *getNullPointer(const CodeGen::CodeGenModule &CGM,
+ llvm::PointerType *T, QualType QT) const;
+
+ /// Perform address space cast of an expression of pointer type.
+ /// \param V is the LLVM value to be casted to another address space.
+ /// \param SrcTy is the QualType of \p V.
+ /// \param DestTy is the destination QualType.
+ virtual llvm::Value *performAddrSpaceCast(CodeGen::CodeGenFunction &CGF,
+ llvm::Value *V, QualType SrcTy, QualType DestTy) const;
+
};
} // namespace CodeGen