[v2] aarch64: Support for FEAT_CMPBR

Message ID 20250415084943.12580-1-Ezra.Sitorus@arm.com
State New
Headers
Series [v2] aarch64: Support for FEAT_CMPBR |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Ezra Sitorus April 15, 2025, 8:49 a.m. UTC
  From: Ezra Sitorus <ezra.sitorus@arm.com>

FEAT_CMPBR - Compare and branch instructions. This patch adds these
instructions:
- CB<CC> (register)
- CB<CC> (immediate)
- CBH<CC>
- CBB<CC>

where CC is one of the following:
- EQ
- NE
- GT
- GE
- LT
- LE
- HI
- HS
- LO
- LS

Some instructions are pseudo-instructions: cbgt w0, w1, <label> is the
same as cblt w1, w0, <label>. No new relocation type for the 9 bit offset has
been introduced to the AArch64 ELF ABI.

Details of the instructions can be found here:
https://developer.arm.com/documentation/109697/2024_12/Feature-descriptions/The-Armv9-6-architecture-extension
https://developer.arm.com/documentation/ddi0602/2024-12/Base-Instructions/CB-cc---register---Compare-registers-and-branch-?lang=en
https://developer.arm.com/documentation/ddi0602/2024-12/Base-Instructions/CB-cc---immediate---Compare-register-with-immediate-and-branch-?lang=en
https://developer.arm.com/documentation/ddi0602/2024-12/Base-Instructions/CBB-cc---Compare-bytes-and-branch-?lang=en
https://developer.arm.com/documentation/ddi0602/2024-12/Base-Instructions/CBH-cc---Compare-halfwords-and-branch-?lang=en
---
This has addressed the formatting issues raised in v1. Regression tested on aarch64-none-linux-gnu.
I still don't have commit rights - could somebody commit this if it looks ok?

Ezra

 bfd/elfnn-aarch64.c                   |  3 ++
 bfd/reloc.c                           |  8 +++-
 gas/config/tc-aarch64.c               | 42 ++++++++++++++++-
 gas/doc/c-aarch64.texi                |  2 +
 gas/testsuite/gas/aarch64/cmpbr-1.d   | 67 +++++++++++++++++++++++++++
 gas/testsuite/gas/aarch64/cmpbr-1.s   | 59 +++++++++++++++++++++++
 gas/testsuite/gas/aarch64/cmpbr-bad.d |  4 ++
 gas/testsuite/gas/aarch64/cmpbr-bad.l | 13 ++++++
 gas/testsuite/gas/aarch64/cmpbr-bad.s |  5 ++
 gas/testsuite/gas/aarch64/cmpbr.d     | 55 ++++++++++++++++++++++
 gas/testsuite/gas/aarch64/cmpbr.s     | 47 +++++++++++++++++++
 include/opcode/aarch64.h              |  5 ++
 opcodes/aarch64-opc.c                 |  3 ++
 opcodes/aarch64-opc.h                 |  1 +
 opcodes/aarch64-tbl.h                 | 66 ++++++++++++++++++++++++++
 15 files changed, 377 insertions(+), 3 deletions(-)
 create mode 100644 gas/testsuite/gas/aarch64/cmpbr-1.d
 create mode 100644 gas/testsuite/gas/aarch64/cmpbr-1.s
 create mode 100644 gas/testsuite/gas/aarch64/cmpbr-bad.d
 create mode 100644 gas/testsuite/gas/aarch64/cmpbr-bad.l
 create mode 100644 gas/testsuite/gas/aarch64/cmpbr-bad.s
 create mode 100644 gas/testsuite/gas/aarch64/cmpbr.d
 create mode 100644 gas/testsuite/gas/aarch64/cmpbr.s
  

Comments

Alice Carlotti April 16, 2025, 4:28 p.m. UTC | #1
On Tue, Apr 15, 2025 at 09:49:43AM +0100, Ezra.Sitorus@arm.com wrote:

Thanks for the patch - I think it's close to ready, but I have a few comments
below.

> From: Ezra Sitorus <ezra.sitorus@arm.com>
> 
> FEAT_CMPBR - Compare and branch instructions. This patch adds these
> instructions:
> - CB<CC> (register)
> - CB<CC> (immediate)
> - CBH<CC>
> - CBB<CC>
> 
> where CC is one of the following:
> - EQ
> - NE
> - GT
> - GE
> - LT
> - LE
> - HI
> - HS
> - LO
> - LS

I've no objection to this much detail, but I think a simple "Add support for
FEAT_CMPBR" as the patch header would be sufficient - the list of instructions
is then obvious from either the spec or the patch content.

> 
> Some instructions are pseudo-instructions: cbgt w0, w1, <label> is the
> same as cblt w1, w0, <label>. No new relocation type for the 9 bit offset has
> been introduced to the AArch64 ELF ABI.
> 
> Details of the instructions can be found here:
> https://developer.arm.com/documentation/109697/2024_12/Feature-descriptions/The-Armv9-6-architecture-extension
> https://developer.arm.com/documentation/ddi0602/2024-12/Base-Instructions/CB-cc---register---Compare-registers-and-branch-?lang=en
> https://developer.arm.com/documentation/ddi0602/2024-12/Base-Instructions/CB-cc---immediate---Compare-register-with-immediate-and-branch-?lang=en
> https://developer.arm.com/documentation/ddi0602/2024-12/Base-Instructions/CBB-cc---Compare-bytes-and-branch-?lang=en
> https://developer.arm.com/documentation/ddi0602/2024-12/Base-Instructions/CBH-cc---Compare-halfwords-and-branch-?lang=en

I don't think it's necessary (or desirable) to give a list of lengthy
documentation links.  If you want to give a link, I'd stick to just
https://developer.arm.com/documentation/ddi0602/2024-12.

> ---
> This has addressed the formatting issues raised in v1. Regression tested on aarch64-none-linux-gnu.
> I still don't have commit rights - could somebody commit this if it looks ok?
> 
> Ezra
> 
>  bfd/elfnn-aarch64.c                   |  3 ++
>  bfd/reloc.c                           |  8 +++-
>  gas/config/tc-aarch64.c               | 42 ++++++++++++++++-
>  gas/doc/c-aarch64.texi                |  2 +
>  gas/testsuite/gas/aarch64/cmpbr-1.d   | 67 +++++++++++++++++++++++++++
>  gas/testsuite/gas/aarch64/cmpbr-1.s   | 59 +++++++++++++++++++++++
>  gas/testsuite/gas/aarch64/cmpbr-bad.d |  4 ++
>  gas/testsuite/gas/aarch64/cmpbr-bad.l | 13 ++++++
>  gas/testsuite/gas/aarch64/cmpbr-bad.s |  5 ++
>  gas/testsuite/gas/aarch64/cmpbr.d     | 55 ++++++++++++++++++++++
>  gas/testsuite/gas/aarch64/cmpbr.s     | 47 +++++++++++++++++++
>  include/opcode/aarch64.h              |  5 ++
>  opcodes/aarch64-opc.c                 |  3 ++
>  opcodes/aarch64-opc.h                 |  1 +
>  opcodes/aarch64-tbl.h                 | 66 ++++++++++++++++++++++++++
>  15 files changed, 377 insertions(+), 3 deletions(-)
>  create mode 100644 gas/testsuite/gas/aarch64/cmpbr-1.d
>  create mode 100644 gas/testsuite/gas/aarch64/cmpbr-1.s
>  create mode 100644 gas/testsuite/gas/aarch64/cmpbr-bad.d
>  create mode 100644 gas/testsuite/gas/aarch64/cmpbr-bad.l
>  create mode 100644 gas/testsuite/gas/aarch64/cmpbr-bad.s
>  create mode 100644 gas/testsuite/gas/aarch64/cmpbr.d
>  create mode 100644 gas/testsuite/gas/aarch64/cmpbr.s
> 
> diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
> index 548da1f8b30..8f399204f04 100644
> --- a/bfd/elfnn-aarch64.c
> +++ b/bfd/elfnn-aarch64.c
> @@ -2268,6 +2268,9 @@ elfNN_aarch64_howto_from_bfd_reloc (bfd_reloc_code_real_type code)
>    if (code == BFD_RELOC_AARCH64_NONE)
>      return &elfNN_aarch64_howto_none;
>  
> +  if (code == BFD_RELOC_AARCH64_BRANCH9)
> +    return &elfNN_aarch64_howto_none;
> +
>    return NULL;
>  }
>  
> diff --git a/bfd/reloc.c b/bfd/reloc.c
> index d3ddafb7305..1aa0add7790 100644
> --- a/bfd/reloc.c
> +++ b/bfd/reloc.c
> @@ -7202,7 +7202,7 @@ ENUM
>    BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC
>  ENUMDOC
>    Similar to BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12, but no
> -  overflow check. 
> +  overflow check.

It's best to avoid unrelated format changes on code that you aren't otherwise
touching, so this hunk should be dropped.

