From: Jason Merrill Date: Fri, 24 Jun 2005 19:30:20 +0000 (-0400) Subject: tree-nrv.c (tree_nrv): Fix to check assignments to the RESULT_DECL rather than just... X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0620800904747ddbcece61fea155906d3c681e2c;p=platform%2Fupstream%2Fgcc.git tree-nrv.c (tree_nrv): Fix to check assignments to the RESULT_DECL rather than just RETURN_EXPRs. * tree-nrv.c (tree_nrv): Fix to check assignments to the RESULT_DECL rather than just RETURN_EXPRs. (finalize_nrv_r): Adjust. From-SVN: r101296 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index f1c680c..ca633f3 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2005-06-24 Jason Merrill + + * tree-nrv.c (tree_nrv): Fix to check assignments to the + RESULT_DECL rather than just RETURN_EXPRs. + (finalize_nrv_r): Adjust. + 2005-06-24 Jan Hubicka * tree-optimize.c (init_tree_optimization_passes): Fix flags of diff --git a/gcc/testsuite/gcc.dg/nrv1.c b/gcc/testsuite/gcc.dg/nrv1.c new file mode 100644 index 0000000..ca70092 --- /dev/null +++ b/gcc/testsuite/gcc.dg/nrv1.c @@ -0,0 +1,28 @@ +/* Test that the NRV optimization doesn't cause a1 to change too soon. This + is equivalent to c++/19317. */ +/* { dg-do run } */ + +void abort (void); + +struct A +{ + int i[100]; +}; + +struct A a1; + +struct A f () +{ + struct A a2; + a2.i[0] = 42; + /* a1.i[0] should still be 0 until we return. */ + if (a1.i[0] != 0) + abort (); + return a2; +} + +int main() +{ + a1 = f(); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/nrv2.c b/gcc/testsuite/gcc.dg/nrv2.c new file mode 100644 index 0000000..e4d1f9a --- /dev/null +++ b/gcc/testsuite/gcc.dg/nrv2.c @@ -0,0 +1,25 @@ +/* Test that the tree_nrv pass works by making sure that we don't generate + a memcpy. Throw in a bit of control flow to make its job a bit harder. */ + +/* { dg-options "-O" } */ + +struct A { int i[100]; }; + +int b; + +struct A f () +{ + struct A a; + if (b) + { + a.i[0] = 42; + return a; + } + else + { + a.i[42] = 1; + return a; + } +} + +/* { dg-final { scan-assembler-not "memcpy" } } */ diff --git a/gcc/tree-nrv.c b/gcc/tree-nrv.c index a88c977..9e1c56d 100644 --- a/gcc/tree-nrv.c +++ b/gcc/tree-nrv.c @@ -81,10 +81,6 @@ finalize_nrv_r (tree *tp, int *walk_subtrees, void *data) if (TYPE_P (*tp)) *walk_subtrees = 0; - /* If this is a RETURN_EXPR, set the expression being returned to RESULT. */ - else if (TREE_CODE (*tp) == RETURN_EXPR) - TREE_OPERAND (*tp, 0) = dp->result; - /* Otherwise replace all occurrences of VAR with RESULT. */ else if (*tp == dp->var) *tp = dp->result; @@ -112,6 +108,7 @@ tree_nrv (void) tree result_type = TREE_TYPE (result); tree found = NULL; basic_block bb; + block_stmt_iterator bsi; struct nrv_data data; /* If this function does not return an aggregate type in memory, then @@ -119,49 +116,53 @@ tree_nrv (void) if (!aggregate_value_p (result, current_function_decl)) return; - /* Look through each block for suitable return expressions. RETURN_EXPRs - end basic blocks, so we only have to look at the last statement in - each block. That makes this very fast. */ + /* Look through each block for assignments to the RESULT_DECL. */ FOR_EACH_BB (bb) { - tree stmt = last_stmt (bb); - - if (stmt && TREE_CODE (stmt) == RETURN_EXPR) + for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) { - tree ret_expr = TREE_OPERAND (stmt, 0); - - /* This probably should not happen, but just to be safe do - not perform NRV optimizations if only some of the return - statement return a value. */ - if (!ret_expr - || TREE_CODE (ret_expr) != MODIFY_EXPR - || TREE_CODE (TREE_OPERAND (ret_expr, 0)) != RESULT_DECL) - return; - - /* Now verify that this return statement uses the same value - as any previously encountered return statement. */ - if (found != NULL) + tree stmt = bsi_stmt (bsi); + tree ret_expr; + + if (TREE_CODE (stmt) == RETURN_EXPR) { - /* If we found a return statement using a different variable - than previous return statements, then we can not perform - NRV optimizations. */ - if (found != TREE_OPERAND (ret_expr, 1)) + /* In a function with an aggregate return value, the + gimplifier has changed all non-empty RETURN_EXPRs to + return the RESULT_DECL. */ + ret_expr = TREE_OPERAND (stmt, 0); + if (ret_expr) + gcc_assert (ret_expr == result); + } + else if (TREE_CODE (stmt) == MODIFY_EXPR + && TREE_OPERAND (stmt, 0) == result) + { + ret_expr = TREE_OPERAND (stmt, 1); + + /* Now verify that this return statement uses the same value + as any previously encountered return statement. */ + if (found != NULL) + { + /* If we found a return statement using a different variable + than previous return statements, then we can not perform + NRV optimizations. */ + if (found != ret_expr) + return; + } + else + found = ret_expr; + + /* The returned value must be a local automatic variable of the + same type and alignment as the function's result. */ + if (TREE_CODE (found) != VAR_DECL + || TREE_THIS_VOLATILE (found) + || DECL_CONTEXT (found) != current_function_decl + || TREE_STATIC (found) + || TREE_ADDRESSABLE (found) + || DECL_ALIGN (found) > DECL_ALIGN (result) + || !lang_hooks.types_compatible_p (TREE_TYPE (found), + result_type)) return; } - else - found = TREE_OPERAND (ret_expr, 1); - - /* The returned value must be a local automatic variable of the - same type and alignment as the function's result. */ - if (TREE_CODE (found) != VAR_DECL - || TREE_THIS_VOLATILE (found) - || DECL_CONTEXT (found) != current_function_decl - || TREE_STATIC (found) - || TREE_ADDRESSABLE (found) - || DECL_ALIGN (found) > DECL_ALIGN (result) - || !lang_hooks.types_compatible_p (TREE_TYPE (found), - result_type)) - return; } } @@ -192,10 +193,20 @@ tree_nrv (void) data.result = result; FOR_EACH_BB (bb) { - block_stmt_iterator bsi; - - for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) - walk_tree (bsi_stmt_ptr (bsi), finalize_nrv_r, &data, 0); + for (bsi = bsi_start (bb); !bsi_end_p (bsi); ) + { + tree *tp = bsi_stmt_ptr (bsi); + /* If this is a copy from VAR to RESULT, remove it. */ + if (TREE_CODE (*tp) == MODIFY_EXPR + && TREE_OPERAND (*tp, 0) == result + && TREE_OPERAND (*tp, 1) == found) + bsi_remove (&bsi); + else + { + walk_tree (tp, finalize_nrv_r, &data, 0); + bsi_next (&bsi); + } + } } /* FOUND is no longer used. Ensure it gets removed. */