1 //===--- SemaOpenCL.cpp --- Semantic Analysis for OpenCL constructs -------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 /// This file implements semantic analysis for OpenCL.
11 //===----------------------------------------------------------------------===//
13 #include "clang/Sema/SemaOpenCL.h"
14 #include "clang/AST/Attr.h"
15 #include "clang/AST/DeclBase.h"
16 #include "clang/Basic/DiagnosticSema.h"
17 #include "clang/Sema/ParsedAttr.h"
18 #include "clang/Sema/Sema.h"
21 SemaOpenCL::SemaOpenCL(Sema
&S
) : SemaBase(S
) {}
23 void SemaOpenCL::handleNoSVMAttr(Decl
*D
, const ParsedAttr
&AL
) {
24 if (getLangOpts().getOpenCLCompatibleVersion() < 200)
25 Diag(AL
.getLoc(), diag::err_attribute_requires_opencl_version
)
28 Diag(AL
.getLoc(), diag::warn_opencl_attr_deprecated_ignored
)
29 << AL
<< getLangOpts().getOpenCLVersionString();
32 void SemaOpenCL::handleAccessAttr(Decl
*D
, const ParsedAttr
&AL
) {
33 if (D
->isInvalidDecl())
36 // Check if there is only one access qualifier.
37 if (D
->hasAttr
<OpenCLAccessAttr
>()) {
38 if (D
->getAttr
<OpenCLAccessAttr
>()->getSemanticSpelling() ==
39 AL
.getSemanticSpelling()) {
40 Diag(AL
.getLoc(), diag::warn_duplicate_declspec
)
41 << AL
.getAttrName()->getName() << AL
.getRange();
43 Diag(AL
.getLoc(), diag::err_opencl_multiple_access_qualifiers
)
44 << D
->getSourceRange();
45 D
->setInvalidDecl(true);
50 // OpenCL v2.0 s6.6 - read_write can be used for image types to specify that
51 // an image object can be read and written. OpenCL v2.0 s6.13.6 - A kernel
52 // cannot read from and write to the same pipe object. Using the read_write
53 // (or __read_write) qualifier with the pipe qualifier is a compilation error.
54 // OpenCL v3.0 s6.8 - For OpenCL C 2.0, or with the
55 // __opencl_c_read_write_images feature, image objects specified as arguments
56 // to a kernel can additionally be declared to be read-write.
57 // C++ for OpenCL 1.0 inherits rule from OpenCL C v2.0.
58 // C++ for OpenCL 2021 inherits rule from OpenCL C v3.0.
59 if (const auto *PDecl
= dyn_cast
<ParmVarDecl
>(D
)) {
60 const Type
*DeclTy
= PDecl
->getType().getCanonicalType().getTypePtr();
61 if (AL
.getAttrName()->getName().contains("read_write")) {
62 bool ReadWriteImagesUnsupported
=
63 (getLangOpts().getOpenCLCompatibleVersion() < 200) ||
64 (getLangOpts().getOpenCLCompatibleVersion() == 300 &&
65 !SemaRef
.getOpenCLOptions().isSupported(
66 "__opencl_c_read_write_images", getLangOpts()));
67 if (ReadWriteImagesUnsupported
|| DeclTy
->isPipeType()) {
68 Diag(AL
.getLoc(), diag::err_opencl_invalid_read_write
)
69 << AL
<< PDecl
->getType() << DeclTy
->isImageType();
70 D
->setInvalidDecl(true);
76 D
->addAttr(::new (getASTContext()) OpenCLAccessAttr(getASTContext(), AL
));
79 void SemaOpenCL::handleSubGroupSize(Decl
*D
, const ParsedAttr
&AL
) {
81 const Expr
*E
= AL
.getArgAsExpr(0);
82 if (!SemaRef
.checkUInt32Argument(AL
, E
, SGSize
))
85 Diag(AL
.getLoc(), diag::err_attribute_argument_is_zero
)
86 << AL
<< E
->getSourceRange();
90 OpenCLIntelReqdSubGroupSizeAttr
*Existing
=
91 D
->getAttr
<OpenCLIntelReqdSubGroupSizeAttr
>();
92 if (Existing
&& Existing
->getSubGroupSize() != SGSize
)
93 Diag(AL
.getLoc(), diag::warn_duplicate_attribute
) << AL
;
95 D
->addAttr(::new (getASTContext())
96 OpenCLIntelReqdSubGroupSizeAttr(getASTContext(), AL
, SGSize
));
99 static inline bool isBlockPointer(Expr
*Arg
) {
100 return Arg
->getType()->isBlockPointerType();
103 /// OpenCL C v2.0, s6.13.17.2 - Checks that the block parameters are all local
104 /// void*, which is a requirement of device side enqueue.
105 static bool checkBlockArgs(Sema
&S
, Expr
*BlockArg
) {
106 const BlockPointerType
*BPT
=
107 cast
<BlockPointerType
>(BlockArg
->getType().getCanonicalType());
108 ArrayRef
<QualType
> Params
=
109 BPT
->getPointeeType()->castAs
<FunctionProtoType
>()->getParamTypes();
110 unsigned ArgCounter
= 0;
111 bool IllegalParams
= false;
112 // Iterate through the block parameters until either one is found that is not
113 // a local void*, or the block is valid.
114 for (ArrayRef
<QualType
>::iterator I
= Params
.begin(), E
= Params
.end();
115 I
!= E
; ++I
, ++ArgCounter
) {
116 if (!(*I
)->isPointerType() || !(*I
)->getPointeeType()->isVoidType() ||
117 (*I
)->getPointeeType().getQualifiers().getAddressSpace() !=
118 LangAS::opencl_local
) {
119 // Get the location of the error. If a block literal has been passed
120 // (BlockExpr) then we can point straight to the offending argument,
121 // else we just point to the variable reference.
122 SourceLocation ErrorLoc
;
123 if (isa
<BlockExpr
>(BlockArg
)) {
124 BlockDecl
*BD
= cast
<BlockExpr
>(BlockArg
)->getBlockDecl();
125 ErrorLoc
= BD
->getParamDecl(ArgCounter
)->getBeginLoc();
126 } else if (isa
<DeclRefExpr
>(BlockArg
)) {
127 ErrorLoc
= cast
<DeclRefExpr
>(BlockArg
)->getBeginLoc();
130 diag::err_opencl_enqueue_kernel_blocks_non_local_void_args
);
131 IllegalParams
= true;
135 return IllegalParams
;
138 bool SemaOpenCL::checkSubgroupExt(CallExpr
*Call
) {
139 // OpenCL device can support extension but not the feature as extension
140 // requires subgroup independent forward progress, but subgroup independent
141 // forward progress is optional in OpenCL C 3.0 __opencl_c_subgroups feature.
142 if (!SemaRef
.getOpenCLOptions().isSupported("cl_khr_subgroups",
144 !SemaRef
.getOpenCLOptions().isSupported("__opencl_c_subgroups",
146 Diag(Call
->getBeginLoc(), diag::err_opencl_requires_extension
)
147 << 1 << Call
->getDirectCallee()
148 << "cl_khr_subgroups or __opencl_c_subgroups";
154 bool SemaOpenCL::checkBuiltinNDRangeAndBlock(CallExpr
*TheCall
) {
155 if (SemaRef
.checkArgCount(TheCall
, 2))
158 if (checkSubgroupExt(TheCall
))
161 // First argument is an ndrange_t type.
162 Expr
*NDRangeArg
= TheCall
->getArg(0);
163 if (NDRangeArg
->getType().getUnqualifiedType().getAsString() != "ndrange_t") {
164 Diag(NDRangeArg
->getBeginLoc(), diag::err_opencl_builtin_expected_type
)
165 << TheCall
->getDirectCallee() << "'ndrange_t'";
169 Expr
*BlockArg
= TheCall
->getArg(1);
170 if (!isBlockPointer(BlockArg
)) {
171 Diag(BlockArg
->getBeginLoc(), diag::err_opencl_builtin_expected_type
)
172 << TheCall
->getDirectCallee() << "block";
175 return checkBlockArgs(SemaRef
, BlockArg
);
178 bool SemaOpenCL::checkBuiltinKernelWorkGroupSize(CallExpr
*TheCall
) {
179 if (SemaRef
.checkArgCount(TheCall
, 1))
182 Expr
*BlockArg
= TheCall
->getArg(0);
183 if (!isBlockPointer(BlockArg
)) {
184 Diag(BlockArg
->getBeginLoc(), diag::err_opencl_builtin_expected_type
)
185 << TheCall
->getDirectCallee() << "block";
188 return checkBlockArgs(SemaRef
, BlockArg
);
191 /// Diagnose integer type and any valid implicit conversion to it.
192 static bool checkOpenCLEnqueueIntType(Sema
&S
, Expr
*E
, const QualType
&IntT
) {
193 // Taking into account implicit conversions,
194 // allow any integer.
195 if (!E
->getType()->isIntegerType()) {
196 S
.Diag(E
->getBeginLoc(),
197 diag::err_opencl_enqueue_kernel_invalid_local_size_type
);
200 // Potentially emit standard warnings for implicit conversions if enabled
201 // using -Wconversion.
202 S
.CheckImplicitConversion(E
, IntT
, E
->getBeginLoc());
206 static bool checkOpenCLEnqueueLocalSizeArgs(Sema
&S
, CallExpr
*TheCall
,
207 unsigned Start
, unsigned End
) {
208 bool IllegalParams
= false;
209 for (unsigned I
= Start
; I
<= End
; ++I
)
210 IllegalParams
|= checkOpenCLEnqueueIntType(S
, TheCall
->getArg(I
),
211 S
.Context
.getSizeType());
212 return IllegalParams
;
215 /// OpenCL v2.0, s6.13.17.1 - Check that sizes are provided for all
216 /// 'local void*' parameter of passed block.
217 static bool checkOpenCLEnqueueVariadicArgs(Sema
&S
, CallExpr
*TheCall
,
219 unsigned NumNonVarArgs
) {
220 const BlockPointerType
*BPT
=
221 cast
<BlockPointerType
>(BlockArg
->getType().getCanonicalType());
222 unsigned NumBlockParams
=
223 BPT
->getPointeeType()->castAs
<FunctionProtoType
>()->getNumParams();
224 unsigned TotalNumArgs
= TheCall
->getNumArgs();
226 // For each argument passed to the block, a corresponding uint needs to
227 // be passed to describe the size of the local memory.
228 if (TotalNumArgs
!= NumBlockParams
+ NumNonVarArgs
) {
229 S
.Diag(TheCall
->getBeginLoc(),
230 diag::err_opencl_enqueue_kernel_local_size_args
);
234 // Check that the sizes of the local memory are specified by integers.
235 return checkOpenCLEnqueueLocalSizeArgs(S
, TheCall
, NumNonVarArgs
,
239 bool SemaOpenCL::checkBuiltinEnqueueKernel(CallExpr
*TheCall
) {
240 ASTContext
&Context
= getASTContext();
241 unsigned NumArgs
= TheCall
->getNumArgs();
244 Diag(TheCall
->getBeginLoc(), diag::err_typecheck_call_too_few_args_at_least
)
245 << 0 << 4 << NumArgs
<< /*is non object*/ 0;
249 Expr
*Arg0
= TheCall
->getArg(0);
250 Expr
*Arg1
= TheCall
->getArg(1);
251 Expr
*Arg2
= TheCall
->getArg(2);
252 Expr
*Arg3
= TheCall
->getArg(3);
254 // First argument always needs to be a queue_t type.
255 if (!Arg0
->getType()->isQueueT()) {
256 Diag(TheCall
->getArg(0)->getBeginLoc(),
257 diag::err_opencl_builtin_expected_type
)
258 << TheCall
->getDirectCallee() << getASTContext().OCLQueueTy
;
262 // Second argument always needs to be a kernel_enqueue_flags_t enum value.
263 if (!Arg1
->getType()->isIntegerType()) {
264 Diag(TheCall
->getArg(1)->getBeginLoc(),
265 diag::err_opencl_builtin_expected_type
)
266 << TheCall
->getDirectCallee() << "'kernel_enqueue_flags_t' (i.e. uint)";
270 // Third argument is always an ndrange_t type.
271 if (Arg2
->getType().getUnqualifiedType().getAsString() != "ndrange_t") {
272 Diag(TheCall
->getArg(2)->getBeginLoc(),
273 diag::err_opencl_builtin_expected_type
)
274 << TheCall
->getDirectCallee() << "'ndrange_t'";
278 // With four arguments, there is only one form that the function could be
279 // called in: no events and no variable arguments.
281 // check that the last argument is the right block type.
282 if (!isBlockPointer(Arg3
)) {
283 Diag(Arg3
->getBeginLoc(), diag::err_opencl_builtin_expected_type
)
284 << TheCall
->getDirectCallee() << "block";
287 // we have a block type, check the prototype
288 const BlockPointerType
*BPT
=
289 cast
<BlockPointerType
>(Arg3
->getType().getCanonicalType());
290 if (BPT
->getPointeeType()->castAs
<FunctionProtoType
>()->getNumParams() >
292 Diag(Arg3
->getBeginLoc(), diag::err_opencl_enqueue_kernel_blocks_no_args
);
297 // we can have block + varargs.
298 if (isBlockPointer(Arg3
))
299 return (checkBlockArgs(SemaRef
, Arg3
) ||
300 checkOpenCLEnqueueVariadicArgs(SemaRef
, TheCall
, Arg3
, 4));
301 // last two cases with either exactly 7 args or 7 args and varargs.
303 // check common block argument.
304 Expr
*Arg6
= TheCall
->getArg(6);
305 if (!isBlockPointer(Arg6
)) {
306 Diag(Arg6
->getBeginLoc(), diag::err_opencl_builtin_expected_type
)
307 << TheCall
->getDirectCallee() << "block";
310 if (checkBlockArgs(SemaRef
, Arg6
))
313 // Forth argument has to be any integer type.
314 if (!Arg3
->getType()->isIntegerType()) {
315 Diag(TheCall
->getArg(3)->getBeginLoc(),
316 diag::err_opencl_builtin_expected_type
)
317 << TheCall
->getDirectCallee() << "integer";
320 // check remaining common arguments.
321 Expr
*Arg4
= TheCall
->getArg(4);
322 Expr
*Arg5
= TheCall
->getArg(5);
324 // Fifth argument is always passed as a pointer to clk_event_t.
325 if (!Arg4
->isNullPointerConstant(Context
,
326 Expr::NPC_ValueDependentIsNotNull
) &&
327 !Arg4
->getType()->getPointeeOrArrayElementType()->isClkEventT()) {
328 Diag(TheCall
->getArg(4)->getBeginLoc(),
329 diag::err_opencl_builtin_expected_type
)
330 << TheCall
->getDirectCallee()
331 << Context
.getPointerType(Context
.OCLClkEventTy
);
335 // Sixth argument is always passed as a pointer to clk_event_t.
336 if (!Arg5
->isNullPointerConstant(Context
,
337 Expr::NPC_ValueDependentIsNotNull
) &&
338 !(Arg5
->getType()->isPointerType() &&
339 Arg5
->getType()->getPointeeType()->isClkEventT())) {
340 Diag(TheCall
->getArg(5)->getBeginLoc(),
341 diag::err_opencl_builtin_expected_type
)
342 << TheCall
->getDirectCallee()
343 << Context
.getPointerType(Context
.OCLClkEventTy
);
350 return checkOpenCLEnqueueVariadicArgs(SemaRef
, TheCall
, Arg6
, 7);
353 // None of the specific case has been detected, give generic error
354 Diag(TheCall
->getBeginLoc(), diag::err_opencl_enqueue_kernel_incorrect_args
);
358 /// Returns OpenCL access qual.
359 static OpenCLAccessAttr
*getOpenCLArgAccess(const Decl
*D
) {
360 return D
->getAttr
<OpenCLAccessAttr
>();
363 /// Returns true if pipe element type is different from the pointer.
364 static bool checkPipeArg(Sema
&S
, CallExpr
*Call
) {
365 const Expr
*Arg0
= Call
->getArg(0);
366 // First argument type should always be pipe.
367 if (!Arg0
->getType()->isPipeType()) {
368 S
.Diag(Call
->getBeginLoc(), diag::err_opencl_builtin_pipe_first_arg
)
369 << Call
->getDirectCallee() << Arg0
->getSourceRange();
372 OpenCLAccessAttr
*AccessQual
=
373 getOpenCLArgAccess(cast
<DeclRefExpr
>(Arg0
)->getDecl());
374 // Validates the access qualifier is compatible with the call.
375 // OpenCL v2.0 s6.13.16 - The access qualifiers for pipe should only be
376 // read_only and write_only, and assumed to be read_only if no qualifier is
378 switch (Call
->getDirectCallee()->getBuiltinID()) {
379 case Builtin::BIread_pipe
:
380 case Builtin::BIreserve_read_pipe
:
381 case Builtin::BIcommit_read_pipe
:
382 case Builtin::BIwork_group_reserve_read_pipe
:
383 case Builtin::BIsub_group_reserve_read_pipe
:
384 case Builtin::BIwork_group_commit_read_pipe
:
385 case Builtin::BIsub_group_commit_read_pipe
:
386 if (!(!AccessQual
|| AccessQual
->isReadOnly())) {
387 S
.Diag(Arg0
->getBeginLoc(),
388 diag::err_opencl_builtin_pipe_invalid_access_modifier
)
389 << "read_only" << Arg0
->getSourceRange();
393 case Builtin::BIwrite_pipe
:
394 case Builtin::BIreserve_write_pipe
:
395 case Builtin::BIcommit_write_pipe
:
396 case Builtin::BIwork_group_reserve_write_pipe
:
397 case Builtin::BIsub_group_reserve_write_pipe
:
398 case Builtin::BIwork_group_commit_write_pipe
:
399 case Builtin::BIsub_group_commit_write_pipe
:
400 if (!(AccessQual
&& AccessQual
->isWriteOnly())) {
401 S
.Diag(Arg0
->getBeginLoc(),
402 diag::err_opencl_builtin_pipe_invalid_access_modifier
)
403 << "write_only" << Arg0
->getSourceRange();
413 /// Returns true if pipe element type is different from the pointer.
414 static bool checkPipePacketType(Sema
&S
, CallExpr
*Call
, unsigned Idx
) {
415 const Expr
*Arg0
= Call
->getArg(0);
416 const Expr
*ArgIdx
= Call
->getArg(Idx
);
417 const PipeType
*PipeTy
= cast
<PipeType
>(Arg0
->getType());
418 const QualType EltTy
= PipeTy
->getElementType();
419 const PointerType
*ArgTy
= ArgIdx
->getType()->getAs
<PointerType
>();
420 // The Idx argument should be a pointer and the type of the pointer and
421 // the type of pipe element should also be the same.
423 !S
.Context
.hasSameType(
424 EltTy
, ArgTy
->getPointeeType()->getCanonicalTypeInternal())) {
425 S
.Diag(Call
->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg
)
426 << Call
->getDirectCallee() << S
.Context
.getPointerType(EltTy
)
427 << ArgIdx
->getType() << ArgIdx
->getSourceRange();
433 bool SemaOpenCL::checkBuiltinRWPipe(CallExpr
*Call
) {
434 // OpenCL v2.0 s6.13.16.2 - The built-in read/write
435 // functions have two forms.
436 switch (Call
->getNumArgs()) {
438 if (checkPipeArg(SemaRef
, Call
))
440 // The call with 2 arguments should be
441 // read/write_pipe(pipe T, T*).
442 // Check packet type T.
443 if (checkPipePacketType(SemaRef
, Call
, 1))
448 if (checkPipeArg(SemaRef
, Call
))
450 // The call with 4 arguments should be
451 // read/write_pipe(pipe T, reserve_id_t, uint, T*).
452 // Check reserve_id_t.
453 if (!Call
->getArg(1)->getType()->isReserveIDT()) {
454 Diag(Call
->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg
)
455 << Call
->getDirectCallee() << getASTContext().OCLReserveIDTy
456 << Call
->getArg(1)->getType() << Call
->getArg(1)->getSourceRange();
461 const Expr
*Arg2
= Call
->getArg(2);
462 if (!Arg2
->getType()->isIntegerType() &&
463 !Arg2
->getType()->isUnsignedIntegerType()) {
464 Diag(Call
->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg
)
465 << Call
->getDirectCallee() << getASTContext().UnsignedIntTy
466 << Arg2
->getType() << Arg2
->getSourceRange();
470 // Check packet type T.
471 if (checkPipePacketType(SemaRef
, Call
, 3))
475 Diag(Call
->getBeginLoc(), diag::err_opencl_builtin_pipe_arg_num
)
476 << Call
->getDirectCallee() << Call
->getSourceRange();
483 bool SemaOpenCL::checkBuiltinReserveRWPipe(CallExpr
*Call
) {
484 if (SemaRef
.checkArgCount(Call
, 2))
487 if (checkPipeArg(SemaRef
, Call
))
490 // Check the reserve size.
491 if (!Call
->getArg(1)->getType()->isIntegerType() &&
492 !Call
->getArg(1)->getType()->isUnsignedIntegerType()) {
493 Diag(Call
->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg
)
494 << Call
->getDirectCallee() << getASTContext().UnsignedIntTy
495 << Call
->getArg(1)->getType() << Call
->getArg(1)->getSourceRange();
499 // Since return type of reserve_read/write_pipe built-in function is
500 // reserve_id_t, which is not defined in the builtin def file , we used int
501 // as return type and need to override the return type of these functions.
502 Call
->setType(getASTContext().OCLReserveIDTy
);
507 bool SemaOpenCL::checkBuiltinCommitRWPipe(CallExpr
*Call
) {
508 if (SemaRef
.checkArgCount(Call
, 2))
511 if (checkPipeArg(SemaRef
, Call
))
514 // Check reserve_id_t.
515 if (!Call
->getArg(1)->getType()->isReserveIDT()) {
516 Diag(Call
->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg
)
517 << Call
->getDirectCallee() << getASTContext().OCLReserveIDTy
518 << Call
->getArg(1)->getType() << Call
->getArg(1)->getSourceRange();
525 bool SemaOpenCL::checkBuiltinPipePackets(CallExpr
*Call
) {
526 if (SemaRef
.checkArgCount(Call
, 1))
529 if (!Call
->getArg(0)->getType()->isPipeType()) {
530 Diag(Call
->getBeginLoc(), diag::err_opencl_builtin_pipe_first_arg
)
531 << Call
->getDirectCallee() << Call
->getArg(0)->getSourceRange();
538 bool SemaOpenCL::checkBuiltinToAddr(unsigned BuiltinID
, CallExpr
*Call
) {
539 if (SemaRef
.checkArgCount(Call
, 1))
542 auto RT
= Call
->getArg(0)->getType();
543 if (!RT
->isPointerType() ||
544 RT
->getPointeeType().getAddressSpace() == LangAS::opencl_constant
) {
545 Diag(Call
->getBeginLoc(), diag::err_opencl_builtin_to_addr_invalid_arg
)
546 << Call
->getArg(0) << Call
->getDirectCallee() << Call
->getSourceRange();
550 if (RT
->getPointeeType().getAddressSpace() != LangAS::opencl_generic
) {
551 Diag(Call
->getArg(0)->getBeginLoc(),
552 diag::warn_opencl_generic_address_space_arg
)
553 << Call
->getDirectCallee()->getNameInfo().getAsString()
554 << Call
->getArg(0)->getSourceRange();
557 RT
= RT
->getPointeeType();
558 auto Qual
= RT
.getQualifiers();
560 case Builtin::BIto_global
:
561 Qual
.setAddressSpace(LangAS::opencl_global
);
563 case Builtin::BIto_local
:
564 Qual
.setAddressSpace(LangAS::opencl_local
);
566 case Builtin::BIto_private
:
567 Qual
.setAddressSpace(LangAS::opencl_private
);
570 llvm_unreachable("Invalid builtin function");
572 Call
->setType(getASTContext().getPointerType(
573 getASTContext().getQualifiedType(RT
.getUnqualifiedType(), Qual
)));