--- gcc/config/s390/linux.h +++ gcc/config/s390/linux.h @@ -61,7 +61,8 @@ along with GCC; see the file COPYING3. If not see /* Target specific assembler settings. */ #undef ASM_SPEC -#define ASM_SPEC "%{m31&m64}%{mesa&mzarch}%{march=*}" +#define ASM_SPEC "%{m31&m64}%{mesa&mzarch}%{march=*} \ + %{mindirect-branch*|mfunction-return*:-mzarch}" /* Target specific linker settings. */ --- gcc/config/s390/s390-protos.h +++ gcc/config/s390/s390-protos.h @@ -46,6 +46,7 @@ extern void s390_set_has_landing_pad_p (bool); extern bool s390_hard_regno_mode_ok (unsigned int, enum machine_mode); extern bool s390_hard_regno_rename_ok (unsigned int, unsigned int); extern bool s390_class_max_nregs (enum reg_class, enum machine_mode); +extern bool s390_return_addr_from_memory(void); #ifdef RTX_CODE extern int s390_extra_constraint_str (rtx, int, const char *); @@ -121,6 +122,17 @@ extern int s390_branch_condition_mask (rtx); extern int s390_compare_and_branch_condition_mask (rtx); extern void s390_asm_declare_function_name (FILE *, const char *, tree); +enum s390_indirect_branch_type + { + s390_indirect_branch_type_jump = 0, + s390_indirect_branch_type_call, + s390_indirect_branch_type_return + }; +extern void s390_indirect_branch_via_thunk (unsigned int regno, + unsigned int return_addr_regno, + rtx comparison_operator, + enum s390_indirect_branch_type type); + #endif /* RTX_CODE */ #ifdef TREE_CODE --- gcc/config/s390/s390.c +++ gcc/config/s390/s390.c @@ -393,6 +393,42 @@ struct machine_function GTY(()) bytes on a z10 (or higher) CPU. */ #define PREDICT_DISTANCE (TARGET_Z10 ? 384 : 2048) +/* Masks per jump target register indicating which thunk need to be + generated. */ +static GTY(()) int indirect_branch_prez10thunk_mask = 0; +static GTY(()) int indirect_branch_z10thunk_mask = 0; + +#define INDIRECT_BRANCH_NUM_OPTIONS 4 + +enum s390_indirect_branch_option + { + s390_opt_indirect_branch_jump = 0, + s390_opt_indirect_branch_call, + s390_opt_function_return_reg, + s390_opt_function_return_mem + }; + +enum indirect_branch s390_indirect_branch; +enum indirect_branch s390_indirect_branch_call; +enum indirect_branch s390_indirect_branch_jump; +enum indirect_branch s390_function_return; +enum indirect_branch s390_function_return_reg; +enum indirect_branch s390_function_return_mem; + +static GTY(()) int indirect_branch_table_label_no[INDIRECT_BRANCH_NUM_OPTIONS] = { 0 }; +const char *indirect_branch_table_label[INDIRECT_BRANCH_NUM_OPTIONS] = \ + { "LJUMP", "LCALL", "LRETREG", "LRETMEM" }; +const char *indirect_branch_table_name[INDIRECT_BRANCH_NUM_OPTIONS] = \ + { ".s390_indirect_jump", ".s390_indirect_call", + ".s390_return_reg", ".s390_return_mem" }; + +bool +s390_return_addr_from_memory (void) +{ + return (cfun_frame_layout.first_save_gpr <= RETURN_REGNUM + && cfun_frame_layout.last_save_gpr >= RETURN_REGNUM); +} + static const int s390_hotpatch_hw_max = 1000000; static int s390_hotpatch_hw_before_label = 0; static int s390_hotpatch_hw_after_label = 0; @@ -1705,6 +1742,39 @@ s390_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED) } } +/* Convert the string argument of -mindirect-branch*= or + -mfunction-return*= into an enum value for further processing. */ + +static void +s390_indirect_branch_str_to_enum (const char *str, enum indirect_branch *opt) +{ + size_t i; + static struct + { + const char *const name; + enum indirect_branch value; + } strtoenum[] = + { + { "keep", indirect_branch_keep }, + { "thunk", indirect_branch_thunk }, + { "thunk-extern", indirect_branch_thunk_extern } + }; + + if (!str) + { + *opt = indirect_branch_unset; + return; + } + + for (i = 0; i < ARRAY_SIZE (strtoenum); i++) + if (strcmp (strtoenum[i].name, str) == 0) + { + *opt = strtoenum[i].value; + return; + } + error ("unknown option value: %qs", str); +} + void override_options (void) { @@ -1720,6 +1790,53 @@ override_options (void) target_flags &= ~MASK_ZARCH; } + s390_indirect_branch_str_to_enum (s390_indirect_branch_string, + &s390_indirect_branch); + s390_indirect_branch_str_to_enum (s390_indirect_branch_call_string, + &s390_indirect_branch_call); + s390_indirect_branch_str_to_enum (s390_indirect_branch_jump_string, + &s390_indirect_branch_jump); + s390_indirect_branch_str_to_enum (s390_function_return_string, + &s390_function_return); + s390_indirect_branch_str_to_enum (s390_function_return_reg_string, + &s390_function_return_reg); + s390_indirect_branch_str_to_enum (s390_function_return_mem_string, + &s390_function_return_mem); + + if (s390_indirect_branch > indirect_branch_keep) + { + if (s390_indirect_branch_call == indirect_branch_unset) + s390_indirect_branch_call = s390_indirect_branch; + + if (s390_indirect_branch_jump == indirect_branch_unset) + s390_indirect_branch_jump = s390_indirect_branch; + } + else + { + if (s390_indirect_branch_call == indirect_branch_unset) + s390_indirect_branch_call = indirect_branch_keep; + + if (s390_indirect_branch_jump == indirect_branch_unset) + s390_indirect_branch_jump = indirect_branch_keep; + } + + if (s390_function_return > indirect_branch_keep) + { + if (s390_function_return_reg == indirect_branch_unset) + s390_function_return_reg = s390_function_return; + + if (s390_function_return_mem == indirect_branch_unset) + s390_function_return_mem = s390_function_return; + } + else + { + if (s390_function_return_reg == indirect_branch_unset) + s390_function_return_reg = indirect_branch_keep; + + if (s390_function_return_mem == indirect_branch_unset) + s390_function_return_mem = indirect_branch_keep; + } + /* Determine processor architectural level. */ if (!s390_arch_string) { @@ -8289,7 +8418,6 @@ s390_emit_epilogue (bool sibcall) rtx frame_pointer, return_reg, cfa_restores = NULL_RTX; int area_bottom, area_top, offset = 0; int next_offset; - rtvec p; int i; if (TARGET_TPF_PROFILING) @@ -8439,8 +8567,14 @@ s390_emit_epilogue (bool sibcall) && cfun_frame_layout.last_restore_gpr > RETURN_REGNUM)) { int return_regnum = find_unused_clobbered_reg(); - if (!return_regnum) - return_regnum = 4; + if (!return_regnum + || (TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION + && !TARGET_CPU_Z10 + && return_regnum == INDIRECT_BRANCH_THUNK_REGNUM)) + { + gcc_assert (INDIRECT_BRANCH_THUNK_REGNUM != 4); + return_regnum = 4; + } return_reg = gen_rtx_REG (Pmode, return_regnum); addr = plus_constant (frame_pointer, @@ -8469,16 +8603,7 @@ s390_emit_epilogue (bool sibcall) } if (! sibcall) - { - - /* Return to caller. */ - - p = rtvec_alloc (2); - - RTVEC_ELT (p, 0) = gen_rtx_RETURN (VOIDmode); - RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, return_reg); - emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p)); - } + emit_jump_insn (gen_return_use (return_reg)); } @@ -9596,6 +9721,84 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED, final_end_function (); } +/* Output either an indirect jump or a an indirect call + (RETURN_ADDR_REGNO != INVALID_REGNUM) with target register REGNO + using a branch trampoline disabling branch target prediction. */ + +void +s390_indirect_branch_via_thunk (unsigned int regno, + unsigned int return_addr_regno, + rtx comparison_operator, + enum s390_indirect_branch_type type) +{ + enum s390_indirect_branch_option option; + + if (type == s390_indirect_branch_type_return) + { + if (s390_function_return_reg != indirect_branch_keep + && !s390_return_addr_from_memory ()) + option = s390_opt_function_return_reg; + + if (s390_function_return_mem != indirect_branch_keep + && s390_return_addr_from_memory ()) + option = s390_opt_function_return_mem; + } + else if (type == s390_indirect_branch_type_jump) + option = s390_opt_indirect_branch_jump; + else if (type == s390_indirect_branch_type_call) + option = s390_opt_indirect_branch_call; + else + gcc_unreachable (); + + if (TARGET_INDIRECT_BRANCH_TABLE) + { + char label[32]; + + ASM_GENERATE_INTERNAL_LABEL (label, + indirect_branch_table_label[option], + indirect_branch_table_label_no[option]++); + ASM_OUTPUT_LABEL (asm_out_file, label); + } + + if (return_addr_regno != INVALID_REGNUM) + { + gcc_assert (comparison_operator == NULL_RTX); + fprintf (asm_out_file, " \tbrasl\t%%r%d,", return_addr_regno); + } + else + { + fputs (" \tjg", asm_out_file); + if (comparison_operator != NULL_RTX) + print_operand (asm_out_file, comparison_operator, 'C'); + + fputs ("\t", asm_out_file); + } + + if (TARGET_CPU_Z10) + fprintf (asm_out_file, + TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL "\n", + regno); + else + fprintf (asm_out_file, + TARGET_INDIRECT_BRANCH_THUNK_NAME_EX "\n", + INDIRECT_BRANCH_THUNK_REGNUM, regno); + + if ((option == s390_opt_indirect_branch_jump + && s390_indirect_branch_jump == indirect_branch_thunk) + || (option == s390_opt_indirect_branch_call + && s390_indirect_branch_call == indirect_branch_thunk) + || (option == s390_opt_function_return_reg + && s390_function_return_reg == indirect_branch_thunk) + || (option == s390_opt_function_return_mem + && s390_function_return_mem == indirect_branch_thunk)) + { + if (TARGET_CPU_Z10) + indirect_branch_z10thunk_mask |= (1 << regno); + else + indirect_branch_prez10thunk_mask |= (1 << regno); + } +} + static bool s390_valid_pointer_mode (enum machine_mode mode) { @@ -9674,6 +9877,14 @@ s390_function_ok_for_sibcall (tree decl, tree exp) if (!TARGET_64BIT && flag_pic && decl && !targetm.binds_local_p (decl)) return false; + /* The thunks for indirect branches require r1 if no exrl is + available. r1 might not be available when doing a sibling + call. */ + if (TARGET_INDIRECT_BRANCH_NOBP_CALL + && !TARGET_CPU_Z10 + && !decl) + return false; + /* Register 6 on s390 is available as an argument register but unfortunately "caller saved". This makes functions needing this register for arguments not suitable for sibcalls. */ @@ -9707,9 +9918,13 @@ s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg, { bool plt_call = false; rtx insn; - rtx call; - rtx clobber; - rtvec vec; + rtx vec[4] = { NULL_RTX }; + int elts = 0; + rtx *call = &vec[0]; + rtx *clobber_ret_reg = &vec[1]; + rtx *use = &vec[2]; + rtx *clobber_thunk_reg = &vec[3]; + int i; /* Direct function calls need special treatment. */ if (GET_CODE (addr_location) == SYMBOL_REF) @@ -9747,26 +9962,57 @@ s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg, addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM); } + if (TARGET_INDIRECT_BRANCH_NOBP_CALL + && GET_CODE (addr_location) != SYMBOL_REF + && !plt_call) + { + /* Indirect branch thunks require the target to be a single GPR. */ + addr_location = force_reg (Pmode, addr_location); + + /* Without exrl the indirect branch thunks need an additional + register for larl;ex */ + if (!TARGET_CPU_Z10) + { + *clobber_thunk_reg = gen_rtx_REG (Pmode, INDIRECT_BRANCH_THUNK_REGNUM); + *clobber_thunk_reg = gen_rtx_CLOBBER (VOIDmode, *clobber_thunk_reg); + } + } + addr_location = gen_rtx_MEM (QImode, addr_location); - call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx); + *call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx); if (result_reg != NULL_RTX) - call = gen_rtx_SET (VOIDmode, result_reg, call); + *call = gen_rtx_SET (VOIDmode, result_reg, *call); if (retaddr_reg != NULL_RTX) { - clobber = gen_rtx_CLOBBER (VOIDmode, retaddr_reg); + *clobber_ret_reg = gen_rtx_CLOBBER (VOIDmode, retaddr_reg); if (tls_call != NULL_RTX) - vec = gen_rtvec (3, call, clobber, - gen_rtx_USE (VOIDmode, tls_call)); - else - vec = gen_rtvec (2, call, clobber); + *use = gen_rtx_USE (VOIDmode, tls_call); + } + + for (i = 0; i < 4; i++) + if (vec[i] != NULL_RTX) + elts++; - call = gen_rtx_PARALLEL (VOIDmode, vec); + if (elts > 1) + { + rtvec v; + int e = 0; + + v = rtvec_alloc (elts); + for (i = 0; i < 4; i++) + if (vec[i] != NULL_RTX) + { + RTVEC_ELT (v, e) = vec[i]; + e++; + } + + *call = gen_rtx_PARALLEL (VOIDmode, v); } - insn = emit_call_insn (call); + insn = emit_call_insn (*call); /* 31-bit PLT stubs and tls calls use the GOT register implicitly. */ if ((!TARGET_64BIT && plt_call) || tls_call != NULL_RTX) @@ -10582,6 +10828,155 @@ s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop) } } +#ifdef HAVE_GAS_HIDDEN +# define USE_HIDDEN_LINKONCE 1 +#else +# define USE_HIDDEN_LINKONCE 0 +#endif + +/* Output an indirect branch trampoline for target register REGNO. */ + +static void +s390_output_indirect_thunk_function (unsigned int regno, bool z10_p) +{ + tree decl; + char thunk_label[32]; + + int i; + + if (z10_p) + sprintf (thunk_label, TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL, regno); + else + sprintf (thunk_label, TARGET_INDIRECT_BRANCH_THUNK_NAME_EX, + INDIRECT_BRANCH_THUNK_REGNUM, regno); + + decl = build_decl (FUNCTION_DECL, + get_identifier (thunk_label), + build_function_type_list (void_type_node, NULL_TREE)); + DECL_RESULT (decl) = build_decl (RESULT_DECL, + NULL_TREE, void_type_node); + TREE_PUBLIC (decl) = 1; + TREE_STATIC (decl) = 1; + DECL_IGNORED_P (decl) = 1; + + if (USE_HIDDEN_LINKONCE) + { + DECL_ONE_ONLY (decl) = 1; + + targetm.asm_out.unique_section (decl, 0); + switch_to_section (get_named_section (decl, NULL, 0)); + + targetm.asm_out.globalize_label (asm_out_file, thunk_label); + fputs ("\t.hidden\t", asm_out_file); + assemble_name (asm_out_file, thunk_label); + putc ('\n', asm_out_file); + ASM_DECLARE_FUNCTION_NAME (asm_out_file, thunk_label, decl); + } + else + { + switch_to_section (text_section); + ASM_OUTPUT_LABEL (asm_out_file, thunk_label); + } + + DECL_INITIAL (decl) = make_node (BLOCK); + current_function_decl = decl; + allocate_struct_function (decl, false); + init_function_start (decl); + cfun->is_thunk = true; + first_function_block_is_cold = false; + final_start_function (emit_barrier (), asm_out_file, 1); + + /* This makes CFI at least usable for indirect jumps. + + jumps: stopping in the thunk: backtrace will point to the thunk + target is if it was interrupted by a signal + + calls: Instead of caller->thunk the backtrace will be + caller->callee->thunk */ + if (flag_asynchronous_unwind_tables) + { + fputs ("\t.cfi_signal_frame\n", asm_out_file); + fprintf (asm_out_file, "\t.cfi_return_column %d\n", regno); + for (i = 0; i < FPR15_REGNUM; i++) + fprintf (asm_out_file, "\t.cfi_same_value %s\n", reg_names[i]); + } + + if (z10_p) + { + /* exrl 0,1f */ + fputs ("\texrl\t0,1f\n", asm_out_file); + } + else if (TARGET_CPU_ZARCH) + { + /* larl %r1,1f */ + fprintf (asm_out_file, "\tlarl\t%%r%d,1f\n", + INDIRECT_BRANCH_THUNK_REGNUM); + + /* ex 0,0(%r1) */ + fprintf (asm_out_file, "\tex\t0,0(%%r%d)\n", + INDIRECT_BRANCH_THUNK_REGNUM); + } + else + gcc_unreachable (); + + /* 0: j 0b */ + fputs ("0:\tj\t0b\n", asm_out_file); + + /* 1: br */ + fprintf (asm_out_file, "1:\tbr\t%%r%d\n", regno); + + final_end_function (); + init_insn_lengths (); + free_after_compilation (cfun); + set_cfun (NULL); + current_function_decl = NULL; +} + +/* Implement the asm.code_end target hook. */ + +static void +s390_code_end (void) +{ + int i; + + for (i = 1; i < 16; i++) + { + if (indirect_branch_z10thunk_mask & (1 << i)) + s390_output_indirect_thunk_function (i, true); + + if (indirect_branch_prez10thunk_mask & (1 << i)) + s390_output_indirect_thunk_function (i, false); + } + + if (TARGET_INDIRECT_BRANCH_TABLE) + { + int o; + int i; + + for (o = 0; o < INDIRECT_BRANCH_NUM_OPTIONS; o++) + { + if (indirect_branch_table_label_no[o] == 0) + continue; + + switch_to_section (get_section (indirect_branch_table_name[o], + 0, + NULL_TREE)); + for (i = 0; i < indirect_branch_table_label_no[o]; i++) + { + char label_start[32]; + + ASM_GENERATE_INTERNAL_LABEL (label_start, + indirect_branch_table_label[o], i); + + fputs ("\t.long\t", asm_out_file); + assemble_name_raw (asm_out_file, label_start); + fputs ("-.\n", asm_out_file); + } + switch_to_section (current_function_section ()); + } + } +} + /* Initialize GCC target structure. */ #undef TARGET_ASM_ALIGNED_HI_OP @@ -10712,6 +11107,9 @@ s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop) #undef TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P #define TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P hook_bool_const_tree_true +#undef TARGET_ASM_CODE_END +#define TARGET_ASM_CODE_END s390_code_end + struct gcc_target targetm = TARGET_INITIALIZER; #include "gt-s390.h" --- gcc/config/s390/s390.h +++ gcc/config/s390/s390.h @@ -1034,4 +1034,50 @@ do { \ (TARGET_LONG_DISPLACEMENT? ((d) >= -524288 && (d) <= 524287) \ : ((d) >= 0 && (d) <= 4095)) +/* Values for -mindirect-branch and -mfunction-return options. */ +enum indirect_branch { + indirect_branch_unset = 0, + indirect_branch_keep, + indirect_branch_thunk, + indirect_branch_thunk_extern +}; + +extern enum indirect_branch s390_indirect_branch; +extern enum indirect_branch s390_indirect_branch_call; +extern enum indirect_branch s390_indirect_branch_jump; +extern enum indirect_branch s390_function_return; +extern enum indirect_branch s390_function_return_reg; +extern enum indirect_branch s390_function_return_mem; + +#define TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION \ + (s390_function_return_reg != indirect_branch_keep \ + || s390_function_return_mem != indirect_branch_keep) + +#define TARGET_INDIRECT_BRANCH_NOBP_RET \ + ((s390_function_return_reg != indirect_branch_keep \ + && !s390_return_addr_from_memory ()) \ + || (s390_function_return_mem != indirect_branch_keep \ + && s390_return_addr_from_memory ())) + +#define TARGET_INDIRECT_BRANCH_NOBP_JUMP \ + (s390_indirect_branch_jump != indirect_branch_keep) + +#define TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK \ + (s390_indirect_branch_jump == indirect_branch_thunk \ + || s390_indirect_branch_jump == indirect_branch_thunk_extern) + +#define TARGET_INDIRECT_BRANCH_NOBP_CALL \ + (s390_indirect_branch_call != indirect_branch_keep) + +#ifndef TARGET_DEFAULT_INDIRECT_BRANCH_TABLE +#define TARGET_DEFAULT_INDIRECT_BRANCH_TABLE 0 +#endif + +#define TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL "__s390_indirect_jump_r%d" +#define TARGET_INDIRECT_BRANCH_THUNK_NAME_EX "__s390_indirect_jump_r%duse_r%d" + +#define TARGET_INDIRECT_BRANCH_TABLE s390_indirect_branch_table + +#define CASE_VALUES_THRESHOLD (TARGET_INDIRECT_BRANCH_NOBP_JUMP ? 20 : 4) + #endif --- gcc/config/s390/s390.md +++ gcc/config/s390/s390.md @@ -155,6 +155,8 @@ [ ; Sibling call register. (SIBCALL_REGNUM 1) + ; A call-clobbered reg which can be used in indirect branch thunks + (INDIRECT_BRANCH_THUNK_REGNUM 1) ; Literal pool base register. (BASE_REGNUM 13) ; Return address register. @@ -174,6 +176,7 @@ ; Floating point registers. (FPR0_REGNUM 16) (FPR2_REGNUM 18) + (FPR15_REGNUM 31) ]) ;; @@ -7699,7 +7702,7 @@ (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)]) (match_operand 0 "address_operand" "ZQZR") (pc)))] - "" + "!TARGET_INDIRECT_BRANCH_NOBP_JUMP" { if (get_attr_op_type (insn) == OP_TYPE_RR) return "b%C1r\t%0"; @@ -7762,7 +7765,7 @@ (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)]) (pc) (match_operand 0 "address_operand" "ZQZR")))] - "" + "!TARGET_INDIRECT_BRANCH_NOBP_JUMP" { if (get_attr_op_type (insn) == OP_TYPE_RR) return "b%D1r\t%0"; @@ -8061,29 +8064,120 @@ ; indirect-jump instruction pattern(s). ; -(define_insn "indirect_jump" - [(set (pc) (match_operand 0 "address_operand" "ZQZR"))] - "" +(define_expand "indirect_jump" + [(set (pc) (match_operand 0 "address_operand" "ZQZR"))] + "" +{ + if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK) + { + operands[0] = force_reg (Pmode, operands[0]); + if (TARGET_CPU_Z10) + { + if (TARGET_64BIT) + emit_jump_insn (gen_indirect_jump_via_thunkdi_z10 (operands[0])); + else + emit_jump_insn (gen_indirect_jump_via_thunksi_z10 (operands[0])); + } + else + { + if (TARGET_64BIT) + emit_jump_insn (gen_indirect_jump_via_thunkdi (operands[0])); + else + emit_jump_insn (gen_indirect_jump_via_thunksi (operands[0])); + } + DONE; + } +}) + +(define_insn "*indirect_jump" + [(set (pc) + (match_operand 0 "address_operand" "ZR"))] + "!TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK" { if (get_attr_op_type (insn) == OP_TYPE_RR) return "br\t%0"; else return "b\t%a0"; } - [(set (attr "op_type") - (if_then_else (match_operand 0 "register_operand" "") - (const_string "RR") (const_string "RX"))) - (set_attr "type" "branch") - (set_attr "atype" "agen")]) + [(set (attr "op_type") + (if_then_else (match_operand 0 "register_operand" "") + (const_string "RR") (const_string "RX"))) + (set_attr "type" "branch") + (set_attr "atype" "agen")]) + +(define_insn "indirect_jump_via_thunk_z10" + [(set (pc) + (match_operand:P 0 "register_operand" "a"))] + "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK + && TARGET_CPU_Z10" +{ + s390_indirect_branch_via_thunk (REGNO (operands[0]), + INVALID_REGNUM, + NULL_RTX, + s390_indirect_branch_type_jump); + return ""; +} + [(set_attr "op_type" "RIL") + (set_attr "type" "branch") + (set_attr "atype" "agen")]) + +(define_insn "indirect_jump_via_thunk" + [(set (pc) + (match_operand:P 0 "register_operand" " a")) + (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))] + "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK + && !TARGET_CPU_Z10" +{ + s390_indirect_branch_via_thunk (REGNO (operands[0]), + INVALID_REGNUM, + NULL_RTX, + s390_indirect_branch_type_jump); + return ""; +} + [(set_attr "op_type" "RIL") + (set_attr "type" "branch") + (set_attr "atype" "agen")]) ; ; casesi instruction pattern(s). ; -(define_insn "casesi_jump" - [(set (pc) (match_operand 0 "address_operand" "ZQZR")) - (use (label_ref (match_operand 1 "" "")))] - "" +(define_expand "casesi_jump" + [(parallel + [(set (pc) (match_operand 0 "address_operand")) + (use (label_ref (match_operand 1 "")))])] + "" +{ + if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK) + { + operands[0] = force_reg (GET_MODE (operands[0]), operands[0]); + + if (TARGET_CPU_Z10) + { + if (TARGET_64BIT) + emit_jump_insn (gen_casesi_jump_via_thunkdi_z10 (operands[0], + operands[1])); + else + emit_jump_insn (gen_casesi_jump_via_thunksi_z10 (operands[0], + operands[1])); + } + else + { + if (TARGET_64BIT) + emit_jump_insn (gen_casesi_jump_via_thunkdi (operands[0], + operands[1])); + else + emit_jump_insn (gen_casesi_jump_via_thunksi (operands[0], + operands[1])); + } + DONE; + } +}) + +(define_insn "*casesi_jump" + [(set (pc) (match_operand 0 "address_operand" "ZR")) + (use (label_ref (match_operand 1 "" "")))] + "!TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK" { if (get_attr_op_type (insn) == OP_TYPE_RR) return "br\t%0"; @@ -8091,11 +8185,45 @@ return "b\t%a0"; } [(set (attr "op_type") - (if_then_else (match_operand 0 "register_operand" "") - (const_string "RR") (const_string "RX"))) + (if_then_else (match_operand 0 "register_operand" "") + (const_string "RR") (const_string "RX"))) (set_attr "type" "branch") (set_attr "atype" "agen")]) +(define_insn "casesi_jump_via_thunk_z10" + [(set (pc) (match_operand:P 0 "register_operand" "a")) + (use (label_ref (match_operand 1 "" "")))] + "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK + && TARGET_CPU_Z10" +{ + s390_indirect_branch_via_thunk (REGNO (operands[0]), + INVALID_REGNUM, + NULL_RTX, + s390_indirect_branch_type_jump); + return ""; +} + [(set_attr "op_type" "RIL") + (set_attr "type" "branch") + (set_attr "atype" "agen")]) + +(define_insn "casesi_jump_via_thunk" + [(set (pc) (match_operand:P 0 "register_operand" "a")) + (use (label_ref (match_operand 1 "" ""))) + (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))] + "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK + && !TARGET_CPU_Z10" +{ + s390_indirect_branch_via_thunk (REGNO (operands[0]), + INVALID_REGNUM, + NULL_RTX, + s390_indirect_branch_type_jump); + return ""; +} + [(set_attr "op_type" "RIL") + (set_attr "type" "branch") + (set_attr "atype" "agen")]) + + (define_expand "casesi" [(match_operand:SI 0 "general_operand" "") (match_operand:SI 1 "general_operand" "") @@ -8197,14 +8325,33 @@ (define_insn "*sibcall_br" [(call (mem:QI (reg SIBCALL_REGNUM)) - (match_operand 0 "const_int_operand" "n"))] + (match_operand 0 "const_int_operand" "n"))] "SIBLING_CALL_P (insn) - && GET_MODE (XEXP (XEXP (PATTERN (insn), 0), 0)) == Pmode" + && GET_MODE (XEXP (XEXP (PATTERN (insn), 0), 0)) == Pmode + && !TARGET_INDIRECT_BRANCH_NOBP_CALL" "br\t%%r1" [(set_attr "op_type" "RR") (set_attr "type" "branch") (set_attr "atype" "agen")]) +(define_insn "*sibcall_br_nobp" + [(call (mem:QI (reg SIBCALL_REGNUM)) + (match_operand 0 "const_int_operand" "n"))] + "SIBLING_CALL_P (insn) + && GET_MODE (XEXP (XEXP (PATTERN (insn), 0), 0)) == Pmode + && TARGET_INDIRECT_BRANCH_NOBP_CALL" +{ + gcc_assert (TARGET_CPU_Z10); + s390_indirect_branch_via_thunk (SIBCALL_REGNUM, + INVALID_REGNUM, + NULL_RTX, + s390_indirect_branch_type_call); + return ""; +} + [(set_attr "op_type" "RIL") + (set_attr "type" "branch") + (set_attr "atype" "agen")]) + (define_insn "*sibcall_brc" [(call (mem:QI (match_operand 0 "bras_sym_operand" "X")) (match_operand 1 "const_int_operand" "n"))] @@ -8240,12 +8387,32 @@ (call (mem:QI (reg SIBCALL_REGNUM)) (match_operand 1 "const_int_operand" "n")))] "SIBLING_CALL_P (insn) - && GET_MODE (XEXP (XEXP (XEXP (PATTERN (insn), 1), 0), 0)) == Pmode" - "br\t%%r1" + && GET_MODE (XEXP (XEXP (XEXP (PATTERN (insn), 1), 0), 0)) == Pmode + && !TARGET_INDIRECT_BRANCH_NOBP_CALL" + "br\t%%r1"; [(set_attr "op_type" "RR") (set_attr "type" "branch") (set_attr "atype" "agen")]) +(define_insn "*sibcall_value_br_nobp" + [(set (match_operand 0 "" "") + (call (mem:QI (reg SIBCALL_REGNUM)) + (match_operand 1 "const_int_operand" "n")))] + "SIBLING_CALL_P (insn) + && GET_MODE (XEXP (XEXP (XEXP (PATTERN (insn), 1), 0), 0)) == Pmode + && TARGET_INDIRECT_BRANCH_NOBP_CALL" +{ + gcc_assert (TARGET_CPU_Z10); + s390_indirect_branch_via_thunk (SIBCALL_REGNUM, + INVALID_REGNUM, + NULL_RTX, + s390_indirect_branch_type_call); + return ""; +} + [(set_attr "op_type" "RIL") + (set_attr "type" "branch") + (set_attr "atype" "agen")]) + (define_insn "*sibcall_value_brc" [(set (match_operand 0 "" "") (call (mem:QI (match_operand 1 "bras_sym_operand" "X")) @@ -8308,7 +8475,9 @@ [(call (mem:QI (match_operand 0 "address_operand" "ZQZR")) (match_operand 1 "const_int_operand" "n")) (clobber (match_operand 2 "register_operand" "=r"))] - "!SIBLING_CALL_P (insn) && GET_MODE (operands[2]) == Pmode" + "!TARGET_INDIRECT_BRANCH_NOBP_CALL + && !SIBLING_CALL_P (insn) + && GET_MODE (operands[2]) == Pmode" { if (get_attr_op_type (insn) == OP_TYPE_RR) return "basr\t%2,%0"; @@ -8322,6 +8491,45 @@ (set_attr "atype" "agen") (set_attr "z196prop" "z196_cracked")]) +(define_insn "*basr_via_thunk_z10" + [(call (mem:QI (match_operand:P 0 "register_operand" "a")) + (match_operand 1 "const_int_operand" "n")) + (clobber (match_operand:P 2 "register_operand" "=&r"))] + "TARGET_INDIRECT_BRANCH_NOBP_CALL + && TARGET_CPU_Z10 + && !SIBLING_CALL_P (insn)" +{ + s390_indirect_branch_via_thunk (REGNO (operands[0]), + REGNO (operands[2]), + NULL_RTX, + s390_indirect_branch_type_call); + return ""; +} + [(set_attr "op_type" "RIL") + (set_attr "type" "jsr") + (set_attr "atype" "agen") + (set_attr "z196prop" "z196_cracked")]) + +(define_insn "*basr_via_thunk" + [(call (mem:QI (match_operand:P 0 "register_operand" "a")) + (match_operand 1 "const_int_operand" "n")) + (clobber (match_operand:P 2 "register_operand" "=&r")) + (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))] + "TARGET_INDIRECT_BRANCH_NOBP_CALL + && !TARGET_CPU_Z10 + && !SIBLING_CALL_P (insn)" +{ + s390_indirect_branch_via_thunk (REGNO (operands[0]), + REGNO (operands[2]), + NULL_RTX, + s390_indirect_branch_type_call); + return ""; +} + [(set_attr "op_type" "RIL") + (set_attr "type" "jsr") + (set_attr "atype" "agen") + (set_attr "z196prop" "z196_cracked")]) + ; ; call_value instruction pattern(s). ; @@ -8369,7 +8577,10 @@ (call (mem:QI (match_operand 1 "address_operand" "ZQZR")) (match_operand 2 "const_int_operand" "n"))) (clobber (match_operand 3 "register_operand" "=r"))] - "!SIBLING_CALL_P (insn) && GET_MODE (operands[3]) == Pmode" + "!TARGET_INDIRECT_BRANCH_NOBP_CALL + && !SIBLING_CALL_P (insn) + && GET_MODE (operands[3]) == Pmode" + { if (get_attr_op_type (insn) == OP_TYPE_RR) return "basr\t%3,%1"; @@ -8487,6 +8698,49 @@ (set_attr "atype" "agen") (set_attr "z196prop" "z196_cracked")]) +(define_insn "*basr_r_via_thunk_z10" + [(set (match_operand 0 "" "") + (call (mem:QI (match_operand 1 "register_operand" "a")) + (match_operand 2 "const_int_operand" "n"))) + (clobber (match_operand 3 "register_operand" "=&r"))] + "TARGET_INDIRECT_BRANCH_NOBP_CALL + && TARGET_CPU_Z10 + && !SIBLING_CALL_P (insn) + && GET_MODE (operands[3]) == Pmode" +{ + s390_indirect_branch_via_thunk (REGNO (operands[1]), + REGNO (operands[3]), + NULL_RTX, + s390_indirect_branch_type_call); + return ""; +} + [(set_attr "op_type" "RIL") + (set_attr "type" "jsr") + (set_attr "atype" "agen") + (set_attr "z196prop" "z196_cracked")]) + +(define_insn "*basr_r_via_thunk" + [(set (match_operand 0 "" "") + (call (mem:QI (match_operand 1 "register_operand" "a")) + (match_operand 2 "const_int_operand" "n"))) + (clobber (match_operand 3 "register_operand" "=&r")) + (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))] + "TARGET_INDIRECT_BRANCH_NOBP_CALL + && !TARGET_CPU_Z10 + && !SIBLING_CALL_P (insn) + && GET_MODE (operands[3]) == Pmode" +{ + s390_indirect_branch_via_thunk (REGNO (operands[1]), + REGNO (operands[3]), + NULL_RTX, + s390_indirect_branch_type_call); + return ""; +} + [(set_attr "op_type" "RIL") + (set_attr "type" "jsr") + (set_attr "atype" "agen") + (set_attr "z196prop" "z196_cracked")]) + ;; ;;- Atomic operations ;; @@ -8959,12 +9213,63 @@ "" "s390_emit_epilogue (true); DONE;") -(define_insn "*return" +(define_expand "return_use" + [(parallel + [(return) + (use (match_operand 0 "register_operand" "a"))])] + "" +{ + if (!TARGET_CPU_Z10 + && TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION) + { + if (TARGET_64BIT) + emit_jump_insn (gen_returndi_prez10 (operands[0])); + else + emit_jump_insn (gen_returnsi_prez10 (operands[0])); + DONE; + } +}) + +(define_insn "*return" [(return) - (use (match_operand 0 "register_operand" "a"))] - "GET_MODE (operands[0]) == Pmode" - "br\t%0" - [(set_attr "op_type" "RR") + (use (match_operand:P 0 "register_operand" "a"))] + "TARGET_CPU_Z10 || !TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION" +{ + if (TARGET_INDIRECT_BRANCH_NOBP_RET) + { + s390_indirect_branch_via_thunk (REGNO (operands[0]), + INVALID_REGNUM, + NULL_RTX, + s390_indirect_branch_type_return); + return ""; + } + else + return "br\t%0"; +} + [(set_attr "op_type" "NN") + (set_attr "length" "6") + (set_attr "type" "jsr") + (set_attr "atype" "agen")]) + +(define_insn "return_prez10" + [(return) + (use (match_operand:P 0 "register_operand" "a")) + (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))] + "!TARGET_CPU_Z10 && TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION" +{ + if (TARGET_INDIRECT_BRANCH_NOBP_RET) + { + s390_indirect_branch_via_thunk (REGNO (operands[0]), + INVALID_REGNUM, + NULL_RTX, + s390_indirect_branch_type_return); + return ""; + } + else + return "br\t%0"; +} + [(set_attr "op_type" "NN") + (set_attr "length" "6") (set_attr "type" "jsr") (set_attr "atype" "agen")]) --- gcc/config/s390/s390.opt +++ gcc/config/s390/s390.opt @@ -111,3 +111,50 @@ Warn if a single function's framesize exceeds the given framesize mzarch Target Report RejectNegative Negative(mesa) Mask(ZARCH) z/Architecture + + +mindirect-branch= +Target Report RejectNegative Joined Var(s390_indirect_branch_string) +Wrap all indirect branches into execute in order to disable branch +prediction. Possible values are keep, thunk, and thunk-extern. + +mindirect-branch-jump= +Target Report RejectNegative Joined Var(s390_indirect_branch_jump_string) +Wrap indirect table jumps and computed gotos into execute in order to +disable branch prediction. Using thunk or thunk-extern with this +option requires the thunks to be considered signal handlers to order to +generate correct CFI. Possible values are keep, thunk, and thunk-extern. + +mindirect-branch-call= +Target Report RejectNegative Joined Var(s390_indirect_branch_call_string) +Wrap all indirect calls into execute in order to disable branch prediction. +Possible values are keep, thunk, and thunk-extern. + +mfunction-return= +Target Report RejectNegative Joined Var(s390_function_return_string) +Wrap all indirect return branches into execute in order to disable branch +prediction. Possible values are keep, thunk, and thunk-extern. + +mfunction-return-mem= +Target Report RejectNegative Joined Var(s390_function_return_mem_string) +Wrap indirect return branches into execute in order to disable branch +prediction. This affects only branches where the return address is +going to be restored from memory. Possible values are keep, thunk, +and thunk-extern. + +mfunction-return-reg= +Target Report RejectNegative Joined Var(s390_function_return_reg_string) +Wrap indirect return branches into execute in order to disable branch +prediction. This affects only branches where the return address +doesn't need to be restored from memory. Possible values are keep, +thunk, and thunk-extern. + +mindirect-branch-table +Target Report Var(s390_indirect_branch_table) Init(TARGET_DEFAULT_INDIRECT_BRANCH_TABLE) +Generate sections .s390_indirect_jump, .s390_indirect_call, +.s390_return_reg, and .s390_return_mem to contain the indirect branch +locations which have been patched as part of using one of the +-mindirect-branch* or -mfunction-return* options. The sections +consist of an array of 32 bit elements. Each entry holds the offset +from the entry to the patched location. Possible values are keep, +thunk, and thunk-extern. --- gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c +++ gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c @@ -0,0 +1,59 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-call=thunk-extern -mindirect-branch-table" } */ + +int gl; + +void __attribute__((noinline)) +foo (int a) +{ + gl = a + 40; +} + +int __attribute__((noinline)) +foo_value (int a) +{ + return a + 40; +} + +void* __attribute__((noinline)) +get_fptr (int a) +{ + switch (a) + { + case 0: return &foo; break; + case 1: return &foo_value; break; + default: __builtin_abort (); + } +} + +void (*f) (int); +int (*g) (int); + +int +main () +{ + int res; + + f = get_fptr(0); + f (2); + if (gl != 42) + __builtin_abort (); + + g = get_fptr(1); + if (g (2) != 42) + __builtin_abort (); + + return 0; +} + +/* 2 x main +/* { dg-final { scan-assembler-times "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */ + +/* No thunks due to thunk-extern. */ +/* { dg-final { scan-assembler-not "exrl" } } */ +/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */ + +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */ +/* { dg-final { scan-assembler "section\t.s390_indirect_call" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */ --- gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c +++ gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c @@ -0,0 +1,56 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-call=thunk -mindirect-branch-table" } */ + +int gl; + +void __attribute__((noinline)) +foo (int a) +{ + gl = a + 40; +} + +int __attribute__((noinline)) +foo_value (int a) +{ + return a + 40; +} + +void* __attribute__((noinline)) +get_fptr (int a) +{ + switch (a) + { + case 0: return &foo; break; + case 1: return &foo_value; break; + default: __builtin_abort (); + } +} + +void (*f) (int); +int (*g) (int); + +int +main () +{ + int res; + + f = get_fptr(0); + f (2); + if (gl != 42) + __builtin_abort (); + + g = get_fptr(1); + if (g (2) != 42) + __builtin_abort (); + + return 0; +} + +/* 2 x main +/* { dg-final { scan-assembler-times "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */ +/* { dg-final { scan-assembler "exrl" } } */ + +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */ +/* { dg-final { scan-assembler "section\t.s390_indirect_call" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */ --- gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c +++ gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c @@ -0,0 +1,56 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z900 --save-temps -mindirect-branch-call=thunk -mindirect-branch-table" } */ + +int gl; + +void __attribute__((noinline)) +foo (int a) +{ + gl = a + 40; +} + +int __attribute__((noinline)) +foo_value (int a) +{ + return a + 40; +} + +void* __attribute__((noinline)) +get_fptr (int a) +{ + switch (a) + { + case 0: return &foo; break; + case 1: return &foo_value; break; + default: __builtin_abort (); + } +} + +void (*f) (int); +int (*g) (int); + +int +main () +{ + int res; + + f = get_fptr(0); + f (2); + if (gl != 42) + __builtin_abort (); + + g = get_fptr(1); + if (g (2) != 42) + __builtin_abort (); + + return 0; +} + +/* 2 x main +/* { dg-final { scan-assembler-times "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */ +/* { dg-final { scan-assembler "ex\t" } } */ + +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */ +/* { dg-final { scan-assembler "section\t.s390_indirect_call" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */ --- gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c +++ gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c @@ -0,0 +1,45 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk-extern -mindirect-branch-table" } */ + +/* This is a copy of the gcc.c-torture/execute/20040302-1.c + testcase. */ + +int code[]={0,0,0,0,1}; + +void +foo(int x) { + volatile int b; + b = 0xffffffff; +} + +void +bar(int *pc) { + static const void *l[] = {&&lab0, &&end}; + + foo(0); + goto *l[*pc]; + lab0: + foo(0); + pc++; + goto *l[*pc]; + end: + return; +} + +int +main() { + bar(code); + return 0; +} + +/* The 2 gotos in bar get merged into one */ +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */ + +/* No thunks due to thunk-extern. */ +/* { dg-final { scan-assembler-not "exrl" } } */ +/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */ + +/* { dg-final { scan-assembler "section\t.s390_indirect_jump" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */ --- gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c +++ gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c @@ -0,0 +1,42 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */ + +/* This is a copy of the gcc.c-torture/execute/20040302-1.c + testcase. */ + +int code[]={0,0,0,0,1}; + +void +foo(int x) { + volatile int b; + b = 0xffffffff; +} + +void +bar(int *pc) { + static const void *l[] = {&&lab0, &&end}; + + foo(0); + goto *l[*pc]; + lab0: + foo(0); + pc++; + goto *l[*pc]; + end: + return; +} + +int +main() { + bar(code); + return 0; +} + +/* The 2 gotos in bar get merged into one */ +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */ +/* { dg-final { scan-assembler "exrl" } } */ + +/* { dg-final { scan-assembler "section\t.s390_indirect_jump" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */ --- gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c +++ gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c @@ -0,0 +1,42 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z900 --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */ + +/* This is a copy of the gcc.c-torture/execute/20040302-1.c + testcase. */ + +int code[]={0,0,0,0,1}; + +void +foo(int x) { + volatile int b; + b = 0xffffffff; +} + +void +bar(int *pc) { + static const void *l[] = {&&lab0, &&end}; + + foo(0); + goto *l[*pc]; + lab0: + foo(0); + pc++; + goto *l[*pc]; + end: + return; +} + +int +main() { + bar(code); + return 0; +} + +/* The 2 gotos in bar get merged into one */ +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */ +/* { dg-final { scan-assembler "ex\t" } } */ + +/* { dg-final { scan-assembler "section\t.s390_indirect_jump" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */ --- gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c +++ gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c @@ -0,0 +1,44 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mfunction-return-mem=thunk-extern -mindirect-branch-table" } */ + +int gl = 0; + +int __attribute__((noinline)) +bar (int a) +{ + return a + 2; +} + +void __attribute__((noinline)) +foo (int a) +{ + int i; + + if (a == 42) + return; + + for (i = 0; i < a; i++) + gl += bar (i); +} + +int +main () +{ + foo (3); + if (gl != 9) + __builtin_abort (); + + return 0; +} + +/* 1 x foo, 1 x main +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */ + +/* No thunks due to thunk-extern. */ +/* { dg-final { scan-assembler-not "exrl" } } */ +/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */ + +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */ +/* { dg-final { scan-assembler "section\t.s390_return_mem" } } */ --- gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c +++ gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c @@ -0,0 +1,41 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mfunction-return-mem=thunk -mindirect-branch-table" } */ + +int gl = 0; + +int __attribute__((noinline)) +bar (int a) +{ + return a + 2; +} + +void __attribute__((noinline)) +foo (int a) +{ + int i; + + if (a == 42) + return; + + for (i = 0; i < a; i++) + gl += bar (i); +} + +int +main () +{ + foo (3); + if (gl != 9) + __builtin_abort (); + + return 0; +} + +/* 1 x foo, 1 x main +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */ +/* { dg-final { scan-assembler "exrl" } } */ + +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */ +/* { dg-final { scan-assembler "section\t.s390_return_mem" } } */ --- gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c +++ gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c @@ -0,0 +1,42 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z900 --save-temps -mfunction-return-mem=thunk -mindirect-branch-table" } */ + +int gl = 0; + +int __attribute__((noinline)) +bar (int a) +{ + return a + 2; +} + +void __attribute__((noinline)) +foo (int a) +{ + int i; + + if (a == 42) + return; + + for (i = 0; i < a; i++) + gl += bar (i); +} + +int +main () +{ + foo (3); + if (gl != 9) + __builtin_abort (); + + return 0; +} + +/* 1 x foo, 1 x main +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */ + +/* { dg-final { scan-assembler "ex\t" } } */ + +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */ +/* { dg-final { scan-assembler "section\t.s390_return_mem" } } */ --- gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c +++ gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c @@ -0,0 +1,44 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -march=z10 --save-temps -mfunction-return-reg=thunk-extern -mindirect-branch-table" } */ + +int gl = 0; + +int __attribute__((noinline)) +bar (int a) +{ + return a + 2; +} + +void __attribute__((noinline)) +foo (int a) +{ + int i; + + if (a == 42) + return; + + for (i = 0; i < a; i++) + gl += bar (i); +} + +int +main () +{ + foo (3); + if (gl != 9) + __builtin_abort (); + + return 0; +} + +/* 1 x bar +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */ + +/* No thunks due to thunk-extern. */ +/* { dg-final { scan-assembler-not "exrl" } } */ +/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */ + +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */ +/* { dg-final { scan-assembler "section\t.s390_return_reg" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */ --- gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c +++ gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c @@ -0,0 +1,41 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z10 --save-temps -mfunction-return-reg=thunk -mindirect-branch-table" } */ + +int gl = 0; + +int __attribute__((noinline)) +bar (int a) +{ + return a + 2; +} + +void __attribute__((noinline)) +foo (int a) +{ + int i; + + if (a == 42) + return; + + for (i = 0; i < a; i++) + gl += bar (i); +} + +int +main () +{ + foo (3); + if (gl != 9) + __builtin_abort (); + + return 0; +} + +/* 1 x bar +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */ +/* { dg-final { scan-assembler "exrl" } } */ + +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */ +/* { dg-final { scan-assembler "section\t.s390_return_reg" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */ --- gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c +++ gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c @@ -0,0 +1,41 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z900 --save-temps -mfunction-return-reg=thunk -mindirect-branch-table" } */ + +int gl = 0; + +int __attribute__((noinline)) +bar (int a) +{ + return a + 2; +} + +void __attribute__((noinline)) +foo (int a) +{ + int i; + + if (a == 42) + return; + + for (i = 0; i < a; i++) + gl += bar (i); +} + +int +main () +{ + foo (3); + if (gl != 9) + __builtin_abort (); + + return 0; +} + +/* 1 x bar +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */ +/* { dg-final { scan-assembler "ex\t" } } */ + +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */ +/* { dg-final { scan-assembler "section\t.s390_return_reg" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */ --- gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c +++ gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c @@ -0,0 +1,77 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */ +/* case-values-threshold will be set to 20 by the back-end when jump + thunk are requested. */ + +int __attribute__((noinline)) foo1 (void) { return 1; } +int __attribute__((noinline)) foo2 (void) { return 2; } +int __attribute__((noinline)) foo3 (void) { return 3; } +int __attribute__((noinline)) foo4 (void) { return 4; } +int __attribute__((noinline)) foo5 (void) { return 5; } +int __attribute__((noinline)) foo6 (void) { return 6; } +int __attribute__((noinline)) foo7 (void) { return 7; } +int __attribute__((noinline)) foo8 (void) { return 8; } +int __attribute__((noinline)) foo9 (void) { return 9; } +int __attribute__((noinline)) foo10 (void) { return 10; } +int __attribute__((noinline)) foo11 (void) { return 11; } +int __attribute__((noinline)) foo12 (void) { return 12; } +int __attribute__((noinline)) foo13 (void) { return 13; } +int __attribute__((noinline)) foo14 (void) { return 14; } +int __attribute__((noinline)) foo15 (void) { return 15; } +int __attribute__((noinline)) foo16 (void) { return 16; } +int __attribute__((noinline)) foo17 (void) { return 17; } +int __attribute__((noinline)) foo18 (void) { return 18; } +int __attribute__((noinline)) foo19 (void) { return 19; } +int __attribute__((noinline)) foo20 (void) { return 20; } + + +int __attribute__((noinline)) +bar (int a) +{ + int ret = 0; + + switch (a) + { + case 1: ret = foo1 (); break; + case 2: ret = foo2 (); break; + case 3: ret = foo3 (); break; + case 4: ret = foo4 (); break; + case 5: ret = foo5 (); break; + case 6: ret = foo6 (); break; + case 7: ret = foo7 (); break; + case 8: ret = foo8 (); break; + case 9: ret = foo9 (); break; + case 10: ret = foo10 (); break; + case 11: ret = foo11 (); break; + case 12: ret = foo12 (); break; + case 13: ret = foo13 (); break; + case 14: ret = foo14 (); break; + case 15: ret = foo15 (); break; + case 16: ret = foo16 (); break; + case 17: ret = foo17 (); break; + case 18: ret = foo18 (); break; + case 19: ret = foo19 (); break; + case 20: ret = foo20 (); break; + default: + __builtin_abort (); + } + + return ret; +} + +int +main () +{ + if (bar (3) != 3) + __builtin_abort (); + + return 0; +} + +/* 1 x bar +/* { dg-final { scan-assembler-times "exrl" 1 } } */ + +/* { dg-final { scan-assembler "section\t.s390_indirect_jump" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_fromreg" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_frommem" } } */ --- gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c +++ gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c @@ -0,0 +1,78 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z900 -mzarch --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */ + +/* case-values-threshold will be set to 20 by the back-end when jump + thunk are requested. */ + +int __attribute__((noinline)) foo1 (void) { return 1; } +int __attribute__((noinline)) foo2 (void) { return 2; } +int __attribute__((noinline)) foo3 (void) { return 3; } +int __attribute__((noinline)) foo4 (void) { return 4; } +int __attribute__((noinline)) foo5 (void) { return 5; } +int __attribute__((noinline)) foo6 (void) { return 6; } +int __attribute__((noinline)) foo7 (void) { return 7; } +int __attribute__((noinline)) foo8 (void) { return 8; } +int __attribute__((noinline)) foo9 (void) { return 9; } +int __attribute__((noinline)) foo10 (void) { return 10; } +int __attribute__((noinline)) foo11 (void) { return 11; } +int __attribute__((noinline)) foo12 (void) { return 12; } +int __attribute__((noinline)) foo13 (void) { return 13; } +int __attribute__((noinline)) foo14 (void) { return 14; } +int __attribute__((noinline)) foo15 (void) { return 15; } +int __attribute__((noinline)) foo16 (void) { return 16; } +int __attribute__((noinline)) foo17 (void) { return 17; } +int __attribute__((noinline)) foo18 (void) { return 18; } +int __attribute__((noinline)) foo19 (void) { return 19; } +int __attribute__((noinline)) foo20 (void) { return 20; } + + +int __attribute__((noinline)) +bar (int a) +{ + int ret = 0; + + switch (a) + { + case 1: ret = foo1 (); break; + case 2: ret = foo2 (); break; + case 3: ret = foo3 (); break; + case 4: ret = foo4 (); break; + case 5: ret = foo5 (); break; + case 6: ret = foo6 (); break; + case 7: ret = foo7 (); break; + case 8: ret = foo8 (); break; + case 9: ret = foo9 (); break; + case 10: ret = foo10 (); break; + case 11: ret = foo11 (); break; + case 12: ret = foo12 (); break; + case 13: ret = foo13 (); break; + case 14: ret = foo14 (); break; + case 15: ret = foo15 (); break; + case 16: ret = foo16 (); break; + case 17: ret = foo17 (); break; + case 18: ret = foo18 (); break; + case 19: ret = foo19 (); break; + case 20: ret = foo20 (); break; + default: + __builtin_abort (); + } + + return ret; +} + +int +main () +{ + if (bar (3) != 3) + __builtin_abort (); + + return 0; +} + +/* 1 x bar +/* { dg-final { scan-assembler-times "ex\t" 1 } } */ + +/* { dg-final { scan-assembler "section\t.s390_indirect_jump" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_fromreg" } } */ +/* { dg-final { scan-assembler-not "section\t.s390_return_frommem" } } */