public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [gfortran,patch] Add MIN/MAX for character type
@ 2007-08-05 12:01 FX Coudert
  2007-08-05 21:00 ` Thomas Koenig
  0 siblings, 1 reply; 3+ messages in thread
From: FX Coudert @ 2007-08-05 12:01 UTC (permalink / raw)
  To: Fortran List, gcc-patches list

[-- Attachment #1: Type: text/plain, Size: 716 bytes --]

Hi all,

Attached patch adds support for MIN and MAX intrinsics on character- 
type expressions, which is part of F2003. It is rather  
straightforward, comes with a simplification routine and a library  
implementation. The most difficult part, in my opinion, is the trans- 
intrinsic.c part: since MIN/MAX accept a variable number of arguments  
(and they're the only intrinsics to do so), it requires special  
handling to create the library function call. I will also mention  
that the patch correctly takes into account the possibility of  
optional arguments.

Bootstrapped and regtested on x86_64-linux, comes with two testcases  
(one for valid code and one for runtime error checking). OK to commit?

FX



[-- Attachment #2: minmax_char.ChangeLog --]
[-- Type: application/octet-stream, Size: 978 bytes --]

2007-08-05  Francois-Xavier Coudert  <fxcoudert@gcc.gnu.org>

	PR fortran/29828
	* trans.h (gfor_fndecl_string_minmax): New prototype.
	* trans-decl.c (gfor_fndecl_string_minmax): New variable.
	(gfc_build_intrinsic_function_decls): Create gfor_fndecl_string_minmax.
	* check.c (gfc_check_min_max): Allow for character arguments.
	* trans-intrinsic.c (gfc_conv_intrinsic_minmax_char): New function.
	(gfc_conv_intrinsic_function): Add special case for MIN and MAX
	intrinsics with character arguments.
	* simplify.c (simplify_min_max): Add simplification for character
	arguments.


2007-08-05  Francois-Xavier Coudert  <fxcoudert@gcc.gnu.org>

	PR fortran/29828
	* intrinsics/string_intrinsics.c (string_minmax): New function
	and prototype.
	* gfortran.map (GFORTRAN_1.0): Add _gfortran_string_minmax


2007-08-05  Francois-Xavier Coudert  <fxcoudert@gcc.gnu.org>

	PR fortran/29828
	* gfortran.dg/minmax_char_1.f90: New test.
	* gfortran.dg/min_max_optional_4.f90: New test.

[-- Attachment #3: minmax_char.diff --]
[-- Type: application/octet-stream, Size: 12981 bytes --]

Index: gcc/fortran/trans.h
===================================================================
--- gcc/fortran/trans.h	(revision 127224)
+++ gcc/fortran/trans.h	(working copy)
@@ -540,6 +540,7 @@ extern GTY(()) tree gfor_fndecl_string_i
 extern GTY(()) tree gfor_fndecl_string_scan;
 extern GTY(()) tree gfor_fndecl_string_verify;
 extern GTY(()) tree gfor_fndecl_string_trim;
+extern GTY(()) tree gfor_fndecl_string_minmax;
 extern GTY(()) tree gfor_fndecl_adjustl;
 extern GTY(()) tree gfor_fndecl_adjustr;
 
Index: gcc/fortran/trans-decl.c
===================================================================
--- gcc/fortran/trans-decl.c	(revision 127224)
+++ gcc/fortran/trans-decl.c	(working copy)
@@ -125,6 +125,7 @@ tree gfor_fndecl_string_index;
 tree gfor_fndecl_string_scan;
 tree gfor_fndecl_string_verify;
 tree gfor_fndecl_string_trim;
+tree gfor_fndecl_string_minmax;
 tree gfor_fndecl_adjustl;
 tree gfor_fndecl_adjustr;
 
@@ -2047,6 +2048,13 @@ gfc_build_intrinsic_function_decls (void
                                      gfc_charlen_type_node,
                                      pchar_type_node);
 
+  gfor_fndecl_string_minmax = 
+    gfc_build_library_function_decl (get_identifier (PREFIX("string_minmax")),
+                                     void_type_node, -4,
+                                     build_pointer_type (gfc_charlen_type_node),
+                                     ppvoid_type_node, integer_type_node,
+                                     integer_type_node);
+
   gfor_fndecl_ttynam =
     gfc_build_library_function_decl (get_identifier (PREFIX("ttynam")),
                                      void_type_node,
Index: gcc/fortran/check.c
===================================================================
--- gcc/fortran/check.c	(revision 127224)
+++ gcc/fortran/check.c	(working copy)
@@ -1512,10 +1512,11 @@ gfc_check_min_max (gfc_actual_arglist *a
 
   x = arg->expr;
 
-  if (x->ts.type != BT_INTEGER && x->ts.type != BT_REAL)
+  if (x->ts.type != BT_INTEGER && x->ts.type != BT_REAL
+      && x->ts.type != BT_CHARACTER)
     {
-      gfc_error ("'a1' argument of '%s' intrinsic at %L must be INTEGER "
-		 "or REAL", gfc_current_intrinsic, &x->where);
+      gfc_error ("'a1' argument of '%s' intrinsic at %L must be INTEGER, "
+		 "REAL or CHARACTER", gfc_current_intrinsic, &x->where);
       return FAILURE;
     }
 
Index: gcc/fortran/trans-intrinsic.c
===================================================================
--- gcc/fortran/trans-intrinsic.c	(revision 127224)
+++ gcc/fortran/trans-intrinsic.c	(working copy)
@@ -1561,6 +1561,45 @@ gfc_conv_intrinsic_minmax (gfc_se * se, 
 }
 
 
+/* Generate library calls for MIN and MAX intrinsics for character
+   variables.  */
+static void
+gfc_conv_intrinsic_minmax_char (gfc_se * se, gfc_expr * expr, int op)
+{
+  tree *args;
+  tree var, len, fndecl, tmp, cond;
+  unsigned int nargs;
+
+  nargs = gfc_intrinsic_argument_list_length (expr);
+  args = alloca (sizeof (tree) * (nargs + 4));
+  gfc_conv_intrinsic_function_args (se, expr, &args[4], nargs);
+
+  /* Create the result variables.  */
+  len = gfc_create_var (gfc_charlen_type_node, "len");
+  args[0] = build_fold_addr_expr (len);
+  var = gfc_create_var (build_pointer_type (gfc_character1_type_node), "pstr");
+  args[1] = gfc_build_addr_expr (ppvoid_type_node, var);
+  args[2] = build_int_cst (NULL_TREE, op);
+  args[3] = build_int_cst (NULL_TREE, nargs / 2);
+
+  /* Make the function call.  */
+  fndecl = build_addr (gfor_fndecl_string_minmax, current_function_decl);
+  tmp = build_call_array (TREE_TYPE (TREE_TYPE (gfor_fndecl_string_minmax)),
+			  fndecl, nargs + 4, args);
+  gfc_add_expr_to_block (&se->pre, tmp);
+
+  /* Free the temporary afterwards, if necessary.  */
+  cond = build2 (GT_EXPR, boolean_type_node, len,
+		 build_int_cst (TREE_TYPE (len), 0));
+  tmp = gfc_call_free (var);
+  tmp = build3_v (COND_EXPR, cond, tmp, build_empty_stmt ());
+  gfc_add_expr_to_block (&se->post, tmp);
+
+  se->expr = var;
+  se->string_length = len;
+}
+
+
 /* Create a symbol node for this intrinsic.  The symbol from the frontend
    has the generic name.  */
 
@@ -4058,7 +4097,10 @@ gfc_conv_intrinsic_function (gfc_se * se
       break;
 
     case GFC_ISYM_MAX:
-      gfc_conv_intrinsic_minmax (se, expr, GT_EXPR);
+      if (expr->ts.type == BT_CHARACTER)
+	gfc_conv_intrinsic_minmax_char (se, expr, 1);
+      else
+	gfc_conv_intrinsic_minmax (se, expr, GT_EXPR);
       break;
 
     case GFC_ISYM_MAXLOC:
@@ -4074,7 +4116,10 @@ gfc_conv_intrinsic_function (gfc_se * se
       break;
 
     case GFC_ISYM_MIN:
-      gfc_conv_intrinsic_minmax (se, expr, LT_EXPR);
+      if (expr->ts.type == BT_CHARACTER)
+	gfc_conv_intrinsic_minmax_char (se, expr, -1);
+      else
+	gfc_conv_intrinsic_minmax (se, expr, LT_EXPR);
       break;
 
     case GFC_ISYM_MINLOC:
Index: gcc/fortran/simplify.c
===================================================================
--- gcc/fortran/simplify.c	(revision 127224)
+++ gcc/fortran/simplify.c	(working copy)
@@ -2361,7 +2361,6 @@ simplify_min_max (gfc_expr *expr, int si
 	  if (mpz_cmp (arg->expr->value.integer,
 		       extremum->expr->value.integer) * sign > 0)
 	    mpz_set (extremum->expr->value.integer, arg->expr->value.integer);
-
 	  break;
 
 	case BT_REAL:
@@ -2369,11 +2368,40 @@ simplify_min_max (gfc_expr *expr, int si
 	      * sign > 0)
 	    mpfr_set (extremum->expr->value.real, arg->expr->value.real,
 		      GFC_RND_MODE);
+	  break;
+
+	case BT_CHARACTER:
+#define LENGTH(x) ((x)->expr->value.character.length)
+#define STRING(x) ((x)->expr->value.character.string)
+	  if (LENGTH(extremum) < LENGTH(arg))
+	    {
+	      char * tmp = STRING(extremum);
 
+	      STRING(extremum) = gfc_getmem (LENGTH(arg) + 1);
+	      memcpy (STRING(extremum), tmp, LENGTH(extremum));
+	      memset (&STRING(extremum)[LENGTH(extremum)], ' ',
+		      LENGTH(arg) - LENGTH(extremum));
+	      STRING(extremum)[LENGTH(arg)] = '\0';  /* For debugger  */
+	      LENGTH(extremum) = LENGTH(arg);
+	      gfc_free (tmp);
+	    }
+
+	  if (gfc_compare_string (arg->expr, extremum->expr) * sign > 0)
+	    {
+	      gfc_free (STRING(extremum));
+	      STRING(extremum) = gfc_getmem (LENGTH(extremum) + 1);
+	      memcpy (STRING(extremum), STRING(arg), LENGTH(arg));
+	      memset (&STRING(extremum)[LENGTH(arg)], ' ',
+		      LENGTH(extremum) - LENGTH(arg));
+	      STRING(extremum)[LENGTH(extremum)] = '\0';  /* For debugger  */
+	    }
+#undef LENGTH
+#undef STRING
 	  break;
+	      
 
 	default:
-	  gfc_internal_error ("gfc_simplify_max(): Bad type in arglist");
+	  gfc_internal_error ("simplify_min_max(): Bad type in arglist");
 	}
 
       /* Delete the extra constant argument.  */
Index: libgfortran/intrinsics/string_intrinsics.c
===================================================================
--- libgfortran/intrinsics/string_intrinsics.c	(revision 127224)
+++ libgfortran/intrinsics/string_intrinsics.c	(working copy)
@@ -1,5 +1,5 @@
 /* String intrinsics helper functions.
-   Copyright 2002, 2005 Free Software Foundation, Inc.
+   Copyright 2002, 2005, 2007 Free Software Foundation, Inc.
    Contributed by Paul Brook <paul@nowt.org>
 
 This file is part of the GNU Fortran 95 runtime library (libgfortran).
@@ -38,6 +38,7 @@ Boston, MA 02110-1301, USA.  */
 
 #include <stdlib.h>
 #include <string.h>
+#include <stdarg.h>
 
 #include "libgfortran.h"
 
@@ -73,6 +74,9 @@ export_proto(string_verify);
 extern void string_trim (GFC_INTEGER_4 *, void **, GFC_INTEGER_4, const char *);
 export_proto(string_trim);
 
+extern void string_minmax (GFC_INTEGER_4 *, void **, int, int, ...);
+export_proto(string_minmax);
+
 /* Strings of unequal length are extended with pad characters.  */
 
 GFC_INTEGER_4
@@ -351,3 +355,62 @@ string_verify (GFC_INTEGER_4 slen, const
 
   return 0;
 }
+
+
+/* MIN and MAX intrinsics for strings.  The front-end makes sure that
+   nargs is at least 2.  */
+
+void
+string_minmax (GFC_INTEGER_4 *rlen, void **dest, int op, int nargs, ...)
+{
+  va_list ap;
+  int i;
+  char * next, * res;
+  GFC_INTEGER_4 nextlen, reslen;
+
+  va_start (ap, nargs);
+  reslen = va_arg (ap, GFC_INTEGER_4);
+  res = va_arg (ap, char *);
+  *rlen = reslen;
+
+  if (res == NULL)
+    runtime_error ("First argument of '%s' intrinsic should be present",
+		   op > 0 ? "MAX" : "MIN");
+
+  for (i = 1; i < nargs; i++)
+    {
+      nextlen = va_arg (ap, GFC_INTEGER_4);
+      next = va_arg (ap, char *);
+
+
+      if (next == NULL)
+	{
+	  if (i == 1)
+	    runtime_error ("Second argument of '%s' intrinsic should be "
+			   "present", op > 0 ? "MAX" : "MIN");
+	  else
+	    continue;
+	}
+
+      if (nextlen > *rlen)
+	*rlen = nextlen;
+
+      if (op * compare_string (reslen, res, nextlen, next) < 0)
+	{
+	  reslen = nextlen;
+	  res = next;
+	}
+    }
+  va_end (ap);
+
+  if (*rlen > 0)
+    {
+      char * tmp = internal_malloc_size (*rlen);
+      memcpy (tmp, res, reslen);
+      memset (&tmp[reslen], ' ', *rlen - reslen);
+      *dest = tmp;
+    }
+  else
+    *dest = NULL;
+}
+
Index: libgfortran/gfortran.map
===================================================================
--- libgfortran/gfortran.map	(revision 127224)
+++ libgfortran/gfortran.map	(working copy)
@@ -941,6 +941,7 @@ GFORTRAN_1.0 {
     _gfortran_st_rewind;
     _gfortran_string_index;
     _gfortran_string_len_trim;
+    _gfortran_string_minmax;
     _gfortran_string_scan;
     _gfortran_string_trim;
     _gfortran_string_verify;
Index: gcc/testsuite/gfortran.dg/minmax_char_1.f90
===================================================================
--- gcc/testsuite/gfortran.dg/minmax_char_1.f90	(revision 0)
+++ gcc/testsuite/gfortran.dg/minmax_char_1.f90	(revision 0)
@@ -0,0 +1,73 @@
+! Tests for MIN and MAX intrinsics with character arguments
+!
+! { dg-do run }
+program test
+  character(len=3), parameter :: sp = "gee"
+  character(len=6), parameter :: tp = "crunch", wp = "flunch"
+  character(len=2), parameter :: up = "az", vp = "da"
+
+  character(len=3) :: s
+  character(len=6) :: t, w
+  character(len=2) :: u, v
+  s = "gee"
+  t = "crunch"
+  u = "az"
+  v = "da"
+  w = "flunch"
+
+  if (.not. equal(min("foo", "bar"), "bar")) call abort
+  if (.not. equal(max("foo", "bar"), "foo")) call abort
+  if (.not. equal(min("bar", "foo"), "bar")) call abort
+  if (.not. equal(max("bar", "foo"), "foo")) call abort
+
+  if (.not. equal(min("bar", "foo", sp), "bar")) call abort
+  if (.not. equal(max("bar", "foo", sp), "gee")) call abort
+  if (.not. equal(min("bar", sp, "foo"), "bar")) call abort
+  if (.not. equal(max("bar", sp, "foo"), "gee")) call abort
+  if (.not. equal(min(sp, "bar", "foo"), "bar")) call abort
+  if (.not. equal(max(sp, "bar", "foo"), "gee")) call abort
+
+  if (.not. equal(min("foo", "bar", s), "bar")) call abort
+  if (.not. equal(max("foo", "bar", s), "gee")) call abort
+  if (.not. equal(min("foo", s, "bar"), "bar")) call abort
+  if (.not. equal(max("foo", s, "bar"), "gee")) call abort
+  if (.not. equal(min(s, "foo", "bar"), "bar")) call abort
+  if (.not. equal(max(s, "foo", "bar"), "gee")) call abort
+
+  if (.not. equal(min("", ""), "")) call abort
+  if (.not. equal(max("", ""), "")) call abort
+  if (.not. equal(min("", " "), " ")) call abort
+  if (.not. equal(max("", " "), " ")) call abort
+
+  if (.not. equal(min(u,v,w), "az    ")) call abort
+  if (.not. equal(max(u,v,w), "flunch")) call abort
+  if (.not. equal(min(u,vp,w), "az    ")) call abort
+  if (.not. equal(max(u,vp,w), "flunch")) call abort
+  if (.not. equal(min(u,v,wp), "az    ")) call abort
+  if (.not. equal(max(u,v,wp), "flunch")) call abort
+  if (.not. equal(min(up,v,w), "az    ")) call abort
+  if (.not. equal(max(up,v,w), "flunch")) call abort
+
+  call foo("gee   ","az    ",s,t,u,v)
+  call foo("gee   ","az    ",s,t,u,v)
+  call foo("gee   ","az    ",s,t,u)
+  call foo("gee   ","crunch",s,t)
+
+contains
+
+  subroutine foo(res_max, res_min, a, b, c, d)
+    character(len=*) :: res_min, res_max
+    character(len=*), optional :: a, b, c, d
+
+    if (.not. equal(min(a,b,c,d), res_min)) call abort
+    if (.not. equal(max(a,b,c,d), res_max)) call abort
+  end subroutine foo
+
+  pure function equal(a,b)
+    character(len=*), intent(in) :: a, b
+    logical :: equal
+
+    equal = (len(a) == len(b)) .and. (a == b)
+  end function equal
+
+end program test
Index: gcc/testsuite/gfortran.dg/min_max_optional_4.f90
===================================================================
--- gcc/testsuite/gfortran.dg/min_max_optional_4.f90	(revision 0)
+++ gcc/testsuite/gfortran.dg/min_max_optional_4.f90	(revision 0)
@@ -0,0 +1,12 @@
+! { dg-do run }
+! { dg-shouldfail "" }
+program test
+  call foo("foo")
+contains
+  subroutine foo(a, b, c, d)
+    character(len=*), optional :: a, b, c, d
+    integer :: i
+    i = len_trim(min(a,b,c,d)) ! { dg-output "Second argument of 'MIN' intrinsic should be present" }
+    print *, i
+  end subroutine foo
+end

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [gfortran,patch] Add MIN/MAX for character type
  2007-08-05 12:01 [gfortran,patch] Add MIN/MAX for character type FX Coudert
@ 2007-08-05 21:00 ` Thomas Koenig
  2007-08-06 20:49   ` FX Coudert
  0 siblings, 1 reply; 3+ messages in thread
From: Thomas Koenig @ 2007-08-05 21:00 UTC (permalink / raw)
  To: FX Coudert; +Cc: Fortran List, gcc-patches list

On Sun, 2007-08-05 at 13:00 +0100, FX Coudert wrote:

Hi FX,

> Attached patch adds support for MIN and MAX intrinsics on character- 
> type expressions, which is part of F2003. 

Great!

> Bootstrapped and regtested on x86_64-linux, comes with two testcases  
> (one for valid code and one for runtime error checking). OK to commit?

There should be errors/warnings for this feature, as for the others
F2003 features, for -std=f95.

OK if you add that.

Thanks!

	Thomas

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [gfortran,patch] Add MIN/MAX for character type
  2007-08-05 21:00 ` Thomas Koenig
@ 2007-08-06 20:49   ` FX Coudert
  0 siblings, 0 replies; 3+ messages in thread
From: FX Coudert @ 2007-08-06 20:49 UTC (permalink / raw)
  To: Thomas Koenig; +Cc: Fortran List, gcc-patches list

> There should be errors/warnings for this feature, as for the others
> F2003 features, for -std=f95.
>
> OK if you add that.

Committed as rev. 127252 after adding the standard-check and  
regtesting on x86_64-linux: http://gcc.gnu.org/ml/gcc-cvs/2007-08/ 
msg00144.html

Thanks for the review,
FX

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2007-08-06 20:49 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-08-05 12:01 [gfortran,patch] Add MIN/MAX for character type FX Coudert
2007-08-05 21:00 ` Thomas Koenig
2007-08-06 20:49   ` FX Coudert

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).