1//===--- SemaOpenCL.cpp --- Semantic Analysis for OpenCL constructs -------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8/// \file
9/// This file implements semantic analysis for OpenCL.
10///
11//===----------------------------------------------------------------------===//
12
13#include "clang/Sema/SemaOpenCL.h"
14#include "clang/AST/Attr.h"
15#include "clang/AST/Decl.h"
16#include "clang/AST/DeclBase.h"
17#include "clang/AST/Expr.h"
18#include "clang/Basic/DiagnosticSema.h"
19#include "clang/Sema/ParsedAttr.h"
20#include "clang/Sema/Sema.h"
21
22namespace clang {
23SemaOpenCL::SemaOpenCL(Sema &S) : SemaBase(S) {}
24
25void SemaOpenCL::handleNoSVMAttr(Decl *D, const ParsedAttr &AL) {
26 if (getLangOpts().getOpenCLCompatibleVersion() < 200)
27 Diag(Loc: AL.getLoc(), DiagID: diag::err_attribute_requires_opencl_version)
28 << AL << "2.0" << 1;
29 else
30 Diag(Loc: AL.getLoc(), DiagID: diag::warn_opencl_attr_deprecated_ignored)
31 << AL << getLangOpts().getOpenCLVersionString();
32}
33
34void SemaOpenCL::handleAccessAttr(Decl *D, const ParsedAttr &AL) {
35 if (D->isInvalidDecl())
36 return;
37
38 // Check if there is only one access qualifier.
39 if (D->hasAttr<OpenCLAccessAttr>()) {
40 if (D->getAttr<OpenCLAccessAttr>()->getSemanticSpelling() ==
41 AL.getSemanticSpelling()) {
42 Diag(Loc: AL.getLoc(), DiagID: diag::warn_duplicate_declspec)
43 << AL.getAttrName()->getName() << AL.getRange();
44 } else {
45 Diag(Loc: AL.getLoc(), DiagID: diag::err_opencl_multiple_access_qualifiers)
46 << D->getSourceRange();
47 D->setInvalidDecl(true);
48 return;
49 }
50 }
51
52 // OpenCL v2.0 s6.6 - read_write can be used for image types to specify that
53 // an image object can be read and written. OpenCL v2.0 s6.13.6 - A kernel
54 // cannot read from and write to the same pipe object. Using the read_write
55 // (or __read_write) qualifier with the pipe qualifier is a compilation error.
56 // OpenCL v3.0 s6.8 - For OpenCL C 2.0, or with the
57 // __opencl_c_read_write_images feature, image objects specified as arguments
58 // to a kernel can additionally be declared to be read-write.
59 // C++ for OpenCL 1.0 inherits rule from OpenCL C v2.0.
60 // C++ for OpenCL 2021 inherits rule from OpenCL C v3.0.
61 if (const auto *PDecl = dyn_cast<ParmVarDecl>(Val: D)) {
62 const Type *DeclTy = PDecl->getType().getCanonicalType().getTypePtr();
63 if (AL.getAttrName()->getName().contains(Other: "read_write")) {
64 bool ReadWriteImagesUnsupported =
65 (getLangOpts().getOpenCLCompatibleVersion() < 200) ||
66 (getLangOpts().getOpenCLCompatibleVersion() == 300 &&
67 !SemaRef.getOpenCLOptions().isSupported(
68 Ext: "__opencl_c_read_write_images", LO: getLangOpts()));
69 if (ReadWriteImagesUnsupported || DeclTy->isPipeType()) {
70 Diag(Loc: AL.getLoc(), DiagID: diag::err_opencl_invalid_read_write)
71 << AL << PDecl->getType() << DeclTy->isImageType();
72 D->setInvalidDecl(true);
73 return;
74 }
75 }
76 }
77
78 D->addAttr(A: ::new (getASTContext()) OpenCLAccessAttr(getASTContext(), AL));
79}
80
81void SemaOpenCL::handleSubGroupSize(Decl *D, const ParsedAttr &AL) {
82 uint32_t SGSize;
83 const Expr *E = AL.getArgAsExpr(Arg: 0);
84 if (!SemaRef.checkUInt32Argument(AI: AL, Expr: E, Val&: SGSize))
85 return;
86 if (SGSize == 0) {
87 Diag(Loc: AL.getLoc(), DiagID: diag::err_attribute_argument_is_zero)
88 << AL << E->getSourceRange();
89 return;
90 }
91
92 OpenCLIntelReqdSubGroupSizeAttr *Existing =
93 D->getAttr<OpenCLIntelReqdSubGroupSizeAttr>();
94 if (Existing && Existing->getSubGroupSize() != SGSize)
95 Diag(Loc: AL.getLoc(), DiagID: diag::warn_duplicate_attribute) << AL;
96
97 D->addAttr(A: ::new (getASTContext())
98 OpenCLIntelReqdSubGroupSizeAttr(getASTContext(), AL, SGSize));
99}
100
101static inline bool isBlockPointer(Expr *Arg) {
102 return Arg->getType()->isBlockPointerType();
103}
104
105/// OpenCL C v2.0, s6.13.17.2 - Checks that the block parameters are all local
106/// void*, which is a requirement of device side enqueue.
107static bool checkBlockArgs(Sema &S, Expr *BlockArg) {
108 const BlockPointerType *BPT =
109 cast<BlockPointerType>(Val: BlockArg->getType().getCanonicalType());
110 ArrayRef<QualType> Params =
111 BPT->getPointeeType()->castAs<FunctionProtoType>()->getParamTypes();
112 unsigned ArgCounter = 0;
113 bool IllegalParams = false;
114 // Iterate through the block parameters until either one is found that is not
115 // a local void*, or the block is valid.
116 for (ArrayRef<QualType>::iterator I = Params.begin(), E = Params.end();
117 I != E; ++I, ++ArgCounter) {
118 if (!(*I)->isPointerType() || !(*I)->getPointeeType()->isVoidType() ||
119 (*I)->getPointeeType().getQualifiers().getAddressSpace() !=
120 LangAS::opencl_local) {
121 // Get the location of the error. If a block literal has been passed
122 // (BlockExpr) then we can point straight to the offending argument,
123 // else we just point to the variable reference.
124 SourceLocation ErrorLoc;
125 if (isa<BlockExpr>(Val: BlockArg)) {
126 BlockDecl *BD = cast<BlockExpr>(Val: BlockArg)->getBlockDecl();
127 ErrorLoc = BD->getParamDecl(i: ArgCounter)->getBeginLoc();
128 } else if (isa<DeclRefExpr>(Val: BlockArg)) {
129 ErrorLoc = cast<DeclRefExpr>(Val: BlockArg)->getBeginLoc();
130 }
131 S.Diag(Loc: ErrorLoc,
132 DiagID: diag::err_opencl_enqueue_kernel_blocks_non_local_void_args);
133 IllegalParams = true;
134 }
135 }
136
137 return IllegalParams;
138}
139
140bool SemaOpenCL::checkSubgroupExt(CallExpr *Call) {
141 // OpenCL device can support extension but not the feature as extension
142 // requires subgroup independent forward progress, but subgroup independent
143 // forward progress is optional in OpenCL C 3.0 __opencl_c_subgroups feature.
144 if (!SemaRef.getOpenCLOptions().isSupported(Ext: "cl_khr_subgroups",
145 LO: getLangOpts()) &&
146 !SemaRef.getOpenCLOptions().isSupported(Ext: "__opencl_c_subgroups",
147 LO: getLangOpts())) {
148 Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_requires_extension)
149 << 1 << Call->getDirectCallee()
150 << "cl_khr_subgroups or __opencl_c_subgroups";
151 return true;
152 }
153 return false;
154}
155
156bool SemaOpenCL::checkBuiltinNDRangeAndBlock(CallExpr *TheCall) {
157 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 2))
158 return true;
159
160 if (checkSubgroupExt(Call: TheCall))
161 return true;
162
163 // First argument is an ndrange_t type.
164 Expr *NDRangeArg = TheCall->getArg(Arg: 0);
165 if (NDRangeArg->getType().getUnqualifiedType().getAsString() != "ndrange_t") {
166 Diag(Loc: NDRangeArg->getBeginLoc(), DiagID: diag::err_opencl_builtin_expected_type)
167 << TheCall->getDirectCallee() << "'ndrange_t'";
168 return true;
169 }
170
171 Expr *BlockArg = TheCall->getArg(Arg: 1);
172 if (!isBlockPointer(Arg: BlockArg)) {
173 Diag(Loc: BlockArg->getBeginLoc(), DiagID: diag::err_opencl_builtin_expected_type)
174 << TheCall->getDirectCallee() << "block";
175 return true;
176 }
177 return checkBlockArgs(S&: SemaRef, BlockArg);
178}
179
180bool SemaOpenCL::checkBuiltinKernelWorkGroupSize(CallExpr *TheCall) {
181 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
182 return true;
183
184 Expr *BlockArg = TheCall->getArg(Arg: 0);
185 if (!isBlockPointer(Arg: BlockArg)) {
186 Diag(Loc: BlockArg->getBeginLoc(), DiagID: diag::err_opencl_builtin_expected_type)
187 << TheCall->getDirectCallee() << "block";
188 return true;
189 }
190 return checkBlockArgs(S&: SemaRef, BlockArg);
191}
192
193/// Diagnose integer type and any valid implicit conversion to it.
194static bool checkOpenCLEnqueueIntType(Sema &S, Expr *E, const QualType &IntT) {
195 // Taking into account implicit conversions,
196 // allow any integer.
197 if (!E->getType()->isIntegerType()) {
198 S.Diag(Loc: E->getBeginLoc(),
199 DiagID: diag::err_opencl_enqueue_kernel_invalid_local_size_type);
200 return true;
201 }
202 // Potentially emit standard warnings for implicit conversions if enabled
203 // using -Wconversion.
204 S.CheckImplicitConversion(E, T: IntT, CC: E->getBeginLoc());
205 return false;
206}
207
208static bool checkOpenCLEnqueueLocalSizeArgs(Sema &S, CallExpr *TheCall,
209 unsigned Start, unsigned End) {
210 bool IllegalParams = false;
211 for (unsigned I = Start; I <= End; ++I)
212 IllegalParams |= checkOpenCLEnqueueIntType(S, E: TheCall->getArg(Arg: I),
213 IntT: S.Context.getSizeType());
214 return IllegalParams;
215}
216
217/// OpenCL v2.0, s6.13.17.1 - Check that sizes are provided for all
218/// 'local void*' parameter of passed block.
219static bool checkOpenCLEnqueueVariadicArgs(Sema &S, CallExpr *TheCall,
220 Expr *BlockArg,
221 unsigned NumNonVarArgs) {
222 const BlockPointerType *BPT =
223 cast<BlockPointerType>(Val: BlockArg->getType().getCanonicalType());
224 unsigned NumBlockParams =
225 BPT->getPointeeType()->castAs<FunctionProtoType>()->getNumParams();
226 unsigned TotalNumArgs = TheCall->getNumArgs();
227
228 // For each argument passed to the block, a corresponding uint needs to
229 // be passed to describe the size of the local memory.
230 if (TotalNumArgs != NumBlockParams + NumNonVarArgs) {
231 S.Diag(Loc: TheCall->getBeginLoc(),
232 DiagID: diag::err_opencl_enqueue_kernel_local_size_args);
233 return true;
234 }
235
236 // Check that the sizes of the local memory are specified by integers.
237 return checkOpenCLEnqueueLocalSizeArgs(S, TheCall, Start: NumNonVarArgs,
238 End: TotalNumArgs - 1);
239}
240
241bool SemaOpenCL::checkBuiltinEnqueueKernel(CallExpr *TheCall) {
242 ASTContext &Context = getASTContext();
243 unsigned NumArgs = TheCall->getNumArgs();
244
245 if (NumArgs < 4) {
246 Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_typecheck_call_too_few_args_at_least)
247 << 0 << 4 << NumArgs << /*is non object*/ 0;
248 return true;
249 }
250
251 Expr *Arg0 = TheCall->getArg(Arg: 0);
252 Expr *Arg1 = TheCall->getArg(Arg: 1);
253 Expr *Arg2 = TheCall->getArg(Arg: 2);
254 Expr *Arg3 = TheCall->getArg(Arg: 3);
255
256 // First argument always needs to be a queue_t type.
257 if (!Arg0->getType()->isQueueT()) {
258 Diag(Loc: TheCall->getArg(Arg: 0)->getBeginLoc(),
259 DiagID: diag::err_opencl_builtin_expected_type)
260 << TheCall->getDirectCallee() << getASTContext().OCLQueueTy;
261 return true;
262 }
263
264 // Second argument always needs to be a kernel_enqueue_flags_t enum value.
265 if (!Arg1->getType()->isIntegerType()) {
266 Diag(Loc: TheCall->getArg(Arg: 1)->getBeginLoc(),
267 DiagID: diag::err_opencl_builtin_expected_type)
268 << TheCall->getDirectCallee() << "'kernel_enqueue_flags_t' (i.e. uint)";
269 return true;
270 }
271
272 // Third argument is always an ndrange_t type.
273 if (Arg2->getType().getUnqualifiedType().getAsString() != "ndrange_t") {
274 Diag(Loc: TheCall->getArg(Arg: 2)->getBeginLoc(),
275 DiagID: diag::err_opencl_builtin_expected_type)
276 << TheCall->getDirectCallee() << "'ndrange_t'";
277 return true;
278 }
279
280 // With four arguments, there is only one form that the function could be
281 // called in: no events and no variable arguments.
282 if (NumArgs == 4) {
283 // check that the last argument is the right block type.
284 if (!isBlockPointer(Arg: Arg3)) {
285 Diag(Loc: Arg3->getBeginLoc(), DiagID: diag::err_opencl_builtin_expected_type)
286 << TheCall->getDirectCallee() << "block";
287 return true;
288 }
289 // we have a block type, check the prototype
290 const BlockPointerType *BPT =
291 cast<BlockPointerType>(Val: Arg3->getType().getCanonicalType());
292 if (BPT->getPointeeType()->castAs<FunctionProtoType>()->getNumParams() >
293 0) {
294 Diag(Loc: Arg3->getBeginLoc(), DiagID: diag::err_opencl_enqueue_kernel_blocks_no_args);
295 return true;
296 }
297 return false;
298 }
299 // we can have block + varargs.
300 if (isBlockPointer(Arg: Arg3))
301 return (checkBlockArgs(S&: SemaRef, BlockArg: Arg3) ||
302 checkOpenCLEnqueueVariadicArgs(S&: SemaRef, TheCall, BlockArg: Arg3, NumNonVarArgs: 4));
303 // last two cases with either exactly 7 args or 7 args and varargs.
304 if (NumArgs >= 7) {
305 // check common block argument.
306 Expr *Arg6 = TheCall->getArg(Arg: 6);
307 if (!isBlockPointer(Arg: Arg6)) {
308 Diag(Loc: Arg6->getBeginLoc(), DiagID: diag::err_opencl_builtin_expected_type)
309 << TheCall->getDirectCallee() << "block";
310 return true;
311 }
312 if (checkBlockArgs(S&: SemaRef, BlockArg: Arg6))
313 return true;
314
315 // Forth argument has to be any integer type.
316 if (!Arg3->getType()->isIntegerType()) {
317 Diag(Loc: TheCall->getArg(Arg: 3)->getBeginLoc(),
318 DiagID: diag::err_opencl_builtin_expected_type)
319 << TheCall->getDirectCallee() << "integer";
320 return true;
321 }
322 // check remaining common arguments.
323 Expr *Arg4 = TheCall->getArg(Arg: 4);
324 Expr *Arg5 = TheCall->getArg(Arg: 5);
325
326 // Fifth argument is always passed as a pointer to clk_event_t.
327 if (!Arg4->isNullPointerConstant(Ctx&: Context,
328 NPC: Expr::NPC_ValueDependentIsNotNull) &&
329 !Arg4->getType()->getPointeeOrArrayElementType()->isClkEventT()) {
330 Diag(Loc: TheCall->getArg(Arg: 4)->getBeginLoc(),
331 DiagID: diag::err_opencl_builtin_expected_type)
332 << TheCall->getDirectCallee()
333 << Context.getPointerType(T: Context.OCLClkEventTy);
334 return true;
335 }
336
337 // Sixth argument is always passed as a pointer to clk_event_t.
338 if (!Arg5->isNullPointerConstant(Ctx&: Context,
339 NPC: Expr::NPC_ValueDependentIsNotNull) &&
340 !(Arg5->getType()->isPointerType() &&
341 Arg5->getType()->getPointeeType()->isClkEventT())) {
342 Diag(Loc: TheCall->getArg(Arg: 5)->getBeginLoc(),
343 DiagID: diag::err_opencl_builtin_expected_type)
344 << TheCall->getDirectCallee()
345 << Context.getPointerType(T: Context.OCLClkEventTy);
346 return true;
347 }
348
349 if (NumArgs == 7)
350 return false;
351
352 return checkOpenCLEnqueueVariadicArgs(S&: SemaRef, TheCall, BlockArg: Arg6, NumNonVarArgs: 7);
353 }
354
355 // None of the specific case has been detected, give generic error
356 Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_opencl_enqueue_kernel_incorrect_args);
357 return true;
358}
359
360/// Returns OpenCL access qual.
361static OpenCLAccessAttr *getOpenCLArgAccess(const Decl *D) {
362 return D->getAttr<OpenCLAccessAttr>();
363}
364
365/// Returns true if pipe element type is different from the pointer.
366static bool checkPipeArg(Sema &S, CallExpr *Call) {
367 const Expr *Arg0 = Call->getArg(Arg: 0);
368 // First argument type should always be pipe.
369 if (!Arg0->getType()->isPipeType()) {
370 S.Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_first_arg)
371 << Call->getDirectCallee() << Arg0->getSourceRange();
372 return true;
373 }
374 OpenCLAccessAttr *AccessQual =
375 getOpenCLArgAccess(D: cast<DeclRefExpr>(Val: Arg0)->getDecl());
376 // Validates the access qualifier is compatible with the call.
377 // OpenCL v2.0 s6.13.16 - The access qualifiers for pipe should only be
378 // read_only and write_only, and assumed to be read_only if no qualifier is
379 // specified.
380 switch (Call->getDirectCallee()->getBuiltinID()) {
381 case Builtin::BIread_pipe:
382 case Builtin::BIreserve_read_pipe:
383 case Builtin::BIcommit_read_pipe:
384 case Builtin::BIwork_group_reserve_read_pipe:
385 case Builtin::BIsub_group_reserve_read_pipe:
386 case Builtin::BIwork_group_commit_read_pipe:
387 case Builtin::BIsub_group_commit_read_pipe:
388 if (!(!AccessQual || AccessQual->isReadOnly())) {
389 S.Diag(Loc: Arg0->getBeginLoc(),
390 DiagID: diag::err_opencl_builtin_pipe_invalid_access_modifier)
391 << "read_only" << Arg0->getSourceRange();
392 return true;
393 }
394 break;
395 case Builtin::BIwrite_pipe:
396 case Builtin::BIreserve_write_pipe:
397 case Builtin::BIcommit_write_pipe:
398 case Builtin::BIwork_group_reserve_write_pipe:
399 case Builtin::BIsub_group_reserve_write_pipe:
400 case Builtin::BIwork_group_commit_write_pipe:
401 case Builtin::BIsub_group_commit_write_pipe:
402 if (!(AccessQual && AccessQual->isWriteOnly())) {
403 S.Diag(Loc: Arg0->getBeginLoc(),
404 DiagID: diag::err_opencl_builtin_pipe_invalid_access_modifier)
405 << "write_only" << Arg0->getSourceRange();
406 return true;
407 }
408 break;
409 default:
410 break;
411 }
412 return false;
413}
414
415/// Returns true if pipe element type is different from the pointer.
416static bool checkPipePacketType(Sema &S, CallExpr *Call, unsigned Idx) {
417 const Expr *Arg0 = Call->getArg(Arg: 0);
418 const Expr *ArgIdx = Call->getArg(Arg: Idx);
419 const PipeType *PipeTy = cast<PipeType>(Val: Arg0->getType());
420 const QualType EltTy = PipeTy->getElementType();
421 const PointerType *ArgTy = ArgIdx->getType()->getAs<PointerType>();
422 // The Idx argument should be a pointer and the type of the pointer and
423 // the type of pipe element should also be the same.
424 if (!ArgTy ||
425 !S.Context.hasSameType(
426 T1: EltTy, T2: ArgTy->getPointeeType()->getCanonicalTypeInternal())) {
427 S.Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_invalid_arg)
428 << Call->getDirectCallee() << S.Context.getPointerType(T: EltTy)
429 << ArgIdx->getType() << ArgIdx->getSourceRange();
430 return true;
431 }
432 return false;
433}
434
435bool SemaOpenCL::checkBuiltinRWPipe(CallExpr *Call) {
436 // OpenCL v2.0 s6.13.16.2 - The built-in read/write
437 // functions have two forms.
438 switch (Call->getNumArgs()) {
439 case 2:
440 if (checkPipeArg(S&: SemaRef, Call))
441 return true;
442 // The call with 2 arguments should be
443 // read/write_pipe(pipe T, T*).
444 // Check packet type T.
445 if (checkPipePacketType(S&: SemaRef, Call, Idx: 1))
446 return true;
447 break;
448
449 case 4: {
450 if (checkPipeArg(S&: SemaRef, Call))
451 return true;
452 // The call with 4 arguments should be
453 // read/write_pipe(pipe T, reserve_id_t, uint, T*).
454 // Check reserve_id_t.
455 if (!Call->getArg(Arg: 1)->getType()->isReserveIDT()) {
456 Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_invalid_arg)
457 << Call->getDirectCallee() << getASTContext().OCLReserveIDTy
458 << Call->getArg(Arg: 1)->getType() << Call->getArg(Arg: 1)->getSourceRange();
459 return true;
460 }
461
462 // Check the index.
463 const Expr *Arg2 = Call->getArg(Arg: 2);
464 if (!Arg2->getType()->isIntegerType() &&
465 !Arg2->getType()->isUnsignedIntegerType()) {
466 Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_invalid_arg)
467 << Call->getDirectCallee() << getASTContext().UnsignedIntTy
468 << Arg2->getType() << Arg2->getSourceRange();
469 return true;
470 }
471
472 // Check packet type T.
473 if (checkPipePacketType(S&: SemaRef, Call, Idx: 3))
474 return true;
475 } break;
476 default:
477 Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_arg_num)
478 << Call->getDirectCallee() << Call->getSourceRange();
479 return true;
480 }
481
482 return false;
483}
484
485bool SemaOpenCL::checkBuiltinReserveRWPipe(CallExpr *Call) {
486 if (SemaRef.checkArgCount(Call, DesiredArgCount: 2))
487 return true;
488
489 if (checkPipeArg(S&: SemaRef, Call))
490 return true;
491
492 // Check the reserve size.
493 if (!Call->getArg(Arg: 1)->getType()->isIntegerType() &&
494 !Call->getArg(Arg: 1)->getType()->isUnsignedIntegerType()) {
495 Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_invalid_arg)
496 << Call->getDirectCallee() << getASTContext().UnsignedIntTy
497 << Call->getArg(Arg: 1)->getType() << Call->getArg(Arg: 1)->getSourceRange();
498 return true;
499 }
500
501 // Since return type of reserve_read/write_pipe built-in function is
502 // reserve_id_t, which is not defined in the builtin def file , we used int
503 // as return type and need to override the return type of these functions.
504 Call->setType(getASTContext().OCLReserveIDTy);
505
506 return false;
507}
508
509bool SemaOpenCL::checkBuiltinCommitRWPipe(CallExpr *Call) {
510 if (SemaRef.checkArgCount(Call, DesiredArgCount: 2))
511 return true;
512
513 if (checkPipeArg(S&: SemaRef, Call))
514 return true;
515
516 // Check reserve_id_t.
517 if (!Call->getArg(Arg: 1)->getType()->isReserveIDT()) {
518 Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_invalid_arg)
519 << Call->getDirectCallee() << getASTContext().OCLReserveIDTy
520 << Call->getArg(Arg: 1)->getType() << Call->getArg(Arg: 1)->getSourceRange();
521 return true;
522 }
523
524 return false;
525}
526
527bool SemaOpenCL::checkBuiltinPipePackets(CallExpr *Call) {
528 if (SemaRef.checkArgCount(Call, DesiredArgCount: 1))
529 return true;
530
531 if (!Call->getArg(Arg: 0)->getType()->isPipeType()) {
532 Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_first_arg)
533 << Call->getDirectCallee() << Call->getArg(Arg: 0)->getSourceRange();
534 return true;
535 }
536
537 return false;
538}
539
540bool SemaOpenCL::checkBuiltinToAddr(unsigned BuiltinID, CallExpr *Call) {
541 if (SemaRef.checkArgCount(Call, DesiredArgCount: 1))
542 return true;
543
544 auto RT = Call->getArg(Arg: 0)->getType();
545 if (!RT->isPointerType() ||
546 RT->getPointeeType().getAddressSpace() == LangAS::opencl_constant) {
547 Diag(Loc: Call->getArg(Arg: 0)->getBeginLoc(),
548 DiagID: diag::err_opencl_builtin_to_addr_invalid_arg)
549 << Call->getArg(Arg: 0) << Call->getDirectCallee() << Call->getSourceRange();
550 return true;
551 }
552
553 if (RT->getPointeeType().getAddressSpace() != LangAS::opencl_generic) {
554 Diag(Loc: Call->getArg(Arg: 0)->getBeginLoc(),
555 DiagID: diag::warn_opencl_generic_address_space_arg)
556 << Call->getDirectCallee()->getNameInfo().getAsString()
557 << Call->getArg(Arg: 0)->getSourceRange();
558 }
559
560 RT = RT->getPointeeType();
561 auto Qual = RT.getQualifiers();
562 switch (BuiltinID) {
563 case Builtin::BIto_global:
564 Qual.setAddressSpace(LangAS::opencl_global);
565 break;
566 case Builtin::BIto_local:
567 Qual.setAddressSpace(LangAS::opencl_local);
568 break;
569 case Builtin::BIto_private:
570 Qual.setAddressSpace(LangAS::opencl_private);
571 break;
572 default:
573 llvm_unreachable("Invalid builtin function");
574 }
575 Call->setType(getASTContext().getPointerType(
576 T: getASTContext().getQualifiedType(T: RT.getUnqualifiedType(), Qs: Qual)));
577
578 return false;
579}
580
581void SemaOpenCL::checkBuiltinReadImage(FunctionDecl *FDecl, CallExpr *Call) {
582 IdentifierInfo *II = FDecl->getIdentifier();
583 if (!II)
584 return;
585 StringRef Name = II->getName();
586 if (Name != "read_imagei" && Name != "read_imageui")
587 return;
588
589 if (FDecl->getNumParams() < 2)
590 return;
591 QualType ParamTy = FDecl->getParamDecl(i: 1)->getType().getCanonicalType();
592 if (!ParamTy->isSamplerT())
593 return;
594 Expr *SamplerArg = Call->getArg(Arg: 1);
595
596 Expr *IntExpr = nullptr;
597 Expr *Inner = SamplerArg->IgnoreParenCasts();
598
599 if (auto *DRE = dyn_cast<DeclRefExpr>(Val: Inner)) {
600 if (auto *Var = dyn_cast<VarDecl>(Val: DRE->getDecl())) {
601 if (const Expr *Init = Var->getAnyInitializer()) {
602 Init = Init->IgnoreParenImpCasts();
603 if (Init->getType()->isIntegerType())
604 IntExpr = const_cast<Expr *>(Init);
605 }
606 }
607 } else if (Inner->getType()->isIntegerType()) {
608 IntExpr = Inner;
609 }
610
611 if (!IntExpr)
612 return;
613
614 Expr::EvalResult EVResult;
615 if (!IntExpr->EvaluateAsInt(Result&: EVResult, Ctx: getASTContext()))
616 return;
617
618 uint64_t SamplerValue = EVResult.Val.getInt().getLimitedValue();
619 // Must stay in sync with CLK_FILTER_* defines in opencl-c-base.h.
620 constexpr unsigned FilterModeMask = 0x30u;
621 constexpr unsigned FilterModeLinear = 0x20u;
622 if ((SamplerValue & FilterModeMask) == FilterModeLinear)
623 Diag(Loc: SamplerArg->getExprLoc(), DiagID: diag::warn_sampler_argument_invalid_filter)
624 << Name << SamplerArg->getSourceRange();
625}
626
627} // namespace clang
628