1// Test code-gen for `omp.parallel` ops with delayed privatizers (i.e. using 2// `omp.private` ops). 3 4// RUN: mlir-translate -mlir-to-llvmir -split-input-file %s | FileCheck %s 5 6llvm.func @parallel_op_firstprivate(%arg0: !llvm.ptr) { 7 omp.parallel private(@x.privatizer %arg0 -> %arg2 : !llvm.ptr) { 8 %0 = llvm.load %arg2 : !llvm.ptr -> f32 9 omp.terminator 10 } 11 llvm.return 12} 13 14omp.private {type = firstprivate} @x.privatizer : !llvm.ptr alloc { 15^bb0(%arg0: !llvm.ptr): 16 %c1 = llvm.mlir.constant(1 : i32) : i32 17 %0 = llvm.alloca %c1 x f32 : (i32) -> !llvm.ptr 18 omp.yield(%0 : !llvm.ptr) 19} copy { 20^bb0(%arg0: !llvm.ptr, %arg1: !llvm.ptr): 21 %0 = llvm.load %arg0 : !llvm.ptr -> f32 22 llvm.store %0, %arg1 : f32, !llvm.ptr 23 omp.yield(%arg1 : !llvm.ptr) 24} 25 26// CHECK-LABEL: @parallel_op_firstprivate 27// CHECK-SAME: (ptr %[[ORIG:.*]]) { 28// CHECK: %[[OMP_PAR_ARG:.*]] = alloca { ptr }, align 8 29// CHECK: %[[ORIG_GEP:.*]] = getelementptr { ptr }, ptr %[[OMP_PAR_ARG]], i32 0, i32 0 30// CHECK: store ptr %[[ORIG]], ptr %[[ORIG_GEP]], align 8 31// CHECK: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @1, i32 1, ptr @parallel_op_firstprivate..omp_par, ptr %[[OMP_PAR_ARG]]) 32// CHECK: } 33 34// CHECK-LABEL: void @parallel_op_firstprivate..omp_par 35// CHECK-SAME: (ptr noalias %{{.*}}, ptr noalias %{{.*}}, ptr %[[ARG:.*]]) 36// CHECK: %[[ORIG_PTR_PTR:.*]] = getelementptr { ptr }, ptr %[[ARG]], i32 0, i32 0 37// CHECK: %[[ORIG_PTR:.*]] = load ptr, ptr %[[ORIG_PTR_PTR]], align 8 38 39// Check that the privatizer alloc region was inlined properly. 40// CHECK: %[[PRIV_ALLOC:.*]] = alloca float, align 4 41 42// Check that the privatizer copy region was inlined properly. 43 44// CHECK: %[[ORIG_VAL:.*]] = load float, ptr %[[ORIG_PTR]], align 4 45// CHECK: store float %[[ORIG_VAL]], ptr %[[PRIV_ALLOC]], align 4 46// CHECK-NEXT: br 47 48// Check that the privatized value is used (rather than the original one). 49// CHECK: load float, ptr %[[PRIV_ALLOC]], align 4 50// CHECK: } 51 52// ----- 53 54llvm.func @parallel_op_firstprivate_multi_block(%arg0: !llvm.ptr) { 55 omp.parallel private(@multi_block.privatizer %arg0 -> %arg2 : !llvm.ptr) { 56 %0 = llvm.load %arg2 : !llvm.ptr -> f32 57 omp.terminator 58 } 59 llvm.return 60} 61 62// CHECK-LABEL: define internal void @parallel_op_firstprivate_multi_block..omp_par 63// CHECK: omp.par.entry: 64// CHECK: %[[ORIG_PTR_PTR:.*]] = getelementptr { ptr }, ptr %{{.*}}, i32 0, i32 0 65// CHECK: %[[ORIG_PTR:.*]] = load ptr, ptr %[[ORIG_PTR_PTR]], align 8 66// CHECK: br label %[[PRIV_BB1:.*]] 67 68// CHECK: [[PRIV_BB1]]: 69// The 1st `alloc` block directly branches to the 2nd `alloc` block since the 70// only insruction is `llvm.mlir.constant` which gets translated to compile-time 71// constant in LLVM IR. 72// CHECK-NEXT: br label %[[PRIV_BB2:.*]] 73 74// CHECK: [[PRIV_BB2]]: 75// CHECK-NEXT: %[[C1:.*]] = phi i32 [ 1, %[[PRIV_BB1]] ] 76// CHECK-NEXT: %[[PRIV_ALLOC:.*]] = alloca float, i32 %[[C1]], align 4 77// CHECK-NEXT: br label %omp.region.cont 78 79// CHECK: omp.region.cont: 80// CHECK-NEXT: %[[PRIV_ALLOC2:.*]] = phi ptr [ %[[PRIV_ALLOC]], %[[PRIV_BB2]] ] 81// CHECK-NEXT: br label %omp.private.latealloc 82 83// CHECK: omp.private.latealloc: 84// CHECK-NEXT: br label %omp.private.copy 85 86// CHECK: omp.private.copy: 87// CHECK-NEXT: br label %omp.private.copy3 88 89// CHECK: omp.private.copy3: 90// CHECK-NEXT: %[[ORIG_VAL:.*]] = load float, ptr %[[ORIG_PTR]], align 4 91// CHECK-NEXT: br label %[[PRIV_BB3:.*]] 92 93// Check contents of the 2nd block in the `copy` region. 94// CHECK: [[PRIV_BB3]]: 95// CHECK-NEXT: %[[ORIG_VAL2:.*]] = phi float [ %[[ORIG_VAL]], %omp.private.copy3 ] 96// CHECK-NEXT: %[[PRIV_ALLOC3:.*]] = phi ptr [ %[[PRIV_ALLOC2]], %omp.private.copy3 ] 97// CHECK-NEXT: store float %[[ORIG_VAL2]], ptr %[[PRIV_ALLOC3]], align 4 98// CHECK-NEXT: br label %[[PRIV_CONT:.*]] 99 100// Check that the privatizer's continuation block yileds the private clone's 101// address. 102// CHECK: [[PRIV_CONT]]: 103// CHECK-NEXT: %[[PRIV_ALLOC4:.*]] = phi ptr [ %[[PRIV_ALLOC3]], %[[PRIV_BB3]] ] 104// CHECK-NEXT: br label %[[PAR_REG:.*]] 105 106// Check that the body of the parallel region loads from the private clone. 107// CHECK: [[PAR_REG]]: 108// CHECK: %{{.*}} = load float, ptr %[[PRIV_ALLOC2]], align 4 109 110omp.private {type = firstprivate} @multi_block.privatizer : !llvm.ptr alloc { 111^bb0(%arg0: !llvm.ptr): 112 %c1 = llvm.mlir.constant(1 : i32) : i32 113 llvm.br ^bb1(%c1 : i32) 114 115^bb1(%arg1: i32): 116 %0 = llvm.alloca %arg1 x f32 : (i32) -> !llvm.ptr 117 omp.yield(%0 : !llvm.ptr) 118 119} copy { 120^bb0(%arg0: !llvm.ptr, %arg1: !llvm.ptr): 121 %0 = llvm.load %arg0 : !llvm.ptr -> f32 122 llvm.br ^bb1(%0, %arg1 : f32, !llvm.ptr) 123 124^bb1(%arg2: f32, %arg3: !llvm.ptr): 125 llvm.store %arg2, %arg3 : f32, !llvm.ptr 126 omp.yield(%arg3 : !llvm.ptr) 127} 128 129// ----- 130 131// Verifies fix for https://github.com/llvm/llvm-project/issues/102935. 132// 133// The issue happens since we previously failed to match MLIR values to their 134// corresponding LLVM values in some cases (e.g. char strings with non-const 135// length). 136llvm.func @non_const_len_char_test(%n: !llvm.ptr {fir.bindc_name = "n"}) { 137 %n_val = llvm.load %n : !llvm.ptr -> i64 138 %orig_alloc = llvm.alloca %n_val x i8 {bindc_name = "str"} : (i64) -> !llvm.ptr 139 %orig_val = llvm.mlir.undef : !llvm.struct<(ptr, i64)> 140 %orig_val_init = llvm.insertvalue %orig_alloc, %orig_val[0] : !llvm.struct<(ptr, i64)> 141 omp.parallel private(@non_const_len_char %orig_val_init -> %priv_arg : !llvm.struct<(ptr, i64)>) { 142 %dummy = llvm.extractvalue %priv_arg[0] : !llvm.struct<(ptr, i64)> 143 omp.terminator 144 } 145 llvm.return 146} 147 148omp.private {type = firstprivate} @non_const_len_char : !llvm.struct<(ptr, i64)> alloc { 149^bb0(%orig_val: !llvm.struct<(ptr, i64)>): 150 %str_len = llvm.extractvalue %orig_val[1] : !llvm.struct<(ptr, i64)> 151 %priv_alloc = llvm.alloca %str_len x i8 {bindc_name = "str", pinned} : (i64) -> !llvm.ptr 152 %priv_val = llvm.mlir.undef : !llvm.struct<(ptr, i64)> 153 %priv_val_init = llvm.insertvalue %priv_alloc, %priv_val[0] : !llvm.struct<(ptr, i64)> 154 omp.yield(%priv_val_init : !llvm.struct<(ptr, i64)>) 155} copy { 156^bb0(%orig_val: !llvm.struct<(ptr, i64)>, %priv_val: !llvm.struct<(ptr, i64)>): 157 llvm.call @foo() : () -> () 158 omp.yield(%priv_val : !llvm.struct<(ptr, i64)>) 159} 160 161llvm.func @foo() 162 163// CHECK-LABEL: @non_const_len_char_test..omp_par({{.*}}) 164// CHECK-NEXT: omp.par.entry: 165// Verify that we found the privatizer by checking that we properly inlined the 166// bodies of the alloc and copy regions. 167// CHECK: %[[STR_LEN:.*]] = extractvalue { ptr, i64 } %{{.*}}, 1 168// CHECK: %{{.*}} = alloca i8, i64 %[[STR_LEN]], align 1 169// CHECK: call void @foo() 170 171// ----- 172 173// Verifies fix for https://github.com/llvm/llvm-project/issues/102939. 174// 175// The issues occurs because the CodeExtractor component only collect inputs 176// (to the parallel regions) that are defined in the same function in which the 177// parallel regions is present. Howerver, this is problematic because if we are 178// privatizing a global value (e.g. a `target` variable which is emitted as a 179// global), then we miss finding that input and we do not privatize the 180// variable. 181 182omp.private {type = firstprivate} @global_privatizer : !llvm.ptr alloc { 183^bb0(%arg0: !llvm.ptr): 184 %0 = llvm.mlir.constant(1 : i64) : i64 185 %1 = llvm.alloca %0 x f32 {bindc_name = "global", pinned} : (i64) -> !llvm.ptr 186 omp.yield(%1 : !llvm.ptr) 187} copy { 188^bb0(%arg0: !llvm.ptr, %arg1: !llvm.ptr): 189 %0 = llvm.load %arg0 : !llvm.ptr -> f32 190 llvm.store %0, %arg1 : f32, !llvm.ptr 191 omp.yield(%arg1 : !llvm.ptr) 192} 193 194llvm.func @global_accessor() { 195 %global_addr = llvm.mlir.addressof @global : !llvm.ptr 196 omp.parallel private(@global_privatizer %global_addr -> %arg0 : !llvm.ptr) { 197 %1 = llvm.mlir.constant(3.140000e+00 : f32) : f32 198 llvm.store %1, %arg0 : f32, !llvm.ptr 199 omp.terminator 200 } 201 llvm.return 202} 203 204llvm.mlir.global internal @global() {addr_space = 0 : i32} : f32 { 205 %0 = llvm.mlir.zero : f32 206 llvm.return %0 : f32 207} 208 209// CHECK-LABEL: @global_accessor..omp_par({{.*}}) 210// CHECK-NEXT: omp.par.entry: 211// Verify that we found the privatizer by checking that we properly inlined the 212// bodies of the alloc and copy regions. 213// CHECK: %[[PRIV_ALLOC:.*]] = alloca float, i64 1, align 4 214// CHECK: %[[GLOB_VAL:.*]] = load float, ptr @global, align 4 215// CHECK: store float %[[GLOB_VAL]], ptr %[[PRIV_ALLOC]], align 4 216