From patchwork Wed Mar 7 13:11:23 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Krebbel X-Patchwork-Id: 882600 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-474382-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=linux.vnet.ibm.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="tHDEUZUN"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zxDZq3pdxz9scc for ; Thu, 8 Mar 2018 00:12:34 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:subject:date:message-id; q=dns; s=default; b=t8UlDGBg6672zIc u4uBerqZ4vTbnr52sZa2HtKopQbsry4VkVDfafQP8MZSR2CEHUlkX5w3grRZjXhs uxFKCqiPMhx07uO6UtyPA0NnjEkbHAvahxRpmBG8qs0sGbWFH5zHEqazLjqz6res p6tLXdcy71fqn2UVu5Aq+Ljkn1+I= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:subject:date:message-id; s=default; bh=jsLKLaXGzEy0ViFoI07Gj uVZse8=; b=tHDEUZUNmHoeQH4zKXRjGcno8NKrs8zyvT8m47ZY6xspOgMcHSOtF rRHBGzJCsSs4p1lNpS+dULu3RjdwQQnxrEd1K7CUqaymSdESbaT0pDQkoReQemLw O7eEU0sAu+/aLVbY9kPxA3sNiE73nJW/ZYZWUeQWk9VoHQEd25duOU= Received: (qmail 108644 invoked by alias); 7 Mar 2018 13:12:21 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 108197 invoked by uid 89); 7 Mar 2018 13:12:21 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-27.6 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_LOW, TIME_LIMIT_EXCEEDED autolearn=unavailable version=3.3.2 spammy=m31 X-HELO: mx0a-001b2d01.pphosted.com Received: from mx0b-001b2d01.pphosted.com (HELO mx0a-001b2d01.pphosted.com) (148.163.158.5) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 07 Mar 2018 13:12:11 +0000 Received: from pps.filterd (m0098420.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.22/8.16.0.22) with SMTP id w27DBPx0122685 for ; Wed, 7 Mar 2018 08:12:08 -0500 Received: from e06smtp14.uk.ibm.com (e06smtp14.uk.ibm.com [195.75.94.110]) by mx0b-001b2d01.pphosted.com with ESMTP id 2gjdx47e4b-1 (version=TLSv1.2 cipher=AES256-SHA256 bits=256 verify=NOT) for ; Wed, 07 Mar 2018 08:12:05 -0500 Received: from localhost by e06smtp14.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Wed, 7 Mar 2018 13:11:26 -0000 Received: from b06cxnps4075.portsmouth.uk.ibm.com (9.149.109.197) by e06smtp14.uk.ibm.com (192.168.101.144) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Wed, 7 Mar 2018 13:11:25 -0000 Received: from d06av25.portsmouth.uk.ibm.com (d06av25.portsmouth.uk.ibm.com [9.149.105.61]) by b06cxnps4075.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id w27DBOwU54001696 for ; Wed, 7 Mar 2018 13:11:24 GMT Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 365EF11C04C for ; Wed, 7 Mar 2018 13:04:10 +0000 (GMT) Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id E999411C050 for ; Wed, 7 Mar 2018 13:04:09 +0000 (GMT) Received: from maggie.boeblingen.de.ibm.com (unknown [9.152.212.134]) by d06av25.portsmouth.uk.ibm.com (Postfix) with ESMTPS for ; Wed, 7 Mar 2018 13:04:09 +0000 (GMT) From: Andreas Krebbel To: gcc-patches@gcc.gnu.org Subject: [Committed] GCC 6: S/390: Disable branch prediction Date: Wed, 7 Mar 2018 14:11:23 +0100 X-TM-AS-GCONF: 00 x-cbid: 18030713-0016-0000-0000-0000052DEACB X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 18030713-0017-0000-0000-0000286B05E2 Message-Id: <20180307131123.31192-1-krebbel@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2018-03-07_05:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=4 phishscore=0 bulkscore=0 spamscore=0 clxscore=1011 lowpriorityscore=0 impostorscore=0 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1709140000 definitions=main-1803070153 X-IsSubscribed: yes Bootstrapped and regression tested with z900 and z10 on s390x. gcc/ChangeLog: 2018-03-07 Andreas Krebbel Backport from mainline 2018-02-08 Andreas Krebbel * config/s390/s390-opts.h (enum indirect_branch): Define. * config/s390/s390-protos.h (s390_return_addr_from_memory) (s390_indirect_branch_via_thunk) (s390_indirect_branch_via_inline_thunk): Add function prototypes. (enum s390_indirect_branch_type): Define. * config/s390/s390.c (struct s390_frame_layout, struct machine_function): Remove. (indirect_branch_prez10thunk_mask, indirect_branch_z10thunk_mask) (indirect_branch_table_label_no, indirect_branch_table_name): Define variables. (INDIRECT_BRANCH_NUM_OPTIONS): Define macro. (enum s390_indirect_branch_option): Define. (s390_return_addr_from_memory): New function. (s390_handle_string_attribute): New function. (s390_attribute_table): Add new attribute handler. (s390_execute_label): Handle UNSPEC_EXECUTE_JUMP patterns. (s390_indirect_branch_via_thunk): New function. (s390_indirect_branch_via_inline_thunk): New function. (s390_function_ok_for_sibcall): When jumping via thunk disallow sibling call optimization for non z10 compiles. (s390_emit_call): Force indirect branch target to be a single register. Add r1 clobber for non-z10 compiles. (s390_emit_epilogue): Emit return jump via return_use expander. (s390_reorg): Handle JUMP_INSNs as execute targets. (s390_option_override_internal): Perform validity checks for the new command line options. (s390_indirect_branch_attrvalue): New function. (s390_indirect_branch_settings): New function. (s390_set_current_function): Invoke s390_indirect_branch_settings. (s390_output_indirect_thunk_function): New function. (s390_code_end): Implement target hook. (s390_case_values_threshold): Implement target hook. (TARGET_ASM_CODE_END, TARGET_CASE_VALUES_THRESHOLD): Define target macros. * config/s390/s390.h (struct s390_frame_layout) (struct machine_function): Move here from s390.c. (TARGET_INDIRECT_BRANCH_NOBP_RET) (TARGET_INDIRECT_BRANCH_NOBP_JUMP) (TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK) (TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK) (TARGET_INDIRECT_BRANCH_NOBP_CALL) (TARGET_DEFAULT_INDIRECT_BRANCH_TABLE) (TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL) (TARGET_INDIRECT_BRANCH_THUNK_NAME_EX) (TARGET_INDIRECT_BRANCH_TABLE): Define macros. * config/s390/s390.md (UNSPEC_EXECUTE_JUMP) (INDIRECT_BRANCH_THUNK_REGNUM): Define constants. (mnemonic attribute): Add values which aren't recognized automatically. ("*cjump_long", "*icjump_long", "*basr", "*basr_r"): Disable pattern for branch conversion. Fix mnemonic attribute. ("*c", "*sibcall_br", "*sibcall_value_br", "*return"): Emit indirect branch via thunk if requested. ("indirect_jump", ""): Expand patterns for branch conversion. ("*indirect_jump"): Disable for branch conversion using out of line thunks. ("indirect_jump_via_thunk_z10") ("indirect_jump_via_thunk") 2018-03-07 Andreas Krebbel Backport from mainline 2018-02-09 Andreas Krebbel PR target/PR84295 * config/s390/s390.c (s390_set_current_function): Invoke s390_indirect_branch_settings also if fndecl didn't change. gcc/testsuite/ChangeLog: 2018-03-07 Andreas Krebbel Backport from mainline 2018-02-08 Andreas Krebbel * gcc.target/s390/nobp-function-pointer-attr.c: New test. * gcc.target/s390/nobp-function-pointer-nothunk.c: New test. * gcc.target/s390/nobp-function-pointer-z10.c: New test. * gcc.target/s390/nobp-function-pointer-z900.c: New test. * gcc.target/s390/nobp-indirect-jump-attr.c: New test. * gcc.target/s390/nobp-indirect-jump-inline-attr.c: New test. * gcc.target/s390/nobp-indirect-jump-inline-z10.c: New test. * gcc.target/s390/nobp-indirect-jump-inline-z900.c: New test. * gcc.target/s390/nobp-indirect-jump-nothunk.c: New test. * gcc.target/s390/nobp-indirect-jump-z10.c: New test. * gcc.target/s390/nobp-indirect-jump-z900.c: New test. * gcc.target/s390/nobp-return-attr-all.c: New test. * gcc.target/s390/nobp-return-attr-neg.c: New test. * gcc.target/s390/nobp-return-mem-attr.c: New test. * gcc.target/s390/nobp-return-mem-nothunk.c: New test. * gcc.target/s390/nobp-return-mem-z10.c: New test. * gcc.target/s390/nobp-return-mem-z900.c: New test. * gcc.target/s390/nobp-return-reg-attr.c: New test. * gcc.target/s390/nobp-return-reg-mixed.c: New test. * gcc.target/s390/nobp-return-reg-nothunk.c: New test. * gcc.target/s390/nobp-return-reg-z10.c: New test. * gcc.target/s390/nobp-return-reg-z900.c: New test. * gcc.target/s390/nobp-table-jump-inline-z10.c: New test. * gcc.target/s390/nobp-table-jump-inline-z900.c: New test. * gcc.target/s390/nobp-table-jump-z10.c: New test. * gcc.target/s390/nobp-table-jump-z900.c: New test. 2018-03-07 Andreas Krebbel Backport from mainline 2018-02-09 Andreas Krebbel PR target/PR84295 * gcc.target/s390/pr84295.c: New test. --- gcc/config/s390/s390-opts.h | 9 + gcc/config/s390/s390-protos.h | 12 + gcc/config/s390/s390.c | 713 +++++++++++++++++---- gcc/config/s390/s390.h | 120 ++++ gcc/config/s390/s390.md | 568 +++++++++++++++- gcc/config/s390/s390.opt | 59 ++ .../gcc.target/s390/nobp-function-pointer-attr.c | 56 ++ .../s390/nobp-function-pointer-nothunk.c | 59 ++ .../gcc.target/s390/nobp-function-pointer-z10.c | 56 ++ .../gcc.target/s390/nobp-function-pointer-z900.c | 56 ++ .../gcc.target/s390/nobp-indirect-jump-attr.c | 42 ++ .../s390/nobp-indirect-jump-inline-attr.c | 42 ++ .../s390/nobp-indirect-jump-inline-z10.c | 43 ++ .../s390/nobp-indirect-jump-inline-z900.c | 43 ++ .../gcc.target/s390/nobp-indirect-jump-nothunk.c | 46 ++ .../gcc.target/s390/nobp-indirect-jump-z10.c | 43 ++ .../gcc.target/s390/nobp-indirect-jump-z900.c | 43 ++ .../gcc.target/s390/nobp-return-attr-all.c | 46 ++ .../gcc.target/s390/nobp-return-attr-neg.c | 40 ++ .../gcc.target/s390/nobp-return-mem-attr.c | 46 ++ .../gcc.target/s390/nobp-return-mem-nothunk.c | 49 ++ .../gcc.target/s390/nobp-return-mem-z10.c | 46 ++ .../gcc.target/s390/nobp-return-mem-z900.c | 48 ++ .../gcc.target/s390/nobp-return-reg-attr.c | 41 ++ .../gcc.target/s390/nobp-return-reg-mixed.c | 44 ++ .../gcc.target/s390/nobp-return-reg-nothunk.c | 44 ++ .../gcc.target/s390/nobp-return-reg-z10.c | 41 ++ .../gcc.target/s390/nobp-return-reg-z900.c | 41 ++ .../gcc.target/s390/nobp-table-jump-inline-z10.c | 78 +++ .../gcc.target/s390/nobp-table-jump-inline-z900.c | 78 +++ .../gcc.target/s390/nobp-table-jump-z10.c | 77 +++ .../gcc.target/s390/nobp-table-jump-z900.c | 78 +++ 32 files changed, 2669 insertions(+), 138 deletions(-) create mode 100644 gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c create mode 100644 gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c diff --git a/gcc/config/s390/s390-opts.h b/gcc/config/s390/s390-opts.h index 0ad87a2..063354d 100644 --- a/gcc/config/s390/s390-opts.h +++ b/gcc/config/s390/s390-opts.h @@ -42,4 +42,13 @@ 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_inline, + indirect_branch_thunk_extern +}; #endif diff --git a/gcc/config/s390/s390-protos.h b/gcc/config/s390/s390-protos.h index 2ccf0bb..236105c 100644 --- a/gcc/config/s390/s390-protos.h +++ b/gcc/config/s390/s390-protos.h @@ -53,6 +53,7 @@ extern int s390_class_max_nregs (enum reg_class, machine_mode); extern int s390_cannot_change_mode_class (machine_mode, machine_mode, enum reg_class); extern bool s390_function_arg_vector (machine_mode, const_tree); +extern bool s390_return_addr_from_memory(void); #if S390_USE_TARGET_ATTRIBUTE extern tree s390_valid_target_attribute_tree (tree args, struct gcc_options *opts, @@ -146,6 +147,17 @@ extern int s390_compare_and_branch_condition_mask (rtx); 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 --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c index ff56695..b061a68 100644 --- a/gcc/config/s390/s390.c +++ b/gcc/config/s390/s390.c @@ -372,84 +372,6 @@ struct s390_address bool literal_pool; }; -/* The following structure is embedded in the machine - specific part of struct function. */ - -struct GTY (()) s390_frame_layout -{ - /* Offset within stack frame. */ - HOST_WIDE_INT gprs_offset; - HOST_WIDE_INT f0_offset; - HOST_WIDE_INT f4_offset; - HOST_WIDE_INT f8_offset; - HOST_WIDE_INT backchain_offset; - - /* Number of first and last gpr where slots in the register - save area are reserved for. */ - int first_save_gpr_slot; - int last_save_gpr_slot; - - /* Location (FP register number) where GPRs (r0-r15) should - be saved to. - 0 - does not need to be saved at all - -1 - stack slot */ -#define SAVE_SLOT_NONE 0 -#define SAVE_SLOT_STACK -1 - signed char gpr_save_slots[16]; - - /* Number of first and last gpr to be saved, restored. */ - int first_save_gpr; - int first_restore_gpr; - int last_save_gpr; - int last_restore_gpr; - - /* Bits standing for floating point registers. Set, if the - respective register has to be saved. Starting with reg 16 (f0) - at the rightmost bit. - Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 - fpr 15 13 11 9 14 12 10 8 7 5 3 1 6 4 2 0 - reg 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 */ - unsigned int fpr_bitmap; - - /* Number of floating point registers f8-f15 which must be saved. */ - int high_fprs; - - /* Set if return address needs to be saved. - This flag is set by s390_return_addr_rtx if it could not use - the initial value of r14 and therefore depends on r14 saved - to the stack. */ - bool save_return_addr_p; - - /* Size of stack frame. */ - HOST_WIDE_INT frame_size; -}; - -/* Define the structure for the machine field in struct function. */ - -struct GTY(()) machine_function -{ - struct s390_frame_layout frame_layout; - - /* Literal pool base register. */ - rtx base_reg; - - /* True if we may need to perform branch splitting. */ - bool split_branches_pending_p; - - bool has_landing_pad_p; - - /* True if the current function may contain a tbegin clobbering - FPRs. */ - bool tbegin_p; - - /* For -fsplit-stack support: A stack local which holds a pointer to - the stack arguments for a function with a variable number of - arguments. This is set at the start of the function and is used - to initialize the overflow_arg_area field of the va_list - structure. */ - rtx split_stack_varargs_pointer; -}; - /* Few accessor macros for struct cfun->machine->s390_frame_layout. */ #define cfun_frame_layout (cfun->machine->frame_layout) @@ -490,6 +412,33 @@ struct GTY(()) machine_function 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 + }; + +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_gpr_save_slot(RETURN_REGNUM) == SAVE_SLOT_STACK; +} /* Indicate which ABI has been used for passing vector args. 0 - no vector type arguments have been passed where the ABI is relevant @@ -1124,9 +1073,83 @@ s390_handle_vectorbool_attribute (tree *node, tree name ATTRIBUTE_UNUSED, return NULL_TREE; } +/* Check syntax of function decl attributes having a string type value. */ + +static tree +s390_handle_string_attribute (tree *node, tree name ATTRIBUTE_UNUSED, + tree args ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED, + bool *no_add_attrs) +{ + tree cst; + + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qE attribute only applies to functions", + name); + *no_add_attrs = true; + } + + cst = TREE_VALUE (args); + + if (TREE_CODE (cst) != STRING_CST) + { + warning (OPT_Wattributes, + "%qE attribute requires a string constant argument", + name); + *no_add_attrs = true; + } + + if (is_attribute_p ("indirect_branch", name) + || is_attribute_p ("indirect_branch_call", name) + || is_attribute_p ("function_return", name) + || is_attribute_p ("function_return_reg", name) + || is_attribute_p ("function_return_mem", name)) + { + if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0 + && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0 + && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0) + { + warning (OPT_Wattributes, + "argument to %qE attribute is not " + "(keep|thunk|thunk-extern)", name); + *no_add_attrs = true; + } + } + + if (is_attribute_p ("indirect_branch_jump", name) + && strcmp (TREE_STRING_POINTER (cst), "keep") != 0 + && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0 + && strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0 + && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0) + { + warning (OPT_Wattributes, + "argument to %qE attribute is not " + "(keep|thunk|thunk-inline|thunk-extern)", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + static const struct attribute_spec s390_attribute_table[] = { - { "hotpatch", 2, 2, true, false, false, s390_handle_hotpatch_attribute, false }, - { "s390_vector_bool", 0, 0, false, true, false, s390_handle_vectorbool_attribute, true }, + { "hotpatch", 2, 2, true, false, false, + s390_handle_hotpatch_attribute, false }, + { "s390_vector_bool", 0, 0, false, true, false, + s390_handle_vectorbool_attribute, true }, + { "indirect_branch", 1, 1, true, false, false, + s390_handle_string_attribute, false }, + { "indirect_branch_jump", 1, 1, true, false, false, + s390_handle_string_attribute, false }, + { "indirect_branch_call", 1, 1, true, false, false, + s390_handle_string_attribute, false }, + { "function_return", 1, 1, true, false, false, + s390_handle_string_attribute, false }, + { "function_return_reg", 1, 1, true, false, false, + s390_handle_string_attribute, false }, + { "function_return_mem", 1, 1, true, false, false, + s390_handle_string_attribute, false }, + /* End element. */ { NULL, 0, 0, false, false, false, NULL, false } }; @@ -8266,11 +8289,25 @@ s390_find_constant (struct constant_pool *pool, rtx val, static rtx s390_execute_label (rtx insn) { - if (NONJUMP_INSN_P (insn) + if (INSN_P (insn) && GET_CODE (PATTERN (insn)) == PARALLEL && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == UNSPEC - && XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE) - return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2); + && (XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE + || XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE_JUMP)) + { + if (XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE) + return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2); + else + { + gcc_assert (JUMP_P (insn)); + /* For jump insns as execute target: + - There is one operand less in the parallel (the + modification register of the execute is always 0). + - The execute target label is wrapped into an + if_then_else in order to hide it from jump analysis. */ + return XEXP (XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 0), 0); + } + } return NULL_RTX; } @@ -10942,7 +10979,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) @@ -11096,10 +11132,15 @@ s390_emit_epilogue (bool sibcall) if (cfun_gpr_save_slot (RETURN_REGNUM) == SAVE_SLOT_STACK) { 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, offset + cfun_frame_layout.gprs_offset + (RETURN_REGNUM @@ -11135,16 +11176,7 @@ s390_emit_epilogue (bool sibcall) s390_restore_gprs_from_fprs (); 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)); } /* Implement TARGET_SET_UP_BY_PROLOGUE. */ @@ -12722,6 +12754,112 @@ 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_return_addr_from_memory ()) + option = s390_opt_function_return_mem; + else + option = s390_opt_function_return_reg; + } + 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 + && cfun->machine->indirect_branch_jump == indirect_branch_thunk) + || (option == s390_opt_indirect_branch_call + && cfun->machine->indirect_branch_call == indirect_branch_thunk) + || (option == s390_opt_function_return_reg + && cfun->machine->function_return_reg == indirect_branch_thunk) + || (option == s390_opt_function_return_mem + && cfun->machine->function_return_mem == indirect_branch_thunk)) + { + if (TARGET_CPU_Z10) + indirect_branch_z10thunk_mask |= (1 << regno); + else + indirect_branch_prez10thunk_mask |= (1 << regno); + } +} + +/* Output an inline thunk for indirect jumps. EXECUTE_TARGET can + either be an address register or a label pointing to the location + of the jump instruction. */ + +void +s390_indirect_branch_via_inline_thunk (rtx execute_target) +{ + if (TARGET_INDIRECT_BRANCH_TABLE) + { + char label[32]; + + ASM_GENERATE_INTERNAL_LABEL (label, + indirect_branch_table_label[s390_opt_indirect_branch_jump], + indirect_branch_table_label_no[s390_opt_indirect_branch_jump]++); + ASM_OUTPUT_LABEL (asm_out_file, label); + } + + if (!TARGET_ZARCH) + fputs ("\t.machinemode zarch\n", asm_out_file); + + if (REG_P (execute_target)) + fprintf (asm_out_file, "\tex\t%%r0,0(%%r%d)\n", REGNO (execute_target)); + else + output_asm_insn ("exrl\t%%r0,%0", &execute_target); + + if (!TARGET_ZARCH) + fputs ("\t.machinemode esa\n", asm_out_file); + + fputs ("0:\tj\t0b\n", asm_out_file); +} + static bool s390_valid_pointer_mode (machine_mode mode) { @@ -12827,6 +12965,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. */ @@ -12860,9 +13006,13 @@ s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg, { bool plt_call = false; rtx_insn *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) @@ -12914,26 +13064,58 @@ 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 (result_reg, call); + *call = gen_rtx_SET (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++; + + 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, vec); + *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) @@ -13563,7 +13745,16 @@ s390_reorg (void) target = emit_label (XEXP (label, 0)); INSN_ADDRESSES_NEW (target, -1); - target = emit_insn (s390_execute_target (insn)); + if (JUMP_P (insn)) + { + target = emit_jump_insn (s390_execute_target (insn)); + /* This is important in order to keep a table jump + pointing at the jump table label. Only this makes it + being recognized as table jump. */ + JUMP_LABEL (target) = JUMP_LABEL (insn); + } + else + target = emit_insn (s390_execute_target (insn)); INSN_ADDRESSES_NEW (target, -1); } } @@ -14213,6 +14404,42 @@ s390_option_override_internal (bool main_args_p, if (TARGET_64BIT && !TARGET_ZARCH_P (opts->x_target_flags)) error ("64-bit ABI not supported in ESA/390 mode"); + if (opts->x_s390_indirect_branch == indirect_branch_thunk_inline + || opts->x_s390_indirect_branch_call == indirect_branch_thunk_inline + || opts->x_s390_function_return == indirect_branch_thunk_inline + || opts->x_s390_function_return_reg == indirect_branch_thunk_inline + || opts->x_s390_function_return_mem == indirect_branch_thunk_inline) + error ("thunk-inline is only supported with -mindirect-branch-jump"); + + if (opts->x_s390_indirect_branch != indirect_branch_keep) + { + if (!opts_set->x_s390_indirect_branch_call) + opts->x_s390_indirect_branch_call = opts->x_s390_indirect_branch; + + if (!opts_set->x_s390_indirect_branch_jump) + opts->x_s390_indirect_branch_jump = opts->x_s390_indirect_branch; + } + + if (opts->x_s390_function_return != indirect_branch_keep) + { + if (!opts_set->x_s390_function_return_reg) + opts->x_s390_function_return_reg = opts->x_s390_function_return; + + if (!opts_set->x_s390_function_return_mem) + opts->x_s390_function_return_mem = opts->x_s390_function_return; + } + + if (!TARGET_CPU_ZARCH) + { + if (opts->x_s390_indirect_branch_call != indirect_branch_keep + || opts->x_s390_indirect_branch_jump != indirect_branch_keep) + error ("-mindirect-branch* options require -march=z900 or higher"); + if (opts->x_s390_function_return_reg != indirect_branch_keep + || opts->x_s390_function_return_mem != indirect_branch_keep) + error ("-mfunction-return* options require -march=z900 or higher"); + } + + /* Enable hardware transactions if available and not explicitly disabled by user. E.g. with -m31 -march=zEC12 -mzarch */ if (!TARGET_OPT_HTM_P (opts_set->x_target_flags)) @@ -14777,6 +15004,79 @@ s390_valid_target_attribute_p (tree fndecl, return ret; } +/* Set VAL to correct enum value according to the indirect-branch or + function-return attribute in ATTR. */ + +static inline void +s390_indirect_branch_attrvalue (tree attr, enum indirect_branch *val) +{ + const char *str = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))); + if (strcmp (str, "keep") == 0) + *val = indirect_branch_keep; + else if (strcmp (str, "thunk") == 0) + *val = indirect_branch_thunk; + else if (strcmp (str, "thunk-inline") == 0) + *val = indirect_branch_thunk_inline; + else if (strcmp (str, "thunk-extern") == 0) + *val = indirect_branch_thunk_extern; +} + +/* Memorize the setting for -mindirect-branch* and -mfunction-return* + from either the cmdline or the function attributes in + cfun->machine. */ + +static void +s390_indirect_branch_settings (tree fndecl) +{ + tree attr; + + if (!fndecl) + return; + + /* Initialize with the cmdline options and let the attributes + override it. */ + cfun->machine->indirect_branch_jump = s390_indirect_branch_jump; + cfun->machine->indirect_branch_call = s390_indirect_branch_call; + + cfun->machine->function_return_reg = s390_function_return_reg; + cfun->machine->function_return_mem = s390_function_return_mem; + + if ((attr = lookup_attribute ("indirect_branch", + DECL_ATTRIBUTES (fndecl)))) + { + s390_indirect_branch_attrvalue (attr, + &cfun->machine->indirect_branch_jump); + s390_indirect_branch_attrvalue (attr, + &cfun->machine->indirect_branch_call); + } + + if ((attr = lookup_attribute ("indirect_branch_jump", + DECL_ATTRIBUTES (fndecl)))) + s390_indirect_branch_attrvalue (attr, &cfun->machine->indirect_branch_jump); + + if ((attr = lookup_attribute ("indirect_branch_call", + DECL_ATTRIBUTES (fndecl)))) + s390_indirect_branch_attrvalue (attr, &cfun->machine->indirect_branch_call); + + if ((attr = lookup_attribute ("function_return", + DECL_ATTRIBUTES (fndecl)))) + { + s390_indirect_branch_attrvalue (attr, + &cfun->machine->function_return_reg); + s390_indirect_branch_attrvalue (attr, + &cfun->machine->function_return_mem); + } + + if ((attr = lookup_attribute ("function_return_reg", + DECL_ATTRIBUTES (fndecl)))) + s390_indirect_branch_attrvalue (attr, &cfun->machine->function_return_reg); + + if ((attr = lookup_attribute ("function_return_mem", + DECL_ATTRIBUTES (fndecl)))) + s390_indirect_branch_attrvalue (attr, &cfun->machine->function_return_mem); +} + + /* Restore targets globals from NEW_TREE and invalidate s390_previous_fndecl cache. */ @@ -14803,7 +15103,10 @@ s390_set_current_function (tree fndecl) several times in the course of compiling a function, and we don't want to slow things down too much or call target_reinit when it isn't safe. */ if (fndecl == s390_previous_fndecl) - return; + { + s390_indirect_branch_settings (fndecl); + return; + } tree old_tree; if (s390_previous_fndecl == NULL_TREE) @@ -14827,6 +15130,8 @@ s390_set_current_function (tree fndecl) if (old_tree != new_tree) s390_activate_target_options (new_tree); s390_previous_fndecl = fndecl; + + s390_indirect_branch_settings (fndecl); } #endif @@ -15062,6 +15367,186 @@ s390_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1, const_tree ty return NULL; } +#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) + { + cgraph_node::create (decl)->set_comdat_group (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. + + Stopping in the thunk: backtrace will point to the thunk target + is if it was interrupted by a signal. For a call this means that + the call chain 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 tables + 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 @@ -15301,6 +15786,12 @@ s390_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1, const_tree ty #undef TARGET_OPTION_RESTORE #define TARGET_OPTION_RESTORE s390_function_specific_restore +#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 --git a/gcc/config/s390/s390.h b/gcc/config/s390/s390.h index 3a7be1a..9a49cdb 100644 --- a/gcc/config/s390/s390.h +++ b/gcc/config/s390/s390.h @@ -1094,4 +1094,124 @@ extern const int processor_flags_table[]; s390_register_target_pragmas (); \ } while (0) +#ifndef USED_FOR_TARGET +/* The following structure is embedded in the machine + specific part of struct function. */ + +struct GTY (()) s390_frame_layout +{ + /* Offset within stack frame. */ + HOST_WIDE_INT gprs_offset; + HOST_WIDE_INT f0_offset; + HOST_WIDE_INT f4_offset; + HOST_WIDE_INT f8_offset; + HOST_WIDE_INT backchain_offset; + + /* Number of first and last gpr where slots in the register + save area are reserved for. */ + int first_save_gpr_slot; + int last_save_gpr_slot; + + /* Location (FP register number) where GPRs (r0-r15) should + be saved to. + 0 - does not need to be saved at all + -1 - stack slot */ +#define SAVE_SLOT_NONE 0 +#define SAVE_SLOT_STACK -1 + signed char gpr_save_slots[16]; + + /* Number of first and last gpr to be saved, restored. */ + int first_save_gpr; + int first_restore_gpr; + int last_save_gpr; + int last_restore_gpr; + + /* Bits standing for floating point registers. Set, if the + respective register has to be saved. Starting with reg 16 (f0) + at the rightmost bit. + Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + fpr 15 13 11 9 14 12 10 8 7 5 3 1 6 4 2 0 + reg 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 */ + unsigned int fpr_bitmap; + + /* Number of floating point registers f8-f15 which must be saved. */ + int high_fprs; + + /* Set if return address needs to be saved. + This flag is set by s390_return_addr_rtx if it could not use + the initial value of r14 and therefore depends on r14 saved + to the stack. */ + bool save_return_addr_p; + + /* Size of stack frame. */ + HOST_WIDE_INT frame_size; +}; + + +/* Define the structure for the machine field in struct function. */ + +struct GTY(()) machine_function +{ + struct s390_frame_layout frame_layout; + + /* Literal pool base register. */ + rtx base_reg; + + /* True if we may need to perform branch splitting. */ + bool split_branches_pending_p; + + bool has_landing_pad_p; + + /* True if the current function may contain a tbegin clobbering + FPRs. */ + bool tbegin_p; + + /* For -fsplit-stack support: A stack local which holds a pointer to + the stack arguments for a function with a variable number of + arguments. This is set at the start of the function and is used + to initialize the overflow_arg_area field of the va_list + structure. */ + rtx split_stack_varargs_pointer; + + enum indirect_branch indirect_branch_jump; + enum indirect_branch indirect_branch_call; + + enum indirect_branch function_return_mem; + enum indirect_branch function_return_reg; +}; +#endif + +#define TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION \ + (cfun->machine->function_return_reg != indirect_branch_keep \ + || cfun->machine->function_return_mem != indirect_branch_keep) + +#define TARGET_INDIRECT_BRANCH_NOBP_RET \ + ((cfun->machine->function_return_reg != indirect_branch_keep \ + && !s390_return_addr_from_memory ()) \ + || (cfun->machine->function_return_mem != indirect_branch_keep \ + && s390_return_addr_from_memory ())) + +#define TARGET_INDIRECT_BRANCH_NOBP_JUMP \ + (cfun->machine->indirect_branch_jump != indirect_branch_keep) + +#define TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK \ + (cfun->machine->indirect_branch_jump == indirect_branch_thunk \ + || cfun->machine->indirect_branch_jump == indirect_branch_thunk_extern) + +#define TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK \ + (cfun->machine->indirect_branch_jump == indirect_branch_thunk_inline) + +#define TARGET_INDIRECT_BRANCH_NOBP_CALL \ + (cfun->machine->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 --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md index dfc8698..928dec6 100644 --- a/gcc/config/s390/s390.md +++ b/gcc/config/s390/s390.md @@ -89,6 +89,7 @@ UNSPEC_LTREF UNSPEC_INSN UNSPEC_EXECUTE + UNSPEC_EXECUTE_JUMP ; Atomic Support UNSPEC_MB @@ -292,6 +293,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. @@ -461,7 +464,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. @@ -8631,7 +8637,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"; @@ -8641,6 +8647,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")]) @@ -8652,8 +8661,26 @@ (ANY_RETURN) (pc)))] "s390_can_use__insn ()" - "b%C0r\t%%r14" - [(set_attr "op_type" "RR") +{ + if (TARGET_INDIRECT_BRANCH_NOBP_RET) + { + s390_indirect_branch_via_thunk (RETURN_REGNUM, + INVALID_REGNUM, + operands[0], + s390_indirect_branch_type_return); + return ""; + } + else + return "b%C0r\t%%r14"; +} + [(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 "brcl") + (const_string "bcr"))) (set_attr "type" "jsr") (set_attr "atype" "agen")]) @@ -8706,7 +8733,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"; @@ -8716,6 +8743,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")]) @@ -9208,30 +9238,220 @@ ; indirect-jump instruction pattern(s). ; -(define_insn "indirect_jump" +(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; + } + + if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK) + { + operands[0] = force_reg (Pmode, operands[0]); + rtx label_ref = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); + if (TARGET_CPU_Z10) + { + if (TARGET_64BIT) + emit_jump_insn (gen_indirect_jump_via_inlinethunkdi_z10 (operands[0], + label_ref)); + else + emit_jump_insn (gen_indirect_jump_via_inlinethunksi_z10 (operands[0], + label_ref)); + } + else + { + if (TARGET_64BIT) + emit_jump_insn (gen_indirect_jump_via_inlinethunkdi (operands[0], + label_ref, + force_reg (Pmode, label_ref))); + else + emit_jump_insn (gen_indirect_jump_via_inlinethunksi (operands[0], + label_ref, + force_reg (Pmode, label_ref))); + } + 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")]) + + +; The label_ref is wrapped into an if_then_else in order to hide it +; from mark_jump_label. Without this the label_ref would become the +; ONLY jump target of that jump breaking the control flow graph. +(define_insn "indirect_jump_via_inlinethunk_z10" + [(unspec [(if_then_else (match_operand:P 1 "larl_operand" "X") + (const_int 0) + (const_int 0)) + (const_int 0)] UNSPEC_EXECUTE_JUMP) + (set (pc) (match_operand:P 0 "register_operand" "a"))] + "TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK + && TARGET_CPU_Z10" +{ + s390_indirect_branch_via_inline_thunk (operands[1]); + return ""; +} + [(set_attr "op_type" "RIL") + (set_attr "type" "branch") + (set_attr "length" "10")]) + +(define_insn "indirect_jump_via_inlinethunk" + [(unspec [(if_then_else (match_operand:P 1 "larl_operand" "X") + (const_int 0) + (const_int 0)) + (match_operand:P 2 "register_operand" "a")] UNSPEC_EXECUTE_JUMP) + (set (pc) (match_operand:P 0 "register_operand" "a"))] + "TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK + && !TARGET_CPU_Z10" +{ + s390_indirect_branch_via_inline_thunk (operands[2]); + return ""; +} + [(set_attr "op_type" "RX") + (set_attr "type" "branch") + (set_attr "length" "8")]) ; ; 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; + } + + if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK) + { + operands[0] = force_reg (Pmode, operands[0]); + rtx label_ref = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ()); + if (TARGET_CPU_Z10) + { + if (TARGET_64BIT) + emit_jump_insn (gen_casesi_jump_via_inlinethunkdi_z10 (operands[0], + operands[1], + label_ref)); + else + emit_jump_insn (gen_casesi_jump_via_inlinethunksi_z10 (operands[0], + operands[1], + label_ref)); + } + else + { + if (TARGET_64BIT) + emit_jump_insn (gen_casesi_jump_via_inlinethunkdi (operands[0], + operands[1], + label_ref, + force_reg (Pmode, label_ref))); + else + emit_jump_insn (gen_casesi_jump_via_inlinethunksi (operands[0], + operands[1], + label_ref, + force_reg (Pmode, label_ref))); + } + 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"; else @@ -9240,9 +9460,85 @@ [(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 "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")]) + + +; The label_ref is wrapped into an if_then_else in order to hide it +; from mark_jump_label. Without this the label_ref would become the +; ONLY jump target of that jump breaking the control flow graph. +(define_insn "casesi_jump_via_inlinethunk_z10" + [(unspec [(if_then_else (match_operand:P 2 "larl_operand" "X") + (const_int 0) + (const_int 0)) + (const_int 0)] UNSPEC_EXECUTE_JUMP) + (set (pc) (match_operand:P 0 "register_operand" "a")) + (use (label_ref (match_operand 1 "" "")))] + "TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK + && TARGET_CPU_Z10" +{ + s390_indirect_branch_via_inline_thunk (operands[2]); + return ""; +} + [(set_attr "op_type" "RIL") + (set_attr "type" "cs") + (set_attr "length" "10")]) + +(define_insn "casesi_jump_via_inlinethunk" + [(unspec [(if_then_else (match_operand:P 2 "larl_operand" "X") + (const_int 0) + (const_int 0)) + (match_operand:P 3 "register_operand" "a")] UNSPEC_EXECUTE_JUMP) + (set (pc) (match_operand:P 0 "register_operand" "a")) + (use (label_ref (match_operand 1 "" "")))] + "TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK + && !TARGET_CPU_Z10" +{ + s390_indirect_branch_via_inline_thunk (operands[3]); + return ""; +} + [(set_attr "op_type" "RX") + (set_attr "type" "cs") + (set_attr "length" "8")]) + (define_expand "casesi" [(match_operand:SI 0 "general_operand" "") (match_operand:SI 1 "general_operand" "") @@ -9347,8 +9643,27 @@ (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") +{ + 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")]) @@ -9388,8 +9703,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")]) @@ -9455,7 +9789,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"; @@ -9465,6 +9801,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")]) @@ -9516,7 +9896,9 @@ (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"; @@ -9526,6 +9908,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")]) @@ -10282,17 +10712,101 @@ (define_insn "" [(ANY_RETURN)] "s390_can_use__insn ()" - "br\t%%r14" - [(set_attr "op_type" "RR") +{ + if (TARGET_INDIRECT_BRANCH_NOBP_RET) + { + /* The target is always r14 so there is no clobber + of r1 needed for pre z10 targets. */ + s390_indirect_branch_via_thunk (RETURN_REGNUM, + INVALID_REGNUM, + NULL_RTX, + s390_indirect_branch_type_return); + return ""; + } + else + return "br\t%%r14"; +} + [(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_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: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" +(define_insn "return_prez10" [(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")) + (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")]) diff --git a/gcc/config/s390/s390.opt b/gcc/config/s390/s390.opt index 1ae1396..fd7cfb0 100644 --- a/gcc/config/s390/s390.opt +++ b/gcc/config/s390/s390.opt @@ -199,3 +199,62 @@ values are small, non-negative integers. The default branch cost is mlra Target Report Var(s390_lra_flag) Init(1) Save Use LRA instead of reload. + +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-inline) Value(indirect_branch_thunk_inline) + +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 --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c new file mode 100644 index 0000000..db9336d --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c @@ -0,0 +1,56 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z10 --save-temps -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 __attribute__((indirect_branch_call("thunk"))) +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 --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c new file mode 100644 index 0000000..c02b45a --- /dev/null +++ b/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,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 --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c new file mode 100644 index 0000000..b5f13eb --- /dev/null +++ b/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,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 --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c new file mode 100644 index 0000000..486495b --- /dev/null +++ b/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,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 --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c new file mode 100644 index 0000000..c62ddf5 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c @@ -0,0 +1,42 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-table" } */ +/* { dg-require-effective-target label_values } */ + +/* 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 __attribute__((indirect_branch_jump("thunk"))) +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 --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c new file mode 100644 index 0000000..63d64c1 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c @@ -0,0 +1,42 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-table" } */ +/* { dg-require-effective-target label_values } */ + +/* 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 __attribute__((indirect_branch_jump("thunk-inline"))) +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 two gotos in bar get merged. */ +/* { dg-final { scan-assembler-times "exrl" 1 } } */ + +/* { dg-final { scan-assembler-not "jg\t__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 --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c new file mode 100644 index 0000000..28d7837 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c @@ -0,0 +1,43 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk-inline -mindirect-branch-table" } */ +/* { dg-require-effective-target label_values } */ + +/* 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 two gotos in bar get merged. */ +/* { dg-final { scan-assembler-times "exrl" 1 } } */ + +/* { dg-final { scan-assembler-not "jg\t__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 --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c new file mode 100644 index 0000000..3c0c007 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c @@ -0,0 +1,43 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z900 --save-temps -mindirect-branch-jump=thunk-inline -mindirect-branch-table" } */ +/* { dg-require-effective-target label_values } */ + +/* 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 two gotos in bar get merged. */ +/* { dg-final { scan-assembler-times "\tex\t" 1 } } */ + +/* { dg-final { scan-assembler-not "jg\t__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 --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c new file mode 100644 index 0000000..05c8bb8 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c @@ -0,0 +1,46 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk-extern -mindirect-branch-table" } */ +/* { dg-require-effective-target label_values } */ + +/* 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 --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c new file mode 100644 index 0000000..71c86fd --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c @@ -0,0 +1,43 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */ +/* { dg-require-effective-target label_values } */ + +/* 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 --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c new file mode 100644 index 0000000..89ad799 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c @@ -0,0 +1,43 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z900 --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */ +/* { dg-require-effective-target label_values } */ + +/* 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 --git a/gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c b/gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c new file mode 100644 index 0000000..4bf88cf --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c @@ -0,0 +1,46 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-table" } */ + +int gl = 0; + +int __attribute__((noinline,noclone)) +bar (int a) +{ + return a + 2; +} + +void __attribute__((function_return("thunk"),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; +} + +/* With -march=z10 -mzarch the shrink wrapped returns use compare and + swap relative to jump to the exit block instead of making use of + the conditional return pattern. + FIXME: Use compare and branch register for that!!!! */ + +/* 2 x foo +/* { 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 --git a/gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c b/gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c new file mode 100644 index 0000000..8b32bfe --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c @@ -0,0 +1,40 @@ +/* { 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__((function_return("keep"),noinline,noclone)) +foo (int a) +{ + int i; + + if (a == 42) + return; + + for (i = 0; i < a; i++) + gl += bar (i); +} + +int __attribute__((function_return("keep"))) +main () +{ + foo (3); + if (gl != 9) + __builtin_abort (); + + return 0; +} + +/* { dg-final { scan-assembler-not "jg\t__s390_indirect_jump" } } */ +/* { dg-final { scan-assembler-not "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-not "section\t.s390_return_mem" } } */ diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c b/gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c new file mode 100644 index 0000000..39cab8b --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c @@ -0,0 +1,46 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-table" } */ + +int gl = 0; + +int __attribute__((noinline,noclone)) +bar (int a) +{ + return a + 2; +} + +void __attribute__((function_return_mem("thunk"),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; +} + +/* With -march=z10 -mzarch the shrink wrapped returns use compare and + swap relative to jump to the exit block instead of making use of + the conditional return pattern. + FIXME: Use compare and branch register for that!!!! */ + +/* 2 x foo +/* { 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 --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c b/gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c new file mode 100644 index 0000000..f99f152 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c @@ -0,0 +1,49 @@ +/* { 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; +} + +/* With -march=z10 -mzarch the shrink wrapped returns use compare and + swap relative to jump to the exit block instead of making use of + the conditional return pattern. + FIXME: Use compare and branch register for that!!!! */ + +/* 2 x foo, 1 x main +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 3 } } */ + +/* 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 --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c new file mode 100644 index 0000000..177fc32 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c @@ -0,0 +1,46 @@ +/* { 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; +} + +/* With -march=z10 -mzarch the shrink wrapped returns use compare and + swap relative to jump to the exit block instead of making use of + the conditional return pattern. + FIXME: Use compare and branch register for that!!!! */ + +/* 2 x foo, 1 x main +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 3 } } */ +/* { 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 --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c new file mode 100644 index 0000000..0b31811 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c @@ -0,0 +1,48 @@ +/* { 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 } } */ + +/* 1 x foo, conditional return, shrink wrapped +/* { dg-final { scan-assembler "jge\t__s390_indirect_jump" } } */ + +/* 1 x foo, conditional return, shrink wrapped +/* { dg-final { scan-assembler "jgle\t__s390_indirect_jump" } } */ + +/* { 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 --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c b/gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c new file mode 100644 index 0000000..ebfc9ff --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c @@ -0,0 +1,41 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-table" } */ + +int gl = 0; + +int __attribute__((function_return_reg("thunk"),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 --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c b/gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c new file mode 100644 index 0000000..82833f7 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c @@ -0,0 +1,44 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z900 --save-temps -mfunction-return-reg=thunk -mindirect-branch-table" } */ + +/* We have to generate different thunks for indirect branches + depending on whether the code is compiled for pre z10 machines or + later. This testcase makes sure this works within the same compile + unit. */ + +int __attribute__((noinline,noclone,target("arch=z10"))) +bar (int a) +{ + return a + 2; +} + +int __attribute__((noinline,noclone,target("arch=z9-ec"))) +foo (int a) +{ + return a + 3; +} + +int +main () +{ + if (bar (42) != 44) + __builtin_abort (); + + if (foo (42) != 45) + __builtin_abort (); + + return 0; +} + +/* 1 x bar, 1 x foo */ +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */ +/* 1 x foo */ +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump_r1use" 1 } } */ + +/* { dg-final { scan-assembler-times "ex\t" 1 } } */ +/* { dg-final { scan-assembler-times "exrl\t" 1 } } */ + +/* { 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 --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c b/gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c new file mode 100644 index 0000000..4ea14e3 --- /dev/null +++ b/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,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 --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c b/gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c new file mode 100644 index 0000000..42c3e74 --- /dev/null +++ b/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,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 --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c b/gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c new file mode 100644 index 0000000..3f4efa5 --- /dev/null +++ b/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,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 --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c new file mode 100644 index 0000000..8dfd7e4 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c @@ -0,0 +1,78 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-jump=thunk-inline -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 --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c new file mode 100644 index 0000000..46d2c54 --- /dev/null +++ b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c @@ -0,0 +1,78 @@ +/* { dg-do run } */ +/* { dg-options "-O3 -march=z900 -mzarch --save-temps -mindirect-branch-jump=thunk-inline -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 "\tex\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" } } */ diff --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c b/gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c new file mode 100644 index 0000000..9dfe391 --- /dev/null +++ b/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,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 --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c b/gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c new file mode 100644 index 0000000..f1439a8 --- /dev/null +++ b/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,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" } } */