* i386.c (ix86_fntype_regparm): Rename from ...
(ix86_function_regparm): ... this one; add fastcall and local
functions.
(ix86_function_ok_for_sibcall): Update.
(ix86_return_pops_args): Likewise.
(init_cumulative_args): Likewise.
(x86_can_output_mi_thunk): Likewise.
(function_arg): Fix formating.
(x86_this_parameter): Fix fastcall.
(x86_output_mi_thunk): Likewise.
* cgraph.c (cgraph_mark_needed_node): Do not mark functions without
body as reachable; mark nested functions as needed too.
(dump_cgraph): Do not output global.calls.
* cgraph.h (cgraph_global_info): Kill.
* cgraphunit.c (cgraph_finalize_function): Enqueue needed functions.
(record_call_1): Speedup.
(cgraph_analyze_function): Break out from ...; compute inlining
parameters.
(cgraph_finalize_compilation_unit): ... here.
(cgraph_mark_inline): Kill computation of calls.
(cgraph_decide_inlining): Do not compute most of initial values.
From-SVN: r70504
+2003-08-16 Jan Hubicka <jh@suse.cz>
+
+ * i386.c (ix86_fntype_regparm): Rename from ...
+ (ix86_function_regparm): ... this one; add fastcall and local
+ functions.
+ (ix86_function_ok_for_sibcall): Update.
+ (ix86_return_pops_args): Likewise.
+ (init_cumulative_args): Likewise.
+ (x86_can_output_mi_thunk): Likewise.
+ (function_arg): Fix formating.
+ (x86_this_parameter): Fix fastcall.
+ (x86_output_mi_thunk): Likewise.
+
+ * cgraph.c (cgraph_mark_needed_node): Do not mark functions without
+ body as reachable; mark nested functions as needed too.
+ (dump_cgraph): Do not output global.calls.
+ * cgraph.h (cgraph_global_info): Kill.
+ * cgraphunit.c (cgraph_finalize_function): Enqueue needed functions.
+ (record_call_1): Speedup.
+ (cgraph_analyze_function): Break out from ...; compute inlining
+ parameters.
+ (cgraph_finalize_compilation_unit): ... here.
+ (cgraph_mark_inline): Kill computation of calls.
+ (cgraph_decide_inlining): Do not compute most of initial values.
+
2003-08-14 Roger Sayle <roger@eyesopen.com>
* fold-const.c (negate_expr_p): MULT_EXPRs and RDIV_EXPRs are easy
cgraph_mark_needed_node (struct cgraph_node *node, int needed)
{
if (needed)
- {
- node->needed = 1;
- }
- if (!node->reachable)
+ node->needed = 1;
+
+ if (!node->reachable && DECL_SAVED_TREE (node->decl))
{
node->reachable = 1;
- if (DECL_SAVED_TREE (node->decl))
+
+ node->next_needed = cgraph_nodes_queue;
+ cgraph_nodes_queue = node;
+
+ /* At the moment frontend automatically emits all nested functions. */
+ if (node->nested)
{
- node->next_needed = cgraph_nodes_queue;
- cgraph_nodes_queue = node;
- }
+ struct cgraph_node *node2;
+
+ for (node2 = node->nested; node2; node2 = node2->next_nested)
+ if (!node2->reachable)
+ cgraph_mark_needed_node (node2, 0);
+ }
}
}
fprintf (f, " %i insns after inlining", node->global.insns);
if (node->global.cloned_times > 1)
fprintf (f, " cloned %ix", node->global.cloned_times);
- if (node->global.calls)
- fprintf (f, " %i calls", node->global.calls);
fprintf (f, "\n called by :");
for (edge = node->callers; edge; edge = edge->next_caller)
/* Estimated size of the function after inlining. */
int insns;
- /* Number of direct calls not inlined into the function body. */
- int calls;
-
/* Number of times given function will be cloned during output. */
int cloned_times;
node->decl = decl;
node->local.finalized = true;
+ /* Function now has DECL_SAVED_TREE set. Enqueue it into cgraph_nodes_queue
+ if needed. */
+ if (node->needed)
+ cgraph_mark_needed_node (node, 0);
if (/* Externally visible functions must be output. The exception are
COMDAT functions that must be output only when they are needed.
Similarly are handled deferred functions and
*walk_subtrees = 0;
}
}
+ /* Save some cycles by not walking types and declaration as we won't find anything
+ usefull there anyway. */
+ if (DECL_P (*tp) || TYPE_P (*tp))
+ *walk_subtrees = 0;
return NULL;
}
walk_tree_without_duplicates (&body, record_call_1, decl);
}
+/* Analyze the function scheduled to be output. */
+static void
+cgraph_analyze_function (struct cgraph_node *node)
+{
+ tree decl = node->decl;
+
+ if (lang_hooks.callgraph.lower_function)
+ (*lang_hooks.callgraph.lower_function) (decl);
+
+ current_function_decl = node->decl;
+
+ /* First kill forward declaration so reverse inlining works properly. */
+ cgraph_create_edges (decl, DECL_SAVED_TREE (decl));
+
+ node->local.inlinable = tree_inlinable_function_p (decl);
+ if (!DECL_ESTIMATED_INSNS (decl))
+ DECL_ESTIMATED_INSNS (decl)
+ = (*lang_hooks.tree_inlining.estimate_num_insns) (decl);
+ node->local.self_insns = DECL_ESTIMATED_INSNS (decl);
+ if (node->local.inlinable)
+ node->local.disregard_inline_limits
+ = (*lang_hooks.tree_inlining.disregard_inline_limits) (decl);
+
+ /* Inlining characteristics are maintained by the cgraph_mark_inline. */
+ node->global.insns = node->local.self_insns;
+ if (!DECL_EXTERNAL (node->decl))
+ {
+ node->global.cloned_times = 1;
+ node->global.will_be_output = true;
+ }
+
+ node->lowered = true;
+}
+
/* Analyze the whole compilation unit once it is parsed completely. */
void
cgraph_finalize_compilation_unit (void)
{
struct cgraph_node *node;
- struct cgraph_edge *edge;
cgraph_varpool_assemble_pending_decls ();
if (!quiet_flag)
method table generation for instance). */
while (cgraph_nodes_queue)
{
+ struct cgraph_edge *edge;
tree decl = cgraph_nodes_queue->decl;
node = cgraph_nodes_queue;
if (node->lowered || !node->reachable || !DECL_SAVED_TREE (decl))
abort ();
- if (lang_hooks.callgraph.lower_function)
- (*lang_hooks.callgraph.lower_function) (decl);
-
- current_function_decl = node->decl;
-
- /* At the moment frontend automatically emits all nested functions. */
- if (node->nested)
- {
- struct cgraph_node *node2;
-
- for (node2 = node->nested; node2; node2 = node2->next_nested)
- if (!node2->reachable)
- cgraph_mark_needed_node (node2, 0);
- }
-
- /* First kill forward declaration so reverse inlining works properly. */
- cgraph_create_edges (decl, DECL_SAVED_TREE (decl));
-
- node->local.inlinable = tree_inlinable_function_p (decl);
- DECL_ESTIMATED_INSNS (decl)
- = (*lang_hooks.tree_inlining.estimate_num_insns) (decl);
- node->local.self_insns = DECL_ESTIMATED_INSNS (decl);
- if (node->local.inlinable)
- node->local.disregard_inline_limits
- = (*lang_hooks.tree_inlining.disregard_inline_limits) (decl);
-
+ cgraph_analyze_function (node);
for (edge = node->callees; edge; edge = edge->next_callee)
- {
- if (!edge->callee->reachable)
- cgraph_mark_needed_node (edge->callee, 0);
- }
- node->lowered = true;
+ {
+ if (!edge->callee->reachable)
+ cgraph_mark_needed_node (edge->callee, 0);
+ }
cgraph_varpool_assemble_pending_decls ();
}
/* Collect entry points to the unit. */
if (node->needed && DECL_SAVED_TREE (node->decl))
fprintf (cgraph_dump_file, " %s", cgraph_node_name (node));
fprintf (cgraph_dump_file, "\n");
+ dump_cgraph (cgraph_dump_file);
}
if (cgraph_dump_file)
overall_insns += new_insns - to->global.insns;
to->global.insns = new_insns;
- to->global.calls += (what->global.calls - 1) *times;
if (!called && !what->needed && !what->origin
&& !DECL_EXTERNAL (what->decl))
{
overall_insns -= what->global.insns;
}
what->global.cloned_times += clones;
- if (to->global.calls < 0)
- abort ();
for (i = 0; i < ninlined; i++)
{
new_insns =
if (inlined[i]->global.will_be_output)
overall_insns += new_insns - inlined[i]->global.insns;
inlined[i]->global.insns = new_insns;
- inlined[i]->global.calls +=
- (what->global.calls - 1) *INLINED_TIMES (inlined[i]) * times;
- if (inlined[i]->global.calls < 0)
- abort ();
}
for (i = 0; i < ninlined_callees; i++)
{
int i, y;
for (node = cgraph_nodes; node; node = node->next)
- {
- int ncalls = 0;
- struct cgraph_edge *e;
-
- node->global.insns = node->local.self_insns;
- for (e = node->callees; e; e = e->next_callee)
- ncalls++;
- node->global.calls = ncalls;
- if (!DECL_EXTERNAL (node->decl))
- {
- node->global.cloned_times = 1;
- initial_insns += node->local.self_insns;
- node->global.will_be_output = true;
- }
- }
+ initial_insns += node->local.self_insns;
overall_insns = initial_insns;
nnodes = cgraph_postorder (order);
static int ix86_save_reg (unsigned int, int);
static void ix86_compute_frame_layout (struct ix86_frame *);
static int ix86_comp_type_attributes (tree, tree);
-static int ix86_fntype_regparm (tree);
+static int ix86_function_regparm (tree, tree);
const struct attribute_spec ix86_attribute_table[];
static bool ix86_function_ok_for_sibcall (tree, tree);
static tree ix86_handle_cdecl_attribute (tree *, tree, tree, int, bool *);
such registers are not used for passing parameters. */
if (!decl && !TARGET_64BIT)
{
- int regparm = ix86_regparm;
- tree attr, type;
+ tree type;
/* We're looking at the CALL_EXPR, we need the type of the function. */
type = TREE_OPERAND (exp, 0); /* pointer expression */
type = TREE_TYPE (type); /* pointer type */
type = TREE_TYPE (type); /* function type */
- attr = lookup_attribute ("regparm", TYPE_ATTRIBUTES (type));
- if (attr)
- regparm = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (attr)));
-
- if (regparm >= 3)
+ if (ix86_function_regparm (type, NULL) >= 3)
{
/* ??? Need to count the actual number of registers to be used,
not the possible number of registers. Fix later. */
}
if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node)))
- {
- error ("fastcall and regparm attributes are not compatible");
- }
+ {
+ error ("fastcall and regparm attributes are not compatible");
+ }
}
return NULL_TREE;
return 1;
}
\f
-/* Return the regparm value for a fuctio with the indicated TYPE. */
+/* Return the regparm value for a fuctio with the indicated TYPE and DECL.
+ DECL may be NULL when calling function indirectly
+ or considerling a libcall. */
static int
-ix86_fntype_regparm (tree type)
+ix86_function_regparm (tree type, tree decl)
{
tree attr;
+ int regparm = ix86_regparm;
+ bool user_convention = false;
- attr = lookup_attribute ("regparm", TYPE_ATTRIBUTES (type));
- if (attr)
- return TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (attr)));
- else
- return ix86_regparm;
+ if (!TARGET_64BIT)
+ {
+ attr = lookup_attribute ("regparm", TYPE_ATTRIBUTES (type));
+ if (attr)
+ {
+ regparm = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (attr)));
+ user_convention = true;
+ }
+
+ if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type)))
+ {
+ regparm = 2;
+ user_convention = true;
+ }
+
+ /* Use register calling convention for local functions when possible. */
+ if (!TARGET_64BIT && !user_convention && decl
+ && flag_unit_at_a_time)
+ {
+ struct cgraph_local_info *i = cgraph_local_info (decl);
+ if (i && i->local)
+ {
+ /* We can't use regparm(3) for nested functions as these use
+ static chain pointer in third argument. */
+ if (DECL_CONTEXT (decl) && !DECL_NO_STATIC_CHAIN (decl))
+ regparm = 2;
+ else
+ regparm = 3;
+ }
+ }
+ }
+ return regparm;
}
/* Value is the number of bytes of arguments automatically
if (aggregate_value_p (TREE_TYPE (funtype))
&& !TARGET_64BIT)
{
- int nregs = ix86_fntype_regparm (funtype);
+ int nregs = ix86_function_regparm (funtype, fundecl);
if (!nregs)
return GET_MODE_SIZE (Pmode);
{
static CUMULATIVE_ARGS zero_cum;
tree param, next_param;
- bool user_convention = false;
if (TARGET_DEBUG_ARG)
{
*cum = zero_cum;
/* Set up the number of registers to use for passing arguments. */
- cum->nregs = ix86_regparm;
+ if (fntype)
+ cum->nregs = ix86_function_regparm (fntype, fndecl);
+ else
+ cum->nregs = ix86_regparm;
cum->sse_nregs = SSE_REGPARM_MAX;
- if (fntype && !TARGET_64BIT)
- {
- tree attr = lookup_attribute ("regparm", TYPE_ATTRIBUTES (fntype));
-
- if (attr)
- {
- cum->nregs = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (attr)));
- user_convention = true;
- }
- }
cum->maybe_vaarg = false;
/* Use ecx and edx registers if function has fastcall attribute */
{
cum->nregs = 2;
cum->fastcall = 1;
- user_convention = true;
- }
- }
-
- /* Use register calling convention for local functions when possible. */
- if (!TARGET_64BIT && !user_convention && fndecl
- && flag_unit_at_a_time)
- {
- struct cgraph_local_info *i = cgraph_local_info (fndecl);
- if (i && i->local)
- {
- /* We can't use regparm(3) for nested functions as these use
- static chain pointer in third argument. */
- if (DECL_CONTEXT (fndecl) && !DECL_NO_STATIC_CHAIN (fndecl))
- cum->nregs = 2;
- else
- cum->nregs = 3;
}
}
/* ECX not EAX is the first allocated register. */
if (regno == 0)
- regno = 2;
+ regno = 2;
}
ret = gen_rtx_REG (mode, regno);
}
return gen_rtx_REG (DImode, x86_64_int_parameter_registers[n]);
}
- if (ix86_fntype_regparm (type) > 0)
+ if (ix86_function_regparm (type, function) > 0)
{
tree parm;
for (; parm; parm = TREE_CHAIN (parm))
if (TREE_VALUE (parm) == void_type_node)
break;
- /* If not, the this parameter is in %eax. */
+ /* If not, the this parameter is in the first argument. */
if (parm)
- return gen_rtx_REG (SImode, 0);
+ {
+ int regno = 0;
+ if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type)))
+ regno = 2;
+ return gen_rtx_REG (SImode, 0);
+ }
}
if (aggregate_value_p (TREE_TYPE (type)))
return true;
/* For 32-bit, everything's fine if we have one free register. */
- if (ix86_fntype_regparm (TREE_TYPE (function)) < 3)
+ if (ix86_function_regparm (TREE_TYPE (function), function) < 3)
return true;
/* Need a free register for vcall_offset. */
if (TARGET_64BIT)
tmp = gen_rtx_REG (DImode, FIRST_REX_INT_REG + 2 /* R10 */);
else
- tmp = gen_rtx_REG (SImode, 2 /* ECX */);
+ {
+ int tmp_regno = 2 /* ECX */;
+ if (lookup_attribute ("fastcall",
+ TYPE_ATTRIBUTES (TREE_TYPE (function))))
+ tmp_regno = 0 /* EAX */;
+ tmp = gen_rtx_REG (SImode, tmp_regno);
+ }
xops[0] = gen_rtx_MEM (Pmode, this_reg);
xops[1] = tmp;