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/DeclBase.h" |
16 | #include "clang/Basic/DiagnosticSema.h" |
17 | #include "clang/Sema/ParsedAttr.h" |
18 | #include "clang/Sema/Sema.h" |
19 | |
20 | namespace clang { |
21 | SemaOpenCL::SemaOpenCL(Sema &S) : SemaBase(S) {} |
22 | |
23 | void SemaOpenCL::handleNoSVMAttr(Decl *D, const ParsedAttr &AL) { |
24 | if (getLangOpts().getOpenCLCompatibleVersion() < 200) |
25 | Diag(Loc: AL.getLoc(), DiagID: diag::err_attribute_requires_opencl_version) |
26 | << AL << "2.0" << 1; |
27 | else |
28 | Diag(Loc: AL.getLoc(), DiagID: diag::warn_opencl_attr_deprecated_ignored) |
29 | << AL << getLangOpts().getOpenCLVersionString(); |
30 | } |
31 | |
32 | void SemaOpenCL::handleAccessAttr(Decl *D, const ParsedAttr &AL) { |
33 | if (D->isInvalidDecl()) |
34 | return; |
35 | |
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(Loc: AL.getLoc(), DiagID: diag::warn_duplicate_declspec) |
41 | << AL.getAttrName()->getName() << AL.getRange(); |
42 | } else { |
43 | Diag(Loc: AL.getLoc(), DiagID: diag::err_opencl_multiple_access_qualifiers) |
44 | << D->getSourceRange(); |
45 | D->setInvalidDecl(true); |
46 | return; |
47 | } |
48 | } |
49 | |
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>(Val: D)) { |
60 | const Type *DeclTy = PDecl->getType().getCanonicalType().getTypePtr(); |
61 | if (AL.getAttrName()->getName().contains(Other: "read_write" )) { |
62 | bool ReadWriteImagesUnsupported = |
63 | (getLangOpts().getOpenCLCompatibleVersion() < 200) || |
64 | (getLangOpts().getOpenCLCompatibleVersion() == 300 && |
65 | !SemaRef.getOpenCLOptions().isSupported( |
66 | Ext: "__opencl_c_read_write_images" , LO: getLangOpts())); |
67 | if (ReadWriteImagesUnsupported || DeclTy->isPipeType()) { |
68 | Diag(Loc: AL.getLoc(), DiagID: diag::err_opencl_invalid_read_write) |
69 | << AL << PDecl->getType() << DeclTy->isImageType(); |
70 | D->setInvalidDecl(true); |
71 | return; |
72 | } |
73 | } |
74 | } |
75 | |
76 | D->addAttr(A: ::new (getASTContext()) OpenCLAccessAttr(getASTContext(), AL)); |
77 | } |
78 | |
79 | void SemaOpenCL::handleSubGroupSize(Decl *D, const ParsedAttr &AL) { |
80 | uint32_t SGSize; |
81 | const Expr *E = AL.getArgAsExpr(Arg: 0); |
82 | if (!SemaRef.checkUInt32Argument(AI: AL, Expr: E, Val&: SGSize)) |
83 | return; |
84 | if (SGSize == 0) { |
85 | Diag(Loc: AL.getLoc(), DiagID: diag::err_attribute_argument_is_zero) |
86 | << AL << E->getSourceRange(); |
87 | return; |
88 | } |
89 | |
90 | OpenCLIntelReqdSubGroupSizeAttr *Existing = |
91 | D->getAttr<OpenCLIntelReqdSubGroupSizeAttr>(); |
92 | if (Existing && Existing->getSubGroupSize() != SGSize) |
93 | Diag(Loc: AL.getLoc(), DiagID: diag::warn_duplicate_attribute) << AL; |
94 | |
95 | D->addAttr(A: ::new (getASTContext()) |
96 | OpenCLIntelReqdSubGroupSizeAttr(getASTContext(), AL, SGSize)); |
97 | } |
98 | |
99 | static inline bool isBlockPointer(Expr *Arg) { |
100 | return Arg->getType()->isBlockPointerType(); |
101 | } |
102 | |
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>(Val: 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>(Val: BlockArg)) { |
124 | BlockDecl *BD = cast<BlockExpr>(Val: BlockArg)->getBlockDecl(); |
125 | ErrorLoc = BD->getParamDecl(i: ArgCounter)->getBeginLoc(); |
126 | } else if (isa<DeclRefExpr>(Val: BlockArg)) { |
127 | ErrorLoc = cast<DeclRefExpr>(Val: BlockArg)->getBeginLoc(); |
128 | } |
129 | S.Diag(Loc: ErrorLoc, |
130 | DiagID: diag::err_opencl_enqueue_kernel_blocks_non_local_void_args); |
131 | IllegalParams = true; |
132 | } |
133 | } |
134 | |
135 | return IllegalParams; |
136 | } |
137 | |
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(Ext: "cl_khr_subgroups" , |
143 | LO: getLangOpts()) && |
144 | !SemaRef.getOpenCLOptions().isSupported(Ext: "__opencl_c_subgroups" , |
145 | LO: getLangOpts())) { |
146 | Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_requires_extension) |
147 | << 1 << Call->getDirectCallee() |
148 | << "cl_khr_subgroups or __opencl_c_subgroups" ; |
149 | return true; |
150 | } |
151 | return false; |
152 | } |
153 | |
154 | bool SemaOpenCL::checkBuiltinNDRangeAndBlock(CallExpr *TheCall) { |
155 | if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 2)) |
156 | return true; |
157 | |
158 | if (checkSubgroupExt(Call: TheCall)) |
159 | return true; |
160 | |
161 | // First argument is an ndrange_t type. |
162 | Expr *NDRangeArg = TheCall->getArg(Arg: 0); |
163 | if (NDRangeArg->getType().getUnqualifiedType().getAsString() != "ndrange_t" ) { |
164 | Diag(Loc: NDRangeArg->getBeginLoc(), DiagID: diag::err_opencl_builtin_expected_type) |
165 | << TheCall->getDirectCallee() << "'ndrange_t'" ; |
166 | return true; |
167 | } |
168 | |
169 | Expr *BlockArg = TheCall->getArg(Arg: 1); |
170 | if (!isBlockPointer(Arg: BlockArg)) { |
171 | Diag(Loc: BlockArg->getBeginLoc(), DiagID: diag::err_opencl_builtin_expected_type) |
172 | << TheCall->getDirectCallee() << "block" ; |
173 | return true; |
174 | } |
175 | return checkBlockArgs(S&: SemaRef, BlockArg); |
176 | } |
177 | |
178 | bool SemaOpenCL::checkBuiltinKernelWorkGroupSize(CallExpr *TheCall) { |
179 | if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1)) |
180 | return true; |
181 | |
182 | Expr *BlockArg = TheCall->getArg(Arg: 0); |
183 | if (!isBlockPointer(Arg: BlockArg)) { |
184 | Diag(Loc: BlockArg->getBeginLoc(), DiagID: diag::err_opencl_builtin_expected_type) |
185 | << TheCall->getDirectCallee() << "block" ; |
186 | return true; |
187 | } |
188 | return checkBlockArgs(S&: SemaRef, BlockArg); |
189 | } |
190 | |
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(Loc: E->getBeginLoc(), |
197 | DiagID: diag::err_opencl_enqueue_kernel_invalid_local_size_type); |
198 | return true; |
199 | } |
200 | // Potentially emit standard warnings for implicit conversions if enabled |
201 | // using -Wconversion. |
202 | S.CheckImplicitConversion(E, T: IntT, CC: E->getBeginLoc()); |
203 | return false; |
204 | } |
205 | |
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, E: TheCall->getArg(Arg: I), |
211 | IntT: S.Context.getSizeType()); |
212 | return IllegalParams; |
213 | } |
214 | |
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, |
218 | Expr *BlockArg, |
219 | unsigned NumNonVarArgs) { |
220 | const BlockPointerType *BPT = |
221 | cast<BlockPointerType>(Val: BlockArg->getType().getCanonicalType()); |
222 | unsigned NumBlockParams = |
223 | BPT->getPointeeType()->castAs<FunctionProtoType>()->getNumParams(); |
224 | unsigned TotalNumArgs = TheCall->getNumArgs(); |
225 | |
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(Loc: TheCall->getBeginLoc(), |
230 | DiagID: diag::err_opencl_enqueue_kernel_local_size_args); |
231 | return true; |
232 | } |
233 | |
234 | // Check that the sizes of the local memory are specified by integers. |
235 | return checkOpenCLEnqueueLocalSizeArgs(S, TheCall, Start: NumNonVarArgs, |
236 | End: TotalNumArgs - 1); |
237 | } |
238 | |
239 | bool SemaOpenCL::checkBuiltinEnqueueKernel(CallExpr *TheCall) { |
240 | ASTContext &Context = getASTContext(); |
241 | unsigned NumArgs = TheCall->getNumArgs(); |
242 | |
243 | if (NumArgs < 4) { |
244 | Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_typecheck_call_too_few_args_at_least) |
245 | << 0 << 4 << NumArgs << /*is non object*/ 0; |
246 | return true; |
247 | } |
248 | |
249 | Expr *Arg0 = TheCall->getArg(Arg: 0); |
250 | Expr *Arg1 = TheCall->getArg(Arg: 1); |
251 | Expr *Arg2 = TheCall->getArg(Arg: 2); |
252 | Expr *Arg3 = TheCall->getArg(Arg: 3); |
253 | |
254 | // First argument always needs to be a queue_t type. |
255 | if (!Arg0->getType()->isQueueT()) { |
256 | Diag(Loc: TheCall->getArg(Arg: 0)->getBeginLoc(), |
257 | DiagID: diag::err_opencl_builtin_expected_type) |
258 | << TheCall->getDirectCallee() << getASTContext().OCLQueueTy; |
259 | return true; |
260 | } |
261 | |
262 | // Second argument always needs to be a kernel_enqueue_flags_t enum value. |
263 | if (!Arg1->getType()->isIntegerType()) { |
264 | Diag(Loc: TheCall->getArg(Arg: 1)->getBeginLoc(), |
265 | DiagID: diag::err_opencl_builtin_expected_type) |
266 | << TheCall->getDirectCallee() << "'kernel_enqueue_flags_t' (i.e. uint)" ; |
267 | return true; |
268 | } |
269 | |
270 | // Third argument is always an ndrange_t type. |
271 | if (Arg2->getType().getUnqualifiedType().getAsString() != "ndrange_t" ) { |
272 | Diag(Loc: TheCall->getArg(Arg: 2)->getBeginLoc(), |
273 | DiagID: diag::err_opencl_builtin_expected_type) |
274 | << TheCall->getDirectCallee() << "'ndrange_t'" ; |
275 | return true; |
276 | } |
277 | |
278 | // With four arguments, there is only one form that the function could be |
279 | // called in: no events and no variable arguments. |
280 | if (NumArgs == 4) { |
281 | // check that the last argument is the right block type. |
282 | if (!isBlockPointer(Arg: Arg3)) { |
283 | Diag(Loc: Arg3->getBeginLoc(), DiagID: diag::err_opencl_builtin_expected_type) |
284 | << TheCall->getDirectCallee() << "block" ; |
285 | return true; |
286 | } |
287 | // we have a block type, check the prototype |
288 | const BlockPointerType *BPT = |
289 | cast<BlockPointerType>(Val: Arg3->getType().getCanonicalType()); |
290 | if (BPT->getPointeeType()->castAs<FunctionProtoType>()->getNumParams() > |
291 | 0) { |
292 | Diag(Loc: Arg3->getBeginLoc(), DiagID: diag::err_opencl_enqueue_kernel_blocks_no_args); |
293 | return true; |
294 | } |
295 | return false; |
296 | } |
297 | // we can have block + varargs. |
298 | if (isBlockPointer(Arg: Arg3)) |
299 | return (checkBlockArgs(S&: SemaRef, BlockArg: Arg3) || |
300 | checkOpenCLEnqueueVariadicArgs(S&: SemaRef, TheCall, BlockArg: Arg3, NumNonVarArgs: 4)); |
301 | // last two cases with either exactly 7 args or 7 args and varargs. |
302 | if (NumArgs >= 7) { |
303 | // check common block argument. |
304 | Expr *Arg6 = TheCall->getArg(Arg: 6); |
305 | if (!isBlockPointer(Arg: Arg6)) { |
306 | Diag(Loc: Arg6->getBeginLoc(), DiagID: diag::err_opencl_builtin_expected_type) |
307 | << TheCall->getDirectCallee() << "block" ; |
308 | return true; |
309 | } |
310 | if (checkBlockArgs(S&: SemaRef, BlockArg: Arg6)) |
311 | return true; |
312 | |
313 | // Forth argument has to be any integer type. |
314 | if (!Arg3->getType()->isIntegerType()) { |
315 | Diag(Loc: TheCall->getArg(Arg: 3)->getBeginLoc(), |
316 | DiagID: diag::err_opencl_builtin_expected_type) |
317 | << TheCall->getDirectCallee() << "integer" ; |
318 | return true; |
319 | } |
320 | // check remaining common arguments. |
321 | Expr *Arg4 = TheCall->getArg(Arg: 4); |
322 | Expr *Arg5 = TheCall->getArg(Arg: 5); |
323 | |
324 | // Fifth argument is always passed as a pointer to clk_event_t. |
325 | if (!Arg4->isNullPointerConstant(Ctx&: Context, |
326 | NPC: Expr::NPC_ValueDependentIsNotNull) && |
327 | !Arg4->getType()->getPointeeOrArrayElementType()->isClkEventT()) { |
328 | Diag(Loc: TheCall->getArg(Arg: 4)->getBeginLoc(), |
329 | DiagID: diag::err_opencl_builtin_expected_type) |
330 | << TheCall->getDirectCallee() |
331 | << Context.getPointerType(T: Context.OCLClkEventTy); |
332 | return true; |
333 | } |
334 | |
335 | // Sixth argument is always passed as a pointer to clk_event_t. |
336 | if (!Arg5->isNullPointerConstant(Ctx&: Context, |
337 | NPC: Expr::NPC_ValueDependentIsNotNull) && |
338 | !(Arg5->getType()->isPointerType() && |
339 | Arg5->getType()->getPointeeType()->isClkEventT())) { |
340 | Diag(Loc: TheCall->getArg(Arg: 5)->getBeginLoc(), |
341 | DiagID: diag::err_opencl_builtin_expected_type) |
342 | << TheCall->getDirectCallee() |
343 | << Context.getPointerType(T: Context.OCLClkEventTy); |
344 | return true; |
345 | } |
346 | |
347 | if (NumArgs == 7) |
348 | return false; |
349 | |
350 | return checkOpenCLEnqueueVariadicArgs(S&: SemaRef, TheCall, BlockArg: Arg6, NumNonVarArgs: 7); |
351 | } |
352 | |
353 | // None of the specific case has been detected, give generic error |
354 | Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_opencl_enqueue_kernel_incorrect_args); |
355 | return true; |
356 | } |
357 | |
358 | /// Returns OpenCL access qual. |
359 | static OpenCLAccessAttr *getOpenCLArgAccess(const Decl *D) { |
360 | return D->getAttr<OpenCLAccessAttr>(); |
361 | } |
362 | |
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(Arg: 0); |
366 | // First argument type should always be pipe. |
367 | if (!Arg0->getType()->isPipeType()) { |
368 | S.Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_first_arg) |
369 | << Call->getDirectCallee() << Arg0->getSourceRange(); |
370 | return true; |
371 | } |
372 | OpenCLAccessAttr *AccessQual = |
373 | getOpenCLArgAccess(D: cast<DeclRefExpr>(Val: 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 |
377 | // specified. |
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(Loc: Arg0->getBeginLoc(), |
388 | DiagID: diag::err_opencl_builtin_pipe_invalid_access_modifier) |
389 | << "read_only" << Arg0->getSourceRange(); |
390 | return true; |
391 | } |
392 | break; |
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(Loc: Arg0->getBeginLoc(), |
402 | DiagID: diag::err_opencl_builtin_pipe_invalid_access_modifier) |
403 | << "write_only" << Arg0->getSourceRange(); |
404 | return true; |
405 | } |
406 | break; |
407 | default: |
408 | break; |
409 | } |
410 | return false; |
411 | } |
412 | |
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(Arg: 0); |
416 | const Expr *ArgIdx = Call->getArg(Arg: Idx); |
417 | const PipeType *PipeTy = cast<PipeType>(Val: 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. |
422 | if (!ArgTy || |
423 | !S.Context.hasSameType( |
424 | T1: EltTy, T2: ArgTy->getPointeeType()->getCanonicalTypeInternal())) { |
425 | S.Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_invalid_arg) |
426 | << Call->getDirectCallee() << S.Context.getPointerType(T: EltTy) |
427 | << ArgIdx->getType() << ArgIdx->getSourceRange(); |
428 | return true; |
429 | } |
430 | return false; |
431 | } |
432 | |
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()) { |
437 | case 2: |
438 | if (checkPipeArg(S&: SemaRef, Call)) |
439 | return true; |
440 | // The call with 2 arguments should be |
441 | // read/write_pipe(pipe T, T*). |
442 | // Check packet type T. |
443 | if (checkPipePacketType(S&: SemaRef, Call, Idx: 1)) |
444 | return true; |
445 | break; |
446 | |
447 | case 4: { |
448 | if (checkPipeArg(S&: SemaRef, Call)) |
449 | return true; |
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(Arg: 1)->getType()->isReserveIDT()) { |
454 | Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_invalid_arg) |
455 | << Call->getDirectCallee() << getASTContext().OCLReserveIDTy |
456 | << Call->getArg(Arg: 1)->getType() << Call->getArg(Arg: 1)->getSourceRange(); |
457 | return true; |
458 | } |
459 | |
460 | // Check the index. |
461 | const Expr *Arg2 = Call->getArg(Arg: 2); |
462 | if (!Arg2->getType()->isIntegerType() && |
463 | !Arg2->getType()->isUnsignedIntegerType()) { |
464 | Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_invalid_arg) |
465 | << Call->getDirectCallee() << getASTContext().UnsignedIntTy |
466 | << Arg2->getType() << Arg2->getSourceRange(); |
467 | return true; |
468 | } |
469 | |
470 | // Check packet type T. |
471 | if (checkPipePacketType(S&: SemaRef, Call, Idx: 3)) |
472 | return true; |
473 | } break; |
474 | default: |
475 | Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_arg_num) |
476 | << Call->getDirectCallee() << Call->getSourceRange(); |
477 | return true; |
478 | } |
479 | |
480 | return false; |
481 | } |
482 | |
483 | bool SemaOpenCL::checkBuiltinReserveRWPipe(CallExpr *Call) { |
484 | if (SemaRef.checkArgCount(Call, DesiredArgCount: 2)) |
485 | return true; |
486 | |
487 | if (checkPipeArg(S&: SemaRef, Call)) |
488 | return true; |
489 | |
490 | // Check the reserve size. |
491 | if (!Call->getArg(Arg: 1)->getType()->isIntegerType() && |
492 | !Call->getArg(Arg: 1)->getType()->isUnsignedIntegerType()) { |
493 | Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_invalid_arg) |
494 | << Call->getDirectCallee() << getASTContext().UnsignedIntTy |
495 | << Call->getArg(Arg: 1)->getType() << Call->getArg(Arg: 1)->getSourceRange(); |
496 | return true; |
497 | } |
498 | |
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); |
503 | |
504 | return false; |
505 | } |
506 | |
507 | bool SemaOpenCL::checkBuiltinCommitRWPipe(CallExpr *Call) { |
508 | if (SemaRef.checkArgCount(Call, DesiredArgCount: 2)) |
509 | return true; |
510 | |
511 | if (checkPipeArg(S&: SemaRef, Call)) |
512 | return true; |
513 | |
514 | // Check reserve_id_t. |
515 | if (!Call->getArg(Arg: 1)->getType()->isReserveIDT()) { |
516 | Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_invalid_arg) |
517 | << Call->getDirectCallee() << getASTContext().OCLReserveIDTy |
518 | << Call->getArg(Arg: 1)->getType() << Call->getArg(Arg: 1)->getSourceRange(); |
519 | return true; |
520 | } |
521 | |
522 | return false; |
523 | } |
524 | |
525 | bool SemaOpenCL::checkBuiltinPipePackets(CallExpr *Call) { |
526 | if (SemaRef.checkArgCount(Call, DesiredArgCount: 1)) |
527 | return true; |
528 | |
529 | if (!Call->getArg(Arg: 0)->getType()->isPipeType()) { |
530 | Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_pipe_first_arg) |
531 | << Call->getDirectCallee() << Call->getArg(Arg: 0)->getSourceRange(); |
532 | return true; |
533 | } |
534 | |
535 | return false; |
536 | } |
537 | |
538 | bool SemaOpenCL::checkBuiltinToAddr(unsigned BuiltinID, CallExpr *Call) { |
539 | if (SemaRef.checkArgCount(Call, DesiredArgCount: 1)) |
540 | return true; |
541 | |
542 | auto RT = Call->getArg(Arg: 0)->getType(); |
543 | if (!RT->isPointerType() || |
544 | RT->getPointeeType().getAddressSpace() == LangAS::opencl_constant) { |
545 | Diag(Loc: Call->getBeginLoc(), DiagID: diag::err_opencl_builtin_to_addr_invalid_arg) |
546 | << Call->getArg(Arg: 0) << Call->getDirectCallee() << Call->getSourceRange(); |
547 | return true; |
548 | } |
549 | |
550 | if (RT->getPointeeType().getAddressSpace() != LangAS::opencl_generic) { |
551 | Diag(Loc: Call->getArg(Arg: 0)->getBeginLoc(), |
552 | DiagID: diag::warn_opencl_generic_address_space_arg) |
553 | << Call->getDirectCallee()->getNameInfo().getAsString() |
554 | << Call->getArg(Arg: 0)->getSourceRange(); |
555 | } |
556 | |
557 | RT = RT->getPointeeType(); |
558 | auto Qual = RT.getQualifiers(); |
559 | switch (BuiltinID) { |
560 | case Builtin::BIto_global: |
561 | Qual.setAddressSpace(LangAS::opencl_global); |
562 | break; |
563 | case Builtin::BIto_local: |
564 | Qual.setAddressSpace(LangAS::opencl_local); |
565 | break; |
566 | case Builtin::BIto_private: |
567 | Qual.setAddressSpace(LangAS::opencl_private); |
568 | break; |
569 | default: |
570 | llvm_unreachable("Invalid builtin function" ); |
571 | } |
572 | Call->setType(getASTContext().getPointerType( |
573 | T: getASTContext().getQualifiedType(T: RT.getUnqualifiedType(), Qs: Qual))); |
574 | |
575 | return false; |
576 | } |
577 | |
578 | } // namespace clang |
579 | |