Home | History | Annotate | Download | only in Sema
      1 //===--- SemaCUDA.cpp - Semantic Analysis for CUDA constructs -------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 /// \file
     10 /// \brief This file implements semantic analysis for CUDA constructs.
     11 ///
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "clang/Sema/Sema.h"
     15 #include "clang/AST/ASTContext.h"
     16 #include "clang/AST/Decl.h"
     17 #include "clang/Lex/Preprocessor.h"
     18 #include "clang/Sema/SemaDiagnostic.h"
     19 #include "llvm/ADT/Optional.h"
     20 #include "llvm/ADT/SmallVector.h"
     21 using namespace clang;
     22 
     23 ExprResult Sema::ActOnCUDAExecConfigExpr(Scope *S, SourceLocation LLLLoc,
     24                                          MultiExprArg ExecConfig,
     25                                          SourceLocation GGGLoc) {
     26   FunctionDecl *ConfigDecl = Context.getcudaConfigureCallDecl();
     27   if (!ConfigDecl)
     28     return ExprError(Diag(LLLLoc, diag::err_undeclared_var_use)
     29                      << "cudaConfigureCall");
     30   QualType ConfigQTy = ConfigDecl->getType();
     31 
     32   DeclRefExpr *ConfigDR = new (Context)
     33       DeclRefExpr(ConfigDecl, false, ConfigQTy, VK_LValue, LLLLoc);
     34   MarkFunctionReferenced(LLLLoc, ConfigDecl);
     35 
     36   return ActOnCallExpr(S, ConfigDR, LLLLoc, ExecConfig, GGGLoc, nullptr,
     37                        /*IsExecConfig=*/true);
     38 }
     39 
     40 /// IdentifyCUDATarget - Determine the CUDA compilation target for this function
     41 Sema::CUDAFunctionTarget Sema::IdentifyCUDATarget(const FunctionDecl *D) {
     42   if (D->hasAttr<CUDAInvalidTargetAttr>())
     43     return CFT_InvalidTarget;
     44 
     45   if (D->hasAttr<CUDAGlobalAttr>())
     46     return CFT_Global;
     47 
     48   if (D->hasAttr<CUDADeviceAttr>()) {
     49     if (D->hasAttr<CUDAHostAttr>())
     50       return CFT_HostDevice;
     51     return CFT_Device;
     52   } else if (D->hasAttr<CUDAHostAttr>()) {
     53     return CFT_Host;
     54   } else if (D->isImplicit()) {
     55     // Some implicit declarations (like intrinsic functions) are not marked.
     56     // Set the most lenient target on them for maximal flexibility.
     57     return CFT_HostDevice;
     58   }
     59 
     60   return CFT_Host;
     61 }
     62 
     63 bool Sema::CheckCUDATarget(const FunctionDecl *Caller,
     64                            const FunctionDecl *Callee) {
     65   // The CUDADisableTargetCallChecks short-circuits this check: we assume all
     66   // cross-target calls are valid.
     67   if (getLangOpts().CUDADisableTargetCallChecks)
     68     return false;
     69 
     70   CUDAFunctionTarget CallerTarget = IdentifyCUDATarget(Caller),
     71                      CalleeTarget = IdentifyCUDATarget(Callee);
     72 
     73   // If one of the targets is invalid, the check always fails, no matter what
     74   // the other target is.
     75   if (CallerTarget == CFT_InvalidTarget || CalleeTarget == CFT_InvalidTarget)
     76     return true;
     77 
     78   // CUDA B.1.1 "The __device__ qualifier declares a function that is [...]
     79   // Callable from the device only."
     80   if (CallerTarget == CFT_Host && CalleeTarget == CFT_Device)
     81     return true;
     82 
     83   // CUDA B.1.2 "The __global__ qualifier declares a function that is [...]
     84   // Callable from the host only."
     85   // CUDA B.1.3 "The __host__ qualifier declares a function that is [...]
     86   // Callable from the host only."
     87   if ((CallerTarget == CFT_Device || CallerTarget == CFT_Global) &&
     88       (CalleeTarget == CFT_Host || CalleeTarget == CFT_Global))
     89     return true;
     90 
     91   // CUDA B.1.3 "The __device__ and __host__ qualifiers can be used together
     92   // however, in which case the function is compiled for both the host and the
     93   // device. The __CUDA_ARCH__ macro [...] can be used to differentiate code
     94   // paths between host and device."
     95   if (CallerTarget == CFT_HostDevice && CalleeTarget != CFT_HostDevice) {
     96     // If the caller is implicit then the check always passes.
     97     if (Caller->isImplicit()) return false;
     98 
     99     bool InDeviceMode = getLangOpts().CUDAIsDevice;
    100     if (!InDeviceMode && CalleeTarget != CFT_Host)
    101         return true;
    102     if (InDeviceMode && CalleeTarget != CFT_Device) {
    103       // Allow host device functions to call host functions if explicitly
    104       // requested.
    105       if (CalleeTarget == CFT_Host &&
    106           getLangOpts().CUDAAllowHostCallsFromHostDevice) {
    107         Diag(Caller->getLocation(),
    108              diag::warn_host_calls_from_host_device)
    109             << Callee->getNameAsString() << Caller->getNameAsString();
    110         return false;
    111       }
    112 
    113       return true;
    114     }
    115   }
    116 
    117   return false;
    118 }
    119 
    120 /// When an implicitly-declared special member has to invoke more than one
    121 /// base/field special member, conflicts may occur in the targets of these
    122 /// members. For example, if one base's member __host__ and another's is
    123 /// __device__, it's a conflict.
    124 /// This function figures out if the given targets \param Target1 and
    125 /// \param Target2 conflict, and if they do not it fills in
    126 /// \param ResolvedTarget with a target that resolves for both calls.
    127 /// \return true if there's a conflict, false otherwise.
    128 static bool
    129 resolveCalleeCUDATargetConflict(Sema::CUDAFunctionTarget Target1,
    130                                 Sema::CUDAFunctionTarget Target2,
    131                                 Sema::CUDAFunctionTarget *ResolvedTarget) {
    132   if (Target1 == Sema::CFT_Global && Target2 == Sema::CFT_Global) {
    133     // TODO: this shouldn't happen, really. Methods cannot be marked __global__.
    134     // Clang should detect this earlier and produce an error. Then this
    135     // condition can be changed to an assertion.
    136     return true;
    137   }
    138 
    139   if (Target1 == Sema::CFT_HostDevice) {
    140     *ResolvedTarget = Target2;
    141   } else if (Target2 == Sema::CFT_HostDevice) {
    142     *ResolvedTarget = Target1;
    143   } else if (Target1 != Target2) {
    144     return true;
    145   } else {
    146     *ResolvedTarget = Target1;
    147   }
    148 
    149   return false;
    150 }
    151 
    152 bool Sema::inferCUDATargetForImplicitSpecialMember(CXXRecordDecl *ClassDecl,
    153                                                    CXXSpecialMember CSM,
    154                                                    CXXMethodDecl *MemberDecl,
    155                                                    bool ConstRHS,
    156                                                    bool Diagnose) {
    157   llvm::Optional<CUDAFunctionTarget> InferredTarget;
    158 
    159   // We're going to invoke special member lookup; mark that these special
    160   // members are called from this one, and not from its caller.
    161   ContextRAII MethodContext(*this, MemberDecl);
    162 
    163   // Look for special members in base classes that should be invoked from here.
    164   // Infer the target of this member base on the ones it should call.
    165   // Skip direct and indirect virtual bases for abstract classes.
    166   llvm::SmallVector<const CXXBaseSpecifier *, 16> Bases;
    167   for (const auto &B : ClassDecl->bases()) {
    168     if (!B.isVirtual()) {
    169       Bases.push_back(&B);
    170     }
    171   }
    172 
    173   if (!ClassDecl->isAbstract()) {
    174     for (const auto &VB : ClassDecl->vbases()) {
    175       Bases.push_back(&VB);
    176     }
    177   }
    178 
    179   for (const auto *B : Bases) {
    180     const RecordType *BaseType = B->getType()->getAs<RecordType>();
    181     if (!BaseType) {
    182       continue;
    183     }
    184 
    185     CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(BaseType->getDecl());
    186     Sema::SpecialMemberOverloadResult *SMOR =
    187         LookupSpecialMember(BaseClassDecl, CSM,
    188                             /* ConstArg */ ConstRHS,
    189                             /* VolatileArg */ false,
    190                             /* RValueThis */ false,
    191                             /* ConstThis */ false,
    192                             /* VolatileThis */ false);
    193 
    194     if (!SMOR || !SMOR->getMethod()) {
    195       continue;
    196     }
    197 
    198     CUDAFunctionTarget BaseMethodTarget = IdentifyCUDATarget(SMOR->getMethod());
    199     if (!InferredTarget.hasValue()) {
    200       InferredTarget = BaseMethodTarget;
    201     } else {
    202       bool ResolutionError = resolveCalleeCUDATargetConflict(
    203           InferredTarget.getValue(), BaseMethodTarget,
    204           InferredTarget.getPointer());
    205       if (ResolutionError) {
    206         if (Diagnose) {
    207           Diag(ClassDecl->getLocation(),
    208                diag::note_implicit_member_target_infer_collision)
    209               << (unsigned)CSM << InferredTarget.getValue() << BaseMethodTarget;
    210         }
    211         MemberDecl->addAttr(CUDAInvalidTargetAttr::CreateImplicit(Context));
    212         return true;
    213       }
    214     }
    215   }
    216 
    217   // Same as for bases, but now for special members of fields.
    218   for (const auto *F : ClassDecl->fields()) {
    219     if (F->isInvalidDecl()) {
    220       continue;
    221     }
    222 
    223     const RecordType *FieldType =
    224         Context.getBaseElementType(F->getType())->getAs<RecordType>();
    225     if (!FieldType) {
    226       continue;
    227     }
    228 
    229     CXXRecordDecl *FieldRecDecl = cast<CXXRecordDecl>(FieldType->getDecl());
    230     Sema::SpecialMemberOverloadResult *SMOR =
    231         LookupSpecialMember(FieldRecDecl, CSM,
    232                             /* ConstArg */ ConstRHS && !F->isMutable(),
    233                             /* VolatileArg */ false,
    234                             /* RValueThis */ false,
    235                             /* ConstThis */ false,
    236                             /* VolatileThis */ false);
    237 
    238     if (!SMOR || !SMOR->getMethod()) {
    239       continue;
    240     }
    241 
    242     CUDAFunctionTarget FieldMethodTarget =
    243         IdentifyCUDATarget(SMOR->getMethod());
    244     if (!InferredTarget.hasValue()) {
    245       InferredTarget = FieldMethodTarget;
    246     } else {
    247       bool ResolutionError = resolveCalleeCUDATargetConflict(
    248           InferredTarget.getValue(), FieldMethodTarget,
    249           InferredTarget.getPointer());
    250       if (ResolutionError) {
    251         if (Diagnose) {
    252           Diag(ClassDecl->getLocation(),
    253                diag::note_implicit_member_target_infer_collision)
    254               << (unsigned)CSM << InferredTarget.getValue()
    255               << FieldMethodTarget;
    256         }
    257         MemberDecl->addAttr(CUDAInvalidTargetAttr::CreateImplicit(Context));
    258         return true;
    259       }
    260     }
    261   }
    262 
    263   if (InferredTarget.hasValue()) {
    264     if (InferredTarget.getValue() == CFT_Device) {
    265       MemberDecl->addAttr(CUDADeviceAttr::CreateImplicit(Context));
    266     } else if (InferredTarget.getValue() == CFT_Host) {
    267       MemberDecl->addAttr(CUDAHostAttr::CreateImplicit(Context));
    268     } else {
    269       MemberDecl->addAttr(CUDADeviceAttr::CreateImplicit(Context));
    270       MemberDecl->addAttr(CUDAHostAttr::CreateImplicit(Context));
    271     }
    272   } else {
    273     // If no target was inferred, mark this member as __host__ __device__;
    274     // it's the least restrictive option that can be invoked from any target.
    275     MemberDecl->addAttr(CUDADeviceAttr::CreateImplicit(Context));
    276     MemberDecl->addAttr(CUDAHostAttr::CreateImplicit(Context));
    277   }
    278 
    279   return false;
    280 }
    281