Index: cfe/trunk/lib/CodeGen/CGOpenMPRuntime.h =================================================================== --- cfe/trunk/lib/CodeGen/CGOpenMPRuntime.h +++ cfe/trunk/lib/CodeGen/CGOpenMPRuntime.h @@ -91,6 +91,10 @@ // Call to kmp_int32 __kmpc_omp_taskyield(ident_t *, kmp_int32 global_tid, // int end_part); OMPRTL__kmpc_omp_taskyield, + // Call to kmp_int32 __kmpc_single(ident_t *, kmp_int32 global_tid); + OMPRTL__kmpc_single, + // Call to void __kmpc_end_single(ident_t *, kmp_int32 global_tid); + OMPRTL__kmpc_end_single, }; /// \brief Values for bit flags used in the ident_t to describe the fields. @@ -312,6 +316,13 @@ /// \brief Emits code for a taskyield directive. virtual void EmitOMPTaskyieldCall(CodeGenFunction &CGF, SourceLocation Loc); + /// \brief Emits a single region. + /// \param SingleOpGen Generator for the statement associated with the given + /// single region. + virtual void EmitOMPSingleRegion(CodeGenFunction &CGF, + const std::function &SingleOpGen, + SourceLocation Loc); + /// \brief Emits explicit barrier for OpenMP threads. /// \param IsExplicit true, if it is explicitly specified barrier. /// Index: cfe/trunk/lib/CodeGen/CGOpenMPRuntime.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGOpenMPRuntime.cpp +++ cfe/trunk/lib/CodeGen/CGOpenMPRuntime.cpp @@ -484,6 +484,22 @@ RTLFn = CGM.CreateRuntimeFunction(FnTy, /*Name=*/"__kmpc_omp_taskyield"); break; } + case OMPRTL__kmpc_single: { + // Build kmp_int32 __kmpc_single(ident_t *loc, kmp_int32 global_tid); + llvm::Type *TypeParams[] = {getIdentTyPointerTy(), CGM.Int32Ty}; + llvm::FunctionType *FnTy = + llvm::FunctionType::get(CGM.Int32Ty, TypeParams, /*isVarArg=*/false); + RTLFn = CGM.CreateRuntimeFunction(FnTy, /*Name=*/"__kmpc_single"); + break; + } + case OMPRTL__kmpc_end_single: { + // Build void __kmpc_end_single(ident_t *loc, kmp_int32 global_tid); + llvm::Type *TypeParams[] = {getIdentTyPointerTy(), CGM.Int32Ty}; + llvm::FunctionType *FnTy = + llvm::FunctionType::get(CGM.VoidTy, TypeParams, /*isVarArg=*/false); + RTLFn = CGM.CreateRuntimeFunction(FnTy, /*Name=*/"__kmpc_end_single"); + break; + } } return RTLFn; } @@ -805,6 +821,39 @@ CGF.EmitRuntimeCall(RTLFn, Args); } +void CGOpenMPRuntime::EmitOMPSingleRegion( + CodeGenFunction &CGF, const std::function &SingleOpGen, + SourceLocation Loc) { + // if(__kmpc_single(ident_t *, gtid)) { + // SingleOpGen(); + // __kmpc_end_single(ident_t *, gtid); + // } + // Prepare arguments and build a call to __kmpc_single + llvm::Value *Args[] = {EmitOpenMPUpdateLocation(CGF, Loc), + GetOpenMPThreadID(CGF, Loc)}; + auto RTLFn = CreateRuntimeFunction(OMPRTL__kmpc_single); + auto *IsSingle = CGF.EmitRuntimeCall(RTLFn, Args); + EmitOMPIfStmt(CGF, IsSingle, [&]() -> void { + SingleOpGen(); + // Build a call to __kmpc_end_single. + // OpenMP [1.2.2 OpenMP Language Terminology] + // For C/C++, an executable statement, possibly compound, with a single + // entry at the top and a single exit at the bottom, or an OpenMP construct. + // * Access to the structured block must not be the result of a branch. + // * The point of exit cannot be a branch out of the structured block. + // * The point of entry must not be a call to setjmp(). + // * longjmp() and throw() must not violate the entry/exit criteria. + // * An expression statement, iteration statement, selection statement, or + // try block is considered to be a structured block if the corresponding + // compound statement obtained by enclosing it in { and } would be a + // structured block. + // It is analyzed in Sema, so we can just call __kmpc_end_single() on + // fallthrough rather than pushing a normal cleanup for it. + RTLFn = CreateRuntimeFunction(OMPRTL__kmpc_end_single); + CGF.EmitRuntimeCall(RTLFn, Args); + }); +} + void CGOpenMPRuntime::EmitOMPBarrierCall(CodeGenFunction &CGF, SourceLocation Loc, bool IsExplicit) { // Build call __kmpc_cancel_barrier(loc, thread_id); Index: cfe/trunk/lib/CodeGen/CGStmtOpenMP.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGStmtOpenMP.cpp +++ cfe/trunk/lib/CodeGen/CGStmtOpenMP.cpp @@ -707,8 +707,13 @@ llvm_unreachable("CodeGen for 'omp section' is not supported yet."); } -void CodeGenFunction::EmitOMPSingleDirective(const OMPSingleDirective &) { - llvm_unreachable("CodeGen for 'omp single' is not supported yet."); +void CodeGenFunction::EmitOMPSingleDirective(const OMPSingleDirective &S) { + CGM.getOpenMPRuntime().EmitOMPSingleRegion(*this, [&]() -> void { + InlinedOpenMPRegion Region(*this, S.getAssociatedStmt()); + RunCleanupsScope Scope(*this); + EmitStmt(cast(S.getAssociatedStmt())->getCapturedStmt()); + EnsureInsertPoint(); + }, S.getLocStart()); } void CodeGenFunction::EmitOMPMasterDirective(const OMPMasterDirective &S) { Index: cfe/trunk/test/OpenMP/single_codegen.cpp =================================================================== --- cfe/trunk/test/OpenMP/single_codegen.cpp +++ cfe/trunk/test/OpenMP/single_codegen.cpp @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -verify -fopenmp=libiomp5 -x c++ -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck %s +// RUN: %clang_cc1 -fopenmp=libiomp5 -x c++ -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp=libiomp5 -x c++ -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +// CHECK: [[IDENT_T_TY:%.+]] = type { i32, i32, i32, i32, i8* } + +// CHECK: define void [[FOO:@.+]]() + +void foo() {} + +// CHECK-LABEL: @main +int main() { + // CHECK: [[A_ADDR:%.+]] = alloca i8 + char a; + +// CHECK: [[GTID:%.+]] = call i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:@.+]]) +// CHECK: [[RES:%.+]] = call i32 @__kmpc_single([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]]) +// CHECK-NEXT: [[IS_SINGLE:%.+]] = icmp ne i32 [[RES]], 0 +// CHECK-NEXT: br i1 [[IS_SINGLE]], label {{%?}}[[THEN:.+]], label {{%?}}[[EXIT:.+]] +// CHECK: [[THEN]] +// CHECK-NEXT: store i8 2, i8* [[A_ADDR]] +// CHECK-NEXT: call void @__kmpc_end_single([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]]) +// CHECK-NEXT: br label {{%?}}[[EXIT]] +// CHECK: [[EXIT]] +#pragma omp single + a = 2; +// CHECK: [[RES:%.+]] = call i32 @__kmpc_single([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]]) +// CHECK-NEXT: [[IS_SINGLE:%.+]] = icmp ne i32 [[RES]], 0 +// CHECK-NEXT: br i1 [[IS_SINGLE]], label {{%?}}[[THEN:.+]], label {{%?}}[[EXIT:.+]] +// CHECK: [[THEN]] +// CHECK-NEXT: call void [[FOO]]() +// CHECK-NEXT: call void @__kmpc_end_single([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]]) +// CHECK-NEXT: br label {{%?}}[[EXIT]] +// CHECK: [[EXIT]] +#pragma omp single + foo(); +// CHECK-NOT: call i32 @__kmpc_single +// CHECK-NOT: call void @__kmpc_end_single + return a; +} + +#endif