Index: include/clang/AST/APValue.h =================================================================== --- include/clang/AST/APValue.h +++ include/clang/AST/APValue.h @@ -135,14 +135,15 @@ } APValue(const APValue &RHS); APValue(APValue &&RHS) : Kind(Uninitialized) { swap(RHS); } - APValue(LValueBase B, const CharUnits &O, NoLValuePath N, unsigned CallIndex) + APValue(LValueBase B, const CharUnits &O, NoLValuePath N, unsigned CallIndex, + bool IsNullPtr = false) : Kind(Uninitialized) { - MakeLValue(); setLValue(B, O, N, CallIndex); + MakeLValue(); setLValue(B, O, N, CallIndex, IsNullPtr); } APValue(LValueBase B, const CharUnits &O, ArrayRef Path, - bool OnePastTheEnd, unsigned CallIndex) + bool OnePastTheEnd, unsigned CallIndex, bool IsNullPtr = false) : Kind(Uninitialized) { - MakeLValue(); setLValue(B, O, Path, OnePastTheEnd, CallIndex); + MakeLValue(); setLValue(B, O, Path, OnePastTheEnd, CallIndex, IsNullPtr); } APValue(UninitArray, unsigned InitElts, unsigned Size) : Kind(Uninitialized) { MakeArray(InitElts, Size); @@ -254,6 +255,7 @@ bool hasLValuePath() const; ArrayRef getLValuePath() const; unsigned getLValueCallIndex() const; + bool isNullPointer() const; APValue &getVectorElt(unsigned I) { assert(isVector() && "Invalid accessor"); @@ -374,10 +376,10 @@ ((ComplexAPFloat *)(char *)Data.buffer)->Imag = std::move(I); } void setLValue(LValueBase B, const CharUnits &O, NoLValuePath, - unsigned CallIndex); + unsigned CallIndex, bool IsNullPtr); void setLValue(LValueBase B, const CharUnits &O, ArrayRef Path, bool OnePastTheEnd, - unsigned CallIndex); + unsigned CallIndex, bool IsNullPtr); void setUnion(const FieldDecl *Field, const APValue &Value) { assert(isUnion() && "Invalid accessor"); ((UnionData*)(char*)Data.buffer)->Field = Field; Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -2261,6 +2261,10 @@ return (*AddrSpaceMap)[AS - LangAS::Offset]; } + /// Get target-dependent integer value for null pointer which is used for + /// constant folding. + uint64_t getTargetNullPointerValue(QualType QT) const; + bool addressSpaceMapManglingFor(unsigned AS) const { return AddrSpaceMapMangling || AS < LangAS::Offset || Index: include/clang/Basic/TargetInfo.h =================================================================== --- include/clang/Basic/TargetInfo.h +++ include/clang/Basic/TargetInfo.h @@ -42,6 +42,7 @@ class LangOptions; class CodeGenOptions; class MacroBuilder; +class QualType; class SourceLocation; class SourceManager; @@ -300,6 +301,12 @@ return PointerWidth; } + /// \brief Get integer value for null pointer. + /// \param AddrSpace address space of pointee in source language. + virtual uint64_t getNullPointerValue(unsigned AddrSpace) const { + return 0; + } + /// \brief Return the size of '_Bool' and C++ 'bool' for this target, in bits. unsigned getBoolWidth() const { return BoolWidth; } Index: lib/AST/APValue.cpp =================================================================== --- lib/AST/APValue.cpp +++ lib/AST/APValue.cpp @@ -27,6 +27,7 @@ CharUnits Offset; unsigned PathLength; unsigned CallIndex; + bool IsNullPtr; }; } @@ -149,10 +150,11 @@ MakeLValue(); if (RHS.hasLValuePath()) setLValue(RHS.getLValueBase(), RHS.getLValueOffset(), RHS.getLValuePath(), - RHS.isLValueOnePastTheEnd(), RHS.getLValueCallIndex()); + RHS.isLValueOnePastTheEnd(), RHS.getLValueCallIndex(), + RHS.isNullPointer()); else setLValue(RHS.getLValueBase(), RHS.getLValueOffset(), NoLValuePath(), - RHS.getLValueCallIndex()); + RHS.getLValueCallIndex(), RHS.isNullPointer()); break; case Array: MakeArray(RHS.getArrayInitializedElts(), RHS.getArraySize()); @@ -579,8 +581,13 @@ return ((const LV*)(const char*)Data.buffer)->CallIndex; } +bool APValue::isNullPointer() const { + assert(isLValue() && "Invalid usage"); + return ((const LV*)(const char*)Data.buffer)->IsNullPtr; +} + void APValue::setLValue(LValueBase B, const CharUnits &O, NoLValuePath, - unsigned CallIndex) { + unsigned CallIndex, bool IsNullPtr) { assert(isLValue() && "Invalid accessor"); LV &LVal = *((LV*)(char*)Data.buffer); LVal.BaseAndIsOnePastTheEnd.setPointer(B); @@ -588,11 +595,12 @@ LVal.Offset = O; LVal.CallIndex = CallIndex; LVal.resizePath((unsigned)-1); + LVal.IsNullPtr = IsNullPtr; } void APValue::setLValue(LValueBase B, const CharUnits &O, ArrayRef Path, bool IsOnePastTheEnd, - unsigned CallIndex) { + unsigned CallIndex, bool IsNullPtr) { assert(isLValue() && "Invalid accessor"); LV &LVal = *((LV*)(char*)Data.buffer); LVal.BaseAndIsOnePastTheEnd.setPointer(B); @@ -601,6 +609,7 @@ LVal.CallIndex = CallIndex; LVal.resizePath(Path.size()); memcpy(LVal.getPath(), Path.data(), Path.size() * sizeof(LValuePathEntry)); + LVal.IsNullPtr = IsNullPtr; } const ValueDecl *APValue::getMemberPointerDecl() const { Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -9426,6 +9426,16 @@ } +uint64_t ASTContext::getTargetNullPointerValue(QualType QT) const { + unsigned AS; + if (QT->getUnqualifiedDesugaredType()->isNullPtrType()) + AS = 0; + else + AS = QT->getPointeeType().getAddressSpace(); + + return getTargetInfo().getNullPointerValue(AS); +} + // Explicitly instantiate this in case a Redeclarable is used from a TU that // doesn't include ASTContext.h template Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -1070,6 +1070,7 @@ unsigned InvalidBase : 1; unsigned CallIndex : 31; SubobjectDesignator Designator; + bool IsNullPtr; const APValue::LValueBase getLValueBase() const { return Base; } CharUnits &getLValueOffset() { return Offset; } @@ -1077,13 +1078,15 @@ unsigned getLValueCallIndex() const { return CallIndex; } SubobjectDesignator &getLValueDesignator() { return Designator; } const SubobjectDesignator &getLValueDesignator() const { return Designator;} + bool isNullPointer() const { return IsNullPtr;} void moveInto(APValue &V) const { if (Designator.Invalid) - V = APValue(Base, Offset, APValue::NoLValuePath(), CallIndex); + V = APValue(Base, Offset, APValue::NoLValuePath(), CallIndex, + IsNullPtr); else V = APValue(Base, Offset, Designator.Entries, - Designator.IsOnePastTheEnd, CallIndex); + Designator.IsOnePastTheEnd, CallIndex, IsNullPtr); } void setFrom(ASTContext &Ctx, const APValue &V) { assert(V.isLValue()); @@ -1092,14 +1095,17 @@ InvalidBase = false; CallIndex = V.getLValueCallIndex(); Designator = SubobjectDesignator(Ctx, V); + IsNullPtr = V.isNullPointer(); } - void set(APValue::LValueBase B, unsigned I = 0, bool BInvalid = false) { + void set(APValue::LValueBase B, unsigned I = 0, bool BInvalid = false, + bool IsNullPtr_ = false, uint64_t Offset_ = 0) { Base = B; - Offset = CharUnits::Zero(); + Offset = CharUnits::fromQuantity(Offset_); InvalidBase = BInvalid; CallIndex = I; Designator = SubobjectDesignator(getType(B)); + IsNullPtr = IsNullPtr_; } void setInvalid(APValue::LValueBase B, unsigned I = 0) { @@ -1112,7 +1118,7 @@ CheckSubobjectKind CSK) { if (Designator.Invalid) return false; - if (!Base) { + if (IsNullPtr) { Info.CCEDiag(E, diag::note_constexpr_null_subobject) << CSK; Designator.setInvalid(); @@ -1141,9 +1147,22 @@ if (checkSubobject(Info, E, Imag ? CSK_Imag : CSK_Real)) Designator.addComplexUnchecked(EltTy, Imag); } - void adjustIndex(EvalInfo &Info, const Expr *E, uint64_t N) { - if (N && checkNullPointer(Info, E, CSK_ArrayIndex)) - Designator.adjustIndex(Info, E, N); + void clearIsNullPointer() { + IsNullPtr = false; + } + void adjustOffsetAndIndex(EvalInfo &Info, const Expr *E, uint64_t Index, + CharUnits ElementSize) { + // Compute the new offset in the appropriate width. + Offset += Index * ElementSize; + if (Index && checkNullPointer(Info, E, CSK_ArrayIndex)) + Designator.adjustIndex(Info, E, Index); + if (Index) + clearIsNullPointer(); + } + void adjustOffset(CharUnits N) { + Offset += N; + if (N.getQuantity()) + clearIsNullPointer(); } }; @@ -2018,7 +2037,7 @@ } unsigned I = FD->getFieldIndex(); - LVal.Offset += Info.Ctx.toCharUnitsFromBits(RL->getFieldOffset(I)); + LVal.adjustOffset(Info.Ctx.toCharUnitsFromBits(RL->getFieldOffset(I))); LVal.addDecl(Info, E, FD); return true; } @@ -2072,9 +2091,7 @@ if (!HandleSizeof(Info, E->getExprLoc(), EltTy, SizeOfPointee)) return false; - // Compute the new offset in the appropriate width. - LVal.Offset += Adjustment * SizeOfPointee; - LVal.adjustIndex(Info, E, Adjustment); + LVal.adjustOffsetAndIndex(Info, E, Adjustment, SizeOfPointee); return true; } @@ -5060,7 +5077,9 @@ return true; } bool ZeroInitialization(const Expr *E) { - return Success((Expr*)nullptr); + auto Offset = Info.Ctx.getTargetNullPointerValue(E->getType()); + Result.set((Expr*)nullptr, 0, false, true, Offset); + return true; } bool VisitBinaryOperator(const BinaryOperator *E); @@ -5159,6 +5178,8 @@ else CCEDiag(E, diag::note_constexpr_invalid_cast) << 2; } + if (E->getCastKind() == CK_AddressSpaceConversion && Result.IsNullPtr) + ZeroInitialization(E); return true; case CK_DerivedToBase: @@ -5200,6 +5221,7 @@ Result.Offset = CharUnits::fromQuantity(N); Result.CallIndex = 0; Result.Designator.setInvalid(); + Result.IsNullPtr = false; return true; } else { // Cast is of an lvalue, no need to change value. @@ -8334,8 +8356,13 @@ return true; } - APSInt AsInt = Info.Ctx.MakeIntValue(LV.getLValueOffset().getQuantity(), - SrcType); + uint64_t V; + if (LV.isNullPointer()) + V = Info.Ctx.getTargetNullPointerValue(SrcType); + else + V = LV.getLValueOffset().getQuantity(); + + APSInt AsInt = Info.Ctx.MakeIntValue(V, SrcType); return Success(HandleIntToIntCast(Info, E, DestType, SrcType, AsInt), E); } Index: lib/Basic/Targets.cpp =================================================================== --- lib/Basic/Targets.cpp +++ lib/Basic/Targets.cpp @@ -2245,6 +2245,13 @@ return CCCR_OK; } } + + // In amdgcn target the null pointer in global, constant, and generic + // address space has value 0 but in private and local address space has + // value ~0. + uint64_t getNullPointerValue(unsigned AS) const override { + return AS != LangAS::opencl_local && AS != 0 ? 0 : ~0; + } }; const Builtin::Info AMDGPUTargetInfo::BuiltinInfo[] = { Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -708,7 +708,7 @@ } auto ty = cast(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) Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -1050,7 +1050,8 @@ return true; // (int*)0 - Null pointer expressions. if (const CastExpr *ICE = dyn_cast(E)) - return ICE->getCastKind() == CK_NullToPointer; + return ICE->getCastKind() == CK_NullToPointer && + CGF.getTypes().isPointerZeroInitializable(E->getType()); // '\0' if (const CharacterLiteral *CL = dyn_cast(E)) return CL->getValue() == 0; Index: lib/CodeGen/CGExprConstant.cpp =================================================================== --- lib/CodeGen/CGExprConstant.cpp +++ 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(DestTy)) { @@ -1323,7 +1329,9 @@ // Convert to the appropriate type; this could be an lvalue for // an integer. - if (isa(DestTy)) { + if (auto PT = dyn_cast(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 elements(numElements); + auto CXXR = dyn_cast(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(I.getType()->castAs()->getDecl()); + const CXXRecordDecl *base = + cast(I.getType()->castAs()->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); + // 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(I.getType()->castAs()->getDecl()); @@ -1605,6 +1617,10 @@ } llvm::Constant *CodeGenModule::EmitNullConstant(QualType T) { + if (auto PT = T->getAs()) + return getNullPointer(cast(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()) { - const CXXRecordDecl *RD = cast(RT->getDecl()); - return ::EmitNullConstant(*this, RD, /*complete object*/ true); - } + if (const RecordType *RT = T->getAs()) + return ::EmitNullConstant(*this, RT->getDecl(), /*complete object*/ true); assert(T->isMemberDataPointerType() && "Should only see pointers to data members here!"); Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ 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(V->getType())); + Value *EmitPointerToBoolConversion(Value *V, QualType QT) { + Value *Zero = CGF.CGM.getNullPointer(cast(V->getType()), QT); + return Builder.CreateICmpNE(V, Zero, "tobool"); } @@ -591,7 +592,7 @@ return EmitIntToBoolConversion(Src); assert(isa(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(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( + 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(ConvertType(DestTy))); + return CGF.CGM.getNullPointer(cast(ConvertType(DestTy)), + DestTy); case CK_NullToMemberPointer: { if (MustVisitNullValue(E)) @@ -1518,9 +1531,21 @@ return Builder.CreateIntToPtr(IntResult, DestLLVMTy); } - case CK_PointerToIntegral: + case CK_PointerToIntegral: { + 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 an + // integer constant. In that case, a DCE pass should be able to + // eliminate the useless instructions emitted during translating E. + if (Result.HasSideEffects) + Visit(E); + return llvm::ConstantInt::get(ConvertType(DestTy), + CGF.getContext().getTargetNullPointerValue(E->getType())); + } assert(!DestTy->isBooleanType() && "bool should use PointerToBool"); return Builder.CreatePtrToInt(Visit(E), ConvertType(DestTy)); + } case CK_ToVoid: { CGF.EmitIgnoredExpr(E); @@ -1547,7 +1572,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: { Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ 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, Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ 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); Index: lib/CodeGen/CodeGenTypes.h =================================================================== --- lib/CodeGen/CodeGenTypes.h +++ 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); Index: lib/CodeGen/CodeGenTypes.cpp =================================================================== --- lib/CodeGen/CodeGenTypes.cpp +++ lib/CodeGen/CodeGenTypes.cpp @@ -736,10 +736,14 @@ return *Layout; } +bool CodeGenTypes::isPointerZeroInitializable(QualType T) { + assert (T->getAs() && "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()) + return Context.getTargetNullPointerValue(T) == 0; if (const auto *AT = Context.getAsArrayType(T)) { if (isa(AT)) @@ -753,7 +757,7 @@ // Records are non-zero-initializable if they contain any // non-zero-initializable subobjects. if (const RecordType *RT = T->getAs()) { - const CXXRecordDecl *RD = cast(RT->getDecl()); + auto RD = cast(RT->getDecl()); return isZeroInitializable(RD); } Index: lib/CodeGen/TargetInfo.h =================================================================== --- lib/CodeGen/TargetInfo.h +++ 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 Index: lib/CodeGen/TargetInfo.cpp =================================================================== --- lib/CodeGen/TargetInfo.cpp +++ 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. Index: test/CodeGenOpenCL/amdgpu-nullptr.cl =================================================================== --- /dev/null +++ test/CodeGenOpenCL/amdgpu-nullptr.cl @@ -0,0 +1,534 @@ +// RUN: %clang_cc1 %s -cl-std=CL2.0 -include opencl-c.h -triple amdgcn -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -O0 -cl-std=CL2.0 -include opencl-c.h -triple amdgcn -emit-llvm -o - | FileCheck --check-prefix=NOOPT %s + +typedef struct { + private char *p1; + local char *p2; + constant char *p3; + global char *p4; + generic char *p5; +} StructTy1; + +typedef struct { + constant char *p3; + global char *p4; + generic char *p5; +} StructTy2; + +// LLVM requests global variable with common linkage to be initialized with zeroinitializer, therefore use -fno-common +// to suppress common linkage for tentative definition. + +// Test 0 as initializer. + +// CHECK: @private_p = local_unnamed_addr addrspace(1) global i8* addrspacecast (i8 addrspace(4)* null to i8*), align 4 +private char *private_p = 0; + +// CHECK: @local_p = local_unnamed_addr addrspace(1) global i8 addrspace(3)* addrspacecast (i8 addrspace(4)* null to i8 addrspace(3)*), align 4 +local char *local_p = 0; + +// CHECK: @global_p = local_unnamed_addr addrspace(1) global i8 addrspace(1)* null, align 4 +global char *global_p = 0; + +// CHECK: @constant_p = local_unnamed_addr addrspace(1) global i8 addrspace(2)* null, align 4 +constant char *constant_p = 0; + +// CHECK: @generic_p = local_unnamed_addr addrspace(1) global i8 addrspace(4)* null, align 4 +generic char *generic_p = 0; + +// Test NULL as initializer. + +// CHECK: @private_p_NULL = local_unnamed_addr addrspace(1) global i8* addrspacecast (i8 addrspace(4)* null to i8*), align 4 +private char *private_p_NULL = NULL; + +// CHECK: @local_p_NULL = local_unnamed_addr addrspace(1) global i8 addrspace(3)* addrspacecast (i8 addrspace(4)* null to i8 addrspace(3)*), align 4 +local char *local_p_NULL = NULL; + +// CHECK: @global_p_NULL = local_unnamed_addr addrspace(1) global i8 addrspace(1)* null, align 4 +global char *global_p_NULL = NULL; + +// CHECK: @constant_p_NULL = local_unnamed_addr addrspace(1) global i8 addrspace(2)* null, align 4 +constant char *constant_p_NULL = NULL; + +// CHECK: @generic_p_NULL = local_unnamed_addr addrspace(1) global i8 addrspace(4)* null, align 4 +generic char *generic_p_NULL = NULL; + +// Test constant folding of null pointer. +// A null pointer should be folded to a null pointer in the target address space. + +// CHECK: @fold_generic = local_unnamed_addr addrspace(1) global i32 addrspace(4)* null, align 4 +generic int *fold_generic = (global int*)(generic float*)(private char*)0; + +// CHECK: @fold_priv = local_unnamed_addr addrspace(1) global i16* addrspacecast (i16 addrspace(4)* null to i16*), align 4 +private short *fold_priv = (private short*)(generic int*)(global void*)0; + +// CHECK: @fold_priv_arith = local_unnamed_addr addrspace(1) global i8* inttoptr (i32 9 to i8*), align 4 +private char *fold_priv_arith = (private char*)0 + 10; + +// CHECK: @fold_int = local_unnamed_addr addrspace(1) global i32 13, align 4 +int fold_int = (int)(private void*)(generic char*)(global int*)0 + 14; + +// CHECK: @fold_int2 = local_unnamed_addr addrspace(1) global i32 12, align 4 +int fold_int2 = (int) ((private void*)0 + 13); + +// CHECK: @fold_int3 = local_unnamed_addr addrspace(1) global i32 -1, align 4 +int fold_int3 = (int) ((private int*)0); + +// CHECK: @fold_int4 = local_unnamed_addr addrspace(1) global i32 7, align 4 +int fold_int4 = (int) &((private int*)0)[2]; + +// CHECK: @fold_int5 = local_unnamed_addr addrspace(1) global i32 3, align 4 +int fold_int5 = (int) &((private StructTy1*)0)->p2; + +// Test static variable initialization. + +// NOOPT: @test_static_var.sp1 = internal addrspace(1) global i8* addrspacecast (i8 addrspace(4)* null to i8*), align 4 +// NOOPT: @test_static_var.sp2 = internal addrspace(1) global i8* addrspacecast (i8 addrspace(4)* null to i8*), align 4 +// NOOPT: @test_static_var.sp3 = internal addrspace(1) global i8* addrspacecast (i8 addrspace(4)* null to i8*), align 4 +// NOOPT: @test_static_var.sp4 = internal addrspace(1) global i8* null, align 4 +// NOOPT: @test_static_var.sp5 = internal addrspace(1) global i8* null, align 4 +// NOOPT: @test_static_var.SS1 = internal addrspace(1) global %struct.StructTy1 { i8* addrspacecast (i8 addrspace(4)* null to i8*), i8 addrspace(3)* addrspacecast (i8 addrspace(4)* null to i8 addrspace(3)*), i8 addrspace(2)* null, i8 addrspace(1)* null, i8 addrspace(4)* null }, align 4 +// NOOPT: @test_static_var.SS2 = internal addrspace(1) global %struct.StructTy2 zeroinitializer, align 4 + +void test_static_var(void) { + static private char *sp1 = 0; + static private char *sp2 = NULL; + static private char *sp3; + static private char *sp4 = (private char*)((void)0, 0); + const int x = 0; + static private char *sp5 = (private char*)x; + static StructTy1 SS1; + static StructTy2 SS2; +} + +// Test function-scope variable initialization. +// NOOPT-LABEL: test_func_scope_var +// NOOPT: store i8* addrspacecast (i8 addrspace(4)* null to i8*), i8** %sp1, align 4 +// NOOPT: store i8* addrspacecast (i8 addrspace(4)* null to i8*), i8** %sp2, align 4 +// NOOPT: store i8* null, i8** %sp3, align 4 +// NOOPT: store i8* null, i8** %sp4, align 4 +// NOOPT: %[[SS1:.*]] = bitcast %struct.StructTy1* %SS1 to i8* +// NOOPT: call void @llvm.memcpy.p0i8.p2i8.i64(i8* %[[SS1]], i8 addrspace(2)* bitcast (%struct.StructTy1 addrspace(2)* @test_func_scope_var.SS1 to i8 addrspace(2)*), i64 32, i32 4, i1 false) +// NOOPT: %[[SS2:.*]] = bitcast %struct.StructTy2* %SS2 to i8* +// NOOPT: call void @llvm.memset.p0i8.i64(i8* %[[SS2]], i8 0, i64 24, i32 4, i1 false) + +void test_func_scope_var(void) { + private char *sp1 = 0; + private char *sp2 = NULL; + private char *sp3 = (private char*)((void)0, 0); + const int x = 0; + private char *sp4 = (private char*)x; + StructTy1 SS1 = {0, 0, 0, 0, 0}; + StructTy2 SS2 = {0, 0, 0}; +} + +// Test default initialization of pointers. + +// Tentative definition of global variables with non-zero initializer +// cannot have common linkage since common linkage requires zero initialization +// and does not have explicit section. + +// CHECK: @p1 = weak local_unnamed_addr addrspace(1) global i8* addrspacecast (i8 addrspace(4)* null to i8*), align 4 +private char *p1; + +// CHECK: @p2 = weak local_unnamed_addr addrspace(1) global i8 addrspace(3)* addrspacecast (i8 addrspace(4)* null to i8 addrspace(3)*), align 4 +local char *p2; + +// CHECK: @p3 = common local_unnamed_addr addrspace(1) global i8 addrspace(2)* null, align 4 +constant char *p3; + +// CHECK: @p4 = common local_unnamed_addr addrspace(1) global i8 addrspace(1)* null, align 4 +global char *p4; + +// CHECK: @p5 = common local_unnamed_addr addrspace(1) global i8 addrspace(4)* null, align 4 +generic char *p5; + +// Test default initialization of sturcture. + +// CHECK: @S1 = weak local_unnamed_addr addrspace(1) global %struct.StructTy1 { i8* addrspacecast (i8 addrspace(4)* null to i8*), i8 addrspace(3)* addrspacecast (i8 addrspace(4)* null to i8 addrspace(3)*), i8 addrspace(2)* null, i8 addrspace(1)* null, i8 addrspace(4)* null }, align 4 +StructTy1 S1; + +// CHECK: @S2 = common local_unnamed_addr addrspace(1) global %struct.StructTy2 zeroinitializer, align 4 +StructTy2 S2; + +// Test default initialization of array. +// CHECK: @A1 = weak local_unnamed_addr addrspace(1) global [2 x %struct.StructTy1] [%struct.StructTy1 { i8* addrspacecast (i8 addrspace(4)* null to i8*), i8 addrspace(3)* addrspacecast (i8 addrspace(4)* null to i8 addrspace(3)*), i8 addrspace(2)* null, i8 addrspace(1)* null, i8 addrspace(4)* null }, %struct.StructTy1 { i8* addrspacecast (i8 addrspace(4)* null to i8*), i8 addrspace(3)* addrspacecast (i8 addrspace(4)* null to i8 addrspace(3)*), i8 addrspace(2)* null, i8 addrspace(1)* null, i8 addrspace(4)* null }], align 4 +StructTy1 A1[2]; + +// CHECK: @A2 = common local_unnamed_addr addrspace(1) global [2 x %struct.StructTy2] zeroinitializer, align 4 +StructTy2 A2[2]; + +// Test comparison with 0. + +// CHECK-LABEL: cmp_private +// CHECK: icmp eq i8* %p, addrspacecast (i8 addrspace(4)* null to i8*) +void cmp_private(private char* p) { + if (p != 0) + *p = 0; +} + +// CHECK-LABEL: cmp_local +// CHECK: icmp eq i8 addrspace(3)* %p, addrspacecast (i8 addrspace(4)* null to i8 addrspace(3)*) +void cmp_local(local char* p) { + if (p != 0) + *p = 0; +} + +// CHECK-LABEL: cmp_global +// CHECK: icmp eq i8 addrspace(1)* %p, null +void cmp_global(global char* p) { + if (p != 0) + *p = 0; +} + +// CHECK-LABEL: cmp_constant +// CHECK: icmp eq i8 addrspace(2)* %p, null +char cmp_constant(constant char* p) { + if (p != 0) + return *p; + else + return 0; +} + +// CHECK-LABEL: cmp_generic +// CHECK: icmp eq i8 addrspace(4)* %p, null +void cmp_generic(generic char* p) { + if (p != 0) + *p = 0; +} + +// Test comparison with NULL. + +// CHECK-LABEL: cmp_NULL_private +// CHECK: icmp eq i8* %p, addrspacecast (i8 addrspace(4)* null to i8*) +void cmp_NULL_private(private char* p) { + if (p != NULL) + *p = 0; +} + +// CHECK-LABEL: cmp_NULL_local +// CHECK: icmp eq i8 addrspace(3)* %p, addrspacecast (i8 addrspace(4)* null to i8 addrspace(3)*) +void cmp_NULL_local(local char* p) { + if (p != NULL) + *p = 0; +} + +// CHECK-LABEL: cmp_NULL_global +// CHECK: icmp eq i8 addrspace(1)* %p, null +void cmp_NULL_global(global char* p) { + if (p != NULL) + *p = 0; +} + +// CHECK-LABEL: cmp_NULL_constant +// CHECK: icmp eq i8 addrspace(2)* %p, null +char cmp_NULL_constant(constant char* p) { + if (p != NULL) + return *p; + else + return 0; +} + +// CHECK-LABEL: cmp_NULL_generic +// CHECK: icmp eq i8 addrspace(4)* %p, null +void cmp_NULL_generic(generic char* p) { + if (p != NULL) + *p = 0; +} + +// Test storage 0 as null pointer. +// CHECK-LABEL: test_storage_null_pointer +// CHECK: store i8* addrspacecast (i8 addrspace(4)* null to i8*), i8* addrspace(4)* %arg_private +// CHECK: store i8 addrspace(3)* addrspacecast (i8 addrspace(4)* null to i8 addrspace(3)*), i8 addrspace(3)* addrspace(4)* %arg_local +// CHECK: store i8 addrspace(1)* null, i8 addrspace(1)* addrspace(4)* %arg_global +// CHECK: store i8 addrspace(2)* null, i8 addrspace(2)* addrspace(4)* %arg_constant +// CHECK: store i8 addrspace(4)* null, i8 addrspace(4)* addrspace(4)* %arg_generic +void test_storage_null_pointer(private char** arg_private, + local char** arg_local, + global char** arg_global, + constant char** arg_constant, + generic char** arg_generic) { + *arg_private = 0; + *arg_local = 0; + *arg_global = 0; + *arg_constant = 0; + *arg_generic = 0; +} + +// Test storage NULL as null pointer. +// CHECK-LABEL: test_storage_null_pointer_NULL +// CHECK: store i8* addrspacecast (i8 addrspace(4)* null to i8*), i8* addrspace(4)* %arg_private +// CHECK: store i8 addrspace(3)* addrspacecast (i8 addrspace(4)* null to i8 addrspace(3)*), i8 addrspace(3)* addrspace(4)* %arg_local +// CHECK: store i8 addrspace(1)* null, i8 addrspace(1)* addrspace(4)* %arg_global +// CHECK: store i8 addrspace(2)* null, i8 addrspace(2)* addrspace(4)* %arg_constant +// CHECK: store i8 addrspace(4)* null, i8 addrspace(4)* addrspace(4)* %arg_generic +void test_storage_null_pointer_NULL(private char** arg_private, + local char** arg_local, + global char** arg_global, + constant char** arg_constant, + generic char** arg_generic) { + *arg_private = NULL; + *arg_local = NULL; + *arg_global = NULL; + *arg_constant = NULL; + *arg_generic = NULL; +} + +// Test pass null pointer to function as argument. +void test_pass_null_pointer_arg_calee(private char* arg_private, + local char* arg_local, + global char* arg_global, + constant char* arg_constant, + generic char* arg_generic); + +// CHECK-LABEL: test_pass_null_pointer_arg +// CHECK: call void @test_pass_null_pointer_arg_calee(i8* addrspacecast (i8 addrspace(4)* null to i8*), i8 addrspace(3)* addrspacecast (i8 addrspace(4)* null to i8 addrspace(3)*), i8 addrspace(1)* null, i8 addrspace(2)* null, i8 addrspace(4)* null) +// CHECK: call void @test_pass_null_pointer_arg_calee(i8* addrspacecast (i8 addrspace(4)* null to i8*), i8 addrspace(3)* addrspacecast (i8 addrspace(4)* null to i8 addrspace(3)*), i8 addrspace(1)* null, i8 addrspace(2)* null, i8 addrspace(4)* null) +void test_pass_null_pointer_arg(void) { + test_pass_null_pointer_arg_calee(0, 0, 0, 0, 0); + test_pass_null_pointer_arg_calee(NULL, NULL, NULL, NULL, NULL); +} + +// Test cast null pointer to size_t. +void test_cast_null_pointer_to_sizet_calee(size_t arg_private, + size_t arg_local, + size_t arg_global, + size_t arg_constant, + size_t arg_generic); + +// CHECK-LABEL: test_cast_null_pointer_to_sizet +// CHECK: call void @test_cast_null_pointer_to_sizet_calee(i64 -1, i64 -1, i64 0, i64 0, i64 0) +// CHeCK: call void @test_cast_null_pointer_to_sizet_calee(i64 -1, i64 -1, i64 0, i64 0, i64 0) +void test_cast_null_pointer_to_sizet(void) { + test_cast_null_pointer_to_sizet_calee((size_t)((private char*)0), + (size_t)((local char*)0), + (size_t)((global char*)0), + (size_t)((constant char*)0), + (size_t)((generic char*)0)); + test_cast_null_pointer_to_sizet_calee((size_t)((private char*)NULL), + (size_t)((local char*)NULL), + (size_t)((global char*)NULL), + (size_t)((constant char*)0), // NULL cannot be casted to constant pointer since it is defined as a generic pointer + (size_t)((generic char*)NULL)); +} + +// Test comparision between null pointers. +#define TEST_EQ00(addr1, addr2) int test_eq00_##addr1##_##addr2(void) { return (addr1 char*)0 == (addr2 char*)0; } +#define TEST_EQ0N(addr1, addr2) int test_eq0N_##addr1##_##addr2(void) { return (addr1 char*)0 == (addr2 char*)NULL; } +#define TEST_EQN0(addr1, addr2) int test_eqN0_##addr1##_##addr2(void) { return (addr1 char*)NULL == (addr2 char*)0; } +#define TEST_EQNN(addr1, addr2) int test_eqNN_##addr1##_##addr2(void) { return (addr1 char*)0 == (addr2 char*)NULL; } +#define TEST_NE00(addr1, addr2) int test_ne00_##addr1##_##addr2(void) { return (addr1 char*)0 != (addr2 char*)0; } +#define TEST_NE0N(addr1, addr2) int test_ne0N_##addr1##_##addr2(void) { return (addr1 char*)0 != (addr2 char*)NULL; } +#define TEST_NEN0(addr1, addr2) int test_neN0_##addr1##_##addr2(void) { return (addr1 char*)NULL != (addr2 char*)0; } +#define TEST_NENN(addr1, addr2) int test_neNN_##addr1##_##addr2(void) { return (addr1 char*)0 != (addr2 char*)NULL; } +#define TEST(addr1, addr2) \ + TEST_EQ00(addr1, addr2) \ + TEST_EQ0N(addr1, addr2) \ + TEST_EQN0(addr1, addr2) \ + TEST_EQNN(addr1, addr2) \ + TEST_NE00(addr1, addr2) \ + TEST_NE0N(addr1, addr2) \ + TEST_NEN0(addr1, addr2) \ + TEST_NENN(addr1, addr2) + +// CHECK-LABEL: test_eq00_generic_private +// CHECK: ret i32 1 +// CHECK-LABEL: test_eq0N_generic_private +// CHECK: ret i32 1 +// CHECK-LABEL: test_eqN0_generic_private +// CHECK: ret i32 1 +// CHECK-LABEL: test_eqNN_generic_private +// CHECK: ret i32 1 +// CHECK-LABEL: test_ne00_generic_private +// CHECK: ret i32 0 +// CHECK-LABEL: test_ne0N_generic_private +// CHECK: ret i32 0 +// CHECK-LABEL: test_neN0_generic_private +// CHECK: ret i32 0 +// CHECK-LABEL: test_neNN_generic_private +// CHECK: ret i32 0 +TEST(generic, private) + +// CHECK-LABEL: test_eq00_generic_local +// CHECK: ret i32 1 +// CHECK-LABEL: test_eq0N_generic_local +// CHECK: ret i32 1 +// CHECK-LABEL: test_eqN0_generic_local +// CHECK: ret i32 1 +// CHECK-LABEL: test_eqNN_generic_local +// CHECK: ret i32 1 +// CHECK-LABEL: test_ne00_generic_local +// CHECK: ret i32 0 +// CHECK-LABEL: test_ne0N_generic_local +// CHECK: ret i32 0 +// CHECK-LABEL: test_neN0_generic_local +// CHECK: ret i32 0 +// CHECK-LABEL: test_neNN_generic_local +// CHECK: ret i32 0 +TEST(generic, local) + +// CHECK-LABEL: test_eq00_generic_global +// CHECK: ret i32 1 +// CHECK-LABEL: test_eq0N_generic_global +// CHECK: ret i32 1 +// CHECK-LABEL: test_eqN0_generic_global +// CHECK: ret i32 1 +// CHECK-LABEL: test_eqNN_generic_global +// CHECK: ret i32 1 +// CHECK-LABEL: test_ne00_generic_global +// CHECK: ret i32 0 +// CHECK-LABEL: test_ne0N_generic_global +// CHECK: ret i32 0 +// CHECK-LABEL: test_neN0_generic_global +// CHECK: ret i32 0 +// CHECK-LABEL: test_neNN_generic_global +// CHECK: ret i32 0 +TEST(generic, global) + +// CHECK-LABEL: test_eq00_generic_generic +// CHECK: ret i32 1 +// CHECK-LABEL: test_eq0N_generic_generic +// CHECK: ret i32 1 +// CHECK-LABEL: test_eqN0_generic_generic +// CHECK: ret i32 1 +// CHECK-LABEL: test_eqNN_generic_generic +// CHECK: ret i32 1 +// CHECK-LABEL: test_ne00_generic_generic +// CHECK: ret i32 0 +// CHECK-LABEL: test_ne0N_generic_generic +// CHECK: ret i32 0 +// CHECK-LABEL: test_neN0_generic_generic +// CHECK: ret i32 0 +// CHECK-LABEL: test_neNN_generic_generic +// CHECK: ret i32 0 +TEST(generic, generic) + +// CHECK-LABEL: test_eq00_constant_constant +// CHECK: ret i32 1 +TEST_EQ00(constant, constant) + +// Test cast to bool. + +// CHECK-LABEL: cast_bool_private +// CHECK: icmp eq i8* %p, addrspacecast (i8 addrspace(4)* null to i8*) +void cast_bool_private(private char* p) { + if (p) + *p = 0; +} + +// CHECK-LABEL: cast_bool_local +// CHECK: icmp eq i8 addrspace(3)* %p, addrspacecast (i8 addrspace(4)* null to i8 addrspace(3)*) +void cast_bool_local(local char* p) { + if (p) + *p = 0; +} + +// CHECK-LABEL: cast_bool_global +// CHECK: icmp eq i8 addrspace(1)* %p, null +void cast_bool_global(global char* p) { + if (p) + *p = 0; +} + +// CHECK-LABEL: cast_bool_constant +// CHECK: icmp eq i8 addrspace(2)* %p, null +char cast_bool_constant(constant char* p) { + if (p) + return *p; + else + return 0; +} + +// CHECK-LABEL: cast_bool_generic +// CHECK: icmp eq i8 addrspace(4)* %p, null +void cast_bool_generic(generic char* p) { + if (p) + *p = 0; +} + +// Test initialize a struct using memset. +// For large structures which is mostly zero, clang generats llvm.memset for +// the zero part and store for non-zero members. +typedef struct { + long a, b, c, d; + private char *p; +} StructTy3; + +// CHECK-LABEL: test_memset +// CHECK: call void @llvm.memset.p0i8.i64(i8* {{.*}}, i8 0, i64 32, i32 8, i1 false) +// CHECK: store i8* addrspacecast (i8 addrspace(4)* null to i8*), i8** {{.*}} +StructTy3 test_memset(void) { + StructTy3 S3 = {0, 0, 0, 0, 0}; + return S3; +} + +// Test casting literal 0 to pointer. +// A 0 literal casted to pointer should become a null pointer. + +// CHECK-LABEL: test_cast_0_to_ptr +// CHECK: ret i32* addrspacecast (i32 addrspace(4)* null to i32*) +private int* test_cast_0_to_ptr(void) { + return (private int*)0; +} + +// Test casting non-literal integer with 0 value to pointer. +// A non-literal integer expression with 0 value is casted to a pointer with +// zero value. + +// CHECK-LABEL: test_cast_int_to_ptr1 +// CHECK: ret i32* null +private int* test_cast_int_to_ptr1(void) { + return (private int*)((void)0, 0); +} + +// CHECK-LABEL: test_cast_int_to_ptr2 +// CHECK: ret i32* null +private int* test_cast_int_to_ptr2(void) { + int x = 0; + return (private int*)x; +} + +// Test logical operations. +// CHECK-LABEL: test_not_nullptr +// CHECK: ret i32 1 +int test_not_nullptr(void) { + return !(private char*)NULL; +} + +// CHECK-LABEL: test_and_nullptr +// CHECK: ret i32 0 +int test_and_nullptr(int a) { + return a && ((private char*)NULL); +} + +// CHECK-LABEL: test_not_ptr +// CHECK: %[[lnot:.*]] = icmp eq i8* %p, addrspacecast (i8 addrspace(4)* null to i8*) +// CHECK: %[[lnot_ext:.*]] = zext i1 %[[lnot]] to i32 +// CHECK: ret i32 %[[lnot_ext]] +int test_not_ptr(private char* p) { + return !p; +} +// CHECK-LABEL: test_and_ptr +// CHECK: %[[tobool:.*]] = icmp ne i8* %p1, addrspacecast (i8 addrspace(4)* null to i8*) +// CHECK: %[[tobool1:.*]] = icmp ne i8 addrspace(3)* %p2, addrspacecast (i8 addrspace(4)* null to i8 addrspace(3)*) +// CHECK: %[[res:.*]] = and i1 %[[tobool]], %[[tobool1]] +// CHECK: %[[land_ext:.*]] = zext i1 %[[res]] to i32 +// CHECK: ret i32 %[[land_ext]] +int test_and_ptr(private char* p1, local char* p2) { + return p1 && p2; +} + +// Test folding of null pointer in function scope. +// NOOPT-LABEL: test_fold +// NOOPT: call void @test_fold_callee +// NOOPT: store i32 addrspace(1)* null, i32 addrspace(1)** %glob, align 4 +// NOOPT: %{{.*}} = sub i64 %{{.*}}, 0 +// NOOPT: call void @test_fold_callee +// NOOPT: %{{.*}} = add nsw i64 %{{.*}}, -1 +// NOOPT: %{{.*}} = sub nsw i64 %{{.*}}, 1 +void test_fold_callee(void); +void test_fold(void) { + global int* glob = (test_fold_callee(), (global int*)(generic char*)0); + long x = glob - (global int*)(generic char*)0; + x = x + (int)(test_fold_callee(), (private int*)(generic char*)(global short*)0); + x = x - (int)((private int*)0 == (private int*)(generic char*)0); +}