This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [Patch, _eh, dawin] Allow targets to suppress epilogues in _eh frames.
- From: Jack Howarth <howarth at bromo dot med dot uc dot edu>
- To: IainS <developer at sandoe-acoustics dot co dot uk>
- Cc: GCC Patches <gcc-patches at gcc dot gnu dot org>, Richard Henderson <rth at redhat dot com>, mrs at gcc dot gnu dot org
- Date: Fri, 13 Aug 2010 22:19:56 -0400
- Subject: Re: [Patch, _eh, dawin] Allow targets to suppress epilogues in _eh frames.
- References: <89584BF2-10CD-45DE-A4A5-4C2EE43396B3@sandoe-acoustics.co.uk>
On Fri, Aug 13, 2010 at 08:50:29PM +0100, IainS wrote:
> As discussed off list and in IRC with Richard,
>
> =----=
>
> Some (if not most) targets do not require the function epilogue in their
> _eh unwind frames.
> In fact, it breaks the darwin unwinder (PR41991) my original motivation
> for looking at this.
>
> However, as we went through the discussion it became apparent that this
> might have wider application than fixing a darwin bug, in saving some
> space in the eh.
>
> What this does is to use the existing DW_CFA_save/restore_state and a
> new DW_CFA_GNU_start_epilogue marker to suppress the emitting of
> function epilogues - when (a) we're emitting _eh and (b) the target
> requests suppression via a hook.
> The hook is a bool function to permit targets to choose whether to emit
> this data or not at run time rather than config time (the default hook
> does nothing).
>
> ----
>
> The DW_CFA_GNU_start_epilogue marker is inserted under the circumstance
> that an epilogue is detected at the end of a function.
> The save/restore markers are not touched and deal with the case that we
> are mid-function.
>
> There is a debug print of # DW_CFA_GNU_start_epilogue to show where we
> are intercepting in the eh frames and not curtailing the debug_frames.
>
> I'd particularly welcome someone's eye over what's happening in the case
> of section switches (it seems to me that the skipping of mid-function
> save/restore is handled OK, but I'm not familiar with that code - and my
> main target(s) don't use that facility).
>
> ====
>
> This has only been lightly tested on i686/powerpc-darwin9 (regtested on
> i686) - but it appears to restore Unwind functionality to the platform :)
> [gcj works again, Yay!]
>
> ====
>
> so we get this in the _eh frame:
>
> LECIE1:
> .globl _main.eh
> _main.eh:
> LSFDE1:
> .set L$set$1,LEFDE1-LASFDE1
> .long L$set$1 # FDE Length
> LASFDE1:
> .long LASFDE1-EH_frame1 # FDE CIE offset
> .long LFB1-. # FDE initial location
> .set L$set$2,LFE1-LFB1
>
> <SNIP>
>
> .set L$set$5,LCFI4-LCFI1
> .long L$set$5
> .byte 0x83 # DW_CFA_offset, column 0x3
> .byte 0x3 # uleb128 0x3
> .byte 0x4 # DW_CFA_advance_loc4
> .set L$set$6,LCFI6-LCFI4
> .long L$set$6
> # # DW_CFA_GNU_start_epilogue
> .align 2
>
> and this in the _debug_frame:
> .section __DWARF,__debug_frame,regular,debug
> Lsection__debug_frame:
> Lframe0:
> .set L$set$7,LECIE0-LSCIE0
> .long L$set$7 # Length of Common Information Entry
> LSCIE0:
>
> <SNIP>
>
> .byte 0x83 # DW_CFA_offset, column 0x3
> .byte 0x3 # uleb128 0x3
> .byte 0x4 # DW_CFA_advance_loc4
> .set L$set$14,LCFI6-LCFI4
> .long L$set$14
> # # DW_CFA_GNU_start_epilogue
> .byte 0xc3 # DW_CFA_restore, column 0x3
> .byte 0x4 # DW_CFA_advance_loc4
> .set L$set$15,LCFI7-LCFI6
> .long L$set$15
> .byte 0xc # DW_CFA_def_cfa
> .byte 0x4 # uleb128 0x4
> .byte 0x4 # uleb128 0x4
> .byte 0xc5 # DW_CFA_restore, column 0x5
> .align 2
> LEFDE2:
>
> =====
> thoughts?
> Iain
>
Iain,
So far the only failure I see with your patch under darwin10.4.0, using the
compact unwinder (by eliminating the addition of -no_compact_unwind
in darwin10.h), is...
FAIL: g++.dg/eh/async-unwind2.C execution test
at -m32. Interestingly, if I pass -Wl,-warn_compact_unwind when building
that testcase, I get...
[MacPro:~/async_unwind] howarth% /sw/src/fink.build/gcc46-4.6.0-1000/darwin_objdir/gcc/testsuite/g++/../../g++ -B/sw/src/fink.build/gcc46-4.6.0-1000/darwin_objdir/gcc/testsuite/g++/../../ /sw/src/fink.build/gcc46-4.6.0-1000/gcc-4.6-20100813/gcc/testsuite/g++.dg/eh/async-unwind2.C -nostdinc++ -I/sw/src/fink.build/gcc46-4.6.0-1000/darwin_objdir/x86_64-apple-darwin10.4.0/i386/libstdc++-v3/include/x86_64-apple-darwin10.4.0 -I/sw/src/fink.build/gcc46-4.6.0-1000/darwin_objdir/x86_64-apple-darwin10.4.0/i386/libstdc++-v3/include -I/sw/src/fink.build/gcc46-4.6.0-1000/gcc-4.6-20100813/libstdc++-v3/libsupc++ -I/sw/src/fink.build/gcc46-4.6.0-1000/gcc-4.6-20100813/libstdc++-v3/include/backward -I/sw/src/fink.build/gcc46-4.6.0-1000/gcc-4.6-20100813/libstdc++-v3/testsuite/util -fmessage-length=0 -Os -fasynchronous-unwind-tables -fpic -fno-inline -L/sw/src/fink.build/gcc46-4.6.0-1000/darwin_objdir/x86_64-apple-darwin10.4.0/i386/libstdc++-v3/src/.libs -B/sw/src/fink.build/gcc46-4.6.0-1000/darwin_objdir/x86_64-apple-darwin10.4.0/i386/libstdc++-v3/src/.libs -L/sw/src/fink.build/gcc46-4.6.0-1000/darwin_objdir/x86_64-apple-darwin10.4.0/i386/libstdc++-v3/src/.libs -L/sw/src/fink.build/gcc46-4.6.0-1000/darwin_objdir/x86_64-apple-darwin10.4.0/i386/libiberty -multiply_defined suppress -lm -m32 -g -Wl,-warn_compact_unwind -o ./async-unwind2.exe
ld: warning: can't make compact unwind encoding from dwarf for S::sfn2(int) in /var/folders/1C/1CdoNxmNFHyOIjNBLNuJh++++TM/-Tmp-//ccw4LTKt.o because dwarf uses DW_CFA_GNU_args_size
ld: warning: can't make compact unwind encoding from dwarf for S::sfn3(char const*) in /var/folders/1C/1CdoNxmNFHyOIjNBLNuJh++++TM/-Tmp-//ccw4LTKt.o because dwarf uses DW_CFA_GNU_args_size
ld: warning: can't make compact unwind encoding from dwarf for baz1(S const&) in /var/folders/1C/1CdoNxmNFHyOIjNBLNuJh++++TM/-Tmp-//ccw4LTKt.o because dwarf uses DW_CFA_GNU_args_size
These errors don't appear when I do the same to build the non-failing async-unwind1.exe testcase. Can we inhibit the emission of
DW_CFA_GNU_args_size on darwin as well?
Jack
>
> Change notes (not a proper changelog, at this juncture)
>
> include/dwarf2.h : DW_CFA_GNU_start_epilogue new enum.
>
> dwarf2out.c: dwarf_cfi_name() recognize DW_CFA_GNU_start_epilogue
> static scope emit_cfa_start_epilogue new var.
> add_fde_cfi() : emit a marker for the epilogue start;
> dwarf2out_cfi_begin_epilogue (): note that we need to emit the epilogue
> start marker when the epilogue is at the end.
> dw_cfi_oprnd1_desc (): recognize DW_CFA_GNU_start_epilogue as a no-op.
> output_cfi(): print debug message for DW_CFA_GNU_start_epilogue
> emit_cfi_or_skip_epilogue (): New.
> output_fde () : use emit_cfi_or_skip_epilogue ();
>
> target.def: suppress_eh_epilogue_p (): New ASM Hook.
>
> === the remainder are the implementation on the darwin side:
>
> gcc/config/darwin.h (TARGET_ASM_SUPPRESS_EH_EPILOGUE_P): New
> gcc/config/darwin.c (darwin_asm_suppress_eh_epilogue_p): New.
> gcc/config/darwin-protos.h: Declare darwin_asm_suppress_eh_epilogue_p.
>
> ========== - ===========
> Index: include/dwarf2.h
> ===================================================================
> --- include/dwarf2.h (revision 163221)
> +++ include/dwarf2.h (working copy)
> @@ -854,7 +854,8 @@ enum dwarf_call_frame_info
> /* GNU extensions. */
> DW_CFA_GNU_window_save = 0x2d,
> DW_CFA_GNU_args_size = 0x2e,
> - DW_CFA_GNU_negative_offset_extended = 0x2f
> + DW_CFA_GNU_negative_offset_extended = 0x2f,
> + DW_CFA_GNU_start_epilogue = 0x30
> };
>
> #define DW_CIE_ID 0xffffffff
> Index: gcc/dwarf2out.c
> ===================================================================
> --- gcc/dwarf2out.c (revision 163221)
> +++ gcc/dwarf2out.c (working copy)
> @@ -720,7 +724,9 @@ dwarf_cfi_name (unsigned int cfi_opc)
> return "DW_CFA_GNU_args_size";
> case DW_CFA_GNU_negative_offset_extended:
> return "DW_CFA_GNU_negative_offset_extended";
> -
> + case DW_CFA_GNU_start_epilogue:
> + return "DW_CFA_GNU_start_epilogue";
> +
> default:
> return "DW_CFA_<unknown>";
> }
> @@ -801,6 +807,9 @@ dwarf2out_cfi_label (bool force)
> /* True if remember_state should be emitted before following CFI directive. */
> static bool emit_cfa_remember;
>
> +/* True if start_epilogue should be emitted before following CFI directive. */
> +static bool emit_cfa_start_epilogue;
> +
> /* Add CFI to the current fde at the PC value indicated by LABEL if specified,
> or to the CIE if LABEL is NULL. */
>
> @@ -809,6 +818,17 @@ add_fde_cfi (const char *label, dw_cfi_ref cfi)
> {
> dw_cfi_ref *list_head;
>
> + if (emit_cfa_start_epilogue)
> + {
> + dw_cfi_ref cfi_epi_start;
> +
> + /* Emit the state save. */
> + emit_cfa_start_epilogue = false;
> + cfi_epi_start = new_cfi ();
> + cfi_epi_start->dw_cfi_opc = DW_CFA_GNU_start_epilogue;
> + add_fde_cfi (label, cfi_epi_start);
> + }
> +
> if (emit_cfa_remember)
> {
> dw_cfi_ref cfi_remember;
> @@ -2898,7 +2918,12 @@ dwarf2out_cfi_begin_epilogue (rtx insn)
> gcc_assert (i != NULL);
> i = next_real_insn (i);
> if (i == NULL)
> - return;
> + {
> + /* But we do mark the start of the epilogue to allow it to be skipped
> + in _eh frames. */
> + emit_cfa_start_epilogue = true;
> + return;
> + }
>
> /* Insert the restore before that next real insn in the stream, and before
> a potential NOTE_INSN_EPILOGUE_BEG -- we do need these notes to be
> @@ -2953,6 +2978,7 @@ dw_cfi_oprnd1_desc (enum dwarf_call_frame_info cfi
> case DW_CFA_GNU_window_save:
> case DW_CFA_remember_state:
> case DW_CFA_restore_state:
> + case DW_CFA_GNU_start_epilogue:
> return dw_cfi_oprnd_unused;
>
> case DW_CFA_set_loc:
> @@ -3121,6 +3148,10 @@ output_cfi (dw_cfi_ref cfi, dw_fde_ref fde, int fo
> dw2_asm_output_data (1, (cfi->dw_cfi_opc | (r & 0x3f)),
> "DW_CFA_restore, column %#lx", r);
> }
> + else if (cfi->dw_cfi_opc == DW_CFA_GNU_start_epilogue)
> +/* DEBUG */
> + fputs (ASM_COMMENT_START"\t\t\t"ASM_COMMENT_START
> + " DW_CFA_GNU_start_epilogue\n",asm_out_file);
> else
> {
> dw2_asm_output_data (1, cfi->dw_cfi_opc,
> @@ -3303,6 +3334,12 @@ output_cfi_directive (dw_cfi_ref cfi)
> cfi->dw_cfi_oprnd1.dw_cfi_offset);
> break;
>
> + case DW_CFA_GNU_start_epilogue:
> +/*DEBUG */
> + fputs (ASM_COMMENT_START"\t\t\t"ASM_COMMENT_START
> + " DW_CFA_GNU_start_epilogue\n",asm_out_file);
> + break;
> +
> case DW_CFA_remember_state:
> fprintf (asm_out_file, "\t.cfi_remember_state\n");
> break;
> @@ -3498,6 +3535,41 @@ output_cfis (dw_cfi_ref cfi, bool do_cfi_asm, dw_f
> }
> }
>
> +/* Output cfi skipping save/restore and epilogues in _eh frames
> + for targets that do not want them. */
> +
> +static dw_cfi_ref
> +emit_cfi_or_skip_epilogue (dw_cfi_ref cfi, dw_fde_ref fde, bool for_eh)
> +{
> + if (for_eh
> + && targetm.asm_out.suppress_eh_epilogue_p())
> + {
> + if (cfi->dw_cfi_opc == DW_CFA_remember_state)
> + {
> + /* Skip to the restore, unless there's an error and we fall off
> + the end. */
> + while (cfi->dw_cfi_next
> + && cfi->dw_cfi_opc != DW_CFA_restore_state)
> + cfi = cfi->dw_cfi_next;
> + return cfi;
> + }
> + if (cfi->dw_cfi_opc == DW_CFA_GNU_start_epilogue)
> + {
> +/*DEBUG */
> + fputs (ASM_COMMENT_START"\t\t\t"ASM_COMMENT_START
> + " DW_CFA_GNU_start_epilogue\n",asm_out_file);
> + while (cfi->dw_cfi_next)
> + /* Skip to the end. */
> + cfi = cfi->dw_cfi_next;
> + return cfi;
> + }
> + }
> +
> + /* if it's not a special case, then just emit it. */
> + output_cfi (cfi, fde, for_eh);
> + return cfi;
> +}
> +
> /* Output one FDE. */
>
> static void
> @@ -3613,13 +3685,13 @@ output_fde (dw_fde_ref fde, bool for_eh, bool seco
> fde->dw_fde_current_label = begin;
> if (!fde->dw_fde_switched_sections)
> for (cfi = fde->dw_fde_cfi; cfi != NULL; cfi = cfi->dw_cfi_next)
> - output_cfi (cfi, fde, for_eh);
> + cfi = emit_cfi_or_skip_epilogue (cfi, fde, for_eh);
> else if (!second)
> {
> if (fde->dw_fde_switch_cfi)
> for (cfi = fde->dw_fde_cfi; cfi != NULL; cfi = cfi->dw_cfi_next)
> {
> - output_cfi (cfi, fde, for_eh);
> + cfi = emit_cfi_or_skip_epilogue (cfi, fde, for_eh);
> if (cfi == fde->dw_fde_switch_cfi)
> break;
> }
> @@ -3636,7 +3707,7 @@ output_fde (dw_fde_ref fde, bool for_eh, bool seco
> fde->dw_fde_switch_cfi->dw_cfi_next = cfi_next;
> }
> for (cfi = cfi_next; cfi != NULL; cfi = cfi->dw_cfi_next)
> - output_cfi (cfi, fde, for_eh);
> + cfi = emit_cfi_or_skip_epilogue (cfi, fde, for_eh);
> }
>
> /* If we are to emit a ref/link from function bodies to their frame tables,
> Index: gcc/target.def
> ===================================================================
> --- gcc/target.def (revision 163221)
> +++ gcc/target.def (working copy)
> @@ -390,6 +406,15 @@ DEFHOOK
> void, (FILE *file, int size, rtx x),
> NULL)
>
> +/* Targets might not need epilogue information in dwarf2 _eh frames. This
> + hook should return true if the epilogue should be suppressed in such frames.
> + Epilogues will still be emitted in _debug_frames. */
> +DEFHOOK_UNDOC
> +(suppress_eh_epilogue_p,
> + "",
> + bool, (void),
> + hook_bool_void_false)
> +
> /* Some target machines need to postscan each insn after it is output. */
> DEFHOOK
> (final_postscan_insn,
> Index: gcc/config/darwin-protos.h
> ===================================================================
> --- gcc/config/darwin-protos.h (revision 163221)
> +++ gcc/config/darwin-protos.h (working copy)
> @@ -83,10 +84,15 @@ extern tree darwin_handle_weak_import_attribute (t
> extern void machopic_output_stub (FILE *, const char *, const char *);
> extern void darwin_globalize_label (FILE *, const char *);
> extern void darwin_assemble_visibility (tree, int);
> +
> +extern bool darwin_asm_suppress_eh_epilogue_p (void);
> +extern void darwin_asm_output_dwarf_section_start_label (FILE *file,
> + section *sect);
> extern void darwin_asm_output_dwarf_delta (FILE *, int, const char *,
> const char *);
> extern void darwin_asm_output_dwarf_offset (FILE *, int, const char *,
> section *);
> +
> extern void darwin_asm_declare_constant_name (FILE *, const char *,
> const_tree, HOST_WIDE_INT);
> extern bool darwin_binds_local_p (const_tree);
> Index: gcc/config/darwin.h
> ===================================================================
> --- gcc/config/darwin.h (revision 163221)
> +++ gcc/config/darwin.h (working copy)
> @@ -669,7 +675,6 @@ extern GTY(()) int darwin_ms_struct;
> Make Objective-C internal symbols local and in doing this, we need
> to accommodate the name mangling done by c++ on file scope locals. */
>
> -
> int darwin_label_is_anonymous_local_objc_name (const char *name);
>
> #undef ASM_OUTPUT_LABELREF
> @@ -927,6 +932,16 @@ enum machopic_addr_class {
> ? (DW_EH_PE_pcrel | DW_EH_PE_indirect | DW_EH_PE_sdata4) : \
> ((CODE) == 1 || (GLOBAL) == 0) ? DW_EH_PE_pcrel : DW_EH_PE_absptr)
>
> +/* Mark the start of each dwarf debug section to allow us to compute local
> + offsets within the sections. We do this in darwin, rather than emitting
> + relocs. */
> +#define TARGET_ASM_OUTPUT_DWARF_SECTION_START_LABEL \
> + darwin_asm_output_dwarf_section_start_label
> +
> +/* For OSX compatibility we do not want to emit epilogues in _eh frames. */
> +#define TARGET_ASM_SUPPRESS_EH_EPILOGUE_P \
> + darwin_asm_suppress_eh_epilogue_p
> +
> #define ASM_OUTPUT_DWARF_DELTA(FILE,SIZE,LABEL1,LABEL2) \
> darwin_asm_output_dwarf_delta (FILE, SIZE, LABEL1, LABEL2)
>
> Index: gcc/config/darwin.c
> ===================================================================
> --- gcc/config/darwin.c (revision 163221)
> +++ gcc/config/darwin.c (working copy)
> @@ -1666,6 +1666,36 @@ darwin_assemble_visibility (tree decl, int vis)
> "not supported in this configuration; ignored");
> }
>
> +/* For compatibility with OSX versions that do not emit epilogues in _eh
> + frames we suppress them. This is made a predicate function to permit
> + us to add an OSX/FSF compatibility switch should that be required. */
> +
> +bool
> +darwin_asm_suppress_eh_epilogue_p (void)
> +{
> + return true;
> +}
> +
> +/* So that we can compute dwarf offsets within sections, we emit a known
> + section marker at the begining of the section. This is distinct from
> + the ones emitted by dwarf2out. The label is constructed by extracting
> + sectname from __DWARF,__sectname,etc,etc. The hook should be invoked
> + once, after the first switch to the section. */
> +
> +void
> +darwin_asm_output_dwarf_section_start_label (FILE *file, section *sect)
> +{
> + const char *dnam;
> + int namelen;
> + gcc_assert (sect && (sect->common.flags & (SECTION_NAMED|SECTION_DEBUG)));
> + dnam = ((struct named_section *)sect)->name;
> + gcc_assert (strncmp (dnam, "__DWARF,", 8) == 0);
> + gcc_assert (strchr (dnam + 8, ','));
> +
> + namelen = strchr (dnam + 8, ',') - (dnam + 8);
> + fprintf (file, "Lsection%.*s:\n", namelen, dnam + 8);
> +}
> +
> /* Output a difference of two labels that will be an assembly time
> constant if the two labels are local. (.long lab1-lab2 will be
> very different if lab1 is at the boundary between two sections; it
>
>
>
>
>