brintos

brintos / llvm-project-archived public Read only

0
0
Text · 20.9 KiB · f11a40e Raw
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