The following patch makes us not allocate decls but SSA names for temporaries required during gimplification. This is basically the same thing as we do when calling the gimplifier on GENERIC expressions from optimization passes (when we are already in SSA).
There are two benefits of doing this. 1) SSA names are smaller (72 bytes) than VAR_DECLs (144 bytes) and we rewrite them into anonymous SSA names later anyway, leaving up the VAR_DECLs for GC reclaim (but not their UID) 2) We keep expressions "connected" by having the use->def link via SSA_NAME_DEF_STMT for example allowing match-and-simplify of larger expressions on early GIMPLE Complications arise from the fact that there is no CFG built and thus we have to make sure to not use SSA names where we'd need PHIs. Or when CFG build may end up separating SSA def and use in a way current into-SSA doesn't fix up (adding of abnormal edges, save-expr placement, gimplification of type sizes, etc.). As-is the patch has the downside of effectively disabling the lookup_tmp_var () CSE for register typed temporaries and not preserving the "fancy" names we derive from val in create_tmp_from_val (that can be recovered easily though if deemed worthwhile). On dwarf2out.c (with -O0) the .gimple dumps show a 10% reduction (~12000) of allocated DECLs (when looking at the highest D.12345). The first two hunks of difference in the dump looks like (to get you an idea): --- t.ii.004t.gimple 2016-04-21 14:21:00.382647093 +0200 +++ t.ii.004t.gimple.patched 2016-04-21 14:16:00.395261019 +0200 @@ -2,9 +2,7 @@ int VALGRIND_PRINTF(const char*, ...) (const char * format) { long long unsigned int retval.0; - long long unsigned int format.1; - long long unsigned int vargs.2; - int D.102545; + int D.102543; long unsigned int _qzz_res; struct vargs[1]; @@ -16,10 +14,10 @@ volatile long long unsigned int _zzq_result; _zzq_args[0] = 5123; - format.1 = (long long unsigned int) format; - _zzq_args[1] = format.1; - vargs.2 = (long long unsigned int) &vargs; - _zzq_args[2] = vargs.2; + _1 = (long long unsigned int) format; + _zzq_args[1] = _1; + _2 = (long long unsigned int) &vargs; + _zzq_args[2] = _2; _zzq_args[3] = 0; _zzq_args[4] = 0; _zzq_args[5] = 0; you can see how we derived the fancy name vargs.2 from &vargs for example. As said, if people prefer that's easy to preserve and would then look like + format_1 = (long long unsigned int) format; + _zzq_args[1] = _1; + vargs_2 = (long long unsigned int) &vargs; + _zzq_args[2] = _2; not sure if that isn't more confusing (given that 'format' may be later written into SSA form and thus format_3 may appear). Bootstrapped on x86_64-unknown-linux-gnu, testing in progress. ISTR some complications in pre-SSA OMP stuff, eventually some adjustments are still needed there but my last full testing pre-dates a lot of development there. Any comments or objections? I'd like to go forward with this once GCC 6.1 is released. Thanks, Richard. 2016-04-21 Richard Biener <rguent...@suse.de> * gimplify.h (get_initialized_tmp_var): Add allow_ssa parameter default true. * gimplify.c (internal_get_tmp_var): Add allow_ssa parameter and override into_ssa with it. (get_formal_tmp_var): Adjust. (get_initialized_tmp_var): Add allow_ssa parameter. (gimplify_call_expr): If the call may return twice do not gimplify parameters into SSA. (prepare_gimple_addressable): Do not allow an SSA name as temporary. (gimplify_modify_expr): Adjust assert. (gimplify_save_expr): Do not allow an SSA name as save-expr result. (gimplify_one_sizepos): Set into_ssa to false around gimplifying a type/decl size. (gimplify_body): Init GIMPLE SSA data structures and gimplify into-SSA. * passes.def (pass_init_datastructures): Remove. * tree-into-ssa.c (mark_def_sites): Ignore existing SSA names. (rewrite_stmt): Likewise. * tree-inline.c (initialize_cfun): Properly transfer SSA state. (replace_locals_op): Replace SSA names. (copy_gimple_seq_and_replace_locals): Init src_cfun. * gimple-low.c (lower_builtin_setjmp): Deal with SSA. * cgraph.c (release_function_body): Free CFG annotations only when we have a CFG. Simplify. * gimple-fold.c (gimplify_and_update_call_from_tree): Use force_gimple_operand instead of get_initialized_tmp_var. * tree-pass.h (make_pass_init_datastructures): Remove. * tree-ssa.c (execute_init_datastructures): Remove. (pass_data_init_datastructures): Likewise. (class pass_init_datastructures): Likewise. (make_pass_init_datastructures): Likewise. Index: gcc/gimplify.c =================================================================== *** gcc/gimplify.c.orig 2016-04-21 14:16:03.683298131 +0200 --- gcc/gimplify.c 2016-04-21 14:33:14.018961050 +0200 *************** lookup_tmp_var (tree val, bool is_formal *** 547,553 **** static tree internal_get_tmp_var (tree val, gimple_seq *pre_p, gimple_seq *post_p, ! bool is_formal) { tree t, mod; --- 547,553 ---- static tree internal_get_tmp_var (tree val, gimple_seq *pre_p, gimple_seq *post_p, ! bool is_formal, bool allow_ssa) { tree t, mod; *************** internal_get_tmp_var (tree val, gimple_s *** 556,562 **** gimplify_expr (&val, pre_p, post_p, is_gimple_reg_rhs_or_call, fb_rvalue); ! if (gimplify_ctxp->into_ssa && is_gimple_reg_type (TREE_TYPE (val))) t = make_ssa_name (TYPE_MAIN_VARIANT (TREE_TYPE (val))); else --- 556,563 ---- gimplify_expr (&val, pre_p, post_p, is_gimple_reg_rhs_or_call, fb_rvalue); ! if (allow_ssa ! && gimplify_ctxp->into_ssa && is_gimple_reg_type (TREE_TYPE (val))) t = make_ssa_name (TYPE_MAIN_VARIANT (TREE_TYPE (val))); else *************** internal_get_tmp_var (tree val, gimple_s *** 588,603 **** tree get_formal_tmp_var (tree val, gimple_seq *pre_p) { ! return internal_get_tmp_var (val, pre_p, NULL, true); } /* Return a temporary variable initialized with VAL. PRE_P and POST_P are as in gimplify_expr. */ tree ! get_initialized_tmp_var (tree val, gimple_seq *pre_p, gimple_seq *post_p) { ! return internal_get_tmp_var (val, pre_p, post_p, false); } /* Declare all the variables in VARS in SCOPE. If DEBUG_INFO is true, --- 589,605 ---- tree get_formal_tmp_var (tree val, gimple_seq *pre_p) { ! return internal_get_tmp_var (val, pre_p, NULL, true, true); } /* Return a temporary variable initialized with VAL. PRE_P and POST_P are as in gimplify_expr. */ tree ! get_initialized_tmp_var (tree val, gimple_seq *pre_p, gimple_seq *post_p, ! bool allow_ssa) { ! return internal_get_tmp_var (val, pre_p, post_p, false, allow_ssa); } /* Declare all the variables in VARS in SCOPE. If DEBUG_INFO is true, *************** gimplify_call_expr (tree *expr_p, gimple *** 2522,2527 **** --- 2524,2539 ---- } } + /* If the call returns twice then after building the CFG the call + argument computations will no longer dominate the call because + we add an abnormal incoming edge to the call. So do not use SSA + vars there. + ??? It's only necessary to avoid SSA names for the actual + arguments, not all of the other temporaries required to compute them. */ + bool saved_into_ssa = gimplify_ctxp->into_ssa; + if (call_expr_flags (*expr_p) & ECF_RETURNS_TWICE) + gimplify_ctxp->into_ssa = false; + /* Gimplify the function arguments. */ if (nargs > 0) { *************** gimplify_call_expr (tree *expr_p, gimple *** 2559,2564 **** --- 2571,2578 ---- } } + gimplify_ctxp->into_ssa = saved_into_ssa; + /* Verify the function result. */ if (want_value && fndecl && VOID_TYPE_P (TREE_TYPE (TREE_TYPE (fnptrtype)))) *************** prepare_gimple_addressable (tree *expr_p *** 3316,3322 **** expr_p = &TREE_OPERAND (*expr_p, 0); if (is_gimple_reg (*expr_p)) { ! tree var = get_initialized_tmp_var (*expr_p, seq_p, NULL); DECL_GIMPLE_REG_P (var) = 0; *expr_p = var; } --- 3330,3337 ---- expr_p = &TREE_OPERAND (*expr_p, 0); if (is_gimple_reg (*expr_p)) { ! /* Do not allow an SSA name as the temporary. */ ! tree var = get_initialized_tmp_var (*expr_p, seq_p, NULL, false); DECL_GIMPLE_REG_P (var) = 0; *expr_p = var; } *************** gimplify_modify_expr (tree *expr_p, gimp *** 4861,4867 **** if (gimplify_ctxp->into_ssa && is_gimple_reg (*to_p)) { /* We should have got an SSA name from the start. */ ! gcc_assert (TREE_CODE (*to_p) == SSA_NAME); } gimplify_seq_add_stmt (pre_p, assign); --- 4876,4883 ---- if (gimplify_ctxp->into_ssa && is_gimple_reg (*to_p)) { /* We should have got an SSA name from the start. */ ! gcc_assert (TREE_CODE (*to_p) == SSA_NAME ! || ! gimple_in_ssa_p (cfun)); } gimplify_seq_add_stmt (pre_p, assign); *************** gimplify_save_expr (tree *expr_p, gimple *** 4994,5000 **** val = NULL; } else ! val = get_initialized_tmp_var (val, pre_p, post_p); TREE_OPERAND (*expr_p, 0) = val; SAVE_EXPR_RESOLVED_P (*expr_p) = 1; --- 5010,5018 ---- val = NULL; } else ! /* The temporary may not be an SSA name as later abnormal and EH ! control flow may invalidate use/def domination. */ ! val = get_initialized_tmp_var (val, pre_p, post_p, false); TREE_OPERAND (*expr_p, 0) = val; SAVE_EXPR_RESOLVED_P (*expr_p) = 1; *************** gimplify_one_sizepos (tree *expr_p, gimp *** 11383,11389 **** --- 11401,11414 ---- *expr_p = unshare_expr (expr); + /* SSA names in decl/type fields are a bad idea - they'll get reclaimed + if the def vanishes. + ??? It's only required that the gimplification result is not a SSA + name, all the other temporaries required to compute it still can be. */ + bool saved_into_ssa = gimplify_ctxp->into_ssa; + gimplify_ctxp->into_ssa = false; gimplify_expr (expr_p, stmt_p, NULL, is_gimple_val, fb_rvalue); + gimplify_ctxp->into_ssa = saved_into_ssa; } /* Gimplify the body of statements of FNDECL and return a GIMPLE_BIND node *************** gimplify_body (tree fndecl, bool do_parm *** 11401,11412 **** timevar_push (TV_TREE_GIMPLIFY); /* Initialize for optimize_insn_for_s{ize,peed}_p possibly called during gimplification. */ default_rtl_profile (); gcc_assert (gimplify_ctxp == NULL); ! push_gimplify_context (); if (flag_openacc || flag_openmp) { --- 11426,11439 ---- timevar_push (TV_TREE_GIMPLIFY); + init_tree_ssa (cfun); + /* Initialize for optimize_insn_for_s{ize,peed}_p possibly called during gimplification. */ default_rtl_profile (); gcc_assert (gimplify_ctxp == NULL); ! push_gimplify_context (true); if (flag_openacc || flag_openmp) { Index: gcc/passes.def =================================================================== *** gcc/passes.def.orig 2016-04-21 14:16:03.683298131 +0200 --- gcc/passes.def 2016-04-21 14:33:14.030961187 +0200 *************** along with GCC; see the file COPYING3. *** 54,60 **** NEXT_PASS (pass_build_ssa_passes); PUSH_INSERT_PASSES_WITHIN (pass_build_ssa_passes) NEXT_PASS (pass_fixup_cfg); - NEXT_PASS (pass_init_datastructures); NEXT_PASS (pass_build_ssa); NEXT_PASS (pass_warn_nonnull_compare); NEXT_PASS (pass_ubsan); --- 54,59 ---- Index: gcc/tree-into-ssa.c =================================================================== *** gcc/tree-into-ssa.c.orig 2016-04-21 14:16:03.683298131 +0200 --- gcc/tree-into-ssa.c 2016-04-21 14:33:14.030961187 +0200 *************** mark_def_sites (basic_block bb, gimple * *** 666,671 **** --- 666,673 ---- FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_ALL_USES) { tree sym = USE_FROM_PTR (use_p); + if (TREE_CODE (sym) == SSA_NAME) + continue; gcc_checking_assert (DECL_P (sym)); if (!bitmap_bit_p (kills, DECL_UID (sym))) set_livein_block (sym, bb); *************** mark_def_sites (basic_block bb, gimple * *** 676,681 **** --- 678,685 ---- each def to the set of killed symbols. */ FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS) { + if (TREE_CODE (def) == SSA_NAME) + continue; gcc_checking_assert (DECL_P (def)); set_def_block (def, bb, false); bitmap_set_bit (kills, DECL_UID (def)); *************** rewrite_stmt (gimple_stmt_iterator *si) *** 1310,1315 **** --- 1314,1321 ---- FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_ALL_USES) { tree var = USE_FROM_PTR (use_p); + if (TREE_CODE (var) == SSA_NAME) + continue; gcc_checking_assert (DECL_P (var)); SET_USE (use_p, get_reaching_def (var)); } *************** rewrite_stmt (gimple_stmt_iterator *si) *** 1323,1328 **** --- 1329,1336 ---- tree name; tree tracked_var; + if (TREE_CODE (var) == SSA_NAME) + continue; gcc_checking_assert (DECL_P (var)); if (gimple_clobber_p (stmt) Index: gcc/tree-inline.c =================================================================== *** gcc/tree-inline.c.orig 2016-04-21 14:16:03.683298131 +0200 --- gcc/tree-inline.c 2016-04-21 14:33:14.030961187 +0200 *************** initialize_cfun (tree new_fndecl, tree c *** 2456,2463 **** if (src_cfun->gimple_df) { init_tree_ssa (cfun); ! cfun->gimple_df->in_ssa_p = true; ! init_ssa_operands (cfun); } } --- 2456,2464 ---- if (src_cfun->gimple_df) { init_tree_ssa (cfun); ! cfun->gimple_df->in_ssa_p = src_cfun->gimple_df->in_ssa_p; ! if (cfun->gimple_df->in_ssa_p) ! init_ssa_operands (cfun); } } *************** replace_locals_op (tree *tp, int *walk_s *** 5119,5128 **** tree *n; tree expr = *tp; /* Only a local declaration (variable or label). */ ! if ((TREE_CODE (expr) == VAR_DECL ! && !TREE_STATIC (expr)) ! || TREE_CODE (expr) == LABEL_DECL) { /* Lookup the declaration. */ n = st->get (expr); --- 5120,5140 ---- tree *n; tree expr = *tp; + /* For recursive invocations this is no longer the LHS itself. */ + bool is_lhs = wi->is_lhs; + wi->is_lhs = false; + + if (TREE_CODE (expr) == SSA_NAME) + { + *tp = remap_ssa_name (*tp, id); + *walk_subtrees = 0; + if (is_lhs) + SSA_NAME_DEF_STMT (*tp) = gsi_stmt (wi->gsi); + } /* Only a local declaration (variable or label). */ ! else if ((TREE_CODE (expr) == VAR_DECL ! && !TREE_STATIC (expr)) ! || TREE_CODE (expr) == LABEL_DECL) { /* Lookup the declaration. */ n = st->get (expr); *************** copy_gimple_seq_and_replace_locals (gimp *** 5262,5267 **** --- 5274,5280 ---- memset (&id, 0, sizeof (id)); id.src_fn = current_function_decl; id.dst_fn = current_function_decl; + id.src_cfun = cfun; id.decl_map = new hash_map<tree, tree>; id.debug_map = NULL; Index: gcc/gimple-low.c =================================================================== *** gcc/gimple-low.c.orig 2016-04-21 14:16:03.683298131 +0200 --- gcc/gimple-low.c 2016-04-21 14:33:14.042961325 +0200 *************** lower_builtin_setjmp (gimple_stmt_iterat *** 740,746 **** passed to both __builtin_setjmp_setup and __builtin_setjmp_receiver. */ FORCED_LABEL (next_label) = 1; ! dest = gimple_call_lhs (stmt); /* Build '__builtin_setjmp_setup (BUF, NEXT_LABEL)' and insert. */ arg = build_addr (next_label); --- 740,748 ---- passed to both __builtin_setjmp_setup and __builtin_setjmp_receiver. */ FORCED_LABEL (next_label) = 1; ! tree orig_dest = dest = gimple_call_lhs (stmt); ! if (TREE_CODE (orig_dest) == SSA_NAME) ! dest = create_tmp_reg (TREE_TYPE (orig_dest)); /* Build '__builtin_setjmp_setup (BUF, NEXT_LABEL)' and insert. */ arg = build_addr (next_label); *************** lower_builtin_setjmp (gimple_stmt_iterat *** 789,794 **** --- 791,803 ---- g = gimple_build_label (cont_label); gsi_insert_before (gsi, g, GSI_SAME_STMT); + /* Build orig_dest = dest if necessary. */ + if (dest != orig_dest) + { + g = gimple_build_assign (orig_dest, dest); + gsi_insert_before (gsi, g, GSI_SAME_STMT); + } + /* Remove the call to __builtin_setjmp. */ gsi_remove (gsi, false); } Index: gcc/cgraph.c =================================================================== *** gcc/cgraph.c.orig 2016-04-21 14:16:03.683298131 +0200 --- gcc/cgraph.c 2016-04-21 14:33:14.042961325 +0200 *************** release_function_body (tree decl) *** 1729,1758 **** if (fn) { if (fn->cfg ! || fn->gimple_df) { ! if (fn->cfg ! && loops_for_fn (fn)) ! { ! fn->curr_properties &= ~PROP_loops; ! loop_optimizer_finalize (fn); ! } ! if (fn->gimple_df) ! { ! delete_tree_ssa (fn); ! delete_tree_cfg_annotations (fn); ! fn->eh = NULL; ! } ! if (fn->cfg) ! { ! gcc_assert (!dom_info_available_p (fn, CDI_DOMINATORS)); ! gcc_assert (!dom_info_available_p (fn, CDI_POST_DOMINATORS)); ! clear_edges (fn); ! fn->cfg = NULL; ! } ! if (fn->value_histograms) ! free_histograms (fn); } gimple_set_body (decl, NULL); /* Struct function hangs a lot of data that would leak if we didn't removed all pointers to it. */ --- 1729,1754 ---- if (fn) { if (fn->cfg ! && loops_for_fn (fn)) { ! fn->curr_properties &= ~PROP_loops; ! loop_optimizer_finalize (fn); } + if (fn->gimple_df) + { + delete_tree_ssa (fn); + fn->eh = NULL; + } + if (fn->cfg) + { + gcc_assert (!dom_info_available_p (fn, CDI_DOMINATORS)); + gcc_assert (!dom_info_available_p (fn, CDI_POST_DOMINATORS)); + delete_tree_cfg_annotations (fn); + clear_edges (fn); + fn->cfg = NULL; + } + if (fn->value_histograms) + free_histograms (fn); gimple_set_body (decl, NULL); /* Struct function hangs a lot of data that would leak if we didn't removed all pointers to it. */ Index: gcc/gimple-fold.c =================================================================== *** gcc/gimple-fold.c.orig 2016-04-21 14:16:03.683298131 +0200 --- gcc/gimple-fold.c 2016-04-21 14:33:14.042961325 +0200 *************** gimplify_and_update_call_from_tree (gimp *** 542,548 **** } else { ! tree tmp = get_initialized_tmp_var (expr, &stmts, NULL); new_stmt = gimple_build_assign (lhs, tmp); i = gsi_last (stmts); gsi_insert_after_without_update (&i, new_stmt, --- 542,548 ---- } else { ! tree tmp = force_gimple_operand (expr, &stmts, false, NULL_TREE); new_stmt = gimple_build_assign (lhs, tmp); i = gsi_last (stmts); gsi_insert_after_without_update (&i, new_stmt, Index: gcc/gimplify.h =================================================================== *** gcc/gimplify.h.orig 2016-04-21 14:16:03.683298131 +0200 --- gcc/gimplify.h 2016-04-21 14:33:14.042961325 +0200 *************** extern gbind *gimple_current_bind_expr ( *** 57,63 **** extern vec<gbind *> gimple_bind_expr_stack (void); extern void gimplify_and_add (tree, gimple_seq *); extern tree get_formal_tmp_var (tree, gimple_seq *); ! extern tree get_initialized_tmp_var (tree, gimple_seq *, gimple_seq *); extern void declare_vars (tree, gimple *, bool); extern void gimple_add_tmp_var (tree); extern void gimple_add_tmp_var_fn (struct function *, tree); --- 57,64 ---- extern vec<gbind *> gimple_bind_expr_stack (void); extern void gimplify_and_add (tree, gimple_seq *); extern tree get_formal_tmp_var (tree, gimple_seq *); ! extern tree get_initialized_tmp_var (tree, gimple_seq *, gimple_seq *, ! bool = true); extern void declare_vars (tree, gimple *, bool); extern void gimple_add_tmp_var (tree); extern void gimple_add_tmp_var_fn (struct function *, tree); Index: gcc/tree-pass.h =================================================================== *** gcc/tree-pass.h.orig 2016-04-21 10:21:48.936615731 +0200 --- gcc/tree-pass.h 2016-04-21 14:35:32.428541241 +0200 *************** extern ipa_opt_pass_d *make_pass_ipa_com *** 506,512 **** extern gimple_opt_pass *make_pass_cleanup_cfg_post_optimizing (gcc::context *ctxt); - extern gimple_opt_pass *make_pass_init_datastructures (gcc::context *ctxt); extern gimple_opt_pass *make_pass_fixup_cfg (gcc::context *ctxt); extern gimple_opt_pass *make_pass_backprop (gcc::context *ctxt); --- 506,511 ---- Index: gcc/tree-ssa.c =================================================================== *** gcc/tree-ssa.c.orig 2016-04-04 12:34:37.479498003 +0200 --- gcc/tree-ssa.c 2016-04-21 14:36:26.517158657 +0200 *************** init_tree_ssa (struct function *fn) *** 1076,1137 **** init_ssanames (fn, 0); } - /* Do the actions required to initialize internal data structures used - in tree-ssa optimization passes. */ - - static unsigned int - execute_init_datastructures (void) - { - /* Allocate hash tables, arrays and other structures. */ - gcc_assert (!cfun->gimple_df); - init_tree_ssa (cfun); - return 0; - } - - namespace { - - const pass_data pass_data_init_datastructures = - { - GIMPLE_PASS, /* type */ - "*init_datastructures", /* name */ - OPTGROUP_NONE, /* optinfo_flags */ - TV_NONE, /* tv_id */ - PROP_cfg, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - 0, /* todo_flags_finish */ - }; - - class pass_init_datastructures : public gimple_opt_pass - { - public: - pass_init_datastructures (gcc::context *ctxt) - : gimple_opt_pass (pass_data_init_datastructures, ctxt) - {} - - /* opt_pass methods: */ - virtual bool gate (function *fun) - { - /* Do nothing for funcions that was produced already in SSA form. */ - return !(fun->curr_properties & PROP_ssa); - } - - virtual unsigned int execute (function *) - { - return execute_init_datastructures (); - } - - }; // class pass_init_datastructures - - } // anon namespace - - gimple_opt_pass * - make_pass_init_datastructures (gcc::context *ctxt) - { - return new pass_init_datastructures (ctxt); - } - /* Deallocate memory associated with SSA data structures for FNDECL. */ void --- 1076,1081 ----