diff --git a/clang/lib/CodeGen/CGStmtOpenMP.cpp b/clang/lib/CodeGen/CGStmtOpenMP.cpp --- a/clang/lib/CodeGen/CGStmtOpenMP.cpp +++ b/clang/lib/CodeGen/CGStmtOpenMP.cpp @@ -1319,7 +1319,6 @@ llvm::SmallVectorImpl &) {} void CodeGenFunction::EmitOMPParallelDirective(const OMPParallelDirective &S) { - if (llvm::OpenMPIRBuilder *OMPBuilder = CGM.getOpenMPIRBuilder()) { // Check if we have any if clause associated with the directive. llvm::Value *IfCond = nullptr; @@ -2991,11 +2990,147 @@ } void CodeGenFunction::EmitOMPMasterDirective(const OMPMasterDirective &S) { + if (llvm::OpenMPIRBuilder *OMPBuilder = CGM.getOpenMPIRBuilder()) { + using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy; + + const CapturedStmt *CS = S.getInnermostCapturedStmt(); + const Stmt *MasterRegionBodyStmt = CS->getCapturedStmt(); + + // TODO: Replace with a generic helper function for finalization + auto FiniCB = [this](InsertPointTy IP) { + CGBuilderTy::InsertPointGuard IPG(Builder); + assert(IP.getBlock()->end() != IP.getPoint() && + "OpenMP IR Builder should cause terminated block!"); + + llvm::BasicBlock *IPBB = IP.getBlock(); + llvm::BasicBlock *DestBB = IPBB->getUniqueSuccessor(); + assert(DestBB && "Finalization block should have one successor!"); + + // erase and replace with cleanup branch. + IPBB->getTerminator()->eraseFromParent(); + Builder.SetInsertPoint(IPBB); + CodeGenFunction::JumpDest Dest = getJumpDestInCurrentScope(DestBB); + EmitBranchThroughCleanup(Dest); + }; + + // TODO: Replace with a generic helper function for emitting body + auto BodyGenCB = [MasterRegionBodyStmt, this](InsertPointTy AllocaIP, + InsertPointTy CodeGenIP, + llvm::BasicBlock &FiniBB) { + // Alloca insertion block should be in the entry block of the containing + // function So it expects an empty AllocaIP in which case will reuse the + // old alloca insertion point, or a new AllocaIP in the same block as the + // old one + assert((!AllocaIP.isSet() || + AllocaInsertPt->getParent() == AllocaIP.getBlock()) && + "Insertion point should be in the entry block of containing " + "function!"); + auto OldAllocaIP = AllocaInsertPt; + if (AllocaIP.isSet()) + AllocaInsertPt = &*AllocaIP.getPoint(); + auto OldReturnBlock = ReturnBlock; + ReturnBlock = getJumpDestInCurrentScope(&FiniBB); + + llvm::BasicBlock *CodeGenIPBB = CodeGenIP.getBlock(); + if (llvm::Instruction *CodeGenIPBBTI = CodeGenIPBB->getTerminator()) + CodeGenIPBBTI->eraseFromParent(); + + Builder.SetInsertPoint(CodeGenIPBB); + + EmitStmt(MasterRegionBodyStmt); + + if (Builder.saveIP().isSet()) + Builder.CreateBr(&FiniBB); + + AllocaInsertPt = OldAllocaIP; + ReturnBlock = OldReturnBlock; + }; + CGCapturedStmtInfo CGSI(*CS, CR_OpenMP); + CodeGenFunction::CGCapturedStmtRAII CapInfoRAII(*this, &CGSI); + Builder.restoreIP(OMPBuilder->CreateMaster(Builder, BodyGenCB, FiniCB)); + + return; + } OMPLexicalScope Scope(*this, S, OMPD_unknown); emitMaster(*this, S); } void CodeGenFunction::EmitOMPCriticalDirective(const OMPCriticalDirective &S) { + if (llvm::OpenMPIRBuilder *OMPBuilder = CGM.getOpenMPIRBuilder()) { + using InsertPointTy = llvm::OpenMPIRBuilder::InsertPointTy; + + const CapturedStmt *CS = S.getInnermostCapturedStmt(); + const Stmt *CriticalRegionBodyStmt = CS->getCapturedStmt(); + const Expr *Hint = nullptr; + if (const auto *HintClause = S.getSingleClause()) + Hint = HintClause->getHint(); + + // TODO: This is slightly different from what's currently being done in + // clang. Fix the Int32Ty to IntPtrTy (pointer width size) when everything + // about typing is final. + llvm::Value *HintInst = nullptr; + if (Hint) + HintInst = + Builder.CreateIntCast(EmitScalarExpr(Hint), CGM.Int32Ty, false); + + // TODO: Replace with a generic helper function for finalization + auto FiniCB = [this](InsertPointTy IP) { + CGBuilderTy::InsertPointGuard IPG(Builder); + assert(IP.getBlock()->end() != IP.getPoint() && + "OpenMP IR Builder should cause terminated block!"); + llvm::BasicBlock *IPBB = IP.getBlock(); + llvm::BasicBlock *DestBB = IPBB->getUniqueSuccessor(); + assert(DestBB && "Finalization block should have one successor!"); + + // erase and replace with cleanup branch. + IPBB->getTerminator()->eraseFromParent(); + Builder.SetInsertPoint(IPBB); + CodeGenFunction::JumpDest Dest = getJumpDestInCurrentScope(DestBB); + EmitBranchThroughCleanup(Dest); + }; + + // TODO: Replace with a generic helper function for emitting body + auto BodyGenCB = [CriticalRegionBodyStmt, this](InsertPointTy AllocaIP, + InsertPointTy CodeGenIP, + llvm::BasicBlock &FiniBB) { + // Alloca insertion block should be in the entry block of the containing + // function So it expects an empty AllocaIP in which case will reuse the + // old alloca insertion point, or a new AllocaIP in the same block as the + // old one + assert((!AllocaIP.isSet() || + AllocaInsertPt->getParent() == AllocaIP.getBlock()) && + "Insertion point should be in the entry block of containing " + "function!"); + auto OldAllocaIP = AllocaInsertPt; + if (AllocaIP.isSet()) + AllocaInsertPt = &*AllocaIP.getPoint(); + auto OldReturnBlock = ReturnBlock; + ReturnBlock = getJumpDestInCurrentScope(&FiniBB); + + llvm::BasicBlock *CodeGenIPBB = CodeGenIP.getBlock(); + if (llvm::Instruction *CodeGenIPBBTI = CodeGenIPBB->getTerminator()) + CodeGenIPBBTI->eraseFromParent(); + + Builder.SetInsertPoint(CodeGenIPBB); + + EmitStmt(CriticalRegionBodyStmt); + + if (Builder.saveIP().isSet()) + Builder.CreateBr(&FiniBB); + + AllocaInsertPt = OldAllocaIP; + ReturnBlock = OldReturnBlock; + }; + + CGCapturedStmtInfo CGSI(*CS, CR_OpenMP); + CodeGenFunction::CGCapturedStmtRAII CapInfoRAII(*this, &CGSI); + Builder.restoreIP(OMPBuilder->CreateCritical( + Builder, BodyGenCB, FiniCB, S.getDirectiveName().getAsString(), + HintInst)); + + return; + } + auto &&CodeGen = [&S](CodeGenFunction &CGF, PrePostActionTy &Action) { Action.Enter(CGF); CGF.EmitStmt(S.getInnermostCapturedStmt()->getCapturedStmt()); diff --git a/clang/test/OpenMP/critical_codegen.cpp b/clang/test/OpenMP/critical_codegen.cpp --- a/clang/test/OpenMP/critical_codegen.cpp +++ b/clang/test/OpenMP/critical_codegen.cpp @@ -1,7 +1,10 @@ -// RUN: %clang_cc1 -verify -fopenmp -x c++ -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck %s +// RUN: %clang_cc1 -verify -fopenmp -x c++ -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck %s --check-prefixes=ALL,NORMAL // RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -o %t %s -// RUN: %clang_cc1 -fopenmp -x c++ -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -fopenmp -x c++ -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s --check-prefixes=ALL,NORMAL // RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -fopenmp -fexceptions -fcxx-exceptions -debug-info-kind=line-tables-only -x c++ -emit-llvm %s -o - | FileCheck %s --check-prefix=TERM_DEBUG +// RUN: %clang_cc1 -verify -fopenmp -fopenmp-enable-irbuilder -x c++ -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck %s --check-prefixes=ALL,IRBUILDER +// RUN: %clang_cc1 -fopenmp -fopenmp-enable-irbuilder -x c++ -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp -fopenmp-enable-irbuilder -x c++ -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s --check-prefixes=ALL,IRBUILDER // RUN: %clang_cc1 -verify -fopenmp-simd -x c++ -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck --check-prefix SIMD-ONLY0 %s // RUN: %clang_cc1 -fopenmp-simd -x c++ -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -o %t %s @@ -12,74 +15,79 @@ #ifndef HEADER #define HEADER -// CHECK: [[IDENT_T_TY:%.+]] = type { i32, i32, i32, i32, i8* } -// CHECK: [[UNNAMED_LOCK:@.+]] = common global [8 x i32] zeroinitializer -// CHECK: [[THE_NAME_LOCK:@.+]] = common global [8 x i32] zeroinitializer -// CHECK: [[THE_NAME_LOCK1:@.+]] = common global [8 x i32] zeroinitializer +// ALL: [[IDENT_T_TY:%.+]] = type { i32, i32, i32, i32, i8* } +// ALL: [[UNNAMED_LOCK:@.+]] = common global [8 x i32] zeroinitializer +// ALL: [[THE_NAME_LOCK:@.+]] = common global [8 x i32] zeroinitializer +// ALL: [[THE_NAME_LOCK1:@.+]] = common global [8 x i32] zeroinitializer -// CHECK: define {{.*}}void [[FOO:@.+]]() +// ALL: define {{.*}}void [[FOO:@.+]]() void foo() {} -// CHECK-LABEL: @main +// ALL-LABEL: @main // TERM_DEBUG-LABEL: @main int main() { -// CHECK: [[A_ADDR:%.+]] = alloca i8 + // ALL: [[A_ADDR:%.+]] = alloca i8 char a; -// CHECK: [[GTID:%.+]] = call {{.*}}i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:@.+]]) -// CHECK: call {{.*}}void @__kmpc_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[UNNAMED_LOCK]]) -// CHECK-NEXT: store i8 2, i8* [[A_ADDR]] -// CHECK-NEXT: call {{.*}}void @__kmpc_end_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[UNNAMED_LOCK]]) +// ALL: [[GTID:%.+]] = call {{.*}}i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:@.+]]) +// ALL: call {{.*}}void @__kmpc_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[UNNAMED_LOCK]]) +// ALL-NEXT: store i8 2, i8* [[A_ADDR]] +// ALL-NEXT: call {{.*}}void @__kmpc_end_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[UNNAMED_LOCK]]) #pragma omp critical a = 2; -// CHECK: call {{.*}}void @__kmpc_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[THE_NAME_LOCK]]) -// CHECK-NEXT: invoke {{.*}}void [[FOO]]() -// CHECK: call {{.*}}void @__kmpc_end_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[THE_NAME_LOCK]]) +// IRBUILDER: [[GTID:%.+]] = call {{.*}}i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:@.+]]) +// ALL: call {{.*}}void @__kmpc_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[THE_NAME_LOCK]]) +// IRBUILDER-NEXT: call {{.*}}void [[FOO]]() +// NORMAL-NEXT: invoke {{.*}}void [[FOO]]() +// ALL: call {{.*}}void @__kmpc_end_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[THE_NAME_LOCK]]) #pragma omp critical(the_name) foo(); -// CHECK: call {{.*}}void @__kmpc_critical_with_hint([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[THE_NAME_LOCK1]], i{{64|32}} 23) -// CHECK-NEXT: invoke {{.*}}void [[FOO]]() -// CHECK: call {{.*}}void @__kmpc_end_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[THE_NAME_LOCK1]]) +// IRBUILDER: [[GTID:%.+]] = call {{.*}}i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:@.+]]) +// ALL: call {{.*}}void @__kmpc_critical_with_hint([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[THE_NAME_LOCK1]], i{{64|32}} 23) +// IRBUILDER-NEXT: call {{.*}}void [[FOO]]() +// NORMAL-NEXT: invoke {{.*}}void [[FOO]]() +// ALL: call {{.*}}void @__kmpc_end_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[THE_NAME_LOCK1]]) #pragma omp critical(the_name1) hint(23) foo(); -// CHECK: call {{.*}}void @__kmpc_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[THE_NAME_LOCK]]) -// CHECK: br label -// CHECK-NOT: call {{.*}}void @__kmpc_end_critical( -// CHECK: br label -// CHECK-NOT: call {{.*}}void @__kmpc_end_critical( -// CHECK: br label + // IRBUILDER: [[GTID:%.+]] = call {{.*}}i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:@.+]]) + // ALL: call {{.*}}void @__kmpc_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[THE_NAME_LOCK]]) + // ALL: br label + // ALL-NOT: call {{.*}}void @__kmpc_end_critical( + // ALL: br label + // ALL-NOT: call {{.*}}void @__kmpc_end_critical( + // NORMAL: br label if (a) #pragma omp critical(the_name) while (1) ; -// CHECK: call {{.*}}void [[FOO]]() + // ALL: call {{.*}}void [[FOO]]() foo(); -// CHECK-NOT: call void @__kmpc_critical -// CHECK-NOT: call void @__kmpc_end_critical + // ALL-NOT: call void @__kmpc_critical + // ALL-NOT: call void @__kmpc_end_critical return a; } struct S { int a; }; -// CHECK-LABEL: critical_ref +// ALL-LABEL: critical_ref void critical_ref(S &s) { - // CHECK: [[S_ADDR:%.+]] = alloca %struct.S*, - // CHECK: [[S_REF:%.+]] = load %struct.S*, %struct.S** [[S_ADDR]], - // CHECK: [[S_A_REF:%.+]] = getelementptr inbounds %struct.S, %struct.S* [[S_REF]], i32 0, i32 0 + // ALL: [[S_ADDR:%.+]] = alloca %struct.S*, + // ALL: [[S_REF:%.+]] = load %struct.S*, %struct.S** [[S_ADDR]], + // ALL: [[S_A_REF:%.+]] = getelementptr inbounds %struct.S, %struct.S* [[S_REF]], i32 0, i32 0 ++s.a; - // CHECK: [[S_REF:%.+]] = load %struct.S*, %struct.S** [[S_ADDR]], - // CHECK: store %struct.S* [[S_REF]], %struct.S** [[S_ADDR:%.+]], - // CHECK: call void @__kmpc_critical( + // NORMAL: [[S_REF:%.+]] = load %struct.S*, %struct.S** [[S_ADDR]], + // NORMAL: store %struct.S* [[S_REF]], %struct.S** [[S_ADDR:%.+]], + // ALL: call void @__kmpc_critical( #pragma omp critical - // CHECK: [[S_REF:%.+]] = load %struct.S*, %struct.S** [[S_ADDR]], - // CHECK: [[S_A_REF:%.+]] = getelementptr inbounds %struct.S, %struct.S* [[S_REF]], i32 0, i32 0 + // ALL: [[S_REF:%.+]] = load %struct.S*, %struct.S** [[S_ADDR]], + // ALL: [[S_A_REF:%.+]] = getelementptr inbounds %struct.S, %struct.S* [[S_REF]], i32 0, i32 0 ++s.a; - // CHECK: call void @__kmpc_end_critical( + // ALL: call void @__kmpc_end_critical( } -// CHECK-LABEL: parallel_critical +// ALL-LABEL: parallel_critical // TERM_DEBUG-LABEL: parallel_critical void parallel_critical() { #pragma omp parallel diff --git a/clang/test/OpenMP/master_codegen.cpp b/clang/test/OpenMP/master_codegen.cpp --- a/clang/test/OpenMP/master_codegen.cpp +++ b/clang/test/OpenMP/master_codegen.cpp @@ -1,7 +1,10 @@ -// RUN: %clang_cc1 -verify -fopenmp -x c++ -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck %s +// RUN: %clang_cc1 -verify -fopenmp -x c++ -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck %s --check-prefixes=ALL,NORMAL // RUN: %clang_cc1 -fopenmp -x c++ -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -o %t %s -// RUN: %clang_cc1 -fopenmp -x c++ -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -fopenmp -x c++ -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s --check-prefixes=ALL,NORMAL // RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -fopenmp -fexceptions -fcxx-exceptions -debug-info-kind=line-tables-only -x c++ -emit-llvm %s -o - | FileCheck %s --check-prefix=TERM_DEBUG +// RUN: %clang_cc1 -verify -fopenmp -fopenmp-enable-irbuilder -x c++ -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck %s --check-prefixes=ALL,IRBUILDER +// RUN: %clang_cc1 -fopenmp -fopenmp-enable-irbuilder -x c++ -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -o %t %s +// RUN: %clang_cc1 -fopenmp -fopenmp-enable-irbuilder -x c++ -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s --check-prefixes=ALL,IRBUILDER // RUN: %clang_cc1 -verify -fopenmp-simd -x c++ -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck --check-prefix SIMD-ONLY0 %s // RUN: %clang_cc1 -fopenmp-simd -x c++ -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -o %t %s @@ -12,45 +15,47 @@ #ifndef HEADER #define HEADER -// CHECK: [[IDENT_T_TY:%.+]] = type { i32, i32, i32, i32, i8* } +// ALL: [[IDENT_T_TY:%.+]] = type { i32, i32, i32, i32, i8* } -// CHECK: define {{.*}}void [[FOO:@.+]]() +// ALL: define {{.*}}void [[FOO:@.+]]() void foo() {} -// CHECK-LABEL: @main +// ALL-LABEL: @main // TERM_DEBUG-LABEL: @main int main() { - // CHECK: [[A_ADDR:%.+]] = alloca i8 + // ALL: [[A_ADDR:%.+]] = alloca i8 char a; -// CHECK: [[GTID:%.+]] = call {{.*}}i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:@.+]]) -// CHECK: [[RES:%.+]] = call {{.*}}i32 @__kmpc_master([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]]) -// CHECK-NEXT: [[IS_MASTER:%.+]] = icmp ne i32 [[RES]], 0 -// CHECK-NEXT: br i1 [[IS_MASTER]], label {{%?}}[[THEN:.+]], label {{%?}}[[EXIT:.+]] -// CHECK: [[THEN]] -// CHECK-NEXT: store i8 2, i8* [[A_ADDR]] -// CHECK-NEXT: call {{.*}}void @__kmpc_end_master([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]]) -// CHECK-NEXT: br label {{%?}}[[EXIT]] -// CHECK: [[EXIT]] +// ALL: [[GTID:%.+]] = call {{.*}}i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:@.+]]) +// ALL: [[RES:%.+]] = call {{.*}}i32 @__kmpc_master([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]]) +// ALL-NEXT: [[IS_MASTER:%.+]] = icmp ne i32 [[RES]], 0 +// ALL-NEXT: br i1 [[IS_MASTER]], label {{%?}}[[THEN:.+]], label {{%?}}[[EXIT:.+]] +// ALL: [[THEN]] +// ALL-NEXT: store i8 2, i8* [[A_ADDR]] +// ALL-NEXT: call {{.*}}void @__kmpc_end_master([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]]) +// ALL-NEXT: br label {{%?}}[[EXIT]] +// ALL: [[EXIT]] #pragma omp master a = 2; -// CHECK: [[RES:%.+]] = call {{.*}}i32 @__kmpc_master([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]]) -// CHECK-NEXT: [[IS_MASTER:%.+]] = icmp ne i32 [[RES]], 0 -// CHECK-NEXT: br i1 [[IS_MASTER]], label {{%?}}[[THEN:.+]], label {{%?}}[[EXIT:.+]] -// CHECK: [[THEN]] -// CHECK-NEXT: invoke {{.*}}void [[FOO]]() -// CHECK: call {{.*}}void @__kmpc_end_master([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]]) -// CHECK-NEXT: br label {{%?}}[[EXIT]] -// CHECK: [[EXIT]] +// IRBUILDER: [[GTID:%.+]] = call {{.*}}i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:@.+]]) +// ALL: [[RES:%.+]] = call {{.*}}i32 @__kmpc_master([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]]) +// ALL-NEXT: [[IS_MASTER:%.+]] = icmp ne i32 [[RES]], 0 +// ALL-NEXT: br i1 [[IS_MASTER]], label {{%?}}[[THEN:.+]], label {{%?}}[[EXIT:.+]] +// ALL: [[THEN]] +// IRBUILDER-NEXT: call {{.*}}void [[FOO]]() +// NORMAL-NEXT: invoke {{.*}}void [[FOO]]() +// ALL: call {{.*}}void @__kmpc_end_master([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]]) +// ALL-NEXT: br label {{%?}}[[EXIT]] +// ALL: [[EXIT]] #pragma omp master foo(); -// CHECK-NOT: call i32 @__kmpc_master -// CHECK-NOT: call void @__kmpc_end_master + // ALL-NOT: call i32 @__kmpc_master + // ALL-NOT: call void @__kmpc_end_master return a; } -// CHECK-LABEL: parallel_master +// ALL-LABEL: parallel_master // TERM_DEBUG-LABEL: parallel_master void parallel_master() { #pragma omp parallel diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPConstants.h b/llvm/include/llvm/Frontend/OpenMP/OMPConstants.h --- a/llvm/include/llvm/Frontend/OpenMP/OMPConstants.h +++ b/llvm/include/llvm/Frontend/OpenMP/OMPConstants.h @@ -20,6 +20,7 @@ namespace llvm { class Type; class Module; +class ArrayType; class StructType; class PointerType; class FunctionType; @@ -84,7 +85,14 @@ ///{ namespace types { +/// Type for kmp_critical_name[8], and related pointer type; +// extern ArrayType *KmpCriticalNameTy; +// extern PointerType *KmpCriticalNamePtrTy; + #define OMP_TYPE(VarName, InitValue) extern Type *VarName; +#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \ + extern ArrayType *VarName##Ty; \ + extern PointerType *VarName##PtrTy; #define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \ extern FunctionType *VarName; \ extern PointerType *VarName##Ptr; diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h --- a/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h +++ b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h @@ -243,6 +243,121 @@ /// Map to remember existing ident_t*. DenseMap, GlobalVariable *> IdentMap; + + /// An ordered map of auto-generated variables to their unique names. + /// It stores variables with the following names: 1) ".gomp_critical_user_" + + /// + ".var" for "omp critical" directives; 2) + /// + ".cache." for cache for threadprivate + /// variables. + StringMap, BumpPtrAllocator> InternalVars; + +public: + /// Generator for '#omp master' + /// + /// \param Loc The insert and source location description. + /// \param BodyGenCB Callback that will generate the region code. + /// \param FiniCB Callback to finalize variable copies. + /// + /// \returns The insertion position *after* the master. + InsertPointTy CreateMaster(const LocationDescription &Loc, + BodyGenCallbackTy BodyGenCB, + FinalizeCallbackTy FiniCB); + + /// Generator for '#omp master' + /// + /// \param Loc The insert and source location description. + /// \param BodyGenCB Callback that will generate the region code. + /// \param FiniCB Callback to finalize variable copies. + /// \param CriticalName name of the lock used by the critical directive + /// \param hasHint whether there is ahint clause associated with critical + /// + /// \returns The insertion position *after* the master. + InsertPointTy CreateCritical(const LocationDescription &Loc, + BodyGenCallbackTy BodyGenCB, + FinalizeCallbackTy FiniCB, + StringRef CriticalName, Value *HintInst); + +private: + /// Common interface for generating entry calls for OMP Directives. + /// if the directive has a region/body, It will set the insertion + /// point to the body + /// + /// \param OMPD Directive to generate entry blocks for + /// \param EntryCall Call to the entry OMP Runtime Function + /// \param ExitBB block where the region ends. + /// \param Conditional indicate if the entry call result will be used + /// to evaluate a conditional of whether a thread will execute + /// body code or not. + /// + /// \return The insertion position in exit block + InsertPointTy emitCommonDirectiveEntry(omp::Directive OMPD, Value *EntryCall, + BasicBlock *ExitBB, + bool Conditional = false); + + /// Common interface to finalize the region + /// + /// \param OMPD Directive to generate exiting code for + /// \param FinIP Insertion point for emitting Finalization code and exit call + /// \param ExitCall Call to the ending OMP Runtime Function + /// \param hasFinalize indicate if the directive will require finalization + /// and has a finalization callback in the stack that + /// should be + /// called. + /// + /// \return The insertion position in exit block + InsertPointTy emitCommonDirectiveExit(omp::Directive OMPD, + InsertPointTy FinIP, + Instruction *ExitCall, + bool HasFinalize = true); + + /// Common Interface to generate OMP inlined regions + /// + /// \param OMPD Directive to generate inlined region for + /// \param EntryCall Call to the entry OMP Runtime Function + /// \param ExitCall Call to the ending OMP Runtime Function + /// \param BodyGenCB Body code generation callback. + /// \param FiniCB Finalization Callback. Will be called when finalizing region + /// \param Conditional indicate if the entry call result will be used + /// to evaluate a conditional of whether a thread will execute + /// body code or not. + /// \param hasFinalize indicate if the directive will require finalization + /// and has a finalization callback in the stack that should + /// be + /// called. + /// + /// \return The insertion point after the region + + InsertPointTy + EmitOMPInlinedRegion(omp::Directive OMPD, Instruction *EntryCall, + Instruction *ExitCall, BodyGenCallbackTy BodyGenCB, + FinalizeCallbackTy FiniCB, bool Conditional = false, + bool HasFinalize = true); + + /// Get the platform-specific name separator. + /// \param Parts different parts of the final name that needs separation + /// \param Firstseparator First separator used between the initial two + /// parts of the name. + /// \param Separator separator used between all of the rest consecutinve + /// parts of the name + static std::string getNameWithSeparators(ArrayRef Parts, + StringRef FirstSeparator, + StringRef Separator); + + /// Gets (if variable with the given name already exist) or creates + /// internal global variable with the specified Name. The created variable has + /// linkage CommonLinkage by default and is initialized by null value. + /// \param Ty Type of the global variable. If it is exist already the type + /// must be the same. + /// \param Name Name of the variable. + Constant *getOrCreateOMPInternalVariable(Type *Ty, const Twine &Name, + unsigned AddressSpace = 0); + + /// Returns corresponding lock object for the specified critical region + /// name. If the lock object does not exist it is created, otherwise the + /// reference to the existing copy is returned. + /// \param CriticalName Name of the critical region. + /// + Value *getOMPCriticalRegionLock(StringRef CriticalName); }; } // end namespace llvm diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPKinds.def b/llvm/include/llvm/Frontend/OpenMP/OMPKinds.def --- a/llvm/include/llvm/Frontend/OpenMP/OMPKinds.def +++ b/llvm/include/llvm/Frontend/OpenMP/OMPKinds.def @@ -122,6 +122,24 @@ ///} +/// array types +/// +///{ + +#ifndef OMP_ARRAY_TYPE +#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) +#endif + +#define __OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \ + OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) + +__OMP_ARRAY_TYPE(KmpCriticalName, Int32, 8) + +#undef __OMP_ARRAY_TYPE +#undef OMP_ARRAY_TYPE + +///} + /// Struct and function types /// ///{ @@ -176,6 +194,12 @@ __OMP_RTL(omp_get_thread_num, false, Int32, ) +__OMP_RTL(__kmpc_master, false, Int32, IdentPtr, Int32) +__OMP_RTL(__kmpc_end_master, false, Void, IdentPtr, Int32) +__OMP_RTL(__kmpc_critical, false, Void, IdentPtr, Int32, KmpCriticalNamePtrTy) +__OMP_RTL(__kmpc_critical_with_hint, false, Void, IdentPtr, Int32, KmpCriticalNamePtrTy, Int32) +__OMP_RTL(__kmpc_end_critical, false, Void, IdentPtr, Int32, KmpCriticalNamePtrTy) + #undef __OMP_RTL #undef OMP_RTL diff --git a/llvm/lib/Frontend/OpenMP/OMPConstants.cpp b/llvm/lib/Frontend/OpenMP/OMPConstants.cpp --- a/llvm/lib/Frontend/OpenMP/OMPConstants.cpp +++ b/llvm/lib/Frontend/OpenMP/OMPConstants.cpp @@ -42,8 +42,10 @@ /// values. /// ///{ - #define OMP_TYPE(VarName, InitValue) Type *llvm::omp::types::VarName = nullptr; +#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \ + ArrayType *llvm::omp::types::VarName##Ty = nullptr; \ + PointerType *llvm::omp::types::VarName##PtrTy = nullptr; #define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \ FunctionType *llvm::omp::types::VarName = nullptr; \ PointerType *llvm::omp::types::VarName##Ptr = nullptr; @@ -63,6 +65,9 @@ // the llvm::PointerTypes of them for easy access later. StructType *T; #define OMP_TYPE(VarName, InitValue) VarName = InitValue; +#define OMP_ARRAY_TYPE(VarName, ElemTy, ArraySize) \ + VarName##Ty = ArrayType::get(ElemTy, ArraySize); \ + VarName##PtrTy = PointerType::getUnqual(VarName##Ty); #define OMP_FUNCTION_TYPE(VarName, IsVarArg, ReturnType, ...) \ VarName = FunctionType::get(ReturnType, {__VA_ARGS__}, IsVarArg); \ VarName##Ptr = PointerType::getUnqual(VarName); diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp --- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp +++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp @@ -630,3 +630,239 @@ return AfterIP; } + +OpenMPIRBuilder::InsertPointTy +OpenMPIRBuilder::CreateMaster(const LocationDescription &Loc, + BodyGenCallbackTy BodyGenCB, + FinalizeCallbackTy FiniCB) { + + if (!updateToLocation(Loc)) + return Loc.IP; + + Directive OMPD = Directive::OMPD_master; + Constant *SrcLocStr = getOrCreateSrcLocStr(Loc); + Value *Ident = getOrCreateIdent(SrcLocStr); + Value *ThreadId = getOrCreateThreadID(Ident); + Value *Args[] = {Ident, ThreadId}; + + Function *EntryRTLFn = getOrCreateRuntimeFunction(OMPRTL___kmpc_master); + Instruction *EntryCall = Builder.CreateCall(EntryRTLFn, Args); + + Function *ExitRTLFn = getOrCreateRuntimeFunction(OMPRTL___kmpc_end_master); + Instruction *ExitCall = Builder.CreateCall(ExitRTLFn, Args); + + return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB, + /*Conditional*/ true, /*hasFinalize*/ true); +} + +OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::CreateCritical( + const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, + FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) { + + if (!updateToLocation(Loc)) + return Loc.IP; + + Directive OMPD = Directive::OMPD_critical; + Constant *SrcLocStr = getOrCreateSrcLocStr(Loc); + Value *Ident = getOrCreateIdent(SrcLocStr); + Value *ThreadId = getOrCreateThreadID(Ident); + Value *LockVar = getOMPCriticalRegionLock(CriticalName); + Value *Args[] = {Ident, ThreadId, LockVar}; + + SmallVector EnterArgs(std::begin(Args), std::end(Args)); + Function *RTFn = nullptr; + if (HintInst) { + // Add Hint to entry Args and create call + EnterArgs.push_back(HintInst); + RTFn = getOrCreateRuntimeFunction(OMPRTL___kmpc_critical_with_hint); + } else { + RTFn = getOrCreateRuntimeFunction(OMPRTL___kmpc_critical); + } + Instruction *EntryCall = Builder.CreateCall(RTFn, EnterArgs); + + Function *ExitRTLFn = getOrCreateRuntimeFunction(OMPRTL___kmpc_end_critical); + Instruction *ExitCall = Builder.CreateCall(ExitRTLFn, Args); + + return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB, + /*Conditional*/ false, /*hasFinalize*/ true); +} + +OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::EmitOMPInlinedRegion( + Directive OMPD, Instruction *EntryCall, Instruction *ExitCall, + BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, bool Conditional, + bool HasFinalize) { + + FinalizationStack.push_back({FiniCB, OMPD, /*IsCancellable*/ false}); + + // Create inlined region's entry and body blocks, in preparation + // for conditional creation + BasicBlock *EntryBB = Builder.GetInsertBlock(); + Instruction *SplitPos = EntryBB->getTerminator(); + if (!isa_and_nonnull(SplitPos)) + SplitPos = new UnreachableInst(Builder.getContext(), EntryBB); + BasicBlock *ExitBB = EntryBB->splitBasicBlock(SplitPos, "omp_region.end"); + BasicBlock *FiniBB = + EntryBB->splitBasicBlock(EntryBB->getTerminator(), "omp_region.finalize"); + + Builder.SetInsertPoint(EntryBB->getTerminator()); + emitCommonDirectiveEntry(OMPD, EntryCall, ExitBB, Conditional); + + // generate body + BodyGenCB(/* AllocaIP */ InsertPointTy(), + /* CodeGenIP */ Builder.saveIP(), *FiniBB); + + // If we didn't emit a branch to FiniBB during body generation, it means + // FiniBB is unreachable (e.g. while(1);). stop generating all the + // unreachable blocks, and remove anything we are not going to use. + auto SkipEmittingRegion = FiniBB->hasNPredecessors(0); + if (SkipEmittingRegion) { + FiniBB->eraseFromParent(); + ExitCall->eraseFromParent(); + // Discard finalization if we have it. + if (HasFinalize) { + assert(!FinalizationStack.empty() && + "Unexpected finalization stack state!"); + FinalizationStack.pop_back(); + } + } else { + // emit exit call and do any needed finalization. + auto FinIP = InsertPointTy(FiniBB, FiniBB->getFirstInsertionPt()); + assert(FiniBB->getTerminator()->getNumSuccessors() == 1 && + FiniBB->getTerminator()->getSuccessor(0) == ExitBB && + "Unexpected control flow graph state!!"); + emitCommonDirectiveExit(OMPD, FinIP, ExitCall, HasFinalize); + assert(FiniBB->getUniquePredecessor()->getUniqueSuccessor() == FiniBB && + "Unexpected Control Flow State!"); + MergeBlockIntoPredecessor(FiniBB); + } + + // If we are skipping the region of a non conditional, remove the exit + // block, and clear the builder's insertion point. + BasicBlock *IPBB = SplitPos->getParent(); + assert(IPBB == ExitBB && "Unexpected Insertion point location!"); + if (!Conditional && SkipEmittingRegion) { + ExitBB->eraseFromParent(); + Builder.ClearInsertionPoint(); + } else { + auto merged = MergeBlockIntoPredecessor(ExitBB); + BasicBlock *ExitPredBB = SplitPos->getParent(); + auto InsertBB = merged ? ExitPredBB : ExitBB; + if (!isa_and_nonnull(SplitPos)) + SplitPos->eraseFromParent(); + Builder.SetInsertPoint(InsertBB); + } + + return Builder.saveIP(); +} + +OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveEntry( + Directive omp, Value *EntryCall, BasicBlock *ExitBB, bool Conditional) { + + // if nothing to do, Return current insertion point. + if (!Conditional) + return Builder.saveIP(); + + BasicBlock *EntryBB = Builder.GetInsertBlock(); + Value *CallBool = Builder.CreateIsNotNull(EntryCall); + auto *ThenBB = BasicBlock::Create(M.getContext(), "omp_region.body"); + auto *UI = new UnreachableInst(Builder.getContext(), ThenBB); + + // Emit thenBB and set the Builder's insertion point there for + // body generation next. Place the block after the current block. + Function *CurFn = EntryBB->getParent(); + CurFn->getBasicBlockList().insertAfter(EntryBB->getIterator(), ThenBB); + + // Move Entry branch to end of ThenBB, and replace with conditional + // branch (If-stmt) + Instruction *EntryBBTI = EntryBB->getTerminator(); + Builder.CreateCondBr(CallBool, ThenBB, ExitBB); + EntryBBTI->removeFromParent(); + Builder.SetInsertPoint(UI); + Builder.Insert(EntryBBTI); + UI->eraseFromParent(); + Builder.SetInsertPoint(ThenBB->getTerminator()); + + // return an insertion point to ExitBB. + return IRBuilder<>::InsertPoint(ExitBB, ExitBB->getFirstInsertionPt()); +} + +OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::emitCommonDirectiveExit( + omp::Directive OMPD, InsertPointTy FinIP, Instruction *ExitCall, + bool hasFinalize) { + + IRBuilder<>::InsertPointGuard IPG(Builder); + Builder.restoreIP(FinIP); + + // If there is finalization to do, emit it before the exit call + if (hasFinalize) { + assert(!FinalizationStack.empty() && + "Unexpected finalization stack state!"); + + FinalizationInfo Fi = FinalizationStack.pop_back_val(); + assert(Fi.DK == OMPD && "Unexpected Directive for Finalization call!"); + + Fi.FiniCB(FinIP); + + BasicBlock *FiniBB = FinIP.getBlock(); + Instruction *FiniBBTI = FiniBB->getTerminator(); + + // set Builder IP for call creation + Builder.SetInsertPoint(FiniBBTI); + } + + // place the Exitcall as last instruction before Finalization block terminator + ExitCall->removeFromParent(); + Builder.Insert(ExitCall); + + return IRBuilder<>::InsertPoint(ExitCall->getParent(), + ExitCall->getIterator()); +} + +std::string OpenMPIRBuilder::getNameWithSeparators(ArrayRef Parts, + StringRef FirstSeparator, + StringRef Separator) { + SmallString<128> Buffer; + llvm::raw_svector_ostream OS(Buffer); + StringRef Sep = FirstSeparator; + for (StringRef Part : Parts) { + OS << Sep << Part; + Sep = Separator; + } + return OS.str(); +} + +Constant *OpenMPIRBuilder::getOrCreateOMPInternalVariable( + llvm::Type *Ty, const llvm::Twine &Name, unsigned AddressSpace) { + // TODO: Replace the twine arg with stringref to get rid of the conversion + // logic. However This is taken from current implementation in clang as is. + // Since this method is used in many places exclusively for OMP internal use + // we will keep it as is for temporarily until we move all users to the + // builder and then, if possible, fix it everywhere in one go. + SmallString<256> Buffer; + llvm::raw_svector_ostream Out(Buffer); + Out << Name; + StringRef RuntimeName = Out.str(); + auto &Elem = *InternalVars.try_emplace(RuntimeName, nullptr).first; + if (Elem.second) { + assert(Elem.second->getType()->getPointerElementType() == Ty && + "OMP internal variable has different type than requested"); + } else { + // TODO: investigate the appropriate linkage type used for the global + // variable for possibly changing that to internal or private, or maybe + // create different versions of the function for different OMP internal + // variables. + Elem.second = new llvm::GlobalVariable( + M, Ty, /*IsConstant*/ false, llvm::GlobalValue::CommonLinkage, + llvm::Constant::getNullValue(Ty), Elem.first(), + /*InsertBefore=*/nullptr, llvm::GlobalValue::NotThreadLocal, + AddressSpace); + } + + return Elem.second; +} + +Value *OpenMPIRBuilder::getOMPCriticalRegionLock(StringRef CriticalName) { + std::string Prefix = Twine("gomp_critical_user_", CriticalName).str(); + std::string Name = getNameWithSeparators({Prefix, "var"}, ".", "."); + return getOrCreateOMPInternalVariable(KmpCriticalNameTy, Name); +} diff --git a/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp --- a/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp +++ b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp @@ -613,4 +613,164 @@ } } +TEST_F(OpenMPIRBuilderTest, MasterDirective) { + using InsertPointTy = OpenMPIRBuilder::InsertPointTy; + OpenMPIRBuilder OMPBuilder(*M); + OMPBuilder.initialize(); + F->setName("func"); + IRBuilder<> Builder(BB); + + OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); + + AllocaInst *PrivAI = nullptr; + + BasicBlock *EntryBB = nullptr; + BasicBlock *ExitBB = nullptr; + BasicBlock *ThenBB = nullptr; + + auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, + BasicBlock &FiniBB) { + if (AllocaIP.isSet()) + Builder.restoreIP(AllocaIP); + else + Builder.SetInsertPoint(&*(F->getEntryBlock().getFirstInsertionPt())); + PrivAI = Builder.CreateAlloca(F->arg_begin()->getType()); + Builder.CreateStore(F->arg_begin(), PrivAI); + + llvm::BasicBlock *CodeGenIPBB = CodeGenIP.getBlock(); + llvm::Instruction *CodeGenIPInst = &*CodeGenIP.getPoint(); + EXPECT_EQ(CodeGenIPBB->getTerminator(), CodeGenIPInst); + + Builder.restoreIP(CodeGenIP); + + // collect some info for checks later + ExitBB = FiniBB.getUniqueSuccessor(); + ThenBB = Builder.GetInsertBlock(); + EntryBB = ThenBB->getUniquePredecessor(); + + // simple instructions for body + Value *PrivLoad = Builder.CreateLoad(PrivAI, "local.use"); + Builder.CreateICmpNE(F->arg_begin(), PrivLoad); + }; + + auto FiniCB = [&](InsertPointTy IP) { + BasicBlock *IPBB = IP.getBlock(); + EXPECT_NE(IPBB->end(), IP.getPoint()); + }; + + Builder.restoreIP(OMPBuilder.CreateMaster(Builder, BodyGenCB, FiniCB)); + Value *EntryBBTI = EntryBB->getTerminator(); + EXPECT_NE(EntryBBTI, nullptr); + EXPECT_TRUE(isa(EntryBBTI)); + BranchInst *EntryBr = cast(EntryBB->getTerminator()); + EXPECT_TRUE(EntryBr->isConditional()); + EXPECT_EQ(EntryBr->getSuccessor(0), ThenBB); + EXPECT_EQ(ThenBB->getUniqueSuccessor(), ExitBB); + EXPECT_EQ(EntryBr->getSuccessor(1), ExitBB); + + CmpInst *CondInst = cast(EntryBr->getCondition()); + EXPECT_TRUE(isa(CondInst->getOperand(0))); + + CallInst *MasterEntryCI = cast(CondInst->getOperand(0)); + EXPECT_EQ(MasterEntryCI->getNumArgOperands(), 2U); + EXPECT_EQ(MasterEntryCI->getCalledFunction()->getName(), "__kmpc_master"); + EXPECT_TRUE(isa(MasterEntryCI->getArgOperand(0))); + + CallInst *MasterEndCI = nullptr; + for (auto &FI : *ThenBB) { + Instruction *cur = &FI; + if (isa(cur)) { + MasterEndCI = cast(cur); + if (MasterEndCI->getCalledFunction()->getName() == "__kmpc_end_master") + break; + else + MasterEndCI = nullptr; + } + } + EXPECT_NE(MasterEndCI, nullptr); + EXPECT_EQ(MasterEndCI->getNumArgOperands(), 2U); + EXPECT_TRUE(isa(MasterEndCI->getArgOperand(0))); + EXPECT_EQ(MasterEndCI->getArgOperand(1), MasterEntryCI->getArgOperand(1)); +} + +TEST_F(OpenMPIRBuilderTest, CriticalDirective) { + using InsertPointTy = OpenMPIRBuilder::InsertPointTy; + OpenMPIRBuilder OMPBuilder(*M); + OMPBuilder.initialize(); + F->setName("func"); + IRBuilder<> Builder(BB); + + OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); + + AllocaInst *PrivAI = Builder.CreateAlloca(F->arg_begin()->getType()); + + BasicBlock *EntryBB = nullptr; + + auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, + BasicBlock &FiniBB) { + // collect some info for checks later + EntryBB = FiniBB.getUniquePredecessor(); + + // actual start for bodyCB + llvm::BasicBlock *CodeGenIPBB = CodeGenIP.getBlock(); + llvm::Instruction *CodeGenIPInst = &*CodeGenIP.getPoint(); + EXPECT_EQ(CodeGenIPBB->getTerminator(), CodeGenIPInst); + EXPECT_EQ(EntryBB, CodeGenIPBB); + + // body begin + Builder.restoreIP(CodeGenIP); + Builder.CreateStore(F->arg_begin(), PrivAI); + Value *PrivLoad = Builder.CreateLoad(PrivAI, "local.use"); + Builder.CreateICmpNE(F->arg_begin(), PrivLoad); + }; + + auto FiniCB = [&](InsertPointTy IP) { + BasicBlock *IPBB = IP.getBlock(); + EXPECT_NE(IPBB->end(), IP.getPoint()); + }; + + Builder.restoreIP(OMPBuilder.CreateCritical(Builder, BodyGenCB, FiniCB, + "testCRT", nullptr)); + + Value *EntryBBTI = EntryBB->getTerminator(); + EXPECT_EQ(EntryBBTI, nullptr); + + CallInst *CriticalEntryCI = nullptr; + for (auto &EI : *EntryBB) { + Instruction *cur = &EI; + if (isa(cur)) { + CriticalEntryCI = cast(cur); + if (CriticalEntryCI->getCalledFunction()->getName() == "__kmpc_critical") + break; + else + CriticalEntryCI = nullptr; + } + } + EXPECT_NE(CriticalEntryCI, nullptr); + EXPECT_EQ(CriticalEntryCI->getNumArgOperands(), 3U); + EXPECT_EQ(CriticalEntryCI->getCalledFunction()->getName(), "__kmpc_critical"); + EXPECT_TRUE(isa(CriticalEntryCI->getArgOperand(0))); + + CallInst *CriticalEndCI = nullptr; + for (auto &FI : *EntryBB) { + Instruction *cur = &FI; + if (isa(cur)) { + CriticalEndCI = cast(cur); + if (CriticalEndCI->getCalledFunction()->getName() == + "__kmpc_end_critical") + break; + else + CriticalEndCI = nullptr; + } + } + EXPECT_NE(CriticalEndCI, nullptr); + EXPECT_EQ(CriticalEndCI->getNumArgOperands(), 3U); + EXPECT_TRUE(isa(CriticalEndCI->getArgOperand(0))); + EXPECT_EQ(CriticalEndCI->getArgOperand(1), CriticalEntryCI->getArgOperand(1)); + PointerType *CriticalNamePtrTy = + PointerType::getUnqual(ArrayType::get(Type::getInt32Ty(Ctx), 8)); + EXPECT_EQ(CriticalEndCI->getArgOperand(2), CriticalEntryCI->getArgOperand(2)); + EXPECT_EQ(CriticalEndCI->getArgOperand(2)->getType(), CriticalNamePtrTy); +} + } // namespace