1/* C++-specific tree lowering bits; see also c-gimplify.c and tree-gimple.c. 2 3 Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. 4 Contributed by Jason Merrill <jason@redhat.com> 5 6This file is part of GCC. 7 8GCC is free software; you can redistribute it and/or modify it under 9the terms of the GNU General Public License as published by the Free 10Software Foundation; either version 2, or (at your option) any later 11version. 12 13GCC is distributed in the hope that it will be useful, but WITHOUT ANY 14WARRANTY; without even the implied warranty of MERCHANTABILITY or 15FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16for more details. 17 18You should have received a copy of the GNU General Public License 19along with GCC; see the file COPYING. If not, write to the Free 20Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 2102110-1301, USA. */ 22 23#include "config.h" 24#include "system.h" 25#include "coretypes.h" 26#include "tm.h" 27#include "tree.h" 28#include "cp-tree.h" 29#include "c-common.h" 30#include "toplev.h" 31#include "tree-gimple.h" 32#include "hashtab.h" 33#include "pointer-set.h" 34#include "flags.h" 35 36/* Local declarations. */ 37 38enum bc_t { bc_break = 0, bc_continue = 1 }; 39 40static struct cp_gimplify_ctx 41{ 42 /* Stack of labels which are targets for "break" or "continue", 43 linked through TREE_CHAIN. */ 44 tree current_label[2]; 45} *ctxp; 46 47static void 48push_context (void) 49{ 50 gcc_assert (!ctxp); 51 ctxp = ((struct cp_gimplify_ctx *) 52 xcalloc (1, sizeof (struct cp_gimplify_ctx))); 53} 54 55static void 56pop_context (void) 57{ 58 gcc_assert (ctxp 59 && !ctxp->current_label[0] 60 && !ctxp->current_label[1]); 61 free (ctxp); 62 ctxp = NULL; 63} 64 65/* Begin a scope which can be exited by a break or continue statement. BC 66 indicates which. 67 68 Just creates a label and pushes it into the current context. */ 69 70static tree 71begin_bc_block (enum bc_t bc) 72{ 73 tree label = create_artificial_label (); 74 TREE_CHAIN (label) = ctxp->current_label[bc]; 75 ctxp->current_label[bc] = label; 76 return label; 77} 78 79/* Finish a scope which can be exited by a break or continue statement. 80 LABEL was returned from the most recent call to begin_bc_block. BODY is 81 an expression for the contents of the scope. 82 83 If we saw a break (or continue) in the scope, append a LABEL_EXPR to 84 body. Otherwise, just forget the label. */ 85 86static tree 87finish_bc_block (enum bc_t bc, tree label, tree body) 88{ 89 gcc_assert (label == ctxp->current_label[bc]); 90 91 if (TREE_USED (label)) 92 { 93 tree t, sl = NULL; 94 95 t = build1 (LABEL_EXPR, void_type_node, label); 96 97 append_to_statement_list (body, &sl); 98 append_to_statement_list (t, &sl); 99 body = sl; 100 } 101 102 ctxp->current_label[bc] = TREE_CHAIN (label); 103 TREE_CHAIN (label) = NULL_TREE; 104 return body; 105} 106 107/* Build a GOTO_EXPR to represent a break or continue statement. BC 108 indicates which. */ 109 110static tree 111build_bc_goto (enum bc_t bc) 112{ 113 tree label = ctxp->current_label[bc]; 114 115 if (label == NULL_TREE) 116 { 117 if (bc == bc_break) 118 error ("break statement not within loop or switch"); 119 else 120 error ("continue statement not within loop or switch"); 121 122 return NULL_TREE; 123 } 124 125 /* Mark the label used for finish_bc_block. */ 126 TREE_USED (label) = 1; 127 return build1 (GOTO_EXPR, void_type_node, label); 128} 129 130/* Genericize a TRY_BLOCK. */ 131 132static void 133genericize_try_block (tree *stmt_p) 134{ 135 tree body = TRY_STMTS (*stmt_p); 136 tree cleanup = TRY_HANDLERS (*stmt_p); 137 138 gimplify_stmt (&body); 139 140 if (CLEANUP_P (*stmt_p)) 141 /* A cleanup is an expression, so it doesn't need to be genericized. */; 142 else 143 gimplify_stmt (&cleanup); 144 145 *stmt_p = build2 (TRY_CATCH_EXPR, void_type_node, body, cleanup); 146} 147 148/* Genericize a HANDLER by converting to a CATCH_EXPR. */ 149 150static void 151genericize_catch_block (tree *stmt_p) 152{ 153 tree type = HANDLER_TYPE (*stmt_p); 154 tree body = HANDLER_BODY (*stmt_p); 155 156 gimplify_stmt (&body); 157 158 /* FIXME should the caught type go in TREE_TYPE? */ 159 *stmt_p = build2 (CATCH_EXPR, void_type_node, type, body); 160} 161 162/* Genericize an EH_SPEC_BLOCK by converting it to a 163 TRY_CATCH_EXPR/EH_FILTER_EXPR pair. */ 164 165static void 166genericize_eh_spec_block (tree *stmt_p) 167{ 168 tree body = EH_SPEC_STMTS (*stmt_p); 169 tree allowed = EH_SPEC_RAISES (*stmt_p); 170 tree failure = build_call (call_unexpected_node, 171 tree_cons (NULL_TREE, build_exc_ptr (), 172 NULL_TREE)); 173 gimplify_stmt (&body); 174 175 *stmt_p = gimple_build_eh_filter (body, allowed, failure); 176} 177 178/* Genericize an IF_STMT by turning it into a COND_EXPR. */ 179 180static void 181gimplify_if_stmt (tree *stmt_p) 182{ 183 tree stmt, cond, then_, else_; 184 185 stmt = *stmt_p; 186 cond = IF_COND (stmt); 187 then_ = THEN_CLAUSE (stmt); 188 else_ = ELSE_CLAUSE (stmt); 189 190 if (!then_) 191 then_ = build_empty_stmt (); 192 if (!else_) 193 else_ = build_empty_stmt (); 194 195 if (integer_nonzerop (cond) && !TREE_SIDE_EFFECTS (else_)) 196 stmt = then_; 197 else if (integer_zerop (cond) && !TREE_SIDE_EFFECTS (then_)) 198 stmt = else_; 199 else 200 stmt = build3 (COND_EXPR, void_type_node, cond, then_, else_); 201 *stmt_p = stmt; 202} 203 204/* Build a generic representation of one of the C loop forms. COND is the 205 loop condition or NULL_TREE. BODY is the (possibly compound) statement 206 controlled by the loop. INCR is the increment expression of a for-loop, 207 or NULL_TREE. COND_IS_FIRST indicates whether the condition is 208 evaluated before the loop body as in while and for loops, or after the 209 loop body as in do-while loops. */ 210 211static tree 212gimplify_cp_loop (tree cond, tree body, tree incr, bool cond_is_first) 213{ 214 tree top, entry, exit, cont_block, break_block, stmt_list, t; 215 location_t stmt_locus; 216 217 stmt_locus = input_location; 218 stmt_list = NULL_TREE; 219 entry = NULL_TREE; 220 221 break_block = begin_bc_block (bc_break); 222 cont_block = begin_bc_block (bc_continue); 223 224 /* If condition is zero don't generate a loop construct. */ 225 if (cond && integer_zerop (cond)) 226 { 227 top = NULL_TREE; 228 exit = NULL_TREE; 229 if (cond_is_first) 230 { 231 t = build_bc_goto (bc_break); 232 append_to_statement_list (t, &stmt_list); 233 } 234 } 235 else 236 { 237 /* If we use a LOOP_EXPR here, we have to feed the whole thing 238 back through the main gimplifier to lower it. Given that we 239 have to gimplify the loop body NOW so that we can resolve 240 break/continue stmts, seems easier to just expand to gotos. */ 241 top = build1 (LABEL_EXPR, void_type_node, NULL_TREE); 242 243 /* If we have an exit condition, then we build an IF with gotos either 244 out of the loop, or to the top of it. If there's no exit condition, 245 then we just build a jump back to the top. */ 246 exit = build_and_jump (&LABEL_EXPR_LABEL (top)); 247 if (cond && !integer_nonzerop (cond)) 248 { 249 t = build_bc_goto (bc_break); 250 exit = build3 (COND_EXPR, void_type_node, cond, exit, t); 251 exit = fold (exit); 252 gimplify_stmt (&exit); 253 254 if (cond_is_first) 255 { 256 if (incr) 257 { 258 entry = build1 (LABEL_EXPR, void_type_node, NULL_TREE); 259 t = build_and_jump (&LABEL_EXPR_LABEL (entry)); 260 } 261 else 262 t = build_bc_goto (bc_continue); 263 append_to_statement_list (t, &stmt_list); 264 } 265 } 266 } 267 268 gimplify_stmt (&body); 269 gimplify_stmt (&incr); 270 271 body = finish_bc_block (bc_continue, cont_block, body); 272 273 append_to_statement_list (top, &stmt_list); 274 append_to_statement_list (body, &stmt_list); 275 append_to_statement_list (incr, &stmt_list); 276 append_to_statement_list (entry, &stmt_list); 277 append_to_statement_list (exit, &stmt_list); 278 279 annotate_all_with_locus (&stmt_list, stmt_locus); 280 281 return finish_bc_block (bc_break, break_block, stmt_list); 282} 283 284/* Gimplify a FOR_STMT node. Move the stuff in the for-init-stmt into the 285 prequeue and hand off to gimplify_cp_loop. */ 286 287static void 288gimplify_for_stmt (tree *stmt_p, tree *pre_p) 289{ 290 tree stmt = *stmt_p; 291 292 if (FOR_INIT_STMT (stmt)) 293 gimplify_and_add (FOR_INIT_STMT (stmt), pre_p); 294 295 *stmt_p = gimplify_cp_loop (FOR_COND (stmt), FOR_BODY (stmt), 296 FOR_EXPR (stmt), 1); 297} 298 299/* Gimplify a WHILE_STMT node. */ 300 301static void 302gimplify_while_stmt (tree *stmt_p) 303{ 304 tree stmt = *stmt_p; 305 *stmt_p = gimplify_cp_loop (WHILE_COND (stmt), WHILE_BODY (stmt), 306 NULL_TREE, 1); 307} 308 309/* Gimplify a DO_STMT node. */ 310 311static void 312gimplify_do_stmt (tree *stmt_p) 313{ 314 tree stmt = *stmt_p; 315 *stmt_p = gimplify_cp_loop (DO_COND (stmt), DO_BODY (stmt), 316 NULL_TREE, 0); 317} 318 319/* Genericize a SWITCH_STMT by turning it into a SWITCH_EXPR. */ 320 321static void 322gimplify_switch_stmt (tree *stmt_p) 323{ 324 tree stmt = *stmt_p; 325 tree break_block, body; 326 location_t stmt_locus = input_location; 327 328 break_block = begin_bc_block (bc_break); 329 330 body = SWITCH_STMT_BODY (stmt); 331 if (!body) 332 body = build_empty_stmt (); 333 334 *stmt_p = build3 (SWITCH_EXPR, SWITCH_STMT_TYPE (stmt), 335 SWITCH_STMT_COND (stmt), body, NULL_TREE); 336 SET_EXPR_LOCATION (*stmt_p, stmt_locus); 337 gimplify_stmt (stmt_p); 338 339 *stmt_p = finish_bc_block (bc_break, break_block, *stmt_p); 340} 341 342/* Gimplify an EXPR_STMT node. */ 343 344static void 345gimplify_expr_stmt (tree *stmt_p) 346{ 347 tree stmt = EXPR_STMT_EXPR (*stmt_p); 348 349 if (stmt == error_mark_node) 350 stmt = NULL; 351 352 /* Gimplification of a statement expression will nullify the 353 statement if all its side effects are moved to *PRE_P and *POST_P. 354 355 In this case we will not want to emit the gimplified statement. 356 However, we may still want to emit a warning, so we do that before 357 gimplification. */ 358 if (stmt && (extra_warnings || warn_unused_value)) 359 { 360 if (!TREE_SIDE_EFFECTS (stmt)) 361 { 362 if (!IS_EMPTY_STMT (stmt) 363 && !VOID_TYPE_P (TREE_TYPE (stmt)) 364 && !TREE_NO_WARNING (stmt)) 365 warning (0, "statement with no effect"); 366 } 367 else if (warn_unused_value) 368 warn_if_unused_value (stmt, input_location); 369 } 370 371 if (stmt == NULL_TREE) 372 stmt = alloc_stmt_list (); 373 374 *stmt_p = stmt; 375} 376 377/* Gimplify initialization from an AGGR_INIT_EXPR. */ 378 379static void 380cp_gimplify_init_expr (tree *expr_p, tree *pre_p, tree *post_p) 381{ 382 tree from = TREE_OPERAND (*expr_p, 1); 383 tree to = TREE_OPERAND (*expr_p, 0); 384 tree sub; 385 386 /* If we are initializing something from a TARGET_EXPR, strip the 387 TARGET_EXPR and initialize it directly. */ 388 /* What about code that pulls out the temp and uses it elsewhere? I 389 think that such code never uses the TARGET_EXPR as an initializer. If 390 I'm wrong, we'll abort because the temp won't have any RTL. In that 391 case, I guess we'll need to replace references somehow. */ 392 if (TREE_CODE (from) == TARGET_EXPR) 393 from = TARGET_EXPR_INITIAL (from); 394 if (TREE_CODE (from) == CLEANUP_POINT_EXPR) 395 from = TREE_OPERAND (from, 0); 396 397 /* Look through any COMPOUND_EXPRs. */ 398 sub = expr_last (from); 399 400 /* If we are initializing from an AGGR_INIT_EXPR, drop the INIT_EXPR and 401 replace the slot operand with our target. 402 403 Should we add a target parm to gimplify_expr instead? No, as in this 404 case we want to replace the INIT_EXPR. */ 405 if (TREE_CODE (sub) == AGGR_INIT_EXPR) 406 { 407 gimplify_expr (&to, pre_p, post_p, is_gimple_lvalue, fb_lvalue); 408 TREE_OPERAND (sub, 2) = to; 409 *expr_p = from; 410 411 /* The initialization is now a side-effect, so the container can 412 become void. */ 413 if (from != sub) 414 TREE_TYPE (from) = void_type_node; 415 } 416} 417 418/* Gimplify a MUST_NOT_THROW_EXPR. */ 419 420static void 421gimplify_must_not_throw_expr (tree *expr_p, tree *pre_p) 422{ 423 tree stmt = *expr_p; 424 tree temp = voidify_wrapper_expr (stmt, NULL); 425 tree body = TREE_OPERAND (stmt, 0); 426 427 gimplify_stmt (&body); 428 429 stmt = gimple_build_eh_filter (body, NULL_TREE, 430 build_call (terminate_node, NULL_TREE)); 431 432 if (temp) 433 { 434 append_to_statement_list (stmt, pre_p); 435 *expr_p = temp; 436 } 437 else 438 *expr_p = stmt; 439} 440 441/* Do C++-specific gimplification. Args are as for gimplify_expr. */ 442 443int 444cp_gimplify_expr (tree *expr_p, tree *pre_p, tree *post_p) 445{ 446 int saved_stmts_are_full_exprs_p = 0; 447 enum tree_code code = TREE_CODE (*expr_p); 448 enum gimplify_status ret; 449 450 if (STATEMENT_CODE_P (code)) 451 { 452 saved_stmts_are_full_exprs_p = stmts_are_full_exprs_p (); 453 current_stmt_tree ()->stmts_are_full_exprs_p 454 = STMT_IS_FULL_EXPR_P (*expr_p); 455 } 456 457 switch (code) 458 { 459 case PTRMEM_CST: 460 *expr_p = cplus_expand_constant (*expr_p); 461 ret = GS_OK; 462 break; 463 464 case AGGR_INIT_EXPR: 465 simplify_aggr_init_expr (expr_p); 466 ret = GS_OK; 467 break; 468 469 case THROW_EXPR: 470 /* FIXME communicate throw type to backend, probably by moving 471 THROW_EXPR into ../tree.def. */ 472 *expr_p = TREE_OPERAND (*expr_p, 0); 473 ret = GS_OK; 474 break; 475 476 case MUST_NOT_THROW_EXPR: 477 gimplify_must_not_throw_expr (expr_p, pre_p); 478 ret = GS_OK; 479 break; 480 481 /* We used to do this for MODIFY_EXPR as well, but that's unsafe; the 482 LHS of an assignment might also be involved in the RHS, as in bug 483 25979. */ 484 case INIT_EXPR: 485 cp_gimplify_init_expr (expr_p, pre_p, post_p); 486 ret = GS_OK; 487 break; 488 489 case EMPTY_CLASS_EXPR: 490 /* We create an empty CONSTRUCTOR with RECORD_TYPE. */ 491 *expr_p = build_constructor (TREE_TYPE (*expr_p), NULL); 492 ret = GS_OK; 493 break; 494 495 case BASELINK: 496 *expr_p = BASELINK_FUNCTIONS (*expr_p); 497 ret = GS_OK; 498 break; 499 500 case TRY_BLOCK: 501 genericize_try_block (expr_p); 502 ret = GS_OK; 503 break; 504 505 case HANDLER: 506 genericize_catch_block (expr_p); 507 ret = GS_OK; 508 break; 509 510 case EH_SPEC_BLOCK: 511 genericize_eh_spec_block (expr_p); 512 ret = GS_OK; 513 break; 514 515 case USING_STMT: 516 /* Just ignore for now. Eventually we will want to pass this on to 517 the debugger. */ 518 *expr_p = build_empty_stmt (); 519 ret = GS_ALL_DONE; 520 break; 521 522 case IF_STMT: 523 gimplify_if_stmt (expr_p); 524 ret = GS_OK; 525 break; 526 527 case FOR_STMT: 528 gimplify_for_stmt (expr_p, pre_p); 529 ret = GS_ALL_DONE; 530 break; 531 532 case WHILE_STMT: 533 gimplify_while_stmt (expr_p); 534 ret = GS_ALL_DONE; 535 break; 536 537 case DO_STMT: 538 gimplify_do_stmt (expr_p); 539 ret = GS_ALL_DONE; 540 break; 541 542 case SWITCH_STMT: 543 gimplify_switch_stmt (expr_p); 544 ret = GS_ALL_DONE; 545 break; 546 547 case CONTINUE_STMT: 548 *expr_p = build_bc_goto (bc_continue); 549 ret = GS_ALL_DONE; 550 break; 551 552 case BREAK_STMT: 553 *expr_p = build_bc_goto (bc_break); 554 ret = GS_ALL_DONE; 555 break; 556 557 case EXPR_STMT: 558 gimplify_expr_stmt (expr_p); 559 ret = GS_OK; 560 break; 561 562 case UNARY_PLUS_EXPR: 563 { 564 tree arg = TREE_OPERAND (*expr_p, 0); 565 tree type = TREE_TYPE (*expr_p); 566 *expr_p = (TREE_TYPE (arg) != type) ? fold_convert (type, arg) 567 : arg; 568 ret = GS_OK; 569 } 570 break; 571 572 default: 573 ret = c_gimplify_expr (expr_p, pre_p, post_p); 574 break; 575 } 576 577 /* Restore saved state. */ 578 if (STATEMENT_CODE_P (code)) 579 current_stmt_tree ()->stmts_are_full_exprs_p 580 = saved_stmts_are_full_exprs_p; 581 582 return ret; 583} 584 585static inline bool 586is_invisiref_parm (tree t) 587{ 588 return ((TREE_CODE (t) == PARM_DECL || TREE_CODE (t) == RESULT_DECL) 589 && DECL_BY_REFERENCE (t)); 590} 591 592/* Return true if the uid in both int tree maps are equal. */ 593 594int 595cxx_int_tree_map_eq (const void *va, const void *vb) 596{ 597 const struct cxx_int_tree_map *a = (const struct cxx_int_tree_map *) va; 598 const struct cxx_int_tree_map *b = (const struct cxx_int_tree_map *) vb; 599 return (a->uid == b->uid); 600} 601 602/* Hash a UID in a cxx_int_tree_map. */ 603 604unsigned int 605cxx_int_tree_map_hash (const void *item) 606{ 607 return ((const struct cxx_int_tree_map *)item)->uid; 608} 609 610/* Perform any pre-gimplification lowering of C++ front end trees to 611 GENERIC. */ 612 613static tree 614cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) 615{ 616 tree stmt = *stmt_p; 617 struct pointer_set_t *p_set = (struct pointer_set_t*) data; 618 619 if (is_invisiref_parm (stmt) 620 /* Don't dereference parms in a thunk, pass the references through. */ 621 && !(DECL_THUNK_P (current_function_decl) 622 && TREE_CODE (stmt) == PARM_DECL)) 623 { 624 *stmt_p = convert_from_reference (stmt); 625 *walk_subtrees = 0; 626 return NULL; 627 } 628 629 /* Map block scope extern declarations to visible declarations with the 630 same name and type in outer scopes if any. */ 631 if (cp_function_chain->extern_decl_map 632 && (TREE_CODE (stmt) == FUNCTION_DECL || TREE_CODE (stmt) == VAR_DECL) 633 && DECL_EXTERNAL (stmt)) 634 { 635 struct cxx_int_tree_map *h, in; 636 in.uid = DECL_UID (stmt); 637 h = (struct cxx_int_tree_map *) 638 htab_find_with_hash (cp_function_chain->extern_decl_map, 639 &in, in.uid); 640 if (h) 641 { 642 *stmt_p = h->to; 643 *walk_subtrees = 0; 644 return NULL; 645 } 646 } 647 648 /* Other than invisiref parms, don't walk the same tree twice. */ 649 if (pointer_set_contains (p_set, stmt)) 650 { 651 *walk_subtrees = 0; 652 return NULL_TREE; 653 } 654 655 if (TREE_CODE (stmt) == ADDR_EXPR 656 && is_invisiref_parm (TREE_OPERAND (stmt, 0))) 657 { 658 *stmt_p = convert (TREE_TYPE (stmt), TREE_OPERAND (stmt, 0)); 659 *walk_subtrees = 0; 660 } 661 else if (TREE_CODE (stmt) == RETURN_EXPR 662 && TREE_OPERAND (stmt, 0) 663 && is_invisiref_parm (TREE_OPERAND (stmt, 0))) 664 /* Don't dereference an invisiref RESULT_DECL inside a RETURN_EXPR. */ 665 *walk_subtrees = 0; 666 else if (IS_TYPE_OR_DECL_P (stmt)) 667 *walk_subtrees = 0; 668 669 /* Due to the way voidify_wrapper_expr is written, we don't get a chance 670 to lower this construct before scanning it, so we need to lower these 671 before doing anything else. */ 672 else if (TREE_CODE (stmt) == CLEANUP_STMT) 673 *stmt_p = build2 (CLEANUP_EH_ONLY (stmt) ? TRY_CATCH_EXPR 674 : TRY_FINALLY_EXPR, 675 void_type_node, 676 CLEANUP_BODY (stmt), 677 CLEANUP_EXPR (stmt)); 678 679 pointer_set_insert (p_set, *stmt_p); 680 681 return NULL; 682} 683 684void 685cp_genericize (tree fndecl) 686{ 687 tree t; 688 struct pointer_set_t *p_set; 689 690 /* Fix up the types of parms passed by invisible reference. */ 691 for (t = DECL_ARGUMENTS (fndecl); t; t = TREE_CHAIN (t)) 692 if (TREE_ADDRESSABLE (TREE_TYPE (t))) 693 { 694 /* If a function's arguments are copied to create a thunk, 695 then DECL_BY_REFERENCE will be set -- but the type of the 696 argument will be a pointer type, so we will never get 697 here. */ 698 gcc_assert (!DECL_BY_REFERENCE (t)); 699 gcc_assert (DECL_ARG_TYPE (t) != TREE_TYPE (t)); 700 TREE_TYPE (t) = DECL_ARG_TYPE (t); 701 DECL_BY_REFERENCE (t) = 1; 702 TREE_ADDRESSABLE (t) = 0; 703 relayout_decl (t); 704 } 705 706 /* Do the same for the return value. */ 707 if (TREE_ADDRESSABLE (TREE_TYPE (DECL_RESULT (fndecl)))) 708 { 709 t = DECL_RESULT (fndecl); 710 TREE_TYPE (t) = build_reference_type (TREE_TYPE (t)); 711 DECL_BY_REFERENCE (t) = 1; 712 TREE_ADDRESSABLE (t) = 0; 713 relayout_decl (t); 714 } 715 716 /* If we're a clone, the body is already GIMPLE. */ 717 if (DECL_CLONED_FUNCTION_P (fndecl)) 718 return; 719 720 /* We do want to see every occurrence of the parms, so we can't just use 721 walk_tree's hash functionality. */ 722 p_set = pointer_set_create (); 723 walk_tree (&DECL_SAVED_TREE (fndecl), cp_genericize_r, p_set, NULL); 724 pointer_set_destroy (p_set); 725 726 /* Do everything else. */ 727 push_context (); 728 c_genericize (fndecl); 729 pop_context (); 730} 731