mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-19 13:26:45 +00:00
[Documentation][NFC] Remove invalid language specifiers in markdown code blocks
This commit is contained in:
parent
2bed2a7a5c
commit
c226d6c88f
@ -35,7 +35,7 @@ end function pow_self
|
||||
```
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
func.func @_QPpow_self(%arg0: !fir.ref<!fir.complex<4>>) -> !fir.complex<4> {
|
||||
%0 = fir.alloca !fir.complex<4>
|
||||
%1 = fir.load %arg0 : !fir.ref<!fir.complex<4>>
|
||||
|
@ -1,9 +1,9 @@
|
||||
<!--===- docs/FIRArrayOperations.md
|
||||
|
||||
<!--===- docs/FIRArrayOperations.md
|
||||
|
||||
Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
See https://llvm.org/LICENSE.txt for license information.
|
||||
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
|
||||
-->
|
||||
|
||||
# Design: FIR Array operations
|
||||
@ -19,7 +19,7 @@ local:
|
||||
The array operations in FIR model the copy-in/copy-out semantics over Fortran
|
||||
statements.
|
||||
|
||||
Fortran language semantics sometimes require the compiler to make a temporary
|
||||
Fortran language semantics sometimes require the compiler to make a temporary
|
||||
copy of an array or array slice. Situations where this can occur include:
|
||||
|
||||
* Passing a non-contiguous array to a procedure that does not declare it as
|
||||
@ -40,7 +40,7 @@ There are currently the following operations:
|
||||
`array_load`(s) and `array_merge_store` are a pairing that brackets the lifetime
|
||||
of the array copies.
|
||||
|
||||
`array_fetch` and `array_update` are defined to work as getter/setter pairs on
|
||||
`array_fetch` and `array_update` are defined to work as getter/setter pairs on
|
||||
values of elements from loaded array copies. These have "GEP-like" syntax and
|
||||
semantics.
|
||||
|
||||
@ -83,7 +83,7 @@ alter its composite value. This operation lets one load an array as a
|
||||
value while applying a runtime shape, shift, or slice to the memory
|
||||
reference, and its semantics guarantee immutability.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%s = fir.shape_shift %lb1, %ex1, %lb2, %ex2 : (index, index, index, index) -> !fir.shapeshift<2>
|
||||
// load the entire array 'a'
|
||||
%v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shapeshift<2>) -> !fir.array<?x?xf32>
|
||||
@ -92,7 +92,7 @@ reference, and its semantics guarantee immutability.
|
||||
|
||||
# array_merge_store
|
||||
|
||||
The `array_merge_store` operation stores a merged array value to memory.
|
||||
The `array_merge_store` operation stores a merged array value to memory.
|
||||
|
||||
|
||||
```fortran
|
||||
@ -104,7 +104,7 @@ The `array_merge_store` operation stores a merged array value to memory.
|
||||
One can use `fir.array_merge_store` to merge/copy the value of `a` in an
|
||||
array expression as shown above.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%v = fir.array_load %a(%shape) : ...
|
||||
%r = fir.array_update %v, %f, %i, %j : (!fir.array<?x?xf32>, f32, index, index) -> !fir.array<?x?xf32>
|
||||
fir.array_merge_store %v, %r to %a : !fir.ref<!fir.array<?x?xf32>>
|
||||
@ -137,7 +137,7 @@ One can use `fir.array_fetch` to fetch the (implied) value of `a(i,j)` in
|
||||
an array expression as shown above. It can also be used to extract the
|
||||
element `a(r,s+1)` in the second expression.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%s = fir.shape %n, %m : (index, index) -> !fir.shape<2>
|
||||
// load the entire array 'a'
|
||||
%v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32>
|
||||
@ -165,7 +165,7 @@ the update.
|
||||
One can use `fir.array_update` to update the (implied) value of `a(i,j)`
|
||||
in an array expression as shown above.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%s = fir.shape %n, %m : (index, index) -> !fir.shape<2>
|
||||
// load the entire array 'a'
|
||||
%v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32>
|
||||
@ -206,7 +206,7 @@ the element `a(i,j)` in an array expression `a` as shown above. It can also
|
||||
be used to recover the reference element `a(r,s+1)` in the second
|
||||
expression.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%s = fir.shape %n, %m : (index, index) -> !fir.shape<2>
|
||||
// load the entire array 'a'
|
||||
%v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32>
|
||||
@ -220,9 +220,9 @@ source. Other array operation such as `array_amend` can be in between.
|
||||
|
||||
`array_access` if mainly used with `character`'s arrays and arrays of derived
|
||||
types where because they might have a non-compile time sizes that would be
|
||||
useless too load entirely or too big to load.
|
||||
useless too load entirely or too big to load.
|
||||
|
||||
Here is a simple example with a `character` array assignment.
|
||||
Here is a simple example with a `character` array assignment.
|
||||
|
||||
Fortran
|
||||
```
|
||||
@ -271,12 +271,12 @@ it has dynamic length, and even if constant, could be too long to do so.
|
||||
|
||||
## array_amend
|
||||
|
||||
The `array_amend` operation marks an array value as having been changed via a
|
||||
The `array_amend` operation marks an array value as having been changed via a
|
||||
reference obtain by an `array_access`. It acts as a logical transaction log
|
||||
that is used to merge the final result back with an `array_merge_store`
|
||||
operation.
|
||||
|
||||
```mlir
|
||||
```
|
||||
// fetch the value of one of the array value's elements
|
||||
%1 = fir.array_access %v, %i, %j : (!fir.array<?x?xT>, index, index) -> !fir.ref<T>
|
||||
// modify the element by storing data using %1 as a reference
|
||||
@ -317,7 +317,7 @@ func @_QPs(%arg0: !fir.box<!fir.array<?x!fir.type<_QFsTt{m:i32}>>>, %arg1: !fir.
|
||||
// This is the "seed" for the "copy-out" array on the LHS. It'll flow from iteration to iteration and gets
|
||||
// updated at each iteration.
|
||||
%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}>>
|
||||
|
||||
|
||||
%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}>>) {
|
||||
// Compute indexing for the RHS and array the element.
|
||||
%u_minus_i = arith.subi %u_index, %i : index // u-i
|
||||
|
@ -119,7 +119,7 @@ its first operand to return an HLFIR variable compatible type.
|
||||
The fir.declare op is the only operation described by this change that will be
|
||||
added to FIR. The rational for this is that it is intended to survive until
|
||||
LLVM dialect codegeneration so that debug info generation can use them and
|
||||
alias information can take advantage of them even on FIR.
|
||||
alias information can take advantage of them even on FIR.
|
||||
|
||||
Note that Fortran variables are not necessarily named objects, they can also be
|
||||
the result of function references returning POINTERs. hlfir.declare will also
|
||||
@ -1021,7 +1021,7 @@ end subroutine
|
||||
|
||||
Lowering output:
|
||||
|
||||
```HLFIR
|
||||
```
|
||||
func.func @_QPfoo(%arg0: !fir.box<!fir.array<?xf32>>, %arg1: !fir.box<!fir.array<?xf32>>) {
|
||||
%a = hlfir.declare %arg0 {fir.def = "_QPfooEa"} : !fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>
|
||||
%b = hlfir.declare %arg1 {fir.def = "_QPfooEb"} : !fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>
|
||||
@ -1107,7 +1107,7 @@ end subroutine
|
||||
|
||||
Lowering output:
|
||||
|
||||
```HLFIR
|
||||
```
|
||||
func.func @_QPfoo(%arg0: !fir.box<!fir.array<?xf32>>, %arg1: !fir.box<!fir.array<?xf32>>, %arg2: !fir.box<!fir.ptr<!fir.array<?xf32>>>, %arg3: !fir.ref<!fir.array<100xf32>>) {
|
||||
%a = hlfir.declare %arg0 {fir.def = "_QPfooEa"} {fir.target} : !fir.box<!fir.array<?xf32>, !fir.box<!fir.array<?xf32>
|
||||
%b = hlfir.declare %arg1 {fir.def = "_QPfooEb"} : !fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>
|
||||
@ -1145,7 +1145,7 @@ Step 1: hlfir.elemental inlining: inline the first hlfir.elemental into the
|
||||
second one at the hlfir.apply.
|
||||
|
||||
|
||||
```HLFIR
|
||||
```
|
||||
func.func @_QPfoo(%arg0: !fir.box<!fir.array<?xf32>>, %arg1: !fir.box<!fir.array<?xf32>>, %arg2: !fir.box<!fir.ptr<!fir.array<?xf32>>>, %arg3: !fir.ref<!fir.array<100xf32>>) {
|
||||
%a = hlfir.declare %arg0 {fir.def = "_QPfooEa"} {fir.target} : !fir.box<!fir.array<?xf32>, !fir.box<!fir.array<?xf32>
|
||||
%b = hlfir.declare %arg1 {fir.def = "_QPfooEb"} : !fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>
|
||||
@ -1190,7 +1190,7 @@ Note that the alias analysis could have already occurred without inlining the
|
||||
%add hlfir.elemental.
|
||||
|
||||
|
||||
```HLFIR
|
||||
```
|
||||
func.func @_QPfoo(%arg0: !fir.box<!fir.array<?xf32>>, %arg1: !fir.box<!fir.array<?xf32>>, %arg2: !fir.box<!fir.ptr<!fir.array<?xf32>>>, %arg3: !fir.ref<!fir.array<100xf32>>) {
|
||||
%a = hlfir.declare %arg0 {fir.def = "_QPfooEa"} {fir.target} : !fir.box<!fir.array<?xf32>, !fir.box<!fir.array<?xf32>
|
||||
%b = hlfir.declare %arg1 {fir.def = "_QPfooEb"} : !fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>
|
||||
@ -1229,7 +1229,7 @@ func.func @_QPfoo(%arg0: !fir.box<!fir.array<?xf32>>, %arg1: !fir.box<!fir.array
|
||||
Step 4: Lower assignments to regular loops since they have the no_overlap
|
||||
attribute, and inline the hlfir.elemental into the first loop nest.
|
||||
|
||||
```HLFIR
|
||||
```
|
||||
func.func @_QPfoo(%arg0: !fir.box<!fir.array<?xf32>>, %arg1: !fir.box<!fir.array<?xf32>>, %arg2: !fir.box<!fir.ptr<!fir.array<?xf32>>>, %arg3: !fir.ref<!fir.array<100xf32>>) {
|
||||
%a = hlfir.declare %arg0 {fir.def = "_QPfooEa"} {fir.target} : !fir.box<!fir.array<?xf32>, !fir.box<!fir.array<?xf32>
|
||||
%b = hlfir.declare %arg1 {fir.def = "_QPfooEb"} : !fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>
|
||||
@ -1275,7 +1275,7 @@ Step 5 (may also occur earlier or several times): shape propagation.
|
||||
conformance checks can be added for %a, %b and %p.
|
||||
- %temp is small, and its fir.allocmem can be promoted to a stack allocation
|
||||
|
||||
```HLFIR
|
||||
```
|
||||
func.func @_QPfoo(%arg0: !fir.box<!fir.array<?xf32>>, %arg1: !fir.box<!fir.array<?xf32>>, %arg2: !fir.box<!fir.ptr<!fir.array<?xf32>>>, %arg3: !fir.ref<!fir.array<100xf32>>) {
|
||||
// .....
|
||||
%cshape = fir.shape %c100
|
||||
@ -1366,7 +1366,7 @@ the MLIR infrastructure experience that was gained.
|
||||
|
||||
## Using symbols for HLFIR variables
|
||||
|
||||
### Using attributes as pseudo variable symbols
|
||||
### Using attributes as pseudo variable symbols
|
||||
|
||||
Instead of restricting the memory types an HLFIR variable can have, it was
|
||||
force the defining operation of HLFIR variable SSA values to always be
|
||||
|
@ -96,7 +96,7 @@ In case of `len_type1`, the size, offset, etc. of `fld1` and `fld2` depend on
|
||||
the runtime values of `i` and `j` when the components are inlined into the
|
||||
derived type. At runtime, this information needs to be computed to be retrieved.
|
||||
While lowering the PDT, compiler generated functions can be created in order to
|
||||
compute this information.
|
||||
compute this information.
|
||||
|
||||
Note: The type description tables generated by semantics and used throughout the
|
||||
runtime have component offsets as constants. Inlining component would require
|
||||
@ -127,7 +127,7 @@ type len_type2(i, j)
|
||||
! allocatable components managed by the compiler. The
|
||||
! `compiler_managed_allocatable` is not a proper keyword but just added here
|
||||
! to have a better understanding.
|
||||
character(i+j), compiler_managed_allocatable :: fld1
|
||||
character(i+j), compiler_managed_allocatable :: fld1
|
||||
character(j-i+2), compiler_managed_allocatable :: fld2
|
||||
end type
|
||||
```
|
||||
@ -266,7 +266,7 @@ the offset of `fld1` in `len_type1` could be 0; its size would be computed as
|
||||
function.
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
// Example of compiler generated functions to compute offsets, size, etc.
|
||||
// This is just an example and actual implementation might have more functions.
|
||||
|
||||
@ -337,7 +337,7 @@ pdt_inlined_array(1)%field%fld2
|
||||
```
|
||||
|
||||
Example of offset computation in the PDTs.
|
||||
```c
|
||||
```
|
||||
%0 = call @_len_type3.field.typeparam.1(%i, %j) : (index, index) -> index
|
||||
%1 = call @_len_type3.field.typeparam.2(%i, %j) : (index, index) -> index
|
||||
%2 = call @_len_type3.offset.fld(%i, %j) : (index, index) -> index
|
||||
@ -367,7 +367,7 @@ operation can take the length type parameters needed for size/offset
|
||||
computation.
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
%5 = fir.field_index i, !fir.type<_QMmod1Tt{l:i32,i:!fir.array<?xi32>}>(%n : i32)
|
||||
```
|
||||
|
||||
@ -431,7 +431,7 @@ allocate(t1(2)::p)
|
||||
```
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
// For allocatable
|
||||
%5 = fir.call @_FortranAAllocatableInitDerived(%desc, %type) : (!fir.box<none>, ) -> ()
|
||||
// The AllocatableSetDerivedLength functions is called for each length type parameters.
|
||||
@ -455,8 +455,8 @@ The `DEALLOCATE` statement is lowered to a runtime call to
|
||||
deallocate(pdt1)
|
||||
```
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
**FIR**
|
||||
```
|
||||
// For allocatable
|
||||
%8 = fir.call @_FortranAAllocatableDeallocate(%desc1) : (!fir.box<none>) -> (i32)
|
||||
|
||||
@ -475,7 +475,7 @@ NULLIFY(p)
|
||||
```
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
%0 = fir.call @_FortranAPointerNullifyDerived(%desc, %type) : (!fir.box<none>, !fir.tdesc) -> ()
|
||||
```
|
||||
|
||||
@ -507,11 +507,11 @@ end subroutine
|
||||
```
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
func.func @_QMpdtPprint_pdt() {
|
||||
%l = arith.constant = 10
|
||||
%0 = fir.alloca !fir.type<_QMpdtTt{l:i32,i:!fir.array<?xi32>}> (%l : i32) {bindc_name = "x", uniq_name = "_QMpdt_initFlocalEx"}
|
||||
%1 = fir.embox %0 : (!fir.ref<!fir.type<_QMpdtTt{l:i32,i:!fir.array<?xi32>}>>) (typeparams %l : i32) -> !fir.box<!fir.type<_QMpdt_initTt{l:i32,i:!fir.array<2xi32>}>>
|
||||
%1 = fir.embox %0 : (!fir.ref<!fir.type<_QMpdtTt{l:i32,i:!fir.array<?xi32>}>>) (typeparams %l : i32) -> !fir.box<!fir.type<_QMpdt_initTt{l:i32,i:!fir.array<2xi32>}>>
|
||||
%2 = fir.address_of(@_QQcl.2E2F6669725F7064745F6578616D706C652E66393000) : !fir.ref<!fir.char<1,22>>
|
||||
%c8_i32 = arith.constant 8 : i32
|
||||
%3 = fir.convert %1 : (!fir.box<!fir.type<_QMpdtTt{l:i32,i:!fir.array<?xi32>}>>) -> !fir.box<none>
|
||||
@ -564,7 +564,7 @@ type(t(10)) :: a, b
|
||||
type(t(20)) :: c
|
||||
type(t(:)), allocatable :: d
|
||||
a = b ! Legal assignment
|
||||
c = b ! Illegal assignment because `c` does not have the same length type
|
||||
c = b ! Illegal assignment because `c` does not have the same length type
|
||||
! parameter value than `b`.
|
||||
d = c ! Legal because `d` is allocatable
|
||||
```
|
||||
@ -607,12 +607,12 @@ module m
|
||||
end type
|
||||
|
||||
contains
|
||||
|
||||
|
||||
subroutine finalize_t1s(x)
|
||||
type(t(kind(0.0))) x
|
||||
if (associated(x%vector)) deallocate(x%vector)
|
||||
END subroutine
|
||||
|
||||
|
||||
subroutine finalize_t1v(x)
|
||||
type(t(kind(0.0))) x(:)
|
||||
do i = lbound(x,1), ubound(x,1)
|
||||
@ -832,7 +832,7 @@ allocating a PDT, the length type parameters are passed to the
|
||||
operation so its size can be computed accordingly.
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
%i = arith.constant 10 : i32
|
||||
%0 = fir.alloca !fir.type<_QMmod1Tpdt{i:i32,data:!fir.array<?xf32>}> (%i : i32)
|
||||
// %i is the ssa value of the length type parameter
|
||||
@ -845,7 +845,7 @@ value of the given type. When creating a PDT, the length type parameters are
|
||||
passed so the size can be computed accordingly.
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
%i = arith.constant 10 : i32
|
||||
%0 = fir.alloca !fir.type<_QMmod1Tpdt{i:i32,data:!fir.array<?xf32>}> (%i : i32)
|
||||
// ...
|
||||
@ -866,7 +866,7 @@ end subroutine
|
||||
```
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
func.func @_QMpdt_initPlocal() {
|
||||
%c2_i32 = arith.constant 2 : i32
|
||||
%0 = fir.alloca !fir.type<_QMpdt_initTt{l:i32,i:!fir.array<?xi32>}> (%c2 : i32)
|
||||
@ -892,7 +892,7 @@ a field identifier in a derived-type. The operation takes length type parameter
|
||||
values with a PDT so it can compute a correct offset.
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
%l = arith.constant 10 : i32
|
||||
%1 = fir.field_index i, !fir.type<_QMpdt_initTt{l:i32,i:i32}> (%l : i32)
|
||||
%2 = fir.coordinate_of %ref, %1 : (!fir.type<_QMpdt_initTt{l:i32,i:i32}>, !fir.field) -> !fir.ref<i32>
|
||||
@ -905,7 +905,7 @@ return %3
|
||||
This operation is used to get the length type parameter offset in from a PDT.
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
func.func @_QPpdt_len_value(%arg0: !fir.box<!fir.type<t1{l:i32,!fir.array<?xi32>}>>) -> i32 {
|
||||
%0 = fir.len_param_index l, !fir.box<!fir.type<t1{l:i32,!fir.array<?xi32>}>>
|
||||
%1 = fir.coordinate_of %arg0, %0 : (!fir.box<!fir.type<t1{l:i32,!fir.array<?xi32>}>>, !fir.len) -> !fir.ref<i32>
|
||||
@ -921,7 +921,7 @@ a memory location given the shape and LEN parameters of the result. Length type
|
||||
parameters is passed if the PDT is not boxed.
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
func.func @return_pdt(%buffer: !fir.ref<!fir.type<t2(l1:i32,l2:i32){x:f32}>>) {
|
||||
%l1 = arith.constant 3 : i32
|
||||
%l2 = arith.constant 5 : i32
|
||||
@ -938,7 +938,7 @@ parameters operands. This is designed to use PDT without descriptor directly in
|
||||
FIR.
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
// Operation used with a boxed PDT does not need the length type parameters as
|
||||
// they are directly retrieved from the box.
|
||||
%0 = fir.array_coor %boxed_pdt, %i, %j (fir.box<fir.array<?x?xfir.type<!fir.type<_QMpdt_initTt{l:i32,i:!fir.array<?xi32>}>>>>, index, index) -> !fir.ref<fir.type<!fir.type<_QMpdt_initTt{l:i32,i:!fir.array<?xi32>}>>>
|
||||
|
@ -75,7 +75,7 @@ end subroutine
|
||||
```
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
func.func @foo(%p : !fir.class<!fir.type<_QTpoint{x:f32,y:f32}>>)
|
||||
```
|
||||
|
||||
@ -93,7 +93,7 @@ end subroutine
|
||||
```
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
func.func @bar(%x : !fir.class<none>)
|
||||
```
|
||||
|
||||
@ -437,7 +437,7 @@ get_all_area = get_all_area + shapes(i)%item%get_area()
|
||||
```
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
%1 = fir.convert %0 : !fir.ref<!fir.class<!fir.type<_QMgeometryTtriangle{color:i32,isFilled:!fir.logical<4>,base:f32,height:f32>>>
|
||||
%2 = fir.dispatch "get_area"(%1 : !fir.class<!fir.type<_QMgeometryTtriangle{color:i32,isFilled:!fir.logical<4>,base:f32,height:f32>>) -> f32
|
||||
```
|
||||
@ -451,7 +451,7 @@ its ancestor types appear first.
|
||||
**LLVMIR**
|
||||
|
||||
Representation of the derived type information with the bindings.
|
||||
```c
|
||||
```
|
||||
%_QM__fortran_type_infoTderivedtype = type { { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, { ptr, i64, i32, i8, i8, i8, i8 }, i64, { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, i32, i8, i8, i8, i8, [4 x i8] }
|
||||
%_QM__fortran_type_infoTbinding = type { %_QM__fortran_builtinsT__builtin_c_funptr, { ptr, i64, i32, i8, i8, i8, i8 } }
|
||||
%_QM__fortran_builtinsT__builtin_c_funptr = type { i64 }
|
||||
@ -463,7 +463,7 @@ correct function from the vtable and to perform the actual call. Here is
|
||||
what it can look like in pseudo LLVM IR code.
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
%2 = fir.box_tdesc %arg0 : (!fir.class<!fir.type<_QMgeometryTtriangle{color:i32,isFilled:!fir.logical<4>,base:f32,height:f32>>) -> !fir.tdesc<none>
|
||||
%3 = fir.box_tdesc %arg0 : (!fir.class<!fir.type<_QMdispatch1Tp1{a:i32,b:i32}>>) -> !fir.tdesc<none>
|
||||
%4 = fir.convert %3 : (!fir.tdesc<none>) -> !fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype{}>>
|
||||
@ -483,7 +483,7 @@ what it can look like in pseudo LLVM IR code.
|
||||
```
|
||||
|
||||
**LLVMIR**
|
||||
```c
|
||||
```
|
||||
// Retrieve the derived type runtime information and the vtable.
|
||||
%14 = getelementptr %_QM__fortran_type_infoTderivedtype, ptr %13, i32 0, i32 0
|
||||
%15 = load { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %14
|
||||
@ -516,7 +516,7 @@ END TYPE
|
||||
2) Dummy argument is polymorphic and actual argument is fixed type. In these
|
||||
cases, the actual argument need to be boxed to be passed to the
|
||||
subroutine/function since those are expecting a descriptor.
|
||||
```c
|
||||
```
|
||||
func.func @_QMmod1Ps(%arg0: !fir.class<!fir.type<_QMmod1Tshape{x:i32,y:i32}>>)
|
||||
func.func @_QQmain() {
|
||||
%0 = fir.alloca !fir.type<_QMmod1Tshape{x:i32,y:i32}> {uniq_name = "_QFEsh"}
|
||||
@ -584,7 +584,7 @@ write(10), x
|
||||
```
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
%5 = fir.call @_FortranAioBeginUnformattedOutput(%c10_i32, %4, %c56_i32) : (i32, !fir.ref<i8>, i32) -> !fir.ref<i8>
|
||||
%6 = fir.embox %2 : (!fir.ref<!fir.type<_QTt>>) -> !fir.class<!fir.type<_QTt>>
|
||||
%7 = fir.convert %6 : (!fir.class<!fir.type<_QTt>>) -> !fir.box<none>
|
||||
@ -608,7 +608,7 @@ finalization with a call the the `@_FortranADestroy` function
|
||||
(`flang/include/flang/Runtime/derived-api.h`).
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
%5 = fir.call @_FortranADestroy(%desc) : (!fir.box<none>) -> none
|
||||
```
|
||||
|
||||
@ -671,7 +671,7 @@ end subroutine
|
||||
**FIR**
|
||||
|
||||
The call to `get_area` in the `type is (triangle)` guard can be replaced.
|
||||
```c
|
||||
```
|
||||
%3 = fir.dispatch "get_area"(%desc)
|
||||
// Replaced by
|
||||
%3 = fir.call @get_area_triangle(%desc)
|
||||
@ -716,7 +716,7 @@ allocate(rectangle::shapes(2)%item)
|
||||
```
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
%0 = fir.address_of(@_QMgeometryE.dt.triangle) : !fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype>>
|
||||
%1 = fir.convert %item1 : (!fir.ref<!fir.class<!fir.type<_QMgeometryTtriangle{color:i32,isFilled:!fir.logical<4>,base:f32,height:f32>>>) -> !fir.ref<!fir.box<none>>
|
||||
%2 = fir.call @_FortranAAllocatableInitDerived(%1, %0)
|
||||
@ -742,7 +742,7 @@ deallocate(shapes(2)%item)
|
||||
```
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
%8 = fir.call @_FortranAAllocatableDeallocate(%desc1)
|
||||
%9 = fir.call @_FortranAAllocatableDeallocate(%desc2)
|
||||
```
|
||||
@ -849,7 +849,7 @@ dynamic type of polymorphic entities.
|
||||
dynamic type of `a` must be copied when it is associated on `b`.
|
||||
|
||||
**FIR**
|
||||
```c
|
||||
```
|
||||
// fir.load must copy the dynamic type from the pointer `a`
|
||||
%0 = fir.address_of(@_QMmod1Ea) : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmod1Tshape{x:i32,y:i32}>>>>
|
||||
%1 = fir.load %0 : !fir.ref<!fir.class<!fir.ptr<!fir.type<_QMmod1Tshape{x:i32,y:i32}>>>>
|
||||
|
@ -73,7 +73,7 @@ def fir_AllocaOp : fir_Op<"alloca", [AttrSizedOperandSegments,
|
||||
an optional name. The allocation may have a dynamic repetition count
|
||||
for allocating a sequence of locations for the specified type.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%c = ... : i64
|
||||
%x = fir.alloca i32
|
||||
%y = fir.alloca !fir.array<8 x i64>
|
||||
@ -192,7 +192,7 @@ def fir_AllocMemOp : fir_Op<"allocmem",
|
||||
The memory object is in an undefined state. `allocmem` operations must
|
||||
be paired with `freemem` operations to avoid memory leaks.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%0 = fir.allocmem !fir.array<10 x f32>
|
||||
fir.freemem %0 : !fir.heap<!fir.array<10 x f32>>
|
||||
```
|
||||
@ -246,7 +246,7 @@ def fir_FreeMemOp : fir_Op<"freemem", [MemoryEffects<[MemFree]>]> {
|
||||
in the undefined state as undefined behavior. This includes aliasing
|
||||
references, such as the result of an `fir.embox`.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%21 = fir.allocmem !fir.type<ZT(p:i32){field:i32}>
|
||||
...
|
||||
fir.freemem %21 : !fir.heap<!fir.type<ZT>>
|
||||
@ -265,7 +265,7 @@ def fir_LoadOp : fir_OneResultOp<"load", []> {
|
||||
Produces an immutable ssa-value of the referent type. A memory reference
|
||||
has type `!fir.ref<T>`, `!fir.heap<T>`, or `!fir.ptr<T>`.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%a = fir.alloca i32
|
||||
%l = fir.load %a : !fir.ref<i32>
|
||||
```
|
||||
@ -293,7 +293,7 @@ def fir_StoreOp : fir_Op<"store", []> {
|
||||
value must be of the same type as the referent type of the memory
|
||||
reference.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%v = ... : f64
|
||||
%p = ... : !fir.ptr<f64>
|
||||
fir.store %v to %p : !fir.ptr<f64>
|
||||
@ -337,7 +337,7 @@ def fir_SaveResultOp : fir_Op<"save_result", [AttrSizedOperandSegments]> {
|
||||
The fir.save_result associated to a function call must immediately follow
|
||||
the call and be in the same block.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%buffer = fir.alloca fir.array<?xf32>, %c100
|
||||
%shape = fir.shape %c100
|
||||
%array_result = fir.call @foo() : () -> fir.array<?xf32>
|
||||
@ -378,7 +378,7 @@ def fir_CharConvertOp : fir_Op<"char_convert", []> {
|
||||
The number of code points copied is specified explicitly as the second
|
||||
argument. The length of the !fir.char type is ignored.
|
||||
|
||||
```mlir
|
||||
```
|
||||
fir.char_convert %1 for %2 to %3 : !fir.ref<!fir.char<1,?>>, i32,
|
||||
!fir.ref<!fir.char<2,20>>
|
||||
```
|
||||
@ -410,7 +410,7 @@ def fir_UndefOp : fir_OneResultOp<"undefined", [NoMemoryEffect]> {
|
||||
This operation is typically created internally by the mem2reg conversion
|
||||
pass. An undefined value can be of any type except `!fir.ref<T>`.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%a = fir.undefined !fir.array<10 x !fir.type<T>>
|
||||
```
|
||||
|
||||
@ -432,7 +432,7 @@ def fir_ZeroOp : fir_OneResultOp<"zero_bits", [NoMemoryEffect]> {
|
||||
Constructs an ssa-value of the specified type with a value of zero for all
|
||||
bits.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%a = fir.zero_bits !fir.box<!fir.array<10 x !fir.type<T>>>
|
||||
```
|
||||
|
||||
@ -563,7 +563,7 @@ def fir_SelectOp : fir_IntegralSwitchTerminatorOp<"select"> {
|
||||
at least one basic block with a corresponding `unit` match, and
|
||||
that block will be selected when all other conditions fail to match.
|
||||
|
||||
```mlir
|
||||
```
|
||||
fir.select %arg:i32 [1, ^bb1(%0 : i32),
|
||||
2, ^bb2(%2,%arg,%arg2 : i32,i32,i32),
|
||||
-3, ^bb3(%arg2,%2 : i32,i32),
|
||||
@ -586,7 +586,7 @@ def fir_SelectRankOp : fir_IntegralSwitchTerminatorOp<"select_rank"> {
|
||||
same as `select`, but `select_rank` determines the rank of the selector
|
||||
variable at runtime to determine the best match.
|
||||
|
||||
```mlir
|
||||
```
|
||||
fir.select_rank %arg:i32 [1, ^bb1(%0 : i32),
|
||||
2, ^bb2(%2,%arg,%arg2 : i32,i32,i32),
|
||||
3, ^bb3(%arg2,%2 : i32,i32),
|
||||
@ -608,7 +608,7 @@ def fir_SelectCaseOp : fir_SwitchTerminatorOp<"select_case"> {
|
||||
the same as `select`, but `select_case` allows for the expression of
|
||||
more complex match conditions.
|
||||
|
||||
```mlir
|
||||
```
|
||||
fir.select_case %arg : i32 [
|
||||
#fir.point, %0, ^bb1(%0 : i32),
|
||||
#fir.lower, %1, ^bb2(%2,%arg,%arg2,%1 : i32,i32,i32,i32),
|
||||
@ -651,7 +651,7 @@ def fir_SelectTypeOp : fir_SwitchTerminatorOp<"select_type"> {
|
||||
same as `select`, but `select_type` determines the type of the selector
|
||||
variable at runtime to determine the best match.
|
||||
|
||||
```mlir
|
||||
```
|
||||
fir.select_type %arg : !fir.box<()> [
|
||||
#fir.type_is<!fir.type<type1>>, ^bb1(%0 : i32),
|
||||
#fir.type_is<!fir.type<type2>>, ^bb2(%2 : i32),
|
||||
@ -684,7 +684,7 @@ def fir_UnreachableOp : fir_Op<"unreachable", [Terminator]> {
|
||||
program, for example. This instruction corresponds to the LLVM IR
|
||||
instruction `unreachable`.
|
||||
|
||||
```mlir
|
||||
```
|
||||
fir.unreachable
|
||||
```
|
||||
}];
|
||||
@ -709,7 +709,7 @@ def fir_HasValueOp : fir_Op<"has_value", [Terminator, HasParent<"GlobalOp">]> {
|
||||
let description = [{
|
||||
The terminator for a GlobalOp with a body.
|
||||
|
||||
```mlir
|
||||
```
|
||||
global @variable : tuple<i32, f32> {
|
||||
%0 = arith.constant 45 : i32
|
||||
%1 = arith.constant 100.0 : f32
|
||||
@ -741,7 +741,7 @@ def fir_EmboxOp : fir_Op<"embox", [NoMemoryEffect, AttrSizedOperandSegments]> {
|
||||
auxiliary information is packaged and abstracted as a value with box type
|
||||
by the calling routine. (In Fortran, these are called descriptors.)
|
||||
|
||||
```mlir
|
||||
```
|
||||
%c1 = arith.constant 1 : index
|
||||
%c10 = arith.constant 10 : index
|
||||
%5 = ... : !fir.ref<!fir.array<10 x i32>>
|
||||
@ -824,7 +824,7 @@ def fir_ReboxOp : fir_Op<"rebox", [NoMemoryEffect, AttrSizedOperandSegments]> {
|
||||
where x is described by a fir.box and has non default lower bounds,
|
||||
and then applying a new 2-dimension shape to this fir.box.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%0 = fir.slice %c10, %c33, %c2 : (index, index, index) -> !fir.slice<1>
|
||||
%1 = fir.shift %c0 : (index) -> !fir.shift<1>
|
||||
%2 = fir.rebox %x(%1) [%0] : (!fir.box<!fir.array<?xf32>>, !fir.shift<1>, !fir.slice<1>) -> !fir.box<!fir.array<?xf32>>
|
||||
@ -861,7 +861,7 @@ def fir_EmboxCharOp : fir_Op<"emboxchar", [NoMemoryEffect]> {
|
||||
```fortran
|
||||
CHARACTER(LEN=10) :: var
|
||||
```
|
||||
```mlir
|
||||
```
|
||||
%4 = ... : !fir.ref<!fir.array<10 x !fir.char<1>>>
|
||||
%5 = arith.constant 10 : i32
|
||||
%6 = fir.emboxchar %4, %5 : (!fir.ref<!fir.array<10 x !fir.char<1>>>, i32) -> !fir.boxchar<1>
|
||||
@ -891,7 +891,7 @@ def fir_EmboxProcOp : fir_Op<"emboxproc", [NoMemoryEffect]> {
|
||||
internal procedure or the internal procedure does not need a host context
|
||||
then the form takes only the procedure's symbol.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%f = ... : (i32) -> i32
|
||||
%0 = fir.emboxproc %f : ((i32) -> i32) -> !fir.boxproc<(i32) -> i32>
|
||||
```
|
||||
@ -902,7 +902,7 @@ def fir_EmboxProcOp : fir_Op<"emboxproc", [NoMemoryEffect]> {
|
||||
context's values accordingly, up to and including inhibiting register
|
||||
promotion of local values.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%4 = ... : !fir.ref<tuple<!fir.ref<i32>, !fir.ref<i32>>>
|
||||
%g = ... : (i32) -> i32
|
||||
%5 = fir.emboxproc %g, %4 : ((i32) -> i32, !fir.ref<tuple<!fir.ref<i32>, !fir.ref<i32>>>) -> !fir.boxproc<(i32) -> i32>
|
||||
@ -927,7 +927,7 @@ def fir_UnboxCharOp : fir_SimpleOp<"unboxchar", [NoMemoryEffect]> {
|
||||
Unboxes a value of `boxchar` type into a pair consisting of a memory
|
||||
reference to the CHARACTER data and the LEN type parameter.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%45 = ... : !fir.boxchar<1>
|
||||
%46:2 = fir.unboxchar %45 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1>>, i32)
|
||||
```
|
||||
@ -945,7 +945,7 @@ def fir_UnboxProcOp : fir_SimpleOp<"unboxproc", [NoMemoryEffect]> {
|
||||
Unboxes a value of `boxproc` type into a pair consisting of a procedure
|
||||
pointer and a pointer to a host context.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%47 = ... : !fir.boxproc<() -> i32>
|
||||
%48:2 = fir.unboxproc %47 : (!fir.ref<() -> i32>, !fir.ref<tuple<f32, i32>>)
|
||||
```
|
||||
@ -967,7 +967,7 @@ def fir_BoxAddrOp : fir_SimpleOneResultOp<"box_addr", [NoMemoryEffect]> {
|
||||
cases, respectively, is the address of the data, the address of the
|
||||
`CHARACTER` data, and the address of the procedure.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%51 = fir.box_addr %box : (!fir.box<f64>) -> !fir.ref<f64>
|
||||
%52 = fir.box_addr %boxchar : (!fir.boxchar<1>) -> !fir.ref<!fir.char<1>>
|
||||
%53 = fir.box_addr %boxproc : (!fir.boxproc<!P>) -> !P
|
||||
@ -989,7 +989,7 @@ def fir_BoxCharLenOp : fir_SimpleOp<"boxchar_len", [NoMemoryEffect]> {
|
||||
let description = [{
|
||||
Extracts the LEN type parameter from a `boxchar` value.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%45 = ... : !boxchar<1> // CHARACTER(20)
|
||||
%59 = fir.boxchar_len %45 : (!fir.boxchar<1>) -> i64 // len=20
|
||||
```
|
||||
@ -1011,7 +1011,7 @@ def fir_BoxDimsOp : fir_Op<"box_dims", [NoMemoryEffect]> {
|
||||
left to right from 0 to rank-1. This operation has undefined behavior if
|
||||
`dim` is out of bounds.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%c1 = arith.constant 0 : i32
|
||||
%52:3 = fir.box_dims %40, %c1 : (!fir.box<!fir.array<*:f64>>, i32) -> (index, index, index)
|
||||
```
|
||||
@ -1044,7 +1044,7 @@ def fir_BoxEleSizeOp : fir_SimpleOneResultOp<"box_elesize", [NoMemoryEffect]> {
|
||||
Returns the size of an element in an entity of `box` type. This size may
|
||||
not be known until runtime.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%53 = fir.box_elesize %40 : (!fir.box<f32>) -> i32 // size=4
|
||||
%54 = fir.box_elesize %40 : (!fir.box<!fir.array<*:f32>>) -> i32
|
||||
```
|
||||
@ -1065,7 +1065,7 @@ def fir_BoxTypeCodeOp : fir_SimpleOneResultOp<"box_typecode", [NoMemoryEffect]>
|
||||
let description = [{
|
||||
Returns the descriptor type code of an entity of `box` type.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%1 = fir.box_typecode %0 : (!fir.box<T>) -> i32
|
||||
```
|
||||
}];
|
||||
@ -1083,7 +1083,7 @@ def fir_BoxIsAllocOp : fir_SimpleOp<"box_isalloc", [NoMemoryEffect]> {
|
||||
return true if the originating box value was from a `fir.embox` op
|
||||
with a mem-ref value that had the type !fir.heap<T>.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%r = ... : !fir.heap<i64>
|
||||
%b = fir.embox %r : (!fir.heap<i64>) -> !fir.box<i64>
|
||||
%a = fir.box_isalloc %b : (!fir.box<i64>) -> i1 // true
|
||||
@ -1106,7 +1106,7 @@ def fir_BoxIsArrayOp : fir_SimpleOp<"box_isarray", [NoMemoryEffect]> {
|
||||
true if the originating box value was from a fir.embox with a memory
|
||||
reference value that had the type !fir.array<T> and/or a shape argument.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%r = ... : !fir.ref<i64>
|
||||
%c_100 = arith.constant 100 : index
|
||||
%d = fir.shape %c_100 : (index) -> !fir.shape<1>
|
||||
@ -1126,7 +1126,7 @@ def fir_BoxIsPtrOp : fir_SimpleOp<"box_isptr", [NoMemoryEffect]> {
|
||||
let description = [{
|
||||
Determine if the boxed value was from a POINTER entity.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%p = ... : !fir.ptr<i64>
|
||||
%b = fir.embox %p : (!fir.ptr<i64>) -> !fir.box<i64>
|
||||
%a = fir.box_isptr %b : (!fir.box<i64>) -> i1 // true
|
||||
@ -1144,7 +1144,7 @@ def fir_BoxProcHostOp : fir_SimpleOp<"boxproc_host", [NoMemoryEffect]> {
|
||||
let description = [{
|
||||
Extract the host context pointer from a boxproc value.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%8 = ... : !fir.boxproc<(!fir.ref<!fir.type<T>>) -> i32>
|
||||
%9 = fir.boxproc_host %8 : (!fir.boxproc<(!fir.ref<!fir.type<T>>) -> i32>) -> !fir.ref<tuple<i32, i32>>
|
||||
```
|
||||
@ -1167,7 +1167,7 @@ def fir_BoxRankOp : fir_SimpleOneResultOp<"box_rank", [NoMemoryEffect]> {
|
||||
Return the rank of a value of `box` type. If the value is scalar, the
|
||||
rank is 0.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%57 = fir.box_rank %40 : (!fir.box<!fir.array<*:f64>>) -> i32
|
||||
%58 = fir.box_rank %41 : (!fir.box<f64>) -> i32
|
||||
```
|
||||
@ -1190,7 +1190,7 @@ def fir_BoxTypeDescOp : fir_SimpleOneResultOp<"box_tdesc", [NoMemoryEffect]> {
|
||||
descriptor is an implementation defined value that fully describes a type
|
||||
to the Fortran runtime.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%7 = fir.box_tdesc %41 : (!fir.box<f64>) -> !fir.tdesc<f64>
|
||||
```
|
||||
}];
|
||||
@ -1271,7 +1271,7 @@ def fir_ArrayLoadOp : fir_Op<"array_load", [AttrSizedOperandSegments]> {
|
||||
value while applying a runtime shape, shift, or slice to the memory
|
||||
reference, and its semantics guarantee immutability.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%s = fir.shape_shift %o, %n, %p, %m : (index, index, index, index) -> !fir.shapeshift<2>
|
||||
// load the entire array 'a'
|
||||
%v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shapeshift<2>) -> !fir.array<?x?xf32>
|
||||
@ -1319,7 +1319,7 @@ def fir_ArrayFetchOp : fir_Op<"array_fetch", [AttrSizedOperandSegments,
|
||||
an array expression as shown above. It can also be used to extract the
|
||||
element `a(r,s+1)` in the second expression.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%s = fir.shape %n, %m : (index, index) -> !fir.shape<2>
|
||||
// load the entire array 'a'
|
||||
%v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32>
|
||||
@ -1365,7 +1365,7 @@ def fir_ArrayUpdateOp : fir_Op<"array_update", [AttrSizedOperandSegments,
|
||||
One can use `fir.array_update` to update the (implied) value of `a(i,j)`
|
||||
in an array expression as shown above.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%s = fir.shape %n, %m : (index, index) -> !fir.shape<2>
|
||||
// load the entire array 'a'
|
||||
%v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32>
|
||||
@ -1419,7 +1419,7 @@ def fir_ArrayModifyOp : fir_Op<"array_modify", [AttrSizedOperandSegments,
|
||||
One can use `fir.array_modify` to update the (implied) value of `a(i)`
|
||||
in an array expression as shown above.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%s = fir.shape %n : (index) -> !fir.shape<1>
|
||||
// Load the entire array 'a'.
|
||||
%v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?xf32>>, !fir.shape<1>) -> !fir.array<?xf32>
|
||||
@ -1482,7 +1482,7 @@ def fir_ArrayAccessOp : fir_Op<"array_access", [AttrSizedOperandSegments,
|
||||
be used to recover the reference element `a(r,s+1)` in the second
|
||||
expression.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%s = fir.shape %n, %m : (index, index) -> !fir.shape<2>
|
||||
// load the entire array 'a'
|
||||
%v = fir.array_load %a(%s) : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>) -> !fir.array<?x?xf32>
|
||||
@ -1527,7 +1527,7 @@ def fir_ArrayAmendOp : fir_Op<"array_amend", [NoMemoryEffect]> {
|
||||
log that is used to merge the final result back with an `array_merge_store`
|
||||
operation.
|
||||
|
||||
```mlir
|
||||
```
|
||||
// fetch the value of one of the array value's elements
|
||||
%1 = fir.array_access %v, %i, %j : (!fir.array<?x?xT>, index, index) -> !fir.ref<T>
|
||||
// modify the element by storing data using %1 as a reference
|
||||
@ -1569,7 +1569,7 @@ def fir_ArrayMergeStoreOp : fir_Op<"array_merge_store",
|
||||
One can use `fir.array_merge_store` to merge/copy the value of `a` in an
|
||||
array expression as shown above.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%v = fir.array_load %a(%shape) : ...
|
||||
%r = fir.array_update %v, %f, %i, %j : (!fir.array<?x?xf32>, f32, index, index) -> !fir.array<?x?xf32>
|
||||
fir.array_merge_store %v, %r to %a : !fir.ref<!fir.array<?x?xf32>>
|
||||
@ -1620,7 +1620,7 @@ def fir_ArrayCoorOp : fir_Op<"array_coor",
|
||||
|
||||
One can use `fir.array_coor` to determine the address of `a(i,j)`.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%s = fir.shape %n, %m : (index, index) -> !fir.shape<2>
|
||||
%1 = fir.array_coor %a(%s) %i, %j : (!fir.ref<!fir.array<?x?xf32>>, !fir.shape<2>, index, index) -> !fir.ref<f32>
|
||||
```
|
||||
@ -1659,7 +1659,7 @@ def fir_CoordinateOp : fir_Op<"coordinate_of", [NoMemoryEffect]> {
|
||||
Unlike LLVM's GEP instruction, one cannot stride over the outermost
|
||||
reference; therefore, the leading 0 index must be omitted.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%i = ... : index
|
||||
%h = ... : !fir.heap<!fir.array<100 x f32>>
|
||||
%p = fir.coordinate_of %h, %i : (!fir.heap<!fir.array<100 x f32>>, index) -> !fir.ref<f32>
|
||||
@ -1701,7 +1701,7 @@ def fir_ExtractValueOp : fir_OneResultOp<"extract_value", [NoMemoryEffect]> {
|
||||
Note that the entity ssa-value must be of compile-time known size in order
|
||||
to use this operation.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%f = fir.field_index field, !fir.type<X{field:i32}>
|
||||
%s = ... : !fir.type<X>
|
||||
%v = fir.extract_value %s, %f : (!fir.type<X>, !fir.field) -> i32
|
||||
@ -1729,7 +1729,7 @@ def fir_FieldIndexOp : fir_OneResultOp<"field_index", [NoMemoryEffect]> {
|
||||
or `fir.insert_value` instructions to compute (abstract) addresses of
|
||||
subobjects.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%f = fir.field_index field, !fir.type<X{field:i32}>
|
||||
```
|
||||
}];
|
||||
@ -1764,7 +1764,7 @@ def fir_ShapeOp : fir_Op<"shape", [NoMemoryEffect]> {
|
||||
must be applied to a reified object, so all shape information must be
|
||||
specified. The extent must be nonnegative.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%d = fir.shape %row_sz, %col_sz : (index, index) -> !fir.shape<2>
|
||||
```
|
||||
}];
|
||||
@ -1796,7 +1796,7 @@ def fir_ShapeShiftOp : fir_Op<"shape_shift", [NoMemoryEffect]> {
|
||||
be applied to a reified object, so all shifted shape information must be
|
||||
specified. The extent must be nonnegative.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%d = fir.shape_shift %lo, %extent : (index, index) -> !fir.shapeshift<1>
|
||||
```
|
||||
}];
|
||||
@ -1843,7 +1843,7 @@ def fir_ShiftOp : fir_Op<"shift", [NoMemoryEffect]> {
|
||||
must be applied to a reified object, so all shift information must be
|
||||
specified.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%d = fir.shift %row_lb, %col_lb : (index, index) -> !fir.shift<2>
|
||||
```
|
||||
}];
|
||||
@ -1872,7 +1872,7 @@ def fir_SliceOp : fir_Op<"slice", [NoMemoryEffect, AttrSizedOperandSegments]> {
|
||||
must be applied to a reified object, so all slice information must be
|
||||
specified. The extent must be nonnegative and the stride must not be zero.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%d = fir.slice %lo, %hi, %step : (index, index, index) -> !fir.slice<1>
|
||||
```
|
||||
|
||||
@ -1880,7 +1880,7 @@ def fir_SliceOp : fir_Op<"slice", [NoMemoryEffect, AttrSizedOperandSegments]> {
|
||||
op can be given a component path (narrowing from the product type of the
|
||||
original array to the specific elemental type of the sliced projection).
|
||||
|
||||
```mlir
|
||||
```
|
||||
%fld = fir.field_index component, !fir.type<t{...component:ct...}>
|
||||
%d = fir.slice %lo, %hi, %step path %fld :
|
||||
(index, index, index, !fir.field) -> !fir.slice<1>
|
||||
@ -1889,7 +1889,7 @@ def fir_SliceOp : fir_Op<"slice", [NoMemoryEffect, AttrSizedOperandSegments]> {
|
||||
Projections of `!fir.char` type can be further narrowed to invariant
|
||||
substrings.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%d = fir.slice %lo, %hi, %step substr %offset, %width :
|
||||
(index, index, index, index, index) -> !fir.slice<1>
|
||||
```
|
||||
@ -1935,7 +1935,7 @@ def fir_InsertValueOp : fir_OneResultOp<"insert_value", [NoMemoryEffect]> {
|
||||
Note that the entity ssa-value must be of compile-time known size in order
|
||||
to use this operation.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%a = ... : !fir.array<10xtuple<i32, f32>>
|
||||
%f = ... : f32
|
||||
%o = ... : i32
|
||||
@ -1965,7 +1965,7 @@ def fir_InsertOnRangeOp : fir_OneResultOp<"insert_on_range", [NoMemoryEffect]> {
|
||||
row-to-column element order as specified by lower and upper bound
|
||||
coordinates.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%a = fir.undefined !fir.array<10x10xf32>
|
||||
%c = arith.constant 3.0 : f32
|
||||
%1 = fir.insert_on_range %a, %c from (0, 0) to (7, 2) : (!fir.array<10x10xf32>, f32) -> !fir.array<10x10xf32>
|
||||
@ -1995,7 +1995,7 @@ def fir_LenParamIndexOp : fir_OneResultOp<"len_param_index", [NoMemoryEffect]> {
|
||||
used with the `fir.coordinate_of` instructions to compute (abstract)
|
||||
addresses of LEN parameters.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%e = fir.len_param_index len1, !fir.type<X(len1:i32)>
|
||||
%p = ... : !fir.box<!fir.type<X>>
|
||||
%q = fir.coordinate_of %p, %e : (!fir.box<!fir.type<X>>, !fir.len) -> !fir.ref<i32>
|
||||
@ -2062,7 +2062,7 @@ def fir_DoLoopOp : region_Op<"do_loop",
|
||||
Generalized high-level looping construct. This operation is similar to
|
||||
MLIR's `scf.for`.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%l = arith.constant 0 : index
|
||||
%u = arith.constant 9 : index
|
||||
%s = arith.constant 1 : index
|
||||
@ -2154,7 +2154,7 @@ def fir_IfOp : region_Op<"if", [DeclareOpInterfaceMethods<RegionBranchOpInterfac
|
||||
Used to conditionally execute operations. This operation is the FIR
|
||||
dialect's version of `loop.if`.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%56 = ... : i1
|
||||
%78 = ... : !fir.ref<!T>
|
||||
fir.if %56 {
|
||||
@ -2218,7 +2218,7 @@ def fir_IterWhileOp : region_Op<"iterate_while",
|
||||
%ok=phi(%okIn,%okNew), and %sh=phi(%shIn,%shNew) from the last executed
|
||||
iteration.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%v:3 = fir.iterate_while (%i = %lo to %up step %c1) and (%ok = %okIn) iter_args(%sh = %shIn) -> (index, i1, i16) {
|
||||
%shNew = fir.call @bar(%sh) : (i16) -> i16
|
||||
%okNew = fir.call @foo(%sh) : (i16) -> i1
|
||||
@ -2304,7 +2304,7 @@ def fir_CallOp : fir_Op<"call",
|
||||
Provides a custom parser and pretty printer to allow a more readable syntax
|
||||
in the FIR dialect, e.g. `fir.call @sub(%12)` or `fir.call %20(%22,%23)`.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%a = fir.call %funcref(%arg0) : (!fir.ref<f32>) -> f32
|
||||
%b = fir.call @function(%arg1, %arg2) : (!fir.ref<f32>, !fir.ref<f32>) -> f32
|
||||
```
|
||||
@ -2383,7 +2383,7 @@ def fir_DispatchOp : fir_Op<"dispatch", []> {
|
||||
used to select a dispatch operand other than the first one. The absence of
|
||||
`pass_arg_pos` attribute means nopass.
|
||||
|
||||
```mlir
|
||||
```
|
||||
// fir.dispatch with no attribute.
|
||||
%r = fir.dispatch "methodA"(%o) : (!fir.class<T>) -> i32
|
||||
|
||||
@ -2430,7 +2430,7 @@ def fir_StringLitOp : fir_Op<"string_lit", [NoMemoryEffect]> {
|
||||
to Fortran's CHARACTER type, including a LEN. We support CHARACTER values
|
||||
of different KINDs (different constant sizes).
|
||||
|
||||
```mlir
|
||||
```
|
||||
%1 = fir.string_lit "Hello, World!"(13) : !fir.char<1> // ASCII
|
||||
%2 = fir.string_lit [158, 2345](2) : !fir.char<2> // Wide chars
|
||||
```
|
||||
@ -2572,7 +2572,7 @@ def fir_AddrOfOp : fir_OneResultOp<"address_of", [NoMemoryEffect]> {
|
||||
used in other operations. References to Fortran symbols are distinguished
|
||||
via this operation from other arbitrary constant values.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%p = fir.address_of(@symbol) : !fir.ref<f64>
|
||||
```
|
||||
}];
|
||||
@ -2593,7 +2593,7 @@ def fir_ConvertOp : fir_OneResultOp<"convert", [NoMemoryEffect]> {
|
||||
type, this instruction is a NOP and may be folded away. This also supports
|
||||
integer to pointer conversion and pointer to integer conversion.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%v = ... : i64
|
||||
%w = fir.convert %v : (i64) -> i32
|
||||
```
|
||||
@ -2639,7 +2639,7 @@ def fir_TypeDescOp : fir_OneResultOp<"type_desc", [NoMemoryEffect]> {
|
||||
specified type. The meta-type of a type descriptor for the type `T`
|
||||
is `!fir.tdesc<T>`.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%t = fir.type_desc !fir.type<> // returns value of !fir.tdesc<!T>
|
||||
```
|
||||
}];
|
||||
@ -2664,7 +2664,7 @@ def fir_NoReassocOp : fir_OneResultOp<"no_reassoc",
|
||||
example below, this would prevent possibly replacing the multiply and add
|
||||
operations with a single FMA operation.
|
||||
|
||||
```mlir
|
||||
```
|
||||
%98 = arith.mulf %96, %97 : f32
|
||||
%99 = fir.no_reassoc %98 : f32
|
||||
%a0 = arith.addf %99, %95 : f32
|
||||
@ -2689,7 +2689,7 @@ def fir_GlobalOp : fir_Op<"global", [IsolatedFromAbove, Symbol]> {
|
||||
`@_QV_Mquark_Vvarble` with some initial values. The initializer should
|
||||
conform to the variable's type.
|
||||
|
||||
```mlir
|
||||
```
|
||||
fir.global @_QV_Mquark_Vvarble : tuple<i32, f32> {
|
||||
%1 = arith.constant 1 : i32
|
||||
%2 = arith.constant 2.0 : f32
|
||||
@ -2792,7 +2792,7 @@ def fir_GlobalLenOp : fir_Op<"global_len", []> {
|
||||
parameter (compile-time) constants associated with the instance's type.
|
||||
These values can be bound to the global instance used `fir.global_len`.
|
||||
|
||||
```mlir
|
||||
```
|
||||
global @g : !fir.type<t(len1:i32)> {
|
||||
fir.global_len len1, 10 : i32
|
||||
%1 = fir.undefined !fir.type<t(len1:i32)>
|
||||
@ -2836,7 +2836,7 @@ def fir_TypeInfoOp : fir_Op<"type_info",
|
||||
The "no_final" flag indicates that there are no final methods for this type,
|
||||
for its parents ,or for components.
|
||||
|
||||
```mlir
|
||||
```
|
||||
fir.type_info @_QMquuzTfoo noinit nofinal : !fir.type<_QMquuzTfoo{i:i32}> dispatch_table {
|
||||
fir.dt_entry method1, @_QFNMquuzTfooPmethod1AfooR
|
||||
fir.dt_entry method2, @_QFNMquuzTfooPmethod2AfooII
|
||||
@ -2895,7 +2895,7 @@ def fir_DTEntryOp : fir_Op<"dt_entry", [HasParent<"TypeInfoOp">]> {
|
||||
and uses the method identifier to select the type-bound procedure to
|
||||
be called.
|
||||
|
||||
```mlir
|
||||
```
|
||||
fir.dt_entry method_name, @uniquedProcedure
|
||||
```
|
||||
}];
|
||||
@ -2919,7 +2919,7 @@ def fir_AbsentOp : fir_OneResultOp<"absent", [NoMemoryEffect]> {
|
||||
a fir.absent operation.
|
||||
It is undefined to use a value that was created by a fir.absent op in any other
|
||||
operation than fir.call and fir.is_present.
|
||||
```mlir
|
||||
```
|
||||
%1 = fir.absent fir.box<fir.array<?xf32>>
|
||||
fir.call @_QPfoo(%1) : (fir.box<fir.array<?xf32>>) -> ()
|
||||
```
|
||||
@ -2936,7 +2936,7 @@ def fir_IsPresentOp : fir_SimpleOp<"is_present", [NoMemoryEffect]> {
|
||||
let description = [{
|
||||
Determine if an optional function argument is PRESENT (i.e. that it was not
|
||||
created by a fir.absent op on the caller side).
|
||||
```mlir
|
||||
```
|
||||
func @_QPfoo(%arg0: !fir.box<!fir.array<?xf32>>) {
|
||||
%0 = fir.is_present %arg0 : (!fir.box<!fir.array<?xf32>>) -> i1
|
||||
...
|
||||
|
Loading…
x
Reference in New Issue
Block a user