diff options
author | Alan Modra <amodra@gmail.com> | 2015-05-20 10:56:28 +0930 |
---|---|---|
committer | Alan Modra <amodra@gcc.gnu.org> | 2015-05-20 10:56:28 +0930 |
commit | 0f0fd745255adf8b2904b0acaf1c3b66ec93ddb2 (patch) | |
tree | 25d754bde890f2750c3489f49713ea0123e09339 /gcc | |
parent | 8a03f86937467dcfe401b4f369307faf2593e97a (diff) | |
download | gcc-0f0fd745255adf8b2904b0acaf1c3b66ec93ddb2.zip gcc-0f0fd745255adf8b2904b0acaf1c3b66ec93ddb2.tar.gz gcc-0f0fd745255adf8b2904b0acaf1c3b66ec93ddb2.tar.bz2 |
rs6000-common.c (TARGET_SUPPORTS_SPLIT_STACK): Define.
gcc/
* common/config/rs6000/rs6000-common.c (TARGET_SUPPORTS_SPLIT_STACK):
Define.
(rs6000_supports_split_stack): New function.
* gcc/config/rs6000/rs6000.c (machine_function): Add
split_stack_arg_pointer.
(TARGET_EXTRA_LIVE_ON_ENTRY, TARGET_INTERNAL_ARG_POINTER): Define.
(setup_incoming_varargs): Use crtl->args.internal_arg_pointer
rather than virtual_incoming_args_rtx.
(rs6000_va_start): Likewise.
(split_stack_arg_pointer_used_p): New function.
(rs6000_emit_prologue): Set up arg pointer for -fsplit-stack.
(morestack_ref): New var.
(gen_add3_const, rs6000_expand_split_stack_prologue,
rs6000_internal_arg_pointer, rs6000_live_on_entry,
rs6000_split_stack_space_check): New functions.
(rs6000_elf_file_end): Call file_end_indicate_split_stack.
* gcc/config/rs6000/rs6000.md (UNSPEC_STACK_CHECK): Define.
(UNSPECV_SPLIT_STACK_RETURN): Define.
(split_stack_prologue, load_split_stack_limit,
load_split_stack_limit_di, load_split_stack_limit_si,
split_stack_return, split_stack_space_check): New expands and insns.
* gcc/config/rs6000/rs6000-protos.h
(rs6000_expand_split_stack_prologue): Declare.
(rs6000_split_stack_space_check): Declare.
libgcc/
* config/rs6000/morestack.S: New.
* config/rs6000/t-stack-rs6000: New.
* config.host (powerpc*-*-linux*): Add t-stack and t-stack-rs6000
to tmake_file.
* generic-morestack.c: Don't build for powerpc 32-bit.
From-SVN: r223426
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 27 | ||||
-rw-r--r-- | gcc/common/config/rs6000/rs6000-common.c | 28 | ||||
-rw-r--r-- | gcc/config/rs6000/rs6000-protos.h | 2 | ||||
-rw-r--r-- | gcc/config/rs6000/rs6000.c | 264 | ||||
-rw-r--r-- | gcc/config/rs6000/rs6000.md | 68 |
5 files changed, 387 insertions, 2 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 56b028d..471291c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,32 @@ 2015-05-20 Alan Modra <amodra@gmail.com> + * common/config/rs6000/rs6000-common.c (TARGET_SUPPORTS_SPLIT_STACK): + Define. + (rs6000_supports_split_stack): New function. + * gcc/config/rs6000/rs6000.c (machine_function): Add + split_stack_arg_pointer. + (TARGET_EXTRA_LIVE_ON_ENTRY, TARGET_INTERNAL_ARG_POINTER): Define. + (setup_incoming_varargs): Use crtl->args.internal_arg_pointer + rather than virtual_incoming_args_rtx. + (rs6000_va_start): Likewise. + (split_stack_arg_pointer_used_p): New function. + (rs6000_emit_prologue): Set up arg pointer for -fsplit-stack. + (morestack_ref): New var. + (gen_add3_const, rs6000_expand_split_stack_prologue, + rs6000_internal_arg_pointer, rs6000_live_on_entry, + rs6000_split_stack_space_check): New functions. + (rs6000_elf_file_end): Call file_end_indicate_split_stack. + * gcc/config/rs6000/rs6000.md (UNSPEC_STACK_CHECK): Define. + (UNSPECV_SPLIT_STACK_RETURN): Define. + (split_stack_prologue, load_split_stack_limit, + load_split_stack_limit_di, load_split_stack_limit_si, + split_stack_return, split_stack_space_check): New expands and insns. + * gcc/config/rs6000/rs6000-protos.h + (rs6000_expand_split_stack_prologue): Declare. + (rs6000_split_stack_space_check): Declare. + +2015-05-20 Alan Modra <amodra@gmail.com> + * config/rs6000/rs6000.c (struct rs6000_stack): Correct comments. (rs6000_stack_info): Don't zero offsets when not saving registers. (debug_stack_info): Adjust to omit printing unused offsets, diff --git a/gcc/common/config/rs6000/rs6000-common.c b/gcc/common/config/rs6000/rs6000-common.c index e0e158f..891bc55 100644 --- a/gcc/common/config/rs6000/rs6000-common.c +++ b/gcc/common/config/rs6000/rs6000-common.c @@ -288,6 +288,31 @@ rs6000_handle_option (struct gcc_options *opts, struct gcc_options *opts_set, return true; } +/* -fsplit-stack uses a field in the TCB, available with glibc-2.19. + We also allow 2.18 because alignment padding guarantees that the + space is available there too. */ + +static bool +rs6000_supports_split_stack (bool report, + struct gcc_options *opts ATTRIBUTE_UNUSED) +{ +#ifndef TARGET_GLIBC_MAJOR +#define TARGET_GLIBC_MAJOR 0 +#endif +#ifndef TARGET_GLIBC_MINOR +#define TARGET_GLIBC_MINOR 0 +#endif + /* Note: Can't test DEFAULT_ABI here, it isn't set until later. */ + if (TARGET_GLIBC_MAJOR * 1000 + TARGET_GLIBC_MINOR >= 2018 + && TARGET_64BIT + && TARGET_ELF) + return true; + + if (report) + error ("%<-fsplit-stack%> currently only supported on PowerPC64 GNU/Linux with glibc-2.18 or later"); + return false; +} + #undef TARGET_HANDLE_OPTION #define TARGET_HANDLE_OPTION rs6000_handle_option @@ -300,4 +325,7 @@ rs6000_handle_option (struct gcc_options *opts, struct gcc_options *opts_set, #undef TARGET_OPTION_OPTIMIZATION_TABLE #define TARGET_OPTION_OPTIMIZATION_TABLE rs6000_option_optimization_table +#undef TARGET_SUPPORTS_SPLIT_STACK +#define TARGET_SUPPORTS_SPLIT_STACK rs6000_supports_split_stack + struct gcc_targetm_common targetm_common = TARGETM_COMMON_INITIALIZER; diff --git a/gcc/config/rs6000/rs6000-protos.h b/gcc/config/rs6000/rs6000-protos.h index 739f1c6..bd1ede1 100644 --- a/gcc/config/rs6000/rs6000-protos.h +++ b/gcc/config/rs6000/rs6000-protos.h @@ -191,6 +191,8 @@ extern void rs6000_emit_prologue (void); extern void rs6000_emit_load_toc_table (int); extern unsigned int rs6000_dbx_register_number (unsigned int, unsigned int); extern void rs6000_emit_epilogue (int); +extern void rs6000_expand_split_stack_prologue (void); +extern void rs6000_split_stack_space_check (rtx, rtx); extern void rs6000_emit_eh_reg_restore (rtx, rtx); extern const char * output_isel (rtx *); extern void rs6000_call_aix (rtx, rtx, rtx, rtx); diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index 92ebd82..8947849 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -187,6 +187,8 @@ typedef struct GTY(()) machine_function 64-bits wide and is allocated early enough so that the offset does not overflow the 16-bit load/store offset field. */ rtx sdmode_stack_slot; + /* Alternative internal arg pointer for -fsplit-stack. */ + rtx split_stack_arg_pointer; /* Flag if r2 setup is needed with ELFv2 ABI. */ bool r2_setup_needed; } machine_function; @@ -1190,6 +1192,7 @@ static bool rs6000_debug_cannot_change_mode_class (machine_mode, machine_mode, enum reg_class); static bool rs6000_save_toc_in_prologue_p (void); +static rtx rs6000_internal_arg_pointer (void); rtx (*rs6000_legitimize_reload_address_ptr) (rtx, machine_mode, int, int, int, int *) @@ -1411,6 +1414,12 @@ static const struct attribute_spec rs6000_attribute_table[] = #undef TARGET_SET_UP_BY_PROLOGUE #define TARGET_SET_UP_BY_PROLOGUE rs6000_set_up_by_prologue +#undef TARGET_EXTRA_LIVE_ON_ENTRY +#define TARGET_EXTRA_LIVE_ON_ENTRY rs6000_live_on_entry + +#undef TARGET_INTERNAL_ARG_POINTER +#define TARGET_INTERNAL_ARG_POINTER rs6000_internal_arg_pointer + #undef TARGET_HAVE_TLS #define TARGET_HAVE_TLS HAVE_AS_TLS @@ -11150,7 +11159,7 @@ setup_incoming_varargs (cumulative_args_t cum, machine_mode mode, else { first_reg_offset = next_cum.words; - save_area = virtual_incoming_args_rtx; + save_area = crtl->args.internal_arg_pointer; if (targetm.calls.must_pass_in_stack (mode, type)) first_reg_offset += rs6000_arg_size (TYPE_MODE (type), type); @@ -11344,7 +11353,7 @@ rs6000_va_start (tree valist, rtx nextarg) } /* Find the overflow area. */ - t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx); + t = make_tree (TREE_TYPE (ovf), crtl->args.internal_arg_pointer); if (words != 0) t = fold_build_pointer_plus_hwi (t, words * MIN_UNITS_PER_WORD); t = build2 (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t); @@ -23424,6 +23433,48 @@ rs6000_reg_live_or_pic_offset_p (int reg) || (DEFAULT_ABI == ABI_DARWIN && flag_pic)))); } +/* Return whether the split-stack arg pointer (r12) is used. */ + +static bool +split_stack_arg_pointer_used_p (void) +{ + /* If the pseudo holding the arg pointer is no longer a pseudo, + then the arg pointer is used. */ + if (cfun->machine->split_stack_arg_pointer != NULL_RTX + && (!REG_P (cfun->machine->split_stack_arg_pointer) + || (REGNO (cfun->machine->split_stack_arg_pointer) + < FIRST_PSEUDO_REGISTER))) + return true; + + /* Unfortunately we also need to do some code scanning, since + r12 may have been substituted for the pseudo. */ + rtx_insn *insn; + basic_block bb = ENTRY_BLOCK_PTR_FOR_FN (cfun); + FOR_BB_INSNS (bb, insn) + if (NONDEBUG_INSN_P (insn)) + { + /* A call destroys r12. */ + if (CALL_P (insn)) + return false; + + df_ref use; + FOR_EACH_INSN_USE (use, insn) + { + rtx x = DF_REF_REG (use); + if (REG_P (x) && REGNO (x) == 12) + return true; + } + df_ref def; + FOR_EACH_INSN_DEF (def, insn) + { + rtx x = DF_REF_REG (def); + if (REG_P (x) && REGNO (x) == 12) + return false; + } + } + return bitmap_bit_p (DF_LR_OUT (bb), 12); +} + /* Emit function prologue as insns. */ void @@ -24375,6 +24426,40 @@ rs6000_emit_prologue (void) rtx reg = gen_rtx_REG (reg_mode, TOC_REGNUM); emit_insn (gen_frame_store (reg, sp_reg_rtx, RS6000_TOC_SAVE_SLOT)); } + + if (flag_split_stack && split_stack_arg_pointer_used_p ()) + { + /* Set up the arg pointer (r12) for -fsplit-stack code. If + __morestack was called, it left the arg pointer to the old + stack in r29. Otherwise, the arg pointer is the top of the + current frame. */ + if (frame_off != 0 || REGNO (frame_reg_rtx) != 12) + { + rtx r12 = gen_rtx_REG (Pmode, 12); + if (frame_off == 0) + emit_move_insn (r12, frame_reg_rtx); + else + emit_insn (gen_add3_insn (r12, frame_reg_rtx, GEN_INT (frame_off))); + } + if (info->push_p) + { + rtx r12 = gen_rtx_REG (Pmode, 12); + rtx r29 = gen_rtx_REG (Pmode, 29); + rtx cr7 = gen_rtx_REG (CCUNSmode, CR7_REGNO); + rtx not_more = gen_label_rtx (); + rtx jump; + + jump = gen_rtx_IF_THEN_ELSE (VOIDmode, + gen_rtx_GEU (VOIDmode, cr7, const0_rtx), + gen_rtx_LABEL_REF (VOIDmode, not_more), + pc_rtx); + jump = emit_jump_insn (gen_rtx_SET (pc_rtx, jump)); + JUMP_LABEL (jump) = not_more; + LABEL_NUSES (not_more) += 1; + emit_move_insn (r12, r29); + emit_label (not_more); + } + } } /* Output .extern statements for the save/restore routines we use. */ @@ -25802,6 +25887,178 @@ rs6000_output_function_epilogue (FILE *file, fputs ("\t.align 2\n", file); } } + +/* -fsplit-stack support. */ + +/* A SYMBOL_REF for __morestack. */ +static GTY(()) rtx morestack_ref; + +static rtx +gen_add3_const (rtx rt, rtx ra, long c) +{ + if (TARGET_64BIT) + return gen_adddi3 (rt, ra, GEN_INT (c)); + else + return gen_addsi3 (rt, ra, GEN_INT (c)); +} + +/* Emit -fsplit-stack prologue, which goes before the regular function + prologue (at local entry point in the case of ELFv2). */ + +void +rs6000_expand_split_stack_prologue (void) +{ + rs6000_stack_t *info = rs6000_stack_info (); + unsigned HOST_WIDE_INT allocate; + long alloc_hi, alloc_lo; + rtx r0, r1, r12, lr, ok_label, compare, jump, call_fusage; + rtx_insn *insn; + + gcc_assert (flag_split_stack && reload_completed); + + if (!info->push_p) + return; + + allocate = info->total_size; + if (allocate > (unsigned HOST_WIDE_INT) 1 << 31) + { + sorry ("Stack frame larger than 2G is not supported for -fsplit-stack"); + return; + } + if (morestack_ref == NULL_RTX) + { + morestack_ref = gen_rtx_SYMBOL_REF (Pmode, "__morestack"); + SYMBOL_REF_FLAGS (morestack_ref) |= (SYMBOL_FLAG_LOCAL + | SYMBOL_FLAG_FUNCTION); + } + + r0 = gen_rtx_REG (Pmode, 0); + r1 = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM); + r12 = gen_rtx_REG (Pmode, 12); + emit_insn (gen_load_split_stack_limit (r0)); + /* Always emit two insns here to calculate the requested stack, + so that the linker can edit them when adjusting size for calling + non-split-stack code. */ + alloc_hi = (-allocate + 0x8000) & ~0xffffL; + alloc_lo = -allocate - alloc_hi; + if (alloc_hi != 0) + { + emit_insn (gen_add3_const (r12, r1, alloc_hi)); + if (alloc_lo != 0) + emit_insn (gen_add3_const (r12, r12, alloc_lo)); + else + emit_insn (gen_nop ()); + } + else + { + emit_insn (gen_add3_const (r12, r1, alloc_lo)); + emit_insn (gen_nop ()); + } + + compare = gen_rtx_REG (CCUNSmode, CR7_REGNO); + emit_insn (gen_rtx_SET (compare, gen_rtx_COMPARE (CCUNSmode, r12, r0))); + ok_label = gen_label_rtx (); + jump = gen_rtx_IF_THEN_ELSE (VOIDmode, + gen_rtx_GEU (VOIDmode, compare, const0_rtx), + gen_rtx_LABEL_REF (VOIDmode, ok_label), + pc_rtx); + jump = emit_jump_insn (gen_rtx_SET (pc_rtx, jump)); + JUMP_LABEL (jump) = ok_label; + /* Mark the jump as very likely to be taken. */ + add_int_reg_note (jump, REG_BR_PROB, + REG_BR_PROB_BASE - REG_BR_PROB_BASE / 100); + + lr = gen_rtx_REG (Pmode, LR_REGNO); + insn = emit_move_insn (r0, lr); + RTX_FRAME_RELATED_P (insn) = 1; + insn = emit_insn (gen_frame_store (r0, r1, info->lr_save_offset)); + RTX_FRAME_RELATED_P (insn) = 1; + + insn = emit_call_insn (gen_call (gen_rtx_MEM (SImode, morestack_ref), + const0_rtx, const0_rtx)); + call_fusage = NULL_RTX; + use_reg (&call_fusage, r12); + add_function_usage_to (insn, call_fusage); + emit_insn (gen_frame_load (r0, r1, info->lr_save_offset)); + insn = emit_move_insn (lr, r0); + add_reg_note (insn, REG_CFA_RESTORE, lr); + RTX_FRAME_RELATED_P (insn) = 1; + emit_insn (gen_split_stack_return ()); + + emit_label (ok_label); + LABEL_NUSES (ok_label) = 1; +} + +/* Return the internal arg pointer used for function incoming + arguments. When -fsplit-stack, the arg pointer is r12 so we need + to copy it to a pseudo in order for it to be preserved over calls + and suchlike. We'd really like to use a pseudo here for the + internal arg pointer but data-flow analysis is not prepared to + accept pseudos as live at the beginning of a function. */ + +static rtx +rs6000_internal_arg_pointer (void) +{ + if (flag_split_stack) + { + if (cfun->machine->split_stack_arg_pointer == NULL_RTX) + { + rtx pat; + + cfun->machine->split_stack_arg_pointer = gen_reg_rtx (Pmode); + REG_POINTER (cfun->machine->split_stack_arg_pointer) = 1; + + /* Put the pseudo initialization right after the note at the + beginning of the function. */ + pat = gen_rtx_SET (cfun->machine->split_stack_arg_pointer, + gen_rtx_REG (Pmode, 12)); + push_topmost_sequence (); + emit_insn_after (pat, get_insns ()); + pop_topmost_sequence (); + } + return plus_constant (Pmode, cfun->machine->split_stack_arg_pointer, + FIRST_PARM_OFFSET (current_function_decl)); + } + return virtual_incoming_args_rtx; +} + +/* We may have to tell the dataflow pass that the split stack prologue + is initializing a register. */ + +static void +rs6000_live_on_entry (bitmap regs) +{ + if (flag_split_stack) + bitmap_set_bit (regs, 12); +} + +/* Emit -fsplit-stack dynamic stack allocation space check. */ + +void +rs6000_split_stack_space_check (rtx size, rtx label) +{ + rtx sp = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM); + rtx limit = gen_reg_rtx (Pmode); + rtx requested = gen_reg_rtx (Pmode); + rtx cmp = gen_reg_rtx (CCUNSmode); + rtx jump; + + emit_insn (gen_load_split_stack_limit (limit)); + if (CONST_INT_P (size)) + emit_insn (gen_add3_insn (requested, sp, GEN_INT (-INTVAL (size)))); + else + { + size = force_reg (Pmode, size); + emit_move_insn (requested, gen_rtx_MINUS (Pmode, sp, size)); + } + emit_insn (gen_rtx_SET (cmp, gen_rtx_COMPARE (CCUNSmode, requested, limit))); + jump = gen_rtx_IF_THEN_ELSE (VOIDmode, + gen_rtx_GEU (VOIDmode, cmp, const0_rtx), + gen_rtx_LABEL_REF (VOIDmode, label), + pc_rtx); + jump = emit_jump_insn (gen_rtx_SET (pc_rtx, jump)); + JUMP_LABEL (jump) = label; +} /* A C compound statement that outputs the assembler code for a thunk function, used to implement C++ virtual function calls with @@ -29810,6 +30067,9 @@ rs6000_elf_file_end (void) if (TARGET_32BIT || DEFAULT_ABI == ABI_ELFv2) file_end_indicate_exec_stack (); #endif + + if (flag_split_stack) + file_end_indicate_split_stack (); } #endif diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md index e156e14..5d3e04b 100644 --- a/gcc/config/rs6000/rs6000.md +++ b/gcc/config/rs6000/rs6000.md @@ -140,6 +140,7 @@ UNSPEC_PACK_128BIT UNSPEC_LSQ UNSPEC_FUSION_GPR + UNSPEC_STACK_CHECK ]) ;; @@ -157,6 +158,7 @@ UNSPECV_NLGR ; non-local goto receiver UNSPECV_MFFS ; Move from FPSCR UNSPECV_MTFSF ; Move to FPSCR Fields + UNSPECV_SPLIT_STACK_RETURN ; A camouflaged return ]) @@ -12345,6 +12347,72 @@ }" [(set_attr "type" "load")]) +;; Handle -fsplit-stack. + +(define_expand "split_stack_prologue" + [(const_int 0)] + "" +{ + rs6000_expand_split_stack_prologue (); + DONE; +}) + +(define_expand "load_split_stack_limit" + [(set (match_operand 0) + (unspec [(const_int 0)] UNSPEC_STACK_CHECK))] + "" +{ + emit_insn (gen_rtx_SET (operands[0], + gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, const0_rtx), + UNSPEC_STACK_CHECK))); + DONE; +}) + +(define_insn "load_split_stack_limit_di" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (unspec:DI [(const_int 0)] UNSPEC_STACK_CHECK))] + "TARGET_64BIT" + "ld %0,-0x7040(13)" + [(set_attr "type" "load") + (set_attr "update" "no") + (set_attr "indexed" "no")]) + +(define_insn "load_split_stack_limit_si" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (unspec:SI [(const_int 0)] UNSPEC_STACK_CHECK))] + "!TARGET_64BIT" + "lwz %0,-0x7020(2)" + [(set_attr "type" "load") + (set_attr "update" "no") + (set_attr "indexed" "no")]) + +;; A return instruction which the middle-end doesn't see. +(define_insn "split_stack_return" + [(unspec_volatile [(const_int 0)] UNSPECV_SPLIT_STACK_RETURN)] + "" + "blr" + [(set_attr "type" "jmpreg")]) + +;; If there are operand 0 bytes available on the stack, jump to +;; operand 1. +(define_expand "split_stack_space_check" + [(set (match_dup 2) + (unspec [(const_int 0)] UNSPEC_STACK_CHECK)) + (set (match_dup 3) + (minus (reg STACK_POINTER_REGNUM) + (match_operand 0))) + (set (match_dup 4) (compare:CCUNS (match_dup 3) (match_dup 2))) + (set (pc) (if_then_else + (geu (match_dup 4) (const_int 0)) + (label_ref (match_operand 1)) + (pc)))] + "" +{ + rs6000_split_stack_space_check (operands[0], operands[1]); + DONE; +}) + (define_insn "bpermd_<mode>" [(set (match_operand:P 0 "gpc_reg_operand" "=r") (unspec:P [(match_operand:P 1 "gpc_reg_operand" "r") |