#include "config.h"
#include "system.h"
#include "rtl.h"
#include "tree.h"
#include "flags.h"
#include "except.h"
#include "function.h"
#include "insn-flags.h"
#include "insn-config.h"
#include "insn-codes.h"
#include "expr.h"
#include "hard-reg-set.h"
#include "obstack.h"
#include "loop.h"
#include "recog.h"
#include "machmode.h"
#include "toplev.h"
#include "output.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
char *emit_filename;
int emit_lineno;
int expr_stmts_for_value;
static tree last_expr_type;
static rtx last_expr_value;
static rtx last_block_end_note;
int block_start_count;
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;
static int use_cost_table;
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 function_call_count;
int exception_region;
int 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;
int num_ranges;
const char *printname;
int line_number_status;
} case_stmt;
} data;
};
struct nesting *block_stack;
struct nesting *stack_block_stack;
struct nesting *cond_stack;
struct nesting *loop_stack;
struct nesting *case_stack;
struct nesting *nesting_stack;
int nesting_depth;
#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;
};
static struct goto_fixup *goto_fixup_chain;
struct label_chain
{
struct label_chain *next;
tree label;
};
static int using_eh_for_cleanups_p = 0;
static int n_occurrences PROTO((int, const char *));
static void expand_goto_internal PROTO((tree, rtx, rtx));
static int expand_fixup PROTO((tree, rtx, rtx));
static rtx expand_nl_handler_label PROTO((rtx, rtx));
static void expand_nl_goto_receiver PROTO((void));
static void expand_nl_goto_receivers PROTO((struct nesting *));
static void fixup_gotos PROTO((struct nesting *, rtx, tree,
rtx, int));
static void expand_null_return_1 PROTO((rtx, int));
static void expand_value_return PROTO((rtx));
static int tail_recursion_args PROTO((tree, tree));
static void expand_cleanups PROTO((tree, tree, int, int));
static void check_seenlabel PROTO((void));
static void do_jump_if_equal PROTO((rtx, rtx, rtx, int));
static int estimate_case_costs PROTO((case_node_ptr));
static void group_case_nodes PROTO((case_node_ptr));
static void balance_case_nodes PROTO((case_node_ptr *,
case_node_ptr));
static int node_has_low_bound PROTO((case_node_ptr, tree));
static int node_has_high_bound PROTO((case_node_ptr, tree));
static int node_is_bounded PROTO((case_node_ptr, tree));
static void emit_jump_if_reachable PROTO((rtx));
static void emit_case_nodes PROTO((rtx, case_node_ptr, rtx, tree));
static int add_case_node PROTO((tree, tree, tree, tree *));
static struct case_node *case_tree2list PROTO((case_node *, case_node *));
void
using_eh_for_cleanups ()
{
using_eh_for_cleanups_p = 1;
}
void
init_stmt ()
{
gcc_obstack_init (&stmt_obstack);
init_eh ();
}
void
init_stmt_for_function ()
{
block_stack = 0;
stack_block_stack = 0;
loop_stack = 0;
case_stack = 0;
cond_stack = 0;
nesting_stack = 0;
nesting_depth = 0;
block_start_count = 0;
goto_fixup_chain = 0;
expr_stmts_for_value = 0;
last_expr_type = 0;
init_eh_for_function ();
}
void
save_stmt_status (p)
struct function *p;
{
p->block_stack = block_stack;
p->stack_block_stack = stack_block_stack;
p->cond_stack = cond_stack;
p->loop_stack = loop_stack;
p->case_stack = case_stack;
p->nesting_stack = nesting_stack;
p->nesting_depth = nesting_depth;
p->block_start_count = block_start_count;
p->last_expr_type = last_expr_type;
p->last_expr_value = last_expr_value;
p->expr_stmts_for_value = expr_stmts_for_value;
p->emit_filename = emit_filename;
p->emit_lineno = emit_lineno;
p->goto_fixup_chain = goto_fixup_chain;
save_eh_status (p);
}
void
restore_stmt_status (p)
struct function *p;
{
block_stack = p->block_stack;
stack_block_stack = p->stack_block_stack;
cond_stack = p->cond_stack;
loop_stack = p->loop_stack;
case_stack = p->case_stack;
nesting_stack = p->nesting_stack;
nesting_depth = p->nesting_depth;
block_start_count = p->block_start_count;
last_expr_type = p->last_expr_type;
last_expr_value = p->last_expr_value;
expr_stmts_for_value = p->expr_stmts_for_value;
emit_filename = p->emit_filename;
emit_lineno = p->emit_lineno;
goto_fixup_chain = p->goto_fixup_chain;
restore_eh_status (p);
}
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 (label))
return DECL_RTL (label);
return DECL_RTL (label) = gen_label_rtx ();
}
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
x = convert_memory_address (Pmode, x);
#endif
emit_queue ();
if (current_function_check_memory_usage)
emit_library_call (chkr_check_exec_libfunc, 1,
VOIDmode, 1, x, ptr_mode);
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 *) oballoc (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 temp, handler_slot;
tree link;
handler_slot = p->nonlocal_goto_handler_slots;
for (link = p->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;
#if HAVE_nonlocal_goto
if (HAVE_nonlocal_goto)
emit_insn (gen_nonlocal_goto (lookup_static_chain (label),
copy_rtx (handler_slot),
copy_rtx (p->nonlocal_goto_stack_level),
label_ref));
else
#endif
{
rtx addr;
emit_move_insn (hard_frame_pointer_rtx, lookup_static_chain (label));
addr = copy_rtx (handler_slot);
temp = copy_to_reg (replace_rtx (addr, virtual_stack_vars_rtx,
hard_frame_pointer_rtx));
addr = p->nonlocal_goto_stack_level;
if (addr)
addr = replace_rtx (copy_rtx (addr),
virtual_stack_vars_rtx,
hard_frame_pointer_rtx);
emit_stack_restore (SAVE_NONLOCAL, addr, NULL_RTX);
emit_insn (gen_rtx_USE (VOIDmode, hard_frame_pointer_rtx));
emit_insn (gen_rtx_USE (VOIDmode, stack_pointer_rtx));
emit_indirect_jump (temp);
}
}
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 ();
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 *) oballoc (sizeof (struct goto_fixup));
if (last_insn == 0)
do_pending_stack_adjust ();
fixup->target = tree_label;
fixup->target_rtl = rtl_label;
{
register rtx original_before_jump
= last_insn ? last_insn : get_last_insn ();
rtx start;
start_sequence ();
pushlevel (0);
start = emit_note (NULL_PTR, NOTE_INSN_BLOCK_BEG);
fixup->before_jump = emit_note (NULL_PTR, NOTE_INSN_DELETED);
last_block_end_note = emit_note (NULL_PTR, NOTE_INSN_BLOCK_END);
fixup->context = poplevel (1, 0, 0);
end_sequence ();
emit_insns_after (start, original_before_jump);
}
fixup->block_start_count = 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_PTR, 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;
{
register 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)
{
register rtx cleanup_insns;
rtx after_label = f->target_rtl;
while (after_label != 0 && GET_CODE (after_label) == CODE_LABEL)
after_label = NEXT_INSN (after_label);
if (f->target != 0
&& (dont_jump_in || stack_level || cleanup_list)
&& (after_label == 0
|| INSN_UID (first_insn) < INSN_UID (after_label))
&& 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)
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 (current_function_check_memory_usage)
{
error ("`asm' cannot be used with `-fcheck-memory-usage'");
return;
}
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;
}
void
expand_asm_operands (string, outputs, inputs, clobbers, vol, filename, line)
tree string, outputs, inputs, clobbers;
int vol;
char *filename;
int line;
{
rtvec argvec, constraints;
rtx body;
int ninputs = list_length (inputs);
int noutputs = list_length (outputs);
int ninout = 0;
int nclobbers;
tree tail;
register 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));
rtx insn;
if (noutputs == 0)
vol = 1;
if (current_function_check_memory_usage)
{
error ("`asm' cannot be used with `-fcheck-memory-usage'");
return;
}
nclobbers = 0;
for (tail = clobbers; tail; tail = TREE_CHAIN (tail))
{
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;
if (outputs || inputs)
{
tree tmp = TREE_PURPOSE (outputs ? outputs : inputs);
int nalternatives = n_occurrences (',', TREE_STRING_POINTER (tmp));
tree next = inputs;
if (nalternatives + 1 > MAX_RECOG_ALTERNATIVES)
{
error ("too many alternatives in `asm'");
return;
}
tmp = outputs;
while (tmp)
{
char *constraint = TREE_STRING_POINTER (TREE_PURPOSE (tmp));
if (n_occurrences (',', constraint) != nalternatives)
{
error ("operand constraints for `asm' differ in number of alternatives");
return;
}
if (TREE_CHAIN (tmp))
tmp = TREE_CHAIN (tmp);
else
tmp = next, next = 0;
}
}
for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
{
tree val = TREE_VALUE (tail);
tree type = TREE_TYPE (val);
char *constraint;
char *p;
int c_len;
int j;
int is_inout = 0;
int allows_reg = 0;
int allows_mem = 0;
if (TREE_TYPE (val) == error_mark_node)
return;
c_len = TREE_STRING_LENGTH (TREE_PURPOSE (tail)) - 1;
constraint = TREE_STRING_POINTER (TREE_PURPOSE (tail));
switch (c_len)
{
default:
if ((p = strchr (constraint, '=')) != NULL)
break;
if ((p = strchr (constraint, '+')) != NULL)
break;
case 0:
error ("output operand constraint lacks `='");
return;
}
if (p != constraint)
{
j = *p;
bcopy (constraint, constraint+1, p-constraint);
*constraint = j;
warning ("output constraint `%c' for operand %d is not at the beginning", j, i);
}
is_inout = constraint[0] == '+';
constraint[0] = '=';
if (is_inout && i > 9)
{
error ("output operand constraint %d contains `+'", i);
return;
}
for (j = 1; j < c_len; j++)
switch (constraint[j])
{
case '+':
case '=':
error ("operand constraint contains '+' or '=' at illegal position.");
return;
case '%':
if (i + 1 == ninputs + noutputs)
{
error ("`%%' constraint used with last operand");
return;
}
break;
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 ',':
#ifdef EXTRA_CONSTRAINT
case 'Q': case 'R': case 'S': case 'T': case 'U':
#endif
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
error ("matching constraint not valid in output operand");
break;
case 'V': case 'm': case 'o':
allows_mem = 1;
break;
case '<': case '>':
allows_mem = 1;
break;
case 'g': case 'X':
allows_reg = 1;
allows_mem = 1;
break;
case 'p': case 'r':
default:
allows_reg = 1;
break;
}
real_output_rtx[i] = NULL_RTX;
if ((TREE_CODE (val) == INDIRECT_REF
&& allows_mem)
|| (TREE_CODE_CLASS (TREE_CODE (val)) == 'd'
&& (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)
{
if (! allows_reg)
mark_addressable (TREE_VALUE (tail));
output_rtx[i]
= expand_expr (TREE_VALUE (tail), NULL_RTX, VOIDmode,
EXPAND_MEMORY_USE_WO);
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)
{
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, 0);
TREE_VALUE (tail) = make_tree (type, output_rtx[i]);
}
if (is_inout)
{
inout_mode[ninout] = TYPE_MODE (TREE_TYPE (TREE_VALUE (tail)));
inout_opnum[ninout++] = i;
}
}
ninputs += ninout;
if (ninputs + noutputs > MAX_RECOG_OPERANDS)
{
error ("more than %d operands in `asm'", MAX_RECOG_OPERANDS);
return;
}
argvec = rtvec_alloc (ninputs);
constraints = rtvec_alloc (ninputs);
body = gen_rtx_ASM_OPERANDS (VOIDmode,
TREE_STRING_POINTER (string), "", 0, argvec,
constraints, filename, line);
MEM_VOLATILE_P (body) = vol;
i = 0;
for (tail = inputs; tail; tail = TREE_CHAIN (tail))
{
int j;
int allows_reg = 0, allows_mem = 0;
char *constraint, *orig_constraint;
int c_len;
rtx op;
if (TREE_TYPE (TREE_VALUE (tail)) == error_mark_node)
return;
if (TREE_PURPOSE (tail) == NULL_TREE)
{
error ("hard register `%s' listed as input operand to `asm'",
TREE_STRING_POINTER (TREE_VALUE (tail)) );
return;
}
c_len = TREE_STRING_LENGTH (TREE_PURPOSE (tail)) - 1;
constraint = TREE_STRING_POINTER (TREE_PURPOSE (tail));
orig_constraint = constraint;
for (j = 0; j < c_len; j++)
switch (constraint[j])
{
case '+': case '=': case '&':
if (constraint == orig_constraint)
{
error ("input operand constraint contains `%c'", constraint[j]);
return;
}
break;
case '%':
if (constraint == orig_constraint
&& i + 1 == ninputs - ninout)
{
error ("`%%' constraint used with last operand");
return;
}
break;
case 'V': case 'm': case 'o':
allows_mem = 1;
break;
case '<': case '>':
case '?': case '!': case '*':
case 'E': case 'F': case 'G': case 'H': case 'X':
case 's': case 'i': case 'n':
case 'I': case 'J': case 'K': case 'L': case 'M':
case 'N': case 'O': case 'P': case ',':
#ifdef EXTRA_CONSTRAINT
case 'Q': case 'R': case 'S': case 'T': case 'U':
#endif
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if (constraint[j] >= '0' + noutputs)
{
error
("matching constraint references invalid operand number");
return;
}
if ((j == 0 && c_len == 1)
|| (j == 1 && c_len == 2 && constraint[0] == '%'))
{
tree o = outputs;
for (j = constraint[j] - '0'; j > 0; --j)
o = TREE_CHAIN (o);
c_len = TREE_STRING_LENGTH (TREE_PURPOSE (o)) - 1;
constraint = TREE_STRING_POINTER (TREE_PURPOSE (o));
j = 0;
break;
}
case 'p': case 'r':
default:
allows_reg = 1;
break;
case 'g':
allows_reg = 1;
allows_mem = 1;
break;
}
if (! allows_reg && allows_mem)
mark_addressable (TREE_VALUE (tail));
op = expand_expr (TREE_VALUE (tail), NULL_RTX, VOIDmode, 0);
if (asm_operand_ok (op, constraint) <= 0)
{
if (allows_reg)
op = force_reg (TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))), op);
else if (!allows_mem)
warning ("asm operand %d probably doesn't match constraints", i);
else if (CONSTANT_P (op))
op = force_const_mem (TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))),
op);
else if (GET_CODE (op) == REG
|| GET_CODE (op) == SUBREG
|| GET_CODE (op) == CONCAT)
{
tree type = TREE_TYPE (TREE_VALUE (tail));
rtx memloc = assign_temp (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);
}
XVECEXP (body, 3, i) = op;
XVECEXP (body, 4, i)
= gen_rtx_ASM_INPUT (TYPE_MODE (TREE_TYPE (TREE_VALUE (tail))),
orig_constraint);
i++;
}
for (i = 0; i < ninputs - ninout; i++)
XVECEXP (body, 3, i) = protect_from_queue (XVECEXP (body, 3, i), 0);
for (i = 0; i < noutputs; i++)
output_rtx[i] = protect_from_queue (output_rtx[i], 1);
for (i = 0; i < ninout; i++)
{
static char match[9+1][2]
= {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
int j = inout_opnum[i];
XVECEXP (body, 3, ninputs - ninout + i)
= output_rtx[j];
XVECEXP (body, 4, ninputs - ninout + i)
= gen_rtx_ASM_INPUT (inout_mode[j], match[j]);
}
if (noutputs == 1 && nclobbers == 0)
{
XSTR (body, 1) = TREE_STRING_POINTER (TREE_PURPOSE (outputs));
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 (VOIDmode,
TREE_STRING_POINTER (string),
TREE_STRING_POINTER (TREE_PURPOSE (tail)),
i, argvec, constraints,
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))
{
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 ();
}
void
expand_expr_stmt (exp)
tree exp;
{
if (expr_stmts_for_value == 0 && exp != error_mark_node)
{
if (! TREE_SIDE_EFFECTS (exp) && (extra_warnings || warn_unused)
&& !(TREE_CODE (exp) == CONVERT_EXPR
&& TREE_TYPE (exp) == void_type_node))
warning_with_file_and_line (emit_filename, emit_lineno,
"statement with no effect");
else if (warn_unused)
warn_if_unused_value (exp);
}
if (expr_stmts_for_value && TREE_CODE (TREE_TYPE (exp)) == FUNCTION_TYPE)
exp = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (exp)), exp);
last_expr_type = TREE_TYPE (exp);
last_expr_value = expand_expr (exp,
(expr_stmts_for_value
? NULL_RTX : const0_rtx),
VOIDmode, 0);
if (last_expr_value != 0 && GET_CODE (last_expr_value) == MEM
&& TREE_THIS_VOLATILE (exp))
{
if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode)
;
else if (TYPE_MODE (TREE_TYPE (exp)) != BLKmode)
copy_to_reg (last_expr_value);
else
{
rtx lab = gen_label_rtx ();
emit_cmp_and_jump_insns (last_expr_value, last_expr_value, EQ,
expand_expr (TYPE_SIZE (last_expr_type),
NULL_RTX, VOIDmode, 0),
BLKmode, 0,
TYPE_ALIGN (last_expr_type) / BITS_PER_UNIT,
lab);
emit_label (lab);
}
}
preserve_temp_slots (last_expr_value);
free_temp_slots ();
emit_queue ();
}
int
warn_if_unused_value (exp)
tree exp;
{
if (TREE_USED (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:
case COND_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_TYPE (exp) == void_type_node)
return 0;
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 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 ((TREE_CODE_CLASS (TREE_CODE (exp)) == 'd'
|| TREE_CODE_CLASS (TREE_CODE (exp)) == 'r')
&& TREE_THIS_VOLATILE (exp))
return 0;
warn:
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 ()
{
int momentary;
tree t;
momentary = suspend_momentary ();
t = make_node (RTL_EXPR);
resume_momentary (momentary);
do_pending_stack_adjust ();
start_sequence_for_rtl_expr (t);
NO_DEFER_POP;
expr_stmts_for_value++;
return t;
}
tree
expand_end_stmt_expr (t)
tree t;
{
OK_DEFER_POP;
if (last_expr_type == 0)
{
last_expr_type = void_type_node;
last_expr_value = const0_rtx;
}
else if (last_expr_value == 0)
last_expr_value = const0_rtx;
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;
{
register 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_PTR, 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;
}
void
expand_loop_continue_here ()
{
do_pending_stack_adjust ();
emit_note (NULL_PTR, 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 insn = get_last_insn ();
int needs_end_jump = 1;
if (start_label == loop_stack->data.loop.continue_label)
emit_note_before (NOTE_INSN_LOOP_CONT, start_label);
do_pending_stack_adjust ();
if (optimize
&& GET_CODE (insn) == CODE_LABEL
&& LABEL_NAME (insn) == NULL
&& GET_CODE (PREV_INSN (insn)) == BARRIER)
{
rtx label = insn;
rtx jump = PREV_INSN (PREV_INSN (label));
if (GET_CODE (jump) == JUMP_INSN
&& GET_CODE (PATTERN (jump)) == SET
&& SET_DEST (PATTERN (jump)) == pc_rtx
&& GET_CODE (SET_SRC (PATTERN (jump))) == LABEL_REF
&& (XEXP (SET_SRC (PATTERN (jump)), 0)
== loop_stack->data.loop.end_label))
{
rtx prev;
insn = PREV_INSN (label);
reorder_insns (label, label, start_label);
for (prev = PREV_INSN (jump); ; prev = PREV_INSN (prev))
{
if (GET_CODE (prev) == NOTE)
{
if (NOTE_LINE_NUMBER (prev) < 0)
break;
continue;
}
if (GET_CODE (prev) == CODE_LABEL)
break;
if (GET_CODE (prev) == JUMP_INSN)
{
if (GET_CODE (PATTERN (prev)) == SET
&& SET_DEST (PATTERN (prev)) == pc_rtx
&& GET_CODE (SET_SRC (PATTERN (prev))) == IF_THEN_ELSE
&& (GET_CODE (XEXP (SET_SRC (PATTERN (prev)), 1))
== LABEL_REF)
&& XEXP (XEXP (SET_SRC (PATTERN (prev)), 1), 0) == label)
{
XEXP (XEXP (SET_SRC (PATTERN (prev)), 1), 0)
= start_label;
emit_note_after (NOTE_INSN_LOOP_END, prev);
needs_end_jump = 0;
}
break;
}
}
}
}
if (optimize
&& needs_end_jump
&&
! (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) == SET
&& SET_DEST (PATTERN (insn)) == pc_rtx
&& GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE))
{
int eh_regions = 0;
int num_insns = 0;
rtx last_test_insn = NULL_RTX;
for (insn = NEXT_INSN (loop_stack->data.loop.start_label); insn;
insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == NOTE)
{
if (optimize < 2
&& (NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_BEG
|| NOTE_LINE_NUMBER (insn) == NOTE_INSN_BLOCK_END))
break;
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)
++eh_regions;
else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END)
{
--eh_regions;
if (eh_regions < 0)
abort ();
}
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG)
break;
continue;
}
if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == INSN)
num_insns++;
if (last_test_insn && num_insns > 30)
break;
if (eh_regions > 0)
continue;
if (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) == SET
&& SET_DEST (PATTERN (insn)) == pc_rtx)
{
rtx dest1 = NULL_RTX;
rtx dest2 = NULL_RTX;
rtx potential_last_test;
if (GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE)
{
dest1 = XEXP (SET_SRC (PATTERN (insn)), 1);
dest2 = XEXP (SET_SRC (PATTERN (insn)), 2);
potential_last_test = insn;
}
else
{
dest1 = SET_SRC (PATTERN (insn));
potential_last_test = NEXT_INSN (insn);
}
do {
if (dest1 && GET_CODE (dest1) == LABEL_REF
&& ((XEXP (dest1, 0)
== loop_stack->data.loop.alt_end_label)
|| (XEXP (dest1, 0)
== loop_stack->data.loop.end_label)))
{
last_test_insn = potential_last_test;
break;
}
dest1 = dest2;
dest2 = NULL_RTX;
} while (dest1);
}
}
if (last_test_insn != 0 && last_test_insn != get_last_insn ())
{
register rtx newstart_label = gen_label_rtx ();
register rtx start_move = start_label;
rtx next_insn;
if (GET_CODE (PREV_INSN (start_move)) == NOTE
&& (NOTE_LINE_NUMBER (PREV_INSN (start_move))
== NOTE_INSN_LOOP_CONT))
start_move = PREV_INSN (start_move);
emit_label_after (newstart_label, PREV_INSN (start_move));
for (insn = start_move; insn; insn = next_insn)
{
if (insn == last_test_insn)
next_insn = NULL_RTX;
else
next_insn = 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_after (gen_jump (start_label),
PREV_INSN (newstart_label));
emit_barrier_after (PREV_INSN (newstart_label));
start_label = newstart_label;
}
}
if (needs_end_jump)
{
emit_jump (start_label);
emit_note (NULL_PTR, NOTE_INSN_LOOP_END);
}
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
stmt_loop_nest_empty ()
{
return (loop_stack == NULL);
}
int
preserve_subexpressions_p ()
{
rtx insn;
if (flag_expensive_optimizations)
return 1;
if (optimize == 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 ()
{
struct nesting *block = block_stack;
rtx last_insn = 0;
while (block && block->data.block.cleanups == 0)
block = block->next;
expand_null_return_1 (last_insn, block != 0);
}
static void
expand_value_return (val)
rtx val;
{
struct nesting *block = block_stack;
rtx last_insn = get_last_insn ();
rtx return_reg = DECL_RTL (DECL_RESULT (current_function_decl));
if (return_reg != val)
{
#ifdef PROMOTE_FUNCTION_RETURN
tree type = TREE_TYPE (DECL_RESULT (current_function_decl));
int unsignedp = TREE_UNSIGNED (type);
enum machine_mode mode
= promote_mode (type, DECL_MODE (DECL_RESULT (current_function_decl)),
&unsignedp, 1);
if (GET_MODE (val) != VOIDmode && GET_MODE (val) != mode)
convert_move (return_reg, val, unsignedp);
else
#endif
emit_move_insn (return_reg, val);
}
if (GET_CODE (return_reg) == REG
&& REGNO (return_reg) < FIRST_PSEUDO_REGISTER)
emit_insn (gen_rtx_USE (VOIDmode, return_reg));
else if (GET_CODE (return_reg) == PARALLEL)
{
int i;
for (i = 0; i < XVECLEN (return_reg, 0); i++)
{
rtx x = XEXP (XVECEXP (return_reg, 0, i), 0);
if (GET_CODE (x) == REG
&& REGNO (x) < FIRST_PSEUDO_REGISTER)
emit_insn (gen_rtx_USE (VOIDmode, x));
}
}
while (block && block->data.block.cleanups == 0)
block = block->next;
expand_null_return_1 (last_insn, block != 0);
}
static void
expand_null_return_1 (last_insn, use_goto)
rtx last_insn;
int use_goto;
{
rtx end_label = cleanup_label ? cleanup_label : return_label;
clear_pending_stack_adjust ();
do_pending_stack_adjust ();
last_expr_type = 0;
if (current_function_returns_pcc_struct || use_goto)
{
if (end_label == 0)
end_label = return_label = gen_label_rtx ();
expand_goto_internal (NULL_TREE, end_label, last_insn);
return;
}
#ifdef HAVE_return
if (HAVE_return && use_goto == 0 && cleanup_label == 0)
{
emit_jump_insn (gen_return ());
emit_barrier ();
return;
}
#endif
expand_goto_internal (NULL_TREE, end_label, last_insn);
}
void
expand_return (retval)
tree retval;
{
rtx last_insn = 0;
register rtx val = 0;
register rtx op0;
tree retval_rhs;
int cleanups;
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 0
cleanups = any_pending_cleanups (1);
#else
cleanups = 1;
#endif
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 (TREE_TYPE (retval) == void_type_node)
retval_rhs = retval;
else
retval_rhs = NULL_TREE;
if (cleanups || cleanup_label != 0)
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;
}
if (optimize_tail_recursion (retval_rhs, last_insn))
return;
#ifdef HAVE_return
if (HAVE_return && cleanup_label == 0
&& ! current_function_returns_pcc_struct
&& BRANCH_COST <= 1)
{
int has_scc = 0;
if (retval_rhs)
switch (TREE_CODE (retval_rhs))
{
case EQ_EXPR:
#ifdef HAVE_seq
has_scc = HAVE_seq;
#endif
case NE_EXPR:
#ifdef HAVE_sne
has_scc = HAVE_sne;
#endif
case GT_EXPR:
#ifdef HAVE_sgt
has_scc = HAVE_sgt;
#endif
case GE_EXPR:
#ifdef HAVE_sge
has_scc = HAVE_sge;
#endif
case LT_EXPR:
#ifdef HAVE_slt
has_scc = HAVE_slt;
#endif
case LE_EXPR:
#ifdef HAVE_sle
has_scc = HAVE_sle;
#endif
case TRUTH_ANDIF_EXPR:
case TRUTH_ORIF_EXPR:
case TRUTH_AND_EXPR:
case TRUTH_OR_EXPR:
case TRUTH_NOT_EXPR:
case TRUTH_XOR_EXPR:
if (! has_scc)
{
op0 = gen_label_rtx ();
jumpifnot (retval_rhs, op0);
expand_value_return (const1_rtx);
emit_label (op0);
expand_value_return (const0_rtx);
return;
}
break;
default:
break;
}
}
#endif
if (retval_rhs != 0
&& TYPE_MODE (TREE_TYPE (retval_rhs)) == BLKmode
&& GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) == REG)
{
int i, bitpos, xbitpos;
int big_endian_correction = 0;
int bytes = int_size_in_bytes (TREE_TYPE (retval_rhs));
int n_regs = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
int bitsize = MIN (TYPE_ALIGN (TREE_TYPE (retval_rhs)),
(unsigned int)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_BIG_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_insn (gen_rtx_CLOBBER (VOIDmode, 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,
bitsize / BITS_PER_UNIT,
BITS_PER_WORD),
bitsize / BITS_PER_UNIT, BITS_PER_WORD);
}
bytes = int_size_in_bytes (TREE_TYPE (retval_rhs));
for (tmpmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
tmpmode != MAX_MACHINE_MODE;
tmpmode = GET_MODE_WIDER_MODE (tmpmode))
{
if (GET_MODE_SIZE (tmpmode) >= bytes)
break;
}
if (tmpmode == MAX_MACHINE_MODE)
abort ();
PUT_MODE (DECL_RTL (DECL_RESULT (current_function_decl)), 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 (cleanups
&& retval_rhs != 0
&& TREE_TYPE (retval_rhs) != void_type_node
&& GET_CODE (DECL_RTL (DECL_RESULT (current_function_decl))) == REG)
{
val = gen_reg_rtx (DECL_MODE (DECL_RESULT (current_function_decl)));
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 (DECL_RTL (DECL_RESULT (current_function_decl)));
}
}
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 (call_expr, last_insn)
tree call_expr;
rtx last_insn;
{
if (optimize && call_expr != 0
&& frame_offset == 0
&& TREE_CODE (call_expr) == CALL_EXPR
&& TREE_CODE (TREE_OPERAND (call_expr, 0)) == ADDR_EXPR
&& TREE_OPERAND (TREE_OPERAND (call_expr, 0), 0) == current_function_decl
&& tail_recursion_args (TREE_OPERAND (call_expr, 1),
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;
{
register tree a = actuals, f = formals;
register int i;
register 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;
register 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 (exit_flag)
int exit_flag;
{
struct nesting *thisblock = ALLOC_NESTING ();
rtx note = emit_note (NULL_PTR, NOTE_INSN_BLOCK_BEG);
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.function_call_count = 0;
thisblock->data.block.exception_region = 0;
thisblock->data.block.target_temp_slot_level = target_temp_slot_level;
thisblock->data.block.conditional_code = 0;
thisblock->data.block.last_unconditional_cleanup = note;
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 = ++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 (0);
target_temp_slot_level = temp_slot_level;
}
void
expand_end_target_temps ()
{
expand_end_bindings (NULL_TREE, 0, 0);
pop_temp_slots ();
}
void
mark_block_as_eh_region ()
{
block_stack->data.block.exception_region = 1;
if (block_stack->next
&& block_stack->next->data.block.conditional_code)
{
block_stack->data.block.conditional_code
= block_stack->next->data.block.conditional_code;
block_stack->data.block.last_unconditional_cleanup
= block_stack->next->data.block.last_unconditional_cleanup;
block_stack->data.block.cleanup_ptr
= block_stack->next->data.block.cleanup_ptr;
}
}
int
conditional_context ()
{
return block_stack && block_stack->data.block.conditional_code;
}
void
mark_block_as_not_eh_region ()
{
block_stack->data.block.exception_region = 0;
}
int
is_eh_region ()
{
return block_stack && block_stack->data.block.exception_region;
}
void
remember_end_note (block)
register tree block;
{
BLOCK_END_NOTE (block) = last_block_end_note;
last_block_end_note = NULL_RTX;
}
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 struct elims {int from, to;} elim_regs[] = ELIMINABLE_REGS;
size_t i;
for (i = 0; i < sizeof elim_regs / sizeof elim_regs[0]; i++)
if (elim_regs[i].from == ARG_POINTER_REGNUM
&& elim_regs[i].to == HARD_FRAME_POINTER_REGNUM)
break;
if (i == sizeof elim_regs / sizeof elim_regs [0])
#endif
{
if (arg_pointer_save_area == 0)
arg_pointer_save_area
= assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0);
emit_move_insn (virtual_incoming_args_rtx,
copy_to_reg (arg_pointer_save_area));
}
}
#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 ();
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "abort"), 0,
VOIDmode, 0);
emit_barrier ();
}
nonlocal_goto_handler_labels = label_list;
emit_label (afterward);
}
void
expand_end_bindings (vars, mark_ends, dont_jump_in)
tree vars;
int mark_ends;
int dont_jump_in;
{
register struct nesting *thisblock;
register tree decl;
while (block_stack->data.block.exception_region)
{
push_temp_slots ();
block_stack->data.block.exception_region = 0;
expand_end_bindings (NULL_TREE, 0, 0);
}
thisblock = block_stack;
if (warn_unused)
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'");
if (thisblock->exit_label)
{
do_pending_stack_adjust ();
emit_label (thisblock->exit_label);
}
if (function_call_count != thisblock->data.block.function_call_count
&& 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 = GET_CODE (get_last_insn ()) != BARRIER;
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;
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)
last_block_end_note = emit_note (NULL_PTR, NOTE_INSN_BLOCK_END);
else
NOTE_LINE_NUMBER (thisblock->data.block.first_insn) = NOTE_INSN_DELETED;
if (obey_regdecls)
for (decl = vars; decl; decl = TREE_CHAIN (decl))
if (TREE_CODE (decl) == VAR_DECL && DECL_RTL (decl))
use_variable (DECL_RTL (decl));
target_temp_slot_level = thisblock->data.block.target_temp_slot_level;
stack_block_stack = thisblock->data.block.innermost_stack_block;
POPSTACK (block_stack);
pop_temp_slots ();
}
void
expand_decl (decl)
register tree decl;
{
struct nesting *thisblock = block_stack;
tree type;
type = TREE_TYPE (decl);
if (TREE_CODE (decl) != VAR_DECL)
return;
if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
return;
if (type == error_mark_node)
DECL_RTL (decl) = gen_rtx_MEM (BLKmode, const0_rtx);
else if (DECL_SIZE (decl) == 0)
{
if (DECL_INITIAL (decl) == 0)
DECL_RTL (decl) = assign_stack_temp (DECL_MODE (decl), 0, 1);
else
DECL_RTL (decl) = gen_rtx_MEM (BLKmode, gen_reg_rtx (Pmode));
MEM_SET_IN_STRUCT_P (DECL_RTL (decl), AGGREGATE_TYPE_P (type));
}
else if (DECL_MODE (decl) != BLKmode
&& !(flag_float_store
&& TREE_CODE (type) == REAL_TYPE)
&& ! TREE_THIS_VOLATILE (decl)
&& ! TREE_ADDRESSABLE (decl)
&& (DECL_REGISTER (decl) || ! obey_regdecls)
&& ! current_function_check_memory_usage)
{
int unsignedp = TREE_UNSIGNED (type);
enum machine_mode reg_mode
= promote_mode (type, DECL_MODE (decl), &unsignedp, 0);
DECL_RTL (decl) = gen_reg_rtx (reg_mode);
mark_user_reg (DECL_RTL (decl));
if (POINTER_TYPE_P (type))
mark_reg_pointer (DECL_RTL (decl),
(TYPE_ALIGN (TREE_TYPE (TREE_TYPE (decl)))
/ BITS_PER_UNIT));
}
else if (TREE_CODE (DECL_SIZE (decl)) == INTEGER_CST
&& ! (flag_stack_check && ! STACK_CHECK_BUILTIN
&& (TREE_INT_CST_HIGH (DECL_SIZE (decl)) != 0
|| (TREE_INT_CST_LOW (DECL_SIZE (decl))
> STACK_CHECK_MAX_VAR_SIZE * BITS_PER_UNIT))))
{
rtx oldaddr = 0;
rtx addr;
if (DECL_RTL (decl) != 0)
{
if (GET_CODE (DECL_RTL (decl)) != MEM
|| GET_CODE (XEXP (DECL_RTL (decl), 0)) != REG)
abort ();
oldaddr = XEXP (DECL_RTL (decl), 0);
}
DECL_RTL (decl) = assign_temp (TREE_TYPE (decl), 1, 1, 1);
MEM_SET_IN_STRUCT_P (DECL_RTL (decl),
AGGREGATE_TYPE_P (TREE_TYPE (decl)));
DECL_ALIGN (decl) = (DECL_MODE (decl) == BLKmode ? BIGGEST_ALIGNMENT
: GET_MODE_BITSIZE (DECL_MODE (decl)));
if (oldaddr)
{
addr = force_operand (XEXP (DECL_RTL (decl), 0), oldaddr);
if (addr != oldaddr)
emit_move_insn (oldaddr, addr);
}
MEM_SET_IN_STRUCT_P (DECL_RTL (decl),
AGGREGATE_TYPE_P (TREE_TYPE (decl)));
#if 0
if (flag_float_store && TREE_CODE (type) == REAL_TYPE)
MEM_VOLATILE_P (DECL_RTL (decl)) = 1;
#endif
MEM_ALIAS_SET (DECL_RTL (decl)) = get_alias_set (decl);
}
else
{
rtx address, size;
if (thisblock->data.block.stack_level == 0)
{
do_pending_stack_adjust ();
emit_stack_save (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION,
&thisblock->data.block.stack_level,
thisblock->data.block.first_insn);
stack_block_stack = thisblock;
}
size = expand_expr (size_binop (CEIL_DIV_EXPR,
DECL_SIZE (decl),
size_int (BITS_PER_UNIT)),
NULL_RTX, VOIDmode, 0);
free_temp_slots ();
address = allocate_dynamic_stack_space (size, NULL_RTX,
TYPE_ALIGN (TREE_TYPE (decl)));
DECL_RTL (decl) = gen_rtx_MEM (DECL_MODE (decl), address);
MEM_SET_IN_STRUCT_P (DECL_RTL (decl),
AGGREGATE_TYPE_P (TREE_TYPE (decl)));
#ifdef STACK_BOUNDARY
DECL_ALIGN (decl) = STACK_BOUNDARY;
#else
DECL_ALIGN (decl) = BIGGEST_ALIGNMENT;
#endif
}
if (TREE_THIS_VOLATILE (decl))
MEM_VOLATILE_P (DECL_RTL (decl)) = 1;
#if 0
if (TREE_READONLY (decl))
RTX_UNCHANGING_P (DECL_RTL (decl)) = 1;
#endif
if (obey_regdecls)
use_variable (DECL_RTL (decl));
}
void
expand_decl_init (decl)
tree decl;
{
int was_used = TREE_USED (decl);
if (TREE_CODE (decl) == CONST_DECL)
{
if (DECL_INITIAL (decl) && TREE_CONSTANT (DECL_INITIAL (decl)))
expand_expr (DECL_INITIAL (decl), NULL_RTX, VOIDmode,
EXPAND_INITIALIZER);
return;
}
if (TREE_STATIC (decl))
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 = block_stack;
if (thisblock == 0)
return 0;
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);
push_obstacks_nochange ();
resume_temporary_allocation ();
cond = build_decl (VAR_DECL, NULL_TREE, type_for_mode (word_mode, 1));
DECL_RTL (cond) = flag;
cleanup = build (COND_EXPR, void_type_node,
truthvalue_conversion (cond),
cleanup, integer_zero_node);
cleanup = fold (cleanup);
pop_obstacks ();
cleanups = thisblock->data.block.cleanup_ptr;
}
push_obstacks_nochange ();
resume_temporary_allocation ();
cleanup = unsave_expr (cleanup);
pop_obstacks ();
t = *cleanups = temp_tree_cons (decl, cleanup, *cleanups);
if (! cond_context)
stack_block_stack = thisblock;
if (cond_context)
{
start_sequence ();
}
if (! using_eh_for_cleanups_p
|| expand_eh_region_start_tree (decl, cleanup))
TREE_ADDRESSABLE (t) = 1;
thisblock = block_stack;
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 ();
thisblock->data.block.cleanup_ptr = &thisblock->data.block.cleanups;
}
}
return 1;
}
int
expand_decl_cleanup_no_eh (decl, cleanup)
tree decl, cleanup;
{
int save_eh = using_eh_for_cleanups_p;
int result;
using_eh_for_cleanups_p = 0;
result = expand_decl_cleanup (decl, cleanup);
using_eh_for_cleanups_p = save_eh;
return result;
}
int
expand_dcc_cleanup (decl)
tree decl;
{
struct nesting *thisblock = block_stack;
tree cleanup;
if (thisblock == 0)
return 0;
push_obstacks_nochange ();
resume_temporary_allocation ();
cleanup = make_node (POPDCC_EXPR);
pop_obstacks ();
thisblock->data.block.cleanups
= temp_tree_cons (decl, cleanup, thisblock->data.block.cleanups);
stack_block_stack = thisblock;
return 1;
}
int
expand_dhc_cleanup (decl)
tree decl;
{
struct nesting *thisblock = block_stack;
tree cleanup;
if (thisblock == 0)
return 0;
push_obstacks_nochange ();
resume_temporary_allocation ();
cleanup = make_node (POPDHC_EXPR);
pop_obstacks ();
thisblock->data.block.cleanups
= temp_tree_cons (decl, cleanup, thisblock->data.block.cleanups);
stack_block_stack = thisblock;
return 1;
}
void
expand_anon_union_decl (decl, cleanup, decl_elts)
tree decl, cleanup, decl_elts;
{
struct nesting *thisblock = block_stack;
rtx x;
expand_decl (decl);
expand_decl_cleanup (decl, cleanup);
x = DECL_RTL (decl);
while (decl_elts)
{
tree decl_elt = TREE_VALUE (decl_elts);
tree cleanup_elt = TREE_PURPOSE (decl_elts);
enum machine_mode mode = TYPE_MODE (TREE_TYPE (decl_elt));
DECL_ALIGN (decl_elt) = DECL_ALIGN (decl);
if (mode == BLKmode && DECL_MODE (decl) != BLKmode)
DECL_MODE (decl_elt) = mode
= mode_for_size (TREE_INT_CST_LOW (DECL_SIZE (decl_elt)),
MODE_INT, 1);
if (GET_CODE (x) == MEM)
{
if (mode == GET_MODE (x))
DECL_RTL (decl_elt) = x;
else
{
DECL_RTL (decl_elt) = gen_rtx_MEM (mode, copy_rtx (XEXP (x, 0)));
MEM_COPY_ATTRIBUTES (DECL_RTL (decl_elt), x);
RTX_UNCHANGING_P (DECL_RTL (decl_elt)) = RTX_UNCHANGING_P (x);
}
}
else if (GET_CODE (x) == REG)
{
if (mode == GET_MODE (x))
DECL_RTL (decl_elt) = x;
else
DECL_RTL (decl_elt) = gen_rtx_SUBREG (mode, x, 0);
}
else
abort ();
if (cleanup != 0)
thisblock->data.block.cleanups
= temp_tree_cons (decl_elt, cleanup_elt,
thisblock->data.block.cleanups);
decl_elts = TREE_CHAIN (decl_elts);
}
}
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)
{
tree cleanup = TREE_VALUE (tail);
if (TREE_CODE (cleanup) != POPDHC_EXPR
&& TREE_CODE (cleanup) != POPDCC_EXPR
&& ! TREE_ADDRESSABLE (tail))
{
cleanup = protect_with_terminate (cleanup);
expand_eh_region_end (cleanup);
}
}
if (reachable)
{
int protect = (in_fixup && ! TREE_ADDRESSABLE (tail));
if (protect)
expand_fixup_region_start ();
expand_expr (TREE_VALUE (tail), const0_rtx, VOIDmode, 0);
if (protect)
expand_fixup_region_end (TREE_VALUE (tail));
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 (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;
{
register 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.num_ranges = 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_PTR, NOTE_INSN_DELETED);
thiscase->data.case_stmt.start = get_last_insn ();
start_cleanup_deferral ();
}
void
expand_start_case_dummy ()
{
register 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;
thiscase->data.case_stmt.num_ranges = 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)
register tree value;
tree (*converter) PROTO((tree, tree));
register 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 && ! int_fits_type_p (value, index_type))
return 3;
if (value == 0)
{
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;
}
else
return add_case_node (value, value, label, duplicate);
expand_label (label);
return 0;
}
int
pushcase_range (value1, value2, converter, label, duplicate)
register tree value1, value2;
tree (*converter) PROTO((tree, tree));
register 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 && tree_int_cst_lt (value2, value1))
return 4;
value1 = (*converter) (nominal_type, value1);
if (!value2)
value2 = TYPE_MAX_VALUE (nominal_type);
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);
}
static int
add_case_node (low, high, label, duplicate)
tree low, high;
tree label;
tree *duplicate;
{
struct case_node *p, **q, *r;
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 *) oballoc (sizeof (struct case_node));
r->low = copy_node (low);
if (tree_int_cst_equal (low, high))
r->high = r->low;
else
{
r->high = copy_node (high);
case_stack->data.case_stmt.num_ranges++;
}
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, spareness)
tree type;
int *spareness;
{
HOST_WIDE_INT count;
*spareness = 0;
switch (TREE_CODE (type))
{
tree t;
case BOOLEAN_TYPE:
count = 2;
break;
case CHAR_TYPE:
count = 1 << BITS_PER_UNIT;
break;
default:
case INTEGER_TYPE:
if (TREE_CODE (TYPE_MIN_VALUE (type)) != INTEGER_CST
|| TYPE_MAX_VALUE (type) == NULL
|| TREE_CODE (TYPE_MAX_VALUE (type)) != INTEGER_CST)
return -1;
else
{
tree mint = TYPE_MIN_VALUE (type);
tree maxt = TYPE_MAX_VALUE (type);
HOST_WIDE_INT lo, hi;
neg_double(TREE_INT_CST_LOW (mint), TREE_INT_CST_HIGH (mint),
&lo, &hi);
add_double(TREE_INT_CST_LOW (maxt), TREE_INT_CST_HIGH (maxt),
lo, hi, &lo, &hi);
add_double (lo, hi, 1, 0, &lo, &hi);
if (hi != 0 || lo < 0)
return -2;
count = lo;
}
break;
case ENUMERAL_TYPE:
count = 0;
for (t = TYPE_VALUES (type); t != NULL_TREE; t = TREE_CHAIN (t))
{
if (TREE_CODE (TYPE_MIN_VALUE (type)) != INTEGER_CST
|| TREE_CODE (TREE_VALUE (t)) != INTEGER_CST
|| TREE_INT_CST_LOW (TYPE_MIN_VALUE (type)) + count
!= TREE_INT_CST_LOW (TREE_VALUE (t)))
*spareness = 1;
count++;
}
if (*spareness == 1)
{
tree prev = TREE_VALUE (TYPE_VALUES (type));
for (t = TYPE_VALUES (type); t = TREE_CHAIN (t), t != NULL_TREE; )
{
if (! tree_int_cst_lt (prev, TREE_VALUE (t)))
{
*spareness = 2;
break;
}
prev = TREE_VALUE (t);
}
}
}
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;
long count;
int sparseness;
{
tree next_node_to_try = NULL_TREE;
long next_node_offset = 0;
register 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;
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))
{
HOST_WIDE_INT xlo, 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 >= 0 && xlo < 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;
{
register struct case_node *n;
register tree chain;
#if 0
register struct case_node **l;
int all_values = 1;
#endif
int sparseness = 0;
HOST_WIDE_INT size;
unsigned char *cases_seen;
long 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 *) malloc (bytes_needed)) != NULL)
{
long i;
tree v = TYPE_VALUES (type);
bzero (cases_seen, bytes_needed);
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))));
}
}
}
#if 0
if (all_values)
{
for (l = &case_stack->data.case_stmt.case_list;
(*l)->right != 0;
l = &(*l)->right)
;
case_stack->data.case_stmt.default_label = (*l)->code_label;
*l = 0;
}
#endif
}
void
expand_end_case (orig_index)
tree orig_index;
{
tree minval = NULL_TREE, maxval = NULL_TREE, range, orig_minval;
rtx default_label = 0;
register struct case_node *n;
unsigned int count;
rtx index;
rtx table_label;
int ncases;
rtx *labelvec;
register int i;
rtx before_case;
register struct nesting *thiscase = case_stack;
tree index_expr, index_type;
int unsignedp;
table_label = gen_label_rtx ();
index_expr = thiscase->data.case_stmt.index_expr;
index_type = TREE_TYPE (index_expr);
unsignedp = TREE_UNSIGNED (index_type);
do_pending_stack_adjust ();
check_seenlabel ();
if (index_type != error_mark_node)
{
if (!thiscase->data.case_stmt.default_label
&& TREE_CODE (TREE_TYPE (orig_index)) == ENUMERAL_TYPE
&& TREE_CODE (index_expr) != INTEGER_CST)
check_for_full_enumeration_handling (TREE_TYPE (orig_index));
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++;
}
orig_minval = minval;
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);
}
#ifndef CASE_VALUES_THRESHOLD
#ifdef HAVE_casesi
#define CASE_VALUES_THRESHOLD (HAVE_casesi ? 4 : 5)
#else
#define CASE_VALUES_THRESHOLD 5
#endif
#endif
else if (TREE_INT_CST_HIGH (range) != 0
|| count < (unsigned int) CASE_VALUES_THRESHOLD
|| ((unsigned HOST_WIDE_INT) (TREE_INT_CST_LOW (range))
> 10 * count)
#ifndef ASM_OUTPUT_ADDR_DIFF_ELT
|| flag_pic
#endif
|| TREE_CODE (index_expr) == INTEGER_CST
|| (TREE_CODE (index_expr) == CALL_EXPR
&& TREE_CODE (TREE_OPERAND (index_expr, 0)) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (TREE_OPERAND (index_expr, 0), 0)) == FUNCTION_DECL
&& DECL_FUNCTION_CODE (TREE_OPERAND (TREE_OPERAND (index_expr, 0), 0)) == BUILT_IN_CLASSIFY_TYPE)
|| (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
&& (cmp_optab->handlers[(int) GET_MODE(index)].insn_code
== CODE_FOR_nothing))
{
enum machine_mode wider_mode;
for (wider_mode = GET_MODE (index); wider_mode != VOIDmode;
wider_mode = GET_MODE_WIDER_MODE (wider_mode))
if (cmp_optab->handlers[(int) wider_mode].insn_code
!= CODE_FOR_nothing)
{
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 (TREE_TYPE (orig_index)) != ENUMERAL_TYPE
&& estimate_case_costs (thiscase->data.case_stmt.case_list));
balance_case_nodes (&thiscase->data.case_stmt.case_list,
NULL_PTR);
emit_case_nodes (index, thiscase->data.case_stmt.case_list,
default_label, index_type);
emit_jump_if_reachable (default_label);
}
}
else
{
int win = 0;
#ifdef HAVE_casesi
if (HAVE_casesi)
{
enum machine_mode index_mode = SImode;
int index_bits = GET_MODE_BITSIZE (index_mode);
rtx op1, op2;
enum machine_mode op_mode;
if (GET_MODE_BITSIZE (TYPE_MODE (index_type))
> GET_MODE_BITSIZE (index_mode))
{
enum machine_mode omode = TYPE_MODE (index_type);
rtx rangertx = expand_expr (range, NULL_RTX, VOIDmode, 0);
index_expr = build (MINUS_EXPR, index_type,
index_expr, minval);
minval = integer_zero_node;
index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0);
emit_cmp_and_jump_insns (rangertx, index, LTU, NULL_RTX,
omode, 1, 0, default_label);
index = convert_to_mode (index_mode, index, 0);
}
else
{
if (TYPE_MODE (index_type) != index_mode)
{
index_expr = convert (type_for_size (index_bits, 0),
index_expr);
index_type = TREE_TYPE (index_expr);
}
index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0);
}
emit_queue ();
index = protect_from_queue (index, 0);
do_pending_stack_adjust ();
op_mode = insn_operand_mode[(int)CODE_FOR_casesi][0];
if (! (*insn_operand_predicate[(int)CODE_FOR_casesi][0])
(index, op_mode))
index = copy_to_mode_reg (op_mode, index);
op1 = expand_expr (minval, NULL_RTX, VOIDmode, 0);
op_mode = insn_operand_mode[(int)CODE_FOR_casesi][1];
if (! (*insn_operand_predicate[(int)CODE_FOR_casesi][1])
(op1, op_mode))
op1 = copy_to_mode_reg (op_mode, op1);
op2 = expand_expr (range, NULL_RTX, VOIDmode, 0);
op_mode = insn_operand_mode[(int)CODE_FOR_casesi][2];
if (! (*insn_operand_predicate[(int)CODE_FOR_casesi][2])
(op2, op_mode))
op2 = copy_to_mode_reg (op_mode, op2);
emit_jump_insn (gen_casesi (index, op1, op2,
table_label, default_label));
win = 1;
}
#endif
#ifdef HAVE_tablejump
if (! win && HAVE_tablejump)
{
index_expr = convert (thiscase->data.case_stmt.nominal_type,
fold (build (MINUS_EXPR, index_type,
index_expr, minval)));
index_type = TREE_TYPE (index_expr);
index = expand_expr (index_expr, NULL_RTX, VOIDmode, 0);
emit_queue ();
index = protect_from_queue (index, 0);
do_pending_stack_adjust ();
do_tablejump (index, TYPE_MODE (index_type),
expand_expr (range, NULL_RTX, VOIDmode, 0),
table_label, default_label);
win = 1;
}
#endif
if (! win)
abort ();
ncases = TREE_INT_CST_LOW (range) + 1;
labelvec = (rtx *) alloca (ncases * sizeof (rtx));
bzero ((char *) labelvec, ncases * sizeof (rtx));
for (n = thiscase->data.case_stmt.case_list; n; n = n->right)
{
register HOST_WIDE_INT i
= TREE_INT_CST_LOW (n->low) - TREE_INT_CST_LOW (orig_minval);
while (1)
{
labelvec[i]
= gen_rtx_LABEL_REF (Pmode, label_rtx (n->code_label));
if (i + TREE_INT_CST_LOW (orig_minval)
== TREE_INT_CST_LOW (n->high))
break;
i++;
}
}
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, 0));
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 = squeeze_notes (NEXT_INSN (before_case), get_last_insn ());
reorder_insns (before_case, get_last_insn (),
thiscase->data.case_stmt.start);
}
else
end_cleanup_deferral ();
if (thiscase->exit_label)
emit_label (thiscase->exit_label);
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
{
enum machine_mode mode = GET_MODE (op1);
if (mode == VOIDmode)
mode = GET_MODE (op2);
emit_cmp_and_jump_insns (op1, op2, EQ, NULL_RTX, mode, unsignedp,
0, label);
}
}
static int
estimate_case_costs (node)
case_node_ptr node;
{
tree min_ascii = build_int_2 (-1, -1);
tree max_ascii = convert (TREE_TYPE (node->high), build_int_2 (127, 0));
case_node_ptr n;
int i;
if (cost_table == NULL)
{
cost_table = ((short *) xmalloc (129 * sizeof (short))) + 1;
bzero ((char *) (cost_table - 1), 129 * sizeof (short));
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 = TREE_INT_CST_LOW (n->low); i <= 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;
{
register case_node_ptr np;
np = *head;
if (np)
{
int cost = 0;
int i = 0;
int ranges = 0;
register 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);
typedef rtx rtx_fn ();
enum machine_mode mode = GET_MODE (index);
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, expand_expr (node->low, NULL_RTX, VOIDmode, 0),
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, expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
GT, NULL_RTX, mode, unsignedp, 0,
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, expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
LT, NULL_RTX, mode, unsignedp, 0,
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, expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
GT, NULL_RTX, mode, unsignedp, 0,
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, expand_expr (node->high,
NULL_RTX,
VOIDmode, 0),
LT, NULL_RTX, mode, unsignedp, 0,
default_label);
}
emit_case_nodes (index, node->right, default_label, index_type);
}
else
do_jump_if_equal (index,
expand_expr (node->right->low, NULL_RTX,
VOIDmode, 0),
label_rtx (node->right->code_label), unsignedp);
}
else if (node->right == 0 && node->left != 0)
{
#if 0
if (use_cost_table
&& cost_table[TREE_INT_CST_LOW (node->high)] < 12)
;
#endif
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, expand_expr (node->high,
NULL_RTX,
VOIDmode, 0),
GT, NULL_RTX, mode, unsignedp, 0,
default_label);
}
emit_case_nodes (index, node->left, default_label, index_type);
}
else
do_jump_if_equal (index,
expand_expr (node->left->low, NULL_RTX,
VOIDmode, 0),
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, expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
GT, NULL_RTX, mode, unsignedp, 0,
label_rtx (node->right->code_label));
else
{
test_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
emit_cmp_and_jump_insns (index, expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
GT, NULL_RTX, mode, unsignedp, 0,
label_rtx (test_label));
}
emit_cmp_and_jump_insns (index, expand_expr (node->low, NULL_RTX,
VOIDmode, 0),
GE, NULL_RTX, mode, unsignedp, 0,
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, expand_expr (node->low, NULL_RTX,
VOIDmode, 0),
LT, NULL_RTX, mode, unsignedp, 0,
default_label);
}
emit_cmp_and_jump_insns (index, expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
LE, NULL_RTX, mode, unsignedp, 0,
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, expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
GT, NULL_RTX, mode, unsignedp, 0,
default_label);
}
emit_cmp_and_jump_insns (index, expand_expr (node->low, NULL_RTX,
VOIDmode, 0),
GE, NULL_RTX, mode, unsignedp, 0,
label_rtx (node->code_label));
emit_case_nodes (index, node->left, default_label, index_type);
}
else
{
if (!node_has_high_bound (node, index_type))
{
emit_cmp_and_jump_insns (index, expand_expr (node->high, NULL_RTX,
VOIDmode, 0),
GT, NULL_RTX, mode, unsignedp, 0,
default_label);
}
if (!node_has_low_bound (node, index_type))
{
emit_cmp_and_jump_insns (index, expand_expr (node->low, NULL_RTX,
VOIDmode, 0),
LT, NULL_RTX, mode, unsignedp, 0,
default_label);
}
emit_jump (label_rtx (node->code_label));
}
}
}
static tree *block_vector;
void
find_loop_tree_blocks ()
{
tree block = DECL_INITIAL (current_function_decl);
block_vector = identify_blocks (block, get_insns ());
}
void
unroll_block_trees ()
{
tree block = DECL_INITIAL (current_function_decl);
reorder_blocks (block_vector, block, get_insns ());
if (block_vector)
free (block_vector);
}