diff -Nrup gcc/config/s390/s390.c gcc/config/s390/s390.c --- gcc/config/s390/s390.c 2018-03-27 09:33:20.158140823 -0600 +++ gcc/config/s390/s390.c 2018-03-27 09:33:58.826861609 -0600 @@ -958,6 +958,35 @@ s390_expand_builtin (tree exp, rtx targe } +/* 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 + }; + +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 () +{ + 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; @@ -2669,6 +2698,34 @@ s390_option_override (void) if (TARGET_64BIT && !TARGET_ZARCH) error ("64-bit ABI not supported in ESA/390 mode"); + if (s390_indirect_branch != indirect_branch_keep) + { + if (!global_options_set.x_s390_indirect_branch_call) + s390_indirect_branch_call = s390_indirect_branch; + + if (!global_options_set.x_s390_indirect_branch_jump) + s390_indirect_branch_jump = s390_indirect_branch; + } + + if (s390_function_return != indirect_branch_keep) + { + if (!global_options_set.x_s390_function_return_reg) + s390_function_return_reg = s390_function_return; + + if (!global_options_set.x_s390_function_return_mem) + s390_function_return_mem = s390_function_return; + } + + if (!TARGET_CPU_ZARCH) + { + if (s390_indirect_branch_call != indirect_branch_keep + || s390_indirect_branch_jump != indirect_branch_keep) + error ("-mindirect-branch* options require -march=z900 or higher"); + if (s390_function_return_reg != indirect_branch_keep + || s390_function_return_mem != indirect_branch_keep) + error ("-mfunction-return* options require -march=z900 or higher"); + } + /* Use hardware DFP if available and not explicitly disabled by user. E.g. with -m31 -march=z10 -mzarch */ if (!(target_flags_explicit & MASK_HARD_DFP) && TARGET_DFP) @@ -10873,7 +10930,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) @@ -11023,8 +11079,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 (Pmode, frame_pointer, @@ -11054,16 +11116,7 @@ s390_emit_epilogue (bool sibcall) } if (! sibcall) - { - - /* Return to caller. */ - - p = rtvec_alloc (2); - - RTVEC_ELT (p, 0) = ret_rtx; - 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)); } @@ -12371,6 +12424,84 @@ s390_output_mi_thunk (FILE *file, tree t 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) { @@ -12476,6 +12607,14 @@ s390_function_ok_for_sibcall (tree decl, 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. */ @@ -12509,9 +12648,13 @@ s390_emit_call (rtx addr_location, rtx t { 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) @@ -12520,7 +12663,7 @@ s390_emit_call (rtx addr_location, rtx t replace the symbol itself with the PLT stub. */ if (flag_pic && !SYMBOL_REF_LOCAL_P (addr_location)) { - if (retaddr_reg != NULL_RTX) + if (TARGET_64BIT || retaddr_reg != NULL_RTX) { addr_location = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr_location), @@ -12563,26 +12706,57 @@ s390_emit_call (rtx addr_location, rtx t 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) @@ -13819,6 +13993,190 @@ s390_asm_file_end (void) file_end_indicate_exec_stack (); } +#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 (BUILTINS_LOCATION, FUNCTION_DECL, + get_identifier (thunk_label), + build_function_type_list (void_type_node, NULL_TREE)); + DECL_RESULT (decl) = build_decl (BUILTINS_LOCATION, 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_COMDAT_GROUP (decl) = DECL_ASSEMBLER_NAME (decl); + + 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 */ + + /* We generate a thunk for z10 compiled code although z10 is + currently not enabled. Tell the assembler to accept the + instruction. */ + if (!TARGET_CPU_Z10) + { + fputs ("\t.machine push\n", asm_out_file); + fputs ("\t.machine z10\n", asm_out_file); + } + /* We use exrl even if -mzarch hasn't been specified on the + command line so we have to tell the assembler to accept + it. */ + if (!TARGET_ZARCH) + fputs ("\t.machinemode zarch\n", asm_out_file); + + fputs ("\texrl\t0,1f\n", asm_out_file); + + if (!TARGET_ZARCH) + fputs ("\t.machinemode esa\n", asm_out_file); + + if (!TARGET_CPU_Z10) + fputs ("\t.machine pop\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 ()); + } + } +} + +/* Implement the TARGET_CASE_VALUES_THRESHOLD target hook. */ + +unsigned int +s390_case_values_threshold (void) +{ + /* Disabling branch prediction for indirect jumps makes jump table + much more expensive. */ + if (TARGET_INDIRECT_BRANCH_NOBP_JUMP) + return 20; + + return default_case_values_threshold (); +} + + /* Initialize GCC target structure. */ #undef TARGET_ASM_ALIGNED_HI_OP @@ -14015,6 +14373,12 @@ s390_asm_file_end (void) #undef TARGET_ASM_FILE_END #define TARGET_ASM_FILE_END s390_asm_file_end +#undef TARGET_ASM_CODE_END +#define TARGET_ASM_CODE_END s390_code_end + +#undef TARGET_CASE_VALUES_THRESHOLD +#define TARGET_CASE_VALUES_THRESHOLD s390_case_values_threshold + struct gcc_target targetm = TARGET_INITIALIZER; #include "gt-s390.h" diff -Nrup gcc/config/s390/s390.h gcc/config/s390/s390.h --- gcc/config/s390/s390.h 2018-03-27 09:33:19.762143683 -0600 +++ gcc/config/s390/s390.h 2018-03-27 09:33:58.827861602 -0600 @@ -1006,4 +1006,37 @@ extern const int processor_flags_table[] s390_register_target_pragmas (); \ } while (0) + +#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_JUMP_INLINE_THUNK \ + (s390_indirect_branch_jump == indirect_branch_thunk_inline) + +#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 + #endif /* S390_H */ diff -Nrup gcc/config/s390/s390.md gcc/config/s390/s390.md --- gcc/config/s390/s390.md 2018-03-27 09:33:19.763143675 -0600 +++ gcc/config/s390/s390.md 2018-03-27 09:33:58.831861573 -0600 @@ -285,6 +285,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. @@ -304,6 +306,7 @@ ; Floating point registers. (FPR0_REGNUM 16) (FPR2_REGNUM 18) + (FPR15_REGNUM 31) (VR0_REGNUM 16) (VR16_REGNUM 38) (VR23_REGNUM 45) @@ -402,7 +405,10 @@ z196_cracked" (const_string "none")) -(define_attr "mnemonic" "bcr_flush,unknown" (const_string "unknown")) +; mnemonics which only get defined through if_then_else currently +; don't get added to the list values automatically and hence need to +; be listed here. +(define_attr "mnemonic" "b,br,bas,bc,bcr,bcr_flush,unknown" (const_string "unknown")) ;; Length in bytes. @@ -8436,7 +8442,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"; @@ -8446,6 +8452,9 @@ [(set (attr "op_type") (if_then_else (match_operand 0 "register_operand" "") (const_string "RR") (const_string "RX"))) + (set (attr "mnemonic") + (if_then_else (match_operand 0 "register_operand" "") + (const_string "bcr") (const_string "bc"))) (set_attr "type" "branch") (set_attr "atype" "agen")]) @@ -8499,7 +8508,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"; @@ -8509,6 +8518,9 @@ [(set (attr "op_type") (if_then_else (match_operand 0 "register_operand" "") (const_string "RR") (const_string "RX"))) + (set (attr "mnemonic") + (if_then_else (match_operand 0 "register_operand" "") + (const_string "bcr") (const_string "bc"))) (set_attr "type" "branch") (set_attr "atype" "agen")]) @@ -9005,29 +9017,125 @@ ; 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 "mnemonic") + (if_then_else (match_operand 0 "register_operand" "") + (const_string "br") (const_string "b"))) + (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 "mnemonic" "jg") + (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 "mnemonic" "jg") + (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"; @@ -9035,11 +9143,50 @@ return "b\t%a0"; } [(set (attr "op_type") + (if_then_else (match_operand 0 "register_operand" "") + (const_string "RR") (const_string "RX"))) + (set (attr "mnemonic") (if_then_else (match_operand 0 "register_operand" "") - (const_string "RR") (const_string "RX"))) + (const_string "br") (const_string "b"))) + (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 "mnemonic" "jg") (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 "mnemonic" "jg") + (set_attr "type" "branch") + (set_attr "atype" "agen")]) + + (define_expand "casesi" [(match_operand:SI 0 "general_operand" "") (match_operand:SI 1 "general_operand" "") @@ -9141,11 +9288,30 @@ (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" - "br\t%%r1" - [(set_attr "op_type" "RR") + && GET_MODE (XEXP (XEXP (PATTERN (insn), 0), 0)) == Pmode" +{ + if (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 ""; + } + else + return "br\t%%r1"; +} + [(set (attr "op_type") + (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL") + (const_string "RIL") + (const_string "RR"))) + (set (attr "mnemonic") + (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL") + (const_string "jg") + (const_string "br"))) (set_attr "type" "branch") (set_attr "atype" "agen")]) @@ -9185,8 +9351,27 @@ (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" - [(set_attr "op_type" "RR") +{ + if (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 ""; + } + else + return "br\t%%r1"; +} + [(set (attr "op_type") + (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL") + (const_string "RIL") + (const_string "RR"))) + (set (attr "mnemonic") + (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL") + (const_string "jg") + (const_string "br"))) (set_attr "type" "branch") (set_attr "atype" "agen")]) @@ -9252,7 +9437,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"; @@ -9262,6 +9449,50 @@ [(set (attr "op_type") (if_then_else (match_operand 0 "register_operand" "") (const_string "RR") (const_string "RX"))) + (set (attr "mnemonic") + (if_then_else (match_operand 0 "register_operand" "") + (const_string "basr") (const_string "bas"))) + (set_attr "type" "jsr") + (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 "mnemonic" "brasl") + (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 "mnemonic" "brasl") (set_attr "type" "jsr") (set_attr "atype" "agen") (set_attr "z196prop" "z196_cracked")]) @@ -9313,7 +9544,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"; @@ -9323,6 +9557,54 @@ [(set (attr "op_type") (if_then_else (match_operand 1 "register_operand" "") (const_string "RR") (const_string "RX"))) + (set (attr "mnemonic") + (if_then_else (match_operand 1 "register_operand" "") + (const_string "basr") (const_string "bas"))) + (set_attr "type" "jsr") + (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 "mnemonic" "brasl") + (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 "mnemonic" "brasl") (set_attr "type" "jsr") (set_attr "atype" "agen") (set_attr "z196prop" "z196_cracked")]) @@ -10056,15 +10338,78 @@ "" "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") + (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET") + (const_string "RIL") + (const_string "RR"))) + (set (attr "mnemonic") + (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET") + (const_string "jg") + (const_string "br"))) (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") + (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET") + (const_string "RIL") + (const_string "RR"))) + (set (attr "mnemonic") + (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET") + (const_string "jg") + (const_string "br"))) + (set_attr "type" "jsr") + (set_attr "atype" "agen")]) + ;; Instruction definition to extend a 31-bit pointer into a 64-bit ;; pointer. This is used for compatibility. diff -Nrup gcc/config/s390/s390.opt gcc/config/s390/s390.opt --- gcc/config/s390/s390.opt 2018-03-27 09:33:19.763143675 -0600 +++ gcc/config/s390/s390.opt 2018-03-27 09:33:58.832861566 -0600 @@ -175,3 +175,59 @@ Target Report Joined RejectNegative UInt Set the branch costs for conditional branch instructions. Reasonable values are small, non-negative integers. The default branch cost is 1. + +mindirect-branch= +Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_indirect_branch) Init(indirect_branch_keep) +Wrap all indirect branches into execute in order to disable branch +prediction. + +mindirect-branch-jump= +Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_indirect_branch_jump) Init(indirect_branch_keep) +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. For environments where unwinding (e.g. for +exceptions) is required please use thunk-inline instead. + +mindirect-branch-call= +Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_indirect_branch_call) Init(indirect_branch_keep) +Wrap all indirect calls into execute in order to disable branch prediction. + +mfunction-return= +Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_function_return) Init(indirect_branch_keep) +Wrap all indirect return branches into execute in order to disable branch +prediction. + +mfunction-return-mem= +Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_function_return_mem) Init(indirect_branch_keep) +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. + +mfunction-return-reg= +Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_function_return_reg) Init(indirect_branch_keep) +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. + +Enum +Name(indirect_branch) Type(enum indirect_branch) +Known indirect branch choices (for use with the -mindirect-branch=/-mfunction-return= options): + +EnumValue +Enum(indirect_branch) String(keep) Value(indirect_branch_keep) + +EnumValue +Enum(indirect_branch) String(thunk) Value(indirect_branch_thunk) + +EnumValue +Enum(indirect_branch) String(thunk-extern) Value(indirect_branch_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. diff -Nrup gcc/config/s390/s390-opts.h gcc/config/s390/s390-opts.h --- gcc/config/s390/s390-opts.h 2018-03-27 09:33:19.764143668 -0600 +++ gcc/config/s390/s390-opts.h 2018-03-27 09:33:58.821861645 -0600 @@ -39,4 +39,12 @@ enum processor_type PROCESSOR_max }; +/* 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 +}; + #endif diff -Nrup gcc/config/s390/s390-protos.h gcc/config/s390/s390-protos.h --- gcc/config/s390/s390-protos.h 2018-03-27 09:33:19.764143668 -0600 +++ gcc/config/s390/s390-protos.h 2018-03-27 09:33:58.821861645 -0600 @@ -41,6 +41,7 @@ extern void s390_set_has_landing_pad_p ( extern bool s390_hard_regno_mode_ok (unsigned int, enum machine_mode); extern bool s390_hard_regno_rename_ok (unsigned int, unsigned int); extern int s390_class_max_nregs (enum reg_class, enum machine_mode); +extern bool s390_return_addr_from_memory(void); extern int s390_cannot_change_mode_class (enum machine_mode, enum machine_mode, enum reg_class); extern bool s390_function_arg_vector (enum machine_mode, const_tree); @@ -124,6 +125,18 @@ extern int s390_compare_and_branch_condi extern bool s390_extzv_shift_ok (int, int, unsigned HOST_WIDE_INT); extern void s390_asm_output_function_label (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); +extern void s390_indirect_branch_via_inline_thunk (rtx execute_target); + #endif /* RTX_CODE */ /* s390-c.c routines */ diff -Nrup gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c --- gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c 2018-03-27 09:33:58.832861566 -0600 @@ -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,noclone)) +foo (int a) +{ + gl = a + 40; +} + +int __attribute__((noinline,noclone)) +foo_value (int a) +{ + return a + 40; +} + +void* __attribute__((noinline,noclone)) +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" } } */ diff -Nrup gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c --- gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c 2018-03-27 09:33:58.833861558 -0600 @@ -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,noclone)) +foo (int a) +{ + gl = a + 40; +} + +int __attribute__((noinline,noclone)) +foo_value (int a) +{ + return a + 40; +} + +void* __attribute__((noinline,noclone)) +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" } } */ diff -Nrup gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c --- gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c 2018-03-27 09:33:58.833861558 -0600 @@ -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,noclone)) +foo (int a) +{ + gl = a + 40; +} + +int __attribute__((noinline,noclone)) +foo_value (int a) +{ + return a + 40; +} + +void* __attribute__((noinline,noclone)) +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" } } */ diff -Nrup gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c --- gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c 2018-03-27 09:33:58.833861558 -0600 @@ -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; +} + +/* 2 x bar +/* { 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 "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" } } */ diff -Nrup gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c --- gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c 2018-03-27 09:33:58.834861551 -0600 @@ -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; +} + +/* 2x bar */ +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */ +/* { 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" } } */ diff -Nrup gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c --- gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c 2018-03-27 09:33:58.834861551 -0600 @@ -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; +} + +/* 2 x bar +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */ +/* { 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" } } */ diff -Nrup gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c --- gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c 2018-03-27 09:33:58.834861551 -0600 @@ -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,noclone)) +bar (int a) +{ + return a + 2; +} + +void __attribute__((noinline,noclone)) +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" } } */ diff -Nrup gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c --- gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c 2018-03-27 09:33:58.835861544 -0600 @@ -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,noclone)) +bar (int a) +{ + return a + 2; +} + +void __attribute__((noinline,noclone)) +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" } } */ diff -Nrup gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c --- gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c 2018-03-27 09:33:58.835861544 -0600 @@ -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,noclone)) +bar (int a) +{ + return a + 2; +} + +void __attribute__((noinline,noclone)) +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" } } */ diff -Nrup gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c --- gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c 2018-03-27 09:33:58.835861544 -0600 @@ -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,noclone)) +bar (int a) +{ + return a + 2; +} + +void __attribute__((noinline,noclone)) +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" } } */ diff -Nrup gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c --- gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c 2018-03-27 09:33:58.836861537 -0600 @@ -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,noclone)) +bar (int a) +{ + return a + 2; +} + +void __attribute__((noinline,noclone)) +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" } } */ diff -Nrup gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c --- gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c 2018-03-27 09:33:58.836861537 -0600 @@ -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,noclone)) +bar (int a) +{ + return a + 2; +} + +void __attribute__((noinline,noclone)) +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" } } */ diff -Nrup gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c --- gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c 2018-03-27 09:33:58.836861537 -0600 @@ -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,noclone)) foo1 (void) { return 1; } +int __attribute__((noinline,noclone)) foo2 (void) { return 2; } +int __attribute__((noinline,noclone)) foo3 (void) { return 3; } +int __attribute__((noinline,noclone)) foo4 (void) { return 4; } +int __attribute__((noinline,noclone)) foo5 (void) { return 5; } +int __attribute__((noinline,noclone)) foo6 (void) { return 6; } +int __attribute__((noinline,noclone)) foo7 (void) { return 7; } +int __attribute__((noinline,noclone)) foo8 (void) { return 8; } +int __attribute__((noinline,noclone)) foo9 (void) { return 9; } +int __attribute__((noinline,noclone)) foo10 (void) { return 10; } +int __attribute__((noinline,noclone)) foo11 (void) { return 11; } +int __attribute__((noinline,noclone)) foo12 (void) { return 12; } +int __attribute__((noinline,noclone)) foo13 (void) { return 13; } +int __attribute__((noinline,noclone)) foo14 (void) { return 14; } +int __attribute__((noinline,noclone)) foo15 (void) { return 15; } +int __attribute__((noinline,noclone)) foo16 (void) { return 16; } +int __attribute__((noinline,noclone)) foo17 (void) { return 17; } +int __attribute__((noinline,noclone)) foo18 (void) { return 18; } +int __attribute__((noinline,noclone)) foo19 (void) { return 19; } +int __attribute__((noinline,noclone)) foo20 (void) { return 20; } + + +int __attribute__((noinline,noclone)) +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" } } */ diff -Nrup gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c --- gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c 1969-12-31 17:00:00.000000000 -0700 +++ gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c 2018-03-27 09:33:58.837861529 -0600 @@ -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,noclone)) foo1 (void) { return 1; } +int __attribute__((noinline,noclone)) foo2 (void) { return 2; } +int __attribute__((noinline,noclone)) foo3 (void) { return 3; } +int __attribute__((noinline,noclone)) foo4 (void) { return 4; } +int __attribute__((noinline,noclone)) foo5 (void) { return 5; } +int __attribute__((noinline,noclone)) foo6 (void) { return 6; } +int __attribute__((noinline,noclone)) foo7 (void) { return 7; } +int __attribute__((noinline,noclone)) foo8 (void) { return 8; } +int __attribute__((noinline,noclone)) foo9 (void) { return 9; } +int __attribute__((noinline,noclone)) foo10 (void) { return 10; } +int __attribute__((noinline,noclone)) foo11 (void) { return 11; } +int __attribute__((noinline,noclone)) foo12 (void) { return 12; } +int __attribute__((noinline,noclone)) foo13 (void) { return 13; } +int __attribute__((noinline,noclone)) foo14 (void) { return 14; } +int __attribute__((noinline,noclone)) foo15 (void) { return 15; } +int __attribute__((noinline,noclone)) foo16 (void) { return 16; } +int __attribute__((noinline,noclone)) foo17 (void) { return 17; } +int __attribute__((noinline,noclone)) foo18 (void) { return 18; } +int __attribute__((noinline,noclone)) foo19 (void) { return 19; } +int __attribute__((noinline,noclone)) foo20 (void) { return 20; } + + +int __attribute__((noinline,noclone)) +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" } } */