c-common.c (handle_cleanup_attribute): New.

* c-common.c (handle_cleanup_attribute): New.
        (c_common_attributes): Add it.
        * c-decl.c (finish_decl): Honor the cleanup attribute.
        * doc/extend.texi (Variable Attributes): Document it.

        * unwind-c.c: New file.
        * Makefile.in (LIB2ADDEH): Add it.
        * config/t-darwin, config/t-linux, config/t-linux-gnulibc1,
        config/ia64/t-ia64: Likewise.

        * gcc.dg/cleanup-1.c: New.
        * gcc.dg/cleanup-2.c: New.
        * gcc.dg/cleanup-3.c: New.
        * gcc.dg/cleanup-4.c: New.
        * gcc.dg/cleanup-5.c: New.
        * gcc.dg/cleanup-6.c: New.
        * gcc.dg/cleanup-7.c: New.

From-SVN: r67449
This commit is contained in:
Richard Henderson 2003-06-04 10:06:00 -07:00 committed by Richard Henderson
parent 3edc56a9e5
commit 0bfa5f65bf
18 changed files with 552 additions and 8 deletions

View File

@ -1,3 +1,15 @@
2003-06-04 Richard Henderson <rth@redhat.com>
* c-common.c (handle_cleanup_attribute): New.
(c_common_attributes): Add it.
* c-decl.c (finish_decl): Honor the cleanup attribute.
* doc/extend.texi (Variable Attributes): Document it.
* unwind-c.c: New file.
* Makefile.in (LIB2ADDEH): Add it.
* config/t-darwin, config/t-linux, config/t-linux-gnulibc1,
config/ia64/t-ia64: Likewise.
2003-06-04 Jakub Jelinek <jakub@redhat.com>
* function.c (trampolines_created): New variable.

View File

@ -472,7 +472,7 @@ CRTSTUFF_CFLAGS = -O2 $(GCC_CFLAGS) $(INCLUDES) $(MULTILIB_CFLAGS) -g0 \
# Additional sources to handle exceptions; overridden by targets as needed.
LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde.c \
$(srcdir)/unwind-sjlj.c $(srcdir)/gthr-gnat.c
$(srcdir)/unwind-sjlj.c $(srcdir)/gthr-gnat.c $(srcdir)/unwind-c.c
LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h
# nm flags to list global symbols in libgcc object files.
@ -1261,7 +1261,7 @@ c-incpath.o: c-incpath.c c-incpath.h $(CONFIG_H) $(SYSTEM_H) $(CPPLIB_H) \
c-decl.o : c-decl.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
$(RTL_H) $(C_TREE_H) $(GGC_H) $(TARGET_H) flags.h function.h output.h \
$(EXPR_H) debug.h toplev.h intl.h $(TM_P_H) tree-inline.h $(TIMEVAR_H) \
c-pragma.h gt-c-decl.h cgraph.h $(HASHTAB_H)
c-pragma.h gt-c-decl.h cgraph.h $(HASHTAB_H) libfuncs.h except.h
c-typeck.o : c-typeck.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(C_TREE_H) \
$(TARGET_H) flags.h intl.h output.h $(EXPR_H) $(RTL_H) toplev.h $(TM_P_H)
c-lang.o : c-lang.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) $(C_TREE_H) \
@ -1310,7 +1310,8 @@ tlink.o: tlink.c $(DEMANGLE_H) $(HASHTAB_H) $(CONFIG_H) $(SYSTEM_H) coretypes.h
c-common.o : c-common.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
$(OBSTACK_H) $(C_COMMON_H) flags.h toplev.h output.h c-pragma.h intl.h \
$(GGC_H) $(EXPR_H) $(TM_P_H) builtin-types.def builtin-attrs.def \
diagnostic.h gt-c-common.h langhooks.h varray.h $(RTL_H) $(TARGET_H)
diagnostic.h gt-c-common.h langhooks.h varray.h $(RTL_H) $(TARGET_H) \
c-tree.h
c-pretty-print.o : c-pretty-print.c c-pretty-print.h pretty-print.h \
$(C_COMMON_H) $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) real.h

View File

