/* 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, 2009
- 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 "libfuncs.h"
#include "recog.h"
#include "machmode.h"
+#include "diagnostic-core.h"
#include "toplev.h"
#include "output.h"
#include "ggc.h"
#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);
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. */
+ /* Make sure stack is consistent for asm goto. */
+ if (nlabels > 0)
+ do_pending_stack_adjust ();
+
ninout = 0;
for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
{
{
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;
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);
break;
else
{
- c += 1;
+ c += 1 + (c[1] == '%');
continue;
}
}
p += 2;
else
{
- p += 1;
+ p += 1 + (p[1] == '%');
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", identifier_to_locale (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. */
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;
+ if (DECL_BY_REFERENCE (decl))
+ mode = promote_function_mode (type, old_mode, &unsignedp, funtype, 2);
+ else
+ 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
xbitpos for the destination store (right justified). */
store_bit_field (dst, bitsize, xbitpos % BITS_PER_WORD, word_mode,
extract_bit_field (src, bitsize,
- bitpos % BITS_PER_WORD, 1,
+ bitpos % BITS_PER_WORD, 1, false,
NULL_RTX, word_mode, word_mode));
}
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)
decrementing fp by STARTING_FRAME_OFFSET. */
emit_move_insn (virtual_stack_vars_rtx, hard_frame_pointer_rtx);
-#if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+#if !HARD_FRAME_POINTER_IS_ARG_POINTER
if (fixed_regs[ARG_POINTER_REGNUM])
{
#ifdef ELIMINABLE_REGS
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));
/* By default, enable case bit tests on targets with ashlsi3. */
#ifndef CASE_USE_BIT_TESTS
-#define CASE_USE_BIT_TESTS (optab_handler (ashl_optab, word_mode)->insn_code \
+#define CASE_USE_BIT_TESTS (optab_handler (ashl_optab, word_mode) \
!= CODE_FOR_nothing)
#endif
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 we have not seen this label yet, then increase the
number of unique case node targets seen. */
lab = label_rtx (n->code_label);
- if (!bitmap_bit_p (label_bitmap, CODE_LABEL_NUMBER (lab)))
- {
- bitmap_set_bit (label_bitmap, CODE_LABEL_NUMBER (lab));
- uniq++;
- }
+ if (bitmap_set_bit (label_bitmap, CODE_LABEL_NUMBER (lab)))
+ uniq++;
}
BITMAP_FREE (label_bitmap);
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