1169689Skan/* Pass computing data for optimizing stdarg functions. 2169689Skan Copyright (C) 2004, 2005 Free Software Foundation, Inc. 3169689Skan Contributed by Jakub Jelinek <jakub@redhat.com> 4169689Skan 5169689SkanThis file is part of GCC. 6169689Skan 7169689SkanGCC is free software; you can redistribute it and/or modify 8169689Skanit under the terms of the GNU General Public License as published by 9169689Skanthe Free Software Foundation; either version 2, or (at your option) 10169689Skanany later version. 11169689Skan 12169689SkanGCC is distributed in the hope that it will be useful, 13169689Skanbut WITHOUT ANY WARRANTY; without even the implied warranty of 14169689SkanMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15169689SkanGNU General Public License for more details. 16169689Skan 17169689SkanYou should have received a copy of the GNU General Public License 18169689Skanalong with GCC; see the file COPYING. If not, write to 19169689Skanthe Free Software Foundation, 51 Franklin Street, Fifth Floor, 20169689SkanBoston, MA 02110-1301, USA. */ 21169689Skan 22169689Skan#include "config.h" 23169689Skan#include "system.h" 24169689Skan#include "coretypes.h" 25169689Skan#include "tm.h" 26169689Skan#include "tree.h" 27169689Skan#include "function.h" 28169689Skan#include "langhooks.h" 29169689Skan#include "diagnostic.h" 30169689Skan#include "target.h" 31169689Skan#include "tree-flow.h" 32169689Skan#include "tree-pass.h" 33169689Skan#include "tree-stdarg.h" 34169689Skan 35169689Skan/* A simple pass that attempts to optimize stdarg functions on architectures 36169689Skan that need to save register arguments to stack on entry to stdarg functions. 37169689Skan If the function doesn't use any va_start macros, no registers need to 38169689Skan be saved. If va_start macros are used, the va_list variables don't escape 39169689Skan the function, it is only necessary to save registers that will be used 40169689Skan in va_arg macros. E.g. if va_arg is only used with integral types 41169689Skan in the function, floating point registers don't need to be saved, etc. */ 42169689Skan 43169689Skan 44169689Skan/* Return true if basic block VA_ARG_BB is dominated by VA_START_BB and 45169689Skan is executed at most as many times as VA_START_BB. */ 46169689Skan 47169689Skanstatic bool 48169689Skanreachable_at_most_once (basic_block va_arg_bb, basic_block va_start_bb) 49169689Skan{ 50169689Skan edge *stack, e; 51169689Skan edge_iterator ei; 52169689Skan int sp; 53169689Skan sbitmap visited; 54169689Skan bool ret; 55169689Skan 56169689Skan if (va_arg_bb == va_start_bb) 57169689Skan return true; 58169689Skan 59169689Skan if (! dominated_by_p (CDI_DOMINATORS, va_arg_bb, va_start_bb)) 60169689Skan return false; 61169689Skan 62169689Skan stack = XNEWVEC (edge, n_basic_blocks + 1); 63169689Skan sp = 0; 64169689Skan 65169689Skan visited = sbitmap_alloc (last_basic_block); 66169689Skan sbitmap_zero (visited); 67169689Skan ret = true; 68169689Skan 69169689Skan FOR_EACH_EDGE (e, ei, va_arg_bb->preds) 70169689Skan stack[sp++] = e; 71169689Skan 72169689Skan while (sp) 73169689Skan { 74169689Skan basic_block src; 75169689Skan 76169689Skan --sp; 77169689Skan e = stack[sp]; 78169689Skan src = e->src; 79169689Skan 80169689Skan if (e->flags & EDGE_COMPLEX) 81169689Skan { 82169689Skan ret = false; 83169689Skan break; 84169689Skan } 85169689Skan 86169689Skan if (src == va_start_bb) 87169689Skan continue; 88169689Skan 89169689Skan /* va_arg_bb can be executed more times than va_start_bb. */ 90169689Skan if (src == va_arg_bb) 91169689Skan { 92169689Skan ret = false; 93169689Skan break; 94169689Skan } 95169689Skan 96169689Skan gcc_assert (src != ENTRY_BLOCK_PTR); 97169689Skan 98169689Skan if (! TEST_BIT (visited, src->index)) 99169689Skan { 100169689Skan SET_BIT (visited, src->index); 101169689Skan FOR_EACH_EDGE (e, ei, src->preds) 102169689Skan stack[sp++] = e; 103169689Skan } 104169689Skan } 105169689Skan 106169689Skan free (stack); 107169689Skan sbitmap_free (visited); 108169689Skan return ret; 109169689Skan} 110169689Skan 111169689Skan 112169689Skan/* For statement COUNTER = RHS, if RHS is COUNTER + constant, 113169689Skan return constant, otherwise return (unsigned HOST_WIDE_INT) -1. 114169689Skan GPR_P is true if this is GPR counter. */ 115169689Skan 116169689Skanstatic unsigned HOST_WIDE_INT 117169689Skanva_list_counter_bump (struct stdarg_info *si, tree counter, tree rhs, 118169689Skan bool gpr_p) 119169689Skan{ 120169689Skan tree stmt, lhs, orig_lhs; 121169689Skan unsigned HOST_WIDE_INT ret = 0, val, counter_val; 122169689Skan unsigned int max_size; 123169689Skan 124169689Skan if (si->offsets == NULL) 125169689Skan { 126169689Skan unsigned int i; 127169689Skan 128169689Skan si->offsets = XNEWVEC (int, num_ssa_names); 129169689Skan for (i = 0; i < num_ssa_names; ++i) 130169689Skan si->offsets[i] = -1; 131169689Skan } 132169689Skan 133169689Skan counter_val = gpr_p ? cfun->va_list_gpr_size : cfun->va_list_fpr_size; 134169689Skan max_size = gpr_p ? VA_LIST_MAX_GPR_SIZE : VA_LIST_MAX_FPR_SIZE; 135169689Skan orig_lhs = lhs = rhs; 136169689Skan while (lhs) 137169689Skan { 138169689Skan if (si->offsets[SSA_NAME_VERSION (lhs)] != -1) 139169689Skan { 140169689Skan if (counter_val >= max_size) 141169689Skan { 142169689Skan ret = max_size; 143169689Skan break; 144169689Skan } 145169689Skan 146169689Skan ret -= counter_val - si->offsets[SSA_NAME_VERSION (lhs)]; 147169689Skan break; 148169689Skan } 149169689Skan 150169689Skan stmt = SSA_NAME_DEF_STMT (lhs); 151169689Skan 152169689Skan if (TREE_CODE (stmt) != MODIFY_EXPR 153169689Skan || TREE_OPERAND (stmt, 0) != lhs) 154169689Skan return (unsigned HOST_WIDE_INT) -1; 155169689Skan 156169689Skan rhs = TREE_OPERAND (stmt, 1); 157169689Skan if (TREE_CODE (rhs) == WITH_SIZE_EXPR) 158169689Skan rhs = TREE_OPERAND (rhs, 0); 159169689Skan 160169689Skan if (TREE_CODE (rhs) == SSA_NAME) 161169689Skan { 162169689Skan lhs = rhs; 163169689Skan continue; 164169689Skan } 165169689Skan 166169689Skan if ((TREE_CODE (rhs) == NOP_EXPR 167169689Skan || TREE_CODE (rhs) == CONVERT_EXPR) 168169689Skan && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME) 169169689Skan { 170169689Skan lhs = TREE_OPERAND (rhs, 0); 171169689Skan continue; 172169689Skan } 173169689Skan 174169689Skan if (TREE_CODE (rhs) == PLUS_EXPR 175169689Skan && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME 176169689Skan && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST 177169689Skan && host_integerp (TREE_OPERAND (rhs, 1), 1)) 178169689Skan { 179169689Skan ret += tree_low_cst (TREE_OPERAND (rhs, 1), 1); 180169689Skan lhs = TREE_OPERAND (rhs, 0); 181169689Skan continue; 182169689Skan } 183169689Skan 184169689Skan if (TREE_CODE (counter) != TREE_CODE (rhs)) 185169689Skan return (unsigned HOST_WIDE_INT) -1; 186169689Skan 187169689Skan if (TREE_CODE (counter) == COMPONENT_REF) 188169689Skan { 189169689Skan if (get_base_address (counter) != get_base_address (rhs) 190169689Skan || TREE_CODE (TREE_OPERAND (rhs, 1)) != FIELD_DECL 191169689Skan || TREE_OPERAND (counter, 1) != TREE_OPERAND (rhs, 1)) 192169689Skan return (unsigned HOST_WIDE_INT) -1; 193169689Skan } 194169689Skan else if (counter != rhs) 195169689Skan return (unsigned HOST_WIDE_INT) -1; 196169689Skan 197169689Skan lhs = NULL; 198169689Skan } 199169689Skan 200169689Skan lhs = orig_lhs; 201169689Skan val = ret + counter_val; 202169689Skan while (lhs) 203169689Skan { 204169689Skan if (si->offsets[SSA_NAME_VERSION (lhs)] != -1) 205169689Skan break; 206169689Skan 207169689Skan if (val >= max_size) 208169689Skan si->offsets[SSA_NAME_VERSION (lhs)] = max_size; 209169689Skan else 210169689Skan si->offsets[SSA_NAME_VERSION (lhs)] = val; 211169689Skan 212169689Skan stmt = SSA_NAME_DEF_STMT (lhs); 213169689Skan 214169689Skan rhs = TREE_OPERAND (stmt, 1); 215169689Skan if (TREE_CODE (rhs) == WITH_SIZE_EXPR) 216169689Skan rhs = TREE_OPERAND (rhs, 0); 217169689Skan 218169689Skan if (TREE_CODE (rhs) == SSA_NAME) 219169689Skan { 220169689Skan lhs = rhs; 221169689Skan continue; 222169689Skan } 223169689Skan 224169689Skan if ((TREE_CODE (rhs) == NOP_EXPR 225169689Skan || TREE_CODE (rhs) == CONVERT_EXPR) 226169689Skan && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME) 227169689Skan { 228169689Skan lhs = TREE_OPERAND (rhs, 0); 229169689Skan continue; 230169689Skan } 231169689Skan 232169689Skan if (TREE_CODE (rhs) == PLUS_EXPR 233169689Skan && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME 234169689Skan && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST 235169689Skan && host_integerp (TREE_OPERAND (rhs, 1), 1)) 236169689Skan { 237169689Skan val -= tree_low_cst (TREE_OPERAND (rhs, 1), 1); 238169689Skan lhs = TREE_OPERAND (rhs, 0); 239169689Skan continue; 240169689Skan } 241169689Skan 242169689Skan lhs = NULL; 243169689Skan } 244169689Skan 245169689Skan return ret; 246169689Skan} 247169689Skan 248169689Skan 249169689Skan/* Called by walk_tree to look for references to va_list variables. */ 250169689Skan 251169689Skanstatic tree 252169689Skanfind_va_list_reference (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, 253169689Skan void *data) 254169689Skan{ 255169689Skan bitmap va_list_vars = (bitmap) data; 256169689Skan tree var = *tp; 257169689Skan 258169689Skan if (TREE_CODE (var) == SSA_NAME) 259169689Skan var = SSA_NAME_VAR (var); 260169689Skan 261169689Skan if (TREE_CODE (var) == VAR_DECL 262169689Skan && bitmap_bit_p (va_list_vars, DECL_UID (var))) 263169689Skan return var; 264169689Skan 265169689Skan return NULL_TREE; 266169689Skan} 267169689Skan 268169689Skan 269169689Skan/* Helper function of va_list_counter_struct_op. Compute 270169689Skan cfun->va_list_{g,f}pr_size. AP is a va_list GPR/FPR counter, 271169689Skan if WRITE_P is true, seen in AP = VAR, otherwise seen in VAR = AP 272169689Skan statement. GPR_P is true if AP is a GPR counter, false if it is 273169689Skan a FPR counter. */ 274169689Skan 275169689Skanstatic void 276169689Skanva_list_counter_op (struct stdarg_info *si, tree ap, tree var, bool gpr_p, 277169689Skan bool write_p) 278169689Skan{ 279169689Skan unsigned HOST_WIDE_INT increment; 280169689Skan 281169689Skan if (si->compute_sizes < 0) 282169689Skan { 283169689Skan si->compute_sizes = 0; 284169689Skan if (si->va_start_count == 1 285169689Skan && reachable_at_most_once (si->bb, si->va_start_bb)) 286169689Skan si->compute_sizes = 1; 287169689Skan 288169689Skan if (dump_file && (dump_flags & TDF_DETAILS)) 289169689Skan fprintf (dump_file, 290169689Skan "bb%d will %sbe executed at most once for each va_start " 291169689Skan "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ", 292169689Skan si->va_start_bb->index); 293169689Skan } 294169689Skan 295169689Skan if (write_p 296169689Skan && si->compute_sizes 297169689Skan && (increment = va_list_counter_bump (si, ap, var, gpr_p)) + 1 > 1) 298169689Skan { 299169689Skan if (gpr_p && cfun->va_list_gpr_size + increment < VA_LIST_MAX_GPR_SIZE) 300169689Skan { 301169689Skan cfun->va_list_gpr_size += increment; 302169689Skan return; 303169689Skan } 304169689Skan 305169689Skan if (!gpr_p && cfun->va_list_fpr_size + increment < VA_LIST_MAX_FPR_SIZE) 306169689Skan { 307169689Skan cfun->va_list_fpr_size += increment; 308169689Skan return; 309169689Skan } 310169689Skan } 311169689Skan 312169689Skan if (write_p || !si->compute_sizes) 313169689Skan { 314169689Skan if (gpr_p) 315169689Skan cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE; 316169689Skan else 317169689Skan cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE; 318169689Skan } 319169689Skan} 320169689Skan 321169689Skan 322169689Skan/* If AP is a va_list GPR/FPR counter, compute cfun->va_list_{g,f}pr_size. 323169689Skan If WRITE_P is true, AP has been seen in AP = VAR assignment, if WRITE_P 324169689Skan is false, AP has been seen in VAR = AP assignment. 325169689Skan Return true if the AP = VAR (resp. VAR = AP) statement is a recognized 326169689Skan va_arg operation that doesn't cause the va_list variable to escape 327169689Skan current function. */ 328169689Skan 329169689Skanstatic bool 330169689Skanva_list_counter_struct_op (struct stdarg_info *si, tree ap, tree var, 331169689Skan bool write_p) 332169689Skan{ 333169689Skan tree base; 334169689Skan 335169689Skan if (TREE_CODE (ap) != COMPONENT_REF 336169689Skan || TREE_CODE (TREE_OPERAND (ap, 1)) != FIELD_DECL) 337169689Skan return false; 338169689Skan 339169689Skan if (TREE_CODE (var) != SSA_NAME 340169689Skan || bitmap_bit_p (si->va_list_vars, DECL_UID (SSA_NAME_VAR (var)))) 341169689Skan return false; 342169689Skan 343169689Skan base = get_base_address (ap); 344169689Skan if (TREE_CODE (base) != VAR_DECL 345169689Skan || !bitmap_bit_p (si->va_list_vars, DECL_UID (base))) 346169689Skan return false; 347169689Skan 348169689Skan if (TREE_OPERAND (ap, 1) == va_list_gpr_counter_field) 349169689Skan va_list_counter_op (si, ap, var, true, write_p); 350169689Skan else if (TREE_OPERAND (ap, 1) == va_list_fpr_counter_field) 351169689Skan va_list_counter_op (si, ap, var, false, write_p); 352169689Skan 353169689Skan return true; 354169689Skan} 355169689Skan 356169689Skan 357169689Skan/* Check for TEM = AP. Return true if found and the caller shouldn't 358169689Skan search for va_list references in the statement. */ 359169689Skan 360169689Skanstatic bool 361169689Skanva_list_ptr_read (struct stdarg_info *si, tree ap, tree tem) 362169689Skan{ 363169689Skan if (TREE_CODE (ap) != VAR_DECL 364169689Skan || !bitmap_bit_p (si->va_list_vars, DECL_UID (ap))) 365169689Skan return false; 366169689Skan 367169689Skan if (TREE_CODE (tem) != SSA_NAME 368169689Skan || bitmap_bit_p (si->va_list_vars, 369169689Skan DECL_UID (SSA_NAME_VAR (tem))) 370169689Skan || is_global_var (SSA_NAME_VAR (tem))) 371169689Skan return false; 372169689Skan 373169689Skan if (si->compute_sizes < 0) 374169689Skan { 375169689Skan si->compute_sizes = 0; 376169689Skan if (si->va_start_count == 1 377169689Skan && reachable_at_most_once (si->bb, si->va_start_bb)) 378169689Skan si->compute_sizes = 1; 379169689Skan 380169689Skan if (dump_file && (dump_flags & TDF_DETAILS)) 381169689Skan fprintf (dump_file, 382169689Skan "bb%d will %sbe executed at most once for each va_start " 383169689Skan "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ", 384169689Skan si->va_start_bb->index); 385169689Skan } 386169689Skan 387169689Skan /* For void * or char * va_list types, there is just one counter. 388169689Skan If va_arg is used in a loop, we don't know how many registers need 389169689Skan saving. */ 390169689Skan if (! si->compute_sizes) 391169689Skan return false; 392169689Skan 393169689Skan if (va_list_counter_bump (si, ap, tem, true) == (unsigned HOST_WIDE_INT) -1) 394169689Skan return false; 395169689Skan 396169689Skan /* Note the temporary, as we need to track whether it doesn't escape 397169689Skan the current function. */ 398169689Skan bitmap_set_bit (si->va_list_escape_vars, 399169689Skan DECL_UID (SSA_NAME_VAR (tem))); 400169689Skan return true; 401169689Skan} 402169689Skan 403169689Skan 404169689Skan/* Check for: 405169689Skan tem1 = AP; 406169689Skan TEM2 = tem1 + CST; 407169689Skan AP = TEM2; 408169689Skan sequence and update cfun->va_list_gpr_size. Return true if found. */ 409169689Skan 410169689Skanstatic bool 411169689Skanva_list_ptr_write (struct stdarg_info *si, tree ap, tree tem2) 412169689Skan{ 413169689Skan unsigned HOST_WIDE_INT increment; 414169689Skan 415169689Skan if (TREE_CODE (ap) != VAR_DECL 416169689Skan || !bitmap_bit_p (si->va_list_vars, DECL_UID (ap))) 417169689Skan return false; 418169689Skan 419169689Skan if (TREE_CODE (tem2) != SSA_NAME 420169689Skan || bitmap_bit_p (si->va_list_vars, DECL_UID (SSA_NAME_VAR (tem2)))) 421169689Skan return false; 422169689Skan 423169689Skan if (si->compute_sizes <= 0) 424169689Skan return false; 425169689Skan 426169689Skan increment = va_list_counter_bump (si, ap, tem2, true); 427169689Skan if (increment + 1 <= 1) 428169689Skan return false; 429169689Skan 430169689Skan if (cfun->va_list_gpr_size + increment < VA_LIST_MAX_GPR_SIZE) 431169689Skan cfun->va_list_gpr_size += increment; 432169689Skan else 433169689Skan cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE; 434169689Skan 435169689Skan return true; 436169689Skan} 437169689Skan 438169689Skan 439169689Skan/* If RHS is X, (some type *) X or X + CST for X a temporary variable 440169689Skan containing value of some va_list variable plus optionally some constant, 441169689Skan either set si->va_list_escapes or add LHS to si->va_list_escape_vars, 442169689Skan depending whether LHS is a function local temporary. */ 443169689Skan 444169689Skanstatic void 445169689Skancheck_va_list_escapes (struct stdarg_info *si, tree lhs, tree rhs) 446169689Skan{ 447169689Skan if (! POINTER_TYPE_P (TREE_TYPE (rhs))) 448169689Skan return; 449169689Skan 450169689Skan if ((TREE_CODE (rhs) == PLUS_EXPR 451169689Skan && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST) 452169689Skan || TREE_CODE (rhs) == NOP_EXPR 453169689Skan || TREE_CODE (rhs) == CONVERT_EXPR) 454169689Skan rhs = TREE_OPERAND (rhs, 0); 455169689Skan 456169689Skan if (TREE_CODE (rhs) != SSA_NAME 457169689Skan || ! bitmap_bit_p (si->va_list_escape_vars, 458169689Skan DECL_UID (SSA_NAME_VAR (rhs)))) 459169689Skan return; 460169689Skan 461169689Skan if (TREE_CODE (lhs) != SSA_NAME || is_global_var (SSA_NAME_VAR (lhs))) 462169689Skan { 463169689Skan si->va_list_escapes = true; 464169689Skan return; 465169689Skan } 466169689Skan 467169689Skan if (si->compute_sizes < 0) 468169689Skan { 469169689Skan si->compute_sizes = 0; 470169689Skan if (si->va_start_count == 1 471169689Skan && reachable_at_most_once (si->bb, si->va_start_bb)) 472169689Skan si->compute_sizes = 1; 473169689Skan 474169689Skan if (dump_file && (dump_flags & TDF_DETAILS)) 475169689Skan fprintf (dump_file, 476169689Skan "bb%d will %sbe executed at most once for each va_start " 477169689Skan "in bb%d\n", si->bb->index, si->compute_sizes ? "" : "not ", 478169689Skan si->va_start_bb->index); 479169689Skan } 480169689Skan 481169689Skan /* For void * or char * va_list types, there is just one counter. 482169689Skan If va_arg is used in a loop, we don't know how many registers need 483169689Skan saving. */ 484169689Skan if (! si->compute_sizes) 485169689Skan { 486169689Skan si->va_list_escapes = true; 487169689Skan return; 488169689Skan } 489169689Skan 490169689Skan if (va_list_counter_bump (si, si->va_start_ap, lhs, true) 491169689Skan == (unsigned HOST_WIDE_INT) -1) 492169689Skan { 493169689Skan si->va_list_escapes = true; 494169689Skan return; 495169689Skan } 496169689Skan 497169689Skan bitmap_set_bit (si->va_list_escape_vars, 498169689Skan DECL_UID (SSA_NAME_VAR (lhs))); 499169689Skan} 500169689Skan 501169689Skan 502169689Skan/* Check all uses of temporaries from si->va_list_escape_vars bitmap. 503169689Skan Return true if va_list might be escaping. */ 504169689Skan 505169689Skanstatic bool 506169689Skancheck_all_va_list_escapes (struct stdarg_info *si) 507169689Skan{ 508169689Skan basic_block bb; 509169689Skan 510169689Skan FOR_EACH_BB (bb) 511169689Skan { 512169689Skan block_stmt_iterator i; 513169689Skan 514169689Skan for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i)) 515169689Skan { 516169689Skan tree stmt = bsi_stmt (i), use; 517169689Skan ssa_op_iter iter; 518169689Skan 519169689Skan FOR_EACH_SSA_TREE_OPERAND (use, stmt, iter, SSA_OP_ALL_USES) 520169689Skan { 521169689Skan if (! bitmap_bit_p (si->va_list_escape_vars, 522169689Skan DECL_UID (SSA_NAME_VAR (use)))) 523169689Skan continue; 524169689Skan 525169689Skan if (TREE_CODE (stmt) == MODIFY_EXPR) 526169689Skan { 527169689Skan tree lhs = TREE_OPERAND (stmt, 0); 528169689Skan tree rhs = TREE_OPERAND (stmt, 1); 529169689Skan 530169689Skan if (TREE_CODE (rhs) == WITH_SIZE_EXPR) 531169689Skan rhs = TREE_OPERAND (rhs, 0); 532169689Skan 533169689Skan /* x = *ap_temp; */ 534169689Skan if (TREE_CODE (rhs) == INDIRECT_REF 535169689Skan && TREE_OPERAND (rhs, 0) == use 536169689Skan && TYPE_SIZE_UNIT (TREE_TYPE (rhs)) 537169689Skan && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (rhs)), 1) 538169689Skan && si->offsets[SSA_NAME_VERSION (use)] != -1) 539169689Skan { 540169689Skan unsigned HOST_WIDE_INT gpr_size; 541169689Skan tree access_size = TYPE_SIZE_UNIT (TREE_TYPE (rhs)); 542169689Skan 543169689Skan gpr_size = si->offsets[SSA_NAME_VERSION (use)] 544169689Skan + tree_low_cst (access_size, 1); 545169689Skan if (gpr_size >= VA_LIST_MAX_GPR_SIZE) 546169689Skan cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE; 547169689Skan else if (gpr_size > cfun->va_list_gpr_size) 548169689Skan cfun->va_list_gpr_size = gpr_size; 549169689Skan continue; 550169689Skan } 551169689Skan 552169689Skan /* va_arg sequences may contain 553169689Skan other_ap_temp = ap_temp; 554169689Skan other_ap_temp = ap_temp + constant; 555169689Skan other_ap_temp = (some_type *) ap_temp; 556169689Skan ap = ap_temp; 557169689Skan statements. */ 558169689Skan if ((TREE_CODE (rhs) == PLUS_EXPR 559169689Skan && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST) 560169689Skan || TREE_CODE (rhs) == NOP_EXPR 561169689Skan || TREE_CODE (rhs) == CONVERT_EXPR) 562169689Skan rhs = TREE_OPERAND (rhs, 0); 563169689Skan 564169689Skan if (rhs == use) 565169689Skan { 566169689Skan if (TREE_CODE (lhs) == SSA_NAME 567169689Skan && bitmap_bit_p (si->va_list_escape_vars, 568169689Skan DECL_UID (SSA_NAME_VAR (lhs)))) 569169689Skan continue; 570169689Skan 571169689Skan if (TREE_CODE (lhs) == VAR_DECL 572169689Skan && bitmap_bit_p (si->va_list_vars, 573169689Skan DECL_UID (lhs))) 574169689Skan continue; 575169689Skan } 576169689Skan } 577169689Skan 578169689Skan if (dump_file && (dump_flags & TDF_DETAILS)) 579169689Skan { 580169689Skan fputs ("va_list escapes in ", dump_file); 581169689Skan print_generic_expr (dump_file, stmt, dump_flags); 582169689Skan fputc ('\n', dump_file); 583169689Skan } 584169689Skan return true; 585169689Skan } 586169689Skan } 587169689Skan } 588169689Skan 589169689Skan return false; 590169689Skan} 591169689Skan 592169689Skan 593169689Skan/* Return true if this optimization pass should be done. 594169689Skan It makes only sense for stdarg functions. */ 595169689Skan 596169689Skanstatic bool 597169689Skangate_optimize_stdarg (void) 598169689Skan{ 599169689Skan /* This optimization is only for stdarg functions. */ 600169689Skan return current_function_stdarg != 0; 601169689Skan} 602169689Skan 603169689Skan 604169689Skan/* Entry point to the stdarg optimization pass. */ 605169689Skan 606169689Skanstatic unsigned int 607169689Skanexecute_optimize_stdarg (void) 608169689Skan{ 609169689Skan basic_block bb; 610169689Skan bool va_list_escapes = false; 611169689Skan bool va_list_simple_ptr; 612169689Skan struct stdarg_info si; 613169689Skan const char *funcname = NULL; 614169689Skan 615169689Skan cfun->va_list_gpr_size = 0; 616169689Skan cfun->va_list_fpr_size = 0; 617169689Skan memset (&si, 0, sizeof (si)); 618169689Skan si.va_list_vars = BITMAP_ALLOC (NULL); 619169689Skan si.va_list_escape_vars = BITMAP_ALLOC (NULL); 620169689Skan 621169689Skan if (dump_file) 622169689Skan funcname = lang_hooks.decl_printable_name (current_function_decl, 2); 623169689Skan 624169689Skan va_list_simple_ptr = POINTER_TYPE_P (va_list_type_node) 625169689Skan && (TREE_TYPE (va_list_type_node) == void_type_node 626169689Skan || TREE_TYPE (va_list_type_node) == char_type_node); 627169689Skan gcc_assert (is_gimple_reg_type (va_list_type_node) == va_list_simple_ptr); 628169689Skan 629169689Skan FOR_EACH_BB (bb) 630169689Skan { 631169689Skan block_stmt_iterator i; 632169689Skan 633169689Skan for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i)) 634169689Skan { 635169689Skan tree stmt = bsi_stmt (i); 636169689Skan tree call = get_call_expr_in (stmt), callee; 637169689Skan tree ap; 638169689Skan 639169689Skan if (!call) 640169689Skan continue; 641169689Skan 642169689Skan callee = get_callee_fndecl (call); 643169689Skan if (!callee 644169689Skan || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL) 645169689Skan continue; 646169689Skan 647169689Skan switch (DECL_FUNCTION_CODE (callee)) 648169689Skan { 649169689Skan case BUILT_IN_VA_START: 650169689Skan break; 651169689Skan /* If old style builtins are used, don't optimize anything. */ 652169689Skan case BUILT_IN_SAVEREGS: 653169689Skan case BUILT_IN_STDARG_START: 654169689Skan case BUILT_IN_ARGS_INFO: 655169689Skan case BUILT_IN_NEXT_ARG: 656169689Skan va_list_escapes = true; 657169689Skan continue; 658169689Skan default: 659169689Skan continue; 660169689Skan } 661169689Skan 662169689Skan si.va_start_count++; 663169689Skan ap = TREE_VALUE (TREE_OPERAND (call, 1)); 664169689Skan 665169689Skan if (TREE_CODE (ap) != ADDR_EXPR) 666169689Skan { 667169689Skan va_list_escapes = true; 668169689Skan break; 669169689Skan } 670169689Skan ap = TREE_OPERAND (ap, 0); 671169689Skan if (TREE_CODE (ap) == ARRAY_REF) 672169689Skan { 673169689Skan if (! integer_zerop (TREE_OPERAND (ap, 1))) 674169689Skan { 675169689Skan va_list_escapes = true; 676169689Skan break; 677169689Skan } 678169689Skan ap = TREE_OPERAND (ap, 0); 679169689Skan } 680169689Skan if (TYPE_MAIN_VARIANT (TREE_TYPE (ap)) 681169689Skan != TYPE_MAIN_VARIANT (va_list_type_node) 682169689Skan || TREE_CODE (ap) != VAR_DECL) 683169689Skan { 684169689Skan va_list_escapes = true; 685169689Skan break; 686169689Skan } 687169689Skan 688169689Skan if (is_global_var (ap)) 689169689Skan { 690169689Skan va_list_escapes = true; 691169689Skan break; 692169689Skan } 693169689Skan 694169689Skan bitmap_set_bit (si.va_list_vars, DECL_UID (ap)); 695169689Skan 696169689Skan /* VA_START_BB and VA_START_AP will be only used if there is just 697169689Skan one va_start in the function. */ 698169689Skan si.va_start_bb = bb; 699169689Skan si.va_start_ap = ap; 700169689Skan } 701169689Skan 702169689Skan if (va_list_escapes) 703169689Skan break; 704169689Skan } 705169689Skan 706169689Skan /* If there were no va_start uses in the function, there is no need to 707169689Skan save anything. */ 708169689Skan if (si.va_start_count == 0) 709169689Skan goto finish; 710169689Skan 711169689Skan /* If some va_list arguments weren't local, we can't optimize. */ 712169689Skan if (va_list_escapes) 713169689Skan goto finish; 714169689Skan 715169689Skan /* For void * or char * va_list, something useful can be done only 716169689Skan if there is just one va_start. */ 717169689Skan if (va_list_simple_ptr && si.va_start_count > 1) 718169689Skan { 719169689Skan va_list_escapes = true; 720169689Skan goto finish; 721169689Skan } 722169689Skan 723169689Skan /* For struct * va_list, if the backend didn't tell us what the counter fields 724169689Skan are, there is nothing more we can do. */ 725169689Skan if (!va_list_simple_ptr 726169689Skan && va_list_gpr_counter_field == NULL_TREE 727169689Skan && va_list_fpr_counter_field == NULL_TREE) 728169689Skan { 729169689Skan va_list_escapes = true; 730169689Skan goto finish; 731169689Skan } 732169689Skan 733169689Skan /* For void * or char * va_list there is just one counter 734169689Skan (va_list itself). Use VA_LIST_GPR_SIZE for it. */ 735169689Skan if (va_list_simple_ptr) 736169689Skan cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE; 737169689Skan 738169689Skan calculate_dominance_info (CDI_DOMINATORS); 739169689Skan 740169689Skan FOR_EACH_BB (bb) 741169689Skan { 742169689Skan block_stmt_iterator i; 743169689Skan 744169689Skan si.compute_sizes = -1; 745169689Skan si.bb = bb; 746169689Skan 747169689Skan /* For va_list_simple_ptr, we have to check PHI nodes too. We treat 748169689Skan them as assignments for the purpose of escape analysis. This is 749169689Skan not needed for non-simple va_list because virtual phis don't perform 750169689Skan any real data movement. */ 751169689Skan if (va_list_simple_ptr) 752169689Skan { 753169689Skan tree phi, lhs, rhs; 754169689Skan use_operand_p uop; 755169689Skan ssa_op_iter soi; 756169689Skan 757169689Skan for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi)) 758169689Skan { 759169689Skan lhs = PHI_RESULT (phi); 760169689Skan 761169689Skan if (!is_gimple_reg (lhs)) 762169689Skan continue; 763169689Skan 764169689Skan FOR_EACH_PHI_ARG (uop, phi, soi, SSA_OP_USE) 765169689Skan { 766169689Skan rhs = USE_FROM_PTR (uop); 767169689Skan if (va_list_ptr_read (&si, rhs, lhs)) 768169689Skan continue; 769169689Skan else if (va_list_ptr_write (&si, lhs, rhs)) 770169689Skan continue; 771169689Skan else 772169689Skan check_va_list_escapes (&si, lhs, rhs); 773169689Skan 774169689Skan if (si.va_list_escapes 775169689Skan || walk_tree (&phi, find_va_list_reference, 776169689Skan si.va_list_vars, NULL)) 777169689Skan { 778169689Skan if (dump_file && (dump_flags & TDF_DETAILS)) 779169689Skan { 780169689Skan fputs ("va_list escapes in ", dump_file); 781169689Skan print_generic_expr (dump_file, phi, dump_flags); 782169689Skan fputc ('\n', dump_file); 783169689Skan } 784169689Skan va_list_escapes = true; 785169689Skan } 786169689Skan } 787169689Skan } 788169689Skan } 789169689Skan 790169689Skan for (i = bsi_start (bb); 791169689Skan !bsi_end_p (i) && !va_list_escapes; 792169689Skan bsi_next (&i)) 793169689Skan { 794169689Skan tree stmt = bsi_stmt (i); 795169689Skan tree call; 796169689Skan 797169689Skan /* Don't look at __builtin_va_{start,end}, they are ok. */ 798169689Skan call = get_call_expr_in (stmt); 799169689Skan if (call) 800169689Skan { 801169689Skan tree callee = get_callee_fndecl (call); 802169689Skan 803169689Skan if (callee 804169689Skan && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL 805169689Skan && (DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_START 806169689Skan || DECL_FUNCTION_CODE (callee) == BUILT_IN_VA_END)) 807169689Skan continue; 808169689Skan } 809169689Skan 810169689Skan if (TREE_CODE (stmt) == MODIFY_EXPR) 811169689Skan { 812169689Skan tree lhs = TREE_OPERAND (stmt, 0); 813169689Skan tree rhs = TREE_OPERAND (stmt, 1); 814169689Skan 815169689Skan if (TREE_CODE (rhs) == WITH_SIZE_EXPR) 816169689Skan rhs = TREE_OPERAND (rhs, 0); 817169689Skan 818169689Skan if (va_list_simple_ptr) 819169689Skan { 820169689Skan /* Check for tem = ap. */ 821169689Skan if (va_list_ptr_read (&si, rhs, lhs)) 822169689Skan continue; 823169689Skan 824169689Skan /* Check for the last insn in: 825169689Skan tem1 = ap; 826169689Skan tem2 = tem1 + CST; 827169689Skan ap = tem2; 828169689Skan sequence. */ 829169689Skan else if (va_list_ptr_write (&si, lhs, rhs)) 830169689Skan continue; 831169689Skan 832169689Skan else 833169689Skan check_va_list_escapes (&si, lhs, rhs); 834169689Skan } 835169689Skan else 836169689Skan { 837169689Skan /* Check for ap[0].field = temp. */ 838169689Skan if (va_list_counter_struct_op (&si, lhs, rhs, true)) 839169689Skan continue; 840169689Skan 841169689Skan /* Check for temp = ap[0].field. */ 842169689Skan else if (va_list_counter_struct_op (&si, rhs, lhs, false)) 843169689Skan continue; 844169689Skan 845169689Skan /* Do any architecture specific checking. */ 846169689Skan else if (targetm.stdarg_optimize_hook 847169689Skan && targetm.stdarg_optimize_hook (&si, lhs, rhs)) 848169689Skan continue; 849169689Skan } 850169689Skan } 851169689Skan 852169689Skan /* All other uses of va_list are either va_copy (that is not handled 853169689Skan in this optimization), taking address of va_list variable or 854169689Skan passing va_list to other functions (in that case va_list might 855169689Skan escape the function and therefore va_start needs to set it up 856169689Skan fully), or some unexpected use of va_list. None of these should 857169689Skan happen in a gimplified VA_ARG_EXPR. */ 858169689Skan if (si.va_list_escapes 859169689Skan || walk_tree (&stmt, find_va_list_reference, 860169689Skan si.va_list_vars, NULL)) 861169689Skan { 862169689Skan if (dump_file && (dump_flags & TDF_DETAILS)) 863169689Skan { 864169689Skan fputs ("va_list escapes in ", dump_file); 865169689Skan print_generic_expr (dump_file, stmt, dump_flags); 866169689Skan fputc ('\n', dump_file); 867169689Skan } 868169689Skan va_list_escapes = true; 869169689Skan } 870169689Skan } 871169689Skan 872169689Skan if (va_list_escapes) 873169689Skan break; 874169689Skan } 875169689Skan 876169689Skan if (! va_list_escapes 877169689Skan && va_list_simple_ptr 878169689Skan && ! bitmap_empty_p (si.va_list_escape_vars) 879169689Skan && check_all_va_list_escapes (&si)) 880169689Skan va_list_escapes = true; 881169689Skan 882169689Skanfinish: 883169689Skan if (va_list_escapes) 884169689Skan { 885169689Skan cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE; 886169689Skan cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE; 887169689Skan } 888169689Skan BITMAP_FREE (si.va_list_vars); 889169689Skan BITMAP_FREE (si.va_list_escape_vars); 890169689Skan free (si.offsets); 891169689Skan if (dump_file) 892169689Skan { 893169689Skan fprintf (dump_file, "%s: va_list escapes %d, needs to save ", 894169689Skan funcname, (int) va_list_escapes); 895169689Skan if (cfun->va_list_gpr_size >= VA_LIST_MAX_GPR_SIZE) 896169689Skan fputs ("all", dump_file); 897169689Skan else 898169689Skan fprintf (dump_file, "%d", cfun->va_list_gpr_size); 899169689Skan fputs (" GPR units and ", dump_file); 900169689Skan if (cfun->va_list_fpr_size >= VA_LIST_MAX_FPR_SIZE) 901169689Skan fputs ("all", dump_file); 902169689Skan else 903169689Skan fprintf (dump_file, "%d", cfun->va_list_fpr_size); 904169689Skan fputs (" FPR units.\n", dump_file); 905169689Skan } 906169689Skan return 0; 907169689Skan} 908169689Skan 909169689Skan 910169689Skanstruct tree_opt_pass pass_stdarg = 911169689Skan{ 912169689Skan "stdarg", /* name */ 913169689Skan gate_optimize_stdarg, /* gate */ 914169689Skan execute_optimize_stdarg, /* execute */ 915169689Skan NULL, /* sub */ 916169689Skan NULL, /* next */ 917169689Skan 0, /* static_pass_number */ 918169689Skan 0, /* tv_id */ 919169689Skan PROP_cfg | PROP_ssa | PROP_alias, /* properties_required */ 920169689Skan 0, /* properties_provided */ 921169689Skan 0, /* properties_destroyed */ 922169689Skan 0, /* todo_flags_start */ 923169689Skan TODO_dump_func, /* todo_flags_finish */ 924169689Skan 0 /* letter */ 925169689Skan}; 926