@ -41,7 +41,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "target.h"
#include "langhooks.h"
#include "tree-inline.h"
#include "c-tree.h"
cpp_reader *parse_in; /* Declared in c-pragma.h. */
@ -792,6 +792,8 @@ static tree handle_nonnull_attribute PARAMS ((tree *, tree, tree, int,
bool *));
static tree handle_nothrow_attribute PARAMS ((tree *, tree, tree, int,
bool *));
static tree handle_cleanup_attribute PARAMS ((tree *, tree, tree, int,
bool *));
static tree vector_size_helper PARAMS ((tree, tree));
static void check_function_nonnull PARAMS ((tree, tree));
@ -868,6 +870,8 @@ const struct attribute_spec c_common_attribute_table[] =
{ "nothrow", 0, 0, true, false, false,
handle_nothrow_attribute },
{ "may_alias", 0, 0, false, true, false, NULL },
{ "cleanup", 1, 1, true, false, false,
handle_cleanup_attribute },
{ NULL, 0, 0, false, false, false, NULL }
};
@ -6093,6 +6097,55 @@ handle_nothrow_attribute (node, name, args, flags, no_add_attrs)
return NULL_TREE;
}
/* Handle a "cleanup" attribute; arguments as in
struct attribute_spec.handler. */
static tree
handle_cleanup_attribute (node, name, args, flags, no_add_attrs)
tree *node;
tree name;
tree args;
int flags ATTRIBUTE_UNUSED;
bool *no_add_attrs;
{
tree decl = *node;
tree cleanup_id, cleanup_decl;
/* ??? Could perhaps support cleanups on TREE_STATIC, much like we do
for global destructors in C++. This requires infrastructure that
we don't have generically at the moment. It's also not a feature
we'd be missing too much, since we do have attribute constructor. */
if (TREE_CODE (decl) != VAR_DECL || TREE_STATIC (decl))
{
warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
*no_add_attrs = true;
return NULL_TREE;
}
/* Verify that the argument is a function in scope. */
/* ??? We could support pointers to functions here as well, if
that was considered desirable. */
cleanup_id = TREE_VALUE (args);
if (TREE_CODE (cleanup_id) != IDENTIFIER_NODE)
{
error ("cleanup arg not an identifier");
*no_add_attrs = true;
return NULL_TREE;
}
cleanup_decl = lookup_name (cleanup_id);
if (!cleanup_decl || TREE_CODE (cleanup_decl) != FUNCTION_DECL)
{
error ("cleanup arg not a function");
*no_add_attrs = true;
return NULL_TREE;
}
/* That the function has proper type is checked with the
eventual call to build_function_call. */
return NULL_TREE;
}
/* Check for valid arguments being passed to a function. */
void

View File

