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::translateNullPtr(llvm::Constant *C) { + return getTargetCodeGenInfo().translateNullPtr(*this, C); +} + llvm::Constant *CodeGenModule::EmitConstantValue(const APValue &Value, QualType DestType, CodeGenFunction *CGF) { @@ -1329,7 +1334,8 @@ C = llvm::ConstantExpr::getIntegerCast( C, getDataLayout().getIntPtrType(DestTy), /*isSigned=*/false); - return llvm::ConstantExpr::getIntToPtr(C, DestTy); + C = llvm::ConstantExpr::getIntToPtr(C, DestTy); + return translateNullPtr(C); } // If the types don't match this should only be a truncate. Index: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ lib/CodeGen/CGExprScalar.cpp @@ -1449,12 +1449,13 @@ case CK_FunctionToPointerDecay: return EmitLValue(E).getPointer(); - case CK_NullToPointer: + case CK_NullToPointer: { if (MustVisitNullValue(E)) (void) Visit(E); - return llvm::ConstantPointerNull::get( - cast(ConvertType(DestTy))); + auto PT = cast(ConvertType(DestTy)); + return CGF.CGM.translateNullPtr(llvm::ConstantPointerNull::get(PT)); + } case CK_NullToMemberPointer: { if (MustVisitNullValue(E)) Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -1152,6 +1152,9 @@ llvm::Value * createOpenCLIntToSamplerConversion(const Expr *E, CodeGenFunction &CGF); + /// Perform target specific translation on null pointer. + llvm::Constant *translateNullPtr(llvm::Constant *C); + private: llvm::Constant * GetOrCreateLLVMFunction(StringRef MangledName, llvm::Type *Ty, GlobalDecl D, Index: lib/CodeGen/TargetInfo.h =================================================================== --- lib/CodeGen/TargetInfo.h +++ lib/CodeGen/TargetInfo.h @@ -220,6 +220,13 @@ /// Get LLVM calling convention for OpenCL kernel. virtual unsigned getOpenCLKernelCallingConv() const; + + /// Translate null pointer to target specific value. + virtual llvm::Constant *translateNullPtr(const CodeGen::CodeGenModule &CGM, + llvm::Constant *C) const { + return C; + } + }; } // namespace CodeGen Index: lib/CodeGen/TargetInfo.cpp =================================================================== --- lib/CodeGen/TargetInfo.cpp +++ lib/CodeGen/TargetInfo.cpp @@ -6953,6 +6953,9 @@ void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &M) const override; unsigned getOpenCLKernelCallingConv() const override; + + llvm::Constant *translateNullPtr(const CodeGen::CodeGenModule &CGM, + llvm::Constant *C) const override; }; } @@ -7018,6 +7021,30 @@ return llvm::CallingConv::AMDGPU_KERNEL; } +// 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 -1. 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::translateNullPtr( + const CodeGen::CodeGenModule &CGM, llvm::Constant *C) const { + if (!isa(C)) + return C; + auto PT = cast(C->getType()); + auto &Ctx = CGM.getContext(); + auto AS = PT->getAddressSpace(); + if (CGM.getTarget().getTriple().getArch() != llvm::Triple::amdgcn || + (AS != Ctx.getTargetAddressSpace(LangAS::opencl_local) && AS != 0)) + return C; + + 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,49 @@ +// RUN: %clang_cc1 %s -cl-std=CL2.0 -triple amdgcn -emit-llvm -o - | FileCheck %s + +// 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; + +// CHECK: icmp eq i8* %p, addrspacecast (i8 addrspace(4)* null to i8*) +void cmp_private(private char* p) { + if (p != 0) + *p = 0; +} + +// 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: icmp eq i8 addrspace(1)* %p, null +void cmp_global(global char* p) { + if (p != 0) + *p = 0; +} + +// CHECK: icmp eq i8 addrspace(2)* %p, null +char cmp_constant(constant char* p) { + if (p != 0) + return *p; + else + return 0; +} + +// CHECK: icmp eq i8 addrspace(4)* %p, null +void cmp_generic(generic char* p) { + if (p != 0) + *p = 0; +} +