public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH,V1 0/9] Synthesize CFI for hand-written asm
@ 2023-10-26 19:13 Indu Bhagat
  2023-10-26 19:13 ` [PATCH,V1 1/9] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections Indu Bhagat
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Indu Bhagat @ 2023-10-26 19:13 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.  The RFC patch series were sent earlier 
(https://sourceware.org/pipermail/binutils/2023-September/129560.html).
I have addressed the review comments, tested the implementation further
and created the V1 series here.  If you prefer to try out the code etc.,
the implementation is also available in the branch
users/ibhagat/try-scfi-v2.

Each patch in this series has notes on how it has evolved since the RFC
posting, but here is a brief summary:
  - Updated some documentation stubs.
  - ginsn are now emitted in the listing output by using the flag -i.
  - GAS now warns once per file if there are CFI directives embedded in
    the user input of asm.  The warning message is:
    "Warning: --scfi=all ignores some user-specified CFI directives"
  - A distinct set of pseudo_typeS are now defined in scfidwgen.c to
    handle (i.e. ignore most of) the .cfi_* directives when --scfi is in
    effect.  Handling of .cfi_sections is TBD.
  - Bug fixes and other improvements.
  - Beginning of a asm block is identified with a .type  foo, @function.
    If the user does not close the block with a .size foo, .-foo, the
    asm block is assumed to continue until the next .type bar, @function
    directive.  The testcases scfi-asm-marker-* should be helpful.

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.  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 (9):
  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: add new command line option --scfi[=all,none]
  gas: scfidw2gen: new functionality to prepapre for SCFI
  gas: synthesize CFI for hand-written asm
  gas: testsuite: add a x86_64 testsuite for SCFI
  gas/NEWS: announce the new command line option

 gas/Makefile.am                               |    6 +
 gas/Makefile.in                               |   18 +-
 gas/NEWS                                      |    2 +
 gas/as.c                                      |   27 +-
 gas/as.h                                      |    8 +
 gas/config/obj-elf.c                          |   17 +
 gas/config/tc-i386.c                          |  730 +++++++++-
 gas/config/tc-i386.h                          |   21 +
 gas/doc/as.texi                               |   12 +
 gas/dw2gencfi.c                               |   42 +-
 gas/dw2gencfi.h                               |   14 +
 gas/ginsn.c                                   | 1225 ++++++++++++++++
 gas/ginsn.h                                   |  396 ++++++
 gas/listing.h                                 |    1 +
 gas/read.c                                    |   21 +-
 gas/scfi.c                                    | 1226 +++++++++++++++++
 gas/scfi.h                                    |   38 +
 gas/scfidw2gen.c                              |  325 +++++
 gas/scfidw2gen.h                              |   37 +
 gas/subsegs.c                                 |    1 +
 gas/subsegs.h                                 |    2 +
 gas/symbols.c                                 |    3 +
 gas/testsuite/gas/scfi/README                 |   19 +
 gas/testsuite/gas/scfi/x86_64/scfi-add-1.d    |   25 +
 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    |   36 +
 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       |   28 +
 .../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       |   24 +
 .../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       |   31 +
 .../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  |   31 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l  |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s  |   22 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d  |   57 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l  |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s  |   49 +
 .../gas/scfi/x86_64/scfi-callee-saved-1.d     |   40 +
 .../gas/scfi/x86_64/scfi-callee-saved-1.l     |    2 +
 .../gas/scfi/x86_64/scfi-callee-saved-1.s     |   26 +
 .../gas/scfi/x86_64/scfi-callee-saved-2.d     |   41 +
 .../gas/scfi/x86_64/scfi-callee-saved-2.l     |    2 +
 .../gas/scfi/x86_64/scfi-callee-saved-2.s     |   42 +
 .../gas/scfi/x86_64/scfi-callee-saved-3.d     |   42 +
 .../gas/scfi/x86_64/scfi-callee-saved-3.l     |    3 +
 .../gas/scfi/x86_64/scfi-callee-saved-3.s     |   40 +
 .../gas/scfi/x86_64/scfi-callee-saved-4.d     |   40 +
 .../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    |   36 +
 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    |   28 +
 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        |   37 +
 .../gas/scfi/x86_64/scfi-cfi-label-1.l        |    2 +
 .../gas/scfi/x86_64/scfi-cfi-label-1.s        |   19 +
 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   |   23 +
 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l   |    4 +
 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s   |   23 +
 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        |   23 +
 .../gas/scfi/x86_64/scfi-dyn-stack-1.l        |    2 +
 .../gas/scfi/x86_64/scfi-dyn-stack-1.s        |   50 +
 .../gas/scfi/x86_64/scfi-fp-diag-2.l          |    3 +
 .../gas/scfi/x86_64/scfi-fp-diag-2.s          |   55 +
 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.d |   26 +
 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.l |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.s |   13 +
 .../gas/scfi/x86_64/scfi-indirect-mov-1.d     |   51 +
 .../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     |   41 +
 .../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     |   41 +
 .../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     |   63 +
 .../gas/scfi/x86_64/scfi-indirect-mov-4.l     |    3 +
 .../gas/scfi/x86_64/scfi-indirect-mov-4.s     |   68 +
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d    |   37 +
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l    |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s    |   40 +
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d  |   36 +
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l  |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s  |   26 +
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d  |   35 +
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l  |    2 +
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s  |   24 +
 .../gas/scfi/x86_64/scfi-selfalign-func-1.d   |   31 +
 .../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 |   26 +
 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 |   30 +
 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    |   25 +
 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    |   31 +
 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      |   10 +
 .../gas/scfi/x86_64/scfi-unsupported-2.l      |    3 +
 .../gas/scfi/x86_64/scfi-unsupported-2.s      |   14 +
 .../gas/scfi/x86_64/scfi-unsupported-3.l      |    3 +
 .../gas/scfi/x86_64/scfi-unsupported-3.s      |   14 +
 .../gas/scfi/x86_64/scfi-unsupported-4.l      |    4 +
 .../gas/scfi/x86_64/scfi-unsupported-4.s      |   23 +
 .../gas/scfi/x86_64/scfi-unsupported-cfg-1.l  |    3 +
 .../gas/scfi/x86_64/scfi-unsupported-cfg-1.s  |   53 +
 .../gas/scfi/x86_64/scfi-unsupported-drap-1.l |    4 +
 .../gas/scfi/x86_64/scfi-unsupported-drap-1.s |   75 +
 gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp |   98 ++
 129 files changed, 6575 insertions(+), 34 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/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-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-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-ignore-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.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-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-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-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-x86-64.exp

-- 
2.40.0


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

* [PATCH,V1 1/9] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections
  2023-10-26 19:13 [PATCH,V1 0/9] Synthesize CFI for hand-written asm Indu Bhagat
@ 2023-10-26 19:13 ` Indu Bhagat
  2023-10-26 19:13 ` [PATCH,V1 2/9] gas: dw2gencfi: use all_cfi_sections instead of cfi_sections Indu Bhagat
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Indu Bhagat @ 2023-10-26 19:13 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[No changes from the RFC patch set]

- 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 f57479457c4..cbfee0a0169 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.40.0


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

* [PATCH,V1 2/9] gas: dw2gencfi: use all_cfi_sections instead of cfi_sections
  2023-10-26 19:13 [PATCH,V1 0/9] Synthesize CFI for hand-written asm Indu Bhagat
  2023-10-26 19:13 ` [PATCH,V1 1/9] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections Indu Bhagat
@ 2023-10-26 19:13 ` Indu Bhagat
  2023-10-26 19:13 ` [PATCH,V1 3/9] gas: dw2gencfi: expose a new cfi_set_last_fde API Indu Bhagat
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Indu Bhagat @ 2023-10-26 19:13 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[No changes from RFC patch set]

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 cbfee0a0169..5fcfb373935 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.40.0


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

* [PATCH,V1 3/9] gas: dw2gencfi: expose a new cfi_set_last_fde API
  2023-10-26 19:13 [PATCH,V1 0/9] Synthesize CFI for hand-written asm Indu Bhagat
  2023-10-26 19:13 ` [PATCH,V1 1/9] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections Indu Bhagat
  2023-10-26 19:13 ` [PATCH,V1 2/9] gas: dw2gencfi: use all_cfi_sections instead of cfi_sections Indu Bhagat
@ 2023-10-26 19:13 ` Indu Bhagat
  2023-10-26 19:13 ` [PATCH,V1 4/9] gas: dw2gencfi: move some tc_* defines to the header file Indu Bhagat
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Indu Bhagat @ 2023-10-26 19:13 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[No changes from the RFC patch set]

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 5fcfb373935..a69854ddc0c 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 962fdcaed17..aa8693df342 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.40.0


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

* [PATCH,V1 4/9] gas: dw2gencfi: move some tc_* defines to the header file
  2023-10-26 19:13 [PATCH,V1 0/9] Synthesize CFI for hand-written asm Indu Bhagat
                   ` (2 preceding siblings ...)
  2023-10-26 19:13 ` [PATCH,V1 3/9] gas: dw2gencfi: expose a new cfi_set_last_fde API Indu Bhagat
@ 2023-10-26 19:13 ` Indu Bhagat
  2023-10-26 19:13 ` [PATCH,V1 5/9] gas: add new command line option --scfi[=all,none] Indu Bhagat
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Indu Bhagat @ 2023-10-26 19:13 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[No changes from the RFC patch set]

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 a69854ddc0c..cdef8d09978 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 aa8693df342..f574826456a 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.40.0


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

* [PATCH,V1 5/9] gas: add new command line option --scfi[=all,none]
  2023-10-26 19:13 [PATCH,V1 0/9] Synthesize CFI for hand-written asm Indu Bhagat
                   ` (3 preceding siblings ...)
  2023-10-26 19:13 ` [PATCH,V1 4/9] gas: dw2gencfi: move some tc_* defines to the header file Indu Bhagat
@ 2023-10-26 19:13 ` Indu Bhagat
  2023-10-26 19:13 ` [PATCH,V1 6/9] gas: scfidw2gen: new functionality to prepapre for SCFI Indu Bhagat
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Indu Bhagat @ 2023-10-26 19:13 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[Changes from the RFC patch set]
  - Update documentation to make it clearer.
  - Guard the --scfi option processing behind both TARGET_USE_GINSN and
    TARGET_USE_GINSN.
[End of changes]

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

The option --scfi=all will also ignore many 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 --scfi=[all,none] may see more options added in future.  For
example, --scfi=inline, for dealing with inline asm may be added in the
future.  In this option, the GNU assembler will 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.
	(parse_args): Likewise.
        * as.h (enum synth_cfi_type): Define new type.
        * doc/as.texi: Document the new option.
---
 gas/as.c        | 22 +++++++++++++++++++++-
 gas/as.h        |  8 ++++++++
 gas/doc/as.texi | 12 ++++++++++++
 3 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/gas/as.c b/gas/as.c
index 6839c841588..97b0ed38fb6 100644
--- a/gas/as.c
+++ b/gas/as.c
@@ -372,6 +372,11 @@ Options:\n\
   -R                      fold data section into text section\n"));
   fprintf (stream, _("\
   --reduce-memory-overheads ignored\n"));
+#if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
+  fprintf (stream, _("\
+  --scfi=[all,none]	  (default: all)\n\
+			  Synthesize DWARF CFI for hand-written asm\n"));
+# endif
   fprintf (stream, _("\
   --statistics            print various measured statistics from execution\n"));
   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.  */
     };
@@ -586,6 +592,9 @@ parse_args (int * pargc, char *** pargv)
     ,{"no-pad-sections", no_argument, NULL, OPTION_NO_PAD_SECTIONS}
     ,{"no-warn", no_argument, NULL, 'W'}
     ,{"reduce-memory-overheads", no_argument, NULL, OPTION_REDUCE_MEMORY_OVERHEADS}
+#if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
+    ,{"scfi", no_argument, NULL, OPTION_SCFI}
+#endif
     ,{"statistics", no_argument, NULL, OPTION_STATISTICS}
     ,{"strip-local-absolute", no_argument, NULL, OPTION_STRIP_LOCAL_ABSOLUTE}
     ,{"version", no_argument, NULL, OPTION_VERSION}
@@ -982,6 +991,17 @@ 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, "all") == 0)
+	    flag_synth_cfi = SYNTH_CFI_ALL;
+	  else if (strcasecmp (optarg, "none") == 0)
+	    flag_synth_cfi = SYNTH_CFI_NONE;
+	  else
+	    as_fatal (_("Invalid --scfi= option: `%s'"), 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 46dd0d0776d..378653e2200 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_ALL,
+};
+
+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 52571d95dd2..cfc1078c36b 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=[all,none]}]
  [@b{--sectname-subst}]
  [@b{--size-check=[error|warning]}]
  [@b{--statistics}]
@@ -932,6 +933,17 @@ Ignored.  Supported for compatibility with tools that apss the same option to
 both the assembler and the linker.
 
 @ifset ELF
+@item --scfi=none
+@itemx --scfi=all
+These options control 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=all} is not intended to be used for compiler-generated code,
+including inline assembly.
+
+The input asm must begin with the @code{.type} directive, and should ideally be
+closed off using a @code{.size} directive.
+
 @item --sectname-subst
 Honor substitution sequences in section names.
 @ifclear man
