| 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 | |
| 22 | namespace clang { |
| 23 | SemaOpenCL::SemaOpenCL(Sema &S) : SemaBase(S) {} |
| 24 | |
| 25 | void 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 | |
| 34 | void 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 | |
| 81 | void 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 | |
| 101 | static 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. |
| 107 | static 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 | |
| 140 | bool 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 | |
| 156 | bool 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 | |
| 180 | bool 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. |
| 194 | static 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 | |
| 208 | static 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. |
| 219 | static 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 | |
| 241 | bool 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. |
| 361 | static OpenCLAccessAttr *getOpenCLArgAccess(const Decl *D) { |
| 362 | return D->getAttr<OpenCLAccessAttr>(); |
| 363 | } |
| 364 | |
| 365 | /// Returns true if pipe element type is different from the pointer. |
| 366 | static 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. |
| 416 | static 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 | |
| 435 | bool 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 | |
| 485 | bool 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 | |
| 509 | bool 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 | |
| 527 | bool 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 | |
| 540 | bool 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 | |
| 581 | void 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 | |