OSDN Git Service

2010-07-25 Thomas Koenig <tkoenig@gcc.gnu.org>
authortkoenig <tkoenig@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 25 Jul 2010 19:31:37 +0000 (19:31 +0000)
committertkoenig <tkoenig@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 25 Jul 2010 19:31:37 +0000 (19:31 +0000)
PR fortran/40628
* Make-lang.in:  Add fortran/frontend-passes.o.
* gfortran.h:  Add prototype for gfc_run_passes.
* resolve.c (gfc_resolve):  Call gfc_run_passes.
* frontend-passes.c:  New file.

2010-07-25  Thomas Koenig  <tkoenig@gcc.gnu.org>

PR fortran/40628
* trim_optimize_1.f90:  New test.
* character_comparision_1.f90:  New test.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@162519 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/fortran/ChangeLog
gcc/fortran/Make-lang.in
gcc/fortran/frontend-passes.c [new file with mode: 0644]
gcc/fortran/gfortran.h
gcc/fortran/resolve.c
gcc/testsuite/ChangeLog
gcc/testsuite/gfortran.dg/character_comparison_1.f90 [new file with mode: 0644]
gcc/testsuite/gfortran.dg/trim_optimize_1.f90 [new file with mode: 0644]

index 7869d51..f29ccca 100644 (file)
@@ -1,3 +1,11 @@
+2010-07-25  Thomas Koenig  <tkoenig@gcc.gnu.org>
+
+       PR fortran/40628
+       * Make-lang.in:  Add fortran/frontend-passes.o.
+       * gfortran.h:  Add prototype for gfc_run_passes.
+       * resolve.c (gfc_resolve):  Call gfc_run_passes.
+       * frontend-passes.c:  New file.
+
 2010-07-25  Jerry DeLisle  <jvdelisle@gcc.gnu.org>
 
        PR fortran/42852
index 2a8c791..02b4c95 100644 (file)
@@ -66,7 +66,7 @@ F95_OBJS = $(F95_PARSER_OBJS) $(FORTRAN_TARGET_OBJS) \
     fortran/trans.o fortran/trans-array.o fortran/trans-common.o \
     fortran/trans-const.o fortran/trans-decl.o fortran/trans-expr.o \
     fortran/trans-intrinsic.o fortran/trans-io.o fortran/trans-openmp.o \
-    fortran/trans-stmt.o fortran/trans-types.o
+    fortran/trans-stmt.o fortran/trans-types.o fortran/frontend-passes.o
 
 fortran_OBJS = $(F95_OBJS) gfortranspec.o
 