-- 
2.40.0


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

* [PATCH,V1 6/9] gas: scfidw2gen: new functionality to prepapre for SCFI
  2023-10-26 19:13 [PATCH,V1 0/9] Synthesize CFI for hand-written asm Indu Bhagat
                   ` (4 preceding siblings ...)
  2023-10-26 19:13 ` [PATCH,V1 5/9] gas: add new command line option --scfi[=all,none] Indu Bhagat
@ 2023-10-26 19:13 ` Indu Bhagat
  2023-10-26 19:13 ` [PATCH,V1 7/9] gas: synthesize CFI for hand-written asm Indu Bhagat
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Indu Bhagat @ 2023-10-26 19:13 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[Changes from the RFC patch set]
  - scfidw2gen.c now defines its own set of handlers.  For many of those
    CFI directives, we simply warn ("Warning: --scfi=all ignores some
    user-specified CFI directives") and return.
  - The above-mentioned warning is issued only once per file.
  - The advantage of adding new set of handlers in scfidwgen.c is that
    dw2gencfi now does not need to add a condition for each handler to
    simply return if --scfi is in effect (the latter was the approach in
    the RFC patch set.)
[End of changes]

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=all is in effect.  The following CFI directives, however, are not
ignored:
      - .cfi_sections  (To be handled).
      - .cfi_label
      - .cfi_signal_frame

Since the option --scfi=inline still needs to be worked out, the
implementation in scfidw2gen may need to be adjusted later.

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       |  17 ++-
 gas/scfidw2gen.c | 325 +++++++++++++++++++++++++++++++++++++++++++++++
 gas/scfidw2gen.h |  37 ++++++
 5 files changed, 385 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 0e98ca3ec85..e174305ca62 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 fae3a47c144..87428bc46b8 100644
--- a/gas/Makefile.in
+++ b/gas/Makefile.in
@@ -170,9 +170,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)
@@ -581,6 +581,7 @@ GAS_CFILES = \
 	read.c \
 	remap.c \
 	sb.c \
+	scfidw2gen.c \
 	sframe-opt.c \
 	stabs.c \
 	subsegs.c \
@@ -615,6 +616,7 @@ HFILES = \
 	output-file.h \
 	read.h \
 	sb.h \
+	scfidw2gen.h \
 	subsegs.h \
 	symbols.h \
 	tc.h \
@@ -1337,6 +1339,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 826156db3fe..9068072493a 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,16 @@ pobegin (void)
   pop_insert (potable);
 
   /* Now CFI ones.  */
