diff options
author | Marcin KoĆcielnicki <koriakin@0x04.net> | 2016-02-15 10:20:18 +0000 |
---|---|---|
committer | Andreas Krebbel <krebbel@gcc.gnu.org> | 2016-02-15 10:20:18 +0000 |
commit | 4cb4721f9371ac318e33cbed99a66a029353e10a (patch) | |
tree | 633620a331dce10653b1b110b0531b75f5ee2376 /gcc | |
parent | 17a3b967929eb6d344ae73bff4f28efc88c35ca9 (diff) | |
download | gcc-4cb4721f9371ac318e33cbed99a66a029353e10a.zip gcc-4cb4721f9371ac318e33cbed99a66a029353e10a.tar.gz gcc-4cb4721f9371ac318e33cbed99a66a029353e10a.tar.bz2 |
S/390: Add -fsplit-stack support
libgcc/ChangeLog:
* config.host: Use t-stack and t-stack-s390 for s390*-*-linux.
* config/s390/morestack.S: New file.
* config/s390/t-stack-s390: New file.
* generic-morestack.c (__splitstack_find): Add s390-specific code.
gcc/ChangeLog:
* common/config/s390/s390-common.c (s390_supports_split_stack):
New function.
(TARGET_SUPPORTS_SPLIT_STACK): New macro.
* config/s390/s390-protos.h: Add s390_expand_split_stack_prologue.
* config/s390/s390.c (struct machine_function): New field
split_stack_varargs_pointer.
(s390_register_info): Mark r12 as clobbered if it'll be used as temp
in s390_emit_prologue.
(s390_emit_prologue): Use r12 as temp if r1 is taken by split-stack
vararg pointer.
(morestack_ref): New global.
(SPLIT_STACK_AVAILABLE): New macro.
(s390_expand_split_stack_prologue): New function.
(s390_live_on_entry): New function.
(s390_va_start): Use split-stack vararg pointer if appropriate.
(s390_asm_file_end): Emit the split-stack note sections.
(TARGET_EXTRA_LIVE_ON_ENTRY): New macro.
* config/s390/s390.md (UNSPEC_STACK_CHECK): New unspec.
(UNSPECV_SPLIT_STACK_CALL): New unspec.
(UNSPECV_SPLIT_STACK_DATA): New unspec.
(split_stack_prologue): New expand.
(split_stack_space_check): New expand.
(split_stack_data): New insn.
(split_stack_call): New expand.
(split_stack_call_*): New insn.
(split_stack_cond_call): New expand.
(split_stack_cond_call_*): New insn.
From-SVN: r233421
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 30 | ||||
-rw-r--r-- | gcc/common/config/s390/s390-common.c | 14 | ||||
-rw-r--r-- | gcc/config/s390/s390-protos.h | 1 | ||||
-rw-r--r-- | gcc/config/s390/s390.c | 214 | ||||
-rw-r--r-- | gcc/config/s390/s390.md | 138 |
5 files changed, 392 insertions, 5 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6ef134f..cf178e7 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,33 @@ +2016-02-15 Marcin KoĆcielnicki <koriakin@0x04.net> + + * common/config/s390/s390-common.c (s390_supports_split_stack): + New function. + (TARGET_SUPPORTS_SPLIT_STACK): New macro. + * config/s390/s390-protos.h: Add s390_expand_split_stack_prologue. + * config/s390/s390.c (struct machine_function): New field + split_stack_varargs_pointer. + (s390_register_info): Mark r12 as clobbered if it'll be used as temp + in s390_emit_prologue. + (s390_emit_prologue): Use r12 as temp if r1 is taken by split-stack + vararg pointer. + (morestack_ref): New global. + (SPLIT_STACK_AVAILABLE): New macro. + (s390_expand_split_stack_prologue): New function. + (s390_live_on_entry): New function. + (s390_va_start): Use split-stack vararg pointer if appropriate. + (s390_asm_file_end): Emit the split-stack note sections. + (TARGET_EXTRA_LIVE_ON_ENTRY): New macro. + * config/s390/s390.md (UNSPEC_STACK_CHECK): New unspec. + (UNSPECV_SPLIT_STACK_CALL): New unspec. + (UNSPECV_SPLIT_STACK_DATA): New unspec. + (split_stack_prologue): New expand. + (split_stack_space_check): New expand. + (split_stack_data): New insn. + (split_stack_call): New expand. + (split_stack_call_*): New insn. + (split_stack_cond_call): New expand. + (split_stack_cond_call_*): New insn. + 2016-02-15 Richard Biener <rguenther@suse.de> PR tree-optimization/69783 diff --git a/gcc/common/config/s390/s390-common.c b/gcc/common/config/s390/s390-common.c index 4519c21..1e497e6 100644 --- a/gcc/common/config/s390/s390-common.c +++ b/gcc/common/config/s390/s390-common.c @@ -105,6 +105,17 @@ s390_handle_option (struct gcc_options *opts ATTRIBUTE_UNUSED, } } +/* -fsplit-stack uses a field in the TCB, available with glibc-2.23. + We don't verify it, since earlier versions just have padding at + its place, which works just as well. */ + +static bool +s390_supports_split_stack (bool report ATTRIBUTE_UNUSED, + struct gcc_options *opts ATTRIBUTE_UNUSED) +{ + return true; +} + #undef TARGET_DEFAULT_TARGET_FLAGS #define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT) @@ -117,4 +128,7 @@ s390_handle_option (struct gcc_options *opts ATTRIBUTE_UNUSED, #undef TARGET_OPTION_INIT_STRUCT #define TARGET_OPTION_INIT_STRUCT s390_option_init_struct +#undef TARGET_SUPPORTS_SPLIT_STACK +#define TARGET_SUPPORTS_SPLIT_STACK s390_supports_split_stack + struct gcc_targetm_common targetm_common = TARGETM_COMMON_INITIALIZER; diff --git a/gcc/config/s390/s390-protos.h b/gcc/config/s390/s390-protos.h index 633bc1e..09032c9 100644 --- a/gcc/config/s390/s390-protos.h +++ b/gcc/config/s390/s390-protos.h @@ -42,6 +42,7 @@ extern bool s390_handle_option (struct gcc_options *opts ATTRIBUTE_UNUSED, extern HOST_WIDE_INT s390_initial_elimination_offset (int, int); extern void s390_emit_prologue (void); extern void s390_emit_epilogue (bool); +extern void s390_expand_split_stack_prologue (void); extern bool s390_can_use_simple_return_insn (void); extern bool s390_can_use_return_insn (void); extern void s390_function_profiler (FILE *, int); diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c index 9facd96..aa82d1c 100644 --- a/gcc/config/s390/s390.c +++ b/gcc/config/s390/s390.c @@ -428,6 +428,13 @@ struct GTY(()) machine_function /* True if the current function may contain a tbegin clobbering FPRs. */ bool tbegin_p; + + /* For -fsplit-stack support: A stack local which holds a pointer to + the stack arguments for a function with a variable number of + arguments. This is set at the start of the function and is used + to initialize the overflow_arg_area field of the va_list + structure. */ + rtx split_stack_varargs_pointer; }; /* Few accessor macros for struct cfun->machine->s390_frame_layout. */ @@ -9371,9 +9378,13 @@ s390_register_info () cfun_frame_layout.high_fprs++; } - if (flag_pic) - clobbered_regs[PIC_OFFSET_TABLE_REGNUM] - |= !!df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM); + /* Register 12 is used for GOT address, but also as temp in prologue + for split-stack stdarg functions (unless r14 is available). */ + clobbered_regs[12] + |= ((flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM)) + || (flag_split_stack && cfun->stdarg + && (crtl->is_leaf || TARGET_TPF_PROFILING + || has_hard_reg_initial_val (Pmode, RETURN_REGNUM)))); clobbered_regs[BASE_REGNUM] |= (cfun->machine->base_reg @@ -10473,12 +10484,15 @@ s390_emit_prologue (void) int next_fpr = 0; /* Choose best register to use for temp use within prologue. - See below for why TPF must use the register 1. */ + TPF with profiling must avoid the register 14 - the tracing function + needs the original contents of r14 to be preserved. */ if (!has_hard_reg_initial_val (Pmode, RETURN_REGNUM) && !crtl->is_leaf && !TARGET_TPF_PROFILING) temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); + else if (flag_split_stack && cfun->stdarg) + temp_reg = gen_rtx_REG (Pmode, 12); else temp_reg = gen_rtx_REG (Pmode, 1); @@ -10972,6 +10986,166 @@ s300_set_up_by_prologue (hard_reg_set_container *regs) SET_HARD_REG_BIT (regs->set, REGNO (cfun->machine->base_reg)); } +/* -fsplit-stack support. */ + +/* A SYMBOL_REF for __morestack. */ +static GTY(()) rtx morestack_ref; + +/* When using -fsplit-stack, the allocation routines set a field in + the TCB to the bottom of the stack plus this much space, measured + in bytes. */ + +#define SPLIT_STACK_AVAILABLE 1024 + +/* Emit -fsplit-stack prologue, which goes before the regular function + prologue. */ + +void +s390_expand_split_stack_prologue (void) +{ + rtx r1, guard, cc = NULL; + rtx_insn *insn; + /* Offset from thread pointer to __private_ss. */ + int psso = TARGET_64BIT ? 0x38 : 0x20; + /* Pointer size in bytes. */ + /* Frame size and argument size - the two parameters to __morestack. */ + HOST_WIDE_INT frame_size = cfun_frame_layout.frame_size; + /* Align argument size to 8 bytes - simplifies __morestack code. */ + HOST_WIDE_INT args_size = crtl->args.size >= 0 + ? ((crtl->args.size + 7) & ~7) + : 0; + /* Label to be called by __morestack. */ + rtx_code_label *call_done = NULL; + rtx_code_label *parm_base = NULL; + rtx tmp; + + gcc_assert (flag_split_stack && reload_completed); + if (!TARGET_CPU_ZARCH) + { + sorry ("CPUs older than z900 are not supported for -fsplit-stack"); + return; + } + + r1 = gen_rtx_REG (Pmode, 1); + + /* If no stack frame will be allocated, don't do anything. */ + if (!frame_size) + { + if (cfun->machine->split_stack_varargs_pointer != NULL_RTX) + { + /* If va_start is used, just use r15. */ + emit_move_insn (r1, + gen_rtx_PLUS (Pmode, stack_pointer_rtx, + GEN_INT (STACK_POINTER_OFFSET))); + + } + 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); + } + + if (CONST_OK_FOR_K (frame_size) || CONST_OK_FOR_Op (frame_size)) + { + /* If frame_size will fit in an add instruction, do a stack space + check, and only call __morestack if there's not enough space. */ + + /* Get thread pointer. r1 is the only register we can always destroy - r0 + could contain a static chain (and cannot be used to address memory + anyway), r2-r6 can contain parameters, and r6-r15 are callee-saved. */ + emit_move_insn (r1, gen_rtx_REG (Pmode, TP_REGNUM)); + /* Aim at __private_ss. */ + guard = gen_rtx_MEM (Pmode, plus_constant (Pmode, r1, psso)); + + /* If less that 1kiB used, skip addition and compare directly with + __private_ss. */ + if (frame_size > SPLIT_STACK_AVAILABLE) + { + emit_move_insn (r1, guard); + if (TARGET_64BIT) + emit_insn (gen_adddi3 (r1, r1, GEN_INT (frame_size))); + else + emit_insn (gen_addsi3 (r1, r1, GEN_INT (frame_size))); + guard = r1; + } + + /* Compare the (maybe adjusted) guard with the stack pointer. */ + cc = s390_emit_compare (LT, stack_pointer_rtx, guard); + } + + call_done = gen_label_rtx (); + parm_base = gen_label_rtx (); + + /* Emit the parameter block. */ + tmp = gen_split_stack_data (parm_base, call_done, + GEN_INT (frame_size), + GEN_INT (args_size)); + insn = emit_insn (tmp); + add_reg_note (insn, REG_LABEL_OPERAND, call_done); + LABEL_NUSES (call_done)++; + add_reg_note (insn, REG_LABEL_OPERAND, parm_base); + LABEL_NUSES (parm_base)++; + + /* %r1 = litbase. */ + insn = emit_move_insn (r1, gen_rtx_LABEL_REF (VOIDmode, parm_base)); + add_reg_note (insn, REG_LABEL_OPERAND, parm_base); + LABEL_NUSES (parm_base)++; + + /* Now, we need to call __morestack. It has very special calling + conventions: it preserves param/return/static chain registers for + calling main function body, and looks for its own parameters at %r1. */ + + if (cc != NULL) + { + tmp = gen_split_stack_cond_call (morestack_ref, cc, call_done); + + insn = emit_jump_insn (tmp); + JUMP_LABEL (insn) = call_done; + LABEL_NUSES (call_done)++; + + /* Mark the jump as very unlikely to be taken. */ + add_int_reg_note (insn, REG_BR_PROB, REG_BR_PROB_BASE / 100); + + if (cfun->machine->split_stack_varargs_pointer != NULL_RTX) + { + /* If va_start is used, and __morestack was not called, just use + r15. */ + emit_move_insn (r1, + gen_rtx_PLUS (Pmode, stack_pointer_rtx, + GEN_INT (STACK_POINTER_OFFSET))); + } + } + else + { + tmp = gen_split_stack_call (morestack_ref, call_done); + insn = emit_jump_insn (tmp); + JUMP_LABEL (insn) = call_done; + LABEL_NUSES (call_done)++; + emit_barrier (); + } + + /* __morestack will call us here. */ + + emit_label (call_done); +} + +/* We may have to tell the dataflow pass that the split stack prologue + is initializing a register. */ + +static void +s390_live_on_entry (bitmap regs) +{ + if (cfun->machine->split_stack_varargs_pointer != NULL_RTX) + { + gcc_assert (flag_split_stack); + bitmap_set_bit (regs, 1); + } +} + /* Return true if the function can use simple_return to return outside of a shrink-wrapped region. At present shrink-wrapping is supported in all cases. */ @@ -11574,6 +11748,27 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED) expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); } + if (flag_split_stack + && (lookup_attribute ("no_split_stack", DECL_ATTRIBUTES (cfun->decl)) + == NULL) + && cfun->machine->split_stack_varargs_pointer == NULL_RTX) + { + rtx reg; + rtx_insn *seq; + + reg = gen_reg_rtx (Pmode); + cfun->machine->split_stack_varargs_pointer = reg; + + start_sequence (); + emit_move_insn (reg, gen_rtx_REG (Pmode, 1)); + seq = get_insns (); + end_sequence (); + + push_topmost_sequence (); + emit_insn_after (seq, entry_of_function ()); + pop_topmost_sequence (); + } + /* Find the overflow area. FIXME: This currently is too pessimistic when the vector ABI is enabled. In that case we *always* set up the overflow area @@ -11582,7 +11777,10 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED) || n_fpr + cfun->va_list_fpr_size > FP_ARG_NUM_REG || TARGET_VX_ABI) { - t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx); + if (cfun->machine->split_stack_varargs_pointer == NULL_RTX) + t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx); + else + t = make_tree (TREE_TYPE (ovf), cfun->machine->split_stack_varargs_pointer); off = INTVAL (crtl->args.arg_offset_rtx); off = off < 0 ? 0 : off; @@ -14502,6 +14700,9 @@ s390_asm_file_end (void) s390_vector_abi); #endif file_end_indicate_exec_stack (); + + if (flag_split_stack) + file_end_indicate_split_stack (); } /* Return true if TYPE is a vector bool type. */ @@ -14757,6 +14958,9 @@ s390_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1, const_tree ty #undef TARGET_SET_UP_BY_PROLOGUE #define TARGET_SET_UP_BY_PROLOGUE s300_set_up_by_prologue +#undef TARGET_EXTRA_LIVE_ON_ENTRY +#define TARGET_EXTRA_LIVE_ON_ENTRY s390_live_on_entry + #undef TARGET_USE_BY_PIECES_INFRASTRUCTURE_P #define TARGET_USE_BY_PIECES_INFRASTRUCTURE_P \ s390_use_by_pieces_infrastructure_p diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md index ccedead..6f0e172 100644 --- a/gcc/config/s390/s390.md +++ b/gcc/config/s390/s390.md @@ -114,6 +114,9 @@ UNSPEC_SP_SET UNSPEC_SP_TEST + ; Split stack support + UNSPEC_STACK_CHECK + ; Test Data Class (TDC) UNSPEC_TDC_INSN @@ -276,6 +279,10 @@ ; Set and get floating point control register UNSPECV_SFPC UNSPECV_EFPC + + ; Split stack support + UNSPECV_SPLIT_STACK_CALL + UNSPECV_SPLIT_STACK_DATA ]) ;; @@ -10909,3 +10916,134 @@ "TARGET_Z13" "lcbb\t%0,%1,%b2" [(set_attr "op_type" "VRX")]) + +; Handle -fsplit-stack. + +(define_expand "split_stack_prologue" + [(const_int 0)] + "" +{ + s390_expand_split_stack_prologue (); + DONE; +}) + +;; If there are operand 0 bytes available on the stack, jump to +;; operand 1. + +(define_expand "split_stack_space_check" + [(set (pc) (if_then_else + (ltu (minus (reg 15) + (match_operand 0 "register_operand")) + (unspec [(const_int 0)] UNSPEC_STACK_CHECK)) + (label_ref (match_operand 1)) + (pc)))] + "" +{ + /* Offset from thread pointer to __private_ss. */ + int psso = TARGET_64BIT ? 0x38 : 0x20; + rtx tp = s390_get_thread_pointer (); + rtx guard = gen_rtx_MEM (Pmode, plus_constant (Pmode, tp, psso)); + rtx reg = gen_reg_rtx (Pmode); + rtx cc; + if (TARGET_64BIT) + emit_insn (gen_subdi3 (reg, stack_pointer_rtx, operands[0])); + else + emit_insn (gen_subsi3 (reg, stack_pointer_rtx, operands[0])); + cc = s390_emit_compare (GT, reg, guard); + s390_emit_jump (operands[1], cc); + + DONE; +}) + +;; __morestack parameter block for split stack prologue. Parameters are: +;; parameter block label, label to be called by __morestack, frame size, +;; stack parameter size. + +(define_insn "split_stack_data" + [(unspec_volatile [(match_operand 0 "" "X") + (match_operand 1 "" "X") + (match_operand 2 "const_int_operand" "X") + (match_operand 3 "const_int_operand" "X")] + UNSPECV_SPLIT_STACK_DATA)] + "TARGET_CPU_ZARCH" +{ + switch_to_section (targetm.asm_out.function_rodata_section + (current_function_decl)); + + if (TARGET_64BIT) + output_asm_insn (".align\t8", operands); + else + output_asm_insn (".align\t4", operands); + (*targetm.asm_out.internal_label) (asm_out_file, "L", + CODE_LABEL_NUMBER (operands[0])); + if (TARGET_64BIT) + { + output_asm_insn (".quad\t%2", operands); + output_asm_insn (".quad\t%3", operands); + output_asm_insn (".quad\t%1-%0", operands); + } + else + { + output_asm_insn (".long\t%2", operands); + output_asm_insn (".long\t%3", operands); + output_asm_insn (".long\t%1-%0", operands); + } + + switch_to_section (current_function_section ()); + return ""; +} + [(set_attr "length" "0")]) + + +;; A jg with minimal fuss for use in split stack prologue. + +(define_expand "split_stack_call" + [(match_operand 0 "bras_sym_operand" "X") + (match_operand 1 "" "")] + "TARGET_CPU_ZARCH" +{ + if (TARGET_64BIT) + emit_jump_insn (gen_split_stack_call_di (operands[0], operands[1])); + else + emit_jump_insn (gen_split_stack_call_si (operands[0], operands[1])); + DONE; +}) + +(define_insn "split_stack_call_<mode>" + [(set (pc) (label_ref (match_operand 1 "" ""))) + (set (reg:P 1) (unspec_volatile [(match_operand 0 "bras_sym_operand" "X") + (reg:P 1)] + UNSPECV_SPLIT_STACK_CALL))] + "TARGET_CPU_ZARCH" + "jg\t%0" + [(set_attr "op_type" "RIL") + (set_attr "type" "branch")]) + +;; Also a conditional one. + +(define_expand "split_stack_cond_call" + [(match_operand 0 "bras_sym_operand" "X") + (match_operand 1 "" "") + (match_operand 2 "" "")] + "TARGET_CPU_ZARCH" +{ + if (TARGET_64BIT) + emit_jump_insn (gen_split_stack_cond_call_di (operands[0], operands[1], operands[2])); + else + emit_jump_insn (gen_split_stack_cond_call_si (operands[0], operands[1], operands[2])); + DONE; +}) + +(define_insn "split_stack_cond_call_<mode>" + [(set (pc) + (if_then_else + (match_operand 1 "" "") + (label_ref (match_operand 2 "" "")) + (pc))) + (set (reg:P 1) (unspec_volatile [(match_operand 0 "bras_sym_operand" "X") + (reg:P 1)] + UNSPECV_SPLIT_STACK_CALL))] + "TARGET_CPU_ZARCH" + "jg%C1\t%0" + [(set_attr "op_type" "RIL") + (set_attr "type" "branch")]) |