diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -1124,7 +1124,8 @@ } else { DSAInfo &Data = getTopOfStack().SharingMap[D]; assert(Data.Attributes == OMPC_unknown || (A == Data.Attributes) || - (A == OMPC_firstprivate && Data.Attributes == OMPC_lastprivate) || + (A == OMPC_firstprivate && (Data.Attributes == OMPC_lastprivate || + Data.Attributes == OMPC_linear)) || (A == OMPC_lastprivate && Data.Attributes == OMPC_firstprivate) || (isLoopControlVariable(D).first && A == OMPC_private)); if (A == OMPC_lastprivate && Data.Attributes == OMPC_firstprivate) { @@ -3829,6 +3830,9 @@ MarkDeclarationsReferencedInExpr(E); } } + if (auto *LC = dyn_cast(Clause)) + if (Expr *E = LC->getStep()) + MarkDeclarationsReferencedInExpr(E); DSAStack->setForceVarCapturing(/*V=*/false); } else if (CaptureRegions.size() > 1 || CaptureRegions.back() != OMPD_unknown) { @@ -4404,6 +4408,37 @@ } } +namespace { +class LinearStepVarChecker : public StmtVisitor { + llvm::SmallVector ImplicitFirstprivate; + DSAStackTy *Stack; +public: + bool VisitDeclRefExpr(DeclRefExpr *E) { + if (auto *VD = dyn_cast(E->getDecl())) { + DSAStackTy::DSAVarData DVar = + Stack->getTopDSA(E->getDecl(), /*FromParent=*/false); + if (DVar.CKind != OMPC_shared && DVar.CKind != OMPC_private && + DVar.CKind != OMPC_firstprivate && DVar.CKind != OMPC_lastprivate) + ImplicitFirstprivate.push_back(cast(E)); + return true; + } + return false; + } + bool VisitStmt(Stmt *S) { + for (Stmt *Child : S->children()) { + if (Child && Visit(Child)) + return true; + } + return false; + } + ArrayRef getImplicitFirstprivate() const { + return ImplicitFirstprivate; + } + + explicit LinearStepVarChecker(DSAStackTy *S) : Stack(S) {} +}; +} // namespace + StmtResult Sema::ActOnOpenMPExecutableDirective( OpenMPDirectiveKind Kind, const DeclarationNameInfo &DirName, OpenMPDirectiveKind CancelRegion, ArrayRef Clauses, @@ -4460,6 +4495,17 @@ for (Expr *E : IRC->taskgroup_descriptors()) if (E) ImplicitFirstprivates.emplace_back(E); + } else if (auto *LC = dyn_cast(C)) { + Expr *E = LC->getStep(); + if (E) { + LinearStepVarChecker LSVChecker(DSAStack); + LSVChecker.Visit(E); + ArrayRef LinearVars = LSVChecker.getImplicitFirstprivate(); + ImplicitFirstprivates.insert( + ImplicitFirstprivates.end(), + std::make_move_iterator(LinearVars.begin()), + std::make_move_iterator(LinearVars.end())); + } } } if (!ImplicitFirstprivates.empty()) { diff --git a/clang/test/OpenMP/parallel_for_ast_print.cpp b/clang/test/OpenMP/parallel_for_ast_print.cpp --- a/clang/test/OpenMP/parallel_for_ast_print.cpp +++ b/clang/test/OpenMP/parallel_for_ast_print.cpp @@ -100,6 +100,11 @@ // CHECK-NEXT: for (int j = 0; j < 2; ++j) // CHECK-NEXT: for (int j = 0; j < 2; ++j) // CHECK-NEXT: foo(); + + T i; +#pragma omp parallel for linear (i: b + 1) + for (T j = 16; j < 64; j++) + b += 4; return T(); } @@ -146,6 +151,11 @@ // CHECK-NEXT: for (int i = 0; i < 10; ++i) // CHECK-NEXT: for (int j = 0; j < 10; ++j) // CHECK-NEXT: foo(); + + int i; +#pragma omp parallel for linear (i: b + 1) + for (int j = 16; j < 64; j++) + b += 4; return (tmain(argc) + tmain(argv[0][0])); } diff --git a/clang/test/OpenMP/parallel_for_linear_codegen.cpp b/clang/test/OpenMP/parallel_for_linear_codegen.cpp --- a/clang/test/OpenMP/parallel_for_linear_codegen.cpp +++ b/clang/test/OpenMP/parallel_for_linear_codegen.cpp @@ -28,6 +28,8 @@ float f; char cnt; +int a[100]; + // CHECK: [[S_FLOAT_TY:%.+]] = type { float } // CHECK: [[S_INT_TY:%.+]] = type { i32 } // CHECK-DAG: [[F:@.+]] = global float 0.0 @@ -255,3 +257,39 @@ // CHECK: ret void #endif +// RUN: %clang_cc1 -DCK1 -verify -fopenmp -x c++ -triple x86_64-apple-darwin10 -emit-llvm %s -o - | FileCheck %s -check-prefix=CK1 +// RUN: %clang_cc1 -DCK1 -fopenmp -x c++ -std=c++11 -triple x86_64-apple-darwin10 -emit-pch -o %t %s + +// RUN: %clang_cc1 -DCK1 -verify -fopenmp-simd -x c++ -triple x86_64-apple-darwin10 -emit-llvm %s -o - | FileCheck --check-prefix SIMD-ONLY0 %s +// RUN: %clang_cc1 -DCK1 -fopenmp-simd -x c++ -std=c++11 -triple x86_64-apple-darwin10 -emit-pch -o %t %s +// SIMD-ONLY0-NOT: {{__kmpc|__tgt}} +#ifdef CK1 + +// CK1: foo +int foo (int i, int k) +{ +#pragma omp parallel for linear (i: k + 1) + for (int j = 16; j < 64; j++) + { + a[i] = j; + i += 4; + } + return i; +} +// CK1: define internal void [[OUTLINE:@.+]](i32* noalias [[GTID:%.+]], i32* noalias [[BTID:%.+]], i32* dereferenceable(4) [[I_VAR:%.+]], i32* dereferenceable(4) [[K_VAR:%.+]]) +// CK1: [[GTID_ADDR:%.+]] = alloca i32* +// CK1: [[BTID_ADDR:%.+]] = alloca i32* +// CK1: [[I_ADDR:%.+]] = alloca i32* +// CK1: [[K_ADDR:%.+]] = alloca i32* +// CK1: store i32* [[GTID]], i32** [[GTID_ADDR]] +// CK1: store i32* [[BTID]], i32** [[BTID_ADDR]] +// CK1: store i32* [[I_VAR:%.+]], i32** [[I_ADDR]] +// CK1: store i32* [[K_VAR:%.+]], i32** [[K_ADDR]] +// CK1: [[ZERO:%.+]] = load i32*, i32** [[I_ADDR]] +// CK1: [[ONE:%.+]] = load i32*, i32** [[K_ADDR]] +// CK1: [[TWO:%.+]] = load i32, i32* [[ZERO]] +// CK1: store i32 [[TWO]], i32* [[LINEAR_START:%.+]] +// CK1: [[THREE:%.+]] = load i32, i32* [[ONE]] +// CK1: [[ADD:%.+]] = add nsw i32 [[THREE]] +// CK1: store i32 [[ADD]], i32* [[LINEAR_STEP:%.+]] +#endif diff --git a/clang/test/OpenMP/target_simd_ast_print.cpp b/clang/test/OpenMP/target_simd_ast_print.cpp --- a/clang/test/OpenMP/target_simd_ast_print.cpp +++ b/clang/test/OpenMP/target_simd_ast_print.cpp @@ -187,6 +187,10 @@ // CHECK-NEXT: for (T i = 0; i < N; ++i) { // CHECK-NEXT: } +#pragma omp target simd linear (i: b + 1) + // CHECK: #pragma omp target simd linear(i: b + 1) + for (T j = 16; j < 64; j++) + i += 4; return T(); } @@ -307,6 +311,10 @@ // CHECK: #pragma omp target simd is_device_ptr(p) // CHECK-NEXT: for (int i = 0; i < 2; ++i) { // CHECK-NEXT: } +#pragma omp target simd linear (i: b + 1) + // CHECK: #pragma omp target simd linear(i: b + 1) + for (int j = 16; j < 64; j++) + i += 4; return (tmain(argc, &argc)); }