- 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 allocate T_SIZE bytes of dynamic stack space for ALLOC. */
-void
-expand_stack_alloc (tree alloc, tree t_size)
-{
- rtx address, dest, size;
- tree var, type;
-
- if (TREE_CODE (alloc) != ADDR_EXPR)
- abort ();
- var = TREE_OPERAND (alloc, 0);
- if (TREE_CODE (var) != VAR_DECL)
- abort ();
-
- type = TREE_TYPE (var);
-
- /* Compute the variable's size, in bytes. */
- size = expand_expr (t_size, NULL_RTX, VOIDmode, 0);
- free_temp_slots ();
-
- /* Allocate space on the stack for the variable. */
- address = XEXP (DECL_RTL (var), 0);
- dest = allocate_dynamic_stack_space (size, address, TYPE_ALIGN (type));
- if (dest != address)
- emit_move_insn (address, dest);
-
- /* Indicate the alignment we actually gave this variable. */
-#ifdef STACK_BOUNDARY
- DECL_ALIGN (var) = STACK_BOUNDARY;
-#else
- DECL_ALIGN (var) = BIGGEST_ALIGNMENT;
-#endif
- DECL_USER_ALIGN (var) = 0;
-}
-
-/* Emit code to save the current value of stack. */
-rtx
-expand_stack_save (void)
-{
- rtx ret = NULL_RTX;
-
- do_pending_stack_adjust ();
- emit_stack_save (SAVE_BLOCK, &ret, NULL_RTX);
- return ret;
-}
-
-/* Emit code to restore the current value of stack. */
-void
-expand_stack_restore (tree var)
-{
- rtx sa = DECL_RTL (var);
-
- emit_stack_restore (SAVE_BLOCK, sa, NULL_RTX);
-}
-\f
-/* Emit code to perform the initialization of a declaration DECL. */
-
-void
-expand_decl_init (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);
- emit_queue ();
- }
- else if (DECL_INITIAL (decl) && TREE_CODE (DECL_INITIAL (decl)) != TREE_LIST)
- {
- emit_line_note (DECL_SOURCE_LOCATION (decl));
- expand_assignment (decl, DECL_INITIAL (decl), 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 (tree decl, tree 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 (tree decl, tree 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 (tree decl, tree cleanup, tree 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 (REG_P (x))
- {
- 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 (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 (void)
-{
- /* 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 (void)
-{
- /* 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 (void)
-{
- if (block_stack == 0)
- return 0;
-
- return block_stack->data.block.cleanups;
-}
-
-
-/* Return nonzero if any containing block has a stack level or
- cleanups. */
-
-int
-containing_blocks_have_cleanups_or_stack_level (void)
-{
- struct nesting *block;
-
- for (block = block_stack; block; block = block->next)
- if (block->data.block.stack_level != 0
- || block->data.block.cleanups != 0)
- return 1;
-
- return 0;
-}
-
-/* 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 (void)
-{
- 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 (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 ();