public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm
@ 2024-01-11  7:48 Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 01/16] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections Indu Bhagat
                   ` (15 more replies)
  0 siblings, 16 replies; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

Hello,

This patch series adds support in GAS to synthesize CFI for hand-written
asm, acronym'd as SCFI.  In the current V5 version, I have addressed
many of the review comments.  If this version looks OK to you, I like
this to be merged for the upcoming 2.42 release.

 The current SCFI support is experimental;  Work
will need to continue to make SCFI a more mature and robust offering.

Thanks for reviewing the previous versions:
  - RFC patch series (https://sourceware.org/pipermail/binutils/2023-September/129560.html).
  - V1 (https://sourceware.org/pipermail/binutils/2023-October/130163.html)
  - V2 (https://sourceware.org/pipermail/binutils/2023-October/130210.html)
  - V3 (https://sourceware.org/pipermail/binutils/2023-December/131079.html)
  - V4 (https://sourceware.org/pipermail/binutils/2024-January/131521.html)

To aid the review process, each patch has a short summary of changes
from V2->V3, from V3->V4, and now from V4->V5.  Following are the most
significant ones:
  - After consensus to merge this feature as experimental, the command
    line option has been changed to --scfi=experimental.
  - Fixed several cases of incorrect, insufficient or confusing code in
    tc-i386.c.
 
Some work items for future include:
  - Allow APX instructions.
  - Handle 8-bit, 16-bit, 32-bit MOV and ALU ops correctly.
  - User-interfacing documentation around best-practices to use SCFI.
  - Other minor items
We will create a bugzilla entry for each and handle them over the coming
weeks.

SCFI
------  
Motivation for the patch series is to alleviate users from manually
adding the CFI directives in hand-written asm. Manually adding CFI
annotations to user input asm needs additional expertise; human-errors
are possible and indeed occur more often than one may like.  These
errors, if present at the time of virtual stack unwind, lead to
unfavorable outcomes: incorrect stacktraces, program state corruption
etc.

For synthesis of CFI to work, the user input must adhere to the ABI and
the appropriate calling conventions, as GAS derives the set of
callee-saved registers based on that contract.  Currently, only System V
AMD64 ABI is supported.  AArch64 support is in the works.

The SCFI implementation is based on some heuristics/rules, please see
the patch "gas: synthesize CFI for hand-written asm" for more details.
Further, at the moment, SCFI does not help in cases when the control
flow graph of the input asm cannot be generated (e.g., in case of
indirect jumps, jump tables).

Thanks,

Indu Bhagat (16):
  gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections
  gas: dw2gencfi: use all_cfi_sections instead of cfi_sections
  gas: dw2gencfi: expose a new cfi_set_last_fde API
  gas: dw2gencfi: move some tc_* defines to the header file
  gas: dw2gencfi: expose dot_cfi_sections for scfidw2gen
  gas: dw2gencfi: externalize the all_cfi_sections
  gas: add new command line option --scfi=experimental
  gas: scfidw2gen: new functionality to prepare for SCFI
  opcodes: i386: fix dw2_regnum data type in reg_entry
  opcodes: gas: i386: define and use Rex2 as attribute not constraint
  opcodes: i386: new marker for insns that implicitly update stack
    pointer
  gas: synthesize CFI for hand-written asm
  gas: doc: update documentation for the new listing option
  opcodes: i386-reg.tbl: Add a comment to reflect dependency on ordering
  gas: testsuite: add an x86_64 testsuite for SCFI
  gas/NEWS: announce the new SCFI command line option

 gas/Makefile.am                               |     6 +
 gas/Makefile.in                               |    18 +-
 gas/NEWS                                      |     3 +
 gas/as.c                                      |    28 +-
 gas/as.h                                      |     8 +
 gas/config/obj-elf.c                          |    18 +
 gas/config/tc-i386.c                          |  1112 +-
 gas/config/tc-i386.h                          |    21 +
 gas/doc/as.texi                               |    31 +-
 gas/dw2gencfi.c                               |    45 +-
 gas/dw2gencfi.h                               |    20 +
 gas/ginsn.c                                   |  1259 ++
 gas/ginsn.h                                   |   384 +
 gas/listing.h                                 |     1 +
 gas/read.c                                    |    29 +-
 gas/scfi.c                                    |  1232 ++
 gas/scfi.h                                    |    38 +
 gas/scfidw2gen.c                              |   272 +
 gas/scfidw2gen.h                              |    35 +
 gas/subsegs.c                                 |     1 +
 gas/subsegs.h                                 |     2 +
 gas/symbols.c                                 |     3 +
 gas/testsuite/gas/scfi/README                 |    16 +
 gas/testsuite/gas/scfi/x86_64/ginsn-add-1.l   |    49 +
 gas/testsuite/gas/scfi/x86_64/ginsn-add-1.s   |    27 +
 .../gas/scfi/x86_64/ginsn-dw2-regnum-1.l      |    69 +
 .../gas/scfi/x86_64/ginsn-dw2-regnum-1.s      |    33 +
 gas/testsuite/gas/scfi/x86_64/ginsn-pop-1.l   |    41 +
 gas/testsuite/gas/scfi/x86_64/ginsn-pop-1.s   |    16 +
 gas/testsuite/gas/scfi/x86_64/ginsn-push-1.l  |    44 +
 gas/testsuite/gas/scfi/x86_64/ginsn-push-1.s  |    17 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-1.d    |    26 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-1.l    |     2 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-1.s    |    13 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-2.d    |    37 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-2.l    |     2 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-2.s    |    48 +
 .../gas/scfi/x86_64/scfi-asm-marker-1.d       |    29 +
 .../gas/scfi/x86_64/scfi-asm-marker-1.l       |     3 +
 .../gas/scfi/x86_64/scfi-asm-marker-1.s       |    27 +
 .../gas/scfi/x86_64/scfi-asm-marker-2.d       |    25 +
 .../gas/scfi/x86_64/scfi-asm-marker-2.l       |     3 +
 .../gas/scfi/x86_64/scfi-asm-marker-2.s       |    11 +
 .../gas/scfi/x86_64/scfi-asm-marker-3.d       |    32 +
 .../gas/scfi/x86_64/scfi-asm-marker-3.l       |     2 +
 .../gas/scfi/x86_64/scfi-asm-marker-3.s       |    38 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d  |    32 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l  |     2 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s  |    21 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d  |    58 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l  |     2 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s  |    52 +
 .../gas/scfi/x86_64/scfi-callee-saved-1.d     |    41 +
 .../gas/scfi/x86_64/scfi-callee-saved-1.l     |     2 +
 .../gas/scfi/x86_64/scfi-callee-saved-1.s     |    25 +
 .../gas/scfi/x86_64/scfi-callee-saved-2.d     |    42 +
 .../gas/scfi/x86_64/scfi-callee-saved-2.l     |     2 +
 .../gas/scfi/x86_64/scfi-callee-saved-2.s     |    40 +
 .../gas/scfi/x86_64/scfi-callee-saved-3.d     |    43 +
 .../gas/scfi/x86_64/scfi-callee-saved-3.l     |     3 +
 .../gas/scfi/x86_64/scfi-callee-saved-3.s     |    39 +
 .../gas/scfi/x86_64/scfi-callee-saved-4.d     |    41 +
 .../gas/scfi/x86_64/scfi-callee-saved-4.l     |     3 +
 .../gas/scfi/x86_64/scfi-callee-saved-4.s     |    55 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d    |    37 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l    |     2 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s    |    47 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d    |    29 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l    |     2 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.s    |    21 +
 .../gas/scfi/x86_64/scfi-cfi-label-1.d        |    38 +
 .../gas/scfi/x86_64/scfi-cfi-label-1.l        |     2 +
 .../gas/scfi/x86_64/scfi-cfi-label-1.s        |    19 +
 .../gas/scfi/x86_64/scfi-cfi-sections-1.d     |    24 +
 .../gas/scfi/x86_64/scfi-cfi-sections-1.l     |     2 +
 .../gas/scfi/x86_64/scfi-cfi-sections-1.s     |    22 +
 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d   |     5 +
 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l   |     3 +
 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s   |    24 +
 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l   |     4 +
 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s   |    22 +
 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l   |     4 +
 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s   |    28 +
 .../gas/scfi/x86_64/scfi-dyn-stack-1.d        |    24 +
 .../gas/scfi/x86_64/scfi-dyn-stack-1.l        |     2 +
 .../gas/scfi/x86_64/scfi-dyn-stack-1.s        |    50 +
 gas/testsuite/gas/scfi/x86_64/scfi-enter-1.d  |    36 +
 gas/testsuite/gas/scfi/x86_64/scfi-enter-1.l  |     2 +
 gas/testsuite/gas/scfi/x86_64/scfi-enter-1.s  |    24 +
 .../gas/scfi/x86_64/scfi-fp-diag-2.l          |     3 +
 .../gas/scfi/x86_64/scfi-fp-diag-2.s          |    55 +
 .../gas/scfi/x86_64/scfi-indirect-mov-1.d     |    52 +
 .../gas/scfi/x86_64/scfi-indirect-mov-1.l     |     2 +
 .../gas/scfi/x86_64/scfi-indirect-mov-1.s     |    48 +
 .../gas/scfi/x86_64/scfi-indirect-mov-2.d     |    42 +
 .../gas/scfi/x86_64/scfi-indirect-mov-2.l     |     2 +
 .../gas/scfi/x86_64/scfi-indirect-mov-2.s     |    38 +
 .../gas/scfi/x86_64/scfi-indirect-mov-3.d     |    42 +
 .../gas/scfi/x86_64/scfi-indirect-mov-3.l     |     2 +
 .../gas/scfi/x86_64/scfi-indirect-mov-3.s     |    38 +
 .../gas/scfi/x86_64/scfi-indirect-mov-4.d     |    64 +
 .../gas/scfi/x86_64/scfi-indirect-mov-4.l     |     3 +
 .../gas/scfi/x86_64/scfi-indirect-mov-4.s     |    68 +
 .../gas/scfi/x86_64/scfi-indirect-mov-5.s     |    35 +
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d    |    38 +
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l    |     2 +
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s    |    39 +
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d  |    37 +
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l  |     2 +
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s  |    25 +
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d  |    36 +
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l  |     2 +
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s  |    23 +
 .../gas/scfi/x86_64/scfi-pushsection-1.d      |    43 +
 .../gas/scfi/x86_64/scfi-pushsection-1.l      |     2 +
 .../gas/scfi/x86_64/scfi-pushsection-1.s      |    40 +
 .../gas/scfi/x86_64/scfi-pushsection-2.d      |    40 +
 .../gas/scfi/x86_64/scfi-pushsection-2.l      |     2 +
 .../gas/scfi/x86_64/scfi-pushsection-2.s      |    40 +
 .../gas/scfi/x86_64/scfi-selfalign-func-1.d   |    32 +
 .../gas/scfi/x86_64/scfi-selfalign-func-1.l   |     2 +
 .../gas/scfi/x86_64/scfi-selfalign-func-1.s   |    36 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d |    27 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l |     2 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.s |    15 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d |    31 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l |     2 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.s |    16 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d    |    26 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l    |     2 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.s    |    12 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d    |    32 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l    |     2 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.s    |    29 +
 .../gas/scfi/x86_64/scfi-unsupported-1.l      |     2 +
 .../gas/scfi/x86_64/scfi-unsupported-1.s      |     9 +
 .../gas/scfi/x86_64/scfi-unsupported-2.l      |     3 +
 .../gas/scfi/x86_64/scfi-unsupported-2.s      |    13 +
 .../gas/scfi/x86_64/scfi-unsupported-3.l      |     3 +
 .../gas/scfi/x86_64/scfi-unsupported-3.s      |    13 +
 .../gas/scfi/x86_64/scfi-unsupported-4.l      |     4 +
 .../gas/scfi/x86_64/scfi-unsupported-4.s      |    22 +
 .../gas/scfi/x86_64/scfi-unsupported-cfg-1.l  |     3 +
 .../gas/scfi/x86_64/scfi-unsupported-cfg-1.s  |    52 +
 .../gas/scfi/x86_64/scfi-unsupported-cfg-2.l  |     4 +
 .../gas/scfi/x86_64/scfi-unsupported-cfg-2.s  |    14 +
 .../gas/scfi/x86_64/scfi-unsupported-drap-1.l |     4 +
 .../gas/scfi/x86_64/scfi-unsupported-drap-1.s |    75 +
 .../gas/scfi/x86_64/scfi-unsupported-insn-1.l |     7 +
 .../gas/scfi/x86_64/scfi-unsupported-insn-1.s |    16 +
 gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp |   113 +
 opcodes/i386-gen.c                            |     1 +
 opcodes/i386-opc.h                            |    10 +-
 opcodes/i386-opc.tbl                          |   106 +-
 opcodes/i386-reg.tbl                          |     3 +
 opcodes/i386-tbl.h                            | 11755 ++++++++++------
 156 files changed, 15482 insertions(+), 4033 deletions(-)
 create mode 100644 gas/ginsn.c
 create mode 100644 gas/ginsn.h
 create mode 100644 gas/scfi.c
 create mode 100644 gas/scfi.h
 create mode 100644 gas/scfidw2gen.c
 create mode 100644 gas/scfidw2gen.h
 create mode 100644 gas/testsuite/gas/scfi/README
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-add-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-add-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-dw2-regnum-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-dw2-regnum-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-pop-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-pop-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-push-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-push-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-sections-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-sections-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-sections-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-enter-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-enter-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-enter-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-5.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-insn-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-insn-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp

-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 01/16] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 02/16] gas: dw2gencfi: use all_cfi_sections instead of cfi_sections Indu Bhagat
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[No changes since V2]

- cfi_sections_set is best set to true in cfi_dot_startproc ().  Setting
  it to true again in other APIs (dot_cfi_endproc, dot_cfi_fde_data, and
  cfi_finish) is unnecessary.
- move setting the global var all_cfi_sections into cfi_set_sections ().

gas/
        * dw2gencfi.c (cfi_set_sections): Set cfi_sections_set and
	cfi_sections here.
        (dot_cfi_startproc): Remove unnecessarily setting
	cfi_set_sections to true.
        (dot_cfi_endproc): Likewise.
        (dot_cfi_fde_data): Likewise.
        (cfi_finish): Likewise.
---
 gas/dw2gencfi.c | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index 3afb20b11a8..01cde827f63 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -492,6 +492,7 @@ cfi_set_return_column (unsigned regno)
 void
 cfi_set_sections (void)
 {
+  all_cfi_sections |= cfi_sections;
   frchain_now->frch_cfi_data->cur_fde_data->sections = all_cfi_sections;
   cfi_sections_set = true;
 }
@@ -1309,9 +1310,8 @@ dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED)
     }
   demand_empty_rest_of_line ();
 
-  cfi_sections_set = true;
-  all_cfi_sections |= cfi_sections;
   cfi_set_sections ();
+
   frchain_now->frch_cfi_data->cur_cfa_offset = 0;
   if (!simple)
     tc_cfi_frame_initial_instructions ();
@@ -1336,7 +1336,6 @@ dot_cfi_endproc (int ignored ATTRIBUTE_UNUSED)
 
   demand_empty_rest_of_line ();
 
-  cfi_sections_set = true;
   if ((cfi_sections & CFI_EMIT_target) != 0)
     tc_cfi_endproc (last_fde);
 }
@@ -1418,7 +1417,6 @@ dot_cfi_fde_data (int ignored ATTRIBUTE_UNUSED)
 
   last_fde = frchain_now->frch_cfi_data->cur_fde_data;
 
-  cfi_sections_set = true;
   if ((cfi_sections & CFI_EMIT_target) != 0
       || (cfi_sections & CFI_EMIT_eh_frame_compact) != 0)
     {
@@ -2315,7 +2313,6 @@ cfi_finish (void)
   if (all_fde_data == 0)
     return;
 
-  cfi_sections_set = true;
   if ((all_cfi_sections & CFI_EMIT_eh_frame) != 0
       || (all_cfi_sections & CFI_EMIT_eh_frame_compact) != 0)
     {
@@ -2502,7 +2499,6 @@ cfi_finish (void)
       flag_traditional_format = save_flag_traditional_format;
     }
 
-  cfi_sections_set = true;
   /* Generate SFrame section if the user specifies:
 	- the command line option to gas, or
 	- .sframe in the .cfi_sections directive.  */
@@ -2524,7 +2520,6 @@ cfi_finish (void)
 	as_bad (_(".sframe not supported for target"));
     }
 
-  cfi_sections_set = true;
   if ((all_cfi_sections & CFI_EMIT_debug_frame) != 0)
     {
       int alignment = ffs (DWARF2_ADDR_SIZE (stdoutput)) - 1;
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 02/16] gas: dw2gencfi: use all_cfi_sections instead of cfi_sections
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 01/16] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 03/16] gas: dw2gencfi: expose a new cfi_set_last_fde API Indu Bhagat
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[No changes since V2]

The code in dw2gencfi.c was checking variable cfi_sections and
all_cfi_sections seemingly randomly.  Accessing all_cfi_sections seems
to the correct variable to access.

The data in cfi_sections has already been propagated to all_cfi_sections
once cfi_dot_startproc () has been called.

gas/
        * dw2gencfi.c (dot_cfi_startproc): Use all_cfi_sections
	instead.
        (dot_cfi_endproc): Likewise.
        (dot_cfi_fde_data): Likewise.
---
 gas/dw2gencfi.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index 01cde827f63..eccfd6470c2 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -1316,7 +1316,7 @@ dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED)
   if (!simple)
     tc_cfi_frame_initial_instructions ();
 
-  if ((cfi_sections & CFI_EMIT_target) != 0)
+  if ((all_cfi_sections & CFI_EMIT_target) != 0)
     tc_cfi_startproc ();
 }
 
@@ -1336,7 +1336,7 @@ dot_cfi_endproc (int ignored ATTRIBUTE_UNUSED)
 
   demand_empty_rest_of_line ();
 
-  if ((cfi_sections & CFI_EMIT_target) != 0)
+  if ((all_cfi_sections & CFI_EMIT_target) != 0)
     tc_cfi_endproc (last_fde);
 }
 
@@ -1417,8 +1417,8 @@ dot_cfi_fde_data (int ignored ATTRIBUTE_UNUSED)
 
   last_fde = frchain_now->frch_cfi_data->cur_fde_data;
 
-  if ((cfi_sections & CFI_EMIT_target) != 0
-      || (cfi_sections & CFI_EMIT_eh_frame_compact) != 0)
+  if ((all_cfi_sections & CFI_EMIT_target) != 0
+      || (all_cfi_sections & CFI_EMIT_eh_frame_compact) != 0)
     {
       struct cfi_escape_data *head, **tail, *e;
       int num_ops = 0;
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 03/16] gas: dw2gencfi: expose a new cfi_set_last_fde API
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 01/16] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 02/16] gas: dw2gencfi: use all_cfi_sections instead of cfi_sections Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 04/16] gas: dw2gencfi: move some tc_* defines to the header file Indu Bhagat
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[No changes since V2]

gas/
	* dw2gencfi.c (cfi_set_last_fde): New definition.
	(dot_cfi_endproc): Use it.
	(dot_cfi_fde_data): Likewise.
	(dot_cfi_inline_lsda): Likewise.
	* dw2gencfi.h (struct fde_entry): New declaration.
	(cfi_set_last_fde): Likewise.
---
 gas/dw2gencfi.c | 13 ++++++++++---
 gas/dw2gencfi.h |  2 ++
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index eccfd6470c2..216da39803f 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -481,6 +481,13 @@ cfi_end_fde (symbolS *label)
   frchain_now->frch_cfi_data = NULL;
 }
 
+/* Set the last FDE  .*/
+void
+cfi_set_last_fde (struct fde_entry *fde)
+{
+  last_fde = fde;
+}
+
 /* Set the return column for the current FDE.  */
 
 void
@@ -1330,7 +1337,7 @@ dot_cfi_endproc (int ignored ATTRIBUTE_UNUSED)
       return;
     }
 
-  last_fde = frchain_now->frch_cfi_data->cur_fde_data;
+  cfi_set_last_fde (frchain_now->frch_cfi_data->cur_fde_data);
 
   cfi_end_fde (symbol_temp_new_now ());
 
@@ -1415,7 +1422,7 @@ dot_cfi_fde_data (int ignored ATTRIBUTE_UNUSED)
       return;
     }
 
-  last_fde = frchain_now->frch_cfi_data->cur_fde_data;
+  cfi_set_last_fde (frchain_now->frch_cfi_data->cur_fde_data);
 
   if ((all_cfi_sections & CFI_EMIT_target) != 0
       || (all_cfi_sections & CFI_EMIT_eh_frame_compact) != 0)
@@ -1568,7 +1575,7 @@ dot_cfi_inline_lsda (int ignored ATTRIBUTE_UNUSED)
   if (last_fde->eh_header_type == EH_COMPACT_HAS_LSDA)
     output_compact_unwind_data (last_fde, align);
 
-  last_fde = NULL;
+  cfi_set_last_fde (NULL);
 
   return;
 }
diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h
index 317cb5e601e..ff52add764c 100644
--- a/gas/dw2gencfi.h
+++ b/gas/dw2gencfi.h
@@ -25,6 +25,7 @@
 #include "dwarf2.h"
 
 struct symbol;
+struct fde_entry;
 
 extern const pseudo_typeS cfi_pseudo_table[];
 
@@ -35,6 +36,7 @@ extern void cfi_finish (void);
 /* Entry points for backends to add unwind information.  */
 extern void cfi_new_fde (struct symbol *);
 extern void cfi_end_fde (struct symbol *);
+extern void cfi_set_last_fde (struct fde_entry *fde);
 extern void cfi_set_return_column (unsigned);
 extern void cfi_set_sections (void);
 extern void cfi_add_advance_loc (struct symbol *);
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 04/16] gas: dw2gencfi: move some tc_* defines to the header file
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
                   ` (2 preceding siblings ...)
  2024-01-11  7:48 ` [PATCH, V5 03/16] gas: dw2gencfi: expose a new cfi_set_last_fde API Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 05/16] gas: dw2gencfi: expose dot_cfi_sections for scfidw2gen Indu Bhagat
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[No changes since V2]

Move the following three defines to the header file, so the SCFI
machinery can use them:
 - tc_cfi_frame_initial_instructions
 - tc_cfi_startproc
 - tc_cfi_endproc

Although, the symthesized CFI cannot be emitted when CFI_EMIT_target is
set, but in future, we may want to allow synthesized CFI to be emitted
to all outputs currently supported by GAS for DWARF CFI.

gas/
        * dw2gencfi.c: Move from ...
	* dw2gencfi.h: ... to here.
---
 gas/dw2gencfi.c | 12 ------------
 gas/dw2gencfi.h | 12 ++++++++++++
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index 216da39803f..2ba721c2955 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -64,18 +64,6 @@
 #define EH_FRAME_ALIGNMENT (bfd_get_arch_size (stdoutput) == 64 ? 3 : 2)
 #endif
 
-#ifndef tc_cfi_frame_initial_instructions
-#define tc_cfi_frame_initial_instructions() ((void)0)
-#endif
-
-#ifndef tc_cfi_startproc
-# define tc_cfi_startproc() ((void)0)
-#endif
-
-#ifndef tc_cfi_endproc
-# define tc_cfi_endproc(fde) ((void) (fde))
-#endif
-
 #define EH_FRAME_LINKONCE (SUPPORT_FRAME_LINKONCE || compact_eh \
 			   || TARGET_MULTIPLE_EH_FRAME_SECTIONS)
 
diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h
index ff52add764c..2e1f175e22f 100644
--- a/gas/dw2gencfi.h
+++ b/gas/dw2gencfi.h
@@ -29,6 +29,18 @@ struct fde_entry;
 
 extern const pseudo_typeS cfi_pseudo_table[];
 
+#ifndef tc_cfi_frame_initial_instructions
+#define tc_cfi_frame_initial_instructions() ((void)0)
+#endif
+
+#ifndef tc_cfi_startproc
+# define tc_cfi_startproc() ((void)0)
+#endif
+
+#ifndef tc_cfi_endproc
+# define tc_cfi_endproc(fde) ((void) (fde))
+#endif
+
 /* cfi_finish() is called at the end of file. It will complain if
    the last CFI wasn't properly closed by .cfi_endproc.  */
 extern void cfi_finish (void);
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 05/16] gas: dw2gencfi: expose dot_cfi_sections for scfidw2gen
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
                   ` (3 preceding siblings ...)
  2024-01-11  7:48 ` [PATCH, V5 04/16] gas: dw2gencfi: move some tc_* defines to the header file Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 06/16] gas: dw2gencfi: externalize the all_cfi_sections Indu Bhagat
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[No changes from V3 to V4]

[New patch in V3]

scfidw2gen will use this for processing the .cfi_sections directive.

gas/
        * dw2gencfi.c (dot_cfi_sections): Not static anymore.
        * dw2gencfi.h (dot_cfi_sections): Mark as extern.
---
 gas/dw2gencfi.c | 3 +--
 gas/dw2gencfi.h | 4 ++++
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index 2ba721c2955..db0261dd187 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -687,7 +687,6 @@ cfi_add_CFA_restore_state (void)
 
 static void dot_cfi (int);
 static void dot_cfi_escape (int);
-static void dot_cfi_sections (int);
 static void dot_cfi_startproc (int);
 static void dot_cfi_endproc (int);
 static void dot_cfi_fde_data (int);
@@ -1206,7 +1205,7 @@ dot_cfi_label (int ignored ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
-static void
+void
 dot_cfi_sections (int ignored ATTRIBUTE_UNUSED)
 {
   int sections = 0;
diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h
index 2e1f175e22f..c73fd40f210 100644
--- a/gas/dw2gencfi.h
+++ b/gas/dw2gencfi.h
@@ -41,6 +41,10 @@ extern const pseudo_typeS cfi_pseudo_table[];
 # define tc_cfi_endproc(fde) ((void) (fde))
 #endif
 
+/* Parse CFI assembler directive .cfi_sections.  This is an external function
+   because SCFI functionality also uses the same implementation.  */
+extern void dot_cfi_sections (int);
+
 /* cfi_finish() is called at the end of file. It will complain if
    the last CFI wasn't properly closed by .cfi_endproc.  */
 extern void cfi_finish (void);
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 06/16] gas: dw2gencfi: externalize the all_cfi_sections
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
                   ` (4 preceding siblings ...)
  2024-01-11  7:48 ` [PATCH, V5 05/16] gas: dw2gencfi: expose dot_cfi_sections for scfidw2gen Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 07/16] gas: add new command line option --scfi=experimental Indu Bhagat
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[No changes since V3]

[New patch in V3]

gas/
        * dw2gencfi.h: Declare all_cfi_sections as extern.
---
 gas/dw2gencfi.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h
index c73fd40f210..7fd6db41cde 100644
--- a/gas/dw2gencfi.h
+++ b/gas/dw2gencfi.h
@@ -27,6 +27,8 @@
 struct symbol;
 struct fde_entry;
 
+extern int all_cfi_sections;
+
 extern const pseudo_typeS cfi_pseudo_table[];
 
 #ifndef tc_cfi_frame_initial_instructions
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 07/16] gas: add new command line option --scfi=experimental
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
                   ` (5 preceding siblings ...)
  2024-01-11  7:48 ` [PATCH, V5 06/16] gas: dw2gencfi: externalize the all_cfi_sections Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 08/16] gas: scfidw2gen: new functionality to prepare for SCFI Indu Bhagat
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[Changes in V5]
  - Change the command line option to --scfi=experimental.  Option name
    "experimental" is mandatory.  If the user specifies just --scfi,
    they get an error:
    "Fatal error: Invalid --scfi= option: ; suggested option: experimental"
  - Fix documentation accordingly.
  - Fix documentation around the use of .type and .size directives in
    SCFI.  Mention System V AMD64 ABI.
[End of changes in V5]

[Changes in V4]
  - Code restructuring so that --scfi=[all,none] is available for ELF
    targets only
[End of changes in V4]

[No changes in V3]

When the command line option --scfi=experimenta is passed to the GNU
assembler, it will synthesize DWARF call frame information (CFI) for the
input assembly.

The option --scfi=experimental will also ignore most of the existing
.cfi_* directives, if already contained in the provided input file.
Only the following CFI directives will not be ignored:
  - .cfi_sections,
  - .cfi_label,
  - .cfi_signal_frame

To use SCFI, a target will need to:
    - define TARGET_USE_SCFI and TARGET_USE_GINSN, and other necessary
    definitions,
    - provide means to help GAS understand the target specific instruction
    semantics by creating ginsns.

The upcoming support for SCFI is inteded to be experimental, hence the
option --scfi=experimental.  The --scfi= may see more options like
--scfi=[all,none] added in future, once the SCFI support in GAS is
mature and robust.  The offering may also see for example, an
--scfi=inline option for dealing with inline asm may be added in the
future.  In --scfi=inline option, the GNU assembler may consume (and not
ignore) the compiler generated CFI for the code surrounding the inline
asm.

Also document the option.

gas/
        * as.c (show_usage): Add support for --scfi=experimental.
        (parse_args): Likewise.
        * as.h (enum synth_cfi_type): Define new type.
        * doc/as.texi: Document the new option.
---
 gas/as.c        | 23 +++++++++++++++++++++--
 gas/as.h        |  8 ++++++++
 gas/doc/as.texi | 15 +++++++++++++++
 3 files changed, 44 insertions(+), 2 deletions(-)

diff --git a/gas/as.c b/gas/as.c
index b83f0c618be..659da188da5 100644
--- a/gas/as.c
+++ b/gas/as.c
@@ -321,6 +321,11 @@ Options:\n\
                           generate GNU Build notes if none are present in the input\n"));
   fprintf (stream, _("\
   --gsframe               generate SFrame stack trace information\n"));
+# if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
+  fprintf (stream, _("\
+  --scfi=experimental     Synthesize DWARF CFI for hand-written asm\n\
+                          (experimental support)\n"));
+# endif
 #endif /* OBJ_ELF */
 
   fprintf (stream, _("\
@@ -511,7 +516,8 @@ parse_args (int * pargc, char *** pargv)
       OPTION_NOCOMPRESS_DEBUG,
       OPTION_NO_PAD_SECTIONS,
       OPTION_MULTIBYTE_HANDLING,  /* = STD_BASE + 40 */
-      OPTION_SFRAME
+      OPTION_SFRAME,
+      OPTION_SCFI
     /* When you add options here, check that they do
        not collide with OPTION_MD_BASE.  See as.h.  */
     };
@@ -543,7 +549,10 @@ parse_args (int * pargc, char *** pargv)
     ,{"sectname-subst", no_argument, NULL, OPTION_SECTNAME_SUBST}
     ,{"generate-missing-build-notes", required_argument, NULL, OPTION_ELF_BUILD_NOTES}
     ,{"gsframe", no_argument, NULL, OPTION_SFRAME}
-#endif
+# if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
+    ,{"scfi", required_argument, NULL, OPTION_SCFI}
+# endif
+#endif /* OBJ_ELF || OBJ_MAYBE_ELF.  */
     ,{"fatal-warnings", no_argument, NULL, OPTION_WARN_FATAL}
     ,{"gdwarf-2", no_argument, NULL, OPTION_GDWARF_2}
     ,{"gdwarf-3", no_argument, NULL, OPTION_GDWARF_3}
@@ -982,6 +991,16 @@ This program has absolutely no warranty.\n"));
 	  flag_execstack = 0;
 	  break;
 
+# if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
+	case OPTION_SCFI:
+	  if (optarg && strcasecmp (optarg, "experimental") == 0)
+	    flag_synth_cfi = SYNTH_CFI_EXPERIMENTAL;
+	  else
+	    as_fatal (_("Invalid --scfi= option: `%s'; suggested option: experimental"),
+		      optarg);
+	  break;
+# endif
+
 	case OPTION_SIZE_CHECK:
 	  if (strcasecmp (optarg, "error") == 0)
 	    flag_allow_nonconst_size = false;
diff --git a/gas/as.h b/gas/as.h
index 1297e3dae41..69d7ae2cd17 100644
--- a/gas/as.h
+++ b/gas/as.h
@@ -324,6 +324,14 @@ COMMON int flag_fatal_warnings; /* --fatal-warnings */
    are detected.  */
 COMMON unsigned char flag_always_generate_output; /* -Z */
 
+enum synth_cfi_type
+{
+  SYNTH_CFI_NONE = 0,
+  SYNTH_CFI_EXPERIMENTAL,
+};
+
+COMMON enum synth_cfi_type flag_synth_cfi;
+
 /* This is true if the assembler should output time and space usage.  */
 COMMON unsigned char flag_print_statistics;
 
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index 7526b221a36..370f40fcbae 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -255,6 +255,7 @@ gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}.
  [@b{--multibyte-handling=[allow|warn|warn-sym-only]}]
  [@b{--no-pad-sections}]
  [@b{-o} @var{objfile}] [@b{-R}]
+ [@b{--scfi=experimental}]
  [@b{--sectname-subst}]
  [@b{--size-check=[error|warning]}]
  [@b{--statistics}]
@@ -932,6 +933,20 @@ Ignored.  Supported for compatibility with tools that apss the same option to
 both the assembler and the linker.
 
 @ifset ELF
+@item --scfi=experimental
+This option controls whether the assembler should synthesize CFI for
+hand-written input.  If the input already contains some synthesizable CFI
+directives, the assembler ignores them and emits a warning.  Note that
+@code{--scfi=experimental} is not intended to be used for compiler-generated
+code, including inline assembly.  This experimental support is work in
+progress.  Only System V AMD64 ABI is supported.
+
+Each input function in assembly must begin with the @code{.type} directive, and
+should ideally be closed off using a @code{.size} directive.  When using SCFI,
+each @code{.type} directive prompts GAS to start a new FDE (a.k.a., Function
+Descriptor Entry).  This implies that with each @code{.type} directive, a
+previous block of instructions, if any, is finalised as a distinct FDE.
+
 @item --sectname-subst
 Honor substitution sequences in section names.
 @ifclear man
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 08/16] gas: scfidw2gen: new functionality to prepare for SCFI
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
                   ` (6 preceding siblings ...)
  2024-01-11  7:48 ` [PATCH, V5 07/16] gas: add new command line option --scfi=experimental Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 09/16] opcodes: i386: fix dw2_regnum data type in reg_entry Indu Bhagat
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[Changes from V4 to V5]
  - Reword the warning.  Now that the command line option has changed to
    --scfi=experimental.  Reword the warning
    from: "--scfi=all ignores most user-specified CFI directives"
    to: "SCFI ignores most user-specified CFI directives"
[End of changes from V4 to V5]

[No Changes from V3 to V4]

[Changes from V2 to V3]
  - Use dot_cfi_sections () from dw2gencfi and do not ignore
    .cfi_sections CFI directive from user anymore.
  - Addressed review comments:
    + Remove dummy functions, use #ifdef in read.c
    + Move the handlers of 'no ignore' functions together
    + Move functions around so no need for forward declaration
    + Use bool for scfi_ignore_warn_once, not int
    + Reword message around not closed CFI entry
    + Fix year range
    + scfidw2gen.c: reword warning message s/some/most/
    + ginsn/scfidw2gen: use const with symbolS*
[End of changes from V2 to V3]

Define a new set of handlers for CFI directives for the purpose of SCFI.
The SCFI machinery ignores many of the user-specified CFI direcives when
SCFI is in effect.  A warning ("Warning: SCFI ignores most
user-specified CFI directives") is issued once per file.  The following
CFI directives, however, are not ignored:
      - .cfi_sections
      - .cfi_label
      - .cfi_signal_frame

gas/
	* Makefile.am: Add new files to GAS_CFILES and HFILES.
	* Makefile.in: Likewise.
	* gas/read.c (scfi_pop_insert): New define.
	(pobegin): Use the SCFI handlers.
	* scfidw2gen.c: New file.
	* scfidw2gen.h: New file.
---
 gas/Makefile.am  |   2 +
 gas/Makefile.in  |   9 +-
 gas/read.c       |  19 +++-
 gas/scfidw2gen.c | 252 +++++++++++++++++++++++++++++++++++++++++++++++
 gas/scfidw2gen.h |  35 +++++++
 5 files changed, 312 insertions(+), 5 deletions(-)
 create mode 100644 gas/scfidw2gen.c
 create mode 100644 gas/scfidw2gen.h

diff --git a/gas/Makefile.am b/gas/Makefile.am
index 77c92d4d8ed..2848fac47b0 100644
--- a/gas/Makefile.am
+++ b/gas/Makefile.am
@@ -93,6 +93,7 @@ GAS_CFILES = \
 	read.c \
 	remap.c \
 	sb.c \
+	scfidw2gen.c \
 	sframe-opt.c \
 	stabs.c \
 	subsegs.c \
@@ -128,6 +129,7 @@ HFILES = \
 	output-file.h \
 	read.h \
 	sb.h \
+	scfidw2gen.h \
 	subsegs.h \
 	symbols.h \
 	tc.h \
diff --git a/gas/Makefile.in b/gas/Makefile.in
index 7aee1e1d460..7d4fbfc055c 100644
--- a/gas/Makefile.in
+++ b/gas/Makefile.in
@@ -176,9 +176,9 @@ am__objects_1 = app.$(OBJEXT) as.$(OBJEXT) atof-generic.$(OBJEXT) \
 	hash.$(OBJEXT) input-file.$(OBJEXT) input-scrub.$(OBJEXT) \
 	listing.$(OBJEXT) literal.$(OBJEXT) macro.$(OBJEXT) \
 	messages.$(OBJEXT) output-file.$(OBJEXT) read.$(OBJEXT) \
-	remap.$(OBJEXT) sb.$(OBJEXT) sframe-opt.$(OBJEXT) \
-	stabs.$(OBJEXT) subsegs.$(OBJEXT) symbols.$(OBJEXT) \
-	write.$(OBJEXT)
+	remap.$(OBJEXT) sb.$(OBJEXT) scfidw2gen.$(OBJEXT) \
+	sframe-opt.$(OBJEXT) stabs.$(OBJEXT) subsegs.$(OBJEXT) \
+	symbols.$(OBJEXT) write.$(OBJEXT)
 am_as_new_OBJECTS = $(am__objects_1)
 am__dirstamp = $(am__leading_dot)dirstamp
 as_new_OBJECTS = $(am_as_new_OBJECTS)
@@ -592,6 +592,7 @@ GAS_CFILES = \
 	read.c \
 	remap.c \
 	sb.c \
+	scfidw2gen.c \
 	sframe-opt.c \
 	stabs.c \
 	subsegs.c \
@@ -626,6 +627,7 @@ HFILES = \
 	output-file.h \
 	read.h \
 	sb.h \
+	scfidw2gen.h \
 	subsegs.h \
 	symbols.h \
 	tc.h \
@@ -1348,6 +1350,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remap.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scfidw2gen.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sframe-opt.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stabs.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/subsegs.Po@am__quote@
diff --git a/gas/read.c b/gas/read.c
index 753c92d2f1a..2aa5ce2facb 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -38,6 +38,7 @@
 #include "obstack.h"
 #include "ecoff.h"
 #include "dw2gencfi.h"
+#include "scfidw2gen.h"
 #include "codeview.h"
 #include "wchar.h"
 #include "filenames.h"
@@ -587,6 +588,10 @@ pop_insert (const pseudo_typeS *table)
 #define cfi_pop_insert()	pop_insert(cfi_pseudo_table)
 #endif
 
+#ifndef scfi_pop_insert
+#define scfi_pop_insert()	pop_insert(scfi_pseudo_table)
+#endif
+
 static void
 pobegin (void)
 {
@@ -607,8 +612,18 @@ pobegin (void)
   pop_insert (potable);
 
   /* Now CFI ones.  */
-  pop_table_name = "cfi";
-  cfi_pop_insert ();
+#if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
+  if (flag_synth_cfi)
+    {
+      pop_table_name = "scfi";
+      scfi_pop_insert ();
+    }
+  else
+#endif
+    {
+      pop_table_name = "cfi";
+      cfi_pop_insert ();
+    }
 }
 
 static void
diff --git a/gas/scfidw2gen.c b/gas/scfidw2gen.c
new file mode 100644
index 00000000000..1b3fb158e20
--- /dev/null
+++ b/gas/scfidw2gen.c
@@ -0,0 +1,252 @@
+/* scfidw2gen.c - Support for emission of synthesized Dwarf2 CFI.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#include "as.h"
+#include "dw2gencfi.h"
+#include "subsegs.h"
+#include "scfidw2gen.h"
+
+#if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
+
+static bool scfi_ignore_warn_once;
+
+static void
+dot_scfi_ignore (int ignored ATTRIBUTE_UNUSED)
+{
+  gas_assert (flag_synth_cfi);
+
+  if (!scfi_ignore_warn_once)
+    {
+      as_warn (_("SCFI ignores most user-specified CFI directives"));
+      scfi_ignore_warn_once = true;
+    }
+  ignore_rest_of_line ();
+}
+
+static void
+scfi_process_cfi_label (void)
+{
+  /* To be implemented. */
+  return;
+}
+
+static void
+scfi_process_cfi_signal_frame (void)
+{
+  /* To be implemented.  */
+  return;
+}
+
+static void
+dot_scfi (int arg)
+{
+  switch (arg)
+    {
+      case CFI_label:
+	scfi_process_cfi_label ();
+	break;
+      case CFI_signal_frame:
+	scfi_process_cfi_signal_frame ();
+	break;
+      default:
+	abort ();
+    }
+}
+
+const pseudo_typeS scfi_pseudo_table[] =
+  {
+    { "cfi_sections", dot_cfi_sections, 0 }, /* No ignore.  */
+    { "cfi_signal_frame", dot_scfi, CFI_signal_frame }, /* No ignore.  */
+    { "cfi_label", dot_scfi, CFI_label }, /* No ignore.  */
+    { "cfi_startproc", dot_scfi_ignore, 0 },
+    { "cfi_endproc", dot_scfi_ignore, 0 },
+    { "cfi_fde_data", dot_scfi_ignore, 0 },
+    { "cfi_def_cfa", dot_scfi_ignore, 0 },
+    { "cfi_def_cfa_register", dot_scfi_ignore, 0 },
+    { "cfi_def_cfa_offset", dot_scfi_ignore, 0 },
+    { "cfi_adjust_cfa_offset", dot_scfi_ignore, 0 },
+    { "cfi_offset", dot_scfi_ignore, 0 },
+    { "cfi_rel_offset", dot_scfi_ignore, 0 },
+    { "cfi_register", dot_scfi_ignore, 0 },
+    { "cfi_return_column", dot_scfi_ignore, 0 },
+    { "cfi_restore", dot_scfi_ignore, 0 },
+    { "cfi_undefined", dot_scfi_ignore, 0 },
+    { "cfi_same_value", dot_scfi_ignore, 0 },
+    { "cfi_remember_state", dot_scfi_ignore, 0 },
+    { "cfi_restore_state", dot_scfi_ignore, 0 },
+    { "cfi_window_save", dot_scfi_ignore, 0 },
+    { "cfi_negate_ra_state", dot_scfi_ignore, 0 },
+    { "cfi_escape", dot_scfi_ignore, 0 },
+    { "cfi_personality", dot_scfi_ignore, 0 },
+    { "cfi_personality_id", dot_scfi_ignore, 0 },
+    { "cfi_lsda", dot_scfi_ignore, 0 },
+    { "cfi_val_encoded_addr", dot_scfi_ignore, 0 },
+    { "cfi_inline_lsda", dot_scfi_ignore, 0 },
+    { "cfi_val_offset", dot_scfi_ignore, 0 },
+    { NULL, NULL, 0 }
+  };
+
+void
+scfi_dot_cfi_startproc (const symbolS *start_sym)
+{
+  if (frchain_now->frch_cfi_data != NULL)
+    {
+      as_bad (_("SCFI: missing previous SCFI endproc marker"));
+      return;
+    }
+
+  cfi_new_fde ((symbolS *)start_sym);
+
+  cfi_set_sections ();
+
+  frchain_now->frch_cfi_data->cur_cfa_offset = 0;
+
+  /* By default, SCFI machinery assumes .cfi_startproc is used without
+     parameter simple.  */
+  tc_cfi_frame_initial_instructions ();
+
+  if ((all_cfi_sections & CFI_EMIT_target) != 0)
+    tc_cfi_startproc ();
+}
+
+void
+scfi_dot_cfi_endproc (const symbolS *end_sym)
+{
+  struct fde_entry *fde_last;
+
+  if (frchain_now->frch_cfi_data == NULL)
+    {
+      as_bad (_(".cfi_endproc without corresponding .cfi_startproc"));
+      return;
+    }
+
+  fde_last = frchain_now->frch_cfi_data->cur_fde_data;
+  cfi_set_last_fde (fde_last);
+
+  cfi_end_fde ((symbolS *)end_sym);
+
+  if ((all_cfi_sections & CFI_EMIT_target) != 0)
+    tc_cfi_endproc (fde_last);
+}
+
+void
+scfi_dot_cfi (int arg, unsigned reg1, unsigned reg2, offsetT offset,
+	      const char *name, const symbolS *advloc)
+{
+  if (frchain_now->frch_cfi_data == NULL)
+    {
+      as_bad (_("CFI instruction used without previous .cfi_startproc"));
+      return;
+    }
+
+  /* If the last address was not at the current PC, advance to current.  */
+  if (frchain_now->frch_cfi_data->last_address != advloc)
+    cfi_add_advance_loc ((symbolS *)advloc);
+
+  switch (arg)
+    {
+    case DW_CFA_offset:
+      cfi_add_CFA_offset (reg1, offset);
+      break;
+
+    case DW_CFA_val_offset:
+      cfi_add_CFA_val_offset (reg1, offset);
+      break;
+
+    case CFI_rel_offset:
+      cfi_add_CFA_offset (reg1,
+			  offset - frchain_now->frch_cfi_data->cur_cfa_offset);
+      break;
+
+    case DW_CFA_def_cfa:
+      cfi_add_CFA_def_cfa (reg1, offset);
+      break;
+
+    case DW_CFA_register:
+      cfi_add_CFA_register (reg1, reg2);
+      break;
+
+    case DW_CFA_def_cfa_register:
+      cfi_add_CFA_def_cfa_register (reg1);
+      break;
+
+    case DW_CFA_def_cfa_offset:
+      cfi_add_CFA_def_cfa_offset (offset);
+      break;
+
+    case CFI_adjust_cfa_offset:
+      cfi_add_CFA_def_cfa_offset (frchain_now->frch_cfi_data->cur_cfa_offset
+				  + offset);
+      break;
+
+    case DW_CFA_restore:
+      cfi_add_CFA_restore (reg1);
+      break;
+
+    case DW_CFA_remember_state:
+      cfi_add_CFA_remember_state ();
+      break;
+
+    case DW_CFA_restore_state:
+      cfi_add_CFA_restore_state ();
+      break;
+
+    case CFI_label:
+      cfi_add_label (name);
+      break;
+
+    case CFI_signal_frame:
+      frchain_now->frch_cfi_data->cur_fde_data->signal_frame = 1;
+      break;
+
+/*
+    case DW_CFA_undefined:
+      for (;;)
+	{
+	  reg1 = cfi_parse_reg ();
+	  cfi_add_CFA_undefined (reg1);
+	  SKIP_WHITESPACE ();
+	  if (*input_line_pointer != ',')
+	    break;
+	  ++input_line_pointer;
+	}
+      break;
+
+    case DW_CFA_same_value:
+      reg1 = cfi_parse_reg ();
+      cfi_add_CFA_same_value (reg1);
+      break;
+
+    case CFI_return_column:
+      reg1 = cfi_parse_reg ();
+      cfi_set_return_column (reg1);
+      break;
+
+    case DW_CFA_GNU_window_save:
+      cfi_add_CFA_insn (DW_CFA_GNU_window_save);
+      break;
+
+*/
+    default:
+      abort ();
+    }
+}
+
+#endif
diff --git a/gas/scfidw2gen.h b/gas/scfidw2gen.h
new file mode 100644
index 00000000000..b96ff4848d9
--- /dev/null
+++ b/gas/scfidw2gen.h
@@ -0,0 +1,35 @@
+/* scfidw2gen.h - Support for emitting synthesized Dwarf2 CFI.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#ifndef SCFIDW2GEN_H
+#define SCFIDW2GEN_H
+
+#include "as.h"
+#include "dwarf2.h"
+
+extern const pseudo_typeS scfi_pseudo_table[];
+
+void scfi_dot_cfi_startproc (const symbolS *start_sym);
+void scfi_dot_cfi_endproc (const symbolS *end_sym);
+void scfi_dot_cfi (int arg, unsigned reg1, unsigned reg2, offsetT offset,
+		   const char *name, const symbolS *advloc);
+
+#endif
+
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 09/16] opcodes: i386: fix dw2_regnum data type in reg_entry
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
                   ` (7 preceding siblings ...)
  2024-01-11  7:48 ` [PATCH, V5 08/16] gas: scfidw2gen: new functionality to prepare for SCFI Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 10/16] opcodes: gas: i386: define and use Rex2 as attribute not constraint Indu Bhagat
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[New in V5]
  - This patch was posted earlier
https://sourceware.org/pipermail/binutils/2024-January/131692.html
    and is under discussion in a separate thread.
  - This patch does not affect the SCFI series ATM as the current
    implementation disallows APX instructions.
[End of New in V5]

The DWARF register numbers for the APX EGPRs start with 130.  The data
type holding the same currently is signed char.

ChangeLog:
	* opcodes/i386-opc.h (reg_entry): Bump to signed short.
---
 opcodes/i386-opc.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/opcodes/i386-opc.h b/opcodes/i386-opc.h
index 61a11214914..38661ffe70c 100644
--- a/opcodes/i386-opc.h
+++ b/opcodes/i386-opc.h
@@ -1047,7 +1047,7 @@ typedef struct
 #define RegIZ	(RegIP - 1)
 /* FLAT is a fake segment register (Intel mode).  */
 #define RegFlat     ((unsigned char) ~0)
-  signed char dw2_regnum[2];
+  signed short dw2_regnum[2];
 #define Dw2Inval (-1)
 }
 reg_entry;
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 10/16] opcodes: gas: i386: define and use Rex2 as attribute not constraint
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
                   ` (8 preceding siblings ...)
  2024-01-11  7:48 ` [PATCH, V5 09/16] opcodes: i386: fix dw2_regnum data type in reg_entry Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 11/16] opcodes: i386: new marker for insns that implicitly update stack pointer Indu Bhagat
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[New in V5]

Rex2 is currently an operand constraint.  For the upcoming SCFI
implementation in GAS, we need to identify operations which implicitly
update the stack pointer.  An operand constraint enumerator for implicit
stack op seems more appropriate than an attribute.  However, two opcodes
currently necessitate both Rex2 and an implicit stack op marker; this
prompts revisiting the current representations a bit.

Make Rex2 a standalone attribute, so that later a new operand constraint
may be added for IMPLICIT_STACK_OP.

PS: Removed the opcodes/i386-tbl.h diffs to keep the message size within
limits.

ChangeLog:
	* gas/config/tc-i386.c (is_apx_rex2_encoding): Update the check.
	* opcodes/i386-gen.c: Add a new BITFIELD for Rex2.
	* opcodes/i386-opc.h (REX2_REQUIRED): Remove.
	* opcodes/i386-opc.tbl: Remove Rex2 operand constraint.
	* opcodes/i386-tbl.h: Regenerated.
---
 gas/config/tc-i386.c |     2 +-
 opcodes/i386-gen.c   |     1 +
 opcodes/i386-opc.h   |     6 +-
 opcodes/i386-opc.tbl |     1 -
 opcodes/i386-tbl.h   | 11659 +++++++++++++++++++++++++++--------------
 5 files changed, 7778 insertions(+), 3891 deletions(-)

diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index d599306b12c..8bbed03bd9a 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -3955,7 +3955,7 @@ static INLINE bool
 is_apx_rex2_encoding (void)
 {
   return i.rex2 || i.rex2_encoding
-	|| i.tm.opcode_modifier.operandconstraint == REX2_REQUIRED;
+	|| i.tm.opcode_modifier.rex2;
 }
 
 static unsigned int
diff --git a/opcodes/i386-gen.c b/opcodes/i386-gen.c
index e8b92e32f45..21331216486 100644
--- a/opcodes/i386-gen.c
+++ b/opcodes/i386-gen.c
@@ -490,6 +490,7 @@ static bitfield opcode_modifiers[] =
   BITFIELD (ISA64),
   BITFIELD (NoEgpr),
   BITFIELD (NF),
+  BITFIELD (Rex2),
 };
 
 #define CLASS(n) #n, n
diff --git a/opcodes/i386-opc.h b/opcodes/i386-opc.h
index 38661ffe70c..eaebfaa9ba8 100644
--- a/opcodes/i386-opc.h
+++ b/opcodes/i386-opc.h
@@ -579,8 +579,6 @@ enum
   /* Instrucion requires that destination must be distinct from source
      registers.  */
 #define DISTINCT_DEST 9
-  /* Instrucion requires REX2 prefix.  */
-#define REX2_REQUIRED 10
   OperandConstraint,
   /* instruction ignores operand size prefix and in Intel mode ignores
      mnemonic size suffix check.  */
@@ -750,6 +748,9 @@ enum
   /* No CSPAZO flags update indication.  */
   NF,
 
+  /* Instrucion requires REX2 prefix.  */
+  Rex2,
+
   /* The last bitfield in i386_opcode_modifier.  */
   Opcode_Modifier_Num
 };
@@ -796,6 +797,7 @@ typedef struct i386_opcode_modifier
   unsigned int isa64:2;
   unsigned int noegpr:1;
   unsigned int nf:1;
+  unsigned int rex2:1;
 } i386_opcode_modifier;
 
 /* Operand classes.  */
diff --git a/opcodes/i386-opc.tbl b/opcodes/i386-opc.tbl
index aceac97dbaa..b9c2f1ce57e 100644
--- a/opcodes/i386-opc.tbl
+++ b/opcodes/i386-opc.tbl
@@ -85,7 +85,6 @@
 #define RegKludge         OperandConstraint=REG_KLUDGE
 #define SwapSources       OperandConstraint=SWAP_SOURCES
 #define Ugh               OperandConstraint=UGH
-#define Rex2              OperandConstraint=REX2_REQUIRED
 
 #define ATTSyntax         Dialect=ATT_SYNTAX
 #define ATTMnemonic       Dialect=ATT_MNEMONIC
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 11/16] opcodes: i386: new marker for insns that implicitly update stack pointer
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
                   ` (9 preceding siblings ...)
  2024-01-11  7:48 ` [PATCH, V5 10/16] opcodes: gas: i386: define and use Rex2 as attribute not constraint Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  2024-01-11  7:57   ` Jan Beulich
  2024-01-11  7:48 ` [PATCH, V5 12/16] gas: synthesize CFI for hand-written asm Indu Bhagat
                   ` (4 subsequent siblings)
  15 siblings, 1 reply; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[Changes from V4 to V5]
  - IMPLICIT_STACK_OP as a new constraint (was attribute in V4).
[End of Changes from V4 to V5]

[New in V4]

Some x86 instructions affect the stack pointer implicitly.  Add a new
operand constraint to reflect this.  This will be useful for SCFI
implmentation to ensure its correctness.

Mark all push, pop, call, ret, enter, leave, INT, iret instructions.

opcodes/
	* i386-gen.c: Update opcode_modifiers.
	* i386-opc.h: Add a new constraint.
	* i386-opc.tbl: Update the affected instructions.
	* i386-tbl.h: Regenerated.
---
 opcodes/i386-opc.h   |   2 +
 opcodes/i386-opc.tbl | 105 ++++++++++++++++++++++---------------------
 opcodes/i386-tbl.h   | 104 +++++++++++++++++++++---------------------
 3 files changed, 107 insertions(+), 104 deletions(-)

diff --git a/opcodes/i386-opc.h b/opcodes/i386-opc.h
index eaebfaa9ba8..d468929f7fe 100644
--- a/opcodes/i386-opc.h
+++ b/opcodes/i386-opc.h
@@ -579,6 +579,8 @@ enum
   /* Instrucion requires that destination must be distinct from source
      registers.  */
 #define DISTINCT_DEST 9
+  /* Instruction updates stack pointer implicitly.  */
+#define IMPLICIT_STACK_OP 10
   OperandConstraint,
   /* instruction ignores operand size prefix and in Intel mode ignores
      mnemonic size suffix check.  */
diff --git a/opcodes/i386-opc.tbl b/opcodes/i386-opc.tbl
index b9c2f1ce57e..c3f6443d089 100644
--- a/opcodes/i386-opc.tbl
+++ b/opcodes/i386-opc.tbl
@@ -85,6 +85,7 @@
 #define RegKludge         OperandConstraint=REG_KLUDGE
 #define SwapSources       OperandConstraint=SWAP_SOURCES
 #define Ugh               OperandConstraint=UGH
+#define ImplicitStackOp   OperandConstraint=IMPLICIT_STACK_OP
 
 #define ATTSyntax         Dialect=ATT_SYNTAX
 #define ATTMnemonic       Dialect=ATT_MNEMONIC
@@ -228,32 +229,32 @@ movzw, 0xfb7, i386, Modrm|No_bSuf|No_wSuf|No_sSuf, { Reg16|Unspecified|BaseIndex
 movzx, 0xfb6, i386, W|Modrm|No_lSuf|No_sSuf|No_qSuf, { Reg8|Reg16|Unspecified|BaseIndex, Reg16|Reg32|Reg64 }
 
 // Push instructions.
-push, 0x50, No64, No_bSuf|No_sSuf|No_qSuf, { Reg16|Reg32 }
-push, 0xff/6, No64, Modrm|DefaultSize|No_bSuf|No_sSuf|No_qSuf, { Reg16|Reg32|Unspecified|BaseIndex }
-push, 0x6a, i186&No64, DefaultSize|No_bSuf|No_sSuf|No_qSuf, { Imm8S }
-push, 0x68, i186&No64, DefaultSize|No_bSuf|No_sSuf|No_qSuf, { Imm16|Imm32 }
-push, 0x6, No64, DefaultSize|No_bSuf|No_sSuf|No_qSuf, { SReg }
+push, 0x50, No64, ImplicitStackOp|No_bSuf|No_sSuf|No_qSuf, { Reg16|Reg32 }
+push, 0xff/6, No64, Modrm|ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf, { Reg16|Reg32|Unspecified|BaseIndex }
+push, 0x6a, i186&No64, ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf, { Imm8S }
+push, 0x68, i186&No64, ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf, { Imm16|Imm32 }
+push, 0x6, No64, ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf, { SReg }
 // In 64bit mode, the operand size is implicitly 64bit.
-push, 0x50, x64, No_bSuf|No_lSuf|No_sSuf|NoRex64, { Reg16|Reg64 }
-pushp, 0x50, APX_F, No_bSuf|No_wSuf|No_lSuf|No_sSuf|Rex2, { Reg64 }
-push, 0xff/6, x64, Modrm|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, { Reg16|Reg64|Unspecified|BaseIndex }
-push, 0x6a, x64, DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, { Imm8S }
-push, 0x68, x64, DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, { Imm16|Imm32S }
-push, 0xfa0, x64, DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, { SReg }
+push, 0x50, x64, ImplicitStackOp|No_bSuf|No_lSuf|No_sSuf|NoRex64, { Reg16|Reg64 }
+pushp, 0x50, APX_F, ImplicitStackOp|No_bSuf|No_wSuf|No_lSuf|No_sSuf|Rex2, { Reg64 }
+push, 0xff/6, x64, Modrm|ImplicitStackOp|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, { Reg16|Reg64|Unspecified|BaseIndex }
+push, 0x6a, x64, ImplicitStackOp|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, { Imm8S }
+push, 0x68, x64, ImplicitStackOp|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, { Imm16|Imm32S }
+push, 0xfa0, x64, ImplicitStackOp|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, { SReg }
 
-pusha, 0x60, i186&No64, DefaultSize|No_bSuf|No_sSuf|No_qSuf, {}
+pusha, 0x60, i186&No64, ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf, {}
 
 // Pop instructions.
-pop, 0x58, No64, No_bSuf|No_sSuf|No_qSuf, { Reg16|Reg32 }
-pop, 0x8f/0, No64, Modrm|DefaultSize|No_bSuf|No_sSuf|No_qSuf, { Reg16|Reg32|Unspecified|BaseIndex }
-pop, 0x7, No64, DefaultSize|No_bSuf|No_sSuf|No_qSuf, { SReg }
+pop, 0x58, No64, ImplicitStackOp|No_bSuf|No_sSuf|No_qSuf, { Reg16|Reg32 }
+pop, 0x8f/0, No64, Modrm|ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf, { Reg16|Reg32|Unspecified|BaseIndex }
+pop, 0x7, No64, ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf, { SReg }
 // In 64bit mode, the operand size is implicitly 64bit.
-pop, 0x58, x64, No_bSuf|No_lSuf|No_sSuf|NoRex64, { Reg16|Reg64 }
-popp, 0x58, APX_F, No_bSuf|No_wSuf|No_lSuf|No_sSuf|Rex2, { Reg64 }
-pop, 0x8f/0, x64, Modrm|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, { Reg16|Reg64|Unspecified|BaseIndex }
-pop, 0xfa1, x64, DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, { SReg }
+pop, 0x58, x64, ImplicitStackOp|No_bSuf|No_lSuf|No_sSuf|NoRex64, { Reg16|Reg64 }
+popp, 0x58, APX_F, ImplicitStackOp|No_bSuf|No_wSuf|No_lSuf|No_sSuf|Rex2, { Reg64 }
+pop, 0x8f/0, x64, Modrm|ImplicitStackOp|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, { Reg16|Reg64|Unspecified|BaseIndex }
+pop, 0xfa1, x64, ImplicitStackOp|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, { SReg }
 
-popa, 0x61, i186&No64, DefaultSize|No_bSuf|No_sSuf|No_qSuf, {}
+popa, 0x61, i186&No64, ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf, {}
 
 // Exchange instructions.
 // xchg commutes:  we allow both operand orders.
@@ -295,10 +296,10 @@ lahf, 0x9f, No64, NoSuf, {}
 lahf, 0x9f, LAHF_SAHF, NoSuf, {}
 sahf, 0x9e, No64, NoSuf, {}
 sahf, 0x9e, LAHF_SAHF, NoSuf, {}
-pushf, 0x9c, No64, DefaultSize|No_bSuf|No_sSuf|No_qSuf, {}
-pushf, 0x9c, x64, DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, {}
-popf, 0x9d, No64, DefaultSize|No_bSuf|No_sSuf|No_qSuf, {}
-popf, 0x9d, x64, DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, {}
+pushf, 0x9c, No64, ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf, {}
+pushf, 0x9c, x64, ImplicitStackOp|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, {}
+popf, 0x9d, No64, ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf, {}
+popf, 0x9d, x64, ImplicitStackOp|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, {}
 stc, 0xf9, 0, NoSuf, {}
 std, 0xfd, 0, NoSuf, {}
 sti, 0xfb, 0, NoSuf, {}
@@ -520,16 +521,16 @@ shrd, 0xad, APX_F, Modrm|CheckOperandSize|No_bSuf|No_sSuf|DstVVVV|EVexMap4|NF, {
 shrd, 0xfad, i386, Modrm|CheckOperandSize|No_bSuf|No_sSuf, { Reg16|Reg32|Reg64, Reg16|Reg32|Reg64|Unspecified|BaseIndex }
 
 // Control transfer instructions.
-call, 0xe8, No64, JumpDword|DefaultSize|No_bSuf|No_sSuf|No_qSuf|BNDPrefixOk, { Disp16|Disp32 }
-call, 0xe8, x64, Amd64|JumpDword|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64|BNDPrefixOk, { Disp16|Disp32 }
-call, 0xe8, x64, Intel64|JumpDword|No_bSuf|No_wSuf|No_lSuf|No_sSuf|NoRex64|BNDPrefixOk, { Disp32 }
-call, 0xff/2, No64, Modrm|JumpAbsolute|DefaultSize|No_bSuf|No_sSuf|No_qSuf|BNDPrefixOk|NoTrackPrefixOk, { Reg16|Reg32|Unspecified|BaseIndex }
-call, 0xff/2, x64, Amd64|Modrm|JumpAbsolute|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64|BNDPrefixOk|NoTrackPrefixOk, { Reg16|Reg64|Unspecified|BaseIndex }
-call, 0xff/2, x64, Intel64|Modrm|JumpAbsolute|No_bSuf|No_wSuf|No_lSuf|No_sSuf|NoRex64|BNDPrefixOk|NoTrackPrefixOk, { Reg64|Unspecified|BaseIndex }
+call, 0xe8, No64, JumpDword|ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf|BNDPrefixOk, { Disp16|Disp32 }
+call, 0xe8, x64, Amd64|JumpDword|ImplicitStackOp|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64|BNDPrefixOk, { Disp16|Disp32 }
+call, 0xe8, x64, Intel64|JumpDword|ImplicitStackOp|No_bSuf|No_wSuf|No_lSuf|No_sSuf|NoRex64|BNDPrefixOk, { Disp32 }
+call, 0xff/2, No64, Modrm|JumpAbsolute|ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf|BNDPrefixOk|NoTrackPrefixOk, { Reg16|Reg32|Unspecified|BaseIndex }
+call, 0xff/2, x64, Amd64|Modrm|JumpAbsolute|ImplicitStackOp|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64|BNDPrefixOk|NoTrackPrefixOk, { Reg16|Reg64|Unspecified|BaseIndex }
+call, 0xff/2, x64, Intel64|Modrm|JumpAbsolute|ImplicitStackOp|No_bSuf|No_wSuf|No_lSuf|No_sSuf|NoRex64|BNDPrefixOk|NoTrackPrefixOk, { Reg64|Unspecified|BaseIndex }
 // Intel Syntax remaining call instances.
-call, 0x9a, No64, JumpInterSegment|DefaultSize|No_bSuf|No_sSuf|No_qSuf, { Imm16, Imm16|Imm32 }
-call, 0xff/3, 0, Amd64|Modrm|JumpAbsolute|DefaultSize|NoSuf, { Dword|Fword|BaseIndex }
-call, 0xff/3, x64, Intel64|Modrm|JumpAbsolute|NoSuf, { Dword|Fword|Tbyte|BaseIndex }
+call, 0x9a, No64, JumpInterSegment|ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf, { Imm16, Imm16|Imm32 }
+call, 0xff/3, 0, Amd64|Modrm|JumpAbsolute|ImplicitStackOp|DefaultSize|NoSuf, { Dword|Fword|BaseIndex }
+call, 0xff/3, x64, Intel64|Modrm|JumpAbsolute|ImplicitStackOp|NoSuf, { Dword|Fword|Tbyte|BaseIndex }
 lcall, 0x9a, No64, JumpInterSegment|DefaultSize|No_bSuf|No_sSuf|No_qSuf, { Imm16, Imm16|Imm32 }
 lcall, 0xff/3, 0, Amd64|Modrm|JumpAbsolute|DefaultSize|No_bSuf|No_sSuf|No_qSuf, { Unspecified|BaseIndex }
 lcall, 0xff/3, x64, Intel64|Modrm|JumpAbsolute|No_bSuf|No_sSuf, { Unspecified|BaseIndex }
@@ -547,22 +548,22 @@ ljmp, 0xea, No64, JumpInterSegment|No_bSuf|No_sSuf|No_qSuf, { Imm16, Imm16|Imm32
 ljmp, 0xff/5, 0, Amd64|Modrm|JumpAbsolute|No_bSuf|No_sSuf|No_qSuf, { Unspecified|BaseIndex }
 ljmp, 0xff/5, x64, Intel64|Modrm|JumpAbsolute|No_bSuf|No_sSuf, { Unspecified|BaseIndex }
 
-ret, 0xc3, No64, DefaultSize|No_bSuf|No_sSuf|No_qSuf|RepPrefixOk|BNDPrefixOk, {}
-ret, 0xc2, No64, DefaultSize|No_bSuf|No_sSuf|No_qSuf|RepPrefixOk|BNDPrefixOk, { Imm16 }
-ret, 0xc3, x64, Amd64|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64|RepPrefixOk|BNDPrefixOk, {}
-ret, 0xc2, x64, Amd64|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64|RepPrefixOk|BNDPrefixOk, { Imm16 }
-ret, 0xc3, x64, Intel64|Size64|No_bSuf|No_wSuf|No_lSuf|No_sSuf|NoRex64|RepPrefixOk|BNDPrefixOk, {}
-ret, 0xc2, x64, Intel64|Size64|No_bSuf|No_wSuf|No_lSuf|No_sSuf|NoRex64|RepPrefixOk|BNDPrefixOk, { Imm16 }
+ret, 0xc3, No64, ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf|RepPrefixOk|BNDPrefixOk, {}
+ret, 0xc2, No64, ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf|RepPrefixOk|BNDPrefixOk, { Imm16 }
+ret, 0xc3, x64, Amd64|ImplicitStackOp|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64|RepPrefixOk|BNDPrefixOk, {}
+ret, 0xc2, x64, Amd64|ImplicitStackOp|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64|RepPrefixOk|BNDPrefixOk, { Imm16 }
+ret, 0xc3, x64, Intel64|ImplicitStackOp|Size64|No_bSuf|No_wSuf|No_lSuf|No_sSuf|NoRex64|RepPrefixOk|BNDPrefixOk, {}
+ret, 0xc2, x64, Intel64|ImplicitStackOp|Size64|No_bSuf|No_wSuf|No_lSuf|No_sSuf|NoRex64|RepPrefixOk|BNDPrefixOk, { Imm16 }
 lret, 0xcb, 0, DefaultSize|No_bSuf|No_sSuf, {}
 lret, 0xca, 0, DefaultSize|No_bSuf|No_sSuf, { Imm16 }
 // Intel Syntax.
 retf, 0xcb, 0, DefaultSize|No_bSuf|No_sSuf, {}
 retf, 0xca, 0, DefaultSize|No_bSuf|No_sSuf, { Imm16 }
 
-enter, 0xc8, i186&No64, DefaultSize|No_bSuf|No_sSuf|No_qSuf, { Imm16, Imm8 }
-enter, 0xc8, x64, DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, { Imm16, Imm8 }
-leave, 0xc9, i186&No64, DefaultSize|No_bSuf|No_sSuf|No_qSuf, {}
-leave, 0xc9, x64, DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, {}
+enter, 0xc8, i186&No64, ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf, { Imm16, Imm8 }
+enter, 0xc8, x64, ImplicitStackOp|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, { Imm16, Imm8 }
+leave, 0xc9, i186&No64, ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf, {}
+leave, 0xc9, x64, ImplicitStackOp|DefaultSize|No_bSuf|No_lSuf|No_sSuf|NoRex64, {}
 
 <cc:opc, o:0, no:1, b:2, c:2, nae:2, nb:3, nc:3, ae:3, e:4, z:4, ne:5, nz:5, be:6, na:6, nbe:7, a:7, +
          s:8, ns:9, p:a, pe:a, np:b, po:b, l:c, nge:c, nl:d, ge:d, le:e, ng:e, nle:f, g:f>
@@ -642,11 +643,11 @@ bts, 0xfba/5, i386, Modrm|No_bSuf|No_sSuf|Optimize|HLEPrefixLock, { Imm8, Reg16|
 // Interrupts & op. sys insns.
 // See gas/config/tc-i386.c for conversion of 'int $3' into the special
 // int 3 insn.
-int, 0xcd, 0, NoSuf, { Imm8 }
-int1, 0xf1, 0, NoSuf, {}
-int3, 0xcc, 0, NoSuf, {}
-into, 0xce, No64, NoSuf, {}
-iret, 0xcf, 0, DefaultSize|No_bSuf|No_sSuf, {}
+int, 0xcd, 0, ImplicitStackOp|NoSuf, { Imm8 }
+int1, 0xf1, 0, ImplicitStackOp|NoSuf, {}
+int3, 0xcc, 0, ImplicitStackOp|NoSuf, {}
+into, 0xce, No64, ImplicitStackOp|NoSuf, {}
+iret, 0xcf, 0, ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf, {}
 // i386sl, i486sl, later 486, and Pentium.
 rsm, 0xfaa, i386, NoSuf, {}
 
@@ -3491,9 +3492,9 @@ uwrmsr, 0xf3f8/0, USER_MSR, Modrm|Vex128|VexMap7|VexW0|NoSuf, { Imm32, Reg64 }
 
 // APX Push2/Pop2 instructions.
 
-push2, 0xff/6, APX_F, Modrm|VexW0|EVex128|EVexMap4|VexVVVV|No_bSuf|No_wSuf|No_lSuf|No_sSuf, { Reg64, Reg64 }
-push2p, 0xff/6, APX_F, Modrm|VexW1|EVex128|EVexMap4|VexVVVV|No_bSuf|No_wSuf|No_lSuf|No_sSuf, { Reg64, Reg64 }
-pop2, 0x8f/0, APX_F, Modrm|VexW0|EVex128|EVexMap4|VexVVVV|No_bSuf|No_wSuf|No_lSuf|No_sSuf, { Reg64, Reg64 }
-pop2p, 0x8f/0, APX_F, Modrm|VexW1|EVex128|EVexMap4|VexVVVV|No_bSuf|No_wSuf|No_lSuf|No_sSuf, { Reg64, Reg64 }
+push2, 0xff/6, APX_F, Modrm|VexW0|EVex128|EVexMap4|VexVVVV|ImplicitStackOp|No_bSuf|No_wSuf|No_lSuf|No_sSuf, { Reg64, Reg64 }
+push2p, 0xff/6, APX_F, Modrm|VexW1|EVex128|EVexMap4|VexVVVV|ImplicitStackOp|No_bSuf|No_wSuf|No_lSuf|No_sSuf, { Reg64, Reg64 }
+pop2, 0x8f/0, APX_F, Modrm|VexW0|EVex128|EVexMap4|VexVVVV|ImplicitStackOp|No_bSuf|No_wSuf|No_lSuf|No_sSuf, { Reg64, Reg64 }
+pop2p, 0x8f/0, APX_F, Modrm|VexW1|EVex128|EVexMap4|VexVVVV|ImplicitStackOp|No_bSuf|No_wSuf|No_lSuf|No_sSuf, { Reg64, Reg64 }
 
 // APX Push2/Pop2 instructions end.
diff --git a/opcodes/i386-tbl.h b/opcodes/i386-tbl.h
index ffd27ff6cac..ac4a01cab14 100644
--- a/opcodes/i386-tbl.h
+++ b/opcodes/i386-tbl.h
@@ -315,7 +315,7 @@ static const insn_template i386_optab[] =
       { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_push, 0x50, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -323,7 +323,7 @@ static const insn_template i386_optab[] =
     { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_push, 0xff, 1, SPACE_BASE, 6,
-    { 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 1, 0, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -331,7 +331,7 @@ static const insn_template i386_optab[] =
     { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
 	  0, 0, 0, 0, 1, 0 } } } },
   { MN_push, 0x6a, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -339,7 +339,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_push, 0x68, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -347,7 +347,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_push, 0x06, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -355,7 +355,7 @@ static const insn_template i386_optab[] =
     { { { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_push, 0x50, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -363,7 +363,7 @@ static const insn_template i386_optab[] =
     { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_push, 0xff, 1, SPACE_BASE, 6,
-    { 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 1, 0, 0, 0, 0, 10, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -371,7 +371,7 @@ static const insn_template i386_optab[] =
     { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0,
 	  0, 0, 0, 0, 1, 0 } } } },
   { MN_push, 0x6a, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -379,7 +379,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_push, 0x68, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -387,7 +387,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_push, 0xa0, 1, SPACE_0F, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -395,7 +395,7 @@ static const insn_template i386_optab[] =
     { { { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_pushp, 0x50, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       1 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 } },
@@ -403,7 +403,7 @@ static const insn_template i386_optab[] =
     { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_pusha, 0x60, 0, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -411,7 +411,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_pop, 0x58, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -419,7 +419,7 @@ static const insn_template i386_optab[] =
     { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_pop, 0x8f, 1, SPACE_BASE, 0,
-    { 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 1, 0, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -427,7 +427,7 @@ static const insn_template i386_optab[] =
     { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
 	  0, 0, 0, 0, 1, 0 } } } },
   { MN_pop, 0x07, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -435,7 +435,7 @@ static const insn_template i386_optab[] =
     { { { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_pop, 0x58, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -443,7 +443,7 @@ static const insn_template i386_optab[] =
     { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_pop, 0x8f, 1, SPACE_BASE, 0,
-    { 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 1, 0, 0, 0, 0, 10, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -451,7 +451,7 @@ static const insn_template i386_optab[] =
     { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0,
 	  0, 0, 0, 0, 1, 0 } } } },
   { MN_pop, 0xa1, 1, SPACE_0F, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -459,7 +459,7 @@ static const insn_template i386_optab[] =
     { { { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_popp, 0x58, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       1 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 } },
@@ -467,7 +467,7 @@ static const insn_template i386_optab[] =
     { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_popa, 0x61, 0, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -729,7 +729,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_pushf, 0x9c, 0, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -737,7 +737,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_pushf, 0x9c, 0, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -745,7 +745,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_popf, 0x9d, 0, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -753,7 +753,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_popf, 0x9d, 0, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -2587,7 +2587,7 @@ static const insn_template i386_optab[] =
       { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0,
 	  0, 0, 0, 0, 1, 0 } } } },
   { MN_call, 0xe8, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0,
+    { 0, 0, 0, 0, 2, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -2595,7 +2595,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_call, 0xe8, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+    { 0, 0, 0, 0, 2, 0, 0, 0, 10, 2, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -2603,7 +2603,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_call, 0xe8, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0,
+    { 0, 0, 0, 0, 2, 0, 0, 0, 10, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -2611,7 +2611,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_call, 0xff, 1, SPACE_BASE, 2,
-    { 0, 0, 0, 1, 5, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 1, 3,
+    { 0, 0, 0, 1, 5, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 1, 3,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -2619,7 +2619,7 @@ static const insn_template i386_optab[] =
     { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0,
 	  0, 0, 0, 0, 1, 0 } } } },
   { MN_call, 0xff, 1, SPACE_BASE, 2,
-    { 0, 0, 0, 1, 5, 0, 0, 0, 0, 2, 1, 0, 1, 1, 0, 0, 0, 0, 1, 3,
+    { 0, 0, 0, 1, 5, 0, 0, 0, 10, 2, 1, 0, 1, 1, 0, 0, 0, 0, 1, 3,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -2627,7 +2627,7 @@ static const insn_template i386_optab[] =
     { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0,
 	  0, 0, 0, 0, 1, 0 } } } },
   { MN_call, 0xff, 1, SPACE_BASE, 2,
-    { 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 3,
+    { 0, 0, 0, 1, 5, 0, 0, 0, 10, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 3,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -2635,7 +2635,7 @@ static const insn_template i386_optab[] =
     { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0,
 	  0, 0, 0, 0, 1, 0 } } } },
   { MN_call, 0x9a, 2, SPACE_BASE, None,
-    { 0, 0, 0, 0, 4, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 4, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -2645,7 +2645,7 @@ static const insn_template i386_optab[] =
       { { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_call, 0xff, 1, SPACE_BASE, 3,
-    { 0, 0, 0, 1, 5, 0, 0, 0, 0, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 1, 5, 0, 0, 0, 10, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
@@ -2653,7 +2653,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_call, 0xff, 1, SPACE_BASE, 3,
-    { 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 1, 5, 0, 0, 0, 10, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -2779,7 +2779,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 1, 0 } } } },
   { MN_ret, 0xc3, 0, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -2787,7 +2787,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_ret, 0xc2, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -2795,7 +2795,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_ret, 0xc3, 0, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -2803,7 +2803,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_ret, 0xc2, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -2811,7 +2811,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_ret, 0xc3, 0, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
+    { 0, 0, 0, 0, 0, 0, 3, 0, 10, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -2819,7 +2819,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_ret, 0xc2, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
+    { 0, 0, 0, 0, 0, 0, 3, 0, 10, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -2859,7 +2859,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_enter, 0xc8, 2, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -2869,7 +2869,7 @@ static const insn_template i386_optab[] =
       { { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_enter, 0xc8, 2, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -2879,7 +2879,7 @@ static const insn_template i386_optab[] =
       { { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_leave, 0xc9, 0, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -2887,7 +2887,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_leave, 0xc9, 0, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 } },
@@ -3859,7 +3859,7 @@ static const insn_template i386_optab[] =
       { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0,
 	  0, 0, 0, 0, 1, 0 } } } },
   { MN_int, 0xcd, 1, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
@@ -3867,7 +3867,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_int1, 0xf1, 0, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
@@ -3875,7 +3875,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_int3, 0xcc, 0, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
@@ -3883,7 +3883,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_into, 0xce, 0, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 } },
@@ -3891,7 +3891,7 @@ static const insn_template i386_optab[] =
     { { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_iret, 0xcf, 0, SPACE_BASE, None,
-    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 0, 0, 0, 0, 0, 10, 2, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
@@ -41183,7 +41183,7 @@ static const insn_template i386_optab[] =
       { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_push2, 0xff, 2, SPACE_EVEXMAP4, 6,
-    { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 1, 0, 0, 0, 0, 10, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 } },
@@ -41193,7 +41193,7 @@ static const insn_template i386_optab[] =
       { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_push2p, 0xff, 2, SPACE_EVEXMAP4, 6,
-    { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 1, 0, 0, 0, 0, 10, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 } },
@@ -41203,7 +41203,7 @@ static const insn_template i386_optab[] =
       { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_pop2, 0x8f, 2, SPACE_EVEXMAP4, 0,
-    { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 1, 0, 0, 0, 0, 10, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 } },
@@ -41213,7 +41213,7 @@ static const insn_template i386_optab[] =
       { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_pop2p, 0x8f, 2, SPACE_EVEXMAP4, 0,
-    { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+    { 0, 0, 0, 1, 0, 0, 0, 0, 10, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 1, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0 },
     { { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0 } },
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 12/16] gas: synthesize CFI for hand-written asm
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
                   ` (10 preceding siblings ...)
  2024-01-11  7:48 ` [PATCH, V5 11/16] opcodes: i386: new marker for insns that implicitly update stack pointer Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 13/16] gas: doc: update documentation for the new listing option Indu Bhagat
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[Changes from V4 to V5]
  - Addressed several review comments.  Following may not be a complete
    list.
  - Fixed code comments for typos and clarity
  - tc-i386: ginsn_dw2_regnum: Removed undefined behaviour construct.
    Handle RegIP, RegIZ
  - tc-i386: Do not compile in unreachable code for non-ELF
  - tc-i386: Add function level comment for x86_scfi_callee_saved_p to
    indicate use of System V AMD64 ABI
  - tc-i386: Use GINSN_TYPE_OTHER instead of GINSN_TYPE_ADD in
    x86_ginsn_lea
  - ginsn: make more symbols const
  - tc-i386: carve out the x86_ginsn_leave to keep code symmetrical.
    Also handle 16-bit leave
  - tc-i386: handle 16-bit enter insn
  - tc-i386: add other opcodes for CMP and TEST
  - tc-i386: User DWARF register number for both pushf and popf
    consistently
  - obj-elf: fix code comments for .type in obj_elf_type
  - tc-i386: add handlig for case of 2 regs for 0x3, 0x29 opcodes
  - tc-i386: disallow .insn with SCFI
  - tc-i386: rename ginsn_prefix_66H_p to ginsn_opsize_prefix_p.  Call
    this API without flag_code check. Check for REX.W when checking for
    insn.prefix[DATA_PREFIX]
  - tc-i386: use the new IMPLICIT_STACK_OP operand constraint
  - tc-i386: handle reg case for push/pop r/m operations.  Although not
    generated now, its better to add this handling now
  - tc-i386: disallow APX opcodes
  - tc-i386: fold x86_ginsn_jump and x86_ginsn_jump_cond into same
    function
  - tc-i386: add a new X86_GINSN_UNHANDLED_UNEXPECTED to detect NULL reg
    in x86_ginsn_unhandled
  - tc-i386: update omment styling around case labels. Add comment for
    those opcodes which appear after relaxation only.
[End of changes from V4 to V5]

[Changes from V3 to V4]
  - x86 backend: detect if any unhandled instuctions affect the stack
    pointer implicitly.  Bail out if so.
  - x86 backend: get rid of insn as argument in all calls for ginsn
    creation.
  - x86 backend: bugfix in add insn translation.
  - scfi: ginsn: all symbol usages are const.  Update args to reflect
    the same.
  - scfi: ginsn: print info about GINSN_TYPE_OTHER in ginsn_print ().
  - scfi: include GINSN_TYPE_OTHER in handling to detect stack pointer
    update.
  - scfi: treat failures in SCFI forward and backward passes as errors.
  - Fix build failure with --target=x86_64-w64-mingw32 (reported by
    Nick) by guarding the offending construct with
    (OBJ_ELF || MAYBE_OBJ_ELF).
  - ginsn: Add new API gcfg_print () to print CFG for (development time)
    debugging.
  - gas: hand-crafting instructions with .byte together with SCFI is not
    supported.
  - Other code comments fixup.
[End of changes from V3 to V4]

[Changes from V2 to V3]
  - Some bugfixes in the SCFI machinery.
  - Addressed most of Jan's review comments. Few highlights:
    + Handle more instructions: LOOP*, J{E,R}CXZ, ENTER, opcodes 0x3 and
      0x2b and 0x8a, popf.
    + Removed some gas_assert, now that we have more clarity on how to
      completely handle all variants of some opcodes.
    + Handled some specific cases brought up in the review (e.g., add
      %eax, symbol etc.)
    + Add operand size override for all push/pop variants: push reg, pop
      reg, push fs, push gs, push imm, push from mem, pop to mem.
    + Add a new function to skip some instructions from SCFI: Skip cmp
      and test insns.
    + ginsn representation: Use offsetT datatype for imm/disp, use
      unsigned int for reg.  Rename some args from src_val to use a more
      appropriate name of src_reg.
    + Other improvements in code layout, code comments and formatting.
      Added a comment with x86_ginsn_new () for explaining what
      instructions are necessary to be visited for SCFI and other details.
    + Other review comments.
  - Pending items:
    + "Adjust the config/tc-i386.h such that e.g. COFF targets don't
      needlessly have a large set of dead code compiled in."
    + Streamline comments in scfi.c so that all constraints are easy to
      find.
[End of changes from V2 to V3]

This patch adds support in GAS to create generic GAS instructions
(a.k.a., the ginsn) for the x86 backend (AMD64 ABI only at this time).
Using this ginsn infrastructure, GAS can then synthesize CFI for
hand-written asm for x86_64.

A ginsn is a target-independent representation of the machine
instructions.  One machine instruction may need one or more ginsn.

This patch also adds skeleton support for printing ginsn in the listing
output for debugging purposes.

Since the current use-case of ginsn is to synthesize CFI, the x86 target
needs to generate ginsns necessary for the following machine
instructions only:

 - All change of flow instructions, including all conditional and
   unconditional branches, call and return from functions.
 - All register saves and unsaves to the stack.
 - All instructions affecting the two registers that could potentially
   be used as the base register for CFA tracking.  For SCFI, the base
   register for CFA tracking is limited to REG_SP and REG_FP only for
   now.

The representation of ginsn is kept simple:

- GAS instruction has GINSN_NUM_SRC_OPNDS (defined to be 2 at this time)
  number of source operands and one destination operand at this time.
- GAS instruction uses DWARF register numbers in its representation and
  does not track register size.
- GAS instructions carry location information (file name and line
  number).
- GAS instructions are ID's with a natural number in order of their
  addtion to the list.  This can be used as a proxy for the static
  program order of the corresponding machine instructions.

Note that, GAS instruction (ginsn) format does not support
GINSN_TYPE_PUSH and GINSN_TYPE_POP.  Some architectures, like aarch64,
do not have push and pop instructions, but rather STP/LDP/STR/LDR etc.
instructions.  Further these instructions have a variety of addressing
modes, like offset, pre-indexing and post-indexing etc.  Among other
things, one of differences in these addressing modes is _when_ the addr
register is updated with the result of the address calculation: before
or after the memory operation.  To best support such needs, the generic
instructions like GINSN_TYPE_LOAD, GINSN_TYPE_STORE together with
GINSN_TYPE_ADD, and GINSN_TYPE_SUB may be used.

The functionality provided in ginsn.c and scfi.c is compiled in when a
target defines TARGET_USE_SCFI and TARGET_USE_GINSN.  This can be
revisited later when there are other use-cases of creating ginsn's in
GAS, apart from the current use-case of synthesizing CFI for
hand-written asm.

Support is added only for System V AMD64 ABI for ELF at this time.  If
the user enables SCFI with --32, GAS issues an error:

  "Fatal error: SCFI is not supported for this ABI"

For synthesizing (DWARF) CFI, the SCFI machinery requires the programmer
to adhere to some pre-requisites for their asm:
   - Hand-written asm block must begin with a .type   foo, @function
It is highly recommended to, additionally, also ensure that:
   - Hand-written asm block ends with a .size foo, .-foo

The SCFI machinery encodes some rules which align with the standard
calling convention specified by the ABI.  Apart from the rules, the SCFI
machinery employs some heuristics.  For example:
   - The base register for CFA tracking may be either REG_SP or REG_FP.
   - If the base register for CFA tracking is REG_SP, the precise amount of
     stack usage (and hence, the value of REG_SP) must be known at all times.
   - If using dynamic stack allocation, the function must switch to
     FP-based CFA.  This means using instructions like the following (in
     AMD64) in prologue:
        pushq   %rbp
        movq    %rsp, %rbp
     and analogous instructions in epilogue.
   - Save and Restore of callee-saved registers must be symmetrical.
     However, the SCFI machinery at this time only warns if any such
     asymmetry is seen.

These heuristics/rules are architecture-independent and are meant to
employed for all architectures/ABIs using SCFI in the future.

gas/
	* Makefile.am: Add new files.
	* Makefile.in: Regenerated.
	* as.c (defined): Handle documentation and listing option for
	ginsns and SCFI.
	* config/obj-elf.c (obj_elf_size): Invoke ginsn_data_end.
	(obj_elf_type): Invoke ginsn_data_begin.
	* config/tc-i386.c (x86_scfi_callee_saved_p): New function.
	(ginsn_prefix_66H_p): Likewise.
	(ginsn_dw2_regnum): Likewise.
	(x86_ginsn_addsub_reg_mem): Likewise.
	(x86_ginsn_addsub_mem_reg): Likewise.
	(x86_ginsn_alu_imm): Likewise.
	(x86_ginsn_move): Likewise.
	(x86_ginsn_lea): Likewise.
	(x86_ginsn_jump): Likewise.
	(x86_ginsn_jump_cond): Likewise.
	(x86_ginsn_enter): Likewise.
	(x86_ginsn_safe_to_skip): Likewise.
	(x86_ginsn_unhandled): Likewise.
	(x86_ginsn_new): New functionality to generate ginsns.
	(md_assemble): Invoke x86_ginsn_new.
	(s_insn): Likewise.
	(i386_target_format): Add hard error for usage of SCFI with non AMD64 ABIs.
	* config/tc-i386.h (TARGET_USE_GINSN): New definition.
	(TARGET_USE_SCFI): Likewise.
	(SCFI_MAX_REG_ID): Likewise.
	(REG_FP): Likewise.
	(REG_SP): Likewise.
	(SCFI_INIT_CFA_OFFSET): Likewise.
	(SCFI_CALLEE_SAVED_REG_P): Likewise.
	(x86_scfi_callee_saved_p): Likewise.
	* gas/listing.h (LISTING_GINSN_SCFI): New define for ginsn and
	SCFI.
	* gas/read.c (read_a_source_file): Close SCFI processing at end
	of file read.
	* gas/scfidw2gen.c (scfi_process_cfi_label): Add implementation.
	(scfi_process_cfi_signal_frame): Likewise.
	* subsegs.h (struct frch_ginsn_data): New forward declaration.
	(struct frchain): New member for ginsn data.
	* gas/subsegs.c (subseg_set_rest): Initialize the new member.
	* symbols.c (colon): Invoke ginsn_frob_label to convey
	user-defined labels to ginsn infrastructure.
	* ginsn.c: New file.
	* ginsn.h: New file.
	* scfi.c: New file.
	* scfi.h: New file.
---
 gas/Makefile.am      |    4 +
 gas/Makefile.in      |   19 +-
 gas/as.c             |    5 +
 gas/config/obj-elf.c |   18 +
 gas/config/tc-i386.c | 1110 +++++++++++++++++++++++++++++++++++++
 gas/config/tc-i386.h |   21 +
 gas/ginsn.c          | 1259 ++++++++++++++++++++++++++++++++++++++++++
 gas/ginsn.h          |  384 +++++++++++++
 gas/listing.h        |    1 +
 gas/read.c           |   10 +
 gas/scfi.c           | 1232 +++++++++++++++++++++++++++++++++++++++++
 gas/scfi.h           |   38 ++
 gas/scfidw2gen.c     |   28 +-
 gas/subsegs.c        |    1 +
 gas/subsegs.h        |    2 +
 gas/symbols.c        |    3 +
 16 files changed, 4125 insertions(+), 10 deletions(-)
 create mode 100644 gas/ginsn.c
 create mode 100644 gas/ginsn.h
 create mode 100644 gas/scfi.c
 create mode 100644 gas/scfi.h

diff --git a/gas/Makefile.am b/gas/Makefile.am
index 2848fac47b0..37ca0952f7e 100644
--- a/gas/Makefile.am
+++ b/gas/Makefile.am
@@ -82,6 +82,7 @@ GAS_CFILES = \
 	flonum-mult.c \
 	frags.c \
 	gen-sframe.c \
+	ginsn.c \
 	hash.c \
 	input-file.c \
 	input-scrub.c \
@@ -94,6 +95,7 @@ GAS_CFILES = \
 	remap.c \
 	sb.c \
 	scfidw2gen.c \
+	scfi.c \
 	sframe-opt.c \
 	stabs.c \
 	subsegs.c \
@@ -119,6 +121,7 @@ HFILES = \
 	flonum.h \
 	frags.h \
 	gen-sframe.h \
+	ginsn.h \
 	hash.h \
 	input-file.h \
 	itbl-lex.h \
@@ -130,6 +133,7 @@ HFILES = \
 	read.h \
 	sb.h \
 	scfidw2gen.h \
+	scfi.h \
 	subsegs.h \
 	symbols.h \
 	tc.h \
diff --git a/gas/Makefile.in b/gas/Makefile.in
index 7d4fbfc055c..bc25765cb5b 100644
--- a/gas/Makefile.in
+++ b/gas/Makefile.in
@@ -173,12 +173,13 @@ am__objects_1 = app.$(OBJEXT) as.$(OBJEXT) atof-generic.$(OBJEXT) \
 	ecoff.$(OBJEXT) ehopt.$(OBJEXT) expr.$(OBJEXT) \
 	flonum-copy.$(OBJEXT) flonum-konst.$(OBJEXT) \
 	flonum-mult.$(OBJEXT) frags.$(OBJEXT) gen-sframe.$(OBJEXT) \
-	hash.$(OBJEXT) input-file.$(OBJEXT) input-scrub.$(OBJEXT) \
-	listing.$(OBJEXT) literal.$(OBJEXT) macro.$(OBJEXT) \
-	messages.$(OBJEXT) output-file.$(OBJEXT) read.$(OBJEXT) \
-	remap.$(OBJEXT) sb.$(OBJEXT) scfidw2gen.$(OBJEXT) \
-	sframe-opt.$(OBJEXT) stabs.$(OBJEXT) subsegs.$(OBJEXT) \
-	symbols.$(OBJEXT) write.$(OBJEXT)
+	ginsn.$(OBJEXT) hash.$(OBJEXT) input-file.$(OBJEXT) \
+	input-scrub.$(OBJEXT) listing.$(OBJEXT) literal.$(OBJEXT) \
+	macro.$(OBJEXT) messages.$(OBJEXT) output-file.$(OBJEXT) \
+	read.$(OBJEXT) remap.$(OBJEXT) sb.$(OBJEXT) \
+	scfidw2gen.$(OBJEXT) scfi.$(OBJEXT) sframe-opt.$(OBJEXT) \
+	stabs.$(OBJEXT) subsegs.$(OBJEXT) symbols.$(OBJEXT) \
+	write.$(OBJEXT)
 am_as_new_OBJECTS = $(am__objects_1)
 am__dirstamp = $(am__leading_dot)dirstamp
 as_new_OBJECTS = $(am_as_new_OBJECTS)
@@ -581,6 +582,7 @@ GAS_CFILES = \
 	flonum-mult.c \
 	frags.c \
 	gen-sframe.c \
+	ginsn.c \
 	hash.c \
 	input-file.c \
 	input-scrub.c \
@@ -593,6 +595,7 @@ GAS_CFILES = \
 	remap.c \
 	sb.c \
 	scfidw2gen.c \
+	scfi.c \
 	sframe-opt.c \
 	stabs.c \
 	subsegs.c \
@@ -617,6 +620,7 @@ HFILES = \
 	flonum.h \
 	frags.h \
 	gen-sframe.h \
+	ginsn.h \
 	hash.h \
 	input-file.h \
 	itbl-lex.h \
@@ -628,6 +632,7 @@ HFILES = \
 	read.h \
 	sb.h \
 	scfidw2gen.h \
+	scfi.h \
 	subsegs.h \
 	symbols.h \
 	tc.h \
@@ -1336,6 +1341,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flonum-mult.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/frags.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gen-sframe.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ginsn.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input-file.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/input-scrub.Po@am__quote@
@@ -1350,6 +1356,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remap.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scfi.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scfidw2gen.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sframe-opt.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stabs.Po@am__quote@
diff --git a/gas/as.c b/gas/as.c
index 659da188da5..9e059f99ef1 100644
--- a/gas/as.c
+++ b/gas/as.c
@@ -45,6 +45,7 @@
 #include "codeview.h"
 #include "bfdver.h"
 #include "write.h"
+#include "ginsn.h"
 
 #ifdef HAVE_ITBL_CPU
 #include "itbl-ops.h"
@@ -245,6 +246,7 @@ Options:\n\
                       	  d      omit debugging directives\n\
                       	  g      include general info\n\
                       	  h      include high-level source\n\
+                      	  i      include ginsn and synthesized CFI info\n\
                       	  l      include assembly\n\
                       	  m      include macro expansions\n\
                       	  n      omit forms processing\n\
@@ -1088,6 +1090,9 @@ This program has absolutely no warranty.\n"));
 		    case 'h':
 		      listing |= LISTING_HLL;
 		      break;
+		    case 'i':
+		      listing |= LISTING_GINSN_SCFI;
+		      break;
 		    case 'l':
 		      listing |= LISTING_LISTING;
 		      break;
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index 1f34f5b6417..00c6e38acf9 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -24,6 +24,7 @@
 #include "subsegs.h"
 #include "obstack.h"
 #include "dwarf2dbg.h"
+#include "ginsn.h"
 
 #ifndef ECOFF_DEBUGGING
 #define ECOFF_DEBUGGING 0
@@ -2311,6 +2312,13 @@ obj_elf_size (int ignore ATTRIBUTE_UNUSED)
       symbol_get_obj (sym)->size = XNEW (expressionS);
       *symbol_get_obj (sym)->size = exp;
     }
+
+  /* If the symbol in the directive matches the current function being
+     processed, indicate end of the current stream of ginsns.  */
+  if (flag_synth_cfi
+      && S_IS_FUNCTION (sym) && sym == ginsn_data_func_symbol ())
+    ginsn_data_end (symbol_temp_new_now ());
+
   demand_empty_rest_of_line ();
 }
 
@@ -2499,6 +2507,16 @@ obj_elf_type (int ignore ATTRIBUTE_UNUSED)
 	elfsym->symbol.flags &= ~mask;
     }
 
+  if (S_IS_FUNCTION (sym) && flag_synth_cfi)
+    {
+      /* When using SCFI, .type directive indicates start of a new FDE for SCFI
+	 processing.  So, we must first demarcate the previous block of ginsns,
+	 if any, to mark the end of a previous FDE.  */
+      if (frchain_now->frch_ginsn_data)
+	ginsn_data_end (symbol_temp_new_now ());
+      ginsn_data_begin (sym);
+    }
+
   demand_empty_rest_of_line ();
 }
 
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 8bbed03bd9a..0e3a7bde026 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -30,6 +30,7 @@
 #include "subsegs.h"
 #include "dwarf2dbg.h"
 #include "dw2gencfi.h"
+#include "scfi.h"
 #include "gen-sframe.h"
 #include "sframe.h"
 #include "elf/x86-64.h"
@@ -5327,6 +5328,1092 @@ static INLINE bool may_need_pass2 (const insn_template *t)
 	       && t->base_opcode == 0x63);
 }
 
+#if defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF)
+
+/* DWARF register number for EFLAGS.  Used for pushf/popf insns.  */
+#define GINSN_DW2_REGNUM_EFLAGS     49
+/* DWARF register number for RSI.  Used as dummy value when RegIP/RegIZ.  */
+#define GINSN_DW2_REGNUM_RSI_DUMMY  4
+
+/* Identify the callee-saved registers in System V AMD64 ABI.  */
+
+bool
+x86_scfi_callee_saved_p (unsigned int dw2reg_num)
+{
+  if (dw2reg_num == 3 /* rbx.  */
+      || dw2reg_num == REG_FP /* rbp.  */
+      || dw2reg_num == REG_SP /* rsp.  */
+      || (dw2reg_num >= 12 && dw2reg_num <= 15) /* r12 - r15.  */)
+    return true;
+
+  return false;
+}
+
+/* Check whether an instruction prefix which affects operation size
+   accompanies.  Setting REX.W takes precedence over the operand-size prefix
+   (66H) when both are used.
+
+   The current users of this API are in the handlers for PUSH, POP
+   instructions.  These instructions affect the stack pointer implicitly:  the
+   operand size (16, 32, or 64 bits) determines the amount by which the stack
+   pointer is decremented (2, 4 or 8).  */
+
+static bool
+ginsn_opsize_prefix_p (void)
+{
+  return (!(i.prefix[REX_PREFIX] & REX_W) && i.prefix[DATA_PREFIX]);
+}
+
+/* Get the DWARF register number for the given register entry.
+   For specific byte/word/dword register accesses like al, cl, ah, ch, r8d,
+   r20w etc., we need to identify the DWARF register number for the
+   corresponding 8-byte GPR.
+
+   This function is a hack - it relies on relative ordering of reg entries in
+   the i386_regtab.  FIXME - it will be good to allow a more direct way to get
+   this information.  */
+
+static unsigned int
+ginsn_dw2_regnum (const reg_entry *ireg)
+{
+  /* PS: Note the data type here as int32_t, because of Dw2Inval (-1).  */
+  int32_t dwarf_reg = Dw2Inval;
+  const reg_entry *temp = ireg;
+  unsigned int idx = 0;
+
+  /* ginsn creation is available for AMD64 abi only ATM.  Other flag_code
+     are not expected.  */
+  gas_assert (ireg && flag_code == CODE_64BIT);
+
+  /* Watch out for RegIP, RegIZ.  These are expected to appear only with
+     base/index addressing modes.  Although creating inaccurate data
+     dependencies, using a dummy value (lets say volatile register rsi) will
+     not hurt SCFI.  TBD_GINSN_GEN_NOT_SCFI.  */
+  if (ireg->reg_num == RegIP || ireg->reg_num == RegIZ)
+    return GINSN_DW2_REGNUM_RSI_DUMMY;
+
+  dwarf_reg = ireg->dw2_regnum[flag_code >> 1];
+
+  if (dwarf_reg == Dw2Inval)
+    {
+      if (ireg <= &i386_regtab[3])
+	/* For al, cl, dl, bl, bump over to axl, cxl, dxl, bxl respectively by
+	   adding 8.  */
+	temp = ireg + 8;
+      else if (ireg <= &i386_regtab[7])
+	/* For ah, ch, dh, bh, bump over to axl, cxl, dxl, bxl respectively by
+	   adding 4.  */
+	temp = ireg + 4;
+      else
+	{
+	  /* The code relies on the relative ordering of the reg entries in
+	     i386_regtab.  There are 32 register entries between axl-r31b,
+	     ax-r31w etc.  The assertions here ensures the code does not
+	     recurse indefinitely.  */
+	  gas_assert ((temp - &i386_regtab[0]) >= 0);
+	  idx = temp - &i386_regtab[0];
+	  gas_assert (idx + 32 < i386_regtab_size - 1);
+
+	  temp = temp + 32;
+	}
+
+      dwarf_reg = ginsn_dw2_regnum (temp);
+    }
+
+  /* Sanity check - failure may indicate state corruption, bad ginsn or
+     perhaps the i386-reg table and the current function got out of sync.  */
+  gas_assert (dwarf_reg >= 0);
+
+  return (unsigned int) dwarf_reg;
+}
+
+static ginsnS *
+x86_ginsn_addsub_reg_mem (const symbolS *insn_end_sym)
+{
+  unsigned int dw2_regnum;
+  unsigned int src1_dw2_regnum;
+  ginsnS *ginsn = NULL;
+  ginsnS * (*ginsn_func) (const symbolS *, bool,
+			  enum ginsn_src_type, unsigned int, offsetT,
+			  enum ginsn_src_type, unsigned int, offsetT,
+			  enum ginsn_dst_type, unsigned int, offsetT);
+  uint16_t opcode = i.tm.base_opcode;
+
+  gas_assert (opcode == 0x1 || opcode == 0x29);
+  ginsn_func = (opcode == 0x1) ? ginsn_new_add : ginsn_new_sub;
+
+  /* op %reg, symbol or even other cases where destination involves indirect
+     access are unnecessary for SCFI correctness.  TBD_GINSN_GEN_NOT_SCFI.  */
+  if (i.mem_operands)
+    return ginsn;
+
+  /* op reg, reg/mem.  */
+  src1_dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+  /* Of interest only when second opnd is not memory.  */
+  if (i.reg_operands == 2)
+    {
+      dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
+      ginsn = ginsn_func (insn_end_sym, true,
+			  GINSN_SRC_REG, src1_dw2_regnum, 0,
+			  GINSN_SRC_REG, dw2_regnum, 0,
+			  GINSN_DST_REG, dw2_regnum, 0);
+      ginsn_set_where (ginsn);
+    }
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_addsub_mem_reg (const symbolS *insn_end_sym)
+{
+  unsigned int dw2_regnum;
+  unsigned int src1_dw2_regnum;
+  const reg_entry *mem_reg;
+  int32_t gdisp = 0;
+  ginsnS *ginsn = NULL;
+  ginsnS * (*ginsn_func) (const symbolS *, bool,
+			  enum ginsn_src_type, unsigned int, offsetT,
+			  enum ginsn_src_type, unsigned int, offsetT,
+			  enum ginsn_dst_type, unsigned int, offsetT);
+  uint16_t opcode = i.tm.base_opcode;
+
+  gas_assert (opcode == 0x3 || opcode == 0x2b);
+  ginsn_func = (opcode == 0x3) ? ginsn_new_add : ginsn_new_sub;
+
+  /* op symbol, %reg.  */
+  if (i.mem_operands && !i.base_reg && !i.index_reg)
+    return ginsn;
+
+  /* op reg/mem, %reg.  */
+  dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
+
+  if (i.reg_operands == 2)
+    {
+      src1_dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      ginsn = ginsn_func (insn_end_sym, true,
+			  GINSN_SRC_REG, src1_dw2_regnum, 0,
+			  GINSN_SRC_REG, dw2_regnum, 0,
+			  GINSN_DST_REG, dw2_regnum, 0);
+      ginsn_set_where (ginsn);
+    }
+  else if (i.mem_operands)
+    {
+      mem_reg = (i.base_reg) ? i.base_reg : i.index_reg;
+      src1_dw2_regnum = ginsn_dw2_regnum (mem_reg);
+      if (i.disp_operands == 1)
+	gdisp = i.op[0].disps->X_add_number;
+      ginsn = ginsn_func (insn_end_sym, true,
+			  GINSN_SRC_INDIRECT, src1_dw2_regnum, gdisp,
+			  GINSN_SRC_REG, dw2_regnum, 0,
+			  GINSN_DST_REG, dw2_regnum, 0);
+      ginsn_set_where (ginsn);
+    }
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_alu_imm (const symbolS *insn_end_sym)
+{
+  offsetT src_imm;
+  unsigned int dw2_regnum;
+  ginsnS *ginsn = NULL;
+  enum ginsn_src_type src_type = GINSN_SRC_REG;
+  enum ginsn_dst_type dst_type = GINSN_DST_REG;
+
+  ginsnS * (*ginsn_func) (const symbolS *, bool,
+			  enum ginsn_src_type, unsigned int, offsetT,
+			  enum ginsn_src_type, unsigned int, offsetT,
+			  enum ginsn_dst_type, unsigned int, offsetT);
+
+  /* FIXME - create ginsn where dest is REG_SP / REG_FP only ? */
+  /* Map for insn.tm.extension_opcode
+     000 ADD    100 AND
+     001 OR     101 SUB
+     010 ADC    110 XOR
+     011 SBB    111 CMP  */
+
+  /* add/sub/and imm, %reg only at this time for SCFI.
+     Although all three ('and', 'or' , 'xor') make the destination reg
+     untraceable, 'and' op is handled but not 'or' / 'xor' because we will look
+     into supporting the DRAP pattern at some point.  Other opcodes ('adc',
+     'sbb' and 'cmp') are not generated here either.  The ginsn representation
+     does not have support for the latter three opcodes;  GINSN_TYPE_OTHER may
+     be added for these after x86_ginsn_unhandled () invocation if the
+     destination register is REG_SP or REG_FP.  */
+  if (i.tm.extension_opcode == 5)
+    ginsn_func = ginsn_new_sub;
+  else if (i.tm.extension_opcode == 4)
+    ginsn_func = ginsn_new_and;
+  else if (i.tm.extension_opcode == 0)
+    ginsn_func = ginsn_new_add;
+  else
+    return ginsn;
+
+  /* TBD_GINSN_REPRESENTATION_LIMIT: There is no representation for when a
+     symbol is used as an operand, like so:
+	  addq    $simd_cmp_op+8, %rdx
+     Skip generating any ginsn for this.  */
+  if (i.imm_operands == 1
+      && i.op[0].imms->X_op != O_constant)
+    return ginsn;
+
+  /* addq    $1, symbol
+     addq    $1, -16(%rbp)
+     These are not of interest for SCFI.  Also, TBD_GINSN_GEN_NOT_SCFI.  */
+  if (i.mem_operands == 1)
+    return ginsn;
+
+  gas_assert (i.imm_operands == 1);
+  src_imm = i.op[0].imms->X_add_number;
+  /* The second operand may be a register or indirect access.  For SCFI, only
+     the case when the second opnd is a register is interesting.  Revisit this
+     if generating ginsns for a different gen mode TBD_GINSN_GEN_NOT_SCFI.  */
+  if (i.reg_operands == 1)
+    {
+      dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
+      /* For ginsn, keep the imm as second src operand.  */
+      ginsn = ginsn_func (insn_end_sym, true,
+			  src_type, dw2_regnum, 0,
+			  GINSN_SRC_IMM, 0, src_imm,
+			  dst_type, dw2_regnum, 0);
+
+      ginsn_set_where (ginsn);
+    }
+
+  return ginsn;
+}
+
+/* Create ginsn(s) for MOV operations.
+
+   The generated ginsns corresponding to mov with indirect access to memory
+   (src or dest) suffer with loss of information: when both index and base
+   registers are at play, only base register gets conveyed in ginsn.  Note
+   this TBD_GINSN_GEN_NOT_SCFI.  */
+
+static ginsnS *
+x86_ginsn_move (const symbolS *insn_end_sym)
+{
+  ginsnS *ginsn = NULL;
+  unsigned int dst_reg;
+  unsigned int src_reg;
+  offsetT src_disp = 0;
+  offsetT dst_disp = 0;
+  const reg_entry *dst = NULL;
+  const reg_entry *src = NULL;
+  uint16_t opcode = i.tm.base_opcode;
+  enum ginsn_src_type src_type = GINSN_SRC_REG;
+  enum ginsn_dst_type dst_type = GINSN_DST_REG;
+
+  /* mov %reg, symbol or mov symbol, %reg.
+     Not of interest for SCFI.  Also, TBD_GINSN_GEN_NOT_SCFI.  */
+  if (i.mem_operands == 1 && !i.base_reg && !i.index_reg)
+    return ginsn;
+
+  if (opcode == 0x8b || opcode == 0x8a)
+    {
+      /* mov  disp(%reg), %reg.  */
+      if (i.mem_operands)
+	{
+	  src = (i.base_reg) ? i.base_reg : i.index_reg;
+	  if (i.disp_operands == 1)
+	    src_disp = i.op[0].disps->X_add_number;
+	  src_type = GINSN_SRC_INDIRECT;
+	}
+      else
+	src = i.op[0].regs;
+
+      dst = i.op[1].regs;
+    }
+  else if (opcode == 0x89 || opcode == 0x88)
+    {
+      /* mov %reg, disp(%reg).  */
+      src = i.op[0].regs;
+      if (i.mem_operands)
+	{
+	  dst = (i.base_reg) ? i.base_reg : i.index_reg;
+	  if (i.disp_operands == 1)
+	    dst_disp = i.op[1].disps->X_add_number;
+	  dst_type = GINSN_DST_INDIRECT;
+	}
+      else
+	dst = i.op[1].regs;
+    }
+
+  src_reg = ginsn_dw2_regnum (src);
+  dst_reg = ginsn_dw2_regnum (dst);
+
+  ginsn = ginsn_new_mov (insn_end_sym, true,
+			 src_type, src_reg, src_disp,
+			 dst_type, dst_reg, dst_disp);
+  ginsn_set_where (ginsn);
+
+  return ginsn;
+}
+
+/* Generate appropriate ginsn for lea.
+   Sub-cases marked with TBD_GINSN_INFO_LOSS indicate some loss of information
+   in the ginsn.  But these are fine for now for GINSN_GEN_SCFI generation
+   mode.  */
+
+static ginsnS *
+x86_ginsn_lea (const symbolS *insn_end_sym)
+{
+  offsetT src_disp = 0;
+  ginsnS *ginsn = NULL;
+  unsigned int base_reg;
+  unsigned int index_reg;
+  offsetT index_scale;
+  unsigned int dst_reg;
+
+  if (!i.index_reg && !i.base_reg)
+    {
+      /* lea symbol, %rN.  */
+      dst_reg = ginsn_dw2_regnum (i.op[1].regs);
+      /* TBD_GINSN_INFO_LOSS - Skip encoding information about the symbol.  */
+      ginsn = ginsn_new_mov (insn_end_sym, false,
+			     GINSN_SRC_IMM, 0xf /* arbitrary const.  */, 0,
+			     GINSN_DST_REG, dst_reg, 0);
+    }
+  else if (i.base_reg && !i.index_reg)
+    {
+      /* lea    -0x2(%base),%dst.  */
+      base_reg = ginsn_dw2_regnum (i.base_reg);
+      dst_reg = ginsn_dw2_regnum (i.op[1].regs);
+
+      if (i.disp_operands)
+	src_disp = i.op[0].disps->X_add_number;
+
+      if (src_disp)
+	/* Generate an ADD ginsn.  */
+	ginsn = ginsn_new_add (insn_end_sym, true,
+			       GINSN_SRC_REG, base_reg, 0,
+			       GINSN_SRC_IMM, 0, src_disp,
+			       GINSN_DST_REG, dst_reg, 0);
+      else
+	/* Generate a MOV ginsn.  */
+	ginsn = ginsn_new_mov (insn_end_sym, true,
+			       GINSN_SRC_REG, base_reg, 0,
+			       GINSN_DST_REG, dst_reg, 0);
+    }
+  else if (!i.base_reg && i.index_reg)
+    {
+      /* lea (,%index,imm), %dst.  */
+      /* TBD_GINSN_INFO_LOSS - There is no explicit ginsn multiply operation,
+	 instead use GINSN_TYPE_OTHER.  Also, note that info about displacement
+	 is not carried forward either.  But this is fine because
+	 GINSN_TYPE_OTHER will cause SCFI pass to bail out any which way if
+	 dest reg is interesting.  */
+      index_scale = i.log2_scale_factor;
+      index_reg = ginsn_dw2_regnum (i.index_reg);
+      dst_reg = ginsn_dw2_regnum (i.op[1].regs);
+      ginsn = ginsn_new_other (insn_end_sym, true,
+			       GINSN_SRC_REG, index_reg,
+			       GINSN_SRC_IMM, index_scale,
+			       GINSN_DST_REG, dst_reg);
+      /* FIXME - It seems to make sense to represent a scale factor of 1
+	 correctly here (i.e. not as "other", but rather similar to the
+	 base-without- index case above)?  */
+    }
+  else
+    {
+      /* lea disp(%base,%index,imm) %dst.  */
+      /* TBD_GINSN_INFO_LOSS - Skip adding information about the disp and imm
+	 for index reg.  */
+      base_reg = ginsn_dw2_regnum (i.base_reg);
+      index_reg = ginsn_dw2_regnum (i.index_reg);
+      dst_reg = ginsn_dw2_regnum (i.op[1].regs);
+      /* Generate an GINSN_TYPE_OTHER ginsn.  */
+      ginsn = ginsn_new_other (insn_end_sym, true,
+			       GINSN_SRC_REG, base_reg,
+			       GINSN_SRC_REG, index_reg,
+			       GINSN_DST_REG, dst_reg);
+    }
+
+  ginsn_set_where (ginsn);
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_jump (const symbolS *insn_end_sym, bool cond_p)
+{
+  ginsnS *ginsn = NULL;
+  const symbolS *src_symbol;
+  ginsnS * (*ginsn_func) (const symbolS *sym, bool real_p,
+			  enum ginsn_src_type src_type, unsigned int src_reg,
+			  const symbolS *src_ginsn_sym);
+
+  gas_assert (i.disp_operands == 1);
+
+  ginsn_func = cond_p ? ginsn_new_jump_cond : ginsn_new_jump;
+  if (i.op[0].disps->X_op == O_symbol && !i.op[0].disps->X_add_number)
+    {
+      src_symbol = i.op[0].disps->X_add_symbol;
+      ginsn = ginsn_func (insn_end_sym, true,
+			  GINSN_SRC_SYMBOL, 0, src_symbol);
+
+      ginsn_set_where (ginsn);
+    }
+  else
+    {
+      /* A non-zero addend in jump/JCC target makes control-flow tracking
+	 difficult.  Skip SCFI for now.  */
+      as_bad (_("SCFI: `%s' insn with non-zero addend to sym not supported"),
+	      cond_p ? "JCC" : "jmp");
+      return ginsn;
+    }
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_enter (const symbolS *insn_end_sym)
+{
+  ginsnS *ginsn = NULL;
+  ginsnS *ginsn_next = NULL;
+  ginsnS *ginsn_last = NULL;
+  /* In 64-bit mode, the default stack update size is 8 bytes.  */
+  int stack_opnd_size = 8;
+
+  gas_assert (i.imm_operands == 2);
+
+  /* For non-zero size operands, bail out as untraceable for SCFI.  */
+  if (i.op[0].imms->X_op != O_constant || i.op[0].imms->X_add_symbol != 0
+      || i.op[1].imms->X_op != O_constant || i.op[1].imms->X_add_symbol != 0)
+    {
+      as_bad ("SCFI: enter insn with non-zero operand not supported");
+      return ginsn;
+    }
+
+  /* Check if this is a 16-bit op.  */
+  if (ginsn_opsize_prefix_p ())
+    stack_opnd_size = 2;
+
+  /* If the nesting level is 0, the processor pushes the frame pointer from
+     the BP/EBP/RBP register onto the stack, copies the current stack
+     pointer from the SP/ESP/RSP register into the BP/EBP/RBP register, and
+     loads the SP/ESP/RSP register with the current stack-pointer value
+     minus the value in the size operand.  */
+  ginsn = ginsn_new_sub (insn_end_sym, false,
+			 GINSN_SRC_REG, REG_SP, 0,
+			 GINSN_SRC_IMM, 0, stack_opnd_size,
+			 GINSN_DST_REG, REG_SP, 0);
+  ginsn_set_where (ginsn);
+  ginsn_next = ginsn_new_store (insn_end_sym, false,
+				GINSN_SRC_REG, REG_FP,
+				GINSN_DST_INDIRECT, REG_SP, 0);
+  ginsn_set_where (ginsn_next);
+  gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+  ginsn_last = ginsn_new_mov (insn_end_sym, false,
+			      GINSN_SRC_REG, REG_SP, 0,
+			      GINSN_DST_REG, REG_FP, 0);
+  ginsn_set_where (ginsn_last);
+  gas_assert (!ginsn_link_next (ginsn_next, ginsn_last));
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_leave (const symbolS *insn_end_sym)
+{
+  ginsnS *ginsn = NULL;
+  ginsnS *ginsn_next = NULL;
+  ginsnS *ginsn_last = NULL;
+  /* In 64-bit mode, the default stack update size is 8 bytes.  */
+  int stack_opnd_size = 8;
+
+  /* Check if this is a 16-bit op.  */
+  if (ginsn_opsize_prefix_p ())
+    stack_opnd_size = 2;
+
+  /* The 'leave' instruction copies the contents of the RBP register
+     into the RSP register to release all stack space allocated to the
+     procedure.  */
+  ginsn = ginsn_new_mov (insn_end_sym, false,
+			 GINSN_SRC_REG, REG_FP, 0,
+			 GINSN_DST_REG, REG_SP, 0);
+  ginsn_set_where (ginsn);
+  /* Then it restores the old value of the RBP register from the stack.  */
+  ginsn_next = ginsn_new_load (insn_end_sym, false,
+			       GINSN_SRC_INDIRECT, REG_SP, 0,
+			       GINSN_DST_REG, REG_FP);
+  ginsn_set_where (ginsn_next);
+  gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+  ginsn_last = ginsn_new_add (insn_end_sym, false,
+			      GINSN_SRC_REG, REG_SP, 0,
+			      GINSN_SRC_IMM, 0, stack_opnd_size,
+			      GINSN_DST_REG, REG_SP, 0);
+  ginsn_set_where (ginsn_next);
+  gas_assert (!ginsn_link_next (ginsn_next, ginsn_last));
+
+  return ginsn;
+}
+
+/* Check if an instruction is whitelisted.
+
+   Some instructions may appear with REG_SP or REG_FP as destination, because
+   which they are deemed 'interesting' for SCFI.  Whitelist them here if they
+   do not affect SCFI correctness.  */
+
+static bool
+x86_ginsn_safe_to_skip_p (void)
+{
+  bool skip_p = false;
+  uint16_t opcode = i.tm.base_opcode;
+
+  switch (opcode)
+    {
+    case 0x80:
+    case 0x81:
+    case 0x83:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* cmp imm, reg/rem.  */
+      if (i.tm.extension_opcode == 7)
+	skip_p = true;
+      break;
+
+    case 0x38:
+    case 0x39:
+    case 0x3a:
+    case 0x3b:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* cmp imm/reg/mem, reg/rem.  */
+      skip_p = true;
+      break;
+
+    case 0xf6:
+    case 0xf7:
+    case 0x84:
+    case 0x85:
+      /* test imm/reg/mem, reg/mem.  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      skip_p = true;
+      break;
+
+    default:
+      break;
+    }
+
+  return skip_p;
+}
+
+#define X86_GINSN_UNHANDLED_NONE        0
+#define X86_GINSN_UNHANDLED_DEST_REG    1
+#define X86_GINSN_UNHANDLED_CFG         2
+#define X86_GINSN_UNHANDLED_STACKOP     3
+#define X86_GINSN_UNHANDLED_UNEXPECTED  4
+
+/* Check the input insn for its impact on the correctness of the synthesized
+   CFI.  Returns an error code to the caller.  */
+
+static int
+x86_ginsn_unhandled (void)
+{
+  int err = X86_GINSN_UNHANDLED_NONE;
+  const reg_entry *reg_op;
+  unsigned int dw2_regnum;
+
+  /* Keep an eye out for instructions affecting control flow.  */
+  if (i.tm.opcode_modifier.jump)
+    err = X86_GINSN_UNHANDLED_CFG;
+  /* Also, for any instructions involving an implicit update to the stack
+     pointer.  */
+  else if (i.tm.opcode_modifier.operandconstraint == IMPLICIT_STACK_OP)
+    err = X86_GINSN_UNHANDLED_STACKOP;
+  /* Finally, also check if the missed instructions are affecting REG_SP or
+     REG_FP.  The destination operand is the last at all stages of assembly
+     (due to following AT&T syntax layout in the internal representation).  In
+     case of Intel syntax input, this still remains true as swap_operands ()
+     is done by now.
+     PS: These checks do not involve index / base reg, as indirect memory
+     accesses via REG_SP or REG_FP do not affect SCFI correctness.
+     (Also note these instructions are candidates for other ginsn generation
+     modes in future.  TBD_GINSN_GEN_NOT_SCFI.)  */
+  else if (i.operands && i.reg_operands
+	   && !(i.flags[i.operands - 1] & Operand_Mem))
+    {
+      reg_op = i.op[i.operands - 1].regs;
+      if (reg_op)
+	{
+	  dw2_regnum = ginsn_dw2_regnum (reg_op);
+	  if (dw2_regnum == REG_SP || dw2_regnum == REG_FP)
+	    err = X86_GINSN_UNHANDLED_DEST_REG;
+	}
+      else
+	/* Something unexpected.  Indicate to caller.  */
+	err = X86_GINSN_UNHANDLED_UNEXPECTED;
+    }
+
+  return err;
+}
+
+/* Generate one or more generic GAS instructions, a.k.a, ginsns for the current
+   machine instruction.
+
+   Returns the head of linked list of ginsn(s) added, if success; Returns NULL
+   if failure.
+
+   The input ginsn_gen_mode GMODE determines the set of minimal necessary
+   ginsns necessary for correctness of any passes applicable for that mode.
+   For supporting the GINSN_GEN_SCFI generation mode, following is the list of
+   machine instructions that must be translated into the corresponding ginsns
+   to ensure correctness of SCFI:
+     - All instructions affecting the two registers that could potentially
+       be used as the base register for CFA tracking.  For SCFI, the base
+       register for CFA tracking is limited to REG_SP and REG_FP only for
+       now.
+     - All change of flow instructions: conditional and unconditional branches,
+       call and return from functions.
+     - All instructions that can potentially be a register save / restore
+       operation.
+     - All instructions that perform stack manipulation implicitly: the CALL,
+       RET, PUSH, POP, ENTER, and LEAVE instructions.
+
+   The function currently supports GINSN_GEN_SCFI ginsn generation mode only.
+   To support other generation modes will require work on this target-specific
+   process of creation of ginsns:
+     - Some of such places are tagged with TBD_GINSN_GEN_NOT_SCFI to serve as
+       possible starting points.
+     - Also note that ginsn representation may need enhancements.  Specifically,
+       note some TBD_GINSN_INFO_LOSS and TBD_GINSN_REPRESENTATION_LIMIT markers.
+   */
+
+static ginsnS *
+x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
+{
+  int err = 0;
+  uint16_t opcode;
+  unsigned int dw2_regnum;
+  const reg_entry *mem_reg;
+  ginsnS *ginsn = NULL;
+  ginsnS *ginsn_next = NULL;
+  /* In 64-bit mode, the default stack update size is 8 bytes.  */
+  int stack_opnd_size = 8;
+
+  /* Currently supports generation of selected ginsns, sufficient for
+     the use-case of SCFI only.  */
+  if (gmode != GINSN_GEN_SCFI)
+    return ginsn;
+
+  opcode = i.tm.base_opcode;
+
+  /* Until it is clear how to handle APX NDD and other new opcodes, disallow
+     them from SCFI.  */
+  if (is_apx_rex2_encoding ()
+      || (i.tm.opcode_modifier.evex && is_apx_evex_encoding ()))
+    {
+      as_bad (_("SCFI: unsupported APX op %#x may cause incorrect CFI"),
+	      opcode);
+      return ginsn;
+    }
+
+  switch (opcode)
+    {
+    case 0x1:  /* add reg, reg/mem.  */
+    case 0x29: /* sub reg, reg/mem.  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      ginsn = x86_ginsn_addsub_reg_mem (insn_end_sym);
+      break;
+
+    case 0x3:  /* add reg/mem, reg.  */
+    case 0x2b: /* sub reg/mem, reg.  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      ginsn = x86_ginsn_addsub_mem_reg (insn_end_sym);
+      break;
+
+    case 0xa0: /* push fs.  */
+    case 0xa8: /* push gs.  */
+      /* push fs / push gs have opcode_space == SPACE_0F.  */
+      if (i.tm.opcode_space != SPACE_0F)
+	break;
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, stack_opnd_size,
+			     GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn);
+      ginsn_next = ginsn_new_store (insn_end_sym, false,
+				    GINSN_SRC_REG, dw2_regnum,
+				    GINSN_DST_INDIRECT, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    case 0xa1: /* pop fs.  */
+    case 0xa9: /* pop gs.  */
+      /* pop fs / pop gs have opcode_space == SPACE_0F.  */
+      if (i.tm.opcode_space != SPACE_0F)
+	break;
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      ginsn = ginsn_new_load (insn_end_sym, false,
+			      GINSN_SRC_INDIRECT, REG_SP, 0,
+			      GINSN_DST_REG, dw2_regnum);
+      ginsn_set_where (ginsn);
+      ginsn_next = ginsn_new_add (insn_end_sym, false,
+				  GINSN_SRC_REG, REG_SP, 0,
+				  GINSN_SRC_IMM, 0, stack_opnd_size,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    case 0x50 ... 0x57:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* push reg.  */
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, stack_opnd_size,
+			     GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn);
+      ginsn_next = ginsn_new_store (insn_end_sym, false,
+				    GINSN_SRC_REG, dw2_regnum,
+				    GINSN_DST_INDIRECT, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    case 0x58 ... 0x5f:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* pop reg.  */
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      ginsn = ginsn_new_load (insn_end_sym, false,
+			      GINSN_SRC_INDIRECT, REG_SP, 0,
+			      GINSN_DST_REG, dw2_regnum);
+      ginsn_set_where (ginsn);
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      ginsn_next = ginsn_new_add (insn_end_sym, false,
+				  GINSN_SRC_REG, REG_SP, 0,
+				  GINSN_SRC_IMM, 0, stack_opnd_size,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    case 0x6a: /* push imm8.  */
+    case 0x68: /* push imm16/imm32.  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      /* Skip getting the value of imm from machine instruction
+	 because this is not important for SCFI.  */
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, stack_opnd_size,
+			     GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn);
+      ginsn_next = ginsn_new_store (insn_end_sym, false,
+				    GINSN_SRC_IMM, 0,
+				    GINSN_DST_INDIRECT, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    /* PS: Opcodes 0x80 ... 0x8f with opcode_space SPACE_0F are present
+       only after relaxation.  They do not need to be handled for ginsn
+       creation.  */
+    case 0x70 ... 0x7f:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      ginsn = x86_ginsn_jump (insn_end_sym, true);
+      break;
+
+    case 0x80:
+    case 0x81:
+    case 0x83:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      ginsn = x86_ginsn_alu_imm (insn_end_sym);
+      break;
+
+    case 0x8a: /* mov r/m8, r8.  */
+    case 0x8b: /* mov r/m(16/32/64), r(16/32/64).  */
+    case 0x88: /* mov r8, r/m8.  */
+    case 0x89: /* mov r(16/32/64), r/m(16/32/64).  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      ginsn = x86_ginsn_move (insn_end_sym);
+      break;
+
+    case 0x8d:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* lea disp(%base,%index,imm) %dst.  */
+      ginsn = x86_ginsn_lea (insn_end_sym);
+      break;
+
+    case 0x8f:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* pop to reg/mem.  */
+      if (i.mem_operands)
+	{
+	  mem_reg = (i.base_reg) ? i.base_reg : i.index_reg;
+	  /* Use dummy register if no base or index.  Unlike other opcodes,
+	     ginsns must be generated as this affect stack pointer.  */
+	  dw2_regnum = (mem_reg
+			? ginsn_dw2_regnum (mem_reg)
+			: GINSN_DW2_REGNUM_RSI_DUMMY);
+	}
+      else
+	dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      ginsn = ginsn_new_load (insn_end_sym, false,
+			      GINSN_SRC_INDIRECT, REG_SP, 0,
+			      GINSN_DST_INDIRECT, dw2_regnum);
+      ginsn_set_where (ginsn);
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      ginsn_next = ginsn_new_add (insn_end_sym, false,
+				  GINSN_SRC_REG, REG_SP, 0,
+				  GINSN_SRC_IMM, 0, stack_opnd_size,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    case 0x9c:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* pushf / pushfq.  */
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, stack_opnd_size,
+			     GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn);
+      /* FIXME - hardcode the actual DWARF reg number value.  As for SCFI
+	 correctness, although this behaves simply a placeholder value; its
+	 just clearer if the value is correct.  */
+      dw2_regnum = GINSN_DW2_REGNUM_EFLAGS;
+      ginsn_next = ginsn_new_store (insn_end_sym, false,
+				    GINSN_SRC_REG, dw2_regnum,
+				    GINSN_DST_INDIRECT, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    case 0x9d:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* popf / popfq.  */
+      /* Check if operation size is 16-bit.  */
+      if (ginsn_opsize_prefix_p ())
+	stack_opnd_size = 2;
+      /* FIXME - hardcode the actual DWARF reg number value.  As for SCFI
+	 correctness, although this behaves simply a placeholder value; its
+	 just clearer if the value is correct.  */
+      dw2_regnum = GINSN_DW2_REGNUM_EFLAGS;
+      ginsn = ginsn_new_load (insn_end_sym, false,
+			      GINSN_SRC_INDIRECT, REG_SP, 0,
+			      GINSN_DST_REG, dw2_regnum);
+      ginsn_set_where (ginsn);
+      ginsn_next = ginsn_new_add (insn_end_sym, false,
+				  GINSN_SRC_REG, REG_SP, 0,
+				  GINSN_SRC_IMM, 0, stack_opnd_size,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+
+    case 0xff:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* push from reg/mem.  */
+      if (i.tm.extension_opcode == 6)
+	{
+	  /* Check if operation size is 16-bit.  */
+	  if (ginsn_opsize_prefix_p ())
+	    stack_opnd_size = 2;
+	  ginsn = ginsn_new_sub (insn_end_sym, false,
+				 GINSN_SRC_REG, REG_SP, 0,
+				 GINSN_SRC_IMM, 0, stack_opnd_size,
+				 GINSN_DST_REG, REG_SP, 0);
+	  ginsn_set_where (ginsn);
+	  if (i.mem_operands)
+	    {
+	      mem_reg = (i.base_reg) ? i.base_reg : i.index_reg;
+	      /* Use dummy register if no base or index.  Unlike other opcodes,
+		 ginsns must be generated as this affect stack pointer.  */
+	      dw2_regnum = (mem_reg
+			    ? ginsn_dw2_regnum (mem_reg)
+			    : GINSN_DW2_REGNUM_RSI_DUMMY);
+	    }
+	  else
+	    dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+	  ginsn_next = ginsn_new_store (insn_end_sym, false,
+					GINSN_SRC_INDIRECT, dw2_regnum,
+					GINSN_DST_INDIRECT, REG_SP, 0);
+	  ginsn_set_where (ginsn_next);
+	  gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+	}
+      else if (i.tm.extension_opcode == 4)
+	{
+	  /* jmp r/m.  E.g., notrack jmp *%rax.  */
+	  if (i.reg_operands)
+	    {
+	      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+	      ginsn = ginsn_new_jump (insn_end_sym, true,
+				      GINSN_SRC_REG, dw2_regnum, NULL);
+	      ginsn_set_where (ginsn);
+	    }
+	  else if (i.mem_operands && i.index_reg)
+	    {
+	      /* jmp    *0x0(,%rax,8).  */
+	      dw2_regnum = ginsn_dw2_regnum (i.index_reg);
+	      ginsn = ginsn_new_jump (insn_end_sym, true,
+				      GINSN_SRC_REG, dw2_regnum, NULL);
+	      ginsn_set_where (ginsn);
+	    }
+	  else if (i.mem_operands && i.base_reg)
+	    {
+	      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
+	      ginsn = ginsn_new_jump (insn_end_sym, true,
+				      GINSN_SRC_REG, dw2_regnum, NULL);
+	      ginsn_set_where (ginsn);
+	    }
+	}
+      else if (i.tm.extension_opcode == 2)
+	{
+	  /* 0xFF /2 (call).  */
+	  if (i.reg_operands)
+	    {
+	      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+	      ginsn = ginsn_new_call (insn_end_sym, true,
+				      GINSN_SRC_REG, dw2_regnum, NULL);
+	      ginsn_set_where (ginsn);
+	    }
+	  else if (i.mem_operands && i.base_reg)
+	    {
+	      dw2_regnum = ginsn_dw2_regnum (i.base_reg);
+	      ginsn = ginsn_new_call (insn_end_sym, true,
+				      GINSN_SRC_REG, dw2_regnum, NULL);
+	      ginsn_set_where (ginsn);
+	    }
+	}
+      break;
+
+    case 0xc2: /* ret imm16.  */
+    case 0xc3: /* ret.  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* Near ret.  */
+      ginsn = ginsn_new_return (insn_end_sym, true);
+      ginsn_set_where (ginsn);
+      break;
+
+    case 0xc8:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* enter.  */
+      ginsn = x86_ginsn_enter (insn_end_sym);
+      break;
+
+    case 0xc9:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* leave.  */
+      ginsn = x86_ginsn_leave (insn_end_sym);
+      break;
+
+    case 0xe0 ... 0xe2: /* loop / loope / loopne.  */
+    case 0xe3:          /* jecxz / jrcxz.  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      ginsn = x86_ginsn_jump (insn_end_sym, true);
+      ginsn_set_where (ginsn);
+      break;
+
+    case 0xe8:
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* PS: SCFI machinery does not care about which func is being
+	 called.  OK to skip that info.  */
+      ginsn = ginsn_new_call (insn_end_sym, true,
+			      GINSN_SRC_SYMBOL, 0, NULL);
+      ginsn_set_where (ginsn);
+      break;
+
+    /* PS: opcode 0xe9 appears only after relaxation.  Skip here.  */
+    case 0xeb:
+      /* If opcode_space != SPACE_BASE, this is not a jmp insn.  Skip it
+	 for GINSN_GEN_SCFI.  */
+      if (i.tm.opcode_space != SPACE_BASE)
+	break;
+      /* Unconditional jmp.  */
+      ginsn = x86_ginsn_jump (insn_end_sym, false);
+      ginsn_set_where (ginsn);
+      break;
+
+    default:
+      /* TBD_GINSN_GEN_NOT_SCFI: Skip all other opcodes uninteresting for
+	 GINSN_GEN_SCFI mode.  */
+      break;
+    }
+
+  if (!ginsn && !x86_ginsn_safe_to_skip_p ())
+    {
+      /* For all unhandled insns that are not whitelisted, check that they do
+	 not impact SCFI correctness.  */
+      err = x86_ginsn_unhandled ();
+      switch (err)
+	{
+	case X86_GINSN_UNHANDLED_NONE:
+	  break;
+	case X86_GINSN_UNHANDLED_DEST_REG:
+	  /* Not all writes to REG_FP are harmful in context of SCFI.  Simply
+	     generate a GINSN_TYPE_OTHER with destination set to the
+	     appropriate register.  The SCFI machinery will bail out if this
+	     ginsn affects SCFI correctness.  */
+	  dw2_regnum = ginsn_dw2_regnum (i.op[i.operands - 1].regs);
+	  ginsn = ginsn_new_other (insn_end_sym, true,
+				   GINSN_SRC_IMM, 0,
+				   GINSN_SRC_IMM, 0,
+				   GINSN_DST_REG, dw2_regnum);
+	  ginsn_set_where (ginsn);
+	  break;
+	case X86_GINSN_UNHANDLED_CFG:
+	case X86_GINSN_UNHANDLED_STACKOP:
+	  as_bad (_("SCFI: unhandled op %#x may cause incorrect CFI"), opcode);
+	  break;
+	case X86_GINSN_UNHANDLED_UNEXPECTED:
+	  as_bad (_("SCFI: unexpected op %#x may cause incorrect CFI"),
+		  opcode);
+	  break;
+	default:
+	  abort ();
+	  break;
+	}
+    }
+
+  return ginsn;
+}
+
+#endif
+
 /* This is the guts of the machine-dependent assembler.  LINE points to a
    machine dependent instruction.  This function is supposed to emit
    the frags/bytes it assembles to.  */
@@ -5870,6 +6957,17 @@ md_assemble (char *line)
   /* We are ready to output the insn.  */
   output_insn (last_insn);
 
+#if defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF)
+  /* PS: SCFI is enabled only for System V AMD64 ABI.  The ABI check has been
+     performed in i386_target_format.  */
+  if (IS_ELF && flag_synth_cfi)
+    {
+      ginsnS *ginsn;
+      ginsn = x86_ginsn_new (symbol_temp_new_now (), frch_ginsn_gen_mode ());
+      frch_ginsn_data_append (ginsn);
+    }
+#endif
+
   insert_lfence_after ();
 
   if (i.tm.opcode_modifier.isprefix)
@@ -12144,6 +13242,13 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
   last_insn->name = ".insn directive";
   last_insn->file = as_where (&last_insn->line);
 
+#if defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF)
+  /* PS: SCFI is enabled only for System V AMD64 ABI.  The ABI check has been
+     performed in i386_target_format.  */
+  if (IS_ELF && flag_synth_cfi)
+    as_bad (_("SCFI: hand-crafting instructions not supported"));
+#endif
+
  done:
   *saved_ilp = saved_char;
   input_line_pointer = line;
@@ -15788,6 +16893,11 @@ i386_target_format (void)
   else
     as_fatal (_("unknown architecture"));
 
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+  if (IS_ELF && flag_synth_cfi && x86_elf_abi != X86_64_ABI)
+    as_fatal (_("SCFI is not supported for this ABI"));
+#endif
+
   if (cpu_flags_all_zero (&cpu_arch_isa_flags))
     cpu_arch_isa_flags = cpu_arch[flag_code == CODE_64BIT].enable;
 
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
index 2499aa692f5..b93799a3b48 100644
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -415,6 +415,27 @@ extern bfd_vma x86_64_section_letter (int, const char **);
 extern void x86_cleanup (void);
 #define md_cleanup() x86_cleanup ()
 
+#define TARGET_USE_GINSN 1
+/* Allow GAS to synthesize DWARF CFI for hand-written asm.
+   PS: TARGET_USE_CFIPOP is a pre-condition.  */
+#define TARGET_USE_SCFI 1
+/* Identify the maximum DWARF register number of all the registers being
+   tracked for SCFI.  This is the last DWARF register number of the set
+   of SP, BP, and all callee-saved registers.  For AMD64, this means
+   R15 (15).  Use SCFI_CALLEE_SAVED_REG_P to identify which registers
+   are callee-saved from this set.  */
+#define SCFI_MAX_REG_ID 15
+/* Identify the DWARF register number of the frame-pointer register.  */
+#define REG_FP 6
+/* Identify the DWARF register number of the stack-pointer register.  */
+#define REG_SP 7
+/* Some ABIs, like AMD64, use stack for call instruction.  For such an ABI,
+   identify the initial (CFA) offset from RSP at the entry of function.  */
+#define SCFI_INIT_CFA_OFFSET 8
+
+#define SCFI_CALLEE_SAVED_REG_P(dw2reg)  x86_scfi_callee_saved_p (dw2reg)
+extern bool x86_scfi_callee_saved_p (uint32_t dw2reg_num);
+
 /* Whether SFrame stack trace info is supported.  */
 extern bool x86_support_sframe_p (void);
 #define support_sframe_p x86_support_sframe_p
diff --git a/gas/ginsn.c b/gas/ginsn.c
new file mode 100644
index 00000000000..5f6a67ce4f2
--- /dev/null
+++ b/gas/ginsn.c
@@ -0,0 +1,1259 @@
+/* ginsn.h - GAS instruction representation.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#include "as.h"
+#include "subsegs.h"
+#include "ginsn.h"
+#include "scfi.h"
+
+#ifdef TARGET_USE_GINSN
+
+static const char *const ginsn_type_names[] =
+{
+#define _GINSN_TYPE_ITEM(NAME, STR) STR,
+  _GINSN_TYPES
+#undef _GINSN_TYPE_ITEM
+};
+
+static ginsnS *
+ginsn_alloc (void)
+{
+  ginsnS *ginsn = XCNEW (ginsnS);
+  return ginsn;
+}
+
+static ginsnS *
+ginsn_init (enum ginsn_type type, const symbolS *sym, bool real_p)
+{
+  ginsnS *ginsn = ginsn_alloc ();
+  ginsn->type = type;
+  ginsn->sym = sym;
+  if (real_p)
+    ginsn->flags |= GINSN_F_INSN_REAL;
+  return ginsn;
+}
+
+static void
+ginsn_cleanup (ginsnS **ginsnp)
+{
+  ginsnS *ginsn;
+
+  if (!ginsnp || !*ginsnp)
+    return;
+
+  ginsn = *ginsnp;
+  if (ginsn->scfi_ops)
+    {
+      scfi_ops_cleanup (ginsn->scfi_ops);
+      ginsn->scfi_ops = NULL;
+    }
+
+  free (ginsn);
+  *ginsnp = NULL;
+}
+
+static void
+ginsn_set_src (struct ginsn_src *src, enum ginsn_src_type type, unsigned int reg,
+	       offsetT immdisp)
+{
+  if (!src)
+    return;
+
+  src->type = type;
+  /* Even when the use-case is SCFI, the value of reg may be > SCFI_MAX_REG_ID.
+     E.g., in AMD64, push fs etc.  */
+  src->reg = reg;
+  src->immdisp = immdisp;
+}
+
+static void
+ginsn_set_dst (struct ginsn_dst *dst, enum ginsn_dst_type type, unsigned int reg,
+	       offsetT disp)
+{
+  if (!dst)
+    return;
+
+  dst->type = type;
+  dst->reg = reg;
+
+  if (type == GINSN_DST_INDIRECT)
+    dst->disp = disp;
+}
+
+static void
+ginsn_set_file_line (ginsnS *ginsn, const char *file, unsigned int line)
+{
+  if (!ginsn)
+    return;
+
+  ginsn->file = file;
+  ginsn->line = line;
+}
+
+struct ginsn_src *
+ginsn_get_src1 (ginsnS *ginsn)
+{
+  return &ginsn->src[0];
+}
+
+struct ginsn_src *
+ginsn_get_src2 (ginsnS *ginsn)
+{
+  return &ginsn->src[1];
+}
+
+struct ginsn_dst *
+ginsn_get_dst (ginsnS *ginsn)
+{
+  return &ginsn->dst;
+}
+
+unsigned int
+ginsn_get_src_reg (struct ginsn_src *src)
+{
+  return src->reg;
+}
+
+enum ginsn_src_type
+ginsn_get_src_type (struct ginsn_src *src)
+{
+  return src->type;
+}
+
+offsetT
+ginsn_get_src_disp (struct ginsn_src *src)
+{
+  return src->immdisp;
+}
+
+offsetT
+ginsn_get_src_imm (struct ginsn_src *src)
+{
+  return src->immdisp;
+}
+
+unsigned int
+ginsn_get_dst_reg (struct ginsn_dst *dst)
+{
+  return dst->reg;
+}
+
+enum ginsn_dst_type
+ginsn_get_dst_type (struct ginsn_dst *dst)
+{
+  return dst->type;
+}
+
+offsetT
+ginsn_get_dst_disp (struct ginsn_dst *dst)
+{
+  return dst->disp;
+}
+
+void
+label_ginsn_map_insert (const symbolS *label, ginsnS *ginsn)
+{
+  const char *name = S_GET_NAME (label);
+  str_hash_insert (frchain_now->frch_ginsn_data->label_ginsn_map,
+		   name, ginsn, 0 /* noreplace.  */);
+}
+
+ginsnS *
+label_ginsn_map_find (const symbolS *label)
+{
+  const char *name = S_GET_NAME (label);
+  ginsnS *ginsn
+    = (ginsnS *) str_hash_find (frchain_now->frch_ginsn_data->label_ginsn_map,
+				name);
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_phantom (const symbolS *sym)
+{
+  ginsnS *ginsn = ginsn_alloc ();
+  ginsn->type = GINSN_TYPE_PHANTOM;
+  ginsn->sym = sym;
+  /* By default, GINSN_F_INSN_REAL is not set in ginsn->flags.  */
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_symbol (const symbolS *sym, bool func_begin_p)
+{
+  ginsnS *ginsn = ginsn_alloc ();
+  ginsn->type = GINSN_TYPE_SYMBOL;
+  ginsn->sym = sym;
+  if (func_begin_p)
+    ginsn->flags |= GINSN_F_FUNC_MARKER;
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_symbol_func_begin (const symbolS *sym)
+{
+  return ginsn_new_symbol (sym, true);
+}
+
+ginsnS *
+ginsn_new_symbol_func_end (const symbolS *sym)
+{
+  return ginsn_new_symbol (sym, false);
+}
+
+ginsnS *
+ginsn_new_symbol_user_label (const symbolS *sym)
+{
+  ginsnS *ginsn = ginsn_new_symbol (sym, false);
+  ginsn->flags |= GINSN_F_USER_LABEL;
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_add (const symbolS *sym, bool real_p,
+	       enum ginsn_src_type src1_type, unsigned int src1_reg, offsetT src1_disp,
+	       enum ginsn_src_type src2_type, unsigned int src2_reg, offsetT src2_disp,
+	       enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_ADD, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src1_type, src1_reg, src1_disp);
+  ginsn_set_src (&ginsn->src[1], src2_type, src2_reg, src2_disp);
+  /* dst info.  */
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_and (const symbolS *sym, bool real_p,
+	       enum ginsn_src_type src1_type, unsigned int src1_reg, offsetT src1_disp,
+	       enum ginsn_src_type src2_type, unsigned int src2_reg, offsetT src2_disp,
+	       enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_AND, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src1_type, src1_reg, src1_disp);
+  ginsn_set_src (&ginsn->src[1], src2_type, src2_reg, src2_disp);
+  /* dst info.  */
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_call (const symbolS *sym, bool real_p,
+		enum ginsn_src_type src_type, unsigned int src_reg,
+		const symbolS *src_text_sym)
+
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_CALL, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src_type, src_reg, 0);
+
+  if (src_type == GINSN_SRC_SYMBOL)
+    ginsn->src[0].sym = src_text_sym;
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_jump (const symbolS *sym, bool real_p,
+		enum ginsn_src_type src_type, unsigned int src_reg,
+		const symbolS *src_ginsn_sym)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_JUMP, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src_type, src_reg, 0);
+
+  if (src_type == GINSN_SRC_SYMBOL)
+    ginsn->src[0].sym = src_ginsn_sym;
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_jump_cond (const symbolS *sym, bool real_p,
+		     enum ginsn_src_type src_type, unsigned int src_reg,
+		     const symbolS *src_ginsn_sym)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_JUMP_COND, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src_type, src_reg, 0);
+
+  if (src_type == GINSN_SRC_SYMBOL)
+    ginsn->src[0].sym = src_ginsn_sym;
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_mov (const symbolS *sym, bool real_p,
+	       enum ginsn_src_type src_type, unsigned int src_reg, offsetT src_disp,
+	       enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_MOV, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp);
+  /* dst info.  */
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_store (const symbolS *sym, bool real_p,
+		 enum ginsn_src_type src_type, unsigned int src_reg,
+		 enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_STORE, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src_type, src_reg, 0);
+  /* dst info.  */
+  gas_assert (dst_type == GINSN_DST_INDIRECT);
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_load (const symbolS *sym, bool real_p,
+		enum ginsn_src_type src_type, unsigned int src_reg, offsetT src_disp,
+		enum ginsn_dst_type dst_type, unsigned int dst_reg)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_LOAD, sym, real_p);
+  /* src info.  */
+  gas_assert (src_type == GINSN_SRC_INDIRECT);
+  ginsn_set_src (&ginsn->src[0], src_type, src_reg, src_disp);
+  /* dst info.  */
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, 0);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_sub (const symbolS *sym, bool real_p,
+	       enum ginsn_src_type src1_type, unsigned int src1_reg, offsetT src1_disp,
+	       enum ginsn_src_type src2_type, unsigned int src2_reg, offsetT src2_disp,
+	       enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_SUB, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src1_type, src1_reg, src1_disp);
+  ginsn_set_src (&ginsn->src[1], src2_type, src2_reg, src2_disp);
+  /* dst info.  */
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
+
+  return ginsn;
+}
+
+/* PS: Note this API does not identify the displacement values of
+   src1/src2/dst.  At this time, it is unnecessary for correctness to support
+   the additional argument.  */
+
+ginsnS *
+ginsn_new_other (const symbolS *sym, bool real_p,
+		 enum ginsn_src_type src1_type, unsigned int src1_val,
+		 enum ginsn_src_type src2_type, unsigned int src2_val,
+		 enum ginsn_dst_type dst_type, unsigned int dst_reg)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_OTHER, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src1_type, src1_val, src1_val);
+  /* GINSN_SRC_INDIRECT src2_type is not expected.  */
+  gas_assert (src2_type != GINSN_SRC_INDIRECT);
+  ginsn_set_src (&ginsn->src[1], src2_type, src2_val, src2_val);
+  /* dst info.  */
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, 0);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_return (const symbolS *sym, bool real_p)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_RETURN, sym, real_p);
+  return ginsn;
+}
+
+void
+ginsn_set_where (ginsnS *ginsn)
+{
+  const char *file;
+  unsigned int line;
+  file = as_where (&line);
+  ginsn_set_file_line (ginsn, file, line);
+}
+
+int
+ginsn_link_next (ginsnS *ginsn, ginsnS *next)
+{
+  int ret = 0;
+
+  /* Avoid data corruption by limiting the scope of the API.  */
+  if (!ginsn || ginsn->next)
+    return 1;
+
+  ginsn->next = next;
+
+  return ret;
+}
+
+bool
+ginsn_track_reg_p (unsigned int dw2reg, enum ginsn_gen_mode gmode)
+{
+  bool track_p = false;
+
+  if (gmode == GINSN_GEN_SCFI && dw2reg <= SCFI_MAX_REG_ID)
+    {
+      /* FIXME - rename this to tc_ ? */
+      track_p |= SCFI_CALLEE_SAVED_REG_P (dw2reg);
+      track_p |= (dw2reg == REG_FP);
+      track_p |= (dw2reg == REG_SP);
+    }
+
+  return track_p;
+}
+
+static bool
+ginsn_indirect_jump_p (ginsnS *ginsn)
+{
+  bool ret_p = false;
+  if (!ginsn)
+    return ret_p;
+
+  ret_p = (ginsn->type == GINSN_TYPE_JUMP
+	   && ginsn->src[0].type == GINSN_SRC_REG);
+  return ret_p;
+}
+
+static bool
+ginsn_direct_local_jump_p (ginsnS *ginsn)
+{
+  bool ret_p = false;
+  if (!ginsn)
+    return ret_p;
+
+  ret_p |= (ginsn->type == GINSN_TYPE_JUMP
+	    && ginsn->src[0].type == GINSN_SRC_SYMBOL);
+  return ret_p;
+}
+
+static char *
+ginsn_src_print (struct ginsn_src *src)
+{
+  size_t len = 40;
+  char *src_str = XNEWVEC (char, len);
+
+  memset (src_str, 0, len);
+
+  switch (src->type)
+    {
+    case GINSN_SRC_REG:
+      snprintf (src_str, len, "%%r%d, ", ginsn_get_src_reg (src));
+      break;
+    case GINSN_SRC_IMM:
+      snprintf (src_str, len, "%lld, ",
+		(long long int) ginsn_get_src_imm (src));
+      break;
+    case GINSN_SRC_INDIRECT:
+      snprintf (src_str, len, "[%%r%d+%lld], ", ginsn_get_src_reg (src),
+		(long long int) ginsn_get_src_disp (src));
+      break;
+    default:
+      break;
+    }
+
+  return src_str;
+}
+
+static char*
+ginsn_dst_print (struct ginsn_dst *dst)
+{
+  size_t len = GINSN_LISTING_OPND_LEN;
+  char *dst_str = XNEWVEC (char, len);
+
+  memset (dst_str, 0, len);
+
+  if (dst->type == GINSN_DST_REG)
+    {
+      char *buf = XNEWVEC (char, 32);
+      sprintf (buf, "%%r%d", ginsn_get_dst_reg (dst));
+      strcat (dst_str, buf);
+    }
+  else if (dst->type == GINSN_DST_INDIRECT)
+    {
+      char *buf = XNEWVEC (char, 32);
+      sprintf (buf, "[%%r%d+%lld]", ginsn_get_dst_reg (dst),
+		 (long long int) ginsn_get_dst_disp (dst));
+      strcat (dst_str, buf);
+    }
+
+  gas_assert (strlen (dst_str) < GINSN_LISTING_OPND_LEN);
+
+  return dst_str;
+}
+
+static const char*
+ginsn_type_func_marker_print (ginsnS *ginsn)
+{
+  int id = 0;
+  static const char * const ginsn_sym_strs[] =
+    { "", "FUNC_BEGIN", "FUNC_END" };
+
+  if (GINSN_F_FUNC_BEGIN_P (ginsn))
+    id = 1;
+  else if (GINSN_F_FUNC_END_P (ginsn))
+    id = 2;
+
+  return ginsn_sym_strs[id];
+}
+
+static char*
+ginsn_print (ginsnS *ginsn)
+{
+  struct ginsn_src *src;
+  struct ginsn_dst *dst;
+  int str_size = 0;
+  size_t len = GINSN_LISTING_LEN;
+  char *ginsn_str = XNEWVEC (char, len);
+
+  memset (ginsn_str, 0, len);
+
+  str_size = snprintf (ginsn_str, GINSN_LISTING_LEN, "ginsn: %s",
+		       ginsn_type_names[ginsn->type]);
+  gas_assert (str_size >= 0 && str_size < GINSN_LISTING_LEN);
+
+  /* For some ginsn types, no further information is printed for now.  */
+  if (ginsn->type == GINSN_TYPE_CALL
+      || ginsn->type == GINSN_TYPE_RETURN)
+    goto end;
+  else if (ginsn->type == GINSN_TYPE_SYMBOL)
+    {
+      if (GINSN_F_USER_LABEL_P (ginsn))
+	str_size += snprintf (ginsn_str + str_size,
+			      GINSN_LISTING_LEN - str_size,
+			      " %s", S_GET_NAME (ginsn->sym));
+      else
+	str_size += snprintf (ginsn_str + str_size,
+			      GINSN_LISTING_LEN - str_size,
+			      " %s", ginsn_type_func_marker_print (ginsn));
+      goto end;
+    }
+
+  /* src 1.  */
+  src = ginsn_get_src1 (ginsn);
+  str_size += snprintf (ginsn_str + str_size, GINSN_LISTING_LEN - str_size,
+			" %s", ginsn_src_print (src));
+  gas_assert (str_size >= 0 && str_size < GINSN_LISTING_LEN);
+
+  /* src 2.  */
+  src = ginsn_get_src2 (ginsn);
+  str_size += snprintf (ginsn_str + str_size, GINSN_LISTING_LEN - str_size,
+			"%s", ginsn_src_print (src));
+  gas_assert (str_size >= 0 && str_size < GINSN_LISTING_LEN);
+
+  /* dst.  */
+  dst = ginsn_get_dst (ginsn);
+  str_size += snprintf (ginsn_str + str_size, GINSN_LISTING_LEN - str_size,
+			"%s", ginsn_dst_print (dst));
+
+end:
+  gas_assert (str_size >= 0 && str_size < GINSN_LISTING_LEN);
+  return ginsn_str;
+}
+
+static void
+gbb_cleanup (gbbS **bbp)
+{
+  gbbS *bb = NULL;
+
+  if (!bbp && !*bbp)
+    return;
+
+  bb = *bbp;
+
+  if (bb->entry_state)
+    {
+      free (bb->entry_state);
+      bb->entry_state = NULL;
+    }
+  if (bb->exit_state)
+    {
+      free (bb->exit_state);
+      bb->exit_state = NULL;
+    }
+  free (bb);
+  *bbp = NULL;
+}
+
+static void
+bb_add_edge (gbbS* from_bb, gbbS *to_bb)
+{
+  gedgeS *tmpedge = NULL;
+  gedgeS *gedge;
+  bool exists = false;
+
+  if (!from_bb || !to_bb)
+    return;
+
+  /* Create a new edge object.  */
+  gedge = XCNEW (gedgeS);
+  gedge->dst_bb = to_bb;
+  gedge->next = NULL;
+  gedge->visited = false;
+
+  /* Add it in.  */
+  if (from_bb->out_gedges == NULL)
+    {
+      from_bb->out_gedges = gedge;
+      from_bb->num_out_gedges++;
+    }
+  else
+    {
+      /* Get the tail of the list.  */
+      tmpedge = from_bb->out_gedges;
+      while (tmpedge)
+	{
+	  /* Do not add duplicate edges.  Duplicated edges will cause unwanted
+	     failures in the forward and backward passes for SCFI.  */
+	  if (tmpedge->dst_bb == to_bb)
+	    {
+	      exists = true;
+	      break;
+	    }
+	  if (tmpedge->next)
+	    tmpedge = tmpedge->next;
+	  else
+	    break;
+	}
+
+      if (!exists)
+	{
+	  tmpedge->next = gedge;
+	  from_bb->num_out_gedges++;
+	}
+      else
+	free (gedge);
+    }
+}
+
+static void
+cfg_add_bb (gcfgS *gcfg, gbbS *gbb)
+{
+  gbbS *last_bb = NULL;
+
+  if (!gcfg->root_bb)
+    gcfg->root_bb = gbb;
+  else
+    {
+      last_bb = gcfg->root_bb;
+      while (last_bb->next)
+	last_bb = last_bb->next;
+
+      last_bb->next = gbb;
+    }
+  gcfg->num_gbbs++;
+
+  gbb->id = gcfg->num_gbbs;
+}
+
+static gbbS *
+add_bb_at_ginsn (const symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb,
+		 int *errp);
+
+static gbbS *
+find_bb (gcfgS *gcfg, ginsnS *ginsn)
+{
+  gbbS *found_bb = NULL;
+  gbbS *gbb = NULL;
+
+  if (!ginsn)
+    return found_bb;
+
+  if (ginsn->visited)
+    {
+      cfg_for_each_bb (gcfg, gbb)
+	{
+	  if (gbb->first_ginsn == ginsn)
+	    {
+	      found_bb = gbb;
+	      break;
+	    }
+	}
+      /* Must be found if ginsn is visited.  */
+      gas_assert (found_bb);
+    }
+
+  return found_bb;
+}
+
+static gbbS *
+find_or_make_bb (const symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb,
+		 int *errp)
+{
+  gbbS *found_bb = NULL;
+
+  found_bb = find_bb (gcfg, ginsn);
+  if (found_bb)
+    return found_bb;
+
+  return add_bb_at_ginsn (func, gcfg, ginsn, prev_bb, errp);
+}
+
+/* Add the basic block starting at GINSN to the given GCFG.
+   Also adds an edge from the PREV_BB to the newly added basic block.
+
+   This is a recursive function which returns the root of the added
+   basic blocks.  */
+
+static gbbS *
+add_bb_at_ginsn (const symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb,
+		 int *errp)
+{
+  gbbS *current_bb = NULL;
+  ginsnS *target_ginsn = NULL;
+  const symbolS *taken_label;
+
+  while (ginsn)
+    {
+      /* Skip these as they may be right after a GINSN_TYPE_RETURN.
+	 For GINSN_TYPE_RETURN, we have already considered that as
+	 end of bb, and a logical exit from function.  */
+      if (GINSN_F_FUNC_END_P (ginsn))
+	{
+	  ginsn = ginsn->next;
+	  continue;
+	}
+
+      if (ginsn->visited)
+	{
+	  /* If the ginsn has been visited earlier, the bb must exist by now
+	     in the cfg.  */
+	  prev_bb = current_bb;
+	  current_bb = find_bb (gcfg, ginsn);
+	  gas_assert (current_bb);
+	  /* Add edge from the prev_bb.  */
+	  if (prev_bb)
+	    bb_add_edge (prev_bb, current_bb);
+	  break;
+	}
+      else if (current_bb && GINSN_F_USER_LABEL_P (ginsn))
+	{
+	  /* Create new bb starting at this label ginsn.  */
+	  prev_bb = current_bb;
+	  find_or_make_bb (func, gcfg, ginsn, prev_bb, errp);
+	  break;
+	}
+
+      if (current_bb == NULL)
+	{
+	  /* Create a new bb.  */
+	  current_bb = XCNEW (gbbS);
+	  cfg_add_bb (gcfg, current_bb);
+	  /* Add edge for the Not Taken, or Fall-through path.  */
+	  if (prev_bb)
+	    bb_add_edge (prev_bb, current_bb);
+	}
+
+      if (current_bb->first_ginsn == NULL)
+	current_bb->first_ginsn = ginsn;
+
+      ginsn->visited = true;
+      current_bb->num_ginsns++;
+      current_bb->last_ginsn = ginsn;
+
+      /* Note that BB is _not_ split on ginsn of type GINSN_TYPE_CALL.  */
+      if (ginsn->type == GINSN_TYPE_JUMP
+	  || ginsn->type == GINSN_TYPE_JUMP_COND
+	  || ginsn->type == GINSN_TYPE_RETURN)
+	{
+	  /* Indirect Jumps or direct jumps to symbols non-local to the
+	     function must not be seen here.  The caller must have already
+	     checked for that.  */
+	  gas_assert (!ginsn_indirect_jump_p (ginsn));
+	  if (ginsn->type == GINSN_TYPE_JUMP)
+	    gas_assert (ginsn_direct_local_jump_p (ginsn));
+
+	  /* Direct Jumps.  May include conditional or unconditional change of
+	     flow.  What is important for CFG creation is that the target be
+	     local to function.  */
+	  if (ginsn->type == GINSN_TYPE_JUMP_COND
+	      || ginsn_direct_local_jump_p (ginsn))
+	    {
+	      gas_assert (ginsn->src[0].type == GINSN_SRC_SYMBOL);
+	      taken_label = ginsn->src[0].sym;
+	      gas_assert (taken_label);
+
+	      /* Preserve the prev_bb to be the dominator bb as we are
+		 going to follow the taken path of the conditional branch
+		 soon.  */
+	      prev_bb = current_bb;
+
+	      /* Follow the target on the taken path.  */
+	      target_ginsn = label_ginsn_map_find (taken_label);
+	      /* Add the bb for the target of the taken branch.  */
+	      if (target_ginsn)
+		find_or_make_bb (func, gcfg, target_ginsn, prev_bb, errp);
+	      else
+		{
+		  *errp = GCFG_JLABEL_NOT_PRESENT;
+		  as_warn_where (ginsn->file, ginsn->line,
+				 _("missing label '%s' in func '%s' may result in imprecise cfg"),
+				 S_GET_NAME (taken_label), S_GET_NAME (func));
+		}
+	      /* Add the bb for the fall through path.  */
+	      find_or_make_bb (func, gcfg, ginsn->next, prev_bb, errp);
+	    }
+	 else if (ginsn->type == GINSN_TYPE_RETURN)
+	   {
+	     /* We'll come back to the ginsns following GINSN_TYPE_RETURN
+		from another path if they are indeed reachable code.  */
+	     break;
+	   }
+
+	 /* Current BB has been processed.  */
+	 current_bb = NULL;
+	}
+      ginsn = ginsn->next;
+    }
+
+  return current_bb;
+}
+
+static int
+gbbs_compare (const void *v1, const void *v2)
+{
+  const gbbS *bb1 = *(const gbbS **) v1;
+  const gbbS *bb2 = *(const gbbS **) v2;
+
+  if (bb1->first_ginsn->id < bb2->first_ginsn->id)
+    return -1;
+  else if (bb1->first_ginsn->id > bb2->first_ginsn->id)
+    return 1;
+  else if (bb1->first_ginsn->id == bb2->first_ginsn->id)
+    return 0;
+
+  return 0;
+}
+
+/* Synthesize DWARF CFI and emit it.  */
+
+static int
+ginsn_pass_execute_scfi (const symbolS *func, gcfgS *gcfg, gbbS *root_bb)
+{
+  int err = scfi_synthesize_dw2cfi (func, gcfg, root_bb);
+  if (!err)
+    scfi_emit_dw2cfi (func);
+
+  return err;
+}
+
+/* Traverse the list of ginsns for the function and warn if some
+   ginsns are not visited.
+
+   FIXME - this code assumes the caller has already performed a pass over
+   ginsns such that the reachable ginsns are already marked.  Revisit this - we
+   should ideally make this pass self-sufficient.  */
+
+static int
+ginsn_pass_warn_unreachable_code (const symbolS *func,
+				  gcfgS *gcfg ATTRIBUTE_UNUSED,
+				  ginsnS *root_ginsn)
+{
+  ginsnS *ginsn;
+  bool unreach_p = false;
+
+  if (!gcfg || !func || !root_ginsn)
+    return 0;
+
+  ginsn = root_ginsn;
+
+  while (ginsn)
+    {
+      /* Some ginsns of type GINSN_TYPE_SYMBOL remain unvisited.  Some
+	 may even be excluded from the CFG as they are not reachable, given
+	 their function, e.g., user labels after return machine insn.  */
+      if (!ginsn->visited
+	  && !GINSN_F_FUNC_END_P (ginsn)
+	  && !GINSN_F_USER_LABEL_P (ginsn))
+	{
+	  unreach_p = true;
+	  break;
+	}
+      ginsn = ginsn->next;
+    }
+
+  if (unreach_p)
+    as_warn_where (ginsn->file, ginsn->line,
+		   _("GINSN: found unreachable code in func '%s'"),
+		   S_GET_NAME (func));
+
+  return unreach_p;
+}
+
+void
+gcfg_get_bbs_in_prog_order (gcfgS *gcfg, gbbS **prog_order_bbs)
+{
+  uint64_t i = 0;
+  gbbS *gbb;
+
+  if (!prog_order_bbs)
+    return;
+
+  cfg_for_each_bb (gcfg, gbb)
+    {
+      gas_assert (i < gcfg->num_gbbs);
+      prog_order_bbs[i++] = gbb;
+    }
+
+  qsort (prog_order_bbs, gcfg->num_gbbs, sizeof (gbbS *), gbbs_compare);
+}
+
+/* Build the control flow graph for the ginsns of the function.
+
+   It is important that the target adds an appropriate ginsn:
+     - GINSN_TYPE_JUMP,
+     - GINSN_TYPE_JUMP_COND,
+     - GINSN_TYPE_CALL,
+     - GINSN_TYPE_RET
+  at the associated points in the function.  The correctness of the CFG
+  depends on the accuracy of these 'change of flow instructions'.  */
+
+gcfgS *
+gcfg_build (const symbolS *func, int *errp)
+{
+  gcfgS *gcfg;
+  ginsnS *first_ginsn;
+
+  gcfg = XCNEW (gcfgS);
+  first_ginsn = frchain_now->frch_ginsn_data->gins_rootP;
+  add_bb_at_ginsn (func, gcfg, first_ginsn, NULL /* prev_bb.  */, errp);
+
+  return gcfg;
+}
+
+void
+gcfg_cleanup (gcfgS **gcfgp)
+{
+  gcfgS *cfg;
+  gbbS *bb, *next_bb;
+  gedgeS *edge, *next_edge;
+
+  if (!gcfgp || !*gcfgp)
+    return;
+
+  cfg = *gcfgp;
+  bb = gcfg_get_rootbb (cfg);
+
+  while (bb)
+    {
+      next_bb = bb->next;
+
+      /* Cleanup all the edges.  */
+      edge = bb->out_gedges;
+      while (edge)
+	{
+	  next_edge = edge->next;
+	  free (edge);
+	  edge = next_edge;
+	}
+
+      gbb_cleanup (&bb);
+      bb = next_bb;
+    }
+
+  free (cfg);
+  *gcfgp = NULL;
+}
+
+gbbS *
+gcfg_get_rootbb (gcfgS *gcfg)
+{
+  gbbS *rootbb = NULL;
+
+  if (!gcfg || !gcfg->num_gbbs)
+    return NULL;
+
+  rootbb = gcfg->root_bb;
+
+  return rootbb;
+}
+
+void
+gcfg_print (const gcfgS *gcfg, FILE *outfile)
+{
+  gbbS *gbb = NULL;
+  gedgeS *gedge = NULL;
+  uint64_t total_ginsns = 0;
+
+  cfg_for_each_bb(gcfg, gbb)
+    {
+      fprintf (outfile, "BB [%" PRIu64 "] with num insns: %" PRIu64,
+	       gbb->id, gbb->num_ginsns);
+      fprintf (outfile, " [insns: %u to %u]\n",
+	       gbb->first_ginsn->line, gbb->last_ginsn->line);
+      total_ginsns += gbb->num_ginsns;
+      bb_for_each_edge(gbb, gedge)
+	fprintf (outfile, "  outgoing edge to %" PRIu64 "\n",
+		 gedge->dst_bb->id);
+    }
+  fprintf (outfile, "\nTotal ginsns in all GBBs = %" PRIu64 "\n",
+	   total_ginsns);
+}
+
+void
+frch_ginsn_data_init (const symbolS *func, symbolS *start_addr,
+		      enum ginsn_gen_mode gmode)
+{
+  /* FIXME - error out if prev object is not free'd ?  */
+  frchain_now->frch_ginsn_data = XCNEW (struct frch_ginsn_data);
+
+  frchain_now->frch_ginsn_data->mode = gmode;
+  /* Annotate with the current function symbol.  */
+  frchain_now->frch_ginsn_data->func = func;
+  /* Create a new start address symbol now.  */
+  frchain_now->frch_ginsn_data->start_addr = start_addr;
+  /* Assume the set of ginsn are apt for CFG creation, by default.  */
+  frchain_now->frch_ginsn_data->gcfg_apt_p = true;
+
+  frchain_now->frch_ginsn_data->label_ginsn_map = str_htab_create ();
+}
+
+void
+frch_ginsn_data_cleanup (void)
+{
+  ginsnS *ginsn = NULL;
+  ginsnS *next_ginsn = NULL;
+
+  ginsn = frchain_now->frch_ginsn_data->gins_rootP;
+  while (ginsn)
+    {
+      next_ginsn = ginsn->next;
+      ginsn_cleanup (&ginsn);
+      ginsn = next_ginsn;
+    }
+
+  if (frchain_now->frch_ginsn_data->label_ginsn_map)
+    htab_delete (frchain_now->frch_ginsn_data->label_ginsn_map);
+
+  free (frchain_now->frch_ginsn_data);
+  frchain_now->frch_ginsn_data = NULL;
+}
+
+/* Append GINSN to the list of ginsns for the current function being
+   assembled.  */
+
+int
+frch_ginsn_data_append (ginsnS *ginsn)
+{
+  ginsnS *last = NULL;
+  ginsnS *temp = NULL;
+  uint64_t id = 0;
+
+  if (!ginsn)
+    return 1;
+
+  if (frchain_now->frch_ginsn_data->gins_lastP)
+    id = frchain_now->frch_ginsn_data->gins_lastP->id;
+
+  /* Do the necessary preprocessing on the set of input GINSNs:
+       - Update each ginsn with its ID.
+     While you iterate, also keep gcfg_apt_p updated by checking whether any
+     ginsn is inappropriate for GCFG creation.  */
+  temp = ginsn;
+  while (temp)
+    {
+      temp->id = ++id;
+
+      if (ginsn_indirect_jump_p (temp)
+	  || (ginsn->type == GINSN_TYPE_JUMP
+	      && !ginsn_direct_local_jump_p (temp)))
+	frchain_now->frch_ginsn_data->gcfg_apt_p = false;
+
+      if (listing & LISTING_GINSN_SCFI)
+	listing_newline (ginsn_print (temp));
+
+      /* The input GINSN may be a linked list of multiple ginsns chained
+	 together.  Find the last ginsn in the input chain of ginsns.  */
+      last = temp;
+
+      temp = temp->next;
+    }
+
+  /* Link in the ginsn to the tail.  */
+  if (!frchain_now->frch_ginsn_data->gins_rootP)
+    frchain_now->frch_ginsn_data->gins_rootP = ginsn;
+  else
+    ginsn_link_next (frchain_now->frch_ginsn_data->gins_lastP, ginsn);
+
+  frchain_now->frch_ginsn_data->gins_lastP = last;
+
+  return 0;
+}
+
+enum ginsn_gen_mode
+frch_ginsn_gen_mode (void)
+{
+  enum ginsn_gen_mode gmode = GINSN_GEN_NONE;
+
+  if (frchain_now->frch_ginsn_data)
+    gmode = frchain_now->frch_ginsn_data->mode;
+
+  return gmode;
+}
+
+int
+ginsn_data_begin (const symbolS *func)
+{
+  ginsnS *ginsn;
+
+  /* The previous block of asm must have been processed by now.  */
+  if (frchain_now->frch_ginsn_data)
+    as_bad (_("GINSN process for prev func not done"));
+
+  /* FIXME - hard code the mode to GINSN_GEN_SCFI.
+     This can be changed later when other passes on ginsns are formalised.  */
+  frch_ginsn_data_init (func, symbol_temp_new_now (), GINSN_GEN_SCFI);
+
+  /* Create and insert ginsn with function begin marker.  */
+  ginsn = ginsn_new_symbol_func_begin (func);
+  frch_ginsn_data_append (ginsn);
+
+  return 0;
+}
+
+int
+ginsn_data_end (const symbolS *label)
+{
+  ginsnS *ginsn;
+  gbbS *root_bb;
+  gcfgS *gcfg = NULL;
+  const symbolS *func;
+  int err = 0;
+
+  if (!frchain_now->frch_ginsn_data)
+    return err;
+
+  /* Insert Function end marker.  */
+  ginsn = ginsn_new_symbol_func_end (label);
+  frch_ginsn_data_append (ginsn);
+
+  func = frchain_now->frch_ginsn_data->func;
+
+  /* Build the cfg of ginsn(s) of the function.  */
+  if (!frchain_now->frch_ginsn_data->gcfg_apt_p)
+    {
+      as_warn (_("Untraceable control flow for func '%s'; Skipping SCFI"),
+	       S_GET_NAME (func));
+      goto end;
+    }
+
+  gcfg = gcfg_build (func, &err);
+
+  root_bb = gcfg_get_rootbb (gcfg);
+  if (!root_bb)
+    {
+      as_bad (_("Bad cfg of ginsn of func '%s'"), S_GET_NAME (func));
+      goto end;
+    }
+
+  /* Execute the desired passes on ginsns.  */
+  err = ginsn_pass_execute_scfi (func, gcfg, root_bb);
+  if (err)
+    goto end;
+
+  /* Other passes, e.g., warn for unreachable code can be enabled too.  */
+  ginsn = frchain_now->frch_ginsn_data->gins_rootP;
+  err = ginsn_pass_warn_unreachable_code (func, gcfg, ginsn);
+
+end:
+  if (gcfg)
+    gcfg_cleanup (&gcfg);
+  frch_ginsn_data_cleanup ();
+
+  return err;
+}
+
+/* Add GINSN_TYPE_SYMBOL type ginsn for user-defined labels.  These may be
+   branch targets, and hence are necessary for control flow graph.  */
+
+void
+ginsn_frob_label (const symbolS *label)
+{
+  ginsnS *label_ginsn;
+  const char *file;
+  unsigned int line;
+
+  if (frchain_now->frch_ginsn_data)
+    {
+      /* PS: Note how we keep the actual LABEL symbol as ginsn->sym.
+	 Take care to avoid inadvertent updates or cleanups of symbols.  */
+      label_ginsn = ginsn_new_symbol_user_label (label);
+      /* Keep the location updated.  */
+      file = as_where (&line);
+      ginsn_set_file_line (label_ginsn, file, line);
+
+      frch_ginsn_data_append (label_ginsn);
+
+      label_ginsn_map_insert (label, label_ginsn);
+    }
+}
+
+const symbolS *
+ginsn_data_func_symbol (void)
+{
+  const symbolS *func = NULL;
+
+  if (frchain_now->frch_ginsn_data)
+    func = frchain_now->frch_ginsn_data->func;
+
+  return func;
+}
+
+#else
+
+int
+ginsn_data_begin (const symbolS *func ATTRIBUTE_UNUSED)
+{
+  as_bad (_("ginsn unsupported for target"));
+  return 1;
+}
+
+int
+ginsn_data_end (const symbolS *label ATTRIBUTE_UNUSED)
+{
+  as_bad (_("ginsn unsupported for target"));
+  return 1;
+}
+
+void
+ginsn_frob_label (const symbolS *sym ATTRIBUTE_UNUSED)
+{
+  return;
+}
+
+const symbolS *
+ginsn_data_func_symbol (void)
+{
+  return NULL;
+}
+
+#endif  /* TARGET_USE_GINSN.  */
diff --git a/gas/ginsn.h b/gas/ginsn.h
new file mode 100644
index 00000000000..2513e3c5cc0
--- /dev/null
+++ b/gas/ginsn.h
@@ -0,0 +1,384 @@
+/* ginsn.h - GAS instruction representation.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#ifndef GINSN_H
+#define GINSN_H
+
+#include "as.h"
+
+/* Maximum number of source operands of a ginsn.  */
+#define GINSN_NUM_SRC_OPNDS   2
+
+/* A ginsn in printed in the following format:
+      "ginsn: OPCD SRC1, SRC2, DST"
+      "<-5->  <--------125------->"
+   where each of SRC1, SRC2, and DST are in the form:
+      "%rNN,"  (up to 5 chars)
+      "imm,"   (up to int32_t+1 chars)
+      "[%rNN+-imm]," (up to int32_t+9 chars)
+      Hence a max of 19 chars.  */
+
+#define GINSN_LISTING_OPND_LEN	40
+#define GINSN_LISTING_LEN 156
+
+enum ginsn_gen_mode
+{
+  GINSN_GEN_NONE,
+  /* Generate ginsns for program validation passes.  */
+  GINSN_GEN_FVAL,
+  /* Generate ginsns for synthesizing DWARF CFI.  */
+  GINSN_GEN_SCFI,
+};
+
+/* ginsn types.
+
+   GINSN_TYPE_PHANTOM are phantom ginsns.  They are used where there is no real
+   machine instruction counterpart, but a ginsn is needed only to carry
+   information to GAS.  For example, to carry an SCFI Op.
+
+   Note that, ginsns do not have a push / pop instructions.
+   Instead, following are used:
+      type=GINSN_TYPE_LOAD, src=GINSN_SRC_INDIRECT, REG_SP: Load from stack.
+      type=GINSN_TYPE_STORE, dst=GINSN_DST_INDIRECT, REG_SP: Store to stack.
+*/
+
+#define _GINSN_TYPES \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_SYMBOL, "SYM") \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_PHANTOM, "PHANTOM")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_ADD, "ADD")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_AND, "AND")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_CALL, "CALL") \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_JUMP, "JMP") \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_JUMP_COND, "JCC")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_MOV, "MOV")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_LOAD, "LOAD")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_STORE, "STORE")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_RETURN, "RET") \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_SUB, "SUB")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_OTHER, "OTH")
+
+enum ginsn_type
+{
+#define _GINSN_TYPE_ITEM(NAME, STR) NAME,
+  _GINSN_TYPES
+#undef _GINSN_TYPE_ITEM
+};
+
+enum ginsn_src_type
+{
+  GINSN_SRC_UNKNOWN,
+  GINSN_SRC_REG,
+  GINSN_SRC_IMM,
+  GINSN_SRC_INDIRECT,
+  GINSN_SRC_SYMBOL,
+};
+
+/* GAS instruction source operand representation.  */
+
+struct ginsn_src
+{
+  enum ginsn_src_type type;
+  /* DWARF register number.  */
+  unsigned int reg;
+  /* Immediate or disp for indirect memory access.  */
+  offsetT immdisp;
+  /* Src symbol.  May be needed for some control flow instructions.  */
+  const symbolS *sym;
+};
+
+enum ginsn_dst_type
+{
+  GINSN_DST_UNKNOWN,
+  GINSN_DST_REG,
+  GINSN_DST_INDIRECT,
+};
+
+/* GAS instruction destination operand representation.  */
+
+struct ginsn_dst
+{
+  enum ginsn_dst_type type;
+  /* DWARF register number.  */
+  unsigned int reg;
+  /* Disp for indirect memory access.  */
+  offsetT disp;
+};
+
+/* Various flags for additional information per GAS instruction.  */
+
+/* Function begin or end symbol.  */
+#define GINSN_F_FUNC_MARKER	    0x1
+/* Identify real or implicit GAS insn.
+   Some targets employ CISC-like instructions.  Multiple ginsn's may be used
+   for a single machine instruction in some ISAs.  For some optimizations,
+   there is need to identify whether a ginsn, e.g., GINSN_TYPE_ADD or
+   GINSN_TYPE_SUB is a result of an user-specified instruction or not.  */
+#define GINSN_F_INSN_REAL	    0x2
+/* Identify if the GAS insn of type GINSN_TYPE_SYMBOL is due to a user-defined
+   label.  Each user-defined labels in a function will cause addition of a new
+   ginsn.  This simplifies control flow graph creation.
+   See htab_t label_ginsn_map usage.  */
+#define GINSN_F_USER_LABEL	    0x4
+/* Max bit position for flags (uint32_t).  */
+#define GINSN_F_MAX		    0x20
+
+#define GINSN_F_FUNC_BEGIN_P(ginsn)	    \
+  ((ginsn != NULL)			    \
+   && (ginsn->type == GINSN_TYPE_SYMBOL)    \
+   && (ginsn->flags & GINSN_F_FUNC_MARKER))
+
+/* PS: For ginsn associated with a user-defined symbol location,
+   GINSN_F_FUNC_MARKER is unset, but GINSN_F_USER_LABEL is set.  */
+#define GINSN_F_FUNC_END_P(ginsn)	    \
+  ((ginsn != NULL)			    \
+   && (ginsn->type == GINSN_TYPE_SYMBOL)    \
+   && !(ginsn->flags & GINSN_F_FUNC_MARKER) \
+   && !(ginsn->flags & GINSN_F_USER_LABEL))
+
+#define GINSN_F_INSN_REAL_P(ginsn)	    \
+  ((ginsn != NULL)			    \
+   && (ginsn->flags & GINSN_F_INSN_REAL))
+
+#define GINSN_F_USER_LABEL_P(ginsn)	    \
+  ((ginsn != NULL)			    \
+   && (ginsn->type == GINSN_TYPE_SYMBOL)    \
+   && !(ginsn->flags & GINSN_F_FUNC_MARKER) \
+   && (ginsn->flags & GINSN_F_USER_LABEL))
+
+typedef struct ginsn ginsnS;
+typedef struct scfi_op scfi_opS;
+typedef struct scfi_state scfi_stateS;
+
+/* GAS generic instruction.
+
+   Generic instructions are used by GAS to abstract out the binary machine
+   instructions.  In other words, ginsn is a target/ABI independent internal
+   representation for GAS.  Note that, depending on the target, there may be
+   more than one ginsn per binary machine instruction.
+
+   ginsns can be used by GAS to perform validations, or even generate
+   additional information like, sythesizing DWARF CFI for hand-written asm.  */
+
+struct ginsn
+{
+  enum ginsn_type type;
+  /* GAS instructions are simple instructions with GINSN_NUM_SRC_OPNDS number
+     of source operands and one destination operand at this time.  */
+  struct ginsn_src src[GINSN_NUM_SRC_OPNDS];
+  struct ginsn_dst dst;
+  /* Additional information per instruction.  */
+  uint32_t flags;
+  /* Symbol.  For ginsn of type other than GINSN_TYPE_SYMBOL, this identifies
+     the end of the corresponding machine instruction in the .text segment.
+     These symbols are created anew by the targets and are not used elsewhere
+     in GAS.  The only exception is some ginsns of type GINSN_TYPE_SYMBOL, when
+     generated for the user-defined labels.  See ginsn_frob_label.  */
+  const symbolS *sym;
+  /* Identifier (linearly increasing natural number) for each ginsn.  Used as
+     a proxy for program order of ginsns.  */
+  uint64_t id;
+  /* Location information for user-interfacing messaging.  Only ginsns with
+     GINSN_F_FUNC_BEGIN_P and GINSN_F_FUNC_END_P may present themselves with no
+     file or line information.  */
+  const char *file;
+  unsigned int line;
+
+  /* Information needed for synthesizing CFI.  */
+  scfi_opS **scfi_ops;
+  uint32_t num_scfi_ops;
+
+  /* Flag to keep track of visited instructions for CFG creation.  */
+  bool visited;
+
+  ginsnS *next; /* A linked list.  */
+};
+
+struct ginsn_src *ginsn_get_src1 (ginsnS *ginsn);
+struct ginsn_src *ginsn_get_src2 (ginsnS *ginsn);
+struct ginsn_dst *ginsn_get_dst (ginsnS *ginsn);
+
+unsigned int ginsn_get_src_reg (struct ginsn_src *src);
+enum ginsn_src_type ginsn_get_src_type (struct ginsn_src *src);
+offsetT ginsn_get_src_disp (struct ginsn_src *src);
+offsetT ginsn_get_src_imm (struct ginsn_src *src);
+
+unsigned int ginsn_get_dst_reg (struct ginsn_dst *dst);
+enum ginsn_dst_type ginsn_get_dst_type (struct ginsn_dst *dst);
+offsetT ginsn_get_dst_disp (struct ginsn_dst *dst);
+
+/* Data object for book-keeping information related to GAS generic
+   instructions.  */
+struct frch_ginsn_data
+{
+  /* Mode for GINSN creation.  */
+  enum ginsn_gen_mode mode;
+  /* Head of the list of ginsns.  */
+  ginsnS *gins_rootP;
+  /* Tail of the list of ginsns.  */
+  ginsnS *gins_lastP;
+  /* Function symbol.  */
+  const symbolS *func;
+  /* Start address of the function.  */
+  symbolS *start_addr;
+  /* User-defined label to ginsn mapping.  */
+  htab_t label_ginsn_map;
+  /* Is the list of ginsn apt for creating CFG.  */
+  bool gcfg_apt_p;
+};
+
+int ginsn_data_begin (const symbolS *func);
+int ginsn_data_end (const symbolS *label);
+const symbolS *ginsn_data_func_symbol (void);
+void ginsn_frob_label (const symbolS *sym);
+
+void frch_ginsn_data_init (const symbolS *func, symbolS *start_addr,
+			   enum ginsn_gen_mode gmode);
+void frch_ginsn_data_cleanup (void);
+int frch_ginsn_data_append (ginsnS *ginsn);
+enum ginsn_gen_mode frch_ginsn_gen_mode (void);
+
+void label_ginsn_map_insert (const symbolS *label, ginsnS *ginsn);
+ginsnS *label_ginsn_map_find (const symbolS *label);
+
+ginsnS *ginsn_new_symbol_func_begin (const symbolS *sym);
+ginsnS *ginsn_new_symbol_func_end (const symbolS *sym);
+ginsnS *ginsn_new_symbol_user_label (const symbolS *sym);
+
+ginsnS *ginsn_new_phantom (const symbolS *sym);
+ginsnS *ginsn_new_symbol (const symbolS *sym, bool real_p);
+ginsnS *ginsn_new_add (const symbolS *sym, bool real_p,
+		       enum ginsn_src_type src1_type, unsigned int src1_reg, offsetT src1_disp,
+		       enum ginsn_src_type src2_type, unsigned int src2_reg, offsetT src2_disp,
+		       enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp);
+ginsnS *ginsn_new_and (const symbolS *sym, bool real_p,
+		       enum ginsn_src_type src1_type, unsigned int src1_reg, offsetT src1_disp,
+		       enum ginsn_src_type src2_type, unsigned int src2_reg, offsetT src2_disp,
+		       enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp);
+ginsnS *ginsn_new_call (const symbolS *sym, bool real_p,
+			enum ginsn_src_type src_type, unsigned int src_reg,
+			const symbolS *src_text_sym);
+ginsnS *ginsn_new_jump (const symbolS *sym, bool real_p,
+			enum ginsn_src_type src_type, unsigned int src_reg,
+			const symbolS *src_ginsn_sym);
+ginsnS *ginsn_new_jump_cond (const symbolS *sym, bool real_p,
+			     enum ginsn_src_type src_type, unsigned int src_reg,
+			     const symbolS *src_ginsn_sym);
+ginsnS *ginsn_new_mov (const symbolS *sym, bool real_p,
+		       enum ginsn_src_type src_type, unsigned int src_reg, offsetT src_disp,
+		       enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp);
+ginsnS *ginsn_new_store (const symbolS *sym, bool real_p,
+			 enum ginsn_src_type src_type, unsigned int src_reg,
+			 enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp);
+ginsnS *ginsn_new_load (const symbolS *sym, bool real_p,
+			enum ginsn_src_type src_type, unsigned int src_reg, offsetT src_disp,
+			enum ginsn_dst_type dst_type, unsigned int dst_reg);
+ginsnS *ginsn_new_sub (const symbolS *sym, bool real_p,
+		       enum ginsn_src_type src1_type, unsigned int src1_reg, offsetT src1_disp,
+		       enum ginsn_src_type src2_type, unsigned int src2_reg, offsetT src2_disp,
+		       enum ginsn_dst_type dst_type, unsigned int dst_reg, offsetT dst_disp);
+ginsnS *ginsn_new_other (const symbolS *sym, bool real_p,
+			 enum ginsn_src_type src1_type, unsigned int src1_val,
+			 enum ginsn_src_type src2_type, unsigned int src2_val,
+			 enum ginsn_dst_type dst_type, unsigned int dst_reg);
+ginsnS *ginsn_new_return (const symbolS *sym, bool real_p);
+
+void ginsn_set_where (ginsnS *ginsn);
+
+bool ginsn_track_reg_p (unsigned int dw2reg, enum ginsn_gen_mode);
+
+int ginsn_link_next (ginsnS *ginsn, ginsnS *next);
+
+enum gcfg_err_code
+{
+  GCFG_OK = 0,
+  GCFG_JLABEL_NOT_PRESENT = 1, /* Warning-level code.  */
+};
+
+typedef struct gbb gbbS;
+typedef struct gedge gedgeS;
+
+/* GBB - Basic block of generic GAS instructions.  */
+
+struct gbb
+{
+  ginsnS *first_ginsn;
+  ginsnS *last_ginsn;
+  uint64_t num_ginsns;
+
+  /* Identifier (linearly increasing natural number) for each gbb.  Added for
+     debugging purpose only.  */
+  uint64_t id;
+
+  bool visited;
+
+  uint32_t num_out_gedges;
+  gedgeS *out_gedges;
+
+  /* Members for SCFI purposes.  */
+  /* SCFI state at the entry of basic block.  */
+  scfi_stateS *entry_state;
+  /* SCFI state at the exit of basic block.  */
+  scfi_stateS *exit_state;
+
+  /* A linked list.  In order of addition.  */
+  gbbS *next;
+};
+
+struct gedge
+{
+  gbbS *dst_bb;
+  /* A linked list.  In order of addition.  */
+  gedgeS *next;
+  bool visited;
+};
+
+/* Control flow graph of generic GAS instructions.  */
+
+struct gcfg
+{
+  uint64_t num_gbbs;
+  gbbS *root_bb;
+};
+
+typedef struct gcfg gcfgS;
+
+#define bb_for_each_insn(bb, ginsn)  \
+  for (ginsn = bb->first_ginsn; ginsn; \
+       ginsn = (ginsn != bb->last_ginsn) ? ginsn->next : NULL)
+
+#define bb_for_each_edge(bb, edge) \
+  for (edge = (edge == NULL) ? bb->out_gedges : edge; edge; edge = edge->next)
+
+#define cfg_for_each_bb(cfg, bb) \
+  for (bb = cfg->root_bb; bb; bb = bb->next)
+
+#define bb_get_first_ginsn(bb)	  \
+  (bb->first_ginsn)
+
+#define bb_get_last_ginsn(bb)	  \
+  (bb->last_ginsn)
+
+gcfgS *gcfg_build (const symbolS *func, int *errp);
+void gcfg_cleanup (gcfgS **gcfg);
+void gcfg_print (const gcfgS *gcfg, FILE *outfile);
+gbbS *gcfg_get_rootbb (gcfgS *gcfg);
+void gcfg_get_bbs_in_prog_order (gcfgS *gcfg, gbbS **prog_order_bbs);
+
+#endif /* GINSN_H.  */
diff --git a/gas/listing.h b/gas/listing.h
index b0391bdd687..8a4635123bf 100644
--- a/gas/listing.h
+++ b/gas/listing.h
@@ -29,6 +29,7 @@
 #define LISTING_NOCOND    32
 #define LISTING_MACEXP    64
 #define LISTING_GENERAL  128
+#define LISTING_GINSN_SCFI  256
 
 #define LISTING_DEFAULT    (LISTING_LISTING | LISTING_HLL | LISTING_SYMBOLS)
 
diff --git a/gas/read.c b/gas/read.c
index 2aa5ce2facb..8c1e399b63f 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -42,6 +42,7 @@
 #include "codeview.h"
 #include "wchar.h"
 #include "filenames.h"
+#include "ginsn.h"
 
 #include <limits.h>
 
@@ -1384,6 +1385,9 @@ read_a_source_file (const char *name)
     }
 #endif
 
+  if (flag_synth_cfi)
+    ginsn_data_end (symbol_temp_new_now ());
+
 #ifdef md_cleanup
   md_cleanup ();
 #endif
@@ -4198,6 +4202,12 @@ cons_worker (int nbytes,	/* 1=.byte, 2=.word, 4=.long.  */
 
   if (flag_mri)
     mri_comment_end (stop, stopc);
+
+  /* Disallow hand-crafting instructions using .byte.  FIXME - what about
+     .word, .long etc ?  */
+  if (flag_synth_cfi && frchain_now && frchain_now->frch_ginsn_data
+      && nbytes == 1)
+    as_bad (_("SCFI: hand-crafting instructions not supported"));
 }
 
 void
diff --git a/gas/scfi.c b/gas/scfi.c
new file mode 100644
index 00000000000..37cc85cfed7
--- /dev/null
+++ b/gas/scfi.c
@@ -0,0 +1,1232 @@
+/* scfi.c - Support for synthesizing DWARF CFI for hand-written asm.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#include "as.h"
+#include "scfi.h"
+#include "subsegs.h"
+#include "scfidw2gen.h"
+#include "dw2gencfi.h"
+
+#if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
+
+/* Beyond the target defined number of registers to be tracked
+   (SCFI_MAX_REG_ID), keep the next register ID, in sequence, for REG_CFA.  */
+#define REG_CFA	      (SCFI_MAX_REG_ID+1)
+/* Define the total number of registers being tracked.
+   Used as index into an array of cfi_reglocS.  Note that a ginsn may carry a
+   register number greater than MAX_NUM_SCFI_REGS, e.g., for the ginsns
+   corresponding to push fs/gs in AMD64.  */
+#define MAX_NUM_SCFI_REGS   (REG_CFA+1)
+
+#define REG_INVALID	    ((unsigned int)-1)
+
+enum cfi_reglocstate
+{
+  CFI_UNDEFINED,
+  CFI_IN_REG,
+  CFI_ON_STACK
+};
+
+/* Location at which CFI register is saved.
+
+   A CFI register (callee-saved registers, RA/LR) are always an offset from
+   the CFA.  REG_CFA itself, however, may have REG_SP or REG_FP as base
+   register.  Hence, keep the base reg ID and offset per tracked register.  */
+
+struct cfi_regloc
+{
+  /* Base reg ID (DWARF register number).  */
+  unsigned int base;
+  /* Location as offset from the CFA.  */
+  offsetT offset;
+  /* Current state of the CFI register.  */
+  enum cfi_reglocstate state;
+};
+
+typedef struct cfi_regloc cfi_reglocS;
+
+struct scfi_op_data
+{
+  const char *name;
+};
+
+typedef struct scfi_op_data scfi_op_dataS;
+
+/* SCFI operation.
+
+   An SCFI operation represents a single atomic change to the SCFI state.
+   This can also be understood as an abstraction for what eventually gets
+   emitted as a DWARF CFI operation.  */
+
+struct scfi_op
+{
+  /* An SCFI op updates the state of either the CFA or other tracked
+     (callee-saved, REG_SP etc) registers.  'reg' is in the DWARF register
+     number space and must be strictly less than MAX_NUM_SCFI_REGS.  */
+  unsigned int reg;
+  /* Location of the reg.  */
+  cfi_reglocS loc;
+  /* DWARF CFI opcode.  */
+  uint32_t dw2cfi_op;
+  /* Some SCFI ops, e.g., for CFI_label, may need to carry additional data.  */
+  scfi_op_dataS *op_data;
+  /* A linked list.  */
+  struct scfi_op *next;
+};
+
+/* SCFI State - accumulated unwind information at a PC.
+
+   SCFI state is the accumulated unwind information encompassing:
+      - REG_SP, REG_FP,
+      - RA, and
+      - all callee-saved registers.
+
+    Note that SCFI_MAX_REG_ID is target/ABI dependent and is provided by the
+    backends.  The backend must also identify the DWARF register numbers for
+    the REG_SP, and REG_FP registers.  */
+
+struct scfi_state
+{
+  cfi_reglocS regs[MAX_NUM_SCFI_REGS];
+  cfi_reglocS scratch[MAX_NUM_SCFI_REGS];
+  /* Current stack size.  */
+  offsetT stack_size;
+  /* Whether the stack size is known.
+     Stack size may become untraceable depending on the specific stack
+     manipulation machine instruction, e.g., rsp = rsp op reg instruction
+     makes the stack size untraceable.  */
+  bool traceable_p;
+};
+
+/* Initialize a new SCFI op.  */
+
+static scfi_opS *
+init_scfi_op (void)
+{
+  scfi_opS *op = XCNEW (scfi_opS);
+
+  return op;
+}
+
+/* Free the SCFI ops, given the HEAD of the list.  */
+
+void
+scfi_ops_cleanup (scfi_opS **head)
+{
+  scfi_opS *op;
+  scfi_opS *next;
+
+  if (!head || !*head)
+    return;
+
+  op = *head;
+  next = op->next;
+
+  while (op)
+    {
+      free (op);
+      op = next;
+      next = op ? op->next : NULL;
+    }
+}
+
+/* Compare two SCFI states.  */
+
+static int
+cmp_scfi_state (scfi_stateS *state1, scfi_stateS *state2)
+{
+  int ret;
+
+  if (!state1 || !state2)
+    ret = 1;
+
+  /* Skip comparing the scratch[] value of registers.  The user visible
+     unwind information is derived from the regs[] from the SCFI state.  */
+  ret = memcmp (state1->regs, state2->regs,
+		sizeof (cfi_reglocS) * MAX_NUM_SCFI_REGS);
+
+  /* For user functions which perform dynamic stack allocation, after switching
+     t REG_FP based CFA tracking, it is perfectly possible to have stack usage
+     in some control flows.  However, double-checking that all control flows
+     have the same idea of CFA tracking before this wont hurt.  */
+  gas_assert (state1->regs[REG_CFA].base == state2->regs[REG_CFA].base);
+  if (state1->regs[REG_CFA].base == REG_SP)
+    ret |= state1->stack_size != state2->stack_size;
+
+  ret |= state1->traceable_p != state2->traceable_p;
+
+  return ret;
+}
+
+#if 0
+static void
+scfi_state_update_reg (scfi_stateS *state, uint32_t dst, uint32_t base,
+		       int32_t offset)
+{
+  if (dst >= MAX_NUM_SCFI_REGS)
+    return;
+
+  state->regs[dst].base = base;
+  state->regs[dst].offset = offset;
+}
+#endif
+
+/* Update the SCFI state of REG as available on execution stack at OFFSET
+   from REG_CFA (BASE).
+
+   Note that BASE must be REG_CFA, because any other base (REG_SP, REG_FP)
+   is by definition transitory in the function.  */
+
+static void
+scfi_state_save_reg (scfi_stateS *state, unsigned int reg, unsigned int base,
+		     offsetT offset)
+{
+  if (reg >= MAX_NUM_SCFI_REGS)
+    return;
+
+  gas_assert (base == REG_CFA);
+
+  state->regs[reg].base = base;
+  state->regs[reg].offset = offset;
+  state->regs[reg].state = CFI_ON_STACK;
+}
+
+static void
+scfi_state_restore_reg (scfi_stateS *state, unsigned int reg)
+{
+  if (reg >= MAX_NUM_SCFI_REGS)
+    return;
+
+  /* Sanity check.  See Rule 4.  */
+  gas_assert (state->regs[reg].state == CFI_ON_STACK);
+  gas_assert (state->regs[reg].base == REG_CFA);
+
+  state->regs[reg].base = reg;
+  state->regs[reg].offset = 0;
+  /* PS: the register may still be on stack much after the restore, but the
+     SCFI state keeps the state as 'in register'.  */
+  state->regs[reg].state = CFI_IN_REG;
+}
+
+/* Identify if the given GAS instruction GINSN saves a register
+   (of interest) on stack.  */
+
+static bool
+ginsn_scfi_save_reg_p (ginsnS *ginsn, scfi_stateS *state)
+{
+  bool save_reg_p = false;
+  struct ginsn_src *src;
+  struct ginsn_dst *dst;
+
+  src = ginsn_get_src1 (ginsn);
+  dst = ginsn_get_dst (ginsn);
+
+  /* The first save to stack of callee-saved register is deemed as
+     register save.  */
+  if (!ginsn_track_reg_p (ginsn_get_src_reg (src), GINSN_GEN_SCFI)
+      || state->regs[ginsn_get_src_reg (src)].state == CFI_ON_STACK)
+    return save_reg_p;
+
+  /* A register save insn may be an indirect mov.  */
+  if (ginsn->type == GINSN_TYPE_MOV
+      && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT
+      && (ginsn_get_dst_reg (dst) == REG_SP
+	  || (ginsn_get_dst_reg (dst) == REG_FP
+	      && state->regs[REG_CFA].base == REG_FP)))
+    save_reg_p = true;
+  /* or an explicit store to stack.  */
+  else if (ginsn->type == GINSN_TYPE_STORE
+	   && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT
+	   && ginsn_get_dst_reg (dst) == REG_SP)
+    save_reg_p = true;
+
+  return save_reg_p;
+}
+
+/* Identify if the given GAS instruction GINSN restores a register
+   (of interest) on stack.  */
+
+static bool
+ginsn_scfi_restore_reg_p (ginsnS *ginsn, scfi_stateS *state)
+{
+  bool restore_reg_p = false;
+  struct ginsn_dst *dst;
+  struct ginsn_src *src1;
+
+  dst = ginsn_get_dst (ginsn);
+  src1 = ginsn_get_src1 (ginsn);
+
+  if (!ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI))
+    return restore_reg_p;
+
+  /* A register restore insn may be an indirect mov...  */
+  if (ginsn->type == GINSN_TYPE_MOV
+      && ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT
+      && (ginsn_get_src_reg (src1) == REG_SP
+	  || (ginsn_get_src_reg (src1) == REG_FP
+	      && state->regs[REG_CFA].base == REG_FP)))
+    restore_reg_p = true;
+  /* ...or an explicit load from stack.  */
+  else if (ginsn->type == GINSN_TYPE_LOAD
+	   && ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT
+	   && ginsn_get_src_reg (src1) == REG_SP)
+    restore_reg_p = true;
+
+  return restore_reg_p;
+}
+
+/* Append the SCFI operation OP to the list of SCFI operations in the
+   given GINSN.  */
+
+static int
+ginsn_append_scfi_op (ginsnS *ginsn, scfi_opS *op)
+{
+  scfi_opS *sop;
+
+  if (!ginsn || !op)
+    return 1;
+
+  if (!ginsn->scfi_ops)
+    {
+      ginsn->scfi_ops = XCNEW (scfi_opS *);
+      *ginsn->scfi_ops = op;
+    }
+  else
+    {
+      /* Add to tail.  Most ginsns have a single SCFI operation,
+	 so this traversal for every insertion is acceptable for now.  */
+      sop = *ginsn->scfi_ops;
+      while (sop->next)
+	sop = sop->next;
+
+      sop->next = op;
+    }
+  ginsn->num_scfi_ops++;
+
+  return 0;
+}
+
+static void
+scfi_op_add_def_cfa_reg (scfi_stateS *state, ginsnS *ginsn, unsigned int reg)
+{
+  scfi_opS *op = NULL;
+
+  state->regs[REG_CFA].base = reg;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_def_cfa_register;
+  op->reg = REG_CFA;
+  op->loc = state->regs[REG_CFA];
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static void
+scfi_op_add_cfa_offset_inc (scfi_stateS *state, ginsnS *ginsn, offsetT num)
+{
+  scfi_opS *op = NULL;
+
+  state->regs[REG_CFA].offset -= num;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_def_cfa_offset;
+  op->reg = REG_CFA;
+  op->loc = state->regs[REG_CFA];
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static void
+scfi_op_add_cfa_offset_dec (scfi_stateS *state, ginsnS *ginsn, offsetT num)
+{
+  scfi_opS *op = NULL;
+
+  state->regs[REG_CFA].offset += num;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_def_cfa_offset;
+  op->reg = REG_CFA;
+  op->loc = state->regs[REG_CFA];
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static void
+scfi_op_add_def_cfa (scfi_stateS *state, ginsnS *ginsn, unsigned int reg,
+		     offsetT num)
+{
+  scfi_opS *op = NULL;
+
+  state->regs[REG_CFA].base = reg;
+  state->regs[REG_CFA].offset = num;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_def_cfa;
+  op->reg = REG_CFA;
+  op->loc = state->regs[REG_CFA];
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static void
+scfi_op_add_cfi_offset (scfi_stateS *state, ginsnS *ginsn, unsigned int reg)
+{
+  scfi_opS *op = NULL;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_offset;
+  op->reg = reg;
+  op->loc = state->regs[reg];
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static void
+scfi_op_add_cfa_restore (ginsnS *ginsn, unsigned int reg)
+{
+  scfi_opS *op = NULL;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_restore;
+  op->reg = reg;
+  op->loc.base = REG_INVALID;
+  op->loc.offset = 0;
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static void
+scfi_op_add_cfi_remember_state (ginsnS *ginsn)
+{
+  scfi_opS *op = NULL;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_remember_state;
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static void
+scfi_op_add_cfi_restore_state (ginsnS *ginsn)
+{
+  scfi_opS *op = NULL;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_restore_state;
+
+  /* FIXME - add to the beginning of the scfi_ops.  */
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+void
+scfi_op_add_cfi_label (ginsnS *ginsn, const char *name)
+{
+  scfi_opS *op = NULL;
+
+  op = init_scfi_op ();
+  op->dw2cfi_op = CFI_label;
+  op->op_data = XCNEW (scfi_op_dataS);
+  op->op_data->name = name;
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+void
+scfi_op_add_signal_frame (ginsnS *ginsn)
+{
+  scfi_opS *op = NULL;
+
+  op = init_scfi_op ();
+  op->dw2cfi_op = CFI_signal_frame;
+
+  ginsn_append_scfi_op (ginsn, op);
+}
+
+static int
+verify_heuristic_traceable_reg_fp (ginsnS *ginsn, scfi_stateS *state)
+{
+  /* The function uses this variable to issue error to user right away.  */
+  int fp_traceable_p = 0;
+  struct ginsn_dst *dst;
+  struct ginsn_src *src1;
+  struct ginsn_src *src2;
+
+  src1 = ginsn_get_src1 (ginsn);
+  src2 = ginsn_get_src2 (ginsn);
+  dst = ginsn_get_dst (ginsn);
+
+  /* Stack manipulation can be done in a variety of ways.  A program may
+     allocate stack statically or may perform dynamic stack allocation in
+     the prologue.
+
+     The SCFI machinery in GAS is based on some heuristics:
+
+       - Rule 3 If the base register for CFA tracking is REG_FP, the program
+       must not clobber REG_FP, unless it is for switch to REG_SP based CFA
+       tracking (via say, a pop %rbp in X86).  */
+
+  /* Check all applicable instructions with dest REG_FP, when the CFA base
+     register is REG_FP.  */
+  if (state->regs[REG_CFA].base == REG_FP && ginsn_get_dst_reg (dst) == REG_FP)
+    {
+      /* Excuse the add/sub with imm usage: They are OK.  */
+      if ((ginsn->type == GINSN_TYPE_ADD || ginsn->type == GINSN_TYPE_SUB)
+	  && ginsn_get_src_reg (src1) == REG_FP
+	  && ginsn_get_src_type (src2) == GINSN_SRC_IMM)
+	fp_traceable_p = 0;
+      /* REG_FP restore is OK too.  */
+      else if (ginsn->type == GINSN_TYPE_LOAD)
+	fp_traceable_p = 0;
+      /* mov's to memory with REG_FP base do not make REG_FP untraceable.  */
+      else if (ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT
+	       && (ginsn->type == GINSN_TYPE_MOV
+		   || ginsn->type == GINSN_TYPE_STORE))
+	fp_traceable_p = 0;
+      /* Manipulations of the values possibly on stack are OK too.  */
+      else if ((ginsn->type == GINSN_TYPE_ADD || ginsn->type == GINSN_TYPE_SUB
+		|| ginsn->type == GINSN_TYPE_AND)
+	       && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT)
+	fp_traceable_p = 0;
+      /* All other ginsns with REG_FP as destination make REG_FP not
+	 traceable.  */
+      else
+	fp_traceable_p = 1;
+    }
+
+  if (fp_traceable_p)
+    as_bad_where (ginsn->file, ginsn->line,
+		  _("SCFI: usage of REG_FP as scratch not supported"));
+
+  return fp_traceable_p;
+}
+
+static int
+verify_heuristic_traceable_stack_manipulation (ginsnS *ginsn,
+					       scfi_stateS *state)
+{
+  /* The function uses this variable to issue error to user right away.  */
+  int sp_untraceable_p = 0;
+  bool possibly_untraceable = false;
+  struct ginsn_dst *dst;
+  struct ginsn_src *src1;
+  struct ginsn_src *src2;
+
+  src1 = ginsn_get_src1 (ginsn);
+  src2 = ginsn_get_src2 (ginsn);
+  dst = ginsn_get_dst (ginsn);
+
+  /* Stack manipulation can be done in a variety of ways.  A program may
+     allocate stack statically in prologue or may need to do dynamic stack
+     allocation.
+
+     The SCFI machinery in GAS is based on some heuristics:
+
+       - Rule 1 The base register for CFA tracking may be either REG_SP or
+       REG_FP.
+
+       - Rule 2 If the base register for CFA tracking is REG_SP, the precise
+       amount of stack usage (and hence, the value of rsp) must be known at
+       all times.  */
+
+  if (ginsn->type == GINSN_TYPE_MOV
+      && ginsn_get_dst_type (dst) == GINSN_DST_REG
+      && ginsn_get_dst_reg (dst) == REG_SP
+      && ginsn_get_src_type (src1) == GINSN_SRC_REG
+      /* Exclude mov %rbp, %rsp from this check.  */
+      && ginsn_get_src_reg (src1) != REG_FP)
+    {
+      /* mov %reg, %rsp.  */
+      /* A previous mov %rsp, %reg must have been seen earlier for this to be
+	 an OK for stack manipulation.  */
+      if (state->scratch[ginsn_get_src_reg (src1)].base != REG_CFA
+	  || state->scratch[ginsn_get_src_reg (src1)].state != CFI_IN_REG)
+	{
+	  possibly_untraceable = true;
+	}
+    }
+  /* Check add/sub/and insn usage when CFA base register is REG_SP.
+     Any stack size manipulation, including stack realignment is not allowed
+     if CFA base register is REG_SP.  */
+  else if (ginsn_get_dst_type (dst) == GINSN_DST_REG
+	   && ginsn_get_dst_reg (dst) == REG_SP
+	   && (((ginsn->type == GINSN_TYPE_ADD || ginsn->type == GINSN_TYPE_SUB)
+		&& ginsn_get_src_type (src2) != GINSN_SRC_IMM)
+	       || ginsn->type == GINSN_TYPE_AND
+	       || ginsn->type == GINSN_TYPE_OTHER))
+    possibly_untraceable = true;
+  /* If a register save operation is seen when REG_SP is untraceable,
+     CFI cannot be synthesized for register saves, hence bail out.  */
+  else if (ginsn_scfi_save_reg_p (ginsn, state) && !state->traceable_p)
+    {
+      sp_untraceable_p = 1;
+      /* If, however, the register save is an REG_FP-based, indirect mov
+	 like: mov reg, disp(%rbp) and CFA base register is REG_BP,
+	 untraceable REG_SP is not a problem.  */
+      if (ginsn->type == GINSN_TYPE_MOV
+	  && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT
+	  && (ginsn_get_dst_reg (dst) == REG_FP
+	      && state->regs[REG_CFA].base == REG_FP))
+	sp_untraceable_p = 0;
+    }
+  else if (ginsn_scfi_restore_reg_p (ginsn, state) && !state->traceable_p)
+    {
+      if (ginsn->type == GINSN_TYPE_MOV
+	  && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT
+	  && (ginsn_get_src_reg (src1) == REG_SP
+	      || (ginsn_get_src_reg (src1) == REG_FP
+		  && state->regs[REG_CFA].base != REG_FP)))
+	sp_untraceable_p = 1;
+    }
+
+  if (possibly_untraceable)
+    {
+      /* See Rule 2.  For SP-based CFA, this makes CFA tracking not possible.
+	 Propagate now to caller.  */
+      if (state->regs[REG_CFA].base == REG_SP)
+	sp_untraceable_p = 1;
+      else if (state->traceable_p)
+	{
+	  /* An extension of Rule 2.
+	     For FP-based CFA, this may be a problem *if* certain specific
+	     changes to the SCFI state are seen beyond this point, e.g.,
+	     register save / restore from stack.  */
+	  gas_assert (state->regs[REG_CFA].base == REG_FP);
+	  /* Simply make a note in the SCFI state object for now and
+	     continue.  Indicate an error when register save / restore
+	     for callee-saved registers is seen.  */
+	  sp_untraceable_p = 0;
+	  state->traceable_p = false;
+	}
+    }
+
+  if (sp_untraceable_p)
+    as_bad_where (ginsn->file, ginsn->line,
+		  _("SCFI: unsupported stack manipulation pattern"));
+
+  return sp_untraceable_p;
+}
+
+static int
+verify_heuristic_symmetrical_restore_reg (scfi_stateS *state, ginsnS* ginsn)
+{
+  int sym_restore = true;
+  offsetT expected_offset = 0;
+  struct ginsn_src *src1;
+  struct ginsn_dst *dst;
+  unsigned int reg;
+
+  /* Rule 4: Save and Restore of callee-saved registers must be symmetrical.
+     It is expected that value of the saved register is restored correctly.
+     E.g.,
+	push  reg1
+	push  reg2
+	...
+	body of func which uses reg1 , reg2 as scratch,
+	and may be even spills them to stack.
+	...
+	pop   reg2
+	pop   reg1
+     It is difficult to verify the Rule 4 in all cases.  For the SCFI machinery,
+     it is difficult to separate prologue-epilogue from the body of the function
+
+     Hence, the SCFI machinery at this time, should only warn on an asymetrical
+     restore.  */
+  src1 = ginsn_get_src1 (ginsn);
+  dst = ginsn_get_dst (ginsn);
+  reg = ginsn_get_dst_reg (dst);
+
+  /* For non callee-saved registers, calling the API is meaningless.  */
+  if (!ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI))
+    return sym_restore;
+
+  /* The register must have been saved on stack, for sure.  */
+  gas_assert (state->regs[reg].state == CFI_ON_STACK);
+  gas_assert (state->regs[reg].base == REG_CFA);
+
+  if ((ginsn->type == GINSN_TYPE_MOV
+       || ginsn->type == GINSN_TYPE_LOAD)
+      && ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT
+      && (ginsn_get_src_reg (src1) == REG_SP
+	  || (ginsn_get_src_reg (src1) == REG_FP
+	      && state->regs[REG_CFA].base == REG_FP)))
+    {
+      /* mov disp(%rsp), reg.  */
+      /* mov disp(%rbp), reg.  */
+      expected_offset = (((ginsn_get_src_reg (src1) == REG_SP)
+			  ? -state->stack_size
+			  : state->regs[REG_FP].offset)
+			 + ginsn_get_src_disp (src1));
+    }
+
+  sym_restore = (expected_offset == state->regs[reg].offset);
+
+  return sym_restore;
+}
+
+/* Perform symbolic execution of the GINSN and update its list of scfi_ops.
+   scfi_ops are later used to directly generate the DWARF CFI directives.
+   Also update the SCFI state object STATE for the caller.  */
+
+static int
+gen_scfi_ops (ginsnS *ginsn, scfi_stateS *state)
+{
+  int ret = 0;
+  offsetT offset;
+  struct ginsn_src *src1;
+  struct ginsn_src *src2;
+  struct ginsn_dst *dst;
+
+  if (!ginsn || !state)
+    ret = 1;
+
+  /* For the first ginsn (of type GINSN_TYPE_SYMBOL) in the gbb, generate
+     the SCFI op with DW_CFA_def_cfa.  Note that the register and offset are
+     target-specific.  */
+  if (GINSN_F_FUNC_BEGIN_P (ginsn))
+    {
+      scfi_op_add_def_cfa (state, ginsn, REG_SP, SCFI_INIT_CFA_OFFSET);
+      state->stack_size += SCFI_INIT_CFA_OFFSET;
+      return ret;
+    }
+
+  src1 = ginsn_get_src1 (ginsn);
+  src2 = ginsn_get_src2 (ginsn);
+  dst = ginsn_get_dst (ginsn);
+
+  ret = verify_heuristic_traceable_stack_manipulation (ginsn, state);
+  if (ret)
+    return ret;
+
+  ret = verify_heuristic_traceable_reg_fp (ginsn, state);
+  if (ret)
+    return ret;
+
+  switch (ginsn->dst.type)
+    {
+    case GINSN_DST_REG:
+      switch (ginsn->type)
+	{
+	case GINSN_TYPE_MOV:
+	  if (ginsn_get_src_type (src1) == GINSN_SRC_REG
+	      && ginsn_get_src_reg (src1) == REG_SP
+	      && ginsn_get_dst_reg (dst) == REG_FP
+	      && state->regs[REG_CFA].base == REG_SP)
+	    {
+	      /* mov %rsp, %rbp.  */
+	      scfi_op_add_def_cfa_reg (state, ginsn, ginsn_get_dst_reg (dst));
+	    }
+	  else if (ginsn_get_src_type (src1) == GINSN_SRC_REG
+		   && ginsn_get_src_reg (src1) == REG_FP
+		   && ginsn_get_dst_reg (dst) == REG_SP
+		   && state->regs[REG_CFA].base == REG_FP)
+	    {
+	      /* mov %rbp, %rsp.  */
+	      state->stack_size = -state->regs[REG_FP].offset;
+	      scfi_op_add_def_cfa_reg (state, ginsn, ginsn_get_dst_reg (dst));
+	      state->traceable_p = true;
+	    }
+	  else if (ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT
+		   && (ginsn_get_src_reg (src1) == REG_SP
+		       || ginsn_get_src_reg (src1) == REG_FP)
+		   && ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI))
+	    {
+	      /* mov disp(%rsp), reg.  */
+	      /* mov disp(%rbp), reg.  */
+	      if (verify_heuristic_symmetrical_restore_reg (state, ginsn))
+		{
+		  scfi_state_restore_reg (state, ginsn_get_dst_reg (dst));
+		  scfi_op_add_cfa_restore (ginsn, ginsn_get_dst_reg (dst));
+		}
+	      else
+		as_warn_where (ginsn->file, ginsn->line,
+			       _("SCFI: asymetrical register restore"));
+	    }
+	  else if (ginsn_get_src_type (src1) == GINSN_SRC_REG
+		   && ginsn_get_dst_type (dst) == GINSN_DST_REG
+		   && ginsn_get_src_reg (src1) == REG_SP)
+	    {
+	      /* mov %rsp, %reg.  */
+	      /* The value of rsp is taken directly from state->stack_size.
+		 IMP: The workflow in gen_scfi_ops must keep it updated.
+		 PS: Not taking the value from state->scratch[REG_SP] is
+		 intentional.  */
+	      state->scratch[ginsn_get_dst_reg (dst)].base = REG_CFA;
+	      state->scratch[ginsn_get_dst_reg (dst)].offset = -state->stack_size;
+	      state->scratch[ginsn_get_dst_reg (dst)].state = CFI_IN_REG;
+	    }
+	  else if (ginsn_get_src_type (src1) == GINSN_SRC_REG
+		   && ginsn_get_dst_type (dst) == GINSN_DST_REG
+		   && ginsn_get_dst_reg (dst) == REG_SP)
+	    {
+	      /* mov %reg, %rsp.  */
+	      /* Keep the value of REG_SP updated.  */
+	      if (state->scratch[ginsn_get_src_reg (src1)].state == CFI_IN_REG)
+		{
+		  state->stack_size = -state->scratch[ginsn_get_src_reg (src1)].offset;
+		  state->traceable_p = true;
+		}
+# if 0
+	      scfi_state_update_reg (state, ginsn_get_dst_reg (dst),
+				     state->scratch[ginsn_get_src_reg (src1)].base,
+				     state->scratch[ginsn_get_src_reg (src1)].offset);
+#endif
+
+	    }
+	  break;
+	case GINSN_TYPE_SUB:
+	  if (ginsn_get_src_reg (src1) == REG_SP
+	      && ginsn_get_dst_reg (dst) == REG_SP)
+	    {
+	      /* Stack inc/dec offset, when generated due to stack push and pop is
+		 target-specific.  Use the value encoded in the ginsn.  */
+	      state->stack_size += ginsn_get_src_imm (src2);
+	      if (state->regs[REG_CFA].base == REG_SP)
+		{
+		  /* push reg.  */
+		  scfi_op_add_cfa_offset_dec (state, ginsn, ginsn_get_src_imm (src2));
+		}
+	    }
+	  break;
+	case GINSN_TYPE_ADD:
+	  if (ginsn_get_src_reg (src1) == REG_SP
+	      && ginsn_get_dst_reg (dst) == REG_SP)
+	    {
+	      /* Stack inc/dec offset is target-specific.  Use the value
+		 encoded in the ginsn.  */
+	      state->stack_size -= ginsn_get_src_imm (src2);
+	      /* pop %reg affects CFA offset only if CFA is currently
+		 stack-pointer based.  */
+	      if (state->regs[REG_CFA].base == REG_SP)
+		{
+		  scfi_op_add_cfa_offset_inc (state, ginsn, ginsn_get_src_imm (src2));
+		}
+	    }
+	  else if (ginsn_get_src_reg (src1) == REG_FP
+		   && ginsn_get_dst_reg (dst) == REG_SP
+		   && state->regs[REG_CFA].base == REG_FP)
+	    {
+	      /* FIXME - what is this for ? */
+	      state->stack_size =  0 - (state->regs[REG_FP].offset + ginsn_get_src_imm (src2));
+	    }
+	  break;
+	case GINSN_TYPE_LOAD:
+	  /* If this is a load from stack.  */
+	  if (ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT
+	      && (ginsn_get_src_reg (src1) == REG_SP
+		  || (ginsn_get_src_reg (src1) == REG_FP
+		      && state->regs[REG_CFA].base == REG_FP)))
+	    {
+	      /* pop %rbp when CFA tracking is REG_FP based.  */
+	      if (ginsn_get_dst_reg (dst) == REG_FP
+		  && state->regs[REG_CFA].base == REG_FP)
+		{
+		  scfi_op_add_def_cfa_reg (state, ginsn, REG_SP);
+		  if (state->regs[REG_CFA].offset != state->stack_size)
+		    scfi_op_add_cfa_offset_inc (state, ginsn,
+						(state->regs[REG_CFA].offset - state->stack_size));
+		}
+	      if (ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI))
+		{
+		  if (verify_heuristic_symmetrical_restore_reg (state, ginsn))
+		    {
+		      scfi_state_restore_reg (state, ginsn_get_dst_reg (dst));
+		      scfi_op_add_cfa_restore (ginsn, ginsn_get_dst_reg (dst));
+		    }
+		  else
+		    as_warn_where (ginsn->file, ginsn->line,
+				   _("SCFI: asymetrical register restore"));
+		}
+	    }
+	  break;
+	default:
+	  break;
+	}
+      break;
+
+    case GINSN_DST_INDIRECT:
+      /* Some operations with an indirect access to memory (or even to stack)
+	 may still be uninteresting for SCFI purpose (e.g, addl %edx, -32(%rsp)
+	 in x86).  In case of x86_64, these can neither be a register
+	 save / unsave, nor can alter the stack size.
+	 PS: This condition may need to be revisited for other arches.  */
+      if (ginsn->type == GINSN_TYPE_ADD || ginsn->type == GINSN_TYPE_SUB
+	  || ginsn->type == GINSN_TYPE_AND)
+	break;
+      gas_assert (ginsn->type == GINSN_TYPE_MOV
+		  || ginsn->type == GINSN_TYPE_STORE
+		  || ginsn->type == GINSN_TYPE_LOAD);
+      /* mov reg, disp(%rbp) */
+      /* mov reg, disp(%rsp) */
+      if (ginsn_scfi_save_reg_p (ginsn, state))
+	{
+	  if (ginsn_get_dst_reg (dst) == REG_SP)
+	    {
+	      /* mov reg, disp(%rsp) */
+	      offset = 0 - state->stack_size + ginsn_get_dst_disp (dst);
+	      scfi_state_save_reg (state, ginsn_get_src_reg (src1), REG_CFA, offset);
+	      scfi_op_add_cfi_offset (state, ginsn, ginsn_get_src_reg (src1));
+	    }
+	  else if (ginsn_get_dst_reg (dst) == REG_FP)
+	    {
+	      gas_assert (state->regs[REG_CFA].base == REG_FP);
+	      /* mov reg, disp(%rbp) */
+	      offset = 0 - state->regs[REG_CFA].offset + ginsn_get_dst_disp (dst);
+	      scfi_state_save_reg (state, ginsn_get_src_reg (src1), REG_CFA, offset);
+	      scfi_op_add_cfi_offset (state, ginsn, ginsn_get_src_reg (src1));
+	    }
+	}
+      break;
+
+    default:
+      /* Skip GINSN_DST_UNKNOWN and GINSN_DST_MEM as they are uninteresting
+	 currently for SCFI.  */
+      break;
+    }
+
+  return ret;
+}
+
+/* Recursively perform forward flow of the (unwind information) SCFI state
+   starting at basic block GBB.
+
+   The forward flow process propagates the SCFI state at exit of a basic block
+   to the successor basic block.
+
+   Returns error code, if any.  */
+
+static int
+forward_flow_scfi_state (gcfgS *gcfg, gbbS *gbb, scfi_stateS *state)
+{
+  ginsnS *ginsn;
+  gbbS *prev_bb;
+  gedgeS *gedge = NULL;
+  int ret = 0;
+
+  if (gbb->visited)
+    {
+      /* Check that the SCFI state is the same as previous.  */
+      ret = cmp_scfi_state (state, gbb->entry_state);
+      if (ret)
+	as_bad (_("SCFI: Bad CFI propagation perhaps"));
+      return ret;
+    }
+
+  gbb->visited = true;
+
+  gbb->entry_state = XCNEW (scfi_stateS);
+  memcpy (gbb->entry_state, state, sizeof (scfi_stateS));
+
+  /* Perform symbolic execution of each ginsn in the gbb and update the
+     scfi_ops list of each ginsn (and also update the STATE object).   */
+  bb_for_each_insn(gbb, ginsn)
+    {
+      ret = gen_scfi_ops (ginsn, state);
+      if (ret)
+	goto fail;
+    }
+
+  gbb->exit_state = XCNEW (scfi_stateS);
+  memcpy (gbb->exit_state, state, sizeof (scfi_stateS));
+
+  /* Forward flow the SCFI state.  Currently, we process the next basic block
+     in DFS order.  But any forward traversal order should be fine.  */
+  prev_bb = gbb;
+  if (gbb->num_out_gedges)
+    {
+      bb_for_each_edge(gbb, gedge)
+	{
+	  gbb = gedge->dst_bb;
+	  if (gbb->visited)
+	    {
+	      ret = cmp_scfi_state (gbb->entry_state, state);
+	      if (ret)
+		goto fail;
+	    }
+
+	  if (!gedge->visited)
+	    {
+	      gedge->visited = true;
+
+	      /* Entry SCFI state for the destination bb of the edge is the
+		 same as the exit SCFI state of the source bb of the edge.  */
+	      memcpy (state, prev_bb->exit_state, sizeof (scfi_stateS));
+	      ret = forward_flow_scfi_state (gcfg, gbb, state);
+	      if (ret)
+		goto fail;
+	    }
+	}
+    }
+
+  return 0;
+
+fail:
+
+  if (gedge)
+    gedge->visited = true;
+  return 1;
+}
+
+static int
+backward_flow_scfi_state (const symbolS *func ATTRIBUTE_UNUSED, gcfgS *gcfg)
+{
+  gbbS **prog_order_bbs;
+  gbbS **restore_bbs;
+  gbbS *current_bb;
+  gbbS *prev_bb;
+  gbbS *dst_bb;
+  ginsnS *ginsn;
+  gedgeS *gedge = NULL;
+
+  int ret = 0;
+  uint64_t i, j;
+
+  /* Basic blocks in reverse program order.  */
+  prog_order_bbs = XCNEWVEC (gbbS *, gcfg->num_gbbs);
+  /* Basic blocks for which CFI remember op needs to be generated.  */
+  restore_bbs = XCNEWVEC (gbbS *, gcfg->num_gbbs);
+
+  gcfg_get_bbs_in_prog_order (gcfg, prog_order_bbs);
+
+  i = gcfg->num_gbbs - 1;
+  /* Traverse in reverse program order.  */
+  while (i > 0)
+    {
+      current_bb = prog_order_bbs[i];
+      prev_bb = prog_order_bbs[i-1];
+      if (cmp_scfi_state (prev_bb->exit_state, current_bb->entry_state))
+	{
+	  /* Candidate for .cfi_restore_state found.  */
+	  ginsn = bb_get_first_ginsn (current_bb);
+	  scfi_op_add_cfi_restore_state (ginsn);
+	  /* Memorize current_bb now to find location for its remember state
+	     later.  */
+	  restore_bbs[i] = current_bb;
+	}
+      else
+	{
+	  bb_for_each_edge (current_bb, gedge)
+	    {
+	      dst_bb = gedge->dst_bb;
+	      for (j = 0; j < gcfg->num_gbbs; j++)
+		if (restore_bbs[j] == dst_bb)
+		  {
+		    ginsn = bb_get_last_ginsn (current_bb);
+		    scfi_op_add_cfi_remember_state (ginsn);
+		    /* Remove the memorised restore_bb from the list.  */
+		    restore_bbs[j] = NULL;
+		    break;
+		  }
+	    }
+	}
+      i--;
+    }
+
+  /* All .cfi_restore_state pseudo-ops must have a corresponding
+     .cfi_remember_state by now.  */
+  for (j = 0; j < gcfg->num_gbbs; j++)
+    if (restore_bbs[j] != NULL)
+      {
+	ret = 1;
+	break;
+      }
+
+  free (restore_bbs);
+  free (prog_order_bbs);
+
+  return ret;
+}
+
+/* Synthesize DWARF CFI for a function.  */
+
+int
+scfi_synthesize_dw2cfi (const symbolS *func, gcfgS *gcfg, gbbS *root_bb)
+{
+  int ret;
+  scfi_stateS *init_state;
+
+  init_state = XCNEW (scfi_stateS);
+  init_state->traceable_p = true;
+
+  /* Traverse the input GCFG and perform forward flow of information.
+     Update the scfi_op(s) per ginsn.  */
+  ret = forward_flow_scfi_state (gcfg, root_bb, init_state);
+  if (ret)
+    {
+      as_bad (_("SCFI: forward pass failed for func '%s'"), S_GET_NAME (func));
+      goto end;
+    }
+
+  ret = backward_flow_scfi_state (func, gcfg);
+  if (ret)
+    {
+      as_bad (_("SCFI: backward pass failed for func '%s'"), S_GET_NAME (func));
+      goto end;
+    }
+
+end:
+  free (init_state);
+  return ret;
+}
+
+static int
+handle_scfi_dot_cfi (ginsnS *ginsn)
+{
+  scfi_opS *op;
+
+  /* Nothing to do.  */
+  if (!ginsn->scfi_ops)
+    return 0;
+
+  op = *ginsn->scfi_ops;
+  if (!op)
+    goto bad;
+
+  while (op)
+    {
+      switch (op->dw2cfi_op)
+	{
+	case DW_CFA_def_cfa_register:
+	  scfi_dot_cfi (DW_CFA_def_cfa_register, op->loc.base, 0, 0, NULL,
+			ginsn->sym);
+	  break;
+	case DW_CFA_def_cfa_offset:
+	  scfi_dot_cfi (DW_CFA_def_cfa_offset, op->loc.base, 0,
+			op->loc.offset, NULL, ginsn->sym);
+	  break;
+	case DW_CFA_def_cfa:
+	  scfi_dot_cfi (DW_CFA_def_cfa, op->loc.base, 0, op->loc.offset,
+			NULL, ginsn->sym);
+	  break;
+	case DW_CFA_offset:
+	  scfi_dot_cfi (DW_CFA_offset, op->reg, 0, op->loc.offset, NULL,
+			ginsn->sym);
+	  break;
+	case DW_CFA_restore:
+	  scfi_dot_cfi (DW_CFA_restore, op->reg, 0, 0, NULL, ginsn->sym);
+	  break;
+	case DW_CFA_remember_state:
+	  scfi_dot_cfi (DW_CFA_remember_state, 0, 0, 0, NULL, ginsn->sym);
+	  break;
+	case DW_CFA_restore_state:
+	  scfi_dot_cfi (DW_CFA_restore_state, 0, 0, 0, NULL, ginsn->sym);
+	  break;
+	case CFI_label:
+	  scfi_dot_cfi (CFI_label, 0, 0, 0, op->op_data->name, ginsn->sym);
+	  break;
+	case CFI_signal_frame:
+	  scfi_dot_cfi (CFI_signal_frame, 0, 0, 0, NULL, ginsn->sym);
+	  break;
+	default:
+	  goto bad;
+	  break;
+	}
+      op = op->next;
+    }
+
+  return 0;
+bad:
+  as_bad (_("SCFI: Invalid DWARF CFI opcode data"));
+  return 1;
+}
+
+/* Emit Synthesized DWARF CFI.  */
+
+int
+scfi_emit_dw2cfi (const symbolS *func)
+{
+  struct frch_ginsn_data *frch_gdata;
+  ginsnS* ginsn = NULL;
+
+  frch_gdata = frchain_now->frch_ginsn_data;
+  ginsn = frch_gdata->gins_rootP;
+
+  while (ginsn)
+    {
+      switch (ginsn->type)
+	{
+	  case GINSN_TYPE_SYMBOL:
+	    /* .cfi_startproc and .cfi_endproc pseudo-ops.  */
+	    if (GINSN_F_FUNC_BEGIN_P (ginsn))
+	      {
+		scfi_dot_cfi_startproc (frch_gdata->start_addr);
+		break;
+	      }
+	    else if (GINSN_F_FUNC_END_P (ginsn))
+	      {
+		scfi_dot_cfi_endproc (ginsn->sym);
+		break;
+	      }
+	    /* Fall through.  */
+	  case GINSN_TYPE_ADD:
+	  case GINSN_TYPE_AND:
+	  case GINSN_TYPE_CALL:
+	  case GINSN_TYPE_JUMP:
+	  case GINSN_TYPE_JUMP_COND:
+	  case GINSN_TYPE_MOV:
+	  case GINSN_TYPE_LOAD:
+	  case GINSN_TYPE_PHANTOM:
+	  case GINSN_TYPE_STORE:
+	  case GINSN_TYPE_SUB:
+	  case GINSN_TYPE_OTHER:
+	  case GINSN_TYPE_RETURN:
+
+	    /* For all other SCFI ops, invoke the handler.  */
+	    if (ginsn->scfi_ops)
+	      handle_scfi_dot_cfi (ginsn);
+	    break;
+
+	  default:
+	    /* No other GINSN_TYPE_* expected.  */
+	    as_bad (_("SCFI: bad ginsn for func '%s'"),
+		    S_GET_NAME (func));
+	    break;
+	}
+      ginsn = ginsn->next;
+    }
+  return 0;
+}
+
+#else
+
+int
+scfi_emit_dw2cfi (const symbolS *func ATTRIBUTE_UNUSED)
+{
+  as_bad (_("SCFI: unsupported for target"));
+  return 1;
+}
+
+int
+scfi_synthesize_dw2cfi (const symbolS *func ATTRIBUTE_UNUSED,
+			gcfgS *gcfg ATTRIBUTE_UNUSED,
+			gbbS *root_bb ATTRIBUTE_UNUSED)
+{
+  as_bad (_("SCFI: unsupported for target"));
+  return 1;
+}
+
+#endif  /* defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN).  */
diff --git a/gas/scfi.h b/gas/scfi.h
new file mode 100644
index 00000000000..07abe99ab27
--- /dev/null
+++ b/gas/scfi.h
@@ -0,0 +1,38 @@
+/* scfi.h - Support for synthesizing CFI for asm.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GAS, the GNU Assembler.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#ifndef SCFI_H
+#define SCFI_H
+
+#include "as.h"
+#include "ginsn.h"
+
+void scfi_ops_cleanup (scfi_opS **head);
+
+/* Some SCFI ops are not synthesized and are only added externally when parsing
+   the assembler input.  Two examples are CFI_label, and CFI_signal_frame.  */
+void scfi_op_add_cfi_label (ginsnS *ginsn, const char *name);
+void scfi_op_add_signal_frame (ginsnS *ginsn);
+
+int scfi_emit_dw2cfi (const symbolS *func);
+
+int scfi_synthesize_dw2cfi (const symbolS *func, gcfgS *gcfg, gbbS *root_bb);
+
+#endif /* SCFI_H.  */
diff --git a/gas/scfidw2gen.c b/gas/scfidw2gen.c
index 1b3fb158e20..ebf2d24dab2 100644
--- a/gas/scfidw2gen.c
+++ b/gas/scfidw2gen.c
@@ -19,6 +19,8 @@
    02110-1301, USA.  */
 
 #include "as.h"
+#include "ginsn.h"
+#include "scfi.h"
 #include "dw2gencfi.h"
 #include "subsegs.h"
 #include "scfidw2gen.h"
@@ -43,15 +45,33 @@ dot_scfi_ignore (int ignored ATTRIBUTE_UNUSED)
 static void
 scfi_process_cfi_label (void)
 {
-  /* To be implemented. */
-  return;
+  char *name;
+  ginsnS *ginsn;
+
+  name = read_symbol_name ();
+  if (name == NULL)
+    return;
+
+  /* Add a new ginsn.  */
+  ginsn = ginsn_new_phantom (symbol_temp_new_now ());
+  frch_ginsn_data_append (ginsn);
+
+  scfi_op_add_cfi_label (ginsn, name);
+  /* TODO.  */
+  // free (name);
+
+  demand_empty_rest_of_line ();
 }
 
 static void
 scfi_process_cfi_signal_frame (void)
 {
-  /* To be implemented.  */
-  return;
+  ginsnS *ginsn;
+
+  ginsn = ginsn_new_phantom (symbol_temp_new_now ());
+  frch_ginsn_data_append (ginsn);
+
+  scfi_op_add_signal_frame (ginsn);
 }
 
 static void
diff --git a/gas/subsegs.c b/gas/subsegs.c
index 42f42c5ffac..6ecfc378010 100644
--- a/gas/subsegs.c
+++ b/gas/subsegs.c
@@ -130,6 +130,7 @@ subseg_set_rest (segT seg, subsegT subseg)
       newP->frch_frag_now = frag_alloc (&newP->frch_obstack);
       newP->frch_frag_now->fr_type = rs_fill;
       newP->frch_cfi_data = NULL;
+      newP->frch_ginsn_data = NULL;
 
       newP->frch_root = newP->frch_last = newP->frch_frag_now;
 
diff --git a/gas/subsegs.h b/gas/subsegs.h
index 8de3950f427..d1e73febc81 100644
--- a/gas/subsegs.h
+++ b/gas/subsegs.h
@@ -40,6 +40,7 @@
 #include "obstack.h"
 
 struct frch_cfi_data;
+struct frch_ginsn_data;
 
 struct frchain			/* control building of a frag chain */
 {				/* FRCH = FRagment CHain control */
@@ -52,6 +53,7 @@ struct frchain			/* control building of a frag chain */
   struct obstack frch_obstack;	/* for objects in this frag chain */
   fragS *frch_frag_now;		/* frag_now for this subsegment */
   struct frch_cfi_data *frch_cfi_data;
+  struct frch_ginsn_data *frch_ginsn_data;
 };
 
 typedef struct frchain frchainS;
diff --git a/gas/symbols.c b/gas/symbols.c
index 10a3f50cf32..41f273c85ff 100644
--- a/gas/symbols.c
+++ b/gas/symbols.c
@@ -25,6 +25,7 @@
 #include "obstack.h"		/* For "symbols.h" */
 #include "subsegs.h"
 #include "write.h"
+#include "scfi.h"
 
 #include <limits.h>
 #ifndef CHAR_BIT
@@ -709,6 +710,8 @@ colon (/* Just seen "x:" - rattle symbols & frags.  */
 #ifdef obj_frob_label
   obj_frob_label (symbolP);
 #endif
+  if (flag_synth_cfi)
+    ginsn_frob_label (symbolP);
 
   return symbolP;
 }
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 13/16] gas: doc: update documentation for the new listing option
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
                   ` (11 preceding siblings ...)
  2024-01-11  7:48 ` [PATCH, V5 12/16] gas: synthesize CFI for hand-written asm Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 14/16] opcodes: i386-reg.tbl: Add a comment to reflect dependency on ordering Indu Bhagat
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[No changes since V2]

Add a new listing option, -i, to emit ginsn in the listing output.  We
may also emit other SCFI information if necessary in the future.

ginsn are most useful when seen alongside the assembly instructions.
Hence, they are emitted when the user includes the assembly instructions
in the listing output, i.e., "-ali=FILE".

gas/doc/:
	* as.texi: Add documentation for the new listing option, -i.
---
 gas/doc/as.texi | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index 370f40fcbae..50c05436141 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -226,7 +226,7 @@ gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}.
 @c to be limited to one line for the header.
 @smallexample
 @c man begin SYNOPSIS
-@value{AS} [@b{-a}[@b{cdghlns}][=@var{file}]]
+@value{AS} [@b{-a}[@b{cdghilns}][=@var{file}]]
  [@b{--alternate}]
  [@b{--compress-debug-sections}] [@b{--nocompress-debug-sections}]
  [@b{-D}]
@@ -673,7 +673,7 @@ gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}.
 @table @gcctabopt
 @include at-file.texi
 
-@item -a[cdghlmns]
+@item -a[cdghilmns]
 Turn on listings, in any of a variety of ways:
 
 @table @gcctabopt
@@ -692,6 +692,9 @@ include high-level source
 @item -al
 include assembly
 
+@item -ali
+include assembly with ginsn
+
 @item -am
 include macro expansions
 
@@ -2418,7 +2421,7 @@ assembler.)
 @c man end
 
 @menu
-* a::             -a[cdghlns] enable listings
+* a::             -a[cdghilns] enable listings
 * alternate::     --alternate enable alternate macro syntax
 * D::             -D for compatibility and debugging
 * f::             -f to work faster
@@ -2445,7 +2448,7 @@ assembler.)
 @end menu
 
 @node a
-@section Enable Listings: @option{-a[cdghlns]}
+@section Enable Listings: @option{-a[cdghilns]}
 
 @kindex -a
 @kindex -ac
@@ -2453,6 +2456,7 @@ assembler.)
 @kindex -ag
 @kindex -ah
 @kindex -al
+@kindex -ali
 @kindex -an
 @kindex -as
 @cindex listings, enabling
@@ -2462,7 +2466,9 @@ These options enable listing output from the assembler.  By itself,
 @samp{-a} requests high-level, assembly, and symbols listing.
 You can use other letters to select specific options for the list:
 @samp{-ah} requests a high-level language listing,
-@samp{-al} requests an output-program assembly listing, and
+@samp{-al} requests an output-program assembly listing,
+@samp{-ali} requests an output-program assembly listing along with the
+associated ginsn, and
 @samp{-as} requests a symbol table listing.
 High-level listings require that a compiler debugging option like
 @samp{-g} be used, and that assembly listings (@samp{-al}) be requested
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 14/16] opcodes: i386-reg.tbl: Add a comment to reflect dependency on ordering
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
                   ` (12 preceding siblings ...)
  2024-01-11  7:48 ` [PATCH, V5 13/16] gas: doc: update documentation for the new listing option Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  2024-01-11  7:59   ` Jan Beulich
  2024-01-11  7:48 ` [PATCH, V5 15/16] gas: testsuite: add an x86_64 testsuite for SCFI Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 16/16] gas/NEWS: announce the new SCFI command line option Indu Bhagat
  15 siblings, 1 reply; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[New in V3. No changes since V3]

The ginsn representation keeps the DWARF register number of the
operands.  The API ginsn_dw2_regnum relies on the the relative ordering
of these register entries in the table.  Add a comment to make it clear.

opcodes/
	* i386-reg.tbl: Add a comment.
---
 opcodes/i386-reg.tbl | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/opcodes/i386-reg.tbl b/opcodes/i386-reg.tbl
index 5b80ee6026e..f92315392b8 100644
--- a/opcodes/i386-reg.tbl
+++ b/opcodes/i386-reg.tbl
@@ -18,6 +18,9 @@
 // Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
 // 02110-1301, USA.
 
+// The code in gas backend for SCFI relies on the relative ordering
+// of 8 bit / 16 bit / 32 bit / 64 bit regs
+
 // 8 bit regs
 al, Class=Reg|Instance=Accum|Byte, 0, 0, Dw2Inval, Dw2Inval
 cl, Class=Reg|Instance=RegC|Byte, 0, 1, Dw2Inval, Dw2Inval
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 15/16] gas: testsuite: add an x86_64 testsuite for SCFI
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
                   ` (13 preceding siblings ...)
  2024-01-11  7:48 ` [PATCH, V5 14/16] opcodes: i386-reg.tbl: Add a comment to reflect dependency on ordering Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  2024-01-11  7:48 ` [PATCH, V5 16/16] gas/NEWS: announce the new SCFI command line option Indu Bhagat
  15 siblings, 0 replies; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[Changes from V4 to V5]
   - Addressed several review comments.
     + Fix indendation in ginsn-add-1.s
     + Consistent styling in testcases
     + Update commit log
     + Update comments in README
     + scfi-unsupported-1.s: Specify --32 and --x32 are not supported
     + ginsn-add-1.s: Add adc, {load} pseudo prefix op
     + Add more test instructions for RegIP in ginsn-dw2-regnum-1.s
     + Add more push, pop type insns
     + Add an .insn and check for not supported message in
       scfi-unsupported-insn-1.s
     + Disallow APX instructions
   - Use --scfi=experimental instead of --scfi or --scfi=all
[End of changes from V4 to V5]

[Changes from V3 to V4]
  - Forward and backward pass failure is an error (was warning in V2).
    Fix the tests.
  - Add new test ginsn-add-1.
  - Add new test scfi-unsupported-insn-1 which includes the new APX
    instructions (pop2, push2).
[End of changes from V3 to V4]

[Changes from V2 to V3]
  - Fix inconsistent indentation across tests.
  - Fixed some issues in scfi-x86.exp: add scfi-callee-saved-1 to the
    list (was missing earlier), removed redundant test scfi-ignore-1
  - Adjsuted tests for some rewording in the warning messages.
  - Use register names instead of numbers in some CFI directives.
  - Run each test with and without --scfi.  This will keep the CFI
    annotations tested.
  - Run scfi-unsupported-1 with --x32 as well.
  - Added more tests:
    + ginsn-dw2-regnum-1
    + ginsn-pop-1
    + ginsn-push-1
    + scfi-enter-1
    + scfi-cfi-sections-1, etc.
[End of changes from V2 to V3]

The testsuite for SCFI contains target-specific tests.

When a test is executed with --scfi=experimental command line option,
the CFI annotations in the test .s files are skipped altogether by the
GAS for processing.  The CFI directives in the input assembly files are,
however, validated by running the assembler one more time without
--scfi=experimental.

Some testcases are used to highlight those asm constructs that the SCFI
machinery in GAS currently does not support:

  - Only System V AMD64 ABI is supported for now. Using either --32 or
    --x32 with SCFI results in hard error.
    See scfi-unsupported-1.s.

  - Untraceable stack-pointer manipulation in function epilougue and prologue.
    See scfi-unsupported-2.s.

  - Using Dynamically Realigned Arguement Pointer (DRAP) register to
    realign the stack.  For SCFI, the CFA must be only REG_SP or REG_FP
    based.  See scfi-unsupported-drap-1.s

Some testcases are used to highlight some diagnostics that the SCFI
machinery in GAS currently issues, with an intent to help user correct
inadvertent errors in their hand-written asm.  An error is issued when
GAS finds that input asm is not amenable to correct CFI synthesis.

  - (#1) "Warning: SCFI: Asymetrical register restore"
  - (#2) "Error: SCFI: usage of REG_FP as scratch not supported"
  - (#3) "Error: SCFI: unsupported stack manipulation pattern"

In case of (#2) and (#3), SCFI generation is skipped for the respective
function.  Above is a subset of the warnings/errors implemented in the
code.

gas/testsuite/:
	* gas/scfi/README: New test.
	* gas/scfi/x86_64/ginsn-add-1.l: New test.
	* gas/scfi/x86_64/ginsn-add-1.s: New test.
	* gas/scfi/x86_64/ginsn-dw2-regnum-1.l: New test.
	* gas/scfi/x86_64/ginsn-dw2-regnum-1.s: New test.
	* gas/scfi/x86_64/ginsn-pop-1.l: New test.
	* gas/scfi/x86_64/ginsn-pop-1.s: New test.
	* gas/scfi/x86_64/ginsn-push-1.l: New test.
	* gas/scfi/x86_64/ginsn-push-1.s: New test.
	* gas/scfi/x86_64/scfi-add-1.d: New test.
	* gas/scfi/x86_64/scfi-add-1.l: New test.
	* gas/scfi/x86_64/scfi-add-1.s: New test.
	* gas/scfi/x86_64/scfi-add-2.d: New test.
	* gas/scfi/x86_64/scfi-add-2.l: New test.
	* gas/scfi/x86_64/scfi-add-2.s: New test.
	* gas/scfi/x86_64/scfi-asm-marker-1.d: New test.
	* gas/scfi/x86_64/scfi-asm-marker-1.l: New test.
	* gas/scfi/x86_64/scfi-asm-marker-1.s: New test.
	* gas/scfi/x86_64/scfi-asm-marker-2.d: New test.
	* gas/scfi/x86_64/scfi-asm-marker-2.l: New test.
	* gas/scfi/x86_64/scfi-asm-marker-2.s: New test.
	* gas/scfi/x86_64/scfi-asm-marker-3.d: New test.
	* gas/scfi/x86_64/scfi-asm-marker-3.l: New test.
	* gas/scfi/x86_64/scfi-asm-marker-3.s: New test.
	* gas/scfi/x86_64/scfi-bp-sp-1.d: New test.
	* gas/scfi/x86_64/scfi-bp-sp-1.l: New test.
	* gas/scfi/x86_64/scfi-bp-sp-1.s: New test.
	* gas/scfi/x86_64/scfi-bp-sp-2.d: New test.
	* gas/scfi/x86_64/scfi-bp-sp-2.l: New test.
	* gas/scfi/x86_64/scfi-bp-sp-2.s: New test.
	* gas/scfi/x86_64/scfi-callee-saved-1.d: New test.
	* gas/scfi/x86_64/scfi-callee-saved-1.l: New test.
	* gas/scfi/x86_64/scfi-callee-saved-1.s: New test.
	* gas/scfi/x86_64/scfi-callee-saved-2.d: New test.
	* gas/scfi/x86_64/scfi-callee-saved-2.l: New test.
	* gas/scfi/x86_64/scfi-callee-saved-2.s: New test.
	* gas/scfi/x86_64/scfi-callee-saved-3.d: New test.
	* gas/scfi/x86_64/scfi-callee-saved-3.l: New test.
	* gas/scfi/x86_64/scfi-callee-saved-3.s: New test.
	* gas/scfi/x86_64/scfi-callee-saved-4.d: New test.
	* gas/scfi/x86_64/scfi-callee-saved-4.l: New test.
	* gas/scfi/x86_64/scfi-callee-saved-4.s: New test.
	* gas/scfi/x86_64/scfi-cfg-1.d: New test.
	* gas/scfi/x86_64/scfi-cfg-1.l: New test.
	* gas/scfi/x86_64/scfi-cfg-1.s: New test.
	* gas/scfi/x86_64/scfi-cfg-2.d: New test.
	* gas/scfi/x86_64/scfi-cfg-2.l: New test.
	* gas/scfi/x86_64/scfi-cfg-2.s: New test.
	* gas/scfi/x86_64/scfi-cfi-label-1.d: New test.
	* gas/scfi/x86_64/scfi-cfi-label-1.l: New test.
	* gas/scfi/x86_64/scfi-cfi-label-1.s: New test.
	* gas/scfi/x86_64/scfi-cfi-sections-1.d: New test.
	* gas/scfi/x86_64/scfi-cfi-sections-1.l: New test.
	* gas/scfi/x86_64/scfi-cfi-sections-1.s: New test.
	* gas/scfi/x86_64/scfi-cofi-1.d: New test.
	* gas/scfi/x86_64/scfi-cofi-1.l: New test.
	* gas/scfi/x86_64/scfi-cofi-1.s: New test.
	* gas/scfi/x86_64/scfi-diag-1.l: New test.
	* gas/scfi/x86_64/scfi-diag-1.s: New test.
	* gas/scfi/x86_64/scfi-diag-2.l: New test.
	* gas/scfi/x86_64/scfi-diag-2.s: New test.
	* gas/scfi/x86_64/scfi-dyn-stack-1.d: New test.
	* gas/scfi/x86_64/scfi-dyn-stack-1.l: New test.
	* gas/scfi/x86_64/scfi-dyn-stack-1.s: New test.
	* gas/scfi/x86_64/scfi-enter-1.d: New test.
	* gas/scfi/x86_64/scfi-enter-1.l: New test.
	* gas/scfi/x86_64/scfi-enter-1.s: New test.
	* gas/scfi/x86_64/scfi-fp-diag-2.l: New test.
	* gas/scfi/x86_64/scfi-fp-diag-2.s: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-1.d: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-1.l: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-1.s: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-2.d: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-2.l: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-2.s: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-3.d: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-3.l: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-3.s: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-4.d: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-4.l: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-4.s: New test.
	* gas/scfi/x86_64/scfi-indirect-mov-5.s: New test.
	* gas/scfi/x86_64/scfi-lea-1.d: New test.
	* gas/scfi/x86_64/scfi-lea-1.l: New test.
	* gas/scfi/x86_64/scfi-lea-1.s: New test.
	* gas/scfi/x86_64/scfi-leave-1.d: New test.
	* gas/scfi/x86_64/scfi-leave-1.l: New test.
	* gas/scfi/x86_64/scfi-leave-1.s: New test.
	* gas/scfi/x86_64/scfi-pushq-1.d: New test.
	* gas/scfi/x86_64/scfi-pushq-1.l: New test.
	* gas/scfi/x86_64/scfi-pushq-1.s: New test.
	* gas/scfi/x86_64/scfi-pushsection-1.d: New test.
	* gas/scfi/x86_64/scfi-pushsection-1.l: New test.
	* gas/scfi/x86_64/scfi-pushsection-1.s: New test.
	* gas/scfi/x86_64/scfi-pushsection-2.d: New test.
	* gas/scfi/x86_64/scfi-pushsection-2.l: New test.
	* gas/scfi/x86_64/scfi-pushsection-2.s: New test.
	* gas/scfi/x86_64/scfi-selfalign-func-1.d: New test.
	* gas/scfi/x86_64/scfi-selfalign-func-1.l: New test.
	* gas/scfi/x86_64/scfi-selfalign-func-1.s: New test.
	* gas/scfi/x86_64/scfi-simple-1.d: New test.
	* gas/scfi/x86_64/scfi-simple-1.l: New test.
	* gas/scfi/x86_64/scfi-simple-1.s: New test.
	* gas/scfi/x86_64/scfi-simple-2.d: New test.
	* gas/scfi/x86_64/scfi-simple-2.l: New test.
	* gas/scfi/x86_64/scfi-simple-2.s: New test.
	* gas/scfi/x86_64/scfi-sub-1.d: New test.
	* gas/scfi/x86_64/scfi-sub-1.l: New test.
	* gas/scfi/x86_64/scfi-sub-1.s: New test.
	* gas/scfi/x86_64/scfi-sub-2.d: New test.
	* gas/scfi/x86_64/scfi-sub-2.l: New test.
	* gas/scfi/x86_64/scfi-sub-2.s: New test.
	* gas/scfi/x86_64/scfi-unsupported-1.l: New test.
	* gas/scfi/x86_64/scfi-unsupported-1.s: New test.
	* gas/scfi/x86_64/scfi-unsupported-2.l: New test.
	* gas/scfi/x86_64/scfi-unsupported-2.s: New test.
	* gas/scfi/x86_64/scfi-unsupported-3.l: New test.
	* gas/scfi/x86_64/scfi-unsupported-3.s: New test.
	* gas/scfi/x86_64/scfi-unsupported-4.l: New test.
	* gas/scfi/x86_64/scfi-unsupported-4.s: New test.
	* gas/scfi/x86_64/scfi-unsupported-cfg-1.l: New test.
	* gas/scfi/x86_64/scfi-unsupported-cfg-1.s: New test.
	* gas/scfi/x86_64/scfi-unsupported-cfg-2.l: New test.
	* gas/scfi/x86_64/scfi-unsupported-cfg-2.s: New test.
	* gas/scfi/x86_64/scfi-unsupported-drap-1.l: New test.
	* gas/scfi/x86_64/scfi-unsupported-drap-1.s: New test.
	* gas/scfi/x86_64/scfi-unsupported-insn-1.l: New test.
	* gas/scfi/x86_64/scfi-unsupported-insn-1.s: New test.
	* gas/scfi/x86_64/scfi-x86-64.exp: New file.
---
 gas/testsuite/gas/scfi/README                 |  16 +++
 gas/testsuite/gas/scfi/x86_64/ginsn-add-1.l   |  49 ++++++++
 gas/testsuite/gas/scfi/x86_64/ginsn-add-1.s   |  27 +++++
 .../gas/scfi/x86_64/ginsn-dw2-regnum-1.l      |  69 +++++++++++
 .../gas/scfi/x86_64/ginsn-dw2-regnum-1.s      |  33 +++++
 gas/testsuite/gas/scfi/x86_64/ginsn-pop-1.l   |  41 +++++++
 gas/testsuite/gas/scfi/x86_64/ginsn-pop-1.s   |  16 +++
 gas/testsuite/gas/scfi/x86_64/ginsn-push-1.l  |  44 +++++++
 gas/testsuite/gas/scfi/x86_64/ginsn-push-1.s  |  17 +++
 gas/testsuite/gas/scfi/x86_64/scfi-add-1.d    |  26 ++++
 gas/testsuite/gas/scfi/x86_64/scfi-add-1.l    |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-1.s    |  13 ++
 gas/testsuite/gas/scfi/x86_64/scfi-add-2.d    |  37 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-add-2.l    |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-2.s    |  48 ++++++++
 .../gas/scfi/x86_64/scfi-asm-marker-1.d       |  29 +++++
 .../gas/scfi/x86_64/scfi-asm-marker-1.l       |   3 +
 .../gas/scfi/x86_64/scfi-asm-marker-1.s       |  27 +++++
 .../gas/scfi/x86_64/scfi-asm-marker-2.d       |  25 ++++
 .../gas/scfi/x86_64/scfi-asm-marker-2.l       |   3 +
 .../gas/scfi/x86_64/scfi-asm-marker-2.s       |  11 ++
 .../gas/scfi/x86_64/scfi-asm-marker-3.d       |  32 +++++
 .../gas/scfi/x86_64/scfi-asm-marker-3.l       |   2 +
 .../gas/scfi/x86_64/scfi-asm-marker-3.s       |  38 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d  |  32 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l  |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s  |  21 ++++
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d  |  58 +++++++++
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l  |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s  |  52 ++++++++
 .../gas/scfi/x86_64/scfi-callee-saved-1.d     |  41 +++++++
 .../gas/scfi/x86_64/scfi-callee-saved-1.l     |   2 +
 .../gas/scfi/x86_64/scfi-callee-saved-1.s     |  25 ++++
 .../gas/scfi/x86_64/scfi-callee-saved-2.d     |  42 +++++++
 .../gas/scfi/x86_64/scfi-callee-saved-2.l     |   2 +
 .../gas/scfi/x86_64/scfi-callee-saved-2.s     |  40 +++++++
 .../gas/scfi/x86_64/scfi-callee-saved-3.d     |  43 +++++++
 .../gas/scfi/x86_64/scfi-callee-saved-3.l     |   3 +
 .../gas/scfi/x86_64/scfi-callee-saved-3.s     |  39 ++++++
 .../gas/scfi/x86_64/scfi-callee-saved-4.d     |  41 +++++++
 .../gas/scfi/x86_64/scfi-callee-saved-4.l     |   3 +
 .../gas/scfi/x86_64/scfi-callee-saved-4.s     |  55 +++++++++
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d    |  37 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l    |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s    |  47 ++++++++
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d    |  29 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l    |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.s    |  21 ++++
 .../gas/scfi/x86_64/scfi-cfi-label-1.d        |  38 ++++++
 .../gas/scfi/x86_64/scfi-cfi-label-1.l        |   2 +
 .../gas/scfi/x86_64/scfi-cfi-label-1.s        |  19 +++
 .../gas/scfi/x86_64/scfi-cfi-sections-1.d     |  24 ++++
 .../gas/scfi/x86_64/scfi-cfi-sections-1.l     |   2 +
 .../gas/scfi/x86_64/scfi-cfi-sections-1.s     |  22 ++++
 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d   |   5 +
 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l   |   3 +
 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s   |  24 ++++
 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l   |   4 +
 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s   |  22 ++++
 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l   |   4 +
 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s   |  28 +++++
 .../gas/scfi/x86_64/scfi-dyn-stack-1.d        |  24 ++++
 .../gas/scfi/x86_64/scfi-dyn-stack-1.l        |   2 +
 .../gas/scfi/x86_64/scfi-dyn-stack-1.s        |  50 ++++++++
 gas/testsuite/gas/scfi/x86_64/scfi-enter-1.d  |  36 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-enter-1.l  |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-enter-1.s  |  24 ++++
 .../gas/scfi/x86_64/scfi-fp-diag-2.l          |   3 +
 .../gas/scfi/x86_64/scfi-fp-diag-2.s          |  55 +++++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-1.d     |  52 ++++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-1.l     |   2 +
 .../gas/scfi/x86_64/scfi-indirect-mov-1.s     |  48 ++++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-2.d     |  42 +++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-2.l     |   2 +
 .../gas/scfi/x86_64/scfi-indirect-mov-2.s     |  38 ++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-3.d     |  42 +++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-3.l     |   2 +
 .../gas/scfi/x86_64/scfi-indirect-mov-3.s     |  38 ++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-4.d     |  64 ++++++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-4.l     |   3 +
 .../gas/scfi/x86_64/scfi-indirect-mov-4.s     |  68 +++++++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-5.s     |  35 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d    |  38 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l    |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s    |  39 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d  |  37 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l  |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s  |  25 ++++
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d  |  36 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l  |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s  |  23 ++++
 .../gas/scfi/x86_64/scfi-pushsection-1.d      |  43 +++++++
 .../gas/scfi/x86_64/scfi-pushsection-1.l      |   2 +
 .../gas/scfi/x86_64/scfi-pushsection-1.s      |  40 +++++++
 .../gas/scfi/x86_64/scfi-pushsection-2.d      |  40 +++++++
 .../gas/scfi/x86_64/scfi-pushsection-2.l      |   2 +
 .../gas/scfi/x86_64/scfi-pushsection-2.s      |  40 +++++++
 .../gas/scfi/x86_64/scfi-selfalign-func-1.d   |  32 +++++
 .../gas/scfi/x86_64/scfi-selfalign-func-1.l   |   2 +
 .../gas/scfi/x86_64/scfi-selfalign-func-1.s   |  36 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d |  27 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.s |  15 +++
 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d |  31 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.s |  16 +++
 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d    |  26 ++++
 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l    |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.s    |  12 ++
 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d    |  32 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l    |   2 +
 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.s    |  29 +++++
 .../gas/scfi/x86_64/scfi-unsupported-1.l      |   2 +
 .../gas/scfi/x86_64/scfi-unsupported-1.s      |   9 ++
 .../gas/scfi/x86_64/scfi-unsupported-2.l      |   3 +
 .../gas/scfi/x86_64/scfi-unsupported-2.s      |  13 ++
 .../gas/scfi/x86_64/scfi-unsupported-3.l      |   3 +
 .../gas/scfi/x86_64/scfi-unsupported-3.s      |  13 ++
 .../gas/scfi/x86_64/scfi-unsupported-4.l      |   4 +
 .../gas/scfi/x86_64/scfi-unsupported-4.s      |  22 ++++
 .../gas/scfi/x86_64/scfi-unsupported-cfg-1.l  |   3 +
 .../gas/scfi/x86_64/scfi-unsupported-cfg-1.s  |  52 ++++++++
 .../gas/scfi/x86_64/scfi-unsupported-cfg-2.l  |   4 +
 .../gas/scfi/x86_64/scfi-unsupported-cfg-2.s  |  14 +++
 .../gas/scfi/x86_64/scfi-unsupported-drap-1.l |   4 +
 .../gas/scfi/x86_64/scfi-unsupported-drap-1.s |  75 ++++++++++++
 .../gas/scfi/x86_64/scfi-unsupported-insn-1.l |   7 ++
 .../gas/scfi/x86_64/scfi-unsupported-insn-1.s |  16 +++
 gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp | 113 ++++++++++++++++++
 129 files changed, 3074 insertions(+)
 create mode 100644 gas/testsuite/gas/scfi/README
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-add-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-add-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-dw2-regnum-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-dw2-regnum-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-pop-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-pop-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-push-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/ginsn-push-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-add-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-sections-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-sections-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cfi-sections-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-enter-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-enter-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-enter-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-5.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-simple-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-sub-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-2.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-2.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-insn-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-unsupported-insn-1.s
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp

diff --git a/gas/testsuite/gas/scfi/README b/gas/testsuite/gas/scfi/README
new file mode 100644
index 00000000000..91191634769
--- /dev/null
+++ b/gas/testsuite/gas/scfi/README
@@ -0,0 +1,16 @@
+Notes on the SCFI testsuite in GAS:
+
+* At this time, SCFI machinery is only supported for x86_64.
+
+* When adding more tests, please keep CFI annotations updated in the .s files.
+  Ideally the test should be run with and without --scfi (as is done currently
+  for the core SCFI tests); Such workflow ensures sanity checking of the CFI
+  annotations. 
+
+* Note that GAS issues a warning:
+    "Warning: --scfi=all ignores some user-specified CFI directive"
+  when it encounters CFI directives in the input assembly and --scfi (=all) is
+  active.  To bypass the noise from this warning, while keeping the testcases
+  useful/understandable, most tests are run twice. E.g.,
+     - run_dump_test "scfi-cfi-add-1" // ignores warnings
+     - run_list_test "scfi-cfi-add-1" "--scfi --warn" // tests warnings
diff --git a/gas/testsuite/gas/scfi/x86_64/ginsn-add-1.l b/gas/testsuite/gas/scfi/x86_64/ginsn-add-1.l
new file mode 100644
index 00000000000..ff078d4d883
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/ginsn-add-1.l
@@ -0,0 +1,49 @@
+GAS LISTING .*
+
+
+   1              	## Testcase with a variety of add.
+   2              	## Some add insns valid in 64-bit mode may not be processed for SCFI.
+   3              		.text
+   4              		.globl foo
+   5              		.type foo, @function
+   5              	ginsn: SYM FUNC_BEGIN
+   6              	foo:
+   6              	ginsn: SYM foo
+   7 0000 54       		push %rsp
+   7              	ginsn: SUB %r7, 8, %r7
+   7              	ginsn: STORE %r7, \[%r7\+0\]
+   8 0001 4889E5   		movq %rsp, %rbp
+   8              	ginsn: MOV %r7, %r6
+   9              	
+  10 0004 48010425 		addq %rax, symbol
+  10      00000000 
+  11 000c 03042500 		add symbol, %eax
+  11      000000
+  12 0013 670320   		add \(%eax\), %esp
+  12              	ginsn: ADD \[%r0\+0\], %r7, %r7
+  13 0016 67012405 		add %esp, \(,%eax\)
+  13      00000000 
+  14 001e 67032405 		add \(,%eax\), %esp
+  14      00000000 
+  14              	ginsn: ADD \[%r0\+0\], %r7, %r7
+  15              	
+  16 0026 4801C3   		addq %rax, %rbx
+  16              	ginsn: ADD %r0, %r3, %r3
+  17 0029 01C3     		add %eax, %ebx
+  17              	ginsn: ADD %r0, %r3, %r3
+  18              	
+  19 002b 4883D408 		adc \$8, %rsp
+  19              	ginsn: OTH 0, 0, %r7
+  20              	
+  21 002f 488345F0 		addq \$1, -16\(%rbp\)
+  21      01
+  22              	
+  23 0034 4803D8   		\{load\} addq %rax, %rbx
+  23              	ginsn: ADD %r0, %r3, %r3
+  24              	
+  25 0037 C3       		ret
+  25              	ginsn: RET
+  26              	.LFE0:
+  26              	ginsn: SYM .LFE0
+  27              		.size foo, .-foo
+  27              	ginsn: SYM FUNC_END
diff --git a/gas/testsuite/gas/scfi/x86_64/ginsn-add-1.s b/gas/testsuite/gas/scfi/x86_64/ginsn-add-1.s
new file mode 100644
index 00000000000..f9398369531
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/ginsn-add-1.s
@@ -0,0 +1,27 @@
+## Testcase with a variety of add.
+## Some add insns valid in 64-bit mode may not be processed for SCFI.
+	.text
+	.globl foo
+	.type foo, @function
+foo:
+	push %rsp
+	movq %rsp, %rbp
+
+	addq %rax, symbol
+	add symbol, %eax
+	add (%eax), %esp
+	add %esp, (,%eax)
+	add (,%eax), %esp
+
+	addq %rax, %rbx
+	add %eax, %ebx
+
+	adc $8, %rsp
+
+	addq $1, -16(%rbp)
+
+	{load} addq %rax, %rbx
+
+	ret
+.LFE0:
+	.size foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/ginsn-dw2-regnum-1.l b/gas/testsuite/gas/scfi/x86_64/ginsn-dw2-regnum-1.l
new file mode 100644
index 00000000000..f242e1f2171
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/ginsn-dw2-regnum-1.l
@@ -0,0 +1,69 @@
+GAS LISTING .*
+
+
+.*# Testcase for DWARF regnum ginsn API
+   2              		.text
+   3              		.globl  foo
+   4              		.type   foo, @function
+   4              	ginsn: SYM FUNC_BEGIN
+   5              	foo:
+   5              	ginsn: SYM foo
+   6 0000 0408     		add    \$8, %al
+   7 0002 80C108   		add    \$8, %cl
+   7              	ginsn: ADD %r2, 8, %r2
+   8 0005 80C208   		add    \$8, %dl
+   8              	ginsn: ADD %r1, 8, %r1
+   9 0008 80C408   		add    \$8, %ah
+   9              	ginsn: ADD %r0, 8, %r0
+  10 000b 80C508   		add    \$8, %ch
+  10              	ginsn: ADD %r2, 8, %r2
+  11 000e 80C608   		add    \$8, %dh
+  11              	ginsn: ADD %r1, 8, %r1
+  12 0011 80C708   		add    \$8, %bh
+  12              	ginsn: ADD %r3, 8, %r3
+  13              	
+  14 0014 4080C008 		add    \$8, %axl
+  14              	ginsn: ADD %r0, 8, %r0
+  15 0018 4080C408 		add    \$8, %spl
+  15              	ginsn: ADD %r7, 8, %r7
+  16              	
+  17 001c 6683C008 		add    \$8, %ax
+  17              	ginsn: ADD %r0, 8, %r0
+  18 0020 664183C0 		add    \$8, %r8w
+  18      08
+  18              	ginsn: ADD %r8, 8, %r8
+  19 0025 6683C408 		add    \$8, %sp
+  19              	ginsn: ADD %r7, 8, %r7
+  20              	
+  21 0029 83C008   		add    \$8, %eax
+  21              	ginsn: ADD %r0, 8, %r0
+  22 002c 4183C008 		add    \$8, %r8d
+  22              	ginsn: ADD %r8, 8, %r8
+  23 0030 81C40040 		add    \$16384, %esp
+  23      0000
+  23              	ginsn: ADD %r7, 16384, %r7
+  24              	
+  25 0036 4883C508 		add    \$8, %rbp
+  25              	ginsn: ADD %r6, 8, %r6
+  26              	
+  27 003a 488D05FE 		lea    -0x2\(%rip\), %rax
+  27      FFFFFF
+  27              	ginsn: ADD %r4, -2, %r0
+  28 0041 67488905 		mov    %rax, 0x2\(%eip\)
+  28      02000000 
+  28              	ginsn: MOV %r0, \[%r4\+2\]
+  29 0049 67488B05 		mov    -0x2\(%eip\), %rax
+  29      FEFFFFFF 
+  29              	ginsn: MOV \[%r4\+-2\], %r0
+  30              	
+  31 0051 C3       		ret
+  31              	ginsn: RET
+\fGAS LISTING .*
+
+
+  32              	.LFE0:
+  32              	ginsn: SYM .LFE0
+  33              		.size   foo, .-foo
+  33              	ginsn: SYM FUNC_END
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/ginsn-dw2-regnum-1.s b/gas/testsuite/gas/scfi/x86_64/ginsn-dw2-regnum-1.s
new file mode 100644
index 00000000000..383c60f635b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/ginsn-dw2-regnum-1.s
@@ -0,0 +1,33 @@
+# Testcase for DWARF regnum ginsn API
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	add    $8, %al
+	add    $8, %cl
+	add    $8, %dl
+	add    $8, %ah
+	add    $8, %ch
+	add    $8, %dh
+	add    $8, %bh
+
+	add    $8, %axl
+	add    $8, %spl
+
+	add    $8, %ax
+	add    $8, %r8w
+	add    $8, %sp
+
+	add    $8, %eax
+	add    $8, %r8d
+	add    $16384, %esp
+
+	add    $8, %rbp
+
+	lea    -0x2(%rip), %rax
+	mov    %rax, 0x2(%eip)
+	mov    -0x2(%eip), %rax
+
+	ret
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/ginsn-pop-1.l b/gas/testsuite/gas/scfi/x86_64/ginsn-pop-1.l
new file mode 100644
index 00000000000..f1161db55fa
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/ginsn-pop-1.l
@@ -0,0 +1,41 @@
+GAS LISTING .*
+
+
+   1              	## Testcase with a variety of pop.
+   2              	## all pop insns valid in 64-bit mode must be processed for SCFI.
+   3              		.text
+   4              		.globl  foo
+   5              		.type   foo, @function
+   5              	ginsn: SYM FUNC_BEGIN
+   6              	foo:
+   6              	ginsn: SYM foo
+   7 0000 660FA1   		popw    %fs
+   7              	ginsn: LOAD \[%r7\+0\], %r54
+   7              	ginsn: ADD %r7, 2, %r7
+   8 0003 660FA9   		popw    %gs
+   8              	ginsn: LOAD \[%r7\+0\], %r55
+   8              	ginsn: ADD %r7, 2, %r7
+   9 0006 669D     		popfw
+   9              	ginsn: LOAD \[%r7\+0\], %r49
+   9              	ginsn: ADD %r7, 2, %r7
+  10 0008 66418F42 		popw    -8\(%r10\)
+  10      F8
+  10              	ginsn: LOAD \[%r7\+0\], \[%r10\+0\]
+  10              	ginsn: ADD %r7, 2, %r7
+  11 000d 428F0415 		popq    -8\(,%r10\)
+  11      F8FFFFFF 
+  11              	ginsn: LOAD \[%r7\+0\], \[%r10\+0\]
+  11              	ginsn: ADD %r7, 8, %r7
+  12 0015 8F042500 		pop     symbol
+  12      000000
+  12              	ginsn: LOAD \[%r7\+0\], \[%r4\+0\]
+  12              	ginsn: ADD %r7, 8, %r7
+  13 001c 58       		popq    %rax
+  13              	ginsn: LOAD \[%r7\+0\], %r0
+  13              	ginsn: ADD %r7, 8, %r7
+  14 001d C3       		ret
+  14              	ginsn: RET
+  15              	.LFE0:
+  15              	ginsn: SYM .LFE0
+  16              		.size   foo, .-foo
+  16              	ginsn: SYM FUNC_END
diff --git a/gas/testsuite/gas/scfi/x86_64/ginsn-pop-1.s b/gas/testsuite/gas/scfi/x86_64/ginsn-pop-1.s
new file mode 100644
index 00000000000..30ec3ed2414
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/ginsn-pop-1.s
@@ -0,0 +1,16 @@
+## Testcase with a variety of pop.
+## all pop insns valid in 64-bit mode must be processed for SCFI.
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	popw    %fs
+	popw    %gs
+	popfw
+	popw    -8(%r10)
+	popq    -8(,%r10)
+	pop     symbol
+	popq    %rax
+	ret
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/ginsn-push-1.l b/gas/testsuite/gas/scfi/x86_64/ginsn-push-1.l
new file mode 100644
index 00000000000..cd49f8e18d1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/ginsn-push-1.l
@@ -0,0 +1,44 @@
+GAS LISTING .*
+
+
+   1              	## Testcase with a variety of push.
+   2              	## all push insns valid in 64-bit mode must be processed for SCFI.
+   3              		.text
+   4              		.globl  foo
+   5              		.type   foo, @function
+   5              	ginsn: SYM FUNC_BEGIN
+   6              	foo:
+   6              	ginsn: SYM foo
+   7 0000 660FA0   		pushw   %fs
+   7              	ginsn: SUB %r7, 2, %r7
+   7              	ginsn: STORE %r54, \[%r7\+0\]
+   8 0003 660FA8   		pushw   %gs
+   8              	ginsn: SUB %r7, 2, %r7
+   8              	ginsn: STORE %r55, \[%r7\+0\]
+   9 0006 666A28   		pushw   \$40
+   9              	ginsn: SUB %r7, 2, %r7
+   9              	ginsn: STORE 0, \[%r7\+0\]
+  10 0009 6641FF72 		pushw   -8\(%r10\)
+  10      F8
+  10              	ginsn: SUB %r7, 2, %r7
+  10              	ginsn: STORE \[%r10\+0\], \[%r7\+0\]
+  11 000e 42FF3415 		pushq   -8\(,%r10\)
+  11      F8FFFFFF 
+  11              	ginsn: SUB %r7, 8, %r7
+  11              	ginsn: STORE \[%r10\+0\], \[%r7\+0\]
+  12 0016 669C     		pushfw
+  12              	ginsn: SUB %r7, 2, %r7
+  12              	ginsn: STORE %r49, \[%r7\+0\]
+  13 0018 FF342500 		push    symbol
+  13      000000
+  13              	ginsn: SUB %r7, 8, %r7
+  13              	ginsn: STORE \[%r4\+0\], \[%r7\+0\]
+  14 001f 50       		push    %rax
+  14              	ginsn: SUB %r7, 8, %r7
+  14              	ginsn: STORE %r0, \[%r7\+0\]
+  15 0020 C3       		ret
+  15              	ginsn: RET
+  16              	.LFE0:
+  16              	ginsn: SYM .LFE0
+  17              		.size   foo, .-foo
+  17              	ginsn: SYM FUNC_END
diff --git a/gas/testsuite/gas/scfi/x86_64/ginsn-push-1.s b/gas/testsuite/gas/scfi/x86_64/ginsn-push-1.s
new file mode 100644
index 00000000000..bc88b8a5c31
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/ginsn-push-1.s
@@ -0,0 +1,17 @@
+## Testcase with a variety of push.
+## all push insns valid in 64-bit mode must be processed for SCFI.
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	pushw   %fs
+	pushw   %gs
+	pushw   $40
+	pushw   -8(%r10)
+	pushq   -8(,%r10)
+	pushfw
+	push    symbol
+	push    %rax
+	ret
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-add-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-add-1.d
new file mode 100644
index 00000000000..383acf5bf66
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-1.d
@@ -0,0 +1,26 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for add insn 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0014 0+001c FDE cie=0+0000 pc=0+0000..0+0005
+  DW_CFA_advance_loc: 4 to 0+0004
+  DW_CFA_def_cfa_offset: 0
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-add-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-add-1.l
new file mode 100644
index 00000000000..bcf3095a9bb
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-add-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-add-1.s
new file mode 100644
index 00000000000..d29c444f41c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-1.s
@@ -0,0 +1,13 @@
+# Testcase for add instruction.
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	addq    $8, %rsp
+	.cfi_def_cfa_offset 0
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-add-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.d
new file mode 100644
index 00000000000..738ddef9c89
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.d
@@ -0,0 +1,37 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for add insn 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0024 0+001c FDE cie=0+0000 pc=0+0000..0+0035
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 2 to 0+0006
+  DW_CFA_offset: r12 \(r12\) at cfa-24
+  DW_CFA_advance_loc: 45 to 0+0033
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_advance_loc: 1 to 0+0034
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-add-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.l
new file mode 100644
index 00000000000..8523c720565
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*14: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-add-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.s
new file mode 100644
index 00000000000..376fab2871e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.s
@@ -0,0 +1,48 @@
+	.section        .rodata
+	.type   simd_cmp_op, @object
+	.size   simd_cmp_op, 8
+simd_cmp_op:
+	.long   2
+	.zero   4
+
+# Testcase for add instruction.
+# add reg, reg instruction
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	pushq   %r12
+	.cfi_offset %r12, -24
+	mov     %rsp, %r12
+# Stack manipulation is permitted if the base register for
+# tracking CFA has been changed to FP.
+	addq    %rdx, %rsp
+	addq    %rsp, %rax
+# Some add instructions may access the stack indirectly.  Such
+# accesses do not make REG_FP untraceable.
+	addl    %eax, -84(%rbp)
+# Other kind of add instructions should not error out in the
+# x86_64 -> ginsn translator
+	addq    $simd_cmp_op+8, %rdx
+	addq    $1, symbol
+	addl    %edx, -32(%rsp)
+	addl    $1, fb_low_counter(,%rbx,4)
+	mov     %r12, %rsp
+# Popping a callee-saved register.
+# RSP must be traceable.
+	pop     %r12
+	.cfi_restore %r12
+	leave
+	.cfi_def_cfa_register %rsp
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.d
new file mode 100644
index 00000000000..1af1eb88f72
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.d
@@ -0,0 +1,29 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for demarcated code blocks 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+0010 0000001c FDE cie=00000000 pc=0+0000..0+000f
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
+0000002c 0+0010 00000030 FDE cie=00000000 pc=0+0000..0+0006
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.l
new file mode 100644
index 00000000000..322191f02a6
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*8: Warning: SCFI ignores most user-specified CFI directives
+.*10: Warning: missing label '.L3' in func 'foo' may result in imprecise cfg
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.s
new file mode 100644
index 00000000000..8a98d1edf1c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.s
@@ -0,0 +1,27 @@
+# Testcase where a user may define hot and cold areas of function
+# Note how the .type, and .size directives may be placed differently
+# than a regular function.
+
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	testl   %edi, %edi
+	je      .L3
+	movl    b(%rip), %eax
+	ret
+	.cfi_endproc
+	.section        .text.unlikely
+	.cfi_startproc
+	.type   foo.cold, @function
+foo.cold:
+.L3:
+	pushq   %rax
+	.cfi_def_cfa_offset 16
+	call    abort
+	.cfi_endproc
+.LFE11:
+	.text
+	.size   foo, .-foo
+	.section        .text.unlikely
+	.size   foo.cold, .-foo.cold
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.d
new file mode 100644
index 00000000000..1199e12091b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.d
@@ -0,0 +1,25 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for demarcated code blocks 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+0014 0000001c FDE cie=00000000 pc=0+0000..0+000f
+  DW_CFA_nop
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.l
new file mode 100644
index 00000000000..f8c42ffd51a
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*6: Warning: SCFI ignores most user-specified CFI directives
+.*8: Warning: missing label '.L3' in func 'foo' may result in imprecise cfg
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.s
new file mode 100644
index 00000000000..2046b7e4078
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.s
@@ -0,0 +1,11 @@
+# A programmer may not bother to set the size of the 
+# function symbols via an explicit .size directive.
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	testl   %edi, %edi
+	je      .L3
+	movl    b(%rip), %eax
+	ret
+	.cfi_endproc
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.d b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.d
new file mode 100644
index 00000000000..79b7619abe2
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.d
@@ -0,0 +1,32 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for demarcated code blocks 3
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+001c 0000001c FDE cie=00000000 pc=0+0000..0+0035
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 48 to 0+0034
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l
new file mode 100644
index 00000000000..6ec24387b8c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*7: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.s b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.s
new file mode 100644
index 00000000000..692601dbd4b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.s
@@ -0,0 +1,38 @@
+# Testcase where the input may have interleaved sections,
+# possibly even text and data.
+	.globl  main
+	.type   main, @function
+main:
+.LFB1:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq    $16, %rsp
+	movl    $17, %esi
+	movl    $5, %edi
+	call    add
+	.section        .rodata
+	.align 16
+	.type   __test_obj.0, @object
+	.size   __test_obj.0, 24
+__test_obj.0:
+	.string "test_elf_objs_in_rodata"
+.LC0:
+	.string "the result is = %d\n"
+	.text
+	movl    %eax, -4(%rbp)
+	movl    -4(%rbp), %eax
+	movl    %eax, %esi
+	movl    $.LC0, %edi
+	movl    $0, %eax
+	call    printf
+	movl    $0, %eax
+	leave
+	.cfi_def_cfa_register %rsp
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d
new file mode 100644
index 00000000000..10a3c392e59
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d
@@ -0,0 +1,32 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for SP/FP based CFA switching 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+001c 0+001c FDE cie=0+0000 pc=0+0000..0+000c
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 6 to 0+000a
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_advance_loc: 1 to 0+000b
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l
new file mode 100644
index 00000000000..bcf3095a9bb
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s
new file mode 100644
index 00000000000..f827f8d440f
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s
@@ -0,0 +1,21 @@
+# Testcase for switching between sp/fp based CFA.
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	addq    %rax, %rdi
+	mov     %rbp, %rsp
+	.cfi_def_cfa_register %rsp
+	pop     %rbp
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d
new file mode 100644
index 00000000000..da44fc8f8a0
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d
@@ -0,0 +1,58 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for SP/FP based CFA switching 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+0044 0000001c FDE cie=00000000 pc=0+0000..0+0021
+  DW_CFA_advance_loc: 2 to 0+0002
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r14 \(r14\) at cfa-16
+  DW_CFA_advance_loc: 2 to 0+0004
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_offset: r13 \(r13\) at cfa-24
+  DW_CFA_advance_loc: 2 to 0+0006
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_offset: r12 \(r12\) at cfa-32
+  DW_CFA_advance_loc: 1 to 0+0007
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_offset: r6 \(rbp\) at cfa-40
+  DW_CFA_advance_loc: 1 to 0+0008
+  DW_CFA_def_cfa_offset: 48
+  DW_CFA_offset: r3 \(rbx\) at cfa-48
+  DW_CFA_advance_loc: 7 to 0+000f
+  DW_CFA_def_cfa_offset: 80
+  DW_CFA_advance_loc: 3 to 0+0012
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 7 to 0+0019
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_advance_loc: 1 to 0+001a
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 2 to 0+001c
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 2 to 0+001e
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 2 to 0+0020
+  DW_CFA_restore: r14 \(r14\)
+  DW_CFA_def_cfa_offset: 8
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l
new file mode 100644
index 00000000000..23ca73422b5
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*9: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s
new file mode 100644
index 00000000000..afc4313427e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s
@@ -0,0 +1,52 @@
+# Testcase for switching between sp/fp based CFA.
+# Although there is stack usage between push %rbp and mov %rsp, %rbp,
+# this is a valid ABI/calling convention complaint pattern; It ought to
+# work for SCFI.
+	.text
+	.globl   foo
+	.type    foo, @function
+foo:
+	.cfi_startproc
+	pushq   %r14
+	.cfi_def_cfa_offset 16
+	.cfi_offset %r14, -16
+	pushq   %r13
+	.cfi_def_cfa_offset 24
+	.cfi_offset %r13, -24
+	pushq   %r12
+	.cfi_def_cfa_offset 32
+	.cfi_offset %r12, -32
+	pushq   %rbp
+	.cfi_def_cfa_offset 40
+	.cfi_offset %rbp, -40
+	pushq   %rbx
+	.cfi_def_cfa_offset 48
+	.cfi_offset %rbx, -48
+	movq    %rdi, %rbx
+	subq    $32, %rsp
+	.cfi_def_cfa_offset 80
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	xorl    %eax, %eax
+	addq    $32, %rsp
+	popq    %rbx
+	.cfi_restore %rbx
+# The SCFI machinery must be able to figure out the offset for CFA
+# as it switches back to REG_SP based tracking after this instruction.
+	popq    %rbp
+	.cfi_def_cfa_register %rsp
+	.cfi_def_cfa_offset 40
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 32
+	popq    %r12
+	.cfi_restore %r12
+	.cfi_def_cfa_offset 24
+	popq    %r13
+	.cfi_restore %r13
+	.cfi_def_cfa_offset 16
+	popq    %r14
+	.cfi_restore %r14
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.d
new file mode 100644
index 00000000000..06e7eb6e3b8
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.d
@@ -0,0 +1,41 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: SCFI for callee-saved registers 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0002c 0+0001c FDE cie=0+0000 pc=0+0000..0+0007
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 1 to 0+0002
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_offset: r3 \(rbx\) at cfa-24
+  DW_CFA_advance_loc: 1 to 0+0003
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_offset: r6 \(rbp\) at cfa-32
+  DW_CFA_advance_loc: 1 to 0+0004
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 1 to 0+0005
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 1 to 0+0006
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.l
new file mode 100644
index 00000000000..abca835a642
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*5: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.s
new file mode 100644
index 00000000000..e1c5b70995c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.s
@@ -0,0 +1,25 @@
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	pushq   %rax
+	.cfi_def_cfa_offset 16
+	push    %rbx
+	.cfi_def_cfa_offset 24
+	.cfi_offset %rbx, -24
+	pushq   %rbp
+	.cfi_def_cfa_offset 32
+	.cfi_offset %rbp, -32
+	popq    %rbp
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 24
+	popq    %rbx
+	.cfi_restore %rbx
+	.cfi_def_cfa_offset 16
+	popq    %rax
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.d
new file mode 100644
index 00000000000..c6fe36bca43
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.d
@@ -0,0 +1,42 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: SCFI for callee-saved registers 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+002c 0000001c FDE cie=00000000 pc=0+0000..0+0017
+  DW_CFA_advance_loc: 2 to 0+0002
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r12 \(r12\) at cfa-16
+  DW_CFA_advance_loc: 2 to 0+0004
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_offset: r13 \(r13\) at cfa-24
+  DW_CFA_advance_loc: 9 to 0+000d
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 1 to 0+000e
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_advance_loc: 4 to 0+0012
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 2 to 0+0014
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 2 to 0+0016
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.l
new file mode 100644
index 00000000000..5ff6048bef0
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*8: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.s
new file mode 100644
index 00000000000..cac4700d35e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.s
@@ -0,0 +1,40 @@
+# Testcase for save reg ops for callee-saved registers
+# These latter two pushq's of callee-saved regs must NOT generate
+# .cfi_offset.
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	pushq   %r12
+	.cfi_def_cfa_offset 16
+	.cfi_offset %r12, -16
+	pushq   %r13
+	.cfi_def_cfa_offset 24
+	.cfi_offset %r13, -24
+# The function may use callee-saved registers for its use, and may even
+# chose to spill them to stack if necessary.
+	addq    %rax, %r13
+	subq    $8, %r13
+# These two pushq's of callee-saved regs must NOT generate
+# .cfi_offset.
+	pushq   %r13
+	.cfi_def_cfa_offset 32
+	pushq   %rax
+	.cfi_def_cfa_offset 40
+# Adjust the REG_SP to get rid of local stack usage.
+	addq    $16, %rsp
+	.cfi_def_cfa_offset 24
+# The SCFI machinery keeps track of where the callee-saved registers
+# are on the stack.  It generates a restore operation if the stack
+# offsets match.
+	popq    %r13
+	.cfi_restore %r13
+	.cfi_def_cfa_offset 16
+	popq    %r12
+	.cfi_restore %r12
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.d b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.d
new file mode 100644
index 00000000000..9720a93caa3
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.d
@@ -0,0 +1,43 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: SCFI for callee-saved registers 3
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+002c 0000001c FDE cie=00000000 pc=0+0000..0+0016
+  DW_CFA_advance_loc: 2 to 0+0002
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r12 \(r12\) at cfa-16
+  DW_CFA_advance_loc: 2 to 0+0004
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_offset: r13 \(r13\) at cfa-24
+  DW_CFA_advance_loc: 9 to 0+000d
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 1 to 0+000e
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_advance_loc: 1 to 0+000f
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 2 to 0+0011
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 2 to 0+0013
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 2 to 0+0015
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.l b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.l
new file mode 100644
index 00000000000..71559d4ea0e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*6: Warning: SCFI ignores most user-specified CFI directives
+.*25: Warning: SCFI: asymetrical register restore
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.s b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.s
new file mode 100644
index 00000000000..e7015fae11d
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.s
@@ -0,0 +1,39 @@
+# Testcase for save reg ops for callee-saved registers
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	pushq   %r12
+	.cfi_def_cfa_offset 16
+	.cfi_offset %r12, -16
+	pushq   %r13
+	.cfi_def_cfa_offset 24
+	.cfi_offset %r13, -24
+# The program may use callee-saved registers for its use, and may even
+# chose to spill them to stack if necessary.
+	addq    %rax, %r13
+	subq    $8, %r13
+# These two pushq's of callee-saved regs must NOT generate
+# .cfi_offset.
+	pushq   %r13
+	.cfi_def_cfa_offset 32
+	pushq   %rax
+	.cfi_def_cfa_offset 40
+	popq    %rax
+	.cfi_def_cfa_offset 32
+	popq    %r13
+	.cfi_def_cfa_offset 24
+# The SCFI machinery keeps track of where the callee-saved registers
+# are on the stack.  It generates a restore operation if the stack
+# offsets match.
+	popq    %r13
+	.cfi_restore %r13
+	.cfi_def_cfa_offset 16
+	popq    %r12
+	.cfi_restore %r12
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.d b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.d
new file mode 100644
index 00000000000..677146953cb
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.d
@@ -0,0 +1,41 @@
+#as: -W --scfi=experimental
+#as:
+#objdump: -Wf
+#name: SCFI for callee-saved registers 4
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+002c 0000001c FDE cie=00000000 pc=0+0000..0+005e
+  DW_CFA_advance_loc: 5 to 0+0005
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0008
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 2 to 0+000a
+  DW_CFA_offset: r12 \(r12\) at cfa-24
+  DW_CFA_advance_loc: 1 to 0+000b
+  DW_CFA_offset: r3 \(rbx\) at cfa-32
+  DW_CFA_advance_loc1: 79 to 0+005a
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_advance_loc: 2 to 0+005c
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_advance_loc: 1 to 0+005d
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.l b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.l
new file mode 100644
index 00000000000..098f70f9ffe
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*4: Warning: SCFI ignores most user-specified CFI directives
+.*24: Warning: SCFI: asymetrical register restore
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.s b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.s
new file mode 100644
index 00000000000..bad56d82133
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.s
@@ -0,0 +1,55 @@
+	.type byte_insert_op1, @function
+byte_insert_op1:
+.LFB10:
+	.cfi_startproc
+	endbr64
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq   %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	pushq   %r12
+	.cfi_offset %r12, -24
+	pushq   %rbx
+	.cfi_offset %rbx, -32
+	subq   $24, %rsp
+	movl   %edi, -20(%rbp)
+	movq   %rsi, -32(%rbp)
+	movl   %edx, -24(%rbp)
+	movq   %rcx, -40(%rbp)
+# The program may use callee-saved registers for its use, and may even
+# chose to read them from stack if necessary.  The following use should
+# not be treated as reg restore for SCFI purposes (because rbx has been
+# saved to -16(%rbp).
+	movq   -40(%rbp), %rbx
+	movq   -40(%rbp), %rax
+	leaq   3(%rax), %r12
+	jmp   .L563
+.L564:
+	subq   $1, %rbx
+	subq   $1, %r12
+	movzbl (%rbx), %eax
+	movb   %al, (%r12)
+.L563:
+	cmpq   -32(%rbp), %rbx
+	jne   .L564
+	movl   -24(%rbp), %edx
+	movq   -32(%rbp), %rcx
+	movl   -20(%rbp), %eax
+	movq   %rcx, %rsi
+	movl   %eax, %edi
+	call   byte_store_op1
+	nop
+	addq   $24, %rsp
+	popq   %rbx
+	.cfi_restore %rbx
+	popq   %r12
+	.cfi_restore %r12
+	popq   %rbp
+	.cfi_def_cfa_register %rsp
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE10:
+	.size   byte_insert_op1, .-byte_insert_op1
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d
new file mode 100644
index 00000000000..8223205d50d
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d
@@ -0,0 +1,37 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI in presence of control flow 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0024 0000001c FDE cie=00000000 pc=0+0000..0+003a
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r3 \(rbx\) at cfa-16
+  DW_CFA_advance_loc: 37 to 0+0026
+  DW_CFA_remember_state
+  DW_CFA_advance_loc: 1 to 0+0027
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_advance_loc: 1 to 0+0028
+  DW_CFA_restore_state
+  DW_CFA_advance_loc: 9 to 0+0031
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l
new file mode 100644
index 00000000000..6081b486ca7
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*15: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s
new file mode 100644
index 00000000000..5eaf20abf31
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s
@@ -0,0 +1,47 @@
+# Testcase with one dominator bb and two exit bbs
+# Something like for: return ferror (f) || fclose (f) != 0;
+	.text
+	.section .rodata.str1.1,"aMS",@progbits,1
+.LC0:
+	.string "w"
+.LC1:
+	.string "conftest.out"
+	.section .text.startup,"ax",@progbits
+	.p2align 4
+	.globl  main
+	.type   main, @function
+main:
+.LFB11:
+	.cfi_startproc
+	pushq   %rbx
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbx, -16
+	movl   $.LC0, %esi
+	movl   $.LC1, %edi
+	call   fopen
+	movq   %rax, %rdi
+	movq   %rax, %rbx
+	call   ferror
+	movl   %eax, %edx
+	movl   $1, %eax
+	testl  %edx, %edx
+	je     .L7
+	.cfi_remember_state
+	popq   %rbx
+	.cfi_restore %rbx
+	.cfi_def_cfa_offset 8
+	ret
+.L7:
+	.cfi_restore_state
+	movq    %rbx, %rdi
+	call    fclose
+	popq    %rbx
+	.cfi_restore %rbx
+	.cfi_def_cfa_offset 8
+	testl   %eax, %eax
+	setne   %al
+	movzbl  %al, %eax
+	ret
+	.cfi_endproc
+.LFE11:
+	.size   main, .-main
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d
new file mode 100644
index 00000000000..82e8b5d9e91
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d
@@ -0,0 +1,29 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI in presence of control flow 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0001c 0+001c FDE cie=00000000 pc=0000000000000000..0000000000000016
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l
new file mode 100644
index 00000000000..5ff6048bef0
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*8: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.s
new file mode 100644
index 00000000000..ee293d6d2de
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.s
@@ -0,0 +1,21 @@
+# Testcase for CFG creation of ginsns
+# This testcase has no return instruction at the end.
+	.text
+	.globl  main
+	.type   main, @function
+main:
+.LFB7:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq   %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	call   foo
+	shrl   $31, %eax
+	movzbl %al, %eax
+	movl   %eax, %edi
+	call   exit
+	.cfi_endproc
+.LFE7:
+	.size   main, .-main
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.d
new file mode 100644
index 00000000000..7a1d1715262
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.d
@@ -0,0 +1,38 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -tWf
+#name: SCFI no ignore .cfi_label
+#...
+.*\.o:     file format elf.*
+
+SYMBOL TABLE:
+0+0000 l    d  \.text	0+0000 \.text
+0+002b l       \.eh_frame	0+0000 cfi2
+0+0000 g     F \.text	0+0008 foo
+0+002a g       \.eh_frame	0+0000 cfi1
+
+
+Contents of the .eh_frame section:
+
+
+00000000 0+0014 00000000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     1b
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+0014 0000001c FDE cie=00000000 pc=0+0000..0+0008
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_advance_loc: 1 to 0+0002
+  DW_CFA_advance_loc: 1 to 0+0003
+  DW_CFA_advance_loc: 4 to 0+0007
+  DW_CFA_def_cfa_offset: 0
+  DW_CFA_nop
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.l
new file mode 100644
index 00000000000..bcf3095a9bb
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.s
new file mode 100644
index 00000000000..f95e6dc2206
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.s
@@ -0,0 +1,19 @@
+# Testcase for .cfi_label directives
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	nop
+	.globl cfi1
+	.cfi_label cfi1
+	nop
+	.cfi_label cfi2
+	nop
+	.cfi_label .Lcfi3
+	addq    $8, %rsp
+	.cfi_def_cfa_offset 0
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfi-sections-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-sections-1.d
new file mode 100644
index 00000000000..5962980256c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-sections-1.d
@@ -0,0 +1,24 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: --sframe
+#name: Synthesize CFI with .cfi_sections
+#...
+Contents of the SFrame section .sframe:
+  Header :
+
+    Version: SFRAME_VERSION_2
+    Flags: NONE
+    Num FDEs: 1
+    Num FREs: 5
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 12 bytes
+    STARTPC + CFA + FP + RA +
+    0+0000 +sp\+8 +u +u +
+    0+0001 +sp\+16 +c\-16 +u +
+    0+0004 +fp\+16 +c-16 +u +
+    0+000a +sp\+16 +c\-16 +u +
+    0+000b +sp\+8 +u +u +
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfi-sections-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-sections-1.l
new file mode 100644
index 00000000000..6ec24387b8c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-sections-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*7: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cfi-sections-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-sections-1.s
new file mode 100644
index 00000000000..b08dcfc4279
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-sections-1.s
@@ -0,0 +1,22 @@
+# Testcase for switching between sp/fp based CFA.
+	.text
+	.globl  foo
+	.type   foo, @function
+	.cfi_sections .sframe
+foo:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	addq    %rax, %rdi
+	mov     %rbp, %rsp
+	.cfi_def_cfa_register %rsp
+	pop     %rbp
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size  foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d
new file mode 100644
index 00000000000..53cc124d860
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d
@@ -0,0 +1,5 @@
+#as: --scfi=experimental -W
+#objdump: -Wf
+#name: Synthesize CFI for add insn
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l
new file mode 100644
index 00000000000..61c29da2d9a
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*12: Warning: SCFI ignores most user-specified CFI directives
+.*24: Warning: Untraceable control flow for func 'foo'; Skipping SCFI
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s
new file mode 100644
index 00000000000..0ea32d4bbe6
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s
@@ -0,0 +1,24 @@
+# Testcase with a variety of "change of flow instructions"
+#
+# Must be run with -W so it remains warning free.
+#
+# This test does not have much going on wrt synthesis of CFI;
+# it just aims to ensure x8_64 -> ginsn decoding behaves
+# gracefully for these "change of flow instructions"
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	addq    %rdx, %rax
+	loop    foo
+	notrack jmp     *%rax
+	call    *%r8
+	jecxz   .L179
+	jmp     *48(%rdi)
+	jo      .L179
+.L179:
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l
new file mode 100644
index 00000000000..0436cfe918b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l
@@ -0,0 +1,4 @@
+.*Assembler messages:
+.*7: Warning: SCFI ignores most user-specified CFI directives
+.*14: Error: SCFI: usage of REG_FP as scratch not supported
+.*22: Error: SCFI: forward pass failed for func 'foo'
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s
new file mode 100644
index 00000000000..5ec24993f0e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s
@@ -0,0 +1,22 @@
+# Testcase for REG_FP based CFA
+# and using REG_FP as scratch.
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+# The following add causes REG_FP to become untraceable
+	addq    %rax, %rbp
+# CFA cannot be recovered via REG_FP anymore
+	pop     %rbp
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l
new file mode 100644
index 00000000000..79539680b91
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l
@@ -0,0 +1,4 @@
+.*Assembler messages:
+.*5: Warning: SCFI ignores most user-specified CFI directives
+.*21: Warning: SCFI: asymetrical register restore
+.*22: Warning: SCFI: asymetrical register restore
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s
new file mode 100644
index 00000000000..8d6066897e7
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s
@@ -0,0 +1,28 @@
+# Testcase for a diagnostic around asymetrical restore
+	.type   foo, @function
+foo:
+.LFB10:
+	.cfi_startproc
+	endbr64
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	pushq   %r12
+	pushq   %rbx
+	subq    $24, %rsp
+	.cfi_offset %r12, -24
+	.cfi_offset %rbx, -32
+	addq    $24, %rsp
+# Note that the order of r12 and rbx restore does not match
+# order of the corresponding save(s).
+# The SCFI machinery warns the user.
+	popq    %r12
+	popq    %rbx
+	popq    %rbp
+	.cfi_def_cfa %rsp, 8
+	ret
+	.cfi_endproc
+.LFE10:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.d
new file mode 100644
index 00000000000..b51546af149
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.d
@@ -0,0 +1,24 @@
+#as: --scfi=experimental --gsframe -W
+#as: --gsframe
+#objdump: --sframe
+#name: SCFI for dynamic alloc stack
+#...
+
+Contents of the SFrame section .sframe:
+  Header :
+
+    Version: SFRAME_VERSION_2
+    Flags: NONE
+    Num FDEs: 1
+    Num FREs: 4
+
+  Function Index :
+
+    func idx \[0\]: pc = 0x0, size = 87 bytes
+    STARTPC + CFA + FP + RA           
+    0+0000 + sp\+8 + u + u            
+    0+0001 + sp\+16 + c-16 + u            
+    0+0004 + fp\+16 + c-16 + u            
+    0+0056 + sp\+8 + u + u            
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.l
new file mode 100644
index 00000000000..abca835a642
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*5: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.s
new file mode 100644
index 00000000000..7e649b92ad1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.s
@@ -0,0 +1,50 @@
+	.text
+	.globl   foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq   %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	pushq   %r13
+	pushq   %r12
+	pushq   %rbx
+	subq    $8, %rsp
+	.cfi_offset %r13, -24
+	.cfi_offset %r12, -32
+	.cfi_offset %rbx, -40
+	call    read_user
+	testl   %eax, %eax
+	jle    .L2
+	cltq
+	xorl   %ebx, %ebx
+	leaq   0(,%rax,4), %r13
+	.p2align 4,,10
+	.p2align 3
+.L3:
+	leaq   15(%rbx), %rax
+	movq   %rsp, %r12
+	addq   $4, %rbx
+	andq   $-16, %rax
+	subq   %rax, %rsp
+	movq   %rsp, %rdi
+	call   foo
+	movq   %r12, %rsp
+	cmpq   %r13, %rbx
+	jne   .L3
+.L2:
+	leaq   -24(%rbp), %rsp
+	xorl   %eax, %eax
+	popq   %rbx
+	popq   %r12
+	popq   %r13
+	popq   %rbp
+	.cfi_restore %rbp
+	.cfi_def_cfa_register %rsp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-enter-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-enter-1.d
new file mode 100644
index 00000000000..f38473d01ad
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-enter-1.d
@@ -0,0 +1,36 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for enter insn
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0024 0+001c FDE cie=0+0000 pc=0+0000..0+000a
+  DW_CFA_advance_loc: 4 to 0+0004
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 1 to 0+0005
+  DW_CFA_offset: r3 \(rbx\) at cfa-24
+  DW_CFA_advance_loc: 3 to 0+0008
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_advance_loc: 1 to 0+0009
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-enter-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-enter-1.l
new file mode 100644
index 00000000000..bcf3095a9bb
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-enter-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-enter-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-enter-1.s
new file mode 100644
index 00000000000..b6aed87d2dd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-enter-1.s
@@ -0,0 +1,24 @@
+# Testcase for leave insn
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	enter   $0,$0
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	.cfi_def_cfa_register %rbp
+	push    %rbx
+	.cfi_offset %rbx, -24
+	push    %rdi
+	pop     %rdi
+	pop     %rbx
+	.cfi_restore %rbx
+	leave
+	.cfi_def_cfa_register %rsp
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.l
new file mode 100644
index 00000000000..8bf0dc31b69
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*7: Warning: SCFI ignores most user-specified CFI directives
+.*28: Warning: SCFI: asymetrical register restore
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s
new file mode 100644
index 00000000000..0a25324430c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s
@@ -0,0 +1,55 @@
+# Testcase for a diagnostic around asymetrical restore
+# Testcase inspired by byte_insert_op1 in libiberty
+# False positive for the diagnostic
+	.type   foo, @function
+foo:
+.LFB10:
+	.cfi_startproc
+	endbr64
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq   %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	pushq   %r12
+	pushq   %rbx
+	subq   $24, %rsp
+	.cfi_offset %r12, -24
+	.cfi_offset %rbx, -32
+	movl   %edi, -20(%rbp)
+	movq   %rsi, -32(%rbp)
+	movl   %edx, -24(%rbp)
+	movq   %rcx, -40(%rbp)
+# The assembler cannot differentiate that the following
+# mov to %rbx is not a true restore operation, but simply
+# %rbx register usage as a scratch reg of some sort.
+# The assembler merely warns of a possible asymetric restore operation
+# In this case, its noise for the user unfortunately.
+	movq   -40(%rbp), %rbx
+	movq   -40(%rbp), %rax
+	leaq   3(%rax), %r12
+	jmp   .L563
+.L564:
+	subq   $1, %rbx
+	subq   $1, %r12
+	movzbl   (%rbx), %eax
+	movb   %al, (%r12)
+.L563:
+	cmpq   -32(%rbp), %rbx
+	jne   .L564
+	movl   -24(%rbp), %edx
+	movq   -32(%rbp), %rcx
+	movl   -20(%rbp), %eax
+	movq   %rcx, %rsi
+	movl   %eax, %edi
+	call   byte_store_op1
+	nop
+	addq   $24, %rsp
+	popq   %rbx
+	popq   %r12
+	popq   %rbp
+	.cfi_def_cfa %rsp, 8
+	ret
+	.cfi_endproc
+.LFE10:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.d
new file mode 100644
index 00000000000..732768c1d1c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.d
@@ -0,0 +1,52 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for indirect mem op to stack 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0034 0+001c FDE cie=00000000 pc=0+0000..0+005b
+  DW_CFA_advance_loc: 4 to 0+0004
+  DW_CFA_def_cfa_offset: 64
+  DW_CFA_advance_loc: 4 to 0+0008
+  DW_CFA_offset: r3 \(rbx\) at cfa-64
+  DW_CFA_advance_loc: 5 to 0+000d
+  DW_CFA_offset: r6 \(rbp\) at cfa-56
+  DW_CFA_advance_loc: 5 to 0+0012
+  DW_CFA_offset: r12 \(r12\) at cfa-48
+  DW_CFA_advance_loc: 5 to 0+0017
+  DW_CFA_offset: r13 \(r13\) at cfa-40
+  DW_CFA_advance_loc: 5 to 0+001c
+  DW_CFA_offset: r14 \(r14\) at cfa-32
+  DW_CFA_advance_loc: 5 to 0+0021
+  DW_CFA_offset: r15 \(r15\) at cfa-24
+  DW_CFA_advance_loc: 29 to 0+003e
+  DW_CFA_restore: r15 \(r15\)
+  DW_CFA_advance_loc: 5 to 0+0043
+  DW_CFA_restore: r14 \(r14\)
+  DW_CFA_advance_loc: 5 to 0+0048
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_advance_loc: 5 to 0+004d
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_advance_loc: 5 to 0+0052
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_advance_loc: 4 to 0+0056
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_advance_loc: 4 to 0+005a
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.l
new file mode 100644
index 00000000000..8662e445edf
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*4: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.s
new file mode 100644
index 00000000000..53c55330321
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.s
@@ -0,0 +1,48 @@
+# An example of static stack allocation by hand
+	.type foo, @function
+foo:
+	.cfi_startproc
+	/* Allocate space for 7 registers.  */
+	subq   $56,%rsp
+	.cfi_adjust_cfa_offset 56
+	movq   %rbx,(%rsp)
+   .cfi_rel_offset %rbx, 0
+	movq   %rbp,8(%rsp)
+	.cfi_rel_offset %rbp, 8
+	movq   %r12,16(%rsp)
+	.cfi_rel_offset %r12, 16
+	movq   %r13,24(%rsp)
+	.cfi_rel_offset %r13, 24
+	movq   %r14,32(%rsp)
+	.cfi_rel_offset %r14, 32
+	movq   %r15,40(%rsp)
+	.cfi_rel_offset %r15, 40
+	movq   %r9,48(%rsp)
+
+	/* Setup parameter for __foo_internal.  */
+	/* selfpc is the return address on the stack.  */
+	movq   56(%rsp),%rsi
+	/* Get frompc via the frame pointer.  */
+	movq   8(%rbp),%rdi
+	call __foo_internal
+	/* Pop the saved registers.  Please note that `foo' has no
+	   return value.  */
+	movq   48(%rsp),%r9
+
+	movq   40(%rsp),%r15
+	.cfi_restore %r15
+	movq   32(%rsp),%r14
+	.cfi_restore %r14
+	movq   24(%rsp),%r13
+	.cfi_restore %r13
+	movq   16(%rsp),%r12
+	.cfi_restore %r12
+	movq   8(%rsp),%rbp
+	.cfi_restore %rbp
+	movq   (%rsp),%rbx
+	.cfi_restore %rbx
+	addq   $56,%rsp
+	.cfi_adjust_cfa_offset -56
+	ret
+	.cfi_endproc
+	.size foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.d
new file mode 100644
index 00000000000..d5e26cd925b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.d
@@ -0,0 +1,42 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for indirect mem op to stack 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+002c 0+001c FDE cie=00000000 pc=0+0000..0+0026
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 8 to 0+000c
+  DW_CFA_offset: r3 \(rbx\) at cfa-32
+  DW_CFA_advance_loc: 4 to 0+0010
+  DW_CFA_offset: r12 \(r12\) at cfa-24
+  DW_CFA_advance_loc: 9 to 0+0019
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_advance_loc: 4 to 0+001d
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_advance_loc: 7 to 0+0024
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_advance_loc: 1 to 0+0025
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.l
new file mode 100644
index 00000000000..bcf3095a9bb
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.s
new file mode 100644
index 00000000000..cf63fdba45f
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.s
@@ -0,0 +1,38 @@
+# Testcase for movq instructions
+	.text
+	.globl   foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+
+	subq   $16,%rsp
+
+# store rbx at %rsp; rsp = rbp - 16;
+	movq   %rbx, -16(%rbp)
+	.cfi_rel_offset %rbx, -16
+# store r12 at %rsp + 8; rsp = rbp -16;
+	movq   %r12, -8(%rbp)
+	.cfi_rel_offset %r12, -8
+
+	call bar
+
+	movq   -16(%rbp), %rbx
+	.cfi_restore %rbx
+	movq   -8(%rbp), %r12
+	.cfi_restore %r12
+
+	addq   $16,%rsp
+
+	mov   %rbp, %rsp
+	.cfi_def_cfa_register %rsp
+	pop   %rbp
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.d b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.d
new file mode 100644
index 00000000000..241b6289974
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.d
@@ -0,0 +1,42 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for indirect mem op to stack 3
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+002c 0+001c FDE cie=00000000 pc=0+0000..0+0028
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 8 to 0+000c
+  DW_CFA_offset: r3 \(rbx\) at cfa-32
+  DW_CFA_advance_loc: 5 to 0+0011
+  DW_CFA_offset: r12 \(r12\) at cfa-24
+  DW_CFA_advance_loc: 9 to 0+001a
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_advance_loc: 5 to 0+001f
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_advance_loc: 7 to 0+0026
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_advance_loc: 1 to 0+0027
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.l b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.l
new file mode 100644
index 00000000000..bcf3095a9bb
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.s b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.s
new file mode 100644
index 00000000000..c6247f74370
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.s
@@ -0,0 +1,38 @@
+# Testcase for movq instructions
+	.text
+	.globl   foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+
+	subq   $16,%rsp
+
+# store rbx at %rsp; rsp = rbp - 16; rsp = CFA - 32
+	movq   %rbx, (%rsp)
+	.cfi_rel_offset %rbx, -16
+# store r12 at %rsp + 8; rsp = CFA - 32
+	movq   %r12, 8(%rsp)
+	.cfi_rel_offset %r12, -8
+
+	call bar
+
+	movq   (%rsp), %rbx
+	.cfi_restore %rbx
+	movq   8(%rsp), %r12
+	.cfi_restore %r12
+
+	addq   $16,%rsp
+
+	mov   %rbp, %rsp
+	.cfi_def_cfa_register %rsp
+	pop   %rbp
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.d b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.d
new file mode 100644
index 00000000000..614be3b5dce
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.d
@@ -0,0 +1,64 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for indirect mem op to stack 3
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+004c 0000001c FDE cie=00000000 pc=0+0000..0+003d
+  DW_CFA_advance_loc: 2 to 0+0002
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r15 \(r15\) at cfa-16
+  DW_CFA_advance_loc: 2 to 0+0004
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_offset: r14 \(r14\) at cfa-24
+  DW_CFA_advance_loc: 5 to 0+0009
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_offset: r13 \(r13\) at cfa-32
+  DW_CFA_advance_loc: 5 to 0+000e
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_offset: r12 \(r12\) at cfa-40
+  DW_CFA_advance_loc: 4 to 0+0012
+  DW_CFA_def_cfa_offset: 48
+  DW_CFA_offset: r6 \(rbp\) at cfa-48
+  DW_CFA_advance_loc: 4 to 0+0016
+  DW_CFA_def_cfa_offset: 56
+  DW_CFA_offset: r3 \(rbx\) at cfa-56
+  DW_CFA_advance_loc: 7 to 0+001d
+  DW_CFA_def_cfa_offset: 96
+  DW_CFA_advance_loc: 21 to 0+0032
+  DW_CFA_def_cfa_offset: 56
+  DW_CFA_advance_loc: 1 to 0+0033
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_def_cfa_offset: 48
+  DW_CFA_advance_loc: 1 to 0+0034
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_advance_loc: 2 to 0+0036
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 2 to 0+0038
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 2 to 0+003a
+  DW_CFA_restore: r14 \(r14\)
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 2 to 0+003c
+  DW_CFA_restore: r15 \(r15\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.l b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.l
new file mode 100644
index 00000000000..d87f01ac77e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*7: Warning: SCFI ignores most user-specified CFI directives
+.*41: Warning: SCFI: asymetrical register restore
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.s b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.s
new file mode 100644
index 00000000000..988ca3f78a7
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.s
@@ -0,0 +1,68 @@
+# Testcase for save/unsave of callee-saved registers
+# Must be run -W as there is an expected warning as
+# noted below
+	.type   foo, @function
+foo:
+.LFB118:
+	.cfi_startproc
+	pushq   %r15
+	.cfi_def_cfa_offset 16
+	.cfi_offset %r15, -16
+	pushq   %r14
+	.cfi_def_cfa_offset 24
+	.cfi_offset %r14, -24
+	movl    %r8d, %r14d
+	pushq   %r13
+	.cfi_def_cfa_offset 32
+	.cfi_offset %r13, -32
+	movq    %rdi, %r13
+	pushq   %r12
+	.cfi_def_cfa_offset 40
+	.cfi_offset %r12, -40
+	movq    %rsi, %r12
+	pushq   %rbp
+	.cfi_def_cfa_offset 48
+	.cfi_offset %rbp, -48
+	movq    %rcx, %rbp
+	pushq   %rbx
+	.cfi_def_cfa_offset 56
+	.cfi_offset %rbx, -56
+	movq   %rdx, %rbx
+	subq   $40, %rsp
+	.cfi_def_cfa_offset 96
+	testb   $1, 37(%rdx)
+	je   .L2
+.L3:
+# The following is not a restore of r15: rbp has been used as
+# scratch register already.  The SCFI machinery must know that
+# REG_FP is not traceable.
+# A warning here is expected:
+# 41: Warning: SCFI: asymetrical register restore
+	movq   32(%rbp), %r15
+	cmpq   $0, 64(%r15)
+	je   .L2
+.L2:
+	addq   $40, %rsp
+	.cfi_def_cfa_offset 56
+	popq   %rbx
+	.cfi_restore %rbx
+	.cfi_def_cfa_offset 48
+	popq   %rbp
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 40
+	popq   %r12
+	.cfi_restore %r12
+	.cfi_def_cfa_offset 32
+	popq   %r13
+	.cfi_restore %r13
+	.cfi_def_cfa_offset 24
+	popq   %r14
+	.cfi_restore %r14
+	.cfi_def_cfa_offset 16
+	popq   %r15
+	.cfi_restore %r15
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE118:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-5.s b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-5.s
new file mode 100644
index 00000000000..9efff69cfa1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-5.s
@@ -0,0 +1,35 @@
+# An example of static stack allocation by hand
+	.type foo, @function
+foo:
+	.cfi_startproc
+	/* Allocate space for 7 registers.  */
+	subq   $56,%rsp
+	.cfi_adjust_cfa_offset 56
+	movq   %rsp, %rax
+	movq   %rbx,(%rax)
+	.cfi_rel_offset %rbx, 0
+	movq   %rbp,8(%rax)
+	.cfi_rel_offset %rbp, 8
+	movq   %r12,16(%rax)
+	.cfi_rel_offset %r12, 16
+
+	/* Setup parameter for __foo_internal.  */
+	/* selfpc is the return address on the stack.  */
+	movq   56(%rsp),%rsi
+	/* Get frompc via the frame pointer.  */
+	movq   8(%rbp),%rdi
+	call __foo_internal
+	/* Pop the saved registers.  Please note that `foo' has no
+	   return value.  */
+
+	movq   16(%rax),%r12
+	.cfi_restore %r12
+	movq   8(%rax),%rbp
+	.cfi_restore %rbp
+	movq   (%rsp),%rbx
+	.cfi_restore %rbx
+	addq   $56,%rsp
+	.cfi_adjust_cfa_offset -56
+	ret
+	.cfi_endproc
+	.size foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d
new file mode 100644
index 00000000000..cf56fc27a48
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d
@@ -0,0 +1,38 @@
+#as: --scfi=experimental -W -O2
+#as: -O2
+#objdump: -Wf
+#name: Synthesize CFI for various lea instructions (-O2)
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0024 0+001c FDE cie=00000000 pc=0+0000..0+0029
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 2 to 0+0006
+  DW_CFA_offset: r13 \(r13\) at cfa-24
+  DW_CFA_advance_loc: 33 to 0+0027
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_advance_loc: 1 to 0+0028
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l
new file mode 100644
index 00000000000..5ff6048bef0
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*8: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s
new file mode 100644
index 00000000000..a31f8593732
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s
@@ -0,0 +1,39 @@
+## Testcase with a variety of lea.
+## This test is run with -O2 by default to check
+## SCFI in wake of certain target optimizations.
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+# This lea gets transformed to mov %rsp, %rbp when -O2.
+# The SCFI machinery must see it as such.
+	lea    (%rsp), %rbp
+	.cfi_def_cfa_register %rbp
+	push   %r13
+	.cfi_offset %r13, -24
+	subq   $8, %rsp
+	testl  %eax, %eax
+	jle   .L2
+.L3:
+	movq   %rsp, %r12
+	lea    -0x2(%r13),%rax
+	lea    0x8(%r12,%rdx,4),%r8
+	movq   %r12, %rsp
+	jne   .L3
+.L2:
+	leaq   -8(%rbp), %rsp
+	xorl   %eax, %eax
+	popq   %r13
+	.cfi_restore %r13
+	popq   %rbp
+	.cfi_def_cfa_register %rsp
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d
new file mode 100644
index 00000000000..7b0a4667849
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d
@@ -0,0 +1,37 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for leave insn
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0024 0+001c FDE cie=0+0000 pc=0+0000..0+000a
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 1 to 0+0005
+  DW_CFA_offset: r3 \(rbx\) at cfa-24
+  DW_CFA_advance_loc: 3 to 0+0008
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_advance_loc: 1 to 0+0009
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l
new file mode 100644
index 00000000000..bcf3095a9bb
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s
new file mode 100644
index 00000000000..d86d944769b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s
@@ -0,0 +1,25 @@
+# Testcase for leave insn
+	.text
+	.globl   foo
+	.type    foo, @function
+foo:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	push    %rbx
+	.cfi_offset %rbx, -24
+	push    %rdi
+	pop     %rdi
+	pop     %rbx
+	.cfi_restore %rbx
+	leave
+	.cfi_def_cfa_register %rsp
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d
new file mode 100644
index 00000000000..78afef3a940
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d
@@ -0,0 +1,36 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for pushq insns
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0024 0+001c FDE cie=0+0000 pc=0+0000..0+000f
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 5 to 0+0009
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 2 to 0+000b
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_advance_loc: 2 to 0+0000d
+  DW_CFA_def_cfa_offset: 48
+  DW_CFA_advance_loc: 1 to 0+0000e
+  DW_CFA_def_cfa_offset: 56
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l
new file mode 100644
index 00000000000..6ec24387b8c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*7: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s
new file mode 100644
index 00000000000..7be53a41d5f
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s
@@ -0,0 +1,23 @@
+## Testcase with a variety of pushq.
+## all push insns valid in 64-bit mode must be processed for SCFI.
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	pushq   %rax
+	.cfi_def_cfa_offset 16
+	pushq   16(%rax)
+	.cfi_def_cfa_offset 24
+	push   $1048576
+	.cfi_def_cfa_offset 32
+	pushq   %fs
+	.cfi_def_cfa_offset 40
+	pushq   %gs
+	.cfi_def_cfa_offset 48
+	pushf
+	.cfi_def_cfa_offset 56
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.d
new file mode 100644
index 00000000000..8056e02eb16
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.d
@@ -0,0 +1,43 @@
+#as: --scfi=experimental -W
+#objdump: -Wf
+#name: Synthesize CFI with pushsection 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+0010 0000001c FDE cie=00000000 pc=0+0000..0+0005
+  DW_CFA_advance_loc: 4 to 0+0004
+  DW_CFA_def_cfa_offset: 48
+
+0000002c 0+0028 00000030 FDE cie=00000000 pc=0+0000..0+0014
+  DW_CFA_advance_loc: 2 to 0+0002
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r12 \(r12\) at cfa-16
+  DW_CFA_advance_loc: 2 to 0+0004
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_offset: r13 \(r13\) at cfa-24
+  DW_CFA_advance_loc: 4 to 0+0008
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 7 to 0+000f
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 2 to 0+0011
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 2 to 0+0013
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.l
new file mode 100644
index 00000000000..6ec24387b8c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*7: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.s
new file mode 100644
index 00000000000..e421ef6d86f
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-1.s
@@ -0,0 +1,40 @@
+# Testcase for pushsection directive and SCFI.
+# The .pushsection directive must cause creation of a new FDE.
+        .text
+        .globl  foo
+        .type   foo, @function
+foo:
+	.cfi_startproc
+	pushq   %r12
+	.cfi_def_cfa_offset 16
+	.cfi_offset %r12, -16
+	pushq   %r13
+	.cfi_def_cfa_offset 24
+	.cfi_offset %r13, -24
+	subq    $8, %rsp
+	.cfi_def_cfa_offset 32
+	mov   %rax, %rbx
+	.pushsection .text2
+# It's the .type directive here that enforces SCFI generation
+# for the code block that follows
+	.type   foo2, @function
+foo2:
+	.cfi_startproc
+	subq    $40, %rsp
+	.cfi_def_cfa_offset 48
+	ret
+	.cfi_endproc
+	.size foo2, .-foo2
+	.popsection
+	addq   $8, %rsp
+	.cfi_def_cfa_offset 24
+	popq    %r13
+	.cfi_restore %r13
+	.cfi_def_cfa_offset 16
+	popq    %r12
+	.cfi_restore %r12
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.d
new file mode 100644
index 00000000000..abddd9dedd6
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.d
@@ -0,0 +1,40 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for demarcated code blocks 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+00000018 0+002c 0000001c FDE cie=00000000 pc=0+0000..0+0014
+  DW_CFA_advance_loc: 2 to 0+0002
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r12 \(r12\) at cfa-16
+  DW_CFA_advance_loc: 2 to 0+0004
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_offset: r13 \(r13\) at cfa-24
+  DW_CFA_advance_loc: 4 to 0+0008
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 7 to 0+000f
+  DW_CFA_def_cfa_offset: 24
+  DW_CFA_advance_loc: 2 to 0+0011
+  DW_CFA_restore: r13 \(r13\)
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_advance_loc: 2 to 0+0013
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.l
new file mode 100644
index 00000000000..bcf3095a9bb
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.s
new file mode 100644
index 00000000000..65eb63e6375
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushsection-2.s
@@ -0,0 +1,40 @@
+# Testcase for pushsection directive and SCFI.
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	pushq   %r12
+	.cfi_def_cfa_offset 16
+	.cfi_offset %r12, -16
+	pushq   %r13
+	.cfi_def_cfa_offset 24
+	.cfi_offset %r13, -24
+	subq    $8, %rsp
+	.cfi_def_cfa_offset 32
+	mov     %rax, %rbx
+# The .pushsection directive creates a new code block,
+# which must not contribute ginsn to the existing one.
+	.pushsection .text2
+# For CFI to be synthesized for this block, the user should have # demarcated
+# the beginning with a .type name, @function.  Note that, the test when run
+# without --scfi=experimental will, however, generate the DWARF FDE if the CFI
+# annotations exist.  Hence, comment them out for now.
+#	.cfi_startproc
+	subq    $40, %rsp
+#	.cfi_def_cfa_offset 48
+	ret
+#	.cfi_endproc
+	.popsection
+	addq    $8, %rsp
+	.cfi_def_cfa_offset 24
+	popq    %r13
+	.cfi_restore %r13
+	.cfi_def_cfa_offset 16
+	popq    %r12
+	.cfi_restore %r12
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.d
new file mode 100644
index 00000000000..fb09269f6cd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.d
@@ -0,0 +1,32 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for self-aligning func
+#...
+Contents of the .eh_frame section:
+
+0+0000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+001c 0000001c FDE cie=00000000 pc=0+0000..0+002f
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 42 to 0+002e
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.l
new file mode 100644
index 00000000000..6ec24387b8c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*7: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.s
new file mode 100644
index 00000000000..1068332c988
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.s
@@ -0,0 +1,36 @@
+# If it is known that the caller of self_aligning_foo may have had
+# the stack pointer unaligned to 16-bytes boundary,  such self-aligning
+# functions may be used by asm programmers.
+	.globl  self_aligning_foo
+	.type   self_aligning_foo, @function
+self_aligning_foo:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+# The following 'and' op aligns the stack pointer.
+# At the same time, it causes REG_SP to become non-traceable
+# for SCFI purposes.  But no warning is issued as no further stack
+# size tracking is needed for SCFI purposes.
+	andq    $-16, %rsp
+	subq    $32, %rsp
+	movl    %edi, 12(%rsp)
+	movl    %esi, 8(%rsp)
+	movl    $0, %eax
+	call    vector_using_function
+	movaps  %xmm0, 16(%rsp)
+	movl    12(%rsp), %edx
+	movl    8(%rsp), %eax
+	addl    %edx, %eax
+	leave
+# GCC typically generates a '.cfi_def_cfa 7, 8' for leave
+# insn.  The SCFI however, will generate the following:
+	.cfi_def_cfa_register %rsp
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   self_aligning_foo, .-self_aligning_foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d
new file mode 100644
index 00000000000..819a1299b53
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d
@@ -0,0 +1,27 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI simple test 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0014 0+001c FDE cie=0+0000 pc=0+0000..0+0002
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_nop
+  DW_CFA_nop
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l
new file mode 100644
index 00000000000..6ec24387b8c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*7: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.s
new file mode 100644
index 00000000000..6fc2b7b8707
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.s
@@ -0,0 +1,15 @@
+# Simple test
+# A wierd function, but SCFI machinery does not complain yet.
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d
new file mode 100644
index 00000000000..de8c3ae7fd2
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d
@@ -0,0 +1,31 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI simple test 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0001c 0+0001c FDE cie=0+0000 pc=0+0000..0+0003
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 1 to 0+0002
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l
new file mode 100644
index 00000000000..abca835a642
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*5: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.s
new file mode 100644
index 00000000000..7055fc10b23
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.s
@@ -0,0 +1,16 @@
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	popq    %rbp
+	.cfi_restore 6
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
+
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d
new file mode 100644
index 00000000000..042ce33cd38
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d
@@ -0,0 +1,26 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for sub insn 1
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+0014 0+001c FDE cie=0+0000 pc=0+0000..0+0008
+  DW_CFA_advance_loc: 7 to 0+0007
+  DW_CFA_def_cfa_offset: 120016
+  DW_CFA_nop
+#...
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l
new file mode 100644
index 00000000000..bcf3095a9bb
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.s
new file mode 100644
index 00000000000..aabd1e7a50f
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.s
@@ -0,0 +1,12 @@
+# Testcase for sub instruction.
+	.text
+	.globl   foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+	subq    $120008, %rsp
+	.cfi_def_cfa_offset 120016
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d
new file mode 100644
index 00000000000..a1481056a4c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d
@@ -0,0 +1,32 @@
+#as: --scfi=experimental -W
+#as:
+#objdump: -Wf
+#name: Synthesize CFI for sub insn 2
+#...
+Contents of the .eh_frame section:
+
+00000000 0+0014 0+0000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 1
+  Data alignment factor: -8
+  Return address column: 16
+  Augmentation data:     [01][abc]
+  DW_CFA_def_cfa: r7 \(rsp\) ofs 8
+  DW_CFA_offset: r16 \(rip\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+
+0+0018 0+001c 0+001c FDE cie=0+0000 pc=0+0000..0+001d
+  DW_CFA_advance_loc: 1 to 0+0001
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r6 \(rbp\) at cfa-16
+  DW_CFA_advance_loc: 3 to 0+0004
+  DW_CFA_def_cfa_register: r6 \(rbp\)
+  DW_CFA_advance_loc: 24 to 0+001c
+  DW_CFA_def_cfa_register: r7 \(rsp\)
+  DW_CFA_restore: r6 \(rbp\)
+  DW_CFA_def_cfa_offset: 8
+  DW_CFA_nop
+
+#pass
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l
new file mode 100644
index 00000000000..bcf3095a9bb
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: SCFI ignores most user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.s
new file mode 100644
index 00000000000..c8e0512d4a8
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.s
@@ -0,0 +1,29 @@
+# Testcase for sub reg, reg instruction.
+	.text
+	.globl   foo
+	.type    foo, @function
+foo:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	subq    %rax, %rsp
+# SCFI: Stack-pointer manipulation after switching
+# to RBP based tracking is OK.
+	addq   %rax, %rsp
+# Other kind of sub instructions should not error out in the
+# x86_64 -> ginsn translator
+	subq    (%r12), %rax
+	subq    $1,(%rdi)
+	subq    %rax, 40(%r12)
+	subl    %edx, -32(%rsp)
+	leave
+	.cfi_def_cfa_register %rsp
+	.cfi_restore %rbp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.l
new file mode 100644
index 00000000000..fc26b86a281
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.l
@@ -0,0 +1,2 @@
+Assembler messages:
+Fatal error: SCFI is not supported for this ABI
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s
new file mode 100644
index 00000000000..74a46dfa9e4
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s
@@ -0,0 +1,9 @@
+# Testcase run with --32 and --x32 (Either not supported with SCFI).
+	.text
+	.globl   foo
+	.type    foo, @function
+foo:
+	pushq   %rbp
+	ret
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.l
new file mode 100644
index 00000000000..98a7ac67ec0
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*8: Error: SCFI: unsupported stack manipulation pattern
+.*13: Error: SCFI: forward pass failed for func 'foo'
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.s
new file mode 100644
index 00000000000..f0ff42fb796
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.s
@@ -0,0 +1,13 @@
+	.text
+	.globl   foo
+	.type    foo, @function
+foo:
+	addq    %rdx, %rax
+# Stack manipulation without switching to RBP
+# based tracking is not supported for SCFI.
+	addq    %rax, %rsp
+	push    %rdi
+	leave
+	ret
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.l
new file mode 100644
index 00000000000..98a7ac67ec0
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*8: Error: SCFI: unsupported stack manipulation pattern
+.*13: Error: SCFI: forward pass failed for func 'foo'
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.s
new file mode 100644
index 00000000000..b0cf59ecadf
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.s
@@ -0,0 +1,13 @@
+	.text
+	.globl  foo
+	.type   foo, @function
+foo:
+	addq    %rdx, %rax
+# Stack manipulation without switching to RBP
+# based tracking is not supported for SCFI.
+	movq    %rax, %rsp
+	push    %rdi
+	leave
+	ret
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.l
new file mode 100644
index 00000000000..4ee445e7ebf
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.l
@@ -0,0 +1,4 @@
+.*Assembler messages:
+.*4: Warning: SCFI ignores most user-specified CFI directives
+.*14: Error: SCFI: unsupported stack manipulation pattern
+.*22: Error: SCFI: forward pass failed for func 'foo'
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.s
new file mode 100644
index 00000000000..eff5a29fad7
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.s
@@ -0,0 +1,22 @@
+	.type   foo, @function
+foo:
+.LFB16:
+	.cfi_startproc
+	pushq   %rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq    %rsp, %rbp
+# The following stack manipulation is OK to do, as CFA tracking
+# REG_FP based.
+	movq   %rax, %rsp
+# But...not quite, if there is a follow up usage of stack
+# like a register save of a callee-saved register.
+	push   %rbx
+	mov    %rbp, %rsp
+	popq   %rbp
+	.cfi_def_cfa %rsp, 8
+	.cfi_restore %rbp
+	ret
+	.cfi_endproc
+.LFE16:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.l
new file mode 100644
index 00000000000..1e138a102fe
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*50: Warning: SCFI ignores most user-specified CFI directives
+.*52: Warning: Untraceable control flow for func 'foo'; Skipping SCFI
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.s
new file mode 100644
index 00000000000..1c6eb63740e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.s
@@ -0,0 +1,52 @@
+# Testcase with an indirect jump
+# Indirect jumps, when present, make the list of ginsn an invalid
+# candidate for CFG creation.  Hence, no SCFI can be generated either.
+#
+# The testcase is rather long to showcase a simple concept.  The reason of
+# such a long testcase is to discuss if it is important to deal with these
+# patterns.  It may be possible to deal with this, if we allow some special
+# directives for helping the assembler with the indirect jump (jump table).
+	.text
+	.globl   foo
+	.type   foo, @function
+foo:
+	pushq   %rbp
+	movq    %rsp, %rbp
+	movl    %edi, -4(%rbp)
+	cmpl    $5, -4(%rbp)
+	ja      .L2
+	movl    -4(%rbp), %eax
+	movq    .L4(,%rax,8), %rax
+	jmp     *%rax
+.L4:
+	.quad   .L9
+	.quad   .L8
+	.quad   .L7
+	.quad   .L6
+	.quad   .L5
+	.quad   .L3
+.L9:
+	movl    $43, %eax
+	jmp     .L1
+.L8:
+	movl    $42, %eax
+	jmp     .L1
+.L7:
+	movl    $45, %eax
+	jmp     .L1
+.L6:
+	movl    $47, %eax
+	jmp     .L1
+.L5:
+	movl    $37, %eax
+	jmp     .L1
+.L3:
+	movl    $63, %eax
+	jmp     .L1
+.L2:
+.L1:
+	popq    %rbp
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-2.l
new file mode 100644
index 00000000000..84cf95fb5cd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-2.l
@@ -0,0 +1,4 @@
+.*Assembler messages:
+.*6: Warning: SCFI ignores most user-specified CFI directives
+.*9: Error: SCFI: `jmp' insn with non-zero addend to sym not supported
+.*9: Error: SCFI: unhandled op 0xeb may cause incorrect CFI
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-2.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-2.s
new file mode 100644
index 00000000000..611041ef3f1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-2.s
@@ -0,0 +1,14 @@
+# Testcase with unsupported jmp instructions
+	.text
+	.globl   foo
+	.type   foo, @function
+foo:
+	.cfi_startproc
+# The non-zero addend makes control flow tracking not impossible, but
+# difficult.  SCFI for such functions is not attempted.
+        jmp RangeLimit+1;
+RangeLimit:
+        nop
+        ret
+	.cfi_endproc
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.l
new file mode 100644
index 00000000000..ea0fc73fac1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.l
@@ -0,0 +1,4 @@
+.*Assembler messages:
+.*16: Warning: SCFI ignores most user-specified CFI directives
+.*19: Error: SCFI: unsupported stack manipulation pattern
+.*75: Error: SCFI: forward pass failed for func 'drap_foo'
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.s
new file mode 100644
index 00000000000..3809e9054c0
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.s
@@ -0,0 +1,75 @@
+# Testcase with Dynamically Realigned Argument Pointer (DRAP)
+# register usage.
+#
+# There are two reasons why this cannot be supported with the current
+# SCFI machinery
+# 1. Not allowed: REG_CFA is r10 based for the few insns after
+#    'leaq    8(%rsp), %r10'.
+# 2. Untraceable stack size after 'andq    $-16, %rsp'
+# Both of these shortcomings may be worked out. FIXME DISCUSS Keep the rather
+# long testcase until then.
+	.text
+	.globl  drap_foo
+	.type   drap_foo, @function
+drap_foo:
+.LFB0:
+	.cfi_startproc
+	leaq    8(%rsp), %r10
+	.cfi_def_cfa %r10, 0
+	andq    $-16, %rsp
+	pushq   -8(%r10)
+	.cfi_def_cfa %rsp, 8
+	pushq   %rbp
+	.cfi_offset %rbp, -16
+	movq   %rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	pushq   %r15
+	pushq   %r14
+	pushq   %r13
+	pushq   %r12
+	pushq   %r10
+	.cfi_offset %r15, -24
+	.cfi_offset %r14, -32
+	.cfi_offset %r13, -40
+	.cfi_offset %r12, -48
+	pushq   %rbx
+	.cfi_offset %rbx, -64
+	subq    $32, %rsp
+	movq    $0, (%rdx)
+	cmpq    $0, (%rdi)
+	movq    $0, -56(%rbp)
+	je      .L21
+	movq    %rdi, %rbx
+	movq    %rsi, %rdi
+	movq    %rsi, %r12
+	call    func2@PLT
+	movq    (%rbx), %rdi
+	leaq    -56(%rbp), %rdx
+	movslq  %eax, %rsi
+	call    func1@PLT
+	testl   %eax, %eax
+	je      .L21
+	movq    -56(%rbp), %r13
+.L21:
+	addq    $32, %rsp
+	xorl    %eax, %eax
+	popq    %rbx
+	.cfi_restore %rbx3
+	popq    %r10
+	popq    %r12
+	.cfi_restore %r12
+	popq    %r13
+	.cfi_restore %r13
+	popq    %r14
+	.cfi_restore %r14
+	popq    %r15
+	.cfi_restore %r15
+	popq    %rbp
+	.cfi_restore %rbp
+	.cfi_def_cfa_register %rsp
+	.cfi_def_cfa_offset 8
+	leaq    -8(%r10), %rsp
+	ret
+	.cfi_endproc
+.LFE0:
+	.size   drap_foo, .-drap_foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-insn-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-insn-1.l
new file mode 100644
index 00000000000..d66a4c461c3
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-insn-1.l
@@ -0,0 +1,7 @@
+.*Assembler messages:
+.*7: Error: SCFI: unsupported APX op 0x8f may cause incorrect CFI
+.*8: Error: SCFI: unsupported APX op 0x8f may cause incorrect CFI
+.*9: Error: SCFI: unsupported APX op 0xff may cause incorrect CFI
+.*10: Error: SCFI: unsupported APX op 0xff may cause incorrect CFI
+.*11: Error: SCFI: unsupported APX op 0x11 may cause incorrect CFI
+.*13: Error: SCFI: hand-crafting instructions not supported
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-insn-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-insn-1.s
new file mode 100644
index 00000000000..97728bee49e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-insn-1.s
@@ -0,0 +1,16 @@
+# Certain APX instructions are not supported currently
+# Also, hand-crafting instructions using .insn directive is not supported.
+	.text
+	.globl   foo
+	.type   foo, @function
+foo:
+	pop2p  %r12, %rax
+	pop2   %r12, %rax
+	push2  %r12, %rax
+	push2p %rax, %r17
+	adc %rsp, %r17, %rsp
+	# test $0x4,%ecx
+	.insn 0xf7/1, $4{:u32}, %ecx
+	ret
+.LFE0:
+	.size   foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp b/gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp
new file mode 100644
index 00000000000..460d5fd2bca
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp
@@ -0,0 +1,113 @@
+# Copyright (C) 2022-2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+
+if { ![is_elf_format] } then {
+    return
+}
+
+# common tests
+if  { ([istarget "x86_64-*-*"]) } then {
+
+    global ASFLAGS
+    set old_ASFLAGS "$ASFLAGS"
+
+    run_list_test "ginsn-dw2-regnum-1" "--scfi=experimental -ali"
+    run_list_test "ginsn-add-1" "--scfi=experimental -ali"
+    run_list_test "ginsn-pop-1" "--scfi=experimental -ali"
+    run_list_test "ginsn-push-1" "--scfi=experimental -ali"
+
+    run_dump_test "scfi-cfi-label-1"
+    run_list_test "scfi-cfi-label-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-cfi-sections-1"
+    run_list_test "scfi-cfi-sections-1" "--scfi=experimental --warn"
+
+    run_list_test "scfi-diag-1" "--scfi=experimental"
+    run_list_test "scfi-fp-diag-2" "--scfi=experimental"
+    run_list_test "scfi-diag-2" "--scfi=experimental"
+
+    run_list_test "scfi-unsupported-1" "--32 --scfi=experimental"
+    run_list_test "scfi-unsupported-1" "--x32 --scfi=experimental"
+    run_list_test "scfi-unsupported-insn-1" "--scfi=experimental"
+    run_list_test "scfi-unsupported-2" "--scfi=experimental"
+    run_list_test "scfi-unsupported-3" "--scfi=experimental"
+    run_list_test "scfi-unsupported-4" "--scfi=experimental"
+    run_list_test "scfi-unsupported-drap-1" "--scfi=experimental"
+    run_list_test "scfi-unsupported-cfg-1" "--scfi=experimental"
+    run_list_test "scfi-unsupported-cfg-2" "--scfi=experimental"
+
+    run_dump_test "scfi-add-1"
+    run_list_test "scfi-add-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-add-2"
+    run_list_test "scfi-add-2" "--scfi=experimental --warn"
+    run_dump_test "scfi-cfg-1"
+    run_list_test "scfi-cfg-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-cfg-2"
+    run_list_test "scfi-cfg-2" "--scfi=experimental --warn"
+    run_dump_test "scfi-asm-marker-1"
+    run_list_test "scfi-asm-marker-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-asm-marker-2"
+    run_list_test "scfi-asm-marker-2" "--scfi=experimental --warn"
+    run_dump_test "scfi-asm-marker-3"
+    run_list_test "scfi-asm-marker-3" "--scfi=experimental --warn"
+    run_dump_test "scfi-pushsection-1"
+    run_list_test "scfi-pushsection-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-pushsection-2"
+    run_list_test "scfi-pushsection-2" "--scfi=experimental --warn"
+
+    run_dump_test "scfi-cofi-1"
+    run_list_test "scfi-cofi-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-sub-1"
+    run_list_test "scfi-sub-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-sub-2"
+    run_list_test "scfi-sub-2" "--scfi=experimental --warn"
+    run_dump_test "scfi-simple-1"
+    run_list_test "scfi-simple-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-simple-2"
+    run_list_test "scfi-simple-2" "--scfi=experimental --warn"
+    run_dump_test "scfi-pushq-1"
+    run_list_test "scfi-pushq-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-lea-1"
+    run_list_test "scfi-lea-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-enter-1"
+    run_list_test "scfi-enter-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-leave-1"
+    run_list_test "scfi-leave-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-bp-sp-1"
+    run_list_test "scfi-bp-sp-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-bp-sp-2"
+    run_list_test "scfi-bp-sp-2" "--scfi=experimental --warn"
+    run_dump_test "scfi-callee-saved-1"
+    run_list_test "scfi-callee-saved-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-callee-saved-2"
+    run_list_test "scfi-callee-saved-2" "--scfi=experimental --warn"
+    run_dump_test "scfi-callee-saved-3"
+    run_list_test "scfi-callee-saved-3" "--scfi=experimental --warn"
+    run_dump_test "scfi-callee-saved-4"
+    run_list_test "scfi-callee-saved-4" "--scfi=experimental --warn"
+    run_dump_test "scfi-dyn-stack-1"
+    run_list_test "scfi-dyn-stack-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-indirect-mov-1"
+    run_list_test "scfi-indirect-mov-1" "--scfi=experimental --warn"
+    run_dump_test "scfi-indirect-mov-2"
+    run_list_test "scfi-indirect-mov-2" "--scfi=experimental --warn"
+    run_dump_test "scfi-indirect-mov-3"
+    run_list_test "scfi-indirect-mov-3" "--scfi=experimental --warn"
+    run_dump_test "scfi-indirect-mov-4"
+    run_list_test "scfi-indirect-mov-4" "--scfi=experimental --warn"
+    run_dump_test "scfi-selfalign-func-1"
+    run_list_test "scfi-selfalign-func-1" "--scfi=experimental --warn"
+}
+
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH, V5 16/16] gas/NEWS: announce the new SCFI command line option
  2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
                   ` (14 preceding siblings ...)
  2024-01-11  7:48 ` [PATCH, V5 15/16] gas: testsuite: add an x86_64 testsuite for SCFI Indu Bhagat
@ 2024-01-11  7:48 ` Indu Bhagat
  15 siblings, 0 replies; 19+ messages in thread
From: Indu Bhagat @ 2024-01-11  7:48 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[Changes in V5]
  - Adapt text to indicate experimental support for SCFI.  Mention
    System V AMD64 ABI.

[No changes in V4, V3]
---
 gas/NEWS | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/gas/NEWS b/gas/NEWS
index 74df7e61349..43662fa1360 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -1,4 +1,7 @@
 -*- text -*-
+* Experimental support in GAS to synthesize CFI for ABI-conformant,
+  hand-written asm using the new command line option --scfi=experimental on
+  x86-64.  Only System V AMD64 ABI is supported.
 
 * Add support for 'armv8.9-a' and 'armv9.4-a' for -march in Arm GAS.
 
-- 
2.41.0


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH, V5 11/16] opcodes: i386: new marker for insns that implicitly update stack pointer
  2024-01-11  7:48 ` [PATCH, V5 11/16] opcodes: i386: new marker for insns that implicitly update stack pointer Indu Bhagat
@ 2024-01-11  7:57   ` Jan Beulich
  0 siblings, 0 replies; 19+ messages in thread
From: Jan Beulich @ 2024-01-11  7:57 UTC (permalink / raw)
  To: Indu Bhagat; +Cc: binutils

On 11.01.2024 08:48, Indu Bhagat wrote:
> [Changes from V4 to V5]
>   - IMPLICIT_STACK_OP as a new constraint (was attribute in V4).
> [End of Changes from V4 to V5]
> 
> [New in V4]
> 
> Some x86 instructions affect the stack pointer implicitly.  Add a new
> operand constraint to reflect this.  This will be useful for SCFI
> implmentation to ensure its correctness.
> 
> Mark all push, pop, call, ret, enter, leave, INT, iret instructions.
> 
> opcodes/
> 	* i386-gen.c: Update opcode_modifiers.
> 	* i386-opc.h: Add a new constraint.
> 	* i386-opc.tbl: Update the affected instructions.
> 	* i386-tbl.h: Regenerated.

This and patch 10 are okay as long as not committed ahead of time.

Jan

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH, V5 14/16] opcodes: i386-reg.tbl: Add a comment to reflect dependency on ordering
  2024-01-11  7:48 ` [PATCH, V5 14/16] opcodes: i386-reg.tbl: Add a comment to reflect dependency on ordering Indu Bhagat
@ 2024-01-11  7:59   ` Jan Beulich
  0 siblings, 0 replies; 19+ messages in thread
From: Jan Beulich @ 2024-01-11  7:59 UTC (permalink / raw)
  To: Indu Bhagat; +Cc: binutils

On 11.01.2024 08:48, Indu Bhagat wrote:
> [New in V3. No changes since V3]
> 
> The ginsn representation keeps the DWARF register number of the
> operands.  The API ginsn_dw2_regnum relies on the the relative ordering
> of these register entries in the table.  Add a comment to make it clear.
> 
> opcodes/
> 	* i386-reg.tbl: Add a comment.

Okay (not sure if I said so on an earlier version already). This is fine
to go in at any time.

Jan

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2024-01-11  7:59 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-11  7:48 [PATCH, V5 00/16] Experimental support for synthesizing CFI for hand-written asm Indu Bhagat
2024-01-11  7:48 ` [PATCH, V5 01/16] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections Indu Bhagat
2024-01-11  7:48 ` [PATCH, V5 02/16] gas: dw2gencfi: use all_cfi_sections instead of cfi_sections Indu Bhagat
2024-01-11  7:48 ` [PATCH, V5 03/16] gas: dw2gencfi: expose a new cfi_set_last_fde API Indu Bhagat
2024-01-11  7:48 ` [PATCH, V5 04/16] gas: dw2gencfi: move some tc_* defines to the header file Indu Bhagat
2024-01-11  7:48 ` [PATCH, V5 05/16] gas: dw2gencfi: expose dot_cfi_sections for scfidw2gen Indu Bhagat
2024-01-11  7:48 ` [PATCH, V5 06/16] gas: dw2gencfi: externalize the all_cfi_sections Indu Bhagat
2024-01-11  7:48 ` [PATCH, V5 07/16] gas: add new command line option --scfi=experimental Indu Bhagat
2024-01-11  7:48 ` [PATCH, V5 08/16] gas: scfidw2gen: new functionality to prepare for SCFI Indu Bhagat
2024-01-11  7:48 ` [PATCH, V5 09/16] opcodes: i386: fix dw2_regnum data type in reg_entry Indu Bhagat
2024-01-11  7:48 ` [PATCH, V5 10/16] opcodes: gas: i386: define and use Rex2 as attribute not constraint Indu Bhagat
2024-01-11  7:48 ` [PATCH, V5 11/16] opcodes: i386: new marker for insns that implicitly update stack pointer Indu Bhagat
2024-01-11  7:57   ` Jan Beulich
2024-01-11  7:48 ` [PATCH, V5 12/16] gas: synthesize CFI for hand-written asm Indu Bhagat
2024-01-11  7:48 ` [PATCH, V5 13/16] gas: doc: update documentation for the new listing option Indu Bhagat
2024-01-11  7:48 ` [PATCH, V5 14/16] opcodes: i386-reg.tbl: Add a comment to reflect dependency on ordering Indu Bhagat
2024-01-11  7:59   ` Jan Beulich
2024-01-11  7:48 ` [PATCH, V5 15/16] gas: testsuite: add an x86_64 testsuite for SCFI Indu Bhagat
2024-01-11  7:48 ` [PATCH, V5 16/16] gas/NEWS: announce the new SCFI command line option Indu Bhagat

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).