/* Expands front end tree to back end RTL for GCC
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997,
- 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
- Free Software Foundation, Inc.
+ 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
+ 2010 Free Software Foundation, Inc.
This file is part of GCC.
#include "predict.h"
#include "optabs.h"
#include "target.h"
+#include "gimple.h"
#include "regs.h"
#include "alloc-pool.h"
+#include "pretty-print.h"
+#include "bitmap.h"
+
\f
/* Functions and data structures for expanding case statements. */
static bool tree_conflicts_with_clobbers_p (tree, HARD_REG_SET *);
static void expand_nl_goto_receiver (void);
static bool check_operand_nalternatives (tree, tree);
-static bool check_unique_operand_names (tree, tree);
-static char *resolve_operand_name_1 (char *, tree, tree);
+static bool check_unique_operand_names (tree, tree, tree);
+static char *resolve_operand_name_1 (char *, tree, tree, tree);
static void expand_null_return_1 (void);
static void expand_value_return (rtx);
static int estimate_case_costs (case_node_ptr);
{
rtx ref = label_rtx (label);
tree function = decl_function_context (label);
- struct function *p;
gcc_assert (function);
- if (function != current_function_decl)
- p = find_function_data (function);
- else
- p = cfun;
-
forced_labels = gen_rtx_EXPR_LIST (VOIDmode, ref, forced_labels);
return ref;
}
if (overlap)
{
- error ("asm-specifier for variable %qs conflicts with asm clobber list",
- IDENTIFIER_POINTER (DECL_NAME (overlap)));
+ error ("asm-specifier for variable %qE conflicts with asm clobber list",
+ DECL_NAME (overlap));
/* Reset registerness to stop multiple errors emitted for a single
variable. */
static void
expand_asm_operands (tree string, tree outputs, tree inputs,
- tree clobbers, int vol, location_t locus)
+ tree clobbers, tree labels, int vol, location_t locus)
{
- rtvec argvec, constraintvec;
+ rtvec argvec, constraintvec, labelvec;
rtx body;
int ninputs = list_length (inputs);
int noutputs = list_length (outputs);
+ int nlabels = list_length (labels);
int ninout;
int nclobbers;
HARD_REG_SET clobbered_regs;
if (! check_operand_nalternatives (outputs, inputs))
return;
- string = resolve_asm_operand_names (string, outputs, inputs);
+ string = resolve_asm_operand_names (string, outputs, inputs, labels);
/* Collect constraints. */
i = 0;
|| (DECL_P (val)
&& REG_P (DECL_RTL (val))
&& GET_MODE (DECL_RTL (val)) != TYPE_MODE (type))))
- lang_hooks.mark_addressable (val);
+ mark_addressable (val);
if (is_inout)
ninout++;
return;
if (! allows_reg && allows_mem)
- lang_hooks.mark_addressable (TREE_VALUE (tail));
+ mark_addressable (TREE_VALUE (tail));
}
/* Second pass evaluates arguments. */
{
op = assign_temp (type, 0, 0, 1);
op = validize_mem (op);
+ if (!MEM_P (op) && TREE_CODE (TREE_VALUE (tail)) == SSA_NAME)
+ set_reg_attrs_for_decl_rtl (SSA_NAME_VAR (TREE_VALUE (tail)), op);
TREE_VALUE (tail) = make_tree (type, op);
}
output_rtx[i] = op;
argvec = rtvec_alloc (ninputs);
constraintvec = rtvec_alloc (ninputs);
+ labelvec = rtvec_alloc (nlabels);
body = gen_rtx_ASM_OPERANDS ((noutputs == 0 ? VOIDmode
: GET_MODE (output_rtx[0])),
ggc_strdup (TREE_STRING_POINTER (string)),
empty_string, 0, argvec, constraintvec,
- locus);
+ labelvec, locus);
MEM_VOLATILE_P (body) = vol;
else if (MEM_P (op))
op = validize_mem (op);
- if (asm_operand_ok (op, constraint) <= 0)
+ if (asm_operand_ok (op, constraint, NULL) <= 0)
{
if (allows_reg && TYPE_MODE (type) != BLKmode)
op = force_reg (TYPE_MODE (type), op);
ASM_OPERANDS_INPUT (body, i) = op;
ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, i)
- = gen_rtx_ASM_INPUT (TYPE_MODE (type),
+ = gen_rtx_ASM_INPUT (TYPE_MODE (type),
ggc_strdup (constraints[i + noutputs]));
if (tree_conflicts_with_clobbers_p (val, &clobbered_regs))
= gen_rtx_ASM_INPUT (inout_mode[i], ggc_strdup (buffer));
}
+ /* Copy labels to the vector. */
+ for (i = 0, tail = labels; i < nlabels; ++i, tail = TREE_CHAIN (tail))
+ ASM_OPERANDS_LABEL (body, i)
+ = gen_rtx_LABEL_REF (Pmode, label_rtx (TREE_VALUE (tail)));
+
generating_concat_p = old_generating_concat_p;
/* Now, for each output, construct an rtx
ARGVEC CONSTRAINTS OPNAMES))
If there is more than one, put them inside a PARALLEL. */
- if (noutputs == 1 && nclobbers == 0)
+ if (nlabels > 0 && nclobbers == 0)
{
- ASM_OPERANDS_OUTPUT_CONSTRAINT (body) = ggc_strdup (constraints[0]);
- emit_insn (gen_rtx_SET (VOIDmode, output_rtx[0], body));
+ gcc_assert (noutputs == 0);
+ emit_jump_insn (body);
}
-
else if (noutputs == 0 && nclobbers == 0)
{
/* No output operands: put in a raw ASM_OPERANDS rtx. */
emit_insn (body);
}
-
+ else if (noutputs == 1 && nclobbers == 0)
+ {
+ ASM_OPERANDS_OUTPUT_CONSTRAINT (body) = ggc_strdup (constraints[0]);
+ emit_insn (gen_rtx_SET (VOIDmode, output_rtx[0], body));
+ }
else
{
rtx obody = body;
(GET_MODE (output_rtx[i]),
ggc_strdup (TREE_STRING_POINTER (string)),
ggc_strdup (constraints[i]),
- i, argvec, constraintvec, locus));
+ i, argvec, constraintvec, labelvec, locus));
MEM_VOLATILE_P (SET_SRC (XVECEXP (body, 0, i))) = vol;
}
= gen_rtx_CLOBBER (VOIDmode, clobbered_reg);
}
- emit_insn (body);
+ if (nlabels > 0)
+ emit_jump_insn (body);
+ else
+ emit_insn (body);
}
/* For any outputs that needed reloading into registers, spill them
}
void
-expand_asm_expr (tree exp)
+expand_asm_stmt (gimple stmt)
{
- int noutputs, i;
- tree outputs, tail;
+ int noutputs;
+ tree outputs, tail, t;
tree *o;
+ size_t i, n;
+ const char *s;
+ tree str, out, in, cl, labels;
+ location_t locus = gimple_location (stmt);
+
+ /* Meh... convert the gimple asm operands into real tree lists.
+ Eventually we should make all routines work on the vectors instead
+ of relying on TREE_CHAIN. */
+ out = NULL_TREE;
+ n = gimple_asm_noutputs (stmt);
+ if (n > 0)
+ {
+ t = out = gimple_asm_output_op (stmt, 0);
+ for (i = 1; i < n; i++)
+ t = TREE_CHAIN (t) = gimple_asm_output_op (stmt, i);
+ }
+
+ in = NULL_TREE;
+ n = gimple_asm_ninputs (stmt);
+ if (n > 0)
+ {
+ t = in = gimple_asm_input_op (stmt, 0);
+ for (i = 1; i < n; i++)
+ t = TREE_CHAIN (t) = gimple_asm_input_op (stmt, i);
+ }
+
+ cl = NULL_TREE;
+ n = gimple_asm_nclobbers (stmt);
+ if (n > 0)
+ {
+ t = cl = gimple_asm_clobber_op (stmt, 0);
+ for (i = 1; i < n; i++)
+ t = TREE_CHAIN (t) = gimple_asm_clobber_op (stmt, i);
+ }
- if (ASM_INPUT_P (exp))
+ labels = NULL_TREE;
+ n = gimple_asm_nlabels (stmt);
+ if (n > 0)
{
- expand_asm_loc (ASM_STRING (exp), ASM_VOLATILE_P (exp), input_location);
+ t = labels = gimple_asm_label_op (stmt, 0);
+ for (i = 1; i < n; i++)
+ t = TREE_CHAIN (t) = gimple_asm_label_op (stmt, i);
+ }
+
+ s = gimple_asm_string (stmt);
+ str = build_string (strlen (s), s);
+
+ if (gimple_asm_input_p (stmt))
+ {
+ expand_asm_loc (str, gimple_asm_volatile_p (stmt), locus);
return;
}
- outputs = ASM_OUTPUTS (exp);
- noutputs = list_length (outputs);
+ outputs = out;
+ noutputs = gimple_asm_noutputs (stmt);
/* o[I] is the place that output number I should be written. */
o = (tree *) alloca (noutputs * sizeof (tree));
/* Generate the ASM_OPERANDS insn; store into the TREE_VALUEs of
OUTPUTS some trees for where the values were actually stored. */
- expand_asm_operands (ASM_STRING (exp), outputs, ASM_INPUTS (exp),
- ASM_CLOBBERS (exp), ASM_VOLATILE_P (exp),
- input_location);
+ expand_asm_operands (str, outputs, in, cl, labels,
+ gimple_asm_volatile_p (stmt), locus);
/* Copy all the intermediate outputs into the specified outputs. */
for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
so all we need are pointer comparisons. */
static bool
-check_unique_operand_names (tree outputs, tree inputs)
+check_unique_operand_names (tree outputs, tree inputs, tree labels)
{
tree i, j;
goto failure;
}
+ for (i = labels; i ; i = TREE_CHAIN (i))
+ {
+ tree i_name = TREE_PURPOSE (i);
+ if (! i_name)
+ continue;
+
+ for (j = TREE_CHAIN (i); j ; j = TREE_CHAIN (j))
+ if (simple_cst_equal (i_name, TREE_PURPOSE (j)))
+ goto failure;
+ for (j = inputs; j ; j = TREE_CHAIN (j))
+ if (simple_cst_equal (i_name, TREE_PURPOSE (TREE_PURPOSE (j))))
+ goto failure;
+ }
+
return true;
failure:
STRING and in the constraints to those numbers. */
tree
-resolve_asm_operand_names (tree string, tree outputs, tree inputs)
+resolve_asm_operand_names (tree string, tree outputs, tree inputs, tree labels)
{
char *buffer;
char *p;
const char *c;
tree t;
- check_unique_operand_names (outputs, inputs);
+ check_unique_operand_names (outputs, inputs, labels);
/* Substitute [<name>] in input constraint strings. There should be no
named operands in output constraints. */
{
p = buffer = xstrdup (c);
while ((p = strchr (p, '[')) != NULL)
- p = resolve_operand_name_1 (p, outputs, inputs);
+ p = resolve_operand_name_1 (p, outputs, inputs, NULL);
TREE_VALUE (TREE_PURPOSE (t))
= build_string (strlen (buffer), buffer);
free (buffer);
continue;
}
- p = resolve_operand_name_1 (p, outputs, inputs);
+ p = resolve_operand_name_1 (p, outputs, inputs, labels);
}
string = build_string (strlen (buffer), buffer);
balance of the string after substitution. */
static char *
-resolve_operand_name_1 (char *p, tree outputs, tree inputs)
+resolve_operand_name_1 (char *p, tree outputs, tree inputs, tree labels)
{
char *q;
int op;
tree t;
- size_t len;
/* Collect the operand name. */
- q = strchr (p, ']');
+ q = strchr (++p, ']');
if (!q)
{
error ("missing close brace for named operand");
return strchr (p, '\0');
}
- len = q - p - 1;
+ *q = '\0';
/* Resolve the name to a number. */
for (op = 0, t = outputs; t ; t = TREE_CHAIN (t), op++)
{
tree name = TREE_PURPOSE (TREE_PURPOSE (t));
- if (name)
- {
- const char *c = TREE_STRING_POINTER (name);
- if (strncmp (c, p + 1, len) == 0 && c[len] == '\0')
- goto found;
- }
+ if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
+ goto found;
}
for (t = inputs; t ; t = TREE_CHAIN (t), op++)
{
tree name = TREE_PURPOSE (TREE_PURPOSE (t));
- if (name)
- {
- const char *c = TREE_STRING_POINTER (name);
- if (strncmp (c, p + 1, len) == 0 && c[len] == '\0')
- goto found;
- }
+ if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
+ goto found;
+ }
+ for (t = labels; t ; t = TREE_CHAIN (t), op++)
+ {
+ tree name = TREE_PURPOSE (t);
+ if (name && strcmp (TREE_STRING_POINTER (name), p) == 0)
+ goto found;
}
- *q = '\0';
- error ("undefined named operand %qs", p + 1);
+ error ("undefined named operand %qs", identifier_to_locale (p));
op = 0;
- found:
+ found:
/* Replace the name with the number. Unfortunately, not all libraries
get the return value of sprintf correct, so search for the end of the
generated string by hand. */
- sprintf (p, "%d", op);
+ sprintf (--p, "%d", op);
p = strchr (p, '\0');
/* Verify the no extra buffer space assumption. */
goto restart;
case SAVE_EXPR:
+ case NON_LVALUE_EXPR:
exp = TREE_OPERAND (exp, 0);
goto restart;
return 0;
warn:
- warning (OPT_Wunused_value, "%Hvalue computed is not used", &locus);
+ warning_at (locus, OPT_Wunused_value, "value computed is not used");
return 1;
}
}
static void
expand_value_return (rtx val)
{
- /* Copy the value to the return location
- unless it's already there. */
+ /* Copy the value to the return location unless it's already there. */
- rtx return_reg = DECL_RTL (DECL_RESULT (current_function_decl));
+ tree decl = DECL_RESULT (current_function_decl);
+ rtx return_reg = DECL_RTL (decl);
if (return_reg != val)
{
- tree type = TREE_TYPE (DECL_RESULT (current_function_decl));
- if (targetm.calls.promote_function_return (TREE_TYPE (current_function_decl)))
- {
- int unsignedp = TYPE_UNSIGNED (type);
- enum machine_mode old_mode
- = DECL_MODE (DECL_RESULT (current_function_decl));
- enum machine_mode mode
- = promote_mode (type, old_mode, &unsignedp, 1);
-
- if (mode != old_mode)
- val = convert_modes (mode, old_mode, val, unsignedp);
- }
+ tree funtype = TREE_TYPE (current_function_decl);
+ tree type = TREE_TYPE (decl);
+ int unsignedp = TYPE_UNSIGNED (type);
+ enum machine_mode old_mode = DECL_MODE (decl);
+ enum machine_mode mode = promote_function_mode (type, old_mode,
+ &unsignedp, funtype, 1);
+
+ if (mode != old_mode)
+ val = convert_modes (mode, old_mode, val, unsignedp);
+
if (GET_CODE (return_reg) == PARALLEL)
emit_group_load (return_reg, val, type, int_size_in_bytes (type));
else
}
}
\f
-/* Given a pointer to a BLOCK node return nonzero if (and only if) the node
- in question represents the outermost pair of curly braces (i.e. the "body
- block") of a function or method.
-
- For any BLOCK node representing a "body block" of a function or method, the
- BLOCK_SUPERCONTEXT of the node will point to another BLOCK node which
- represents the outermost (function) scope for the function or method (i.e.
- the one which includes the formal parameters). The BLOCK_SUPERCONTEXT of
- *that* node in turn will point to the relevant FUNCTION_DECL node. */
-
-int
-is_body_block (const_tree stmt)
-{
- if (lang_hooks.no_body_blocks)
- return 0;
-
- if (TREE_CODE (stmt) == BLOCK)
- {
- tree parent = BLOCK_SUPERCONTEXT (stmt);
-
- if (parent && TREE_CODE (parent) == BLOCK)
- {
- tree grandparent = BLOCK_SUPERCONTEXT (parent);
-
- if (grandparent && TREE_CODE (grandparent) == FUNCTION_DECL)
- return 1;
- }
- }
-
- return 0;
-}
-
/* Emit code to restore vital registers at the beginning of a nonlocal goto
handler. */
static void
expand_nl_goto_receiver (void)
{
+ rtx chain;
+
/* Clobber the FP when we get here, so we have to make sure it's
marked as used by this function. */
emit_use (hard_frame_pointer_rtx);
/* Mark the static chain as clobbered here so life information
doesn't get messed up for it. */
- emit_clobber (static_chain_rtx);
+ chain = targetm.calls.static_chain (current_function_decl, true);
+ if (chain && REG_P (chain))
+ emit_clobber (chain);
#ifdef HAVE_nonlocal_goto
if (! HAVE_nonlocal_goto)
else if (use_register_for_decl (decl))
{
/* Automatic variable that can go in a register. */
- int unsignedp = TYPE_UNSIGNED (type);
- enum machine_mode reg_mode
- = promote_mode (type, DECL_MODE (decl), &unsignedp, 0);
+ enum machine_mode reg_mode = promote_decl_mode (decl, NULL);
SET_DECL_RTL (decl, gen_reg_rtx (reg_mode));
emit_stack_restore (SAVE_BLOCK, sa, NULL_RTX);
}
\f
-/* DECL is an anonymous union. CLEANUP is a cleanup for DECL.
- DECL_ELTS is the list of elements that belong to DECL's type.
- In each, the TREE_VALUE is a VAR_DECL, and the TREE_PURPOSE a cleanup. */
-
-void
-expand_anon_union_decl (tree decl, tree cleanup ATTRIBUTE_UNUSED,
- tree decl_elts)
-{
- rtx x;
- tree t;
-
- /* If any of the elements are addressable, so is the entire union. */
- for (t = decl_elts; t; t = TREE_CHAIN (t))
- if (TREE_ADDRESSABLE (TREE_VALUE (t)))
- {
- TREE_ADDRESSABLE (decl) = 1;
- break;
- }
-
- expand_decl (decl);
- x = DECL_RTL (decl);
-
- /* Go through the elements, assigning RTL to each. */
- for (t = decl_elts; t; t = TREE_CHAIN (t))
- {
- tree decl_elt = TREE_VALUE (t);
- enum machine_mode mode = TYPE_MODE (TREE_TYPE (decl_elt));
- rtx decl_rtl;
-
- /* If any of the elements are addressable, so is the entire
- union. */
- if (TREE_USED (decl_elt))
- TREE_USED (decl) = 1;
-
- /* Propagate the union's alignment to the elements. */
- DECL_ALIGN (decl_elt) = DECL_ALIGN (decl);
- DECL_USER_ALIGN (decl_elt) = DECL_USER_ALIGN (decl);
-
- /* If the element has BLKmode and the union doesn't, the union is
- aligned such that the element doesn't need to have BLKmode, so
- change the element's mode to the appropriate one for its size. */
- if (mode == BLKmode && DECL_MODE (decl) != BLKmode)
- DECL_MODE (decl_elt) = mode
- = mode_for_size_tree (DECL_SIZE (decl_elt), MODE_INT, 1);
-
- if (mode == GET_MODE (x))
- decl_rtl = x;
- else if (MEM_P (x))
- /* (SUBREG (MEM ...)) at RTL generation time is invalid, so we
- instead create a new MEM rtx with the proper mode. */
- decl_rtl = adjust_address_nv (x, mode, 0);
- else
- {
- gcc_assert (REG_P (x));
- decl_rtl = gen_lowpart_SUBREG (mode, x);
- }
- SET_DECL_RTL (decl_elt, decl_rtl);
- }
-}
-\f
/* Do the insertion of a case label into case_list. The labels are
fed to us in descending order from the sorted vector of case labels used
in the tree part of the middle end. So the list we construct is
Generate the code to test it and jump to the right place. */
void
-expand_case (tree exp)
+expand_case (gimple stmt)
{
tree minval = NULL_TREE, maxval = NULL_TREE, range = NULL_TREE;
rtx default_label = 0;
int i;
rtx before_case, end, lab;
- tree vec = SWITCH_LABELS (exp);
- tree orig_type = TREE_TYPE (exp);
- tree index_expr = SWITCH_COND (exp);
+ tree index_expr = gimple_switch_index (stmt);
tree index_type = TREE_TYPE (index_expr);
int unsignedp = TYPE_UNSIGNED (index_type);
sizeof (struct case_node),
100);
- /* The switch body is lowered in gimplify.c, we should never have
- switches with a non-NULL SWITCH_BODY here. */
- gcc_assert (!SWITCH_BODY (exp));
- gcc_assert (SWITCH_LABELS (exp));
-
do_pending_stack_adjust ();
/* An ERROR_MARK occurs for various reasons including invalid data type. */
{
tree elt;
bitmap label_bitmap;
- int vl = TREE_VEC_LENGTH (vec);
+ int stopi = 0;
/* cleanup_tree_cfg removes all SWITCH_EXPR with their index
expressions being INTEGER_CST. */
gcc_assert (TREE_CODE (index_expr) != INTEGER_CST);
- /* The default case, if ever taken, is at the end of TREE_VEC. */
- elt = TREE_VEC_ELT (vec, vl - 1);
+ /* The default case, if ever taken, is the first element. */
+ elt = gimple_switch_label (stmt, 0);
if (!CASE_LOW (elt) && !CASE_HIGH (elt))
{
default_label_decl = CASE_LABEL (elt);
- --vl;
+ stopi = 1;
}
- for (i = vl - 1; i >= 0; --i)
+ for (i = gimple_switch_num_labels (stmt) - 1; i >= stopi; --i)
{
tree low, high;
- elt = TREE_VEC_ELT (vec, i);
+ elt = gimple_switch_label (stmt, i);
low = CASE_LOW (elt);
gcc_assert (low);
If the switch-index is a constant, do it this way
because we can optimize it. */
- else if (count < case_values_threshold ()
+ else if (count < targetm.case_values_threshold ()
|| compare_tree_int (range,
(optimize_insn_for_size_p () ? 3 : 10) * count) > 0
/* RANGE may be signed, and really large ranges will show up
decision tree an unconditional jump to the
default code is emitted. */
- use_cost_table
- = (TREE_CODE (orig_type) != ENUMERAL_TYPE
- && estimate_case_costs (case_list));
+ use_cost_table = estimate_case_costs (case_list);
balance_case_nodes (&case_list, NULL);
emit_case_nodes (index, case_list, default_label, index_type);
if (default_label)
int unsignedp)
{
do_compare_rtx_and_jump (op0, op1, EQ, unsignedp, mode,
- NULL_RTX, NULL_RTX, label);
+ NULL_RTX, NULL_RTX, label, -1);
}
\f
/* Not all case values are encountered equally. This function
/* Neither node is bounded. First distinguish the two sides;
then emit the code for one side at a time. */
- tree test_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+ tree test_label
+ = build_decl (CURR_INSN_LOCATION,
+ LABEL_DECL, NULL_TREE, NULL_TREE);
/* See if the value is on the right. */
emit_cmp_and_jump_insns (index,
/* Right hand node requires testing.
Branch to a label where we will handle it later. */
- test_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+ test_label = build_decl (CURR_INSN_LOCATION,
+ LABEL_DECL, NULL_TREE, NULL_TREE);
emit_cmp_and_jump_insns (index,
convert_modes
(mode, imode,