-  pop_table_name = "cfi";
-  cfi_pop_insert ();
+  if (flag_synth_cfi)
+    {
+      pop_table_name = "scfi";
+      scfi_pop_insert ();
+    }
+  else
+    {
+      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..613ef919a1e
--- /dev/null
+++ b/gas/scfidw2gen.c
@@ -0,0 +1,325 @@
+/* scfidw2gen.c - Support for emission of synthesized Dwarf2 CFI.
+   Copyright (C) 2003-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 "ginsn.h"
+#include "scfi.h"
+#include "dw2gencfi.h"
+#include "subsegs.h"
+#include "scfidw2gen.h"
+
+#if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN)
+
+static int scfi_ignore_warn_once = 0;
+
+static void dot_scfi_sections (int);
+static void dot_scfi_ignore (int);
+static void dot_scfi (int);
+
+const pseudo_typeS scfi_pseudo_table[] =
+  {
+    { "cfi_sections", dot_scfi_sections, 0 }, /* 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_signal_frame", dot_scfi, CFI_signal_frame }, /* No ignore.  */
+    { "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_label", dot_scfi, CFI_label }, /* No ignore.  */
+    { "cfi_val_offset", dot_scfi_ignore, 0 },
+    { NULL, NULL, 0 }
+  };
+
+static void
+dot_scfi_ignore (int ignored ATTRIBUTE_UNUSED)
+{
+  gas_assert (flag_synth_cfi);
+
+  if (scfi_ignore_warn_once == 0)
+    {
+      as_warn (_("--scfi=all ignores some user-specified CFI directives"));
+      scfi_ignore_warn_once = 1;
+    }
+  ignore_rest_of_line ();
+}
+
+static void
+dot_scfi_sections (int ignored ATTRIBUTE_UNUSED)
+{
+  /* To be implemented.  */
+  return;
+}
+
+static void
+scfi_process_cfi_label (void)
+{
+  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)
+{
+  ginsnS *ginsn;
+
+  ginsn = ginsn_new_phantom (symbol_temp_new_now ());
+  frch_ginsn_data_append (ginsn);
+
+  scfi_op_add_signal_frame (ginsn);
+}
+
+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 ();
+    }
+}
+
+void
+scfi_dot_cfi_startproc (symbolS *start_sym)
+{
+  if (frchain_now->frch_cfi_data != NULL)
+    {
+      as_bad (_("previous CFI entry not closed (missing .cfi_endproc)"));
+      return;
+    }
+
+  cfi_new_fde (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 (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 (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, 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 (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 ();
+    }
+}
+
+#else
+
+static void
+dot_scfi_dummy (int ignored ATTRIBUTE_UNUSED)
+{
+  as_bad (_("SCFI is not supported for this target"));
+  ignore_rest_of_line ();
+}
+
+const pseudo_typeS scfi_pseudo_table[] =
+  {
+    { "cfi_sections", dot_scfi_dummy, 0 },
+    { "cfi_startproc", dot_scfi_dummy, 0 },
+    { "cfi_endproc", dot_scfi_dummy, 0 },
+    { "cfi_fde_data", dot_scfi_dummy, 0 },
+    { "cfi_def_cfa", dot_scfi_dummy, 0 },
+    { "cfi_def_cfa_register", dot_scfi_dummy, 0 },
+    { "cfi_def_cfa_offset", dot_scfi_dummy, 0 },
+    { "cfi_adjust_cfa_offset", dot_scfi_dummy, 0 },
+    { "cfi_offset", dot_scfi_dummy, 0 },
+    { "cfi_rel_offset", dot_scfi_dummy, 0 },
+    { "cfi_register", dot_scfi_dummy, 0 },
+    { "cfi_return_column", dot_scfi_dummy, 0 },
+    { "cfi_restore", dot_scfi_dummy, 0 },
+    { "cfi_undefined", dot_scfi_dummy, 0 },
+    { "cfi_same_value", dot_scfi_dummy, 0 },
+    { "cfi_remember_state", dot_scfi_dummy, 0 },
+    { "cfi_restore_state", dot_scfi_dummy, 0 },
+    { "cfi_window_save", dot_scfi_dummy, 0 },
+    { "cfi_negate_ra_state", dot_scfi_dummy, 0 },
+    { "cfi_escape", dot_scfi_dummy, 0 },
+    { "cfi_signal_frame", dot_scfi_dummy, 0 },
+    { "cfi_personality", dot_scfi_dummy, 0 },
+    { "cfi_personality_id", dot_scfi_dummy, 0 },
+    { "cfi_lsda", dot_scfi_dummy, 0 },
+    { "cfi_val_encoded_addr", dot_scfi_dummy, 0 },
+    { "cfi_inline_lsda", dot_scfi_dummy, 0 },
+    { "cfi_label", dot_scfi_dummy, 0 },
+    { "cfi_val_offset", dot_scfi_dummy, 0 },
+    { NULL, NULL, 0 }
+  };
+
+#endif
diff --git a/gas/scfidw2gen.h b/gas/scfidw2gen.h
new file mode 100644
index 00000000000..c3d6f80b3db
--- /dev/null
+++ b/gas/scfidw2gen.h
@@ -0,0 +1,37 @@
+/* scfidw2gen.h - Support for emitting synthesized Dwarf2 CFI.
+   Copyright (C) 2003-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 int all_cfi_sections;
+
+extern const pseudo_typeS scfi_pseudo_table[];
+
+void scfi_dot_cfi_startproc (symbolS *start_sym);
+void scfi_dot_cfi_endproc (symbolS *end_sym);
+void scfi_dot_cfi (int arg, unsigned reg1, unsigned reg2, offsetT offset,
+		   const char *name, symbolS *advloc);
+
+#endif
+
-- 
2.40.0


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

* [PATCH,V1 7/9] gas: synthesize CFI for hand-written asm
  2023-10-26 19:13 [PATCH,V1 0/9] Synthesize CFI for hand-written asm Indu Bhagat
                   ` (5 preceding siblings ...)
  2023-10-26 19:13 ` [PATCH,V1 6/9] gas: scfidw2gen: new functionality to prepapre for SCFI Indu Bhagat
@ 2023-10-26 19:13 ` Indu Bhagat
  2023-10-26 19:13 ` [PATCH,V1 8/9] gas: testsuite: add a x86_64 testsuite for SCFI Indu Bhagat
  2023-10-26 19:13 ` [PATCH,V1 9/9] gas/NEWS: announce the new command line option Indu Bhagat
  8 siblings, 0 replies; 10+ messages in thread
From: Indu Bhagat @ 2023-10-26 19:13 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[Changes from the RFC patch set]
  - Fixed few more unhandled cases in the x86 creation of ginsn.
  - Added skeleton functionality to print ginsn in the listing output of
    GAS.  The sub-option chosen is "i":
    $ as --help
    -a[sub-option...]    turn on listings
                         Sub-options [default hls]:
                         ...
                         d      omit debugging directives
                         ...
                         i      include ginsn and synthesized CFI info
    Documentation for ginsn is still pending.
  - Other bugfixes in the SCFI machinery.
[End of changes]

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
generates 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_LDS, GINSN_TYPE_STS (load and store to
stack) 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 AMD64 ABI at this time.  If the user
specifies, --scfi --32, GAS issues an error:

  "Fatal error: Synthesizing CFI 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 (ginsn_new): New functionality to generate
	ginsns.
	(x86_scfi_callee_saved_p): New function.
	(ginsn_dw2_regnum): Likewise.
	(ginsn_set_where): Likewise.
	(x86_ginsn_alu): Likewise.
	(x86_ginsn_move): Likewise.
	(x86_ginsn_lea): Likewise.
	(x86_ginsn_jump): Likewise.
	(x86_ginsn_jump_cond): Likewise.
	(md_assemble): Invoke 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_NUM_REGS): 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.
	* 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 |   17 +
 gas/config/tc-i386.c |  730 ++++++++++++++++++++++++-
 gas/config/tc-i386.h |   21 +
 gas/ginsn.c          | 1225 +++++++++++++++++++++++++++++++++++++++++
 gas/ginsn.h          |  396 ++++++++++++++
 gas/listing.h        |    1 +
 gas/read.c           |    4 +
 gas/scfi.c           | 1226 ++++++++++++++++++++++++++++++++++++++++++
 gas/scfi.h           |   38 ++
 gas/subsegs.c        |    1 +
 gas/subsegs.h        |    2 +
 gas/symbols.c        |    3 +
 15 files changed, 3685 insertions(+), 7 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 e174305ca62..b477d74cb53 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 87428bc46b8..99edb365a00 100644
--- a/gas/Makefile.in
+++ b/gas/Makefile.in
@@ -167,12 +167,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)
@@ -570,6 +571,7 @@ GAS_CFILES = \
 	flonum-mult.c \
 	frags.c \
 	gen-sframe.c \
+	ginsn.c \
 	hash.c \
 	input-file.c \
 	input-scrub.c \
@@ -582,6 +584,7 @@ GAS_CFILES = \
 	remap.c \
 	sb.c \
 	scfidw2gen.c \
+	scfi.c \
 	sframe-opt.c \
 	stabs.c \
 	subsegs.c \
@@ -606,6 +609,7 @@ HFILES = \
 	flonum.h \
 	frags.h \
 	gen-sframe.h \
+	ginsn.h \
 	hash.h \
 	input-file.h \
 	itbl-lex.h \
@@ -617,6 +621,7 @@ HFILES = \
 	read.h \
 	sb.h \
 	scfidw2gen.h \
+	scfi.h \
 	subsegs.h \
 	symbols.h \
 	tc.h \
@@ -1325,6 +1330,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@
@@ -1339,6 +1345,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 97b0ed38fb6..9f962fd5816 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\
@@ -1089,6 +1091,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 eaf2d6a2f29..0702550b34a 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
@@ -2302,6 +2303,14 @@ 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 ();
 }
 
@@ -2490,6 +2499,14 @@ obj_elf_type (int ignore ATTRIBUTE_UNUSED)
 	elfsym->symbol.flags &= ~mask;
     }
 
+  if (S_IS_FUNCTION (sym) && flag_synth_cfi)
+    {
+      /* Wrap up processing the previous block of ginsns first.  */
+      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 57ae6c522a7..ffa0c3c1b9d 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"
@@ -193,8 +194,11 @@ static unsigned int x86_isa_1_used;
 static unsigned int x86_feature_2_used;
 /* Generate x86 used ISA and feature properties.  */
 static unsigned int x86_used_note = DEFAULT_X86_USED_NOTE;
+
 #endif
 
+static ginsnS *ginsn_new (symbolS *sym, enum ginsn_gen_mode gmode);
+
 static const char *default_arch = DEFAULT_ARCH;
 
 /* parse_register() returns this when a register alias cannot be used.  */
@@ -5116,6 +5120,711 @@ static INLINE bool may_need_pass2 (const insn_template *t)
 	       && t->base_opcode == 0x63);
 }
 
+bool
+x86_scfi_callee_saved_p (uint32_t 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;
+}
+
+static uint32_t
+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;
+
+  if (ireg->dw2_regnum[0] == Dw2Inval && ireg->dw2_regnum[1] == Dw2Inval)
+    return dwarf_reg;
+
+  dwarf_reg = ireg->dw2_regnum[flag_code >> 1];
+  if (dwarf_reg == Dw2Inval)
+    {
+      temp = ireg + 16;
+      dwarf_reg = ginsn_dw2_regnum (temp);
+    }
+
+  if (dwarf_reg == Dw2Inval)
+    gas_assert (1); /* Needs to be addressed.  */
+
+  return (uint32_t) dwarf_reg;
+}
+
+static void
+ginsn_set_where (ginsnS* ginsn)
+{
+  const char *file;
+  unsigned int line;
+  file = as_where (&line);
+  ginsn_set_file_line (ginsn, file, line);
+}
+
+static ginsnS *
+x86_ginsn_alu (i386_insn insn, symbolS *insn_end_sym)
+{
+  offsetT src_imm;
+  uint32_t dw2_regnum;
+  enum ginsn_src_type src_type;
+  enum ginsn_dst_type dst_type;
+  ginsnS *ginsn = NULL;
+
+  /* FIXME - create ginsn for REG_SP target 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 imm, %reg.
+     and imm, %reg only at this time for SCFI. */
+  if (!(insn.tm.extension_opcode == 0
+	|| insn.tm.extension_opcode == 4
+	|| insn.tm.extension_opcode == 5))
+    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 (insn.imm_operands == 1
+      && insn.op[0].imms->X_op == O_symbol)
+    return ginsn;
+
+  gas_assert (insn.imm_operands == 1
+	      && insn.op[0].imms->X_op == O_constant);
+  src_imm = insn.op[0].imms->X_add_number;
+  /* The second operand may be a register or indirect access.  */
+  if (insn.mem_operands == 1 && insn.base_reg)
+    {
+      dw2_regnum = ginsn_dw2_regnum (insn.base_reg);
+      src_type = GINSN_SRC_INDIRECT;
+      dst_type = GINSN_DST_INDIRECT;
+    }
+  else if (insn.mem_operands == 1 && insn.index_reg)
+    {
+      dw2_regnum = ginsn_dw2_regnum (insn.index_reg);
+      src_type = GINSN_SRC_INDIRECT;
+      dst_type = GINSN_DST_INDIRECT;
+    }
+  else
+    {
+      gas_assert (insn.reg_operands == 1);
+      dw2_regnum = ginsn_dw2_regnum (insn.op[1].regs);
+      src_type = GINSN_SRC_REG;
+      dst_type = GINSN_DST_REG;
+    }
+
+  /* For ginsn, keep the imm as second src operand.  */
+  if (insn.tm.extension_opcode == 5)
+    ginsn = ginsn_new_sub (insn_end_sym, true,
+			   src_type, dw2_regnum, 0,
+			   GINSN_SRC_IMM, 0, src_imm,
+			   dst_type, dw2_regnum, 0);
+  else if (insn.tm.extension_opcode == 4)
+    ginsn = ginsn_new_and (insn_end_sym, true,
+			   src_type, dw2_regnum, 0,
+			   GINSN_SRC_IMM, 0, src_imm,
+			   dst_type, dw2_regnum, 0);
+  else if (insn.tm.extension_opcode == 0)
+    ginsn = ginsn_new_add (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;
+}
+
+static ginsnS *
+x86_ginsn_move (i386_insn insn, symbolS *insn_end_sym)
+{
+  ginsnS *ginsn;
+  uint16_t opcode;
+  uint32_t dst_reg;
+  uint32_t src_reg;
+  offsetT dst_disp;
+  offsetT src_disp;
+  const reg_entry *dst = NULL;
+  const reg_entry *src = NULL;
+  enum ginsn_dst_type dst_type;
+  enum ginsn_src_type src_type;
+
+  opcode = insn.tm.base_opcode;
+  src_type = GINSN_SRC_REG;
+  src_disp = dst_disp = 0;
+  dst_type = GINSN_DST_REG;
+
+  if (opcode == 0x8b)
+    {
+      /* mov  disp(%reg), %reg.  */
+      if (insn.mem_operands && insn.base_reg)
+	{
+	  src = insn.base_reg;
+	  if (insn.disp_operands == 1)
+	    src_disp = insn.op[0].disps->X_add_number;
+	  src_type = GINSN_SRC_INDIRECT;
+	}
+      else
+	src = insn.op[0].regs;
+
+      dst = insn.op[1].regs;
+    }
+  else if (opcode == 0x89 || opcode == 0x88)
+    {
+      /* mov %reg, disp(%reg).  */
+      src = insn.op[0].regs;
+      if (insn.mem_operands && insn.base_reg)
+	{
+	  dst = insn.base_reg;
+	  if (insn.disp_operands == 1)
+	    dst_disp = insn.op[1].disps->X_add_number;
+	  dst_type = GINSN_DST_INDIRECT;
+	}
+      else
+	dst = insn.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;
+}
+
+static ginsnS *
+x86_ginsn_lea (i386_insn insn, symbolS *insn_end_sym)
+{
+  offsetT src_disp = 0;
+  ginsnS *ginsn = NULL;
+  uint32_t base_reg;
+  uint32_t index_reg;
+  offsetT index_scale;
+  uint32_t dst_reg;
+
+  if (!insn.index_reg && !insn.base_reg)
+    {
+      /* lea symbol, %rN.  */
+      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
+      /* FIXME - Skip encoding information about the symbol.
+	 This is TBD_GINSN_INFO_LOSS, but it is fine if the mode is
+	 GINSN_GEN_SCFI.  */
+      ginsn = ginsn_new_mov (insn_end_sym, false,
+			     GINSN_SRC_IMM, 0xf /* arbitrary const.  */, 0,
+			     GINSN_DST_REG, dst_reg, 0);
+    }
+  else if (insn.base_reg && !insn.index_reg)
+    {
+      /* lea    -0x2(%base),%dst.  */
+      base_reg = ginsn_dw2_regnum (insn.base_reg);
+      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
+
+      if (insn.disp_operands)
+	src_disp = insn.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 (!insn.base_reg && insn.index_reg)
+    {
+      /* lea (,%index,imm), %dst.  */
+      /* FIXME - Skip encoding an explicit multiply operation, instead use
+	 GINSN_TYPE_OTHER.  This is TBD_GINSN_INFO_LOSS, but it is fine if
+	 the mode is GINSN_GEN_SCFI.  */
+      index_scale = insn.log2_scale_factor;
+      index_reg = ginsn_dw2_regnum (insn.index_reg);
+      dst_reg = ginsn_dw2_regnum (insn.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);
+    }
+  else
+    {
+      /* lea disp(%base,%index,imm) %dst.  */
+      /* FIXME - Skip encoding information about the disp and imm for index
+	 reg.  This is TBD_GINSN_INFO_LOSS, but it is fine if the mode is
+	 GINSN_GEN_SCFI.  */
+      base_reg = ginsn_dw2_regnum (insn.base_reg);
+      index_reg = ginsn_dw2_regnum (insn.index_reg);
+      dst_reg = ginsn_dw2_regnum (insn.op[1].regs);
+      /* Generate an ADD ginsn.  */
+      ginsn = ginsn_new_add (insn_end_sym, true,
+			     GINSN_SRC_REG, base_reg, 0,
+			     GINSN_SRC_REG, index_reg, 0,
+			     GINSN_DST_REG, dst_reg, 0);
+    }
+
+  ginsn_set_where (ginsn);
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_jump (i386_insn insn, symbolS *insn_end_sym)
+{
+  ginsnS *ginsn = NULL;
+  symbolS *src_symbol;
+
+  gas_assert (insn.disp_operands == 1);
+
+  if (insn.op[0].disps->X_op == O_symbol)
+    {
+      src_symbol = insn.op[0].disps->X_add_symbol;
+      /* The jump target is expected to be a symbol with 0 addend.
+	 Assert for now to see if this assumption is true.  */
+      gas_assert (insn.op[0].disps->X_add_number == 0);
+      ginsn = ginsn_new_jump (insn_end_sym, true,
+			      GINSN_SRC_SYMBOL, 0, src_symbol);
+
+      ginsn_set_where (ginsn);
+    }
+
+  return ginsn;
+}
+
+static ginsnS *
+x86_ginsn_jump_cond (i386_insn insn, symbolS *insn_end_sym)
+{
+  ginsnS *ginsn = NULL;
+  symbolS *src_symbol;
+
+  /* TBD_GINSN_GEN_NOT_SCFI: Ignore move to or from xmm reg for mode.  */
+  if (i.tm.opcode_space == SPACE_0F)
+    return ginsn;
+
+  gas_assert (insn.disp_operands == 1);
+
+  if (insn.op[0].disps->X_op == O_symbol)
+    {
+      src_symbol = insn.op[0].disps->X_add_symbol;
+      /* The jump target is expected to be a symbol with 0 addend.
+	 Assert for now to see if this assumption is true.  */
+      gas_assert (insn.op[0].disps->X_add_number == 0);
+      ginsn = ginsn_new_jump_cond (insn_end_sym, true,
+				   GINSN_SRC_SYMBOL, 0, src_symbol);
+      ginsn_set_where (ginsn);
+    }
+  else
+    /* Catch them for now so we know what we are dealing with.  */
+    gas_assert (0);
+
+  return ginsn;
+}
+
+/* Generate one or more GAS instructions for the current machine dependent
+   instruction.
+
+   Returns the head of linked list of ginsn(s) added, if success;
+   Returns NULL if failure.  */
+
+static ginsnS *
+ginsn_new (symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
+{
+  uint16_t opcode;
+  uint32_t dw2_regnum;
+  uint32_t src2_dw2_regnum;
+  int32_t gdisp = 0;
+  ginsnS *ginsn = NULL;
+  ginsnS *ginsn_next = NULL;
+  ginsnS *ginsn_last = NULL;
+
+  /* FIXME - Need a way to check whether the decoding is sane.  The specific
+     checks around i.tm.opcode_space were added as issues were seen.  Likely
+     insufficient.  */
+
+  /* Currently supports generation of selected ginsns, sufficient for
+     the use-case of SCFI only.  To remove this condition 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
+     examples.  */
+  if (gmode != GINSN_GEN_SCFI)
+    return ginsn;
+
+  opcode = i.tm.base_opcode;
+
+  switch (opcode)
+    {
+    case 0x1:
+      /* add reg, reg.  */
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+
+      if (i.reg_operands == 2)
+	{
+	  src2_dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
+	  ginsn = ginsn_new_add (insn_end_sym, true,
+				 GINSN_SRC_REG, dw2_regnum, 0,
+				 GINSN_SRC_REG, src2_dw2_regnum, 0,
+				 GINSN_DST_REG, src2_dw2_regnum, 0);
+	  ginsn_set_where (ginsn);
+	}
+      else if (i.mem_operands && i.base_reg)
+	{
+	  src2_dw2_regnum = ginsn_dw2_regnum (i.base_reg);
+	  if (i.disp_operands == 1)
+	    gdisp = i.op[1].disps->X_add_number;
+
+	  ginsn = ginsn_new_add (insn_end_sym, true,
+				 GINSN_SRC_REG, dw2_regnum, 0,
+				 GINSN_SRC_INDIRECT, src2_dw2_regnum, gdisp,
+				 GINSN_DST_INDIRECT, src2_dw2_regnum, gdisp);
+	  ginsn_set_where (ginsn);
+	}
+      else
+	/* Catch them for now so we know what we are dealing with.  */
+	gas_assert (0);
+
+      break;
+    case 0x29:
+      /* If opcode_space == SPACE_0F, this is a movaps insn.  Skip it
+	 for GINSN_GEN_SCFI.  */
+      if (i.tm.opcode_space == SPACE_0F)
+	break;
+
+      /* sub reg, reg.  */
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+
+      if (i.reg_operands == 2)
+	{
+	  src2_dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
+	  ginsn = ginsn_new_sub (insn_end_sym, true,
+				 GINSN_SRC_REG, dw2_regnum, 0,
+				 GINSN_SRC_REG, src2_dw2_regnum, 0,
+				 GINSN_DST_REG, src2_dw2_regnum, 0);
+	  ginsn_set_where (ginsn);
+	}
+      else if (i.mem_operands && i.base_reg)
+	{
+	  src2_dw2_regnum = ginsn_dw2_regnum (i.base_reg);
+	  if (i.disp_operands == 1)
+	    gdisp = i.op[1].disps->X_add_number;
+
+	  ginsn = ginsn_new_sub (insn_end_sym, true,
+				 GINSN_SRC_REG, dw2_regnum, 0,
+				 GINSN_SRC_INDIRECT, src2_dw2_regnum, gdisp,
+				 GINSN_DST_INDIRECT, src2_dw2_regnum, gdisp);
+	  ginsn_set_where (ginsn);
+	}
+      else
+	/* Catch them for now so we know what we are dealing with.  */
+	gas_assert (0);
+
+      break;
+    case 0xa0:
+    case 0xa8:
+      /* If opcode_space != SPACE_0F, this is a test insn.  Skip it
+	 for GINSN_GEN_SCFI.  */
+      if (i.tm.opcode_space != SPACE_0F)
+	break;
+
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      /* push fs / push gs.  */
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, 8,
+			     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_STACK);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+    case 0xa1:
+    case 0xa9:
+      /* If opcode_space != SPACE_0F, this is test insn.  Skip it
+	 for GINSN_GEN_SCFI.  */
+      if (i.tm.opcode_space != SPACE_0F)
+	break;
+
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      /* pop fs / pop gs.  */
+      ginsn = ginsn_new_load (insn_end_sym, false,
+			      GINSN_SRC_STACK,
+			      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, 8,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+    case 0x50 ... 0x57:
+      /* push reg.  */
+      dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, 8,
+			     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_STACK);
+      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_STACK,
+			      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, 8,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+    case 0x68:
+    case 0x6a:
+      /* push imm. */
+      /* Skip getting the value of imm from machine instruction
+	 because for ginsn generation this is not important.  */
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, 8,
+			     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_STACK);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+    case 0x70 ... 0x7f:
+      ginsn = x86_ginsn_jump_cond (i, insn_end_sym);
+      break;
+    case 0x81:
+    case 0x83:
+      ginsn = x86_ginsn_alu (i, insn_end_sym);
+      break;
+    case 0x8b:
+      /* Move r/m64 to r64.  */
+    case 0x88:
+    case 0x89:
+      /* mov reg, reg/mem.  */
+      ginsn = x86_ginsn_move (i, insn_end_sym);
+      break;
+    case 0x8d:
+      /* lea disp(%src), %dst */
+      ginsn = x86_ginsn_lea (i, insn_end_sym);
+      break;
+    case 0x8f:
+      /* pop to mem.  */
+      ginsn = ginsn_new_load (insn_end_sym, false,
+			      GINSN_SRC_STACK,
+			      GINSN_DST_MEM, 0);
+      ginsn_set_where (ginsn);
+
+      ginsn_next = ginsn_new_add (insn_end_sym, false,
+				  GINSN_SRC_REG, REG_SP, 0,
+				  GINSN_SRC_IMM, 0, 8,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+      break;
+    case 0x9c:
+      /* pushf / pushfd / pushfq.
+	 Tracking EFLAGS register by number is not necessary.  */
+      ginsn = ginsn_new_sub (insn_end_sym, false,
+			     GINSN_SRC_REG, REG_SP, 0,
+			     GINSN_SRC_IMM, 0, 8,
+			     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_STACK);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn, ginsn_next));
+
+      break;
+    case 0xff:
+      /* push from mem.  */
+      if (i.tm.extension_opcode == 6)
+	{
+	  ginsn = ginsn_new_sub (insn_end_sym, false,
+				 GINSN_SRC_REG, REG_SP, 0,
+				 GINSN_SRC_IMM, 0, 8,
+				 GINSN_DST_REG, REG_SP, 0);
+	  ginsn_set_where (ginsn);
+
+	  ginsn_next = ginsn_new_store (insn_end_sym, false,
+					GINSN_SRC_MEM, 0,
+					GINSN_DST_STACK);
+	  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
+	    /* Catch them for now so we know what we are dealing with.  */
+	    gas_assert (0);
+	}
+      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);
+	    }
+	  else
+	    /* Catch them for now so we know what we are dealing with.  */
+	    gas_assert (0);
+	}
+      else
+	/* Catch them for now so we know what we are dealing with.  */
+	gas_assert (0);
+      break;
+    case 0xc2:
+    case 0xc3:
+      /* Near ret.  */
+      ginsn = ginsn_new_return (insn_end_sym, true);
+      ginsn_set_where (ginsn);
+      break;
+    case 0xc9:
+      /* 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_STACK,
+				   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, 8,
+				  GINSN_DST_REG, REG_SP, 0);
+      ginsn_set_where (ginsn_next);
+
+      gas_assert (!ginsn_link_next (ginsn_next, ginsn_last));
+      break;
+    case 0xe8:
+      /* 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;
+    case 0xe9:
+    case 0xeb:
+      /* If opcode_space == SPACE_0F, this is a psubw por insn.  Skip it
+	 for GINSN_GEN_SCFI.  */
+      if (i.tm.opcode_space == SPACE_0F)
+	break;
+
+      /* Unconditional jmp.  */
+      ginsn = x86_ginsn_jump (i, insn_end_sym);
+      ginsn_set_where (ginsn);
+      break;
+      /* Fall Through.  */
+    default:
+      /* TBD_GINSN_GEN_NOT_SCFI: Keep a warning, for now, to find out about
+	 possibly missed instructions affecting REG_SP or REG_FP.  These
+	 checks may not be completely exhaustive as they do not involve
+	 index / base reg.  */
+      if (i.op[0].regs)
+	{
+	  dw2_regnum = ginsn_dw2_regnum (i.op[0].regs);
+	  if (dw2_regnum == REG_SP || dw2_regnum == REG_FP)
+	    as_warn_where (last_insn.file, last_insn.line,
+			   _("SCFI: unhandled op 0x%x may cause incorrect CFI"),
+			   i.tm.base_opcode);
+	}
+      if (i.op[1].regs)
+	{
+	  dw2_regnum = ginsn_dw2_regnum (i.op[1].regs);
+	  if (dw2_regnum == REG_SP || dw2_regnum == REG_FP)
+	    as_warn_where (last_insn.file, last_insn.line,
+			   _("SCFI: unhandled op 0x%x may cause incorrect CFI"),
+			   i.tm.base_opcode);
+	}
+      /* Keep an eye on other instructions affecting control flow.  */
+      gas_assert (!i.tm.opcode_modifier.jump);
+      /* TBD_GINSN_GEN_NOT_SCFI: Skip all other opcodes uninteresting for
+	 GINSN_GEN_SCFI mode.  */
+      break;
+    }
+
+  return ginsn;
+}
+
 /* 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.  */
