Hello world, the attached patch goes one step further in matmul inlinding.
It converts statements like r = dot_product(matmul(a2,v1),v2) into tmp = matmul(a2,v1) r = dot_product(tmp,v2) to enable inlining of matmul (but only if inlining is active, of course). In order to detect multiple uses of matmul, this is run several times. I did this because, with the current implementation, create_var can fail if -fno-realloc-lhs is specified. This is also not optimal, but that's a bug for another day, and I don't see any drawbacks in code generation for this (the extra basic blocks will be removed). The actual overhead in the case of non-constant bounds should be small to non-existent, this only replaces one type of temporary with another. I had to adjust some test cases which counted things to use the library version. Regression-tested. OK for trunk? Regards Thomas P.S: Next on the agenda is to better handle the left-over combinations for Matmul, and to create temporaries for dependencies and function evaluations in the arguments that we currently do not handle. 2017-05-07 Thomas Koenig <tkoe...@gcc.gnu.org> PR fortran/79930 * frontend-passes.c (matmul_to_var_expr): New function, add prototype. (matmul_to_var_code): Likewise. (optimize_namespace): Use them from gfc_code_walker. 2017-05-07 Thomas Koenig <tkoe...@gcc.gnu.org> PR fortran/79930 * gfortran.dg/inline_transpose_1.f90: Add -finline-matmul-limit=0 to options. * gfortran.dg/matmul_5.f90: Likewise. * gfortran.dg/vect/vect-8.f90: Likewise. * gfortran.dg/inline_matmul_14.f90: New test. * gfortran.dg/inline_matmul_15.f90: New test.
Index: fortran/frontend-passes.c =================================================================== --- fortran/frontend-passes.c (Revision 247566) +++ fortran/frontend-passes.c (Arbeitskopie) @@ -43,6 +43,8 @@ static void optimize_reduction (gfc_namespace *); static int callback_reduction (gfc_expr **, int *, void *); static void realloc_strings (gfc_namespace *); static gfc_expr *create_var (gfc_expr *, const char *vname=NULL); +static int matmul_to_var_expr (gfc_expr **, int *, void *); +static int matmul_to_var_code (gfc_code **, int *, void *); static int inline_matmul_assign (gfc_code **, int *, void *); static gfc_code * create_do_loop (gfc_expr *, gfc_expr *, gfc_expr *, locus *, gfc_namespace *, @@ -1076,9 +1078,20 @@ optimize_namespace (gfc_namespace *ns) gfc_code_walker (&ns->code, cfe_code, cfe_expr_0, NULL); gfc_code_walker (&ns->code, optimize_code, optimize_expr, NULL); if (flag_inline_matmul_limit != 0) - gfc_code_walker (&ns->code, inline_matmul_assign, dummy_expr_callback, - NULL); - + { + bool found; + do + { + found = false; + gfc_code_walker (&ns->code, matmul_to_var_code, matmul_to_var_expr, + (void *) &found); + } + while (found); + + gfc_code_walker (&ns->code, inline_matmul_assign, dummy_expr_callback, + NULL); + } + /* BLOCKs are handled in the expression walker below. */ for (ns = ns->contained; ns; ns = ns->sibling) { @@ -2086,6 +2099,64 @@ doloop_warn (gfc_namespace *ns) /* This selction deals with inlining calls to MATMUL. */ +/* Replace calls to matmul outside of straight assignments with a temporary + variable so that later inlining will work. */ + +static int +matmul_to_var_expr (gfc_expr **ep, int *walk_subtrees ATTRIBUTE_UNUSED, + void *data) +{ + gfc_expr *e, *n; + bool *found = (bool *) data; + + e = *ep; + + if (e->expr_type != EXPR_FUNCTION + || e->value.function.isym == NULL + || e->value.function.isym->id != GFC_ISYM_MATMUL) + return 0; + + if (forall_level > 0 || iterator_level > 0 || in_omp_workshare + || in_where) + return 0; + + /* Check if this is already in the form c = matmul(a,b). */ + + if ((*current_code)->expr2 == e) + return 0; + + n = create_var (e, "matmul"); + + /* If create_var is unable to create a variable (for example if + -fno-realloc-lhs is in force with a variable that does not have bounds + known at compile-time), just return. */ + + if (n == NULL) + return 0; + + *ep = n; + *found = true; + return 0; +} + +/* Set current_code and associated variables so that matmul_to_var_expr can + work. */ + +static int +matmul_to_var_code (gfc_code **c, int *walk_subtrees ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) +{ + if (current_code != c) + { + current_code = c; + inserted_block = NULL; + changed_statement = NULL; + } + + return 0; +} + + /* Auxiliary function to build and simplify an array inquiry function. dim is zero-based. */ Index: testsuite/gfortran.dg/inline_transpose_1.f90 =================================================================== --- testsuite/gfortran.dg/inline_transpose_1.f90 (Revision 247566) +++ testsuite/gfortran.dg/inline_transpose_1.f90 (Arbeitskopie) @@ -1,5 +1,5 @@ ! { dg-do run } -! { dg-options "-fdump-tree-original -fdump-tree-optimized -Warray-temporaries -fbounds-check" } +! { dg-options "-finline-matmul-limit=0 -fdump-tree-original -fdump-tree-optimized -Warray-temporaries -fbounds-check" } implicit none Index: testsuite/gfortran.dg/matmul_5.f90 =================================================================== --- testsuite/gfortran.dg/matmul_5.f90 (Revision 247566) +++ testsuite/gfortran.dg/matmul_5.f90 (Arbeitskopie) @@ -1,5 +1,6 @@ ! { dg-do run } ! { dg-shouldfail "dimension of array B incorrect in MATMUL intrinsic" } +! { dg-options "-finline-matmul-limit=0" } program main real, dimension(:,:), allocatable :: a real, dimension(:), allocatable :: b Index: testsuite/gfortran.dg/vect/vect-8.f90 =================================================================== --- testsuite/gfortran.dg/vect/vect-8.f90 (Revision 247566) +++ testsuite/gfortran.dg/vect/vect-8.f90 (Arbeitskopie) @@ -1,5 +1,6 @@ ! { dg-do compile } ! { dg-require-effective-target vect_double } +! { dg-additional-options "-finline-matmul-limit=0" } module lfk_prec integer, parameter :: dp=kind(1.d0)
! { dg-do run } ! { dg-options "-O -ffrontend-optimize -fdump-tree-optimized" } ! PR 79930 - missed optimization by not inlining matmul in expressions. module foo implicit none contains subroutine test1 ! Test with fixed dimensions real, dimension(3,2) :: a1 real, dimension(2,4) :: b1 real, dimension(3,4) :: cres1 real, dimension(3,3) :: a2 real, dimension(3) :: v1, v2 real :: r character(len=9*18) :: r1, r2 real(kind=8), dimension(3,3) :: a3, b3, c3, d3, res3 data a1 / 2., -3., 5., -7., 11., -13./ data b1 /17., -23., 29., -31., 37., -39., 41., -47./ data cres1 /195., -304., 384., 275., -428., 548., 347., -540., 692., 411., -640., 816./ data a2 / 2., -3., 5., -7., 11., -13., 17., -23., 29./ data v1 /-31., 37., -41./ data v2 /43., -47., 53./ data a3/-2.d0, 3.d0, 5.d0, -7.d0, -11.d0, 13.d0, 17.d0, -19.d0, -23.d0/ data b3/29.d0, -31.d0, 37.d0, -41.d0, 43.d0, -47.d0, 53.d0, -59.d0, 61.d0/ data c3/-67.d0,71.d0, 73.d0, -79.d0, -83.d0, 89.d0, 97.d0, -101.d0, 103.d0/ data d3/107.d0, 109.d0, 113.d0, 127.d0, 131.d0, 137.d0, 139.d0, 149.d0, 151.d0/ data res3/48476106.d0, -12727087.d0, -68646789.d0, 58682206.d0, -15428737.d0, -83096539.d0,& & 65359710.d0, -17176589.d0, -92551887.d0/ write (unit=r1, fmt='(12F12.5)') matmul(a1,b1) write (unit=r2, fmt='(12F12.5)') cres1 if (r1 /= r2) call abort r = dot_product(matmul(a2,v1),v2) if (abs(r+208320) > 1) call abort write (unit=r1,fmt='(1P,9E18.10)') matmul(matmul(a3,b3),matmul(c3,d3)) write (unit=r2,fmt='(1P,9E18.10)') res3 if (r1 /= r2) call abort end subroutine test1 subroutine test2 ! Test with dimensions not known at compile-time real, dimension(:,:), allocatable :: a1 real, dimension(:,:), allocatable :: b1 real, dimension(3,4) :: cres1 real, dimension(:,:), allocatable :: a2 real, dimension(:), allocatable :: v1, v2 real :: r character(len=9*18) :: r1, r2 real(kind=8), dimension(3,3) :: a3, b3, c3, d3, res3 data cres1 /195., -304., 384., 275., -428., 548., 347., -540., 692., 411., -640., 816./ data res3/48476106.d0, -12727087.d0, -68646789.d0, 58682206.d0, -15428737.d0, -83096539.d0,& & 65359710.d0, -17176589.d0, -92551887.d0/ a1 = reshape([ 2., -3., 5., -7., 11., -13.], [3,2]) b1 = reshape([17., -23., 29., -31., 37., -39., 41., -47.],[2,4]) a2 = reshape([2., -3., 5., -7., 11., -13., 17., -23., 29.],[3,3]); v1 = [-31., 37., -41.] v2 = [43., -47., 53.] a3 = reshape([-2.d0, 3.d0, 5.d0, -7.d0, -11.d0, 13.d0, 17.d0, -19.d0, -23.d0], [3,3]) b3 = reshape([29.d0, -31.d0, 37.d0, -41.d0, 43.d0, -47.d0, 53.d0, -59.d0, 61.d0], [3,3]) c3 = reshape([-67.d0,71.d0, 73.d0, -79.d0, -83.d0, 89.d0, 97.d0, -101.d0, 103.d0], [3,3]) d3 = reshape([107.d0, 109.d0, 113.d0, 127.d0, 131.d0, 137.d0, 139.d0, 149.d0, 151.d0],[3,3]) write (unit=r1, fmt='(12F12.5)') matmul(a1,b1) write (unit=r2, fmt='(12F12.5)') cres1 if (r1 /= r2) call abort r = dot_product(matmul(a2,v1),v2) if (abs(r+208320) > 1) call abort write (unit=r1,fmt='(1P,9E18.10)') matmul(matmul(a3,b3),matmul(c3,d3)) write (unit=r2,fmt='(1P,9E18.10)') res3 if (r1 /= r2) call abort end subroutine test2 end module foo program main use foo implicit none call test1 call test2 ! call test3 end program main ! { dg-final { scan-tree-dump-times "_gfortran_matmul" 0 "optimized" } }
! { dg-do run } ! { dg-shouldfail "dimension of array B incorrect in MATMUL intrinsic" } ! { dg-options "-O -finline-matmul-limit=100 -fcheck=bounds" } program main real, dimension(:,:), allocatable :: a real, dimension(:), allocatable :: b allocate (a(2,2), b(3)) call random_number(a) call random_number(b) print *,matmul(a,b) end program main ! { dg-output "Fortran runtime error: Dimension of array B incorrect in MATMUL intrinsic.*" }