580 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8/// \file9/// 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 20namespace clang {21SemaOpenCL::SemaOpenCL(Sema &S) : SemaBase(S) {}22 23void SemaOpenCL::handleNoSVMAttr(Decl *D, const ParsedAttr &AL) {24 if (getLangOpts().getOpenCLCompatibleVersion() < 200)25 Diag(AL.getLoc(), diag::err_attribute_requires_opencl_version)26 << AL << "2.0" << 1;27 else28 Diag(AL.getLoc(), diag::warn_opencl_attr_deprecated_ignored)29 << AL << getLangOpts().getOpenCLVersionString();30}31 32void 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(AL.getLoc(), diag::warn_duplicate_declspec)41 << AL.getAttrName()->getName() << AL.getRange();42 } else {43 Diag(AL.getLoc(), 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 that51 // an image object can be read and written. OpenCL v2.0 s6.13.6 - A kernel52 // cannot read from and write to the same pipe object. Using the read_write53 // (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 the55 // __opencl_c_read_write_images feature, image objects specified as arguments56 // to a kernel can additionally be declared to be read-write.57 // C++ for OpenCL 1.0 inherits rule from OpenCL C v2.0.58 // C++ for OpenCL 2021 inherits rule from OpenCL C v3.0.59 if (const auto *PDecl = dyn_cast<ParmVarDecl>(D)) {60 const Type *DeclTy = PDecl->getType().getCanonicalType().getTypePtr();61 if (AL.getAttrName()->getName().contains("read_write")) {62 bool ReadWriteImagesUnsupported =63 (getLangOpts().getOpenCLCompatibleVersion() < 200) ||64 (getLangOpts().getOpenCLCompatibleVersion() == 300 &&65 !SemaRef.getOpenCLOptions().isSupported(66 "__opencl_c_read_write_images", getLangOpts()));67 if (ReadWriteImagesUnsupported || DeclTy->isPipeType()) {68 Diag(AL.getLoc(), diag::err_opencl_invalid_read_write)69 << AL << PDecl->getType() << DeclTy->isImageType();70 D->setInvalidDecl(true);71 return;72 }73 }74 }75 76 D->addAttr(::new (getASTContext()) OpenCLAccessAttr(getASTContext(), AL));77}78 79void SemaOpenCL::handleSubGroupSize(Decl *D, const ParsedAttr &AL) {80 uint32_t SGSize;81 const Expr *E = AL.getArgAsExpr(0);82 if (!SemaRef.checkUInt32Argument(AL, E, SGSize))83 return;84 if (SGSize == 0) {85 Diag(AL.getLoc(), 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(AL.getLoc(), diag::warn_duplicate_attribute) << AL;94 95 D->addAttr(::new (getASTContext())96 OpenCLIntelReqdSubGroupSizeAttr(getASTContext(), AL, SGSize));97}98 99static 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 local104/// void*, which is a requirement of device side enqueue.105static bool checkBlockArgs(Sema &S, Expr *BlockArg) {106 const BlockPointerType *BPT =107 cast<BlockPointerType>(BlockArg->getType().getCanonicalType());108 ArrayRef<QualType> Params =109 BPT->getPointeeType()->castAs<FunctionProtoType>()->getParamTypes();110 unsigned ArgCounter = 0;111 bool IllegalParams = false;112 // Iterate through the block parameters until either one is found that is not113 // 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 passed120 // (BlockExpr) then we can point straight to the offending argument,121 // else we just point to the variable reference.122 SourceLocation ErrorLoc;123 if (isa<BlockExpr>(BlockArg)) {124 BlockDecl *BD = cast<BlockExpr>(BlockArg)->getBlockDecl();125 ErrorLoc = BD->getParamDecl(ArgCounter)->getBeginLoc();126 } else if (isa<DeclRefExpr>(BlockArg)) {127 ErrorLoc = cast<DeclRefExpr>(BlockArg)->getBeginLoc();128 }129 S.Diag(ErrorLoc,130 diag::err_opencl_enqueue_kernel_blocks_non_local_void_args);131 IllegalParams = true;132 }133 }134 135 return IllegalParams;136}137 138bool SemaOpenCL::checkSubgroupExt(CallExpr *Call) {139 // OpenCL device can support extension but not the feature as extension140 // requires subgroup independent forward progress, but subgroup independent141 // forward progress is optional in OpenCL C 3.0 __opencl_c_subgroups feature.142 if (!SemaRef.getOpenCLOptions().isSupported("cl_khr_subgroups",143 getLangOpts()) &&144 !SemaRef.getOpenCLOptions().isSupported("__opencl_c_subgroups",145 getLangOpts())) {146 Diag(Call->getBeginLoc(), 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 154bool SemaOpenCL::checkBuiltinNDRangeAndBlock(CallExpr *TheCall) {155 if (SemaRef.checkArgCount(TheCall, 2))156 return true;157 158 if (checkSubgroupExt(TheCall))159 return true;160 161 // First argument is an ndrange_t type.162 Expr *NDRangeArg = TheCall->getArg(0);163 if (NDRangeArg->getType().getUnqualifiedType().getAsString() != "ndrange_t") {164 Diag(NDRangeArg->getBeginLoc(), diag::err_opencl_builtin_expected_type)165 << TheCall->getDirectCallee() << "'ndrange_t'";166 return true;167 }168 169 Expr *BlockArg = TheCall->getArg(1);170 if (!isBlockPointer(BlockArg)) {171 Diag(BlockArg->getBeginLoc(), diag::err_opencl_builtin_expected_type)172 << TheCall->getDirectCallee() << "block";173 return true;174 }175 return checkBlockArgs(SemaRef, BlockArg);176}177 178bool SemaOpenCL::checkBuiltinKernelWorkGroupSize(CallExpr *TheCall) {179 if (SemaRef.checkArgCount(TheCall, 1))180 return true;181 182 Expr *BlockArg = TheCall->getArg(0);183 if (!isBlockPointer(BlockArg)) {184 Diag(BlockArg->getBeginLoc(), diag::err_opencl_builtin_expected_type)185 << TheCall->getDirectCallee() << "block";186 return true;187 }188 return checkBlockArgs(SemaRef, BlockArg);189}190 191/// Diagnose integer type and any valid implicit conversion to it.192static bool checkOpenCLEnqueueIntType(Sema &S, Expr *E, const QualType &IntT) {193 // Taking into account implicit conversions,194 // allow any integer.195 if (!E->getType()->isIntegerType()) {196 S.Diag(E->getBeginLoc(),197 diag::err_opencl_enqueue_kernel_invalid_local_size_type);198 return true;199 }200 // Potentially emit standard warnings for implicit conversions if enabled201 // using -Wconversion.202 S.CheckImplicitConversion(E, IntT, E->getBeginLoc());203 return false;204}205 206static bool checkOpenCLEnqueueLocalSizeArgs(Sema &S, CallExpr *TheCall,207 unsigned Start, unsigned End) {208 bool IllegalParams = false;209 for (unsigned I = Start; I <= End; ++I)210 IllegalParams |= checkOpenCLEnqueueIntType(S, TheCall->getArg(I),211 S.Context.getSizeType());212 return IllegalParams;213}214 215/// OpenCL v2.0, s6.13.17.1 - Check that sizes are provided for all216/// 'local void*' parameter of passed block.217static bool checkOpenCLEnqueueVariadicArgs(Sema &S, CallExpr *TheCall,218 Expr *BlockArg,219 unsigned NumNonVarArgs) {220 const BlockPointerType *BPT =221 cast<BlockPointerType>(BlockArg->getType().getCanonicalType());222 unsigned NumBlockParams =223 BPT->getPointeeType()->castAs<FunctionProtoType>()->getNumParams();224 unsigned TotalNumArgs = TheCall->getNumArgs();225 226 // For each argument passed to the block, a corresponding uint needs to227 // be passed to describe the size of the local memory.228 if (TotalNumArgs != NumBlockParams + NumNonVarArgs) {229 S.Diag(TheCall->getBeginLoc(),230 diag::err_opencl_enqueue_kernel_local_size_args);231 return true;232 }233 234 // Check that the sizes of the local memory are specified by integers.235 return checkOpenCLEnqueueLocalSizeArgs(S, TheCall, NumNonVarArgs,236 TotalNumArgs - 1);237}238 239bool SemaOpenCL::checkBuiltinEnqueueKernel(CallExpr *TheCall) {240 ASTContext &Context = getASTContext();241 unsigned NumArgs = TheCall->getNumArgs();242 243 if (NumArgs < 4) {244 Diag(TheCall->getBeginLoc(), 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(0);250 Expr *Arg1 = TheCall->getArg(1);251 Expr *Arg2 = TheCall->getArg(2);252 Expr *Arg3 = TheCall->getArg(3);253 254 // First argument always needs to be a queue_t type.255 if (!Arg0->getType()->isQueueT()) {256 Diag(TheCall->getArg(0)->getBeginLoc(),257 diag::err_opencl_builtin_expected_type)258 << TheCall->getDirectCallee() << getASTContext().OCLQueueTy;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(TheCall->getArg(1)->getBeginLoc(),265 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(TheCall->getArg(2)->getBeginLoc(),273 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 be279 // 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(Arg3)) {283 Diag(Arg3->getBeginLoc(), diag::err_opencl_builtin_expected_type)284 << TheCall->getDirectCallee() << "block";285 return true;286 }287 // we have a block type, check the prototype288 const BlockPointerType *BPT =289 cast<BlockPointerType>(Arg3->getType().getCanonicalType());290 if (BPT->getPointeeType()->castAs<FunctionProtoType>()->getNumParams() >291 0) {292 Diag(Arg3->getBeginLoc(), 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(Arg3))299 return (checkBlockArgs(SemaRef, Arg3) ||300 checkOpenCLEnqueueVariadicArgs(SemaRef, TheCall, Arg3, 4));301 // last two cases with either exactly 7 args or 7 args and varargs.302 if (NumArgs >= 7) {303 // check common block argument.304 Expr *Arg6 = TheCall->getArg(6);305 if (!isBlockPointer(Arg6)) {306 Diag(Arg6->getBeginLoc(), diag::err_opencl_builtin_expected_type)307 << TheCall->getDirectCallee() << "block";308 return true;309 }310 if (checkBlockArgs(SemaRef, Arg6))311 return true;312 313 // Forth argument has to be any integer type.314 if (!Arg3->getType()->isIntegerType()) {315 Diag(TheCall->getArg(3)->getBeginLoc(),316 diag::err_opencl_builtin_expected_type)317 << TheCall->getDirectCallee() << "integer";318 return true;319 }320 // check remaining common arguments.321 Expr *Arg4 = TheCall->getArg(4);322 Expr *Arg5 = TheCall->getArg(5);323 324 // Fifth argument is always passed as a pointer to clk_event_t.325 if (!Arg4->isNullPointerConstant(Context,326 Expr::NPC_ValueDependentIsNotNull) &&327 !Arg4->getType()->getPointeeOrArrayElementType()->isClkEventT()) {328 Diag(TheCall->getArg(4)->getBeginLoc(),329 diag::err_opencl_builtin_expected_type)330 << TheCall->getDirectCallee()331 << Context.getPointerType(Context.OCLClkEventTy);332 return true;333 }334 335 // Sixth argument is always passed as a pointer to clk_event_t.336 if (!Arg5->isNullPointerConstant(Context,337 Expr::NPC_ValueDependentIsNotNull) &&338 !(Arg5->getType()->isPointerType() &&339 Arg5->getType()->getPointeeType()->isClkEventT())) {340 Diag(TheCall->getArg(5)->getBeginLoc(),341 diag::err_opencl_builtin_expected_type)342 << TheCall->getDirectCallee()343 << Context.getPointerType(Context.OCLClkEventTy);344 return true;345 }346 347 if (NumArgs == 7)348 return false;349 350 return checkOpenCLEnqueueVariadicArgs(SemaRef, TheCall, Arg6, 7);351 }352 353 // None of the specific case has been detected, give generic error354 Diag(TheCall->getBeginLoc(), diag::err_opencl_enqueue_kernel_incorrect_args);355 return true;356}357 358/// Returns OpenCL access qual.359static OpenCLAccessAttr *getOpenCLArgAccess(const Decl *D) {360 return D->getAttr<OpenCLAccessAttr>();361}362 363/// Returns true if pipe element type is different from the pointer.364static bool checkPipeArg(Sema &S, CallExpr *Call) {365 const Expr *Arg0 = Call->getArg(0);366 // First argument type should always be pipe.367 if (!Arg0->getType()->isPipeType()) {368 S.Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_first_arg)369 << Call->getDirectCallee() << Arg0->getSourceRange();370 return true;371 }372 OpenCLAccessAttr *AccessQual =373 getOpenCLArgAccess(cast<DeclRefExpr>(Arg0)->getDecl());374 // Validates the access qualifier is compatible with the call.375 // OpenCL v2.0 s6.13.16 - The access qualifiers for pipe should only be376 // read_only and write_only, and assumed to be read_only if no qualifier is377 // 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(Arg0->getBeginLoc(),388 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(Arg0->getBeginLoc(),402 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.414static bool checkPipePacketType(Sema &S, CallExpr *Call, unsigned Idx) {415 const Expr *Arg0 = Call->getArg(0);416 const Expr *ArgIdx = Call->getArg(Idx);417 const PipeType *PipeTy = cast<PipeType>(Arg0->getType());418 const QualType EltTy = PipeTy->getElementType();419 const PointerType *ArgTy = ArgIdx->getType()->getAs<PointerType>();420 // The Idx argument should be a pointer and the type of the pointer and421 // the type of pipe element should also be the same.422 if (!ArgTy ||423 !S.Context.hasSameType(424 EltTy, ArgTy->getPointeeType()->getCanonicalTypeInternal())) {425 S.Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg)426 << Call->getDirectCallee() << S.Context.getPointerType(EltTy)427 << ArgIdx->getType() << ArgIdx->getSourceRange();428 return true;429 }430 return false;431}432 433bool SemaOpenCL::checkBuiltinRWPipe(CallExpr *Call) {434 // OpenCL v2.0 s6.13.16.2 - The built-in read/write435 // functions have two forms.436 switch (Call->getNumArgs()) {437 case 2:438 if (checkPipeArg(SemaRef, Call))439 return true;440 // The call with 2 arguments should be441 // read/write_pipe(pipe T, T*).442 // Check packet type T.443 if (checkPipePacketType(SemaRef, Call, 1))444 return true;445 break;446 447 case 4: {448 if (checkPipeArg(SemaRef, Call))449 return true;450 // The call with 4 arguments should be451 // read/write_pipe(pipe T, reserve_id_t, uint, T*).452 // Check reserve_id_t.453 if (!Call->getArg(1)->getType()->isReserveIDT()) {454 Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg)455 << Call->getDirectCallee() << getASTContext().OCLReserveIDTy456 << Call->getArg(1)->getType() << Call->getArg(1)->getSourceRange();457 return true;458 }459 460 // Check the index.461 const Expr *Arg2 = Call->getArg(2);462 if (!Arg2->getType()->isIntegerType() &&463 !Arg2->getType()->isUnsignedIntegerType()) {464 Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg)465 << Call->getDirectCallee() << getASTContext().UnsignedIntTy466 << Arg2->getType() << Arg2->getSourceRange();467 return true;468 }469 470 // Check packet type T.471 if (checkPipePacketType(SemaRef, Call, 3))472 return true;473 } break;474 default:475 Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_arg_num)476 << Call->getDirectCallee() << Call->getSourceRange();477 return true;478 }479 480 return false;481}482 483bool SemaOpenCL::checkBuiltinReserveRWPipe(CallExpr *Call) {484 if (SemaRef.checkArgCount(Call, 2))485 return true;486 487 if (checkPipeArg(SemaRef, Call))488 return true;489 490 // Check the reserve size.491 if (!Call->getArg(1)->getType()->isIntegerType() &&492 !Call->getArg(1)->getType()->isUnsignedIntegerType()) {493 Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg)494 << Call->getDirectCallee() << getASTContext().UnsignedIntTy495 << Call->getArg(1)->getType() << Call->getArg(1)->getSourceRange();496 return true;497 }498 499 // Since return type of reserve_read/write_pipe built-in function is500 // reserve_id_t, which is not defined in the builtin def file , we used int501 // as return type and need to override the return type of these functions.502 Call->setType(getASTContext().OCLReserveIDTy);503 504 return false;505}506 507bool SemaOpenCL::checkBuiltinCommitRWPipe(CallExpr *Call) {508 if (SemaRef.checkArgCount(Call, 2))509 return true;510 511 if (checkPipeArg(SemaRef, Call))512 return true;513 514 // Check reserve_id_t.515 if (!Call->getArg(1)->getType()->isReserveIDT()) {516 Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_invalid_arg)517 << Call->getDirectCallee() << getASTContext().OCLReserveIDTy518 << Call->getArg(1)->getType() << Call->getArg(1)->getSourceRange();519 return true;520 }521 522 return false;523}524 525bool SemaOpenCL::checkBuiltinPipePackets(CallExpr *Call) {526 if (SemaRef.checkArgCount(Call, 1))527 return true;528 529 if (!Call->getArg(0)->getType()->isPipeType()) {530 Diag(Call->getBeginLoc(), diag::err_opencl_builtin_pipe_first_arg)531 << Call->getDirectCallee() << Call->getArg(0)->getSourceRange();532 return true;533 }534 535 return false;536}537 538bool SemaOpenCL::checkBuiltinToAddr(unsigned BuiltinID, CallExpr *Call) {539 if (SemaRef.checkArgCount(Call, 1))540 return true;541 542 auto RT = Call->getArg(0)->getType();543 if (!RT->isPointerType() ||544 RT->getPointeeType().getAddressSpace() == LangAS::opencl_constant) {545 Diag(Call->getArg(0)->getBeginLoc(),546 diag::err_opencl_builtin_to_addr_invalid_arg)547 << Call->getArg(0) << Call->getDirectCallee() << Call->getSourceRange();548 return true;549 }550 551 if (RT->getPointeeType().getAddressSpace() != LangAS::opencl_generic) {552 Diag(Call->getArg(0)->getBeginLoc(),553 diag::warn_opencl_generic_address_space_arg)554 << Call->getDirectCallee()->getNameInfo().getAsString()555 << Call->getArg(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 getASTContext().getQualifiedType(RT.getUnqualifiedType(), Qual)));575 576 return false;577}578 579} // namespace clang580