Index: clang/include/clang/AST/StmtOpenMP.h =================================================================== --- clang/include/clang/AST/StmtOpenMP.h +++ clang/include/clang/AST/StmtOpenMP.h @@ -5945,6 +5945,30 @@ unsigned CollapsedNum, ArrayRef Clauses, Stmt *AssociatedStmt, const HelperExprs &Exprs); + struct LoopDirCrParam { + const ASTContext *C; + SourceLocation StartLoc; + SourceLocation EndLoc; + unsigned CollapsedNum; + ArrayRef Clauses; + Stmt *AssociatedStmt; + const HelperExprs Exprs; + + LoopDirCrParam(const ASTContext &C, SourceLocation StartLoc, + SourceLocation EndLoc, unsigned CollapsedNum, + ArrayRef Clauses, Stmt *AssociatedStmt, + const HelperExprs &Exprs) + : C(&C), StartLoc(StartLoc), EndLoc(EndLoc), CollapsedNum(CollapsedNum), + Clauses(Clauses), AssociatedStmt(AssociatedStmt), Exprs(Exprs) {} + }; + + void LoopParamInit(const ASTContext &C, SourceLocation StartLoc, + SourceLocation EndLoc, unsigned CollapsedNum, + ArrayRef Clauses, Stmt *AssociatedStmt, + const HelperExprs &Exprs); + + struct LoopDirCrParam *LoopDirCrParmV; + /// Creates an empty directive with a place for \a NumClauses clauses. /// /// \param C AST context. Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -9786,6 +9786,11 @@ def warn_loop_ctrl_binds_to_inner : Warning< "'%0' is bound to current loop, GCC binds it to the enclosing loop">, InGroup; +def err_omp_bind_required_on_loop : Error< + "expected 'bind' clause for loop construct without an enclosing OpenMP " + "construct">; +def error_loop_reduction_clause : Error< + "reduction clause not handled with '#pragma omp loop bind(teams)'">; def warn_break_binds_to_switch : Warning< "'break' is bound to loop, GCC binds it to switch">, InGroup; Index: clang/include/clang/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -1102,6 +1102,8 @@ void checkCompoundToken(SourceLocation FirstTokLoc, tok::TokenKind FirstTokKind, CompoundToken Op); + void SetExecutableFlags(unsigned ExecFlags); + public: //===--------------------------------------------------------------------===// // Scope manipulation @@ -1134,6 +1136,10 @@ } } + void SetExecutableFlags(unsigned ExecFlags) { + Self->SetExecutableFlags(ExecFlags); + } + // Exit - Exit the scope associated with this object now, rather // than waiting until the object is destroyed. void Exit() { Index: clang/lib/AST/StmtOpenMP.cpp =================================================================== --- clang/lib/AST/StmtOpenMP.cpp +++ clang/lib/AST/StmtOpenMP.cpp @@ -2340,6 +2340,10 @@ Dir->setDependentInits(Exprs.DependentInits); Dir->setFinalsConditions(Exprs.FinalsConditions); Dir->setPreInits(Exprs.PreInits); + + Dir->LoopParamInit(C, StartLoc, EndLoc, CollapsedNum, Clauses, AssociatedStmt, + Exprs); + return Dir; } @@ -2351,6 +2355,14 @@ numLoopChildren(CollapsedNum, OMPD_loop), CollapsedNum); } +void OMPGenericLoopDirective::LoopParamInit( + const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, + unsigned CollapsedNum, ArrayRef Clauses, Stmt *AssociatedStmt, + const HelperExprs &Exprs) { + this->LoopDirCrParmV = new LoopDirCrParam(C, StartLoc, EndLoc, CollapsedNum, + Clauses, AssociatedStmt, Exprs); +} + OMPTeamsGenericLoopDirective *OMPTeamsGenericLoopDirective::Create( const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc, unsigned CollapsedNum, ArrayRef Clauses, Stmt *AssociatedStmt, Index: clang/lib/Sema/SemaOpenMP.cpp =================================================================== --- clang/lib/Sema/SemaOpenMP.cpp +++ clang/lib/Sema/SemaOpenMP.cpp @@ -635,6 +635,11 @@ const SharingMapTy *Top = getTopOfStackOrNull(); return Top ? Top->Directive : OMPD_unknown; } + void setCurrentDirective(OpenMPDirectiveKind newDK) { + SharingMapTy *Top = (SharingMapTy *)getTopOfStackOrNull(); + assert(Top != NULL); + Top->Directive = newDK; + } /// Returns directive kind at specified level. OpenMPDirectiveKind getDirective(unsigned Level) const { assert(!isStackEmpty() && "No directive at specified level."); @@ -6116,6 +6121,7 @@ if (const OMPBindClause *BC = OMPExecutableDirective::getSingleClause(Clauses)) BindKind = BC->getBindKind(); + // First check CancelRegion which is then used in checkNestingOfRegions. if (checkCancelRegion(*this, Kind, CancelRegion, StartLoc) || checkNestingOfRegions(*this, DSAStack, Kind, DirName, CancelRegion, @@ -6127,10 +6133,75 @@ isOpenMPTargetDataManagementDirective(Kind))) Diag(StartLoc, diag::warn_hip_omp_target_directives); + llvm::SmallVector ClausesWithoutBind; + bool UseClausesWithoutBind = false; + + // Restricting to "#pragma omp loop bind" + if (Kind == OMPD_loop) { + if (BindKind == OMPC_BIND_unknown) { + // Setting the enclosing teams or parallel construct for the loop + // directive without bind clause. + BindKind = OMPC_BIND_thread; // Default bind(thread) if binding is unknown + + const OpenMPDirectiveKind parentDirective = + DSAStack->getParentDirective(); + if (parentDirective == OMPD_unknown) { + Diag(DSAStack->getDefaultDSALocation(), + diag::err_omp_bind_required_on_loop); + } else if (parentDirective == OMPD_parallel || + parentDirective == OMPD_target_parallel) { + BindKind = OMPC_BIND_parallel; + } else if (parentDirective == OMPD_teams || + parentDirective == OMPD_target_teams) { + BindKind = OMPC_BIND_teams; + } + } else { + // bind clause is present, so we should set flag indicating to only + // use the clauses that aren't the bind clause for the new directive that + // loop is lowered to. + UseClausesWithoutBind = true; + } + + for (OMPClause *C : Clauses) { + // Spec restriction : bind(teams) and reduction not permitted. + if ((BindKind == OMPC_BIND_teams) && + (C->getClauseKind() == llvm::omp::Clause::OMPC_reduction)) + Diag(SourceLocation(), diag::error_loop_reduction_clause); + + // A new Vector ClausesWithoutBind, which does not contain the bind + // clause, for passing to new directive. + if (C->getClauseKind() != llvm::omp::Clause::OMPC_bind) { + ClausesWithoutBind.push_back(C); + } + } + + switch (BindKind) { + case OMPC_BIND_parallel: + Kind = OMPD_for; + DSAStack->setCurrentDirective(OMPD_for); + break; + case OMPC_BIND_teams: + Kind = OMPD_distribute; + DSAStack->setCurrentDirective(OMPD_distribute); + break; + case OMPC_BIND_thread: + Kind = OMPD_simd; + DSAStack->setCurrentDirective(OMPD_simd); + break; + case OMPC_BIND_unknown: + break; + } + } + llvm::SmallVector ClausesWithImplicit; VarsWithInheritedDSAType VarsWithInheritedDSA; bool ErrorFound = false; - ClausesWithImplicit.append(Clauses.begin(), Clauses.end()); + if (UseClausesWithoutBind) { + ClausesWithImplicit.append(ClausesWithoutBind.begin(), + ClausesWithoutBind.end()); + } else { + ClausesWithImplicit.append(Clauses.begin(), Clauses.end()); + } if (AStmt && !CurContext->isDependentContext() && Kind != OMPD_atomic && Kind != OMPD_critical && Kind != OMPD_section && Kind != OMPD_master && Kind != OMPD_masked && !isOpenMPLoopTransformationDirective(Kind)) { Index: clang/test/OpenMP/loop_bind_codegen.cpp =================================================================== --- /dev/null +++ clang/test/OpenMP/loop_bind_codegen.cpp @@ -0,0 +1,132 @@ +// Copyright 2020 Hewlett Packard Enterprise Development LP + +// expected-no-diagnostics +#ifndef HEADER +#define HEADER +// RUN: %clang_cc1 -DCK1 -verify -fopenmp -x c++ %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple x86_64-unknown-unknown -emit-llvm %s -o - | FileCheck %s + + +/* +#include +#include +#include +#include +*/ + +#define NNN 50 +int aaa[NNN]; + +void parallel_loop() { + #pragma omp parallel + { + #pragma omp loop bind(parallel) + for (int j = 0 ; j < NNN ; j++) { + aaa[j] = j*NNN; + } + } +} + + +void teams_loop() { + #pragma omp teams + { + #pragma omp loop bind(teams) + for (int j = 0 ; j < NNN ; j++) { + aaa[j] = j*NNN; + } + } +} + +void thread_loop() { + #pragma omp parallel + { + #pragma omp loop bind(thread) + for (int j = 0 ; j < NNN ; j++) { + aaa[j] = j*NNN; + } + } +} + +void thread_loop2() { + #pragma omp loop bind(thread) + for (int j = 0 ; j < NNN ; j++) { + aaa[j] = j*NNN; + } +} + +int main() { + parallel_loop(); + teams_loop(); + thread_loop(); + thread_loop2(); + + return 0; +} +#endif +// CHECK-LABEL: define {{[^@]+}}@_Z13parallel_loopv +// CHECK-NEXT: entry: +// CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @3, i32 0, ptr @.omp_outlined.) +// CHECK-NEXT: ret void +// CHECK-LABEL: define {{[^@]+}}@.omp_outlined +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DOTGLOBAL_TID__ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTBOUND_TID__ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTOMP_IV:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[TMP:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTOMP_LB:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTOMP_UB:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTOMP_STRIDE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTOMP_IS_LAST:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[J:%.*]] = alloca i32, align 4 +// CHECK: call void @__kmpc_for_static_init_4(ptr @1, i32 %1, i32 34, ptr [[DOTOMP_IS_LAST]], ptr [[DOTOMP_LB]], ptr [[DOTOMP_UB]], ptr [[DOTOMP_STRIDE]], i32 1, i32 1) +// CHECK-LABEL: cond.true: +// CHECK-NEXT: br label [[COND_END:%.*]] +// CHECK-LABEL: cond.false: +// CHECK-NEXT: [[TMP11:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4 +// CHECK-NEXT: br label [[COND_END]] +// CHECK: omp.inner.for.cond: +// CHECK-NEXT: [[TMP13:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4 +// CHECK: omp.inner.for.body: +// CHECK: omp.loop.exit: +// CHECK-NEXT: call void @__kmpc_for_static_fini(ptr @1, i32 %1) +// CHECK-NEXT: call void @__kmpc_barrier(ptr @2, i32 %1) +// CHECK-NEXT: ret void +// +// +// CHECK-LABEL: define {{[^@]+}}@_Z10teams_loopv +// CHECK-NEXT: entry: +// CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_teams(ptr @3, i32 0, ptr @.omp_outlined..1) +// CHECK-NEXT: ret void +// +// CHECK-LABEL: define {{[^@]+}}@.omp_outlined +// CHECK-NEXT: entry: +// CHECK-NEXT: [[DOTGLOBAL_TID__ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTBOUND_TID__ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[DOTOMP_IV:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[TMP:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTOMP_LB:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTOMP_UB:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTOMP_STRIDE:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[DOTOMP_IS_LAST:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[J:%.*]] = alloca i32, align 4 +// CHECK: call void @__kmpc_for_static_init_4(ptr @4, i32 %1, i32 92, ptr [[DOTOMP_IS_LAST]], ptr [[DOTOMP_LB]], ptr [[DOTOMP_UB]], ptr [[DOTOMP_STRIDE]], i32 1, i32 1) +// CHECK-LABEL: cond.true: +// CHECK-NEXT: br label [[COND_END:%.*]] +// CHECK-LABEL: cond.false: +// CHECK-NEXT: [[TMP11:%.*]] = load i32, ptr [[DOTOMP_UB]], align 4 +// CHECK-NEXT: br label [[COND_END]] +// CHECK: omp.inner.for.cond: +// CHECK-NEXT: [[TMP13:%.*]] = load i32, ptr [[DOTOMP_IV]], align 4 +// CHECK: omp.inner.for.body: +// CHECK: omp.loop.exit: +// CHECK-NEXT: call void @__kmpc_for_static_fini(ptr @4, i32 %1) +// CHECK-NEXT: ret void +// +// CHECK-LABEL: define {{[^@]+}}@_Z11thread_loopv() +// CHECK-NEXT: entry: +// CHECK-NEXT: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @3, i32 0, ptr @.omp_outlined.) +// CHECK-LABEL: define {{[^@]+}}@.omp_outlined +// CHECK: omp.inner.for.cond: +// +// CHECK-LABEL: @main{{.*}}