This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
fix for memcpy/memset bug
- To: egcs-patches at egcs dot cygnus dot com
- Subject: fix for memcpy/memset bug
- From: Jeffrey A Law <law at cygnus dot com>
- Date: Tue, 29 Jun 1999 01:41:08 -0600
- Reply-To: law at cygnus dot com
Here's the patch for the memcpy/memset bug I mentioned this morning.
* expr.c (emit_block_move): Properly handle case where one of the
block move arguments has a queued increment or decrement.
(clear_storage): Similarly. Fix formatting goof.
Index: expr.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/expr.c,v
retrieving revision 1.144
diff -c -3 -p -r1.144 expr.c
*** expr.c 1999/05/17 07:21:14 1.144
--- expr.c 1999/06/29 07:35:33
*************** emit_block_move (x, y, size, align)
*** 1710,1715 ****
--- 1710,1746 ----
}
}
+ /* X, Y, or SIZE may have been passed through protect_from_queue.
+
+ It is unsafe to save the value generated by protect_from_queue
+ and reuse it later. Consider what happens if emit_queue is
+ called before the return value from protect_from_queue is used.
+
+ Expansion of the CALL_EXPR below will call emit_queue before
+ we are finished emitting RTL for argument setup. So if we are
+ not careful we could get the wrong value for an argument.
+
+ To avoid this problem we go ahead and emit code to copy X, Y &
+ SIZE into new pseudos. We can then place those new pseudos
+ into an RTL_EXPR and use them later, even after a call to
+ emit_queue.
+
+ Note this is not strictly needed for library calls since they
+ do not call emit_queue before loading their arguments. However,
+ we may need to have library calls call emit_queue in the future
+ since failing to do so could cause problems for targets which
+ define SMALL_REGISTER_CLASSES and pass arguments in registers. */
+ x = copy_to_mode_reg (Pmode, XEXP (x, 0));
+ y = copy_to_mode_reg (Pmode, XEXP (y, 0));
+
+ #ifdef TARGET_MEM_FUNCTIONS
+ size = copy_to_mode_reg (TYPE_MODE (sizetype), size);
+ #else
+ size = convert_to_mode (TYPE_MODE (integer_type_node), size,
+ TREE_UNSIGNED (integer_type_node));
+ size = copy_to_reg (size);
+ #endif
+
#ifdef TARGET_MEM_FUNCTIONS
/* It is incorrect to use the libcall calling conventions to call
memcpy in this context.
*************** emit_block_move (x, y, size, align)
*** 1748,1759 ****
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;
--- 1779,1788 ----
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), x));
TREE_CHAIN (arg_list)
= build_tree_list (NULL_TREE,
! make_tree (build_pointer_type (void_type_node), y));
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;
*************** emit_block_move (x, y, size, align)
*** 1767,1774 ****
retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
#else
emit_library_call (bcopy_libfunc, 0,
! VOIDmode, 3, XEXP (y, 0), Pmode,
! XEXP (x, 0), Pmode,
convert_to_mode (TYPE_MODE (integer_type_node), size,
TREE_UNSIGNED (integer_type_node)),
TYPE_MODE (integer_type_node));
--- 1796,1802 ----
retval = expand_expr (call_expr, NULL_RTX, VOIDmode, 0);
#else
emit_library_call (bcopy_libfunc, 0,
! VOIDmode, 3, y, Pmode, x, Pmode
convert_to_mode (TYPE_MODE (integer_type_node), size,
TREE_UNSIGNED (integer_type_node)),
TYPE_MODE (integer_type_node));
*************** clear_storage (object, size, align)
*** 2444,2512 ****
}
}
#ifdef TARGET_MEM_FUNCTIONS
! /* It is incorrect to use the libcall calling conventions to call
! memset in this context.
- This could be a user call to memset and the user may wish to
- examine the return value from memset.
! 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 ("memset");
! 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.
! memset has three arguments, the first is a void * addresses, the
! second a integer with the initialization value, 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 (object, 0)));
! TREE_CHAIN (arg_list)
! = build_tree_list (NULL_TREE,
! make_tree (integer_type_node, const0_rtx));
! 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 (bzero_libfunc, 0,
! VOIDmode, 2,
! XEXP (object, 0), Pmode,
! convert_to_mode
! (TYPE_MODE (integer_type_node), size,
! TREE_UNSIGNED (integer_type_node)),
TYPE_MODE (integer_type_node));
#endif
}
--- 2472,2568 ----
}
}
+ /* OBJECT or SIZE may have been passed through protect_from_queue.
+ It is unsafe to save the value generated by protect_from_queue
+ and reuse it later. Consider what happens if emit_queue is
+ called before the return value from protect_from_queue is used.
+
+ Expansion of the CALL_EXPR below will call emit_queue before
+ we are finished emitting RTL for argument setup. So if we are
+ not careful we could get the wrong value for an argument.
+
+ To avoid this problem we go ahead and emit code to copy OBJECT
+ and SIZE into new pseudos. We can then place those new pseudos
+ into an RTL_EXPR and use them later, even after a call to
+ emit_queue.
+
+ Note this is not strictly needed for library calls since they
+ do not call emit_queue before loading their arguments. However,
+ we may need to have library calls call emit_queue in the future
+ since failing to do so could cause problems for targets which
+ define SMALL_REGISTER_CLASSES and pass arguments in registers. */
+ object = copy_to_mode_reg (Pmode, XEXP (object, 0));
+
#ifdef TARGET_MEM_FUNCTIONS
! size = copy_to_mode_reg (TYPE_MODE (sizetype), size);
! #else
! size = convert_to_mode (TYPE_MODE (integer_type_node), size,
! TREE_UNSIGNED (integer_type_node));
! size = copy_to_reg (size);
! #endif
! #ifdef TARGET_MEM_FUNCTIONS
! /* It is incorrect to use the libcall calling conventions to call
! memset in this context.
! This could be a user call to memset and the user may wish to
! examine the return value from memset.
! 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 ("memset");
! 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.
! memset has three arguments, the first is a void * addresses, the
! second a integer with the initialization value, 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),
! object));
! TREE_CHAIN (arg_list)
! = build_tree_list (NULL_TREE,
! make_tree (integer_type_node, const0_rtx));
! 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 (bzero_libfunc, 0,
! VOIDmode, 2, object, Pmode, size
TYPE_MODE (integer_type_node));
#endif
}
'