xref: /llvm-project/mlir/test/Target/LLVMIR/openmp-firstprivate.mlir (revision 621fcf892bcd3c2d81e25c6ee39ca32db3c6b05a)
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