@@ -5128,6 +5837,7 @@ md_assemble (char *line)
   const char *end, *pass1_mnem = NULL;
   enum i386_error pass1_err = 0;
   const insn_template *t;
+  ginsnS *ginsn;
 
   /* Initialize globals.  */
   current_templates = NULL;
@@ -5659,6 +6369,13 @@ md_assemble (char *line)
   /* We are ready to output the insn.  */
   output_insn ();
 
+  /* At this time, SCFI is enabled only for AMD64 ABI.  */
+  if (flag_synth_cfi && x86_elf_abi == X86_64_ABI)
+    {
+      ginsn = ginsn_new (symbol_temp_new_now (), frch_ginsn_gen_mode ());
+      frch_ginsn_data_append (ginsn);
+    }
+
   insert_lfence_after ();
 
   last_insn.seg = now_seg;
@@ -10904,6 +11621,7 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
   valueT val;
   bool vex = false, xop = false, evex = false;
   static const templates tt = { &i.tm, &i.tm + 1 };
+  ginsnS *ginsn;
 
   init_globals ();
 
@@ -11658,7 +12376,14 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
 
   output_insn ();
 
- done:
+  /* At this time, SCFI is enabled only for AMD64 ABI.  */
+  if (flag_synth_cfi && x86_elf_abi == X86_64_ABI)
+    {
+      ginsn = ginsn_new (symbol_temp_new_now (), frch_ginsn_gen_mode ());
+      frch_ginsn_data_append (ginsn);
+    }
+
+done:
   *saved_ilp = saved_char;
   input_line_pointer = line;
 
@@ -15293,6 +16018,9 @@ i386_target_format (void)
   else
     as_fatal (_("unknown architecture"));
 
+  if (flag_synth_cfi && x86_elf_abi != X86_64_ABI)
+    as_fatal (_("Synthesizing CFI is not supported for this ABI"));
+
   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 a3d463d961f..f63ad1694a9 100644
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -373,6 +373,27 @@ extern int i386_elf_section_type (const char *, size_t);
 extern void i386_solaris_fix_up_eh_frame (segT);
 #endif
 
+#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_NUM_REGS 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);
+
 /* Support for SHF_X86_64_LARGE */
 extern bfd_vma x86_64_section_letter (int, const char **);
 #define md_elf_section_letter(LETTER, PTR_MSG)	x86_64_section_letter (LETTER, PTR_MSG)
