This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[trans-mem] automatically wrap malloc type functions
- From: Aldy Hernandez <aldyh at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org, rth at redhat dot com
- Date: Mon, 3 May 2010 16:33:44 -0400
- Subject: [trans-mem] automatically wrap malloc type functions
Hi.
Calls to malloc/calloc/free inside a transaction should be automatically
converted to their TM counterparts. This patch accomplishes this.
A few notes.
- I am setting the _ITM_malloc type builtins to transaction_pure.
- I did not put realloc in the mix because in a separate IRC chat we
agreed that the libitm implementation of _ITM_realloc is not thread
safe, and should be deleted. I will do so next.
- In diagnose_tm_1(), I have equated is_tm_pure() implies is_tm_safe().
Let me know if you disagree.
- Lastly, I set TREE_NOTHROW for the replacement functions for the C++
FE (see tm_malloc_replacement), as I've seen you do in cp/except.c for
some replacements. Otherwise verify_stmt() dies in the C++ testcase
with:
"statement marked for throw, but doesn't"
I noticed Linux's <stdlib.h> sets "throw()" on the malloc prototype for
C++, so I assume this is the expected declaration. This code in
tm_malloc_replacement automates it, like I believe is happening in the
TM code in cp/except.c.
OK for branch?
* tree.h (tm_malloc_replacement): Protoize.
* builtin-attrs.def (ATTR_TM_PURE_MALLOC_NOTHROW_LIST): New.
(ATTR_TM_PURE_NOTHROW_LIST): New.
* builtins.def: Add comments regarding TM synchronization.
* gtm-builtins.def (BUILT_IN_TM_MALLOC): New.
(BUILT_IN_TM_CALLOC): New.
(BUILT_IN_TM_FREE): New.
* trans-mem.c (tm_malloc_replacement): New.
(diagnose_tm_1): TM pure functions are safe.
Handle replacements.
* c-typeck.c (build_function_call_vec): Call
tm_malloc_replacement.
* cp/call.c (build_new_function_call): Call tm_malloc_replacement.
Index: tree.h
===================================================================
--- tree.h (revision 158824)
+++ tree.h (working copy)
@@ -5388,6 +5388,7 @@ extern bool is_tm_callable (tree);
extern bool is_tm_irrevocable (tree);
extern bool is_tm_may_cancel_outer (tree);
extern void record_tm_replacement (tree, tree);
+extern void tm_malloc_replacement (tree, bool);
/* In tree-inline.c. */
Index: builtin-attrs.def
===================================================================
--- builtin-attrs.def (revision 158824)
+++ builtin-attrs.def (working copy)
@@ -207,6 +207,13 @@ DEF_ATTR_TREE_LIST (ATTR_TM_NORETURN_NOT
DEF_ATTR_TREE_LIST (ATTR_TM_CONST_NOTHROW_LIST,
ATTR_TM_REGPARM, ATTR_NULL, ATTR_CONST_NOTHROW_LIST)
+/* Same attributes used for BUILT_IN_MALLOC except with TM_PURE thrown in. */
+DEF_ATTR_TREE_LIST (ATTR_TM_PURE_MALLOC_NOTHROW_LIST,
+ ATTR_TM_PURE, ATTR_NULL, ATTR_MALLOC_NOTHROW_LIST)
+/* Same attributes used for BUILT_IN_FREE except with TM_PURE thrown in. */
+DEF_ATTR_TREE_LIST (ATTR_TM_PURE_NOTHROW_LIST,
+ ATTR_TM_PURE, ATTR_NULL, ATTR_NOTHROW_LIST)
+
/* Construct a tree for a format_arg attribute. */
#define DEF_FORMAT_ARG_ATTRIBUTE(FA) \
DEF_ATTR_TREE_LIST (ATTR_FORMAT_ARG_##FA, ATTR_FORMAT_ARG, \
Index: testsuite/c-c++-common/tm/malloc.c
===================================================================
--- testsuite/c-c++-common/tm/malloc.c (revision 0)
+++ testsuite/c-c++-common/tm/malloc.c (revision 0)
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-fgnu-tm -fdump-tree-tmmark" } */
+
+#include <stdlib.h>
+
+char *z;
+
+void foobar(void)
+{
+ char *p, *q;
+ __transaction {
+ p = (char *)malloc(123);
+ q = (char *)calloc(555,1);
+ free(q);
+ free(p);
+ }
+ z = (char *)malloc (666);
+}
+
+/* { dg-final { scan-tree-dump-times " malloc .666" 1 "tmmark" } } */
+/* { dg-final { scan-tree-dump-times "__builtin__ITM_malloc" 1 "tmmark" } } */
+/* { dg-final { scan-tree-dump-times "__builtin__ITM_calloc" 1 "tmmark" } } */
+/* { dg-final { scan-tree-dump-times "__builtin__ITM_free" 2 "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmmark" } } */
Index: cp/call.c
===================================================================
--- cp/call.c (revision 158824)
+++ cp/call.c (working copy)
@@ -3169,6 +3169,9 @@ build_new_function_call (tree fn, VEC(tr
return error_mark_node;
}
+ if (flag_tm)
+ tm_malloc_replacement (fn, true);
+
/* If this function was found without using argument dependent
lookup, then we want to ignore any undeclared friend
functions. */
Index: builtins.def
===================================================================
--- builtins.def (revision 158824)
+++ builtins.def (working copy)
@@ -617,6 +617,7 @@ DEF_GCC_BUILTIN (BUILT_IN_ARGS_IN
DEF_GCC_BUILTIN (BUILT_IN_BSWAP32, "bswap32", BT_FN_UINT32_UINT32, ATTR_CONST_NOTHROW_LIST)
DEF_GCC_BUILTIN (BUILT_IN_BSWAP64, "bswap64", BT_FN_UINT64_UINT64, ATTR_CONST_NOTHROW_LIST)
DEF_EXT_LIB_BUILTIN (BUILT_IN_CLEAR_CACHE, "__clear_cache", BT_FN_VOID_PTR_PTR, ATTR_NOTHROW_LIST)
+/* Adjust BUILT_IN_TM_CALLOC if BUILT_IN_CALLOC is changed. */
DEF_LIB_BUILTIN (BUILT_IN_CALLOC, "calloc", BT_FN_PTR_SIZE_SIZE, ATTR_MALLOC_NOTHROW_LIST)
DEF_GCC_BUILTIN (BUILT_IN_CLASSIFY_TYPE, "classify_type", BT_FN_INT_VAR, ATTR_NULL)
DEF_GCC_BUILTIN (BUILT_IN_CLZ, "clz", BT_FN_INT_UINT, ATTR_CONST_NOTHROW_LIST)
@@ -650,6 +651,7 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_FFSL, "
DEF_EXT_LIB_BUILTIN (BUILT_IN_FFSLL, "ffsll", BT_FN_INT_LONGLONG, ATTR_CONST_NOTHROW_LIST)
DEF_EXT_LIB_BUILTIN (BUILT_IN_FORK, "fork", BT_FN_PID, ATTR_NOTHROW_LIST)
DEF_GCC_BUILTIN (BUILT_IN_FRAME_ADDRESS, "frame_address", BT_FN_PTR_UINT, ATTR_NULL)
+/* Adjust BUILT_IN_TM_FREE if BUILT_IN_FREE is changed. */
DEF_LIB_BUILTIN (BUILT_IN_FREE, "free", BT_FN_VOID_PTR, ATTR_NOTHROW_LIST)
DEF_GCC_BUILTIN (BUILT_IN_FROB_RETURN_ADDR, "frob_return_addr", BT_FN_PTR_PTR, ATTR_NULL)
DEF_EXT_LIB_BUILTIN (BUILT_IN_GETTEXT, "gettext", BT_FN_STRING_CONST_STRING, ATTR_FORMAT_ARG_1)
@@ -686,6 +688,8 @@ DEF_GCC_BUILTIN (BUILT_IN_ISUNORD
DEF_LIB_BUILTIN (BUILT_IN_LABS, "labs", BT_FN_LONG_LONG, ATTR_CONST_NOTHROW_LIST)
DEF_C99_BUILTIN (BUILT_IN_LLABS, "llabs", BT_FN_LONGLONG_LONGLONG, ATTR_CONST_NOTHROW_LIST)
DEF_GCC_BUILTIN (BUILT_IN_LONGJMP, "longjmp", BT_FN_VOID_PTR_INT, ATTR_NORETURN_NOTHROW_LIST)
+/* If the attribute used for malloc below is changed, also change the memory
+ allocation builtins in gtm-builtins.def (See BUILT_IN_TM_MALLOC). */
DEF_LIB_BUILTIN (BUILT_IN_MALLOC, "malloc", BT_FN_PTR_SIZE, ATTR_MALLOC_NOTHROW_LIST)
DEF_GCC_BUILTIN (BUILT_IN_NEXT_ARG, "next_arg", BT_FN_PTR_VAR, ATTR_NULL)
DEF_GCC_BUILTIN (BUILT_IN_PARITY, "parity", BT_FN_INT_UINT, ATTR_CONST_NOTHROW_LIST)
Index: trans-mem.c
===================================================================
--- trans-mem.c (revision 158930)
+++ trans-mem.c (working copy)
@@ -485,6 +485,44 @@ find_tm_replacement_function (tree fndec
return NULL;
}
+
+/* When appropriate, record TM replacement for memory allocation functions.
+
+ FROM is the FNDECL to wrap.
+
+ NOTHROW is true if TREE_NOTHROW must be set for the replacement.
+ This is usually set for C++. */
+void
+tm_malloc_replacement (tree from, bool nothrow)
+{
+ const char *str;
+ tree to;
+
+ if (TREE_CODE (from) != FUNCTION_DECL)
+ return;
+
+ /* If we have a previous replacement, the user must be explicitly
+ wrapping malloc/calloc/free. They better know what they're
+ doing... */
+ if (find_tm_replacement_function (from))
+ return;
+
+ str = IDENTIFIER_POINTER (DECL_NAME (from));
+
+ if (!strcmp (str, "malloc"))
+ to = built_in_decls[BUILT_IN_TM_MALLOC];
+ else if (!strcmp (str, "calloc"))
+ to = built_in_decls[BUILT_IN_TM_CALLOC];
+ else if (!strcmp (str, "free"))
+ to = built_in_decls[BUILT_IN_TM_FREE];
+ else
+ return;
+
+ if (nothrow)
+ TREE_NOTHROW (to) = 0;
+
+ record_tm_replacement (from, to);
+}
/* Diagnostics for tm_safe functions/regions. Called by the front end
once we've lowered the function to high-gimple. */
@@ -529,9 +567,25 @@ diagnose_tm_1 (gimple_stmt_iterator *gsi
if (d->summary_flags & DIAG_TM_SAFE)
{
- bool is_safe;
+ bool is_safe, direct_call_p;
+ tree replacement;
+
+ if (TREE_CODE (fn) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL)
+ {
+ direct_call_p = true;
+ replacement = TREE_OPERAND (fn, 0);
+ replacement = find_tm_replacement_function (replacement);
+ if (replacement)
+ fn = replacement;
+ }
+ else
+ {
+ direct_call_p = false;
+ replacement = NULL_TREE;
+ }
- if (is_tm_safe (fn))
+ if (is_tm_safe (fn) || is_tm_pure (fn))
is_safe = true;
else if (is_tm_callable (fn) || is_tm_irrevocable (fn))
{
@@ -540,10 +594,9 @@ diagnose_tm_1 (gimple_stmt_iterator *gsi
unsafe as part of its ABI, regardless of its contents. */
is_safe = false;
}
- else if (TREE_CODE (fn) == ADDR_EXPR
- && TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL)
+ else if (direct_call_p)
{
- if (find_tm_replacement_function (TREE_OPERAND (fn, 0)))
+ if (replacement)
{
/* ??? At present we've been considering replacements
merely transaction_callable, and therefore might
Index: c-typeck.c
===================================================================
--- c-typeck.c (revision 158824)
+++ c-typeck.c (working copy)
@@ -2638,6 +2638,9 @@ build_function_call_vec (location_t loc,
return tem;
name = DECL_NAME (function);
+
+ if (flag_tm)
+ tm_malloc_replacement (function, false);
fundecl = function;
}
if (TREE_CODE (TREE_TYPE (function)) == FUNCTION_TYPE)
Index: gtm-builtins.def
===================================================================
--- gtm-builtins.def (revision 158824)
+++ gtm-builtins.def (working copy)
@@ -20,6 +20,14 @@ DEF_TM_BUILTIN (BUILT_IN_TM_GETTMCLONE_I
DEF_TM_BUILTIN (BUILT_IN_TM_GETTMCLONE_SAFE, "_ITM_getTMCloneSafe",
BT_FN_PTR_PTR, ATTR_TM_CONST_NOTHROW_LIST)
+/* Memory allocation builtins. */
+DEF_TM_BUILTIN (BUILT_IN_TM_MALLOC, "_ITM_malloc",
+ BT_FN_PTR_SIZE, ATTR_TM_PURE_MALLOC_NOTHROW_LIST)
+DEF_TM_BUILTIN (BUILT_IN_TM_CALLOC, "_ITM_calloc",
+ BT_FN_PTR_SIZE_SIZE, ATTR_TM_PURE_MALLOC_NOTHROW_LIST)
+DEF_TM_BUILTIN (BUILT_IN_TM_FREE, "_ITM_free",
+ BT_FN_VOID_PTR, ATTR_TM_PURE_NOTHROW_LIST)
+
/* Logging builtins. */
DEF_TM_BUILTIN (BUILT_IN_TM_LOG_1, "_ITM_LU1",
BT_FN_VOID_VPTR, ATTR_TM_PURE_TMPURE_NOTHROW_LIST)