@ -50,6 +50,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "c-pragma.h"
#include "cgraph.h"
#include "hashtab.h"
#include "libfuncs.h"
#include "except.h"
/* In grokdeclarator, distinguish syntactic contexts of declarators. */
enum decl_context
@ -2985,6 +2987,41 @@ finish_decl (decl, init, asmspec_tree)
computing them in the following function definition. */
if (current_binding_level == global_binding_level)
get_pending_sizes ();
/* Install a cleanup (aka destructor) if one was given. */
if (TREE_CODE (decl) == VAR_DECL && !TREE_STATIC (decl))
{
tree attr = lookup_attribute ("cleanup", DECL_ATTRIBUTES (decl));
if (attr)
{
static bool eh_initialized_p;
tree cleanup_id = TREE_VALUE (TREE_VALUE (attr));
tree cleanup_decl = lookup_name (cleanup_id);
tree cleanup;
/* Build "cleanup(&decl)" for the destructor. */
cleanup = build_unary_op (ADDR_EXPR, decl, 0);
cleanup = build_tree_list (NULL_TREE, cleanup);
cleanup = build_function_call (cleanup_decl, cleanup);
/* Don't warn about decl unused; the cleanup uses it. */
TREE_USED (decl) = 1;
/* Initialize EH, if we've been told to do so. */
if (flag_exceptions && !eh_initialized_p)
{
eh_initialized_p = true;
eh_personality_libfunc
= init_one_libfunc (USING_SJLJ_EXCEPTIONS
? "__gcc_personality_sj0"
: "__gcc_personality_v0");
using_eh_for_cleanups ();
}
add_stmt (build_stmt (CLEANUP_STMT, decl, cleanup));
}
}
}
/* Given a parsed parameter declaration,

View File

@ -40,7 +40,8 @@ crtfastmath.o: $(srcdir)/config/ia64/crtfastmath.c $(GCC_PASSES)
$(GCC_FOR_TARGET) $(LIBGCC2_CFLAGS) -c -o crtfastmath.o \
$(srcdir)/config/ia64/crtfastmath.c
LIB2ADDEH = $(srcdir)/config/ia64/unwind-ia64.c $(srcdir)/unwind-sjlj.c
LIB2ADDEH = $(srcdir)/config/ia64/unwind-ia64.c $(srcdir)/unwind-sjlj.c \
$(srcdir)/unwind-c.c
ia64-c.o: $(srcdir)/config/ia64/ia64-c.c $(CONFIG_H) $(SYSTEM_H) \
coretypes.h $(TM_H) $(TREE_H) $(CPPLIB_H) $(C_COMMON_H) c-pragma.h toplev.h

View File

@ -18,5 +18,5 @@ $(T)crt2$(objext): $(srcdir)/config/darwin-crt2.c $(GCC_PASSES) \
# Use unwind-dw2-fde-darwin
LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde-darwin.c \
$(srcdir)/unwind-sjlj.c
$(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h unwind-dw2-fde.c

View File

@ -12,5 +12,5 @@ SHLIB_MAPFILES += $(srcdir)/config/libgcc-glibc.ver
# Use unwind-dw2-fde-glibc
LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde-glibc.c \
$(srcdir)/unwind-sjlj.c $(srcdir)/gthr-gnat.c
$(srcdir)/unwind-sjlj.c $(srcdir)/gthr-gnat.c $(srcdir)/unwind-c.c
LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h unwind-dw2-fde.c gthr-gnat.c

View File

@ -3,5 +3,5 @@ T_CFLAGS = -DUSE_GNULIBC_1
# Use unwind-dw2-fde
LIB2ADDEH = $(srcdir)/unwind-dw2.c $(srcdir)/unwind-dw2-fde.c \
$(srcdir)/unwind-sjlj.c
$(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
LIB2ADDEHDEP = unwind.inc unwind-dw2-fde.h

View File

@ -3034,6 +3034,22 @@ up to a maximum of 8 byte alignment, then specifying @code{aligned(16)}
in an @code{__attribute__} will still only provide you with 8 byte
alignment. See your linker documentation for further information.
@item cleanup (@var{cleanup_function})
@cindex @code{cleanup} attribute
The @code{cleanup} attribute runs a function when the variable goes
out of scope. This attribute can only be applied to auto function
scope variables; it may not be applied to parameters or variables
with static storage duration. The function must take one parameter,
a pointer to a type compatible with the variable. The return value
of the function (if any) is ignored.
If @option{-fexceptions} is enabled, then @var{cleanup_function}
will be run during the stack unwinding that happens during the
processing of the exception. Note that the @code{cleanup} attribute
does not allow the exception to be caught, only to perform an action.
It is undefined what happens if @var{cleanup_function} does not
return normally.
@item common
@itemx nocommon
@cindex @code{common} attribute

View File

@ -1,3 +1,13 @@
2003-06-04 Richard Henderson <rth@redhat.com>
* gcc.dg/cleanup-1.c: New.
* gcc.dg/cleanup-2.c: New.
* gcc.dg/cleanup-3.c: New.
* gcc.dg/cleanup-4.c: New.
* gcc.dg/cleanup-5.c: New.
* gcc.dg/cleanup-6.c: New.
* gcc.dg/cleanup-7.c: New.
2003-06-04 Mark Mitchell <mark@codesourcery.com>
* g++.dg/abi/vague1.C: Use xfail, rather than embedded Tcl code.

View File

@ -0,0 +1,36 @@
/* { dg-do compile } */
/* { dg-options "-Wall" } */
/* Validate expected warnings and errors. */
#define U __attribute__((unused))
#define C(x) __attribute__((cleanup(x)))
static int f1(void *x U) { return 0; }
static void f2() { }
static void f3(void) { }
static void f4(void *x U) { }
static void f5(int *x U) { }
static void f6(double *x U) { }
static void f7(const int *x U) { }
static void f8(const int *x U, int y U) { }
static void f9(int x U) { }
void test(void)
{
int o1 C(f1);
int o2 C(f2);
int o3 C(f3); /* { dg-error "too many arguments" } */
int o4 C(f4);
int o5 C(f5);
int o6 C(f6); /* { dg-warning "incompatible pointer type" } */
int o7 C(f7);
int o8 C(f8); /* { dg-error "too few arguments" } */
int o9 C(f9); /* { dg-warning "from pointer without a cast" } */
int o10 U C(undef); /* { dg-error "not a function" } */
int o11 U C(o1); /* { dg-error "not a function" } */
int o12 U C("f1"); /* { dg-error "not an identifier" } */
static int o13 U C(f1); /* { dg-warning "attribute ignored" } */
}
int o14 C(f1); /* { dg-warning "attribute ignored" } */
void t15(int o U C(f1)) {} /* { dg-warning "attribute ignored" } */

