[WebAssembly] Fix asan issue from https://reviews.llvm.org/D121349
[llvm-project.git] / flang / docs / FIRArrayOperations.md
blob822bafca84eb5064002ca54e34371287ac46f20c
1 <!--===- docs/FIRArrayOperations.md 
2   
3    Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4    See https://llvm.org/LICENSE.txt for license information.
5    SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6   
7 -->
9 # Design: FIR Array operations
11 ```eval_rst
12 .. contents::
13    :local:
14 ```
16 ## General
18 The array operations in FIR model the copy-in/copy-out semantics over Fortran
19 statements.
21 Fortran language semantics sometimes require the compiler to make a temporary 
22 copy of an array or array slice. Situations where this can occur include:
24 * Passing a non-contiguous array to a procedure that does not declare it as
25   assumed-shape.
26 * Array expressions, especially those involving `RESHAPE`, `PACK`, and `MERGE`.
27 * Assignments of arrays where the array appears on both the left and right-hand
28   sides of the assignment.
29 * Assignments of `POINTER` arrays.
31 There are currently the following operations:
32 - `fir.array_load`
33 - `fir.array_merge_store`
34 - `fir.array_fetch`
35 - `fir.array_update`
36 - `fir.array_access`
37 - `fir.array_amend`
39 `array_load`(s) and `array_merge_store` are a pairing that brackets the lifetime
40 of the array copies.
42 `array_fetch` and `array_update` are defined to work as getter/setter pairs on 
43 values of elements from loaded array copies. These have "GEP-like" syntax and
44 semantics.
46 Fortran arrays are implicitly memory bound as are some other Fortran type/kind
47 entities. For entities that can be atomically promoted to the value domain,
48 we use `array_fetch` and `array_update`.
50 `array_access` and `array_amend` are defined to work as getter/setter pairs on
51 references to elements in loaded array copies. `array_access` has "GEP-like"
52 syntax. `array_amend` annotates which loaded array copy is being written to.
53 It is invalid to update an array copy without `array_amend`; doing so will
54 result in undefined behavior.
55 For those type/kinds that cannot be promoted to values, we must leave them in a
56 memory reference domain, and we use `array_access` and `array_amend`.
58 ## array_load
60 This operation taken with `array_merge_store` captures Fortran's
61 copy-in/copy-out semantics. One way to think of this is that array_load
62 creates a snapshot copy of the entire array. This copy can then be used
63 as the "original value" of the array while the array's new value is
64 computed. The `array_merge_store` operation is the copy-out semantics, which
65 merge the updates with the original array value to produce the final array
66 result. This abstracts the copy operations as opposed to always creating
67 copies or requiring dependence analysis be performed on the syntax trees
68 and before lowering to the IR.
70 Load an entire array as a single SSA value.
72 ```fortran
73   real :: a(o:n,p:m)
74   ...
75   ... = ... a ...
76 ```
78 One can use `fir.array_load` to produce an ssa-value that captures an
79 immutable value of the entire array `a`, as in the Fortran array expression
80 shown above. Subsequent changes to the memory containing the array do not
81 alter its composite value. This operation lets one load an array as a
82 value while applying a runtime shape, shift, or slice to the memory
83 reference, and its semantics guarantee immutability.
85 ```mlir
86 %s = fir.shape_shift %lb1, %ex1, %lb2, %ex2 : (index, index, index, index) -> !fir.shape<2>
87 // load the entire array 'a'
88 %v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32>
89 // a fir.store here into array %a does not change %v
90 ```
92 # array_merge_store
94 The `array_merge_store` operation stores a merged array value to memory. 
97 ```fortran
98   real :: a(n,m)
99   ...
100   a = ...
103 One can use `fir.array_merge_store` to merge/copy the value of `a` in an
104 array expression as shown above.
106 ```mlir
107   %v = fir.array_load %a(%shape) : ...
108   %r = fir.array_update %v, %f, %i, %j : (!fir.array<?x?xf32>, f32, index, index) -> !fir.array<?x?xf32>
109   fir.array_merge_store %v, %r to %a : !fir.ref<!fir.array<?x?xf32>>
112 This operation merges the original loaded array value, `%v`, with the
113 chained updates, `%r`, and stores the result to the array at address, `%a`.
115 This operation taken with `array_load`'s captures Fortran's
116 copy-in/copy-out semantics. The first operands of `array_merge_store` is the
117 result of the initial `array_load` operation. While this value could be
118 retrieved by reference chasiing through the different array operations it is
119 useful to have it on hand directly for analysis passes since this directly
120 defines the "bounds" of the Fortran statement represented by these operations.
121 The intention is to allow copy-in/copy-out regions to be easily delineated,
122 analyzed, and optimized.
124 ## array_fetch
126 The `array_fetch` operation fetches the value of an element in an array value.
128 ```fortran
129   real :: a(n,m)
130   ...
131   ... a ...
132   ... a(r,s+1) ...
135 One can use `fir.array_fetch` to fetch the (implied) value of `a(i,j)` in
136 an array expression as shown above. It can also be used to extract the
137 element `a(r,s+1)` in the second expression.
139 ```mlir
140   %s = fir.shape %n, %m : (index, index) -> !fir.shape<2>
141   // load the entire array 'a'
142   %v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32>
143   // fetch the value of one of the array value's elements
144   %1 = fir.array_fetch %v, %i, %j : (!fir.array<?x?xf32>, index, index) -> f32
147 It is only possible to use `array_fetch` on an `array_load` result value or a
148 value that can be trace back transitively to an `array_load` as the dominating
149 source. Other array operation such as `array_update` can be in between.
151 ## array_update
153 The `array_update` operation is used to update the value of an element in an
154 array value. A new array value is returned where all element values of the input
155 array are identical except for the selected element which is the value passed in
156 the update.
158 ```fortran
159   real :: a(n,m)
160   ...
161   a = ...
164 One can use `fir.array_update` to update the (implied) value of `a(i,j)`
165 in an array expression as shown above.
167 ```mlir
168   %s = fir.shape %n, %m : (index, index) -> !fir.shape<2>
169   // load the entire array 'a'
170   %v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32>
171   // update the value of one of the array value's elements
172   // %r_{ij} = %f  if (i,j) = (%i,%j),   %v_{ij} otherwise
173   %r = fir.array_update %v, %f, %i, %j : (!fir.array<?x?xf32>, f32, index, index) -> !fir.array<?x?xf32>
174   fir.array_merge_store %v, %r to %a : !fir.ref<!fir.array<?x?xf32>>
177 An array value update behaves as if a mapping function from the indices
178 to the new value has been added, replacing the previous mapping. These
179 mappings can be added to the ssa-value, but will not be materialized in
180 memory until the `fir.array_merge_store` is performed.
181 `fir.array_update` can be seen as an array access with a notion that the array
182 will be changed at the accessed position when `fir.array_merge_store` is
183 performed.
185 ## array_access
187 The `array_access` provides a reference to a single element from an array value.
188 This is *not* a view in the immutable array, otherwise it couldn't be stored to.
189 It can be see as a logical copy of the element and its position in the array.
190 Tis reference can be written to and modified withoiut changing the original
191 array.
193 The `array_access` operation is used to fetch the memory reference of an element
194 in an array value.
196 ```fortran
197   real :: a(n,m)
198   ...
199   ... a ...
200   ... a(r,s+1) ...
203 One can use `fir.array_access` to recover the implied memory reference to
204 the element `a(i,j)` in an array expression `a` as shown above. It can also
205 be used to recover the reference element `a(r,s+1)` in the second
206 expression.
208 ```mlir
209   %s = fir.shape %n, %m : (index, index) -> !fir.shape<2>
210   // load the entire array 'a'
211   %v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32>
212   // fetch the value of one of the array value's elements
213   %1 = fir.array_access %v, %i, %j : (!fir.array<?x?xf32>, index, index) -> !fir.ref<f32>
216 It is only possible to use `array_access` on an `array_load` result value or a
217 value that can be trace back transitively to an `array_load` as the dominating
218 source. Other array operation such as `array_amend` can be in between.
220 `array_access` if mainly used with `character`'s arrays and arrays of derived
221 types where because they might have a non-compile time sizes that would be
222 useless too load entirely or too big to load. 
224 Here is a simple example with a `character` array assignment. 
226 Fortran
228 subroutine foo(c1, c2, n)
229   integer(8) :: n
230   character(n) :: c1(:), c2(:)
231   c1 = c2
232 end subroutine
235 It results in this cleaned-up FIR:
237 func @_QPfoo(%arg0: !fir.box<!fir.array<?x!fir.char<1,?>>>, %arg1: !fir.box<!fir.array<?x!fir.char<1,?>>>, %arg2: !fir.ref<i64>) {
238     %0 = fir.load %arg2 : !fir.ref<i64>
239     %c0 = arith.constant 0 : index
240     %1:3 = fir.box_dims %arg0, %c0 : (!fir.box<!fir.array<?x!fir.char<1,?>>>, index) -> (index, index, index)
241     %2 = fir.array_load %arg0 : (!fir.box<!fir.array<?x!fir.char<1,?>>>) -> !fir.array<?x!fir.char<1,?>>
242     %3 = fir.array_load %arg1 : (!fir.box<!fir.array<?x!fir.char<1,?>>>) -> !fir.array<?x!fir.char<1,?>>
243     %c1 = arith.constant 1 : index
244     %4 = arith.subi %1#1, %c1 : index
245     %5 = fir.do_loop %arg3 = %c0 to %4 step %c1 unordered iter_args(%arg4 = %2) -> (!fir.array<?x!fir.char<1,?>>) {
246       %6 = fir.array_access %3, %arg3 : (!fir.array<?x!fir.char<1,?>>, index) -> !fir.ref<!fir.char<1,?>>
247       %7 = fir.array_access %arg4, %arg3 : (!fir.array<?x!fir.char<1,?>>, index) -> !fir.ref<!fir.char<1,?>>
248       %false = arith.constant false
249       %8 = fir.convert %7 : (!fir.ref<!fir.char<1,?>>) -> !fir.ref<i8>
250       %9 = fir.convert %6 : (!fir.ref<!fir.char<1,?>>) -> !fir.ref<i8>
251       fir.call @llvm.memmove.p0i8.p0i8.i64(%8, %9, %0, %false) : (!fir.ref<i8>, !fir.ref<i8>, i64, i1) -> ()
252       %10 = fir.array_amend %arg4, %7 : (!fir.array<?x!fir.char<1,?>>, !fir.ref<!fir.char<1,?>>) -> !fir.array<?x!fir.char<1,?>>
253       fir.result %10 : !fir.array<?x!fir.char<1,?>>
254     }
255     fir.array_merge_store %2, %5 to %arg0 : !fir.array<?x!fir.char<1,?>>, !fir.array<?x!fir.char<1,?>>, !fir.box<!fir.array<?x!fir.char<1,?>>>
256     return
257   }
258   func private @llvm.memmove.p0i8.p0i8.i64(!fir.ref<i8>, !fir.ref<i8>, i64, i1)
262 `fir.array_access` and `fir.array_amend` split the two purposes of
263 `fir.array_update` into two distinct operations to work on type/kind that must
264 reside in the memory reference domain. `fir.array_access` captures the array
265 access semantics and `fir.array_amend` denotes which `fir.array_access` is the
266 lhs.
268 We do not want to start loading the entire `!fir.ref<!fir.char<1,?>>` here since
269 it has dynamic length, and even if constant, could be too long to do so.
271 ## array_amend
273 The `array_amend` operation marks an array value as having been changed via a 
274 reference obtain by an `array_access`. It acts as a logical transaction log
275 that is used to merge the final result back with an `array_merge_store`
276 operation.
278 ```mlir
279   // fetch the value of one of the array value's elements
280   %1 = fir.array_access %v, %i, %j : (!fir.array<?x?xT>, index, index) -> !fir.ref<T>
281   // modify the element by storing data using %1 as a reference
282   %2 = ... %1 ...
283   // mark the array value
284   %new_v = fir.array_amend %v, %2 : (!fir.array<?x?xT>, !fir.ref<T>) -> !fir.array<?x?xT>
287 ## Example
289 Here is an example of a FIR code using several array operations together. The
290 example below is a simplified version of the FIR code comiing from the
291 following Fortran code snippet.
293 ```fortran
294 subroutine s(a,l,u)
295   type t
296     integer m
297   end type t
298   type(t) :: a(:)
299   integer :: l, u
300   forall (i=l:u)
301     a(i) = a(u-i+1)
302   end forall
307 func @_QPs(%arg0: !fir.box<!fir.array<?x!fir.type<_QFsTt{m:i32}>>>, %arg1: !fir.ref<i32>, %arg2: !fir.ref<i32>) {
308   %l = fir.load %arg1 : !fir.ref<i32>
309   %l_index = fir.convert %l : (i32) -> index
310   %u = fir.load %arg2 : !fir.ref<i32>
311   %u_index = fir.convert %u : (i32) -> index
312   %c1 = arith.constant 1 : index
313   // This is the "copy-in" array used on the RHS of the expression. It will be indexed into and loaded at each iteration.
314   %array_a_src = fir.array_load %arg0 : (!fir.box<!fir.array<?x!fir.type<_QFsTt{m:i32}>>>) -> !fir.array<?x!fir.type<_QFsTt{m:i32}>>
316   // This is the "seed" for the "copy-out" array on the LHS. It'll flow from iteration to iteration and gets
317   // updated at each iteration.
318   %array_a_dest_init = fir.array_load %arg0 : (!fir.box<!fir.array<?x!fir.type<_QFsTt{m:i32}>>>) -> !fir.array<?x!fir.type<_QFsTt{m:i32}>>
319   
320   %array_a_final = fir.do_loop %i = %l_index to %u_index step %c1 unordered iter_args(%array_a_dest = %array_a_dest_init) -> (!fir.array<?x!fir.type<_QFsTt{m:i32}>>) {
321     // Compute indexing for the RHS and array the element.
322     %u_minus_i = arith.subi %u_index, %i : index // u-i
323     %u_minus_i_plus_one = arith.addi %u_minus_i, %c1: index // u-i+1
324     %a_src_ref = fir.array_access %array_a_src, %u_minus_i_plus_one {Fortran.offsets} : (!fir.array<?x!fir.type<_QFsTt{m:i32}>>, index) -> !fir.ref<!fir.type<_QFsTt{m:i32}>>
325     %a_src_elt = fir.load %a_src_ref : !fir.ref<!fir.type<_QFsTt{m:i32}>>
327     // Get the reference to the element in the array on the LHS
328     %a_dst_ref = fir.array_access %array_a_dest, %i {Fortran.offsets} : (!fir.array<?x!fir.type<_QFsTt{m:i32}>>, index) -> !fir.ref<!fir.type<_QFsTt{m:i32}>>
330     // Store the value, and update the array
331     fir.store %a_src_elt to %a_dst_ref : !fir.ref<!fir.type<_QFsTt{m:i32}>>
332     %updated_array_a = fir.array_amend %array_a_dest, %a_dst_ref : (!fir.array<?x!fir.type<_QFsTt{m:i32}>>, !fir.ref<!fir.type<_QFsTt{m:i32}>>) -> !fir.array<?x!fir.type<_QFsTt{m:i32}>>
334     // Forward the current updated array to the next iteration.
335     fir.result %updated_array_a : !fir.array<?x!fir.type<_QFsTt{m:i32}>>
336   }
337   // Store back the result by merging the initial value loaded before the loop
338   // with the final one produced by the loop.
339   fir.array_merge_store %array_a_dest_init, %array_a_final to %arg0 : !fir.array<?x!fir.type<_QFsTt{m:i32}>>, !fir.array<?x!fir.type<_QFsTt{m:i32}>>, !fir.box<!fir.array<?x!fir.type<_QFsTt{m:i32}>>>
340   return