#include "config.h"
#include "system.h"
#include "rtl.h"
#include "tree.h"
#include "tm_p.h"
#include "flags.h"
#include "except.h"
#include "function.h"
#include "insn-config.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;
#ifndef CASE_VECTOR_PC_RELATIVE
#define CASE_VECTOR_PC_RELATIVE 0
#endif
struct case_node
{
struct case_node *left;
struct case_node *right;
struct case_node *parent;
tree low;
tree high;
tree code_label;
int balance;
};
typedef struct case_node case_node;
typedef struct case_node *case_node_ptr;
static short cost_table_[129];
static int use_cost_table;
static int cost_table_initialized;
#define COST_TABLE(I) cost_table_[(unsigned HOST_WIDE_INT) ((I) + 1)]
struct nesting
{
struct nesting *all;
struct nesting *next;
int depth;
rtx exit_label;
union
{
struct
{
rtx endif_label;
rtx next_label;
} cond;
struct
{
rtx start_label;
rtx end_label;
rtx alt_end_label;
rtx continue_label;
} loop;
struct
{
int block_start_count;
rtx stack_level;
rtx first_insn;
struct nesting *innermost_stack_block;
tree cleanups;
tree outer_cleanups;
struct label_chain *label_chain;
int n_function_calls;
int exception_region;
int block_target_temp_slot_level;
int conditional_code;
rtx last_unconditional_cleanup;
tree *cleanup_ptr;
} block;
struct
{
rtx start;
struct case_node *case_list;
tree default_label;
tree index_expr;
tree nominal_type;
const char *printname;
int line_number_status;
} case_stmt;
} data;
};
#define ALLOC_NESTING() \
(struct nesting *) obstack_alloc (&stmt_obstack, sizeof (struct nesting))
#define POPSTACK(STACK) \
do { struct nesting *target = STACK; \
struct nesting *this; \
do { this = nesting_stack; \
if (loop_stack == this) \
loop_stack = loop_stack->next; \
if (cond_stack == this) \
cond_stack = cond_stack->next; \
if (block_stack == this) \
block_stack = block_stack->next; \
if (stack_block_stack == this) \
stack_block_stack = stack_block_stack->next; \
if (case_stack == this) \
case_stack = case_stack->next; \
nesting_depth = nesting_stack->depth - 1; \
nesting_stack = this->all; \
obstack_free (&stmt_obstack, this); } \
while (this != target); } while (0)
struct goto_fixup
{
struct goto_fixup *next;
rtx before_jump;
tree target;
tree context;
rtx target_rtl;
int block_start_count;
rtx stack_level;
tree cleanup_list_list;
};
struct label_chain
{
struct label_chain *next;
tree label;
};
struct stmt_status
{
struct nesting *x_block_stack;
struct nesting *x_stack_block_stack;
struct nesting *x_cond_stack;
struct nesting *x_loop_stack;
struct nesting *x_case_stack;
struct nesting *x_nesting_stack;
int x_nesting_depth;
int x_block_start_count;
tree x_last_expr_type;
rtx x_last_expr_value;
int x_expr_stmts_for_value;
const char *x_emit_filename;
int x_emit_lineno;
struct goto_fixup *x_goto_fixup_chain;
};
#define block_stack (cfun->stmt->x_block_stack)
#define stack_block_stack (cfun->stmt->x_stack_block_stack)
#define cond_stack (cfun->stmt->x_cond_stack)
#define loop_stack (cfun->stmt->x_loop_stack)
#define case_stack (cfun->stmt->x_case_stack)
#define nesting_stack (cfun->stmt->x_nesting_stack)
#define nesting_depth (cfun->stmt->x_nesting_depth)
#define current_block_start_count (cfun->stmt->x_block_start_count)
#define last_expr_type (cfun->stmt->x_last_expr_type)
#define last_expr_value (cfun->stmt->x_last_expr_value)
#define expr_stmts_for_value (cfun->stmt->x_expr_stmts_for_value)
#define emit_filename (cfun->stmt->x_emit_filename)
#define emit_lineno (cfun->stmt->x_emit_lineno)
#define goto_fixup_chain (cfun->stmt->x_goto_fixup_chain)
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 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));
static void expand_nl_goto_receiver PARAMS ((void));
static void expand_nl_goto_receivers PARAMS ((struct nesting *));
static void fixup_gotos PARAMS ((struct nesting *, rtx, tree,
rtx, int));
static bool check_operand_nalternatives PARAMS ((tree, tree));
static bool check_unique_operand_names PARAMS ((tree, tree));
static tree resolve_operand_names PARAMS ((tree, tree, tree,
const char **));
static char *resolve_operand_name_1 PARAMS ((char *, tree, tree));
static void expand_null_return_1 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 void group_case_nodes PARAMS ((case_node_ptr));
static void balance_case_nodes PARAMS ((case_node_ptr *,
case_node_ptr));
static int node_has_low_bound PARAMS ((case_node_ptr, tree));
static int node_has_high_bound PARAMS ((case_node_ptr, tree));
static int node_is_bounded PARAMS ((case_node_ptr, tree));
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));
void
using_eh_for_cleanups ()
{
using_eh_for_cleanups_p = 1;
}
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;
}
}
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;
}
}
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);
n = n->next;
}
}
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;
}
}
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);
}
}
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;
}
}
void
free_stmt_status (f)
struct function *f;
{
if (f->stmt)
free (f->stmt);
f->stmt = NULL;
}
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);
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));
block_stack = 0;
stack_block_stack = 0;
loop_stack = 0;
case_stack = 0;
cond_stack = 0;
nesting_stack = 0;
nesting_depth = 0;
current_block_start_count = 0;
goto_fixup_chain = 0;
expr_stmts_for_value = 0;
last_expr_type = 0;
last_expr_value = NULL_RTX;
}
int
in_control_zone_p ()
{
return cond_stack || loop_stack || case_stack;
}
void
set_file_and_line_for_stmt (file, line)
const char *file;
int line;
{
if (cfun->stmt)
{
emit_filename = file;
emit_lineno = line;
}
}
void
emit_nop ()
{
rtx last_insn;
last_insn = get_last_insn ();
if (!optimize
&& (GET_CODE (last_insn) == CODE_LABEL
|| (GET_CODE (last_insn) == NOTE
&& prev_real_insn (last_insn) == 0)))
emit_insn (gen_nop ());
}
rtx
label_rtx (label)
tree label;
{
if (TREE_CODE (label) != LABEL_DECL)
abort ();
if (!DECL_RTL_SET_P (label))
SET_DECL_RTL (label, gen_label_rtx ());
return DECL_RTL (label);
}
void
emit_jump (label)
rtx label;
{
do_pending_stack_adjust ();
emit_jump_insn (gen_jump (label));
emit_barrier ();
}
void
expand_computed_goto (exp)
tree exp;
{
rtx x = expand_expr (exp, NULL_RTX, VOIDmode, 0);
#ifdef POINTERS_EXTEND_UNSIGNED
if (GET_MODE (x) != Pmode)
x = convert_memory_address (Pmode, x);
#endif
emit_queue ();
do_pending_stack_adjust ();
emit_indirect_jump (x);
current_function_has_computed_jump = 1;
}
void
expand_label (label)
tree label;
{
struct label_chain *p;
do_pending_stack_adjust ();
emit_label (label_rtx (label));
if (DECL_NAME (label))
LABEL_NAME (DECL_RTL (label)) = IDENTIFIER_POINTER (DECL_NAME (label));
if (stack_block_stack != 0)
{
p = (struct label_chain *) ggc_alloc (sizeof (struct label_chain));
p->next = stack_block_stack->data.block.label_chain;
stack_block_stack->data.block.label_chain = p;
p->label = label;
}
}
void
declare_nonlocal_label (label)
tree label;
{
rtx slot = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0);
nonlocal_labels = tree_cons (NULL_TREE, label, nonlocal_labels);
LABEL_PRESERVE_P (label_rtx (label)) = 1;
if (nonlocal_goto_handler_slots == 0)
{
emit_stack_save (SAVE_NONLOCAL,
&nonlocal_goto_stack_level,
PREV_INSN (tail_recursion_reentry));
}
nonlocal_goto_handler_slots
= gen_rtx_EXPR_LIST (VOIDmode, slot, nonlocal_goto_handler_slots);
}
void
expand_goto (label)
tree label;
{
tree context;
context = decl_function_context (label);
if (context != 0 && context != current_function_decl)
{
struct function *p = find_function_data (context);
rtx label_ref = gen_rtx_LABEL_REF (Pmode, label_rtx (label));
rtx handler_slot, static_chain, save_area, insn;
tree link;
handler_slot = p->x_nonlocal_goto_handler_slots;
for (link = p->x_nonlocal_labels; TREE_VALUE (link) != label;
link = TREE_CHAIN (link))
handler_slot = XEXP (handler_slot, 1);
handler_slot = XEXP (handler_slot, 0);
p->has_nonlocal_label = 1;
current_function_has_nonlocal_goto = 1;
LABEL_REF_NONLOCAL_P (label_ref) = 1;
static_chain = copy_to_reg (lookup_static_chain (label));
handler_slot = copy_to_reg (replace_rtx (copy_rtx (handler_slot),
virtual_stack_vars_rtx,
static_chain));
save_area = p->x_nonlocal_goto_stack_level;
if (save_area)
save_area = replace_rtx (copy_rtx (save_area),
virtual_stack_vars_rtx, static_chain);
#if HAVE_nonlocal_goto
if (HAVE_nonlocal_goto)
emit_insn (gen_nonlocal_goto (static_chain, handler_slot,
save_area, label_ref));
else
#endif
{
emit_move_insn (hard_frame_pointer_rtx, static_chain);
emit_stack_restore (SAVE_NONLOCAL, save_area, NULL_RTX);
emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx));
emit_insn (gen_rtx_USE (VOIDmode, stack_pointer_rtx));
emit_indirect_jump (handler_slot);
}
for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
{
if (GET_CODE (insn) == JUMP_INSN)
{
REG_NOTES (insn) = alloc_EXPR_LIST (REG_NON_LOCAL_GOTO,
const0_rtx, REG_NOTES (insn));
break;
}
else if (GET_CODE (insn) == CALL_INSN)
break;
}
}
else
expand_goto_internal (label, label_rtx (label), NULL_RTX);
}
static void
expand_goto_internal (body, label, last_insn)
tree body;
rtx label;
rtx last_insn;
{
struct nesting *block;
rtx stack_level = 0;
if (GET_CODE (label) != CODE_LABEL)
abort ();
if (PREV_INSN (label) != 0)
{
for (block = block_stack; block; block = block->next)
{
if (INSN_UID (block->data.block.first_insn) < INSN_UID (label))
break;
if (block->data.block.stack_level != 0)
stack_level = block->data.block.stack_level;
if (block->data.block.cleanups != 0)
{
expand_cleanups (block->data.block.cleanups, NULL_TREE, 1, 1);
do_pending_stack_adjust ();
}
}
if (stack_level)
{
clear_pending_stack_adjust ();
do_pending_stack_adjust ();
if (label == return_label
&& (((TREE_CODE (TREE_TYPE (current_function_decl))
== FUNCTION_TYPE)
&& (TYPE_RETURNS_STACK_DEPRESSED
(TREE_TYPE (current_function_decl))))))
;
else
emit_stack_restore (SAVE_BLOCK, stack_level, NULL_RTX);
}
if (body != 0 && DECL_TOO_LATE (body))
error ("jump to `%s' invalidly jumps into binding contour",
IDENTIFIER_POINTER (DECL_NAME (body)));
}
else if (! expand_fixup (body, label, last_insn))
{
if (body != 0)
TREE_ADDRESSABLE (body) = 1;
}
emit_jump (label);
}
static int
expand_fixup (tree_label, rtl_label, last_insn)
tree tree_label;
rtx rtl_label;
rtx last_insn;
{
struct nesting *block, *end_block;
if (cond_stack
&& (rtl_label == cond_stack->data.cond.endif_label
|| rtl_label == cond_stack->data.cond.next_label))
end_block = cond_stack;
else if (loop_stack
&& (rtl_label == loop_stack->data.loop.start_label
|| rtl_label == loop_stack->data.loop.end_label
|| rtl_label == loop_stack->data.loop.continue_label))
end_block = loop_stack;
else
end_block = 0;
if (end_block)
{
struct nesting *next_block = end_block->all;
block = block_stack;
while (next_block && next_block != block)
next_block = next_block->all;
if (next_block)
return 0;
next_block = block_stack->next;
for (block = block_stack; block != end_block; block = block->all)
if (block == next_block)
next_block = next_block->next;
end_block = next_block;
}
for (block = block_stack; block != end_block; block = block->next)
if (block->data.block.stack_level != 0
|| block->data.block.cleanups != 0)
break;
if (block != end_block)
{
struct goto_fixup *fixup
= (struct goto_fixup *) ggc_alloc (sizeof (struct goto_fixup));
if (last_insn == 0)
do_pending_stack_adjust ();
fixup->target = tree_label;
fixup->target_rtl = rtl_label;
{
rtx original_before_jump
= last_insn ? last_insn : get_last_insn ();
rtx start;
rtx end;
tree block;
block = make_node (BLOCK);
TREE_USED (block) = 1;
if (!cfun->x_whole_function_mode_p)
insert_block (block);
else
{
BLOCK_CHAIN (block)
= BLOCK_CHAIN (DECL_INITIAL (current_function_decl));
BLOCK_CHAIN (DECL_INITIAL (current_function_decl))
= block;
}
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->block_start_count = current_block_start_count;
fixup->stack_level = 0;
fixup->cleanup_list_list
= ((block->data.block.outer_cleanups
|| block->data.block.cleanups)
? tree_cons (NULL_TREE, block->data.block.cleanups,
block->data.block.outer_cleanups)
: 0);
fixup->next = goto_fixup_chain;
goto_fixup_chain = fixup;
}
return block != 0;
}
void
expand_fixups (first_insn)
rtx first_insn;
{
fixup_gotos (NULL, NULL_RTX, NULL_TREE, first_insn, 0);
}
static void
fixup_gotos (thisblock, stack_level, cleanup_list, first_insn, dont_jump_in)
struct nesting *thisblock;
rtx stack_level;
tree cleanup_list;
rtx first_insn;
int dont_jump_in;
{
struct goto_fixup *f, *prev;
for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next)
{
if (f->before_jump == 0)
{
if (prev != 0)
prev->next = f->next;
}
else if (PREV_INSN (f->target_rtl) != 0)
{
rtx cleanup_insns;
if (f->target != 0
&& (dont_jump_in || stack_level || cleanup_list)
&& INSN_UID (first_insn) < INSN_UID (f->target_rtl)
&& INSN_UID (first_insn) > INSN_UID (f->before_jump)
&& ! DECL_ERROR_ISSUED (f->target))
{
error_with_decl (f->target,
"label `%s' used before containing binding contour");
DECL_ERROR_ISSUED (f->target) = 1;
}
start_sequence ();
pushlevel (0);
set_block (f->context);
if (f->cleanup_list_list)
{
tree lists;
for (lists = f->cleanup_list_list; lists; lists = TREE_CHAIN (lists))
if (TREE_ADDRESSABLE (lists)
&& TREE_VALUE (lists) != 0)
{
expand_cleanups (TREE_VALUE (lists), NULL_TREE, 1, 1);
do_pending_stack_adjust ();
}
}
if (f->stack_level
&& ! (f->target_rtl == return_label
&& ((TREE_CODE (TREE_TYPE (current_function_decl))
== FUNCTION_TYPE)
&& (TYPE_RETURNS_STACK_DEPRESSED
(TREE_TYPE (current_function_decl))))))
emit_stack_restore (SAVE_BLOCK, f->stack_level, f->before_jump);
cleanup_insns = get_insns ();
poplevel (1, 0, 0);
end_sequence ();
emit_insns_after (cleanup_insns, f->before_jump);
f->before_jump = 0;
}
}
for (prev = 0, f = goto_fixup_chain; f; prev = f, f = f->next)
if (f->before_jump != 0
&& PREV_INSN (f->target_rtl) == 0
&& thisblock != 0
&& (thisblock->data.block.block_start_count
<= f->block_start_count))
{
tree lists = f->cleanup_list_list;
rtx cleanup_insns;
for (; lists; lists = TREE_CHAIN (lists))
if (TREE_CHAIN (lists) == thisblock->data.block.outer_cleanups)
{
start_sequence ();
pushlevel (0);
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);
end_sequence ();
if (cleanup_insns != 0)
f->before_jump
= emit_insns_after (cleanup_insns, f->before_jump);
f->cleanup_list_list = TREE_CHAIN (lists);
}
if (stack_level)
f->stack_level = stack_level;
}
}
static int
n_occurrences (c, s)
int c;
const char *s;
{
int n = 0;
while (*s)
n += (*s++ == c);
return n;
}
void
expand_asm (body)
tree body;
{
if (TREE_CODE (body) == ADDR_EXPR)
body = TREE_OPERAND (body, 0);
emit_insn (gen_rtx_ASM_INPUT (VOIDmode,
TREE_STRING_POINTER (body)));
last_expr_type = 0;
}
bool
parse_output_constraint (constraint_p, operand_num, ninputs, noutputs,
allows_mem, allows_reg, is_inout)
const char **constraint_p;
int operand_num;
int ninputs;
int noutputs;
bool *allows_mem;
bool *allows_reg;
bool *is_inout;
{
const char *constraint = *constraint_p;
const char *p;
*allows_mem = false;
*allows_reg = false;
p = strchr (constraint, '=');
if (!p)
p = strchr (constraint, '+');
if (!p)
{
error ("output operand constraint lacks `='");
return false;
}
*is_inout = (*p == '+');
if (p != constraint || is_inout)
{
char *buf;
size_t c_len = strlen (constraint);
if (p != constraint)
warning ("output constraint `%c' for operand %d is not at the beginning",
*p, operand_num);
buf = alloca (c_len + 1);
strcpy (buf, constraint);
buf[p - constraint] = buf[0];
buf[0] = '=';
*constraint_p = ggc_alloc_string (buf, c_len);
constraint = *constraint_p;
}
for (p = constraint + 1; *p; ++p)
switch (*p)
{
case '+':
case '=':
error ("operand constraint contains incorrectly positioned '+' or '='");
return false;
case '%':
if (operand_num + 1 == ninputs + noutputs)
{
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 '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;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '[':
error ("matching constraint not valid in output operand");
return false;
case '<': case '>':
*allows_mem = true;
break;
case 'g': case 'X':
*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)
*allows_reg = true;
#ifdef EXTRA_CONSTRAINT
else
{
*allows_reg = true;
*allows_mem = true;
}
#endif
break;
}
return true;
}
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;
*allows_mem = false;
*allows_reg = false;
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 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;
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;
}
if (*end == '\0'
&& (j == 0 || (j == 1 && constraint[0] == '%')))
{
constraint = constraints[match];
*constraint_p = constraint;
c_len = strlen (constraint);
j = 0;
break;
}
else
j = end - constraint;
}
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_LETTER (constraint[j]) != NO_REGS)
*allows_reg = true;
#ifdef EXTRA_CONSTRAINT
else
{
*allows_reg = true;
*allows_mem = true;
}
#endif
break;
}
return true;
}
void
expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
tree string, outputs, inputs, clobbers;
int vol;
const char *filename;
int line;
{
rtvec argvec, constraintvec;
rtx body;
int ninputs = list_length (inputs);
int noutputs = list_length (outputs);
int ninout;
int nclobbers;
tree tail;
int i;
rtx *output_rtx = (rtx *) alloca (noutputs * sizeof (rtx));
int *inout_opnum = (int *) alloca (noutputs * sizeof (int));
rtx *real_output_rtx = (rtx *) alloca (noutputs * sizeof (rtx));
enum machine_mode *inout_mode
= (enum machine_mode *) alloca (noutputs * sizeof (enum machine_mode));
const char **constraints
= (const char **) alloca ((noutputs + ninputs) * sizeof (const char *));
rtx insn;
int old_generating_concat_p = generating_concat_p;
if (noutputs == 0)
vol = 1;
if (! check_operand_nalternatives (outputs, inputs))
return;
if (! check_unique_operand_names (outputs, inputs))
return;
string = resolve_operand_names (string, outputs, inputs, constraints);
#ifdef MD_ASM_CLOBBERS
MD_ASM_CLOBBERS (clobbers);
#endif
nclobbers = 0;
for (tail = clobbers; tail; tail = TREE_CHAIN (tail))
{
const char *regname = TREE_STRING_POINTER (TREE_VALUE (tail));
i = decode_reg_name (regname);
if (i >= 0 || i == -4)
++nclobbers;
else if (i == -2)
error ("unknown register name `%s' in `asm'", regname);
}
last_expr_type = 0;
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;
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))))
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 (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)
mark_addressable (TREE_VALUE (tail));
}
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;
if (!parse_output_constraint (&constraints[i], i, ninputs,
noutputs, &allows_mem, &allows_reg,
&is_inout))
abort ();
generating_concat_p = 0;
real_output_rtx[i] = NULL_RTX;
if ((TREE_CODE (val) == INDIRECT_REF
&& allows_mem)
|| (DECL_P (val)
&& (allows_mem || GET_CODE (DECL_RTL (val)) == REG)
&& ! (GET_CODE (DECL_RTL (val)) == REG
&& GET_MODE (DECL_RTL (val)) != TYPE_MODE (type)))
|| ! allows_reg
|| is_inout)
{
output_rtx[i] = expand_expr (val, NULL_RTX, VOIDmode, EXPAND_WRITE);
if (! allows_reg && GET_CODE (output_rtx[i]) != MEM)
error ("output number %d not directly addressable", i);
if ((! allows_mem && GET_CODE (output_rtx[i]) == MEM)
|| GET_CODE (output_rtx[i]) == CONCAT)
{
real_output_rtx[i] = protect_from_queue (output_rtx[i], 1);
output_rtx[i] = gen_reg_rtx (GET_MODE (output_rtx[i]));
if (is_inout)
emit_move_insn (output_rtx[i], real_output_rtx[i]);
}
}
else
{
output_rtx[i] = assign_temp (type, 0, 0, 1);
TREE_VALUE (tail) = make_tree (type, output_rtx[i]);
}
generating_concat_p = old_generating_concat_p;
if (is_inout)
{
inout_mode[ninout] = TYPE_MODE (type);
inout_opnum[ninout++] = i;
}
}
argvec = rtvec_alloc (ninputs);
constraintvec = rtvec_alloc (ninputs);
body = gen_rtx_ASM_OPERANDS ((noutputs == 0 ? VOIDmode
: GET_MODE (output_rtx[0])),
TREE_STRING_POINTER (string),
empty_string, 0, argvec, constraintvec,
filename, line);
MEM_VOLATILE_P (body) = vol;
for (i = 0, tail = inputs; tail; tail = TREE_CHAIN (tail), ++i)
{
bool allows_reg, allows_mem;
const char *constraint;
tree val, type;
rtx op;
constraint = constraints[i + noutputs];
if (! parse_input_constraint (&constraint, i, ninputs, noutputs, ninout,
constraints, &allows_mem, &allows_reg))
abort ();
generating_concat_p = 0;
val = TREE_VALUE (tail);
type = TREE_TYPE (val);
op = expand_expr (val, NULL_RTX, VOIDmode, 0);
if (GET_CODE (op) == CONCAT)
op = force_reg (GET_MODE (op), op);
if (asm_operand_ok (op, constraint) <= 0)
{
if (allows_reg)
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 (type), op);
else if (GET_CODE (op) == REG
|| GET_CODE (op) == SUBREG
|| GET_CODE (op) == ADDRESSOF
|| GET_CODE (op) == CONCAT)
{
tree qual_type = build_qualified_type (type,
(TYPE_QUALS (type)
| TYPE_QUAL_CONST));
rtx memloc = assign_temp (qual_type, 1, 1, 1);
emit_move_insn (memloc, op);
op = memloc;
}
else if (GET_CODE (op) == MEM && MEM_VOLATILE_P (op))
{
}
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 (type), constraints[i + noutputs]);
}
generating_concat_p = 0;
for (i = 0; i < ninputs - ninout; i++)
ASM_OPERANDS_INPUT (body, i)
= protect_from_queue (ASM_OPERANDS_INPUT (body, i), 0);
for (i = 0; i < noutputs; i++)
output_rtx[i] = protect_from_queue (output_rtx[i], 1);
for (i = 0; i < ninout; i++)
{
int j = inout_opnum[i];
char buffer[16];
ASM_OPERANDS_INPUT (body, ninputs - ninout + i)
= output_rtx[j];
sprintf (buffer, "%d", j);
ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, ninputs - ninout + i)
= gen_rtx_ASM_INPUT (inout_mode[i], ggc_alloc_string (buffer, -1));
}
generating_concat_p = old_generating_concat_p;
if (noutputs == 1 && nclobbers == 0)
{
ASM_OPERANDS_OUTPUT_CONSTRAINT (body) = constraints[0];
insn = emit_insn (gen_rtx_SET (VOIDmode, output_rtx[0], body));
}
else if (noutputs == 0 && nclobbers == 0)
{
insn = emit_insn (body);
}
else
{
rtx obody = body;
int num = noutputs;
if (num == 0)
num = 1;
body = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (num + nclobbers));
for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
{
XVECEXP (body, 0, i)
= gen_rtx_SET (VOIDmode,
output_rtx[i],
gen_rtx_ASM_OPERANDS
(GET_MODE (output_rtx[i]),
TREE_STRING_POINTER (string),
constraints[i], i, argvec, constraintvec,
filename, line));
MEM_VOLATILE_P (SET_SRC (XVECEXP (body, 0, i))) = vol;
}
if (i == 0)
XVECEXP (body, 0, i++) = obody;
for (tail = clobbers; tail; tail = TREE_CHAIN (tail))
{
const char *regname = TREE_STRING_POINTER (TREE_VALUE (tail));
int j = decode_reg_name (regname);
if (j < 0)
{
if (j == -3)
continue;
if (j == -4)
{
XVECEXP (body, 0, i++)
= gen_rtx_CLOBBER (VOIDmode,
gen_rtx_MEM
(BLKmode,
gen_rtx_SCRATCH (VOIDmode)));
continue;
}
continue;
}
XVECEXP (body, 0, i++)
= gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (QImode, j));
}
insn = emit_insn (body);
}
for (i = 0; i < noutputs; ++i)
if (real_output_rtx[i])
emit_move_insn (real_output_rtx[i], output_rtx[i]);
free_temp_slots ();
}
static bool
check_operand_nalternatives (outputs, inputs)
tree outputs, inputs;
{
if (outputs || inputs)
{
tree tmp = TREE_PURPOSE (outputs ? outputs : inputs);
int nalternatives
= n_occurrences (',', TREE_STRING_POINTER (TREE_VALUE (tmp)));
tree next = inputs;
if (nalternatives + 1 > MAX_RECOG_ALTERNATIVES)
{
error ("too many alternatives in `asm'");
return false;
}
tmp = outputs;
while (tmp)
{
const char *constraint
= TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (tmp)));
if (n_occurrences (',', constraint) != nalternatives)
{
error ("operand constraints for `asm' differ in number of alternatives");
return false;
}
if (TREE_CHAIN (tmp))
tmp = TREE_CHAIN (tmp);
else
tmp = next, next = 0;
}
}
return true;
}
static bool
check_unique_operand_names (outputs, inputs)
tree outputs, inputs;
{
tree i, j;
for (i = outputs; i ; i = TREE_CHAIN (i))
{
tree i_name = TREE_PURPOSE (TREE_PURPOSE (i));
if (! i_name)
continue;
for (j = TREE_CHAIN (i); j ; j = TREE_CHAIN (j))
if (i_name == TREE_PURPOSE (TREE_PURPOSE (j)))
goto failure;
}
for (i = inputs; i ; i = TREE_CHAIN (i))
{
tree i_name = TREE_PURPOSE (TREE_PURPOSE (i));
if (! i_name)
continue;
for (j = TREE_CHAIN (i); j ; j = TREE_CHAIN (j))
if (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)))
goto failure;
}
return true;
failure:
error ("duplicate asm operand name '%s'",
IDENTIFIER_POINTER (TREE_PURPOSE (TREE_PURPOSE (i))));
return false;
}
static tree
resolve_operand_names (string, outputs, inputs, pconstraints)
tree string;
tree outputs, inputs;
const char **pconstraints;
{
char *buffer = xstrdup (TREE_STRING_POINTER (string));
char *p;
tree t;
p = buffer;
while ((p = strchr (p, '%')) != NULL)
{
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);
}
string = build_string (strlen (buffer), buffer);
free (buffer);
for (t = outputs; t ; t = TREE_CHAIN (t), pconstraints++)
*pconstraints = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
for (t = inputs; t ; t = TREE_CHAIN (t), pconstraints++)
{
const char *c = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
if (strchr (c, '[') == NULL)
*pconstraints = c;
else
{
p = buffer = xstrdup (c);
while ((p = strchr (p, '[')) != NULL)
p = resolve_operand_name_1 (p, outputs, inputs);
*pconstraints = ggc_alloc_string (buffer, -1);
free (buffer);
}
}
return string;
}
static char *
resolve_operand_name_1 (p, outputs, inputs)
char *p;
tree outputs, inputs;
{
char *q;
int op;
tree t;
size_t len;
q = strchr (p, ']');
if (!q)
{
error ("missing close brace for named operand");
return strchr (p, '\0');
}
len = q - p - 1;
for (op = 0, t = outputs; t ; t = TREE_CHAIN (t), op++)
{
tree id = TREE_PURPOSE (TREE_PURPOSE (t));
if (id)
{
const char *c = IDENTIFIER_POINTER (id);
if (strncmp (c, p + 1, len) == 0 && c[len] == '\0')
goto found;
}
}
for (t = inputs; t ; t = TREE_CHAIN (t), op++)
{
tree id = TREE_PURPOSE (TREE_PURPOSE (t));
if (id)
{
const char *c = IDENTIFIER_POINTER (id);
if (strncmp (c, p + 1, len) == 0 && c[len] == '\0')
goto found;
}
}
*q = '\0';
error ("undefined named operand '%s'", p + 1);
op = 0;
found:
sprintf (p, "%d", op);
p = strchr (p, '\0');
if (p > q)
abort ();
memmove (p, q + 1, strlen (q + 1) + 1);
return p;
}
void
expand_expr_stmt (exp)
tree exp;
{
expand_expr_stmt_value (exp, -1, 1);
}
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 (! want_value
&& (expr_stmts_for_value == 0 || ! maybe_last)
&& exp != error_mark_node)
{
if (! TREE_SIDE_EFFECTS (exp))
{
if ((extra_warnings || warn_unused_value)
&& !(TREE_CODE (exp) == CONVERT_EXPR
&& VOID_TYPE_P (TREE_TYPE (exp))))
warning_with_file_and_line (emit_filename, emit_lineno,
"statement with no effect");
}
else if (warn_unused_value)
warn_if_unused_value (exp);
}
if (want_value && TREE_CODE (TREE_TYPE (exp)) == FUNCTION_TYPE)
exp = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (exp)), exp);
value = expand_expr (exp, want_value ? NULL_RTX : const0_rtx,
VOIDmode, 0);
type = TREE_TYPE (exp);
if (value && GET_CODE (value) == MEM && TREE_THIS_VOLATILE (exp))
{
if (TYPE_MODE (type) == VOIDmode)
;
else if (TYPE_MODE (type) != BLKmode)
value = copy_to_reg (value);
else
{
rtx lab = gen_label_rtx ();
emit_cmp_and_jump_insns (value, value, EQ,
expand_expr (TYPE_SIZE (type),
NULL_RTX, VOIDmode, 0),
BLKmode, 0, lab);
emit_label (lab);
}
}
preserve_temp_slots (value);
free_temp_slots ();
if (want_value)
{
last_expr_value = value;
last_expr_type = type;
}
emit_queue ();
}
int
warn_if_unused_value (exp)
tree exp;
{
if (TREE_USED (exp))
return 0;
if (VOID_TYPE_P (TREE_TYPE (exp)))
return 0;
switch (TREE_CODE (exp))
{
case PREINCREMENT_EXPR:
case POSTINCREMENT_EXPR:
case PREDECREMENT_EXPR:
case POSTDECREMENT_EXPR:
case MODIFY_EXPR:
case INIT_EXPR:
case TARGET_EXPR:
case CALL_EXPR:
case METHOD_CALL_EXPR:
case RTL_EXPR:
case TRY_CATCH_EXPR:
case WITH_CLEANUP_EXPR:
case EXIT_EXPR:
return 0;
case BIND_EXPR:
return warn_if_unused_value (TREE_OPERAND (exp, 1));
case SAVE_EXPR:
return warn_if_unused_value (TREE_OPERAND (exp, 1));
case TRUTH_ORIF_EXPR:
case TRUTH_ANDIF_EXPR:
return warn_if_unused_value (TREE_OPERAND (exp, 1));
case COMPOUND_EXPR:
if (TREE_NO_UNUSED_WARNING (exp))
return 0;
if (warn_if_unused_value (TREE_OPERAND (exp, 0)))
return 1;
if (TREE_CONSTANT (TREE_OPERAND (exp, 1)))
return 0;
return warn_if_unused_value (TREE_OPERAND (exp, 1));
case NOP_EXPR:
case CONVERT_EXPR:
case NON_LVALUE_EXPR:
if (TREE_NO_UNUSED_WARNING (exp))
return 0;
{
tree tem = TREE_OPERAND (exp, 0);
while (TREE_CODE (tem) == CONVERT_EXPR || TREE_CODE (tem) == NOP_EXPR)
tem = TREE_OPERAND (tem, 0);
if (TREE_CODE (tem) == MODIFY_EXPR || TREE_CODE (tem) == INIT_EXPR
|| TREE_CODE (tem) == CALL_EXPR)
return 0;
}
goto maybe_warn;
case INDIRECT_REF:
if (TREE_CODE (TREE_TYPE (TREE_OPERAND (exp, 0))) == REFERENCE_TYPE)
return warn_if_unused_value (TREE_OPERAND (exp, 0));
default:
if ((DECL_P (exp)
|| TREE_CODE_CLASS (TREE_CODE (exp)) == 'r')
&& TREE_THIS_VOLATILE (exp))
return 0;
if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'e'
&& TREE_CODE_LENGTH (TREE_CODE (exp)) == 0)
return 0;
maybe_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;
}
tree
expand_start_stmt_expr (has_scope)
int has_scope;
{
tree t;
t = make_node (RTL_EXPR);
do_pending_stack_adjust ();
if (has_scope)
start_sequence_for_rtl_expr (t);
else
start_sequence ();
NO_DEFER_POP;
expr_stmts_for_value++;
last_expr_value = NULL_RTX;
return t;
}
tree
expand_end_stmt_expr (t)
tree t;
{
OK_DEFER_POP;
if (! last_expr_value || ! last_expr_type)
{
last_expr_value = const0_rtx;
last_expr_type = void_type_node;
}
else if (GET_CODE (last_expr_value) != REG && ! CONSTANT_P (last_expr_value))
last_expr_value = protect_from_queue (last_expr_value, 0);
emit_queue ();
TREE_TYPE (t) = last_expr_type;
RTL_EXPR_RTL (t) = last_expr_value;
RTL_EXPR_SEQUENCE (t) = get_insns ();
rtl_expr_chain = tree_cons (NULL_TREE, t, rtl_expr_chain);
end_sequence ();
TREE_SIDE_EFFECTS (t) = 1;
TREE_THIS_VOLATILE (t) = volatile_refs_p (last_expr_value);
last_expr_type = 0;
expr_stmts_for_value--;
return t;
}
void
expand_start_cond (cond, exitflag)
tree cond;
int exitflag;
{
struct nesting *thiscond = ALLOC_NESTING ();
thiscond->next = cond_stack;
thiscond->all = nesting_stack;
thiscond->depth = ++nesting_depth;
thiscond->data.cond.next_label = gen_label_rtx ();
thiscond->exit_label = exitflag ? gen_label_rtx () : 0;
thiscond->data.cond.endif_label = thiscond->exit_label;
cond_stack = thiscond;
nesting_stack = thiscond;
do_jump (cond, thiscond->data.cond.next_label, NULL_RTX);
}
void
expand_start_elseif (cond)
tree cond;
{
if (cond_stack->data.cond.endif_label == 0)
cond_stack->data.cond.endif_label = gen_label_rtx ();
emit_jump (cond_stack->data.cond.endif_label);
emit_label (cond_stack->data.cond.next_label);
cond_stack->data.cond.next_label = gen_label_rtx ();
do_jump (cond, cond_stack->data.cond.next_label, NULL_RTX);
}
void
expand_start_else ()
{
if (cond_stack->data.cond.endif_label == 0)
cond_stack->data.cond.endif_label = gen_label_rtx ();
emit_jump (cond_stack->data.cond.endif_label);
emit_label (cond_stack->data.cond.next_label);
cond_stack->data.cond.next_label = 0;
}
void
expand_elseif (cond)
tree cond;
{
cond_stack->data.cond.next_label = gen_label_rtx ();
do_jump (cond, cond_stack->data.cond.next_label, NULL_RTX);
}
void
expand_end_cond ()
{
struct nesting *thiscond = cond_stack;
do_pending_stack_adjust ();
if (thiscond->data.cond.next_label)
emit_label (thiscond->data.cond.next_label);
if (thiscond->data.cond.endif_label)
emit_label (thiscond->data.cond.endif_label);
POPSTACK (cond_stack);
last_expr_type = 0;
}
struct nesting *
expand_start_loop (exit_flag)
int exit_flag;
{
struct nesting *thisloop = ALLOC_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;
nesting_stack = thisloop;
do_pending_stack_adjust ();
emit_queue ();
emit_note (NULL, NOTE_INSN_LOOP_BEG);
emit_label (thisloop->data.loop.start_label);
return thisloop;
}
struct nesting *
expand_start_loop_continue_elsewhere (exit_flag)
int exit_flag;
{
struct nesting *thisloop = expand_start_loop (exit_flag);
loop_stack->data.loop.continue_label = gen_label_rtx ();
return thisloop;
}
struct nesting *
expand_start_null_loop ()
{
struct nesting *thisloop = ALLOC_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;
nesting_stack = thisloop;
return thisloop;
}
void
expand_loop_continue_here ()
{
do_pending_stack_adjust ();
emit_note (NULL, NOTE_INSN_LOOP_CONT);
emit_label (loop_stack->data.loop.continue_label);
}
void
expand_end_loop ()
{
rtx start_label = loop_stack->data.loop.start_label;
rtx etc_note;
int eh_regions, debug_blocks;
if (start_label == loop_stack->data.loop.continue_label)
emit_note_before (NOTE_INSN_LOOP_CONT, start_label);
do_pending_stack_adjust ();
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;
else if (NOTE_LINE_NUMBER (etc_note) == NOTE_INSN_LOOP_BEG)
{
etc_note = NULL_RTX;
break;
}
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)
abort ();
}
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--;
}
if (etc_note
&& optimize
&& eh_regions == 0
&& (debug_blocks == 0 || optimize >= 2)
&& NEXT_INSN (etc_note) != NULL_RTX
&& ! any_condjump_p (get_last_insn ()))
{
rtx top_label = gen_label_rtx ();
rtx start_move = start_label;
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);
if (debug_blocks == 0)
reorder_insns (start_move, etc_note, get_last_insn ());
else
{
rtx insn, next_insn;
for (insn = start_move; insn; insn = next_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))
continue;
reorder_insns (insn, insn, get_last_insn ());
}
}
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;
}
void
expand_end_null_loop ()
{
do_pending_stack_adjust ();
emit_label (loop_stack->data.loop.end_label);
POPSTACK (loop_stack);
last_expr_type = 0;
}
int
expand_continue_loop (whichloop)
struct nesting *whichloop;
{
last_expr_type = 0;
if (whichloop == 0)
whichloop = loop_stack;
if (whichloop == 0)
return 0;
expand_goto_internal (NULL_TREE, whichloop->data.loop.continue_label,
NULL_RTX);
return 1;
}
int
expand_exit_loop (whichloop)
struct nesting *whichloop;
{
last_expr_type = 0;
if (whichloop == 0)
whichloop = loop_stack;
if (whichloop == 0)
return 0;
expand_goto_internal (NULL_TREE, whichloop->data.loop.end_label, NULL_RTX);
return 1;
}
int
expand_exit_loop_if_false (whichloop, cond)
struct nesting *whichloop;
tree cond;
{
rtx label = gen_label_rtx ();
rtx last_insn;
last_expr_type = 0;
if (whichloop == 0)
whichloop = loop_stack;
if (whichloop == 0)
return 0;
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;
expand_goto_internal (NULL_TREE, whichloop->data.loop.end_label,
NULL_RTX);
emit_label (label);
return 1;
}
int
expand_exit_loop_top_cond (whichloop, cond)
struct nesting *whichloop;
tree cond;
{
if (! expand_exit_loop_if_false (whichloop, cond))
return 0;
emit_note (NULL, NOTE_INSN_LOOP_END_TOP_COND);
return 1;
}
int
stmt_loop_nest_empty ()
{
return (cfun->stmt == NULL || loop_stack == NULL);
}
int
preserve_subexpressions_p ()
{
rtx insn;
if (flag_expensive_optimizations)
return 1;
if (optimize == 0 || cfun == 0 || cfun->stmt == 0 || loop_stack == 0)
return 0;
insn = get_last_insn_anywhere ();
return (insn
&& (INSN_UID (insn) - INSN_UID (loop_stack->data.loop.start_label)
< n_non_fixed_regs * 3));
}
int
expand_exit_something ()
{
struct nesting *n;
last_expr_type = 0;
for (n = nesting_stack; n; n = n->all)
if (n->exit_label != 0)
{
expand_goto_internal (NULL_TREE, n->exit_label, NULL_RTX);
return 1;
}
return 0;
}
void
expand_null_return ()
{
rtx last_insn = get_last_insn ();
clobber_return_register ();
expand_null_return_1 (last_insn);
}
static void
expand_value_return (val)
rtx val;
{
rtx last_insn = get_last_insn ();
rtx return_reg = DECL_RTL (DECL_RESULT (current_function_decl));
if (return_reg != val)
{
tree type = TREE_TYPE (DECL_RESULT (current_function_decl));
#ifdef PROMOTE_FUNCTION_RETURN
int unsignedp = TREE_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);
#endif
if (GET_CODE (return_reg) == PARALLEL)
emit_group_load (return_reg, val, int_size_in_bytes (type));
else
emit_move_insn (return_reg, val);
}
expand_null_return_1 (last_insn);
}
static void
expand_null_return_1 (last_insn)
rtx last_insn;
{
rtx end_label = cleanup_label ? cleanup_label : return_label;
clear_pending_stack_adjust ();
do_pending_stack_adjust ();
last_expr_type = 0;
if (end_label == 0)
end_label = return_label = gen_label_rtx ();
expand_goto_internal (NULL_TREE, end_label, last_insn);
}
void
expand_return (retval)
tree retval;
{
rtx last_insn = 0;
rtx result_rtl;
rtx val = 0;
tree retval_rhs;
if (TREE_CODE (TREE_TYPE (TREE_TYPE (current_function_decl))) == VOID_TYPE)
{
expand_expr (retval, NULL_RTX, VOIDmode, 0);
emit_queue ();
expand_null_return ();
return;
}
if (retval == error_mark_node)
{
expand_null_return ();
return;
}
else if (TREE_CODE (retval) == RESULT_DECL)
retval_rhs = retval;
else if ((TREE_CODE (retval) == MODIFY_EXPR || TREE_CODE (retval) == INIT_EXPR)
&& TREE_CODE (TREE_OPERAND (retval, 0)) == RESULT_DECL)
retval_rhs = TREE_OPERAND (retval, 1);
else if (VOID_TYPE_P (TREE_TYPE (retval)))
retval_rhs = retval;
else
retval_rhs = NULL_TREE;
last_insn = get_last_insn ();
if (optimize && retval_rhs != 0
&& frame_offset == 0
&& TREE_CODE (retval_rhs) == COND_EXPR
&& (TREE_CODE (TREE_OPERAND (retval_rhs, 1)) == CALL_EXPR
|| TREE_CODE (TREE_OPERAND (retval_rhs, 2)) == CALL_EXPR))
{
rtx label = gen_label_rtx ();
tree expr;
do_jump (TREE_OPERAND (retval_rhs, 0), label, NULL_RTX);
start_cleanup_deferral ();
expr = build (MODIFY_EXPR, TREE_TYPE (TREE_TYPE (current_function_decl)),
DECL_RESULT (current_function_decl),
TREE_OPERAND (retval_rhs, 1));
TREE_SIDE_EFFECTS (expr) = 1;
expand_return (expr);
emit_label (label);
expr = build (MODIFY_EXPR, TREE_TYPE (TREE_TYPE (current_function_decl)),
DECL_RESULT (current_function_decl),
TREE_OPERAND (retval_rhs, 2));
TREE_SIDE_EFFECTS (expr) = 1;
expand_return (expr);
end_cleanup_deferral ();
return;
}
result_rtl = DECL_RTL (DECL_RESULT (current_function_decl));
if (retval_rhs != 0
&& TYPE_MODE (TREE_TYPE (retval_rhs)) == BLKmode
&& GET_CODE (result_rtl) == REG)
{
int i;
unsigned HOST_WIDE_INT bitpos, xbitpos;
unsigned HOST_WIDE_INT big_endian_correction = 0;
unsigned HOST_WIDE_INT bytes
= int_size_in_bytes (TREE_TYPE (retval_rhs));
int n_regs = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
unsigned int bitsize
= MIN (TYPE_ALIGN (TREE_TYPE (retval_rhs)), BITS_PER_WORD);
rtx *result_pseudos = (rtx *) alloca (sizeof (rtx) * n_regs);
rtx result_reg, src = NULL_RTX, dst = NULL_RTX;
rtx result_val = expand_expr (retval_rhs, NULL_RTX, VOIDmode, 0);
enum machine_mode tmpmode, result_reg_mode;
if (bytes == 0)
{
expand_null_return ();
return;
}
if (BYTES_BIG_ENDIAN
&& !FUNCTION_ARG_REG_LITTLE_ENDIAN
&& bytes % UNITS_PER_WORD)
big_endian_correction = (BITS_PER_WORD - ((bytes % UNITS_PER_WORD)
* BITS_PER_UNIT));
for (bitpos = 0, xbitpos = big_endian_correction;
bitpos < bytes * BITS_PER_UNIT;
bitpos += bitsize, xbitpos += bitsize)
{
if (xbitpos % BITS_PER_WORD == 0
|| xbitpos == big_endian_correction)
{
dst = gen_reg_rtx (word_mode);
result_pseudos[xbitpos / BITS_PER_WORD] = dst;
emit_move_insn (dst, CONST0_RTX (GET_MODE (dst)));
}
if (bitpos % BITS_PER_WORD == 0)
src = operand_subword_force (result_val,
bitpos / BITS_PER_WORD,
BLKmode);
store_bit_field (dst, bitsize, xbitpos % BITS_PER_WORD, word_mode,
extract_bit_field (src, bitsize,
bitpos % BITS_PER_WORD, 1,
NULL_RTX, word_mode, word_mode,
BITS_PER_WORD),
BITS_PER_WORD);
}
for (tmpmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
tmpmode != VOIDmode;
tmpmode = GET_MODE_WIDER_MODE (tmpmode))
if (GET_MODE_SIZE (tmpmode) >= bytes)
break;
if (tmpmode == VOIDmode)
abort ();
PUT_MODE (result_rtl, tmpmode);
if (GET_MODE_SIZE (tmpmode) < GET_MODE_SIZE (word_mode))
result_reg_mode = word_mode;
else
result_reg_mode = tmpmode;
result_reg = gen_reg_rtx (result_reg_mode);
emit_queue ();
for (i = 0; i < n_regs; i++)
emit_move_insn (operand_subword (result_reg, i, 0, result_reg_mode),
result_pseudos[i]);
if (tmpmode != result_reg_mode)
result_reg = gen_lowpart (tmpmode, result_reg);
expand_value_return (result_reg);
}
else if (retval_rhs != 0
&& !VOID_TYPE_P (TREE_TYPE (retval_rhs))
&& (GET_CODE (result_rtl) == REG
|| (GET_CODE (result_rtl) == PARALLEL)))
{
tree ot = TREE_TYPE (DECL_RESULT (current_function_decl));
tree nt = build_qualified_type (ot, TYPE_QUALS (ot) | TYPE_QUAL_CONST);
val = assign_temp (nt, 0, 0, 1);
val = expand_expr (retval_rhs, val, GET_MODE (val), 0);
val = force_not_mem (val);
emit_queue ();
expand_value_return (val);
}
else
{
expand_expr (retval, const0_rtx, VOIDmode, 0);
emit_queue ();
expand_value_return (result_rtl);
}
}
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;
}
int
optimize_tail_recursion (arguments, last_insn)
tree arguments;
rtx last_insn;
{
if (tail_recursion_args (arguments, DECL_ARGUMENTS (current_function_decl)))
{
if (tail_recursion_label == 0)
{
tail_recursion_label = gen_label_rtx ();
emit_label_after (tail_recursion_label,
tail_recursion_reentry);
}
emit_queue ();
expand_goto_internal (NULL_TREE, tail_recursion_label, last_insn);
emit_barrier ();
return 1;
}
return 0;
}
static int
tail_recursion_args (actuals, formals)
tree actuals, formals;
{
tree a = actuals, f = formals;
int i;
rtx *argvec;
for (a = actuals, f = formals, i = 0; a && f; a = TREE_CHAIN (a), f = TREE_CHAIN (f), i++)
{
if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_VALUE (a)))
!= TYPE_MAIN_VARIANT (TREE_TYPE (f)))
return 0;
if (GET_CODE (DECL_RTL (f)) != REG || DECL_MODE (f) == BLKmode)
return 0;
}
if (a != 0 || f != 0)
return 0;
argvec = (rtx *) alloca (i * sizeof (rtx));
for (a = actuals, i = 0; a; a = TREE_CHAIN (a), i++)
argvec[i] = expand_expr (TREE_VALUE (a), NULL_RTX, VOIDmode, 0);
for (a = actuals, i = 0; a; a = TREE_CHAIN (a), i++)
{
int copy = 0;
int j;
for (f = formals, j = 0; j < i; f = TREE_CHAIN (f), j++)
if (reg_mentioned_p (DECL_RTL (f), argvec[i]))
{
copy = 1;
break;
}
if (copy)
argvec[i] = copy_to_reg (argvec[i]);
}
for (f = formals, a = actuals, i = 0; f;
f = TREE_CHAIN (f), a = TREE_CHAIN (a), i++)
{
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))));
}
free_temp_slots ();
return 1;
}
void
expand_start_bindings_and_block (flags, block)
int flags;
tree block;
{
struct nesting *thisblock = ALLOC_NESTING ();
rtx note;
int exit_flag = ((flags & 1) != 0);
int block_flag = ((flags & 2) == 0);
if (!block_flag && block)
abort ();
if (block_flag)
{
note = emit_note (NULL, NOTE_INSN_BLOCK_BEG);
NOTE_BLOCK (note) = block;
}
else
note = emit_note (NULL, NOTE_INSN_DELETED);
thisblock->next = block_stack;
thisblock->all = nesting_stack;
thisblock->depth = ++nesting_depth;
thisblock->data.block.stack_level = 0;
thisblock->data.block.cleanups = 0;
thisblock->data.block.n_function_calls = 0;
thisblock->data.block.exception_region = 0;
thisblock->data.block.block_target_temp_slot_level = target_temp_slot_level;
thisblock->data.block.conditional_code = 0;
thisblock->data.block.last_unconditional_cleanup = note;
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
&& block_stack->data.block.outer_cleanups == NULL_TREE))
thisblock->data.block.outer_cleanups
= tree_cons (NULL_TREE, block_stack->data.block.cleanups,
block_stack->data.block.outer_cleanups);
else
thisblock->data.block.outer_cleanups = 0;
thisblock->data.block.label_chain = 0;
thisblock->data.block.innermost_stack_block = stack_block_stack;
thisblock->data.block.first_insn = note;
thisblock->data.block.block_start_count = ++current_block_start_count;
thisblock->exit_label = exit_flag ? gen_label_rtx () : 0;
block_stack = thisblock;
nesting_stack = thisblock;
push_temp_slots ();
}
void
expand_start_target_temps ()
{
push_temp_slots ();
expand_start_bindings (2);
target_temp_slot_level = temp_slot_level;
}
void
expand_end_target_temps ()
{
expand_end_bindings (NULL_TREE, 0, 0);
pop_temp_slots ();
}
int
is_body_block (stmt)
tree stmt;
{
if (TREE_CODE (stmt) == BLOCK)
{
tree parent = BLOCK_SUPERCONTEXT (stmt);
if (parent && TREE_CODE (parent) == BLOCK)
{
tree grandparent = BLOCK_SUPERCONTEXT (parent);
if (grandparent && TREE_CODE (grandparent) == FUNCTION_DECL)
return 1;
}
}
return 0;
}
int
conditional_context ()
{
return block_stack && block_stack->data.block.conditional_code;
}
struct nesting *
current_nesting_level ()
{
return cfun ? block_stack : 0;
}
static rtx
expand_nl_handler_label (slot, before_insn)
rtx slot, before_insn;
{
rtx insns;
rtx handler_label = gen_label_rtx ();
LABEL_PRESERVE_P (handler_label) = 1;
start_sequence ();
emit_move_insn (slot, gen_rtx_LABEL_REF (Pmode, handler_label));
insns = get_insns ();
end_sequence ();
emit_insns_before (insns, before_insn);
emit_label (handler_label);
return handler_label;
}
static void
expand_nl_goto_receiver ()
{
#ifdef HAVE_nonlocal_goto
if (! HAVE_nonlocal_goto)
#endif
emit_move_insn (virtual_stack_vars_rtx, hard_frame_pointer_rtx);
#if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
if (fixed_regs[ARG_POINTER_REGNUM])
{
#ifdef ELIMINABLE_REGS
static const struct elims {const int from, to;} elim_regs[] = ELIMINABLE_REGS;
size_t i;
for (i = 0; i < ARRAY_SIZE (elim_regs); i++)
if (elim_regs[i].from == ARG_POINTER_REGNUM
&& elim_regs[i].to == HARD_FRAME_POINTER_REGNUM)
break;
if (i == ARRAY_SIZE (elim_regs))
#endif
{
emit_move_insn (virtual_incoming_args_rtx,
copy_to_reg (get_arg_pointer_save_area (cfun)));
}
}
#endif
#ifdef HAVE_nonlocal_goto_receiver
if (HAVE_nonlocal_goto_receiver)
emit_insn (gen_nonlocal_goto_receiver ());
#endif
}
static void
expand_nl_goto_receivers (thisblock)
struct nesting *thisblock;
{
tree link;
rtx afterward = gen_label_rtx ();
rtx insns, slot;
rtx label_list;
int any_invalid;
if (thisblock->next != 0)
for (slot = nonlocal_goto_handler_slots; slot; slot = XEXP (slot, 1))
{
rtx save_receiver = gen_reg_rtx (Pmode);
emit_move_insn (XEXP (slot, 0), save_receiver);
start_sequence ();
emit_move_insn (save_receiver, XEXP (slot, 0));
insns = get_insns ();
end_sequence ();
emit_insns_before (insns, thisblock->data.block.first_insn);
}
emit_jump (afterward);
link = nonlocal_labels;
slot = nonlocal_goto_handler_slots;
label_list = NULL_RTX;
for (; link; link = TREE_CHAIN (link), slot = XEXP (slot, 1))
if (! DECL_TOO_LATE (TREE_VALUE (link)))
{
rtx lab;
lab = expand_nl_handler_label (XEXP (slot, 0),
thisblock->data.block.first_insn);
label_list = gen_rtx_EXPR_LIST (VOIDmode, lab, label_list);
expand_nl_goto_receiver ();
expand_goto (TREE_VALUE (link));
}
link = nonlocal_labels;
slot = nonlocal_goto_handler_slots;
any_invalid = 0;
for (; link; link = TREE_CHAIN (link), slot = XEXP (slot, 1))
if (DECL_TOO_LATE (TREE_VALUE (link)))
{
rtx lab;
lab = expand_nl_handler_label (XEXP (slot, 0),
thisblock->data.block.first_insn);
label_list = gen_rtx_EXPR_LIST (VOIDmode, lab, label_list);
any_invalid = 1;
}
if (any_invalid)
{
expand_nl_goto_receiver ();
expand_builtin_trap ();
}
nonlocal_goto_handler_labels = label_list;
emit_label (afterward);
}
void
warn_about_unused_variables (vars)
tree vars;
{
tree decl;
if (warn_unused_variable)
for (decl = vars; decl; decl = TREE_CHAIN (decl))
if (TREE_CODE (decl) == VAR_DECL
&& ! TREE_USED (decl)
&& ! DECL_IN_SYSTEM_HEADER (decl)
&& DECL_NAME (decl) && ! DECL_ARTIFICIAL (decl))
warning_with_decl (decl, "unused variable `%s'");
}
void
expand_end_bindings (vars, mark_ends, dont_jump_in)
tree vars;
int mark_ends;
int dont_jump_in;
{
struct nesting *thisblock = block_stack;
warn_about_unused_variables (vars);
if (thisblock->exit_label)
{
do_pending_stack_adjust ();
emit_label (thisblock->exit_label);
}
if (function_call_count != thisblock->data.block.n_function_calls
&& nonlocal_labels
&& (thisblock->next == 0 ? current_function_has_nonlocal_label
: (thisblock->data.block.cleanups != 0
|| thisblock->data.block.stack_level != 0)))
expand_nl_goto_receivers (thisblock);
if (dont_jump_in
|| thisblock->data.block.stack_level != 0)
{
struct label_chain *chain;
for (chain = thisblock->data.block.label_chain; chain; chain = chain->next)
{
DECL_TOO_LATE (chain->label) = 1;
if (TREE_ADDRESSABLE (chain->label))
error_with_decl (chain->label,
"label `%s' used before containing binding contour");
}
}
if (thisblock->data.block.stack_level != 0
|| thisblock->data.block.cleanups != 0)
{
int reachable;
rtx insn;
int old_expr_stmts_for_value = expr_stmts_for_value;
rtx old_last_expr_value = last_expr_value;
tree old_last_expr_type = last_expr_type;
expr_stmts_for_value = 0;
insn = get_last_insn ();
if (GET_CODE (insn) == NOTE)
insn = prev_nonnote_insn (insn);
reachable = (! insn || GET_CODE (insn) != BARRIER);
expand_cleanups (thisblock->data.block.cleanups, NULL_TREE, 0, reachable);
if (reachable)
do_pending_stack_adjust ();
expr_stmts_for_value = old_expr_stmts_for_value;
last_expr_value = old_last_expr_value;
last_expr_type = old_last_expr_type;
if (reachable && thisblock->data.block.stack_level != 0)
{
emit_stack_restore (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION,
thisblock->data.block.stack_level, NULL_RTX);
if (nonlocal_goto_handler_slots != 0)
emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level,
NULL_RTX);
}
fixup_gotos (thisblock,
thisblock->data.block.stack_level,
thisblock->data.block.cleanups,
thisblock->data.block.first_insn,
dont_jump_in);
}
if (mark_ends)
{
rtx note = emit_note (NULL, NOTE_INSN_BLOCK_END);
NOTE_BLOCK (note) = NOTE_BLOCK (thisblock->data.block.first_insn);
}
else
NOTE_LINE_NUMBER (thisblock->data.block.first_insn) = NOTE_INSN_DELETED;
target_temp_slot_level = thisblock->data.block.block_target_temp_slot_level;
stack_block_stack = thisblock->data.block.innermost_stack_block;
POPSTACK (block_stack);
pop_temp_slots ();
}
void
save_stack_pointer ()
{
struct nesting *thisblock = block_stack;
if (thisblock->data.block.stack_level == 0)
{
emit_stack_save (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION,
&thisblock->data.block.stack_level,
thisblock->data.block.first_insn);
stack_block_stack = thisblock;
}
}
void
expand_decl (decl)
tree decl;
{
struct nesting *thisblock;
tree type;
type = TREE_TYPE (decl);
if (TREE_CODE (decl) == CONST_DECL)
{
DECL_MODE (decl) = TYPE_MODE (type);
DECL_ALIGN (decl) = TYPE_ALIGN (type);
DECL_SIZE (decl) = TYPE_SIZE (type);
DECL_SIZE_UNIT (decl) = TYPE_SIZE_UNIT (type);
return;
}
if (TREE_CODE (decl) != VAR_DECL)
return;
if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
return;
thisblock = block_stack;
if (type == error_mark_node)
SET_DECL_RTL (decl, gen_rtx_MEM (BLKmode, const0_rtx));
else if (DECL_SIZE (decl) == 0)
{
rtx x;
if (DECL_INITIAL (decl) == 0)
x = gen_rtx_MEM (BLKmode, const0_rtx);
else
x = gen_rtx_MEM (BLKmode, gen_reg_rtx (Pmode));
set_mem_attributes (x, decl, 1);
SET_DECL_RTL (decl, x);
}
else if (DECL_MODE (decl) != BLKmode
&& !(flag_float_store
&& TREE_CODE (type) == REAL_TYPE)
&& ! TREE_THIS_VOLATILE (decl)
&& (DECL_REGISTER (decl) || optimize))
{
int unsignedp = TREE_UNSIGNED (type);
enum machine_mode reg_mode
= promote_mode (type, DECL_MODE (decl), &unsignedp, 0);
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))
mark_reg_pointer (DECL_RTL (decl),
TYPE_ALIGN (TREE_TYPE (TREE_TYPE (decl))));
maybe_set_unchanging (DECL_RTL (decl), decl);
if (TREE_ADDRESSABLE (decl))
put_var_into_stack (decl);
}
else if (TREE_CODE (DECL_SIZE_UNIT (decl)) == INTEGER_CST
&& ! (flag_stack_check && ! STACK_CHECK_BUILTIN
&& 0 < compare_tree_int (DECL_SIZE_UNIT (decl),
STACK_CHECK_MAX_VAR_SIZE)))
{
rtx oldaddr = 0;
rtx addr;
rtx x;
if (DECL_RTL_SET_P (decl))
{
if (GET_CODE (DECL_RTL (decl)) != MEM
|| GET_CODE (XEXP (DECL_RTL (decl), 0)) != REG)
abort ();
oldaddr = XEXP (DECL_RTL (decl), 0);
}
DECL_ALIGN (decl) = (DECL_MODE (decl) == BLKmode ? BIGGEST_ALIGNMENT
: GET_MODE_BITSIZE (DECL_MODE (decl)));
DECL_USER_ALIGN (decl) = 0;
x = assign_temp (decl, 1, 1, 1);
set_mem_attributes (x, decl, 1);
SET_DECL_RTL (decl, x);
if (oldaddr)
{
addr = force_operand (XEXP (DECL_RTL (decl), 0), oldaddr);
if (addr != oldaddr)
emit_move_insn (oldaddr, addr);
}
}
else
{
rtx address, size, x;
do_pending_stack_adjust ();
save_stack_pointer ();
if (TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type))
expand_expr (TYPE_MAX_VALUE (TYPE_DOMAIN (type)),
const0_rtx, VOIDmode, 0);
size = expand_expr (DECL_SIZE_UNIT (decl), NULL_RTX, VOIDmode, 0);
free_temp_slots ();
address = allocate_dynamic_stack_space (size, NULL_RTX,
TYPE_ALIGN (TREE_TYPE (decl)));
x = gen_rtx_MEM (DECL_MODE (decl), address);
set_mem_attributes (x, decl, 1);
SET_DECL_RTL (decl, x);
#ifdef STACK_BOUNDARY
DECL_ALIGN (decl) = STACK_BOUNDARY;
#else
DECL_ALIGN (decl) = BIGGEST_ALIGNMENT;
#endif
DECL_USER_ALIGN (decl) = 0;
}
}
void
expand_decl_init (decl)
tree decl;
{
int was_used = TREE_USED (decl);
if (TREE_CODE (decl) == CONST_DECL
|| TREE_STATIC (decl))
return;
if (DECL_INITIAL (decl) == error_mark_node)
{
enum tree_code code = TREE_CODE (TREE_TYPE (decl));
if (code == INTEGER_TYPE || code == REAL_TYPE || code == ENUMERAL_TYPE
|| code == POINTER_TYPE || code == REFERENCE_TYPE)
expand_assignment (decl, convert (TREE_TYPE (decl), integer_zero_node),
0, 0);
emit_queue ();
}
else if (DECL_INITIAL (decl) && TREE_CODE (DECL_INITIAL (decl)) != TREE_LIST)
{
emit_line_note (DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl));
expand_assignment (decl, DECL_INITIAL (decl), 0, 0);
emit_queue ();
}
TREE_USED (decl) = was_used;
preserve_temp_slots (NULL_RTX);
free_temp_slots ();
}
int
expand_decl_cleanup (decl, cleanup)
tree decl, cleanup;
{
struct nesting *thisblock;
if (cfun == 0 || block_stack == 0)
return 0;
thisblock = block_stack;
if (cleanup != 0)
{
tree t;
rtx seq;
tree *cleanups = &thisblock->data.block.cleanups;
int cond_context = conditional_context ();
if (cond_context)
{
rtx flag = gen_reg_rtx (word_mode);
rtx set_flag_0;
tree cond;
start_sequence ();
emit_move_insn (flag, const0_rtx);
set_flag_0 = get_insns ();
end_sequence ();
thisblock->data.block.last_unconditional_cleanup
= emit_insns_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));
SET_DECL_RTL (cond, flag);
cleanup = build (COND_EXPR, void_type_node,
truthvalue_conversion (cond),
cleanup, integer_zero_node);
cleanup = fold (cleanup);
cleanups = thisblock->data.block.cleanup_ptr;
}
cleanup = unsave_expr (cleanup);
t = *cleanups = tree_cons (decl, cleanup, *cleanups);
if (! cond_context)
stack_block_stack = thisblock;
if (cond_context)
{
start_sequence ();
}
if (! using_eh_for_cleanups_p)
TREE_ADDRESSABLE (t) = 1;
else
expand_eh_region_start ();
if (cond_context)
{
seq = get_insns ();
end_sequence ();
if (seq)
thisblock->data.block.last_unconditional_cleanup
= emit_insns_after (seq,
thisblock->data.block.last_unconditional_cleanup);
}
else
{
thisblock->data.block.last_unconditional_cleanup
= get_last_insn ();
emit_note (NULL, NOTE_INSN_DELETED);
thisblock->data.block.cleanup_ptr = &thisblock->data.block.cleanups;
}
}
return 1;
}
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;
}
void
expand_anon_union_decl (decl, cleanup, decl_elts)
tree decl, cleanup, decl_elts;
{
struct nesting *thisblock = cfun == 0 ? 0 : block_stack;
rtx x;
tree t;
for (t = decl_elts; t; t = TREE_CHAIN (t))
if (TREE_ADDRESSABLE (TREE_VALUE (t)))
{
TREE_ADDRESSABLE (decl) = 1;
break;
}
expand_decl (decl);
expand_decl_cleanup (decl, cleanup);
x = DECL_RTL (decl);
for (t = decl_elts; t; t = TREE_CHAIN (t))
{
tree decl_elt = TREE_VALUE (t);
tree cleanup_elt = TREE_PURPOSE (t);
enum machine_mode mode = TYPE_MODE (TREE_TYPE (decl_elt));
if (TREE_USED (decl_elt))
TREE_USED (decl) = 1;
DECL_ALIGN (decl_elt) = DECL_ALIGN (decl);
DECL_USER_ALIGN (decl_elt) = DECL_USER_ALIGN (decl);
if (mode == BLKmode && DECL_MODE (decl) != BLKmode)
DECL_MODE (decl_elt) = mode
= mode_for_size_tree (DECL_SIZE (decl_elt), MODE_INT, 1);
if (GET_CODE (x) == MEM)
{
if (mode == GET_MODE (x))
SET_DECL_RTL (decl_elt, x);
else
SET_DECL_RTL (decl_elt, adjust_address_nv (x, mode, 0));
}
else if (GET_CODE (x) == REG)
{
if (mode == GET_MODE (x))
SET_DECL_RTL (decl_elt, x);
else
SET_DECL_RTL (decl_elt, gen_lowpart_SUBREG (mode, x));
}
else
abort ();
if (cleanup != 0)
thisblock->data.block.cleanups
= tree_cons (decl_elt, cleanup_elt,
thisblock->data.block.cleanups);
}
}
static void
expand_cleanups (list, dont_do, in_fixup, reachable)
tree list;
tree dont_do;
int in_fixup;
int reachable;
{
tree tail;
for (tail = list; tail; tail = TREE_CHAIN (tail))
if (dont_do == 0 || TREE_PURPOSE (tail) != dont_do)
{
if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST)
expand_cleanups (TREE_VALUE (tail), dont_do, in_fixup, reachable);
else
{
if (! in_fixup && using_eh_for_cleanups_p)
expand_eh_region_end_cleanup (TREE_VALUE (tail));
if (reachable && !CLEANUP_EH_ONLY (tail))
{
if (in_fixup && using_eh_for_cleanups_p)
{
expand_eh_region_start ();
expand_expr (TREE_VALUE (tail), const0_rtx, VOIDmode, 0);
expand_eh_region_end_fixup (TREE_VALUE (tail));
}
else
expand_expr (TREE_VALUE (tail), const0_rtx, VOIDmode, 0);
free_temp_slots ();
}
}
}
}
void
start_cleanup_deferral ()
{
if (block_stack)
++block_stack->data.block.conditional_code;
}
void
end_cleanup_deferral ()
{
if (block_stack)
--block_stack->data.block.conditional_code;
}
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 ()
{
if (block_stack == 0)
return 0;
return block_stack->data.block.cleanups;
}
int
any_pending_cleanups (this_contour)
int this_contour;
{
struct nesting *block;
if (cfun == NULL || cfun->stmt == NULL || block_stack == 0)
return 0;
if (this_contour && block_stack->data.block.cleanups != NULL)
return 1;
if (block_stack->data.block.cleanups == 0
&& block_stack->data.block.outer_cleanups == 0)
return 0;
for (block = block_stack->next; block; block = block->next)
if (block->data.block.cleanups != 0)
return 1;
return 0;
}
void
expand_start_case (exit_flag, expr, type, printname)
int exit_flag;
tree expr;
tree type;
const char *printname;
{
struct nesting *thiscase = ALLOC_NESTING ();
thiscase->next = case_stack;
thiscase->all = nesting_stack;
thiscase->depth = ++nesting_depth;
thiscase->exit_label = exit_flag ? gen_label_rtx () : 0;
thiscase->data.case_stmt.case_list = 0;
thiscase->data.case_stmt.index_expr = expr;
thiscase->data.case_stmt.nominal_type = type;
thiscase->data.case_stmt.default_label = 0;
thiscase->data.case_stmt.printname = printname;
thiscase->data.case_stmt.line_number_status = force_line_numbers ();
case_stack = thiscase;
nesting_stack = thiscase;
do_pending_stack_adjust ();
if (GET_CODE (get_last_insn ()) != NOTE)
emit_note (NULL, NOTE_INSN_DELETED);
thiscase->data.case_stmt.start = get_last_insn ();
start_cleanup_deferral ();
}
void
expand_start_case_dummy ()
{
struct nesting *thiscase = ALLOC_NESTING ();
thiscase->next = case_stack;
thiscase->all = nesting_stack;
thiscase->depth = ++nesting_depth;
thiscase->exit_label = 0;
thiscase->data.case_stmt.case_list = 0;
thiscase->data.case_stmt.start = 0;
thiscase->data.case_stmt.nominal_type = 0;
thiscase->data.case_stmt.default_label = 0;
case_stack = thiscase;
nesting_stack = thiscase;
start_cleanup_deferral ();
}
void
expand_end_case_dummy ()
{
end_cleanup_deferral ();
POPSTACK (case_stack);
}
tree
case_index_expr_type ()
{
if (case_stack)
return TREE_TYPE (case_stack->data.case_stmt.index_expr);
return 0;
}
static void
check_seenlabel ()
{
if (case_stack->data.case_stmt.line_number_status >= 0)
{
rtx insn;
restore_line_number_status
(case_stack->data.case_stmt.line_number_status);
case_stack->data.case_stmt.line_number_status = -1;
for (insn = case_stack->data.case_stmt.start;
insn;
insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == CODE_LABEL)
break;
if (GET_CODE (insn) != NOTE
&& (GET_CODE (insn) != INSN || GET_CODE (PATTERN (insn)) != USE))
{
do
insn = PREV_INSN (insn);
while (insn && (GET_CODE (insn) != NOTE || NOTE_LINE_NUMBER (insn) < 0));
if (insn)
warning_with_file_and_line (NOTE_SOURCE_FILE (insn),
NOTE_LINE_NUMBER (insn),
"unreachable code at beginning of %s",
case_stack->data.case_stmt.printname);
break;
}
}
}
}
int
pushcase (value, converter, label, duplicate)
tree value;
tree (*converter) PARAMS ((tree, tree));
tree label;
tree *duplicate;
{
tree index_type;
tree nominal_type;
if (! (case_stack && case_stack->data.case_stmt.start))
return 1;
if (stack_block_stack
&& stack_block_stack->depth > case_stack->depth)
return 5;
index_type = TREE_TYPE (case_stack->data.case_stmt.index_expr);
nominal_type = case_stack->data.case_stmt.nominal_type;
if (index_type == error_mark_node)
return 0;
if (value != 0)
value = (*converter) (nominal_type, value);
check_seenlabel ();
if (value != 0
&& (TREE_CONSTANT_OVERFLOW (value)
|| ! int_fits_type_p (value, index_type)))
return 3;
return add_case_node (value, value, label, duplicate);
}
int
pushcase_range (value1, value2, converter, label, duplicate)
tree value1, value2;
tree (*converter) PARAMS ((tree, tree));
tree label;
tree *duplicate;
{
tree index_type;
tree nominal_type;
if (! (case_stack && case_stack->data.case_stmt.start))
return 1;
if (stack_block_stack
&& stack_block_stack->depth > case_stack->depth)
return 5;
index_type = TREE_TYPE (case_stack->data.case_stmt.index_expr);
nominal_type = case_stack->data.case_stmt.nominal_type;
if (index_type == error_mark_node)
return 0;
check_seenlabel ();
if (value1 == 0)
value1 = TYPE_MIN_VALUE (index_type);
if (value2 == 0)
value2 = TYPE_MAX_VALUE (index_type);
if (value2 != 0 && tree_int_cst_lt (value2, value1))
return 4;
if (value2 == 0)
value2 = TYPE_MAX_VALUE (nominal_type);
value1 = (*converter) (nominal_type, value1);
value2 = (*converter) (nominal_type, value2);
if (TREE_CONSTANT_OVERFLOW (value1)
|| ! int_fits_type_p (value1, index_type))
return 3;
if (TREE_CONSTANT_OVERFLOW (value2)
|| ! int_fits_type_p (value2, index_type))
return 3;
return add_case_node (value1, value2, label, duplicate);
}
int
add_case_node (low, high, label, duplicate)
tree low, high;
tree label;
tree *duplicate;
{
struct case_node *p, **q, *r;
if (!high)
high = low;
if (!high && !low)
{
if (case_stack->data.case_stmt.default_label != 0)
{
*duplicate = case_stack->data.case_stmt.default_label;
return 2;
}
case_stack->data.case_stmt.default_label = label;
expand_label (label);
return 0;
}
q = &case_stack->data.case_stmt.case_list;
p = *q;
while ((r = *q))
{
p = r;
if (tree_int_cst_lt (high, p->low))
q = &p->left;
else if (tree_int_cst_lt (p->high, low))
q = &p->right;
else
{
*duplicate = p->code_label;
return 2;
}
}
r = (struct case_node *) xmalloc (sizeof (struct case_node));
r->low = low;
if (tree_int_cst_equal (low, high))
r->high = r->low;
else
r->high = high;
r->code_label = label;
expand_label (label);
*q = r;
r->parent = p;
r->left = 0;
r->right = 0;
r->balance = 0;
while (p)
{
struct case_node *s;
if (r == p->left)
{
int b;
if (! (b = p->balance))
p->balance = -1;
else if (b < 0)
{
if (r->balance < 0)
{
if ((p->left = s = r->right))
s->parent = p;
r->right = p;
p->balance = 0;
r->balance = 0;
s = p->parent;
p->parent = r;
if ((r->parent = s))
{
if (s->left == p)
s->left = r;
else
s->right = r;
}
else
case_stack->data.case_stmt.case_list = r;
}
else
{
int b2;
struct case_node *t = r->right;
if ((p->left = s = t->right))
s->parent = p;
t->right = p;
if ((r->right = s = t->left))
s->parent = r;
t->left = r;
b = t->balance;
b2 = b < 0;
p->balance = b2;
b2 = -b2 - b;
r->balance = b2;
t->balance = 0;
s = p->parent;
p->parent = t;
r->parent = t;
if ((t->parent = s))
{
if (s->left == p)
s->left = t;
else
s->right = t;
}
else
case_stack->data.case_stmt.case_list = t;
}
break;
}
else
{
p->balance = 0;
break;
}
}
else
{
int b;
if (! (b = p->balance))
p->balance++;
else if (b > 0)
{
if (r->balance > 0)
{
if ((p->right = s = r->left))
s->parent = p;
r->left = p;
p->balance = 0;
r->balance = 0;
s = p->parent;
p->parent = r;
if ((r->parent = s))
{
if (s->left == p)
s->left = r;
else
s->right = r;
}
else
case_stack->data.case_stmt.case_list = r;
}
else
{
int b2;
struct case_node *t = r->left;
if ((p->right = s = t->left))
s->parent = p;
t->left = p;
if ((r->left = s = t->right))
s->parent = r;
t->right = r;
b = t->balance;
b2 = b < 0;
r->balance = b2;
b2 = -b2 - b;
p->balance = b2;
t->balance = 0;
s = p->parent;
p->parent = t;
r->parent = t;
if ((t->parent = s))
{
if (s->left == p)
s->left = t;
else
s->right = t;
}
else
case_stack->data.case_stmt.case_list = t;
}
break;
}
else
{
p->balance = 0;
break;
}
}
r = p;
p = p->parent;
}
return 0;
}
HOST_WIDE_INT
all_cases_count (type, sparseness)
tree type;
int *sparseness;
{
tree t;
HOST_WIDE_INT count, minval, lastval;
*sparseness = 0;
switch (TREE_CODE (type))
{
case BOOLEAN_TYPE:
count = 2;
break;
case CHAR_TYPE:
count = 1 << BITS_PER_UNIT;
break;
default:
case INTEGER_TYPE:
if (TYPE_MAX_VALUE (type) != 0
&& 0 != (t = fold (build (MINUS_EXPR, type, TYPE_MAX_VALUE (type),
TYPE_MIN_VALUE (type))))
&& 0 != (t = fold (build (PLUS_EXPR, type, t,
convert (type, integer_zero_node))))
&& host_integerp (t, 1))
count = tree_low_cst (t, 1);
else
return -1;
break;
case ENUMERAL_TYPE:
if (! host_integerp (TYPE_MIN_VALUE (type), 0)
|| TYPE_MAX_VALUE (type) == 0
|| ! host_integerp (TYPE_MAX_VALUE (type), 0))
return -1;
lastval = minval = tree_low_cst (TYPE_MIN_VALUE (type), 0);
count = 0;
for (t = TYPE_VALUES (type); t != NULL_TREE; t = TREE_CHAIN (t))
{
HOST_WIDE_INT thisval = tree_low_cst (TREE_VALUE (t), 0);
if (*sparseness == 2 || thisval <= lastval)
*sparseness = 2;
else if (thisval != minval + count)
*sparseness = 1;
lastval = thisval;
count++;
}
}
return count;
}
#define BITARRAY_TEST(ARRAY, INDEX) \
((ARRAY)[(unsigned) (INDEX) / HOST_BITS_PER_CHAR]\
& (1 << ((unsigned) (INDEX) % HOST_BITS_PER_CHAR)))
#define BITARRAY_SET(ARRAY, INDEX) \
((ARRAY)[(unsigned) (INDEX) / HOST_BITS_PER_CHAR]\
|= 1 << ((unsigned) (INDEX) % HOST_BITS_PER_CHAR))
void
mark_seen_cases (type, cases_seen, count, sparseness)
tree type;
unsigned char *cases_seen;
HOST_WIDE_INT count;
int sparseness;
{
tree next_node_to_try = NULL_TREE;
HOST_WIDE_INT next_node_offset = 0;
struct case_node *n, *root = case_stack->data.case_stmt.case_list;
tree val = make_node (INTEGER_CST);
TREE_TYPE (val) = type;
if (! root)
;
else if (sparseness == 2)
{
tree t;
unsigned HOST_WIDE_INT xlo;
TREE_TYPE (val) = TREE_TYPE (root->low);
for (t = TYPE_VALUES (type), xlo = 0; t != NULL_TREE;
t = TREE_CHAIN (t), xlo++)
{
TREE_INT_CST_LOW (val) = TREE_INT_CST_LOW (TREE_VALUE (t));
TREE_INT_CST_HIGH (val) = TREE_INT_CST_HIGH (TREE_VALUE (t));
n = root;
do
{
if (tree_int_cst_lt (val, n->low))
n = n->left;
else if (tree_int_cst_lt (n->high, val))
n = n->right;
else
{
BITARRAY_SET (cases_seen, xlo);
break;
}
}
while (n);
}
}
else
{
if (root->left)
case_stack->data.case_stmt.case_list = root = case_tree2list (root, 0);
for (n = root; n; n = n->right)
{
TREE_INT_CST_LOW (val) = TREE_INT_CST_LOW (n->low);
TREE_INT_CST_HIGH (val) = TREE_INT_CST_HIGH (n->low);
while (! tree_int_cst_lt (n->high, val))
{
unsigned HOST_WIDE_INT xlo;
HOST_WIDE_INT xhi;
tree t;
if (sparseness && TYPE_VALUES (type) != NULL_TREE)
{
t = next_node_to_try;
xlo = next_node_offset;
xhi = 0;
for (;;)
{
if (t == NULL_TREE)
{
t = TYPE_VALUES (type);
xlo = 0;
}
if (tree_int_cst_equal (val, TREE_VALUE (t)))
{
next_node_to_try = TREE_CHAIN (t);
next_node_offset = xlo + 1;
break;
}
xlo++;
t = TREE_CHAIN (t);
if (t == next_node_to_try)
{
xlo = -1;
break;
}
}
}
else
{
t = TYPE_MIN_VALUE (type);
if (t)
neg_double (TREE_INT_CST_LOW (t), TREE_INT_CST_HIGH (t),
&xlo, &xhi);
else
xlo = xhi = 0;
add_double (xlo, xhi,
TREE_INT_CST_LOW (val), TREE_INT_CST_HIGH (val),
&xlo, &xhi);
}
if (xhi == 0 && xlo < (unsigned HOST_WIDE_INT) count)
BITARRAY_SET (cases_seen, xlo);
add_double (TREE_INT_CST_LOW (val), TREE_INT_CST_HIGH (val),
1, 0,
&TREE_INT_CST_LOW (val), &TREE_INT_CST_HIGH (val));
}
}
}
}
void
check_for_full_enumeration_handling (type)
tree type;
{
struct case_node *n;
tree chain;
int sparseness = 0;
HOST_WIDE_INT size;
unsigned char *cases_seen;
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;
if (size > 0 && size < 600000
&& (cases_seen =
(unsigned char *) really_call_calloc (bytes_needed, 1)) != NULL)
{
HOST_WIDE_INT i;
tree v = TYPE_VALUES (type);
mark_seen_cases (type, cases_seen, size, sparseness);
for (i = 0; v != NULL_TREE && i < size; i++, v = TREE_CHAIN (v))
if (BITARRAY_TEST (cases_seen, i) == 0)
warning ("enumeration value `%s' not handled in switch",
IDENTIFIER_POINTER (TREE_PURPOSE (v)));
free (cases_seen);
}
if (case_stack->data.case_stmt.case_list
&& 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))
;
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))));
}
}
}
}
static void
free_case_nodes (cn)
case_node_ptr cn;
{
if (cn)
{
free_case_nodes (cn->left);
free_case_nodes (cn->right);
free (cn);
}
}
void
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;
rtx index;
rtx table_label;
int ncases;
rtx *labelvec;
int i;
rtx before_case, end;
struct nesting *thiscase = case_stack;
tree index_expr, index_type;
int unsignedp;
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 ();
check_seenlabel ();
if (index_type != error_mark_node)
{
if (!thiscase->data.case_stmt.default_label
&& TREE_CODE (orig_type) == ENUMERAL_TYPE
&& TREE_CODE (index_expr) != INTEGER_CST)
check_for_full_enumeration_handling (orig_type);
if (thiscase->data.case_stmt.default_label == 0)
{
thiscase->data.case_stmt.default_label
= build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
expand_label (thiscase->data.case_stmt.default_label);
}
default_label = label_rtx (thiscase->data.case_stmt.default_label);
before_case = get_last_insn ();
if (thiscase->data.case_stmt.case_list
&& thiscase->data.case_stmt.case_list->left)
thiscase->data.case_stmt.case_list
= case_tree2list (thiscase->data.case_stmt.case_list, 0);
group_case_nodes (thiscase->data.case_stmt.case_list);
count = 0;
for (n = thiscase->data.case_stmt.case_list; n; n = n->right)
{
if (TREE_CODE (n->low) != INTEGER_CST)
abort ();
if (TREE_CODE (n->high) != INTEGER_CST)
abort ();
n->low = convert (index_type, n->low);
n->high = convert (index_type, n->high);
if (count++ == 0)
{
minval = n->low;
maxval = n->high;
}
else
{
if (INT_CST_LT (n->low, minval))
minval = n->low;
if (INT_CST_LT (maxval, n->high))
maxval = n->high;
}
if (! tree_int_cst_equal (n->low, n->high))
count++;
}
if (count != 0)
range = fold (build (MINUS_EXPR, index_type, maxval, minval));
end_cleanup_deferral ();
if (count == 0)
{
expand_expr (index_expr, const0_rtx, VOIDmode, 0);
emit_queue ();
emit_jump (default_label);
}
else if (count < case_values_threshold ()
|| compare_tree_int (range, 10 * count) > 0
|| compare_tree_int (range, 0) < 0
#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))
{
index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0);
if (GET_MODE_CLASS (GET_MODE (index)) == MODE_INT
&& ! have_insn_for (COMPARE, GET_MODE (index)))
{
enum machine_mode wider_mode;
for (wider_mode = GET_MODE (index); wider_mode != VOIDmode;
wider_mode = GET_MODE_WIDER_MODE (wider_mode))
if (have_insn_for (COMPARE, wider_mode))
{
index = convert_to_mode (wider_mode, index, unsignedp);
break;
}
}
emit_queue ();
do_pending_stack_adjust ();
index = protect_from_queue (index, 0);
if (GET_CODE (index) == MEM)
index = copy_to_reg (index);
if (GET_CODE (index) == CONST_INT
|| TREE_CODE (index_expr) == INTEGER_CST)
{
if (TREE_CODE (index_expr) != INTEGER_CST)
{
index_expr
= build_int_2 (INTVAL (index),
unsignedp || INTVAL (index) >= 0 ? 0 : -1);
index_expr = convert (index_type, index_expr);
}
for (n = thiscase->data.case_stmt.case_list; n; n = n->right)
if (! tree_int_cst_lt (index_expr, n->low)
&& ! tree_int_cst_lt (n->high, index_expr))
break;
if (n)
emit_jump (label_rtx (n->code_label));
else
emit_jump (default_label);
}
else
{
use_cost_table
= (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,
default_label, index_type);
emit_jump_if_reachable (default_label);
}
}
else
{
if (! try_casesi (index_type, index_expr, minval, range,
table_label, default_label))
{
index_type = thiscase->data.case_stmt.nominal_type;
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 ();
}
ncases = tree_low_cst (range, 0) + 1;
labelvec = (rtx *) alloca (ncases * sizeof (rtx));
memset ((char *) labelvec, 0, ncases * sizeof (rtx));
for (n = thiscase->data.case_stmt.case_list; n; n = n->right)
{
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));
}
for (i = 0; i < ncases; i++)
if (labelvec[i] == 0)
labelvec[i] = gen_rtx_LABEL_REF (Pmode, default_label);
emit_label (table_label);
if (CASE_VECTOR_PC_RELATIVE || flag_pic)
emit_jump_insn (gen_rtx_ADDR_DIFF_VEC (CASE_VECTOR_MODE,
gen_rtx_LABEL_REF (Pmode, table_label),
gen_rtvec_v (ncases, labelvec),
const0_rtx, const0_rtx));
else
emit_jump_insn (gen_rtx_ADDR_VEC (CASE_VECTOR_MODE,
gen_rtvec_v (ncases, labelvec)));
#ifdef CASE_DROPS_THROUGH
emit_jump (default_label);
#else
emit_barrier ();
#endif
}
before_case = NEXT_INSN (before_case);
end = get_last_insn ();
if (squeeze_notes (&before_case, &end))
abort ();
reorder_insns (before_case, end,
thiscase->data.case_stmt.start);
}
else
end_cleanup_deferral ();
if (thiscase->exit_label)
emit_label (thiscase->exit_label);
free_case_nodes (case_stack->data.case_stmt.case_list);
POPSTACK (case_stack);
free_temp_slots ();
}
static struct case_node *
case_tree2list (node, right)
struct case_node *node, *right;
{
struct case_node *left;
if (node->right)
right = case_tree2list (node->right, right);
node->right = right;
if ((left = node->left))
{
node->left = 0;
return case_tree2list (left, node);
}
return node;
}
static void
do_jump_if_equal (op1, op2, label, unsignedp)
rtx op1, op2, label;
int unsignedp;
{
if (GET_CODE (op1) == CONST_INT && GET_CODE (op2) == CONST_INT)
{
if (INTVAL (op1) == INTVAL (op2))
emit_jump (label);
}
else
emit_cmp_and_jump_insns (op1, op2, EQ, NULL_RTX,
(GET_MODE (op1) == VOIDmode
? GET_MODE (op2) : GET_MODE (op1)),
unsignedp, label);
}
static int
estimate_case_costs (node)
case_node_ptr node;
{
tree min_ascii = integer_minus_one_node;
tree max_ascii = convert (TREE_TYPE (node->high), build_int_2 (127, 0));
case_node_ptr n;
int i;
if (! cost_table_initialized)
{
cost_table_initialized = 1;
for (i = 0; i < 128; i++)
{
if (ISALNUM (i))
COST_TABLE (i) = 16;
else if (ISPUNCT (i))
COST_TABLE (i) = 8;
else if (ISCNTRL (i))
COST_TABLE (i) = -1;
}
COST_TABLE (' ') = 8;
COST_TABLE ('\t') = 4;
COST_TABLE ('\0') = 4;
COST_TABLE ('\n') = 2;
COST_TABLE ('\f') = 1;
COST_TABLE ('\v') = 1;
COST_TABLE ('\b') = 1;
}
for (n = node; n; n = n->right)
{
if ((INT_CST_LT (n->low, min_ascii)) || INT_CST_LT (max_ascii, n->high))
return 0;
for (i = (HOST_WIDE_INT) TREE_INT_CST_LOW (n->low);
i <= (HOST_WIDE_INT) TREE_INT_CST_LOW (n->high); i++)
if (COST_TABLE (i) < 0)
return 0;
}
return 1;
}
static void
group_case_nodes (head)
case_node_ptr head;
{
case_node_ptr node = head;
while (node)
{
rtx lb = next_real_insn (label_rtx (node->code_label));
rtx lb2;
case_node_ptr np = node;
while (((np = np->right) != 0)
&& ((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)))))
&& tree_int_cst_equal (np->low,
fold (build (PLUS_EXPR,
TREE_TYPE (node->high),
node->high,
integer_one_node)))
&& tree_int_cst_lt (node->high,
fold (build (PLUS_EXPR,
TREE_TYPE (node->high),
node->high,
integer_one_node))))
{
node->high = np->high;
}
node->right = np;
node = np;
}
}
static void
balance_case_nodes (head, parent)
case_node_ptr *head;
case_node_ptr parent;
{
case_node_ptr np;
np = *head;
if (np)
{
int cost = 0;
int i = 0;
int ranges = 0;
case_node_ptr *npp;
case_node_ptr left;
while (np)
{
if (!tree_int_cst_equal (np->low, np->high))
{
ranges++;
if (use_cost_table)
cost += COST_TABLE (TREE_INT_CST_LOW (np->high));
}
if (use_cost_table)
cost += COST_TABLE (TREE_INT_CST_LOW (np->low));
i++;
np = np->right;
}
if (i > 2)
{
npp = head;
left = *npp;
if (use_cost_table)
{
int n_moved = 0;
i = (cost + 1) / 2;
while (1)
{
if (!tree_int_cst_equal ((*npp)->low, (*npp)->high))
i -= COST_TABLE (TREE_INT_CST_LOW ((*npp)->high));
i -= COST_TABLE (TREE_INT_CST_LOW ((*npp)->low));
if (i <= 0)
break;
npp = &(*npp)->right;
n_moved += 1;
}
if (n_moved == 0)
{
np = *head;
np->parent = parent;
balance_case_nodes (&np->left, np);
for (; np->right; np = np->right)
np->right->parent = np;
return;
}
}
else if (i == 3)
npp = &(*npp)->right;
else
{
i = (i + ranges + 1) / 2;
while (1)
{
if (!tree_int_cst_equal ((*npp)->low, (*npp)->high))
i--;
i--;
if (i <= 0)
break;
npp = &(*npp)->right;
}
}
*head = np = *npp;
*npp = 0;
np->parent = parent;
np->left = left;
balance_case_nodes (&np->left, np);
balance_case_nodes (&np->right, np);
}
else
{
np = *head;
np->parent = parent;
for (; np->right; np = np->right)
np->right->parent = np;
}
}
}
static int
node_has_low_bound (node, index_type)
case_node_ptr node;
tree index_type;
{
tree low_minus_one;
case_node_ptr pnode;
if (tree_int_cst_equal (node->low, TYPE_MIN_VALUE (index_type)))
return 1;
if (node->left)
return 0;
low_minus_one = fold (build (MINUS_EXPR, TREE_TYPE (node->low),
node->low, integer_one_node));
if (! tree_int_cst_lt (low_minus_one, node->low))
return 0;
for (pnode = node->parent; pnode; pnode = pnode->parent)
if (tree_int_cst_equal (low_minus_one, pnode->high))
return 1;
return 0;
}
static int
node_has_high_bound (node, index_type)
case_node_ptr node;
tree index_type;
{
tree high_plus_one;
case_node_ptr pnode;
if (TYPE_MAX_VALUE (index_type) == NULL)
return 1;
if (tree_int_cst_equal (node->high, TYPE_MAX_VALUE (index_type)))
return 1;
if (node->right)
return 0;
high_plus_one = fold (build (PLUS_EXPR, TREE_TYPE (node->high),
node->high, integer_one_node));
if (! tree_int_cst_lt (node->high, high_plus_one))
return 0;
for (pnode = node->parent; pnode; pnode = pnode->parent)
if (tree_int_cst_equal (high_plus_one, pnode->low))
return 1;
return 0;
}
static int
node_is_bounded (node, index_type)
case_node_ptr node;
tree index_type;
{
return (node_has_low_bound (node, index_type)
&& node_has_high_bound (node, index_type));
}
static void
emit_jump_if_reachable (label)
rtx label;
{
if (GET_CODE (get_last_insn ()) != BARRIER)
emit_jump (label);
}
static void
emit_case_nodes (index, node, default_label, index_type)
rtx index;
case_node_ptr node;
rtx default_label;
tree index_type;
{
int unsignedp = TREE_UNSIGNED (index_type);
enum machine_mode mode = GET_MODE (index);
enum machine_mode imode = TYPE_MODE (index_type);
if (node_is_bounded (node, index_type))
emit_jump (label_rtx (node->code_label));
else if (tree_int_cst_equal (node->low, node->high))
{
do_jump_if_equal (index,
convert_modes (mode, imode,
expand_expr (node->low, NULL_RTX,
VOIDmode, 0),
unsignedp),
label_rtx (node->code_label), unsignedp);
if (node->right != 0 && node->left != 0)
{
if (node_is_bounded (node->right, index_type))
{
emit_cmp_and_jump_insns (index,
convert_modes
(mode, imode,
expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
unsignedp),
GT, NULL_RTX, mode, unsignedp,
label_rtx (node->right->code_label));
emit_case_nodes (index, node->left, default_label, index_type);
}
else if (node_is_bounded (node->left, index_type))
{
emit_cmp_and_jump_insns (index,
convert_modes
(mode, imode,
expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
unsignedp),
LT, NULL_RTX, mode, unsignedp,
label_rtx (node->left->code_label));
emit_case_nodes (index, node->right, default_label, index_type);
}
else
{
tree test_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
emit_cmp_and_jump_insns (index,
convert_modes
(mode, imode,
expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
unsignedp),
GT, NULL_RTX, mode, unsignedp,
label_rtx (test_label));
emit_case_nodes (index, node->left, default_label, index_type);
emit_jump_if_reachable (default_label);
expand_label (test_label);
emit_case_nodes (index, node->right, default_label, index_type);
}
}
else if (node->right != 0 && node->left == 0)
{
if (node->right->right || node->right->left
|| !tree_int_cst_equal (node->right->low, node->right->high))
{
if (!node_has_low_bound (node, index_type))
{
emit_cmp_and_jump_insns (index,
convert_modes
(mode, imode,
expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
unsignedp),
LT, NULL_RTX, mode, unsignedp,
default_label);
}
emit_case_nodes (index, node->right, default_label, index_type);
}
else
do_jump_if_equal (index,
convert_modes
(mode, imode,
expand_expr (node->right->low, NULL_RTX,
VOIDmode, 0),
unsignedp),
label_rtx (node->right->code_label), unsignedp);
}
else if (node->right == 0 && node->left != 0)
{
if (node->left->left || node->left->right
|| !tree_int_cst_equal (node->left->low, node->left->high))
{
if (!node_has_high_bound (node, index_type))
{
emit_cmp_and_jump_insns (index,
convert_modes
(mode, imode,
expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
unsignedp),
GT, NULL_RTX, mode, unsignedp,
default_label);
}
emit_case_nodes (index, node->left, default_label, index_type);
}
else
do_jump_if_equal (index,
convert_modes
(mode, imode,
expand_expr (node->left->low, NULL_RTX,
VOIDmode, 0),
unsignedp),
label_rtx (node->left->code_label), unsignedp);
}
}
else
{
if (node->right != 0 && node->left != 0)
{
tree test_label = 0;
if (node_is_bounded (node->right, index_type))
emit_cmp_and_jump_insns (index,
convert_modes
(mode, imode,
expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
unsignedp),
GT, NULL_RTX, mode, unsignedp,
label_rtx (node->right->code_label));
else
{
test_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
emit_cmp_and_jump_insns (index,
convert_modes
(mode, imode,
expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
unsignedp),
GT, NULL_RTX, mode, unsignedp,
label_rtx (test_label));
}
emit_cmp_and_jump_insns (index,
convert_modes
(mode, imode,
expand_expr (node->low, NULL_RTX,
VOIDmode, 0),
unsignedp),
GE, NULL_RTX, mode, unsignedp,
label_rtx (node->code_label));
emit_case_nodes (index, node->left, default_label, index_type);
if (test_label)
{
emit_jump_if_reachable (default_label);
expand_label (test_label);
emit_case_nodes (index, node->right, default_label, index_type);
}
}
else if (node->right != 0 && node->left == 0)
{
if (!node_has_low_bound (node, index_type))
{
emit_cmp_and_jump_insns (index,
convert_modes
(mode, imode,
expand_expr (node->low, NULL_RTX,
VOIDmode, 0),
unsignedp),
LT, NULL_RTX, mode, unsignedp,
default_label);
}
emit_cmp_and_jump_insns (index,
convert_modes
(mode, imode,
expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
unsignedp),
LE, NULL_RTX, mode, unsignedp,
label_rtx (node->code_label));
emit_case_nodes (index, node->right, default_label, index_type);
}
else if (node->right == 0 && node->left != 0)
{
if (!node_has_high_bound (node, index_type))
{
emit_cmp_and_jump_insns (index,
convert_modes
(mode, imode,
expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
unsignedp),
GT, NULL_RTX, mode, unsignedp,
default_label);
}
emit_cmp_and_jump_insns (index,
convert_modes
(mode, imode,
expand_expr (node->low, NULL_RTX,
VOIDmode, 0),
unsignedp),
GE, NULL_RTX, mode, unsignedp,
label_rtx (node->code_label));
emit_case_nodes (index, node->left, default_label, index_type);
}
else
{
int high_bound = node_has_high_bound (node, index_type);
int low_bound = node_has_low_bound (node, index_type);
if (!high_bound && low_bound)
{
emit_cmp_and_jump_insns (index,
convert_modes
(mode, imode,
expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
unsignedp),
GT, NULL_RTX, mode, unsignedp,
default_label);
}
else if (!low_bound && high_bound)
{
emit_cmp_and_jump_insns (index,
convert_modes
(mode, imode,
expand_expr (node->low, NULL_RTX,
VOIDmode, 0),
unsignedp),
LT, NULL_RTX, mode, unsignedp,
default_label);
}
else if (!low_bound && !high_bound)
{
tree type = 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;
low_rtx = expand_expr (low, NULL_RTX, mode, 0);
new_index = expand_simple_binop (mode, MINUS, index, low_rtx,
NULL_RTX, unsignedp,
OPTAB_WIDEN);
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);
}
emit_jump (label_rtx (node->code_label));
}
}
}