>  ENUM
>    BFD_RELOC_AARCH64_TLSLD_LDST64_DTPREL_LO12
>  ENUMDOC
> @@ -7418,6 +7418,12 @@ ENUM
>  ENUMDOC
>    AArch64 pseudo relocation code to be used internally by the AArch64
>    assembler and not (currently) written to any object files.
> +ENUM
> +  BFD_RELOC_AARCH64_BRANCH9
> +ENUMDOC
> +  AArch64 9 bit pc-relative conditional branch and compare & branch.
> +  The lowest two bits must be zero and are not stored in the
> +  instruction, giving an 11 bit signed byte offset.
>  ENUM
>    BFD_RELOC_TILEPRO_COPY
>  ENUMX
...
> diff --git a/gas/doc/c-aarch64.texi b/gas/doc/c-aarch64.texi
> index 10888d1e78f..5927998c0bd 100644
> --- a/gas/doc/c-aarch64.texi
> +++ b/gas/doc/c-aarch64.texi
> @@ -229,6 +229,8 @@ automatically cause those extensions to be disabled.
>   @tab Enable the Lookup Table (LUT) extension.
>  @item @code{memtag} @tab
>   @tab Enable Armv8.5-A Memory Tagging Extensions.
> +@item @code{cmpbr} @tab
> + @tab Enable Compare and Branch instructions.

This list is in alphabetical order; please preserve that.

>  @item @code{mops} @tab
>   @tab Enable Armv8.8-A memcpy and memset acceleration instructions
>  @item @code{pan} @tab
> diff --git a/gas/testsuite/gas/aarch64/cmpbr-1.d b/gas/testsuite/gas/aarch64/cmpbr-1.d
> new file mode 100644
> index 00000000000..d5e02a09f4b
> --- /dev/null
> +++ b/gas/testsuite/gas/aarch64/cmpbr-1.d
> @@ -0,0 +1,67 @@
> +#name: Test for FEAT_CMPBR pseudo-instructions
> +#as: -march=armv8-a+cmpbr
> +#objdump: -dr
> +
> +.*:     file format .*
> +
> +Disassembly of section .text:
> +
> +.* <a>:
> +.*:	74010000 	cbgt	w0, w1, 0 <a>
> +.*:	74013fe0 	cbgt	w0, w1, 0 <a>
> +.*:	74213fc0 	cbge	w0, w1, 0 <a>
> +.*:	74213fa0 	cbge	w0, w1, 0 <a>
> +.*:	74413f80 	cbhi	w0, w1, 0 <a>
> +.*:	74413f60 	cbhi	w0, w1, 0 <a>
> +.*:	74613f40 	cbhs	w0, w1, 0 <a>
> +.*:	74613f20 	cbhs	w0, w1, 0 <a>
> +
> +.* <b>:
> +.*:	f4010000 	cbgt	x0, x1, 20 <b>
> +.*:	f4013fe0 	cbgt	x0, x1, 20 <b>
> +.*:	f4213fc0 	cbge	x0, x1, 20 <b>
> +.*:	f4213fa0 	cbge	x0, x1, 20 <b>
> +.*:	f4413f80 	cbhi	x0, x1, 20 <b>
> +.*:	f4413f60 	cbhi	x0, x1, 20 <b>
> +.*:	f4613f40 	cbhs	x0, x1, 20 <b>
> +.*:	f4613f20 	cbhs	x0, x1, 20 <b>
> +
> +.* <c>:
> +.*:	75050000 	cbgt	w0, #10, 40 <c>
> +.*:	75053fe0 	cbgt	w0, #10, 40 <c>
> +.*:	75263fc0 	cblt	w0, #12, 40 <c>
> +.*:	75263fa1 	cblt	w1, #12, 40 <c>
> +.*:	75453f80 	cbhi	w0, #10, 40 <c>
> +.*:	75453f60 	cbhi	w0, #10, 40 <c>
> +.*:	75663f41 	cblo	w1, #12, 40 <c>
> +.*:	7566bf21 	cblo	w1, #13, 40 <c>
> +
> +.* <d>:
> +.*:	f5050000 	cbgt	x0, #10, 60 <d>
> +.*:	f5053fe0 	cbgt	x0, #10, 60 <d>
> +.*:	f5263fc0 	cblt	x0, #12, 60 <d>
> +.*:	f5263fa1 	cblt	x1, #12, 60 <d>
> +.*:	f5453f80 	cbhi	x0, #10, 60 <d>
> +.*:	f5453f60 	cbhi	x0, #10, 60 <d>
> +.*:	f5663f41 	cblo	x1, #12, 60 <d>
> +.*:	f566bf21 	cblo	x1, #13, 60 <d>
> +
> +.* <e>:
> +.*:	74018000 	cbbgt	w0, w1, 80 <e>
> +.*:	7401bfe0 	cbbgt	w0, w1, 80 <e>
> +.*:	7421bfc0 	cbbge	w0, w1, 80 <e>
> +.*:	7421bfa0 	cbbge	w0, w1, 80 <e>
> +.*:	7441bf80 	cbbhi	w0, w1, 80 <e>
> +.*:	7441bf60 	cbbhi	w0, w1, 80 <e>
> +.*:	7461bf40 	cbbhs	w0, w1, 80 <e>
> +.*:	7461bf20 	cbbhs	w0, w1, 80 <e>
> +
> +.* <f>:
> +.*:	7401c000 	cbhgt	w0, w1, a0 <f>
> +.*:	7401ffe0 	cbhgt	w0, w1, a0 <f>
> +.*:	7421ffc0 	cbhge	w0, w1, a0 <f>
> +.*:	7421ffa0 	cbhge	w0, w1, a0 <f>
> +.*:	7441ff80 	cbhhi	w0, w1, a0 <f>
> +.*:	7440ff61 	cbhhi	w1, w0, a0 <f>
> +.*:	7461ff40 	cbhhs	w0, w1, a0 <f>
> +.*:	7461ff20 	cbhhs	w0, w1, a0 <f>
> diff --git a/gas/testsuite/gas/aarch64/cmpbr-1.s b/gas/testsuite/gas/aarch64/cmpbr-1.s
> new file mode 100644
> index 00000000000..bd46851faa2
> --- /dev/null
> +++ b/gas/testsuite/gas/aarch64/cmpbr-1.s
> @@ -0,0 +1,59 @@
> +a:
> +	cbgt	w0, w1, a
> +	cblt	w1, w0, a
> +	cbge	w0, w1, a
> +	cble	w1, w0, a
> +	cbhi	w0, w1, a
> +	cblo	w1, w0, a
> +	cbhs	w0, w1, a
> +	cbls	w1, w0, a
> +
> +b:
> +	cbgt	x0, x1, b
> +	cblt	x1, x0, b
> +	cbge	x0, x1, b
> +	cble	x1, x0, b
> +	cbhi	x0, x1, b
> +	cblo	x1, x0, b
> +	cbhs	x0, x1, b
> +	cbls	x1, x0, b
> +
> +c:
> +	cbgt	w0, #10, c
> +	cbge	w0, #11, c
> +	cblt	w0, #12, c
> +	cble	w1, #11, c
> +	cbhi	w0, #10, c
> +	cbhs	w0, #11, c
> +	cblo	w1, #12, c
> +	cbls	w1, #12, c
> +
> +d:
> +	cbgt	x0, #10, d
> +	cbge	x0, #11, d
> +	cblt	x0, #12, d
> +	cble	x1, #11, d
> +	cbhi	x0, #10, d
> +	cbhs	x0, #11, d
> +	cblo	x1, #12, d
> +	cbls	x1, #12, d
> +
> +e:
> +	cbbgt	w0, w1, e
> +	cbblt	w1, w0, e
> +	cbbge	w0, w1, e
> +	cbble	w1, w0, e
> +	cbbhi	w0, w1, e
> +	cbblo	w1, w0, e
> +	cbbhs	w0, w1, e
> +	cbbls	w1, w0, e
> +
> +f:
> +	cbhgt	w0, w1, f
> +	cbhlt	w1, w0, f
> +	cbhge	w0, w1, f
> +	cbhle	w1, w0, f
> +	cbhhi	w0, w1, f
> +	cbhlo	w1, w0, f
> +	cbhhs	w0, w1, f
> +	cbhls	w1, w0, f
> diff --git a/gas/testsuite/gas/aarch64/cmpbr-bad.d b/gas/testsuite/gas/aarch64/cmpbr-bad.d
> new file mode 100644
> index 00000000000..6487979e85f
> --- /dev/null
> +++ b/gas/testsuite/gas/aarch64/cmpbr-bad.d
> @@ -0,0 +1,4 @@
> +#name: Test of invalid cmpbr operands
> +#source: cmpbr-bad.s
> +#as: -march=armv8-a+cmpbr
> +#error_output: cmpbr-bad.l
> diff --git a/gas/testsuite/gas/aarch64/cmpbr-bad.l b/gas/testsuite/gas/aarch64/cmpbr-bad.l
> new file mode 100644
> index 00000000000..f705cfd0043
> --- /dev/null
> +++ b/gas/testsuite/gas/aarch64/cmpbr-bad.l
> @@ -0,0 +1,13 @@
> +.[^ :]+: Assembler messages:
> +[^ :]+:[0-9]+: Error: operand mismatch -- `cbgt w0,x1,a'
> +[^ :]+:[0-9]+: Info:    did you mean this\?
> +[^ :]+:[0-9]+: Info:    	cbgt w0, w1, #0x0
> +[^ :]+:[0-9]+: Info:    other valid variant\(s\):
> +[^ :]+:[0-9]+: Info:    	cbgt x0, x1, #0x0
> +[^ :]+:[0-9]+: Error: immediate value out of range 0 to 63 at operand 2 -- `cbgt w0,#64,a'
> +[^ :]+:[0-9]+: Error: operand mismatch -- `cbbgt x0,x1,a'
> +[^ :]+:[0-9]+: Info:    did you mean this\?
> +[^ :]+:[0-9]+: Info:    	cbbgt w0, w1, #0x0
> +[^ :]+:[0-9]+: Error: operand mismatch -- `cbhgt x0,x1,a'
> +[^ :]+:[0-9]+: Info:    did you mean this\?
> +[^ :]+:[0-9]+: Info:    	cbhgt w0, w1, #0x0
> diff --git a/gas/testsuite/gas/aarch64/cmpbr-bad.s b/gas/testsuite/gas/aarch64/cmpbr-bad.s
> new file mode 100644
> index 00000000000..8fb1e617254
> --- /dev/null
> +++ b/gas/testsuite/gas/aarch64/cmpbr-bad.s
> @@ -0,0 +1,5 @@
> +a:
> +	cbgt	w0, x1, a
> +	cbgt	w0, #64, a
> +	cbbgt	x0, x1, a
> +	cbhgt	x0, x1, a
> diff --git a/gas/testsuite/gas/aarch64/cmpbr.d b/gas/testsuite/gas/aarch64/cmpbr.d
> new file mode 100644
> index 00000000000..dd693496822
> --- /dev/null
> +++ b/gas/testsuite/gas/aarch64/cmpbr.d
> @@ -0,0 +1,55 @@
> +#name: Test for FEAT_CMPBR
> +#as: -march=armv8-a+cmpbr
> +#objdump: -dr
> +
> +.*:     file format .*
> +
> +Disassembly of section .text:
> +
> +.* <a>:
> +.*:	74010000 	cbgt	w0, w1, 0 <a>
> +.*:	74213fe0 	cbge	w0, w1, 0 <a>
> +.*:	74413fc0 	cbhi	w0, w1, 0 <a>
> +.*:	74613fa0 	cbhs	w0, w1, 0 <a>
> +.*:	74c13f80 	cbeq	w0, w1, 0 <a>
> +.*:	74e13f60 	cbne	w0, w1, 0 <a>
> +
> +.* <b>:
> +.*:	f4010000 	cbgt	x0, x1, 18 <b>
> +.*:	f4213fe0 	cbge	x0, x1, 18 <b>
> +.*:	f4413fc0 	cbhi	x0, x1, 18 <b>
> +.*:	f4613fa0 	cbhs	x0, x1, 18 <b>
> +.*:	f4c13f80 	cbeq	x0, x1, 18 <b>
> +.*:	f4e13f60 	cbne	x0, x1, 18 <b>
> +
> +.* <c>:
> +.*:	75010000 	cbgt	w0, #2, 30 <c>
> +.*:	75223fe0 	cblt	w0, #4, 30 <c>
> +.*:	75433fc0 	cbhi	w0, #6, 30 <c>
> +.*:	75643fa0 	cblo	w0, #8, 30 <c>
> +.*:	75c53f80 	cbeq	w0, #10, 30 <c>
> +.*:	75e63f60 	cbne	w0, #12, 30 <c>
> +
> +.* <d>:
> +.*:	f5070000 	cbgt	x0, #14, 48 <d>
> +.*:	f5283fe0 	cblt	x0, #16, 48 <d>
> +.*:	f5493fc0 	cbhi	x0, #18, 48 <d>
> +.*:	f56a3fa0 	cblo	x0, #20, 48 <d>
> +.*:	f5cb3f80 	cbeq	x0, #22, 48 <d>
> +.*:	f5ec3f60 	cbne	x0, #24, 48 <d>
> +
> +.* <e>:
> +.*:	74018000 	cbbgt	w0, w1, 60 <e>
> +.*:	7421bfe0 	cbbge	w0, w1, 60 <e>
> +.*:	7441bfc0 	cbbhi	w0, w1, 60 <e>
> +.*:	7461bfa0 	cbbhs	w0, w1, 60 <e>
> +.*:	74c1bf80 	cbbeq	w0, w1, 60 <e>
> +.*:	74e1bf60 	cbbne	w0, w1, 60 <e>
> +
> +.* <f>:
> +.*:	7401c000 	cbhgt	w0, w1, 78 <f>
> +.*:	7421ffe0 	cbhge	w0, w1, 78 <f>
> +.*:	7441ffc0 	cbhhi	w0, w1, 78 <f>
> +.*:	7461ffa0 	cbhhs	w0, w1, 78 <f>
> +.*:	74c1ff80 	cbheq	w0, w1, 78 <f>
> +.*:	74e1ff60 	cbhne	w0, w1, 78 <f>
> diff --git a/gas/testsuite/gas/aarch64/cmpbr.s b/gas/testsuite/gas/aarch64/cmpbr.s
> new file mode 100644
> index 00000000000..95d3fae5bab
> --- /dev/null
> +++ b/gas/testsuite/gas/aarch64/cmpbr.s
> @@ -0,0 +1,47 @@
> +a:
> +	cbgt	w0, w1, a
> +	cbge	w0, w1, a
> +	cbhi	w0, w1, a
> +	cbhs	w0, w1, a
> +	cbeq	w0, w1, a
> +	cbne	w0, w1, a
> +
> +b:
> +	cbgt	x0, x1, b
> +	cbge	x0, x1, b
> +	cbhi	x0, x1, b
> +	cbhs	x0, x1, b
> +	cbeq	x0, x1, b
> +	cbne	x0, x1, b
> +
> +c:
> +	cbgt	w0, #2, c
> +	cblt	w0, #4, c
> +	cbhi	w0, #6, c
> +	cblo	w0, #8, c
> +	cbeq	w0, #10, c
> +	cbne	w0, #12, c
> +
> +d:
> +	cbgt	x0, #14, d
> +	cblt	x0, #16, d
> +	cbhi	x0, #18, d
> +	cblo	x0, #20, d
> +	cbeq	x0, #22, d
> +	cbne	x0, #24, d
> +
> +e:
> +	cbbgt	w0, w1, e
> +	cbbge	w0, w1, e
> +	cbbhi	w0, w1, e
> +	cbbhs	w0, w1, e
> +	cbbeq	w0, w1, e
> +	cbbne	w0, w1, e
> +
> +f:
> +	cbhgt	w0, w1, f
> +	cbhge	w0, w1, f
> +	cbhhi	w0, w1, f
> +	cbhhs	w0, w1, f
> +	cbheq	w0, w1, f
> +	cbhne	w0, w1, f

