aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2020-02-03 10:22:57 -0800
committerH.J. Lu <hjl.tools@gmail.com>2020-06-11 20:45:19 -0700
commit3dcea658c9e2ac84f0726e679fd7d3b14f9106f0 (patch)
tree8d46980f42dd2aa7d8ab08541276d2ebe7398bed /gcc
parente68e80c8ddb961130b59528d1b865a7143c40537 (diff)
downloadgcc-3dcea658c9e2ac84f0726e679fd7d3b14f9106f0.zip
gcc-3dcea658c9e2ac84f0726e679fd7d3b14f9106f0.tar.gz
gcc-3dcea658c9e2ac84f0726e679fd7d3b14f9106f0.tar.bz2
x86: Add UNSPECV_PATCHABLE_AREA
Currently patchable area is at the wrong place. It is placed immediately after function label, before both .cfi_startproc and ENDBR. This patch adds UNSPECV_PATCHABLE_AREA for pseudo patchable area instruction and changes ENDBR insertion pass to also insert patchable area instruction. TARGET_ASM_PRINT_PATCHABLE_FUNCTION_ENTRY is defined to avoid placing patchable area before .cfi_startproc and ENDBR. gcc/ PR target/93492 * config/i386/i386-features.c (rest_of_insert_endbranch): Renamed to ... (rest_of_insert_endbr_and_patchable_area): Change return type to void. Add need_endbr and patchable_area_size arguments. Don't call timevar_push nor timevar_pop. Replace endbr_queued_at_entrance with insn_queued_at_entrance. Insert UNSPECV_PATCHABLE_AREA for patchable area. (pass_data_insert_endbranch): Renamed to ... (pass_data_insert_endbr_and_patchable_area): This. Change pass name to endbr_and_patchable_area. (pass_insert_endbranch): Renamed to ... (pass_insert_endbr_and_patchable_area): This. Add need_endbr and patchable_area_size;. (pass_insert_endbr_and_patchable_area::gate): Set and check need_endbr and patchable_area_size. (pass_insert_endbr_and_patchable_area::execute): Call timevar_push and timevar_pop. Pass need_endbr and patchable_area_size to rest_of_insert_endbr_and_patchable_area. (make_pass_insert_endbranch): Renamed to ... (make_pass_insert_endbr_and_patchable_area): This. * config/i386/i386-passes.def: Replace pass_insert_endbranch with pass_insert_endbr_and_patchable_area. * config/i386/i386-protos.h (ix86_output_patchable_area): New. (make_pass_insert_endbranch): Renamed to ... (make_pass_insert_endbr_and_patchable_area): This. * config/i386/i386.c (ix86_asm_output_function_label): Set function_label_emitted to true. (ix86_print_patchable_function_entry): New function. (ix86_output_patchable_area): Likewise. (x86_function_profiler): Replace endbr_queued_at_entrance with insn_queued_at_entrance. Generate ENDBR only for TYPE_ENDBR. Call ix86_output_patchable_area to generate patchable area if needed. (TARGET_ASM_PRINT_PATCHABLE_FUNCTION_ENTRY): New. * config/i386/i386.h (queued_insn_type): New. (machine_function): Add function_label_emitted. Replace endbr_queued_at_entrance with insn_queued_at_entrance. * config/i386/i386.md (UNSPECV_PATCHABLE_AREA): New. (patchable_area): New. gcc/testsuite/ PR target/93492 * gcc.target/i386/pr93492-1.c: New test. * gcc.target/i386/pr93492-2.c: Likewise. * gcc.target/i386/pr93492-3.c: Likewise. * gcc.target/i386/pr93492-4.c: Likewise. * gcc.target/i386/pr93492-5.c: Likewise.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/config/i386/i386-features.c142
-rw-r--r--gcc/config/i386/i386-passes.def2
-rw-r--r--gcc/config/i386/i386-protos.h5
-rw-r--r--gcc/config/i386/i386.c51
-rw-r--r--gcc/config/i386/i386.h14
-rw-r--r--gcc/config/i386/i386.md17
-rw-r--r--gcc/testsuite/gcc.target/i386/pr93492-1.c73
-rw-r--r--gcc/testsuite/gcc.target/i386/pr93492-2.c12
-rw-r--r--gcc/testsuite/gcc.target/i386/pr93492-3.c13
-rw-r--r--gcc/testsuite/gcc.target/i386/pr93492-4.c11
-rw-r--r--gcc/testsuite/gcc.target/i386/pr93492-5.c12
11 files changed, 296 insertions, 56 deletions
diff --git a/gcc/config/i386/i386-features.c b/gcc/config/i386/i386-features.c
index b9b764c..535fc7e 100644
--- a/gcc/config/i386/i386-features.c
+++ b/gcc/config/i386/i386-features.c
@@ -1946,48 +1946,83 @@ make_pass_stv (gcc::context *ctxt)
return new pass_stv (ctxt);
}
-/* Inserting ENDBRANCH instructions. */
+/* Inserting ENDBR and pseudo patchable-area instructions. */
-static unsigned int
-rest_of_insert_endbranch (void)
+static void
+rest_of_insert_endbr_and_patchable_area (bool need_endbr,
+ unsigned int patchable_area_size)
{
- timevar_push (TV_MACH_DEP);
-
- rtx cet_eb;
+ rtx endbr;
rtx_insn *insn;
+ rtx_insn *endbr_insn = NULL;
basic_block bb;
- /* Currently emit EB if it's a tracking function, i.e. 'nocf_check' is
- absent among function attributes. Later an optimization will be
- introduced to make analysis if an address of a static function is
- taken. A static function whose address is not taken will get a
- nocf_check attribute. This will allow to reduce the number of EB. */
-
- if (!lookup_attribute ("nocf_check",
- TYPE_ATTRIBUTES (TREE_TYPE (cfun->decl)))
- && (!flag_manual_endbr
- || lookup_attribute ("cf_check",
- DECL_ATTRIBUTES (cfun->decl)))
- && (!cgraph_node::get (cfun->decl)->only_called_directly_p ()
- || ix86_cmodel == CM_LARGE
- || ix86_cmodel == CM_LARGE_PIC
- || flag_force_indirect_call
- || (TARGET_DLLIMPORT_DECL_ATTRIBUTES
- && DECL_DLLIMPORT_P (cfun->decl))))
- {
- /* Queue ENDBR insertion to x86_function_profiler. */
+ if (need_endbr)
+ {
+ /* Currently emit EB if it's a tracking function, i.e. 'nocf_check'
+ is absent among function attributes. Later an optimization will
+ be introduced to make analysis if an address of a static function
+ is taken. A static function whose address is not taken will get
+ a nocf_check attribute. This will allow to reduce the number of
+ EB. */
+ if (!lookup_attribute ("nocf_check",
+ TYPE_ATTRIBUTES (TREE_TYPE (cfun->decl)))
+ && (!flag_manual_endbr
+ || lookup_attribute ("cf_check",
+ DECL_ATTRIBUTES (cfun->decl)))
+ && (!cgraph_node::get (cfun->decl)->only_called_directly_p ()
+ || ix86_cmodel == CM_LARGE
+ || ix86_cmodel == CM_LARGE_PIC
+ || flag_force_indirect_call
+ || (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+ && DECL_DLLIMPORT_P (cfun->decl))))
+ {
+ if (crtl->profile && flag_fentry)
+ {
+ /* Queue ENDBR insertion to x86_function_profiler.
+ NB: Any patchable-area insn will be inserted after
+ ENDBR. */
+ cfun->machine->insn_queued_at_entrance = TYPE_ENDBR;
+ }
+ else
+ {
+ endbr = gen_nop_endbr ();
+ bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb;
+ rtx_insn *insn = BB_HEAD (bb);
+ endbr_insn = emit_insn_before (endbr, insn);
+ }
+ }
+ }
+
+ if (patchable_area_size)
+ {
if (crtl->profile && flag_fentry)
- cfun->machine->endbr_queued_at_entrance = true;
+ {
+ /* Queue patchable-area insertion to x86_function_profiler.
+ NB: If there is a queued ENDBR, x86_function_profiler
+ will also handle patchable-area. */
+ if (!cfun->machine->insn_queued_at_entrance)
+ cfun->machine->insn_queued_at_entrance = TYPE_PATCHABLE_AREA;
+ }
else
{
- cet_eb = gen_nop_endbr ();
-
- bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb;
- insn = BB_HEAD (bb);
- emit_insn_before (cet_eb, insn);
+ rtx patchable_area
+ = gen_patchable_area (GEN_INT (patchable_area_size),
+ GEN_INT (crtl->patch_area_entry == 0));
+ if (endbr_insn)
+ emit_insn_after (patchable_area, endbr_insn);
+ else
+ {
+ bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb;
+ insn = BB_HEAD (bb);
+ emit_insn_before (patchable_area, insn);
+ }
}
}
+ if (!need_endbr)
+ return;
+
bb = 0;
FOR_EACH_BB_FN (bb, cfun)
{
@@ -1996,7 +2031,6 @@ rest_of_insert_endbranch (void)
{
if (CALL_P (insn))
{
- bool need_endbr;
need_endbr = find_reg_note (insn, REG_SETJMP, NULL) != NULL;
if (!need_endbr && !SIBLING_CALL_P (insn))
{
@@ -2027,8 +2061,8 @@ rest_of_insert_endbranch (void)
/* Generate ENDBRANCH after CALL, which can return more than
twice, setjmp-like functions. */
- cet_eb = gen_nop_endbr ();
- emit_insn_after_setloc (cet_eb, insn, INSN_LOCATION (insn));
+ endbr = gen_nop_endbr ();
+ emit_insn_after_setloc (endbr, insn, INSN_LOCATION (insn));
continue;
}
@@ -2058,31 +2092,30 @@ rest_of_insert_endbranch (void)
dest_blk = e->dest;
insn = BB_HEAD (dest_blk);
gcc_assert (LABEL_P (insn));
- cet_eb = gen_nop_endbr ();
- emit_insn_after (cet_eb, insn);
+ endbr = gen_nop_endbr ();
+ emit_insn_after (endbr, insn);
}
continue;
}
if (LABEL_P (insn) && LABEL_PRESERVE_P (insn))
{
- cet_eb = gen_nop_endbr ();
- emit_insn_after (cet_eb, insn);
+ endbr = gen_nop_endbr ();
+ emit_insn_after (endbr, insn);
continue;
}
}
}
- timevar_pop (TV_MACH_DEP);
- return 0;
+ return;
}
namespace {
-const pass_data pass_data_insert_endbranch =
+const pass_data pass_data_insert_endbr_and_patchable_area =
{
RTL_PASS, /* type. */
- "cet", /* name. */
+ "endbr_and_patchable_area", /* name. */
OPTGROUP_NONE, /* optinfo_flags. */
TV_MACH_DEP, /* tv_id. */
0, /* properties_required. */
@@ -2092,32 +2125,41 @@ const pass_data pass_data_insert_endbranch =
0, /* todo_flags_finish. */
};
-class pass_insert_endbranch : public rtl_opt_pass
+class pass_insert_endbr_and_patchable_area : public rtl_opt_pass
{
public:
- pass_insert_endbranch (gcc::context *ctxt)
- : rtl_opt_pass (pass_data_insert_endbranch, ctxt)
+ pass_insert_endbr_and_patchable_area (gcc::context *ctxt)
+ : rtl_opt_pass (pass_data_insert_endbr_and_patchable_area, ctxt)
{}
/* opt_pass methods: */
virtual bool gate (function *)
{
- return ((flag_cf_protection & CF_BRANCH));
+ need_endbr = (flag_cf_protection & CF_BRANCH) != 0;
+ patchable_area_size = crtl->patch_area_size - crtl->patch_area_entry;
+ return need_endbr || patchable_area_size;
}
virtual unsigned int execute (function *)
{
- return rest_of_insert_endbranch ();
+ timevar_push (TV_MACH_DEP);
+ rest_of_insert_endbr_and_patchable_area (need_endbr,
+ patchable_area_size);
+ timevar_pop (TV_MACH_DEP);
+ return 0;
}
-}; // class pass_insert_endbranch
+private:
+ bool need_endbr;
+ unsigned int patchable_area_size;
+}; // class pass_insert_endbr_and_patchable_area
} // anon namespace
rtl_opt_pass *
-make_pass_insert_endbranch (gcc::context *ctxt)
+make_pass_insert_endbr_and_patchable_area (gcc::context *ctxt)
{
- return new pass_insert_endbranch (ctxt);
+ return new pass_insert_endbr_and_patchable_area (ctxt);
}
/* At entry of the nearest common dominator for basic blocks with
diff --git a/gcc/config/i386/i386-passes.def b/gcc/config/i386/i386-passes.def
index 41386a1..d83c7b9 100644
--- a/gcc/config/i386/i386-passes.def
+++ b/gcc/config/i386/i386-passes.def
@@ -30,6 +30,6 @@ along with GCC; see the file COPYING3. If not see
CONSTM1_RTX generated by the STV pass can be CSEed. */
INSERT_PASS_BEFORE (pass_cse2, 1, pass_stv, true /* timode_p */);
- INSERT_PASS_BEFORE (pass_shorten_branches, 1, pass_insert_endbranch);
+ INSERT_PASS_BEFORE (pass_shorten_branches, 1, pass_insert_endbr_and_patchable_area);
INSERT_PASS_AFTER (pass_combine, 1, pass_remove_partial_avx_dependency);
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 39fcaa0..e557449 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -89,6 +89,8 @@ extern const char *output_fp_compare (rtx_insn *, rtx*, bool, bool);
extern const char *output_adjust_stack_and_probe (rtx);
extern const char *output_probe_stack_range (rtx, rtx);
+extern void ix86_output_patchable_area (unsigned int, bool);
+
extern void ix86_expand_clear (rtx);
extern void ix86_expand_move (machine_mode, rtx[]);
extern void ix86_expand_vector_move (machine_mode, rtx[]);
@@ -378,6 +380,7 @@ class rtl_opt_pass;
extern rtl_opt_pass *make_pass_insert_vzeroupper (gcc::context *);
extern rtl_opt_pass *make_pass_stv (gcc::context *);
-extern rtl_opt_pass *make_pass_insert_endbranch (gcc::context *);
+extern rtl_opt_pass *make_pass_insert_endbr_and_patchable_area
+ (gcc::context *);
extern rtl_opt_pass *make_pass_remove_partial_avx_dependency
(gcc::context *);
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 060e2df..3b776c0 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -1562,6 +1562,9 @@ ix86_asm_output_function_label (FILE *asm_out_file, const char *fname,
{
bool is_ms_hook = ix86_function_ms_hook_prologue (decl);
+ if (cfun)
+ cfun->machine->function_label_emitted = true;
+
if (is_ms_hook)
{
int i, filler_count = (TARGET_64BIT ? 32 : 16);
@@ -9367,6 +9370,38 @@ ix86_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED)
}
}
+/* Implement TARGET_ASM_PRINT_PATCHABLE_FUNCTION_ENTRY. */
+
+void
+ix86_print_patchable_function_entry (FILE *file,
+ unsigned HOST_WIDE_INT patch_area_size,
+ bool record_p)
+{
+ if (cfun->machine->function_label_emitted)
+ {
+ /* NB: When ix86_print_patchable_function_entry is called after
+ function table has been emitted, we have inserted or queued
+ a pseudo UNSPECV_PATCHABLE_AREA instruction at the proper
+ place. There is nothing to do here. */
+ return;
+ }
+
+ default_print_patchable_function_entry (file, patch_area_size,
+ record_p);
+}
+
+/* Output patchable area. NB: default_print_patchable_function_entry
+ isn't available in i386.md. */
+
+void
+ix86_output_patchable_area (unsigned int patch_area_size,
+ bool record_p)
+{
+ default_print_patchable_function_entry (asm_out_file,
+ patch_area_size,
+ record_p);
+}
+
/* Return a scratch register to use in the split stack prologue. The
split stack prologue is used for -fsplit-stack. It is the first
instructions in the function, even before the regular prologue.
@@ -20418,8 +20453,16 @@ current_fentry_section (const char **name)
void
x86_function_profiler (FILE *file, int labelno ATTRIBUTE_UNUSED)
{
- if (cfun->machine->endbr_queued_at_entrance)
- fprintf (file, "\t%s\n", TARGET_64BIT ? "endbr64" : "endbr32");
+ if (cfun->machine->insn_queued_at_entrance)
+ {
+ if (cfun->machine->insn_queued_at_entrance == TYPE_ENDBR)
+ fprintf (file, "\t%s\n", TARGET_64BIT ? "endbr64" : "endbr32");
+ unsigned int patch_area_size
+ = crtl->patch_area_size - crtl->patch_area_entry;
+ if (patch_area_size)
+ ix86_output_patchable_area (patch_area_size,
+ crtl->patch_area_entry == 0);
+ }
const char *mcount_name = MCOUNT_NAME;
@@ -23019,6 +23062,10 @@ ix86_run_selftests (void)
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE ix86_output_function_epilogue
+#undef TARGET_ASM_PRINT_PATCHABLE_FUNCTION_ENTRY
+#define TARGET_ASM_PRINT_PATCHABLE_FUNCTION_ENTRY \
+ ix86_print_patchable_function_entry
+
#undef TARGET_ENCODE_SECTION_INFO
#ifndef SUBTARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO ix86_encode_section_info
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 5bae257..5d0ea16 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -2761,6 +2761,13 @@ enum function_type
TYPE_EXCEPTION
};
+enum queued_insn_type
+{
+ TYPE_NONE = 0,
+ TYPE_ENDBR,
+ TYPE_PATCHABLE_AREA
+};
+
struct GTY(()) machine_function {
struct stack_local_entry *stack_locals;
int varargs_gpr_size;
@@ -2851,8 +2858,11 @@ struct GTY(()) machine_function {
/* Nonzero if the function places outgoing arguments on stack. */
BOOL_BITFIELD outgoing_args_on_stack : 1;
- /* If true, ENDBR is queued at function entrance. */
- BOOL_BITFIELD endbr_queued_at_entrance : 1;
+ /* If true, ENDBR or patchable area is queued at function entrance. */
+ ENUM_BITFIELD(queued_insn_type) insn_queued_at_entrance : 2;
+
+ /* If true, the function label has been emitted. */
+ BOOL_BITFIELD function_label_emitted : 1;
/* True if the function needs a stack frame. */
BOOL_BITFIELD stack_frame_required : 1;
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 9db7469..4d45558 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -307,6 +307,9 @@
;; For SERIALIZE support
UNSPECV_SERIALIZE
+
+ ;; For patchable area support
+ UNSPECV_PATCHABLE_AREA
])
;; Constants to represent rounding modes in the ROUND instruction
@@ -21529,6 +21532,20 @@
[(set_attr "type" "other")
(set_attr "length" "3")])
+(define_insn "patchable_area"
+ [(unspec_volatile [(match_operand 0 "const_int_operand")
+ (match_operand 1 "const_int_operand")]
+ UNSPECV_PATCHABLE_AREA)]
+ ""
+{
+ ix86_output_patchable_area (INTVAL (operands[0]),
+ INTVAL (operands[1]) != 0);
+ return "";
+}
+ [(set (attr "length") (symbol_ref "INTVAL (operands[0])"))
+ (set_attr "length_immediate" "0")
+ (set_attr "modrm" "0")])
+
(include "mmx.md")
(include "sse.md")
(include "sync.md")
diff --git a/gcc/testsuite/gcc.target/i386/pr93492-1.c b/gcc/testsuite/gcc.target/i386/pr93492-1.c
new file mode 100644
index 0000000..f978d2e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr93492-1.c
@@ -0,0 +1,73 @@
+/* { dg-do "compile" } */
+/* { dg-options "-O1 -fcf-protection -mmanual-endbr" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+/* Note: this test only checks the instructions in the function bodies,
+ not the placement of the patch label or nops before the function. */
+
+/*
+**f10_none:
+** nop
+** ret
+*/
+void
+__attribute__ ((nocf_check,patchable_function_entry (1, 0)))
+f10_none (void)
+{
+}
+
+/*
+**f10_endbr:
+** endbr(32|64)
+** nop
+** ret
+*/
+void
+__attribute__ ((cf_check,patchable_function_entry (1, 0)))
+f10_endbr (void)
+{
+}
+
+/*
+**f11_none:
+** ret
+*/
+void
+__attribute__ ((nocf_check,patchable_function_entry (1, 1)))
+f11_none (void)
+{
+}
+
+/*
+**f11_endbr:
+** endbr(32|64)
+** ret
+*/
+void
+__attribute__ ((cf_check,patchable_function_entry (1, 1)))
+f11_endbr (void)
+{
+}
+
+/*
+**f21_none:
+** nop
+** ret
+*/
+void
+__attribute__ ((nocf_check,patchable_function_entry (2, 1)))
+f21_none (void)
+{
+}
+
+/*
+**f21_endbr:
+** endbr(32|64)
+** nop
+** ret
+*/
+void
+__attribute__ ((cf_check,patchable_function_entry (2, 1)))
+f21_endbr (void)
+{
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr93492-2.c b/gcc/testsuite/gcc.target/i386/pr93492-2.c
new file mode 100644
index 0000000..ec26d4c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr93492-2.c
@@ -0,0 +1,12 @@
+/* { dg-do "compile" } */
+/* { dg-options "-O1 -fcf-protection -mmanual-endbr -fasynchronous-unwind-tables" } */
+
+/* Test the placement of the .LPFE1 label. */
+
+void
+__attribute__ ((cf_check,patchable_function_entry (1, 0)))
+f10_endbr (void)
+{
+}
+
+/* { dg-final { scan-assembler "\t\.cfi_startproc\n\tendbr(32|64)\n.*\.LPFE1:\n\tnop\n\tret\n" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr93492-3.c b/gcc/testsuite/gcc.target/i386/pr93492-3.c
new file mode 100644
index 0000000..1f03c62
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr93492-3.c
@@ -0,0 +1,13 @@
+/* { dg-do "compile" } */
+/* { dg-require-effective-target mfentry } */
+/* { dg-options "-O1 -fcf-protection -mmanual-endbr -mfentry -pg -fasynchronous-unwind-tables" } */
+
+/* Test the placement of the .LPFE1 label. */
+
+void
+__attribute__ ((cf_check,patchable_function_entry (1, 0)))
+f10_endbr (void)
+{
+}
+
+/* { dg-final { scan-assembler "\t\.cfi_startproc\n\tendbr(32|64)\n.*\.LPFE1:\n\tnop\n1:\tcall\t__fentry__\n\tret\n" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr93492-4.c b/gcc/testsuite/gcc.target/i386/pr93492-4.c
new file mode 100644
index 0000000..d193df8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr93492-4.c
@@ -0,0 +1,11 @@
+/* { dg-do "compile" } */
+/* { dg-options "-O1 -fpatchable-function-entry=1 -fasynchronous-unwind-tables" } */
+
+/* Test the placement of the .LPFE1 label. */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "\t\.cfi_startproc\n.*\.LPFE1:\n\tnop\n\tret\n" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr93492-5.c b/gcc/testsuite/gcc.target/i386/pr93492-5.c
new file mode 100644
index 0000000..d04077c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr93492-5.c
@@ -0,0 +1,12 @@
+/* { dg-do "compile" } */
+/* { dg-require-effective-target mfentry } */
+/* { dg-options "-O1 -fpatchable-function-entry=1 -mfentry -pg -fasynchronous-unwind-tables" } */
+
+/* Test the placement of the .LPFE1 label. */
+
+void
+foo (void)
+{
+}
+
+/* { dg-final { scan-assembler "\t\.cfi_startproc\n.*\.LPFE1:\n\tnop\n1:\tcall\t__fentry__\n\tret\n" } } */