Subject: bin/7735: Compiler generates wrong code
To: None <gnats-bugs@gnats.netbsd.org>
From: Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
List: netbsd-bugs
Date: 06/08/1999 02:51:12
>Number:         7735
>Category:       bin
>Synopsis:       Compiler generates wrong code
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    bin-bug-people (Utility Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Jun  8 02:50:01 1999
>Last-Modified:
>Originator:     Juergen Hannken-Illjes
>Organization:
	
>Release:        <NetBSD-current source date>-current as of Fri Jun 4
>Environment:
	
System: NetBSD toaster 1.4C NetBSD 1.4C (CUSTOM) #20: Fri Jun 4 11:39:25 MEST 1999 hannken@toaster:/usr/obj/kern.sparc/CUSTOM sparc


>Description:
	
Complex post increment / mem copy gets wrong order. See next section.
>How-To-Repeat:
	
Compile the following program on a sparc and get the output:

................................................................
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

See that nextblock is incremented before the assignment.
This is very dangerous, this program is a stripped down example of
/sbin/dump. This error ruins all dumps created on this machine.

#define TP_BSIZE 64

char	buf[2*TP_BSIZE];
char	(*nextblock)[TP_BSIZE] = (char (*)[TP_BSIZE]) buf;

union u_test {
	char dummy[TP_BSIZE];
	struct s_test {
		int a;
		int b;
		int c;
	} s_test;
};

main(int argc, char **argv)
{
	int i;
	char dp[TP_BSIZE];

	for (i = 0; i < 2*TP_BSIZE; i++)
		buf[i] = '.';
	for (i = 0; i < TP_BSIZE; i++)
		dp[i] = 'a';

	*(union u_test *)(*(nextblock)++) = *(union u_test *)dp;

	for (i = 0; i < 2*TP_BSIZE; i++)
		printf("%c%s", buf[i], (i % 64) == 63 ? "\n" : "");
	exit(0);
}
>Fix:
	
Backing out the following change from egcs-1.1.1 -> egcs-1.1.2 cures the problem:

--- expr.c	1998/11/14 04:27:16	1.3
+++ expr.c	1999/04/06 16:04:02	1.4
@@ -1619,4 +1619,8 @@
 {
   rtx retval = 0;
+#ifdef TARGET_MEM_FUNCTIONS
+  static tree fn;
+  tree call_expr, arg_list;
+#endif
 
   if (GET_MODE (x) != BLKmode)
@@ -1692,11 +1696,58 @@
 
 #ifdef TARGET_MEM_FUNCTIONS
-      retval
-	= emit_library_call_value (memcpy_libfunc, NULL_RTX, 0,
-				   ptr_mode, 3, XEXP (x, 0), Pmode,
-				   XEXP (y, 0), Pmode,
-				   convert_to_mode (TYPE_MODE (sizetype), size,
-						    TREE_UNSIGNED (sizetype)),
-				   TYPE_MODE (sizetype));
+      /* It is incorrect to use the libcall calling conventions to call
+	 memcpy in this context.
+
+	 This could be a user call to memcpy and the user may wish to
+	 examine the return value from memcpy.
+
+	 For targets where libcalls and normal calls have different conventions
+	 for returning pointers, we could end up generating incorrect code. 
+
+	 So instead of using a libcall sequence we build up a suitable
+	 CALL_EXPR and expand the call in the normal fashion.  */
+      if (fn == NULL_TREE)
+	{
+	  tree fntype;
+
+	  /* This was copied from except.c, I don't know if all this is
+	     necessary in this context or not.  */
+	  fn = get_identifier ("memcpy");
+	  push_obstacks_nochange ();
+	  end_temporary_allocation ();
+	  fntype = build_pointer_type (void_type_node);
+	  fntype = build_function_type (fntype, NULL_TREE);
+	  fn = build_decl (FUNCTION_DECL, fn, fntype);
+	  DECL_EXTERNAL (fn) = 1;
+	  TREE_PUBLIC (fn) = 1;
+	  DECL_ARTIFICIAL (fn) = 1;
+	  make_decl_rtl (fn, NULL_PTR, 1);
+	  assemble_external (fn);
+	  pop_obstacks ();
+	}
+
+      /* We need to make an argument list for the function call. 
+
+	 memcpy has three arguments, the first two are void * addresses and
+	 the last is a size_t byte count for the copy.  */
+      arg_list
+	= build_tree_list (NULL_TREE,
+			    make_tree (build_pointer_type (void_type_node),
+				       XEXP (x, 0)));
+      TREE_CHAIN (arg_list)
+	= build_tree_list (NULL_TREE,
+			   make_tree (build_pointer_type (void_type_node),
+				      XEXP (y, 0)));
+      TREE_CHAIN (TREE_CHAIN (arg_list))
+	 = build_tree_list (NULL_TREE, make_tree (sizetype, size));
+      TREE_CHAIN (TREE_CHAIN (TREE_CHAIN (arg_list))) = NULL_TREE;
+
+      /* Now we have to build up the CALL_EXPR itself.  */
+      call_expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn);
+      call_expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)),
+			 call_expr, arg_list, NULL_TREE);
+      TREE_SIDE_EFFECTS (call_expr) = 1;
+
+      retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
 #else
       emit_library_call (bcopy_libfunc, 0,
>Audit-Trail:
>Unformatted: