This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[trans-mem] thread-private malloc optimizations
- From: Aldy Hernandez <aldyh at redhat dot com>
- To: rth at redhat dot com, gcc-patches at gcc dot gnu dot org
- Date: Mon, 7 Dec 2009 18:31:15 -0400
- Subject: [trans-mem] thread-private malloc optimizations
In the previous patch I attacked transaction local malloc optimizations,
which are new memories created within a transaction. This is a subset
of the more general thread-private malloc optimizations. As rth
explained, thread-private memory is memory that is created in a function
but whose address does not escape it, and is thus invisible to other
threads.
Here I implement the more general case of thread-private malloc optimizations.
This should be it for malloc type optimizations, although perhaps... we
could be more aggressive with some phi nodes if we previously calculated
logging information on any of its components. Hmmmm, I'll think about
this.
OK for branch?
* trans-mem.c (transaction_invariant_address_p): Exclude self.
Look inside INDIRECT_REF.
(transaction_local_new_memory_p): Handle thread-private memory
that is not transaction local.
(requires_barrier): Pass additional argument.
Index: testsuite/gcc.dg/tm/memopt-10.c
===================================================================
--- testsuite/gcc.dg/tm/memopt-10.c (revision 0)
+++ testsuite/gcc.dg/tm/memopt-10.c (revision 0)
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-fgnu-tm -O -fdump-tree-tmmark" } */
+
+extern int something(void) __attribute__((transaction_safe));
+extern void *malloc (__SIZE_TYPE__) __attribute__((malloc,transaction_safe));
+
+int f()
+{
+ int *p;
+
+ p = malloc (sizeof (*p) * 100);
+
+ __transaction {
+ /* p[5] is thread private, but not transaction local since the
+ malloc is outside of the transaction. We can use the logging
+ functions for this. */
+ p[5] = 123;
+
+ if (something())
+ __transaction_cancel;
+ }
+ return p[5];
+}
+
+/* { dg-final { scan-tree-dump-times "ITM_LU" 1 "tmmark" } } */
+/* { dg-final { scan-tree-dump-times "ITM_WU" 0 "tmmark" } } */
+/* { dg-final { scan-tree-dump-times "tm_save" 0 "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmmark" } } */
Index: testsuite/gcc.dg/tm/memopt-11.c
===================================================================
--- testsuite/gcc.dg/tm/memopt-11.c (revision 0)
+++ testsuite/gcc.dg/tm/memopt-11.c (revision 0)
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-fgnu-tm -O -fdump-tree-tmmark" } */
+
+extern int something(void) __attribute__((transaction_safe));
+extern void *malloc (__SIZE_TYPE__) __attribute__((malloc,transaction_safe));
+
+int f()
+{
+ int *p;
+
+ p = malloc (sizeof (*p) * 100);
+ foo(p[5]);
+
+ __transaction {
+ /* p[5] is thread private, however the SSA_NAME that holds the
+ address dominates the entire transaction (transaction
+ invariant) so we can use a save/restore pair. */
+ p[5] = 123;
+
+ if (something())
+ __transaction_cancel;
+ }
+ return p[5];
+}
+
+/* { dg-final { scan-tree-dump-times "ITM_LU" 0 "tmmark" } } */
+/* { dg-final { scan-tree-dump-times "ITM_WU" 0 "tmmark" } } */
+/* { dg-final { scan-tree-dump-times "tm_save" 1 "tmmark" } } */
+/* { dg-final { cleanup-tree-dump "tmmark" } } */
Index: trans-mem.c
===================================================================
--- trans-mem.c (revision 155051)
+++ trans-mem.c (working copy)
@@ -832,10 +832,15 @@ tm_log_delete (void)
static bool
transaction_invariant_address_p (const_tree mem, basic_block region_entry_block)
{
- if (TREE_CODE (mem) == SSA_NAME)
- return dominated_by_p (CDI_DOMINATORS,
- region_entry_block,
- gimple_bb (SSA_NAME_DEF_STMT (mem)));
+ if (TREE_CODE (mem) == INDIRECT_REF
+ && TREE_CODE (TREE_OPERAND (mem, 0)) == SSA_NAME)
+ {
+ basic_block def_bb;
+
+ def_bb = gimple_bb (SSA_NAME_DEF_STMT (TREE_OPERAND (mem, 0)));
+ return def_bb != region_entry_block
+ && dominated_by_p (CDI_DOMINATORS, region_entry_block, def_bb);
+ }
mem = strip_invariant_refs (mem);
return mem && (CONSTANT_CLASS_P (mem) || decl_address_invariant_p (mem));
@@ -1145,20 +1150,28 @@ static tree lower_sequence_tm (gimple_st
static tree lower_sequence_no_tm (gimple_stmt_iterator *, bool *,
struct walk_stmt_info *);
-/* Given an address that is being dereferenced, return TRUE if we can
- prove that the memory being dereferenced is a non aliased new chunk
- of memory local to the transaction (malloc, alloca, etc), which
- therefore requires no instrumentation.
+/* Given an address X that is being dereferenced, return TRUE if we
+ can prove that the memory being dereferenced is a non aliased new
+ chunk of memory local to the transaction (malloc, alloca, etc),
+ which therefore requires no instrumentation.
+
+ ORIG_MEM is the original memory reference.
+
+ ORIG_STMT is the statement the memory reference is in.
ENTRY_BLOCK is the entry block to the transaction containing the
dereference of X. */
static bool
-transaction_local_new_memory_p (basic_block entry_block, tree x)
+transaction_local_new_memory_p (basic_block entry_block, tree x,
+ tree orig_mem, gimple orig_stmt)
{
gimple stmt = NULL;
enum tree_code code;
void **slot;
tm_new_mem_map_t elt, *elt_p;
+ bool transaction_local_p = true;
+ bool retval = false;
+ tree val = x;
if (!entry_block)
return false;
@@ -1172,13 +1185,6 @@ transaction_local_new_memory_p (basic_bl
elt_p = (tm_new_mem_map_t *) *slot;
if (elt_p)
return elt_p->local_new_memory_p;
- else
- {
- elt_p = XNEW (tm_new_mem_map_t);
- elt_p->val = x;
- elt_p->local_new_memory_p = false;
- *slot = elt_p;
- }
/* Search back through the DEF chain to find the original definition
of this address. */
@@ -1187,7 +1193,7 @@ transaction_local_new_memory_p (basic_bl
if (ptr_deref_may_alias_global_p (x))
{
/* Pointed to address escapes. This is not thread-private. */
- return false;
+ goto new_memory_ret;
}
stmt = SSA_NAME_DEF_STMT (x);
@@ -1196,8 +1202,9 @@ transaction_local_new_memory_p (basic_bl
care about transaction local mallocs. There are no edges
into a transaction from without, so a simple dominance test
will do. */
- if (!dominated_by_p (CDI_DOMINATORS, gimple_bb (stmt), entry_block))
- return false;
+ if (transaction_local_p
+ && !dominated_by_p (CDI_DOMINATORS, gimple_bb (stmt), entry_block))
+ transaction_local_p = false;
if (is_gimple_assign (stmt))
{
@@ -1212,7 +1219,7 @@ transaction_local_new_memory_p (basic_bl
else if (code == VIEW_CONVERT_EXPR || code == NOP_EXPR)
x = gimple_assign_rhs1 (stmt);
else
- return false;
+ goto new_memory_ret;
}
else
break;
@@ -1221,14 +1228,31 @@ transaction_local_new_memory_p (basic_bl
if (stmt && is_gimple_call (stmt) && gimple_call_flags (stmt) & ECF_MALLOC)
{
- /* No logging to do. For alloca, the stack pointer gets reset
- by the retry and we reallocate. For malloc, a transaction
- restart frees the memory and we reallocate. Similarly for
- user-defined malloc type routines, which should release the
- memory on a transaction restart. */
- return elt_p->local_new_memory_p = true;
+ retval = true;
+ if (transaction_local_p)
+ {
+ /* No logging to do. For alloca, the stack pointer gets
+ reset by the retry and we reallocate. For malloc, a
+ transaction restart frees the memory and we reallocate.
+ Similarly for user-defined malloc type routines, which
+ should release the memory on a transaction restart. */
+ }
+ else
+ {
+ /* Thread-private chunk, use the log if requested. */
+ if (orig_stmt)
+ tm_log_add (entry_block, orig_mem, orig_stmt);
+ else
+ return true; /* Don't cache just yet. */
+ }
}
- return false;
+
+ new_memory_ret:
+ elt_p = XNEW (tm_new_mem_map_t);
+ elt_p->val = val;
+ elt_p->local_new_memory_p = retval;
+ *slot = elt_p;
+ return retval;
}
/* Determine whether X has to be instrumented using a read
@@ -1250,7 +1274,11 @@ requires_barrier (basic_block entry_bloc
switch (TREE_CODE (x))
{
case INDIRECT_REF:
- if (transaction_local_new_memory_p (entry_block, TREE_OPERAND (x, 0)))
+ if (transaction_local_new_memory_p
+ (entry_block, TREE_OPERAND (x, 0),
+ /* ?? Should we pass `orig', or the INDIRECT_REF X. ?? */
+ orig,
+ stmt))
return false;
return true;