diff --git a/gas/ginsn.c b/gas/ginsn.c
new file mode 100644
index 00000000000..ce4aa99cedc
--- /dev/null
+++ b/gas/ginsn.c
@@ -0,0 +1,1225 @@
+/* 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
+
+const char *const ginsn_type_names[] =
+{
+#define _GINSN_TYPE_ITEM(NAME, STR) STR,
+  _GINSN_TYPES
+#undef _GINSN_TYPE_ITEM
+};
+
+const char *const ginsn_src_type_names[] =
+{
+#define _GINSN_SRC_TYPE_ITEM(NAME, STR) STR,
+  _GINSN_SRC_TYPES
+#undef _GINSN_SRC_TYPE_ITEM
+};
+const char *const ginsn_dst_type_names[] =
+{
+#define _GINSN_DST_TYPE_ITEM(NAME, STR) STR,
+  _GINSN_DST_TYPES
+#undef _GINSN_DST_TYPE_ITEM
+};
+
+static
+ginsnS *ginsn_alloc (void)
+{
+  ginsnS *ginsn = XCNEW (ginsnS);
+  return ginsn;
+}
+
+static ginsnS*
+ginsn_init (enum ginsn_type type, 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);
+  ginsn = NULL;
+}
+
+static void
+ginsn_set_src (struct ginsn_src *src, enum ginsn_src_type type, uint32_t reg,
+	       int32_t immdisp)
+{
+  if (!src)
+    return;
+
+  src->type = type;
+  /* Even when the use-case is SCFI, the value of reg may be > SCFI_NUM_REGS.
+     E.g., in AMD64, push fs etc.  */
+  src->reg = reg;
+
+  if (type == GINSN_SRC_IMM || type == GINSN_SRC_INDIRECT)
+    src->immdisp = immdisp;
+}
+
+static void
+ginsn_set_dst (struct ginsn_dst *dst, enum ginsn_dst_type type, uint32_t reg,
+	       int32_t disp)
+{
+  if (!dst)
+    return;
+
+  dst->type = type;
+  dst->reg = reg;
+
+  if (type == GINSN_DST_INDIRECT)
+    dst->disp = disp;
+}
+
+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;
+}
+
+uint32_t
+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;
+}
+
+uint32_t
+ginsn_get_src_disp (struct ginsn_src *src)
+{
+  return src->immdisp;
+}
+
+uint32_t
+ginsn_get_src_imm (struct ginsn_src *src)
+{
+  return src->immdisp;
+}
+
+uint32_t
+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;
+}
+
+int32_t
+ginsn_get_dst_disp (struct ginsn_dst *dst)
+{
+  return (int32_t) dst->disp;
+}
+
+void
+label_ginsn_map_insert (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 (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 (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 (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 (symbolS *sym)
+{
+  return ginsn_new_symbol (sym, true);
+}
+
+ginsnS *
+ginsn_new_symbol_func_end (symbolS *sym)
+{
+  return ginsn_new_symbol (sym, false);
+}
+
+ginsnS *
+ginsn_new_symbol_user_label (symbolS *sym)
+{
+  ginsnS *ginsn = ginsn_new_symbol (sym, false);
+  ginsn->flags |= GINSN_F_USER_LABEL;
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_add (symbolS *sym, bool real_p,
+	       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
+	       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
+	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_ADD, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src1_type, src1_val, src1_disp);
+  ginsn_set_src (&ginsn->src[1], src2_type, src2_val, src2_disp);
+  /* dst info.  */
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_and (symbolS *sym, bool real_p,
+	       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
+	       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
+	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_AND, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src1_type, src1_val, src1_disp);
+  ginsn_set_src (&ginsn->src[1], src2_type, src2_val, src2_disp);
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, dst_disp);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_call (symbolS *sym, bool real_p,
+		enum ginsn_src_type src_type, uint32_t src_val,
+		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_val, 0);
+
+  if (src_type == GINSN_SRC_SYMBOL)
+    ginsn->src[0].sym = src_text_sym;
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_jump (symbolS *sym, bool real_p,
+		enum ginsn_src_type src_type, uint32_t src_val,
+		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_val, 0);
+
+  if (src_type == GINSN_SRC_SYMBOL)
+    ginsn->src[0].sym = src_ginsn_sym;
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_jump_cond (symbolS *sym, bool real_p,
+		     enum ginsn_src_type src_type, uint32_t src_val,
+		     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_val, 0);
+
+  if (src_type == GINSN_SRC_SYMBOL)
+    ginsn->src[0].sym = src_ginsn_sym;
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_mov (symbolS *sym, bool real_p,
+	       enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
+	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t 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 (symbolS *sym, bool real_p,
+		 enum ginsn_src_type src_type, uint32_t src_reg,
+		 enum ginsn_dst_type dst_type)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_STS, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src_type, src_reg, 0);
+  /* dst info.  */
+  gas_assert (dst_type == GINSN_DST_STACK || dst_type == GINSN_DST_MEM);
+  ginsn_set_dst (&ginsn->dst, dst_type, 0, 0);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_load (symbolS *sym, bool real_p,
+		enum ginsn_src_type src_type,
+		enum ginsn_dst_type dst_type, uint32_t dst_reg)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_LDS, sym, real_p);
+  /* src info.  */
+  gas_assert (src_type == GINSN_SRC_STACK || src_type == GINSN_SRC_MEM);
+  ginsn_set_src (&ginsn->src[0], src_type, 0, 0);
+  /* dst info.  */
+  ginsn_set_dst (&ginsn->dst, dst_type, dst_reg, 0);
+
+  return ginsn;
+}
+
+ginsnS *
+ginsn_new_sub (symbolS *sym, bool real_p,
+	       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
+	       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
+	       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_SUB, sym, real_p);
+  /* src info.  */
+  ginsn_set_src (&ginsn->src[0], src1_type, src1_val, src1_disp);
+  ginsn_set_src (&ginsn->src[1], src2_type, src2_val, 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 (symbolS *sym, bool real_p,
+		 enum ginsn_src_type src1_type, uint32_t src1_val,
+		 enum ginsn_src_type src2_type, uint32_t src2_val,
+		 enum ginsn_dst_type dst_type, uint32_t 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 (symbolS *sym, bool real_p)
+{
+  ginsnS *ginsn = ginsn_init (GINSN_TYPE_RETURN, sym, real_p);
+  return ginsn;
+}
+
+void
+ginsn_set_file_line (ginsnS *ginsn, const char *file, unsigned int line)
+{
+  if (!ginsn)
+    return;
+
+  ginsn->file = file;
+  ginsn->line = 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 (uint32_t dw2reg, enum ginsn_gen_mode gmode)
+{
+  bool track_p = false;
+
+  if (gmode == GINSN_GEN_SCFI && dw2reg <= SCFI_NUM_REGS)
+    {
+      /* 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
+	    && S_IS_LOCAL (ginsn->src[0].sym));
+  return ret_p;
+}
+
+static char*
+ginsn_src_print (struct ginsn_src *src)
+{
+  size_t len = 39;
+  char *src_str = XNEWVEC (char, len);
+
+  memset (src_str, 0, len);
+
+  if (src->type == GINSN_SRC_REG)
+    {
+      char *buf = XNEWVEC (char, 32);
+      sprintf (buf, "%%r%d, ", ginsn_get_src_reg (src));
+      strcat (src_str, buf);
+    }
+  else if (src->type == GINSN_SRC_IMM)
+    {
+      char *buf = XNEWVEC (char, 32);
+      sprintf (buf, "%d, ", ginsn_get_src_imm (src));
+      strcat (src_str, buf);
+    }
+  else if (src->type == GINSN_SRC_INDIRECT)
+    {
+      char *buf = XNEWVEC (char, 32);
+      sprintf (buf, "[%%r%d+%d], ", ginsn_get_src_reg (src),
+	       ginsn_get_src_disp (src));
+      strcat (src_str, buf);
+    }
+
+  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+%d]", ginsn_get_dst_reg (dst),
+	       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_sym_begin_end_print (ginsnS *ginsn)
+{
+  int id = 0;
+  const char *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;
+  size_t len = GINSN_LISTING_LEN;
+  char *ginsn_str = XNEWVEC (char, len);
+
+  memset (ginsn_str, 0, len);
+
+  strcpy (ginsn_str, "ginsn: ");
+
+  strcat (ginsn_str, ginsn_type_names[ginsn->type]);
+  strcat (ginsn_str, " ");
+
+  /* For some ginsn types, no further information is printed for now.  */
+  if (ginsn->type == GINSN_TYPE_CALL
+      || ginsn->type == GINSN_TYPE_RETURN
+      || ginsn->type == GINSN_TYPE_OTHER)
+    goto end;
+  else if (ginsn->type == GINSN_TYPE_SYMBOL)
+    {
+      if (GINSN_F_USER_LABEL_P (ginsn))
+	strncat (ginsn_str, S_GET_NAME (ginsn->sym), len - 10);
+      else
+	strcat (ginsn_str, ginsn_type_sym_begin_end_print (ginsn));
+      goto end;
+    }
+
+  /* src 1.  */
+  src = ginsn_get_src1 (ginsn);
+  strcat (ginsn_str, ginsn_src_print (src));
+
+  /* src 2.  */
+  src = ginsn_get_src2 (ginsn);
+  strcat (ginsn_str, ginsn_src_print (src));
+
+  /* dst.  */
+  dst = ginsn_get_dst (ginsn);
+  strcat (ginsn_str, ginsn_dst_print (dst));
+
+end:
+  gas_assert (strlen (ginsn_str) < 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 (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 (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 (symbolS *func, gcfgS *gcfg, ginsnS *ginsn, gbbS *prev_bb,
+		 int *errp)
+{
+  gbbS *current_bb = NULL;
+  ginsnS *target_ginsn = NULL;
+  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 && S_IS_LOCAL (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;
+}
+
+/* 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 (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)
+{
+  int 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 (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
+frch_ginsn_data_init (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 (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 (symbolS *label)
+{
+  ginsnS *ginsn;
+  gbbS *root_bb;
+  gcfgS *gcfg = NULL;
+  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;
+    }
+
+  /* Synthesize DWARF CFI and emit it.  */
+  err = scfi_synthesize_dw2cfi (func, gcfg, root_bb);
+  if (err)
+    goto end;
+  scfi_emit_dw2cfi (func);
+
+  /* Other passes, e.g., warn for unreachable code can be enabled too.  */
+  ginsn = frchain_now->frch_ginsn_data->gins_rootP;
+  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 (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);
+    }
+}
+
+symbolS *
+ginsn_data_func_symbol (void)
+{
+  symbolS *func = NULL;
+
+  if (frchain_now->frch_ginsn_data)
+    func = frchain_now->frch_ginsn_data->func;
+
+  return func;
+}
+
+#else
+
+int
+ginsn_data_begin (symbolS *func ATTRIBUTE_UNUSED)
+{
+  as_bad (_("ginsn unsupported for target"));
+  return 1;
+}
+
+int
+ginsn_data_end (symbolS *label ATTRIBUTE_UNUSED)
+{
+  as_bad (_("ginsn unsupported for target"));
+  return 1;
+}
+
+void
+ginsn_frob_label (symbolS *sym ATTRIBUTE_UNUSED)
+{
+  return;
+}
+
+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..c970cfdc5e6
--- /dev/null
+++ b/gas/ginsn.h
@@ -0,0 +1,396 @@
+/* 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:
+      GINSN_TYPE_LDS, "LDS": Load from stack.
+      GINSN_TYPE_STS, "STS": 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_LDS, "LDS")  \
+  _GINSN_TYPE_ITEM (GINSN_TYPE_STS, "STS")  \
+  _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
+};
+
+#define _GINSN_SRC_TYPES \
+  _GINSN_SRC_TYPE_ITEM (GINSN_SRC_UNKNOWN, "UNK") \
+  _GINSN_SRC_TYPE_ITEM (GINSN_SRC_REG, "REG") \
+  _GINSN_SRC_TYPE_ITEM (GINSN_SRC_IMM, "IMM") \
+  _GINSN_SRC_TYPE_ITEM (GINSN_SRC_INDIRECT, "IND")  \
+  _GINSN_SRC_TYPE_ITEM (GINSN_SRC_STACK, "STK")	\
+  _GINSN_SRC_TYPE_ITEM (GINSN_SRC_SYMBOL, "SYM")  \
+  _GINSN_SRC_TYPE_ITEM (GINSN_SRC_MEM, "MEM")
+
+enum ginsn_src_type
+{
+#define _GINSN_SRC_TYPE_ITEM(NAME, STR)	NAME,
+  _GINSN_SRC_TYPES
+#undef _GINSN_SRC_TYPE_ITEM
+};
+
+/* GAS instruction source operand representation.  */
+
+struct ginsn_src
+{
+  enum ginsn_src_type type;
+  /* DWARF register number.  */
+  uint32_t reg;
+  /* 32-bit immediate or disp for indirect memory access.  */
+  int32_t immdisp;
+  /* Src symbol.  May be needed for some control flow instructions.  */
+  symbolS *sym;
+};
+
+#define _GINSN_DST_TYPES  \
+  _GINSN_DST_TYPE_ITEM (GINSN_DST_UNKNOWN, "UNK") \
+  _GINSN_DST_TYPE_ITEM (GINSN_DST_REG, "REG") \
+  _GINSN_DST_TYPE_ITEM (GINSN_DST_INDIRECT, "IND")  \
+  _GINSN_DST_TYPE_ITEM (GINSN_DST_STACK, "STK")	\
+  _GINSN_DST_TYPE_ITEM (GINSN_DST_MEM, "MEM")
+
+enum ginsn_dst_type
+{
+#define _GINSN_DST_TYPE_ITEM(NAME, STR) NAME,
+  _GINSN_DST_TYPES
+#undef _GINSN_DST_TYPE_ITEM
+};
+
+/* GAS instruction destination operand representation.  */
+
+struct ginsn_dst
+{
+  enum ginsn_dst_type type;
+  /* DWARF register number.  */
+  uint32_t reg;
+  /* 32-bit disp for indirect memory access.  */
+  int32_t 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.  */
+  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);
+
+uint32_t ginsn_get_src_reg (struct ginsn_src *src);
+enum ginsn_src_type ginsn_get_src_type (struct ginsn_src *src);
+uint32_t ginsn_get_src_disp (struct ginsn_src *src);
+uint32_t ginsn_get_src_imm (struct ginsn_src *src);
+
+uint32_t ginsn_get_dst_reg (struct ginsn_dst *dst);
+enum ginsn_dst_type ginsn_get_dst_type (struct ginsn_dst *dst);
+int32_t 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.  */
+  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 (symbolS *func);
+int ginsn_data_end (symbolS *label);
+symbolS *ginsn_data_func_symbol (void);
+void ginsn_frob_label (symbolS *sym);
+
+void frch_ginsn_data_init (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 (symbolS *label, ginsnS *ginsn);
+ginsnS *label_ginsn_map_find (symbolS *label);
+
+ginsnS *ginsn_new_symbol_func_begin (symbolS *sym);
+ginsnS *ginsn_new_symbol_func_end (symbolS *sym);
+ginsnS *ginsn_new_symbol_user_label (symbolS *sym);
+
+ginsnS* ginsn_new_phantom (symbolS *sym);
+ginsnS *ginsn_new_symbol (symbolS *sym, bool real_p);
+ginsnS *ginsn_new_add (symbolS *sym, bool real_p,
+		       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
+		       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
+		       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp);
+ginsnS *ginsn_new_and (symbolS *sym, bool real_p,
+		       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
+		       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
+		       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp);
+ginsnS *ginsn_new_call (symbolS *sym, bool real_p,
+			enum ginsn_src_type src_type, uint32_t src_val,
+			symbolS *src_text_sym);
+ginsnS *ginsn_new_jump (symbolS *sym, bool real_p,
+			enum ginsn_src_type src_type, uint32_t src_val,
+			symbolS *src_ginsn_sym);
+ginsnS *ginsn_new_jump_cond (symbolS *sym, bool real_p,
+			     enum ginsn_src_type src_type, uint32_t src_val,
+			     symbolS *src_ginsn_sym);
+ginsnS *ginsn_new_mov (symbolS *sym, bool real_p,
+		       enum ginsn_src_type src_type, uint32_t src_reg, int32_t src_disp,
+		       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp);
+ginsnS *ginsn_new_store (symbolS *sym, bool real_p,
+			 enum ginsn_src_type src_type, uint32_t src_reg,
+			 enum ginsn_dst_type dst_type);
+ginsnS *ginsn_new_load (symbolS *sym, bool real_p,
+			enum ginsn_src_type src_type,
+			enum ginsn_dst_type dst_type, uint32_t dst_reg);
+ginsnS *ginsn_new_sub (symbolS *sym, bool real_p,
+		       enum ginsn_src_type src1_type, uint32_t src1_val, int32_t src1_disp,
+		       enum ginsn_src_type src2_type, uint32_t src2_val, int32_t src2_disp,
+		       enum ginsn_dst_type dst_type, uint32_t dst_reg, int32_t dst_disp);
+ginsnS *ginsn_new_other (symbolS *sym, bool real_p,
+			 enum ginsn_src_type src1_type, uint32_t src1_val,
+			 enum ginsn_src_type src2_type, uint32_t src2_val,
+			 enum ginsn_dst_type dst_type, uint32_t dst_reg);
+ginsnS *ginsn_new_return (symbolS *sym, bool real_p);
+
+void ginsn_set_file_line (ginsnS *ginsn, const char *file, unsigned int line);
+
+bool ginsn_track_reg_p (uint32_t 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;
+  int64_t num_ginsns;
+
+  /* Identifier (linearly increasing natural number) for each gbb.  Added for
+     debugging purpose only.  */
+  int64_t id;
+
+  bool visited;
+
+  int32_t num_out_gedges;
+  gedgeS *out_gedges;
+
+  /* FIXME - keep a separate map or add like this.  */
+  /* 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
+{
+  int64_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 (symbolS *func, int *errp);
+void gcfg_cleanup (gcfgS **gcfg);
+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 a2cc1f51958..ff89f2219dd 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 9068072493a..8ffc3d35ce1 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>
 
@@ -1382,6 +1383,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
diff --git a/gas/scfi.c b/gas/scfi.c
new file mode 100644
index 00000000000..21550aa2a6f
--- /dev/null
+++ b/gas/scfi.c
@@ -0,0 +1,1226 @@
+/* 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_NUM_REGS),
+   keep the next register ID, in sequence, for REG_CFA.  */
+#define REG_CFA	      (SCFI_NUM_REGS+1)
+/* Define the total number of registers being tracked.  Used as index into an
+   array of cfi_reglocS.  */
+#define MAX_NUM_SCFI_REGS   (REG_CFA+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).  */
+  uint32_t base;
+  /* Location as offset from the CFA.  */
+  int32_t 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.  */
+  uint32_t 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_NUM_REGS is target/ABI dependent and is provided by the
+    backends.  The backend must also identify 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.  */
+  int32_t 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;
+}
+
+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);
+  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, uint32_t reg, uint32_t base,
+		     int32_t 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, uint32_t 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_STS)
+    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_LDS)
+    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, uint32_t 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, int32_t 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, int32_t 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, uint32_t reg,
+		     int32_t 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, uint32_t 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, uint32_t reg)
+{
+  scfi_opS *op = NULL;
+
+  op = init_scfi_op ();
+
+  op->dw2cfi_op = DW_CFA_restore;
+  op->reg = reg;
+  op->loc.base = -1; /* FIXME invalidate.  */
+  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_LDS)
+	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_STS))
+	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))
+    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;
+  int32_t expected_offset = 0;
+  struct ginsn_src *src1;
+  struct ginsn_dst *dst;
+  uint32_t 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_LDS)
+      && 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));
+    }
+  else if (ginsn->type == GINSN_TYPE_LDS)
+    expected_offset = -state->stack_size;
+
+  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;
+  int32_t 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
+	      /* That REG_FP has just been saved on stack previously.  This
+		 check guards against the case that there is stack usage
+		 between the two insns:
+		    pushq %rbp
+		    ...stack usage
+		    mov %rsp, %rbp
+		 Do not switch CFA tracking in this case.  */
+	      && state->regs[REG_FP].offset == -state->stack_size)
+	    {
+	      /* 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_LDS:
+	  /* 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 (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_STACK:
+      gas_assert (ginsn->type == GINSN_TYPE_STS);
+
+      if (ginsn_scfi_save_reg_p (ginsn, state))
+	{
+	  /* reg is saved on stack at the current value of REG_SP.  */
+	  offset = 0 - state->stack_size;
+	  scfi_state_save_reg (state, ginsn_get_src_reg (src1), REG_CFA, offset);
+	  /* Track save of callee-saved registers.  */
+	  scfi_op_add_cfi_offset (state, ginsn, ginsn_get_src_reg (src1));
+	}
+      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_STS);
+      /* 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 (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;
+  int 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 (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_warn (_("SCFI: forward pass failed for func '%s'"), S_GET_NAME (func));
+      goto end;
+    }
+
+  ret = backward_flow_scfi_state (func, gcfg);
+  if (ret)
+    {
+      as_warn (_("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 (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_LDS:
+	  case GINSN_TYPE_PHANTOM:
+	  case GINSN_TYPE_STS:
+	  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 (symbolS *func ATTRIBUTE_UNUSED)
+{
+  as_bad (_("SCFI: unsupported for target"));
+  return 1;
+}
+
+int
+scfi_synthesize_dw2cfi (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..3eb1bf9ddd7
--- /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 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 (symbolS *func);
+
+int scfi_synthesize_dw2cfi (symbolS *func, gcfgS *gcfg, gbbS *root_bb);
+
+#endif /* SCFI_H.  */
diff --git a/gas/subsegs.c b/gas/subsegs.c
index ae42b4e76d9..6b9834c8566 100644
--- a/gas/subsegs.c
+++ b/gas/subsegs.c
@@ -131,6 +131,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 ace0657bdfb..c90c5622465 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 45e46ed39b7..b595ffad104 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.40.0


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