diff --git a/gcc/fortran/frontend-passes.c b/gcc/fortran/frontend-passes.c
new file mode 100644 (file)
index 0000000..83251cc
--- /dev/null
@@ -0,0 +1,435 @@
+/* Pass manager for Fortran front end.
+   Copyright (C) 2010 Free Software Foundation, Inc.
+   Contributed by Thomas König.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "gfortran.h"
+#include "arith.h"
+#include "flags.h"
+
+/* Forward declarations.  */
+
+static void strip_function_call (gfc_expr *);
+static void optimize_assignment (gfc_code *);
+static void optimize_expr_0 (gfc_expr *);
+static bool optimize_expr (gfc_expr *);
+static bool optimize_op (gfc_expr *);
+static bool optimize_equality (gfc_expr *, bool);
+static void optimize_code (gfc_code *);
+static void optimize_code_node (gfc_code *);
+static void optimize_actual_arglist (gfc_actual_arglist *);
+
+/* Entry point - run all passes for a namespace.  So far, only an
+   optimization pass is run.  */
+
+void
+gfc_run_passes (gfc_namespace * ns)
+{
+  if (optimize)
+    optimize_code (ns->code);
+}
+
+static void
+optimize_code (gfc_code *c)
+{
+  for (; c; c = c->next)
+    optimize_code_node (c);
+}
+
+
+/* Do the optimizations for a code node.  */
+
+static void
+optimize_code_node (gfc_code *c)
+{
+
+  gfc_forall_iterator *fa;
+  gfc_code *d;
+  gfc_alloc *a;
+
+  switch (c->op)
+    {
+    case EXEC_ASSIGN:
+      optimize_assignment (c);
+      break;
+
+    case EXEC_CALL:
+    case EXEC_ASSIGN_CALL:
+    case EXEC_CALL_PPC:
+      optimize_actual_arglist (c->ext.actual);
+      break;
+
+    case EXEC_ARITHMETIC_IF:
+      optimize_expr_0 (c->expr1);
+      break;
+
+    case EXEC_PAUSE:
+    case EXEC_RETURN:
+    case EXEC_ERROR_STOP:
+    case EXEC_STOP:
+    case EXEC_COMPCALL:
+      optimize_expr_0 (c->expr1);
+      break;
+
+    case EXEC_SYNC_ALL:
+    case EXEC_SYNC_MEMORY:
+    case EXEC_SYNC_IMAGES:
+      optimize_expr_0 (c->expr2);
+      break;
+
+    case EXEC_IF:
+      d = c->block;
+      optimize_expr_0 (d->expr1);
+      optimize_code (d->next);
+
+      for (d = d->block; d; d = d->block)
+       {
+         optimize_expr_0 (d->expr1);
+
+         optimize_code (d->next);
+       }
+
+
+      break;
+
+    case EXEC_SELECT:
+    case EXEC_SELECT_TYPE:
+      d = c->block;
+
+      optimize_expr_0 (c->expr1);
+
+      for (; d; d = d->block)
+       optimize_code (d->next);
+
+      break;
+
+    case EXEC_WHERE:
+      d = c->block;
+      optimize_expr_0 (d->expr1);
+      optimize_code (d->next);
+
+      for (d = d->block; d; d = d->block)
+       {
+         optimize_expr_0 (d->expr1);
+         optimize_code (d->next);
+       }
+      break;
+
+    case EXEC_FORALL:
+
+      for (fa = c->ext.forall_iterator; fa; fa = fa->next)
+       {
+         optimize_expr_0 (fa->start);
+         optimize_expr_0 (fa->end);
+         optimize_expr_0 (fa->stride);
+       }
+
+      if (c->expr1 != NULL)
+         optimize_expr_0 (c->expr1);
+
+      optimize_code (c->block->next);
+
+      break;
+
+    case EXEC_CRITICAL:
+      optimize_code (c->block->next);
+      break;
+
+    case EXEC_DO:
+      optimize_expr_0 (c->ext.iterator->start);
+      optimize_expr_0 (c->ext.iterator->end);
+      optimize_expr_0 (c->ext.iterator->step);
+      optimize_code (c->block->next);
+
+      break;
+
+    case EXEC_DO_WHILE:
+      optimize_expr_0 (c->expr1);
+      optimize_code (c->block->next);
+      break;
+
+
+    case EXEC_ALLOCATE:
+      for (a = c->ext.alloc.list; a; a = a->next)
+         optimize_expr_0 (a->expr);
+      break;
+
+      /* Todo:  Some of these may need to be optimized, as well.  */
+    case EXEC_WRITE:
+    case EXEC_READ:
+    case EXEC_OPEN:
+    case EXEC_INQUIRE:
+    case EXEC_REWIND:
+    case EXEC_ENDFILE:
+    case EXEC_BACKSPACE:
+    case EXEC_CLOSE:
+    case EXEC_WAIT:
+    case EXEC_TRANSFER:
+    case EXEC_FLUSH:
+    case EXEC_IOLENGTH:
+    case EXEC_END_PROCEDURE:
+    case EXEC_NOP:
+    case EXEC_CONTINUE:
+    case EXEC_ENTRY:
+    case EXEC_INIT_ASSIGN:
+    case EXEC_LABEL_ASSIGN:
+    case EXEC_POINTER_ASSIGN:
+    case EXEC_GOTO:
+    case EXEC_CYCLE:
+    case EXEC_EXIT:
+    case EXEC_BLOCK:
+    case EXEC_END_BLOCK:
+    case EXEC_OMP_ATOMIC:
+    case EXEC_OMP_BARRIER:
+    case EXEC_OMP_CRITICAL:
+    case EXEC_OMP_FLUSH:
+    case EXEC_OMP_DO:
+    case EXEC_OMP_MASTER:
+    case EXEC_OMP_ORDERED:
+    case EXEC_OMP_PARALLEL:
+    case EXEC_OMP_PARALLEL_DO:
+    case EXEC_OMP_PARALLEL_SECTIONS:
+    case EXEC_OMP_PARALLEL_WORKSHARE:
+    case EXEC_OMP_SECTIONS:
+    case EXEC_OMP_SINGLE:
+    case EXEC_OMP_TASK:
+    case EXEC_OMP_TASKWAIT:
+    case EXEC_OMP_WORKSHARE:
+    case EXEC_DEALLOCATE:
+      
+      break;
+
+    default:
+      gcc_unreachable ();
+
+    }
+}
+
+/* Optimizations for an assignment.  */
+
+static void
+optimize_assignment (gfc_code * c)
+{
+  gfc_expr *lhs, *rhs;
+
+  lhs = c->expr1;
+  rhs = c->expr2;
+
+  /* Optimize away a = trim(b), where a is a character variable.  */
+
+  if (lhs->ts.type == BT_CHARACTER)
+    {
+      if (rhs->expr_type == EXPR_FUNCTION &&
+         rhs->value.function.isym &&
+         rhs->value.function.isym->id == GFC_ISYM_TRIM)
+       {
+         strip_function_call (rhs);
+         optimize_assignment (c);
+         return;
+       }
+    }
+
+  /* All direct optimizations have been done.  Now it's time
+     to optimize the rhs.  */
+
+  optimize_expr_0 (rhs);
+}
+
+
+/* Remove an unneeded function call, modifying the expression.
+   This replaces the function call with the value of its
+   first argument.  The rest of the argument list is freed.  */
+
+static void
+strip_function_call (gfc_expr *e)
+{
+  gfc_expr *e1;
+  gfc_actual_arglist *a;
+
+  a = e->value.function.actual;
+
+  /* We should have at least one argument.  */
+  gcc_assert (a->expr != NULL);
+
+  e1 = a->expr;
+
+  /* Free the remaining arglist, if any.  */
+  if (a->next)
+    gfc_free_actual_arglist (a->next);
+
+  /* Graft the argument expression onto the original function.  */
+  *e = *e1;
+  gfc_free (e1);
+
+}
+
+/* Top-level optimization of expressions.  Calls gfc_simplify_expr if
+   optimize_expr succeeds in doing something.
+   TODO: Optimization of multiple function occurrence to come here.  */
+
+static void
+optimize_expr_0 (gfc_expr * e)
+{
+  if (optimize_expr (e))
+    gfc_simplify_expr (e, 0);
+
+  return;
+}
+
+/* Recursive optimization of expressions.
+ TODO:  Make this handle many more things.  */
+
+static bool
+optimize_expr (gfc_expr *e)
+{
+  bool ret;
+
+  if (e == NULL)
+    return false;
+
+  ret = false;
+
+  switch (e->expr_type)
+    {
+    case EXPR_OP:
+      return optimize_op (e);
+      break;
+
+    case EXPR_FUNCTION:
+      optimize_actual_arglist (e->value.function.actual);
+      break;
+
+    default:
+      break;
+    }
+
+  return ret;
+}
+
+/* Recursive optimization of operators.  */
+
+static bool
+optimize_op (gfc_expr *e)
+{
+
+  gfc_intrinsic_op op;
+
+  op = e->value.op.op;
+
+  switch (op)
+    {
+    case INTRINSIC_EQ:
+    case INTRINSIC_EQ_OS:
+    case INTRINSIC_GE:
+    case INTRINSIC_GE_OS:
+    case INTRINSIC_LE:
+    case INTRINSIC_LE_OS:
+      return optimize_equality (e, true);
+      break;
+
+    case INTRINSIC_NE:
+    case INTRINSIC_NE_OS:
+    case INTRINSIC_GT:
+    case INTRINSIC_GT_OS:
+    case INTRINSIC_LT:
+    case INTRINSIC_LT_OS:
+      return optimize_equality (e, false);
+      break;
+
+    default:
+      break;
+    }
+
+  return false;
+}
+
+/* Optimize expressions for equality.  */
+
+static bool
+optimize_equality (gfc_expr *e, bool equal)
+{
+
+  gfc_expr *op1, *op2;
+  bool change;
+
+  op1 = e->value.op.op1;
+  op2 = e->value.op.op2;
+
+  /* Strip off unneeded TRIM calls from string comparisons.  */
+
+  change = false;
+
+  if (op1->expr_type == EXPR_FUNCTION 
+      && op1->value.function.isym
+      && op1->value.function.isym->id == GFC_ISYM_TRIM)
+    {
+      strip_function_call (op1);
+      change = true;
+    }
+
+  if (op2->expr_type == EXPR_FUNCTION 
+      && op2->value.function.isym
+      && op2->value.function.isym->id == GFC_ISYM_TRIM)
+    {
+      strip_function_call (op2);
+      change = true;
+    }
+
+  if (change)
+    {
+      optimize_equality (e, equal);
+      return true;
+    }
+
+  /* Check for direct comparison between identical variables.
+     TODO: Handle cases with identical refs.  */
+  if (op1->expr_type == EXPR_VARIABLE
+      && op2->expr_type == EXPR_VARIABLE
+      && op1->symtree == op2->symtree
+      && op1->ref == NULL && op2->ref == NULL
+      && op1->ts.type != BT_REAL && op2->ts.type != BT_REAL
+      && op1->ts.type != BT_COMPLEX && op2->ts.type !=BT_COMPLEX)
+    {
+      /* Replace the expression by a constant expression.  The typespec
+        and where remains the way it is.  */
+      gfc_free (op1);
+      gfc_free (op2);
+      e->expr_type = EXPR_CONSTANT;
+      e->value.logical = equal;
+      return true;
+    }
+  return false;
+}
+
+/* Optimize a call list.  Right now, this just goes through the actual
+   arg list and optimizes each expression in turn.  */
+
+static void
+optimize_actual_arglist (gfc_actual_arglist *a)
+{
+
+  for (; a; a = a->next)
+    {
+      if (a->expr != NULL)
+       optimize_expr_0 (a->expr);
+    }
+  
+  return;
+}
index 70cc4fd..bb056e2 100644 (file)
@@ -2842,4 +2842,8 @@ gfc_symtree* gfc_get_tbp_symtree (gfc_symtree**, const char*);
 
 #define CLASS_DATA(sym) sym->ts.u.derived->components
 
