From 77ebcbe8edcf3f5190a27a778c8aecaeb68b209e Mon Sep 17 00:00:00 2001 From: Anastasia Stulova Date: Wed, 26 Nov 2014 15:36:41 +0000 Subject: [PATCH] [OpenCL] Implemented restrictions for pointer conversions specified in OpenCL v2.0. OpenCL v2.0 s6.5.5 restricts conversion of pointers to different address spaces: - the named address spaces (__global, __local, and __private) => __generic - implicitly converted; - __generic => named - with an explicit cast; - named <=> named - disallowed; - __constant <=> any other - disallowed. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@222834 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Type.h | 54 ++++- include/clang/Basic/DiagnosticSemaKinds.td | 4 + lib/AST/ExprConstant.cpp | 1 + lib/CodeGen/CGExprScalar.cpp | 4 +- lib/Sema/SemaCast.cpp | 4 +- lib/Sema/SemaExpr.cpp | 26 +- .../address-spaces-conversions.cl | 22 ++ .../address-spaces-conversions-cl2.0.cl | 227 ++++++++++++++++++ 8 files changed, 325 insertions(+), 17 deletions(-) create mode 100644 test/CodeGenOpenCL/address-spaces-conversions.cl create mode 100644 test/SemaOpenCL/address-spaces-conversions-cl2.0.cl diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 711ce0c5af..9c3af0781f 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -16,6 +16,7 @@ #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/TemplateName.h" +#include "clang/Basic/AddressSpaces.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/ExceptionSpecificationType.h" #include "clang/Basic/LLVM.h" @@ -400,21 +401,36 @@ public: Mask |= qs.Mask; } + /// \brief Returns true if this address space is a superset of the other one. + /// OpenCL v2.0 defines conversion rules (OpenCLC v2.0 s6.5.5) and notion of + /// overlapping address spaces. + /// CL1.1 or CL1.2: + /// every address space is a superset of itself. + /// CL2.0 adds: + /// __generic is a superset of any address space except for __constant. + bool isAddressSpaceSupersetOf(Qualifiers other) const { + return + // Address spaces must match exactly. + getAddressSpace() == other.getAddressSpace() || + // Otherwise in OpenCLC v2.0 s6.5.5: every address space except + // for __constant can be used as __generic. + (getAddressSpace() == LangAS::opencl_generic && + other.getAddressSpace() != LangAS::opencl_constant); + } + /// \brief Determines if these qualifiers compatibly include another set. /// Generally this answers the question of whether an object with the other /// qualifiers can be safely used as an object with these qualifiers. bool compatiblyIncludes(Qualifiers other) const { - return - // Address spaces must match exactly. - getAddressSpace() == other.getAddressSpace() && - // ObjC GC qualifiers can match, be added, or be removed, but can't be - // changed. - (getObjCGCAttr() == other.getObjCGCAttr() || - !hasObjCGCAttr() || !other.hasObjCGCAttr()) && - // ObjC lifetime qualifiers must match exactly. - getObjCLifetime() == other.getObjCLifetime() && - // CVR qualifiers may subset. - (((Mask & CVRMask) | (other.Mask & CVRMask)) == (Mask & CVRMask)); + return isAddressSpaceSupersetOf(other) && + // ObjC GC qualifiers can match, be added, or be removed, but can't + // be changed. + (getObjCGCAttr() == other.getObjCGCAttr() || !hasObjCGCAttr() || + !other.hasObjCGCAttr()) && + // ObjC lifetime qualifiers must match exactly. + getObjCLifetime() == other.getObjCLifetime() && + // CVR qualifiers may subset. + (((Mask & CVRMask) | (other.Mask & CVRMask)) == (Mask & CVRMask)); } /// \brief Determines if these qualifiers compatibly include another set of @@ -1988,6 +2004,22 @@ public: QualType getPointeeType() const { return PointeeType; } + /// \brief Returns true if address spaces of pointers overlap. + /// OpenCL v2.0 defines conversion rules for pointers to different + /// address spaces (OpenCLC v2.0 s6.5.5) and notion of overlapping + /// address spaces. + /// CL1.1 or CL1.2: + /// address spaces overlap iff they are they same. + /// CL2.0 adds: + /// __generic overlaps with any address space except for __constant. + bool isAddressSpaceOverlapping(const PointerType &other) const { + Qualifiers thisQuals = PointeeType.getQualifiers(); + Qualifiers otherQuals = other.getPointeeType().getQualifiers(); + // Address spaces overlap if at least one of them is a superset of another + return thisQuals.isAddressSpaceSupersetOf(otherQuals) || + otherQuals.isAddressSpaceSupersetOf(thisQuals); + } + bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 25441cd28e..901ba9535c 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -4970,6 +4970,10 @@ def err_typecheck_comparison_of_distinct_pointers : Error< def ext_typecheck_comparison_of_distinct_pointers_nonstandard : ExtWarn< "comparison of distinct pointer types (%0 and %1) uses non-standard " "composite pointer type %2">, InGroup; +def err_typecheck_op_on_nonoverlapping_address_space_pointers : Error< + "%select{comparison between %diff{ ($ and $)|}0,1" + "|arithmetic operation with operands of type %diff{ ($ and $)|}0,1}2" + " which are pointers to non-overlapping address spaces">; def err_typecheck_assign_const : Error<"read-only variable is not assignable">; def err_stmtexpr_file_scope : Error< "statement expression not allowed at file scope">; diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 9a2c552795..28913385f0 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -4829,6 +4829,7 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr* E) { case CK_CPointerToObjCPointerCast: case CK_BlockPointerToObjCPointerCast: case CK_AnyPointerToBlockPointerCast: + case CK_AddressSpaceConversion: if (!Visit(SubExpr)) return false; // Bitcasts to cv void* are static_casts, not reinterpret_casts, so are diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index 19d453d3ca..a9e63dc7f7 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -1351,8 +1351,8 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { llvm::Type *DstTy = ConvertType(DestTy); if (SrcTy->isPtrOrPtrVectorTy() && DstTy->isPtrOrPtrVectorTy() && SrcTy->getPointerAddressSpace() != DstTy->getPointerAddressSpace()) { - llvm::Type *MidTy = CGF.CGM.getDataLayout().getIntPtrType(SrcTy); - return Builder.CreateIntToPtr(Builder.CreatePtrToInt(Src, MidTy), DstTy); + llvm_unreachable("wrong cast for pointers in different address spaces" + "(must be an address space cast)!"); } return Builder.CreateBitCast(Src, DstTy); } diff --git a/lib/Sema/SemaCast.cpp b/lib/Sema/SemaCast.cpp index 00d0775be4..d38db87fa5 100644 --- a/lib/Sema/SemaCast.cpp +++ b/lib/Sema/SemaCast.cpp @@ -2212,8 +2212,8 @@ void CastOperation::CheckCStyleCast() { // address space B is illegal. if (Self.getLangOpts().OpenCL && DestType->isPointerType() && SrcType->isPointerType()) { - if (DestType->getPointeeType().getAddressSpace() != - SrcType->getPointeeType().getAddressSpace()) { + const PointerType *DestPtr = DestType->getAs(); + if (!DestPtr->isAddressSpaceOverlapping(*SrcType->getAs())) { Self.Diag(OpRange.getBegin(), diag::err_typecheck_incompatible_address_space) << SrcType << DestType << Sema::AA_Casting diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index ae299c3c98..76e3612327 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -6256,7 +6256,7 @@ checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType) { if (!lhq.compatiblyIncludes(rhq)) { // Treat address-space mismatches as fatal. TODO: address subspaces - if (lhq.getAddressSpace() != rhq.getAddressSpace()) + if (!lhq.isAddressSpaceSupersetOf(rhq)) ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; // It's okay to add or remove GC or lifetime qualifiers when converting to @@ -6541,7 +6541,9 @@ Sema::CheckAssignmentConstraints(QualType LHSType, ExprResult &RHS, if (const PointerType *LHSPointer = dyn_cast(LHSType)) { // U* -> T* if (isa(RHSType)) { - Kind = CK_BitCast; + unsigned AddrSpaceL = LHSPointer->getPointeeType().getAddressSpace(); + unsigned AddrSpaceR = RHSType->getPointeeType().getAddressSpace(); + Kind = AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion : CK_BitCast; return checkPointerTypesForAssignment(*this, LHSType, RHSType); } @@ -7224,6 +7226,19 @@ static bool checkArithmeticBinOpPointerOperands(Sema &S, SourceLocation Loc, if (isLHSPointer) LHSPointeeTy = LHSExpr->getType()->getPointeeType(); if (isRHSPointer) RHSPointeeTy = RHSExpr->getType()->getPointeeType(); + // if both are pointers check if operation is valid wrt address spaces + if (isLHSPointer && isRHSPointer) { + const PointerType *lhsPtr = LHSExpr->getType()->getAs(); + const PointerType *rhsPtr = RHSExpr->getType()->getAs(); + if (!lhsPtr->isAddressSpaceOverlapping(*rhsPtr)) { + S.Diag(Loc, + diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) + << LHSExpr->getType() << RHSExpr->getType() << 1 /*arithmetic op*/ + << LHSExpr->getSourceRange() << RHSExpr->getSourceRange(); + return false; + } + } + // Check for arithmetic on pointers to incomplete types. bool isLHSVoidPtr = isLHSPointer && LHSPointeeTy->isVoidType(); bool isRHSVoidPtr = isRHSPointer && RHSPointeeTy->isVoidType(); @@ -8152,6 +8167,13 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, diagnoseDistinctPointerComparison(*this, Loc, LHS, RHS, /*isError*/false); } if (LCanPointeeTy != RCanPointeeTy) { + const PointerType *lhsPtr = LHSType->getAs(); + if (!lhsPtr->isAddressSpaceOverlapping(*RHSType->getAs())) { + Diag(Loc, + diag::err_typecheck_op_on_nonoverlapping_address_space_pointers) + << LHSType << RHSType << 0 /* comparison */ + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + } unsigned AddrSpaceL = LCanPointeeTy.getAddressSpace(); unsigned AddrSpaceR = RCanPointeeTy.getAddressSpace(); CastKind Kind = AddrSpaceL != AddrSpaceR ? CK_AddressSpaceConversion diff --git a/test/CodeGenOpenCL/address-spaces-conversions.cl b/test/CodeGenOpenCL/address-spaces-conversions.cl new file mode 100644 index 0000000000..b30be8d904 --- /dev/null +++ b/test/CodeGenOpenCL/address-spaces-conversions.cl @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 %s -O0 -ffake-address-space-map -cl-std=CL2.0 -emit-llvm -o - | FileCheck %s + +// test that we generate address space casts everywhere we need conversions of +// pointers to different address spaces + +void test(global int *arg_glob, generic int *arg_gen) { + int var_priv; + arg_gen = arg_glob; // implicit cast global -> generic + // CHECK: %{{[0-9]+}} = addrspacecast i32 addrspace(1)* %{{[0-9]+}} to i32 addrspace(4)* + arg_gen = &var_priv; // implicit cast with obtaining adr, private -> generic + // CHECK: %{{[0-9]+}} = addrspacecast i32* %var_priv to i32 addrspace(4)* + arg_glob = (global int *)arg_gen; // explicit cast + // CHECK: %{{[0-9]+}} = addrspacecast i32 addrspace(4)* %{{[0-9]+}} to i32 addrspace(1)* + global int *var_glob = + (global int *)arg_glob; // explicit cast in the same address space + // CHECK-NOT: %{{[0-9]+}} = addrspacecast i32 addrspace(1)* %{{[0-9]+}} to i32 addrspace(1)* + var_priv = arg_gen - arg_glob; // arithmetic operation + // CHECK: %sub.ptr.lhs.cast = ptrtoint i32 addrspace(4)* %6 to i64 + // CHECK: %sub.ptr.rhs.cast = ptrtoint i32 addrspace(1)* %7 to i64 + var_priv = arg_gen > arg_glob; // comparison + // CHECK: %{{[0-9]+}} = addrspacecast i32 addrspace(1)* %{{[0-9]+}} to i32 addrspace(4)* +} diff --git a/test/SemaOpenCL/address-spaces-conversions-cl2.0.cl b/test/SemaOpenCL/address-spaces-conversions-cl2.0.cl new file mode 100644 index 0000000000..50363f23a9 --- /dev/null +++ b/test/SemaOpenCL/address-spaces-conversions-cl2.0.cl @@ -0,0 +1,227 @@ +// RUN: %clang_cc1 %s -ffake-address-space-map -verify -pedantic -fsyntax-only -DCONSTANT -cl-std=CL2.0 +// RUN: %clang_cc1 %s -ffake-address-space-map -verify -pedantic -fsyntax-only -DGLOBAL -cl-std=CL2.0 +// RUN: %clang_cc1 %s -ffake-address-space-map -verify -pedantic -fsyntax-only -DGENERIC -cl-std=CL2.0 + +/* OpenCLC v2.0 adds a set of restrictions for conversions between pointers to +* different address spaces, mainly described in Sections 6.5.5 and 6.5.6. +* +* It adds notion of overlapping address spaces. The main differention is that +* an unnamed address space is added, called '__generic'. Pointers to the +* generic address space can be interchangabley used with pointers to any +* other address space except for __constant address space (Section 6.5.5). +* +* Based on this there are 3 sets of tests: __generic, named (__global in this +* case), and __constant, that should cover all program paths for CL address +* space conversions used in initialisations, assignments, casts, comparisons +* and arithmetic operations. +*/ + +#ifdef GENERIC +#define AS generic +#endif + +#ifdef GLOBAL +#define AS global +#endif + +#ifdef CONSTANT +#define AS constant +#endif + +void f_glob(global int *arg_glob) {} +#ifndef GLOBAL +// expected-note@-2{{passing argument to parameter 'arg_glob' here}} +#endif + +void f_loc(local int *arg_loc) { +} // expected-note@-1{{passing argument to parameter 'arg_loc' here}} + +void f_const(constant int *arg_const) {} +#ifndef CONSTANT +// expected-note@-2{{passing argument to parameter 'arg_const' here}} +#endif + +void f_priv(private int *arg_priv) { +} // expected-note@-1{{passing argument to parameter 'arg_priv' here}} + +void f_gen(generic int *arg_gen) {} +#ifdef CONSTANT +// expected-note@-2{{passing argument to parameter 'arg_gen' here}} +#endif + +void test_conversion(global int *arg_glob, local int *arg_loc, + constant int *arg_const, private int *arg_priv, + generic int *arg_gen) { + + AS int *var_init1 = arg_glob; +#ifdef CONSTANT +// expected-error@-2{{initializing '__constant int *' with an expression of type '__global int *' changes address space of pointer}} +#endif + + AS int *var_init2 = arg_loc; +#ifndef GENERIC +// expected-error-re@-2{{initializing '__{{global|constant}} int *' with an expression of type '__local int *' changes address space of pointer}} +#endif + + AS int *var_init3 = arg_const; +#ifndef CONSTANT +// expected-error-re@-2{{initializing '__{{global|generic}} int *' with an expression of type '__constant int *' changes address space of pointer}} +#endif + + AS int *var_init4 = arg_priv; +#ifndef GENERIC +// expected-error-re@-2{{initializing '__{{global|constant}} int *' with an expression of type 'int *' changes address space of pointer}} +#endif + + AS int *var_init5 = arg_gen; +#ifndef GENERIC +// expected-error-re@-2{{initializing '__{{global|constant}} int *' with an expression of type '__generic int *' changes address space of pointer}} +#endif + + AS int *var_cast1 = (AS int *)arg_glob; +#ifdef CONSTANT +// expected-error@-2{{casting '__global int *' to type '__constant int *' changes address space of pointer}} +#endif + + AS int *var_cast2 = (AS int *)arg_loc; +#ifndef GENERIC +// expected-error-re@-2{{casting '__local int *' to type '__{{global|constant}} int *' changes address space of pointer}} +#endif + + AS int *var_cast3 = (AS int *)arg_const; +#ifndef CONSTANT +// expected-error-re@-2{{casting '__constant int *' to type '__{{global|generic}} int *' changes address space of pointer}} +#endif + + AS int *var_cast4 = (AS int *)arg_priv; +#ifndef GENERIC +// expected-error-re@-2{{casting 'int *' to type '__{{global|constant}} int *' changes address space of pointer}} +#endif + + AS int *var_cast5 = (AS int *)arg_gen; +#ifdef CONSTANT +// expected-error@-2{{casting '__generic int *' to type '__constant int *' changes address space of pointer}} +#endif + + AS int *var_impl; + var_impl = arg_glob; +#ifdef CONSTANT +// expected-error@-2{{assigning '__global int *' to '__constant int *' changes address space of pointer}} +#endif + + var_impl = arg_loc; +#ifndef GENERIC +// expected-error-re@-2{{assigning '__local int *' to '__{{global|constant}} int *' changes address space of pointer}} +#endif + + var_impl = arg_const; +#ifndef CONSTANT +// expected-error-re@-2{{assigning '__constant int *' to '__{{global|generic}} int *' changes address space of pointer}} +#endif + + var_impl = arg_priv; +#ifndef GENERIC +// expected-error-re@-2{{assigning 'int *' to '__{{global|constant}} int *' changes address space of pointer}} +#endif + + var_impl = arg_gen; +#ifndef GENERIC +// expected-error-re@-2{{assigning '__generic int *' to '__{{global|constant}} int *' changes address space of pointer}} +#endif + + var_cast1 = (AS int *)arg_glob; +#ifdef CONSTANT +// expected-error@-2{{casting '__global int *' to type '__constant int *' changes address space of pointer}} +#endif + + var_cast2 = (AS int *)arg_loc; +#ifndef GENERIC +// expected-error-re@-2{{casting '__local int *' to type '__{{global|constant}} int *' changes address space of pointer}} +#endif + + var_cast3 = (AS int *)arg_const; +#ifndef CONSTANT +// expected-error-re@-2{{casting '__constant int *' to type '__{{global|generic}} int *' changes address space of pointer}} +#endif + + var_cast4 = (AS int *)arg_priv; +#ifndef GENERIC +// expected-error-re@-2{{casting 'int *' to type '__{{global|constant}} int *' changes address space of pointer}} +#endif + + var_cast5 = (AS int *)arg_gen; +#ifdef CONSTANT +// expected-error@-2{{casting '__generic int *' to type '__constant int *' changes address space of pointer}} +#endif + + AS int *var_cmp; + int b = var_cmp != arg_glob; +#ifdef CONSTANT +// expected-error@-2{{comparison between ('__constant int *' and '__global int *') which are pointers to non-overlapping address spaces}} +#endif + + b = var_cmp != arg_loc; +#ifndef GENERIC +// expected-error-re@-2{{comparison between ('__{{global|constant}} int *' and '__local int *') which are pointers to non-overlapping address spaces}} +#endif + + b = var_cmp == arg_const; +#ifndef CONSTANT +// expected-error-re@-2{{comparison between ('__{{global|generic}} int *' and '__constant int *') which are pointers to non-overlapping address spaces}} +#endif + + b = var_cmp <= arg_priv; +#ifndef GENERIC +// expected-error-re@-2{{comparison between ('__{{global|constant}} int *' and 'int *') which are pointers to non-overlapping address spaces}} +#endif + + b = var_cmp >= arg_gen; +#ifdef CONSTANT +// expected-error@-2{{comparison between ('__constant int *' and '__generic int *') which are pointers to non-overlapping address spaces}} +#endif + + AS int *var_sub; + b = var_sub - arg_glob; +#ifdef CONSTANT +// expected-error@-2{{arithmetic operation with operands of type ('__constant int *' and '__global int *') which are pointers to non-overlapping address spaces}} +#endif + + b = var_sub - arg_loc; +#ifndef GENERIC +// expected-error-re@-2{{arithmetic operation with operands of type ('__{{global|constant}} int *' and '__local int *') which are pointers to non-overlapping address spaces}} +#endif + + b = var_sub - arg_const; +#ifndef CONSTANT +// expected-error-re@-2{{arithmetic operation with operands of type ('__{{global|generic}} int *' and '__constant int *') which are pointers to non-overlapping address spaces}} +#endif + + b = var_sub - arg_priv; +#ifndef GENERIC +// expected-error-re@-2{{arithmetic operation with operands of type ('__{{global|constant}} int *' and 'int *') which are pointers to non-overlapping address spaces}} +#endif + + b = var_sub - arg_gen; +#ifdef CONSTANT +// expected-error@-2{{arithmetic operation with operands of type ('__constant int *' and '__generic int *') which are pointers to non-overlapping address spaces}} +#endif + + f_glob(var_sub); +#ifndef GLOBAL +// expected-error-re@-2{{passing '__{{constant|generic}} int *' to parameter of type '__global int *' changes address space of pointer}} +#endif + + f_loc(var_sub); // expected-error-re{{passing '__{{global|constant|generic}} int *' to parameter of type '__local int *' changes address space of pointer}} + + f_const(var_sub); +#ifndef CONSTANT +// expected-error-re@-2{{passing '__{{global|generic}} int *' to parameter of type '__constant int *' changes address space of pointer}} +#endif + + f_priv(var_sub); // expected-error-re{{passing '__{{global|constant|generic}} int *' to parameter of type 'int *' changes address space of pointer}} + + f_gen(var_sub); +#ifdef CONSTANT +// expected-error@-2{{passing '__constant int *' to parameter of type '__generic int *' changes address space of pointer}} +#endif +} -- 2.40.0