I'd like to see more thorough tests for each opcode table entry, that verify
that every variable bit in the entry can be set to both 0 and 1 and verify that
operands (and any separate fields within an operand) are in the correct order.
There are various ways to arrange this, but usually I start with a test in
which all the variable bits are zero, then set each operand in turn to its
all-ones value.  If there's multiple qualifier sequences allowed, then I'd
usually repeat this for each qualifier sequence.

I'd also like to see tests for the new IMMP1_2 and IMMS1_2 operands that show
that there are still sensible error messages for the value 0 and 63
respectively (which would normally be in range for a 6-bit immediate).

> diff --git a/opcodes/aarch64-opc.h b/opcodes/aarch64-opc.h
> index 9ab9bdf5123..3086f0285d8 100644
> --- a/opcodes/aarch64-opc.h
> +++ b/opcodes/aarch64-opc.h
> @@ -185,6 +185,7 @@ enum aarch64_field_kind
>    FLD_imm7,
>    FLD_imm8,
>    FLD_imm9,
> +  FLD_imm9_5,
>    FLD_imm12,
>    FLD_imm14,
>    FLD_imm16_0,
> diff --git a/opcodes/aarch64-tbl.h b/opcodes/aarch64-tbl.h
> index 8b64eb07067..d4ab134e0a4 100644
> --- a/opcodes/aarch64-tbl.h
> +++ b/opcodes/aarch64-tbl.h
> @@ -136,6 +136,19 @@
>    QLF2(X,NIL),			\
>  }
>  
> +/* e.g. CBBGT <Wt>, <Wm>, <label>.  */
> +#define QL_W2_PCREL		\
> +{				\
> +  QLF3(W,W,NIL),		\
> +}
> +

Could you use more generic names - e.g. QL_W2NIL to match the existing
QL_R2NIL?  Then it can be reused when useful without worrying about the name
being wrong (as is the case with the equivalent QL_W2_LDST_EXC macro), and it's
more consistent with the other instructions you've added.