+/* frontend-passes.c */
+
+void gfc_run_passes (gfc_namespace *);
+
 #endif /* GCC_GFORTRAN_H  */
index a938ab3..fb9aadc 100644 (file)
@@ -13081,4 +13081,6 @@ gfc_resolve (gfc_namespace *ns)
   gfc_current_ns = old_ns;
   cs_base = old_cs_base;
   ns->resolved = 1;
+
+  gfc_run_passes (ns);
 }
index 5771a06..1489b66 100644 (file)
@@ -1,3 +1,9 @@
+2010-07-25  Thomas Koenig  <tkoenig@gcc.gnu.org>
+
+       PR fortran/40628
+       * trim_optimize_1.f90:  New test.
+       * character_comparision_1.f90:  New test.
+
 2010-07-25  Jerry DeLisle  <jvdelisle@gcc.gnu.org>
 
        PR fortran/42852
diff --git a/gcc/testsuite/gfortran.dg/character_comparison_1.f90 b/gcc/testsuite/gfortran.dg/character_comparison_1.f90
new file mode 100644 (file)
index 0000000..d34af30
--- /dev/null
@@ -0,0 +1,37 @@
+! { dg-do run }
+! { dg-options "-O -fdump-tree-original" }
+program main
+  implicit none
+  character(len=4) :: c
+  integer :: n
+  integer :: i
+  common /foo/ i
+
+  n = 0
+  i = 0
+  c = 'abcd'
+  n = n + 1 ; if (c == c) call yes
+  n = n + 1 ; if (c >= c) call yes
+  n = n + 1 ; if (c <= c) call yes
+  n = n + 1 ; if (c .eq. c) call yes
+  n = n + 1 ; if (c .ge. c) call yes
+  n = n + 1 ; if (c .le. c) call yes
+  if (c /= c) call abort
+  if (c > c) call abort
+  if (c < c) call abort
+  if (c .ne. c) call abort
+  if (c .gt. c) call abort
+  if (c .lt. c) call abort
+  if (n /= i) call abort
+end program main
+
+subroutine yes
+  implicit none
+  common /foo/ i
+  integer :: i
+  i = i + 1
+end subroutine yes
+
+! { dg-final { scan-tree-dump-times "gfortran_compare_string" 0 "original" } }
+! { dg-final { cleanup-tree-dump "original" } }
+
diff --git a/gcc/testsuite/gfortran.dg/trim_optimize_1.f90 b/gcc/testsuite/gfortran.dg/trim_optimize_1.f90
new file mode 100644 (file)
index 0000000..26aa5cd
--- /dev/null
@@ -0,0 +1,16 @@
+! { dg-do run }
+! { dg-options "-O -fdump-tree-original" }
+! PR 40628 - optimize unnecessary TRIMs on assignment
+program main
+  character(len=3) :: a
+  character(len=4) :: b,c
+  b = 'abcd'
+  a = trim(b)
+  c = trim(trim(a))
+  if (a /= 'abc') call abort
+  if (c /= 'abc') call abort
+end program main
+
+! { dg-final { scan-tree-dump-times "memmove" 2 "original" } }
+! { dg-final { scan-tree-dump-times "string_trim" 0 "original" } }
+! { dg-final { cleanup-tree-dump "original" } }