diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 90 | ||||
-rw-r--r-- | gcc/config/sparc/sparc-protos.h | 4 | ||||
-rw-r--r-- | gcc/config/sparc/sparc.c | 845 | ||||
-rw-r--r-- | gcc/config/sparc/sparc.h | 76 | ||||
-rw-r--r-- | gcc/config/sparc/sparc.md | 104 | ||||
-rw-r--r-- | gcc/config/sparc/sparc.opt | 4 | ||||
-rw-r--r-- | gcc/config/sparc/t-elf | 4 | ||||
-rw-r--r-- | gcc/config/sparc/t-leon | 4 | ||||
-rw-r--r-- | gcc/doc/invoke.texi | 15 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 6 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/20020503-1.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/gcc.target/sparc/sparc-ret.c | 5 |
12 files changed, 893 insertions, 266 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 43ec6bf..8fb1ad9 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,93 @@ +2011-06-10 Eric Botcazou <ebotcazou@adacore.com> + Laurent Rougé <laurent.rouge@menta.fr> + + * doc/invoke.texi (SPARC options): Add -mflat. + * config/sparc/sparc.opt: Likewise. + * config/sparc/sparc-protos.h (sparc_expand_epilogue): Add parameter. + (sparc_flat_expand_prologue): Declare. + (sparc_flat_expand_epilogue): Likewise. + * config/sparc/sparc.h (CPP_CPU_SPEC): Do not handle -msoft-float. + (CPP_ENDIAN_SPEC): Replace with... + (CPP_OTHER_SPEC): ...this. Also handle -mflat and -msoft-float. + (CPP_SPEC): Adjust to above change. + (EXTRA_SPECS): Likewise. + (SPARC_INCOMING_INT_ARG_FIRST): Add TARGET_FLAT handling. + (INCOMING_REGNO): Likewise. + (OUTGOING_REGNO): Likewise. + (LOCAL_REGNO): Likewise. + (SETUP_FRAME_ADDRESSES): Likewise. + (FIXED_REGISTERS): Set 0 for %fp. + (CALL_USED_REGISTERS): Likewise. + (INITIAL_ELIMINATION_OFFSET): Pass current_function_is_leaf. + (EXIT_IGNORE_STACK): Define to 1 unconditionally. + (RETURN_ADDR_REGNUM): Define. + (RETURN_ADDR_RTX): Use it. + (INCOMING_RETURN_ADDR_REGNUM): Define. + (INCOMING_RETURN_ADDR_RTX): Use it. + (DWARF_FRAME_RETURN_COLUMN): Likewise. + (EH_RETURN_REGNUM): Define. + (EH_RETURN_STACKADJ_RTX): Use it. + (EH_RETURN_HANDLER_RTX): Delete. + (EPILOGUE_USES): Use them and add TARGET_FLAT handling. + * config/sparc/sparc.c (apparent_fsize, actual_fsize, num_gfregs): + Delete. + (struct machine_function): Add frame_size, apparent_frame_size, + frame_base_reg, frame_base_offset, n_global_fp_regs and + save_local_in_regs_p fields. + (sparc_frame_size, sparc_apparent_frame_size, sparc_frame_base_reg, + sparc_frame_base_offset, sparc_n_global_fp_regs, + sparc_save_local_in_regs_p): New macros. + (sparc_option_override): Error out if -fcall-saved-REG is specified + for Out registers. + (eligible_for_restore_insn): Fix formatting. + (eligible_for_return_delay): Likewise. Add TARGET_FLAT handling. + (eligible_for_sibcall_delay): Likewise. + (RTX_OK_FOR_OFFSET_P, RTX_OK_FOR_OLO10_P): Add MODE parameter. + (sparc_legitimate_address_p): Adjust to above change. + (save_global_or_fp_reg_p): New predicate. + (return_addr_reg_needed_p): Likewise. + (save_local_or_in_reg_p): Likewise. + (sparc_compute_frame_size): Use them. Add TARGET_FLAT handling. + (SORR_SAVE, SORR_RESTORE): Delete. + (sorr_pred_t): New typedef. + (sorr_act_t): New enum. + (save_or_restore_regs): Rename to... + (emit_save_or_restore_regs): ...this. Change type of LOW and HIGH + parameters, remove ACTION parameter, add LEAF_FUNCTION_P, SAVE_P, + ACTION_TRUE and ACTION_FALSE parameters. Implement more general + mechanism. Add CFI information for double-word saves in 32-bit mode. + (emit_adjust_base_to_offset): New function extracted from... + (emit_save_or_restore_regs): ...this. Rename the rest to... + (emit_save_or_restore_regs_global_fp_regs): ...this. + (emit_save_or_restore_regs_local_in_regs): New function. + (gen_create_flat_frame_[123]): New functions. + (sparc_expand_prologue): Use SIZE local variable. Adjust. + (sparc_flat_expand_prologue): New function. + (sparc_asm_function_prologue): Add TARGET_FLAT handling. + (sparc_expand_epilogue): Use SIZE local variable. Adjust. + (sparc_flat_expand_epilogue): New function. + (sparc_can_use_return_insn_p): Add TARGET_FLAT handling. + (output_return): Likewise. + (output_sibcall): Likewise. + (sparc_output_mi_thunk): Likewise. + (sparc_frame_pointer_required): Likewise. + (sparc_conditional_register_usage): If TARGET_FLAT, disable the leaf + function optimization. + * config/sparc/sparc.md (flat): New attribute. + (prologue): Add TARGET_FLAT handling. + (save_register_window): Disable if TARGET_FLAT. + (create_flat_frame_[123]): New patterns. + (epilogue): Add TARGET_FLAT handling. + (sibcall_epilogue): Likewise. + (eh_return): New expander. + (eh_return_internal): New insn and splitter. + (return_internal): Add TARGET_FLAT handling. + (untyped_return): Remove bogus test and use RETURN_ADDR_REGNUM. + (save_stack_nonlocal): Use RETURN_ADDR_REGNUM. + (nonlocal_goto): Add TARGET_FLAT handling. + * config/sparc/t-elf: Add -mflat multilib. + * config/sparc/t-leon: Likewise. + 2011-06-10 Jan Hubicka <jh@suse.cz> * ipa-utils.c (searchc): Use cgraph_function_or_thunk_node. diff --git a/gcc/config/sparc/sparc-protos.h b/gcc/config/sparc/sparc-protos.h index ccf1657..a5e2587 100644 --- a/gcc/config/sparc/sparc-protos.h +++ b/gcc/config/sparc/sparc-protos.h @@ -38,7 +38,9 @@ extern enum direction function_arg_padding (enum machine_mode, const_tree); extern void order_regs_for_local_alloc (void); extern HOST_WIDE_INT sparc_compute_frame_size (HOST_WIDE_INT, int); extern void sparc_expand_prologue (void); -extern void sparc_expand_epilogue (void); +extern void sparc_flat_expand_prologue (void); +extern void sparc_expand_epilogue (bool); +extern void sparc_flat_expand_epilogue (bool); extern bool sparc_can_use_return_insn_p (void); extern int check_pic (int); extern int short_branch (int, int); diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c index ae35cf8..0de98c6 100644 --- a/gcc/config/sparc/sparc.c +++ b/gcc/config/sparc/sparc.c @@ -287,21 +287,6 @@ const struct processor_costs *sparc_costs = &cypress_costs; ((TARGET_ARCH64 && !TARGET_CM_MEDLOW) || flag_pic) #endif -/* Global variables for machine-dependent things. */ - -/* Size of frame. Need to know this to emit return insns from leaf procedures. - ACTUAL_FSIZE is set by sparc_compute_frame_size() which is called during the - reload pass. This is important as the value is later used for scheduling - (to see what can go in a delay slot). - APPARENT_FSIZE is the size of the stack less the register save area and less - the outgoing argument area. It is used when saving call preserved regs. */ -static HOST_WIDE_INT apparent_fsize; -static HOST_WIDE_INT actual_fsize; - -/* Number of live general or floating point registers needed to be - saved (as 4-byte quantities). */ -static int num_gfregs; - /* Vector to say how input registers are mapped to output registers. HARD_FRAME_POINTER_REGNUM cannot be remapped by this function to eliminate it. You must use -fomit-frame-pointer to get that. */ @@ -341,28 +326,46 @@ char sparc_leaf_regs[] = struct GTY(()) machine_function { + /* Size of the frame of the function. */ + HOST_WIDE_INT frame_size; + + /* Size of the frame of the function minus the register window save area + and the outgoing argument area. */ + HOST_WIDE_INT apparent_frame_size; + + /* Register we pretend the frame pointer is allocated to. Normally, this + is %fp, but if we are in a leaf procedure, this is (%sp + offset). We + record "offset" separately as it may be too big for (reg + disp). */ + rtx frame_base_reg; + HOST_WIDE_INT frame_base_offset; + /* Some local-dynamic TLS symbol name. */ const char *some_ld_name; + /* Number of global or FP registers to be saved (as 4-byte quantities). */ + int n_global_fp_regs; + /* True if the current function is leaf and uses only leaf regs, so that the SPARC leaf function optimization can be applied. Private version of current_function_uses_only_leaf_regs, see sparc_expand_prologue for the rationale. */ int leaf_function_p; + /* True if the prologue saves local or in registers. */ + bool save_local_in_regs_p; + /* True if the data calculated by sparc_expand_prologue are valid. */ bool prologue_data_valid_p; }; -#define sparc_leaf_function_p cfun->machine->leaf_function_p -#define sparc_prologue_data_valid_p cfun->machine->prologue_data_valid_p - -/* Register we pretend to think the frame pointer is allocated to. - Normally, this is %fp, but if we are in a leaf procedure, this - is %sp+"something". We record "something" separately as it may - be too big for reg+constant addressing. */ -static rtx frame_base_reg; -static HOST_WIDE_INT frame_base_offset; +#define sparc_frame_size cfun->machine->frame_size +#define sparc_apparent_frame_size cfun->machine->apparent_frame_size +#define sparc_frame_base_reg cfun->machine->frame_base_reg +#define sparc_frame_base_offset cfun->machine->frame_base_offset +#define sparc_n_global_fp_regs cfun->machine->n_global_fp_regs +#define sparc_leaf_function_p cfun->machine->leaf_function_p +#define sparc_save_local_in_regs_p cfun->machine->save_local_in_regs_p +#define sparc_prologue_data_valid_p cfun->machine->prologue_data_valid_p /* 1 if the next opcode is to be specially indented. */ int sparc_indent_opcode = 0; @@ -387,8 +390,6 @@ static rtx sparc_builtin_saveregs (void); static int epilogue_renumber (rtx *, int); static bool sparc_assemble_integer (rtx, unsigned int, int); static int set_extends (rtx); -static int save_or_restore_regs (int, int, rtx, int, int); -static void emit_save_or_restore_regs (int); static void sparc_asm_function_prologue (FILE *, HOST_WIDE_INT); static void sparc_asm_function_epilogue (FILE *, HOST_WIDE_INT); #ifdef TARGET_SOLARIS @@ -764,6 +765,7 @@ sparc_option_override (void) { MASK_ISA, MASK_V9}, }; const struct cpu_table *cpu; + unsigned int i; int fpu; #ifdef SUBTARGET_OVERRIDE_OPTIONS @@ -808,6 +810,14 @@ sparc_option_override (void) error ("-mcmodel= is not supported on 32 bit systems"); } + /* Check that -fcall-saved-REG wasn't specified for out registers. */ + for (i = 8; i < 16; i++) + if (!call_used_regs [i]) + { + error ("-fcall-saved-REG is not supported for out registers"); + call_used_regs [i] = 1; + } + fpu = target_flags & MASK_FPU; /* save current -mfpu status */ /* Set the default CPU. */ @@ -2769,9 +2779,11 @@ eligible_for_restore_insn (rtx trial, bool return_p) /* If we have the 'return' instruction, anything that does not use local or output registers and can go into a delay slot wins. */ - else if (return_p && TARGET_V9 && ! epilogue_renumber (&pat, 1) - && (get_attr_in_uncond_branch_delay (trial) - == IN_UNCOND_BRANCH_DELAY_TRUE)) + else if (return_p + && TARGET_V9 + && !epilogue_renumber (&pat, 1) + && get_attr_in_uncond_branch_delay (trial) + == IN_UNCOND_BRANCH_DELAY_TRUE) return 1; /* The 'restore src1,src2,dest' pattern for SImode. */ @@ -2806,8 +2818,7 @@ eligible_for_restore_insn (rtx trial, bool return_p) return 0; } -/* Return nonzero if TRIAL can go into the function return's - delay slot. */ +/* Return nonzero if TRIAL can go into the function return's delay slot. */ int eligible_for_return_delay (rtx trial) @@ -2825,10 +2836,10 @@ eligible_for_return_delay (rtx trial) if (crtl->calls_eh_return) return 0; - /* In the case of a true leaf function, anything can go into the slot. */ - if (sparc_leaf_function_p) - return get_attr_in_uncond_branch_delay (trial) - == IN_UNCOND_BRANCH_DELAY_TRUE; + /* In the case of a leaf or flat function, anything can go into the slot. */ + if (sparc_leaf_function_p || TARGET_FLAT) + return + get_attr_in_uncond_branch_delay (trial) == IN_UNCOND_BRANCH_DELAY_TRUE; pat = PATTERN (trial); @@ -2843,15 +2854,14 @@ eligible_for_return_delay (rtx trial) with FP_REGS. */ if (REGNO (SET_DEST (pat)) >= 32) return (TARGET_V9 - && ! epilogue_renumber (&pat, 1) - && (get_attr_in_uncond_branch_delay (trial) - == IN_UNCOND_BRANCH_DELAY_TRUE)); + && !epilogue_renumber (&pat, 1) + && get_attr_in_uncond_branch_delay (trial) + == IN_UNCOND_BRANCH_DELAY_TRUE); return eligible_for_restore_insn (trial, true); } -/* Return nonzero if TRIAL can go into the sibling call's - delay slot. */ +/* Return nonzero if TRIAL can go into the sibling call's delay slot. */ int eligible_for_sibcall_delay (rtx trial) @@ -2866,7 +2876,7 @@ eligible_for_sibcall_delay (rtx trial) pat = PATTERN (trial); - if (sparc_leaf_function_p) + if (sparc_leaf_function_p || TARGET_FLAT) { /* If the tail call is done using the call instruction, we have to restore %o7 in the delay slot. */ @@ -3117,11 +3127,15 @@ legitimate_pic_operand_p (rtx x) return true; } -#define RTX_OK_FOR_OFFSET_P(X) \ - (CONST_INT_P (X) && INTVAL (X) >= -0x1000 && INTVAL (X) < 0x1000 - 8) +#define RTX_OK_FOR_OFFSET_P(X, MODE) \ + (CONST_INT_P (X) \ + && INTVAL (X) >= -0x1000 \ + && INTVAL (X) < (0x1000 - GET_MODE_SIZE (MODE))) -#define RTX_OK_FOR_OLO10_P(X) \ - (CONST_INT_P (X) && INTVAL (X) >= -0x1000 && INTVAL (X) < 0xc00 - 8) +#define RTX_OK_FOR_OLO10_P(X, MODE) \ + (CONST_INT_P (X) \ + && INTVAL (X) >= -0x1000 \ + && INTVAL (X) < (0xc00 - GET_MODE_SIZE (MODE))) /* Handle the TARGET_LEGITIMATE_ADDRESS_P target hook. @@ -3163,7 +3177,7 @@ sparc_legitimate_address_p (enum machine_mode mode, rtx addr, bool strict) && (GET_CODE (rs2) != CONST_INT || SMALL_INT (rs2))) || ((REG_P (rs1) || GET_CODE (rs1) == SUBREG) - && RTX_OK_FOR_OFFSET_P (rs2))) + && RTX_OK_FOR_OFFSET_P (rs2, mode))) { imm1 = rs2; rs2 = NULL; @@ -3193,7 +3207,7 @@ sparc_legitimate_address_p (enum machine_mode mode, rtx addr, bool strict) && GET_CODE (rs1) == LO_SUM && TARGET_ARCH64 && ! TARGET_CM_MEDMID - && RTX_OK_FOR_OLO10_P (rs2)) + && RTX_OK_FOR_OLO10_P (rs2, mode)) { rs2 = NULL; imm1 = XEXP (rs1, 1); @@ -4104,59 +4118,138 @@ sparc_init_modes (void) } } +/* Return whether REGNO, a global or FP register, must be saved/restored. */ + +static inline bool +save_global_or_fp_reg_p (unsigned int regno, + int leaf_function ATTRIBUTE_UNUSED) +{ + return !call_used_regs[regno] && df_regs_ever_live_p (regno); +} + +/* Return whether the return address register (%i7) is needed. */ + +static inline bool +return_addr_reg_needed_p (int leaf_function) +{ + /* If it is live, for example because of __builtin_return_address (0). */ + if (df_regs_ever_live_p (RETURN_ADDR_REGNUM)) + return true; + + /* Otherwise, it is needed as save register if %o7 is clobbered. */ + if (!leaf_function + /* Loading the GOT register clobbers %o7. */ + || crtl->uses_pic_offset_table + || df_regs_ever_live_p (INCOMING_RETURN_ADDR_REGNUM)) + return true; + + return false; +} + +/* Return whether REGNO, a local or in register, must be saved/restored. */ + +static bool +save_local_or_in_reg_p (unsigned int regno, int leaf_function) +{ + /* General case: call-saved registers live at some point. */ + if (!call_used_regs[regno] && df_regs_ever_live_p (regno)) + return true; + + /* Frame pointer register (%fp) if needed. */ + if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed) + return true; + + /* Return address register (%i7) if needed. */ + if (regno == RETURN_ADDR_REGNUM && return_addr_reg_needed_p (leaf_function)) + return true; + + /* PIC register (%l7) if needed. */ + if (regno == PIC_OFFSET_TABLE_REGNUM && crtl->uses_pic_offset_table) + return true; + + /* If the function accesses prior frames, the frame pointer and the return + address of the previous frame must be saved on the stack. */ + if (crtl->accesses_prior_frames + && (regno == HARD_FRAME_POINTER_REGNUM || regno == RETURN_ADDR_REGNUM)) + return true; + + return false; +} + /* Compute the frame size required by the function. This function is called during the reload pass and also by sparc_expand_prologue. */ HOST_WIDE_INT -sparc_compute_frame_size (HOST_WIDE_INT size, int leaf_function_p) +sparc_compute_frame_size (HOST_WIDE_INT size, int leaf_function) { - int outgoing_args_size = (crtl->outgoing_args_size - + REG_PARM_STACK_SPACE (current_function_decl)); - int n_regs = 0; /* N_REGS is the number of 4-byte regs saved thus far. */ - int i; + HOST_WIDE_INT frame_size, apparent_frame_size; + int args_size, n_global_fp_regs = 0; + bool save_local_in_regs_p = false; + unsigned int i; - if (TARGET_ARCH64) - { - for (i = 0; i < 8; i++) - if (df_regs_ever_live_p (i) && ! call_used_regs[i]) - n_regs += 2; - } + /* If the function allocates dynamic stack space, the dynamic offset is + computed early and contains REG_PARM_STACK_SPACE, so we need to cope. */ + if (leaf_function && !cfun->calls_alloca) + args_size = 0; else - { - for (i = 0; i < 8; i += 2) - if ((df_regs_ever_live_p (i) && ! call_used_regs[i]) - || (df_regs_ever_live_p (i+1) && ! call_used_regs[i+1])) - n_regs += 2; - } + args_size = crtl->outgoing_args_size + REG_PARM_STACK_SPACE (cfun->decl); - for (i = 32; i < (TARGET_V9 ? 96 : 64); i += 2) - if ((df_regs_ever_live_p (i) && ! call_used_regs[i]) - || (df_regs_ever_live_p (i+1) && ! call_used_regs[i+1])) - n_regs += 2; + /* Calculate space needed for global registers. */ + if (TARGET_ARCH64) + for (i = 0; i < 8; i++) + if (save_global_or_fp_reg_p (i, 0)) + n_global_fp_regs += 2; + else + for (i = 0; i < 8; i += 2) + if (save_global_or_fp_reg_p (i, 0) || save_global_or_fp_reg_p (i + 1, 0)) + n_global_fp_regs += 2; - /* Set up values for use in prologue and epilogue. */ - num_gfregs = n_regs; + /* In the flat window model, find out which local and in registers need to + be saved. We don't reserve space in the current frame for them as they + will be spilled into the register window save area of the caller's frame. + However, as soon as we use this register window save area, we must create + that of the current frame to make it the live one. */ + if (TARGET_FLAT) + for (i = 16; i < 32; i++) + if (save_local_or_in_reg_p (i, leaf_function)) + { + save_local_in_regs_p = true; + break; + } - if (leaf_function_p - && n_regs == 0 - && size == 0 - && crtl->outgoing_args_size == 0) - actual_fsize = apparent_fsize = 0; + /* Calculate space needed for FP registers. */ + for (i = 32; i < (TARGET_V9 ? 96 : 64); i += 2) + if (save_global_or_fp_reg_p (i, 0) || save_global_or_fp_reg_p (i + 1, 0)) + n_global_fp_regs += 2; + + if (size == 0 + && n_global_fp_regs == 0 + && args_size == 0 + && !save_local_in_regs_p) + frame_size = apparent_frame_size = 0; else { /* We subtract STARTING_FRAME_OFFSET, remember it's negative. */ - apparent_fsize = (size - STARTING_FRAME_OFFSET + 7) & -8; - apparent_fsize += n_regs * 4; - actual_fsize = apparent_fsize + ((outgoing_args_size + 7) & -8); + apparent_frame_size = (size - STARTING_FRAME_OFFSET + 7) & -8; + apparent_frame_size += n_global_fp_regs * 4; + + /* We need to add the size of the outgoing argument area. */ + frame_size = apparent_frame_size + ((args_size + 7) & -8); + + /* And that of the register window save area. */ + frame_size += FIRST_PARM_OFFSET (cfun->decl); + + /* Finally, bump to the appropriate alignment. */ + frame_size = SPARC_STACK_ALIGN (frame_size); } - /* Make sure nothing can clobber our register windows. - If a SAVE must be done, or there is a stack-local variable, - the register window area must be allocated. */ - if (! leaf_function_p || size > 0) - actual_fsize += FIRST_PARM_OFFSET (current_function_decl); + /* Set up values for use in prologue and epilogue. */ + sparc_frame_size = frame_size; + sparc_apparent_frame_size = apparent_frame_size; + sparc_n_global_fp_regs = n_global_fp_regs; + sparc_save_local_in_regs_p = save_local_in_regs_p; - return SPARC_STACK_ALIGN (actual_fsize); + return frame_size; } /* Output any necessary .register pseudo-ops. */ @@ -4342,43 +4435,66 @@ output_probe_stack_range (rtx reg1, rtx reg2) return ""; } -/* Save/restore call-saved registers from LOW to HIGH at BASE+OFFSET - as needed. LOW should be double-word aligned for 32-bit registers. - Return the new OFFSET. */ +/* Emit code to save/restore registers from LOW to HIGH at BASE+OFFSET as + needed. LOW is supposed to be double-word aligned for 32-bit registers. + SAVE_P decides whether a register must be saved/restored. ACTION_TRUE + is the action to be performed if SAVE_P returns true and ACTION_FALSE + the action to be performed if it returns false. Return the new offset. */ -#define SORR_SAVE 0 -#define SORR_RESTORE 1 +typedef bool (*sorr_pred_t) (unsigned int, int); +typedef enum { SORR_NONE, SORR_ADVANCE, SORR_SAVE, SORR_RESTORE } sorr_act_t; static int -save_or_restore_regs (int low, int high, rtx base, int offset, int action) +emit_save_or_restore_regs (unsigned int low, unsigned int high, rtx base, + int offset, int leaf_function, sorr_pred_t save_p, + sorr_act_t action_true, sorr_act_t action_false) { + unsigned int i; rtx mem, insn; - int i; if (TARGET_ARCH64 && high <= 32) { + int fp_offset = -1; + for (i = low; i < high; i++) { - if (df_regs_ever_live_p (i) && ! call_used_regs[i]) + if (save_p (i, leaf_function)) { mem = gen_frame_mem (DImode, plus_constant (base, offset)); - if (action == SORR_SAVE) + if (action_true == SORR_SAVE) { insn = emit_move_insn (mem, gen_rtx_REG (DImode, i)); RTX_FRAME_RELATED_P (insn) = 1; } - else /* action == SORR_RESTORE */ - emit_move_insn (gen_rtx_REG (DImode, i), mem); + else /* action_true == SORR_RESTORE */ + { + /* The frame pointer must be restored last since its old + value may be used as base address for the frame. This + is problematic in 64-bit mode only because of the lack + of double-word load instruction. */ + if (i == HARD_FRAME_POINTER_REGNUM) + fp_offset = offset; + else + emit_move_insn (gen_rtx_REG (DImode, i), mem); + } offset += 8; } + else if (action_false == SORR_ADVANCE) + offset += 8; + } + + if (fp_offset >= 0) + { + mem = gen_frame_mem (DImode, plus_constant (base, fp_offset)); + emit_move_insn (hard_frame_pointer_rtx, mem); } } else { for (i = low; i < high; i += 2) { - bool reg0 = df_regs_ever_live_p (i) && ! call_used_regs[i]; - bool reg1 = df_regs_ever_live_p (i+1) && ! call_used_regs[i+1]; + bool reg0 = save_p (i, leaf_function); + bool reg1 = save_p (i + 1, leaf_function); enum machine_mode mode; int regno; @@ -4399,15 +4515,35 @@ save_or_restore_regs (int low, int high, rtx base, int offset, int action) offset += 4; } else - continue; + { + if (action_false == SORR_ADVANCE) + offset += 8; + continue; + } mem = gen_frame_mem (mode, plus_constant (base, offset)); - if (action == SORR_SAVE) + if (action_true == SORR_SAVE) { insn = emit_move_insn (mem, gen_rtx_REG (mode, regno)); RTX_FRAME_RELATED_P (insn) = 1; + if (mode == DImode) + { + rtx set1, set2; + mem = gen_frame_mem (SImode, plus_constant (base, offset)); + set1 = gen_rtx_SET (VOIDmode, mem, + gen_rtx_REG (SImode, regno)); + RTX_FRAME_RELATED_P (set1) = 1; + mem + = gen_frame_mem (SImode, plus_constant (base, offset + 4)); + set2 = gen_rtx_SET (VOIDmode, mem, + gen_rtx_REG (SImode, regno + 1)); + RTX_FRAME_RELATED_P (set2) = 1; + add_reg_note (insn, REG_FRAME_RELATED_EXPR, + gen_rtx_PARALLEL (VOIDmode, + gen_rtvec (2, set1, set2))); + } } - else /* action == SORR_RESTORE */ + else /* action_true == SORR_RESTORE */ emit_move_insn (gen_rtx_REG (mode, regno), mem); /* Always preserve double-word alignment. */ @@ -4418,36 +4554,54 @@ save_or_restore_regs (int low, int high, rtx base, int offset, int action) return offset; } -/* Emit code to save call-saved registers. */ +/* Emit code to adjust BASE to OFFSET. Return the new base. */ + +static rtx +emit_adjust_base_to_offset (rtx base, int offset) +{ + /* ??? This might be optimized a little as %g1 might already have a + value close enough that a single add insn will do. */ + /* ??? Although, all of this is probably only a temporary fix because + if %g1 can hold a function result, then sparc_expand_epilogue will + lose (the result will be clobbered). */ + rtx new_base = gen_rtx_REG (Pmode, 1); + emit_move_insn (new_base, GEN_INT (offset)); + emit_insn (gen_rtx_SET (VOIDmode, + new_base, gen_rtx_PLUS (Pmode, base, new_base))); + return new_base; +} + +/* Emit code to save/restore call-saved global and FP registers. */ static void -emit_save_or_restore_regs (int action) +emit_save_or_restore_global_fp_regs (rtx base, int offset, sorr_act_t action) { - HOST_WIDE_INT offset; - rtx base; + if (offset < -4096 || offset + sparc_n_global_fp_regs * 4 > 4095) + { + base = emit_adjust_base_to_offset (base, offset); + offset = 0; + } - offset = frame_base_offset - apparent_fsize; + offset + = emit_save_or_restore_regs (0, 8, base, offset, 0, + save_global_or_fp_reg_p, action, SORR_NONE); + emit_save_or_restore_regs (32, TARGET_V9 ? 96 : 64, base, offset, 0, + save_global_or_fp_reg_p, action, SORR_NONE); +} - if (offset < -4096 || offset + num_gfregs * 4 > 4095) +/* Emit code to save/restore call-saved local and in registers. */ + +static void +emit_save_or_restore_local_in_regs (rtx base, int offset, sorr_act_t action) +{ + if (offset < -4096 || offset + 16 * UNITS_PER_WORD > 4095) { - /* ??? This might be optimized a little as %g1 might already have a - value close enough that a single add insn will do. */ - /* ??? Although, all of this is probably only a temporary fix - because if %g1 can hold a function result, then - sparc_expand_epilogue will lose (the result will be - clobbered). */ - base = gen_rtx_REG (Pmode, 1); - emit_move_insn (base, GEN_INT (offset)); - emit_insn (gen_rtx_SET (VOIDmode, - base, - gen_rtx_PLUS (Pmode, frame_base_reg, base))); + base = emit_adjust_base_to_offset (base, offset); offset = 0; } - else - base = frame_base_reg; - offset = save_or_restore_regs (0, 8, base, offset, action); - save_or_restore_regs (32, TARGET_V9 ? 96 : 64, base, offset, action); + emit_save_or_restore_regs (16, 32, base, offset, sparc_leaf_function_p, + save_local_or_in_reg_p, action, SORR_ADVANCE); } /* Generate a save_register_window insn. */ @@ -4461,6 +4615,39 @@ gen_save_register_window (rtx increment) return gen_save_register_windowsi (increment); } +/* Generate a create_flat_frame_1 insn. */ + +static rtx +gen_create_flat_frame_1 (rtx increment) +{ + if (TARGET_ARCH64) + return gen_create_flat_frame_1di (increment); + else + return gen_create_flat_frame_1si (increment); +} + +/* Generate a create_flat_frame_2 insn. */ + +static rtx +gen_create_flat_frame_2 (rtx increment) +{ + if (TARGET_ARCH64) + return gen_create_flat_frame_2di (increment); + else + return gen_create_flat_frame_2si (increment); +} + +/* Generate a create_flat_frame_3 insn. */ + +static rtx +gen_create_flat_frame_3 (rtx increment) +{ + if (TARGET_ARCH64) + return gen_create_flat_frame_3di (increment); + else + return gen_create_flat_frame_3si (increment); +} + /* Generate an increment for the stack pointer. */ static rtx @@ -4492,6 +4679,7 @@ gen_stack_pointer_dec (rtx decrement) void sparc_expand_prologue (void) { + HOST_WIDE_INT size; rtx insn; int i; @@ -4520,70 +4708,52 @@ sparc_expand_prologue (void) sparc_leaf_function_p = optimize > 0 && current_function_is_leaf && only_leaf_regs_used (); - /* Need to use actual_fsize, since we are also allocating - space for our callee (and our own register save area). */ - actual_fsize - = sparc_compute_frame_size (get_frame_size(), sparc_leaf_function_p); - - /* Advertise that the data calculated just above are now valid. */ - sparc_prologue_data_valid_p = true; + size = sparc_compute_frame_size (get_frame_size(), sparc_leaf_function_p); if (flag_stack_usage_info) - current_function_static_stack_size = actual_fsize; + current_function_static_stack_size = size; - if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK && actual_fsize) - sparc_emit_probe_stack_range (STACK_CHECK_PROTECT, actual_fsize); + if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK && size) + sparc_emit_probe_stack_range (STACK_CHECK_PROTECT, size); - if (sparc_leaf_function_p) - { - frame_base_reg = stack_pointer_rtx; - frame_base_offset = actual_fsize + SPARC_STACK_BIAS; - } - else - { - frame_base_reg = hard_frame_pointer_rtx; - frame_base_offset = SPARC_STACK_BIAS; - } - - if (actual_fsize == 0) - /* do nothing. */ ; + if (size == 0) + ; /* do nothing. */ else if (sparc_leaf_function_p) { - if (actual_fsize <= 4096) - insn = emit_insn (gen_stack_pointer_inc (GEN_INT (-actual_fsize))); - else if (actual_fsize <= 8192) + if (size <= 4096) + insn = emit_insn (gen_stack_pointer_inc (GEN_INT (-size))); + else if (size <= 8192) { insn = emit_insn (gen_stack_pointer_inc (GEN_INT (-4096))); /* %sp is still the CFA register. */ RTX_FRAME_RELATED_P (insn) = 1; - insn - = emit_insn (gen_stack_pointer_inc (GEN_INT (4096-actual_fsize))); + insn = emit_insn (gen_stack_pointer_inc (GEN_INT (4096 - size))); } else { rtx reg = gen_rtx_REG (Pmode, 1); - emit_move_insn (reg, GEN_INT (-actual_fsize)); + emit_move_insn (reg, GEN_INT (-size)); insn = emit_insn (gen_stack_pointer_inc (reg)); add_reg_note (insn, REG_FRAME_RELATED_EXPR, - gen_stack_pointer_inc (GEN_INT (-actual_fsize))); + gen_stack_pointer_inc (GEN_INT (-size))); } RTX_FRAME_RELATED_P (insn) = 1; } else { - if (actual_fsize <= 4096) - insn = emit_insn (gen_save_register_window (GEN_INT (-actual_fsize))); - else if (actual_fsize <= 8192) + if (size <= 4096) + insn = emit_insn (gen_save_register_window (GEN_INT (-size))); + else if (size <= 8192) { insn = emit_insn (gen_save_register_window (GEN_INT (-4096))); /* %sp is not the CFA register anymore. */ - emit_insn (gen_stack_pointer_inc (GEN_INT (4096-actual_fsize))); + emit_insn (gen_stack_pointer_inc (GEN_INT (4096 - size))); } else { rtx reg = gen_rtx_REG (Pmode, 1); - emit_move_insn (reg, GEN_INT (-actual_fsize)); + emit_move_insn (reg, GEN_INT (-size)); insn = emit_insn (gen_save_register_window (reg)); } @@ -4592,12 +4762,179 @@ sparc_expand_prologue (void) RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, i)) = 1; } - if (num_gfregs) - emit_save_or_restore_regs (SORR_SAVE); + if (sparc_leaf_function_p) + { + sparc_frame_base_reg = stack_pointer_rtx; + sparc_frame_base_offset = size + SPARC_STACK_BIAS; + } + else + { + sparc_frame_base_reg = hard_frame_pointer_rtx; + sparc_frame_base_offset = SPARC_STACK_BIAS; + } + + if (sparc_n_global_fp_regs > 0) + emit_save_or_restore_global_fp_regs (sparc_frame_base_reg, + sparc_frame_base_offset + - sparc_apparent_frame_size, + SORR_SAVE); /* Load the GOT register if needed. */ if (crtl->uses_pic_offset_table) load_got_register (); + + /* Advertise that the data calculated just above are now valid. */ + sparc_prologue_data_valid_p = true; +} + +/* Expand the function prologue. The prologue is responsible for reserving + storage for the frame, saving the call-saved registers and loading the + GOT register if needed. */ + +void +sparc_flat_expand_prologue (void) +{ + HOST_WIDE_INT size; + rtx insn; + int i; + + sparc_leaf_function_p = optimize > 0 && current_function_is_leaf; + + size = sparc_compute_frame_size (get_frame_size(), sparc_leaf_function_p); + + if (flag_stack_usage_info) + current_function_static_stack_size = size; + + if (flag_stack_check == STATIC_BUILTIN_STACK_CHECK && size) + sparc_emit_probe_stack_range (STACK_CHECK_PROTECT, size); + + if (sparc_save_local_in_regs_p) + emit_save_or_restore_local_in_regs (stack_pointer_rtx, SPARC_STACK_BIAS, + SORR_SAVE); + + if (size == 0) + ; /* do nothing. */ + else if (frame_pointer_needed) + { + if (size <= 4096) + { + if (return_addr_reg_needed_p (sparc_leaf_function_p)) + insn = emit_insn (gen_create_flat_frame_1 (GEN_INT (-size))); + else + insn = emit_insn (gen_create_flat_frame_2 (GEN_INT (-size))); + RTX_FRAME_RELATED_P (insn) = 1; + for (i=0; i < XVECLEN (PATTERN (insn), 0); i++) + RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, i)) = 1; + } + else + { + rtx reg = gen_rtx_REG (Pmode, 1), note; + emit_move_insn (reg, GEN_INT (-size)); + if (return_addr_reg_needed_p (sparc_leaf_function_p)) + { + insn = emit_insn (gen_create_flat_frame_1 (reg)); + note + = gen_rtx_PARALLEL (VOIDmode, + gen_rtvec + (3, copy_rtx + (XVECEXP (PATTERN (insn), 0, 0)), + gen_stack_pointer_inc + (GEN_INT (-size)), + copy_rtx + (XVECEXP (PATTERN (insn), 0, 2)))); + } + else + { + insn = emit_insn (gen_create_flat_frame_2 (reg)); + note + = gen_rtx_PARALLEL (VOIDmode, + gen_rtvec + (2, copy_rtx + (XVECEXP (PATTERN (insn), 0, 0)), + gen_stack_pointer_inc + (GEN_INT (-size)))); + } + + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_FRAME_RELATED_EXPR, note); + for (i=0; i < XVECLEN (note, 0); i++) + RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1; + } + } + else if (return_addr_reg_needed_p (sparc_leaf_function_p)) + { + if (size <= 4096) + { + insn = emit_insn (gen_create_flat_frame_3 (GEN_INT (-size))); + RTX_FRAME_RELATED_P (insn) = 1; + for (i=0; i < XVECLEN (PATTERN (insn), 0); i++) + RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, i)) = 1; + } + else + { + rtx reg = gen_rtx_REG (Pmode, 1), note; + emit_move_insn (reg, GEN_INT (-size)); + insn = emit_insn (gen_create_flat_frame_3 (reg)); + note + = gen_rtx_PARALLEL (VOIDmode, + gen_rtvec + (2, gen_stack_pointer_inc (GEN_INT (-size)), + copy_rtx + (XVECEXP (PATTERN (insn), 0, 1)))); + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_FRAME_RELATED_EXPR, note); + for (i=0; i < XVECLEN (note, 0); i++) + RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1; + } + } + else + { + if (size <= 4096) + insn = emit_insn (gen_stack_pointer_inc (GEN_INT (-size))); + else if (size <= 8192) + { + insn = emit_insn (gen_stack_pointer_inc (GEN_INT (-4096))); + RTX_FRAME_RELATED_P (insn) = 1; + insn = emit_insn (gen_stack_pointer_inc (GEN_INT (4096 - size))); + } + else + { + rtx reg = gen_rtx_REG (Pmode, 1); + emit_move_insn (reg, GEN_INT (-size)); + insn = emit_insn (gen_stack_pointer_inc (reg)); + add_reg_note (insn, REG_FRAME_RELATED_EXPR, + gen_stack_pointer_inc (GEN_INT (-size))); + } + + RTX_FRAME_RELATED_P (insn) = 1; + } + + /* Make sure nothing is scheduled until after the frame is established. */ + emit_insn (gen_blockage ()); + + if (frame_pointer_needed) + { + sparc_frame_base_reg = hard_frame_pointer_rtx; + sparc_frame_base_offset = SPARC_STACK_BIAS; + } + else + { + sparc_frame_base_reg = stack_pointer_rtx; + sparc_frame_base_offset = size + SPARC_STACK_BIAS; + } + + if (sparc_n_global_fp_regs > 0) + emit_save_or_restore_global_fp_regs (sparc_frame_base_reg, + sparc_frame_base_offset + - sparc_apparent_frame_size, + SORR_SAVE); + + /* Load the GOT register if needed. */ + if (crtl->uses_pic_offset_table) + load_got_register (); + + /* Advertise that the data calculated just above are now valid. */ + sparc_prologue_data_valid_p = true; } /* This function generates the assembly code for function entry, which boils @@ -4607,7 +4944,8 @@ static void sparc_asm_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) { /* Check that the assumption we made in sparc_expand_prologue is valid. */ - gcc_assert (sparc_leaf_function_p == current_function_uses_only_leaf_regs); + if (!TARGET_FLAT) + gcc_assert (sparc_leaf_function_p == current_function_uses_only_leaf_regs); sparc_output_scratch_registers (file); } @@ -4616,26 +4954,91 @@ sparc_asm_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) We emit all the instructions except the return or the call. */ void -sparc_expand_epilogue (void) +sparc_expand_epilogue (bool for_eh) { - if (num_gfregs) - emit_save_or_restore_regs (SORR_RESTORE); + HOST_WIDE_INT size = sparc_frame_size; + + if (sparc_n_global_fp_regs > 0) + emit_save_or_restore_global_fp_regs (sparc_frame_base_reg, + sparc_frame_base_offset + - sparc_apparent_frame_size, + SORR_RESTORE); - if (actual_fsize == 0) - /* do nothing. */ ; + if (size == 0 || for_eh) + ; /* do nothing. */ else if (sparc_leaf_function_p) { - if (actual_fsize <= 4096) - emit_insn (gen_stack_pointer_dec (GEN_INT (- actual_fsize))); - else if (actual_fsize <= 8192) + if (size <= 4096) + emit_insn (gen_stack_pointer_dec (GEN_INT (-size))); + else if (size <= 8192) { emit_insn (gen_stack_pointer_dec (GEN_INT (-4096))); - emit_insn (gen_stack_pointer_dec (GEN_INT (4096 - actual_fsize))); + emit_insn (gen_stack_pointer_dec (GEN_INT (4096 - size))); } else { rtx reg = gen_rtx_REG (Pmode, 1); - emit_move_insn (reg, GEN_INT (-actual_fsize)); + emit_move_insn (reg, GEN_INT (-size)); + emit_insn (gen_stack_pointer_dec (reg)); + } + } +} + +/* Expand the function epilogue, either normal or part of a sibcall. + We emit all the instructions except the return or the call. */ + +void +sparc_flat_expand_epilogue (bool for_eh) +{ + HOST_WIDE_INT size = sparc_frame_size; + + if (sparc_n_global_fp_regs > 0) + emit_save_or_restore_global_fp_regs (sparc_frame_base_reg, + sparc_frame_base_offset + - sparc_apparent_frame_size, + SORR_RESTORE); + + /* If we have a frame pointer, we'll need both to restore it before the + frame is destroyed and use its current value in destroying the frame. + Since we don't have an atomic way to do that in the flat window model, + we save the current value into a temporary register (%g1). */ + if (frame_pointer_needed && !for_eh) + emit_move_insn (gen_rtx_REG (Pmode, 1), hard_frame_pointer_rtx); + + if (return_addr_reg_needed_p (sparc_leaf_function_p)) + emit_move_insn (gen_rtx_REG (Pmode, INCOMING_RETURN_ADDR_REGNUM), + gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM)); + + if (sparc_save_local_in_regs_p) + emit_save_or_restore_local_in_regs (sparc_frame_base_reg, + sparc_frame_base_offset, + SORR_RESTORE); + + if (size == 0 || for_eh) + ; /* do nothing. */ + else if (frame_pointer_needed) + { + /* Make sure the frame is destroyed after everything else is done. */ + emit_insn (gen_blockage ()); + + emit_move_insn (stack_pointer_rtx, gen_rtx_REG (Pmode, 1)); + } + else + { + /* Likewise. */ + emit_insn (gen_blockage ()); + + if (size <= 4096) + emit_insn (gen_stack_pointer_dec (GEN_INT (-size))); + else if (size <= 8192) + { + emit_insn (gen_stack_pointer_dec (GEN_INT (-4096))); + emit_insn (gen_stack_pointer_dec (GEN_INT (4096 - size))); + } + else + { + rtx reg = gen_rtx_REG (Pmode, 1); + emit_move_insn (reg, GEN_INT (-size)); emit_insn (gen_stack_pointer_dec (reg)); } } @@ -4648,8 +5051,10 @@ bool sparc_can_use_return_insn_p (void) { return sparc_prologue_data_valid_p - && num_gfregs == 0 - && (actual_fsize == 0 || !sparc_leaf_function_p); + && sparc_n_global_fp_regs == 0 + && TARGET_FLAT + ? (sparc_frame_size == 0 && !sparc_save_local_in_regs_p) + : (sparc_frame_size == 0 || !sparc_leaf_function_p); } /* This function generates the assembly code for function exit. */ @@ -4728,15 +5133,42 @@ output_restore (rtx pat) const char * output_return (rtx insn) { - if (sparc_leaf_function_p) + if (crtl->calls_eh_return) + { + /* If the function uses __builtin_eh_return, the eh_return + machinery occupies the delay slot. */ + gcc_assert (!final_sequence); + + if (flag_delayed_branch) + { + if (!TARGET_FLAT && TARGET_V9) + fputs ("\treturn\t%i7+8\n", asm_out_file); + else + { + if (!TARGET_FLAT) + fputs ("\trestore\n", asm_out_file); + + fputs ("\tjmp\t%o7+8\n", asm_out_file); + } + + fputs ("\t add\t%sp, %g1, %sp\n", asm_out_file); + } + else + { + if (!TARGET_FLAT) + fputs ("\trestore\n", asm_out_file); + + fputs ("\tadd\t%sp, %g1, %sp\n", asm_out_file); + fputs ("\tjmp\t%o7+8\n\t nop\n", asm_out_file); + } + } + else if (sparc_leaf_function_p || TARGET_FLAT) { - /* This is a leaf function so we don't have to bother restoring the - register window, which frees us from dealing with the convoluted + /* This is a leaf or flat function so we don't have to bother restoring + the register window, which frees us from dealing with the convoluted semantics of restore/return. We simply output the jump to the return address and the insn in the delay slot (if any). */ - gcc_assert (! crtl->calls_eh_return); - return "jmp\t%%o7+%)%#"; } else @@ -4746,28 +5178,7 @@ output_return (rtx insn) combined with the 'restore' instruction or put in the delay slot of the 'return' instruction. */ - if (crtl->calls_eh_return) - { - /* If the function uses __builtin_eh_return, the eh_return - machinery occupies the delay slot. */ - gcc_assert (! final_sequence); - - if (flag_delayed_branch) - { - if (TARGET_V9) - fputs ("\treturn\t%i7+8\n", asm_out_file); - else - fputs ("\trestore\n\tjmp\t%o7+8\n", asm_out_file); - - fputs ("\t add\t%sp, %g1, %sp\n", asm_out_file); - } - else - { - fputs ("\trestore\n\tadd\t%sp, %g1, %sp\n", asm_out_file); - fputs ("\tjmp\t%o7+8\n\t nop\n", asm_out_file); - } - } - else if (final_sequence) + if (final_sequence) { rtx delay, pat; @@ -4815,10 +5226,10 @@ output_sibcall (rtx insn, rtx call_operand) operands[0] = call_operand; - if (sparc_leaf_function_p) + if (sparc_leaf_function_p || TARGET_FLAT) { - /* This is a leaf function so we don't have to bother restoring the - register window. We simply output the jump to the function and + /* This is a leaf or flat function so we don't have to bother restoring + the register window. We simply output the jump to the function and the insn in the delay slot (if any). */ gcc_assert (!(LEAF_SIBCALL_SLOT_RESERVED_P && final_sequence)); @@ -9352,7 +9763,13 @@ sparc_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, emit_note (NOTE_INSN_PROLOGUE_END); - if (flag_delayed_branch) + if (TARGET_FLAT) + { + sparc_leaf_function_p = 1; + + int_arg_first = SPARC_OUTGOING_INT_ARG_FIRST; + } + else if (flag_delayed_branch) { /* We will emit a regular sibcall below, so we need to instruct output_sibcall that we are in a leaf function. */ @@ -9473,8 +9890,6 @@ sparc_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, { spill_reg = gen_rtx_REG (word_mode, 15); /* %o7 */ start_sequence (); - /* Delay emitting the GOT helper function because it needs to - change the section and we are emitting assembly code. */ load_got_register (); /* clobbers %o7 */ scratch = sparc_legitimize_pic_address (funexp, scratch); seq = get_insns (); @@ -9593,8 +10008,10 @@ get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED) } /* Handle the TARGET_DWARF_HANDLE_FRAME_UNSPEC hook. + This is called from dwarf2out.c to emit call frame instructions - for frame-related insns containing UNSPECs and UNSPEC_VOLATILEs. */ + for frame-related insns containing UNSPECs and UNSPEC_VOLATILEs. */ + static void sparc_dwarf_handle_frame_unspec (const char *label, rtx pattern ATTRIBUTE_UNUSED, @@ -9810,6 +10227,16 @@ sparc_expand_compare_and_swap_12 (rtx result, rtx mem, rtx oldval, rtx newval) static bool sparc_frame_pointer_required (void) { + /* If the stack pointer is dynamically modified in the function, it cannot + serve as the frame pointer. */ + if (cfun->calls_alloca) + return true; + + /* In flat mode, that's it. */ + if (TARGET_FLAT) + return false; + + /* Otherwise, the frame pointer is required if the function isn't leaf. */ return !(current_function_is_leaf && only_leaf_regs_used ()); } @@ -9880,6 +10307,14 @@ sparc_conditional_register_usage (void) fixed_regs[4] = 1; else if (fixed_regs[4] == 2) fixed_regs[4] = 0; + if (TARGET_FLAT) + { + int regno; + /* Disable leaf functions. */ + memset (sparc_leaf_regs, 0, FIRST_PSEUDO_REGISTER); + for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + leaf_reg_remap [regno] = regno; + } } /* Implement TARGET_PREFERRED_RELOAD_CLASS diff --git a/gcc/config/sparc/sparc.h b/gcc/config/sparc/sparc.h index 4f9ef3a..8f3ace3 100644 --- a/gcc/config/sparc/sparc.h +++ b/gcc/config/sparc/sparc.h @@ -360,7 +360,6 @@ extern enum cmodel sparc_cmodel; /* Common CPP definitions used by CPP_SPEC amongst the various targets for handling -mcpu=xxx switches. */ #define CPP_CPU_SPEC "\ -%{msoft-float:-D_SOFT_FLOAT} \ %{mcpu=sparclet:-D__sparclet__} %{mcpu=tsc701:-D__sparclet__} \ %{mcpu=sparclite:-D__sparclite__} \ %{mcpu=f930:-D__sparclite__} %{mcpu=f934:-D__sparclite__} \ @@ -388,14 +387,18 @@ extern enum cmodel sparc_cmodel; %{!m32:%{!m64:%(cpp_arch_default)}} \ " -/* Macro to distinguish endianness. */ -#define CPP_ENDIAN_SPEC "\ -%{mlittle-endian:-D__LITTLE_ENDIAN__}" +/* Macros to distinguish the endianness, window model and FP support. */ +#define CPP_OTHER_SPEC "\ +%{mlittle-endian:-D__LITTLE_ENDIAN__} \ +%{mflat:-D_FLAT} \ +%{msoft-float:-D_SOFT_FLOAT} \ +" /* Macros to distinguish the particular subtarget. */ #define CPP_SUBTARGET_SPEC "" -#define CPP_SPEC "%(cpp_cpu) %(cpp_arch) %(cpp_endian) %(cpp_subtarget)" +#define CPP_SPEC \ + "%(cpp_cpu) %(cpp_arch) %(cpp_endian) %(cpp_other) %(cpp_subtarget)" /* This used to translate -dalign to -malign, but that is no good because it can't turn off the usual meaning of making debugging dumps. */ @@ -464,7 +467,7 @@ extern enum cmodel sparc_cmodel; { "cpp_arch64", CPP_ARCH64_SPEC }, \ { "cpp_arch_default", CPP_ARCH_DEFAULT_SPEC },\ { "cpp_arch", CPP_ARCH_SPEC }, \ - { "cpp_endian", CPP_ENDIAN_SPEC }, \ + { "cpp_other", CPP_OTHER_SPEC }, \ { "cpp_subtarget", CPP_SUBTARGET_SPEC }, \ { "asm_cpu", ASM_CPU_SPEC }, \ { "asm_cpu_default", ASM_CPU_DEFAULT_SPEC }, \ @@ -687,7 +690,7 @@ extern enum cmodel sparc_cmodel; /* Argument passing regs. */ #define SPARC_OUTGOING_INT_ARG_FIRST 8 -#define SPARC_INCOMING_INT_ARG_FIRST 24 +#define SPARC_INCOMING_INT_ARG_FIRST (TARGET_FLAT ? 8 : 24) #define SPARC_FP_ARG_FIRST 32 /* 1 for registers that have pervasive standard uses @@ -721,7 +724,7 @@ extern enum cmodel sparc_cmodel; {1, 0, 2, 2, 2, 2, 1, 1, \ 0, 0, 0, 0, 0, 0, 1, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, \ - 0, 0, 0, 0, 0, 0, 1, 1, \ + 0, 0, 0, 0, 0, 0, 0, 1, \ \ 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, \ @@ -746,7 +749,7 @@ extern enum cmodel sparc_cmodel; {1, 1, 1, 1, 1, 1, 1, 1, \ 1, 1, 1, 1, 1, 1, 1, 1, \ 0, 0, 0, 0, 0, 0, 0, 0, \ - 0, 0, 0, 0, 0, 0, 1, 1, \ + 0, 0, 0, 0, 0, 0, 0, 1, \ \ 1, 1, 1, 1, 1, 1, 1, 1, \ 1, 1, 1, 1, 1, 1, 1, 1, \ @@ -1223,13 +1226,11 @@ extern char leaf_reg_remap[]; {{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ { FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM} } -/* We always pretend that this is a leaf function because if it's not, - there's no point in trying to eliminate the frame pointer. If it - is a leaf function, we guessed right! */ #define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ do { \ if ((TO) == STACK_POINTER_REGNUM) \ - (OFFSET) = sparc_compute_frame_size (get_frame_size (), 1); \ + (OFFSET) = sparc_compute_frame_size (get_frame_size (), \ + current_function_is_leaf); \ else \ (OFFSET) = 0; \ (OFFSET) += SPARC_STACK_BIAS; \ @@ -1247,7 +1248,7 @@ extern char leaf_reg_remap[]; Return OUT if register number OUT is not an outbound register. */ #define INCOMING_REGNO(OUT) \ - (((OUT) < 8 || (OUT) > 15) ? (OUT) : (OUT) + 16) + ((TARGET_FLAT || (OUT) < 8 || (OUT) > 15) ? (OUT) : (OUT) + 16) /* Define this macro if the target machine has "register windows". This C expression returns the register number as seen by the calling function @@ -1255,14 +1256,14 @@ extern char leaf_reg_remap[]; Return IN if register number IN is not an inbound register. */ #define OUTGOING_REGNO(IN) \ - (((IN) < 24 || (IN) > 31) ? (IN) : (IN) - 16) + ((TARGET_FLAT || (IN) < 24 || (IN) > 31) ? (IN) : (IN) - 16) /* Define this macro if the target machine has register windows. This C expression returns true if the register is call-saved but is in the register window. */ #define LOCAL_REGNO(REGNO) \ - ((REGNO) >= 16 && (REGNO) <= 31) + (!TARGET_FLAT && (REGNO) >= 16 && (REGNO) <= 31) /* Define the size of space to allocate for the return value of an untyped_call. */ @@ -1373,35 +1374,27 @@ do { \ /* EXIT_IGNORE_STACK should be nonzero if, when returning from a function, the stack pointer does not matter. The value is tested only in - functions that have frame pointers. - No definition is equivalent to always zero. */ + functions that have frame pointers. */ +#define EXIT_IGNORE_STACK 1 -#define EXIT_IGNORE_STACK \ - (get_frame_size () != 0 \ - || cfun->calls_alloca || crtl->outgoing_args_size) - -/* Define registers used by the epilogue and return instruction. */ -#define EPILOGUE_USES(REGNO) ((REGNO) == 31 \ - || (crtl->calls_eh_return && (REGNO) == 1)) - /* We need 2 words, so we can save the stack pointer and the return register of the function containing a non-local goto target. */ - #define STACK_SAVEAREA_MODE(LEVEL) \ ((LEVEL) == SAVE_NONLOCAL ? (TARGET_ARCH64 ? TImode : DImode) : Pmode) /* Length in units of the trampoline for entering a nested function. */ - #define TRAMPOLINE_SIZE (TARGET_ARCH64 ? 32 : 16) /* Alignment required for trampolines, in bits. */ - #define TRAMPOLINE_ALIGNMENT 128 /* Generate RTL to flush the register windows so as to make arbitrary frames available. */ -#define SETUP_FRAME_ADDRESSES() \ - emit_insn (gen_flush_register_windows ()) +#define SETUP_FRAME_ADDRESSES() \ + do { \ + if (!TARGET_FLAT) \ + emit_insn (gen_flush_register_windows ());\ + } while (0) /* Given an rtx for the address of a frame, return an rtx for the address of the word in the frame @@ -1428,9 +1421,10 @@ do { \ farther back is in the register window save area at [%fp+60]. */ /* ??? This ignores the fact that the actual return address is +8 for normal returns, and +12 for structure returns. */ +#define RETURN_ADDR_REGNUM 31 #define RETURN_ADDR_RTX(count, frame) \ ((count == -1) \ - ? gen_rtx_REG (Pmode, 31) \ + ? gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM) \ : gen_rtx_MEM (Pmode, \ memory_address (Pmode, plus_constant (frame, \ 15 * UNITS_PER_WORD \ @@ -1440,9 +1434,11 @@ do { \ +12, but always using +8 is close enough for frame unwind purposes. Actually, just using %o7 is close enough for unwinding, but %o7+8 is something you can return to. */ +#define INCOMING_RETURN_ADDR_REGNUM 15 #define INCOMING_RETURN_ADDR_RTX \ - plus_constant (gen_rtx_REG (word_mode, 15), 8) -#define DWARF_FRAME_RETURN_COLUMN DWARF_FRAME_REGNUM (15) + plus_constant (gen_rtx_REG (word_mode, INCOMING_RETURN_ADDR_REGNUM), 8) +#define DWARF_FRAME_RETURN_COLUMN \ + DWARF_FRAME_REGNUM (INCOMING_RETURN_ADDR_REGNUM) /* The offset from the incoming value of %sp to the top of the stack frame for the current function. On sparc64, we have to account for the stack @@ -1450,9 +1446,17 @@ do { \ #define INCOMING_FRAME_SP_OFFSET SPARC_STACK_BIAS /* Describe how we implement __builtin_eh_return. */ +#define EH_RETURN_REGNUM 1 #define EH_RETURN_DATA_REGNO(N) ((N) < 4 ? (N) + 24 : INVALID_REGNUM) -#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, 1) /* %g1 */ -#define EH_RETURN_HANDLER_RTX gen_rtx_REG (Pmode, 31) /* %i7 */ +#define EH_RETURN_STACKADJ_RTX gen_rtx_REG (Pmode, EH_RETURN_REGNUM) + +/* Define registers used by the epilogue and return instruction. */ +#define EPILOGUE_USES(REGNO) \ + ((REGNO) == RETURN_ADDR_REGNUM \ + || (TARGET_FLAT \ + && epilogue_completed \ + && (REGNO) == INCOMING_RETURN_ADDR_REGNUM) \ + || (crtl->calls_eh_return && (REGNO) == EH_RETURN_REGNUM)) /* Select a format to encode pointers in exception handling data. CODE is 0 for data, 1 for code labels, 2 for function pointers. GLOBAL is diff --git a/gcc/config/sparc/sparc.md b/gcc/config/sparc/sparc.md index aa9f9fb..acf317d 100644 --- a/gcc/config/sparc/sparc.md +++ b/gcc/config/sparc/sparc.md @@ -164,7 +164,7 @@ (define_attr "calls_eh_return" "false,true" (symbol_ref "(crtl->calls_eh_return != 0 ? CALLS_EH_RETURN_TRUE : CALLS_EH_RETURN_FALSE)")) - + (define_attr "leaf_function" "false,true" (symbol_ref "(current_function_uses_only_leaf_regs != 0 ? LEAF_FUNCTION_TRUE : LEAF_FUNCTION_FALSE)")) @@ -173,6 +173,10 @@ (symbol_ref "(flag_delayed_branch != 0 ? DELAYED_BRANCH_TRUE : DELAYED_BRANCH_FALSE)")) +(define_attr "flat" "false,true" + (symbol_ref "(TARGET_FLAT != 0 + ? FLAT_TRUE : FLAT_FALSE)")) + ;; Length (in # of insns). ;; Beware that setting a length greater or equal to 3 for conditional branches ;; has a side-effect (see output_cbranch and output_v9branch). @@ -6265,7 +6269,10 @@ [(const_int 0)] "" { - sparc_expand_prologue (); + if (TARGET_FLAT) + sparc_flat_expand_prologue (); + else + sparc_expand_prologue (); DONE; }) @@ -6282,25 +6289,87 @@ (set (reg:P 14) (unspec_volatile:P [(reg:P 14) (match_operand:P 0 "arith_operand" "rI")] UNSPECV_SAVEW)) (set (reg:P 31) (reg:P 15))] - "" + "!TARGET_FLAT" "save\t%%sp, %0, %%sp" [(set_attr "type" "savew")]) +;; Likewise for the "create flat frame" insns. We need to use special insns +;; because %fp cannot be clobbered until after the frame is established (so +;; that it contains the live register window save area) and %i7 changed with +;; a simple move as it is a fixed register and the move would be eliminated. + +(define_insn "create_flat_frame_1<P:mode>" + [(set (reg:P 30) (reg:P 14)) + (set (reg:P 14) (plus:P (reg:P 14) + (match_operand:P 0 "arith_operand" "rI"))) + (set (reg:P 31) (reg:P 15))] + "TARGET_FLAT" + "add\t%%sp, %0, %%sp\n\tsub\t%%sp, %0, %%fp\n\tmov\t%%o7, %%i7" + [(set_attr "type" "multi") + (set_attr "length" "3")]) + +(define_insn "create_flat_frame_2<P:mode>" + [(set (reg:P 30) (reg:P 14)) + (set (reg:P 14) (plus:P (reg:P 14) + (match_operand:P 0 "arith_operand" "rI")))] + "TARGET_FLAT" + "add\t%%sp, %0, %%sp\n\tsub\t%%sp, %0, %%fp" + [(set_attr "type" "multi") + (set_attr "length" "2")]) + +(define_insn "create_flat_frame_3<P:mode>" + [(set (reg:P 14) (plus:P (reg:P 14) + (match_operand:P 0 "arith_operand" "rI"))) + (set (reg:P 31) (reg:P 15))] + "TARGET_FLAT" + "add\t%%sp, %0, %%sp\n\tmov\t%%o7, %%i7" + [(set_attr "type" "multi") + (set_attr "length" "2")]) + (define_expand "epilogue" [(return)] "" { - sparc_expand_epilogue (); + if (TARGET_FLAT) + sparc_flat_expand_epilogue (false); + else + sparc_expand_epilogue (false); }) (define_expand "sibcall_epilogue" [(return)] "" { - sparc_expand_epilogue (); + if (TARGET_FLAT) + sparc_flat_expand_epilogue (false); + else + sparc_expand_epilogue (false); + DONE; +}) + +(define_expand "eh_return" + [(use (match_operand 0 "general_operand" ""))] + "" +{ + emit_move_insn (gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM), operands[0]); + emit_jump_insn (gen_eh_return_internal ()); + emit_barrier (); DONE; }) +(define_insn_and_split "eh_return_internal" + [(eh_return)] + "" + "#" + "epilogue_completed" + [(return)] +{ + if (TARGET_FLAT) + sparc_flat_expand_epilogue (true); + else + sparc_expand_epilogue (true); +}) + (define_expand "return" [(return)] "sparc_can_use_return_insn_p ()" @@ -6312,16 +6381,19 @@ "* return output_return (insn);" [(set_attr "type" "return") (set (attr "length") - (cond [(eq_attr "leaf_function" "true") + (cond [(eq_attr "calls_eh_return" "true") + (if_then_else (eq_attr "delayed_branch" "true") + (if_then_else (ior (eq_attr "isa" "v9") + (eq_attr "flat" "true")) + (const_int 2) + (const_int 3)) + (if_then_else (eq_attr "flat" "true") + (const_int 3) + (const_int 4))) + (ior (eq_attr "leaf_function" "true") (eq_attr "flat" "true")) (if_then_else (eq_attr "empty_delay_slot" "true") (const_int 2) (const_int 1)) - (eq_attr "calls_eh_return" "true") - (if_then_else (eq_attr "delayed_branch" "true") - (if_then_else (eq_attr "isa" "v9") - (const_int 2) - (const_int 3)) - (const_int 4)) (eq_attr "empty_delay_slot" "true") (if_then_else (eq_attr "delayed_branch" "true") (const_int 2) @@ -6367,8 +6439,7 @@ if (! TARGET_ARCH64) { - rtx rtnreg = gen_rtx_REG (SImode, (current_function_uses_only_leaf_regs - ? 15 : 31)); + rtx rtnreg = gen_rtx_REG (SImode, RETURN_ADDR_REGNUM); rtx value = gen_reg_rtx (SImode); /* Fetch the instruction where we will return to and see if it's an unimp @@ -6449,7 +6520,7 @@ { operands[0] = adjust_address_nv (operands[0], Pmode, 0); operands[2] = adjust_address_nv (operands[0], Pmode, GET_MODE_SIZE (Pmode)); - operands[3] = gen_rtx_REG (Pmode, 31); /* %i7 */ + operands[3] = gen_rtx_REG (Pmode, RETURN_ADDR_REGNUM); }) (define_expand "restore_stack_nonlocal" @@ -6474,7 +6545,8 @@ /* We need to flush all the register windows so that their contents will be re-synchronized by the restore insn of the target function. */ - emit_insn (gen_flush_register_windows ()); + if (!TARGET_FLAT) + emit_insn (gen_flush_register_windows ()); emit_clobber (gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode))); emit_clobber (gen_rtx_MEM (BLKmode, hard_frame_pointer_rtx)); diff --git a/gcc/config/sparc/sparc.opt b/gcc/config/sparc/sparc.opt index b7fac09..d729214 100644 --- a/gcc/config/sparc/sparc.opt +++ b/gcc/config/sparc/sparc.opt @@ -33,6 +33,10 @@ msoft-float Target RejectNegative InverseMask(FPU) Do not use hardware FP +mflat +Target Report Mask(FLAT) +Use flat register window model + munaligned-doubles Target Report Mask(UNALIGNED_DOUBLES) Assume possible double misalignment diff --git a/gcc/config/sparc/t-elf b/gcc/config/sparc/t-elf index b1d18fd..962d000 100644 --- a/gcc/config/sparc/t-elf +++ b/gcc/config/sparc/t-elf @@ -32,8 +32,8 @@ fp-bit.c: $(srcdir)/config/fp-bit.c echo '#define FLOAT' > fp-bit.c cat $(srcdir)/config/fp-bit.c >> fp-bit.c -MULTILIB_OPTIONS = msoft-float mcpu=v8 -MULTILIB_DIRNAMES = soft v8 +MULTILIB_OPTIONS = msoft-float mcpu=v8 mflat +MULTILIB_DIRNAMES = soft v8 flat MULTILIB_MATCHES = msoft-float=mno-fpu LIBGCC = stmp-multilib diff --git a/gcc/config/sparc/t-leon b/gcc/config/sparc/t-leon index 6573f82..e3ab3bc 100644 --- a/gcc/config/sparc/t-leon +++ b/gcc/config/sparc/t-leon @@ -34,8 +34,8 @@ fp-bit.c: $(srcdir)/config/fp-bit.c # Multilibs for LEON # LEON is a SPARC-V8, but the AT697 implementation has a bug in the # V8-specific instructions. -MULTILIB_OPTIONS = mcpu=v7 msoft-float -MULTILIB_DIRNAMES = v7 soft +MULTILIB_OPTIONS = mcpu=v7 msoft-float mflat +MULTILIB_DIRNAMES = v7 soft flat MULTILIB_MATCHES = mcpu?v7=mv7 msoft-float=mno-fpu LIBGCC = stmp-multilib diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 0069f78..fee3a2f 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -863,7 +863,7 @@ See RS/6000 and PowerPC Options. -mtune=@var{cpu-type} @gol -mcmodel=@var{code-model} @gol -m32 -m64 -mapp-regs -mno-app-regs @gol --mfaster-structs -mno-faster-structs @gol +-mfaster-structs -mno-faster-structs -mflat -mno-flat @gol -mfpu -mno-fpu -mhard-float -msoft-float @gol -mhard-quad-float -msoft-quad-float @gol -mlittle-endian @gol @@ -17043,6 +17043,19 @@ To be fully SVR4 ABI compliant at the cost of some performance loss, specify @option{-mno-app-regs}. You should compile libraries and system software with this option. +@item -mflat +@itemx -mno-flat +@opindex mflat +@opindex mno-flat +With @option{-mflat}, the compiler does not generate save/restore instructions +and uses a ``flat'' or single register window model. This model is compatible +with the regular register window model. The local registers and the input +registers (0--5) are still treated as ``call-saved'' registers and will be +saved on the stack as needed. + +With @option{-mno-flat} (the default), the compiler generates save/restore +instructions (except for leaf functions). This is the normal operating mode. + @item -mfpu @itemx -mhard-float @opindex mfpu diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index a80c3cd..31f5d1e 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2011-06-10 Eric Botcazou <ebotcazou@adacore.com> + Laurent Rougé <laurent.rouge@menta.fr> + + * gcc.dg/20020503-1.c: Add back -mflat option on the SPARC. + * gcc.target/sparc/sparc-ret.c: Skip if -mflat is passed. + 2011-06-10 Daniel Carrera <dcarrera@gmail.com> * gfortran.dg/coarray/sync_1.f90: New test for diff --git a/gcc/testsuite/gcc.dg/20020503-1.c b/gcc/testsuite/gcc.dg/20020503-1.c index 3edc7cf..e13cf95 100644 --- a/gcc/testsuite/gcc.dg/20020503-1.c +++ b/gcc/testsuite/gcc.dg/20020503-1.c @@ -4,7 +4,7 @@ for leaf functions, the function was still leaf, but LEAF_REG_REMAP returned -1 for some registers (like %o0). */ /* { dg-do compile } */ -/* { dg-options "-O2 -g" } */ +/* { dg-options "-O2 -g -mflat" { target sparc*-*-* } } */ void foo (char *a, char *b, char *c, char *d) { diff --git a/gcc/testsuite/gcc.target/sparc/sparc-ret.c b/gcc/testsuite/gcc.target/sparc/sparc-ret.c index 11afc10..f58b059 100644 --- a/gcc/testsuite/gcc.target/sparc/sparc-ret.c +++ b/gcc/testsuite/gcc.target/sparc/sparc-ret.c @@ -1,4 +1,5 @@ /* { dg-do compile } */ +/* { dg-skip-if "no register windows" { *-*-* } { "-mflat" } { "" } } */ /* { dg-require-effective-target ilp32 } */ /* { dg-options "-mcpu=ultrasparc -O" } */ @@ -11,7 +12,7 @@ int bar (int a, int b, int c, int d, int e, int f, int g, int h) toto (&res); return h; } -/* { dg-final { global compiler_flags; if ![string match "*-m64 *" $compiler_flags] { scan-assembler "return\[ \t\]*%i7\\+8\n\[^\n\]*ld\[ \t\]*\\\[%sp\\+96\\\]" } } } */ +/* { dg-final { scan-assembler "return\[ \t\]*%i7\\+8\n\[^\n\]*ld\[ \t\]*\\\[%sp\\+96\\\]" } } */ int bar2 () { @@ -20,4 +21,4 @@ int bar2 () toto (&res); return res; } -/* { dg-final { global compiler_flags; if ![string match "*-m64 *" $compiler_flags] { scan-assembler "return\[ \t\]*%i7\\+8\n\[^\n\]*nop" } } } */ +/* { dg-final { scan-assembler "return\[ \t\]*%i7\\+8\n\[^\n\]*nop" } } */ |