diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 7fe45994235..b77d6688ce3 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,14 @@ +Sat Jan 17 23:41:36 1998 David S. Miller + + * explow.c (optimize_save_area_alloca): New function for targets + where SETJMP_VIA_SAVE_AREA is true. + (allocate_dynamic_stack_space): On SETJMP_VIA_SAVE_AREA targets, + compute the amount of stack space needed should we find later that + setjmp is never called by this function, stuff rtl for this inside + a REG_NOTE of the final SET of stack_pointer_rtx. + * toplev.c (rest_of_compilation): If SETJMP_VIA_SAVE_AREA and + current_function_calls_alloca, call optimize_save_area_alloca. + Sat Jan 17 23:22:59 1998 John Wehle (john@feith.com) * i386.md: Remove redundant integer push patterns. diff --git a/gcc/explow.c b/gcc/explow.c index e38c1e43f8b..0fcc547ff56 100644 --- a/gcc/explow.c +++ b/gcc/explow.c @@ -1004,6 +1004,86 @@ emit_stack_restore (save_level, sa, after) emit_insn (fcn (stack_pointer_rtx, sa)); } +#ifdef SETJMP_VIA_SAVE_AREA +/* Optimize RTL generated by allocate_dynamic_stack_space for targets + where SETJMP_VIA_SAVE_AREA is true. The problem is that on these + platforms, the dynamic stack space used can corrupt the original + frame, thus causing a crash if a longjmp unwinds to it. */ + +void +optimize_save_area_alloca (insns) + rtx insns; +{ + rtx insn; + + for (insn = insns; insn; insn = NEXT_INSN(insn)) + { + rtx note; + + if (GET_CODE (insn) != INSN) + continue; + + for (note = REG_NOTES (insn); note; note = XEXP (note, 1)) + { + if (REG_NOTE_KIND (note) != REG_SAVE_AREA) + continue; + + if (!current_function_calls_setjmp) + { + rtx pat = PATTERN (insn); + + /* If we do not see the note in a pattern matching + these precise characteristics, we did something + entirely wrong in allocate_dynamic_stack_space. + + Note, one way this could happen if if SETJMP_VIA_SAVE_AREA + was defined on a machine where stacks grow towards higher + addresses. + + Right now only supported port with stack that grow upward + is the HPPA and it does not define SETJMP_VIA_SAVE_AREA. */ + if (GET_CODE (pat) != SET + || SET_DEST (pat) != stack_pointer_rtx + || GET_CODE (SET_SRC (pat)) != MINUS + || XEXP (SET_SRC (pat), 0) != stack_pointer_rtx) + abort (); + + /* This will now be transformed into a (set REG REG) + so we can just blow away all the other notes. */ + XEXP (SET_SRC (pat), 1) = XEXP (note, 0); + REG_NOTES (insn) = NULL_RTX; + } + else + { + /* setjmp was called, we must remove the REG_SAVE_AREA + note so that later passes do not get confused by its + presence. */ + if (note == REG_NOTES (insn)) + { + REG_NOTES (insn) = XEXP (note, 1); + } + else + { + rtx srch; + + for (srch = REG_NOTES (insn); srch; srch = XEXP (srch, 1)) + if (XEXP (srch, 1) == note) + break; + + if (srch == NULL_RTX) + abort(); + + XEXP (srch, 1) = XEXP (note, 1); + } + } + /* Once we've seen the note of interest, we need not look at + the rest of them. */ + break; + } + } +} +#endif /* SETJMP_VIA_SAVE_AREA */ + /* Return an rtx representing the address of an area of memory dynamically pushed on the stack. This region of memory is always aligned to a multiple of BIGGEST_ALIGNMENT. @@ -1021,6 +1101,10 @@ allocate_dynamic_stack_space (size, target, known_align) rtx target; int known_align; { +#ifdef SETJMP_VIA_SAVE_AREA + rtx setjmpless_size = NULL_RTX; +#endif + /* If we're asking for zero bytes, it doesn't matter what we point to since we can't dereference it. But return a reasonable address anyway. */ @@ -1074,6 +1158,45 @@ allocate_dynamic_stack_space (size, target, known_align) rtx dynamic_offset = expand_binop (Pmode, sub_optab, virtual_stack_dynamic_rtx, stack_pointer_rtx, NULL_RTX, 1, OPTAB_LIB_WIDEN); + + if (!current_function_calls_setjmp) + { + int align = STACK_BOUNDARY / BITS_PER_UNIT; + + /* See optimize_save_area_alloca to understand what is being + set up here. */ + +#if !defined(STACK_BOUNDARY) || !defined(MUST_ALIGN) || (STACK_BOUNDARY != BIGGEST_ALIGNMENT) + /* If anyone creates a target with these characteristics, let them + know that our optimization cannot work correctly in such a case. */ + abort(); +#endif + + if (GET_CODE (size) == CONST_INT) + { + int new = INTVAL (size) / align * align; + + if (INTVAL (size) != new) + setjmpless_size = GEN_INT (new); + else + setjmpless_size = size; + } + else + { + /* Since we know overflow is not possible, we avoid using + CEIL_DIV_EXPR and use TRUNC_DIV_EXPR instead. */ + setjmpless_size = expand_divmod (0, TRUNC_DIV_EXPR, Pmode, size, + GEN_INT (align), NULL_RTX, 1); + setjmpless_size = expand_mult (Pmode, setjmpless_size, + GEN_INT (align), NULL_RTX, 1); + } + /* Our optimization works based upon being able to perform a simple + transformation of this RTL into a (set REG REG) so make sure things + did in fact end up in a REG. */ + if (!arith_operand (setjmpless_size, Pmode)) + setjmpless_size = force_reg (Pmode, setjmpless_size); + } + size = expand_binop (Pmode, add_optab, size, dynamic_offset, NULL_RTX, 1, OPTAB_LIB_WIDEN); } @@ -1145,6 +1268,16 @@ allocate_dynamic_stack_space (size, target, known_align) #endif size = convert_modes (Pmode, ptr_mode, size, 1); anti_adjust_stack (size); +#ifdef SETJMP_VIA_SAVE_AREA + if (setjmpless_size != NULL_RTX) + { + rtx note_target = get_last_insn (); + + REG_NOTES (note_target) = gen_rtx (EXPR_LIST, REG_SAVE_AREA, + setjmpless_size, + REG_NOTES (note_target)); + } +#endif /* SETJMP_VIA_SAVE_AREA */ #ifdef STACK_GROWS_DOWNWARD emit_move_insn (target, virtual_stack_dynamic_rtx); #endif diff --git a/gcc/toplev.c b/gcc/toplev.c index b0ffcd713e8..86a6c63f7fe 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -3242,6 +3242,12 @@ rest_of_compilation (decl) unshare_all_rtl (insns); +#ifdef SETJMP_VIA_SAVE_AREA + /* This must be performed before virutal register instantiation. */ + if (current_function_calls_alloca) + optimize_save_area_alloca (insns); +#endif + /* Instantiate all virtual registers. */ instantiate_virtual_regs (current_function_decl, get_insns ());