From patchwork Sun Mar 4 18:55:39 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Botcazou X-Patchwork-Id: 881220 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-474261-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=adacore.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="oWVPeNqM"; 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 3zvXMX3BT3z9sX4 for ; Mon, 5 Mar 2018 05:56:53 +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:mime-version:content-type :content-transfer-encoding; q=dns; s=default; b=dxrja8gUT+8Hv7zf NKQ0FYjXePueJtnuu9eLG/7UJl2VDYvmFm0b6FkyUVV/zWq39KFd6QclWbOrwP/7 uckjdb9ahGQtce9+7L3Q1IMtdg3RuEeAN6XQZm0dwtF8AKzyoIHBouUMl/1Ff1l+ vpYu5EkWybneWiM3Vn+CLaB6hhk= 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:mime-version:content-type :content-transfer-encoding; s=default; bh=vRYkEVledJgmAAjij3O6tv H3L+A=; b=oWVPeNqMK1Lj9P16CJjiQpegZ2ajfrO4y3jQAIh5EJIFZbnOCXlv6B +p74DpLfRuU8pHiijwUumWveuhwKYbnhxxv1SvHfbpJ3Z4AK2xfNEk8nuIwe/WEc 1lAzWz9VBkT/AiMuC/NMiP+P2ugbKmPINdZAzx2GtSbW6K48ALyOU= Received: (qmail 69804 invoked by alias); 4 Mar 2018 18:56:46 -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 69792 invoked by uid 89); 4 Mar 2018 18:56:44 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-9.9 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_2, GIT_PATCH_3, KAM_ASCII_DIVIDERS, KAM_NUMSUBJECT, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy=31210, 88211 X-HELO: smtp.eu.adacore.com Received: from mel.act-europe.fr (HELO smtp.eu.adacore.com) (194.98.77.210) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sun, 04 Mar 2018 18:56:42 +0000 Received: from localhost (localhost [127.0.0.1]) by filtered-smtp.eu.adacore.com (Postfix) with ESMTP id B74558139B for ; Sun, 4 Mar 2018 19:56:39 +0100 (CET) Received: from smtp.eu.adacore.com ([127.0.0.1]) by localhost (smtp.eu.adacore.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id KO1hUxT1Of2t for ; Sun, 4 Mar 2018 19:56:39 +0100 (CET) Received: from polaris.localnet (bon31-6-88-161-99-133.fbx.proxad.net [88.161.99.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.eu.adacore.com (Postfix) with ESMTPSA id 83A3B8139A for ; Sun, 4 Mar 2018 19:56:39 +0100 (CET) From: Eric Botcazou To: gcc-patches@gcc.gnu.org Subject: [patch] Fix PR target/84277 Date: Sun, 04 Mar 2018 19:55:39 +0100 Message-ID: <4011576.KGStx97QI1@polaris> MIME-Version: 1.0 Hi, this is the breakage of SEH on 64-bit Windows introduced by defaulting to the -freorder-blocks-and-partition optimization. It is fixed by: 1. Defining ASM_DECLARE_COLD_FUNCTION_NAME & ASM_DECLARE_COLD_FUNCTION_SIZE, 2. Emitting a nop in one more case for SEH, 3. Splitting the exception table into hot and cold parts; that's necessary because the LSDA is referenced directly in the SEH scheme. Tested on x86-64/Windows, x86-64/Linux and IA-64/Linux, OK for mainline? 2018-03-04 Eric Botcazou PR target/84277 * except.h (output_function_exception_table): Adjust prototype. * except.c (output_function_exception_table): Remove FNNAME parameter and add SECTION parameter. Ouput one part of the table at a time. * final.c (final_scan_insn_1) : Output the first part of the exception table and emit unwind directives, if any. * config/i386/i386-protos.h (i386_pe_end_cold_function): Declare. (i386_pe_seh_cold_init): Likewise. * config/i386/cygming.h (ASM_DECLARE_COLD_FUNCTION_NAME): New macro. (ASM_DECLARE_COLD_FUNCTION_SIZE): Likewise. * config/i386/i386.c (x86_expand_epilogue): Fix wording in comment. (ix86_output_call_insn): Emit a nop in one more case for SEH. * config/i386/winnt.c: Include except.h. (struct seh_frame_state): Add reg_offset, after_prologue, in_cold_section. (i386_pe_seh_end_prologue): Set seh->after_prologue. (i386_pe_seh_cold_init): New function. (i386_pe_seh_fini): Add COLD parameter and bail out if it is not equal to seh->in_cold_section. (seh_emit_push): Record the offset of the push. (seh_emit_save): Record the offet of the save. (i386_pe_seh_unwind_emit): Deal with NOTE_INSN_SWITCH_TEXT_SECTIONS. Test seh->after_prologue to disregard the epilogue. (i386_pe_end_function): Pass FALSE to i386_pe_seh_fini. (i386_pe_end_cold_function): New function. Index: config/i386/cygming.h =================================================================== --- config/i386/cygming.h (revision 258231) +++ config/i386/cygming.h (working copy) @@ -312,10 +312,27 @@ do { \ #define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL) \ i386_pe_start_function (FILE, NAME, DECL) +/* Write the extra assembler code needed to declare the name of a + cold function partition properly. */ + +#undef ASM_DECLARE_COLD_FUNCTION_NAME +#define ASM_DECLARE_COLD_FUNCTION_NAME(FILE, NAME, DECL) \ + do \ + { \ + i386_pe_declare_function_type (FILE, NAME, 0); \ + i386_pe_seh_cold_init (FILE, NAME); \ + ASM_OUTPUT_LABEL (FILE, NAME); \ + } \ + while (0) + #undef ASM_DECLARE_FUNCTION_SIZE #define ASM_DECLARE_FUNCTION_SIZE(FILE,NAME,DECL) \ i386_pe_end_function (FILE, NAME, DECL) +#undef ASM_DECLARE_COLD_FUNCTION_SIZE +#define ASM_DECLARE_COLD_FUNCTION_SIZE(FILE,NAME,DECL) \ + i386_pe_end_cold_function (FILE, NAME, DECL) + /* Add an external function to the list of functions to be declared at the end of the file. */ #define ASM_OUTPUT_EXTERNAL(FILE, DECL, NAME) \ Index: config/i386/i386-protos.h =================================================================== --- config/i386/i386-protos.h (revision 258231) +++ config/i386/i386-protos.h (working copy) @@ -256,6 +256,7 @@ extern void i386_pe_asm_output_aligned_d extern void i386_pe_file_end (void); extern void i386_pe_start_function (FILE *, const char *, tree); extern void i386_pe_end_function (FILE *, const char *, tree); +extern void i386_pe_end_cold_function (FILE *, const char *, tree); extern void i386_pe_assemble_visibility (tree, int); extern tree i386_pe_mangle_decl_assembler_name (tree, tree); extern tree i386_pe_mangle_assembler_name (const char *); @@ -263,6 +264,7 @@ extern void i386_pe_record_stub (const c extern void i386_pe_seh_init (FILE *); extern void i386_pe_seh_end_prologue (FILE *); +extern void i386_pe_seh_cold_init (FILE *, const char *); extern void i386_pe_seh_unwind_emit (FILE *, rtx_insn *); extern void i386_pe_seh_emit_except_personality (rtx); extern void i386_pe_seh_init_sections (void); Index: config/i386/i386.c =================================================================== --- config/i386/i386.c (revision 258231) +++ config/i386/i386.c (working copy) @@ -14702,9 +14702,9 @@ ix86_expand_epilogue (int style) if (TARGET_SEH) { /* Prevent a catch region from being adjacent to the standard - epilogue sequence. Unfortuantely crtl->uses_eh_lsda nor - several other flags that would be interesting to test are - not yet set up. */ + epilogue sequence. Unfortunately neither crtl->uses_eh_lsda + nor several other flags that would be interesting to test are + set up yet. */ if (flag_non_call_exceptions) emit_insn (gen_nops (const1_rtx)); else @@ -29206,6 +29206,14 @@ ix86_output_call_insn (rtx_insn *insn, r for (i = NEXT_INSN (insn); i ; i = NEXT_INSN (i)) { + /* Prevent a catch region from being adjacent to a jump that would + be interpreted as an epilogue sequence by the unwinder. */ + if (JUMP_P(i) && CROSSING_JUMP_P (i)) + { + seh_nop_p = true; + break; + } + /* If we get to another real insn, we don't need the nop. */ if (INSN_P (i)) break; Index: config/i386/winnt.c =================================================================== --- config/i386/winnt.c (revision 258231) +++ config/i386/winnt.c (working copy) @@ -36,6 +36,7 @@ along with GCC; see the file COPYING3. #include "emit-rtl.h" #include "cgraph.h" #include "lto-streamer.h" +#include "except.h" #include "output.h" #include "varasm.h" #include "lto-section-names.h" @@ -820,6 +821,15 @@ struct seh_frame_state /* The CFA is located at CFA_REG + CFA_OFFSET. */ HOST_WIDE_INT cfa_offset; rtx cfa_reg; + + /* The offset wrt the CFA where register N has been saved. */ + HOST_WIDE_INT reg_offset[FIRST_PSEUDO_REGISTER]; + + /* True if we are past the end of the epilogue. */ + bool after_prologue; + + /* True if we are in the cold section. */ + bool in_cold_section; }; /* Set up data structures beginning output for SEH. */ @@ -850,10 +860,26 @@ i386_pe_seh_init (FILE *f) fputc ('\n', f); } +/* Emit an assembler directive for the end of the prologue. */ + void i386_pe_seh_end_prologue (FILE *f) { + if (!TARGET_SEH) + return; + if (cfun->is_thunk) + return; + cfun->machine->seh->after_prologue = true; + fputs ("\t.seh_endprologue\n", f); +} + +/* Emit assembler directives to reconstruct the SEH state. */ + +void +i386_pe_seh_cold_init (FILE *f, const char *name) +{ struct seh_frame_state *seh; + HOST_WIDE_INT offset; if (!TARGET_SEH) return; @@ -861,19 +887,56 @@ i386_pe_seh_end_prologue (FILE *f) return; seh = cfun->machine->seh; - XDELETE (seh); - cfun->machine->seh = NULL; + fputs ("\t.seh_proc\t", f); + assemble_name (f, name); + fputc ('\n', f); + + offset = seh->sp_offset - INCOMING_FRAME_SP_OFFSET; + if (offset > 0 && offset < SEH_MAX_FRAME_SIZE) + fprintf (f, "\t.seh_stackalloc\t" HOST_WIDE_INT_PRINT_DEC "\n", offset); + + for (int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (seh->reg_offset[regno] > 0) + { + fputs ((SSE_REGNO_P (regno) ? "\t.seh_savexmm\t" + : GENERAL_REGNO_P (regno) ? "\t.seh_savereg\t" + : (gcc_unreachable (), "")), f); + print_reg (gen_rtx_REG (DImode, regno), 0, f); + fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n", + seh->sp_offset - seh->reg_offset[regno]); + } + + if (seh->cfa_reg != stack_pointer_rtx) + { + offset = seh->sp_offset - seh->cfa_offset; + + gcc_assert ((offset & 15) == 0); + gcc_assert (IN_RANGE (offset, 0, 240)); + + fputs ("\t.seh_setframe\t", f); + print_reg (seh->cfa_reg, 0, f); + fprintf (f, ", " HOST_WIDE_INT_PRINT_DEC "\n", offset); + } fputs ("\t.seh_endprologue\n", f); } +/* Emit an assembler directive for the end of the function. */ + static void -i386_pe_seh_fini (FILE *f) +i386_pe_seh_fini (FILE *f, bool cold) { + struct seh_frame_state *seh; + if (!TARGET_SEH) return; if (cfun->is_thunk) return; + seh = cfun->machine->seh; + if (cold != seh->in_cold_section) + return; + XDELETE (seh); + cfun->machine->seh = NULL; fputs ("\t.seh_endproc\n", f); } @@ -882,11 +945,12 @@ i386_pe_seh_fini (FILE *f) static void seh_emit_push (FILE *f, struct seh_frame_state *seh, rtx reg) { - unsigned int regno = REGNO (reg); + const unsigned int regno = REGNO (reg); gcc_checking_assert (GENERAL_REGNO_P (regno)); seh->sp_offset += UNITS_PER_WORD; + seh->reg_offset[regno] = seh->sp_offset; if (seh->cfa_reg == stack_pointer_rtx) seh->cfa_offset += UNITS_PER_WORD; @@ -901,9 +965,11 @@ static void seh_emit_save (FILE *f, struct seh_frame_state *seh, rtx reg, HOST_WIDE_INT cfa_offset) { - unsigned int regno = REGNO (reg); + const unsigned int regno = REGNO (reg); HOST_WIDE_INT offset; + seh->reg_offset[regno] = cfa_offset; + /* Negative save offsets are of course not supported, since that would be a store below the stack pointer and thus clobberable. */ gcc_assert (seh->sp_offset >= cfa_offset); @@ -1112,15 +1178,21 @@ i386_pe_seh_unwind_emit (FILE *asm_out_f if (!TARGET_SEH) return; - /* We free the SEH data once done with the prologue. Ignore those - RTX_FRAME_RELATED_P insns that are associated with the epilogue. */ seh = cfun->machine->seh; - if (seh == NULL) - return; + if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS) + { + fputs ("\t.seh_endproc\n", asm_out_file); + seh->in_cold_section = true; + return; + } if (NOTE_P (insn) || !RTX_FRAME_RELATED_P (insn)) return; + /* Skip RTX_FRAME_RELATED_P insns that are associated with the epilogue. */ + if (seh->after_prologue) + return; + for (note = REG_NOTES (insn); note ; note = XEXP (note, 1)) { switch (REG_NOTE_KIND (note)) @@ -1227,8 +1299,13 @@ i386_pe_start_function (FILE *f, const c void i386_pe_end_function (FILE *f, const char *, tree) { - i386_pe_seh_fini (f); + i386_pe_seh_fini (f, false); } +void +i386_pe_end_cold_function (FILE *f, const char *, tree) +{ + i386_pe_seh_fini (f, true); +} #include "gt-winnt.h" Index: except.c =================================================================== --- except.c (revision 258231) +++ except.c (working copy) @@ -3168,12 +3168,16 @@ output_one_function_exception_table (int } void -output_function_exception_table (const char *fnname) +output_function_exception_table (int section) { + const char *fnname = get_fnname_from_decl (current_function_decl); rtx personality = get_personality_function (current_function_decl); /* Not all functions need anything. */ - if (! crtl->uses_eh_lsda) + if (!crtl->uses_eh_lsda) + return; + + if (section == 1 && !crtl->eh.call_site_record_v[1]) return; if (personality) @@ -3189,9 +3193,7 @@ output_function_exception_table (const c /* If the target wants a label to begin the table, emit it here. */ targetm.asm_out.emit_except_table_label (asm_out_file); - output_one_function_exception_table (0); - if (crtl->eh.call_site_record_v[1]) - output_one_function_exception_table (1); + output_one_function_exception_table (section); switch_to_section (current_function_section ()); } Index: except.h =================================================================== --- except.h (revision 258231) +++ except.h (working copy) @@ -229,7 +229,7 @@ extern void remove_eh_handler (eh_region extern void remove_unreachable_eh_regions (sbitmap); extern bool current_function_has_exception_handlers (void); -extern void output_function_exception_table (const char *); +extern void output_function_exception_table (int); extern rtx expand_builtin_eh_pointer (tree); extern rtx expand_builtin_eh_filter (tree); Index: final.c =================================================================== --- final.c (revision 258231) +++ final.c (working copy) @@ -2265,6 +2265,11 @@ final_scan_insn_1 (rtx_insn *insn, FILE case NOTE_INSN_SWITCH_TEXT_SECTIONS: maybe_output_next_view (seen); + output_function_exception_table (0); + + if (targetm.asm_out.unwind_emit) + targetm.asm_out.unwind_emit (asm_out_file, insn); + in_cold_section_p = !in_cold_section_p; if (in_cold_section_p) @@ -4672,7 +4677,7 @@ rest_of_handle_final (void) /* The IA-64 ".handlerdata" directive must be issued before the ".endp" directive that closes the procedure descriptor. Similarly, for x64 SEH. Otherwise it's not strictly necessary, but it doesn't hurt either. */ - output_function_exception_table (fnname); + output_function_exception_table (crtl->has_bb_partition ? 1 : 0); assemble_end_function (current_function_decl, fnname);