View File

@ -0,0 +1,22 @@
/* { dg-do run } */
/* { dg-options "" } */
/* Verify that cleanup works in the most basic of ways. */
extern void exit(int);
extern void abort(void);
static void handler(void *p __attribute__((unused)))
{
exit (0);
}
static void doit(void)
{
int x __attribute__((cleanup (handler)));
}
int main()
{
doit ();
abort ();
}

View File

@ -0,0 +1,45 @@
/* { dg-do run } */
/* { dg-options "" } */
/* Verify that the cleanup handler receives the proper contents
of the variable. */
extern void exit(int);
extern void abort(void);
static int expected;
static void
handler(int *p)
{
if (*p != expected)
abort ();
}
static void __attribute__((noinline))
bar(void)
{
}
static void doit(int x, int y)
{
int r __attribute__((cleanup (handler)));
if (x < y)
{
r = 0;
return;
}
bar();
r = x + y;
}
int main()
{
expected = 0;
doit (1, 2);
expected = 3;
doit (2, 1);
return 0;
}

View File

@ -0,0 +1,39 @@
/* { dg-do run } */
/* { dg-options "" } */
/* Verify cleanup execution on non-trivial exit from a block. */
extern void exit(int);
extern void abort(void);
static int counter;
static void
handler(int *p)
{
counter += *p;
}
static void __attribute__((noinline))
bar(void)
{
}
static void doit(int n, int n2)
{
int i;
for (i = 0; i < n; ++i)
{
int dummy __attribute__((cleanup (handler))) = i;
if (i == n2)
break;
bar();
}
}
int main()
{
doit (10, 6);
if (counter != 0 + 1 + 2 + 3 + 4 + 5 + 6)
abort ();
return 0;
}

View File

@ -0,0 +1,50 @@
/* { dg-do run } */
/* { dg-options "-fexceptions" } */
/* Verify that cleanups work with exception handling. */
#include <unwind.h>
#include <stdlib.h>
static _Unwind_Reason_Code
force_unwind_stop (int version, _Unwind_Action actions,
_Unwind_Exception_Class exc_class,
struct _Unwind_Exception *exc_obj,
struct _Unwind_Context *context,
void *stop_parameter)
{
if (actions & _UA_END_OF_STACK)
abort ();
return _URC_NO_REASON;
}
static void force_unwind ()
{
struct _Unwind_Exception *exc = malloc (sizeof (*exc));
exc->exception_class = 0;
exc->exception_cleanup = 0;
#ifndef __USING_SJLJ_EXCEPTIONS__
_Unwind_ForcedUnwind (exc, force_unwind_stop, 0);
#else
_Unwind_SjLj_ForcedUnwind (exc, force_unwind_stop, 0);
#endif
abort ();
}
static void handler (void *p __attribute__((unused)))
{
exit (0);
}
static void doit ()
{
char dummy __attribute__((cleanup (handler)));
force_unwind ();
}
int main()
{
doit ();
abort ();
}

View File

@ -0,0 +1,14 @@
/* { dg-do compile } */
/* { dg-options "-O" } */
/* Verify that a cleanup marked "inline" gets inlined. */
static inline void xyzzy(void *p __attribute__((unused)))
{
}
void doit(void)
{
int x __attribute__((cleanup (xyzzy)));
}
/* { dg-final { scan-assembler-not "xyzzy" } } */

View File

@ -0,0 +1,22 @@
/* { dg-do run } */
/* { dg-options "" } */
/* Verify that the search for function happens in the proper scope. */
extern void exit(int);
extern void abort(void);
int main()
{
auto void xyzzy(void *p __attribute__((unused)))
{
exit (0);
}
auto void doit ()
{
int x __attribute__((cleanup (xyzzy)));
}
doit ();
abort ();
}

186
gcc/unwind-c.c Normal file
View File

