lfortran
lfortran copied to clipboard
feat: add support for array type indices in array sections
resolves #4405
We convert
array_1(:, array_2)
to
DO i = lbound(array_1, 1), ubound(array_1, 1)
DO j = lbound(array_1, 2), ubound(array_1, 2)
array_1(i, j) = a(i, array_2(j))
END DO
END DO
Excellent! Thanks for working on this.
I have a few questions which I need help with.
- For the provided MRE,
program main
implicit none
real :: A(2, 2), B(2, 2)
integer :: P(2)
! Initialize the matrices and arrays
A = reshape([1.0, 2.0, 3.0, 4.0], shape(A))
P = [2, 1]
! Slicing
B = A(:, P)
print*, rank(A), rank(B)
end program main
the transformed code after the array_op pass is:
! Fortran code after applying the pass: array_op
program main
implicit none
integer(4) :: __1_k
integer(4) :: __1_t
integer(4) :: __1_v
integer(4) :: __2_t
integer(4) :: __2_v
integer(4), dimension(2) :: __libasr__created__var__0__array_constant_
real(4), dimension(:), pointer :: __libasr__created__var__0__array_section_pointer_
real(4), dimension(2, 2) :: a
real(4), dimension(2, 2) :: b
integer(4), dimension(2) :: p
a = reshape([1.00000000e+00, 2.00000000e+00, 3.00000000e+00, 4.00000000e+00], Shape(a))
__1_k = lbound(__libasr__created__var__0__array_constant_, 1)
__libasr__created__var__0__array_constant_(__1_k) = 2
__1_k = __1_k + 1
__libasr__created__var__0__array_constant_(__1_k) = 1
__1_k = __1_k + 1
__1_v = lbound(__libasr__created__var__0__array_constant_, 1)
do __1_t = lbound(p, 1), ubound(p, 1)
p(__1_t) = __libasr__created__var__0__array_constant_(__1_v)
__1_v = __1_v + 1
end do
__1_v = lbound(p, 1)
do __1_t = lbound(b, 1), ubound(b, 1)
__2_v = lbound(p, 1)
do __2_t = lbound(b, 2), ubound(b, 2)
b(__1_t, __2_t) = a(ubound(a, 1), p(__2_t))
__2_v = __2_v + 1
end do
__1_v = __1_v + 1
end do
__libasr__created__var__0__array_section_pointer_ => b
print *, Rank(a), Rank(b)
end program main
I see that the current transformation has the assignment value as a(ubound(a, 1), p(__2_t)). We need this to be a(__1_t, p(__2_t)) to get the desired output as GFortran. How do we get access to the outer loop variable?
- For every array section, we create an array section pointer which is later utilized for all purposes like printing. I think the above case is special as we cannot have a definitive array section for it due to an array being the index. I tried handling it using a flag variable and not creating in this case, but then the generated output is
0 0 0 0. How do we handle the array section pointer for this? - Minor: I notice an unused variable being created here -
__2_v. Why is it created? How do we remove it?
This implementation currently does not handle the case of the left index being an array. I will handle it along with the proposed methods for handling the queries stated above.
Regarding the first question, you need to keep track of the loops in the visitor I think.
Regarding the second question, can you give an example code and ASR for that? It might not be possible to use pointers, let's see.
can you give an example code and ASR for that?
MRE
program main
implicit none
real :: A(2, 2), B(2, 2)
integer :: P(2)
A = reshape([1.0, 2.0, 3.0, 4.0], shape(A))
P = [2, 1]
B = A(:, P)
print *, B
end program main
ASR after array_op pass
;; ASR after applying the pass: array_op
(TranslationUnit
(SymbolTable
1
{
main:
(Program
(SymbolTable
2
{
__1_k:
(Variable
2
__1_k
[]
Local
()
()
Default
(Integer 4)
()
Source
Public
Required
.false.
),
__1_t:
(Variable
2
__1_t
[]
Local
()
()
Default
(Integer 4)
()
Source
Public
Required
.false.
),
__1_v:
(Variable
2
__1_v
[]
Local
()
()
Default
(Integer 4)
()
Source
Public
Required
.false.
),
__2_t:
(Variable
2
__2_t
[]
Local
()
()
Default
(Integer 4)
()
Source
Public
Required
.false.
),
__2_v:
(Variable
2
__2_v
[]
Local
()
()
Default
(Integer 4)
()
Source
Public
Required
.false.
),
__libasr__created__var__0__array_constant_:
(Variable
2
__libasr__created__var__0__array_constant_
[]
Local
()
()
Default
(Array
(Integer 4)
[((IntegerConstant 1 (Integer 4))
(IntegerConstant 2 (Integer 4)))]
FixedSizeArray
)
()
Source
Public
Required
.false.
),
__libasr__created__var__0__array_section_pointer_:
(Variable
2
__libasr__created__var__0__array_section_pointer_
[]
Local
()
()
Default
(Pointer
(Array
(Real 4)
[(()
())]
DescriptorArray
)
)
()
Source
Public
Required
.false.
),
a:
(Variable
2
a
[]
Local
()
()
Default
(Array
(Real 4)
[((IntegerConstant 1 (Integer 4))
(IntegerConstant 2 (Integer 4)))
((IntegerConstant 1 (Integer 4))
(IntegerConstant 2 (Integer 4)))]
FixedSizeArray
)
()
Source
Public
Required
.false.
),
b:
(Variable
2
b
[]
Local
()
()
Default
(Array
(Real 4)
[((IntegerConstant 1 (Integer 4))
(IntegerConstant 2 (Integer 4)))
((IntegerConstant 1 (Integer 4))
(IntegerConstant 2 (Integer 4)))]
FixedSizeArray
)
()
Source
Public
Required
.false.
),
p:
(Variable
2
p
[]
Local
()
()
Default
(Array
(Integer 4)
[((IntegerConstant 1 (Integer 4))
(IntegerConstant 2 (Integer 4)))]
FixedSizeArray
)
()
Source
Public
Required
.false.
)
})
main
[]
[(Assignment
(Var 2 a)
(ArrayReshape
(ArrayConstant
16
[1.00000000e+00, 2.00000000e+00, 3.00000000e+00, 4.00000000e+00]
(Array
(Real 4)
[((IntegerConstant 1 (Integer 4))
(IntegerConstant 4 (Integer 4)))]
FixedSizeArray
)
ColMajor
)
(ArrayPhysicalCast
(IntrinsicArrayFunction
Shape
[(ArrayPhysicalCast
(Var 2 a)
FixedSizeArray
DescriptorArray
(Array
(Real 4)
[((IntegerConstant 1 (Integer 4))
(IntegerConstant 2 (Integer 4)))
((IntegerConstant 1 (Integer 4))
(IntegerConstant 2 (Integer 4)))]
DescriptorArray
)
()
)]
0
(Array
(Integer 4)
[((IntegerConstant 1 (Integer 4))
(IntegerConstant 2 (Integer 4)))]
FixedSizeArray
)
(ArrayConstant
8
[2, 2]
(Array
(Integer 4)
[((IntegerConstant 1 (Integer 4))
(IntegerConstant 2 (Integer 4)))]
FixedSizeArray
)
ColMajor
)
)
FixedSizeArray
DescriptorArray
(Array
(Integer 4)
[((IntegerConstant 1 (Integer 4))
(IntegerConstant 2 (Integer 4)))]
DescriptorArray
)
()
)
(Array
(Real 4)
[(()
())]
FixedSizeArray
)
()
)
()
)
(Assignment
(Var 2 __1_k)
(ArrayBound
(Var 2 __libasr__created__var__0__array_constant_)
(IntegerConstant 1 (Integer 4))
(Integer 4)
LBound
()
)
()
)
(Assignment
(ArrayItem
(Var 2 __libasr__created__var__0__array_constant_)
[(()
(Var 2 __1_k)
())]
(Integer 4)
RowMajor
()
)
(IntegerConstant 2 (Integer 4))
()
)
(Assignment
(Var 2 __1_k)
(IntegerBinOp
(Var 2 __1_k)
Add
(IntegerConstant 1 (Integer 4))
(Integer 4)
()
)
()
)
(Assignment
(ArrayItem
(Var 2 __libasr__created__var__0__array_constant_)
[(()
(Var 2 __1_k)
())]
(Integer 4)
RowMajor
()
)
(IntegerConstant 1 (Integer 4))
()
)
(Assignment
(Var 2 __1_k)
(IntegerBinOp
(Var 2 __1_k)
Add
(IntegerConstant 1 (Integer 4))
(Integer 4)
()
)
()
)
(Assignment
(Var 2 __1_v)
(ArrayBound
(Var 2 __libasr__created__var__0__array_constant_)
(IntegerConstant 1 (Integer 4))
(Integer 4)
LBound
()
)
()
)
(DoLoop
()
((Var 2 __1_t)
(ArrayBound
(Var 2 p)
(IntegerConstant 1 (Integer 4))
(Integer 4)
LBound
()
)
(ArrayBound
(Var 2 p)
(IntegerConstant 1 (Integer 4))
(Integer 4)
UBound
()
)
())
[(Assignment
(ArrayItem
(Var 2 p)
[(()
(Var 2 __1_t)
())]
(Integer 4)
RowMajor
()
)
(ArrayItem
(Var 2 __libasr__created__var__0__array_constant_)
[(()
(Var 2 __1_v)
())]
(Integer 4)
RowMajor
()
)
()
)
(Assignment
(Var 2 __1_v)
(IntegerBinOp
(Var 2 __1_v)
Add
(IntegerConstant 1 (Integer 4))
(Integer 4)
()
)
()
)]
[]
)
(Assignment
(Var 2 __1_v)
(ArrayBound
(Var 2 p)
(IntegerConstant 1 (Integer 4))
(Integer 4)
LBound
()
)
()
)
(DoLoop
()
((Var 2 __1_t)
(ArrayBound
(Var 2 b)
(IntegerConstant 1 (Integer 4))
(Integer 4)
LBound
()
)
(ArrayBound
(Var 2 b)
(IntegerConstant 1 (Integer 4))
(Integer 4)
UBound
()
)
())
[(Assignment
(Var 2 __2_v)
(ArrayBound
(Var 2 p)
(IntegerConstant 1 (Integer 4))
(Integer 4)
LBound
()
)
()
)
(DoLoop
()
((Var 2 __2_t)
(ArrayBound
(Var 2 b)
(IntegerConstant 2 (Integer 4))
(Integer 4)
LBound
()
)
(ArrayBound
(Var 2 b)
(IntegerConstant 2 (Integer 4))
(Integer 4)
UBound
()
)
())
[(Assignment
(ArrayItem
(Var 2 b)
[(()
(Var 2 __1_t)
())
(()
(Var 2 __2_t)
())]
(Real 4)
RowMajor
()
)
(ArrayItem
(Var 2 a)
[((ArrayBound
(Var 2 a)
(IntegerConstant 1 (Integer 4))
(Integer 4)
LBound
(IntegerConstant 1 (Integer 4))
)
(ArrayBound
(Var 2 a)
(IntegerConstant 1 (Integer 4))
(Integer 4)
UBound
(IntegerConstant 2 (Integer 4))
)
(IntegerConstant 1 (Integer 4)))
(()
(ArrayItem
(Var 2 p)
[(()
(Var 2 __2_t)
())]
(Real 4)
ColMajor
()
)
())]
(Real 4)
ColMajor
()
)
()
)
(Assignment
(Var 2 __2_v)
(IntegerBinOp
(Var 2 __2_v)
Add
(IntegerConstant 1 (Integer 4))
(Integer 4)
()
)
()
)]
[]
)
(Assignment
(Var 2 __1_v)
(IntegerBinOp
(Var 2 __1_v)
Add
(IntegerConstant 1 (Integer 4))
(Integer 4)
()
)
()
)]
[]
)
(Associate
(Var 2 __libasr__created__var__0__array_section_pointer_)
(ArrayPhysicalCast
(Var 2 b)
FixedSizeArray
DescriptorArray
(Array
(Real 4)
[((IntegerConstant 1 (Integer 4))
(IntegerConstant 2 (Integer 4)))
((IntegerConstant 1 (Integer 4))
(IntegerConstant 2 (Integer 4)))]
DescriptorArray
)
()
)
)
(Print
[(Var 2 b)]
()
()
)]
)
})
[]
)
Since this feature is used quite some times in PRIMA, I am just quickly updating this PR. I will update it finally with a test tomorrow morning (IST).
Done! Adding tests now.
program main
implicit none
real :: A(2, 2, 2), B(2, 2, 2)
integer :: P(2)
! Initialize the matrices and arrays
A = reshape([1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 5.0], shape(A))
P = [2, 1]
! Slicing
B = A(:, :, P)
print*, rank(A), rank(B)
end program main
(lf) saurabh-kumar@Awadh:~/Projects/System/lfortran$ gfortran ./examples/example.f90 && ./a.out
3 3
(lf) saurabh-kumar@Awadh:~/Projects/System/lfortran$ lfortran ./examples/example.f90
3 3
Ready.
This looks good, great job!
Can you send this against the simplifier also? We need to ensure it doesn't break things there.
The CI failed due to issue related to ArrayReshape when using --experimental-simplifier. Skipping testing with it for now.
The failure in #5139 is a missing implementation detail. Marking this as draft for now.
This doesn't work in #5139 since there is no result_var there. We need to create a temporary result to fix that. I tried making a temporary but it is being created as an allocatable, and I don't think that is required. @kmr-srbh could you take a look?
@advikkabra right, that's exactly the cause of the failure. As for the creation of the temporary as an allocatable, I am guessing the ttype passed to get_result_type() might be that of ArraySection (x->m_type). We handle array sections as DescriptorArray. Could you please push a commit to this PR? Let's iterate on this further.
I rebased everything to get the latest CI tests.
@kmr-srbh if this is ready to be merged, then go ahead and merge it.
There is some formatting issue (braces required), I am resolving it and merging this.