* [PATCH,V1 8/9] gas: testsuite: add a x86_64 testsuite for SCFI
  2023-10-26 19:13 [PATCH,V1 0/9] Synthesize CFI for hand-written asm Indu Bhagat
                   ` (6 preceding siblings ...)
  2023-10-26 19:13 ` [PATCH,V1 7/9] gas: synthesize CFI for hand-written asm Indu Bhagat
@ 2023-10-26 19:13 ` Indu Bhagat
  2023-10-26 19:13 ` [PATCH,V1 9/9] gas/NEWS: announce the new command line option Indu Bhagat
  8 siblings, 0 replies; 10+ messages in thread
From: Indu Bhagat @ 2023-10-26 19:13 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[Changes from the RFC patch set]
  - New testcases based on the fixes in the previous patches
  - New testcase for not ignoring .cfi_label
  - New testcases scfi-asm-marker-* which test for some patterns of
    .type and .size attributes in the input asm for --scfi=all.
  - Recall that all assembler testcases here include the .cfi_*
    directives corresponding to the asm.  GAS now issues a warning if
    --scfi is invoked with input assembly which includes .cfi_*
    directives:
    "Warning: --scfi=all ignores some user-specified CFI directives"
  - But the SCFI algorithm in GAS issues other SCFI specific warnings too.
    Hence, many of the tests are run twice, like so:
    + run_dump_test "scfi-add-1", which runs the test with a -W and checks
      the objdump output of the .eh_frame (or sometimes .sframe) section.
    + run_list_test "scfi-add-1" "--scfi --warn", which runs the test
      with warnings enabled, and explicitly checks all warning messages
      per test.
[End of changes]

The testsuite for SCFI contains target-specific tests.

As all the tests are executed with --scfi command line option, the CFI
annotations in the test .s files are skipped altogether by the GAS for
processing.  The CFI directives in the assembly files are added with the
intention to aid maintainence only: CFI annotations in .s files help
convey the expected EH Frame / SFrame data in a format-oblivious way.

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

  - Only AMD64 ABI is supported for now. Using --m32 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 in a
