| // RUN: mlir-translate -mlir-to-llvmir -split-input-file %s | FileCheck %s |
| |
| // Single scalar task_reduction on omp.taskgroup. Verifies that the |
| // kmp_taskred_input_t descriptor is allocated, populated, and handed off to |
| // __kmpc_taskred_init, and that init / combiner helper functions are emitted. |
| |
| omp.declare_reduction @add_i32 : i32 |
| init { |
| ^bb0(%arg0: i32): |
| %c0 = llvm.mlir.constant(0 : i32) : i32 |
| omp.yield(%c0 : i32) |
| } |
| combiner { |
| ^bb0(%arg0: i32, %arg1: i32): |
| %s = llvm.add %arg0, %arg1 : i32 |
| omp.yield(%s : i32) |
| } |
| |
| llvm.func @taskgroup_task_reduction_single(%x: !llvm.ptr) { |
| omp.taskgroup task_reduction(@add_i32 %x -> %prv : !llvm.ptr) { |
| omp.terminator |
| } |
| llvm.return |
| } |
| |
| // CHECK: %kmp_taskred_input_t = type { ptr, ptr, i64, ptr, ptr, ptr, i32 } |
| |
| // CHECK-LABEL: define void @taskgroup_task_reduction_single( |
| // CHECK-SAME: ptr %[[X:.+]]) |
| // CHECK: %[[ARR:.+]] = alloca [1 x %kmp_taskred_input_t] |
| // CHECK: call void @__kmpc_taskgroup( |
| // Descriptor entry 0. |
| // CHECK: %[[GEP0:.+]] = getelementptr inbounds [1 x %kmp_taskred_input_t], ptr %[[ARR]], i32 0, i32 0 |
| // CHECK: %[[SHAR:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[GEP0]], i32 0, i32 0 |
| // CHECK: store ptr %[[X]], ptr %[[SHAR]] |
| // CHECK: %[[ORIG:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[GEP0]], i32 0, i32 1 |
| // CHECK: store ptr %[[X]], ptr %[[ORIG]] |
| // CHECK: %[[SZF:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[GEP0]], i32 0, i32 2 |
| // CHECK: store i64 4, ptr %[[SZF]] |
| // CHECK: %[[INITF:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[GEP0]], i32 0, i32 3 |
| // CHECK: store ptr @__omp_taskred_add_i32.red.init, ptr %[[INITF]] |
| // CHECK: %[[FINIF:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[GEP0]], i32 0, i32 4 |
| // CHECK: store ptr null, ptr %[[FINIF]] |
| // CHECK: %[[COMBF:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[GEP0]], i32 0, i32 5 |
| // CHECK: store ptr @__omp_taskred_add_i32.red.comb, ptr %[[COMBF]] |
| // CHECK: %[[FLAGSF:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[GEP0]], i32 0, i32 6 |
| // CHECK: store i32 0, ptr %[[FLAGSF]] |
| // CHECK: call ptr @__kmpc_taskred_init(i32 %{{.+}}, i32 1, ptr %[[ARR]]) |
| // CHECK: call void @__kmpc_end_taskgroup( |
| |
| // CHECK-LABEL: define internal void @__omp_taskred_add_i32.red.init( |
| // CHECK-SAME: ptr %priv, ptr %orig) |
| // CHECK: load i32, ptr %orig |
| // CHECK: store i32 0, ptr %priv |
| // CHECK: ret void |
| |
| // CHECK-LABEL: define internal void @__omp_taskred_add_i32.red.comb( |
| // CHECK-SAME: ptr %lhs, ptr %rhs) |
| // CHECK: %[[L:.+]] = load i32, ptr %lhs |
| // CHECK: %[[R:.+]] = load i32, ptr %rhs |
| // CHECK: %[[S:.+]] = add i32 %[[L]], %[[R]] |
| // CHECK: store i32 %[[S]], ptr %lhs |
| // CHECK: ret void |
| |
| // ----- |
| |
| // Non-default address space reduction storage must be normalized before |
| // populating kmp_taskred_input_t, while callback ABI remains generic ptr. |
| |
| omp.declare_reduction @add_i32_as1 : i32 |
| init { |
| ^bb0(%arg0: i32): |
| %c0 = llvm.mlir.constant(0 : i32) : i32 |
| omp.yield(%c0 : i32) |
| } |
| combiner { |
| ^bb0(%arg0: i32, %arg1: i32): |
| %s = llvm.add %arg0, %arg1 : i32 |
| omp.yield(%s : i32) |
| } |
| |
| llvm.func @taskgroup_task_reduction_single_as1(%x: !llvm.ptr<1>) { |
| omp.taskgroup task_reduction(@add_i32_as1 %x -> %prv : !llvm.ptr<1>) { |
| omp.terminator |
| } |
| llvm.return |
| } |
| |
| // CHECK-LABEL: define void @taskgroup_task_reduction_single_as1( |
| // CHECK-SAME: ptr addrspace(1) %[[X:.+]]) |
| // CHECK: %[[ARR:.+]] = alloca [1 x %kmp_taskred_input_t] |
| // CHECK: %[[X_CAST:.+]] = addrspacecast ptr addrspace(1) %[[X]] to ptr |
| // CHECK: %[[GEP0:.+]] = getelementptr inbounds [1 x %kmp_taskred_input_t], ptr %[[ARR]], i32 0, i32 0 |
| // CHECK: %[[SHAR:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[GEP0]], i32 0, i32 0 |
| // CHECK: store ptr %[[X_CAST]], ptr %[[SHAR]] |
| // CHECK: %[[ORIG:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[GEP0]], i32 0, i32 1 |
| // CHECK: store ptr %[[X_CAST]], ptr %[[ORIG]] |
| |
| // CHECK-LABEL: define internal void @__omp_taskred_add_i32_as1.red.init( |
| // CHECK-SAME: ptr %priv, ptr %orig) |
| // CHECK: load i32, ptr %orig |
| // CHECK: store i32 0, ptr %priv |
| |
| // CHECK-LABEL: define internal void @__omp_taskred_add_i32_as1.red.comb( |
| // CHECK-SAME: ptr %lhs, ptr %rhs) |
| // CHECK: %[[AS1_L:.+]] = load i32, ptr %lhs |
| // CHECK: %[[AS1_R:.+]] = load i32, ptr %rhs |
| // CHECK: %[[AS1_S:.+]] = add i32 %[[AS1_L]], %[[AS1_R]] |
| // CHECK: store i32 %[[AS1_S]], ptr %lhs |
| |
| // ----- |
| |
| // Pointer-typed task_reduction. Here the declare_reduction's reduction type is |
| // itself a pointer, so the initializer's mold argument is the storage pointer. |
| // Following the regular reduction path (mapInitializationArgs), the init helper |
| // must pass %orig through directly instead of emitting a spurious |
| // `load ptr, ptr %orig`. The combiner still loads its pointer operands, exactly |
| // like the by-value reduction path. |
| |
| omp.declare_reduction @keep_ptr : !llvm.ptr |
| init { |
| ^bb0(%mold: !llvm.ptr): |
| omp.yield(%mold : !llvm.ptr) |
| } |
| combiner { |
| ^bb0(%a: !llvm.ptr, %b: !llvm.ptr): |
| omp.yield(%a : !llvm.ptr) |
| } |
| |
| llvm.func @taskgroup_task_reduction_ptr(%x: !llvm.ptr) { |
| omp.taskgroup task_reduction(@keep_ptr %x -> %prv : !llvm.ptr) { |
| omp.terminator |
| } |
| llvm.return |
| } |
| |
| // CHECK-LABEL: define internal void @__omp_taskred_keep_ptr.red.init( |
| // CHECK-SAME: ptr %priv, ptr %orig) |
| // CHECK-NOT: load ptr, ptr %orig |
| // CHECK: store ptr %orig, ptr %priv |
| // CHECK: ret void |
| |
| // CHECK-LABEL: define internal void @__omp_taskred_keep_ptr.red.comb( |
| // CHECK-SAME: ptr %lhs, ptr %rhs) |
| // CHECK: %[[L:.+]] = load ptr, ptr %lhs |
| // CHECK: %[[R:.+]] = load ptr, ptr %rhs |
| // CHECK: store ptr %[[L]], ptr %lhs |
| // CHECK: ret void |
| |
| // ----- |
| |
| // Multiple task_reduction items on the same taskgroup. |
| |
| omp.declare_reduction @add_i32 : i32 |
| init { |
| ^bb0(%arg0: i32): |
| %c0 = llvm.mlir.constant(0 : i32) : i32 |
| omp.yield(%c0 : i32) |
| } |
| combiner { |
| ^bb0(%arg0: i32, %arg1: i32): |
| %s = llvm.add %arg0, %arg1 : i32 |
| omp.yield(%s : i32) |
| } |
| |
| omp.declare_reduction @mul_i64 : i64 |
| init { |
| ^bb0(%arg0: i64): |
| %c1 = llvm.mlir.constant(1 : i64) : i64 |
| omp.yield(%c1 : i64) |
| } |
| combiner { |
| ^bb0(%arg0: i64, %arg1: i64): |
| %p = llvm.mul %arg0, %arg1 : i64 |
| omp.yield(%p : i64) |
| } |
| |
| llvm.func @taskgroup_task_reduction_multi(%x: !llvm.ptr, %y: !llvm.ptr) { |
| omp.taskgroup task_reduction(@add_i32 %x -> %a, @mul_i64 %y -> %b : !llvm.ptr, !llvm.ptr) { |
| omp.terminator |
| } |
| llvm.return |
| } |
| |
| // CHECK-LABEL: define void @taskgroup_task_reduction_multi( |
| // CHECK-SAME: ptr %[[XA:[^,)]+]], ptr %[[YA:[^,)]+]]) |
| // CHECK: %[[ARR2:.+]] = alloca [2 x %kmp_taskred_input_t] |
| // CHECK: call void @__kmpc_taskgroup( |
| // Descriptor entry 0: @add_i32 on %x. |
| // CHECK: %[[E0:.+]] = getelementptr inbounds [2 x %kmp_taskred_input_t], ptr %[[ARR2]], i32 0, i32 0 |
| // CHECK: %[[E0_SHAR:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[E0]], i32 0, i32 0 |
| // CHECK: store ptr %[[XA]], ptr %[[E0_SHAR]] |
| // CHECK: %[[E0_ORIG:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[E0]], i32 0, i32 1 |
| // CHECK: store ptr %[[XA]], ptr %[[E0_ORIG]] |
| // CHECK: %[[E0_SZ:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[E0]], i32 0, i32 2 |
| // CHECK: store i64 4, ptr %[[E0_SZ]] |
| // CHECK: %[[E0_INIT:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[E0]], i32 0, i32 3 |
| // CHECK: store ptr @__omp_taskred_add_i32.red.init, ptr %[[E0_INIT]] |
| // CHECK: %[[E0_FINI:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[E0]], i32 0, i32 4 |
| // CHECK: store ptr null, ptr %[[E0_FINI]] |
| // CHECK: %[[E0_COMB:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[E0]], i32 0, i32 5 |
| // CHECK: store ptr @__omp_taskred_add_i32.red.comb, ptr %[[E0_COMB]] |
| // CHECK: %[[E0_FL:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[E0]], i32 0, i32 6 |
| // CHECK: store i32 0, ptr %[[E0_FL]] |
| // Descriptor entry 1: @mul_i64 on %y. |
| // CHECK: %[[E1:.+]] = getelementptr inbounds [2 x %kmp_taskred_input_t], ptr %[[ARR2]], i32 0, i32 1 |
| // CHECK: %[[E1_SHAR:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[E1]], i32 0, i32 0 |
| // CHECK: store ptr %[[YA]], ptr %[[E1_SHAR]] |
| // CHECK: %[[E1_ORIG:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[E1]], i32 0, i32 1 |
| // CHECK: store ptr %[[YA]], ptr %[[E1_ORIG]] |
| // CHECK: %[[E1_SZ:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[E1]], i32 0, i32 2 |
| // CHECK: store i64 8, ptr %[[E1_SZ]] |
| // CHECK: %[[E1_INIT:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[E1]], i32 0, i32 3 |
| // CHECK: store ptr @__omp_taskred_mul_i64.red.init, ptr %[[E1_INIT]] |
| // CHECK: %[[E1_FINI:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[E1]], i32 0, i32 4 |
| // CHECK: store ptr null, ptr %[[E1_FINI]] |
| // CHECK: %[[E1_COMB:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[E1]], i32 0, i32 5 |
| // CHECK: store ptr @__omp_taskred_mul_i64.red.comb, ptr %[[E1_COMB]] |
| // CHECK: %[[E1_FL:.+]] = getelementptr {{.+}} %kmp_taskred_input_t, ptr %[[E1]], i32 0, i32 6 |
| // CHECK: store i32 0, ptr %[[E1_FL]] |
| // CHECK: call ptr @__kmpc_taskred_init(i32 %{{.+}}, i32 2, ptr %[[ARR2]]) |
| |
| // ----- |
| |
| // Plain taskgroup without task_reduction must still translate (regression |
| // guard for the rewrite of convertOmpTaskgroupOp). |
| |
| llvm.func @taskgroup_plain() { |
| omp.taskgroup { |
| omp.terminator |
| } |
| llvm.return |
| } |
| |
| // CHECK-LABEL: define void @taskgroup_plain() |
| // CHECK: call void @__kmpc_taskgroup( |
| // CHECK-NOT: call ptr @__kmpc_taskred_init( |
| // CHECK: call void @__kmpc_end_taskgroup( |