From 3a4fb0cc2d9c0f87b0680a4dd5eb63b8632a3f1e Mon Sep 17 00:00:00 2001 From: Achim Gsell Date: Tue, 24 Jan 2017 15:59:34 +0100 Subject: [PATCH] Programming/gcc - work-around issue with empty functions and ld64 on Darwin --- ..._function_bodies_and_trailing_labels.patch | 394 ++++++++++++++++++ Programming/gcc/build | 3 + 2 files changed, 397 insertions(+) create mode 100644 Programming/gcc/5/PR57438_avoiding_empty_function_bodies_and_trailing_labels.patch diff --git a/Programming/gcc/5/PR57438_avoiding_empty_function_bodies_and_trailing_labels.patch b/Programming/gcc/5/PR57438_avoiding_empty_function_bodies_and_trailing_labels.patch new file mode 100644 index 0000000..6c6d0ed --- /dev/null +++ b/Programming/gcc/5/PR57438_avoiding_empty_function_bodies_and_trailing_labels.patch @@ -0,0 +1,394 @@ +diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c +index 9e9fe02..a60bc69 100644 +--- a/gcc/config/i386/i386.c ++++ b/gcc/config/i386/i386.c +@@ -11866,6 +11866,9 @@ ix86_code_end (void) + current_function_decl = decl; + allocate_struct_function (decl, false); + init_function_start (decl); ++ /* We're about to hide the function body from callees of final_* by ++ emitting it directly; tell them we're a thunk, if they care. */ ++ cfun->is_thunk = true; + first_function_block_is_cold = false; + /* Make sure unwind info is emitted for the thunk if needed. */ + final_start_function (emit_barrier (), asm_out_file, 1); +@@ -14562,36 +14565,65 @@ ix86_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED, HOST_WIDE_INT) + if (pic_offset_table_rtx + && !ix86_use_pseudo_pic_reg ()) + SET_REGNO (pic_offset_table_rtx, REAL_PIC_OFFSET_TABLE_REGNUM); +-#if TARGET_MACHO +- /* Mach-O doesn't support labels at the end of objects, so if +- it looks like we might want one, insert a NOP. */ +- { +- rtx_insn *insn = get_last_insn (); +- rtx_insn *deleted_debug_label = NULL; +- while (insn +- && NOTE_P (insn) +- && NOTE_KIND (insn) != NOTE_INSN_DELETED_LABEL) +- { +- /* Don't insert a nop for NOTE_INSN_DELETED_DEBUG_LABEL +- notes only, instead set their CODE_LABEL_NUMBER to -1, +- otherwise there would be code generation differences +- in between -g and -g0. */ +- if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_DELETED_DEBUG_LABEL) +- deleted_debug_label = insn; ++ ++ if (TARGET_MACHO) ++ { ++ rtx_insn *insn = get_last_insn (); ++ rtx_insn *deleted_debug_label = NULL; ++ ++ /* Mach-O doesn't support labels at the end of objects, so if ++ it looks like we might want one, take special action. ++ First, Collect any sequence deleted debug labels. */ ++ while (insn ++ && NOTE_P (insn) ++ && NOTE_KIND (insn) != NOTE_INSN_DELETED_LABEL) ++ { ++ /* Don't insert a nop for NOTE_INSN_DELETED_DEBUG_LABEL ++ notes only, instead set their CODE_LABEL_NUMBER to -1, ++ otherwise there would be code generation differences ++ in between -g and -g0. */ ++ if (NOTE_P (insn) && NOTE_KIND (insn) ++ == NOTE_INSN_DELETED_DEBUG_LABEL) ++ deleted_debug_label = insn; ++ insn = PREV_INSN (insn); ++ } ++ ++ /* If we have ++ label: ++ barrier ++ That need to be guarded. */ ++ ++ if (insn && BARRIER_P (insn)) + insn = PREV_INSN (insn); +- } +- if (insn +- && (LABEL_P (insn) +- || (NOTE_P (insn) +- && NOTE_KIND (insn) == NOTE_INSN_DELETED_LABEL))) +- fputs ("\tnop\n", file); +- else if (deleted_debug_label) +- for (insn = deleted_debug_label; insn; insn = NEXT_INSN (insn)) +- if (NOTE_KIND (insn) == NOTE_INSN_DELETED_DEBUG_LABEL) +- CODE_LABEL_NUMBER (insn) = -1; +- } +-#endif + ++ /* Up to now we've only seen notes or barriers. */ ++ if (insn) ++ { ++ if (LABEL_P (insn) ++ || (NOTE_P (insn) ++ && NOTE_KIND (insn) == NOTE_INSN_DELETED_LABEL)) ++ /* Trailing label. */ ++ fputs ("\tnop\n", file); ++ else if (cfun && ! cfun->is_thunk) ++ { ++ /* See if have a completely empty function body, skipping ++ the special case of the picbase thunk emitted as asm. */ ++ while (insn && ! INSN_P (insn)) ++ insn = PREV_INSN (insn); ++ /* If we don't find any, we've got an empty function body; i.e. ++ completely empty - without a return or branch. GCC declares ++ that reaching __builtin_unreachable() means UB (but we want ++ finite-sized function bodies; to help the user out, let's ++ trap the case. */ ++ if (insn == NULL) ++ fputs ("\tud2\n", file); ++ } ++ } ++ else if (deleted_debug_label) ++ for (insn = deleted_debug_label; insn; insn = NEXT_INSN (insn)) ++ if (NOTE_KIND (insn) == NOTE_INSN_DELETED_DEBUG_LABEL) ++ CODE_LABEL_NUMBER (insn) = -1; ++ } + } + + /* Return a scratch register to use in the split stack prologue. The +diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c +index b0d2b64..326e2e9 100644 +--- a/gcc/config/rs6000/rs6000.c ++++ b/gcc/config/rs6000/rs6000.c +@@ -30148,11 +30148,16 @@ rs6000_output_function_epilogue (FILE *file, + { + #if TARGET_MACHO + macho_branch_islands (); +- /* Mach-O doesn't support labels at the end of objects, so if +- it looks like we might want one, insert a NOP. */ ++ ++ if (TARGET_MACHO) + { + rtx_insn *insn = get_last_insn (); + rtx_insn *deleted_debug_label = NULL; ++ ++ /* Mach-O doesn't support labels at the end of objects, so if ++ it looks like we might want one, take special action. ++ ++ First, collect any sequence deleted debug labels. */ + while (insn + && NOTE_P (insn) + && NOTE_KIND (insn) != NOTE_INSN_DELETED_LABEL) +@@ -30165,11 +30170,37 @@ rs6000_output_function_epilogue (FILE *file, + deleted_debug_label = insn; + insn = PREV_INSN (insn); + } +- if (insn +- && (LABEL_P (insn) ++ ++ /* If we have: ++ label: ++ barrier ++ That need to be guarded. */ ++ ++ if (insn && BARRIER_P (insn)) ++ insn = PREV_INSN (insn); ++ ++ /* up to now we've only seen notes or barriers. */ ++ if (insn) ++ { ++ if (LABEL_P (insn) + || (NOTE_P (insn) +- && NOTE_KIND (insn) == NOTE_INSN_DELETED_LABEL))) +- fputs ("\tnop\n", file); ++ && NOTE_KIND (insn) == NOTE_INSN_DELETED_LABEL)) ++ /* Trailing label: . */ ++ fputs ("\tnop\n", file); ++ else ++ { ++ /* See if have a completely empty function body. */ ++ while (insn && ! INSN_P (insn)) ++ insn = PREV_INSN (insn); ++ /* If we don't find any, we've got an empty function body; i.e. ++ completely empty - without a return or branch. GCC declares ++ that reaching __builtin_unreachable() means UB (but we want ++ finite-sized function bodies; to help the user out, let's trap ++ the case. */ ++ if (insn == NULL) ++ fputs ("\ttrap\n", file); ++ } ++ } + else if (deleted_debug_label) + for (insn = deleted_debug_label; insn; insn = NEXT_INSN (insn)) + if (NOTE_KIND (insn) == NOTE_INSN_DELETED_DEBUG_LABEL) +diff --git a/gcc/testsuite/gcc.dg/pr57438-1.c b/gcc/testsuite/gcc.dg/pr57438-1.c +new file mode 100644 +index 0000000..809c96d +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/pr57438-1.c +@@ -0,0 +1,15 @@ ++/* { dg-do compile { target *-*-darwin* } } */ ++/* { dg-options "-m32 -O1" } */ ++ ++/* This is testing that a completely empty function body results in the ++ insertion of a ud2/trap instruction to prevent a zero-sized FDE, and/or ++ the function label apparently pointing to following code. */ ++ ++__attribute__((noinline)) ++void foo (void) ++{ ++ __builtin_unreachable(); ++} ++ ++/* { dg-final { scan-assembler "ud2" { target { i?86-*-darwin* x86_64-*-darwin* } } } } */ ++/* { dg-final { scan-assembler "trap" { target { powerpc*-*-darwin* } } } } */ +diff --git a/gcc/testsuite/gcc.dg/pr57438-2.c b/gcc/testsuite/gcc.dg/pr57438-2.c +new file mode 100644 +index 0000000..6d67dad +--- /dev/null ++++ b/gcc/testsuite/gcc.dg/pr57438-2.c +@@ -0,0 +1,194 @@ ++/* { dg-do compile { target *-*-darwin* } } */ ++/* { dg-options "-m32 -O1" } */ ++ ++/* This is testing that a trailing local label is followed by a ++ nop where required. */ ++ ++extern int Error(void *, const char *); ++ ++typedef enum { ++ Match_MissingFeature, ++ Match_InvalidOperand, ++ Match_InvalidSuffix, ++ Match_InvalidCondCode, ++ Match_AddSubRegExtendSmall, ++ Match_AddSubRegExtendLarge, ++ Match_AddSubSecondSource, ++ Match_LogicalSecondSource, ++ Match_InvalidMovImm32Shift, ++ Match_InvalidMovImm64Shift, ++ Match_AddSubRegShift32, ++ Match_AddSubRegShift64, ++ Match_InvalidFPImm, ++ Match_InvalidMemoryIndexedSImm9, ++ Match_InvalidMemoryIndexed4SImm7, ++ Match_InvalidMemoryIndexed8SImm7, ++ Match_InvalidMemoryIndexed16SImm7, ++ Match_InvalidMemoryWExtend8, ++ Match_InvalidMemoryWExtend16, ++ Match_InvalidMemoryWExtend32, ++ Match_InvalidMemoryWExtend64, ++ Match_InvalidMemoryWExtend128, ++ Match_InvalidMemoryXExtend8, ++ Match_InvalidMemoryXExtend16, ++ Match_InvalidMemoryXExtend32, ++ Match_InvalidMemoryXExtend64, ++ Match_InvalidMemoryXExtend128, ++ Match_InvalidMemoryIndexed1, ++ Match_InvalidMemoryIndexed2, ++ Match_InvalidMemoryIndexed4, ++ Match_InvalidMemoryIndexed8, ++ Match_InvalidMemoryIndexed16, ++ Match_InvalidImm0_1, ++ Match_InvalidImm0_7, ++ Match_InvalidImm0_15, ++ Match_InvalidImm0_31, ++ Match_InvalidImm0_63, ++ Match_InvalidImm0_127, ++ Match_InvalidImm0_65535, ++ Match_InvalidImm1_8, ++ Match_InvalidImm1_16, ++ Match_InvalidImm1_32, ++ Match_InvalidImm1_64, ++ Match_InvalidIndex1, ++ Match_InvalidIndexB, ++ Match_InvalidIndexH, ++ Match_InvalidIndexS, ++ Match_InvalidIndexD, ++ Match_InvalidLabel, ++ Match_MRS, ++ Match_MSR, ++ Match_MnemonicFail ++} ErrCode; ++ ++//bool showMatchError(void *, unsigned ) __attribute__((__weak__)); ++ ++int showMatchError(void *Loc, unsigned ErrCode) { ++ switch (ErrCode) { ++ case Match_MissingFeature: ++ return Error(Loc, ++ "instruction requires a CPU feature not currently enabled"); ++ case Match_InvalidOperand: ++ return Error(Loc, "invalid operand for instruction"); ++ case Match_InvalidSuffix: ++ return Error(Loc, "invalid type suffix for instruction"); ++ case Match_InvalidCondCode: ++ return Error(Loc, "expected AArch64 condition code"); ++ case Match_AddSubRegExtendSmall: ++ return Error(Loc, ++ "expected '[su]xt[bhw]' or 'lsl' with optional integer in range [0, 4]"); ++ case Match_AddSubRegExtendLarge: ++ return Error(Loc, ++ "expected 'sxtx' 'uxtx' or 'lsl' with optional integer in range [0, 4]"); ++ case Match_AddSubSecondSource: ++ return Error(Loc, ++ "expected compatible register, symbol or integer in range [0, 4095]"); ++ case Match_LogicalSecondSource: ++ return Error(Loc, "expected compatible register or logical immediate"); ++ case Match_InvalidMovImm32Shift: ++ return Error(Loc, "expected 'lsl' with optional integer 0 or 16"); ++ case Match_InvalidMovImm64Shift: ++ return Error(Loc, "expected 'lsl' with optional integer 0, 16, 32 or 48"); ++ case Match_AddSubRegShift32: ++ return Error(Loc, ++ "expected 'lsl', 'lsr' or 'asr' with optional integer in range [0, 31]"); ++ case Match_AddSubRegShift64: ++ return Error(Loc, ++ "expected 'lsl', 'lsr' or 'asr' with optional integer in range [0, 63]"); ++ case Match_InvalidFPImm: ++ return Error(Loc, ++ "expected compatible register or floating-point constant"); ++ case Match_InvalidMemoryIndexedSImm9: ++ return Error(Loc, "index must be an integer in range [-256, 255]."); ++ case Match_InvalidMemoryIndexed4SImm7: ++ return Error(Loc, "index must be a multiple of 4 in range [-256, 252]."); ++ case Match_InvalidMemoryIndexed8SImm7: ++ return Error(Loc, "index must be a multiple of 8 in range [-512, 504]."); ++ case Match_InvalidMemoryIndexed16SImm7: ++ return Error(Loc, "index must be a multiple of 16 in range [-1024, 1008]."); ++ case Match_InvalidMemoryWExtend8: ++ return Error(Loc, ++ "expected 'uxtw' or 'sxtw' with optional shift of #0"); ++ case Match_InvalidMemoryWExtend16: ++ return Error(Loc, ++ "expected 'uxtw' or 'sxtw' with optional shift of #0 or #1"); ++ case Match_InvalidMemoryWExtend32: ++ return Error(Loc, ++ "expected 'uxtw' or 'sxtw' with optional shift of #0 or #2"); ++ case Match_InvalidMemoryWExtend64: ++ return Error(Loc, ++ "expected 'uxtw' or 'sxtw' with optional shift of #0 or #3"); ++ case Match_InvalidMemoryWExtend128: ++ return Error(Loc, ++ "expected 'uxtw' or 'sxtw' with optional shift of #0 or #4"); ++ case Match_InvalidMemoryXExtend8: ++ return Error(Loc, ++ "expected 'lsl' or 'sxtx' with optional shift of #0"); ++ case Match_InvalidMemoryXExtend16: ++ return Error(Loc, ++ "expected 'lsl' or 'sxtx' with optional shift of #0 or #1"); ++ case Match_InvalidMemoryXExtend32: ++ return Error(Loc, ++ "expected 'lsl' or 'sxtx' with optional shift of #0 or #2"); ++ case Match_InvalidMemoryXExtend64: ++ return Error(Loc, ++ "expected 'lsl' or 'sxtx' with optional shift of #0 or #3"); ++ case Match_InvalidMemoryXExtend128: ++ return Error(Loc, ++ "expected 'lsl' or 'sxtx' with optional shift of #0 or #4"); ++ case Match_InvalidMemoryIndexed1: ++ return Error(Loc, "index must be an integer in range [0, 4095]."); ++ case Match_InvalidMemoryIndexed2: ++ return Error(Loc, "index must be a multiple of 2 in range [0, 8190]."); ++ case Match_InvalidMemoryIndexed4: ++ return Error(Loc, "index must be a multiple of 4 in range [0, 16380]."); ++ case Match_InvalidMemoryIndexed8: ++ return Error(Loc, "index must be a multiple of 8 in range [0, 32760]."); ++ case Match_InvalidMemoryIndexed16: ++ return Error(Loc, "index must be a multiple of 16 in range [0, 65520]."); ++ case Match_InvalidImm0_1: ++ return Error(Loc, "immediate must be an integer in range [0, 1]."); ++ case Match_InvalidImm0_7: ++ return Error(Loc, "immediate must be an integer in range [0, 7]."); ++ case Match_InvalidImm0_15: ++ return Error(Loc, "immediate must be an integer in range [0, 15]."); ++ case Match_InvalidImm0_31: ++ return Error(Loc, "immediate must be an integer in range [0, 31]."); ++ case Match_InvalidImm0_63: ++ return Error(Loc, "immediate must be an integer in range [0, 63]."); ++ case Match_InvalidImm0_127: ++ return Error(Loc, "immediate must be an integer in range [0, 127]."); ++ case Match_InvalidImm0_65535: ++ return Error(Loc, "immediate must be an integer in range [0, 65535]."); ++ case Match_InvalidImm1_8: ++ return Error(Loc, "immediate must be an integer in range [1, 8]."); ++ case Match_InvalidImm1_16: ++ return Error(Loc, "immediate must be an integer in range [1, 16]."); ++ case Match_InvalidImm1_32: ++ return Error(Loc, "immediate must be an integer in range [1, 32]."); ++ case Match_InvalidImm1_64: ++ return Error(Loc, "immediate must be an integer in range [1, 64]."); ++ case Match_InvalidIndex1: ++ return Error(Loc, "expected lane specifier '[1]'"); ++ case Match_InvalidIndexB: ++ return Error(Loc, "vector lane must be an integer in range [0, 15]."); ++ case Match_InvalidIndexH: ++ return Error(Loc, "vector lane must be an integer in range [0, 7]."); ++ case Match_InvalidIndexS: ++ return Error(Loc, "vector lane must be an integer in range [0, 3]."); ++ case Match_InvalidIndexD: ++ return Error(Loc, "vector lane must be an integer in range [0, 1]."); ++ case Match_InvalidLabel: ++ return Error(Loc, "expected label or encodable integer pc offset"); ++ case Match_MRS: ++ return Error(Loc, "expected readable system register"); ++ case Match_MSR: ++ return Error(Loc, "expected writable system register or pstate"); ++ case Match_MnemonicFail: ++ return Error(Loc, "unrecognized instruction mnemonic"); ++ default: ++ __builtin_unreachable(); ++ } ++} ++ ++/* { dg-final { scan-assembler "nop\\nLFE.*" { target { *-*-darwin* } } } } */ diff --git a/Programming/gcc/build b/Programming/gcc/build index c7bd9b8..c2189a5 100755 --- a/Programming/gcc/build +++ b/Programming/gcc/build @@ -6,6 +6,9 @@ pbuild::patch_sources_Darwin() { patch -p1 < "${BUILD_BLOCK_DIR}/${V_MAJOR}/non-dead-strip.patch" patch -p1 < "${BUILD_BLOCK_DIR}/${V_MAJOR}/configure-as.patch" fi + if (( V_MAJOR == 5 )); then + patch -p1 < "${BUILD_BLOCK_DIR}/${V_MAJOR}/PR57438_avoiding_empty_function_bodies_and_trailing_labels.patch" + fi } pbuild::configure() {