/* Expands front end tree to back end RTL for GNU C-Compiler
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997,
- 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "rtl.h"
#include "tree.h"
#include "expr.h"
#include "libfuncs.h"
#include "hard-reg-set.h"
-#include "obstack.h"
#include "loop.h"
#include "recog.h"
#include "machmode.h"
#include "toplev.h"
#include "output.h"
#include "ggc.h"
-
-#define obstack_chunk_alloc xmalloc
-#define obstack_chunk_free free
-struct obstack stmt_obstack;
+#include "langhooks.h"
+#include "predict.h"
+#include "optabs.h"
/* Assume that case vectors are not pc-relative. */
#ifndef CASE_VECTOR_PC_RELATIVE
and nodes on the right having higher values. We then output the tree
in order. */
-struct case_node
+struct case_node GTY(())
{
struct case_node *left; /* Left son in binary tree */
struct case_node *right; /* Right son in binary tree; also node chain */
/* Special care is needed because we allow -1, but TREE_INT_CST_LOW
is unsigned. */
-#define COST_TABLE(I) cost_table_[(unsigned HOST_WIDE_INT)((I) + 1)]
+#define COST_TABLE(I) cost_table_[(unsigned HOST_WIDE_INT) ((I) + 1)]
\f
/* Stack of control and binding constructs we are currently inside.
The construct is visible if the `exit_label' field is non-null.
In that case, the value should be a CODE_LABEL rtx. */
-struct nesting
+struct nesting GTY(())
{
struct nesting *all;
struct nesting *next;
int depth;
rtx exit_label;
- union
+ enum nesting_desc {
+ COND_NESTING,
+ LOOP_NESTING,
+ BLOCK_NESTING,
+ CASE_NESTING
+ } desc;
+ union nesting_u
{
/* For conds (if-then and if-then-else statements). */
- struct
+ struct nesting_cond
{
/* Label for the end of the if construct.
There is none if EXITFLAG was not set
/* Label for the end of this alternative.
This may be the end of the if or the next else/elseif. */
rtx next_label;
- } cond;
+ } GTY ((tag ("COND_NESTING"))) cond;
/* For loops. */
- struct
+ struct nesting_loop
{
/* Label at the top of the loop; place to loop back to. */
rtx start_label;
/* Label at the end of the whole construct. */
rtx end_label;
- /* Label before a jump that branches to the end of the whole
- construct. This is where destructors go if any. */
- rtx alt_end_label;
/* Label for `continue' statement to jump to;
this is in front of the stepper of the loop. */
rtx continue_label;
- } loop;
+ } GTY ((tag ("LOOP_NESTING"))) loop;
/* For variable binding contours. */
- struct
+ struct nesting_block
{
/* Sequence number of this binding contour within the function,
in order of entry. */
struct label_chain *label_chain;
/* Number of function calls seen, as of start of this block. */
int n_function_calls;
- /* Nonzero if this is associated with a EH region. */
+ /* Nonzero if this is associated with an EH region. */
int exception_region;
/* The saved target_temp_slot_level from our outer block.
We may reset target_temp_slot_level to be the level of
the start of the last unconditional cleanup, and before any
conditional branch points. */
rtx last_unconditional_cleanup;
- /* When in a conditional context, this is the specific
- cleanup list associated with last_unconditional_cleanup,
- where we place the conditionalized cleanups. */
- tree *cleanup_ptr;
- } block;
+ } GTY ((tag ("BLOCK_NESTING"))) block;
/* For switch (C) or case (Pascal) statements,
and also for dummies (see `expand_start_case_dummy'). */
- struct
+ struct nesting_case
{
/* The insn after which the case dispatch should finally
be emitted. Zero for a dummy. */
We set this to -1 when we see the first case label in this
case statement. */
int line_number_status;
- } case_stmt;
- } data;
+ } GTY ((tag ("CASE_NESTING"))) case_stmt;
+ } GTY ((desc ("%1.desc"))) data;
};
/* Allocate and return a new `struct nesting'. */
#define ALLOC_NESTING() \
- (struct nesting *) obstack_alloc (&stmt_obstack, sizeof (struct nesting))
+ (struct nesting *) ggc_alloc (sizeof (struct nesting))
/* Pop the nesting stack element by element until we pop off
the element which is at the top of STACK.
if (case_stack == this) \
case_stack = case_stack->next; \
nesting_depth = nesting_stack->depth - 1; \
- nesting_stack = this->all; \
- obstack_free (&stmt_obstack, this); } \
+ nesting_stack = this->all; } \
while (this != target); } while (0)
\f
/* In some cases it is impossible to generate code for a forward goto
we check each fixup.
If the target label has now been defined, we can insert the proper code. */
-struct goto_fixup
+struct goto_fixup GTY(())
{
/* Points to following fixup. */
struct goto_fixup *next;
/* Within any binding contour that must restore a stack level,
all labels are recorded with a chain of these structures. */
-struct label_chain
+struct label_chain GTY(())
{
/* Points to following fixup. */
struct label_chain *next;
tree label;
};
-struct stmt_status
+struct stmt_status GTY(())
{
/* Chain of all pending binding contours. */
- struct nesting *x_block_stack;
+ struct nesting * x_block_stack;
/* If any new stacks are added here, add them to POPSTACKS too. */
/* Chain of all pending binding contours that restore stack levels
or have cleanups. */
- struct nesting *x_stack_block_stack;
+ struct nesting * x_stack_block_stack;
/* Chain of all pending conditional statements. */
- struct nesting *x_cond_stack;
+ struct nesting * x_cond_stack;
/* Chain of all pending loops. */
- struct nesting *x_loop_stack;
+ struct nesting * x_loop_stack;
/* Chain of all pending case or switch statements. */
- struct nesting *x_case_stack;
+ struct nesting * x_case_stack;
/* Separate chain including all of the above,
chained through the `all' field. */
- struct nesting *x_nesting_stack;
+ struct nesting * x_nesting_stack;
/* Number of entries on nesting_stack now. */
int x_nesting_depth;
#define emit_lineno (cfun->stmt->x_emit_lineno)
#define goto_fixup_chain (cfun->stmt->x_goto_fixup_chain)
-/* Non-zero if we are using EH to handle cleanus. */
+/* Nonzero if we are using EH to handle cleanups. */
static int using_eh_for_cleanups_p = 0;
static int n_occurrences PARAMS ((int, const char *));
+static bool parse_input_constraint PARAMS ((const char **, int, int, int,
+ int, const char * const *,
+ bool *, bool *));
+static bool decl_conflicts_with_clobbers_p PARAMS ((tree, const HARD_REG_SET));
static void expand_goto_internal PARAMS ((tree, rtx, rtx));
static int expand_fixup PARAMS ((tree, rtx, rtx));
static rtx expand_nl_handler_label PARAMS ((rtx, rtx));
const char **));
static char *resolve_operand_name_1 PARAMS ((char *, tree, tree));
static void expand_null_return_1 PARAMS ((rtx));
+static enum br_predictor return_prediction PARAMS ((rtx));
static void expand_value_return PARAMS ((rtx));
static int tail_recursion_args PARAMS ((tree, tree));
static void expand_cleanups PARAMS ((tree, tree, int, int));
static void check_seenlabel PARAMS ((void));
static void do_jump_if_equal PARAMS ((rtx, rtx, rtx, int));
static int estimate_case_costs PARAMS ((case_node_ptr));
+static bool same_case_target_p PARAMS ((rtx, rtx));
+static void strip_default_case_nodes PARAMS ((case_node_ptr *, rtx));
+static bool lshift_cheap_p PARAMS ((void));
+static int case_bit_test_cmp PARAMS ((const void *, const void *));
+static void emit_case_bit_tests PARAMS ((tree, tree, tree, tree,
+ case_node_ptr, rtx));
static void group_case_nodes PARAMS ((case_node_ptr));
static void balance_case_nodes PARAMS ((case_node_ptr *,
case_node_ptr));
static void emit_jump_if_reachable PARAMS ((rtx));
static void emit_case_nodes PARAMS ((rtx, case_node_ptr, rtx, tree));
static struct case_node *case_tree2list PARAMS ((case_node *, case_node *));
-static void mark_cond_nesting PARAMS ((struct nesting *));
-static void mark_loop_nesting PARAMS ((struct nesting *));
-static void mark_block_nesting PARAMS ((struct nesting *));
-static void mark_case_nesting PARAMS ((struct nesting *));
-static void mark_case_node PARAMS ((struct case_node *));
-static void mark_goto_fixup PARAMS ((struct goto_fixup *));
-static void free_case_nodes PARAMS ((case_node_ptr));
\f
void
using_eh_for_cleanups ()
using_eh_for_cleanups_p = 1;
}
-/* Mark N (known to be a cond-nesting) for GC. */
-
-static void
-mark_cond_nesting (n)
- struct nesting *n;
-{
- while (n)
- {
- ggc_mark_rtx (n->exit_label);
- ggc_mark_rtx (n->data.cond.endif_label);
- ggc_mark_rtx (n->data.cond.next_label);
-
- n = n->next;
- }
-}
-
-/* Mark N (known to be a loop-nesting) for GC. */
-
-static void
-mark_loop_nesting (n)
- struct nesting *n;
-{
-
- while (n)
- {
- ggc_mark_rtx (n->exit_label);
- ggc_mark_rtx (n->data.loop.start_label);
- ggc_mark_rtx (n->data.loop.end_label);
- ggc_mark_rtx (n->data.loop.alt_end_label);
- ggc_mark_rtx (n->data.loop.continue_label);
-
- n = n->next;
- }
-}
-
-/* Mark N (known to be a block-nesting) for GC. */
-
-static void
-mark_block_nesting (n)
- struct nesting *n;
-{
- while (n)
- {
- struct label_chain *l;
-
- ggc_mark_rtx (n->exit_label);
- ggc_mark_rtx (n->data.block.stack_level);
- ggc_mark_rtx (n->data.block.first_insn);
- ggc_mark_tree (n->data.block.cleanups);
- ggc_mark_tree (n->data.block.outer_cleanups);
-
- for (l = n->data.block.label_chain; l != NULL; l = l->next)
- {
- ggc_mark (l);
- ggc_mark_tree (l->label);
- }
-
- ggc_mark_rtx (n->data.block.last_unconditional_cleanup);
-
- /* ??? cleanup_ptr never points outside the stack, does it? */
-
- n = n->next;
- }
-}
-
-/* Mark N (known to be a case-nesting) for GC. */
-
-static void
-mark_case_nesting (n)
- struct nesting *n;
-{
- while (n)
- {
- ggc_mark_rtx (n->exit_label);
- ggc_mark_rtx (n->data.case_stmt.start);
-
- ggc_mark_tree (n->data.case_stmt.default_label);
- ggc_mark_tree (n->data.case_stmt.index_expr);
- ggc_mark_tree (n->data.case_stmt.nominal_type);
-
- mark_case_node (n->data.case_stmt.case_list);
- n = n->next;
- }
-}
-
-/* Mark C for GC. */
-
-static void
-mark_case_node (c)
- struct case_node *c;
-{
- if (c != 0)
- {
- ggc_mark_tree (c->low);
- ggc_mark_tree (c->high);
- ggc_mark_tree (c->code_label);
-
- mark_case_node (c->right);
- mark_case_node (c->left);
- }
-}
-
-/* Mark G for GC. */
-
-static void
-mark_goto_fixup (g)
- struct goto_fixup *g;
-{
- while (g)
- {
- ggc_mark (g);
- ggc_mark_rtx (g->before_jump);
- ggc_mark_tree (g->target);
- ggc_mark_tree (g->context);
- ggc_mark_rtx (g->target_rtl);
- ggc_mark_rtx (g->stack_level);
- ggc_mark_tree (g->cleanup_list_list);
-
- g = g->next;
- }
-}
-
-/* Clear out all parts of the state in F that can safely be discarded
- after the function has been compiled, to let garbage collection
- reclaim the memory. */
-
-void
-free_stmt_status (f)
- struct function *f;
-{
- /* We're about to free the function obstack. If we hold pointers to
- things allocated there, then we'll try to mark them when we do
- GC. So, we clear them out here explicitly. */
- if (f->stmt)
- free (f->stmt);
- f->stmt = NULL;
-}
-
-/* Mark P for GC. */
-
-void
-mark_stmt_status (p)
- struct stmt_status *p;
-{
- if (p == 0)
- return;
-
- mark_block_nesting (p->x_block_stack);
- mark_cond_nesting (p->x_cond_stack);
- mark_loop_nesting (p->x_loop_stack);
- mark_case_nesting (p->x_case_stack);
-
- ggc_mark_tree (p->x_last_expr_type);
- /* last_epxr_value is only valid if last_expr_type is nonzero. */
- if (p->x_last_expr_type)
- ggc_mark_rtx (p->x_last_expr_value);
-
- mark_goto_fixup (p->x_goto_fixup_chain);
-}
-
-void
-init_stmt ()
-{
- gcc_obstack_init (&stmt_obstack);
-}
-
void
init_stmt_for_function ()
{
- cfun->stmt = (struct stmt_status *) xmalloc (sizeof (struct stmt_status));
+ cfun->stmt = ((struct stmt_status *)ggc_alloc (sizeof (struct stmt_status)));
/* We are not currently within any block, conditional, loop or case. */
block_stack = 0;
/* We are not processing a ({...}) grouping. */
expr_stmts_for_value = 0;
- last_expr_type = 0;
- last_expr_value = NULL_RTX;
+ clear_last_expr ();
}
\f
-/* Return nonzero if anything is pushed on the loop, condition, or case
- stack. */
-int
-in_control_zone_p ()
-{
- return cond_stack || loop_stack || case_stack;
-}
-
/* Record the current file and line. Called from emit_line_note. */
void
set_file_and_line_for_stmt (file, line)
#endif
emit_queue ();
- /* Be sure the function is executable. */
- if (current_function_check_memory_usage)
- emit_library_call (chkr_check_exec_libfunc, LCT_CONST_MAKE_BLOCK,
- VOIDmode, 1, x, ptr_mode);
-
do_pending_stack_adjust ();
emit_indirect_jump (x);
emit_indirect_jump (handler_slot);
}
- /* Search backwards to the jump insn and mark it as a
+ /* Search backwards to the jump insn and mark it as a
non-local goto. */
for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
{
as a placeholder. */
{
- rtx original_before_jump
- = last_insn ? last_insn : get_last_insn ();
+ rtx original_before_jump
+ = last_insn ? last_insn : get_last_insn ();
rtx start;
rtx end;
tree block;
TREE_USED (block) = 1;
if (!cfun->x_whole_function_mode_p)
- insert_block (block);
+ (*lang_hooks.decls.insert_block) (block);
else
{
BLOCK_CHAIN (block)
= block;
}
- start_sequence ();
- start = emit_note (NULL, NOTE_INSN_BLOCK_BEG);
+ start_sequence ();
+ start = emit_note (NULL, NOTE_INSN_BLOCK_BEG);
if (cfun->x_whole_function_mode_p)
NOTE_BLOCK (start) = block;
fixup->before_jump = emit_note (NULL, NOTE_INSN_DELETED);
end = emit_note (NULL, NOTE_INSN_BLOCK_END);
if (cfun->x_whole_function_mode_p)
NOTE_BLOCK (end) = block;
- fixup->context = block;
- end_sequence ();
- emit_insns_after (start, original_before_jump);
+ fixup->context = block;
+ end_sequence ();
+ emit_insn_after (start, original_before_jump);
}
fixup->block_start_count = current_block_start_count;
logically be inserting the fixup code. We do this for the
sake of getting the debugging information right. */
- pushlevel (0);
- set_block (f->context);
+ (*lang_hooks.decls.pushlevel) (0);
+ (*lang_hooks.decls.set_block) (f->context);
/* Expand the cleanups for blocks this jump exits. */
if (f->cleanup_list_list)
destructed are still "in scope". */
cleanup_insns = get_insns ();
- poplevel (1, 0, 0);
+ (*lang_hooks.decls.poplevel) (1, 0, 0);
end_sequence ();
- emit_insns_after (cleanup_insns, f->before_jump);
+ emit_insn_after (cleanup_insns, f->before_jump);
f->before_jump = 0;
}
if (TREE_CHAIN (lists) == thisblock->data.block.outer_cleanups)
{
start_sequence ();
- pushlevel (0);
- set_block (f->context);
+ (*lang_hooks.decls.pushlevel) (0);
+ (*lang_hooks.decls.set_block) (f->context);
expand_cleanups (TREE_VALUE (lists), NULL_TREE, 1, 1);
do_pending_stack_adjust ();
cleanup_insns = get_insns ();
- poplevel (1, 0, 0);
+ (*lang_hooks.decls.poplevel) (1, 0, 0);
end_sequence ();
if (cleanup_insns != 0)
f->before_jump
- = emit_insns_after (cleanup_insns, f->before_jump);
+ = emit_insn_after (cleanup_insns, f->before_jump);
f->cleanup_list_list = TREE_CHAIN (lists);
}
}
\f
/* Generate RTL for an asm statement (explicit assembler code).
- BODY is a STRING_CST node containing the assembler code text,
- or an ADDR_EXPR containing a STRING_CST. */
+ STRING is a STRING_CST node containing the assembler code text,
+ or an ADDR_EXPR containing a STRING_CST. VOL nonzero means the
+ insn is volatile; don't optimize it. */
void
-expand_asm (body)
- tree body;
+expand_asm (string, vol)
+ tree string;
+ int vol;
{
- if (current_function_check_memory_usage)
- {
- error ("`asm' cannot be used in function where memory usage is checked");
- return;
- }
+ rtx body;
+
+ if (TREE_CODE (string) == ADDR_EXPR)
+ string = TREE_OPERAND (string, 0);
- if (TREE_CODE (body) == ADDR_EXPR)
- body = TREE_OPERAND (body, 0);
+ body = gen_rtx_ASM_INPUT (VOIDmode, TREE_STRING_POINTER (string));
- emit_insn (gen_rtx_ASM_INPUT (VOIDmode,
- TREE_STRING_POINTER (body)));
- last_expr_type = 0;
+ MEM_VOLATILE_P (body) = vol;
+
+ emit_insn (body);
+
+ clear_last_expr ();
}
/* Parse the output constraint pointed to by *CONSTRAINT_P. It is the
will be true if the operand is read-write, i.e., if it is used as
an input as well as an output. If *CONSTRAINT_P is not in
canonical form, it will be made canonical. (Note that `+' will be
- rpelaced with `=' as part of this process.)
+ replaced with `=' as part of this process.)
Returns TRUE if all went well; FALSE if an error occurred. */
bool
-parse_output_constraint (constraint_p,
- operand_num,
- ninputs,
- noutputs,
- allows_mem,
- allows_reg,
- is_inout)
+parse_output_constraint (constraint_p, operand_num, ninputs, noutputs,
+ allows_mem, allows_reg, is_inout)
const char **constraint_p;
int operand_num;
int ninputs;
}
/* Loop through the constraint string. */
- for (p = constraint + 1; *p; ++p)
+ for (p = constraint + 1; *p; p += CONSTRAINT_LEN (*p, p))
switch (*p)
{
case '+':
case '=':
- error ("operand constraint contains '+' or '=' at illegal position.");
+ error ("operand constraint contains incorrectly positioned '+' or '='");
return false;
-
+
case '%':
if (operand_num + 1 == ninputs + noutputs)
{
*allows_reg = true;
*allows_mem = true;
break;
-
+
case 'p': case 'r':
*allows_reg = true;
break;
default:
if (!ISALPHA (*p))
break;
- if (REG_CLASS_FROM_LETTER (*p) != NO_REGS)
+ if (REG_CLASS_FROM_CONSTRAINT (*p, p) != NO_REGS)
+ *allows_reg = true;
+#ifdef EXTRA_CONSTRAINT_STR
+ else if (EXTRA_ADDRESS_CONSTRAINT (*p, p))
+ *allows_reg = true;
+ else if (EXTRA_MEMORY_CONSTRAINT (*p, p))
+ *allows_mem = true;
+ else
+ {
+ /* Otherwise we can't assume anything about the nature of
+ the constraint except that it isn't purely registers.
+ Treat it like "g" and hope for the best. */
+ *allows_reg = true;
+ *allows_mem = true;
+ }
+#endif
+ break;
+ }
+
+ return true;
+}
+
+/* Similar, but for input constraints. */
+
+static bool
+parse_input_constraint (constraint_p, input_num, ninputs, noutputs, ninout,
+ constraints, allows_mem, allows_reg)
+ const char **constraint_p;
+ int input_num;
+ int ninputs;
+ int noutputs;
+ int ninout;
+ const char * const * constraints;
+ bool *allows_mem;
+ bool *allows_reg;
+{
+ const char *constraint = *constraint_p;
+ const char *orig_constraint = constraint;
+ size_t c_len = strlen (constraint);
+ size_t j;
+
+ /* Assume the constraint doesn't allow the use of either
+ a register or memory. */
+ *allows_mem = false;
+ *allows_reg = false;
+
+ /* Make sure constraint has neither `=', `+', nor '&'. */
+
+ for (j = 0; j < c_len; j += CONSTRAINT_LEN (constraint[j], constraint+j))
+ switch (constraint[j])
+ {
+ case '+': case '=': case '&':
+ if (constraint == orig_constraint)
+ {
+ error ("input operand constraint contains `%c'", constraint[j]);
+ return false;
+ }
+ break;
+
+ case '%':
+ if (constraint == orig_constraint
+ && input_num + 1 == ninputs - ninout)
+ {
+ error ("`%%' constraint used with last operand");
+ return false;
+ }
+ break;
+
+ case 'V': case 'm': case 'o':
+ *allows_mem = true;
+ break;
+
+ case '<': case '>':
+ case '?': case '!': case '*': case '#':
+ case 'E': case 'F': case 'G': case 'H':
+ case 's': case 'i': case 'n':
+ case 'I': case 'J': case 'K': case 'L': case 'M':
+ case 'N': case 'O': case 'P': case ',':
+ break;
+
+ /* Whether or not a numeric constraint allows a register is
+ decided by the matching constraint, and so there is no need
+ to do anything special with them. We must handle them in
+ the default case, so that we don't unnecessarily force
+ operands to memory. */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ char *end;
+ unsigned long match;
+
+ match = strtoul (constraint + j, &end, 10);
+ if (match >= (unsigned long) noutputs)
+ {
+ error ("matching constraint references invalid operand number");
+ return false;
+ }
+
+ /* Try and find the real constraint for this dup. Only do this
+ if the matching constraint is the only alternative. */
+ if (*end == '\0'
+ && (j == 0 || (j == 1 && constraint[0] == '%')))
+ {
+ constraint = constraints[match];
+ *constraint_p = constraint;
+ c_len = strlen (constraint);
+ j = 0;
+ /* ??? At the end of the loop, we will skip the first part of
+ the matched constraint. This assumes not only that the
+ other constraint is an output constraint, but also that
+ the '=' or '+' come first. */
+ break;
+ }
+ else
+ j = end - constraint;
+ /* Anticipate increment at end of loop. */
+ j--;
+ }
+ /* Fall through. */
+
+ case 'p': case 'r':
+ *allows_reg = true;
+ break;
+
+ case 'g': case 'X':
+ *allows_reg = true;
+ *allows_mem = true;
+ break;
+
+ default:
+ if (! ISALPHA (constraint[j]))
+ {
+ error ("invalid punctuation `%c' in constraint", constraint[j]);
+ return false;
+ }
+ if (REG_CLASS_FROM_CONSTRAINT (constraint[j], constraint + j)
+ != NO_REGS)
+ *allows_reg = true;
+#ifdef EXTRA_CONSTRAINT_STR
+ else if (EXTRA_ADDRESS_CONSTRAINT (constraint[j], constraint + j))
*allows_reg = true;
-#ifdef EXTRA_CONSTRAINT
+ else if (EXTRA_MEMORY_CONSTRAINT (constraint[j], constraint + j))
+ *allows_mem = true;
else
{
/* Otherwise we can't assume anything about the nature of
return true;
}
+/* Check for overlap between registers marked in CLOBBERED_REGS and
+ anything inappropriate in DECL. Emit error and return TRUE for error,
+ FALSE for ok. */
+
+static bool
+decl_conflicts_with_clobbers_p (decl, clobbered_regs)
+ tree decl;
+ const HARD_REG_SET clobbered_regs;
+{
+ /* Conflicts between asm-declared register variables and the clobber
+ list are not allowed. */
+ if ((TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == PARM_DECL)
+ && DECL_REGISTER (decl)
+ && REG_P (DECL_RTL (decl))
+ && REGNO (DECL_RTL (decl)) < FIRST_PSEUDO_REGISTER)
+ {
+ rtx reg = DECL_RTL (decl);
+ unsigned int regno;
+
+ for (regno = REGNO (reg);
+ regno < (REGNO (reg)
+ + HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)));
+ regno++)
+ if (TEST_HARD_REG_BIT (clobbered_regs, regno))
+ {
+ error ("asm-specifier for variable `%s' conflicts with asm clobber list",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+
+ /* Reset registerness to stop multiple errors emitted for a
+ single variable. */
+ DECL_REGISTER (decl) = 0;
+ return true;
+ }
+ }
+ return false;
+}
+
/* Generate RTL for an asm statement with arguments.
STRING is the instruction template.
OUTPUTS is a list of output arguments (lvalues); INPUTS a list of inputs.
Each output or input has an expression in the TREE_VALUE and
and a tree list in TREE_PURPOSE which in turn contains a constraint
- name in TREE_VALUE (or NULL_TREE) and a constraint string
+ name in TREE_VALUE (or NULL_TREE) and a constraint string
in TREE_PURPOSE.
CLOBBERS is a list of STRING_CST nodes each naming a hard register
that is clobbered by this insn.
rtx body;
int ninputs = list_length (inputs);
int noutputs = list_length (outputs);
- int ninout = 0;
+ int ninout;
int nclobbers;
+ HARD_REG_SET clobbered_regs;
+ int clobber_conflict_found = 0;
tree tail;
int i;
/* Vector of RTX's of evaluated output operands. */
= (enum machine_mode *) alloca (noutputs * sizeof (enum machine_mode));
const char **constraints
= (const char **) alloca ((noutputs + ninputs) * sizeof (const char *));
- /* The insn we have emitted. */
- rtx insn;
int old_generating_concat_p = generating_concat_p;
/* An ASM with no outputs needs to be treated as volatile, for now. */
if (noutputs == 0)
vol = 1;
- if (current_function_check_memory_usage)
- {
- error ("`asm' cannot be used in function where memory usage is checked");
- return;
- }
-
if (! check_operand_nalternatives (outputs, inputs))
return;
/* Count the number of meaningful clobbered registers, ignoring what
we would ignore later. */
nclobbers = 0;
+ CLEAR_HARD_REG_SET (clobbered_regs);
for (tail = clobbers; tail; tail = TREE_CHAIN (tail))
{
const char *regname = TREE_STRING_POINTER (TREE_VALUE (tail));
++nclobbers;
else if (i == -2)
error ("unknown register name `%s' in `asm'", regname);
+
+ /* Mark clobbered registers. */
+ if (i >= 0)
+ {
+ /* Clobbering the PIC register is an error */
+ if (i == (int) PIC_OFFSET_TABLE_REGNUM)
+ {
+ error ("PIC register `%s' clobbered in `asm'", regname);
+ return;
+ }
+
+ SET_HARD_REG_BIT (clobbered_regs, i);
+ }
}
- last_expr_type = 0;
+ clear_last_expr ();
+ /* First pass over inputs and outputs checks validity and sets
+ mark_addressable if needed. */
+
+ ninout = 0;
for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
{
tree val = TREE_VALUE (tail);
tree type = TREE_TYPE (val);
+ const char *constraint;
bool is_inout;
bool allows_reg;
bool allows_mem;
if (type == error_mark_node)
return;
- /* Make sure constraint has `=' and does not have `+'. Also, see
- if it allows any register. Be liberal on the latter test, since
- the worst that happens if we get it wrong is we issue an error
- message. */
-
/* Try to parse the output constraint. If that fails, there's
no point in going further. */
- if (!parse_output_constraint (&constraints[i],
- i,
- ninputs,
- noutputs,
- &allows_mem,
- &allows_reg,
- &is_inout))
+ constraint = constraints[i];
+ if (!parse_output_constraint (&constraint, i, ninputs, noutputs,
+ &allows_mem, &allows_reg, &is_inout))
return;
+ if (! allows_reg
+ && (allows_mem
+ || is_inout
+ || (DECL_P (val)
+ && GET_CODE (DECL_RTL (val)) == REG
+ && GET_MODE (DECL_RTL (val)) != TYPE_MODE (type))))
+ (*lang_hooks.mark_addressable) (val);
+
+ if (is_inout)
+ ninout++;
+ }
+
+ ninputs += ninout;
+ if (ninputs + noutputs > MAX_RECOG_OPERANDS)
+ {
+ error ("more than %d operands in `asm'", MAX_RECOG_OPERANDS);
+ return;
+ }
+
+ for (i = 0, tail = inputs; tail; i++, tail = TREE_CHAIN (tail))
+ {
+ bool allows_reg, allows_mem;
+ const char *constraint;
+
+ /* If there's an erroneous arg, emit no insn, because the ASM_INPUT
+ would get VOIDmode and that could cause a crash in reload. */
+ if (TREE_TYPE (TREE_VALUE (tail)) == error_mark_node)
+ return;
+
+ constraint = constraints[i + noutputs];
+ if (! parse_input_constraint (&constraint, i, ninputs, noutputs, ninout,
+ constraints, &allows_mem, &allows_reg))
+ return;
+
+ if (! allows_reg && allows_mem)
+ (*lang_hooks.mark_addressable) (TREE_VALUE (tail));
+ }
+
+ /* Second pass evaluates arguments. */
+
+ ninout = 0;
+ for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
+ {
+ tree val = TREE_VALUE (tail);
+ tree type = TREE_TYPE (val);
+ bool is_inout;
+ bool allows_reg;
+ bool allows_mem;
+ rtx op;
+
+ if (!parse_output_constraint (&constraints[i], i, ninputs,
+ noutputs, &allows_mem, &allows_reg,
+ &is_inout))
+ abort ();
+
/* If an output operand is not a decl or indirect ref and our constraint
allows a register, make a temporary to act as an intermediate.
Make the asm insn write into that, then our caller will copy it to
|| ! allows_reg
|| is_inout)
{
- if (! allows_reg)
- mark_addressable (TREE_VALUE (tail));
-
- output_rtx[i]
- = expand_expr (TREE_VALUE (tail), NULL_RTX, VOIDmode,
- EXPAND_MEMORY_USE_WO);
+ op = expand_expr (val, NULL_RTX, VOIDmode, EXPAND_WRITE);
+ if (GET_CODE (op) == MEM)
+ op = validize_mem (op);
- if (! allows_reg && GET_CODE (output_rtx[i]) != MEM)
+ if (! allows_reg && GET_CODE (op) != MEM)
error ("output number %d not directly addressable", i);
- if ((! allows_mem && GET_CODE (output_rtx[i]) == MEM)
- || GET_CODE (output_rtx[i]) == CONCAT)
+ if ((! allows_mem && GET_CODE (op) == MEM)
+ || GET_CODE (op) == CONCAT)
{
- real_output_rtx[i] = protect_from_queue (output_rtx[i], 1);
- output_rtx[i] = gen_reg_rtx (GET_MODE (output_rtx[i]));
+ real_output_rtx[i] = protect_from_queue (op, 1);
+ op = gen_reg_rtx (GET_MODE (op));
if (is_inout)
- emit_move_insn (output_rtx[i], real_output_rtx[i]);
+ emit_move_insn (op, real_output_rtx[i]);
}
}
else
{
- output_rtx[i] = assign_temp (type, 0, 0, 1);
- TREE_VALUE (tail) = make_tree (type, output_rtx[i]);
+ op = assign_temp (type, 0, 0, 1);
+ op = validize_mem (op);
+ TREE_VALUE (tail) = make_tree (type, op);
}
+ output_rtx[i] = op;
generating_concat_p = old_generating_concat_p;
if (is_inout)
{
- inout_mode[ninout] = TYPE_MODE (TREE_TYPE (TREE_VALUE (tail)));
+ inout_mode[ninout] = TYPE_MODE (type);
inout_opnum[ninout++] = i;
}
- }
- ninputs += ninout;
- if (ninputs + noutputs > MAX_RECOG_OPERANDS)
- {
- error ("more than %d operands in `asm'", MAX_RECOG_OPERANDS);
- return;
+ if (decl_conflicts_with_clobbers_p (val, clobbered_regs))
+ clobber_conflict_found = 1;
}
/* Make vectors for the expression-rtx, constraint strings,
body = gen_rtx_ASM_OPERANDS ((noutputs == 0 ? VOIDmode
: GET_MODE (output_rtx[0])),
- TREE_STRING_POINTER (string),
+ TREE_STRING_POINTER (string),
empty_string, 0, argvec, constraintvec,
filename, line);
for (i = 0, tail = inputs; tail; tail = TREE_CHAIN (tail), ++i)
{
- int j;
- int allows_reg = 0, allows_mem = 0;
- const char *constraint, *orig_constraint;
- int c_len;
+ bool allows_reg, allows_mem;
+ const char *constraint;
+ tree val, type;
rtx op;
- /* If there's an erroneous arg, emit no insn,
- because the ASM_INPUT would get VOIDmode
- and that could cause a crash in reload. */
- if (TREE_TYPE (TREE_VALUE (tail)) == error_mark_node)
- return;
-
- /* ??? Can this happen, and does the error message make any sense? */
- if (TREE_PURPOSE (tail) == NULL_TREE)
- {
- error ("hard register `%s' listed as input operand to `asm'",
- TREE_STRING_POINTER (TREE_VALUE (tail)) );
- return;
- }
-
- orig_constraint = constraint = constraints[i + noutputs];
- c_len = strlen (constraint);
-
- /* Make sure constraint has neither `=', `+', nor '&'. */
-
- for (j = 0; j < c_len; j++)
- switch (constraint[j])
- {
- case '+': case '=': case '&':
- if (constraint == orig_constraint)
- {
- error ("input operand constraint contains `%c'",
- constraint[j]);
- return;
- }
- break;
-
- case '%':
- if (constraint == orig_constraint
- && i + 1 == ninputs - ninout)
- {
- error ("`%%' constraint used with last operand");
- return;
- }
- break;
-
- case 'V': case 'm': case 'o':
- allows_mem = 1;
- break;
-
- case '<': case '>':
- case '?': case '!': case '*': case '#':
- case 'E': case 'F': case 'G': case 'H':
- case 's': case 'i': case 'n':
- case 'I': case 'J': case 'K': case 'L': case 'M':
- case 'N': case 'O': case 'P': case ',':
- break;
-
- /* Whether or not a numeric constraint allows a register is
- decided by the matching constraint, and so there is no need
- to do anything special with them. We must handle them in
- the default case, so that we don't unnecessarily force
- operands to memory. */
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- {
- char *end;
- unsigned long match;
-
- match = strtoul (constraint + j, &end, 10);
- if (match >= (unsigned long) noutputs)
- {
- error ("matching constraint references invalid operand number");
- return;
- }
-
- /* Try and find the real constraint for this dup. Only do
- this if the matching constraint is the only alternative. */
- if (*end == '\0'
- && (j == 0 || (j == 1 && constraint[0] == '%')))
- {
- constraint = constraints[match];
- c_len = strlen (constraint);
- j = 0;
- break;
- }
- else
- j = end - constraint;
- }
- /* Fall through. */
-
- case 'p': case 'r':
- allows_reg = 1;
- break;
-
- case 'g': case 'X':
- allows_reg = 1;
- allows_mem = 1;
- break;
-
- default:
- if (! ISALPHA (constraint[j]))
- {
- error ("invalid punctuation `%c' in constraint",
- constraint[j]);
- return;
- }
- if (REG_CLASS_FROM_LETTER (constraint[j]) != NO_REGS)
- allows_reg = 1;
-#ifdef EXTRA_CONSTRAINT
- else
- {
- /* Otherwise we can't assume anything about the nature of
- the constraint except that it isn't purely registers.
- Treat it like "g" and hope for the best. */
- allows_reg = 1;
- allows_mem = 1;
- }
-#endif
- break;
- }
+ constraint = constraints[i + noutputs];
+ if (! parse_input_constraint (&constraint, i, ninputs, noutputs, ninout,
+ constraints, &allows_mem, &allows_reg))
+ abort ();
- if (! allows_reg && allows_mem)
- mark_addressable (TREE_VALUE (tail));
+ generating_concat_p = 0;
- op = expand_expr (TREE_VALUE (tail), NULL_RTX, VOIDmode, 0);
+ val = TREE_VALUE (tail);
+ type = TREE_TYPE (val);
+ op = expand_expr (val, NULL_RTX, VOIDmode, 0);
/* Never pass a CONCAT to an ASM. */
- generating_concat_p = 0;
if (GET_CODE (op) == CONCAT)
op = force_reg (GET_MODE (op), op);
+ else if (GET_CODE (op) == MEM)
+ op = validize_mem (op);
if (asm_operand_ok (op, constraint) <= 0)
{
if (allows_reg)
- op = force_reg (TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))), op);
+ op = force_reg (TYPE_MODE (type), op);
else if (!allows_mem)
warning ("asm operand %d probably doesn't match constraints",
i + noutputs);
else if (CONSTANT_P (op))
- op = force_const_mem (TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))),
- op);
+ {
+ op = force_const_mem (TYPE_MODE (type), op);
+ op = validize_mem (op);
+ }
else if (GET_CODE (op) == REG
|| GET_CODE (op) == SUBREG
|| GET_CODE (op) == ADDRESSOF
|| GET_CODE (op) == CONCAT)
{
- tree type = TREE_TYPE (TREE_VALUE (tail));
tree qual_type = build_qualified_type (type,
(TYPE_QUALS (type)
| TYPE_QUAL_CONST));
rtx memloc = assign_temp (qual_type, 1, 1, 1);
-
+ memloc = validize_mem (memloc);
emit_move_insn (memloc, op);
op = memloc;
}
else if (GET_CODE (op) == MEM && MEM_VOLATILE_P (op))
- /* We won't recognize volatile memory as available a
- memory_operand at this point. Ignore it. */
- ;
+ {
+ /* We won't recognize volatile memory as available a
+ memory_operand at this point. Ignore it. */
+ }
else if (queued_subexp_p (op))
;
else
warning ("asm operand %d probably doesn't match constraints",
i + noutputs);
}
+
generating_concat_p = old_generating_concat_p;
ASM_OPERANDS_INPUT (body, i) = op;
ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, i)
- = gen_rtx_ASM_INPUT (TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))),
- orig_constraint);
+ = gen_rtx_ASM_INPUT (TYPE_MODE (type), constraints[i + noutputs]);
+
+ if (decl_conflicts_with_clobbers_p (val, clobbered_regs))
+ clobber_conflict_found = 1;
}
/* Protect all the operands from the queue now that they have all been
if (noutputs == 1 && nclobbers == 0)
{
ASM_OPERANDS_OUTPUT_CONSTRAINT (body) = constraints[0];
- insn = emit_insn (gen_rtx_SET (VOIDmode, output_rtx[0], body));
+ emit_insn (gen_rtx_SET (VOIDmode, output_rtx[0], body));
}
else if (noutputs == 0 && nclobbers == 0)
{
/* No output operands: put in a raw ASM_OPERANDS rtx. */
- insn = emit_insn (body);
+ emit_insn (body);
}
else
{
const char *regname = TREE_STRING_POINTER (TREE_VALUE (tail));
int j = decode_reg_name (regname);
+ rtx clobbered_reg;
if (j < 0)
{
}
/* Use QImode since that's guaranteed to clobber just one reg. */
+ clobbered_reg = gen_rtx_REG (QImode, j);
+
+ /* Do sanity check for overlap between clobbers and respectively
+ input and outputs that hasn't been handled. Such overlap
+ should have been detected and reported above. */
+ if (!clobber_conflict_found)
+ {
+ int opno;
+
+ /* We test the old body (obody) contents to avoid tripping
+ over the under-construction body. */
+ for (opno = 0; opno < noutputs; opno++)
+ if (reg_overlap_mentioned_p (clobbered_reg, output_rtx[opno]))
+ internal_error ("asm clobber conflict with output operand");
+
+ for (opno = 0; opno < ninputs - ninout; opno++)
+ if (reg_overlap_mentioned_p (clobbered_reg,
+ ASM_OPERANDS_INPUT (obody, opno)))
+ internal_error ("asm clobber conflict with input operand");
+ }
+
XVECEXP (body, 0, i++)
- = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (QImode, j));
+ = gen_rtx_CLOBBER (VOIDmode, clobbered_reg);
}
- insn = emit_insn (body);
+ emit_insn (body);
}
/* For any outputs that needed reloading into registers, spill them
continue;
for (j = TREE_CHAIN (i); j ; j = TREE_CHAIN (j))
- if (i_name == TREE_PURPOSE (TREE_PURPOSE (j)))
+ if (simple_cst_equal (i_name, TREE_PURPOSE (TREE_PURPOSE (j))))
goto failure;
}
continue;
for (j = TREE_CHAIN (i); j ; j = TREE_CHAIN (j))
- if (i_name == TREE_PURPOSE (TREE_PURPOSE (j)))
+ if (simple_cst_equal (i_name, TREE_PURPOSE (TREE_PURPOSE (j))))
goto failure;
for (j = outputs; j ; j = TREE_CHAIN (j))
- if (i_name == TREE_PURPOSE (TREE_PURPOSE (j)))
+ if (simple_cst_equal (i_name, TREE_PURPOSE (TREE_PURPOSE (j))))
goto failure;
}
failure:
error ("duplicate asm operand name '%s'",
- IDENTIFIER_POINTER (TREE_PURPOSE (TREE_PURPOSE (i))));
+ TREE_STRING_POINTER (TREE_PURPOSE (TREE_PURPOSE (i))));
return false;
}
p = buffer;
while ((p = strchr (p, '%')) != NULL)
{
- if (*++p != '[')
- continue;
+ if (p[1] == '[')
+ p += 1;
+ else if (ISALPHA (p[1]) && p[2] == '[')
+ p += 2;
+ else
+ {
+ p += 1;
+ continue;
+ }
+
p = resolve_operand_name_1 (p, outputs, inputs);
}
/* A subroutine of resolve_operand_names. P points to the '[' for a
potential named operand of the form [<name>]. In place, replace
- the name and brackets with a number. Return a pointer to the
+ the name and brackets with a number. Return a pointer to the
balance of the string after substitution. */
static char *
/* Resolve the name to a number. */
for (op = 0, t = outputs; t ; t = TREE_CHAIN (t), op++)
{
- const char *c = IDENTIFIER_POINTER (TREE_PURPOSE (TREE_PURPOSE (t)));
- if (strncmp (c, p + 1, len) == 0 && c[len] == '\0')
- goto found;
+ 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;
+ }
}
for (t = inputs; t ; t = TREE_CHAIN (t), op++)
{
- const char *c = IDENTIFIER_POINTER (TREE_PURPOSE (TREE_PURPOSE (t)));
- if (strncmp (c, p + 1, len) == 0 && c[len] == '\0')
- goto found;
+ 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;
+ }
}
*q = '\0';
}
\f
/* Generate RTL to evaluate the expression EXP
- and remember it in case this is the VALUE in a ({... VALUE; }) constr. */
+ and remember it in case this is the VALUE in a ({... VALUE; }) constr.
+ Provided just for backward-compatibility. expand_expr_stmt_value()
+ should be used for new code. */
void
expand_expr_stmt (exp)
tree exp;
{
- /* If -W, warn about statements with no side effects,
+ expand_expr_stmt_value (exp, -1, 1);
+}
+
+/* Generate RTL to evaluate the expression EXP. WANT_VALUE tells
+ whether to (1) save the value of the expression, (0) discard it or
+ (-1) use expr_stmts_for_value to tell. The use of -1 is
+ deprecated, and retained only for backward compatibility. */
+
+void
+expand_expr_stmt_value (exp, want_value, maybe_last)
+ tree exp;
+ int want_value, maybe_last;
+{
+ rtx value;
+ tree type;
+
+ if (want_value == -1)
+ want_value = expr_stmts_for_value != 0;
+
+ /* If -Wextra, warn about statements with no side effects,
except for an explicit cast to void (e.g. for assert()), and
- except inside a ({...}) where they may be useful. */
- if (expr_stmts_for_value == 0 && exp != error_mark_node)
+ except for last statement in ({...}) where they may be useful. */
+ if (! want_value
+ && (expr_stmts_for_value == 0 || ! maybe_last)
+ && exp != error_mark_node)
{
if (! TREE_SIDE_EFFECTS (exp))
{
/* If EXP is of function type and we are expanding statements for
value, convert it to pointer-to-function. */
- if (expr_stmts_for_value && TREE_CODE (TREE_TYPE (exp)) == FUNCTION_TYPE)
+ if (want_value && TREE_CODE (TREE_TYPE (exp)) == FUNCTION_TYPE)
exp = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (exp)), exp);
/* The call to `expand_expr' could cause last_expr_type and
last_expr_value to get reset. Therefore, we set last_expr_value
and last_expr_type *after* calling expand_expr. */
- last_expr_value = expand_expr (exp,
- (expr_stmts_for_value
- ? NULL_RTX : const0_rtx),
- VOIDmode, 0);
- last_expr_type = TREE_TYPE (exp);
+ value = expand_expr (exp, want_value ? NULL_RTX : const0_rtx,
+ VOIDmode, 0);
+ type = TREE_TYPE (exp);
/* If all we do is reference a volatile value in memory,
copy it to a register to be sure it is actually touched. */
- if (last_expr_value != 0 && GET_CODE (last_expr_value) == MEM
- && TREE_THIS_VOLATILE (exp))
+ if (value && GET_CODE (value) == MEM && TREE_THIS_VOLATILE (exp))
{
- if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode)
+ if (TYPE_MODE (type) == VOIDmode)
;
- else if (TYPE_MODE (TREE_TYPE (exp)) != BLKmode)
- copy_to_reg (last_expr_value);
+ else if (TYPE_MODE (type) != BLKmode)
+ value = copy_to_reg (value);
else
{
rtx lab = gen_label_rtx ();
/* Compare the value with itself to reference it. */
- emit_cmp_and_jump_insns (last_expr_value, last_expr_value, EQ,
- expand_expr (TYPE_SIZE (last_expr_type),
+ emit_cmp_and_jump_insns (value, value, EQ,
+ expand_expr (TYPE_SIZE (type),
NULL_RTX, VOIDmode, 0),
BLKmode, 0, lab);
emit_label (lab);
/* If this expression is part of a ({...}) and is in memory, we may have
to preserve temporaries. */
- preserve_temp_slots (last_expr_value);
+ preserve_temp_slots (value);
/* Free any temporaries used to evaluate this expression. Any temporary
used as a result of this expression will already have been preserved
above. */
free_temp_slots ();
+ if (want_value)
+ {
+ last_expr_value = value;
+ last_expr_type = type;
+ }
+
emit_queue ();
}
if (VOID_TYPE_P (TREE_TYPE (exp)))
return 0;
- /* If this is an expression with side effects, don't warn. */
- if (TREE_SIDE_EFFECTS (exp))
- return 0;
-
switch (TREE_CODE (exp))
{
case PREINCREMENT_EXPR:
|| TREE_CODE (tem) == CALL_EXPR)
return 0;
}
- goto warn;
+ goto maybe_warn;
case INDIRECT_REF:
/* Don't warn about automatic dereferencing of references, since
&& TREE_CODE_LENGTH (TREE_CODE (exp)) == 0)
return 0;
- warn:
+ maybe_warn:
+ /* If this is an expression with side effects, don't warn. */
+ if (TREE_SIDE_EFFECTS (exp))
+ return 0;
+
warning_with_file_and_line (emit_filename, emit_lineno,
"value computed is not used");
return 1;
void
clear_last_expr ()
{
- last_expr_type = 0;
+ last_expr_type = NULL_TREE;
+ last_expr_value = NULL_RTX;
}
-/* Begin a statement which will return a value.
- Return the RTL_EXPR for this statement expr.
- The caller must save that value and pass it to expand_end_stmt_expr. */
+/* Begin a statement-expression, i.e., a series of statements which
+ may return a value. Return the RTL_EXPR for this statement expr.
+ The caller must save that value and pass it to
+ expand_end_stmt_expr. If HAS_SCOPE is nonzero, temporaries created
+ in the statement-expression are deallocated at the end of the
+ expression. */
tree
-expand_start_stmt_expr ()
+expand_start_stmt_expr (has_scope)
+ int has_scope;
{
tree t;
so that rtl_expr_chain doesn't become garbage. */
t = make_node (RTL_EXPR);
do_pending_stack_adjust ();
- start_sequence_for_rtl_expr (t);
+ if (has_scope)
+ start_sequence_for_rtl_expr (t);
+ else
+ start_sequence ();
NO_DEFER_POP;
expr_stmts_for_value++;
return t;
{
OK_DEFER_POP;
- if (last_expr_type == 0)
+ if (! last_expr_value || ! last_expr_type)
{
- last_expr_type = void_type_node;
last_expr_value = const0_rtx;
+ last_expr_type = void_type_node;
}
- else if (last_expr_value == 0)
- /* There are some cases where this can happen, such as when the
- statement is void type. */
- last_expr_value = const0_rtx;
else if (GET_CODE (last_expr_value) != REG && ! CONSTANT_P (last_expr_value))
/* Remove any possible QUEUED. */
last_expr_value = protect_from_queue (last_expr_value, 0);
/* Propagate volatility of the actual RTL expr. */
TREE_THIS_VOLATILE (t) = volatile_refs_p (last_expr_value);
- last_expr_type = 0;
+ clear_last_expr ();
expr_stmts_for_value--;
return t;
/* Make an entry on cond_stack for the cond we are entering. */
+ thiscond->desc = COND_NESTING;
thiscond->next = cond_stack;
thiscond->all = nesting_stack;
thiscond->depth = ++nesting_depth;
emit_label (thiscond->data.cond.endif_label);
POPSTACK (cond_stack);
- last_expr_type = 0;
+ clear_last_expr ();
}
\f
/* Generate RTL for the start of a loop. EXIT_FLAG is nonzero if this
/* Make an entry on loop_stack for the loop we are entering. */
+ thisloop->desc = LOOP_NESTING;
thisloop->next = loop_stack;
thisloop->all = nesting_stack;
thisloop->depth = ++nesting_depth;
thisloop->data.loop.start_label = gen_label_rtx ();
thisloop->data.loop.end_label = gen_label_rtx ();
- thisloop->data.loop.alt_end_label = 0;
thisloop->data.loop.continue_label = thisloop->data.loop.start_label;
thisloop->exit_label = exit_flag ? thisloop->data.loop.end_label : 0;
loop_stack = thisloop;
/* Make an entry on loop_stack for the loop we are entering. */
+ thisloop->desc = LOOP_NESTING;
thisloop->next = loop_stack;
thisloop->all = nesting_stack;
thisloop->depth = ++nesting_depth;
thisloop->data.loop.start_label = emit_note (NULL, NOTE_INSN_DELETED);
thisloop->data.loop.end_label = gen_label_rtx ();
- thisloop->data.loop.alt_end_label = NULL_RTX;
thisloop->data.loop.continue_label = thisloop->data.loop.end_label;
thisloop->exit_label = thisloop->data.loop.end_label;
loop_stack = thisloop;
return thisloop;
}
-/* Specify the continuation point for a loop started with
- expand_start_loop_continue_elsewhere.
- Use this at the point in the code to which a continue statement
- should jump. */
-
-void
-expand_loop_continue_here ()
-{
- do_pending_stack_adjust ();
- emit_note (NULL, NOTE_INSN_LOOP_CONT);
- emit_label (loop_stack->data.loop.continue_label);
-}
-
-/* Finish a loop. Generate a jump back to the top and the loop-exit label.
- Pop the block off of loop_stack. */
-
-void
-expand_end_loop ()
-{
- rtx start_label = loop_stack->data.loop.start_label;
- rtx insn = get_last_insn ();
- int needs_end_jump = 1;
-
- /* Mark the continue-point at the top of the loop if none elsewhere. */
- if (start_label == loop_stack->data.loop.continue_label)
- emit_note_before (NOTE_INSN_LOOP_CONT, start_label);
-
- do_pending_stack_adjust ();
-
- /* If optimizing, perhaps reorder the loop.
- First, try to use a condjump near the end.
- expand_exit_loop_if_false ends loops with unconditional jumps,
- like this:
-
- if (test) goto label;
- optional: cleanup
- goto loop_stack->data.loop.end_label
- barrier
- label:
-
- If we find such a pattern, we can end the loop earlier. */
-
- if (optimize
- && GET_CODE (insn) == CODE_LABEL
- && LABEL_NAME (insn) == NULL
- && GET_CODE (PREV_INSN (insn)) == BARRIER)
- {
- rtx label = insn;
- rtx jump = PREV_INSN (PREV_INSN (label));
-
- if (GET_CODE (jump) == JUMP_INSN
- && GET_CODE (PATTERN (jump)) == SET
- && SET_DEST (PATTERN (jump)) == pc_rtx
- && GET_CODE (SET_SRC (PATTERN (jump))) == LABEL_REF
- && (XEXP (SET_SRC (PATTERN (jump)), 0)
- == loop_stack->data.loop.end_label))
- {
- rtx prev;
-
- /* The test might be complex and reference LABEL multiple times,
- like the loop in loop_iterations to set vtop. To handle this,
- we move LABEL. */
- insn = PREV_INSN (label);
- reorder_insns (label, label, start_label);
-
- for (prev = PREV_INSN (jump);; prev = PREV_INSN (prev))
- {
- /* We ignore line number notes, but if we see any other note,
- in particular NOTE_INSN_BLOCK_*, NOTE_INSN_EH_REGION_*,
- NOTE_INSN_LOOP_*, we disable this optimization. */
- if (GET_CODE (prev) == NOTE)
- {
- if (NOTE_LINE_NUMBER (prev) < 0)
- break;
- continue;
- }
- if (GET_CODE (prev) == CODE_LABEL)
- break;
- if (GET_CODE (prev) == JUMP_INSN)
- {
- if (GET_CODE (PATTERN (prev)) == SET
- && SET_DEST (PATTERN (prev)) == pc_rtx
- && GET_CODE (SET_SRC (PATTERN (prev))) == IF_THEN_ELSE
- && (GET_CODE (XEXP (SET_SRC (PATTERN (prev)), 1))
- == LABEL_REF)
- && XEXP (XEXP (SET_SRC (PATTERN (prev)), 1), 0) == label)
- {
- XEXP (XEXP (SET_SRC (PATTERN (prev)), 1), 0)
- = start_label;
- emit_note_after (NOTE_INSN_LOOP_END, prev);
- needs_end_jump = 0;
- }
- break;
- }
- }
- }
- }
-
- /* If the loop starts with a loop exit, roll that to the end where
- it will optimize together with the jump back.
-
- We look for the conditional branch to the exit, except that once
- we find such a branch, we don't look past 30 instructions.
-
- In more detail, if the loop presently looks like this (in pseudo-C):
-
- start_label:
- if (test) goto end_label;
- body;
- goto start_label;
- end_label:
-
- transform it to look like:
-
- goto start_label;
- newstart_label:
- body;
- start_label:
- if (test) goto end_label;
- goto newstart_label;
- end_label:
-
- Here, the `test' may actually consist of some reasonably complex
- code, terminating in a test. */
-
- if (optimize
- && needs_end_jump
- &&
- ! (GET_CODE (insn) == JUMP_INSN
- && GET_CODE (PATTERN (insn)) == SET
- && SET_DEST (PATTERN (insn)) == pc_rtx
- && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE))
- {
- int eh_regions = 0;
- int num_insns = 0;
- rtx last_test_insn = NULL_RTX;
-
- /* Scan insns from the top of the loop looking for a qualified
- conditional exit. */
- for (insn = NEXT_INSN (loop_stack->data.loop.start_label); insn;
- insn = NEXT_INSN (insn))
- {
- if (GET_CODE (insn) == NOTE)
- {
- if (optimize < 2
- && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG
- || NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END))
- /* The code that actually moves the exit test will
- carefully leave BLOCK notes in their original
- location. That means, however, that we can't debug
- the exit test itself. So, we refuse to move code
- containing BLOCK notes at low optimization levels. */
- break;
-
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)
- ++eh_regions;
- else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END)
- {
- --eh_regions;
- if (eh_regions < 0)
- /* We've come to the end of an EH region, but
- never saw the beginning of that region. That
- means that an EH region begins before the top
- of the loop, and ends in the middle of it. The
- existence of such a situation violates a basic
- assumption in this code, since that would imply
- that even when EH_REGIONS is zero, we might
- move code out of an exception region. */
- abort ();
- }
+/* Specify the continuation point for a loop started with
+ expand_start_loop_continue_elsewhere.
+ Use this at the point in the code to which a continue statement
+ should jump. */
- /* We must not walk into a nested loop. */
- if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
- break;
+void
+expand_loop_continue_here ()
+{
+ do_pending_stack_adjust ();
+ emit_note (NULL, NOTE_INSN_LOOP_CONT);
+ emit_label (loop_stack->data.loop.continue_label);
+}
- /* We already know this INSN is a NOTE, so there's no
- point in looking at it to see if it's a JUMP. */
- continue;
- }
+/* Finish a loop. Generate a jump back to the top and the loop-exit label.
+ Pop the block off of loop_stack. */
- if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == INSN)
- num_insns++;
+void
+expand_end_loop ()
+{
+ rtx start_label = loop_stack->data.loop.start_label;
+ rtx etc_note;
+ int eh_regions, debug_blocks;
+ bool empty_test;
- if (last_test_insn && num_insns > 30)
- break;
+ /* Mark the continue-point at the top of the loop if none elsewhere. */
+ if (start_label == loop_stack->data.loop.continue_label)
+ emit_note_before (NOTE_INSN_LOOP_CONT, start_label);
- if (eh_regions > 0)
- /* We don't want to move a partial EH region. Consider:
-
- while ( ( { try {
- if (cond ()) 0;
- else {
- bar();
- 1;
- }
- } catch (...) {
- 1;
- } )) {
- body;
- }
+ do_pending_stack_adjust ();
- This isn't legal C++, but here's what it's supposed to
- mean: if cond() is true, stop looping. Otherwise,
- call bar, and keep looping. In addition, if cond
- throws an exception, catch it and keep looping. Such
- constructs are certainy legal in LISP.
+ /* If the loop starts with a loop exit, roll that to the end where
+ it will optimize together with the jump back.
- We should not move the `if (cond()) 0' test since then
- the EH-region for the try-block would be broken up.
- (In this case we would the EH_BEG note for the `try'
- and `if cond()' but not the call to bar() or the
- EH_END note.)
+ If the loop presently looks like this (in pseudo-C):
- So we don't look for tests within an EH region. */
- continue;
+ LOOP_BEG
+ start_label:
+ if (test) goto end_label;
+ LOOP_END_TOP_COND
+ body;
+ goto start_label;
+ end_label:
- if (GET_CODE (insn) == JUMP_INSN
- && GET_CODE (PATTERN (insn)) == SET
- && SET_DEST (PATTERN (insn)) == pc_rtx)
- {
- /* This is indeed a jump. */
- rtx dest1 = NULL_RTX;
- rtx dest2 = NULL_RTX;
- rtx potential_last_test;
- if (GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE)
- {
- /* A conditional jump. */
- dest1 = XEXP (SET_SRC (PATTERN (insn)), 1);
- dest2 = XEXP (SET_SRC (PATTERN (insn)), 2);
- potential_last_test = insn;
- }
- else
- {
- /* An unconditional jump. */
- dest1 = SET_SRC (PATTERN (insn));
- /* Include the BARRIER after the JUMP. */
- potential_last_test = NEXT_INSN (insn);
- }
+ transform it to look like:
- do {
- if (dest1 && GET_CODE (dest1) == LABEL_REF
- && ((XEXP (dest1, 0)
- == loop_stack->data.loop.alt_end_label)
- || (XEXP (dest1, 0)
- == loop_stack->data.loop.end_label)))
- {
- last_test_insn = potential_last_test;
- break;
- }
+ LOOP_BEG
+ goto start_label;
+ top_label:
+ body;
+ start_label:
+ if (test) goto end_label;
+ goto top_label;
+ end_label:
+
+ We rely on the presence of NOTE_INSN_LOOP_END_TOP_COND to mark
+ the end of the entry conditional. Without this, our lexical scan
+ can't tell the difference between an entry conditional and a
+ body conditional that exits the loop. Mistaking the two means
+ that we can misplace the NOTE_INSN_LOOP_CONT note, which can
+ screw up loop unrolling.
+
+ Things will be oh so much better when loop optimization is done
+ off of a proper control flow graph... */
+
+ /* Scan insns from the top of the loop looking for the END_TOP_COND note. */
+
+ empty_test = true;
+ eh_regions = debug_blocks = 0;
+ for (etc_note = start_label; etc_note ; etc_note = NEXT_INSN (etc_note))
+ if (GET_CODE (etc_note) == NOTE)
+ {
+ if (NOTE_LINE_NUMBER (etc_note) == NOTE_INSN_LOOP_END_TOP_COND)
+ break;
- /* If this was a conditional jump, there may be
- another label at which we should look. */
- dest1 = dest2;
- dest2 = NULL_RTX;
- } while (dest1);
- }
- }
+ /* We must not walk into a nested loop. */
+ else if (NOTE_LINE_NUMBER (etc_note) == NOTE_INSN_LOOP_BEG)
+ {
+ etc_note = NULL_RTX;
+ break;
+ }
+
+ /* At the same time, scan for EH region notes, as we don't want
+ to scrog region nesting. This shouldn't happen, but... */
+ else if (NOTE_LINE_NUMBER (etc_note) == NOTE_INSN_EH_REGION_BEG)
+ eh_regions++;
+ else if (NOTE_LINE_NUMBER (etc_note) == NOTE_INSN_EH_REGION_END)
+ {
+ if (--eh_regions < 0)
+ /* We've come to the end of an EH region, but never saw the
+ beginning of that region. That means that an EH region
+ begins before the top of the loop, and ends in the middle
+ of it. The existence of such a situation violates a basic
+ assumption in this code, since that would imply that even
+ when EH_REGIONS is zero, we might move code out of an
+ exception region. */
+ abort ();
+ }
- if (last_test_insn != 0 && last_test_insn != get_last_insn ())
+ /* Likewise for debug scopes. In this case we'll either (1) move
+ all of the notes if they are properly nested or (2) leave the
+ notes alone and only rotate the loop at high optimization
+ levels when we expect to scrog debug info. */
+ else if (NOTE_LINE_NUMBER (etc_note) == NOTE_INSN_BLOCK_BEG)
+ debug_blocks++;
+ else if (NOTE_LINE_NUMBER (etc_note) == NOTE_INSN_BLOCK_END)
+ debug_blocks--;
+ }
+ else if (INSN_P (etc_note))
+ empty_test = false;
+
+ if (etc_note
+ && optimize
+ && ! empty_test
+ && eh_regions == 0
+ && (debug_blocks == 0 || optimize >= 2)
+ && NEXT_INSN (etc_note) != NULL_RTX
+ && ! any_condjump_p (get_last_insn ()))
+ {
+ /* We found one. Move everything from START to ETC to the end
+ of the loop, and add a jump from the top of the loop. */
+ rtx top_label = gen_label_rtx ();
+ rtx start_move = start_label;
+
+ /* If the start label is preceded by a NOTE_INSN_LOOP_CONT note,
+ then we want to move this note also. */
+ if (GET_CODE (PREV_INSN (start_move)) == NOTE
+ && NOTE_LINE_NUMBER (PREV_INSN (start_move)) == NOTE_INSN_LOOP_CONT)
+ start_move = PREV_INSN (start_move);
+
+ emit_label_before (top_label, start_move);
+
+ /* Actually move the insns. If the debug scopes are nested, we
+ can move everything at once. Otherwise we have to move them
+ one by one and squeeze out the block notes. */
+ if (debug_blocks == 0)
+ reorder_insns (start_move, etc_note, get_last_insn ());
+ else
{
- /* We found one. Move everything from there up
- to the end of the loop, and add a jump into the loop
- to jump to there. */
- rtx newstart_label = gen_label_rtx ();
- rtx start_move = start_label;
- rtx next_insn;
-
- /* If the start label is preceded by a NOTE_INSN_LOOP_CONT note,
- then we want to move this note also. */
- if (GET_CODE (PREV_INSN (start_move)) == NOTE
- && (NOTE_LINE_NUMBER (PREV_INSN (start_move))
- == NOTE_INSN_LOOP_CONT))
- start_move = PREV_INSN (start_move);
-
- emit_label_after (newstart_label, PREV_INSN (start_move));
-
- /* Actually move the insns. Start at the beginning, and
- keep copying insns until we've copied the
- last_test_insn. */
+ rtx insn, next_insn;
for (insn = start_move; insn; insn = next_insn)
{
/* Figure out which insn comes after this one. We have
to do this before we move INSN. */
- if (insn == last_test_insn)
- /* We've moved all the insns. */
- next_insn = NULL_RTX;
- else
- next_insn = NEXT_INSN (insn);
+ next_insn = (insn == etc_note ? NULL : NEXT_INSN (insn));
if (GET_CODE (insn) == NOTE
&& (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END))
- /* We don't want to move NOTE_INSN_BLOCK_BEGs or
- NOTE_INSN_BLOCK_ENDs because the correct generation
- of debugging information depends on these appearing
- in the same order in the RTL and in the tree
- structure, where they are represented as BLOCKs.
- So, we don't move block notes. Of course, moving
- the code inside the block is likely to make it
- impossible to debug the instructions in the exit
- test, but such is the price of optimization. */
continue;
- /* Move the INSN. */
reorder_insns (insn, insn, get_last_insn ());
}
-
- emit_jump_insn_after (gen_jump (start_label),
- PREV_INSN (newstart_label));
- emit_barrier_after (PREV_INSN (newstart_label));
- start_label = newstart_label;
}
- }
- if (needs_end_jump)
- {
- emit_jump (start_label);
- emit_note (NULL, NOTE_INSN_LOOP_END);
+ /* Add the jump from the top of the loop. */
+ emit_jump_insn_before (gen_jump (start_label), top_label);
+ emit_barrier_before (top_label);
+ start_label = top_label;
}
+
+ emit_jump (start_label);
+ emit_note (NULL, NOTE_INSN_LOOP_END);
emit_label (loop_stack->data.loop.end_label);
POPSTACK (loop_stack);
- last_expr_type = 0;
+ clear_last_expr ();
}
/* Finish a null loop, aka do { } while (0). */
POPSTACK (loop_stack);
- last_expr_type = 0;
+ clear_last_expr ();
}
/* Generate a jump to the current loop's continue-point.
expand_continue_loop (whichloop)
struct nesting *whichloop;
{
- last_expr_type = 0;
+ /* Emit information for branch prediction. */
+ rtx note;
+
+ note = emit_note (NULL, NOTE_INSN_PREDICTION);
+ NOTE_PREDICTION (note) = NOTE_PREDICT (PRED_CONTINUE, IS_TAKEN);
+ clear_last_expr ();
if (whichloop == 0)
whichloop = loop_stack;
if (whichloop == 0)
expand_exit_loop (whichloop)
struct nesting *whichloop;
{
- last_expr_type = 0;
+ clear_last_expr ();
if (whichloop == 0)
whichloop = loop_stack;
if (whichloop == 0)
struct nesting *whichloop;
tree cond;
{
- rtx label = gen_label_rtx ();
- rtx last_insn;
- last_expr_type = 0;
+ rtx label;
+ clear_last_expr ();
if (whichloop == 0)
whichloop = loop_stack;
if (whichloop == 0)
return 0;
+
+ if (integer_nonzerop (cond))
+ return 1;
+ if (integer_zerop (cond))
+ return expand_exit_loop (whichloop);
+
+ /* Check if we definitely won't need a fixup. */
+ if (whichloop == nesting_stack)
+ {
+ jumpifnot (cond, whichloop->data.loop.end_label);
+ return 1;
+ }
+
/* In order to handle fixups, we actually create a conditional jump
around an unconditional branch to exit the loop. If fixups are
necessary, they go before the unconditional branch. */
- do_jump (cond, NULL_RTX, label);
- last_insn = get_last_insn ();
- if (GET_CODE (last_insn) == CODE_LABEL)
- whichloop->data.loop.alt_end_label = last_insn;
+ label = gen_label_rtx ();
+ jumpif (cond, label);
expand_goto_internal (NULL_TREE, whichloop->data.loop.end_label,
NULL_RTX);
emit_label (label);
return 1;
}
-/* Return nonzero if the loop nest is empty. Else return zero. */
+/* Like expand_exit_loop_if_false except also emit a note marking
+ the end of the conditional. Should only be used immediately
+ after expand_loop_start. */
int
-stmt_loop_nest_empty ()
+expand_exit_loop_top_cond (whichloop, cond)
+ struct nesting *whichloop;
+ tree cond;
{
- /* cfun->stmt can be NULL if we are building a call to get the
- EH context for a setjmp/longjmp EH target and the current
- function was a deferred inline function. */
- return (cfun->stmt == NULL || loop_stack == NULL);
+ if (! expand_exit_loop_if_false (whichloop, cond))
+ return 0;
+
+ emit_note (NULL, NOTE_INSN_LOOP_END_TOP_COND);
+ return 1;
}
-/* Return non-zero if we should preserve sub-expressions as separate
+/* Return nonzero if we should preserve sub-expressions as separate
pseudos. We never do so if we aren't optimizing. We always do so
if -fexpensive-optimizations.
expand_exit_something ()
{
struct nesting *n;
- last_expr_type = 0;
+ clear_last_expr ();
for (n = nesting_stack; n; n = n->all)
if (n->exit_label != 0)
{
void
expand_null_return ()
{
- rtx last_insn = get_last_insn ();
+ rtx last_insn;
+
+ last_insn = get_last_insn ();
/* If this function was declared to return a value, but we
didn't, clobber the return registers so that they are not
expand_null_return_1 (last_insn);
}
+/* Try to guess whether the value of return means error code. */
+static enum br_predictor
+return_prediction (val)
+ rtx val;
+{
+ /* Different heuristics for pointers and scalars. */
+ if (POINTER_TYPE_P (TREE_TYPE (DECL_RESULT (current_function_decl))))
+ {
+ /* NULL is usually not returned. */
+ if (val == const0_rtx)
+ return PRED_NULL_RETURN;
+ }
+ else
+ {
+ /* Negative return values are often used to indicate
+ errors. */
+ if (GET_CODE (val) == CONST_INT
+ && INTVAL (val) < 0)
+ return PRED_NEGATIVE_RETURN;
+ /* Constant return values are also usually erors,
+ zero/one often mean booleans so exclude them from the
+ heuristics. */
+ if (CONSTANT_P (val)
+ && (val != const0_rtx && val != const1_rtx))
+ return PRED_CONST_RETURN;
+ }
+ return PRED_NO_PREDICTION;
+}
+
/* Generate RTL to return from the current function, with value VAL. */
static void
expand_value_return (val)
rtx val;
{
- rtx last_insn = get_last_insn ();
- rtx return_reg = DECL_RTL (DECL_RESULT (current_function_decl));
+ rtx last_insn;
+ rtx return_reg;
+ enum br_predictor pred;
+
+ if ((pred = return_prediction (val)) != PRED_NO_PREDICTION)
+ {
+ /* Emit information for branch prediction. */
+ rtx note;
+
+ note = emit_note (NULL, NOTE_INSN_PREDICTION);
+
+ NOTE_PREDICTION (note) = NOTE_PREDICT (pred, NOT_TAKEN);
+
+ }
+
+ last_insn = get_last_insn ();
+ return_reg = DECL_RTL (DECL_RESULT (current_function_decl));
/* Copy the value to the return location
unless it's already there. */
clear_pending_stack_adjust ();
do_pending_stack_adjust ();
- last_expr_type = 0;
+ clear_last_expr ();
if (end_label == 0)
end_label = return_label = gen_label_rtx ();
/* Treat this like a return of no value from a function that
returns a value. */
expand_null_return ();
- return;
+ return;
}
else if (TREE_CODE (retval) == RESULT_DECL)
retval_rhs = retval;
to the least significant byte (to the right). On a BYTES_BIG_ENDIAN
machine, this means we must skip the empty high order bytes when
calculating the bit offset. */
- if (BYTES_BIG_ENDIAN && bytes % UNITS_PER_WORD)
+ if (BYTES_BIG_ENDIAN
+ && bytes % UNITS_PER_WORD)
big_endian_correction = (BITS_PER_WORD - ((bytes % UNITS_PER_WORD)
* BITS_PER_UNIT));
dst = gen_reg_rtx (word_mode);
result_pseudos[xbitpos / BITS_PER_WORD] = dst;
- /* Clobber the destination before we move anything into it. */
- emit_insn (gen_rtx_CLOBBER (VOIDmode, dst));
+ /* Clear the destination before we move anything into it. */
+ emit_move_insn (dst, CONST0_RTX (GET_MODE (dst)));
}
/* We need a new source operand each time bitpos is on a word
expand_value_return (result_rtl);
}
}
-
-/* Return 1 if the end of the generated RTX is not a barrier.
- This means code already compiled can drop through. */
-
-int
-drop_through_at_end_p ()
-{
- rtx insn = get_last_insn ();
- while (insn && GET_CODE (insn) == NOTE)
- insn = PREV_INSN (insn);
- return insn && GET_CODE (insn) != BARRIER;
-}
\f
/* Attempt to optimize a potential tail recursion call into a goto.
ARGUMENTS are the arguments to a CALL_EXPR; LAST_INSN indicates
if (GET_MODE (DECL_RTL (f)) == GET_MODE (argvec[i]))
emit_move_insn (DECL_RTL (f), argvec[i]);
else
- convert_move (DECL_RTL (f), argvec[i],
- TREE_UNSIGNED (TREE_TYPE (TREE_VALUE (a))));
+ {
+ rtx tmp = argvec[i];
+
+ if (DECL_MODE (f) != GET_MODE (DECL_RTL (f)))
+ {
+ tmp = gen_reg_rtx (DECL_MODE (f));
+ convert_move (tmp, argvec[i],
+ TREE_UNSIGNED (TREE_TYPE (TREE_VALUE (a))));
+ }
+ convert_move (DECL_RTL (f), tmp,
+ TREE_UNSIGNED (TREE_TYPE (TREE_VALUE (a))));
+ }
}
free_temp_slots ();
/* Make an entry on block_stack for the block we are entering. */
+ thisblock->desc = BLOCK_NESTING;
thisblock->next = block_stack;
thisblock->all = nesting_stack;
thisblock->depth = ++nesting_depth;
instructions inserted after the last unconditional cleanup are
never the last instruction. */
emit_note (NULL, NOTE_INSN_DELETED);
- thisblock->data.block.cleanup_ptr = &thisblock->data.block.cleanups;
if (block_stack
&& !(block_stack->data.block.cleanups == NULL_TREE
pop_temp_slots ();
}
-/* Given a pointer to a BLOCK node return non-zero if (and only if) the node
+/* 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.
emit_move_insn (slot, gen_rtx_LABEL_REF (Pmode, handler_label));
insns = get_insns ();
end_sequence ();
- emit_insns_before (insns, before_insn);
+ emit_insn_before (insns, before_insn);
emit_label (handler_label);
emit_move_insn (save_receiver, XEXP (slot, 0));
insns = get_insns ();
end_sequence ();
- emit_insns_before (insns, thisblock->data.block.first_insn);
+ emit_insn_before (insns, thisblock->data.block.first_insn);
}
/* Jump around the handlers; they run only when specially invoked. */
if (any_invalid)
{
expand_nl_goto_receiver ();
- emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "abort"), LCT_NORETURN,
- VOIDmode, 0);
- emit_barrier ();
+ expand_builtin_trap ();
}
nonlocal_goto_handler_labels = label_list;
expand_decl (decl)
tree decl;
{
- struct nesting *thisblock;
tree type;
type = TREE_TYPE (decl);
if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
return;
- thisblock = block_stack;
-
/* Create the RTL representation for the variable. */
if (type == error_mark_node)
&& !(flag_float_store
&& TREE_CODE (type) == REAL_TYPE)
&& ! TREE_THIS_VOLATILE (decl)
- && (DECL_REGISTER (decl) || optimize)
- /* if -fcheck-memory-usage, check all variables. */
- && ! current_function_check_memory_usage)
+ && (DECL_REGISTER (decl) || optimize))
{
/* Automatic variable that can go in a register. */
int unsignedp = TREE_UNSIGNED (type);
SET_DECL_RTL (decl, gen_reg_rtx (reg_mode));
- if (GET_CODE (DECL_RTL (decl)) == REG)
- REGNO_DECL (REGNO (DECL_RTL (decl))) = decl;
- else if (GET_CODE (DECL_RTL (decl)) == CONCAT)
- {
- REGNO_DECL (REGNO (XEXP (DECL_RTL (decl), 0))) = decl;
- REGNO_DECL (REGNO (XEXP (DECL_RTL (decl), 1))) = decl;
- }
-
mark_user_reg (DECL_RTL (decl));
if (POINTER_TYPE_P (type))
: GET_MODE_BITSIZE (DECL_MODE (decl)));
DECL_USER_ALIGN (decl) = 0;
- x = assign_temp (TREE_TYPE (decl), 1, 1, 1);
+ x = assign_temp (decl, 1, 1, 1);
set_mem_attributes (x, decl, 1);
SET_DECL_RTL (decl, x);
{
int was_used = TREE_USED (decl);
- /* If this is a CONST_DECL, we don't have to generate any code, but
- if DECL_INITIAL is a constant, call expand_expr to force TREE_CST_RTL
- to be set while in the obstack containing the constant. If we don't
- do this, we can lose if we have functions nested three deep and the middle
- function makes a CONST_DECL whose DECL_INITIAL is a STRING_CST while
- the innermost function is the first to expand that STRING_CST. */
- if (TREE_CODE (decl) == CONST_DECL)
- {
- if (DECL_INITIAL (decl) && TREE_CONSTANT (DECL_INITIAL (decl)))
- expand_expr (DECL_INITIAL (decl), NULL_RTX, VOIDmode,
- EXPAND_INITIALIZER);
- return;
- }
-
- if (TREE_STATIC (decl))
+ /* If this is a CONST_DECL, we don't have to generate any code. Likewise
+ for static decls. */
+ if (TREE_CODE (decl) == CONST_DECL
+ || TREE_STATIC (decl))
return;
/* Compute and store the initial value now. */
end_sequence ();
thisblock->data.block.last_unconditional_cleanup
- = emit_insns_after (set_flag_0,
+ = emit_insn_after (set_flag_0,
thisblock->data.block.last_unconditional_cleanup);
emit_move_insn (flag, const1_rtx);
- cond = build_decl (VAR_DECL, NULL_TREE, type_for_mode (word_mode, 1));
+ cond = build_decl (VAR_DECL, NULL_TREE,
+ (*lang_hooks.types.type_for_mode) (word_mode, 1));
SET_DECL_RTL (cond, flag);
/* Conditionalize the cleanup. */
cleanup = build (COND_EXPR, void_type_node,
- truthvalue_conversion (cond),
+ (*lang_hooks.truthvalue_conversion) (cond),
cleanup, integer_zero_node);
cleanup = fold (cleanup);
- cleanups = thisblock->data.block.cleanup_ptr;
+ cleanups = &thisblock->data.block.cleanups;
}
cleanup = unsave_expr (cleanup);
end_sequence ();
if (seq)
thisblock->data.block.last_unconditional_cleanup
- = emit_insns_after (seq,
- thisblock->data.block.last_unconditional_cleanup);
+ = emit_insn_after (seq,
+ thisblock->data.block.last_unconditional_cleanup);
}
else
{
instructions inserted after the last unconditional cleanup are
never the last instruction. */
emit_note (NULL, NOTE_INSN_DELETED);
- thisblock->data.block.cleanup_ptr = &thisblock->data.block.cleanups;
}
}
return 1;
}
+
+/* Like expand_decl_cleanup, but maybe only run the cleanup if an exception
+ is thrown. */
+
+int
+expand_decl_cleanup_eh (decl, cleanup, eh_only)
+ tree decl, cleanup;
+ int eh_only;
+{
+ int ret = expand_decl_cleanup (decl, cleanup);
+ if (cleanup && ret)
+ {
+ tree node = block_stack->data.block.cleanups;
+ CLEANUP_EH_ONLY (node) = eh_only;
+ }
+ return ret;
+}
\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.
tree cleanup_elt = TREE_PURPOSE (t);
enum machine_mode mode = TYPE_MODE (TREE_TYPE (decl_elt));
+ /* 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);
This is sometimes used to avoid a cleanup associated with
a value that is being returned out of the scope.
- If IN_FIXUP is non-zero, we are generating this cleanup for a fixup
+ If IN_FIXUP is nonzero, we are generating this cleanup for a fixup
goto and handle protection regions specially in that case.
If REACHABLE, we emit code, otherwise just inform the exception handling
if (! in_fixup && using_eh_for_cleanups_p)
expand_eh_region_end_cleanup (TREE_VALUE (tail));
- if (reachable)
+ if (reachable && !CLEANUP_EH_ONLY (tail))
{
/* Cleanups may be run multiple times. For example,
when exiting a binding contour, we expand the
--block_stack->data.block.conditional_code;
}
-/* Move all cleanups from the current block_stack
- to the containing block_stack, where they are assumed to
- have been created. If anything can cause a temporary to
- be created, but not expanded for more than one level of
- block_stacks, then this code will have to change. */
-
-void
-move_cleanups_up ()
-{
- struct nesting *block = block_stack;
- struct nesting *outer = block->next;
-
- outer->data.block.cleanups
- = chainon (block->data.block.cleanups,
- outer->data.block.cleanups);
- block->data.block.cleanups = 0;
-}
-
tree
last_cleanup_this_contour ()
{
/* Make an entry on case_stack for the case we are entering. */
+ thiscase->desc = CASE_NESTING;
thiscase->next = case_stack;
thiscase->all = nesting_stack;
thiscase->depth = ++nesting_depth;
/* Make an entry on case_stack for the dummy. */
+ thiscase->desc = CASE_NESTING;
thiscase->next = case_stack;
thiscase->all = nesting_stack;
thiscase->depth = ++nesting_depth;
nesting_stack = thiscase;
start_cleanup_deferral ();
}
-
-/* End a dummy case statement. */
-
-void
-expand_end_case_dummy ()
-{
- end_cleanup_deferral ();
- POPSTACK (case_stack);
-}
-
-/* Return the data type of the index-expression
- of the innermost case statement, or null if none. */
-
-tree
-case_index_expr_type ()
-{
- if (case_stack)
- return TREE_TYPE (case_stack->data.case_stmt.index_expr);
- return 0;
-}
\f
static void
check_seenlabel ()
/* Add this label to the chain, and succeed. */
- r = (struct case_node *) xmalloc (sizeof (struct case_node));
+ r = (struct case_node *) ggc_alloc (sizeof (struct case_node));
r->low = low;
/* If the bounds are equal, turn this into the one-value case. */
/* Returns the number of possible values of TYPE.
Returns -1 if the number is unknown, variable, or if the number does not
fit in a HOST_WIDE_INT.
- Sets *SPARENESS to 2 if TYPE is an ENUMERAL_TYPE whose values
+ Sets *SPARSENESS to 2 if TYPE is an ENUMERAL_TYPE whose values
do not increase monotonically (there may be duplicates);
to 1 if the values increase monotonically, but not always by 1;
otherwise sets it to 0. */
HOST_WIDE_INT
-all_cases_count (type, spareness)
+all_cases_count (type, sparseness)
tree type;
- int *spareness;
+ int *sparseness;
{
tree t;
HOST_WIDE_INT count, minval, lastval;
- *spareness = 0;
+ *sparseness = 0;
switch (TREE_CODE (type))
{
{
HOST_WIDE_INT thisval = tree_low_cst (TREE_VALUE (t), 0);
- if (*spareness == 2 || thisval < lastval)
- *spareness = 2;
+ if (*sparseness == 2 || thisval <= lastval)
+ *sparseness = 2;
else if (thisval != minval + count)
- *spareness = 1;
+ *sparseness = 1;
+ lastval = thisval;
count++;
}
}
}
}
-/* Called when the index of a switch statement is an enumerated type
- and there is no default label.
+/* Given a switch statement with an expression that is an enumeration
+ type, warn if any of the enumeration type's literals are not
+ covered by the case expressions of the switch. Also, warn if there
+ are any extra switch cases that are *not* elements of the
+ enumerated type.
+
+ Historical note:
- Checks that all enumeration literals are covered by the case
- expressions of a switch. Also, warn if there are any extra
- switch cases that are *not* elements of the enumerated type.
+ At one stage this function would: ``If all enumeration literals
+ were covered by the case expressions, turn one of the expressions
+ into the default expression since it should not be possible to fall
+ through such a switch.''
- If all enumeration literals were covered by the case expressions,
- turn one of the expressions into the default expression since it should
- not be possible to fall through such a switch. */
+ That code has since been removed as: ``This optimization is
+ disabled because it causes valid programs to fail. ANSI C does not
+ guarantee that an expression with enum type will have a value that
+ is the same as one of the enumeration literals.'' */
void
check_for_full_enumeration_handling (type)
/* The allocated size of cases_seen, in chars. */
HOST_WIDE_INT bytes_needed;
- if (! warn_switch)
- return;
-
size = all_cases_count (type, &sparseness);
bytes_needed = (size + HOST_BITS_PER_CHAR) / HOST_BITS_PER_CHAR;
/* The time complexity of this code is normally O(N), where
N being the number of members in the enumerated type.
- However, if type is a ENUMERAL_TYPE whose values do not
+ However, if type is an ENUMERAL_TYPE whose values do not
increase monotonically, O(N*log(N)) time may be needed. */
mark_seen_cases (type, cases_seen, size, sparseness);
&& case_stack->data.case_stmt.case_list->left)
case_stack->data.case_stmt.case_list
= case_tree2list (case_stack->data.case_stmt.case_list, 0);
- if (warn_switch)
- for (n = case_stack->data.case_stmt.case_list; n; n = n->right)
- {
- for (chain = TYPE_VALUES (type);
- chain && !tree_int_cst_equal (n->low, TREE_VALUE (chain));
- chain = TREE_CHAIN (chain))
- ;
+ for (n = case_stack->data.case_stmt.case_list; n; n = n->right)
+ {
+ for (chain = TYPE_VALUES (type);
+ chain && !tree_int_cst_equal (n->low, TREE_VALUE (chain));
+ chain = TREE_CHAIN (chain))
+ ;
- if (!chain)
- {
- if (TYPE_NAME (type) == 0)
- warning ("case value `%ld' not in enumerated type",
- (long) TREE_INT_CST_LOW (n->low));
- else
- warning ("case value `%ld' not in enumerated type `%s'",
- (long) TREE_INT_CST_LOW (n->low),
- IDENTIFIER_POINTER ((TREE_CODE (TYPE_NAME (type))
- == IDENTIFIER_NODE)
- ? TYPE_NAME (type)
- : DECL_NAME (TYPE_NAME (type))));
- }
- if (!tree_int_cst_equal (n->low, n->high))
- {
- for (chain = TYPE_VALUES (type);
- chain && !tree_int_cst_equal (n->high, TREE_VALUE (chain));
- chain = TREE_CHAIN (chain))
- ;
+ if (!chain)
+ {
+ if (TYPE_NAME (type) == 0)
+ warning ("case value `%ld' not in enumerated type",
+ (long) TREE_INT_CST_LOW (n->low));
+ else
+ warning ("case value `%ld' not in enumerated type `%s'",
+ (long) TREE_INT_CST_LOW (n->low),
+ IDENTIFIER_POINTER ((TREE_CODE (TYPE_NAME (type))
+ == IDENTIFIER_NODE)
+ ? TYPE_NAME (type)
+ : DECL_NAME (TYPE_NAME (type))));
+ }
+ if (!tree_int_cst_equal (n->low, n->high))
+ {
+ for (chain = TYPE_VALUES (type);
+ chain && !tree_int_cst_equal (n->high, TREE_VALUE (chain));
+ chain = TREE_CHAIN (chain))
+ ;
- if (!chain)
- {
- if (TYPE_NAME (type) == 0)
- warning ("case value `%ld' not in enumerated type",
- (long) TREE_INT_CST_LOW (n->high));
- else
- warning ("case value `%ld' not in enumerated type `%s'",
- (long) TREE_INT_CST_LOW (n->high),
- IDENTIFIER_POINTER ((TREE_CODE (TYPE_NAME (type))
- == IDENTIFIER_NODE)
- ? TYPE_NAME (type)
- : DECL_NAME (TYPE_NAME (type))));
- }
- }
- }
+ if (!chain)
+ {
+ if (TYPE_NAME (type) == 0)
+ warning ("case value `%ld' not in enumerated type",
+ (long) TREE_INT_CST_LOW (n->high));
+ else
+ warning ("case value `%ld' not in enumerated type `%s'",
+ (long) TREE_INT_CST_LOW (n->high),
+ IDENTIFIER_POINTER ((TREE_CODE (TYPE_NAME (type))
+ == IDENTIFIER_NODE)
+ ? TYPE_NAME (type)
+ : DECL_NAME (TYPE_NAME (type))));
+ }
+ }
+ }
}
-/* Free CN, and its children. */
+\f
+/* Maximum number of case bit tests. */
+#define MAX_CASE_BIT_TESTS 3
+
+/* By default, enable case bit tests on targets with ashlsi3. */
+#ifndef CASE_USE_BIT_TESTS
+#define CASE_USE_BIT_TESTS (ashl_optab->handlers[word_mode].insn_code \
+ != CODE_FOR_nothing)
+#endif
+
+
+/* A case_bit_test represents a set of case nodes that may be
+ selected from using a bit-wise comparison. HI and LO hold
+ the integer to be tested against, LABEL contains the label
+ to jump to upon success and BITS counts the number of case
+ nodes handled by this test, typically the number of bits
+ set in HI:LO. */
+
+struct case_bit_test
+{
+ HOST_WIDE_INT hi;
+ HOST_WIDE_INT lo;
+ rtx label;
+ int bits;
+};
+
+/* Determine whether "1 << x" is relatively cheap in word_mode. */
-static void
-free_case_nodes (cn)
- case_node_ptr cn;
+static bool lshift_cheap_p ()
{
- if (cn)
+ static bool init = false;
+ static bool cheap = true;
+
+ if (!init)
{
- free_case_nodes (cn->left);
- free_case_nodes (cn->right);
- free (cn);
+ rtx reg = gen_rtx_REG (word_mode, 10000);
+ int cost = rtx_cost (gen_rtx_ASHIFT (word_mode, const1_rtx, reg), SET);
+ cheap = cost < COSTS_N_INSNS (3);
+ init = true;
}
+
+ return cheap;
}
-\f
+/* Comparison function for qsort to order bit tests by decreasing
+ number of case nodes, i.e. the node with the most cases gets
+ tested first. */
+
+static int case_bit_test_cmp (p1, p2)
+ const void *p1;
+ const void *p2;
+{
+ const struct case_bit_test *d1 = p1;
+ const struct case_bit_test *d2 = p2;
+
+ return d2->bits - d1->bits;
+}
+
+/* Expand a switch statement by a short sequence of bit-wise
+ comparisons. "switch(x)" is effectively converted into
+ "if ((1 << (x-MINVAL)) & CST)" where CST and MINVAL are
+ integer constants.
+
+ INDEX_EXPR is the value being switched on, which is of
+ type INDEX_TYPE. MINVAL is the lowest case value of in
+ the case nodes, of INDEX_TYPE type, and RANGE is highest
+ value minus MINVAL, also of type INDEX_TYPE. NODES is
+ the set of case nodes, and DEFAULT_LABEL is the label to
+ branch to should none of the cases match.
+
+ There *MUST* be MAX_CASE_BIT_TESTS or less unique case
+ node targets. */
+
+static void
+emit_case_bit_tests (index_type, index_expr, minval, range,
+ nodes, default_label)
+ tree index_type, index_expr, minval, range;
+ case_node_ptr nodes;
+ rtx default_label;
+{
+ struct case_bit_test test[MAX_CASE_BIT_TESTS];
+ enum machine_mode mode;
+ rtx expr, index, label;
+ unsigned int i,j,lo,hi;
+ struct case_node *n;
+ unsigned int count;
+
+ count = 0;
+ for (n = nodes; n; n = n->right)
+ {
+ label = label_rtx (n->code_label);
+ for (i = 0; i < count; i++)
+ if (same_case_target_p (label, test[i].label))
+ break;
+
+ if (i == count)
+ {
+ if (count >= MAX_CASE_BIT_TESTS)
+ abort ();
+ test[i].hi = 0;
+ test[i].lo = 0;
+ test[i].label = label;
+ test[i].bits = 1;
+ count++;
+ }
+ else
+ test[i].bits++;
+
+ lo = tree_low_cst (fold (build (MINUS_EXPR, index_type,
+ n->low, minval)), 1);
+ hi = tree_low_cst (fold (build (MINUS_EXPR, index_type,
+ n->high, minval)), 1);
+ for (j = lo; j <= hi; j++)
+ if (j >= HOST_BITS_PER_WIDE_INT)
+ test[i].hi |= (HOST_WIDE_INT) 1 << (j - HOST_BITS_PER_INT);
+ else
+ test[i].lo |= (HOST_WIDE_INT) 1 << j;
+ }
+
+ qsort (test, count, sizeof(*test), case_bit_test_cmp);
+
+ index_expr = fold (build (MINUS_EXPR, index_type,
+ convert (index_type, index_expr),
+ convert (index_type, minval)));
+ index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0);
+ emit_queue ();
+ index = protect_from_queue (index, 0);
+ do_pending_stack_adjust ();
+
+ mode = TYPE_MODE (index_type);
+ expr = expand_expr (range, NULL_RTX, VOIDmode, 0);
+ emit_cmp_and_jump_insns (index, expr, GTU, NULL_RTX, mode, 1,
+ default_label);
+
+ index = convert_to_mode (word_mode, index, 0);
+ index = expand_binop (word_mode, ashl_optab, const1_rtx,
+ index, NULL_RTX, 1, OPTAB_WIDEN);
+
+ for (i = 0; i < count; i++)
+ {
+ expr = immed_double_const (test[i].lo, test[i].hi, word_mode);
+ expr = expand_binop (word_mode, and_optab, index, expr,
+ NULL_RTX, 1, OPTAB_WIDEN);
+ emit_cmp_and_jump_insns (expr, const0_rtx, NE, NULL_RTX,
+ word_mode, 1, test[i].label);
+ }
+
+ emit_jump (default_label);
+}
/* Terminate a case (Pascal) or switch (C) statement
in which ORIG_INDEX is the expression to be tested.
+ If ORIG_TYPE is not NULL, it is the original ORIG_INDEX
+ type as given in the source before any compiler conversions.
Generate the code to test it and jump to the right place. */
void
-expand_end_case (orig_index)
- tree orig_index;
+expand_end_case_type (orig_index, orig_type)
+ tree orig_index, orig_type;
{
tree minval = NULL_TREE, maxval = NULL_TREE, range = NULL_TREE;
rtx default_label = 0;
- struct case_node *n;
- unsigned int count;
+ struct case_node *n, *m;
+ unsigned int count, uniq;
rtx index;
rtx table_label;
int ncases;
rtx *labelvec;
int i;
- rtx before_case, end;
+ rtx before_case, end, lab;
struct nesting *thiscase = case_stack;
tree index_expr, index_type;
+ bool exit_done = false;
int unsignedp;
/* Don't crash due to previous errors. */
if (thiscase == NULL)
return;
- table_label = gen_label_rtx ();
index_expr = thiscase->data.case_stmt.index_expr;
index_type = TREE_TYPE (index_expr);
unsignedp = TREE_UNSIGNED (index_type);
+ if (orig_type == NULL)
+ orig_type = TREE_TYPE (orig_index);
do_pending_stack_adjust ();
- /* This might get an spurious warning in the presence of a syntax error;
+ /* This might get a spurious warning in the presence of a syntax error;
it could be fixed by moving the call to check_seenlabel after the
check for error_mark_node, and copying the code of check_seenlabel that
deals with case_stack->data.case_stmt.line_number_status /
/* An ERROR_MARK occurs for various reasons including invalid data type. */
if (index_type != error_mark_node)
{
- /* If switch expression was an enumerated type, check that all
- enumeration literals are covered by the cases.
- No sense trying this if there's a default case, however. */
-
- if (!thiscase->data.case_stmt.default_label
- && TREE_CODE (TREE_TYPE (orig_index)) == ENUMERAL_TYPE
+ /* If the switch expression was an enumerated type, check that
+ exactly all enumeration literals are covered by the cases.
+ The check is made when -Wswitch was specified and there is no
+ default case, or when -Wswitch-enum was specified. */
+ if (((warn_switch && !thiscase->data.case_stmt.default_label)
+ || warn_switch_enum)
+ && TREE_CODE (orig_type) == ENUMERAL_TYPE
&& TREE_CODE (index_expr) != INTEGER_CST)
- check_for_full_enumeration_handling (TREE_TYPE (orig_index));
+ check_for_full_enumeration_handling (orig_type);
+
+ if (warn_switch_default && !thiscase->data.case_stmt.default_label)
+ warning ("switch missing default case");
/* If we don't have a default-label, create one here,
after the body of the switch. */
{
thiscase->data.case_stmt.default_label
= build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
+ /* Share the exit label if possible. */
+ if (thiscase->exit_label)
+ {
+ SET_DECL_RTL (thiscase->data.case_stmt.default_label,
+ thiscase->exit_label);
+ exit_done = true;
+ }
expand_label (thiscase->data.case_stmt.default_label);
}
default_label = label_rtx (thiscase->data.case_stmt.default_label);
/* Simplify the case-list before we count it. */
group_case_nodes (thiscase->data.case_stmt.case_list);
+ strip_default_case_nodes (&thiscase->data.case_stmt.case_list,
+ default_label);
/* Get upper and lower bounds of case values.
Also convert all the case values to the index expr's data type. */
+ uniq = 0;
count = 0;
for (n = thiscase->data.case_stmt.case_list; n; n = n->right)
{
/* A range counts double, since it requires two compares. */
if (! tree_int_cst_equal (n->low, n->high))
count++;
+
+ /* Count the number of unique case node targets. */
+ uniq++;
+ lab = label_rtx (n->code_label);
+ for (m = thiscase->data.case_stmt.case_list; m != n; m = m->right)
+ if (same_case_target_p (label_rtx (m->code_label), lab))
+ {
+ uniq--;
+ break;
+ }
}
/* Compute span of values. */
emit_jump (default_label);
}
+ /* Try implementing this switch statement by a short sequence of
+ bit-wise comparisons. However, we let the binary-tree case
+ below handle constant index expressions. */
+ else if (CASE_USE_BIT_TESTS
+ && ! TREE_CONSTANT (index_expr)
+ && compare_tree_int (range, GET_MODE_BITSIZE (word_mode)) < 0
+ && lshift_cheap_p ()
+ && ((uniq == 1 && count >= 3)
+ || (uniq == 2 && count >= 5)
+ || (uniq == 3 && count >= 6)))
+ {
+ /* Optimize the case where all the case values fit in a
+ word without having to subtract MINVAL. In this case,
+ we can optimize away the subtraction. */
+ if (compare_tree_int (minval, 0) > 0
+ && compare_tree_int (maxval, GET_MODE_BITSIZE (word_mode)) < 0)
+ {
+ minval = integer_zero_node;
+ range = maxval;
+ }
+ emit_case_bit_tests (index_type, index_expr, minval, range,
+ thiscase->data.case_stmt.case_list,
+ default_label);
+ }
+
/* If range of values is much bigger than number of values,
make a sequence of conditional branches instead of a dispatch.
If the switch-index is a constant, do it this way
#ifndef ASM_OUTPUT_ADDR_DIFF_ELT
|| flag_pic
#endif
- || TREE_CODE (index_expr) == INTEGER_CST
- || (TREE_CODE (index_expr) == COMPOUND_EXPR
- && TREE_CODE (TREE_OPERAND (index_expr, 1)) == INTEGER_CST))
+ || TREE_CONSTANT (index_expr))
{
index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0);
default code is emitted. */
use_cost_table
- = (TREE_CODE (TREE_TYPE (orig_index)) != ENUMERAL_TYPE
+ = (TREE_CODE (orig_type) != ENUMERAL_TYPE
&& estimate_case_costs (thiscase->data.case_stmt.case_list));
balance_case_nodes (&thiscase->data.case_stmt.case_list, NULL);
emit_case_nodes (index, thiscase->data.case_stmt.case_list,
}
else
{
+ table_label = gen_label_rtx ();
if (! try_casesi (index_type, index_expr, minval, range,
table_label, default_label))
{
index_type = thiscase->data.case_stmt.nominal_type;
- /* Index jumptables from zero for suitable values of
+ /* Index jumptables from zero for suitable values of
minval to avoid a subtraction. */
- if (! optimize_size
- && compare_tree_int (minval, 0) > 0
- && compare_tree_int (minval, 3) < 0)
- {
- minval = integer_zero_node;
- range = maxval;
- }
+ if (! optimize_size
+ && compare_tree_int (minval, 0) > 0
+ && compare_tree_int (minval, 3) < 0)
+ {
+ minval = integer_zero_node;
+ range = maxval;
+ }
if (! try_tablejump (index_type, index_expr, minval, range,
table_label, default_label))
abort ();
}
-
+
/* Get table of labels to jump to, in order of case index. */
ncases = tree_low_cst (range, 0) + 1;
for (n = thiscase->data.case_stmt.case_list; n; n = n->right)
{
- HOST_WIDE_INT i
- = tree_low_cst (n->low, 0) - tree_low_cst (minval, 0);
-
- while (1)
- {
- labelvec[i]
- = gen_rtx_LABEL_REF (Pmode, label_rtx (n->code_label));
- if (i + tree_low_cst (minval, 0)
- == tree_low_cst (n->high, 0))
- break;
- i++;
- }
+ /* Compute the low and high bounds relative to the minimum
+ value since that should fit in a HOST_WIDE_INT while the
+ actual values may not. */
+ HOST_WIDE_INT i_low
+ = tree_low_cst (fold (build (MINUS_EXPR, index_type,
+ n->low, minval)), 1);
+ HOST_WIDE_INT i_high
+ = tree_low_cst (fold (build (MINUS_EXPR, index_type,
+ n->high, minval)), 1);
+ HOST_WIDE_INT i;
+
+ for (i = i_low; i <= i_high; i ++)
+ labelvec[i]
+ = gen_rtx_LABEL_REF (Pmode, label_rtx (n->code_label));
}
/* Fill in the gaps with the default. */
else
end_cleanup_deferral ();
- if (thiscase->exit_label)
+ if (thiscase->exit_label && !exit_done)
emit_label (thiscase->exit_label);
- free_case_nodes (case_stack->data.case_stmt.case_list);
POPSTACK (case_stack);
free_temp_slots ();
return 1;
}
+/* Determine whether two case labels branch to the same target. */
+
+static bool
+same_case_target_p (l1, l2)
+ rtx l1, l2;
+{
+ rtx i1, i2;
+
+ if (l1 == l2)
+ return true;
+
+ i1 = next_real_insn (l1);
+ i2 = next_real_insn (l2);
+ if (i1 == i2)
+ return true;
+
+ if (i1 && simplejump_p (i1))
+ {
+ l1 = XEXP (SET_SRC (PATTERN (i1)), 0);
+ }
+
+ if (i2 && simplejump_p (i2))
+ {
+ l2 = XEXP (SET_SRC (PATTERN (i2)), 0);
+ }
+ return l1 == l2;
+}
+
+/* Delete nodes that branch to the default label from a list of
+ case nodes. Eg. case 5: default: becomes just default: */
+
+static void
+strip_default_case_nodes (prev, deflab)
+ case_node_ptr *prev;
+ rtx deflab;
+{
+ case_node_ptr ptr;
+
+ while (*prev)
+ {
+ ptr = *prev;
+ if (same_case_target_p (label_rtx (ptr->code_label), deflab))
+ *prev = ptr->right;
+ else
+ prev = &ptr->right;
+ }
+}
+
/* Scan an ordered list of case nodes
combining those with consecutive values or ranges.
while (node)
{
- rtx lb = next_real_insn (label_rtx (node->code_label));
- rtx lb2;
+ rtx lab = label_rtx (node->code_label);
case_node_ptr np = node;
/* Try to group the successors of NODE with NODE. */
while (((np = np->right) != 0)
/* Do they jump to the same place? */
- && ((lb2 = next_real_insn (label_rtx (np->code_label))) == lb
- || (lb != 0 && lb2 != 0
- && simplejump_p (lb)
- && simplejump_p (lb2)
- && rtx_equal_p (SET_SRC (PATTERN (lb)),
- SET_SRC (PATTERN (lb2)))))
+ && same_case_target_p (label_rtx (np->code_label), lab)
/* Are their ranges consecutive? */
&& tree_int_cst_equal (np->low,
fold (build (PLUS_EXPR,
else if (!low_bound && !high_bound)
{
/* Widen LOW and HIGH to the same width as INDEX. */
- tree type = type_for_mode (mode, unsignedp);
+ tree type = (*lang_hooks.types.type_for_mode) (mode, unsignedp);
tree low = build1 (CONVERT_EXPR, type, node->low);
tree high = build1 (CONVERT_EXPR, type, node->high);
rtx low_rtx, new_index, new_bound;
new_bound = expand_expr (fold (build (MINUS_EXPR, type,
high, low)),
NULL_RTX, mode, 0);
-
+
emit_cmp_and_jump_insns (new_index, new_bound, GT, NULL_RTX,
mode, 1, default_label);
}
}
}
}
+
+#include "gt-stmt.h"