Skip to content

Commit

Permalink
PR19957: [OpenCL] Incorrectly accepts implicit address space conversi…
Browse files Browse the repository at this point in the history
…on with ternary operator.

Generates addrspacecast instead of bitcast for ternary operator when necessary, and diagnose ternary operator with incompatible second and third operands.

https://llvm.org/bugs/show_bug.cgi?id=19957

Differential Revision: http://reviews.llvm.org/D17412

llvm-svn: 266111
  • Loading branch information
yxsamliu committed Apr 12, 2016
1 parent 49bd58f commit a1a87ad
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 10 deletions.
4 changes: 3 additions & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -5376,7 +5376,9 @@ def ext_typecheck_comparison_of_distinct_pointers_nonstandard : ExtWarn<
"composite pointer type %2">, InGroup<CompareDistinctPointerType>;
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"
"|arithmetic operation with operands of type %diff{ ($ and $)|}0,1"
"|conditional operator with the second and third operands of type "
"%diff{ ($ and $)|}0,1}2"
" which are pointers to non-overlapping address spaces">;

def err_typecheck_assign_const : Error<
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Expand Up @@ -7617,6 +7617,15 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS,
Qualifiers LQuals = LHSCan.getLocalQualifiers();
Qualifiers RQuals = RHSCan.getLocalQualifiers();
if (LQuals != RQuals) {
if (getLangOpts().OpenCL) {
if (LHS.getUnqualifiedType() != RHS.getUnqualifiedType() ||
LQuals.getCVRQualifiers() != RQuals.getCVRQualifiers())
return QualType();
if (LQuals.isAddressSpaceSupersetOf(RQuals))
return LHS;
if (RQuals.isAddressSpaceSupersetOf(LQuals))
return RHS;
}
// If any of these qualifiers are different, we have a type
// mismatch.
if (LQuals.getCVRQualifiers() != RQuals.getCVRQualifiers() ||
Expand Down
75 changes: 66 additions & 9 deletions clang/lib/Sema/SemaExpr.cpp
Expand Up @@ -6193,30 +6193,87 @@ static QualType checkConditionalPointerCompatibility(Sema &S, ExprResult &LHS,
lhptee = S.Context.getQualifiedType(lhptee.getUnqualifiedType(), lhQual);
rhptee = S.Context.getQualifiedType(rhptee.getUnqualifiedType(), rhQual);

// For OpenCL:
// 1. If LHS and RHS types match exactly and:
// (a) AS match => use standard C rules, no bitcast or addrspacecast
// (b) AS overlap => generate addrspacecast
// (c) AS don't overlap => give an error
// 2. if LHS and RHS types don't match:
// (a) AS match => use standard C rules, generate bitcast
// (b) AS overlap => generate addrspacecast instead of bitcast
// (c) AS don't overlap => give an error

// For OpenCL, non-null composite type is returned only for cases 1a and 1b.
QualType CompositeTy = S.Context.mergeTypes(lhptee, rhptee);

// OpenCL cases 1c, 2a, 2b, and 2c.
if (CompositeTy.isNull()) {
S.Diag(Loc, diag::ext_typecheck_cond_incompatible_pointers)
<< LHSTy << RHSTy << LHS.get()->getSourceRange()
<< RHS.get()->getSourceRange();
// In this situation, we assume void* type. No especially good
// reason, but this is what gcc does, and we do have to pick
// to get a consistent AST.
QualType incompatTy = S.Context.getPointerType(S.Context.VoidTy);
LHS = S.ImpCastExprToType(LHS.get(), incompatTy, CK_BitCast);
RHS = S.ImpCastExprToType(RHS.get(), incompatTy, CK_BitCast);
QualType incompatTy;
if (S.getLangOpts().OpenCL) {
// OpenCL v1.1 s6.5 - Conversion between pointers to distinct address
// spaces is disallowed.
unsigned ResultAddrSpace;
if (lhQual.isAddressSpaceSupersetOf(rhQual)) {
// Cases 2a and 2b.
ResultAddrSpace = lhQual.getAddressSpace();
} else if (rhQual.isAddressSpaceSupersetOf(lhQual)) {
// Cases 2a and 2b.
ResultAddrSpace = rhQual.getAddressSpace();
} else {
// Cases 1c and 2c.
S.Diag(Loc,
diag::err_typecheck_op_on_nonoverlapping_address_space_pointers)
<< LHSTy << RHSTy << 2 << LHS.get()->getSourceRange()
<< RHS.get()->getSourceRange();
return QualType();
}

// Continue handling cases 2a and 2b.
incompatTy = S.Context.getPointerType(
S.Context.getAddrSpaceQualType(S.Context.VoidTy, ResultAddrSpace));
LHS = S.ImpCastExprToType(LHS.get(), incompatTy,
(lhQual.getAddressSpace() != ResultAddrSpace)
? CK_AddressSpaceConversion /* 2b */
: CK_BitCast /* 2a */);
RHS = S.ImpCastExprToType(RHS.get(), incompatTy,
(rhQual.getAddressSpace() != ResultAddrSpace)
? CK_AddressSpaceConversion /* 2b */
: CK_BitCast /* 2a */);
} else {
S.Diag(Loc, diag::ext_typecheck_cond_incompatible_pointers)
<< LHSTy << RHSTy << LHS.get()->getSourceRange()
<< RHS.get()->getSourceRange();
incompatTy = S.Context.getPointerType(S.Context.VoidTy);
LHS = S.ImpCastExprToType(LHS.get(), incompatTy, CK_BitCast);
RHS = S.ImpCastExprToType(RHS.get(), incompatTy, CK_BitCast);
}
return incompatTy;
}

// The pointer types are compatible.
QualType ResultTy = CompositeTy.withCVRQualifiers(MergedCVRQual);
auto LHSCastKind = CK_BitCast, RHSCastKind = CK_BitCast;
if (IsBlockPointer)
ResultTy = S.Context.getBlockPointerType(ResultTy);
else
else {
// Cases 1a and 1b for OpenCL.
auto ResultAddrSpace = ResultTy.getQualifiers().getAddressSpace();
LHSCastKind = lhQual.getAddressSpace() == ResultAddrSpace
? CK_BitCast /* 1a */
: CK_AddressSpaceConversion /* 1b */;
RHSCastKind = rhQual.getAddressSpace() == ResultAddrSpace
? CK_BitCast /* 1a */
: CK_AddressSpaceConversion /* 1b */;
ResultTy = S.Context.getPointerType(ResultTy);
}

LHS = S.ImpCastExprToType(LHS.get(), ResultTy, CK_BitCast);
RHS = S.ImpCastExprToType(RHS.get(), ResultTy, CK_BitCast);
// For case 1a of OpenCL, S.ImpCastExprToType will not insert bitcast
// if the target type does not change.
LHS = S.ImpCastExprToType(LHS.get(), ResultTy, LHSCastKind);
RHS = S.ImpCastExprToType(RHS.get(), ResultTy, RHSCastKind);
return ResultTy;
}

Expand Down
39 changes: 39 additions & 0 deletions clang/test/CodeGenOpenCL/address-spaces-conversions.cl
Expand Up @@ -5,6 +5,7 @@
// test that we generate address space casts everywhere we need conversions of
// pointers to different address spaces

// CHECK: define void @test
void test(global int *arg_glob, generic int *arg_gen) {
int var_priv;
arg_gen = arg_glob; // implicit cast global -> generic
Expand Down Expand Up @@ -39,3 +40,41 @@ void test(global int *arg_glob, generic int *arg_gen) {
// CHECK-NOFAKE: bitcast
// CHECK-NOFAKE-NOT: addrspacecast
}

// Test ternary operator.
// CHECK: define void @test_ternary
void test_ternary(void) {
global int *var_glob;
generic int *var_gen;
generic int *var_gen2;
generic float *var_gen_f;
generic void *var_gen_v;

var_gen = var_gen ? var_gen : var_gen2; // operands of the same addr spaces and the same type
// CHECK: icmp
// CHECK-NOT: addrspacecast
// CHECK-NOT: bitcast
// CHECK: phi
// CHECK: store i32 addrspace(4)* %{{.+}}, i32 addrspace(4)** %{{.+}}

var_gen = var_gen ? var_gen : var_glob; // operands of overlapping addr spaces and the same type
// CHECK: icmp
// CHECK-NOT: bitcast
// CHECK: %{{.+}} = addrspacecast i32 addrspace(1)* %{{.+}} to i32 addrspace(4)*
// CHECK: phi
// CHECK: store

var_gen_v = var_gen ? var_gen : var_gen_f; // operands of the same addr space and different types
// CHECK: icmp
// CHECK: %{{.+}} = bitcast i32 addrspace(4)* %{{.+}} to i8 addrspace(4)*
// CHECK: %{{.+}} = bitcast float addrspace(4)* %{{.+}} to i8 addrspace(4)*
// CHECK: phi
// CHECK: store

var_gen_v = var_gen ? var_glob : var_gen_f; // operands of overlapping addr spaces and different types
// CHECK: icmp
// CHECK: %{{.+}} = addrspacecast i32 addrspace(1)* %{{.+}} to i8 addrspace(4)*
// CHECK: %{{.+}} = bitcast float addrspace(4)* %{{.+}} to i8 addrspace(4)*
// CHECK: phi
// CHECK: store
}
66 changes: 66 additions & 0 deletions clang/test/SemaOpenCL/address-spaces-conversions-cl2.0.cl
Expand Up @@ -225,3 +225,69 @@ void test_conversion(global int *arg_glob, local int *arg_loc,
// expected-error@-2{{passing '__constant int *' to parameter of type '__generic int *' changes address space of pointer}}
#endif
}

void test_ternary() {
AS int *var_cond;
generic int *var_gen;
global int *var_glob;
var_gen = 0 ? var_cond : var_glob;
#ifdef CONSTANT
// expected-error@-2{{conditional operator with the second and third operands of type ('__constant int *' and '__global int *') which are pointers to non-overlapping address spaces}}
#endif

local int *var_loc;
var_gen = 0 ? var_cond : var_loc;
#ifndef GENERIC
// expected-error-re@-2{{conditional operator with the second and third operands of type ('__{{global|constant}} int *' and '__local int *') which are pointers to non-overlapping address spaces}}
#endif

constant int *var_const;
var_cond = 0 ? var_cond : var_const;
#ifndef CONSTANT
// expected-error-re@-2{{conditional operator with the second and third operands of type ('__{{global|generic}} int *' and '__constant int *') which are pointers to non-overlapping address spaces}}
#endif

private int *var_priv;
var_gen = 0 ? var_cond : var_priv;
#ifndef GENERIC
// expected-error-re@-2{{conditional operator with the second and third operands of type ('__{{global|constant}} int *' and 'int *') which are pointers to non-overlapping address spaces}}
#endif

var_gen = 0 ? var_cond : var_gen;
#ifdef CONSTANT
// expected-error@-2{{conditional operator with the second and third operands of type ('__constant int *' and '__generic int *') which are pointers to non-overlapping address spaces}}
#endif

void *var_void_gen;
global char *var_glob_ch;
var_void_gen = 0 ? var_cond : var_glob_ch;
#ifdef CONSTANT
// expected-error@-2{{conditional operator with the second and third operands of type ('__constant int *' and '__global char *') which are pointers to non-overlapping address spaces}}
#endif

local char *var_loc_ch;
var_void_gen = 0 ? var_cond : var_loc_ch;
#ifndef GENERIC
// expected-error-re@-2{{conditional operator with the second and third operands of type ('__{{global|constant}} int *' and '__local char *') which are pointers to non-overlapping address spaces}}
#endif

constant void *var_void_const;
constant char *var_const_ch;
var_void_const = 0 ? var_cond : var_const_ch;
#ifndef CONSTANT
// expected-error-re@-2{{conditional operator with the second and third operands of type ('__{{global|generic}} int *' and '__constant char *') which are pointers to non-overlapping address spaces}}
#endif

private char *var_priv_ch;
var_void_gen = 0 ? var_cond : var_priv_ch;
#ifndef GENERIC
// expected-error-re@-2{{conditional operator with the second and third operands of type ('__{{global|constant}} int *' and 'char *') which are pointers to non-overlapping address spaces}}
#endif

generic char *var_gen_ch;
var_void_gen = 0 ? var_cond : var_gen_ch;
#ifdef CONSTANT
// expected-error@-2{{conditional operator with the second and third operands of type ('__constant int *' and '__generic char *') which are pointers to non-overlapping address spaces}}
#endif
}

0 comments on commit a1a87ad

Please sign in to comment.