@ -0,0 +1,186 @@
/* Supporting functions for C exception handling.
Copyright (C) 2002, 2003 Free Software Foundation, Inc.
Contributed by Aldy Hernandez <aldy@quesejoda.com>.
Shamelessly stolen from the Java front end.
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 2, 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 COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#include "tconfig.h"
#include "tsystem.h"
#include "unwind.h"
#include "unwind-pe.h"
typedef struct
{
_Unwind_Ptr Start;
_Unwind_Ptr LPStart;
_Unwind_Ptr ttype_base;
const unsigned char *TType;
const unsigned char *action_table;
unsigned char ttype_encoding;
unsigned char call_site_encoding;
} lsda_header_info;
static const unsigned char *
parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
lsda_header_info *info)
{
_Unwind_Word tmp;
unsigned char lpstart_encoding;
info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
/* Find @LPStart, the base to which landing pad offsets are relative. */
lpstart_encoding = *p++;
if (lpstart_encoding != DW_EH_PE_omit)
p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart);
else
info->LPStart = info->Start;
/* Find @TType, the base of the handler and exception spec type data. */
info->ttype_encoding = *p++;
if (info->ttype_encoding != DW_EH_PE_omit)
{
p = read_uleb128 (p, &tmp);
info->TType = p + tmp;
}
else
info->TType = 0;
/* The encoding and length of the call-site table; the action table
immediately follows. */
info->call_site_encoding = *p++;
p = read_uleb128 (p, &tmp);
info->action_table = p + tmp;
return p;
}
#ifdef __USING_SJLJ_EXCEPTIONS__
#define PERSONALITY_FUNCTION __gcc_personality_sj0
#define __builtin_eh_return_data_regno(x) x
#else
#define PERSONALITY_FUNCTION __gcc_personality_v0
#endif
#define PERSONALITY_FUNCTION __gcc_personality_v0
_Unwind_Reason_Code
PERSONALITY_FUNCTION (int, _Unwind_Action, _Unwind_Exception_Class,
struct _Unwind_Exception *, struct _Unwind_Context *);
_Unwind_Reason_Code
PERSONALITY_FUNCTION (int version,
_Unwind_Action actions,
_Unwind_Exception_Class exception_class ATTRIBUTE_UNUSED,
struct _Unwind_Exception *ue_header,
struct _Unwind_Context *context)
{
lsda_header_info info;
const unsigned char *language_specific_data, *p, *action_record;
_Unwind_Ptr landing_pad, ip;
if (version != 1)
return _URC_FATAL_PHASE1_ERROR;
/* Currently we only support cleanups for C. */
if ((actions & _UA_CLEANUP_PHASE) == 0)
return _URC_CONTINUE_UNWIND;
language_specific_data = (const unsigned char *)
_Unwind_GetLanguageSpecificData (context);
/* If no LSDA, then there are no handlers or cleanups. */
if (! language_specific_data)
return _URC_CONTINUE_UNWIND;
/* Parse the LSDA header. */
p = parse_lsda_header (context, language_specific_data, &info);
ip = _Unwind_GetIP (context) - 1;
landing_pad = 0;
#ifdef __USING_SJLJ_EXCEPTIONS__
/* The given "IP" is an index into the call-site table, with two
exceptions -- -1 means no-action, and 0 means terminate. But
since we're using uleb128 values, we've not got random access
to the array. */
if ((int) ip <= 0)
return _URC_CONTINUE_UNWIND;
else
{
_Unwind_Word cs_lp, cs_action;
do
{
p = read_uleb128 (p, &cs_lp);
p = read_uleb128 (p, &cs_action);
}
while (--ip);
/* Can never have null landing pad for sjlj -- that would have
been indicated by a -1 call site index. */
landing_pad = cs_lp + 1;
if (cs_action)
action_record = info.action_table + cs_action - 1;
goto found_something;
}
#else
/* Search the call-site table for the action associated with this IP. */
while (p < info.action_table)
{
_Unwind_Ptr cs_start, cs_len, cs_lp;
_Unwind_Word cs_action;
/* Note that all call-site encodings are "absolute" displacements. */
p = read_encoded_value (0, info.call_site_encoding, p, &cs_start);
p = read_encoded_value (0, info.call_site_encoding, p, &cs_len);
p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp);
p = read_uleb128 (p, &cs_action);
/* The table is sorted, so if we've passed the ip, stop. */
if (ip < info.Start + cs_start)
p = info.action_table;
else if (ip < info.Start + cs_start + cs_len)
{
if (cs_lp)
landing_pad = info.LPStart + cs_lp;
if (cs_action)
action_record = info.action_table + cs_action - 1;
goto found_something;
}
}
#endif
/* IP is not in table. No associated cleanups. */
/* ??? This is where C++ calls std::terminate to catch throw
from a destructor. */
return _URC_CONTINUE_UNWIND;
found_something:
if (landing_pad == 0)
{
/* IP is present, but has a null landing pad.
No handler to be run. */
return _URC_CONTINUE_UNWIND;
}
_Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
(_Unwind_Ptr) ue_header);
_Unwind_SetGR (context, __builtin_eh_return_data_regno (1), 0);
_Unwind_SetIP (context, landing_pad);
return _URC_INSTALL_CONTEXT;
}