| 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->getArg(Arg: 0)->getBeginLoc(), |
| 546 | DiagID: diag::err_opencl_builtin_to_addr_invalid_arg) |
| 547 | << Call->getArg(Arg: 0) << Call->getDirectCallee() << Call->getSourceRange(); |
| 548 | return true; |
| 549 | } |
| 550 | |
| 551 | if (RT->getPointeeType().getAddressSpace() != LangAS::opencl_generic) { |
| 552 | Diag(Loc: Call->getArg(Arg: 0)->getBeginLoc(), |
| 553 | DiagID: diag::warn_opencl_generic_address_space_arg) |
| 554 | << Call->getDirectCallee()->getNameInfo().getAsString() |
| 555 | << Call->getArg(Arg: 0)->getSourceRange(); |
| 556 | } |
| 557 | |
| 558 | RT = RT->getPointeeType(); |
| 559 | auto Qual = RT.getQualifiers(); |
| 560 | switch (BuiltinID) { |
| 561 | case Builtin::BIto_global: |
| 562 | Qual.setAddressSpace(LangAS::opencl_global); |
| 563 | break; |
| 564 | case Builtin::BIto_local: |
| 565 | Qual.setAddressSpace(LangAS::opencl_local); |
| 566 | break; |
| 567 | case Builtin::BIto_private: |
| 568 | Qual.setAddressSpace(LangAS::opencl_private); |
| 569 | break; |
| 570 | default: |
| 571 | llvm_unreachable("Invalid builtin function" ); |
| 572 | } |
| 573 | Call->setType(getASTContext().getPointerType( |
| 574 | T: getASTContext().getQualifiedType(T: RT.getUnqualifiedType(), Qs: Qual))); |
| 575 | |
| 576 | return false; |
| 577 | } |
| 578 | |
| 579 | } // namespace clang |
| 580 | |