> +/* e.g. CBGT <Wt>, #<imm6>, <label>.  */
> +#define QL_R_IMM_PCREL		\
> +{				\
> +  QLF3(W,imm_0_63,NIL),		\
> +  QLF3(X,imm_0_63,NIL),		\
> +}
> +
>  /* e.g. LDR <Dt>, <label>.  */
>  #define QL_FP_PCREL		\
>  {				\
> @@ -2737,6 +2750,8 @@ static const aarch64_feature_set aarch64_feature_predres =
>    AARCH64_FEATURE (PREDRES);
>  static const aarch64_feature_set aarch64_feature_predres2 =
>    AARCH64_FEATURES (2, PREDRES, PREDRES2);
> +static const aarch64_feature_set aarch64_feature_cmpbr =
> +  AARCH64_FEATURE (CMPBR);
>  static const aarch64_feature_set aarch64_feature_memtag =
>    AARCH64_FEATURE (MEMTAG);
>  static const aarch64_feature_set aarch64_feature_bfloat16 =
> @@ -2897,6 +2912,7 @@ static const aarch64_feature_set aarch64_feature_sve2p1_sme2p1 =
>  #define SB		&aarch64_feature_sb
>  #define PREDRES		&aarch64_feature_predres
>  #define PREDRES2	&aarch64_feature_predres2
> +#define CMPBR		&aarch64_feature_cmpbr
>  #define MEMTAG		&aarch64_feature_memtag
>  #define TME		&aarch64_feature_tme
>  #define SVE2		&aarch64_feature_sve2
> @@ -3022,6 +3038,8 @@ static const aarch64_feature_set aarch64_feature_sve2p1_sme2p1 =
>    { NAME, OPCODE, MASK, CLASS, 0, SB, OPS, QUALS, FLAGS, 0, 0, NULL }
>  #define PREDRES_INSN(NAME,OPCODE,MASK,CLASS,OPS,QUALS,FLAGS) \
>    { NAME, OPCODE, MASK, CLASS, 0, PREDRES, OPS, QUALS, FLAGS, 0, 0, NULL }
> +#define CMPBR_INSN(NAME,OPCODE,MASK,CLASS,OPS,QUALS,FLAGS) \
> +  { NAME, OPCODE, MASK, CLASS, 0, CMPBR, OPS, QUALS, FLAGS, 0, 0, NULL }
>  #define MEMTAG_INSN(NAME,OPCODE,MASK,CLASS,OPS,QUALS,FLAGS) \
>    { NAME, OPCODE, MASK, CLASS, 0, MEMTAG, OPS, QUALS, FLAGS, 0, 0, NULL }
>  #define _TME_INSN(NAME,OPCODE,MASK,CLASS,OP,OPS,QUALS,FLAGS) \
> @@ -3965,6 +3983,50 @@ const struct aarch64_opcode aarch64_opcode_table[] =
>    CORE_INSN ("cbnz", 0x35000000, 0x7f000000, compbranch, 0, OP2 (Rt, ADDR_PCREL19), QL_R_PCREL, F_SF),
>    /* Conditional branch (immediate).  */
>    CORE_INSN ("b.c", 0x54000000, 0xff000010, condbranch, 0, OP1 (ADDR_PCREL19), QL_PCREL_NIL, F_COND),
> +  /* Compare registers and branch. */
> +  CMPBR_INSN ("cbgt", 0x74000000, 0x7fe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_R2NIL, F_SF | F_HAS_ALIAS),
> +  CMPBR_INSN ("cblt", 0x74000000, 0x7fe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_R2NIL, F_SF | F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cbge", 0x74200000, 0x7fe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_R2NIL, F_SF | F_HAS_ALIAS),
> +  CMPBR_INSN ("cble", 0x74200000, 0x7fe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_R2NIL, F_SF | F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cbhi", 0x74400000, 0x7fe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_R2NIL, F_SF | F_HAS_ALIAS),
> +  CMPBR_INSN ("cblo", 0x74400000, 0x7fe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_R2NIL, F_SF | F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cbhs", 0x74600000, 0x7fe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_R2NIL, F_SF | F_HAS_ALIAS),
> +  CMPBR_INSN ("cbls", 0x74600000, 0x7fe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_R2NIL, F_SF | F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cbeq", 0x74c00000, 0x7fe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_R2NIL, F_SF),
> +  CMPBR_INSN ("cbne", 0x74e00000, 0x7fe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_R2NIL, F_SF),
> +  /* Compare register with immediate and branch.  */
> +  CMPBR_INSN ("cbgt", 0x75000000, 0x7fe04000, compbranch, OP3 (Rt, IMM_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_HAS_ALIAS),
> +  CMPBR_INSN ("cbge", 0x75000000, 0x7fe04000, compbranch, OP3 (Rt, IMMP1_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cblt", 0x75200000, 0x7fe04000, compbranch, OP3 (Rt, IMM_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_HAS_ALIAS),
> +  CMPBR_INSN ("cble", 0x75200000, 0x7fe04000, compbranch, OP3 (Rt, IMMS1_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cbhi", 0x75400000, 0x7fe04000, compbranch, OP3 (Rt, IMM_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_HAS_ALIAS),
> +  CMPBR_INSN ("cbhs", 0x75400000, 0x7fe04000, compbranch, OP3 (Rt, IMMP1_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cblo", 0x75600000, 0x7fe04000, compbranch, OP3 (Rt, IMM_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_HAS_ALIAS),
> +  CMPBR_INSN ("cbls", 0x75600000, 0x7fe04000, compbranch, OP3 (Rt, IMMS1_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cbeq", 0x75c00000, 0x7fe04000, compbranch, OP3 (Rt, IMM_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF),
> +  CMPBR_INSN ("cbne", 0x75e00000, 0x7fe04000, compbranch, OP3 (Rt, IMM_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF),
> +  /* Compare bytes and branch.  */
> +  CMPBR_INSN ("cbbgt", 0x74008000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
> +  CMPBR_INSN ("cbblt", 0x74008000, 0xffe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cbbge", 0x74208000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
> +  CMPBR_INSN ("cbble", 0x74208000, 0xffe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cbbhi", 0x74408000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
> +  CMPBR_INSN ("cbblo", 0x74408000, 0xffe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cbbhs", 0x74608000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
> +  CMPBR_INSN ("cbbls", 0x74608000, 0xffe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cbbeq", 0x74c08000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, 0),
> +  CMPBR_INSN ("cbbne", 0x74e08000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, 0),
> +  /* Compare halfwords and branch.  */
> +  CMPBR_INSN ("cbhgt", 0x7400c000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
> +  CMPBR_INSN ("cbhlt", 0x7400c000, 0xffe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cbhge", 0x7420c000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
> +  CMPBR_INSN ("cbhle", 0x7420c000, 0xffe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cbhhi", 0x7440c000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
> +  CMPBR_INSN ("cbhlo", 0x7440c000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cbhhs", 0x7460c000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
> +  CMPBR_INSN ("cbhls", 0x7460c000, 0xffe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
> +  CMPBR_INSN ("cbheq", 0x74c0c000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, 0),
> +  CMPBR_INSN ("cbhne", 0x74e0c000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, 0),
>    /* Conditional compare (immediate).  */
>    CORE_INSN ("ccmn", 0x3a400800, 0x7fe00c10, condcmp_imm, 0, OP4 (Rn, CCMP_IMM, NZCV, COND), QL_CCMP_IMM, F_SF),
>    CORE_INSN ("ccmp", 0x7a400800, 0x7fe00c10, condcmp_imm, 0, OP4 (Rn, CCMP_IMM, NZCV, COND), QL_CCMP_IMM, F_SF),
> @@ -7126,6 +7188,8 @@ const struct aarch64_opcode aarch64_opcode_table[] =
>        "the width of the bit-field")					\
>      Y(IMMEDIATE, imm, "IMM", 0, F(FLD_imm6_10), "an immediate")         \
>      Y(IMMEDIATE, imm, "IMM_2", 0, F(FLD_imm6_15), "an immediate")       \
> +    Y(IMMEDIATE, imm, "IMMP1_2", 0, F(FLD_imm6_15), "an immediate plus 1")       \
> +    Y(IMMEDIATE, imm, "IMMS1_2", 0, F(FLD_imm6_15), "an immediate minus 1")       \
>      Y(IMMEDIATE, imm, "UIMM3_OP1", 0, F(FLD_op1),			\
>        "a 3-bit unsigned immediate")					\
>      Y(IMMEDIATE, imm, "UIMM3_OP2", 0, F(FLD_op2),			\
> @@ -7170,6 +7234,8 @@ const struct aarch64_opcode aarch64_opcode_table[] =
>        "one of the standard conditions, excluding AL and NV.")		\
>      X(ADDRESS, 0, ext_imm, "ADDR_ADRP", OPD_F_SEXT, F(FLD_immhi, FLD_immlo),\
>        "21-bit PC-relative address of a 4KB page")			\
> +    Y(ADDRESS, imm, "ADDR_PCREL9", OPD_F_SEXT | OPD_F_SHIFT_BY_2,	\
> +      F(FLD_imm9_5), "9-bit PC-relative address")			\
>      Y(ADDRESS, imm, "ADDR_PCREL14", OPD_F_SEXT | OPD_F_SHIFT_BY_2,	\
>        F(FLD_imm14), "14-bit PC-relative address")			\
>      Y(ADDRESS, imm, "ADDR_PCREL19", OPD_F_SEXT | OPD_F_SHIFT_BY_2,	\
> -- 
> 2.45.2
  

Patch

diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
index 548da1f8b30..8f399204f04 100644
--- a/bfd/elfnn-aarch64.c
+++ b/bfd/elfnn-aarch64.c
@@ -2268,6 +2268,9 @@  elfNN_aarch64_howto_from_bfd_reloc (bfd_reloc_code_real_type code)
   if (code == BFD_RELOC_AARCH64_NONE)
     return &elfNN_aarch64_howto_none;
 
+  if (code == BFD_RELOC_AARCH64_BRANCH9)
+    return &elfNN_aarch64_howto_none;
+
   return NULL;
 }
 
diff --git a/bfd/reloc.c b/bfd/reloc.c
index d3ddafb7305..1aa0add7790 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -7202,7 +7202,7 @@  ENUM
   BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC
 ENUMDOC
   Similar to BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12, but no
-  overflow check. 
+  overflow check.
 ENUM
   BFD_RELOC_AARCH64_TLSLD_LDST64_DTPREL_LO12
 ENUMDOC
@@ -7418,6 +7418,12 @@  ENUM
 ENUMDOC
   AArch64 pseudo relocation code to be used internally by the AArch64
   assembler and not (currently) written to any object files.
+ENUM
+  BFD_RELOC_AARCH64_BRANCH9
+ENUMDOC
+  AArch64 9 bit pc-relative conditional branch and compare & branch.
+  The lowest two bits must be zero and are not stored in the
+  instruction, giving an 11 bit signed byte offset.
 ENUM
   BFD_RELOC_TILEPRO_COPY
 ENUMX
diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c
index e071ad11f46..6607d31467d 100644
--- a/gas/config/tc-aarch64.c
+++ b/gas/config/tc-aarch64.c
@@ -5175,6 +5175,13 @@  encode_branch_ofs_26 (uint32_t ofs)
   return ofs & ((1 << 26) - 1);
 }
 
+/* encode the 9-bit offset of FEAT_CMPBR compare and branch */
+static inline uint32_t
+encode_cond_branch_ofs_9 (uint32_t ofs)
+{
+  return (ofs & ((1 << 9) - 1)) << 5;
+}
+
 /* encode the 19-bit offset of conditional branch and compare & branch */
 static inline uint32_t
 encode_cond_branch_ofs_19 (uint32_t ofs)
@@ -6388,6 +6395,8 @@  process_omitted_operand (enum aarch64_opnd type, const aarch64_opcode *opcode,
     case AARCH64_OPND_UIMM3_OP2:
     case AARCH64_OPND_IMM:
     case AARCH64_OPND_IMM_2:
+    case AARCH64_OPND_IMMP1_2:
+    case AARCH64_OPND_IMMS1_2:
     case AARCH64_OPND_WIDTH:
     case AARCH64_OPND_UIMM7:
     case AARCH64_OPND_NZCV:
@@ -7154,6 +7163,16 @@  parse_operands (char *str, const aarch64_opcode *opcode)
 	  info->imm.value = val;
 	  break;
 
+	case AARCH64_OPND_IMMP1_2:
+	  po_imm_nc_or_fail ();
+	  info->imm.value = val - 1;
+	  break;
+
+	case AARCH64_OPND_IMMS1_2:
+	  po_imm_nc_or_fail ();
+	  info->imm.value = val + 1;
+	  break;
+
 	case AARCH64_OPND_SVE_AIMM:
 	case AARCH64_OPND_SVE_ASIMM:
 	  po_imm_nc_or_fail ();
@@ -7450,6 +7469,7 @@  parse_operands (char *str, const aarch64_opcode *opcode)
 	  info->imm.value = 0;
 	  break;
 
+	case AARCH64_OPND_ADDR_PCREL9:
 	case AARCH64_OPND_ADDR_PCREL14:
 	case AARCH64_OPND_ADDR_PCREL19:
 	case AARCH64_OPND_ADDR_PCREL21:
@@ -7487,8 +7507,11 @@  parse_operands (char *str, const aarch64_opcode *opcode)
 		  case compbranch:
 		  case condbranch:
 		    /* e.g. CBZ or B.COND  */
-		    gas_assert (operands[i] == AARCH64_OPND_ADDR_PCREL19);
-		    inst.reloc.type = BFD_RELOC_AARCH64_BRANCH19;
+		    gas_assert (operands[i] == AARCH64_OPND_ADDR_PCREL9
+		                || operands[i] == AARCH64_OPND_ADDR_PCREL19);
+		    inst.reloc.type = (operands[i] == AARCH64_OPND_ADDR_PCREL9)
+		                       ? BFD_RELOC_AARCH64_BRANCH9
+		                       : BFD_RELOC_AARCH64_BRANCH19;
 		    break;
 		  case testbranch:
 		    /* e.g. TBZ  */
@@ -9670,6 +9693,20 @@  md_apply_fix (fixS * fixP, valueT * valP, segT seg)
 	}
       break;
 
+    case BFD_RELOC_AARCH64_BRANCH9:
+      if (fixP->fx_done || !seg->use_rela_p)
+	{
+	  if (value & 3)
+	    as_bad_where (fixP->fx_file, fixP->fx_line,
+			  _("conditional branch target not word aligned"));
+	  if (signed_overflow (value, 11))
+	    as_bad_where (fixP->fx_file, fixP->fx_line,
+			  _("conditional branch out of range"));
+	  insn = get_aarch64_insn (buf);
+	  insn |= encode_cond_branch_ofs_9 (value >> 2);
+	  put_aarch64_insn (buf, insn);
+	}
+        break;
     case BFD_RELOC_AARCH64_BRANCH19:
       if (fixP->fx_done || !seg->use_rela_p)
 	{
@@ -10672,6 +10709,7 @@  static const struct aarch64_option_cpu_value_table aarch64_features[] = {
   {"rng",		AARCH64_FEATURE (RNG), AARCH64_NO_FEATURES},
   {"ssbs",		AARCH64_FEATURE (SSBS), AARCH64_NO_FEATURES},
   {"memtag",		AARCH64_FEATURE (MEMTAG), AARCH64_NO_FEATURES},
+  {"cmpbr",		AARCH64_FEATURE (CMPBR), AARCH64_NO_FEATURES},
   {"sve2",		AARCH64_FEATURE (SVE2), AARCH64_FEATURE (SVE)},
   {"sve2-sm4",		AARCH64_FEATURE (SVE2_SM4),
 			AARCH64_FEATURES (2, SVE2, SM4)},
diff --git a/gas/doc/c-aarch64.texi b/gas/doc/c-aarch64.texi
index 10888d1e78f..5927998c0bd 100644
--- a/gas/doc/c-aarch64.texi
+++ b/gas/doc/c-aarch64.texi
@@ -229,6 +229,8 @@  automatically cause those extensions to be disabled.
  @tab Enable the Lookup Table (LUT) extension.
 @item @code{memtag} @tab
  @tab Enable Armv8.5-A Memory Tagging Extensions.
+@item @code{cmpbr} @tab
+ @tab Enable Compare and Branch instructions.
 @item @code{mops} @tab
  @tab Enable Armv8.8-A memcpy and memset acceleration instructions
 @item @code{pan} @tab
diff --git a/gas/testsuite/gas/aarch64/cmpbr-1.d b/gas/testsuite/gas/aarch64/cmpbr-1.d
new file mode 100644
index 00000000000..d5e02a09f4b
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/cmpbr-1.d
@@ -0,0 +1,67 @@ 
+#name: Test for FEAT_CMPBR pseudo-instructions
+#as: -march=armv8-a+cmpbr
+#objdump: -dr
+
+.*:     file format .*
+
+Disassembly of section .text:
+
+.* <a>:
+.*:	74010000 	cbgt	w0, w1, 0 <a>
+.*:	74013fe0 	cbgt	w0, w1, 0 <a>
+.*:	74213fc0 	cbge	w0, w1, 0 <a>
+.*:	74213fa0 	cbge	w0, w1, 0 <a>
+.*:	74413f80 	cbhi	w0, w1, 0 <a>
+.*:	74413f60 	cbhi	w0, w1, 0 <a>
+.*:	74613f40 	cbhs	w0, w1, 0 <a>
+.*:	74613f20 	cbhs	w0, w1, 0 <a>
+
+.* <b>:
+.*:	f4010000 	cbgt	x0, x1, 20 <b>
+.*:	f4013fe0 	cbgt	x0, x1, 20 <b>
+.*:	f4213fc0 	cbge	x0, x1, 20 <b>
+.*:	f4213fa0 	cbge	x0, x1, 20 <b>
+.*:	f4413f80 	cbhi	x0, x1, 20 <b>
+.*:	f4413f60 	cbhi	x0, x1, 20 <b>
+.*:	f4613f40 	cbhs	x0, x1, 20 <b>
+.*:	f4613f20 	cbhs	x0, x1, 20 <b>
+
+.* <c>:
+.*:	75050000 	cbgt	w0, #10, 40 <c>
+.*:	75053fe0 	cbgt	w0, #10, 40 <c>
+.*:	75263fc0 	cblt	w0, #12, 40 <c>
+.*:	75263fa1 	cblt	w1, #12, 40 <c>
+.*:	75453f80 	cbhi	w0, #10, 40 <c>
+.*:	75453f60 	cbhi	w0, #10, 40 <c>
+.*:	75663f41 	cblo	w1, #12, 40 <c>
+.*:	7566bf21 	cblo	w1, #13, 40 <c>
+
+.* <d>:
+.*:	f5050000 	cbgt	x0, #10, 60 <d>
+.*:	f5053fe0 	cbgt	x0, #10, 60 <d>
+.*:	f5263fc0 	cblt	x0, #12, 60 <d>
+.*:	f5263fa1 	cblt	x1, #12, 60 <d>
+.*:	f5453f80 	cbhi	x0, #10, 60 <d>
+.*:	f5453f60 	cbhi	x0, #10, 60 <d>
+.*:	f5663f41 	cblo	x1, #12, 60 <d>
+.*:	f566bf21 	cblo	x1, #13, 60 <d>
+
+.* <e>:
+.*:	74018000 	cbbgt	w0, w1, 80 <e>
+.*:	7401bfe0 	cbbgt	w0, w1, 80 <e>
+.*:	7421bfc0 	cbbge	w0, w1, 80 <e>
+.*:	7421bfa0 	cbbge	w0, w1, 80 <e>
+.*:	7441bf80 	cbbhi	w0, w1, 80 <e>
+.*:	7441bf60 	cbbhi	w0, w1, 80 <e>
+.*:	7461bf40 	cbbhs	w0, w1, 80 <e>
+.*:	7461bf20 	cbbhs	w0, w1, 80 <e>
+
+.* <f>:
+.*:	7401c000 	cbhgt	w0, w1, a0 <f>
+.*:	7401ffe0 	cbhgt	w0, w1, a0 <f>
+.*:	7421ffc0 	cbhge	w0, w1, a0 <f>
+.*:	7421ffa0 	cbhge	w0, w1, a0 <f>
+.*:	7441ff80 	cbhhi	w0, w1, a0 <f>
+.*:	7440ff61 	cbhhi	w1, w0, a0 <f>
+.*:	7461ff40 	cbhhs	w0, w1, a0 <f>
+.*:	7461ff20 	cbhhs	w0, w1, a0 <f>
diff --git a/gas/testsuite/gas/aarch64/cmpbr-1.s b/gas/testsuite/gas/aarch64/cmpbr-1.s
new file mode 100644
index 00000000000..bd46851faa2
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/cmpbr-1.s
@@ -0,0 +1,59 @@ 
+a:
+	cbgt	w0, w1, a
+	cblt	w1, w0, a
+	cbge	w0, w1, a
+	cble	w1, w0, a
+	cbhi	w0, w1, a
+	cblo	w1, w0, a
+	cbhs	w0, w1, a
+	cbls	w1, w0, a
+
+b:
+	cbgt	x0, x1, b
+	cblt	x1, x0, b
+	cbge	x0, x1, b
+	cble	x1, x0, b
+	cbhi	x0, x1, b
+	cblo	x1, x0, b
+	cbhs	x0, x1, b
+	cbls	x1, x0, b
+
+c:
+	cbgt	w0, #10, c
+	cbge	w0, #11, c
+	cblt	w0, #12, c
+	cble	w1, #11, c
+	cbhi	w0, #10, c
+	cbhs	w0, #11, c
+	cblo	w1, #12, c
+	cbls	w1, #12, c
+
+d:
+	cbgt	x0, #10, d
+	cbge	x0, #11, d
+	cblt	x0, #12, d
+	cble	x1, #11, d
+	cbhi	x0, #10, d
+	cbhs	x0, #11, d
+	cblo	x1, #12, d
+	cbls	x1, #12, d
+
+e:
+	cbbgt	w0, w1, e
+	cbblt	w1, w0, e
+	cbbge	w0, w1, e
+	cbble	w1, w0, e
+	cbbhi	w0, w1, e
+	cbblo	w1, w0, e
+	cbbhs	w0, w1, e
+	cbbls	w1, w0, e
+
+f:
+	cbhgt	w0, w1, f
+	cbhlt	w1, w0, f
+	cbhge	w0, w1, f
+	cbhle	w1, w0, f
+	cbhhi	w0, w1, f
+	cbhlo	w1, w0, f
+	cbhhs	w0, w1, f
+	cbhls	w1, w0, f
diff --git a/gas/testsuite/gas/aarch64/cmpbr-bad.d b/gas/testsuite/gas/aarch64/cmpbr-bad.d
new file mode 100644
index 00000000000..6487979e85f
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/cmpbr-bad.d
@@ -0,0 +1,4 @@ 
+#name: Test of invalid cmpbr operands
+#source: cmpbr-bad.s
+#as: -march=armv8-a+cmpbr
+#error_output: cmpbr-bad.l
diff --git a/gas/testsuite/gas/aarch64/cmpbr-bad.l b/gas/testsuite/gas/aarch64/cmpbr-bad.l
new file mode 100644
index 00000000000..f705cfd0043
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/cmpbr-bad.l
@@ -0,0 +1,13 @@ 
+.[^ :]+: Assembler messages:
+[^ :]+:[0-9]+: Error: operand mismatch -- `cbgt w0,x1,a'
+[^ :]+:[0-9]+: Info:    did you mean this\?
+[^ :]+:[0-9]+: Info:    	cbgt w0, w1, #0x0
+[^ :]+:[0-9]+: Info:    other valid variant\(s\):
+[^ :]+:[0-9]+: Info:    	cbgt x0, x1, #0x0
+[^ :]+:[0-9]+: Error: immediate value out of range 0 to 63 at operand 2 -- `cbgt w0,#64,a'
+[^ :]+:[0-9]+: Error: operand mismatch -- `cbbgt x0,x1,a'
+[^ :]+:[0-9]+: Info:    did you mean this\?
+[^ :]+:[0-9]+: Info:    	cbbgt w0, w1, #0x0
+[^ :]+:[0-9]+: Error: operand mismatch -- `cbhgt x0,x1,a'
+[^ :]+:[0-9]+: Info:    did you mean this\?
+[^ :]+:[0-9]+: Info:    	cbhgt w0, w1, #0x0
diff --git a/gas/testsuite/gas/aarch64/cmpbr-bad.s b/gas/testsuite/gas/aarch64/cmpbr-bad.s
new file mode 100644
index 00000000000..8fb1e617254
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/cmpbr-bad.s
@@ -0,0 +1,5 @@ 
+a:
+	cbgt	w0, x1, a
+	cbgt	w0, #64, a
+	cbbgt	x0, x1, a
+	cbhgt	x0, x1, a
diff --git a/gas/testsuite/gas/aarch64/cmpbr.d b/gas/testsuite/gas/aarch64/cmpbr.d
new file mode 100644
index 00000000000..dd693496822
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/cmpbr.d
@@ -0,0 +1,55 @@ 
+#name: Test for FEAT_CMPBR
+#as: -march=armv8-a+cmpbr
+#objdump: -dr
+
+.*:     file format .*
+
+Disassembly of section .text:
+
+.* <a>:
+.*:	74010000 	cbgt	w0, w1, 0 <a>
+.*:	74213fe0 	cbge	w0, w1, 0 <a>
+.*:	74413fc0 	cbhi	w0, w1, 0 <a>
+.*:	74613fa0 	cbhs	w0, w1, 0 <a>
+.*:	74c13f80 	cbeq	w0, w1, 0 <a>
+.*:	74e13f60 	cbne	w0, w1, 0 <a>
+
+.* <b>:
+.*:	f4010000 	cbgt	x0, x1, 18 <b>
+.*:	f4213fe0 	cbge	x0, x1, 18 <b>
+.*:	f4413fc0 	cbhi	x0, x1, 18 <b>
+.*:	f4613fa0 	cbhs	x0, x1, 18 <b>
+.*:	f4c13f80 	cbeq	x0, x1, 18 <b>
+.*:	f4e13f60 	cbne	x0, x1, 18 <b>
+
+.* <c>:
+.*:	75010000 	cbgt	w0, #2, 30 <c>
+.*:	75223fe0 	cblt	w0, #4, 30 <c>
+.*:	75433fc0 	cbhi	w0, #6, 30 <c>
+.*:	75643fa0 	cblo	w0, #8, 30 <c>
+.*:	75c53f80 	cbeq	w0, #10, 30 <c>
+.*:	75e63f60 	cbne	w0, #12, 30 <c>
+
+.* <d>:
+.*:	f5070000 	cbgt	x0, #14, 48 <d>
+.*:	f5283fe0 	cblt	x0, #16, 48 <d>
+.*:	f5493fc0 	cbhi	x0, #18, 48 <d>
+.*:	f56a3fa0 	cblo	x0, #20, 48 <d>
+.*:	f5cb3f80 	cbeq	x0, #22, 48 <d>
+.*:	f5ec3f60 	cbne	x0, #24, 48 <d>
+
+.* <e>:
+.*:	74018000 	cbbgt	w0, w1, 60 <e>
+.*:	7421bfe0 	cbbge	w0, w1, 60 <e>
+.*:	7441bfc0 	cbbhi	w0, w1, 60 <e>
+.*:	7461bfa0 	cbbhs	w0, w1, 60 <e>
+.*:	74c1bf80 	cbbeq	w0, w1, 60 <e>
+.*:	74e1bf60 	cbbne	w0, w1, 60 <e>
+
+.* <f>:
+.*:	7401c000 	cbhgt	w0, w1, 78 <f>
+.*:	7421ffe0 	cbhge	w0, w1, 78 <f>
+.*:	7441ffc0 	cbhhi	w0, w1, 78 <f>
+.*:	7461ffa0 	cbhhs	w0, w1, 78 <f>
+.*:	74c1ff80 	cbheq	w0, w1, 78 <f>
+.*:	74e1ff60 	cbhne	w0, w1, 78 <f>
diff --git a/gas/testsuite/gas/aarch64/cmpbr.s b/gas/testsuite/gas/aarch64/cmpbr.s
new file mode 100644
index 00000000000..95d3fae5bab
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/cmpbr.s
@@ -0,0 +1,47 @@ 
+a:
+	cbgt	w0, w1, a
+	cbge	w0, w1, a
+	cbhi	w0, w1, a
+	cbhs	w0, w1, a
+	cbeq	w0, w1, a
+	cbne	w0, w1, a
+
+b:
+	cbgt	x0, x1, b
+	cbge	x0, x1, b
+	cbhi	x0, x1, b
+	cbhs	x0, x1, b
+	cbeq	x0, x1, b
+	cbne	x0, x1, b
+
+c:
+	cbgt	w0, #2, c
+	cblt	w0, #4, c
+	cbhi	w0, #6, c
+	cblo	w0, #8, c
+	cbeq	w0, #10, c
+	cbne	w0, #12, c
+
+d:
+	cbgt	x0, #14, d
+	cblt	x0, #16, d
+	cbhi	x0, #18, d
+	cblo	x0, #20, d
+	cbeq	x0, #22, d
+	cbne	x0, #24, d
+
+e:
+	cbbgt	w0, w1, e
+	cbbge	w0, w1, e
+	cbbhi	w0, w1, e
+	cbbhs	w0, w1, e
+	cbbeq	w0, w1, e
+	cbbne	w0, w1, e
+
+f:
+	cbhgt	w0, w1, f
+	cbhge	w0, w1, f
+	cbhhi	w0, w1, f
+	cbhhs	w0, w1, f
+	cbheq	w0, w1, f
+	cbhne	w0, w1, f
diff --git a/include/opcode/aarch64.h b/include/opcode/aarch64.h
index dfe3f05820a..0b124de59d7 100644
--- a/include/opcode/aarch64.h
+++ b/include/opcode/aarch64.h
@@ -135,6 +135,8 @@  enum aarch64_feature_bit {
   AARCH64_FEATURE_ID_PFR2,
   /* SSBS mechanism enabled.  */
   AARCH64_FEATURE_SSBS,
+  /* Compare and branch instructions.  */
+  AARCH64_FEATURE_CMPBR,
   /* Memory Tagging Extension.  */
   AARCH64_FEATURE_MEMTAG,
   /* Transactional Memory Extension.  */
@@ -618,6 +620,8 @@  enum aarch64_opnd
   AARCH64_OPND_WIDTH,	/* Immediate #<width> in e.g. BFI.  */
   AARCH64_OPND_IMM,	/* Immediate.  */
   AARCH64_OPND_IMM_2,	/* Immediate.  */
+  AARCH64_OPND_IMMP1_2,	/* Immediate plus 1.  */
+  AARCH64_OPND_IMMS1_2,	/* Immediate minus 1.  */
   AARCH64_OPND_UIMM3_OP1,/* Unsigned 3-bit immediate in the op1 field.  */
   AARCH64_OPND_UIMM3_OP2,/* Unsigned 3-bit immediate in the op2 field.  */
   AARCH64_OPND_UIMM4,	/* Unsigned 4-bit immediate in the CRm field.  */
@@ -645,6 +649,7 @@  enum aarch64_opnd
   AARCH64_OPND_COND1,	/* Same as the above, but excluding AL and NV.  */
 
   AARCH64_OPND_ADDR_ADRP,	/* Memory address for ADRP */
+  AARCH64_OPND_ADDR_PCREL9,	/* 9-bit PC-relative address for e.g. CB<cc>.  */
   AARCH64_OPND_ADDR_PCREL14,	/* 14-bit PC-relative address for e.g. TBZ.  */
   AARCH64_OPND_ADDR_PCREL19,	/* 19-bit PC-relative address for e.g. LDR.  */
   AARCH64_OPND_ADDR_PCREL21,	/* 21-bit PC-relative address for e.g. ADR.  */
diff --git a/opcodes/aarch64-opc.c b/opcodes/aarch64-opc.c
index 4f0c71696fa..b93c7069ef6 100644
--- a/opcodes/aarch64-opc.c
+++ b/opcodes/aarch64-opc.c
@@ -381,6 +381,7 @@  const aarch64_field fields[] =
     { 15,  7 },	/* imm7: in load/store pair pre/post index instructions.  */
     { 13,  8 },	/* imm8: in floating-point scalar move immediate inst.  */
     { 12,  9 },	/* imm9: in load/store pre/post index instructions.  */
+    {  5,  9 },	/* imm9_5: in CB<cc> (immediate).  */
     { 10, 12 },	/* imm12: in ld/st unsigned imm or add/sub shifted inst.  */
     {  5, 14 },	/* imm14: in test bit and branch instructions.  */
     {  0, 16 },	/* imm16_0: in udf instruction. */
@@ -2417,6 +2418,7 @@  operand_general_constraint_met_p (const aarch64_opnd_info *opnds, int idx,
 	    }
 	  break;
 
+	case AARCH64_OPND_ADDR_PCREL9:
 	case AARCH64_OPND_ADDR_PCREL14:
 	case AARCH64_OPND_ADDR_PCREL19:
 	case AARCH64_OPND_ADDR_PCREL21:
@@ -4781,6 +4783,7 @@  aarch64_print_operand (char *buf, size_t size, bfd_vma pc,
       snprintf (buf, size, "%s", style_addr (styler, "#0x%" PRIx64 , addr));
       break;
 
+    case AARCH64_OPND_ADDR_PCREL9:
     case AARCH64_OPND_ADDR_PCREL14:
     case AARCH64_OPND_ADDR_PCREL19:
     case AARCH64_OPND_ADDR_PCREL21:
diff --git a/opcodes/aarch64-opc.h b/opcodes/aarch64-opc.h
index 9ab9bdf5123..3086f0285d8 100644
--- a/opcodes/aarch64-opc.h
+++ b/opcodes/aarch64-opc.h
@@ -185,6 +185,7 @@  enum aarch64_field_kind
   FLD_imm7,
   FLD_imm8,
   FLD_imm9,
+  FLD_imm9_5,
   FLD_imm12,
   FLD_imm14,
   FLD_imm16_0,
diff --git a/opcodes/aarch64-tbl.h b/opcodes/aarch64-tbl.h
index 8b64eb07067..d4ab134e0a4 100644
--- a/opcodes/aarch64-tbl.h
+++ b/opcodes/aarch64-tbl.h
@@ -136,6 +136,19 @@ 
   QLF2(X,NIL),			\
 }
 
+/* e.g. CBBGT <Wt>, <Wm>, <label>.  */
+#define QL_W2_PCREL		\
+{				\
+  QLF3(W,W,NIL),		\
+}
+
+/* e.g. CBGT <Wt>, #<imm6>, <label>.  */
+#define QL_R_IMM_PCREL		\
+{				\
+  QLF3(W,imm_0_63,NIL),		\
+  QLF3(X,imm_0_63,NIL),		\
+}
+
 /* e.g. LDR <Dt>, <label>.  */
 #define QL_FP_PCREL		\
 {				\
@@ -2737,6 +2750,8 @@  static const aarch64_feature_set aarch64_feature_predres =
   AARCH64_FEATURE (PREDRES);
 static const aarch64_feature_set aarch64_feature_predres2 =
   AARCH64_FEATURES (2, PREDRES, PREDRES2);
+static const aarch64_feature_set aarch64_feature_cmpbr =
+  AARCH64_FEATURE (CMPBR);
 static const aarch64_feature_set aarch64_feature_memtag =
   AARCH64_FEATURE (MEMTAG);
 static const aarch64_feature_set aarch64_feature_bfloat16 =
@@ -2897,6 +2912,7 @@  static const aarch64_feature_set aarch64_feature_sve2p1_sme2p1 =
 #define SB		&aarch64_feature_sb
 #define PREDRES		&aarch64_feature_predres
 #define PREDRES2	&aarch64_feature_predres2
+#define CMPBR		&aarch64_feature_cmpbr
 #define MEMTAG		&aarch64_feature_memtag
 #define TME		&aarch64_feature_tme
 #define SVE2		&aarch64_feature_sve2
@@ -3022,6 +3038,8 @@  static const aarch64_feature_set aarch64_feature_sve2p1_sme2p1 =
   { NAME, OPCODE, MASK, CLASS, 0, SB, OPS, QUALS, FLAGS, 0, 0, NULL }
 #define PREDRES_INSN(NAME,OPCODE,MASK,CLASS,OPS,QUALS,FLAGS) \
   { NAME, OPCODE, MASK, CLASS, 0, PREDRES, OPS, QUALS, FLAGS, 0, 0, NULL }
+#define CMPBR_INSN(NAME,OPCODE,MASK,CLASS,OPS,QUALS,FLAGS) \
+  { NAME, OPCODE, MASK, CLASS, 0, CMPBR, OPS, QUALS, FLAGS, 0, 0, NULL }
 #define MEMTAG_INSN(NAME,OPCODE,MASK,CLASS,OPS,QUALS,FLAGS) \
   { NAME, OPCODE, MASK, CLASS, 0, MEMTAG, OPS, QUALS, FLAGS, 0, 0, NULL }
 #define _TME_INSN(NAME,OPCODE,MASK,CLASS,OP,OPS,QUALS,FLAGS) \
@@ -3965,6 +3983,50 @@  const struct aarch64_opcode aarch64_opcode_table[] =
   CORE_INSN ("cbnz", 0x35000000, 0x7f000000, compbranch, 0, OP2 (Rt, ADDR_PCREL19), QL_R_PCREL, F_SF),
   /* Conditional branch (immediate).  */
   CORE_INSN ("b.c", 0x54000000, 0xff000010, condbranch, 0, OP1 (ADDR_PCREL19), QL_PCREL_NIL, F_COND),
+  /* Compare registers and branch. */
+  CMPBR_INSN ("cbgt", 0x74000000, 0x7fe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_R2NIL, F_SF | F_HAS_ALIAS),
+  CMPBR_INSN ("cblt", 0x74000000, 0x7fe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_R2NIL, F_SF | F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cbge", 0x74200000, 0x7fe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_R2NIL, F_SF | F_HAS_ALIAS),
+  CMPBR_INSN ("cble", 0x74200000, 0x7fe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_R2NIL, F_SF | F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cbhi", 0x74400000, 0x7fe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_R2NIL, F_SF | F_HAS_ALIAS),
+  CMPBR_INSN ("cblo", 0x74400000, 0x7fe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_R2NIL, F_SF | F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cbhs", 0x74600000, 0x7fe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_R2NIL, F_SF | F_HAS_ALIAS),
+  CMPBR_INSN ("cbls", 0x74600000, 0x7fe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_R2NIL, F_SF | F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cbeq", 0x74c00000, 0x7fe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_R2NIL, F_SF),
+  CMPBR_INSN ("cbne", 0x74e00000, 0x7fe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_R2NIL, F_SF),
+  /* Compare register with immediate and branch.  */
+  CMPBR_INSN ("cbgt", 0x75000000, 0x7fe04000, compbranch, OP3 (Rt, IMM_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_HAS_ALIAS),
+  CMPBR_INSN ("cbge", 0x75000000, 0x7fe04000, compbranch, OP3 (Rt, IMMP1_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cblt", 0x75200000, 0x7fe04000, compbranch, OP3 (Rt, IMM_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_HAS_ALIAS),
+  CMPBR_INSN ("cble", 0x75200000, 0x7fe04000, compbranch, OP3 (Rt, IMMS1_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cbhi", 0x75400000, 0x7fe04000, compbranch, OP3 (Rt, IMM_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_HAS_ALIAS),
+  CMPBR_INSN ("cbhs", 0x75400000, 0x7fe04000, compbranch, OP3 (Rt, IMMP1_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cblo", 0x75600000, 0x7fe04000, compbranch, OP3 (Rt, IMM_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_HAS_ALIAS),
+  CMPBR_INSN ("cbls", 0x75600000, 0x7fe04000, compbranch, OP3 (Rt, IMMS1_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF | F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cbeq", 0x75c00000, 0x7fe04000, compbranch, OP3 (Rt, IMM_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF),
+  CMPBR_INSN ("cbne", 0x75e00000, 0x7fe04000, compbranch, OP3 (Rt, IMM_2, ADDR_PCREL9), QL_R_IMM_PCREL, F_SF),
+  /* Compare bytes and branch.  */
+  CMPBR_INSN ("cbbgt", 0x74008000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
+  CMPBR_INSN ("cbblt", 0x74008000, 0xffe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cbbge", 0x74208000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
+  CMPBR_INSN ("cbble", 0x74208000, 0xffe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cbbhi", 0x74408000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
+  CMPBR_INSN ("cbblo", 0x74408000, 0xffe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cbbhs", 0x74608000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
+  CMPBR_INSN ("cbbls", 0x74608000, 0xffe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cbbeq", 0x74c08000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, 0),
+  CMPBR_INSN ("cbbne", 0x74e08000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, 0),
+  /* Compare halfwords and branch.  */
+  CMPBR_INSN ("cbhgt", 0x7400c000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
+  CMPBR_INSN ("cbhlt", 0x7400c000, 0xffe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cbhge", 0x7420c000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
+  CMPBR_INSN ("cbhle", 0x7420c000, 0xffe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cbhhi", 0x7440c000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
+  CMPBR_INSN ("cbhlo", 0x7440c000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cbhhs", 0x7460c000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, F_HAS_ALIAS),
+  CMPBR_INSN ("cbhls", 0x7460c000, 0xffe0c000, compbranch, OP3 (Rm, Rt, ADDR_PCREL9), QL_W2_PCREL, F_ALIAS | F_PSEUDO),
+  CMPBR_INSN ("cbheq", 0x74c0c000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, 0),
+  CMPBR_INSN ("cbhne", 0x74e0c000, 0xffe0c000, compbranch, OP3 (Rt, Rm, ADDR_PCREL9), QL_W2_PCREL, 0),
   /* Conditional compare (immediate).  */
   CORE_INSN ("ccmn", 0x3a400800, 0x7fe00c10, condcmp_imm, 0, OP4 (Rn, CCMP_IMM, NZCV, COND), QL_CCMP_IMM, F_SF),
   CORE_INSN ("ccmp", 0x7a400800, 0x7fe00c10, condcmp_imm, 0, OP4 (Rn, CCMP_IMM, NZCV, COND), QL_CCMP_IMM, F_SF),
@@ -7126,6 +7188,8 @@  const struct aarch64_opcode aarch64_opcode_table[] =
       "the width of the bit-field")					\
     Y(IMMEDIATE, imm, "IMM", 0, F(FLD_imm6_10), "an immediate")         \
     Y(IMMEDIATE, imm, "IMM_2", 0, F(FLD_imm6_15), "an immediate")       \
+    Y(IMMEDIATE, imm, "IMMP1_2", 0, F(FLD_imm6_15), "an immediate plus 1")       \
+    Y(IMMEDIATE, imm, "IMMS1_2", 0, F(FLD_imm6_15), "an immediate minus 1")       \
     Y(IMMEDIATE, imm, "UIMM3_OP1", 0, F(FLD_op1),			\
       "a 3-bit unsigned immediate")					\
     Y(IMMEDIATE, imm, "UIMM3_OP2", 0, F(FLD_op2),			\
@@ -7170,6 +7234,8 @@  const struct aarch64_opcode aarch64_opcode_table[] =
       "one of the standard conditions, excluding AL and NV.")		\
     X(ADDRESS, 0, ext_imm, "ADDR_ADRP", OPD_F_SEXT, F(FLD_immhi, FLD_immlo),\
       "21-bit PC-relative address of a 4KB page")			\
+    Y(ADDRESS, imm, "ADDR_PCREL9", OPD_F_SEXT | OPD_F_SHIFT_BY_2,	\
+      F(FLD_imm9_5), "9-bit PC-relative address")			\
     Y(ADDRESS, imm, "ADDR_PCREL14", OPD_F_SEXT | OPD_F_SHIFT_BY_2,	\
       F(FLD_imm14), "14-bit PC-relative address")			\
     Y(ADDRESS, imm, "ADDR_PCREL19", OPD_F_SEXT | OPD_F_SHIFT_BY_2,	\