situation where GAS is not sure it will be able to synthesize valid CFI.

  - (#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/testsuite/gas/scfi/README: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-add-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-add-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-add-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-add-2.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-add-2.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-add-2.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-simple-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-simple-2.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-sub-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-sub-2.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.l: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.s: New test.
	* gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp: New test.
---
 gas/testsuite/gas/scfi/README                 | 19 ++++
 gas/testsuite/gas/scfi/x86_64/scfi-add-1.d    | 25 +++++
 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    | 36 +++++++
 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       | 28 ++++++
 .../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       | 24 +++++
 .../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       | 31 ++++++
 .../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  | 31 ++++++
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l  |  2 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s  | 22 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d  | 57 +++++++++++
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l  |  2 +
 gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s  | 49 ++++++++++
 .../gas/scfi/x86_64/scfi-callee-saved-1.d     | 40 ++++++++
 .../gas/scfi/x86_64/scfi-callee-saved-1.l     |  2 +
 .../gas/scfi/x86_64/scfi-callee-saved-1.s     | 26 +++++
 .../gas/scfi/x86_64/scfi-callee-saved-2.d     | 41 ++++++++
 .../gas/scfi/x86_64/scfi-callee-saved-2.l     |  2 +
 .../gas/scfi/x86_64/scfi-callee-saved-2.s     | 42 ++++++++
 .../gas/scfi/x86_64/scfi-callee-saved-3.d     | 42 ++++++++
 .../gas/scfi/x86_64/scfi-callee-saved-3.l     |  3 +
 .../gas/scfi/x86_64/scfi-callee-saved-3.s     | 40 ++++++++
 .../gas/scfi/x86_64/scfi-callee-saved-4.d     | 40 ++++++++
 .../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    | 36 +++++++
 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    | 28 ++++++
 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        | 37 +++++++
 .../gas/scfi/x86_64/scfi-cfi-label-1.l        |  2 +
 .../gas/scfi/x86_64/scfi-cfi-label-1.s        | 19 ++++
 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   | 23 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l   |  4 +
 gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s   | 23 +++++
 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        | 23 +++++
 .../gas/scfi/x86_64/scfi-dyn-stack-1.l        |  2 +
 .../gas/scfi/x86_64/scfi-dyn-stack-1.s        | 50 ++++++++++
 .../gas/scfi/x86_64/scfi-fp-diag-2.l          |  3 +
 .../gas/scfi/x86_64/scfi-fp-diag-2.s          | 55 +++++++++++
 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.d | 26 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.l |  2 +
 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.s | 13 +++
 .../gas/scfi/x86_64/scfi-indirect-mov-1.d     | 51 ++++++++++
 .../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     | 41 ++++++++
 .../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     | 41 ++++++++
 .../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     | 63 ++++++++++++
 .../gas/scfi/x86_64/scfi-indirect-mov-4.l     |  3 +
 .../gas/scfi/x86_64/scfi-indirect-mov-4.s     | 68 +++++++++++++
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d    | 37 +++++++
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l    |  2 +
 gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s    | 40 ++++++++
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d  | 36 +++++++
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l  |  2 +
 gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s  | 26 +++++
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d  | 35 +++++++
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l  |  2 +
 gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s  | 24 +++++
 .../gas/scfi/x86_64/scfi-selfalign-func-1.d   | 31 ++++++
 .../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 | 26 +++++
 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 | 30 ++++++
 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    | 25 +++++
 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    | 31 ++++++
 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      | 10 ++
 .../gas/scfi/x86_64/scfi-unsupported-2.l      |  3 +
 .../gas/scfi/x86_64/scfi-unsupported-2.s      | 14 +++
 .../gas/scfi/x86_64/scfi-unsupported-3.l      |  3 +
 .../gas/scfi/x86_64/scfi-unsupported-3.s      | 14 +++
 .../gas/scfi/x86_64/scfi-unsupported-4.l      |  4 +
 .../gas/scfi/x86_64/scfi-unsupported-4.s      | 23 +++++
 .../gas/scfi/x86_64/scfi-unsupported-cfg-1.l  |  3 +
 .../gas/scfi/x86_64/scfi-unsupported-cfg-1.s  | 53 ++++++++++
 .../gas/scfi/x86_64/scfi-unsupported-drap-1.l |  4 +
 .../gas/scfi/x86_64/scfi-unsupported-drap-1.s | 75 ++++++++++++++
 gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp | 98 +++++++++++++++++++
 107 files changed, 2437 insertions(+)
 create mode 100644 gas/testsuite/gas/scfi/README
 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-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-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-ignore-1.d
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.l
 create mode 100644 gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.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-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-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-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-x86-64.exp

diff --git a/gas/testsuite/gas/scfi/README b/gas/testsuite/gas/scfi/README
new file mode 100644
index 00000000000..168475829cf
--- /dev/null
+++ b/gas/testsuite/gas/scfi/README
@@ -0,0 +1,19 @@
+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.
+  Recall that user-specified, synthesizable CFI annotations are ignored by the
+  GAS when --scfi (=all) is in effect.  Adding CFI annocations, irrespectively,
+  makes the testcases clearer in terms of understanding the expected unwind
+  data.  The only excpetion to this guideline is the following item below.
+
+* Note that GAS issues a warning:
+    "Warning: --scfi=all ignores .cfi_startproc and other sythesizable CFI
+    directives"
+  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, the following CFI annotations are kept commented out:
+      #	      .cfi_startproc
+      ...
+      #       .cfi_endproc
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..d224990641a
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-1.d
@@ -0,0 +1,25 @@
+#as: --scfi -W
+#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..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some 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..99002c5e34e
--- /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..4b378caa117
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.d
@@ -0,0 +1,36 @@
+#as: --scfi -W
+#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+002c
+  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: 36 to 0+002a
+  DW_CFA_restore: r12 \(r12\)
+  DW_CFA_advance_loc: 1 to 0+002b
+  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..147ffcb6bda
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-add-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*14: Warning: --scfi=all ignores some 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..6ea0fefc7b5
--- /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 6, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register 6
+	pushq	%r12
+	.cfi_offset 12, -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
+	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 12
+	leave
+	.cfi_def_cfa_register 7
+	.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-asm-marker-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.d
new file mode 100644
index 00000000000..9103e9f1881
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.d
@@ -0,0 +1,28 @@
+#as: --scfi -W
+#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..f6a884c238f
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-1.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*8: Warning: --scfi=all ignores some 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..88d78795643
--- /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..3e5a4f05ea4
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.d
@@ -0,0 +1,24 @@
+#as: --scfi -W
+#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..6688ea8b5d8
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-2.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some 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..9bcfe8d2e0a
--- /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..3c1fcb2c85c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.d
@@ -0,0 +1,31 @@
+#as: --scfi -W
+#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..bcf4bacf823
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-asm-marker-3.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*9: Warning: --scfi=all ignores some 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..6a2b8e1ac14
--- /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 6, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register 6
+	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 7
+	.cfi_restore 6
+	.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..373895c7caf
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.d
@@ -0,0 +1,31 @@
+#as: --scfi -W
+#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..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some 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..c9b949fdf8f
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-1.s
@@ -0,0 +1,22 @@
+# 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 6, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register 6
+	addq	%rax, %rdi
+	mov	%rbp, %rsp
+	.cfi_def_cfa_register 7
+	pop	%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-bp-sp-2.d b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d
new file mode 100644
index 00000000000..3f167c3ef1a
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.d
@@ -0,0 +1,57 @@
+#as: --scfi -W
+#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
+
+0+0018 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: 9 to 0+0018
+  DW_CFA_def_cfa_offset: 48
+  DW_CFA_advance_loc: 1 to 0+0019
+  DW_CFA_restore: r3 \(rbx\)
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_advance_loc: 1 to 0+001a
+  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
+  DW_CFA_nop
+#...
+
+#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..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some 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..f17c5c8dfc4
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-bp-sp-2.s
@@ -0,0 +1,49 @@
+# Testcase for switching between sp/fp based CFA.
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+        .cfi_startproc
+        pushq   %r14
+        .cfi_def_cfa_offset 16
+        .cfi_offset 14, -16
+        pushq   %r13
+        .cfi_def_cfa_offset 24
+        .cfi_offset 13, -24
+        pushq   %r12
+        .cfi_def_cfa_offset 32
+        .cfi_offset 12, -32
+        pushq   %rbp
+        .cfi_def_cfa_offset 40
+        .cfi_offset 6, -40
+        pushq   %rbx
+        .cfi_def_cfa_offset 48
+        .cfi_offset 3, -48
+        movq    %rdi, %rbx
+        subq    $32, %rsp
+        .cfi_def_cfa_offset 80
+# This mov does not switch CFA tracking to REG_FP, because there has already
+# been stack usage between here and the push %rbp
+        movq    %rsp, %rbp
+        xorl    %eax, %eax
+        addq    $32, %rsp
+        .cfi_def_cfa_offset 48
+        popq    %rbx
+	.cfi_restore 3
+        .cfi_def_cfa_offset 40
+        popq    %rbp
+	.cfi_restore 6
+        .cfi_def_cfa_offset 32
+        popq    %r12
+	.cfi_restore 12
+        .cfi_def_cfa_offset 24
+        popq    %r13
+	.cfi_restore 13
+        .cfi_def_cfa_offset 16
+        popq    %r14
+	.cfi_restore 14
+        .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..feb93d28d7a
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.d
@@ -0,0 +1,40 @@
+#as: --scfi
+#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..2762adba5b1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*8: Warning: --scfi=all ignores some 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..db3a2690bf3
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-1.s
@@ -0,0 +1,26 @@
+	.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 3, -24
+	pushq	%rbp
+        .cfi_def_cfa_offset 32
+        .cfi_offset 6, -32
+	popq	%rbp
+	.cfi_def_cfa_offset 24
+	.cfi_restore 6
+	popq	%rbx
+	.cfi_def_cfa_offset 16
+	.cfi_restore 3
+	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..57a99b04557
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.d
@@ -0,0 +1,41 @@
+#as: --scfi -W
+#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..bcf4bacf823
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*9: Warning: --scfi=all ignores some 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..7b10a2c1c6b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-2.s
@@ -0,0 +1,42 @@
+# 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 12, -16
+	pushq	%r13
+	.cfi_def_cfa_offset 24
+	.cfi_offset 13, -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 13
+	.cfi_def_cfa_offset 16
+	popq	%r12
+	.cfi_restore 12
+        .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..73eb147a539
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.d
@@ -0,0 +1,42 @@
+#as: --scfi -W
+#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..2930658dc2d
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some 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..a2bb53017f1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-3.s
@@ -0,0 +1,40 @@
+# 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 12, -16
+	pushq	%r13
+	.cfi_def_cfa_offset 24
+	.cfi_offset 13, -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 13
+	.cfi_def_cfa_offset 16
+	popq	%r12
+	.cfi_restore 12
+        .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..d6543fa8f6c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.d
@@ -0,0 +1,40 @@
+#as: -W --scfi
+#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..58b417460e5
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-callee-saved-4.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*4: Warning: --scfi=all ignores some 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..be29a3ccfa8
--- /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 6, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register 6
+	pushq	%r12
+	.cfi_offset 12, -24
+	pushq	%rbx
+	.cfi_offset 3, -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 3
+	popq	%r12
+	.cfi_restore 12
+	popq	%rbp
+	.cfi_def_cfa_register 7
+	.cfi_restore 6
+	.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..aa14af48613
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.d
@@ -0,0 +1,36 @@
+#as: --scfi -W
+#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..653937edbf9
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*15: Warning: --scfi=all ignores some 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..9e72ed0ed2b
--- /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 3, -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 3
+	.cfi_def_cfa_offset 8
+	ret
+.L7:
+	.cfi_restore_state
+	movq	%rbx, %rdi
+	call	fclose
+	popq	%rbx
+	.cfi_restore 3
+	.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..736c26386e7
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.d
@@ -0,0 +1,28 @@
+#as: --scfi -W
+#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..2762adba5b1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfg-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*8: Warning: --scfi=all ignores some 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..adf1df08bc4
--- /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 6, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register 6
+	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..6e11583a534
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.d
@@ -0,0 +1,37 @@
+#as: --scfi -W
+#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..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cfi-label-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some 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..845696d4749
--- /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-cofi-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d
new file mode 100644
index 00000000000..af4fa429339
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.d
@@ -0,0 +1,5 @@
+#as: --scfi -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..fd57194cedf
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*12: Warning: --scfi=all ignores some user-specified CFI directives
+.*22: 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..fa03b5ef5e4
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-cofi-1.s
@@ -0,0 +1,23 @@
+# 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 must behave
+# gracefully for these "change of flow instructions"
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	addq    %rdx, %rax
+	notrack jmp     *%rax
+	call   *%r8
+	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..ecc6b82ce97
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.l
@@ -0,0 +1,4 @@
+.*Assembler messages:
+.*7: Warning: --scfi=all ignores some user-specified CFI directives
+.*14: Error: SCFI: usage of REG_FP as scratch not supported
+.*22: Warning: 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..c09ec0da1c4
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-1.s
@@ -0,0 +1,23 @@
+# 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 6, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register 6
+# The following add causes REG_FP to become untraceable
+	addq	%rax, %rbp
+	.cfi_def_cfa_register 7
+	pop	%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-diag-2.l b/gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l
new file mode 100644
index 00000000000..d3adbeca35a
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-2.l
@@ -0,0 +1,4 @@
+.*Assembler messages:
+.*5: Warning: --scfi=all ignores some 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..f58915d0a72
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-diag-2.s
@@ -0,0 +1,28 @@
+# Testcase for a diagnostic around assymetrical restore
+.type	foo, @function
+foo:
+.LFB10:
+	.cfi_startproc
+	endbr64
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register 6
+	pushq	%r12
+	pushq	%rbx
+	subq	$24, %rsp
+	.cfi_offset 12, -24
+	.cfi_offset 3, -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 7, 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..935deace54f
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.d
@@ -0,0 +1,23 @@
+#as: --scfi --gsframe -W
+#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..ce97dd14432
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-dyn-stack-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*5: Warning: --scfi=all ignores some 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..12f1cd78994
--- /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 6, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register 6
+	pushq	%r13
+	pushq	%r12
+	pushq	%rbx
+	subq	$8, %rsp
+	.cfi_offset 13, -24
+	.cfi_offset 12, -32
+	.cfi_offset 3, -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 6
+	.cfi_def_cfa_register 7
+	.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..734666af914
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*7: Warning: --scfi=all ignores some 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..fd46fe3f65c
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-fp-diag-2.s
@@ -0,0 +1,55 @@
+# Testcase for a diagnostic around assymetrical 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 6, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register 6
+	pushq	%r12
+	pushq	%rbx
+	subq	$24, %rsp
+	.cfi_offset 12, -24
+	.cfi_offset 3, -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 assymetric 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 7, 8
+	ret
+	.cfi_endproc
+.LFE10:
+	.size	foo, .-foo
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.d
new file mode 100644
index 00000000000..1cf5919add6
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.d
@@ -0,0 +1,26 @@
+#as: --scfi -W
+#objdump: -Wf
+#name: Ignore user specified CFI directives
+#...
+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-ignore-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.l
new file mode 100644
index 00000000000..ce97dd14432
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*5: Warning: --scfi=all ignores some user-specified CFI directives
diff --git a/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.s b/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.s
new file mode 100644
index 00000000000..68b731c58ba
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-ignore-1.s
@@ -0,0 +1,13 @@
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%rbp
+        .cfi_def_cfa_offset 16
+        .cfi_offset 6, -16
+	ret
+	.cfi_endproc
+.LFE0:
+	.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..4f4436506f6
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.d
@@ -0,0 +1,51 @@
+#as: --scfi -W
+#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..229fb4e7035
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*4: Warning: --scfi=all ignores some 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..e3b5ab30ff1
--- /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 3, 0
+	movq	%rbp,8(%rsp)
+	.cfi_rel_offset 6, 8
+	movq	%r12,16(%rsp)
+	.cfi_rel_offset 12, 16
+	movq	%r13,24(%rsp)
+	.cfi_rel_offset 13, 24
+	movq	%r14,32(%rsp)
+	.cfi_rel_offset 14, 32
+	movq	%r15,40(%rsp)
+	.cfi_rel_offset 15, 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 15
+	movq	32(%rsp),%r14
+	.cfi_restore 14
+	movq	24(%rsp),%r13
+	.cfi_restore 13
+	movq	16(%rsp),%r12
+	.cfi_restore 12
+	movq	8(%rsp),%rbp
+	.cfi_restore 6
+	movq	(%rsp),%rbx
+	.cfi_restore 3
+	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..3caa66547f6
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.d
@@ -0,0 +1,41 @@
+#as: --scfi -W
+#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..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some 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..bed2050241c
--- /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 6, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register 6
+
+	subq	$16,%rsp
+
+# store rbx at %rsp; rsp = rbp - 16;
+	movq	%rbx, -16(%rbp)
+	.cfi_rel_offset 3, -16
+# store r12 at %rsp + 8; rsp = rbp -16;
+	movq	%r12, -8(%rbp)
+	.cfi_rel_offset 12, -8
+
+	call bar
+
+	movq	-16(%rbp), %rbx
+	.cfi_restore 3
+	movq	-8(%rbp), %r12
+	.cfi_restore 12
+
+	addq	$16,%rsp
+
+	mov	%rbp, %rsp
+	.cfi_def_cfa_register 7
+	pop	%rbp
+	.cfi_restore 6
+	.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..8070e220b7a
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.d
@@ -0,0 +1,41 @@
+#as: --scfi -W
+#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..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-3.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some 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..0eb90bc34e3
--- /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 6, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register 6
+
+	subq	$16,%rsp
+
+# store rbx at %rsp; rsp = rbp - 16; rsp = CFA - 32
+	movq	%rbx, (%rsp)
+	.cfi_rel_offset 3, -16
+# store r12 at %rsp + 8; rsp = CFA - 32
+	movq	%r12, 8(%rsp)
+	.cfi_rel_offset 12, -8
+
+	call bar
+
+	movq	(%rsp), %rbx
+	.cfi_restore 3
+	movq	8(%rsp), %r12
+	.cfi_restore 12
+
+	addq	$16,%rsp
+
+	mov	%rbp, %rsp
+	.cfi_def_cfa_register 7
+	pop	%rbp
+	.cfi_restore 6
+	.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..dbb16d7d05e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.d
@@ -0,0 +1,63 @@
+#as: --scfi -W
+#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..1793f4b4799
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-indirect-mov-4.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*7: Warning: --scfi=all ignores some 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..ae0c0fde16c
--- /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 15, -16
+	pushq	%r14
+	.cfi_def_cfa_offset 24
+	.cfi_offset 14, -24
+	movl	%r8d, %r14d
+	pushq	%r13
+	.cfi_def_cfa_offset 32
+	.cfi_offset 13, -32
+	movq	%rdi, %r13
+	pushq	%r12
+	.cfi_def_cfa_offset 40
+	.cfi_offset 12, -40
+	movq	%rsi, %r12
+	pushq	%rbp
+	.cfi_def_cfa_offset 48
+	.cfi_offset 6, -48
+	movq	%rcx, %rbp
+	pushq	%rbx
+	.cfi_def_cfa_offset 56
+	.cfi_offset 3, -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 3
+	.cfi_def_cfa_offset 48
+	popq	%rbp
+	.cfi_restore 6
+	.cfi_def_cfa_offset 40
+	popq	%r12
+	.cfi_restore 12
+	.cfi_def_cfa_offset 32
+	popq	%r13
+	.cfi_restore 13
+	.cfi_def_cfa_offset 24
+	popq	%r14
+	.cfi_restore 14
+	.cfi_def_cfa_offset 16
+	popq	%r15
+	.cfi_restore 15
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE118:
+	.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..d2fa442df63
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.d
@@ -0,0 +1,37 @@
+#as: --scfi -W -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..2762adba5b1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*8: Warning: --scfi=all ignores some 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..8fb8fe794d6
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-lea-1.s
@@ -0,0 +1,40 @@
+## 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 6, -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 6
+	push   %r13
+	.cfi_offset 13, -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 13
+	popq	%rbp
+	.cfi_def_cfa_register 7
+	.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-leave-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d
new file mode 100644
index 00000000000..0d9e5943a30
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.d
@@ -0,0 +1,36 @@
+#as: --scfi -W
+#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..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some 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..0466ab15708
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-leave-1.s
@@ -0,0 +1,26 @@
+# Testcase for leave insn
+	.text
+	.globl	foo
+	.type	foo, @function
+foo:
+	.cfi_startproc
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register 6
+	push    %rbx
+	.cfi_offset 3, -24
+	push    %rdi
+	pop	%rdi
+	pop	%rbx
+	.cfi_restore 3
+	leave
+	.cfi_def_cfa_register 7
+	.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-pushq-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d
new file mode 100644
index 00000000000..e37d4a93917
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.d
@@ -0,0 +1,35 @@
+#as: --scfi -W
+#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+000c
+  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: 2 to 0+0006
+  DW_CFA_def_cfa_offset: 32
+  DW_CFA_advance_loc: 2 to 0+0008
+  DW_CFA_def_cfa_offset: 40
+  DW_CFA_advance_loc: 2 to 0+0000a
+  DW_CFA_def_cfa_offset: 48
+  DW_CFA_advance_loc: 1 to 0+0000b
+  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..bba199d24b9
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*7: Warning: --scfi=all ignores some 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..b896a295b56
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-pushq-1.s
@@ -0,0 +1,24 @@
+## 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
+	pushq	$36
+	.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-selfalign-func-1.d b/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.d
new file mode 100644
index 00000000000..c05c0314c0f
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.d
@@ -0,0 +1,31 @@
+#as: --scfi -W
+#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..bba199d24b9
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-selfalign-func-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*7: Warning: --scfi=all ignores some 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..51a40b5940b
--- /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 6, -16
+        movq    %rsp, %rbp
+        .cfi_def_cfa_register 6
+# 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 7
+	.cfi_restore 6
+	.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..849833cb5c8
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.d
@@ -0,0 +1,26 @@
+#as: --scfi -W
+#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..bba199d24b9
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*7: Warning: --scfi=all ignores some 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..f36b553cbff
--- /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 6, -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..2fa5e565f8b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.d
@@ -0,0 +1,30 @@
+#as: --scfi -W
+#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..ce97dd14432
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-simple-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*5: Warning: --scfi=all ignores some 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..e778cef517b
--- /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..80fb5bfb210
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.d
@@ -0,0 +1,25 @@
+#as: --scfi -W
+#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..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-1.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some 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..741a0abf69f
--- /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..3fb232f1e1b
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.d
@@ -0,0 +1,31 @@
+#as: --scfi -W
+#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..0e094077edd
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-sub-2.l
@@ -0,0 +1,2 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some 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..2435c4d5c1a
--- /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 6, -16
+	movq    %rsp, %rbp
+	.cfi_def_cfa_register 6
+	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 7
+	.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-unsupported-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.l
new file mode 100644
index 00000000000..c856c08c2ae
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.l
@@ -0,0 +1,2 @@
+Assembler messages:
+Fatal error: Synthesizing CFI 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..87d2a4971a1
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-1.s
@@ -0,0 +1,10 @@
+# Testcase run with --m32 (Not supported).
+	.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..69c71fb9d7e
--- /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: Warning: 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..22e9d88ea58
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-2.s
@@ -0,0 +1,14 @@
+	.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..69c71fb9d7e
--- /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: Warning: 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..c515171f2b5
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-3.s
@@ -0,0 +1,14 @@
+	.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..c30d2057ddb
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.l
@@ -0,0 +1,4 @@
+.*Assembler messages:
+.*6: Warning: --scfi=all ignores some user-specified CFI directives
+.*14: Error: SCFI: unsupported stack manipulation pattern
+.*22: Warning: 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..51ff92b7e50
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-4.s
@@ -0,0 +1,23 @@
+	.type	foo, @function
+foo:
+.LFB16:
+#	.cfi_startproc
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -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 7, 8
+	.cfi_restore 6
+	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..ac0d49388d6
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.l
@@ -0,0 +1,3 @@
+.*Assembler messages:
+.*50: Warning: --scfi=all ignores some 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..7d241f880d7
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-cfg-1.s
@@ -0,0 +1,53 @@
+# 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-drap-1.l b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.l
new file mode 100644
index 00000000000..c4e48ced26e
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-unsupported-drap-1.l
@@ -0,0 +1,4 @@
+.*Assembler messages:
+.*16: Warning: --scfi=all ignores some user-specified CFI directives
+.*19: Error: SCFI: unsupported stack manipulation pattern
+.*75: Warning: 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..c23828b8f9b
--- /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 10, 0
+	andq	$-16, %rsp
+	pushq	-8(%r10)
+        .cfi_def_cfa 7, 8
+	pushq	%rbp
+	.cfi_offset 6, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register 6
+	pushq	%r15
+	pushq	%r14
+	pushq	%r13
+	pushq	%r12
+	pushq	%r10
+	.cfi_offset 15, -24
+	.cfi_offset 14, -32
+	.cfi_offset 13, -40
+	.cfi_offset 12, -48
+	pushq	%rbx
+	.cfi_offset 3, -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 3
+	popq	%r10
+	popq	%r12
+	.cfi_restore 12
+	popq	%r13
+	.cfi_restore 13
+	popq	%r14
+	.cfi_restore 14
+	popq	%r15
+	.cfi_restore 15
+	popq	%rbp
+	.cfi_restore 6
+	.cfi_def_cfa_register 7
+	.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-x86-64.exp b/gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp
new file mode 100644
index 00000000000..50e19491023
--- /dev/null
+++ b/gas/testsuite/gas/scfi/x86_64/scfi-x86-64.exp
@@ -0,0 +1,98 @@
+# 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_dump_test "scfi-cfi-label-1"
+    run_list_test "scfi-cfi-label-1" "--scfi --warn"
+
+    run_list_test "scfi-diag-1" "--scfi"
+    run_list_test "scfi-fp-diag-2" "--scfi"
+    run_list_test "scfi-diag-2" "--scfi"
+
+    run_list_test "scfi-unsupported-1" "--32 --scfi"
+    run_list_test "scfi-unsupported-2" "--scfi"
+    run_list_test "scfi-unsupported-3" "--scfi"
+    run_list_test "scfi-unsupported-4" "--scfi"
+    run_list_test "scfi-unsupported-drap-1" "--scfi"
+    run_list_test "scfi-unsupported-cfg-1" "--scfi"
+
+    run_dump_test "scfi-add-1"
+    run_list_test "scfi-add-1" "--scfi --warn"
+    run_dump_test "scfi-add-2"
+    run_list_test "scfi-add-2" "--scfi --warn"
+    run_dump_test "scfi-cfg-1"
+    run_list_test "scfi-cfg-1" "--scfi --warn"
+    run_dump_test "scfi-cfg-2"
+    run_list_test "scfi-cfg-2" "--scfi --warn"
+    run_dump_test "scfi-asm-marker-1"
+    run_list_test "scfi-asm-marker-1" "--scfi --warn"
+    run_dump_test "scfi-asm-marker-2"
+    run_list_test "scfi-asm-marker-2" "--scfi --warn"
+    run_dump_test "scfi-asm-marker-3"
+    run_list_test "scfi-asm-marker-3" "--scfi --warn"
+    run_dump_test "scfi-cofi-1"
+    run_list_test "scfi-cofi-1" "--scfi --warn"
+    run_dump_test "scfi-sub-1"
+    run_list_test "scfi-sub-1" "--scfi --warn"
+    run_dump_test "scfi-sub-2"
+    run_list_test "scfi-sub-2" "--scfi --warn"
+    run_dump_test "scfi-ignore-1"
+    run_list_test "scfi-ignore-1" "--scfi --warn"
+    run_dump_test "scfi-simple-1"
+    run_list_test "scfi-simple-1" "--scfi --warn"
+    run_dump_test "scfi-simple-2"
+    run_list_test "scfi-simple-2" "--scfi --warn"
+    run_dump_test "scfi-pushq-1"
+    run_list_test "scfi-pushq-1" "--scfi --warn"
+    run_dump_test "scfi-lea-1"
+    run_list_test "scfi-lea-1" "--scfi --warn"
+    run_dump_test "scfi-leave-1"
+    run_list_test "scfi-leave-1" "--scfi --warn"
+    run_dump_test "scfi-bp-sp-1"
+    run_list_test "scfi-bp-sp-1" "--scfi --warn"
+    run_dump_test "scfi-bp-sp-2"
+    run_list_test "scfi-bp-sp-2" "--scfi --warn"
+    run_dump_test "scfi-cfg-1"
+    run_list_test "scfi-cfg-1" "--scfi --warn"
+    run_dump_test "scfi-callee-saved-2"
+    run_list_test "scfi-callee-saved-2" "--scfi --warn"
+    run_dump_test "scfi-callee-saved-3"
+    run_list_test "scfi-callee-saved-3" "--scfi --warn"
+    run_dump_test "scfi-callee-saved-4"
+    run_list_test "scfi-callee-saved-4" "--scfi --warn"
+    run_dump_test "scfi-dyn-stack-1"
+    run_list_test "scfi-dyn-stack-1" "--scfi --warn"
+    run_dump_test "scfi-indirect-mov-1"
+    run_list_test "scfi-indirect-mov-1" "--scfi --warn"
+    run_dump_test "scfi-indirect-mov-2"
+    run_list_test "scfi-indirect-mov-2" "--scfi --warn"
+    run_dump_test "scfi-indirect-mov-3"
+    run_list_test "scfi-indirect-mov-3" "--scfi --warn"
+    run_dump_test "scfi-indirect-mov-4"
+    run_list_test "scfi-indirect-mov-4" "--scfi --warn"
+    run_dump_test "scfi-selfalign-func-1"
+    run_list_test "scfi-selfalign-func-1" "--scfi --warn"
+}
+
-- 
2.40.0


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

