summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorraeburn <raeburn@138bc75d-0d04-0410-961f-82ee72b054a4>1998-10-11 02:21:54 +0000
committerraeburn <raeburn@138bc75d-0d04-0410-961f-82ee72b054a4>1998-10-11 02:21:54 +0000
commitefea460c5eda4c7369e81ba7c7ebc6a27f8eccc2 (patch)
tree7d04180ff0e97d1e5bb80e16c8389a2a43d9e018 /gcc
parent5eabf72a2dc1dba34d9d1a1386692418b448722d (diff)
downloadgcc-efea460c5eda4c7369e81ba7c7ebc6a27f8eccc2.tar.gz
Fine-grained control of -fcheck-memory-usage with new no_check_memory_usage
attribute. Misc minor bugfixes and tests for it too. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@22983 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog29
-rw-r--r--gcc/c-common.c20
-rw-r--r--gcc/c-decl.c2
-rw-r--r--gcc/calls.c16
-rw-r--r--gcc/config/alpha/alpha.c4
-rw-r--r--gcc/config/clipper/clipper.c2
-rw-r--r--gcc/config/m88k/m88k.c2
-rw-r--r--gcc/config/pa/pa.c2
-rw-r--r--gcc/config/sparc/sparc.c2
-rw-r--r--gcc/expr.c49
-rw-r--r--gcc/expr.h4
-rw-r--r--gcc/extend.texi13
-rw-r--r--gcc/function.c16
-rw-r--r--gcc/function.h1
-rw-r--r--gcc/invoke.texi21
-rw-r--r--gcc/optabs.c14
-rw-r--r--gcc/stmt.c8
-rw-r--r--gcc/testsuite/gcc.c-torture/ChangeLog5
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/memcheck/blkarg.c64
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/memcheck/driver.c254
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/memcheck/driver.h28
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/memcheck/memcheck.exp54
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/memcheck/t1.c27
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/memcheck/t2.c26
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/memcheck/t3.c25
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/memcheck/t4.c34
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/memcheck/t5.c33
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/memcheck/t6.c39
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/memcheck/t7.c40
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/memcheck/t8.c41
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/memcheck/t9.c40
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/memcheck/template16
-rw-r--r--gcc/tree.h5
33 files changed, 881 insertions, 55 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 5197ef3f3ee..b1c44a00b86 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,32 @@
+Sun Oct 11 05:03:41 1998 Ken Raeburn <raeburn@cygnus.com>
+
+ * tree.h (DECL_NO_CHECK_MEMORY_USAGE): New macros.
+ (struct tree_decl): New fields no_check_memory_usage.
+ * c-common.c (enum attrs): Add A_NO_CHECK_MEMORY_USAGE.
+ (init_attributes): Register it as a new attribute.
+ (decl_attributes): Set flags on functions given that attribute.
+ * c-decl.c (duplicate_decls): Merge new attribute.
+ * expr.h (current_function_check_memory_usage): Declare new var.
+ * calls.c, expr.c, function.c, stmt.c, alpha.c, clipper.c, m88k.c,
+ pa.c, sparc.c: Replace uses of flag_check_memory_usage with
+ current_function_check_memory_usage.
+ * function.h: Add field to struct function.
+ * function.c (current_function_check_memory_usage): Define it.
+ (push_function_context_to, pop_function_context_from): Save and
+ restore it.
+ (expand_function_start): Set it, based on global flag and function
+ attribute.
+
+ * expr.c (expand_expr, case VAR_DECL): In memory-checking code, do
+ check non-automatic variables, to permit detection of writes to
+ read-only locations in embedded systems without memory management.
+ * calls.c (store_one_arg): Use ARGS_SIZE_RTX to get size of argument
+ when emitting chkr_set_right_libfunc call, even if the argument is
+ BLKmode or variable-sized; don't abort.
+
+ * optabs.c (init_optabs): Create Checker and __cyg_profile_*
+ symbols in Pmode, not VOIDmode.
+
Sun Oct 11 01:03:05 1998 Zack Weinberg <zack@rabi.phys.columbia.edu>
* cppexp.c: When forcing unsigned comparisons, cast both sides
diff --git a/gcc/c-common.c b/gcc/c-common.c
index 820473f836c..3157e1d0f7d 100644
--- a/gcc/c-common.c
+++ b/gcc/c-common.c
@@ -51,7 +51,7 @@ extern struct obstack permanent_obstack;
int skip_evaluation;
enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION,
- A_NO_INSTRUMENT_FUNCTION,
+ A_NO_CHECK_MEMORY_USAGE, A_NO_INSTRUMENT_FUNCTION,
A_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED,
A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS,
A_INIT_PRIORITY};
@@ -394,6 +394,7 @@ init_attributes ()
add_attribute (A_ALIAS, "alias", 1, 1, 1);
add_attribute (A_INIT_PRIORITY, "init_priority", 0, 1, 0);
add_attribute (A_NO_INSTRUMENT_FUNCTION, "no_instrument_function", 0, 0, 1);
+ add_attribute (A_NO_CHECK_MEMORY_USAGE, "no_check_memory_usage", 0, 0, 1);
}
/* Process the attributes listed in ATTRIBUTES and PREFIX_ATTRIBUTES
@@ -889,6 +890,23 @@ decl_attributes (node, attributes, prefix_attributes)
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
break;
+ case A_NO_CHECK_MEMORY_USAGE:
+ if (TREE_CODE (decl) != FUNCTION_DECL)
+ {
+ error_with_decl (decl,
+ "`%s' attribute applies only to functions",
+ IDENTIFIER_POINTER (name));
+ }
+ else if (DECL_INITIAL (decl))
+ {
+ error_with_decl (decl,
+ "can't set `%s' attribute after definition",
+ IDENTIFIER_POINTER (name));
+ }
+ else
+ DECL_NO_CHECK_MEMORY_USAGE (decl) = 1;
+ break;
+
case A_INIT_PRIORITY:
{
tree initp_expr = (args ? TREE_VALUE (args): NULL_TREE);
diff --git a/gcc/c-decl.c b/gcc/c-decl.c
index 99b4ad5ab52..399acb08148 100644
--- a/gcc/c-decl.c
+++ b/gcc/c-decl.c
@@ -1938,6 +1938,8 @@ duplicate_decls (newdecl, olddecl, different_binding_level)
DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (newdecl)
|= DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (olddecl);
+ DECL_NO_CHECK_MEMORY_USAGE (newdecl)
+ |= DECL_NO_CHECK_MEMORY_USAGE (olddecl);
}
pop_obstacks ();
diff --git a/gcc/calls.c b/gcc/calls.c
index 2bddc2a32a1..c44617e6bb4 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -595,7 +595,7 @@ expand_call (exp, target, ignore)
if -fcheck-memory-usage, code which invokes functions (and thus
damages some hard registers) can be inserted before using the value.
So, target is always a pseudo-register in that case. */
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
target = 0;
/* See if we can find a DECL-node for the actual function.
@@ -1625,7 +1625,7 @@ expand_call (exp, target, ignore)
pop_temp_slots (); /* FUNEXP can't be BLKmode */
/* Check the function is executable. */
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
emit_library_call (chkr_check_exec_libfunc, 1,
VOIDmode, 1,
funexp, ptr_mode);
@@ -1864,7 +1864,7 @@ expand_call (exp, target, ignore)
NULL_RTX)));
/* Mark the memory for the aggregate as write-only. */
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
emit_library_call (chkr_set_right_libfunc, 1,
VOIDmode, 3,
structure_value_addr, ptr_mode,
@@ -3508,15 +3508,13 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
if (arg->value == arg->stack)
{
- /* If the value is already in the stack slot, we are done. */
- if (flag_check_memory_usage && GET_CODE (arg->stack) == MEM)
+ /* If the value is already in the stack slot, we are done moving
+ data. */
+ if (current_function_check_memory_usage && GET_CODE (arg->stack) == MEM)
{
- if (arg->mode == BLKmode)
- abort ();
-
emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
XEXP (arg->stack, 0), ptr_mode,
- GEN_INT (GET_MODE_SIZE (arg->mode)),
+ ARGS_SIZE_RTX (arg->size),
TYPE_MODE (sizetype),
GEN_INT (MEMORY_USE_RW),
TYPE_MODE (integer_type_node));
diff --git a/gcc/config/alpha/alpha.c b/gcc/config/alpha/alpha.c
index 5f738b599b2..dca15802a8f 100644
--- a/gcc/config/alpha/alpha.c
+++ b/gcc/config/alpha/alpha.c
@@ -2975,7 +2975,7 @@ alpha_builtin_saveregs (arglist)
dest = change_address (block, ptr_mode, XEXP (block, 0));
emit_move_insn (dest, addr);
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
dest, ptr_mode,
GEN_INT (GET_MODE_SIZE (ptr_mode)),
@@ -2989,7 +2989,7 @@ alpha_builtin_saveregs (arglist)
POINTER_SIZE/BITS_PER_UNIT));
emit_move_insn (dest, argsize);
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
dest, ptr_mode,
GEN_INT (GET_MODE_SIZE
diff --git a/gcc/config/clipper/clipper.c b/gcc/config/clipper/clipper.c
index 4bee0e6aaa7..d59d3f1671e 100644
--- a/gcc/config/clipper/clipper.c
+++ b/gcc/config/clipper/clipper.c
@@ -438,7 +438,7 @@ clipper_builtin_saveregs (arglist)
scratch);
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
{
emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
addr, ptr_mode,
diff --git a/gcc/config/m88k/m88k.c b/gcc/config/m88k/m88k.c
index b1250e5e61b..877ecf9b8fa 100644
--- a/gcc/config/m88k/m88k.c
+++ b/gcc/config/m88k/m88k.c
@@ -2644,7 +2644,7 @@ m88k_builtin_saveregs (arglist)
UNITS_PER_WORD * (8 - fixed));
}
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
{
emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
block, ptr_mode,
diff --git a/gcc/config/pa/pa.c b/gcc/config/pa/pa.c
index 41642041e32..91ada8e3aec 100644
--- a/gcc/config/pa/pa.c
+++ b/gcc/config/pa/pa.c
@@ -4391,7 +4391,7 @@ hppa_builtin_saveregs (arglist)
last argument register store. So we emit a blockage insn here. */
emit_insn (gen_blockage ());
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
dest, ptr_mode,
GEN_INT (4 * UNITS_PER_WORD), TYPE_MODE (sizetype),
diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c
index ae207027b0f..686350b8694 100644
--- a/gcc/config/sparc/sparc.c
+++ b/gcc/config/sparc/sparc.c
@@ -4279,7 +4279,7 @@ sparc_builtin_saveregs (arglist)
GEN_INT (STACK_POINTER_OFFSET
+ UNITS_PER_WORD * first_reg));
- if (flag_check_memory_usage
+ if (current_function_check_memory_usage
&& first_reg < NPARM_REGS (word_mode))
emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
address, ptr_mode,
diff --git a/gcc/expr.c b/gcc/expr.c
index d2dcdf8c592..53dce8fdb0e 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -110,8 +110,8 @@ static rtx apply_args_value;
static int can_handle_constant_p;
/* Don't check memory usage, since code is being emitted to check a memory
- usage. Used when flag_check_memory_usage is true, to avoid infinite
- recursion. */
+ usage. Used when current_function_check_memory_usage is true, to avoid
+ infinite recursion. */
static int in_check_memory_usage;
/* This structure is used by move_by_pieces to describe the move to
@@ -2865,7 +2865,7 @@ emit_push_insn (x, mode, type, size, align, partial, reg, extra,
move_by_pieces (gen_rtx_MEM (BLKmode, gen_push_operand ()), xinner,
INTVAL (size) - used, align);
- if (flag_check_memory_usage && ! in_check_memory_usage)
+ if (current_function_check_memory_usage && ! in_check_memory_usage)
{
rtx temp;
@@ -2922,7 +2922,7 @@ emit_push_insn (x, mode, type, size, align, partial, reg, extra,
args_addr,
args_so_far),
skip));
- if (flag_check_memory_usage && ! in_check_memory_usage)
+ if (current_function_check_memory_usage && ! in_check_memory_usage)
{
rtx target;
@@ -3122,7 +3122,7 @@ emit_push_insn (x, mode, type, size, align, partial, reg, extra,
emit_move_insn (gen_rtx_MEM (mode, addr), x);
- if (flag_check_memory_usage && ! in_check_memory_usage)
+ if (current_function_check_memory_usage && ! in_check_memory_usage)
{
in_check_memory_usage = 1;
if (target == 0)
@@ -3290,7 +3290,7 @@ expand_assignment (to, from, want_value, suggest_reg)
}
/* Check the access. */
- if (flag_check_memory_usage && GET_CODE (to_rtx) == MEM)
+ if (current_function_check_memory_usage && GET_CODE (to_rtx) == MEM)
{
rtx to_addr;
int size;
@@ -3416,7 +3416,7 @@ expand_assignment (to, from, want_value, suggest_reg)
EXPAND_MEMORY_USE_DONT);
/* Copy the rights of the bitmap. */
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
emit_library_call (chkr_copy_bitmap_libfunc, 1, VOIDmode, 3,
XEXP (to_rtx, 0), ptr_mode,
XEXP (from_rtx, 0), ptr_mode,
@@ -3638,7 +3638,7 @@ store_expr (exp, target, want_value)
temp = convert_modes (GET_MODE (target), TYPE_MODE (TREE_TYPE (exp)),
temp, TREE_UNSIGNED (TREE_TYPE (exp)));
- if (flag_check_memory_usage
+ if (current_function_check_memory_usage
&& GET_CODE (target) == MEM
&& AGGREGATE_TYPE_P (TREE_TYPE (exp)))
{
@@ -3742,7 +3742,7 @@ store_expr (exp, target, want_value)
if (size != const0_rtx)
{
/* Be sure we can write on ADDR. */
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
emit_library_call (chkr_check_addr_libfunc, 1, VOIDmode, 3,
addr, ptr_mode,
size, TYPE_MODE (sizetype),
@@ -5584,13 +5584,16 @@ expand_expr (exp, target, tmode, modifier)
pop_obstacks ();
}
- /* Only check automatic variables. Currently, function arguments are
- not checked (this can be done at compile-time with prototypes).
- Aggregates are not checked. */
- if (flag_check_memory_usage && code == VAR_DECL
+ /* Although static-storage variables start off initialized, according to
+ ANSI C, a memcpy could overwrite them with uninitialized values. So
+ we check them too. This also lets us check for read-only variables
+ accessed via a non-const declaration, in case it won't be detected
+ any other way (e.g., in an embedded system or OS kernel without
+ memory protection).
+
+ Aggregates are not checked here; they're handled elsewhere. */
+ if (current_function_check_memory_usage && code == VAR_DECL
&& GET_CODE (DECL_RTL (exp)) == MEM
- && DECL_CONTEXT (exp) != NULL_TREE
- && ! TREE_STATIC (exp)
&& ! AGGREGATE_TYPE_P (TREE_TYPE (exp)))
{
enum memory_use_mode memory_usage;
@@ -6107,7 +6110,7 @@ expand_expr (exp, target, tmode, modifier)
op0 = expand_expr (exp1, NULL_RTX, VOIDmode, EXPAND_SUM);
op0 = memory_address (mode, op0);
- if (flag_check_memory_usage && !AGGREGATE_TYPE_P (TREE_TYPE (exp)))
+ if (current_function_check_memory_usage && !AGGREGATE_TYPE_P (TREE_TYPE (exp)))
{
enum memory_use_mode memory_usage;
memory_usage = get_memory_usage_from_modifier (modifier);
@@ -6411,7 +6414,7 @@ expand_expr (exp, target, tmode, modifier)
}
/* Check the access. */
- if (flag_check_memory_usage && GET_CODE (op0) == MEM)
+ if (current_function_check_memory_usage && GET_CODE (op0) == MEM)
{
enum memory_use_mode memory_usage;
memory_usage = get_memory_usage_from_modifier (modifier);
@@ -9163,7 +9166,7 @@ expand_builtin (exp, target, subtarget, mode, ignore)
src_rtx = copy_to_mode_reg (Pmode, src_rtx);
/* Check the string is readable and has an end. */
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
emit_library_call (chkr_check_str_libfunc, 1, VOIDmode, 2,
src_rtx, ptr_mode,
GEN_INT (MEMORY_USE_RO),
@@ -9256,7 +9259,7 @@ expand_builtin (exp, target, subtarget, mode, ignore)
len_rtx = expand_expr (len, NULL_RTX, VOIDmode, 0);
/* Just copy the rights of SRC to the rights of DEST. */
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
emit_library_call (chkr_copy_bitmap_libfunc, 1, VOIDmode, 3,
XEXP (dest_mem, 0), ptr_mode,
XEXP (src_mem, 0), ptr_mode,
@@ -9327,7 +9330,7 @@ expand_builtin (exp, target, subtarget, mode, ignore)
dest_mem = get_memory_rtx (dest);
/* Just check DST is writable and mark it as readable. */
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
emit_library_call (chkr_check_addr_libfunc, 1, VOIDmode, 3,
XEXP (dest_mem, 0), ptr_mode,
len_rtx, TYPE_MODE (sizetype),
@@ -9353,7 +9356,7 @@ expand_builtin (exp, target, subtarget, mode, ignore)
break;
/* If we need to check memory accesses, call the library function. */
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
break;
if (arglist == 0
@@ -9409,7 +9412,7 @@ expand_builtin (exp, target, subtarget, mode, ignore)
break;
/* If we need to check memory accesses, call the library function. */
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
break;
if (arglist == 0
@@ -10238,7 +10241,7 @@ expand_increment (exp, post, ignore)
/* Increment however we can. */
op1 = expand_binop (mode, this_optab, value, op1,
- flag_check_memory_usage ? NULL_RTX : op0,
+ current_function_check_memory_usage ? NULL_RTX : op0,
TREE_UNSIGNED (TREE_TYPE (exp)), OPTAB_LIB_WIDEN);
/* Make sure the value is stored into OP0. */
if (op1 != op0)
diff --git a/gcc/expr.h b/gcc/expr.h
index 353e6fdfe46..b1665aa7de9 100644
--- a/gcc/expr.h
+++ b/gcc/expr.h
@@ -91,6 +91,10 @@ extern int current_function_uses_pic_offset_table;
/* The arg pointer hard register, or the pseudo into which it was copied. */
extern rtx current_function_internal_arg_pointer;
+/* This is nonzero if memory access checking be enabled in the current
+ function. */
+extern int current_function_check_memory_usage;
+
/* Nonzero means stack pops must not be deferred, and deferred stack
pops must not be output. It is nonzero inside a function call,
inside a conditional expression, inside a statement expression,
diff --git a/gcc/extend.texi b/gcc/extend.texi
index 45b31b82322..a2c278b001c 100644
--- a/gcc/extend.texi
+++ b/gcc/extend.texi
@@ -1518,6 +1518,19 @@ mangled name for the target must be used.
Not all target machines support this attribute.
+@item no_check_memory_usage
+@cindex @code{no_check_memory_usage} function attribute
+If @samp{-fcheck-memory-usage} is given, calls to support routines will
+be generated before most memory accesses, to permit support code to
+record usage and detect uses of uninitialized or unallocated storage.
+Since the compiler cannot handle them properly, @code{asm} statements
+are not allowed. Declaring a function with this attribute disables the
+memory checking code for that function, permitting the use of @code{asm}
+statements without requiring separate compilation with different
+options, and allowing you to write support routines of your own if you
+wish, without getting infinite recursion if they get compiled with this
+option.
+
@item regparm (@var{number})
@cindex functions that are passed arguments in registers on the 386
On the Intel 386, the @code{regparm} attribute causes the compiler to
diff --git a/gcc/function.c b/gcc/function.c
index 5ad113cfc93..4645f5af7cc 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -221,6 +221,9 @@ char *current_function_cannot_inline;
generated. */
int current_function_instrument_entry_exit;
+/* Nonzero if memory access checking be enabled in the current function. */
+int current_function_check_memory_usage;
+
/* The FUNCTION_DECL for an inline function currently being expanded. */
tree inline_function_decl;
@@ -543,6 +546,7 @@ push_function_context_to (context)
p->fixup_var_refs_queue = 0;
p->epilogue_delay_list = current_function_epilogue_delay_list;
p->args_info = current_function_args_info;
+ p->check_memory_usage = current_function_check_memory_usage;
p->instrument_entry_exit = current_function_instrument_entry_exit;
save_tree_status (p, context);
@@ -626,6 +630,7 @@ pop_function_context_from (context)
current_function_epilogue_delay_list = p->epilogue_delay_list;
reg_renumber = 0;
current_function_args_info = p->args_info;
+ current_function_check_memory_usage = p->check_memory_usage;
current_function_instrument_entry_exit = p->instrument_entry_exit;
restore_tree_status (p, context);
@@ -1484,7 +1489,7 @@ put_var_into_stack (decl)
else
return;
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
XEXP (reg, 0), ptr_mode,
GEN_INT (GET_MODE_SIZE (GET_MODE (reg))),
@@ -4364,7 +4369,7 @@ assign_parms (fndecl, second_time)
store_expr (parm, copy, 0);
emit_move_insn (parmreg, XEXP (copy, 0));
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
XEXP (copy, 0), ptr_mode,
GEN_INT (int_size_in_bytes (type)),
@@ -4529,7 +4534,7 @@ assign_parms (fndecl, second_time)
emit_move_insn (validize_mem (stack_parm),
validize_mem (entry_parm));
}
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
{
push_to_sequence (conversion_insns);
emit_library_call (chkr_set_right_libfunc, 1, VOIDmode, 3,
@@ -5550,6 +5555,11 @@ expand_function_start (subr, parms_have_cleanups)
valid operands of arithmetic insns. */
init_recog_no_volatile ();
+ /* Set this before generating any memory accesses. */
+ current_function_check_memory_usage
+ = (flag_check_memory_usage
+ && ! DECL_NO_CHECK_MEMORY_USAGE (current_function_decl));
+
current_function_instrument_entry_exit
= (flag_instrument_function_entry_exit
&& ! DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (subr));
diff --git a/gcc/function.h b/gcc/function.h
index 06e90dc88aa..014ed135b74 100644
--- a/gcc/function.h
+++ b/gcc/function.h
@@ -151,6 +151,7 @@ struct function
rtx saveregs_value;
rtx apply_args_value;
rtx forced_labels;
+ int check_memory_usage;
/* For emit-rtl.c. */
int reg_rtx_no;
diff --git a/gcc/invoke.texi b/gcc/invoke.texi
index d0339e47699..cf461987208 100644
--- a/gcc/invoke.texi
+++ b/gcc/invoke.texi
@@ -5843,8 +5843,7 @@ the offsets of structure members won't agree with system libraries.
@item -fcheck-memory-usage
Generate extra code to check each memory access. GNU CC will generate
code that is suitable for a detector of bad memory accesses such as
-@file{Checker}. If you specify this option, you can not use the
-@code{asm} or @code{__asm__} keywords.
+@file{Checker}.
You must also specify this option when you compile functions you call that
have side effects. If you do not, you may get erroneous messages from
@@ -5859,6 +5858,24 @@ which are provided by the detector. If you cannot find or build
stubs for every function you call, you may have to specify
@samp{-fcheck-memory-usage} without @samp{-fprefix-function-name}.
+If you specify this option, you can not use the @code{asm} or
+@code{__asm__} keywords in functions with memory checking enabled. The
+compiler cannot understand what the @code{asm} statement will do, and
+therefore cannot generate the appropriate code, so it is rejected.
+However, the function attribute @code{no_check_memory_usage} will
+disable memory checking within a function, and @code{asm} statements can
+be put inside such functions. Inline expansion of a non-checked
+function within a checked function is permitted; the inline function's
+memory accesses won't be checked, but the rest will.
+
+If you move your @code{asm} statements to non-checked inline functions,
+but they do access memory, you can add calls to the support code in your
+inline function, to indicate any reads, writes, or copies being done.
+These calls would be similar to those done in the stubs described above.
+
+@c FIXME: The support-routine interface is defined by the compiler and
+@c should be documented!
+
@item -fprefix-function-name
Request GNU CC to add a prefix to the symbols generated for function names.
GNU CC adds a prefix to the names of functions defined as well as
diff --git a/gcc/optabs.c b/gcc/optabs.c
index 2157f3f8784..2a321690dc4 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -4390,17 +4390,17 @@ init_optabs ()
fixunstfti_libfunc = gen_rtx_SYMBOL_REF (Pmode, "__fixunstfti");
/* For check-memory-usage. */
- chkr_check_addr_libfunc = gen_rtx_SYMBOL_REF (VOIDmode, "chkr_check_addr");
- chkr_set_right_libfunc = gen_rtx_SYMBOL_REF (VOIDmode, "chkr_set_right");
- chkr_copy_bitmap_libfunc = gen_rtx_SYMBOL_REF (VOIDmode, "chkr_copy_bitmap");
- chkr_check_exec_libfunc = gen_rtx_SYMBOL_REF (VOIDmode, "chkr_check_exec");
- chkr_check_str_libfunc = gen_rtx_SYMBOL_REF (VOIDmode, "chkr_check_str");
+ chkr_check_addr_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_check_addr");
+ chkr_set_right_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_set_right");
+ chkr_copy_bitmap_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_copy_bitmap");
+ chkr_check_exec_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_check_exec");
+ chkr_check_str_libfunc = gen_rtx_SYMBOL_REF (Pmode, "chkr_check_str");
/* For function entry/exit instrumentation. */
profile_function_entry_libfunc
- = gen_rtx_SYMBOL_REF (VOIDmode, "__cyg_profile_func_enter");
+ = gen_rtx_SYMBOL_REF (Pmode, "__cyg_profile_func_enter");
profile_function_exit_libfunc
- = gen_rtx_SYMBOL_REF (VOIDmode, "__cyg_profile_func_exit");
+ = gen_rtx_SYMBOL_REF (Pmode, "__cyg_profile_func_exit");
#ifdef HAVE_conditional_trap
init_traps ();
diff --git a/gcc/stmt.c b/gcc/stmt.c
index 658c872fad4..b28df90c48f 100644
--- a/gcc/stmt.c
+++ b/gcc/stmt.c
@@ -584,7 +584,7 @@ expand_computed_goto (exp)
emit_queue ();
/* Be sure the function is executable. */
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
emit_library_call (chkr_check_exec_libfunc, 1,
VOIDmode, 1, x, ptr_mode);
@@ -1118,7 +1118,7 @@ void
expand_asm (body)
tree body;
{
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
{
error ("`asm' cannot be used with `-fcheck-memory-usage'");
return;
@@ -1174,7 +1174,7 @@ expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
if (noutputs == 0)
vol = 1;
- if (flag_check_memory_usage)
+ if (current_function_check_memory_usage)
{
error ("`asm' cannot be used with `-fcheck-memory-usage'");
return;
@@ -3291,7 +3291,7 @@ expand_decl (decl)
&& ! TREE_ADDRESSABLE (decl)
&& (DECL_REGISTER (decl) || ! obey_regdecls)
/* if -fcheck-memory-usage, check all variables. */
- && ! flag_check_memory_usage)
+ && ! current_function_check_memory_usage)
{
/* Automatic variable that can go in a register. */
int unsignedp = TREE_UNSIGNED (type);
diff --git a/gcc/testsuite/gcc.c-torture/ChangeLog b/gcc/testsuite/gcc.c-torture/ChangeLog
index 9b2d6badaa5..14cd5746146 100644
--- a/gcc/testsuite/gcc.c-torture/ChangeLog
+++ b/gcc/testsuite/gcc.c-torture/ChangeLog
@@ -1,3 +1,8 @@
+Sun Oct 11 05:04:28 1998 Ken Raeburn <raeburn@cygnus.com>
+
+ * execute/memcheck: New directory of tests for
+ -fcheck-memory-usage.
+
1998-10-06 Ken Raeburn <raeburn@cygnus.com>
* special/981006-1.c: New test. Make sure gcc doesn't lose track
diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/blkarg.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/blkarg.c
new file mode 100644
index 00000000000..43c5b39498a
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/blkarg.c
@@ -0,0 +1,64 @@
+/* Must define:
+ int expect_error;
+ void test ();
+ void setup () NOCHECK; */
+
+#include "driver.h"
+
+/* Test permissions of BLKmode arguments constructed purely on the
+ stack.
+
+ Maybe we can't guarantee that we'll always wind up with stack args,
+ but if we don't, they're in registers, and permissions should just
+ always yield success. So while this test may not be effective on
+ all platforms, failure probably does indicate a real bug.
+
+ Note that because of the implementation, we do want to test BLKmode
+ arguments that live purely on the stack and are constructed there.
+ We want to test other situations of function arguments, of course,
+ but don't assume this case would be covered by using one monster
+ argument that is read from memory (including using constructor
+ syntax but constant values), or may live partially in registers. */
+
+int expect_error = 0;
+
+/* Must be BLKmode. Using only two fields gets TImode on Alpha. */
+struct S {
+ unsigned long long ll;
+ long xx, yy;
+};
+
+unsigned long long x = 0x12345689ULL;
+#define I2 42
+
+/* Leading six arguments force X into stack on both Alpha and MIPS. */
+
+static int first_time = 1;
+int foo (int a1, int a2, int a3, int a4, int a5, int a6, struct S s) {
+ if (a1 != 1 || a2 != 2 || a3 != 3 || a4 != 4 || a5 != 5 || a6 != 6)
+ abort ();
+ if (first_time)
+ {
+ if (s.ll != x || s.xx != I2 || s.yy != 0)
+ abort ();
+ first_time = 0;
+ }
+ else
+ {
+ if (s.ll != 0 || s.xx != 0 || s.yy != 0)
+ abort ();
+ }
+ return 0;
+}
+
+void test ()
+{
+ foo (1, 2, 3, 4, 5, 6, (struct S) { x, I2 });
+ foo (1, 2, 3, 4, 5, 6, (struct S) { 0 });
+}
+
+void setup () /* NOCHECK */
+{
+ mark_region (&x, sizeof (x), ACCESS_RO);
+ mark_region (&first_time, sizeof (first_time), ACCESS_RW);
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/driver.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/driver.c
new file mode 100644
index 00000000000..9002a0b8d24
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/driver.c
@@ -0,0 +1,254 @@
+/* GNU C dependencies:
+ Checker support hooks
+ ISO C 9x array element initialization
+ void-pointer arithmetic */
+
+#include "driver.h"
+
+int verbose = 0;
+int debug = 0;
+int bad_accesses = 0;
+
+const char *const memory_use_strings[] = {
+#define INIT(x) [x] = #x
+ INIT (MEMORY_USE_BAD),
+ INIT (MEMORY_USE_DONT),
+ INIT (MEMORY_USE_RO),
+ INIT (MEMORY_USE_RW),
+ INIT (MEMORY_USE_TW),
+ INIT (MEMORY_USE_WO),
+#undef INIT
+};
+
+/* This won't be used for any really huge test cases, so a simple
+ linked list is adequate. We won't even worry about overlapping
+ regions; the matching entry that comes up first wins. */
+const char *const access_mode_strings[] = {
+ "none", "ro", "wo", "rw",
+};
+struct access_node {
+ struct access_node *next;
+ const void *addr;
+ size_t sz;
+ enum access_mode mode;
+};
+
+static struct access_node *access_list;
+
+void mark_region (const void *addr, size_t sz, enum access_mode mode)
+{
+ struct access_node *a;
+ if (debug)
+ printf ("mark_region (%p, %ld, %s)\n", addr, (long) sz,
+ access_mode_strings[mode]);
+ a = malloc (sizeof (struct access_node));
+ a->next = access_list;
+ a->addr = addr;
+ a->sz = sz;
+ a->mode = mode;
+ access_list = a;
+}
+
+void report_bad_access (void *, size_t, enum memory_use_mode) NOCHECK;
+void report_bad_access (void *addr, size_t sz, enum memory_use_mode mode)
+{
+ if (++bad_accesses > 100)
+ bad_accesses = 100;
+ if (verbose)
+ {
+ static char x[100];
+ const char *mode_str;
+ if (mode >= 0
+ && mode < sizeof (memory_use_strings) / sizeof (*memory_use_strings)
+ && memory_use_strings[mode] != 0)
+ mode_str = memory_use_strings[mode];
+ else
+ {
+ sprintf (x, "<bad mode %d>", mode);
+ mode_str = x;
+ }
+ printf ("bad access (%p, %ld, %s)\n", addr, (long) sz, mode_str);
+ }
+}
+
+int verify1 (void *, size_t, enum access_mode, struct access_node *) NOCHECK;
+int verify1 (void *addr, size_t sz, enum access_mode mode,
+ struct access_node *a)
+{
+ while (a && (addr + sz <= a->addr || addr >= a->addr + a->sz))
+ a = a->next;
+ if (a == 0)
+ return 0;
+
+ if (debug)
+ printf ("verify1 (%p, %ld, %s)\n", addr, (long) sz,
+ access_mode_strings[mode]);
+
+ if (mode & ~a->mode)
+ return 0;
+
+ if (addr < a->addr)
+ if (verify1 (a, a->addr - addr, mode, a->next) == 0)
+ return 0;
+ if (addr + sz > a->addr + a->sz)
+ if (verify1 (a->addr + a->sz, (addr + sz) - (a->addr + a->sz), mode, a->next) == 0)
+ return 0;
+
+ /* All regions okay. */
+ return 1;
+}
+
+int verify_range_permission (void *, size_t, enum access_mode) NOCHECK;
+
+int verify_range_permission (void *addr, size_t sz, enum access_mode mode)
+{
+ if (debug)
+ printf ("verify_range_permission (%p, %ld, %s)\n", addr, (long) sz,
+ access_mode_strings[mode]);
+ return verify1 (addr, sz, mode, access_list);
+}
+
+void chkr_check_addr (void *, size_t, int) NOCHECK;
+
+void chkr_check_addr (void *addr, size_t sz, int mode)
+{
+ switch (mode)
+ {
+ case MEMORY_USE_BAD:
+ case MEMORY_USE_DONT:
+ default:
+ report_bad_access (addr, sz, mode);
+ return;
+ case MEMORY_USE_RO:
+ /* verify range readable */
+ if (!verify_range_permission (addr, sz, ACCESS_RO))
+ report_bad_access (addr, sz, mode);
+ return;
+ case MEMORY_USE_WO:
+ /* verify writeable, set writeable and readable */
+ if (!verify_range_permission (addr, sz, ACCESS_WO))
+ report_bad_access (addr, sz, mode);
+ mark_region (addr, sz, ACCESS_RW);
+ return;
+ case MEMORY_USE_RW:
+ /* verify readable and writeable, no change */
+ if (!verify_range_permission (addr, sz, ACCESS_RW))
+ report_bad_access (addr, sz, mode);
+ return;
+ case MEMORY_USE_TW:
+ /* verify writeable, no change */
+ if (!verify_range_permission (addr, sz, ACCESS_WO))
+ report_bad_access (addr, sz, mode);
+ return;
+ }
+ /* All branches should return. */
+ abort ();
+}
+
+void copy1 (void *, void *, size_t, struct access_node *) NOCHECK;
+void copy1 (void *dest, void *src, size_t sz, struct access_node *a)
+{
+ while (a && (src + sz <= a->addr || src >= a->addr + a->sz))
+ a = a->next;
+ if (a == 0)
+ {
+ report_bad_access (src, sz, MEMORY_USE_RO);
+ return;
+ }
+
+ if (debug)
+ printf ("copy1 (%p, %p, %ld)\n", dest, src, (long) sz);
+
+ {
+ void *start, *end;
+ start = src;
+ if (start < a->addr)
+ start = a->addr;
+ end = src + sz;
+ if (end > a->addr + a->sz)
+ end = a->addr + a->sz;
+ mark_region (dest + (start - src), end - start, a->mode);
+ }
+
+ if (src < a->addr)
+ copy1 (dest, src, a->addr - src, a->next);
+ if (src + sz > a->addr + a->sz)
+ copy1 (dest + (a->addr + a->sz - src), a->addr + a->sz,
+ (src + sz) - (a->addr + a->sz), a->next);
+}
+
+void chkr_copy_bitmap (void *, void *, size_t) NOCHECK;
+void chkr_copy_bitmap (void *dest, void *src, size_t sz)
+{
+ if (verify_range_permission (dest, sz, MEMORY_USE_WO) == 0)
+ report_bad_access (dest, sz, MEMORY_USE_WO);
+ copy1 (dest, src, sz, access_list);
+}
+
+void chkr_set_right (void *, size_t, enum access_mode) NOCHECK;
+void chkr_set_right (void *addr, size_t sz, enum access_mode mode)
+{
+ mark_region (addr, sz, mode);
+}
+
+int main () NOCHECK;
+int main ()
+{
+ setup ();
+ test ();
+ bad_accesses = !!bad_accesses; /* get 0 or 1 */
+ /* Return 0 if got expected results, 1 otherwise. */
+ return !(bad_accesses == expect_error);
+}
+
+struct malloc_node {
+ struct malloc_node *next;
+ void *addr;
+ size_t sz;
+ unsigned is_free : 1;
+};
+static struct malloc_node *malloc_list;
+
+void *c_malloc (size_t sz)
+{
+ void *p;
+ struct malloc_node *m;
+ if (sz == 0)
+ return 0;
+ p = malloc (sz);
+ if (p == 0)
+ {
+ if (verbose)
+ printf ("malloc(%ld) failed\n", (long) sz);
+ exit (1);
+ }
+ m = malloc (sizeof (struct malloc_node));
+ if (m == 0)
+ {
+ if (verbose)
+ printf ("malloc(%ld) failed\n", (long) sizeof (struct malloc_node));
+ exit (1);
+ }
+ mark_region (p, sz, ACCESS_WO);
+ m->addr = p;
+ m->sz = sz;
+ m->is_free = 0;
+ m->next = malloc_list;
+ malloc_list = m;
+ return p;
+}
+
+void c_free (void *p)
+{
+ struct malloc_node *m;
+ if (p == 0)
+ return;
+ for (m = malloc_list; m; m = m->next)
+ if (m->addr == p)
+ break;
+ if (m == 0 || m->is_free)
+ /* Test is broken. */
+ abort ();
+ m->is_free = 1;
+ free (p);
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/driver.h b/gcc/testsuite/gcc.c-torture/execute/memcheck/driver.h
new file mode 100644
index 00000000000..d8d22d2496a
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/driver.h
@@ -0,0 +1,28 @@
+/* GNU C dependencies:
+ Checker support hooks
+ ISO C 9x array element initialization
+ void-pointer arithmetic */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void *malloc (size_t);
+extern int printf (const char *, ...);
+
+/* This comes from gcc internals. Should be exported. */
+enum memory_use_mode {MEMORY_USE_BAD = 0, MEMORY_USE_RO = 1,
+ MEMORY_USE_WO = 2, MEMORY_USE_RW = 3,
+ MEMORY_USE_TW = 6, MEMORY_USE_DONT = 99};
+
+enum access_mode {
+ ACCESS_NONE = 0, ACCESS_RO = 1, ACCESS_WO = 2, ACCESS_RW = 3
+};
+
+#define NOCHECK __attribute__ ((no_check_memory_usage))
+
+void mark_region (const void *, size_t, enum access_mode) NOCHECK;
+void setup () NOCHECK;
+void test ();
+extern int expect_error;
+
+void *c_malloc (size_t) NOCHECK;
+void c_free (void *) NOCHECK;
diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/memcheck.exp b/gcc/testsuite/gcc.c-torture/execute/memcheck/memcheck.exp
new file mode 100644
index 00000000000..7fb756b4f5c
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/memcheck.exp
@@ -0,0 +1,54 @@
+# Copyright (C) 1991, 92-93, 95, 97, 1998 Free Software Foundation, Inc.
+
+# This program 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 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Please email any bugs, comments, and/or additions to this file to:
+# bug-gcc@prep.ai.mit.edu
+
+# This file was written by Rob Savoye. (rob@cygnus.com)
+# Modified and maintained by Jeffrey Wheat (cassidy@cygnus.com)
+
+#
+# These tests come from Torbjorn Granlund (tege@cygnus.com)
+# C torture test suite.
+#
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+# load support procs
+load_lib c-torture.exp
+
+#
+# main test loop
+#
+
+set tests [lsort [glob -nocomplain $srcdir/$subdir/*.c]]
+set idx [lsearch $tests */driver.c]
+if $idx>=0 {
+ set tests [lreplace $tests $idx $idx]
+} else {
+ error "list can't find driver.c in $srcdir/$subdir"
+}
+gcc_target_compile $srcdir/$subdir/driver.c driver.o object {additional_flags=-w additional_flags=-g}
+foreach src $tests {
+ # If we're only testing specific files and this isn't one of them, skip it.
+ if ![runtest_file_p $runtests $src] then {
+ continue
+ }
+
+ c-torture-execute $src "-fcheck-memory-usage driver.o"
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t1.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t1.c
new file mode 100644
index 00000000000..03b6acc1335
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t1.c
@@ -0,0 +1,27 @@
+/* Must define:
+ int expect_error;
+ void test ();
+ void setup (); -- NOCHECK */
+
+#include "driver.h"
+
+int expect_error = 0;
+
+int *ip;
+
+void test ()
+{
+ ip = c_malloc (sizeof (int));
+ *ip = 42;
+ t2 ();
+}
+
+int t2 ()
+{
+ return *ip;
+}
+
+void setup () /* NOCHECK */
+{
+ mark_region (&ip, sizeof (ip), ACCESS_RW);
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t2.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t2.c
new file mode 100644
index 00000000000..d386eb7ebb1
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t2.c
@@ -0,0 +1,26 @@
+/* Must define:
+ int expect_error;
+ void test ();
+ void setup () NOCHECK; */
+
+#include "driver.h"
+
+int expect_error = 1;
+
+int *ip;
+
+void test ()
+{
+ ip = c_malloc (sizeof (int));
+ t2 ();
+}
+
+int t2 ()
+{
+ return *ip;
+}
+
+void setup () /* NOCHECK */
+{
+ mark_region (&ip, sizeof (ip), ACCESS_RW);
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t3.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t3.c
new file mode 100644
index 00000000000..5b6333d4e8e
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t3.c
@@ -0,0 +1,25 @@
+/* Must define:
+ int expect_error;
+ void test ();
+ void setup () NOCHECK; */
+
+#include "driver.h"
+
+int expect_error = 0;
+
+int *ip;
+
+void test ()
+{
+ ip = c_malloc (sizeof (int));
+ t2 (ip);
+}
+
+int t2 (int *ip)
+{
+}
+
+void setup () /* NOCHECK */
+{
+ mark_region (&ip, sizeof (ip), ACCESS_RW);
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t4.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t4.c
new file mode 100644
index 00000000000..25010a06818
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t4.c
@@ -0,0 +1,34 @@
+/* Must define:
+ int expect_error;
+ void test ();
+ void setup () NOCHECK; */
+
+#include "driver.h"
+
+int expect_error = 0;
+
+struct s {
+ char c;
+ int a, b;
+};
+
+struct s *sp;
+
+void test ()
+{
+ sp = c_malloc (sizeof (struct s));
+ sp->c = 0;
+ sp->a = 12;
+ sp->b = 47;
+ foo (sp);
+}
+
+int foo (struct s *sp)
+{
+ return sp->c + sp->a + sp->b;
+}
+
+void setup () /* NOCHECK */
+{
+ mark_region (&sp, sizeof (sp), ACCESS_RW);
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t5.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t5.c
new file mode 100644
index 00000000000..c3bbf640517
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t5.c
@@ -0,0 +1,33 @@
+/* Must define:
+ int expect_error;
+ void test ();
+ void setup () NOCHECK; */
+
+#include "driver.h"
+
+int expect_error = 1;
+
+struct s {
+ char c;
+ int a, b;
+};
+
+struct s *sp;
+
+void test ()
+{
+ sp = c_malloc (sizeof (struct s));
+ sp->c = 0;
+ sp->b = 47;
+ foo (sp);
+}
+
+int foo (struct s *sp)
+{
+ return sp->c + sp->a + sp->b;
+}
+
+void setup () /* NOCHECK */
+{
+ mark_region (&sp, sizeof (sp), ACCESS_RW);
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t6.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t6.c
new file mode 100644
index 00000000000..652d33d0f8c
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t6.c
@@ -0,0 +1,39 @@
+/* Must define:
+ int expect_error;
+ void test ();
+ void setup () NOCHECK; */
+
+#include "driver.h"
+
+int expect_error = 1;
+
+struct s {
+ char c;
+ int a, b;
+};
+
+struct s *sp;
+
+void test ()
+{
+ sp = c_malloc (sizeof (struct s) * 2);
+ sp->c = 0;
+ sp->b = 47;
+ cp (sp);
+ foo (sp);
+}
+
+int foo (struct s *sp)
+{
+ return sp[1].c + sp[1].a + sp[1].b;
+}
+
+int cp (struct s *sp)
+{
+ sp[1] = sp[0];
+}
+
+void setup () /* NOCHECK */
+{
+ mark_region (&sp, sizeof (sp), ACCESS_RW);
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t7.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t7.c
new file mode 100644
index 00000000000..a7c6f5127ed
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t7.c
@@ -0,0 +1,40 @@
+/* Must define:
+ int expect_error;
+ void test ();
+ void setup () NOCHECK; */
+
+#include "driver.h"
+
+int expect_error = 0;
+
+struct s {
+ char c;
+ int a, b;
+};
+
+struct s *sp;
+
+void test ()
+{
+ sp = c_malloc (sizeof (struct s) * 2);
+ sp->c = 0;
+ sp->a = 13;
+ sp->b = 47;
+ cp (sp);
+ foo (sp);
+}
+
+int foo (struct s *sp)
+{
+ return sp[1].c + sp[1].a + sp[1].b;
+}
+
+int cp (struct s *sp)
+{
+ sp[1] = sp[0];
+}
+
+void setup () /* NOCHECK */
+{
+ mark_region (&sp, sizeof (sp), ACCESS_RW);
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t8.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t8.c
new file mode 100644
index 00000000000..01c167247f8
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t8.c
@@ -0,0 +1,41 @@
+/* Must define:
+ int expect_error;
+ void test ();
+ void setup () NOCHECK; */
+
+#include "driver.h"
+
+int expect_error = 0;
+
+typedef struct {
+ short a;
+ char b;
+} S1;
+typedef struct {
+ struct { int x; S1 *s1p; } *p;
+} S2;
+
+S1 *s1;
+S2 *s2;
+
+void test ()
+{
+ s1 = c_malloc (sizeof (S1));
+ s2 = c_malloc (sizeof (S2));
+ s2->p = c_malloc (sizeof (*s2->p));
+ s2->p->s1p = s1;
+ s1->a = 47;
+ s1->b = 3;
+ foo ();
+}
+
+int foo ()
+{
+ return s2->p->s1p->b;
+}
+
+void setup () /* NOCHECK */
+{
+ mark_region (&s1, sizeof (s1), ACCESS_RW);
+ mark_region (&s2, sizeof (s2), ACCESS_RW);
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/t9.c b/gcc/testsuite/gcc.c-torture/execute/memcheck/t9.c
new file mode 100644
index 00000000000..f32ca011dd8
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/t9.c
@@ -0,0 +1,40 @@
+/* Must define:
+ int expect_error;
+ void test ();
+ void setup () NOCHECK; */
+
+#include "driver.h"
+
+int expect_error = 1;
+
+typedef struct {
+ short a;
+ char b;
+} S1;
+typedef struct {
+ struct { int x; S1 *s1p; } *p;
+} S2;
+
+S1 *s1;
+S2 *s2;
+
+void test ()
+{
+ s1 = c_malloc (sizeof (S1));
+ s2 = c_malloc (sizeof (S2));
+ s2->p = c_malloc (sizeof (*s2->p));
+ s2->p->s1p = s1;
+ s1->a = 47;
+ foo ();
+}
+
+int foo ()
+{
+ return s2->p->s1p->b;
+}
+
+void setup () /* NOCHECK */
+{
+ mark_region (&s1, sizeof (s1), ACCESS_RW);
+ mark_region (&s2, sizeof (s2), ACCESS_RW);
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/memcheck/template b/gcc/testsuite/gcc.c-torture/execute/memcheck/template
new file mode 100644
index 00000000000..37ebb137c87
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/memcheck/template
@@ -0,0 +1,16 @@
+/* Must define:
+ int expect_error;
+ void test ();
+ void setup () NOCHECK; */
+
+#include "driver.h"
+
+int expect_error = ;
+
+void test ()
+{
+}
+
+void setup () /* NOCHECK */
+{
+}
diff --git a/gcc/tree.h b/gcc/tree.h
index cd9fdbb163e..8bd8df3368f 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1226,6 +1226,10 @@ struct tree_type
be instrumented with calls to support routines. */
#define DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT(NODE) ((NODE)->decl.no_instrument_function_entry_exit)
+/* Used in FUNCTION_DECLs to indicate that in this function,
+ check-memory-usage should be disabled. */
+#define DECL_NO_CHECK_MEMORY_USAGE(NODE) ((NODE)->decl.no_check_memory_usage)
+
/* Additional flags for language-specific uses. */
#define DECL_LANG_FLAG_0(NODE) (DECL_CHECK (NODE)->decl.lang_flag_0)
#define DECL_LANG_FLAG_1(NODE) (DECL_CHECK (NODE)->decl.lang_flag_1)
@@ -1282,6 +1286,7 @@ struct tree_decl
unsigned non_addr_const_p : 1;
unsigned no_instrument_function_entry_exit : 1;
+ unsigned no_check_memory_usage : 1;
/* For a FUNCTION_DECL, if inline, this is the size of frame needed.
If built-in, this is the code for which built-in function.