brintos

brintos / llvm-project-archived public Read only

0
0
Text · 93.5 KiB · 572d59e Raw
2408 lines · cpp
1//===---- CGOpenMPRuntimeGPU.cpp - Interface to OpenMP GPU Runtimes ----===//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//9// This provides a generalized class for OpenMP runtime code generation10// specialized by GPU targets NVPTX, AMDGCN and SPIR-V.11//12//===----------------------------------------------------------------------===//13 14#include "CGOpenMPRuntimeGPU.h"15#include "CGDebugInfo.h"16#include "CodeGenFunction.h"17#include "clang/AST/Attr.h"18#include "clang/AST/DeclOpenMP.h"19#include "clang/AST/OpenMPClause.h"20#include "clang/AST/StmtOpenMP.h"21#include "clang/AST/StmtVisitor.h"22#include "clang/Basic/Cuda.h"23#include "llvm/ADT/SmallPtrSet.h"24#include "llvm/Frontend/OpenMP/OMPDeviceConstants.h"25#include "llvm/Frontend/OpenMP/OMPGridValues.h"26 27using namespace clang;28using namespace CodeGen;29using namespace llvm::omp;30 31namespace {32/// Pre(post)-action for different OpenMP constructs specialized for NVPTX.33class NVPTXActionTy final : public PrePostActionTy {34  llvm::FunctionCallee EnterCallee = nullptr;35  ArrayRef<llvm::Value *> EnterArgs;36  llvm::FunctionCallee ExitCallee = nullptr;37  ArrayRef<llvm::Value *> ExitArgs;38  bool Conditional = false;39  llvm::BasicBlock *ContBlock = nullptr;40 41public:42  NVPTXActionTy(llvm::FunctionCallee EnterCallee,43                ArrayRef<llvm::Value *> EnterArgs,44                llvm::FunctionCallee ExitCallee,45                ArrayRef<llvm::Value *> ExitArgs, bool Conditional = false)46      : EnterCallee(EnterCallee), EnterArgs(EnterArgs), ExitCallee(ExitCallee),47        ExitArgs(ExitArgs), Conditional(Conditional) {}48  void Enter(CodeGenFunction &CGF) override {49    llvm::Value *EnterRes = CGF.EmitRuntimeCall(EnterCallee, EnterArgs);50    if (Conditional) {51      llvm::Value *CallBool = CGF.Builder.CreateIsNotNull(EnterRes);52      auto *ThenBlock = CGF.createBasicBlock("omp_if.then");53      ContBlock = CGF.createBasicBlock("omp_if.end");54      // Generate the branch (If-stmt)55      CGF.Builder.CreateCondBr(CallBool, ThenBlock, ContBlock);56      CGF.EmitBlock(ThenBlock);57    }58  }59  void Done(CodeGenFunction &CGF) {60    // Emit the rest of blocks/branches61    CGF.EmitBranch(ContBlock);62    CGF.EmitBlock(ContBlock, true);63  }64  void Exit(CodeGenFunction &CGF) override {65    CGF.EmitRuntimeCall(ExitCallee, ExitArgs);66  }67};68 69/// A class to track the execution mode when codegening directives within70/// a target region. The appropriate mode (SPMD|NON-SPMD) is set on entry71/// to the target region and used by containing directives such as 'parallel'72/// to emit optimized code.73class ExecutionRuntimeModesRAII {74private:75  CGOpenMPRuntimeGPU::ExecutionMode SavedExecMode =76      CGOpenMPRuntimeGPU::EM_Unknown;77  CGOpenMPRuntimeGPU::ExecutionMode &ExecMode;78 79public:80  ExecutionRuntimeModesRAII(CGOpenMPRuntimeGPU::ExecutionMode &ExecMode,81                            CGOpenMPRuntimeGPU::ExecutionMode EntryMode)82      : ExecMode(ExecMode) {83    SavedExecMode = ExecMode;84    ExecMode = EntryMode;85  }86  ~ExecutionRuntimeModesRAII() { ExecMode = SavedExecMode; }87};88 89static const ValueDecl *getPrivateItem(const Expr *RefExpr) {90  RefExpr = RefExpr->IgnoreParens();91  if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(RefExpr)) {92    const Expr *Base = ASE->getBase()->IgnoreParenImpCasts();93    while (const auto *TempASE = dyn_cast<ArraySubscriptExpr>(Base))94      Base = TempASE->getBase()->IgnoreParenImpCasts();95    RefExpr = Base;96  } else if (auto *OASE = dyn_cast<ArraySectionExpr>(RefExpr)) {97    const Expr *Base = OASE->getBase()->IgnoreParenImpCasts();98    while (const auto *TempOASE = dyn_cast<ArraySectionExpr>(Base))99      Base = TempOASE->getBase()->IgnoreParenImpCasts();100    while (const auto *TempASE = dyn_cast<ArraySubscriptExpr>(Base))101      Base = TempASE->getBase()->IgnoreParenImpCasts();102    RefExpr = Base;103  }104  RefExpr = RefExpr->IgnoreParenImpCasts();105  if (const auto *DE = dyn_cast<DeclRefExpr>(RefExpr))106    return cast<ValueDecl>(DE->getDecl()->getCanonicalDecl());107  const auto *ME = cast<MemberExpr>(RefExpr);108  return cast<ValueDecl>(ME->getMemberDecl()->getCanonicalDecl());109}110 111static RecordDecl *buildRecordForGlobalizedVars(112    ASTContext &C, ArrayRef<const ValueDecl *> EscapedDecls,113    ArrayRef<const ValueDecl *> EscapedDeclsForTeams,114    llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *>115        &MappedDeclsFields,116    int BufSize) {117  using VarsDataTy = std::pair<CharUnits /*Align*/, const ValueDecl *>;118  if (EscapedDecls.empty() && EscapedDeclsForTeams.empty())119    return nullptr;120  SmallVector<VarsDataTy, 4> GlobalizedVars;121  for (const ValueDecl *D : EscapedDecls)122    GlobalizedVars.emplace_back(C.getDeclAlign(D), D);123  for (const ValueDecl *D : EscapedDeclsForTeams)124    GlobalizedVars.emplace_back(C.getDeclAlign(D), D);125 126  // Build struct _globalized_locals_ty {127  //         /*  globalized vars  */[WarSize] align (decl_align)128  //         /*  globalized vars  */ for EscapedDeclsForTeams129  //       };130  RecordDecl *GlobalizedRD = C.buildImplicitRecord("_globalized_locals_ty");131  GlobalizedRD->startDefinition();132  llvm::SmallPtrSet<const ValueDecl *, 16> SingleEscaped(llvm::from_range,133                                                         EscapedDeclsForTeams);134  for (const auto &Pair : GlobalizedVars) {135    const ValueDecl *VD = Pair.second;136    QualType Type = VD->getType();137    if (Type->isLValueReferenceType())138      Type = C.getPointerType(Type.getNonReferenceType());139    else140      Type = Type.getNonReferenceType();141    SourceLocation Loc = VD->getLocation();142    FieldDecl *Field;143    if (SingleEscaped.count(VD)) {144      Field = FieldDecl::Create(145          C, GlobalizedRD, Loc, Loc, VD->getIdentifier(), Type,146          C.getTrivialTypeSourceInfo(Type, SourceLocation()),147          /*BW=*/nullptr, /*Mutable=*/false,148          /*InitStyle=*/ICIS_NoInit);149      Field->setAccess(AS_public);150      if (VD->hasAttrs()) {151        for (specific_attr_iterator<AlignedAttr> I(VD->getAttrs().begin()),152             E(VD->getAttrs().end());153             I != E; ++I)154          Field->addAttr(*I);155      }156    } else {157      if (BufSize > 1) {158        llvm::APInt ArraySize(32, BufSize);159        Type = C.getConstantArrayType(Type, ArraySize, nullptr,160                                      ArraySizeModifier::Normal, 0);161      }162      Field = FieldDecl::Create(163          C, GlobalizedRD, Loc, Loc, VD->getIdentifier(), Type,164          C.getTrivialTypeSourceInfo(Type, SourceLocation()),165          /*BW=*/nullptr, /*Mutable=*/false,166          /*InitStyle=*/ICIS_NoInit);167      Field->setAccess(AS_public);168      llvm::APInt Align(32, Pair.first.getQuantity());169      Field->addAttr(AlignedAttr::CreateImplicit(170          C, /*IsAlignmentExpr=*/true,171          IntegerLiteral::Create(C, Align,172                                 C.getIntTypeForBitwidth(32, /*Signed=*/0),173                                 SourceLocation()),174          {}, AlignedAttr::GNU_aligned));175    }176    GlobalizedRD->addDecl(Field);177    MappedDeclsFields.try_emplace(VD, Field);178  }179  GlobalizedRD->completeDefinition();180  return GlobalizedRD;181}182 183/// Get the list of variables that can escape their declaration context.184class CheckVarsEscapingDeclContext final185    : public ConstStmtVisitor<CheckVarsEscapingDeclContext> {186  CodeGenFunction &CGF;187  llvm::SetVector<const ValueDecl *> EscapedDecls;188  llvm::SetVector<const ValueDecl *> EscapedVariableLengthDecls;189  llvm::SetVector<const ValueDecl *> DelayedVariableLengthDecls;190  llvm::SmallPtrSet<const Decl *, 4> EscapedParameters;191  RecordDecl *GlobalizedRD = nullptr;192  llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *> MappedDeclsFields;193  bool AllEscaped = false;194  bool IsForCombinedParallelRegion = false;195 196  void markAsEscaped(const ValueDecl *VD) {197    // Do not globalize declare target variables.198    if (!isa<VarDecl>(VD) ||199        OMPDeclareTargetDeclAttr::isDeclareTargetDeclaration(VD))200      return;201    VD = cast<ValueDecl>(VD->getCanonicalDecl());202    // Use user-specified allocation.203    if (VD->hasAttrs() && VD->hasAttr<OMPAllocateDeclAttr>())204      return;205    // Variables captured by value must be globalized.206    bool IsCaptured = false;207    if (auto *CSI = CGF.CapturedStmtInfo) {208      if (const FieldDecl *FD = CSI->lookup(cast<VarDecl>(VD))) {209        // Check if need to capture the variable that was already captured by210        // value in the outer region.211        IsCaptured = true;212        if (!IsForCombinedParallelRegion) {213          if (!FD->hasAttrs())214            return;215          const auto *Attr = FD->getAttr<OMPCaptureKindAttr>();216          if (!Attr)217            return;218          if (((Attr->getCaptureKind() != OMPC_map) &&219               !isOpenMPPrivate(Attr->getCaptureKind())) ||220              ((Attr->getCaptureKind() == OMPC_map) &&221               !FD->getType()->isAnyPointerType()))222            return;223        }224        if (!FD->getType()->isReferenceType()) {225          assert(!VD->getType()->isVariablyModifiedType() &&226                 "Parameter captured by value with variably modified type");227          EscapedParameters.insert(VD);228        } else if (!IsForCombinedParallelRegion) {229          return;230        }231      }232    }233    if ((!CGF.CapturedStmtInfo ||234         (IsForCombinedParallelRegion && CGF.CapturedStmtInfo)) &&235        VD->getType()->isReferenceType())236      // Do not globalize variables with reference type.237      return;238    if (VD->getType()->isVariablyModifiedType()) {239      // If not captured at the target region level then mark the escaped240      // variable as delayed.241      if (IsCaptured)242        EscapedVariableLengthDecls.insert(VD);243      else244        DelayedVariableLengthDecls.insert(VD);245    } else246      EscapedDecls.insert(VD);247  }248 249  void VisitValueDecl(const ValueDecl *VD) {250    if (VD->getType()->isLValueReferenceType())251      markAsEscaped(VD);252    if (const auto *VarD = dyn_cast<VarDecl>(VD)) {253      if (!isa<ParmVarDecl>(VarD) && VarD->hasInit()) {254        const bool SavedAllEscaped = AllEscaped;255        AllEscaped = VD->getType()->isLValueReferenceType();256        Visit(VarD->getInit());257        AllEscaped = SavedAllEscaped;258      }259    }260  }261  void VisitOpenMPCapturedStmt(const CapturedStmt *S,262                               ArrayRef<OMPClause *> Clauses,263                               bool IsCombinedParallelRegion) {264    if (!S)265      return;266    for (const CapturedStmt::Capture &C : S->captures()) {267      if (C.capturesVariable() && !C.capturesVariableByCopy()) {268        const ValueDecl *VD = C.getCapturedVar();269        bool SavedIsForCombinedParallelRegion = IsForCombinedParallelRegion;270        if (IsCombinedParallelRegion) {271          // Check if the variable is privatized in the combined construct and272          // those private copies must be shared in the inner parallel273          // directive.274          IsForCombinedParallelRegion = false;275          for (const OMPClause *C : Clauses) {276            if (!isOpenMPPrivate(C->getClauseKind()) ||277                C->getClauseKind() == OMPC_reduction ||278                C->getClauseKind() == OMPC_linear ||279                C->getClauseKind() == OMPC_private)280              continue;281            ArrayRef<const Expr *> Vars;282            if (const auto *PC = dyn_cast<OMPFirstprivateClause>(C))283              Vars = PC->getVarRefs();284            else if (const auto *PC = dyn_cast<OMPLastprivateClause>(C))285              Vars = PC->getVarRefs();286            else287              llvm_unreachable("Unexpected clause.");288            for (const auto *E : Vars) {289              const Decl *D =290                  cast<DeclRefExpr>(E)->getDecl()->getCanonicalDecl();291              if (D == VD->getCanonicalDecl()) {292                IsForCombinedParallelRegion = true;293                break;294              }295            }296            if (IsForCombinedParallelRegion)297              break;298          }299        }300        markAsEscaped(VD);301        if (isa<OMPCapturedExprDecl>(VD))302          VisitValueDecl(VD);303        IsForCombinedParallelRegion = SavedIsForCombinedParallelRegion;304      }305    }306  }307 308  void buildRecordForGlobalizedVars(bool IsInTTDRegion) {309    assert(!GlobalizedRD &&310           "Record for globalized variables is built already.");311    ArrayRef<const ValueDecl *> EscapedDeclsForParallel, EscapedDeclsForTeams;312    unsigned WarpSize = CGF.getTarget().getGridValue().GV_Warp_Size;313    if (IsInTTDRegion)314      EscapedDeclsForTeams = EscapedDecls.getArrayRef();315    else316      EscapedDeclsForParallel = EscapedDecls.getArrayRef();317    GlobalizedRD = ::buildRecordForGlobalizedVars(318        CGF.getContext(), EscapedDeclsForParallel, EscapedDeclsForTeams,319        MappedDeclsFields, WarpSize);320  }321 322public:323  CheckVarsEscapingDeclContext(CodeGenFunction &CGF,324                               ArrayRef<const ValueDecl *> TeamsReductions)325      : CGF(CGF), EscapedDecls(llvm::from_range, TeamsReductions) {}326  ~CheckVarsEscapingDeclContext() = default;327  void VisitDeclStmt(const DeclStmt *S) {328    if (!S)329      return;330    for (const Decl *D : S->decls())331      if (const auto *VD = dyn_cast_or_null<ValueDecl>(D))332        VisitValueDecl(VD);333  }334  void VisitOMPExecutableDirective(const OMPExecutableDirective *D) {335    if (!D)336      return;337    if (!D->hasAssociatedStmt())338      return;339    if (const auto *S =340            dyn_cast_or_null<CapturedStmt>(D->getAssociatedStmt())) {341      // Do not analyze directives that do not actually require capturing,342      // like `omp for` or `omp simd` directives.343      llvm::SmallVector<OpenMPDirectiveKind, 4> CaptureRegions;344      getOpenMPCaptureRegions(CaptureRegions, D->getDirectiveKind());345      if (CaptureRegions.size() == 1 && CaptureRegions.back() == OMPD_unknown) {346        VisitStmt(S->getCapturedStmt());347        return;348      }349      VisitOpenMPCapturedStmt(350          S, D->clauses(),351          CaptureRegions.back() == OMPD_parallel &&352              isOpenMPDistributeDirective(D->getDirectiveKind()));353    }354  }355  void VisitCapturedStmt(const CapturedStmt *S) {356    if (!S)357      return;358    for (const CapturedStmt::Capture &C : S->captures()) {359      if (C.capturesVariable() && !C.capturesVariableByCopy()) {360        const ValueDecl *VD = C.getCapturedVar();361        markAsEscaped(VD);362        if (isa<OMPCapturedExprDecl>(VD))363          VisitValueDecl(VD);364      }365    }366  }367  void VisitLambdaExpr(const LambdaExpr *E) {368    if (!E)369      return;370    for (const LambdaCapture &C : E->captures()) {371      if (C.capturesVariable()) {372        if (C.getCaptureKind() == LCK_ByRef) {373          const ValueDecl *VD = C.getCapturedVar();374          markAsEscaped(VD);375          if (E->isInitCapture(&C) || isa<OMPCapturedExprDecl>(VD))376            VisitValueDecl(VD);377        }378      }379    }380  }381  void VisitBlockExpr(const BlockExpr *E) {382    if (!E)383      return;384    for (const BlockDecl::Capture &C : E->getBlockDecl()->captures()) {385      if (C.isByRef()) {386        const VarDecl *VD = C.getVariable();387        markAsEscaped(VD);388        if (isa<OMPCapturedExprDecl>(VD) || VD->isInitCapture())389          VisitValueDecl(VD);390      }391    }392  }393  void VisitCallExpr(const CallExpr *E) {394    if (!E)395      return;396    for (const Expr *Arg : E->arguments()) {397      if (!Arg)398        continue;399      if (Arg->isLValue()) {400        const bool SavedAllEscaped = AllEscaped;401        AllEscaped = true;402        Visit(Arg);403        AllEscaped = SavedAllEscaped;404      } else {405        Visit(Arg);406      }407    }408    Visit(E->getCallee());409  }410  void VisitDeclRefExpr(const DeclRefExpr *E) {411    if (!E)412      return;413    const ValueDecl *VD = E->getDecl();414    if (AllEscaped)415      markAsEscaped(VD);416    if (isa<OMPCapturedExprDecl>(VD))417      VisitValueDecl(VD);418    else if (VD->isInitCapture())419      VisitValueDecl(VD);420  }421  void VisitUnaryOperator(const UnaryOperator *E) {422    if (!E)423      return;424    if (E->getOpcode() == UO_AddrOf) {425      const bool SavedAllEscaped = AllEscaped;426      AllEscaped = true;427      Visit(E->getSubExpr());428      AllEscaped = SavedAllEscaped;429    } else {430      Visit(E->getSubExpr());431    }432  }433  void VisitImplicitCastExpr(const ImplicitCastExpr *E) {434    if (!E)435      return;436    if (E->getCastKind() == CK_ArrayToPointerDecay) {437      const bool SavedAllEscaped = AllEscaped;438      AllEscaped = true;439      Visit(E->getSubExpr());440      AllEscaped = SavedAllEscaped;441    } else {442      Visit(E->getSubExpr());443    }444  }445  void VisitExpr(const Expr *E) {446    if (!E)447      return;448    bool SavedAllEscaped = AllEscaped;449    if (!E->isLValue())450      AllEscaped = false;451    for (const Stmt *Child : E->children())452      if (Child)453        Visit(Child);454    AllEscaped = SavedAllEscaped;455  }456  void VisitStmt(const Stmt *S) {457    if (!S)458      return;459    for (const Stmt *Child : S->children())460      if (Child)461        Visit(Child);462  }463 464  /// Returns the record that handles all the escaped local variables and used465  /// instead of their original storage.466  const RecordDecl *getGlobalizedRecord(bool IsInTTDRegion) {467    if (!GlobalizedRD)468      buildRecordForGlobalizedVars(IsInTTDRegion);469    return GlobalizedRD;470  }471 472  /// Returns the field in the globalized record for the escaped variable.473  const FieldDecl *getFieldForGlobalizedVar(const ValueDecl *VD) const {474    assert(GlobalizedRD &&475           "Record for globalized variables must be generated already.");476    return MappedDeclsFields.lookup(VD);477  }478 479  /// Returns the list of the escaped local variables/parameters.480  ArrayRef<const ValueDecl *> getEscapedDecls() const {481    return EscapedDecls.getArrayRef();482  }483 484  /// Checks if the escaped local variable is actually a parameter passed by485  /// value.486  const llvm::SmallPtrSetImpl<const Decl *> &getEscapedParameters() const {487    return EscapedParameters;488  }489 490  /// Returns the list of the escaped variables with the variably modified491  /// types.492  ArrayRef<const ValueDecl *> getEscapedVariableLengthDecls() const {493    return EscapedVariableLengthDecls.getArrayRef();494  }495 496  /// Returns the list of the delayed variables with the variably modified497  /// types.498  ArrayRef<const ValueDecl *> getDelayedVariableLengthDecls() const {499    return DelayedVariableLengthDecls.getArrayRef();500  }501};502} // anonymous namespace503 504CGOpenMPRuntimeGPU::ExecutionMode505CGOpenMPRuntimeGPU::getExecutionMode() const {506  return CurrentExecutionMode;507}508 509CGOpenMPRuntimeGPU::DataSharingMode510CGOpenMPRuntimeGPU::getDataSharingMode() const {511  return CurrentDataSharingMode;512}513 514/// Check for inner (nested) SPMD construct, if any515static bool hasNestedSPMDDirective(ASTContext &Ctx,516                                   const OMPExecutableDirective &D) {517  const auto *CS = D.getInnermostCapturedStmt();518  const auto *Body =519      CS->getCapturedStmt()->IgnoreContainers(/*IgnoreCaptured=*/true);520  const Stmt *ChildStmt = CGOpenMPRuntime::getSingleCompoundChild(Ctx, Body);521 522  if (const auto *NestedDir =523          dyn_cast_or_null<OMPExecutableDirective>(ChildStmt)) {524    OpenMPDirectiveKind DKind = NestedDir->getDirectiveKind();525    switch (D.getDirectiveKind()) {526    case OMPD_target:527      if (isOpenMPParallelDirective(DKind))528        return true;529      if (DKind == OMPD_teams) {530        Body = NestedDir->getInnermostCapturedStmt()->IgnoreContainers(531            /*IgnoreCaptured=*/true);532        if (!Body)533          return false;534        ChildStmt = CGOpenMPRuntime::getSingleCompoundChild(Ctx, Body);535        if (const auto *NND =536                dyn_cast_or_null<OMPExecutableDirective>(ChildStmt)) {537          DKind = NND->getDirectiveKind();538          if (isOpenMPParallelDirective(DKind))539            return true;540        }541      }542      return false;543    case OMPD_target_teams:544      return isOpenMPParallelDirective(DKind);545    case OMPD_target_simd:546    case OMPD_target_parallel:547    case OMPD_target_parallel_for:548    case OMPD_target_parallel_for_simd:549    case OMPD_target_teams_distribute:550    case OMPD_target_teams_distribute_simd:551    case OMPD_target_teams_distribute_parallel_for:552    case OMPD_target_teams_distribute_parallel_for_simd:553    case OMPD_parallel:554    case OMPD_for:555    case OMPD_parallel_for:556    case OMPD_parallel_master:557    case OMPD_parallel_sections:558    case OMPD_for_simd:559    case OMPD_parallel_for_simd:560    case OMPD_cancel:561    case OMPD_cancellation_point:562    case OMPD_ordered:563    case OMPD_threadprivate:564    case OMPD_allocate:565    case OMPD_task:566    case OMPD_simd:567    case OMPD_sections:568    case OMPD_section:569    case OMPD_single:570    case OMPD_master:571    case OMPD_critical:572    case OMPD_taskyield:573    case OMPD_barrier:574    case OMPD_taskwait:575    case OMPD_taskgroup:576    case OMPD_atomic:577    case OMPD_flush:578    case OMPD_depobj:579    case OMPD_scan:580    case OMPD_teams:581    case OMPD_target_data:582    case OMPD_target_exit_data:583    case OMPD_target_enter_data:584    case OMPD_distribute:585    case OMPD_distribute_simd:586    case OMPD_distribute_parallel_for:587    case OMPD_distribute_parallel_for_simd:588    case OMPD_teams_distribute:589    case OMPD_teams_distribute_simd:590    case OMPD_teams_distribute_parallel_for:591    case OMPD_teams_distribute_parallel_for_simd:592    case OMPD_target_update:593    case OMPD_declare_simd:594    case OMPD_declare_variant:595    case OMPD_begin_declare_variant:596    case OMPD_end_declare_variant:597    case OMPD_declare_target:598    case OMPD_end_declare_target:599    case OMPD_declare_reduction:600    case OMPD_declare_mapper:601    case OMPD_taskloop:602    case OMPD_taskloop_simd:603    case OMPD_master_taskloop:604    case OMPD_master_taskloop_simd:605    case OMPD_parallel_master_taskloop:606    case OMPD_parallel_master_taskloop_simd:607    case OMPD_requires:608    case OMPD_unknown:609    default:610      llvm_unreachable("Unexpected directive.");611    }612  }613 614  return false;615}616 617static bool supportsSPMDExecutionMode(ASTContext &Ctx,618                                      const OMPExecutableDirective &D) {619  OpenMPDirectiveKind DirectiveKind = D.getDirectiveKind();620  switch (DirectiveKind) {621  case OMPD_target:622  case OMPD_target_teams:623    return hasNestedSPMDDirective(Ctx, D);624  case OMPD_target_parallel_loop:625  case OMPD_target_parallel:626  case OMPD_target_parallel_for:627  case OMPD_target_parallel_for_simd:628  case OMPD_target_teams_distribute_parallel_for:629  case OMPD_target_teams_distribute_parallel_for_simd:630  case OMPD_target_simd:631  case OMPD_target_teams_distribute_simd:632    return true;633  case OMPD_target_teams_distribute:634    return false;635  case OMPD_target_teams_loop:636    // Whether this is true or not depends on how the directive will637    // eventually be emitted.638    if (auto *TTLD = dyn_cast<OMPTargetTeamsGenericLoopDirective>(&D))639      return TTLD->canBeParallelFor();640    return false;641  case OMPD_parallel:642  case OMPD_for:643  case OMPD_parallel_for:644  case OMPD_parallel_master:645  case OMPD_parallel_sections:646  case OMPD_for_simd:647  case OMPD_parallel_for_simd:648  case OMPD_cancel:649  case OMPD_cancellation_point:650  case OMPD_ordered:651  case OMPD_threadprivate:652  case OMPD_allocate:653  case OMPD_task:654  case OMPD_simd:655  case OMPD_sections:656  case OMPD_section:657  case OMPD_single:658  case OMPD_master:659  case OMPD_critical:660  case OMPD_taskyield:661  case OMPD_barrier:662  case OMPD_taskwait:663  case OMPD_taskgroup:664  case OMPD_atomic:665  case OMPD_flush:666  case OMPD_depobj:667  case OMPD_scan:668  case OMPD_teams:669  case OMPD_target_data:670  case OMPD_target_exit_data:671  case OMPD_target_enter_data:672  case OMPD_distribute:673  case OMPD_distribute_simd:674  case OMPD_distribute_parallel_for:675  case OMPD_distribute_parallel_for_simd:676  case OMPD_teams_distribute:677  case OMPD_teams_distribute_simd:678  case OMPD_teams_distribute_parallel_for:679  case OMPD_teams_distribute_parallel_for_simd:680  case OMPD_target_update:681  case OMPD_declare_simd:682  case OMPD_declare_variant:683  case OMPD_begin_declare_variant:684  case OMPD_end_declare_variant:685  case OMPD_declare_target:686  case OMPD_end_declare_target:687  case OMPD_declare_reduction:688  case OMPD_declare_mapper:689  case OMPD_taskloop:690  case OMPD_taskloop_simd:691  case OMPD_master_taskloop:692  case OMPD_master_taskloop_simd:693  case OMPD_parallel_master_taskloop:694  case OMPD_parallel_master_taskloop_simd:695  case OMPD_requires:696  case OMPD_unknown:697  default:698    break;699  }700  llvm_unreachable(701      "Unknown programming model for OpenMP directive on NVPTX target.");702}703 704void CGOpenMPRuntimeGPU::emitNonSPMDKernel(const OMPExecutableDirective &D,705                                             StringRef ParentName,706                                             llvm::Function *&OutlinedFn,707                                             llvm::Constant *&OutlinedFnID,708                                             bool IsOffloadEntry,709                                             const RegionCodeGenTy &CodeGen) {710  ExecutionRuntimeModesRAII ModeRAII(CurrentExecutionMode, EM_NonSPMD);711  EntryFunctionState EST;712  WrapperFunctionsMap.clear();713 714  [[maybe_unused]] bool IsBareKernel = D.getSingleClause<OMPXBareClause>();715  assert(!IsBareKernel && "bare kernel should not be at generic mode");716 717  // Emit target region as a standalone region.718  class NVPTXPrePostActionTy : public PrePostActionTy {719    CGOpenMPRuntimeGPU::EntryFunctionState &EST;720    const OMPExecutableDirective &D;721 722  public:723    NVPTXPrePostActionTy(CGOpenMPRuntimeGPU::EntryFunctionState &EST,724                         const OMPExecutableDirective &D)725        : EST(EST), D(D) {}726    void Enter(CodeGenFunction &CGF) override {727      auto &RT = static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());728      RT.emitKernelInit(D, CGF, EST, /* IsSPMD */ false);729      // Skip target region initialization.730      RT.setLocThreadIdInsertPt(CGF, /*AtCurrentPoint=*/true);731    }732    void Exit(CodeGenFunction &CGF) override {733      auto &RT = static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());734      RT.clearLocThreadIdInsertPt(CGF);735      RT.emitKernelDeinit(CGF, EST, /* IsSPMD */ false);736    }737  } Action(EST, D);738  CodeGen.setAction(Action);739  IsInTTDRegion = true;740  emitTargetOutlinedFunctionHelper(D, ParentName, OutlinedFn, OutlinedFnID,741                                   IsOffloadEntry, CodeGen);742  IsInTTDRegion = false;743}744 745void CGOpenMPRuntimeGPU::emitKernelInit(const OMPExecutableDirective &D,746                                        CodeGenFunction &CGF,747                                        EntryFunctionState &EST, bool IsSPMD) {748  llvm::OpenMPIRBuilder::TargetKernelDefaultAttrs Attrs;749  Attrs.ExecFlags =750      IsSPMD ? llvm::omp::OMPTgtExecModeFlags::OMP_TGT_EXEC_MODE_SPMD751             : llvm::omp::OMPTgtExecModeFlags::OMP_TGT_EXEC_MODE_GENERIC;752  computeMinAndMaxThreadsAndTeams(D, CGF, Attrs);753 754  CGBuilderTy &Bld = CGF.Builder;755  Bld.restoreIP(OMPBuilder.createTargetInit(Bld, Attrs));756  if (!IsSPMD)757    emitGenericVarsProlog(CGF, EST.Loc);758}759 760void CGOpenMPRuntimeGPU::emitKernelDeinit(CodeGenFunction &CGF,761                                          EntryFunctionState &EST,762                                          bool IsSPMD) {763  if (!IsSPMD)764    emitGenericVarsEpilog(CGF);765 766  // This is temporary until we remove the fixed sized buffer.767  ASTContext &C = CGM.getContext();768  RecordDecl *StaticRD = C.buildImplicitRecord(769      "_openmp_teams_reduction_type_$_", RecordDecl::TagKind::Union);770  StaticRD->startDefinition();771  for (const RecordDecl *TeamReductionRec : TeamsReductions) {772    CanQualType RecTy = C.getCanonicalTagType(TeamReductionRec);773    auto *Field = FieldDecl::Create(774        C, StaticRD, SourceLocation(), SourceLocation(), nullptr, RecTy,775        C.getTrivialTypeSourceInfo(RecTy, SourceLocation()),776        /*BW=*/nullptr, /*Mutable=*/false,777        /*InitStyle=*/ICIS_NoInit);778    Field->setAccess(AS_public);779    StaticRD->addDecl(Field);780  }781  StaticRD->completeDefinition();782  CanQualType StaticTy = C.getCanonicalTagType(StaticRD);783  llvm::Type *LLVMReductionsBufferTy =784      CGM.getTypes().ConvertTypeForMem(StaticTy);785  const auto &DL = CGM.getModule().getDataLayout();786  uint64_t ReductionDataSize =787      TeamsReductions.empty()788          ? 0789          : DL.getTypeAllocSize(LLVMReductionsBufferTy).getFixedValue();790  CGBuilderTy &Bld = CGF.Builder;791  OMPBuilder.createTargetDeinit(Bld, ReductionDataSize,792                                C.getLangOpts().OpenMPCUDAReductionBufNum);793  TeamsReductions.clear();794}795 796void CGOpenMPRuntimeGPU::emitSPMDKernel(const OMPExecutableDirective &D,797                                          StringRef ParentName,798                                          llvm::Function *&OutlinedFn,799                                          llvm::Constant *&OutlinedFnID,800                                          bool IsOffloadEntry,801                                          const RegionCodeGenTy &CodeGen) {802  ExecutionRuntimeModesRAII ModeRAII(CurrentExecutionMode, EM_SPMD);803  EntryFunctionState EST;804 805  bool IsBareKernel = D.getSingleClause<OMPXBareClause>();806 807  // Emit target region as a standalone region.808  class NVPTXPrePostActionTy : public PrePostActionTy {809    CGOpenMPRuntimeGPU &RT;810    CGOpenMPRuntimeGPU::EntryFunctionState &EST;811    bool IsBareKernel;812    DataSharingMode Mode;813    const OMPExecutableDirective &D;814 815  public:816    NVPTXPrePostActionTy(CGOpenMPRuntimeGPU &RT,817                         CGOpenMPRuntimeGPU::EntryFunctionState &EST,818                         bool IsBareKernel, const OMPExecutableDirective &D)819        : RT(RT), EST(EST), IsBareKernel(IsBareKernel),820          Mode(RT.CurrentDataSharingMode), D(D) {}821    void Enter(CodeGenFunction &CGF) override {822      if (IsBareKernel) {823        RT.CurrentDataSharingMode = DataSharingMode::DS_CUDA;824        return;825      }826      RT.emitKernelInit(D, CGF, EST, /* IsSPMD */ true);827      // Skip target region initialization.828      RT.setLocThreadIdInsertPt(CGF, /*AtCurrentPoint=*/true);829    }830    void Exit(CodeGenFunction &CGF) override {831      if (IsBareKernel) {832        RT.CurrentDataSharingMode = Mode;833        return;834      }835      RT.clearLocThreadIdInsertPt(CGF);836      RT.emitKernelDeinit(CGF, EST, /* IsSPMD */ true);837    }838  } Action(*this, EST, IsBareKernel, D);839  CodeGen.setAction(Action);840  IsInTTDRegion = true;841  emitTargetOutlinedFunctionHelper(D, ParentName, OutlinedFn, OutlinedFnID,842                                   IsOffloadEntry, CodeGen);843  IsInTTDRegion = false;844}845 846void CGOpenMPRuntimeGPU::emitTargetOutlinedFunction(847    const OMPExecutableDirective &D, StringRef ParentName,848    llvm::Function *&OutlinedFn, llvm::Constant *&OutlinedFnID,849    bool IsOffloadEntry, const RegionCodeGenTy &CodeGen) {850  if (!IsOffloadEntry) // Nothing to do.851    return;852 853  assert(!ParentName.empty() && "Invalid target region parent name!");854 855  bool Mode = supportsSPMDExecutionMode(CGM.getContext(), D);856  bool IsBareKernel = D.getSingleClause<OMPXBareClause>();857  if (Mode || IsBareKernel)858    emitSPMDKernel(D, ParentName, OutlinedFn, OutlinedFnID, IsOffloadEntry,859                   CodeGen);860  else861    emitNonSPMDKernel(D, ParentName, OutlinedFn, OutlinedFnID, IsOffloadEntry,862                      CodeGen);863}864 865CGOpenMPRuntimeGPU::CGOpenMPRuntimeGPU(CodeGenModule &CGM)866    : CGOpenMPRuntime(CGM) {867  llvm::OpenMPIRBuilderConfig Config(868      CGM.getLangOpts().OpenMPIsTargetDevice, isGPU(),869      CGM.getLangOpts().OpenMPOffloadMandatory,870      /*HasRequiresReverseOffload*/ false, /*HasRequiresUnifiedAddress*/ false,871      hasRequiresUnifiedSharedMemory(), /*HasRequiresDynamicAllocators*/ false);872  Config.setDefaultTargetAS(873      CGM.getContext().getTargetInfo().getTargetAddressSpace(LangAS::Default));874  Config.setRuntimeCC(CGM.getRuntimeCC());875 876  OMPBuilder.setConfig(Config);877 878  if (!CGM.getLangOpts().OpenMPIsTargetDevice)879    llvm_unreachable("OpenMP can only handle device code.");880 881  if (CGM.getLangOpts().OpenMPCUDAMode)882    CurrentDataSharingMode = CGOpenMPRuntimeGPU::DS_CUDA;883 884  llvm::OpenMPIRBuilder &OMPBuilder = getOMPBuilder();885  if (CGM.getLangOpts().NoGPULib || CGM.getLangOpts().OMPHostIRFile.empty())886    return;887 888  OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPTargetDebug,889                              "__omp_rtl_debug_kind");890  OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPTeamSubscription,891                              "__omp_rtl_assume_teams_oversubscription");892  OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPThreadSubscription,893                              "__omp_rtl_assume_threads_oversubscription");894  OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPNoThreadState,895                              "__omp_rtl_assume_no_thread_state");896  OMPBuilder.createGlobalFlag(CGM.getLangOpts().OpenMPNoNestedParallelism,897                              "__omp_rtl_assume_no_nested_parallelism");898}899 900void CGOpenMPRuntimeGPU::emitProcBindClause(CodeGenFunction &CGF,901                                              ProcBindKind ProcBind,902                                              SourceLocation Loc) {903  // Nothing to do.904}905 906llvm::Value *CGOpenMPRuntimeGPU::emitMessageClause(CodeGenFunction &CGF,907                                                   const Expr *Message,908                                                   SourceLocation Loc) {909  CGM.getDiags().Report(Loc, diag::warn_omp_gpu_unsupported_clause)910      << getOpenMPClauseName(OMPC_message);911  return nullptr;912}913 914llvm::Value *915CGOpenMPRuntimeGPU::emitSeverityClause(OpenMPSeverityClauseKind Severity,916                                       SourceLocation Loc) {917  CGM.getDiags().Report(Loc, diag::warn_omp_gpu_unsupported_clause)918      << getOpenMPClauseName(OMPC_severity);919  return nullptr;920}921 922void CGOpenMPRuntimeGPU::emitNumThreadsClause(923    CodeGenFunction &CGF, llvm::Value *NumThreads, SourceLocation Loc,924    OpenMPNumThreadsClauseModifier Modifier, OpenMPSeverityClauseKind Severity,925    SourceLocation SeverityLoc, const Expr *Message,926    SourceLocation MessageLoc) {927  if (Modifier == OMPC_NUMTHREADS_strict) {928    CGM.getDiags().Report(Loc,929                          diag::warn_omp_gpu_unsupported_modifier_for_clause)930        << "strict" << getOpenMPClauseName(OMPC_num_threads);931    return;932  }933 934  // Nothing to do.935}936 937void CGOpenMPRuntimeGPU::emitNumTeamsClause(CodeGenFunction &CGF,938                                              const Expr *NumTeams,939                                              const Expr *ThreadLimit,940                                              SourceLocation Loc) {}941 942llvm::Function *CGOpenMPRuntimeGPU::emitParallelOutlinedFunction(943    CodeGenFunction &CGF, const OMPExecutableDirective &D,944    const VarDecl *ThreadIDVar, OpenMPDirectiveKind InnermostKind,945    const RegionCodeGenTy &CodeGen) {946  // Emit target region as a standalone region.947  bool PrevIsInTTDRegion = IsInTTDRegion;948  IsInTTDRegion = false;949  auto *OutlinedFun =950      cast<llvm::Function>(CGOpenMPRuntime::emitParallelOutlinedFunction(951          CGF, D, ThreadIDVar, InnermostKind, CodeGen));952  IsInTTDRegion = PrevIsInTTDRegion;953  if (getExecutionMode() != CGOpenMPRuntimeGPU::EM_SPMD) {954    llvm::Function *WrapperFun =955        createParallelDataSharingWrapper(OutlinedFun, D);956    WrapperFunctionsMap[OutlinedFun] = WrapperFun;957  }958 959  return OutlinedFun;960}961 962/// Get list of lastprivate variables from the teams distribute ... or963/// teams {distribute ...} directives.964static void965getDistributeLastprivateVars(ASTContext &Ctx, const OMPExecutableDirective &D,966                             llvm::SmallVectorImpl<const ValueDecl *> &Vars) {967  assert(isOpenMPTeamsDirective(D.getDirectiveKind()) &&968         "expected teams directive.");969  const OMPExecutableDirective *Dir = &D;970  if (!isOpenMPDistributeDirective(D.getDirectiveKind())) {971    if (const Stmt *S = CGOpenMPRuntime::getSingleCompoundChild(972            Ctx,973            D.getInnermostCapturedStmt()->getCapturedStmt()->IgnoreContainers(974                /*IgnoreCaptured=*/true))) {975      Dir = dyn_cast_or_null<OMPExecutableDirective>(S);976      if (Dir && !isOpenMPDistributeDirective(Dir->getDirectiveKind()))977        Dir = nullptr;978    }979  }980  if (!Dir)981    return;982  for (const auto *C : Dir->getClausesOfKind<OMPLastprivateClause>()) {983    for (const Expr *E : C->getVarRefs())984      Vars.push_back(getPrivateItem(E));985  }986}987 988/// Get list of reduction variables from the teams ... directives.989static void990getTeamsReductionVars(ASTContext &Ctx, const OMPExecutableDirective &D,991                      llvm::SmallVectorImpl<const ValueDecl *> &Vars) {992  assert(isOpenMPTeamsDirective(D.getDirectiveKind()) &&993         "expected teams directive.");994  for (const auto *C : D.getClausesOfKind<OMPReductionClause>()) {995    for (const Expr *E : C->privates())996      Vars.push_back(getPrivateItem(E));997  }998}999 1000llvm::Function *CGOpenMPRuntimeGPU::emitTeamsOutlinedFunction(1001    CodeGenFunction &CGF, const OMPExecutableDirective &D,1002    const VarDecl *ThreadIDVar, OpenMPDirectiveKind InnermostKind,1003    const RegionCodeGenTy &CodeGen) {1004  SourceLocation Loc = D.getBeginLoc();1005 1006  const RecordDecl *GlobalizedRD = nullptr;1007  llvm::SmallVector<const ValueDecl *, 4> LastPrivatesReductions;1008  llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *> MappedDeclsFields;1009  unsigned WarpSize = CGM.getTarget().getGridValue().GV_Warp_Size;1010  // Globalize team reductions variable unconditionally in all modes.1011  if (getExecutionMode() != CGOpenMPRuntimeGPU::EM_SPMD)1012    getTeamsReductionVars(CGM.getContext(), D, LastPrivatesReductions);1013  if (getExecutionMode() == CGOpenMPRuntimeGPU::EM_SPMD) {1014    getDistributeLastprivateVars(CGM.getContext(), D, LastPrivatesReductions);1015    if (!LastPrivatesReductions.empty()) {1016      GlobalizedRD = ::buildRecordForGlobalizedVars(1017          CGM.getContext(), {}, LastPrivatesReductions, MappedDeclsFields,1018          WarpSize);1019    }1020  } else if (!LastPrivatesReductions.empty()) {1021    assert(!TeamAndReductions.first &&1022           "Previous team declaration is not expected.");1023    TeamAndReductions.first = D.getCapturedStmt(OMPD_teams)->getCapturedDecl();1024    std::swap(TeamAndReductions.second, LastPrivatesReductions);1025  }1026 1027  // Emit target region as a standalone region.1028  class NVPTXPrePostActionTy : public PrePostActionTy {1029    SourceLocation &Loc;1030    const RecordDecl *GlobalizedRD;1031    llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *>1032        &MappedDeclsFields;1033 1034  public:1035    NVPTXPrePostActionTy(1036        SourceLocation &Loc, const RecordDecl *GlobalizedRD,1037        llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *>1038            &MappedDeclsFields)1039        : Loc(Loc), GlobalizedRD(GlobalizedRD),1040          MappedDeclsFields(MappedDeclsFields) {}1041    void Enter(CodeGenFunction &CGF) override {1042      auto &Rt =1043          static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());1044      if (GlobalizedRD) {1045        auto I = Rt.FunctionGlobalizedDecls.try_emplace(CGF.CurFn).first;1046        I->getSecond().MappedParams =1047            std::make_unique<CodeGenFunction::OMPMapVars>();1048        DeclToAddrMapTy &Data = I->getSecond().LocalVarData;1049        for (const auto &Pair : MappedDeclsFields) {1050          assert(Pair.getFirst()->isCanonicalDecl() &&1051                 "Expected canonical declaration");1052          Data.try_emplace(Pair.getFirst());1053        }1054      }1055      Rt.emitGenericVarsProlog(CGF, Loc);1056    }1057    void Exit(CodeGenFunction &CGF) override {1058      static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime())1059          .emitGenericVarsEpilog(CGF);1060    }1061  } Action(Loc, GlobalizedRD, MappedDeclsFields);1062  CodeGen.setAction(Action);1063  llvm::Function *OutlinedFun = CGOpenMPRuntime::emitTeamsOutlinedFunction(1064      CGF, D, ThreadIDVar, InnermostKind, CodeGen);1065 1066  return OutlinedFun;1067}1068 1069void CGOpenMPRuntimeGPU::emitGenericVarsProlog(CodeGenFunction &CGF,1070                                               SourceLocation Loc) {1071  if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)1072    return;1073 1074  CGBuilderTy &Bld = CGF.Builder;1075 1076  const auto I = FunctionGlobalizedDecls.find(CGF.CurFn);1077  if (I == FunctionGlobalizedDecls.end())1078    return;1079 1080  for (auto &Rec : I->getSecond().LocalVarData) {1081    const auto *VD = cast<VarDecl>(Rec.first);1082    bool EscapedParam = I->getSecond().EscapedParameters.count(Rec.first);1083    QualType VarTy = VD->getType();1084 1085    // Get the local allocation of a firstprivate variable before sharing1086    llvm::Value *ParValue;1087    if (EscapedParam) {1088      LValue ParLVal =1089          CGF.MakeAddrLValue(CGF.GetAddrOfLocalVar(VD), VD->getType());1090      ParValue = CGF.EmitLoadOfScalar(ParLVal, Loc);1091    }1092 1093    // Allocate space for the variable to be globalized1094    llvm::Value *AllocArgs[] = {CGF.getTypeSize(VD->getType())};1095    llvm::CallBase *VoidPtr =1096        CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(1097                                CGM.getModule(), OMPRTL___kmpc_alloc_shared),1098                            AllocArgs, VD->getName());1099    // FIXME: We should use the variables actual alignment as an argument.1100    VoidPtr->addRetAttr(llvm::Attribute::get(1101        CGM.getLLVMContext(), llvm::Attribute::Alignment,1102        CGM.getContext().getTargetInfo().getNewAlign() / 8));1103 1104    // Cast the void pointer and get the address of the globalized variable.1105    llvm::Value *CastedVoidPtr = Bld.CreatePointerBitCastOrAddrSpaceCast(1106        VoidPtr, Bld.getPtrTy(0), VD->getName() + "_on_stack");1107    LValue VarAddr =1108        CGF.MakeNaturalAlignPointeeRawAddrLValue(CastedVoidPtr, VarTy);1109    Rec.second.PrivateAddr = VarAddr.getAddress();1110    Rec.second.GlobalizedVal = VoidPtr;1111 1112    // Assign the local allocation to the newly globalized location.1113    if (EscapedParam) {1114      CGF.EmitStoreOfScalar(ParValue, VarAddr);1115      I->getSecond().MappedParams->setVarAddr(CGF, VD, VarAddr.getAddress());1116    }1117    if (auto *DI = CGF.getDebugInfo())1118      VoidPtr->setDebugLoc(DI->SourceLocToDebugLoc(VD->getLocation()));1119  }1120 1121  for (const auto *ValueD : I->getSecond().EscapedVariableLengthDecls) {1122    const auto *VD = cast<VarDecl>(ValueD);1123    std::pair<llvm::Value *, llvm::Value *> AddrSizePair =1124        getKmpcAllocShared(CGF, VD);1125    I->getSecond().EscapedVariableLengthDeclsAddrs.emplace_back(AddrSizePair);1126    LValue Base = CGF.MakeAddrLValue(AddrSizePair.first, VD->getType(),1127                                     CGM.getContext().getDeclAlign(VD),1128                                     AlignmentSource::Decl);1129    I->getSecond().MappedParams->setVarAddr(CGF, VD, Base.getAddress());1130  }1131  I->getSecond().MappedParams->apply(CGF);1132}1133 1134bool CGOpenMPRuntimeGPU::isDelayedVariableLengthDecl(CodeGenFunction &CGF,1135                                                     const VarDecl *VD) const {1136  const auto I = FunctionGlobalizedDecls.find(CGF.CurFn);1137  if (I == FunctionGlobalizedDecls.end())1138    return false;1139 1140  // Check variable declaration is delayed:1141  return llvm::is_contained(I->getSecond().DelayedVariableLengthDecls, VD);1142}1143 1144std::pair<llvm::Value *, llvm::Value *>1145CGOpenMPRuntimeGPU::getKmpcAllocShared(CodeGenFunction &CGF,1146                                       const VarDecl *VD) {1147  CGBuilderTy &Bld = CGF.Builder;1148 1149  // Compute size and alignment.1150  llvm::Value *Size = CGF.getTypeSize(VD->getType());1151  CharUnits Align = CGM.getContext().getDeclAlign(VD);1152  Size = Bld.CreateNUWAdd(1153      Size, llvm::ConstantInt::get(CGF.SizeTy, Align.getQuantity() - 1));1154  llvm::Value *AlignVal =1155      llvm::ConstantInt::get(CGF.SizeTy, Align.getQuantity());1156  Size = Bld.CreateUDiv(Size, AlignVal);1157  Size = Bld.CreateNUWMul(Size, AlignVal);1158 1159  // Allocate space for this VLA object to be globalized.1160  llvm::Value *AllocArgs[] = {Size};1161  llvm::CallBase *VoidPtr =1162      CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(1163                              CGM.getModule(), OMPRTL___kmpc_alloc_shared),1164                          AllocArgs, VD->getName());1165  VoidPtr->addRetAttr(llvm::Attribute::get(1166      CGM.getLLVMContext(), llvm::Attribute::Alignment, Align.getQuantity()));1167 1168  return std::make_pair(VoidPtr, Size);1169}1170 1171void CGOpenMPRuntimeGPU::getKmpcFreeShared(1172    CodeGenFunction &CGF,1173    const std::pair<llvm::Value *, llvm::Value *> &AddrSizePair) {1174  // Deallocate the memory for each globalized VLA object1175  CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(1176                          CGM.getModule(), OMPRTL___kmpc_free_shared),1177                      {AddrSizePair.first, AddrSizePair.second});1178}1179 1180void CGOpenMPRuntimeGPU::emitGenericVarsEpilog(CodeGenFunction &CGF) {1181  if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)1182    return;1183 1184  const auto I = FunctionGlobalizedDecls.find(CGF.CurFn);1185  if (I != FunctionGlobalizedDecls.end()) {1186    // Deallocate the memory for each globalized VLA object that was1187    // globalized in the prolog (i.e. emitGenericVarsProlog).1188    for (const auto &AddrSizePair :1189         llvm::reverse(I->getSecond().EscapedVariableLengthDeclsAddrs)) {1190      CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(1191                              CGM.getModule(), OMPRTL___kmpc_free_shared),1192                          {AddrSizePair.first, AddrSizePair.second});1193    }1194    // Deallocate the memory for each globalized value1195    for (auto &Rec : llvm::reverse(I->getSecond().LocalVarData)) {1196      const auto *VD = cast<VarDecl>(Rec.first);1197      I->getSecond().MappedParams->restore(CGF);1198 1199      llvm::Value *FreeArgs[] = {Rec.second.GlobalizedVal,1200                                 CGF.getTypeSize(VD->getType())};1201      CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(1202                              CGM.getModule(), OMPRTL___kmpc_free_shared),1203                          FreeArgs);1204    }1205  }1206}1207 1208void CGOpenMPRuntimeGPU::emitTeamsCall(CodeGenFunction &CGF,1209                                         const OMPExecutableDirective &D,1210                                         SourceLocation Loc,1211                                         llvm::Function *OutlinedFn,1212                                         ArrayRef<llvm::Value *> CapturedVars) {1213  if (!CGF.HaveInsertPoint())1214    return;1215 1216  bool IsBareKernel = D.getSingleClause<OMPXBareClause>();1217 1218  RawAddress ZeroAddr = CGF.CreateDefaultAlignTempAlloca(CGF.Int32Ty,1219                                                         /*Name=*/".zero.addr");1220  CGF.Builder.CreateStore(CGF.Builder.getInt32(/*C*/ 0), ZeroAddr);1221  llvm::SmallVector<llvm::Value *, 16> OutlinedFnArgs;1222  // We don't emit any thread id function call in bare kernel, but because the1223  // outlined function has a pointer argument, we emit a nullptr here.1224  if (IsBareKernel)1225    OutlinedFnArgs.push_back(llvm::ConstantPointerNull::get(CGM.VoidPtrTy));1226  else1227    OutlinedFnArgs.push_back(emitThreadIDAddress(CGF, Loc).emitRawPointer(CGF));1228  OutlinedFnArgs.push_back(ZeroAddr.getPointer());1229  OutlinedFnArgs.append(CapturedVars.begin(), CapturedVars.end());1230  emitOutlinedFunctionCall(CGF, Loc, OutlinedFn, OutlinedFnArgs);1231}1232 1233void CGOpenMPRuntimeGPU::emitParallelCall(1234    CodeGenFunction &CGF, SourceLocation Loc, llvm::Function *OutlinedFn,1235    ArrayRef<llvm::Value *> CapturedVars, const Expr *IfCond,1236    llvm::Value *NumThreads, OpenMPNumThreadsClauseModifier NumThreadsModifier,1237    OpenMPSeverityClauseKind Severity, const Expr *Message) {1238  if (!CGF.HaveInsertPoint())1239    return;1240 1241  auto &&ParallelGen = [this, Loc, OutlinedFn, CapturedVars, IfCond,1242                        NumThreads](CodeGenFunction &CGF,1243                                    PrePostActionTy &Action) {1244    CGBuilderTy &Bld = CGF.Builder;1245    llvm::Value *NumThreadsVal = NumThreads;1246    llvm::Function *WFn = WrapperFunctionsMap[OutlinedFn];1247    llvm::PointerType *FnPtrTy = llvm::PointerType::get(1248        CGF.getLLVMContext(), CGM.getDataLayout().getProgramAddressSpace());1249 1250    llvm::Value *ID = llvm::ConstantPointerNull::get(FnPtrTy);1251    if (WFn)1252      ID = Bld.CreateBitOrPointerCast(WFn, FnPtrTy);1253 1254    llvm::Value *FnPtr = Bld.CreateBitOrPointerCast(OutlinedFn, FnPtrTy);1255 1256    // Create a private scope that will globalize the arguments1257    // passed from the outside of the target region.1258    // TODO: Is that needed?1259    CodeGenFunction::OMPPrivateScope PrivateArgScope(CGF);1260 1261    Address CapturedVarsAddrs = CGF.CreateDefaultAlignTempAlloca(1262        llvm::ArrayType::get(CGM.VoidPtrTy, CapturedVars.size()),1263        "captured_vars_addrs");1264    // There's something to share.1265    if (!CapturedVars.empty()) {1266      // Prepare for parallel region. Indicate the outlined function.1267      ASTContext &Ctx = CGF.getContext();1268      unsigned Idx = 0;1269      for (llvm::Value *V : CapturedVars) {1270        Address Dst = Bld.CreateConstArrayGEP(CapturedVarsAddrs, Idx);1271        llvm::Value *PtrV;1272        if (V->getType()->isIntegerTy())1273          PtrV = Bld.CreateIntToPtr(V, CGF.VoidPtrTy);1274        else1275          PtrV = Bld.CreatePointerBitCastOrAddrSpaceCast(V, CGF.VoidPtrTy);1276        CGF.EmitStoreOfScalar(PtrV, Dst, /*Volatile=*/false,1277                              Ctx.getPointerType(Ctx.VoidPtrTy));1278        ++Idx;1279      }1280    }1281 1282    llvm::Value *IfCondVal = nullptr;1283    if (IfCond)1284      IfCondVal = Bld.CreateIntCast(CGF.EvaluateExprAsBool(IfCond), CGF.Int32Ty,1285                                    /* isSigned */ false);1286    else1287      IfCondVal = llvm::ConstantInt::get(CGF.Int32Ty, 1);1288 1289    if (!NumThreadsVal)1290      NumThreadsVal = llvm::ConstantInt::get(CGF.Int32Ty, -1);1291    else1292      NumThreadsVal = Bld.CreateZExtOrTrunc(NumThreadsVal, CGF.Int32Ty);1293 1294    assert(IfCondVal && "Expected a value");1295    llvm::Value *RTLoc = emitUpdateLocation(CGF, Loc);1296    llvm::Value *Args[] = {1297        RTLoc,1298        getThreadID(CGF, Loc),1299        IfCondVal,1300        NumThreadsVal,1301        llvm::ConstantInt::get(CGF.Int32Ty, -1),1302        FnPtr,1303        ID,1304        Bld.CreateBitOrPointerCast(CapturedVarsAddrs.emitRawPointer(CGF),1305                                   CGF.VoidPtrPtrTy),1306        llvm::ConstantInt::get(CGM.SizeTy, CapturedVars.size())};1307    CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(1308                            CGM.getModule(), OMPRTL___kmpc_parallel_51),1309                        Args);1310  };1311 1312  RegionCodeGenTy RCG(ParallelGen);1313  RCG(CGF);1314}1315 1316void CGOpenMPRuntimeGPU::syncCTAThreads(CodeGenFunction &CGF) {1317  // Always emit simple barriers!1318  if (!CGF.HaveInsertPoint())1319    return;1320  // Build call __kmpc_barrier_simple_spmd(nullptr, 0);1321  // This function does not use parameters, so we can emit just default values.1322  llvm::Value *Args[] = {1323      llvm::ConstantPointerNull::get(1324          cast<llvm::PointerType>(getIdentTyPointerTy())),1325      llvm::ConstantInt::get(CGF.Int32Ty, /*V=*/0, /*isSigned=*/true)};1326  CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(1327                          CGM.getModule(), OMPRTL___kmpc_barrier_simple_spmd),1328                      Args);1329}1330 1331void CGOpenMPRuntimeGPU::emitBarrierCall(CodeGenFunction &CGF,1332                                           SourceLocation Loc,1333                                           OpenMPDirectiveKind Kind, bool,1334                                           bool) {1335  // Always emit simple barriers!1336  if (!CGF.HaveInsertPoint())1337    return;1338  // Build call __kmpc_cancel_barrier(loc, thread_id);1339  unsigned Flags = getDefaultFlagsForBarriers(Kind);1340  llvm::Value *Args[] = {emitUpdateLocation(CGF, Loc, Flags),1341                         getThreadID(CGF, Loc)};1342 1343  CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(1344                          CGM.getModule(), OMPRTL___kmpc_barrier),1345                      Args);1346}1347 1348void CGOpenMPRuntimeGPU::emitCriticalRegion(1349    CodeGenFunction &CGF, StringRef CriticalName,1350    const RegionCodeGenTy &CriticalOpGen, SourceLocation Loc,1351    const Expr *Hint) {1352  llvm::BasicBlock *LoopBB = CGF.createBasicBlock("omp.critical.loop");1353  llvm::BasicBlock *TestBB = CGF.createBasicBlock("omp.critical.test");1354  llvm::BasicBlock *SyncBB = CGF.createBasicBlock("omp.critical.sync");1355  llvm::BasicBlock *BodyBB = CGF.createBasicBlock("omp.critical.body");1356  llvm::BasicBlock *ExitBB = CGF.createBasicBlock("omp.critical.exit");1357 1358  auto &RT = static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());1359 1360  // Get the mask of active threads in the warp.1361  llvm::Value *Mask = CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(1362      CGM.getModule(), OMPRTL___kmpc_warp_active_thread_mask));1363  // Fetch team-local id of the thread.1364  llvm::Value *ThreadID = RT.getGPUThreadID(CGF);1365 1366  // Get the width of the team.1367  llvm::Value *TeamWidth = RT.getGPUNumThreads(CGF);1368 1369  // Initialize the counter variable for the loop.1370  QualType Int32Ty =1371      CGF.getContext().getIntTypeForBitwidth(/*DestWidth=*/32, /*Signed=*/0);1372  Address Counter = CGF.CreateMemTemp(Int32Ty, "critical_counter");1373  LValue CounterLVal = CGF.MakeAddrLValue(Counter, Int32Ty);1374  CGF.EmitStoreOfScalar(llvm::Constant::getNullValue(CGM.Int32Ty), CounterLVal,1375                        /*isInit=*/true);1376 1377  // Block checks if loop counter exceeds upper bound.1378  CGF.EmitBlock(LoopBB);1379  llvm::Value *CounterVal = CGF.EmitLoadOfScalar(CounterLVal, Loc);1380  llvm::Value *CmpLoopBound = CGF.Builder.CreateICmpSLT(CounterVal, TeamWidth);1381  CGF.Builder.CreateCondBr(CmpLoopBound, TestBB, ExitBB);1382 1383  // Block tests which single thread should execute region, and which threads1384  // should go straight to synchronisation point.1385  CGF.EmitBlock(TestBB);1386  CounterVal = CGF.EmitLoadOfScalar(CounterLVal, Loc);1387  llvm::Value *CmpThreadToCounter =1388      CGF.Builder.CreateICmpEQ(ThreadID, CounterVal);1389  CGF.Builder.CreateCondBr(CmpThreadToCounter, BodyBB, SyncBB);1390 1391  // Block emits the body of the critical region.1392  CGF.EmitBlock(BodyBB);1393 1394  // Output the critical statement.1395  CGOpenMPRuntime::emitCriticalRegion(CGF, CriticalName, CriticalOpGen, Loc,1396                                      Hint);1397 1398  // After the body surrounded by the critical region, the single executing1399  // thread will jump to the synchronisation point.1400  // Block waits for all threads in current team to finish then increments the1401  // counter variable and returns to the loop.1402  CGF.EmitBlock(SyncBB);1403  // Reconverge active threads in the warp.1404  (void)CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(1405                                CGM.getModule(), OMPRTL___kmpc_syncwarp),1406                            Mask);1407 1408  llvm::Value *IncCounterVal =1409      CGF.Builder.CreateNSWAdd(CounterVal, CGF.Builder.getInt32(1));1410  CGF.EmitStoreOfScalar(IncCounterVal, CounterLVal);1411  CGF.EmitBranch(LoopBB);1412 1413  // Block that is reached when  all threads in the team complete the region.1414  CGF.EmitBlock(ExitBB, /*IsFinished=*/true);1415}1416 1417/// Cast value to the specified type.1418static llvm::Value *castValueToType(CodeGenFunction &CGF, llvm::Value *Val,1419                                    QualType ValTy, QualType CastTy,1420                                    SourceLocation Loc) {1421  assert(!CGF.getContext().getTypeSizeInChars(CastTy).isZero() &&1422         "Cast type must sized.");1423  assert(!CGF.getContext().getTypeSizeInChars(ValTy).isZero() &&1424         "Val type must sized.");1425  llvm::Type *LLVMCastTy = CGF.ConvertTypeForMem(CastTy);1426  if (ValTy == CastTy)1427    return Val;1428  if (CGF.getContext().getTypeSizeInChars(ValTy) ==1429      CGF.getContext().getTypeSizeInChars(CastTy))1430    return CGF.Builder.CreateBitCast(Val, LLVMCastTy);1431  if (CastTy->isIntegerType() && ValTy->isIntegerType())1432    return CGF.Builder.CreateIntCast(Val, LLVMCastTy,1433                                     CastTy->hasSignedIntegerRepresentation());1434  Address CastItem = CGF.CreateMemTemp(CastTy);1435  Address ValCastItem = CastItem.withElementType(Val->getType());1436  CGF.EmitStoreOfScalar(Val, ValCastItem, /*Volatile=*/false, ValTy,1437                        LValueBaseInfo(AlignmentSource::Type),1438                        TBAAAccessInfo());1439  return CGF.EmitLoadOfScalar(CastItem, /*Volatile=*/false, CastTy, Loc,1440                              LValueBaseInfo(AlignmentSource::Type),1441                              TBAAAccessInfo());1442}1443 1444///1445/// Design of OpenMP reductions on the GPU1446///1447/// Consider a typical OpenMP program with one or more reduction1448/// clauses:1449///1450/// float foo;1451/// double bar;1452/// #pragma omp target teams distribute parallel for \1453///             reduction(+:foo) reduction(*:bar)1454/// for (int i = 0; i < N; i++) {1455///   foo += A[i]; bar *= B[i];1456/// }1457///1458/// where 'foo' and 'bar' are reduced across all OpenMP threads in1459/// all teams.  In our OpenMP implementation on the NVPTX device an1460/// OpenMP team is mapped to a CUDA threadblock and OpenMP threads1461/// within a team are mapped to CUDA threads within a threadblock.1462/// Our goal is to efficiently aggregate values across all OpenMP1463/// threads such that:1464///1465///   - the compiler and runtime are logically concise, and1466///   - the reduction is performed efficiently in a hierarchical1467///     manner as follows: within OpenMP threads in the same warp,1468///     across warps in a threadblock, and finally across teams on1469///     the NVPTX device.1470///1471/// Introduction to Decoupling1472///1473/// We would like to decouple the compiler and the runtime so that the1474/// latter is ignorant of the reduction variables (number, data types)1475/// and the reduction operators.  This allows a simpler interface1476/// and implementation while still attaining good performance.1477///1478/// Pseudocode for the aforementioned OpenMP program generated by the1479/// compiler is as follows:1480///1481/// 1. Create private copies of reduction variables on each OpenMP1482///    thread: 'foo_private', 'bar_private'1483/// 2. Each OpenMP thread reduces the chunk of 'A' and 'B' assigned1484///    to it and writes the result in 'foo_private' and 'bar_private'1485///    respectively.1486/// 3. Call the OpenMP runtime on the GPU to reduce within a team1487///    and store the result on the team master:1488///1489///     __kmpc_nvptx_parallel_reduce_nowait_v2(...,1490///        reduceData, shuffleReduceFn, interWarpCpyFn)1491///1492///     where:1493///       struct ReduceData {1494///         double *foo;1495///         double *bar;1496///       } reduceData1497///       reduceData.foo = &foo_private1498///       reduceData.bar = &bar_private1499///1500///     'shuffleReduceFn' and 'interWarpCpyFn' are pointers to two1501///     auxiliary functions generated by the compiler that operate on1502///     variables of type 'ReduceData'.  They aid the runtime perform1503///     algorithmic steps in a data agnostic manner.1504///1505///     'shuffleReduceFn' is a pointer to a function that reduces data1506///     of type 'ReduceData' across two OpenMP threads (lanes) in the1507///     same warp.  It takes the following arguments as input:1508///1509///     a. variable of type 'ReduceData' on the calling lane,1510///     b. its lane_id,1511///     c. an offset relative to the current lane_id to generate a1512///        remote_lane_id.  The remote lane contains the second1513///        variable of type 'ReduceData' that is to be reduced.1514///     d. an algorithm version parameter determining which reduction1515///        algorithm to use.1516///1517///     'shuffleReduceFn' retrieves data from the remote lane using1518///     efficient GPU shuffle intrinsics and reduces, using the1519///     algorithm specified by the 4th parameter, the two operands1520///     element-wise.  The result is written to the first operand.1521///1522///     Different reduction algorithms are implemented in different1523///     runtime functions, all calling 'shuffleReduceFn' to perform1524///     the essential reduction step.  Therefore, based on the 4th1525///     parameter, this function behaves slightly differently to1526///     cooperate with the runtime to ensure correctness under1527///     different circumstances.1528///1529///     'InterWarpCpyFn' is a pointer to a function that transfers1530///     reduced variables across warps.  It tunnels, through CUDA1531///     shared memory, the thread-private data of type 'ReduceData'1532///     from lane 0 of each warp to a lane in the first warp.1533/// 4. Call the OpenMP runtime on the GPU to reduce across teams.1534///    The last team writes the global reduced value to memory.1535///1536///     ret = __kmpc_nvptx_teams_reduce_nowait(...,1537///             reduceData, shuffleReduceFn, interWarpCpyFn,1538///             scratchpadCopyFn, loadAndReduceFn)1539///1540///     'scratchpadCopyFn' is a helper that stores reduced1541///     data from the team master to a scratchpad array in1542///     global memory.1543///1544///     'loadAndReduceFn' is a helper that loads data from1545///     the scratchpad array and reduces it with the input1546///     operand.1547///1548///     These compiler generated functions hide address1549///     calculation and alignment information from the runtime.1550/// 5. if ret == 1:1551///     The team master of the last team stores the reduced1552///     result to the globals in memory.1553///     foo += reduceData.foo; bar *= reduceData.bar1554///1555///1556/// Warp Reduction Algorithms1557///1558/// On the warp level, we have three algorithms implemented in the1559/// OpenMP runtime depending on the number of active lanes:1560///1561/// Full Warp Reduction1562///1563/// The reduce algorithm within a warp where all lanes are active1564/// is implemented in the runtime as follows:1565///1566/// full_warp_reduce(void *reduce_data,1567///                  kmp_ShuffleReductFctPtr ShuffleReduceFn) {1568///   for (int offset = WARPSIZE/2; offset > 0; offset /= 2)1569///     ShuffleReduceFn(reduce_data, 0, offset, 0);1570/// }1571///1572/// The algorithm completes in log(2, WARPSIZE) steps.1573///1574/// 'ShuffleReduceFn' is used here with lane_id set to 0 because it is1575/// not used therefore we save instructions by not retrieving lane_id1576/// from the corresponding special registers.  The 4th parameter, which1577/// represents the version of the algorithm being used, is set to 0 to1578/// signify full warp reduction.1579///1580/// In this version, 'ShuffleReduceFn' behaves, per element, as follows:1581///1582/// #reduce_elem refers to an element in the local lane's data structure1583/// #remote_elem is retrieved from a remote lane1584/// remote_elem = shuffle_down(reduce_elem, offset, WARPSIZE);1585/// reduce_elem = reduce_elem REDUCE_OP remote_elem;1586///1587/// Contiguous Partial Warp Reduction1588///1589/// This reduce algorithm is used within a warp where only the first1590/// 'n' (n <= WARPSIZE) lanes are active.  It is typically used when the1591/// number of OpenMP threads in a parallel region is not a multiple of1592/// WARPSIZE.  The algorithm is implemented in the runtime as follows:1593///1594/// void1595/// contiguous_partial_reduce(void *reduce_data,1596///                           kmp_ShuffleReductFctPtr ShuffleReduceFn,1597///                           int size, int lane_id) {1598///   int curr_size;1599///   int offset;1600///   curr_size = size;1601///   mask = curr_size/2;1602///   while (offset>0) {1603///     ShuffleReduceFn(reduce_data, lane_id, offset, 1);1604///     curr_size = (curr_size+1)/2;1605///     offset = curr_size/2;1606///   }1607/// }1608///1609/// In this version, 'ShuffleReduceFn' behaves, per element, as follows:1610///1611/// remote_elem = shuffle_down(reduce_elem, offset, WARPSIZE);1612/// if (lane_id < offset)1613///     reduce_elem = reduce_elem REDUCE_OP remote_elem1614/// else1615///     reduce_elem = remote_elem1616///1617/// This algorithm assumes that the data to be reduced are located in a1618/// contiguous subset of lanes starting from the first.  When there is1619/// an odd number of active lanes, the data in the last lane is not1620/// aggregated with any other lane's dat but is instead copied over.1621///1622/// Dispersed Partial Warp Reduction1623///1624/// This algorithm is used within a warp when any discontiguous subset of1625/// lanes are active.  It is used to implement the reduction operation1626/// across lanes in an OpenMP simd region or in a nested parallel region.1627///1628/// void1629/// dispersed_partial_reduce(void *reduce_data,1630///                          kmp_ShuffleReductFctPtr ShuffleReduceFn) {1631///   int size, remote_id;1632///   int logical_lane_id = number_of_active_lanes_before_me() * 2;1633///   do {1634///       remote_id = next_active_lane_id_right_after_me();1635///       # the above function returns 0 of no active lane1636///       # is present right after the current lane.1637///       size = number_of_active_lanes_in_this_warp();1638///       logical_lane_id /= 2;1639///       ShuffleReduceFn(reduce_data, logical_lane_id,1640///                       remote_id-1-threadIdx.x, 2);1641///   } while (logical_lane_id % 2 == 0 && size > 1);1642/// }1643///1644/// There is no assumption made about the initial state of the reduction.1645/// Any number of lanes (>=1) could be active at any position.  The reduction1646/// result is returned in the first active lane.1647///1648/// In this version, 'ShuffleReduceFn' behaves, per element, as follows:1649///1650/// remote_elem = shuffle_down(reduce_elem, offset, WARPSIZE);1651/// if (lane_id % 2 == 0 && offset > 0)1652///     reduce_elem = reduce_elem REDUCE_OP remote_elem1653/// else1654///     reduce_elem = remote_elem1655///1656///1657/// Intra-Team Reduction1658///1659/// This function, as implemented in the runtime call1660/// '__kmpc_nvptx_parallel_reduce_nowait_v2', aggregates data across OpenMP1661/// threads in a team.  It first reduces within a warp using the1662/// aforementioned algorithms.  We then proceed to gather all such1663/// reduced values at the first warp.1664///1665/// The runtime makes use of the function 'InterWarpCpyFn', which copies1666/// data from each of the "warp master" (zeroth lane of each warp, where1667/// warp-reduced data is held) to the zeroth warp.  This step reduces (in1668/// a mathematical sense) the problem of reduction across warp masters in1669/// a block to the problem of warp reduction.1670///1671///1672/// Inter-Team Reduction1673///1674/// Once a team has reduced its data to a single value, it is stored in1675/// a global scratchpad array.  Since each team has a distinct slot, this1676/// can be done without locking.1677///1678/// The last team to write to the scratchpad array proceeds to reduce the1679/// scratchpad array.  One or more workers in the last team use the helper1680/// 'loadAndReduceDataFn' to load and reduce values from the array, i.e.,1681/// the k'th worker reduces every k'th element.1682///1683/// Finally, a call is made to '__kmpc_nvptx_parallel_reduce_nowait_v2' to1684/// reduce across workers and compute a globally reduced value.1685///1686void CGOpenMPRuntimeGPU::emitReduction(1687    CodeGenFunction &CGF, SourceLocation Loc, ArrayRef<const Expr *> Privates,1688    ArrayRef<const Expr *> LHSExprs, ArrayRef<const Expr *> RHSExprs,1689    ArrayRef<const Expr *> ReductionOps, ReductionOptionsTy Options) {1690  if (!CGF.HaveInsertPoint())1691    return;1692 1693  bool ParallelReduction = isOpenMPParallelDirective(Options.ReductionKind);1694  bool TeamsReduction = isOpenMPTeamsDirective(Options.ReductionKind);1695 1696  ASTContext &C = CGM.getContext();1697 1698  if (Options.SimpleReduction) {1699    assert(!TeamsReduction && !ParallelReduction &&1700           "Invalid reduction selection in emitReduction.");1701    (void)ParallelReduction;1702    CGOpenMPRuntime::emitReduction(CGF, Loc, Privates, LHSExprs, RHSExprs,1703                                   ReductionOps, Options);1704    return;1705  }1706 1707  llvm::SmallDenseMap<const ValueDecl *, const FieldDecl *> VarFieldMap;1708  llvm::SmallVector<const ValueDecl *, 4> PrivatesReductions(Privates.size());1709  int Cnt = 0;1710  for (const Expr *DRE : Privates) {1711    PrivatesReductions[Cnt] = cast<DeclRefExpr>(DRE)->getDecl();1712    ++Cnt;1713  }1714  const RecordDecl *ReductionRec = ::buildRecordForGlobalizedVars(1715      CGM.getContext(), PrivatesReductions, {}, VarFieldMap, 1);1716 1717  if (TeamsReduction)1718    TeamsReductions.push_back(ReductionRec);1719 1720  // Source location for the ident struct1721  llvm::Value *RTLoc = emitUpdateLocation(CGF, Loc);1722 1723  using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy;1724  InsertPointTy AllocaIP(CGF.AllocaInsertPt->getParent(),1725                         CGF.AllocaInsertPt->getIterator());1726  InsertPointTy CodeGenIP(CGF.Builder.GetInsertBlock(),1727                          CGF.Builder.GetInsertPoint());1728  llvm::OpenMPIRBuilder::LocationDescription OmpLoc(1729      CodeGenIP, CGF.SourceLocToDebugLoc(Loc));1730  llvm::SmallVector<llvm::OpenMPIRBuilder::ReductionInfo, 2> ReductionInfos;1731 1732  CodeGenFunction::OMPPrivateScope Scope(CGF);1733  unsigned Idx = 0;1734  for (const Expr *Private : Privates) {1735    llvm::Type *ElementType;1736    llvm::Value *Variable;1737    llvm::Value *PrivateVariable;1738    llvm::OpenMPIRBuilder::ReductionGenAtomicCBTy AtomicReductionGen = nullptr;1739    ElementType = CGF.ConvertTypeForMem(Private->getType());1740    const auto *RHSVar =1741        cast<VarDecl>(cast<DeclRefExpr>(RHSExprs[Idx])->getDecl());1742    PrivateVariable = CGF.GetAddrOfLocalVar(RHSVar).emitRawPointer(CGF);1743    const auto *LHSVar =1744        cast<VarDecl>(cast<DeclRefExpr>(LHSExprs[Idx])->getDecl());1745    Variable = CGF.GetAddrOfLocalVar(LHSVar).emitRawPointer(CGF);1746    llvm::OpenMPIRBuilder::EvalKind EvalKind;1747    switch (CGF.getEvaluationKind(Private->getType())) {1748    case TEK_Scalar:1749      EvalKind = llvm::OpenMPIRBuilder::EvalKind::Scalar;1750      break;1751    case TEK_Complex:1752      EvalKind = llvm::OpenMPIRBuilder::EvalKind::Complex;1753      break;1754    case TEK_Aggregate:1755      EvalKind = llvm::OpenMPIRBuilder::EvalKind::Aggregate;1756      break;1757    }1758    auto ReductionGen = [&](InsertPointTy CodeGenIP, unsigned I,1759                            llvm::Value **LHSPtr, llvm::Value **RHSPtr,1760                            llvm::Function *NewFunc) {1761      CGF.Builder.restoreIP(CodeGenIP);1762      auto *CurFn = CGF.CurFn;1763      CGF.CurFn = NewFunc;1764 1765      *LHSPtr = CGF.GetAddrOfLocalVar(1766                       cast<VarDecl>(cast<DeclRefExpr>(LHSExprs[I])->getDecl()))1767                    .emitRawPointer(CGF);1768      *RHSPtr = CGF.GetAddrOfLocalVar(1769                       cast<VarDecl>(cast<DeclRefExpr>(RHSExprs[I])->getDecl()))1770                    .emitRawPointer(CGF);1771 1772      emitSingleReductionCombiner(CGF, ReductionOps[I], Privates[I],1773                                  cast<DeclRefExpr>(LHSExprs[I]),1774                                  cast<DeclRefExpr>(RHSExprs[I]));1775 1776      CGF.CurFn = CurFn;1777 1778      return InsertPointTy(CGF.Builder.GetInsertBlock(),1779                           CGF.Builder.GetInsertPoint());1780    };1781    ReductionInfos.emplace_back(llvm::OpenMPIRBuilder::ReductionInfo(1782        ElementType, Variable, PrivateVariable, EvalKind,1783        /*ReductionGen=*/nullptr, ReductionGen, AtomicReductionGen,1784        /*DataPtrPtrGen=*/nullptr));1785    Idx++;1786  }1787 1788  llvm::OpenMPIRBuilder::InsertPointTy AfterIP =1789      cantFail(OMPBuilder.createReductionsGPU(1790          OmpLoc, AllocaIP, CodeGenIP, ReductionInfos, /*IsByRef=*/{}, false,1791          TeamsReduction, llvm::OpenMPIRBuilder::ReductionGenCBKind::Clang,1792          CGF.getTarget().getGridValue(),1793          C.getLangOpts().OpenMPCUDAReductionBufNum, RTLoc));1794  CGF.Builder.restoreIP(AfterIP);1795}1796 1797const VarDecl *1798CGOpenMPRuntimeGPU::translateParameter(const FieldDecl *FD,1799                                       const VarDecl *NativeParam) const {1800  if (!NativeParam->getType()->isReferenceType())1801    return NativeParam;1802  QualType ArgType = NativeParam->getType();1803  QualifierCollector QC;1804  const Type *NonQualTy = QC.strip(ArgType);1805  QualType PointeeTy = cast<ReferenceType>(NonQualTy)->getPointeeType();1806  if (const auto *Attr = FD->getAttr<OMPCaptureKindAttr>()) {1807    if (Attr->getCaptureKind() == OMPC_map) {1808      PointeeTy = CGM.getContext().getAddrSpaceQualType(PointeeTy,1809                                                        LangAS::opencl_global);1810    }1811  }1812  ArgType = CGM.getContext().getPointerType(PointeeTy);1813  QC.addRestrict();1814  enum { NVPTX_local_addr = 5 };1815  QC.addAddressSpace(getLangASFromTargetAS(NVPTX_local_addr));1816  ArgType = QC.apply(CGM.getContext(), ArgType);1817  if (isa<ImplicitParamDecl>(NativeParam))1818    return ImplicitParamDecl::Create(1819        CGM.getContext(), /*DC=*/nullptr, NativeParam->getLocation(),1820        NativeParam->getIdentifier(), ArgType, ImplicitParamKind::Other);1821  return ParmVarDecl::Create(1822      CGM.getContext(),1823      const_cast<DeclContext *>(NativeParam->getDeclContext()),1824      NativeParam->getBeginLoc(), NativeParam->getLocation(),1825      NativeParam->getIdentifier(), ArgType,1826      /*TInfo=*/nullptr, SC_None, /*DefArg=*/nullptr);1827}1828 1829Address1830CGOpenMPRuntimeGPU::getParameterAddress(CodeGenFunction &CGF,1831                                          const VarDecl *NativeParam,1832                                          const VarDecl *TargetParam) const {1833  assert(NativeParam != TargetParam &&1834         NativeParam->getType()->isReferenceType() &&1835         "Native arg must not be the same as target arg.");1836  Address LocalAddr = CGF.GetAddrOfLocalVar(TargetParam);1837  QualType NativeParamType = NativeParam->getType();1838  QualifierCollector QC;1839  const Type *NonQualTy = QC.strip(NativeParamType);1840  QualType NativePointeeTy = cast<ReferenceType>(NonQualTy)->getPointeeType();1841  unsigned NativePointeeAddrSpace =1842      CGF.getTypes().getTargetAddressSpace(NativePointeeTy);1843  QualType TargetTy = TargetParam->getType();1844  llvm::Value *TargetAddr = CGF.EmitLoadOfScalar(LocalAddr, /*Volatile=*/false,1845                                                 TargetTy, SourceLocation());1846  // Cast to native address space.1847  TargetAddr = CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(1848      TargetAddr,1849      llvm::PointerType::get(CGF.getLLVMContext(), NativePointeeAddrSpace));1850  Address NativeParamAddr = CGF.CreateMemTemp(NativeParamType);1851  CGF.EmitStoreOfScalar(TargetAddr, NativeParamAddr, /*Volatile=*/false,1852                        NativeParamType);1853  return NativeParamAddr;1854}1855 1856void CGOpenMPRuntimeGPU::emitOutlinedFunctionCall(1857    CodeGenFunction &CGF, SourceLocation Loc, llvm::FunctionCallee OutlinedFn,1858    ArrayRef<llvm::Value *> Args) const {1859  SmallVector<llvm::Value *, 4> TargetArgs;1860  TargetArgs.reserve(Args.size());1861  auto *FnType = OutlinedFn.getFunctionType();1862  for (unsigned I = 0, E = Args.size(); I < E; ++I) {1863    if (FnType->isVarArg() && FnType->getNumParams() <= I) {1864      TargetArgs.append(std::next(Args.begin(), I), Args.end());1865      break;1866    }1867    llvm::Type *TargetType = FnType->getParamType(I);1868    llvm::Value *NativeArg = Args[I];1869    if (!TargetType->isPointerTy()) {1870      TargetArgs.emplace_back(NativeArg);1871      continue;1872    }1873    TargetArgs.emplace_back(1874        CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(NativeArg, TargetType));1875  }1876  CGOpenMPRuntime::emitOutlinedFunctionCall(CGF, Loc, OutlinedFn, TargetArgs);1877}1878 1879/// Emit function which wraps the outline parallel region1880/// and controls the arguments which are passed to this function.1881/// The wrapper ensures that the outlined function is called1882/// with the correct arguments when data is shared.1883llvm::Function *CGOpenMPRuntimeGPU::createParallelDataSharingWrapper(1884    llvm::Function *OutlinedParallelFn, const OMPExecutableDirective &D) {1885  ASTContext &Ctx = CGM.getContext();1886  const auto &CS = *D.getCapturedStmt(OMPD_parallel);1887 1888  // Create a function that takes as argument the source thread.1889  FunctionArgList WrapperArgs;1890  QualType Int16QTy =1891      Ctx.getIntTypeForBitwidth(/*DestWidth=*/16, /*Signed=*/false);1892  QualType Int32QTy =1893      Ctx.getIntTypeForBitwidth(/*DestWidth=*/32, /*Signed=*/false);1894  ImplicitParamDecl ParallelLevelArg(Ctx, /*DC=*/nullptr, D.getBeginLoc(),1895                                     /*Id=*/nullptr, Int16QTy,1896                                     ImplicitParamKind::Other);1897  ImplicitParamDecl WrapperArg(Ctx, /*DC=*/nullptr, D.getBeginLoc(),1898                               /*Id=*/nullptr, Int32QTy,1899                               ImplicitParamKind::Other);1900  WrapperArgs.emplace_back(&ParallelLevelArg);1901  WrapperArgs.emplace_back(&WrapperArg);1902 1903  const CGFunctionInfo &CGFI =1904      CGM.getTypes().arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, WrapperArgs);1905 1906  auto *Fn = llvm::Function::Create(1907      CGM.getTypes().GetFunctionType(CGFI), llvm::GlobalValue::InternalLinkage,1908      Twine(OutlinedParallelFn->getName(), "_wrapper"), &CGM.getModule());1909 1910  // Ensure we do not inline the function. This is trivially true for the ones1911  // passed to __kmpc_fork_call but the ones calles in serialized regions1912  // could be inlined. This is not a perfect but it is closer to the invariant1913  // we want, namely, every data environment starts with a new function.1914  // TODO: We should pass the if condition to the runtime function and do the1915  //       handling there. Much cleaner code.1916  Fn->addFnAttr(llvm::Attribute::NoInline);1917 1918  CGM.SetInternalFunctionAttributes(GlobalDecl(), Fn, CGFI);1919  Fn->setLinkage(llvm::GlobalValue::InternalLinkage);1920  Fn->setDoesNotRecurse();1921 1922  CodeGenFunction CGF(CGM, /*suppressNewContext=*/true);1923  CGF.StartFunction(GlobalDecl(), Ctx.VoidTy, Fn, CGFI, WrapperArgs,1924                    D.getBeginLoc(), D.getBeginLoc());1925 1926  const auto *RD = CS.getCapturedRecordDecl();1927  auto CurField = RD->field_begin();1928 1929  Address ZeroAddr = CGF.CreateDefaultAlignTempAlloca(CGF.Int32Ty,1930                                                      /*Name=*/".zero.addr");1931  CGF.Builder.CreateStore(CGF.Builder.getInt32(/*C*/ 0), ZeroAddr);1932  // Get the array of arguments.1933  SmallVector<llvm::Value *, 8> Args;1934 1935  Args.emplace_back(CGF.GetAddrOfLocalVar(&WrapperArg).emitRawPointer(CGF));1936  Args.emplace_back(ZeroAddr.emitRawPointer(CGF));1937 1938  CGBuilderTy &Bld = CGF.Builder;1939  auto CI = CS.capture_begin();1940 1941  // Use global memory for data sharing.1942  // Handle passing of global args to workers.1943  RawAddress GlobalArgs =1944      CGF.CreateDefaultAlignTempAlloca(CGF.VoidPtrPtrTy, "global_args");1945  llvm::Value *GlobalArgsPtr = GlobalArgs.getPointer();1946  llvm::Value *DataSharingArgs[] = {GlobalArgsPtr};1947  CGF.EmitRuntimeCall(OMPBuilder.getOrCreateRuntimeFunction(1948                          CGM.getModule(), OMPRTL___kmpc_get_shared_variables),1949                      DataSharingArgs);1950 1951  // Retrieve the shared variables from the list of references returned1952  // by the runtime. Pass the variables to the outlined function.1953  Address SharedArgListAddress = Address::invalid();1954  if (CS.capture_size() > 0 ||1955      isOpenMPLoopBoundSharingDirective(D.getDirectiveKind())) {1956    SharedArgListAddress = CGF.EmitLoadOfPointer(1957        GlobalArgs, CGF.getContext()1958                        .getPointerType(CGF.getContext().VoidPtrTy)1959                        .castAs<PointerType>());1960  }1961  unsigned Idx = 0;1962  if (isOpenMPLoopBoundSharingDirective(D.getDirectiveKind())) {1963    Address Src = Bld.CreateConstInBoundsGEP(SharedArgListAddress, Idx);1964    Address TypedAddress = Bld.CreatePointerBitCastOrAddrSpaceCast(1965        Src, Bld.getPtrTy(0), CGF.SizeTy);1966    llvm::Value *LB = CGF.EmitLoadOfScalar(1967        TypedAddress,1968        /*Volatile=*/false,1969        CGF.getContext().getPointerType(CGF.getContext().getSizeType()),1970        cast<OMPLoopDirective>(D).getLowerBoundVariable()->getExprLoc());1971    Args.emplace_back(LB);1972    ++Idx;1973    Src = Bld.CreateConstInBoundsGEP(SharedArgListAddress, Idx);1974    TypedAddress = Bld.CreatePointerBitCastOrAddrSpaceCast(Src, Bld.getPtrTy(0),1975                                                           CGF.SizeTy);1976    llvm::Value *UB = CGF.EmitLoadOfScalar(1977        TypedAddress,1978        /*Volatile=*/false,1979        CGF.getContext().getPointerType(CGF.getContext().getSizeType()),1980        cast<OMPLoopDirective>(D).getUpperBoundVariable()->getExprLoc());1981    Args.emplace_back(UB);1982    ++Idx;1983  }1984  if (CS.capture_size() > 0) {1985    ASTContext &CGFContext = CGF.getContext();1986    for (unsigned I = 0, E = CS.capture_size(); I < E; ++I, ++CI, ++CurField) {1987      QualType ElemTy = CurField->getType();1988      Address Src = Bld.CreateConstInBoundsGEP(SharedArgListAddress, I + Idx);1989      Address TypedAddress = Bld.CreatePointerBitCastOrAddrSpaceCast(1990          Src, CGF.ConvertTypeForMem(CGFContext.getPointerType(ElemTy)),1991          CGF.ConvertTypeForMem(ElemTy));1992      llvm::Value *Arg = CGF.EmitLoadOfScalar(TypedAddress,1993                                              /*Volatile=*/false,1994                                              CGFContext.getPointerType(ElemTy),1995                                              CI->getLocation());1996      if (CI->capturesVariableByCopy() &&1997          !CI->getCapturedVar()->getType()->isAnyPointerType()) {1998        Arg = castValueToType(CGF, Arg, ElemTy, CGFContext.getUIntPtrType(),1999                              CI->getLocation());2000      }2001      Args.emplace_back(Arg);2002    }2003  }2004 2005  emitOutlinedFunctionCall(CGF, D.getBeginLoc(), OutlinedParallelFn, Args);2006  CGF.FinishFunction();2007  return Fn;2008}2009 2010void CGOpenMPRuntimeGPU::emitFunctionProlog(CodeGenFunction &CGF,2011                                              const Decl *D) {2012  if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)2013    return;2014 2015  assert(D && "Expected function or captured|block decl.");2016  assert(FunctionGlobalizedDecls.count(CGF.CurFn) == 0 &&2017         "Function is registered already.");2018  assert((!TeamAndReductions.first || TeamAndReductions.first == D) &&2019         "Team is set but not processed.");2020  const Stmt *Body = nullptr;2021  bool NeedToDelayGlobalization = false;2022  if (const auto *FD = dyn_cast<FunctionDecl>(D)) {2023    Body = FD->getBody();2024  } else if (const auto *BD = dyn_cast<BlockDecl>(D)) {2025    Body = BD->getBody();2026  } else if (const auto *CD = dyn_cast<CapturedDecl>(D)) {2027    Body = CD->getBody();2028    NeedToDelayGlobalization = CGF.CapturedStmtInfo->getKind() == CR_OpenMP;2029    if (NeedToDelayGlobalization &&2030        getExecutionMode() == CGOpenMPRuntimeGPU::EM_SPMD)2031      return;2032  }2033  if (!Body)2034    return;2035  CheckVarsEscapingDeclContext VarChecker(CGF, TeamAndReductions.second);2036  VarChecker.Visit(Body);2037  const RecordDecl *GlobalizedVarsRecord =2038      VarChecker.getGlobalizedRecord(IsInTTDRegion);2039  TeamAndReductions.first = nullptr;2040  TeamAndReductions.second.clear();2041  ArrayRef<const ValueDecl *> EscapedVariableLengthDecls =2042      VarChecker.getEscapedVariableLengthDecls();2043  ArrayRef<const ValueDecl *> DelayedVariableLengthDecls =2044      VarChecker.getDelayedVariableLengthDecls();2045  if (!GlobalizedVarsRecord && EscapedVariableLengthDecls.empty() &&2046      DelayedVariableLengthDecls.empty())2047    return;2048  auto I = FunctionGlobalizedDecls.try_emplace(CGF.CurFn).first;2049  I->getSecond().MappedParams =2050      std::make_unique<CodeGenFunction::OMPMapVars>();2051  I->getSecond().EscapedParameters.insert(2052      VarChecker.getEscapedParameters().begin(),2053      VarChecker.getEscapedParameters().end());2054  I->getSecond().EscapedVariableLengthDecls.append(2055      EscapedVariableLengthDecls.begin(), EscapedVariableLengthDecls.end());2056  I->getSecond().DelayedVariableLengthDecls.append(2057      DelayedVariableLengthDecls.begin(), DelayedVariableLengthDecls.end());2058  DeclToAddrMapTy &Data = I->getSecond().LocalVarData;2059  for (const ValueDecl *VD : VarChecker.getEscapedDecls()) {2060    assert(VD->isCanonicalDecl() && "Expected canonical declaration");2061    Data.try_emplace(VD);2062  }2063  if (!NeedToDelayGlobalization) {2064    emitGenericVarsProlog(CGF, D->getBeginLoc());2065    struct GlobalizationScope final : EHScopeStack::Cleanup {2066      GlobalizationScope() = default;2067 2068      void Emit(CodeGenFunction &CGF, Flags flags) override {2069        static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime())2070            .emitGenericVarsEpilog(CGF);2071      }2072    };2073    CGF.EHStack.pushCleanup<GlobalizationScope>(NormalAndEHCleanup);2074  }2075}2076 2077Address CGOpenMPRuntimeGPU::getAddressOfLocalVariable(CodeGenFunction &CGF,2078                                                        const VarDecl *VD) {2079  if (VD && VD->hasAttr<OMPAllocateDeclAttr>()) {2080    const auto *A = VD->getAttr<OMPAllocateDeclAttr>();2081    auto AS = LangAS::Default;2082    switch (A->getAllocatorType()) {2083    case OMPAllocateDeclAttr::OMPNullMemAlloc:2084    case OMPAllocateDeclAttr::OMPDefaultMemAlloc:2085    case OMPAllocateDeclAttr::OMPHighBWMemAlloc:2086    case OMPAllocateDeclAttr::OMPLowLatMemAlloc:2087      break;2088    case OMPAllocateDeclAttr::OMPThreadMemAlloc:2089      return Address::invalid();2090    case OMPAllocateDeclAttr::OMPUserDefinedMemAlloc:2091      // TODO: implement aupport for user-defined allocators.2092      return Address::invalid();2093    case OMPAllocateDeclAttr::OMPConstMemAlloc:2094      AS = LangAS::cuda_constant;2095      break;2096    case OMPAllocateDeclAttr::OMPPTeamMemAlloc:2097      AS = LangAS::cuda_shared;2098      break;2099    case OMPAllocateDeclAttr::OMPLargeCapMemAlloc:2100    case OMPAllocateDeclAttr::OMPCGroupMemAlloc:2101      break;2102    }2103    llvm::Type *VarTy = CGF.ConvertTypeForMem(VD->getType());2104    auto *GV = new llvm::GlobalVariable(2105        CGM.getModule(), VarTy, /*isConstant=*/false,2106        llvm::GlobalValue::InternalLinkage, llvm::PoisonValue::get(VarTy),2107        VD->getName(),2108        /*InsertBefore=*/nullptr, llvm::GlobalValue::NotThreadLocal,2109        CGM.getContext().getTargetAddressSpace(AS));2110    CharUnits Align = CGM.getContext().getDeclAlign(VD);2111    GV->setAlignment(Align.getAsAlign());2112    return Address(2113        CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(2114            GV, CGF.Builder.getPtrTy(CGM.getContext().getTargetAddressSpace(2115                    VD->getType().getAddressSpace()))),2116        VarTy, Align);2117  }2118 2119  if (getDataSharingMode() != CGOpenMPRuntimeGPU::DS_Generic)2120    return Address::invalid();2121 2122  VD = VD->getCanonicalDecl();2123  auto I = FunctionGlobalizedDecls.find(CGF.CurFn);2124  if (I == FunctionGlobalizedDecls.end())2125    return Address::invalid();2126  auto VDI = I->getSecond().LocalVarData.find(VD);2127  if (VDI != I->getSecond().LocalVarData.end())2128    return VDI->second.PrivateAddr;2129  if (VD->hasAttrs()) {2130    for (specific_attr_iterator<OMPReferencedVarAttr> IT(VD->attr_begin()),2131         E(VD->attr_end());2132         IT != E; ++IT) {2133      auto VDI = I->getSecond().LocalVarData.find(2134          cast<VarDecl>(cast<DeclRefExpr>(IT->getRef())->getDecl())2135              ->getCanonicalDecl());2136      if (VDI != I->getSecond().LocalVarData.end())2137        return VDI->second.PrivateAddr;2138    }2139  }2140 2141  return Address::invalid();2142}2143 2144void CGOpenMPRuntimeGPU::functionFinished(CodeGenFunction &CGF) {2145  FunctionGlobalizedDecls.erase(CGF.CurFn);2146  CGOpenMPRuntime::functionFinished(CGF);2147}2148 2149void CGOpenMPRuntimeGPU::getDefaultDistScheduleAndChunk(2150    CodeGenFunction &CGF, const OMPLoopDirective &S,2151    OpenMPDistScheduleClauseKind &ScheduleKind,2152    llvm::Value *&Chunk) const {2153  auto &RT = static_cast<CGOpenMPRuntimeGPU &>(CGF.CGM.getOpenMPRuntime());2154  if (getExecutionMode() == CGOpenMPRuntimeGPU::EM_SPMD) {2155    ScheduleKind = OMPC_DIST_SCHEDULE_static;2156    Chunk = CGF.EmitScalarConversion(2157        RT.getGPUNumThreads(CGF),2158        CGF.getContext().getIntTypeForBitwidth(32, /*Signed=*/0),2159        S.getIterationVariable()->getType(), S.getBeginLoc());2160    return;2161  }2162  CGOpenMPRuntime::getDefaultDistScheduleAndChunk(2163      CGF, S, ScheduleKind, Chunk);2164}2165 2166void CGOpenMPRuntimeGPU::getDefaultScheduleAndChunk(2167    CodeGenFunction &CGF, const OMPLoopDirective &S,2168    OpenMPScheduleClauseKind &ScheduleKind,2169    const Expr *&ChunkExpr) const {2170  ScheduleKind = OMPC_SCHEDULE_static;2171  // Chunk size is 1 in this case.2172  llvm::APInt ChunkSize(32, 1);2173  ChunkExpr = IntegerLiteral::Create(CGF.getContext(), ChunkSize,2174      CGF.getContext().getIntTypeForBitwidth(32, /*Signed=*/0),2175      SourceLocation());2176}2177 2178void CGOpenMPRuntimeGPU::adjustTargetSpecificDataForLambdas(2179    CodeGenFunction &CGF, const OMPExecutableDirective &D) const {2180  assert(isOpenMPTargetExecutionDirective(D.getDirectiveKind()) &&2181         " Expected target-based directive.");2182  const CapturedStmt *CS = D.getCapturedStmt(OMPD_target);2183  for (const CapturedStmt::Capture &C : CS->captures()) {2184    // Capture variables captured by reference in lambdas for target-based2185    // directives.2186    if (!C.capturesVariable())2187      continue;2188    const VarDecl *VD = C.getCapturedVar();2189    const auto *RD = VD->getType()2190                         .getCanonicalType()2191                         .getNonReferenceType()2192                         ->getAsCXXRecordDecl();2193    if (!RD || !RD->isLambda())2194      continue;2195    Address VDAddr = CGF.GetAddrOfLocalVar(VD);2196    LValue VDLVal;2197    if (VD->getType().getCanonicalType()->isReferenceType())2198      VDLVal = CGF.EmitLoadOfReferenceLValue(VDAddr, VD->getType());2199    else2200      VDLVal = CGF.MakeAddrLValue(2201          VDAddr, VD->getType().getCanonicalType().getNonReferenceType());2202    llvm::DenseMap<const ValueDecl *, FieldDecl *> Captures;2203    FieldDecl *ThisCapture = nullptr;2204    RD->getCaptureFields(Captures, ThisCapture);2205    if (ThisCapture && CGF.CapturedStmtInfo->isCXXThisExprCaptured()) {2206      LValue ThisLVal =2207          CGF.EmitLValueForFieldInitialization(VDLVal, ThisCapture);2208      llvm::Value *CXXThis = CGF.LoadCXXThis();2209      CGF.EmitStoreOfScalar(CXXThis, ThisLVal);2210    }2211    for (const LambdaCapture &LC : RD->captures()) {2212      if (LC.getCaptureKind() != LCK_ByRef)2213        continue;2214      const ValueDecl *VD = LC.getCapturedVar();2215      // FIXME: For now VD is always a VarDecl because OpenMP does not support2216      //  capturing structured bindings in lambdas yet.2217      if (!CS->capturesVariable(cast<VarDecl>(VD)))2218        continue;2219      auto It = Captures.find(VD);2220      assert(It != Captures.end() && "Found lambda capture without field.");2221      LValue VarLVal = CGF.EmitLValueForFieldInitialization(VDLVal, It->second);2222      Address VDAddr = CGF.GetAddrOfLocalVar(cast<VarDecl>(VD));2223      if (VD->getType().getCanonicalType()->isReferenceType())2224        VDAddr = CGF.EmitLoadOfReferenceLValue(VDAddr,2225                                               VD->getType().getCanonicalType())2226                     .getAddress();2227      CGF.EmitStoreOfScalar(VDAddr.emitRawPointer(CGF), VarLVal);2228    }2229  }2230}2231 2232bool CGOpenMPRuntimeGPU::hasAllocateAttributeForGlobalVar(const VarDecl *VD,2233                                                            LangAS &AS) {2234  if (!VD || !VD->hasAttr<OMPAllocateDeclAttr>())2235    return false;2236  const auto *A = VD->getAttr<OMPAllocateDeclAttr>();2237  switch(A->getAllocatorType()) {2238  case OMPAllocateDeclAttr::OMPNullMemAlloc:2239  case OMPAllocateDeclAttr::OMPDefaultMemAlloc:2240  // Not supported, fallback to the default mem space.2241  case OMPAllocateDeclAttr::OMPLargeCapMemAlloc:2242  case OMPAllocateDeclAttr::OMPCGroupMemAlloc:2243  case OMPAllocateDeclAttr::OMPHighBWMemAlloc:2244  case OMPAllocateDeclAttr::OMPLowLatMemAlloc:2245  case OMPAllocateDeclAttr::OMPThreadMemAlloc:2246    AS = LangAS::Default;2247    return true;2248  case OMPAllocateDeclAttr::OMPConstMemAlloc:2249    AS = LangAS::cuda_constant;2250    return true;2251  case OMPAllocateDeclAttr::OMPPTeamMemAlloc:2252    AS = LangAS::cuda_shared;2253    return true;2254  case OMPAllocateDeclAttr::OMPUserDefinedMemAlloc:2255    llvm_unreachable("Expected predefined allocator for the variables with the "2256                     "static storage.");2257  }2258  return false;2259}2260 2261// Get current OffloadArch and ignore any unknown values2262static OffloadArch getOffloadArch(CodeGenModule &CGM) {2263  if (!CGM.getTarget().hasFeature("ptx"))2264    return OffloadArch::UNKNOWN;2265  for (const auto &Feature : CGM.getTarget().getTargetOpts().FeatureMap) {2266    if (Feature.getValue()) {2267      OffloadArch Arch = StringToOffloadArch(Feature.getKey());2268      if (Arch != OffloadArch::UNKNOWN)2269        return Arch;2270    }2271  }2272  return OffloadArch::UNKNOWN;2273}2274 2275/// Check to see if target architecture supports unified addressing which is2276/// a restriction for OpenMP requires clause "unified_shared_memory".2277void CGOpenMPRuntimeGPU::processRequiresDirective(const OMPRequiresDecl *D) {2278  for (const OMPClause *Clause : D->clauselists()) {2279    if (Clause->getClauseKind() == OMPC_unified_shared_memory) {2280      OffloadArch Arch = getOffloadArch(CGM);2281      switch (Arch) {2282      case OffloadArch::SM_20:2283      case OffloadArch::SM_21:2284      case OffloadArch::SM_30:2285      case OffloadArch::SM_32_:2286      case OffloadArch::SM_35:2287      case OffloadArch::SM_37:2288      case OffloadArch::SM_50:2289      case OffloadArch::SM_52:2290      case OffloadArch::SM_53: {2291        SmallString<256> Buffer;2292        llvm::raw_svector_ostream Out(Buffer);2293        Out << "Target architecture " << OffloadArchToString(Arch)2294            << " does not support unified addressing";2295        CGM.Error(Clause->getBeginLoc(), Out.str());2296        return;2297      }2298      case OffloadArch::SM_60:2299      case OffloadArch::SM_61:2300      case OffloadArch::SM_62:2301      case OffloadArch::SM_70:2302      case OffloadArch::SM_72:2303      case OffloadArch::SM_75:2304      case OffloadArch::SM_80:2305      case OffloadArch::SM_86:2306      case OffloadArch::SM_87:2307      case OffloadArch::SM_89:2308      case OffloadArch::SM_90:2309      case OffloadArch::SM_90a:2310      case OffloadArch::SM_100:2311      case OffloadArch::SM_100a:2312      case OffloadArch::SM_101:2313      case OffloadArch::SM_101a:2314      case OffloadArch::SM_103:2315      case OffloadArch::SM_103a:2316      case OffloadArch::SM_120:2317      case OffloadArch::SM_120a:2318      case OffloadArch::SM_121:2319      case OffloadArch::SM_121a:2320      case OffloadArch::GFX600:2321      case OffloadArch::GFX601:2322      case OffloadArch::GFX602:2323      case OffloadArch::GFX700:2324      case OffloadArch::GFX701:2325      case OffloadArch::GFX702:2326      case OffloadArch::GFX703:2327      case OffloadArch::GFX704:2328      case OffloadArch::GFX705:2329      case OffloadArch::GFX801:2330      case OffloadArch::GFX802:2331      case OffloadArch::GFX803:2332      case OffloadArch::GFX805:2333      case OffloadArch::GFX810:2334      case OffloadArch::GFX9_GENERIC:2335      case OffloadArch::GFX900:2336      case OffloadArch::GFX902:2337      case OffloadArch::GFX904:2338      case OffloadArch::GFX906:2339      case OffloadArch::GFX908:2340      case OffloadArch::GFX909:2341      case OffloadArch::GFX90a:2342      case OffloadArch::GFX90c:2343      case OffloadArch::GFX9_4_GENERIC:2344      case OffloadArch::GFX942:2345      case OffloadArch::GFX950:2346      case OffloadArch::GFX10_1_GENERIC:2347      case OffloadArch::GFX1010:2348      case OffloadArch::GFX1011:2349      case OffloadArch::GFX1012:2350      case OffloadArch::GFX1013:2351      case OffloadArch::GFX10_3_GENERIC:2352      case OffloadArch::GFX1030:2353      case OffloadArch::GFX1031:2354      case OffloadArch::GFX1032:2355      case OffloadArch::GFX1033:2356      case OffloadArch::GFX1034:2357      case OffloadArch::GFX1035:2358      case OffloadArch::GFX1036:2359      case OffloadArch::GFX11_GENERIC:2360      case OffloadArch::GFX1100:2361      case OffloadArch::GFX1101:2362      case OffloadArch::GFX1102:2363      case OffloadArch::GFX1103:2364      case OffloadArch::GFX1150:2365      case OffloadArch::GFX1151:2366      case OffloadArch::GFX1152:2367      case OffloadArch::GFX1153:2368      case OffloadArch::GFX12_GENERIC:2369      case OffloadArch::GFX1200:2370      case OffloadArch::GFX1201:2371      case OffloadArch::GFX1250:2372      case OffloadArch::GFX1251:2373      case OffloadArch::AMDGCNSPIRV:2374      case OffloadArch::Generic:2375      case OffloadArch::GRANITERAPIDS:2376      case OffloadArch::BMG_G21:2377      case OffloadArch::UNUSED:2378      case OffloadArch::UNKNOWN:2379        break;2380      case OffloadArch::LAST:2381        llvm_unreachable("Unexpected GPU arch.");2382      }2383    }2384  }2385  CGOpenMPRuntime::processRequiresDirective(D);2386}2387 2388llvm::Value *CGOpenMPRuntimeGPU::getGPUNumThreads(CodeGenFunction &CGF) {2389  CGBuilderTy &Bld = CGF.Builder;2390  llvm::Module *M = &CGF.CGM.getModule();2391  const char *LocSize = "__kmpc_get_hardware_num_threads_in_block";2392  llvm::Function *F = M->getFunction(LocSize);2393  if (!F) {2394    F = llvm::Function::Create(llvm::FunctionType::get(CGF.Int32Ty, {}, false),2395                               llvm::GlobalVariable::ExternalLinkage, LocSize,2396                               &CGF.CGM.getModule());2397  }2398  return Bld.CreateCall(F, {}, "nvptx_num_threads");2399}2400 2401llvm::Value *CGOpenMPRuntimeGPU::getGPUThreadID(CodeGenFunction &CGF) {2402  ArrayRef<llvm::Value *> Args{};2403  return CGF.EmitRuntimeCall(2404      OMPBuilder.getOrCreateRuntimeFunction(2405          CGM.getModule(), OMPRTL___kmpc_get_hardware_thread_id_in_block),2406      Args);2407}2408