* [PATCH,V1 9/9] gas/NEWS: announce the new command line option
  2023-10-26 19:13 [PATCH,V1 0/9] Synthesize CFI for hand-written asm Indu Bhagat
                   ` (7 preceding siblings ...)
  2023-10-26 19:13 ` [PATCH,V1 8/9] gas: testsuite: add a x86_64 testsuite for SCFI Indu Bhagat
@ 2023-10-26 19:13 ` Indu Bhagat
  8 siblings, 0 replies; 10+ messages in thread
From: Indu Bhagat @ 2023-10-26 19:13 UTC (permalink / raw)
  To: binutils; +Cc: Indu Bhagat

[New patch in the current patch set]
---
 gas/NEWS | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/gas/NEWS b/gas/NEWS
index 71a1269b893..eddec7fa2c2 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -1,4 +1,6 @@
 -*- text -*-
+* New command line option --scfi[=all,none] to synthesize CFI for hand-written
+  asm.
 
 * Add support for Intel AVX10.1.
 
-- 
2.40.0


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

end of thread, other threads:[~2023-10-26 19:14 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-10-26 19:13 [PATCH,V1 0/9] Synthesize CFI for hand-written asm Indu Bhagat
2023-10-26 19:13 ` [PATCH,V1 1/9] gas: dw2gencfi: minor rejig for cfi_sections_set and all_cfi_sections Indu Bhagat
2023-10-26 19:13 ` [PATCH,V1 2/9] gas: dw2gencfi: use all_cfi_sections instead of cfi_sections Indu Bhagat
2023-10-26 19:13 ` [PATCH,V1 3/9] gas: dw2gencfi: expose a new cfi_set_last_fde API Indu Bhagat
2023-10-26 19:13 ` [PATCH,V1 4/9] gas: dw2gencfi: move some tc_* defines to the header file Indu Bhagat
2023-10-26 19:13 ` [PATCH,V1 5/9] gas: add new command line option --scfi[=all,none] Indu Bhagat
2023-10-26 19:13 ` [PATCH,V1 6/9] gas: scfidw2gen: new functionality to prepapre for SCFI Indu Bhagat
2023-10-26 19:13 ` [PATCH,V1 7/9] gas: synthesize CFI for hand-written asm Indu Bhagat
2023-10-26 19:13 ` [PATCH,V1 8/9] gas: testsuite: add a x86_64 testsuite for SCFI Indu Bhagat
2023-10-26 19:13 ` [PATCH,V1 9/9] gas/NEWS: announce the new 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).