-
- /* Structures whose size is not a multiple of a word are aligned
- to the least significant byte (to the right). On a BYTES_BIG_ENDIAN
- machine, this means we must skip the empty high order bytes when
- calculating the bit offset. */
- if (BYTES_BIG_ENDIAN
- && bytes % UNITS_PER_WORD)
- big_endian_correction = (BITS_PER_WORD - ((bytes % UNITS_PER_WORD)
- * BITS_PER_UNIT));
-
- /* Copy the structure BITSIZE bits at a time. */
- for (bitpos = 0, xbitpos = big_endian_correction;
- bitpos < bytes * BITS_PER_UNIT;
- bitpos += bitsize, xbitpos += bitsize)
- {
- /* We need a new destination pseudo each time xbitpos is
- on a word boundary and when xbitpos == big_endian_correction
- (the first time through). */
- if (xbitpos % BITS_PER_WORD == 0
- || xbitpos == big_endian_correction)
- {
- /* Generate an appropriate register. */
- dst = gen_reg_rtx (word_mode);
- result_pseudos[xbitpos / BITS_PER_WORD] = dst;
-
- /* Clear the destination before we move anything into it. */
- emit_move_insn (dst, CONST0_RTX (GET_MODE (dst)));
- }
-
- /* We need a new source operand each time bitpos is on a word
- boundary. */
- if (bitpos % BITS_PER_WORD == 0)
- src = operand_subword_force (result_val,
- bitpos / BITS_PER_WORD,
- BLKmode);
-
- /* Use bitpos for the source extraction (left justified) and
- xbitpos for the destination store (right justified). */
- store_bit_field (dst, bitsize, xbitpos % BITS_PER_WORD, word_mode,
- extract_bit_field (src, bitsize,
- bitpos % BITS_PER_WORD, 1,
- NULL_RTX, word_mode, word_mode,
- BITS_PER_WORD),
- BITS_PER_WORD);
- }
-
- /* Find the smallest integer mode large enough to hold the
- entire structure and use that mode instead of BLKmode
- on the USE insn for the return register. */
- for (tmpmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
- tmpmode != VOIDmode;
- tmpmode = GET_MODE_WIDER_MODE (tmpmode))
- /* Have we found a large enough mode? */
- if (GET_MODE_SIZE (tmpmode) >= bytes)
- break;
-
- /* No suitable mode found. */
- if (tmpmode == VOIDmode)
- abort ();
-
- PUT_MODE (result_rtl, tmpmode);
-
- if (GET_MODE_SIZE (tmpmode) < GET_MODE_SIZE (word_mode))
- result_reg_mode = word_mode;
- else
- result_reg_mode = tmpmode;
- result_reg = gen_reg_rtx (result_reg_mode);
-
- emit_queue ();
- for (i = 0; i < n_regs; i++)
- emit_move_insn (operand_subword (result_reg, i, 0, result_reg_mode),
- result_pseudos[i]);
-
- if (tmpmode != result_reg_mode)
- result_reg = gen_lowpart (tmpmode, result_reg);
-
- expand_value_return (result_reg);
- }
- else if (retval_rhs != 0
- && !VOID_TYPE_P (TREE_TYPE (retval_rhs))
- && (GET_CODE (result_rtl) == REG
- || (GET_CODE (result_rtl) == PARALLEL)))
- {
- /* Calculate the return value into a temporary (usually a pseudo
- reg). */
- tree ot = TREE_TYPE (DECL_RESULT (current_function_decl));
- tree nt = build_qualified_type (ot, TYPE_QUALS (ot) | TYPE_QUAL_CONST);
-
- val = assign_temp (nt, 0, 0, 1);
- val = expand_expr (retval_rhs, val, GET_MODE (val), 0);
- val = force_not_mem (val);
- emit_queue ();
- /* Return the calculated value, doing cleanups first. */
- expand_value_return (val);
- }
- else
- {
- /* No cleanups or no hard reg used;
- calculate value into hard return reg. */
- expand_expr (retval, const0_rtx, VOIDmode, 0);
- emit_queue ();
- expand_value_return (result_rtl);
- }
-}
-\f
-/* Attempt to optimize a potential tail recursion call into a goto.
- ARGUMENTS are the arguments to a CALL_EXPR; LAST_INSN indicates
- where to place the jump to the tail recursion label.
-
- Return TRUE if the call was optimized into a goto. */
-
-int
-optimize_tail_recursion (arguments, last_insn)
- tree arguments;
- rtx last_insn;
-{
- /* Finish checking validity, and if valid emit code to set the
- argument variables for the new call. */
- if (tail_recursion_args (arguments, DECL_ARGUMENTS (current_function_decl)))
- {
- if (tail_recursion_label == 0)
- {
- tail_recursion_label = gen_label_rtx ();
- emit_label_after (tail_recursion_label,
- tail_recursion_reentry);
- }
- emit_queue ();
- expand_goto_internal (NULL_TREE, tail_recursion_label, last_insn);
- emit_barrier ();
- return 1;
- }
- return 0;
-}
-
-/* Emit code to alter this function's formal parms for a tail-recursive call.
- ACTUALS is a list of actual parameter expressions (chain of TREE_LISTs).
- FORMALS is the chain of decls of formals.
- Return 1 if this can be done;
- otherwise return 0 and do not emit any code. */
-
-static int
-tail_recursion_args (actuals, formals)
- tree actuals, formals;
-{
- tree a = actuals, f = formals;
- int i;
- rtx *argvec;
-
- /* Check that number and types of actuals are compatible
- with the formals. This is not always true in valid C code.
- Also check that no formal needs to be addressable
- and that all formals are scalars. */
-
- /* Also count the args. */
-
- 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;
-
- /* Compute all the actuals. */
-
- 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);
-
- /* Find which actual values refer to current values of previous formals.
- Copy each of them now, before any formal is changed. */
-
- for (a = actuals, i = 0; a; a = TREE_CHAIN (a), i++)
- {
- int copy = 0;
- int j;
- for (f = formals, j = 0; j < i; f = TREE_CHAIN (f), j++)
- if (reg_mentioned_p (DECL_RTL (f), argvec[i]))
- {
- copy = 1;
- break;
- }
- if (copy)
- argvec[i] = copy_to_reg (argvec[i]);
- }
-
- /* Store the values of the actuals into the formals. */
-
- 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
- {
- rtx tmp = argvec[i];
- int unsignedp = TREE_UNSIGNED (TREE_TYPE (TREE_VALUE (a)));
- promote_mode(TREE_TYPE (TREE_VALUE (a)), GET_MODE (tmp),
- &unsignedp, 0);
- if (DECL_MODE (f) != GET_MODE (DECL_RTL (f)))
- {
- tmp = gen_reg_rtx (DECL_MODE (f));
- convert_move (tmp, argvec[i], unsignedp);
- }
- convert_move (DECL_RTL (f), tmp, unsignedp);
- }
- }
-
- free_temp_slots ();
- return 1;
-}
-\f
-/* Generate the RTL code for entering a binding contour.
- The variables are declared one by one, by calls to `expand_decl'.
-
- FLAGS is a bitwise or of the following flags:
-
- 1 - Nonzero if this construct should be visible to
- `exit_something'.
-
- 2 - Nonzero if this contour does not require a
- NOTE_INSN_BLOCK_BEG note. Virtually all calls from
- language-independent code should set this flag because they
- will not create corresponding BLOCK nodes. (There should be
- a one-to-one correspondence between NOTE_INSN_BLOCK_BEG notes
- and BLOCKs.) If this flag is set, MARK_ENDS should be zero
- when expand_end_bindings is called.
-
- If we are creating a NOTE_INSN_BLOCK_BEG note, a BLOCK may
- optionally be supplied. If so, it becomes the NOTE_BLOCK for the
- note. */
-
-void
-expand_start_bindings_and_block (flags, block)
- int flags;
- tree block;
-{
- struct nesting *thisblock = ALLOC_NESTING ();
- rtx note;
- int exit_flag = ((flags & 1) != 0);
- int block_flag = ((flags & 2) == 0);
-
- /* If a BLOCK is supplied, then the caller should be requesting a
- NOTE_INSN_BLOCK_BEG note. */
- if (!block_flag && block)
- abort ();
-
- /* Create a note to mark the beginning of the block. */
- if (block_flag)
- {
- note = emit_note (NOTE_INSN_BLOCK_BEG);
- NOTE_BLOCK (note) = block;
- }
- else
- note = emit_note (NOTE_INSN_DELETED);
-
- /* Make an entry on block_stack for the block we are entering. */
-
- thisblock->desc = BLOCK_NESTING;
- thisblock->next = block_stack;
- thisblock->all = nesting_stack;
- thisblock->depth = ++nesting_depth;
- thisblock->data.block.stack_level = 0;
- thisblock->data.block.cleanups = 0;
- thisblock->data.block.exception_region = 0;
- thisblock->data.block.block_target_temp_slot_level = target_temp_slot_level;
-
- thisblock->data.block.conditional_code = 0;
- thisblock->data.block.last_unconditional_cleanup = note;
- /* When we insert instructions after the last unconditional cleanup,
- we don't adjust last_insn. That means that a later add_insn will
- clobber the instructions we've just added. The easiest way to
- fix this is to just insert another instruction here, so that the
- instructions inserted after the last unconditional cleanup are
- never the last instruction. */
- emit_note (NOTE_INSN_DELETED);
-
- if (block_stack
- && !(block_stack->data.block.cleanups == NULL_TREE
- && block_stack->data.block.outer_cleanups == NULL_TREE))
- thisblock->data.block.outer_cleanups
- = tree_cons (NULL_TREE, block_stack->data.block.cleanups,
- block_stack->data.block.outer_cleanups);
- else
- thisblock->data.block.outer_cleanups = 0;
- thisblock->data.block.label_chain = 0;
- thisblock->data.block.innermost_stack_block = stack_block_stack;
- thisblock->data.block.first_insn = note;
- thisblock->data.block.block_start_count = ++current_block_start_count;
- thisblock->exit_label = exit_flag ? gen_label_rtx () : 0;
- block_stack = thisblock;
- nesting_stack = thisblock;
-
- /* Make a new level for allocating stack slots. */
- push_temp_slots ();
-}
-
-/* Specify the scope of temporaries created by TARGET_EXPRs. Similar
- to CLEANUP_POINT_EXPR, but handles cases when a series of calls to
- expand_expr are made. After we end the region, we know that all
- space for all temporaries that were created by TARGET_EXPRs will be
- destroyed and their space freed for reuse. */
-
-void
-expand_start_target_temps ()
-{
- /* This is so that even if the result is preserved, the space
- allocated will be freed, as we know that it is no longer in use. */
- push_temp_slots ();
-
- /* Start a new binding layer that will keep track of all cleanup
- actions to be performed. */
- expand_start_bindings (2);
-
- target_temp_slot_level = temp_slot_level;
-}
-
-void
-expand_end_target_temps ()
-{
- expand_end_bindings (NULL_TREE, 0, 0);
-
- /* This is so that even if the result is preserved, the space
- allocated will be freed, as we know that it is no longer in use. */
- pop_temp_slots ();
-}
-
-/* Given a pointer to a BLOCK node return nonzero if (and only if) the node
- in question represents the outermost pair of curly braces (i.e. the "body
- block") of a function or method.
-
- For any BLOCK node representing a "body block" of a function or method, the
- BLOCK_SUPERCONTEXT of the node will point to another BLOCK node which
- represents the outermost (function) scope for the function or method (i.e.
- the one which includes the formal parameters). The BLOCK_SUPERCONTEXT of
- *that* node in turn will point to the relevant FUNCTION_DECL node. */
-
-int
-is_body_block (stmt)
- tree stmt;
-{
- if (lang_hooks.no_body_blocks)
- return 0;
-
- if (TREE_CODE (stmt) == BLOCK)
- {
- tree parent = BLOCK_SUPERCONTEXT (stmt);
-
- if (parent && TREE_CODE (parent) == BLOCK)
- {
- tree grandparent = BLOCK_SUPERCONTEXT (parent);
-
- if (grandparent && TREE_CODE (grandparent) == FUNCTION_DECL)
- return 1;
- }
- }
-
- return 0;
-}
-
-/* True if we are currently emitting insns in an area of output code
- that is controlled by a conditional expression. This is used by
- the cleanup handling code to generate conditional cleanup actions. */
-
-int
-conditional_context ()
-{
- return block_stack && block_stack->data.block.conditional_code;
-}
-
-/* Return an opaque pointer to the current nesting level, so frontend code
- can check its own sanity. */
-
-struct nesting *
-current_nesting_level ()
-{
- return cfun ? block_stack : 0;
-}
-
-/* Emit a handler label for a nonlocal goto handler.
- Also emit code to store the handler label in SLOT before BEFORE_INSN. */
-
-static rtx
-expand_nl_handler_label (slot, before_insn)
- rtx slot, before_insn;
-{
- rtx insns;
- rtx handler_label = gen_label_rtx ();
-
- /* Don't let cleanup_cfg delete the handler. */
- 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_insn_before (insns, before_insn);
-
- emit_label (handler_label);
-
- return handler_label;
-}
-
-/* Emit code to restore vital registers at the beginning of a nonlocal goto
- handler. */
-static void
-expand_nl_goto_receiver ()
-{
-#ifdef HAVE_nonlocal_goto
- if (! HAVE_nonlocal_goto)
-#endif
- /* First adjust our frame pointer to its actual value. It was
- previously set to the start of the virtual area corresponding to
- the stacked variables when we branched here and now needs to be
- adjusted to the actual hardware fp value.
-
- Assignments are to virtual registers are converted by
- instantiate_virtual_regs into the corresponding assignment
- to the underlying register (fp in this case) that makes
- the original assignment true.
- So the following insn will actually be
- decrementing fp by STARTING_FRAME_OFFSET. */
- emit_move_insn (virtual_stack_vars_rtx, hard_frame_pointer_rtx);
-
-#if ARG_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
- if (fixed_regs[ARG_POINTER_REGNUM])
- {
-#ifdef ELIMINABLE_REGS
- /* If the argument pointer can be eliminated in favor of the
- frame pointer, we don't need to restore it. We assume here
- that if such an elimination is present, it can always be used.
- This is the case on all known machines; if we don't make this
- assumption, we do unnecessary saving on many machines. */
- static const struct elims {const int from, to;} elim_regs[] = ELIMINABLE_REGS;
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE (elim_regs); i++)
- if (elim_regs[i].from == ARG_POINTER_REGNUM
- && elim_regs[i].to == HARD_FRAME_POINTER_REGNUM)
- break;
-
- if (i == ARRAY_SIZE (elim_regs))
-#endif
- {
- /* Now restore our arg pointer from the address at which it
- was saved in our stack frame. */
- emit_move_insn (virtual_incoming_args_rtx,
- copy_to_reg (get_arg_pointer_save_area (cfun)));
- }
- }
-#endif
-
-#ifdef HAVE_nonlocal_goto_receiver
- if (HAVE_nonlocal_goto_receiver)
- emit_insn (gen_nonlocal_goto_receiver ());
-#endif
-}
-
-/* Make handlers for nonlocal gotos taking place in the function calls in
- block THISBLOCK. */
-
-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;
-
- /* Record the handler address in the stack slot for that purpose,
- during this block, saving and restoring the outer value. */
- 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_insn_before (insns, thisblock->data.block.first_insn);
- }
-
- /* Jump around the handlers; they run only when specially invoked. */
- emit_jump (afterward);
-
- /* Make a separate handler for each label. */
- link = nonlocal_labels;
- slot = nonlocal_goto_handler_slots;
- label_list = NULL_RTX;
- for (; link; link = TREE_CHAIN (link), slot = XEXP (slot, 1))
- /* Skip any labels we shouldn't be able to jump to from here,
- we generate one special handler for all of them below which just calls
- abort. */
- 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 ();
-
- /* Jump to the "real" nonlocal label. */
- expand_goto (TREE_VALUE (link));
- }
-
- /* A second pass over all nonlocal labels; this time we handle those
- we should not be able to jump to at this point. */
- link = nonlocal_labels;
- slot = nonlocal_goto_handler_slots;
- any_invalid = 0;
- for (; link; link = TREE_CHAIN (link), slot = XEXP (slot, 1))
- if (DECL_TOO_LATE (TREE_VALUE (link)))
- {
- rtx lab;
- lab = expand_nl_handler_label (XEXP (slot, 0),
- thisblock->data.block.first_insn);
- label_list = gen_rtx_EXPR_LIST (VOIDmode, lab, label_list);
- any_invalid = 1;
- }
-
- if (any_invalid)
- {
- expand_nl_goto_receiver ();
- expand_builtin_trap ();
- }
-
- nonlocal_goto_handler_labels = label_list;
- emit_label (afterward);
-}
-
-/* Warn about any unused VARS (which may contain nodes other than
- VAR_DECLs, but such nodes are ignored). The nodes are connected
- via the TREE_CHAIN field. */
-
-void
-warn_about_unused_variables (vars)
- tree vars;
-{
- tree decl;
-
- if (warn_unused_variable)
- for (decl = vars; decl; decl = TREE_CHAIN (decl))
- if (TREE_CODE (decl) == VAR_DECL
- && ! TREE_USED (decl)
- && ! DECL_IN_SYSTEM_HEADER (decl)
- && DECL_NAME (decl) && ! DECL_ARTIFICIAL (decl))
- warning_with_decl (decl, "unused variable `%s'");
-}
-
-/* Generate RTL code to terminate a binding contour.
-
- VARS is the chain of VAR_DECL nodes for the variables bound in this
- contour. There may actually be other nodes in this chain, but any
- nodes other than VAR_DECLS are ignored.
-
- MARK_ENDS is nonzero if we should put a note at the beginning
- and end of this binding contour.
-
- DONT_JUMP_IN is positive if it is not valid to jump into this contour,
- zero if we can jump into this contour only if it does not have a saved
- stack level, and negative if we are not to check for invalid use of
- labels (because the front end does that). */
-
-void
-expand_end_bindings (vars, mark_ends, dont_jump_in)
- tree vars;
- int mark_ends;
- int dont_jump_in;
-{
- struct nesting *thisblock = block_stack;
-
- /* If any of the variables in this scope were not used, warn the
- user. */
- warn_about_unused_variables (vars);
-
- if (thisblock->exit_label)
- {
- do_pending_stack_adjust ();
- emit_label (thisblock->exit_label);
- }
-
- /* If necessary, make handlers for nonlocal gotos taking
- place in the function calls in this block. */
- if (function_call_count != 0 && nonlocal_labels
- /* Make handler for outermost block
- if there were any nonlocal gotos to this function. */
- && (thisblock->next == 0 ? current_function_has_nonlocal_label
- /* Make handler for inner block if it has something
- special to do when you jump out of it. */
- : (thisblock->data.block.cleanups != 0
- || thisblock->data.block.stack_level != 0)))
- expand_nl_goto_receivers (thisblock);
-
- /* Don't allow jumping into a block that has a stack level.
- Cleanups are allowed, though. */
- if (dont_jump_in > 0
- || (dont_jump_in == 0 && thisblock->data.block.stack_level != 0))
- {
- struct label_chain *chain;
-
- /* Any labels in this block are no longer valid to go to.
- Mark them to cause an error message. */
- for (chain = thisblock->data.block.label_chain; chain; chain = chain->next)
- {
- DECL_TOO_LATE (chain->label) = 1;
- /* If any goto without a fixup came to this label,
- that must be an error, because gotos without fixups
- come from outside all saved stack-levels. */
- if (TREE_ADDRESSABLE (chain->label))
- error_with_decl (chain->label,
- "label `%s' used before containing binding contour");
- }
- }
-
- /* Restore stack level in effect before the block
- (only if variable-size objects allocated). */
- /* Perform any cleanups associated with the block. */
-
- if (thisblock->data.block.stack_level != 0
- || thisblock->data.block.cleanups != 0)
- {
- int reachable;
- rtx insn;
-
- /* Don't let cleanups affect ({...}) constructs. */
- 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;
-
- /* Only clean up here if this point can actually be reached. */
- insn = get_last_insn ();
- if (GET_CODE (insn) == NOTE)
- insn = prev_nonnote_insn (insn);
- reachable = (! insn || GET_CODE (insn) != BARRIER);
-
- /* Do the cleanups. */
- expand_cleanups (thisblock->data.block.cleanups, 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;
-
- /* Restore the stack level. */
-
- 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);
- }
-
- /* Any gotos out of this block must also do these things.
- Also report any gotos with fixups that came to labels in this
- level. */
- fixup_gotos (thisblock,
- thisblock->data.block.stack_level,
- thisblock->data.block.cleanups,
- thisblock->data.block.first_insn,
- dont_jump_in);
- }
-
- /* Mark the beginning and end of the scope if requested.
- We do this now, after running cleanups on the variables
- just going out of scope, so they are in scope for their cleanups. */
-
- if (mark_ends)
- {
- rtx note = emit_note (NOTE_INSN_BLOCK_END);
- NOTE_BLOCK (note) = NOTE_BLOCK (thisblock->data.block.first_insn);
- }
- else
- /* Get rid of the beginning-mark if we don't make an end-mark. */
- NOTE_LINE_NUMBER (thisblock->data.block.first_insn) = NOTE_INSN_DELETED;
-
- /* Restore the temporary level of TARGET_EXPRs. */
- target_temp_slot_level = thisblock->data.block.block_target_temp_slot_level;
-
- /* Restore block_stack level for containing block. */
-
- stack_block_stack = thisblock->data.block.innermost_stack_block;
- POPSTACK (block_stack);
-
- /* Pop the stack slot nesting and free any slots at this level. */
- pop_temp_slots ();
-}
-\f
-/* Generate code to save the stack pointer at the start of the current block
- and set up to restore it on exit. */
-
-void
-save_stack_pointer ()
-{
- struct nesting *thisblock = block_stack;
-
- if (thisblock->data.block.stack_level == 0)
- {
- emit_stack_save (thisblock->next ? SAVE_BLOCK : SAVE_FUNCTION,
- &thisblock->data.block.stack_level,
- thisblock->data.block.first_insn);
- stack_block_stack = thisblock;
- }
-}
-\f
-/* Generate RTL for the automatic variable declaration DECL.
- (Other kinds of declarations are simply ignored if seen here.) */
-
-void
-expand_decl (decl)
- tree decl;
-{
- tree type;
-
- type = TREE_TYPE (decl);
-
- /* For a CONST_DECL, set mode, alignment, and sizes from those of the
- type in case this node is used in a reference. */
- if (TREE_CODE (decl) == CONST_DECL)
- {
- DECL_MODE (decl) = TYPE_MODE (type);
- DECL_ALIGN (decl) = TYPE_ALIGN (type);
- DECL_SIZE (decl) = TYPE_SIZE (type);
- DECL_SIZE_UNIT (decl) = TYPE_SIZE_UNIT (type);
- return;
- }
-
- /* Otherwise, only automatic variables need any expansion done. Static and
- external variables, and external functions, will be handled by
- `assemble_variable' (called from finish_decl). TYPE_DECL requires
- nothing. PARM_DECLs are handled in `assign_parms'. */
- if (TREE_CODE (decl) != VAR_DECL)
- return;
-
- if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
- return;
-
- /* Create the RTL representation for the variable. */
-
- if (type == error_mark_node)
- SET_DECL_RTL (decl, gen_rtx_MEM (BLKmode, const0_rtx));
-
- else if (DECL_SIZE (decl) == 0)
- /* Variable with incomplete type. */
- {
- rtx x;
- if (DECL_INITIAL (decl) == 0)
- /* Error message was already done; now avoid a crash. */
- x = gen_rtx_MEM (BLKmode, const0_rtx);
- else
- /* An initializer is going to decide the size of this array.
- Until we know the size, represent its address with a reg. */
- x = gen_rtx_MEM (BLKmode, gen_reg_rtx (Pmode));
-
- set_mem_attributes (x, decl, 1);
- SET_DECL_RTL (decl, x);
- }
- else if (DECL_MODE (decl) != BLKmode
- /* If -ffloat-store, don't put explicit float vars
- into regs. */
- && !(flag_float_store
- && TREE_CODE (type) == REAL_TYPE)
- && ! TREE_THIS_VOLATILE (decl)
- && ! DECL_NONLOCAL (decl)
- && (DECL_REGISTER (decl) || DECL_ARTIFICIAL (decl) || optimize))
- {
- /* Automatic variable that can go in a register. */
- int unsignedp = TREE_UNSIGNED (type);
- enum machine_mode reg_mode
- = promote_mode (type, DECL_MODE (decl), &unsignedp, 0);
-
- SET_DECL_RTL (decl, gen_reg_rtx (reg_mode));
-
- if (!DECL_ARTIFICIAL (decl))
- mark_user_reg (DECL_RTL (decl));
-
- if (POINTER_TYPE_P (type))
- mark_reg_pointer (DECL_RTL (decl),
- TYPE_ALIGN (TREE_TYPE (TREE_TYPE (decl))));
-
- maybe_set_unchanging (DECL_RTL (decl), decl);
-
- /* If something wants our address, try to use ADDRESSOF. */
- if (TREE_ADDRESSABLE (decl))
- put_var_into_stack (decl, /*rescan=*/false);
- }
-
- else if (TREE_CODE (DECL_SIZE_UNIT (decl)) == INTEGER_CST
- && ! (flag_stack_check && ! STACK_CHECK_BUILTIN
- && 0 < compare_tree_int (DECL_SIZE_UNIT (decl),
- STACK_CHECK_MAX_VAR_SIZE)))
- {
- /* Variable of fixed size that goes on the stack. */
- rtx oldaddr = 0;
- rtx addr;
- rtx x;
-
- /* If we previously made RTL for this decl, it must be an array
- whose size was determined by the initializer.
- The old address was a register; set that register now
- to the proper address. */
- if (DECL_RTL_SET_P (decl))
- {
- if (GET_CODE (DECL_RTL (decl)) != MEM
- || GET_CODE (XEXP (DECL_RTL (decl), 0)) != REG)
- abort ();
- oldaddr = XEXP (DECL_RTL (decl), 0);
- }
-
- /* Set alignment we actually gave this decl. */
- DECL_ALIGN (decl) = (DECL_MODE (decl) == BLKmode ? BIGGEST_ALIGNMENT
- : GET_MODE_BITSIZE (DECL_MODE (decl)));
- DECL_USER_ALIGN (decl) = 0;
-
- x = assign_temp (decl, 1, 1, 1);
- set_mem_attributes (x, decl, 1);
- SET_DECL_RTL (decl, x);
-
- if (oldaddr)
- {
- addr = force_operand (XEXP (DECL_RTL (decl), 0), oldaddr);
- if (addr != oldaddr)
- emit_move_insn (oldaddr, addr);
- }
- }
- else
- /* Dynamic-size object: must push space on the stack. */
- {
- rtx address, size, x;
-
- /* Record the stack pointer on entry to block, if have
- not already done so. */
- do_pending_stack_adjust ();
- save_stack_pointer ();
-
- /* In function-at-a-time mode, variable_size doesn't expand this,
- so do it now. */
- if (TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type))
- expand_expr (TYPE_MAX_VALUE (TYPE_DOMAIN (type)),
- const0_rtx, VOIDmode, 0);
-
- /* Compute the variable's size, in bytes. */
- size = expand_expr (DECL_SIZE_UNIT (decl), NULL_RTX, VOIDmode, 0);
- free_temp_slots ();
-
- /* Allocate space on the stack for the variable. Note that
- DECL_ALIGN says how the variable is to be aligned and we
- cannot use it to conclude anything about the alignment of
- the size. */
- address = allocate_dynamic_stack_space (size, NULL_RTX,
- TYPE_ALIGN (TREE_TYPE (decl)));
-
- /* Reference the variable indirect through that rtx. */
- x = gen_rtx_MEM (DECL_MODE (decl), address);
- set_mem_attributes (x, decl, 1);
- SET_DECL_RTL (decl, x);
-
-
- /* Indicate the alignment we actually gave this variable. */
-#ifdef STACK_BOUNDARY
- DECL_ALIGN (decl) = STACK_BOUNDARY;
-#else
- DECL_ALIGN (decl) = BIGGEST_ALIGNMENT;
-#endif
- DECL_USER_ALIGN (decl) = 0;
- }
-}
-\f
-/* Emit code to perform the initialization of a declaration DECL. */
-
-void
-expand_decl_init (decl)
- tree decl;
-{
- int was_used = TREE_USED (decl);
-
- /* If this is a CONST_DECL, we don't have to generate any code. Likewise
- for static decls. */
- if (TREE_CODE (decl) == CONST_DECL
- || TREE_STATIC (decl))
- return;
-
- /* Compute and store the initial value now. */
-
- push_temp_slots ();
-
- 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 ();
- }
-
- /* Don't let the initialization count as "using" the variable. */
- TREE_USED (decl) = was_used;
-
- /* Free any temporaries we made while initializing the decl. */
- preserve_temp_slots (NULL_RTX);
- free_temp_slots ();
- pop_temp_slots ();
-}
-
-/* CLEANUP is an expression to be executed at exit from this binding contour;
- for example, in C++, it might call the destructor for this variable.
-
- We wrap CLEANUP in an UNSAVE_EXPR node, so that we can expand the
- CLEANUP multiple times, and have the correct semantics. This
- happens in exception handling, for gotos, returns, breaks that
- leave the current scope.
-
- If CLEANUP is nonzero and DECL is zero, we record a cleanup
- that is not associated with any particular variable. */
-
-int
-expand_decl_cleanup (decl, cleanup)
- tree decl, cleanup;
-{
- struct nesting *thisblock;
-
- /* Error if we are not in any block. */
- if (cfun == 0 || block_stack == 0)
- return 0;
-
- thisblock = block_stack;
-
- /* Record the cleanup if there is one. */
-
- 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_insn_after (set_flag_0,
- thisblock->data.block.last_unconditional_cleanup);
-
- emit_move_insn (flag, const1_rtx);
-
- cond = build_decl (VAR_DECL, NULL_TREE,
- (*lang_hooks.types.type_for_mode) (word_mode, 1));
- SET_DECL_RTL (cond, flag);
-
- /* Conditionalize the cleanup. */
- cleanup = build (COND_EXPR, void_type_node,
- (*lang_hooks.truthvalue_conversion) (cond),
- cleanup, integer_zero_node);
- cleanup = fold (cleanup);
-
- cleanups = &thisblock->data.block.cleanups;
- }
-
- cleanup = unsave_expr (cleanup);
-
- t = *cleanups = tree_cons (decl, cleanup, *cleanups);
-
- if (! cond_context)
- /* If this block has a cleanup, it belongs in stack_block_stack. */
- stack_block_stack = thisblock;
-
- if (cond_context)
- {
- start_sequence ();
- }
-
- if (! using_eh_for_cleanups_p)
- TREE_ADDRESSABLE (t) = 1;
- else
- expand_eh_region_start ();
-
- if (cond_context)
- {
- seq = get_insns ();
- end_sequence ();
- if (seq)
- thisblock->data.block.last_unconditional_cleanup
- = emit_insn_after (seq,
- thisblock->data.block.last_unconditional_cleanup);
- }
- else
- {
- thisblock->data.block.last_unconditional_cleanup
- = get_last_insn ();
- /* When we insert instructions after the last unconditional cleanup,
- we don't adjust last_insn. That means that a later add_insn will
- clobber the instructions we've just added. The easiest way to
- fix this is to just insert another instruction here, so that the
- instructions inserted after the last unconditional cleanup are
- never the last instruction. */
- emit_note (NOTE_INSN_DELETED);
- }
- }
- return 1;
-}
-
-/* Like expand_decl_cleanup, but maybe only run the cleanup if an exception
- is thrown. */
-
-int
-expand_decl_cleanup_eh (decl, cleanup, eh_only)
- tree decl, cleanup;
- int eh_only;
-{
- int ret = expand_decl_cleanup (decl, cleanup);
- if (cleanup && ret)
- {
- tree node = block_stack->data.block.cleanups;
- CLEANUP_EH_ONLY (node) = eh_only;
- }
- return ret;
-}
-\f
-/* DECL is an anonymous union. CLEANUP is a cleanup for DECL.
- DECL_ELTS is the list of elements that belong to DECL's type.
- In each, the TREE_VALUE is a VAR_DECL, and the TREE_PURPOSE a cleanup. */
-
-void
-expand_anon_union_decl (decl, cleanup, decl_elts)
- tree decl, cleanup, decl_elts;
-{
- struct nesting *thisblock = cfun == 0 ? 0 : block_stack;
- rtx x;
- tree t;
-
- /* If any of the elements are addressable, so is the entire union. */
- for (t = decl_elts; t; t = TREE_CHAIN (t))
- if (TREE_ADDRESSABLE (TREE_VALUE (t)))
- {
- TREE_ADDRESSABLE (decl) = 1;
- break;
- }
-
- expand_decl (decl);
- expand_decl_cleanup (decl, cleanup);
- x = DECL_RTL (decl);
-
- /* Go through the elements, assigning RTL to each. */
- for (t = decl_elts; t; t = TREE_CHAIN (t))
- {
- tree decl_elt = TREE_VALUE (t);
- tree cleanup_elt = TREE_PURPOSE (t);
- enum machine_mode mode = TYPE_MODE (TREE_TYPE (decl_elt));
-
- /* If any of the elements are addressable, so is the entire
- union. */
- if (TREE_USED (decl_elt))
- TREE_USED (decl) = 1;
-
- /* Propagate the union's alignment to the elements. */
- DECL_ALIGN (decl_elt) = DECL_ALIGN (decl);
- DECL_USER_ALIGN (decl_elt) = DECL_USER_ALIGN (decl);
-
- /* If the element has BLKmode and the union doesn't, the union is
- aligned such that the element doesn't need to have BLKmode, so
- change the element's mode to the appropriate one for its size. */
- if (mode == BLKmode && DECL_MODE (decl) != BLKmode)
- DECL_MODE (decl_elt) = mode
- = mode_for_size_tree (DECL_SIZE (decl_elt), MODE_INT, 1);
-
- /* (SUBREG (MEM ...)) at RTL generation time is invalid, so we
- instead create a new MEM rtx with the proper mode. */
- if (GET_CODE (x) == MEM)
- {
- if (mode == GET_MODE (x))
- SET_DECL_RTL (decl_elt, x);
- else
- SET_DECL_RTL (decl_elt, adjust_address_nv (x, mode, 0));
- }
- else if (GET_CODE (x) == REG)
- {
- if (mode == GET_MODE (x))
- SET_DECL_RTL (decl_elt, x);
- else
- SET_DECL_RTL (decl_elt, gen_lowpart_SUBREG (mode, x));
- }
- else
- abort ();
-
- /* Record the cleanup if there is one. */
-
- if (cleanup != 0)
- thisblock->data.block.cleanups
- = tree_cons (decl_elt, cleanup_elt,
- thisblock->data.block.cleanups);
- }
-}
-\f
-/* Expand a list of cleanups LIST.
- Elements may be expressions or may be nested lists.
-
- If IN_FIXUP is nonzero, we are generating this cleanup for a fixup
- goto and handle protection regions specially in that case.
-
- If REACHABLE, we emit code, otherwise just inform the exception handling
- code about this finalization. */
-
-static void
-expand_cleanups (list, in_fixup, reachable)
- tree list;
- int in_fixup;
- int reachable;
-{
- tree tail;
- for (tail = list; tail; tail = TREE_CHAIN (tail))
- if (TREE_CODE (TREE_VALUE (tail)) == TREE_LIST)
- expand_cleanups (TREE_VALUE (tail), in_fixup, reachable);
- else
- {
- if (! in_fixup && using_eh_for_cleanups_p)
- expand_eh_region_end_cleanup (TREE_VALUE (tail));
-
- if (reachable && !CLEANUP_EH_ONLY (tail))
- {
- /* Cleanups may be run multiple times. For example,
- when exiting a binding contour, we expand the
- cleanups associated with that contour. When a goto
- within that binding contour has a target outside that
- contour, it will expand all cleanups from its scope to
- the target. Though the cleanups are expanded multiple
- times, the control paths are non-overlapping so the
- cleanups will not be executed twice. */
-
- /* We may need to protect from outer cleanups. */
- if (in_fixup && using_eh_for_cleanups_p)
- {
- expand_eh_region_start ();
-
- expand_expr (TREE_VALUE (tail), const0_rtx, VOIDmode, 0);
-
- expand_eh_region_end_fixup (TREE_VALUE (tail));
- }
- else
- expand_expr (TREE_VALUE (tail), const0_rtx, VOIDmode, 0);
-
- free_temp_slots ();
- }
- }
-}
-
-/* Mark when the context we are emitting RTL for as a conditional
- context, so that any cleanup actions we register with
- expand_decl_init will be properly conditionalized when those
- cleanup actions are later performed. Must be called before any
- expression (tree) is expanded that is within a conditional context. */
-
-void
-start_cleanup_deferral ()
-{
- /* block_stack can be NULL if we are inside the parameter list. It is
- OK to do nothing, because cleanups aren't possible here. */
- if (block_stack)
- ++block_stack->data.block.conditional_code;
-}
-
-/* Mark the end of a conditional region of code. Because cleanup
- deferrals may be nested, we may still be in a conditional region
- after we end the currently deferred cleanups, only after we end all
- deferred cleanups, are we back in unconditional code. */
-
-void
-end_cleanup_deferral ()
-{
- /* block_stack can be NULL if we are inside the parameter list. It is
- OK to do nothing, because cleanups aren't possible here. */
- if (block_stack)
- --block_stack->data.block.conditional_code;
-}
-
-tree
-last_cleanup_this_contour ()
-{
- if (block_stack == 0)
- return 0;
-
- return block_stack->data.block.cleanups;
-}
-
-/* Return 1 if there are any pending cleanups at this point.
- Check the current contour as well as contours that enclose
- the current contour. */
-
-int
-any_pending_cleanups ()
-{
- struct nesting *block;
-
- if (cfun == NULL || cfun->stmt == NULL || block_stack == 0)
- return 0;
-
- if (block_stack->data.block.cleanups != NULL)
- return 1;
-
- if (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;
-}
-\f
-/* Enter a case (Pascal) or switch (C) statement.
- Push a block onto case_stack and nesting_stack
- to accumulate the case-labels that are seen
- and to record the labels generated for the statement.
-
- EXIT_FLAG is nonzero if `exit_something' should exit this case stmt.
- Otherwise, this construct is transparent for `exit_something'.
-
- EXPR is the index-expression to be dispatched on.
- TYPE is its nominal type. We could simply convert EXPR to this type,
- but instead we take short cuts. */
-
-void
-expand_start_case (exit_flag, expr, type, printname)
- int exit_flag;
- tree expr;
- tree type;
- const char *printname;
-{
- struct nesting *thiscase = ALLOC_NESTING ();
-
- /* Make an entry on case_stack for the case we are entering. */
-
- thiscase->desc = CASE_NESTING;
- thiscase->next = case_stack;
- thiscase->all = nesting_stack;
- thiscase->depth = ++nesting_depth;
- thiscase->exit_label = exit_flag ? gen_label_rtx () : 0;
- thiscase->data.case_stmt.case_list = 0;
- thiscase->data.case_stmt.index_expr = expr;
- thiscase->data.case_stmt.nominal_type = type;
- thiscase->data.case_stmt.default_label = 0;
- thiscase->data.case_stmt.printname = printname;
- thiscase->data.case_stmt.line_number_status = force_line_numbers ();
- case_stack = thiscase;
- nesting_stack = thiscase;
-
- do_pending_stack_adjust ();
- emit_queue ();
-
- /* Make sure case_stmt.start points to something that won't
- need any transformation before expand_end_case. */
- if (GET_CODE (get_last_insn ()) != NOTE)
- emit_note (NOTE_INSN_DELETED);
-
- thiscase->data.case_stmt.start = get_last_insn ();
-
- start_cleanup_deferral ();
-}
-
-/* Start a "dummy case statement" within which case labels are invalid
- and are not connected to any larger real case statement.
- This can be used if you don't want to let a case statement jump
- into the middle of certain kinds of constructs. */
-
-void
-expand_start_case_dummy ()
-{
- struct nesting *thiscase = ALLOC_NESTING ();
-
- /* Make an entry on case_stack for the dummy. */
-
- thiscase->desc = CASE_NESTING;
- thiscase->next = case_stack;
- thiscase->all = nesting_stack;
- thiscase->depth = ++nesting_depth;
- thiscase->exit_label = 0;
- thiscase->data.case_stmt.case_list = 0;
- thiscase->data.case_stmt.start = 0;
- thiscase->data.case_stmt.nominal_type = 0;
- thiscase->data.case_stmt.default_label = 0;
- case_stack = thiscase;
- nesting_stack = thiscase;
- start_cleanup_deferral ();
-}
-\f
-static void
-check_seenlabel ()
-{
- /* If this is the first label, warn if any insns have been emitted. */
- 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 is zero, then there must have been a syntax error. */
- if (insn)
- {
- location_t locus;
- locus.file = NOTE_SOURCE_FILE (insn);
- locus.line = NOTE_LINE_NUMBER (insn);
- warning ("%Hunreachable code at beginning of %s", &locus,
- case_stack->data.case_stmt.printname);
- }
- break;
- }
- }
- }
-}
-
-/* Accumulate one case or default label inside a case or switch statement.
- VALUE is the value of the case (a null pointer, for a default label).
- The function CONVERTER, when applied to arguments T and V,
- converts the value V to the type T.
-
- If not currently inside a case or switch statement, return 1 and do
- nothing. The caller will print a language-specific error message.
- If VALUE is a duplicate or overlaps, return 2 and do nothing
- except store the (first) duplicate node in *DUPLICATE.
- If VALUE is out of range, return 3 and do nothing.
- If we are jumping into the scope of a cleanup or var-sized array, return 5.
- Return 0 on success.
-
- Extended to handle range statements. */
-
-int
-pushcase (value, converter, label, duplicate)
- tree value;
- tree (*converter) PARAMS ((tree, tree));
- tree label;
- tree *duplicate;
-{
- tree index_type;
- tree nominal_type;
-
- /* Fail if not inside a real case statement. */
- 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 the index is erroneous, avoid more problems: pretend to succeed. */
- if (index_type == error_mark_node)
- return 0;
-
- /* Convert VALUE to the type in which the comparisons are nominally done. */
- if (value != 0)
- value = (*converter) (nominal_type, value);
-
- check_seenlabel ();
-
- /* Fail if this value is out of range for the actual type of the index
- (which may be narrower than NOMINAL_TYPE). */
- if (value != 0
- && (TREE_CONSTANT_OVERFLOW (value)
- || ! int_fits_type_p (value, index_type)))
- return 3;
-
- return add_case_node (value, value, label, duplicate);
-}
-
-/* Like pushcase but this case applies to all values between VALUE1 and
- VALUE2 (inclusive). If VALUE1 is NULL, the range starts at the lowest
- value of the index type and ends at VALUE2. If VALUE2 is NULL, the range
- starts at VALUE1 and ends at the highest value of the index type.
- If both are NULL, this case applies to all values.
-
- The return value is the same as that of pushcase but there is one
- additional error code: 4 means the specified range was empty. */
-
-int
-pushcase_range (value1, value2, converter, label, duplicate)
- tree value1, value2;
- tree (*converter) PARAMS ((tree, tree));
- tree label;
- tree *duplicate;
-{
- tree index_type;
- tree nominal_type;
-
- /* Fail if not inside a real case statement. */
- 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 the index is erroneous, avoid more problems: pretend to succeed. */
- if (index_type == error_mark_node)
- return 0;
-
- check_seenlabel ();
-
- /* Convert VALUEs to type in which the comparisons are nominally done
- and replace any unspecified value with the corresponding bound. */
- if (value1 == 0)
- value1 = TYPE_MIN_VALUE (index_type);
- if (value2 == 0)
- value2 = TYPE_MAX_VALUE (index_type);
-
- /* Fail if the range is empty. Do this before any conversion since
- we want to allow out-of-range empty ranges. */
- if (value2 != 0 && tree_int_cst_lt (value2, value1))
- return 4;
-
- /* If the max was unbounded, use the max of the nominal_type we are
- converting to. Do this after the < check above to suppress false
- positives. */
- if (value2 == 0)
- value2 = TYPE_MAX_VALUE (nominal_type);
-
- value1 = (*converter) (nominal_type, value1);
- value2 = (*converter) (nominal_type, value2);
-
- /* Fail if these values are out of range. */
- 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);
-}
-
-/* Do the actual insertion of a case label for pushcase and pushcase_range
- into case_stack->data.case_stmt.case_list. Use an AVL tree to avoid
- slowdown for large switch statements. */
-
-int
-add_case_node (low, high, label, duplicate)
- tree low, high;
- tree label;
- tree *duplicate;
-{
- struct case_node *p, **q, *r;
-
- /* If there's no HIGH value, then this is not a case range; it's
- just a simple case label. But that's just a degenerate case
- range. */
- if (!high)
- high = low;
-
- /* Handle default labels specially. */
- if (!high && !low)
- {
- if (case_stack->data.case_stmt.default_label != 0)
- {
- *duplicate = case_stack->data.case_stmt.default_label;
- return 2;
- }
- case_stack->data.case_stmt.default_label = label;
- expand_label (label);
- return 0;
- }
-
- q = &case_stack->data.case_stmt.case_list;
- p = *q;
-
- while ((r = *q))
- {
- p = r;
-
- /* Keep going past elements distinctly greater than HIGH. */
- if (tree_int_cst_lt (high, p->low))
- q = &p->left;
-
- /* or distinctly less than LOW. */
- else if (tree_int_cst_lt (p->high, low))
- q = &p->right;
-
- else
- {
- /* We have an overlap; this is an error. */
- *duplicate = p->code_label;
- return 2;
- }
- }
-
- /* Add this label to the chain, and succeed. */
-
- r = (struct case_node *) ggc_alloc (sizeof (struct case_node));
- r->low = low;
-
- /* If the bounds are equal, turn this into the one-value case. */
- if (tree_int_cst_equal (low, high))
- r->high = r->low;
- else
- r->high = high;
-
- r->code_label = label;
- expand_label (label);
-
- *q = r;
- r->parent = p;
- r->left = 0;
- r->right = 0;
- r->balance = 0;
-
- while (p)
- {
- struct case_node *s;
-
- if (r == p->left)
- {
- int b;
-
- if (! (b = p->balance))
- /* Growth propagation from left side. */
- p->balance = -1;
- else if (b < 0)
- {
- if (r->balance < 0)
- {
- /* R-Rotation */
- 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
- /* r->balance == +1 */
- {
- /* LR-Rotation */
-
- 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 == +1; growth of left side balances the node. */
- p->balance = 0;
- break;
- }
- }
- else
- /* r == p->right */
- {
- int b;
-
- if (! (b = p->balance))
- /* Growth propagation from right side. */
- p->balance++;
- else if (b > 0)
- {
- if (r->balance > 0)
- {
- /* L-Rotation */
-
- 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
- /* r->balance == -1 */
- {
- /* RL-Rotation */
- 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 == -1; growth of right side balances the node. */
- p->balance = 0;
- break;
- }
- }
-
- r = p;
- p = p->parent;