[RFC,5/5] RISC-V: Add Zcmt table-jump linker tests

Message ID 20260627104555.331493-6-jiawei@iscas.ac.cn
State New
Headers
Series RISC-V: Add Zcmt table-jump relaxation support |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 warning Skipped because it is an RFC
linaro-tcwg-bot/tcwg_binutils_build--master-arm warning Skipped because it is an RFC

Commit Message

Jiawei June 27, 2026, 10:45 a.m. UTC
  This patch adds zcmt table jump relaxation tests.

Cover opt-in behavior, profitability, RV32/RV64 table emission,
no-benefit and no-Zcmt cases, PIC/shared negative behavior,
section layout, symbol policy, addends, GC, overflow,
and the Zcd encoding conflict.

ld/ChangeLog:

	* testsuite/ld-riscv-elf/ld-riscv-elf.exp: Add Zcmt tests.
	* testsuite/ld-riscv-elf/table-jump-addend.d: New test.
	* testsuite/ld-riscv-elf/table-jump-addend.s: New test.
	* testsuite/ld-riscv-elf/table-jump-base-collision.d: New test.
	* testsuite/ld-riscv-elf/table-jump-base-collision.s: New test.
	* testsuite/ld-riscv-elf/table-jump-base-local-collision.d: New test.
	* testsuite/ld-riscv-elf/table-jump-base-local-collision.s: New test.
	* testsuite/ld-riscv-elf/table-jump-be32-bytes.d: New test.
	* testsuite/ld-riscv-elf/table-jump-be32-text.d: New test.
	* testsuite/ld-riscv-elf/table-jump-be64-bytes.d: New test.
	* testsuite/ld-riscv-elf/table-jump-be64-text.d: New test.
	* testsuite/ld-riscv-elf/table-jump-benefit.d: New test.
	* testsuite/ld-riscv-elf/table-jump-benefit.s: New test.
	* testsuite/ld-riscv-elf/table-jump-default-jvt-relocs.d: New test.
	* testsuite/ld-riscv-elf/table-jump-default-jvt-symbol.d: New test.
	* testsuite/ld-riscv-elf/table-jump-default-jvt.d: New test.
	* testsuite/ld-riscv-elf/table-jump-default-no-jvt.d: New test.
	* testsuite/ld-riscv-elf/table-jump-dump-unknown.d: New test.
	* testsuite/ld-riscv-elf/table-jump-emit-relocs.d: New test.
	* testsuite/ld-riscv-elf/table-jump-equal-benefit.d: New test.
	* testsuite/ld-riscv-elf/table-jump-equal-benefit.s: New test.
	* testsuite/ld-riscv-elf/table-jump-gc-comdat-symbol.d: New test.
	* testsuite/ld-riscv-elf/table-jump-gc-comdat.d: New test.
	* testsuite/ld-riscv-elf/table-jump-gc-comdat.s: New test.
	* testsuite/ld-riscv-elf/table-jump-gc-dead-symbol.d: New test.
	* testsuite/ld-riscv-elf/table-jump-gc-dead.d: New test.
	* testsuite/ld-riscv-elf/table-jump-gc-dead.s: New test.
	* testsuite/ld-riscv-elf/table-jump-jalt.d: New test.
	* testsuite/ld-riscv-elf/table-jump-jt.d: New test.
	* testsuite/ld-riscv-elf/table-jump-multiple-local-sym-2.s: New test.
	* testsuite/ld-riscv-elf/table-jump-multiple-local-sym.d: New test.
	* testsuite/ld-riscv-elf/table-jump-multiple-local-sym.s: New test.
	* testsuite/ld-riscv-elf/table-jump-no-benefit-jvt-ref.d: New test.
	* testsuite/ld-riscv-elf/table-jump-no-benefit-jvt-ref.s: New test.
	* testsuite/ld-riscv-elf/table-jump-no-benefit-section.d: New test.
	* testsuite/ld-riscv-elf/table-jump-no-benefit-symbol.d: New test.
	* testsuite/ld-riscv-elf/table-jump-no-benefit.s: New test.
	* testsuite/ld-riscv-elf/table-jump-no-relax-zcmt.d: New test.
	* testsuite/ld-riscv-elf/table-jump-no-relo-1.s: New test.
	* testsuite/ld-riscv-elf/table-jump-no-relo-2.s: New test.
	* testsuite/ld-riscv-elf/table-jump-no-relo.d: New test.
	* testsuite/ld-riscv-elf/table-jump-no-zcmt.d: New test.
	* testsuite/ld-riscv-elf/table-jump-no-zcmt.s: New test.
	* testsuite/ld-riscv-elf/table-jump-norelax.d: New test.
	* testsuite/ld-riscv-elf/table-jump-norelax.s: New test.
	* testsuite/ld-riscv-elf/table-jump-overflow.d: New test.
	* testsuite/ld-riscv-elf/table-jump-overflow.s: New test.
	* testsuite/ld-riscv-elf/table-jump-pie-relocs.d: New test.
	* testsuite/ld-riscv-elf/table-jump-pie-text.d: New test.
	* testsuite/ld-riscv-elf/table-jump-pie.d: New test.
	* testsuite/ld-riscv-elf/table-jump-pie.s: New test.
	* testsuite/ld-riscv-elf/table-jump-relax-zcmt.d: New test.
	* testsuite/ld-riscv-elf/table-jump-rv32.d: New test.
	* testsuite/ld-riscv-elf/table-jump-rv32.s: New test.
	* testsuite/ld-riscv-elf/table-jump-shared-relocs.d: New test.
	* testsuite/ld-riscv-elf/table-jump-shared-symbol.d: New test.
	* testsuite/ld-riscv-elf/table-jump-shared-text.d: New test.
	* testsuite/ld-riscv-elf/table-jump-shared.d: New test.
	* testsuite/ld-riscv-elf/table-jump-shared.s: New test.
	* testsuite/ld-riscv-elf/table-jump-strip-symbol.d: New test.
	* testsuite/ld-riscv-elf/table-jump-symbol-policy.d: New test.
	* testsuite/ld-riscv-elf/table-jump-vma-lma.d: New test.
	* testsuite/ld-riscv-elf/table-jump-vma-lma.ld: New test.
	* testsuite/ld-riscv-elf/table-jump-vma-zero.d: New test.
	* testsuite/ld-riscv-elf/table-jump-vma-zero.ld: New test.
	* testsuite/ld-riscv-elf/table-jump-vma-zero.s: New test.
	* testsuite/ld-riscv-elf/table-jump-zcd-conflict.d: New test.
	* testsuite/ld-riscv-elf/table-jump-zcmt-no-zca.d: New test.
	* testsuite/ld-riscv-elf/table-jump-zcmt-no-zca.s: New test.
	* testsuite/ld-riscv-elf/table-jump-zcmt-no-zicsr.d: New test.
	* testsuite/ld-riscv-elf/table-jump-zcmt-no-zicsr.s: New test.
	* testsuite/ld-riscv-elf/table-jump.ld: New test.
	* testsuite/ld-riscv-elf/table-jump.s: New test.
---
 ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp    | 50 ++++++++++++++++++
 ld/testsuite/ld-riscv-elf/table-jump-addend.d | 18 +++++++
 ld/testsuite/ld-riscv-elf/table-jump-addend.s | 21 ++++++++
 .../ld-riscv-elf/table-jump-base-collision.d  |  4 ++
 .../ld-riscv-elf/table-jump-base-collision.s  | 18 +++++++
 .../table-jump-base-local-collision.d         |  4 ++
 .../table-jump-base-local-collision.s         | 17 +++++++
 .../ld-riscv-elf/table-jump-be32-bytes.d      | 10 ++++
 .../ld-riscv-elf/table-jump-be32-text.d       | 13 +++++
 .../ld-riscv-elf/table-jump-be64-bytes.d      | 10 ++++
 .../ld-riscv-elf/table-jump-be64-text.d       | 13 +++++
 .../ld-riscv-elf/table-jump-benefit.d         | 17 +++++++
 .../ld-riscv-elf/table-jump-benefit.s         | 51 +++++++++++++++++++
 .../table-jump-default-jvt-relocs.d           |  9 ++++
 .../table-jump-default-jvt-symbol.d           | 10 ++++
 .../ld-riscv-elf/table-jump-default-jvt.d     | 12 +++++
 .../ld-riscv-elf/table-jump-default-no-jvt.d  | 15 ++++++
 .../ld-riscv-elf/table-jump-dump-unknown.d    | 25 +++++++++
 .../ld-riscv-elf/table-jump-emit-relocs.d     |  9 ++++
 .../ld-riscv-elf/table-jump-equal-benefit.d   | 18 +++++++
 .../ld-riscv-elf/table-jump-equal-benefit.s   | 19 +++++++
 .../table-jump-gc-comdat-symbol.d             |  9 ++++
 .../ld-riscv-elf/table-jump-gc-comdat.d       | 11 ++++
 .../ld-riscv-elf/table-jump-gc-comdat.s       | 18 +++++++
 .../ld-riscv-elf/table-jump-gc-dead-symbol.d  |  9 ++++
 .../ld-riscv-elf/table-jump-gc-dead.d         | 11 ++++
 .../ld-riscv-elf/table-jump-gc-dead.s         | 18 +++++++
 ld/testsuite/ld-riscv-elf/table-jump-jalt.d   | 18 +++++++
 ld/testsuite/ld-riscv-elf/table-jump-jt.d     | 23 +++++++++
 .../table-jump-multiple-local-sym-2.s         | 12 +++++
 .../table-jump-multiple-local-sym.d           | 16 ++++++
 .../table-jump-multiple-local-sym.s           | 18 +++++++
 .../table-jump-no-benefit-jvt-ref.d           |  4 ++
 .../table-jump-no-benefit-jvt-ref.s           | 16 ++++++
 .../table-jump-no-benefit-section.d           |  9 ++++
 .../table-jump-no-benefit-symbol.d            |  9 ++++
 .../ld-riscv-elf/table-jump-no-benefit.s      | 12 +++++
 .../ld-riscv-elf/table-jump-no-relax-zcmt.d   | 17 +++++++
 .../ld-riscv-elf/table-jump-no-relo-1.s       |  7 +++
 .../ld-riscv-elf/table-jump-no-relo-2.s       | 12 +++++
 .../ld-riscv-elf/table-jump-no-relo.d         | 16 ++++++
 .../ld-riscv-elf/table-jump-no-zcmt.d         | 16 ++++++
 .../ld-riscv-elf/table-jump-no-zcmt.s         | 16 ++++++
 .../ld-riscv-elf/table-jump-norelax.d         | 15 ++++++
 .../ld-riscv-elf/table-jump-norelax.s         | 17 +++++++
 .../ld-riscv-elf/table-jump-overflow.d        | 11 ++++
 .../ld-riscv-elf/table-jump-overflow.s        | 49 ++++++++++++++++++
 .../ld-riscv-elf/table-jump-pie-relocs.d      |  9 ++++
 .../ld-riscv-elf/table-jump-pie-text.d        |  9 ++++
 ld/testsuite/ld-riscv-elf/table-jump-pie.d    |  9 ++++
 ld/testsuite/ld-riscv-elf/table-jump-pie.s    | 13 +++++
 .../ld-riscv-elf/table-jump-relax-zcmt.d      | 23 +++++++++
 ld/testsuite/ld-riscv-elf/table-jump-rv32.d   | 15 ++++++
 ld/testsuite/ld-riscv-elf/table-jump-rv32.s   | 25 +++++++++
 .../ld-riscv-elf/table-jump-shared-relocs.d   |  9 ++++
 .../ld-riscv-elf/table-jump-shared-symbol.d   |  9 ++++
 .../ld-riscv-elf/table-jump-shared-text.d     |  9 ++++
 ld/testsuite/ld-riscv-elf/table-jump-shared.d | 12 +++++
 ld/testsuite/ld-riscv-elf/table-jump-shared.s | 15 ++++++
 .../ld-riscv-elf/table-jump-strip-symbol.d    |  9 ++++
 .../ld-riscv-elf/table-jump-symbol-policy.d   |  8 +++
 .../ld-riscv-elf/table-jump-vma-lma.d         | 16 ++++++
 .../ld-riscv-elf/table-jump-vma-lma.ld        | 10 ++++
 .../ld-riscv-elf/table-jump-vma-zero.d        | 15 ++++++
 .../ld-riscv-elf/table-jump-vma-zero.ld       | 13 +++++
 .../ld-riscv-elf/table-jump-vma-zero.s        | 14 +++++
 .../ld-riscv-elf/table-jump-zcd-conflict.d    |  9 ++++
 .../ld-riscv-elf/table-jump-zcmt-no-zca.d     |  9 ++++
 .../ld-riscv-elf/table-jump-zcmt-no-zca.s     | 24 +++++++++
 .../ld-riscv-elf/table-jump-zcmt-no-zicsr.d   |  9 ++++
 .../ld-riscv-elf/table-jump-zcmt-no-zicsr.s   | 24 +++++++++
 ld/testsuite/ld-riscv-elf/table-jump.ld       | 16 ++++++
 ld/testsuite/ld-riscv-elf/table-jump.s        | 46 +++++++++++++++++
 73 files changed, 1131 insertions(+)
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-addend.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-addend.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-base-collision.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-base-collision.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-base-local-collision.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-base-local-collision.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-be32-bytes.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-be32-text.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-be64-bytes.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-be64-text.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-benefit.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-benefit.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-default-jvt-relocs.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-default-jvt-symbol.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-default-jvt.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-default-no-jvt.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-dump-unknown.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-emit-relocs.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-equal-benefit.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-equal-benefit.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-gc-comdat-symbol.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-gc-comdat.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-gc-comdat.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-gc-dead-symbol.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-gc-dead.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-gc-dead.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-jalt.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-jt.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-multiple-local-sym-2.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-multiple-local-sym.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-multiple-local-sym.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-no-benefit-jvt-ref.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-no-benefit-jvt-ref.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-no-benefit-section.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-no-benefit-symbol.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-no-benefit.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-no-relax-zcmt.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-no-relo-1.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-no-relo-2.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-no-relo.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-no-zcmt.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-no-zcmt.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-norelax.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-norelax.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-overflow.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-overflow.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-pie-relocs.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-pie-text.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-pie.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-pie.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-relax-zcmt.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-rv32.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-rv32.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-shared-relocs.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-shared-symbol.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-shared-text.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-shared.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-shared.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-strip-symbol.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-symbol-policy.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-vma-lma.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-vma-lma.ld
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-vma-zero.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-vma-zero.ld
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-vma-zero.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-zcd-conflict.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zca.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zca.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zicsr.d
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zicsr.s
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump.ld
 create mode 100644 ld/testsuite/ld-riscv-elf/table-jump.s
  

Patch

diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
index 8e26ccff10a..32f953aba78 100644
--- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
+++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
@@ -182,11 +182,60 @@  if [istarget "riscv*-*-*"] {
 	    "-march=rv64i -mabi=lp64" {weakref64.s} \
 	    {{objdump -d weakref64.d}} "weakref64"]]
 
+    # Zcmt (jump table) tests.
+    run_dump_test "table-jump-jalt"
+    run_dump_test "table-jump-jt"
+    run_dump_test "table-jump-dump-unknown"
+    run_dump_test "table-jump-no-relo"
+    run_dump_test "table-jump-relax-zcmt"
+    run_dump_test "table-jump-no-relax-zcmt"
+    run_dump_test "table-jump-multiple-local-sym"
+    run_dump_test "table-jump-benefit"
+    run_dump_test "table-jump-addend"
+    run_dump_test "table-jump-norelax"
+    run_dump_test "table-jump-no-zcmt"
+    run_dump_test "table-jump-zcmt-no-zca"
+    run_dump_test "table-jump-zcmt-no-zicsr"
+    run_dump_test "table-jump-zcd-conflict"
+    run_dump_test "table-jump-overflow"
+    run_dump_test "table-jump-equal-benefit"
+    run_dump_test "table-jump-rv32"
+    run_dump_test "table-jump-vma-zero"
+    run_dump_test "table-jump-emit-relocs"
+    run_dump_test "table-jump-be64-bytes"
+    run_dump_test "table-jump-be64-text"
+    run_dump_test "table-jump-be32-bytes"
+    run_dump_test "table-jump-be32-text"
+    run_dump_test "table-jump-no-benefit-symbol"
+    run_dump_test "table-jump-no-benefit-section"
+    run_dump_test "table-jump-no-benefit-jvt-ref"
+    run_dump_test "table-jump-default-no-jvt"
+    run_dump_test "table-jump-default-jvt"
+    run_dump_test "table-jump-default-jvt-symbol"
+    run_dump_test "table-jump-default-jvt-relocs"
+    run_dump_test "table-jump-base-collision"
+    run_dump_test "table-jump-base-local-collision"
+    run_dump_test "table-jump-symbol-policy"
+    run_dump_test "table-jump-strip-symbol"
+    run_dump_test "table-jump-gc-dead"
+    run_dump_test "table-jump-gc-dead-symbol"
+    run_dump_test "table-jump-gc-comdat"
+    run_dump_test "table-jump-gc-comdat-symbol"
+    run_dump_test "table-jump-vma-lma"
+
     # The following tests require shared library support.
     if ![check_shared_lib_support] {
 	return
     }
 
+    run_dump_test "table-jump-pie"
+    run_dump_test "table-jump-pie-text"
+    run_dump_test "table-jump-pie-relocs"
+    run_dump_test "table-jump-shared"
+    run_dump_test "table-jump-shared-text"
+    run_dump_test "table-jump-shared-symbol"
+    run_dump_test "table-jump-shared-relocs"
+
     run_dump_test "relro-relax-lui"
     run_dump_test "relro-relax-pcrel"
     run_dump_test "variant_cc-now"
@@ -342,4 +391,5 @@  if [istarget "riscv*-*-*"] {
     run_dump_test "tls"
     run_dump_test "tlsbin"
     run_dump_test "absolute-no-relative"
+
 }
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-addend.d b/ld/testsuite/ld-riscv-elf/table-jump-addend.d
new file mode 100644
index 00000000000..11cc2b9fdfe
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-addend.d
@@ -0,0 +1,18 @@ 
+#source: table-jump-addend.s
+#as: -march=rv64ic_zcmt
+#ld: -Ttable-jump.ld --relax-zcmt
+#objdump: -d
+#...
+Disassembly of section .riscv.jvt:
+0+[0-9a-f]+ <__jvt_base\$>:
+[ 	]+...
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jalt\[32\]:[ 	]+target
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jalt\[33\]:[ 	]+target_4
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jalt\[34\]:[ 	]+target_8
+#...
+Disassembly of section .text:
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+32 # [0-9a-f]+ <target>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+33 # [0-9a-f]+ <target_4>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+34 # [0-9a-f]+ <target_8>
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-addend.s b/ld/testsuite/ld-riscv-elf/table-jump-addend.s
new file mode 100644
index 00000000000..823e0710e08
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-addend.s
@@ -0,0 +1,21 @@ 
+	.section .text
+
+	.globl _start
+	.type _start, %function
+_start:
+	.rept 50
+	call target
+	call target + 4
+	call target + 8
+	.endr
+
+	.globl target
+	.type target, %function
+target:
+	.word 0x00000013
+	.globl target_4
+target_4:
+	.word 0x00000013
+	.globl target_8
+target_8:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-base-collision.d b/ld/testsuite/ld-riscv-elf/table-jump-base-collision.d
new file mode 100644
index 00000000000..5cdfe8a8e77
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-base-collision.d
@@ -0,0 +1,4 @@ 
+#source: table-jump-base-collision.s
+#as: -march=rv64ic_zcmt
+#ld: -Ttable-jump.ld --relax-zcmt
+#error: .*__jvt_base\$ is reserved for Zcmt table-jump relaxation
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-base-collision.s b/ld/testsuite/ld-riscv-elf/table-jump-base-collision.s
new file mode 100644
index 00000000000..182150f86a5
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-base-collision.s
@@ -0,0 +1,18 @@ 
+	.section .text
+
+	.globl _start
+	.type _start, %function
+_start:
+	.rept 5
+	tail target
+	.endr
+	ret
+	.space 4096
+
+	.globl __jvt_base$
+__jvt_base$:
+	.word 0
+
+	.type target, %function
+target:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-base-local-collision.d b/ld/testsuite/ld-riscv-elf/table-jump-base-local-collision.d
new file mode 100644
index 00000000000..2708f2e86d1
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-base-local-collision.d
@@ -0,0 +1,4 @@ 
+#source: table-jump-base-local-collision.s
+#as: -march=rv64ic_zcmt
+#ld: -Ttable-jump.ld --relax-zcmt
+#error: .*__jvt_base\$ is reserved for Zcmt table-jump relaxation
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-base-local-collision.s b/ld/testsuite/ld-riscv-elf/table-jump-base-local-collision.s
new file mode 100644
index 00000000000..f7206d15b49
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-base-local-collision.s
@@ -0,0 +1,17 @@ 
+	.section .text
+
+	.globl _start
+	.type _start, %function
+_start:
+	.rept 5
+	tail target
+	.endr
+	ret
+	.space 4096
+
+__jvt_base$:
+	.word 0
+
+	.type target, %function
+target:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-be32-bytes.d b/ld/testsuite/ld-riscv-elf/table-jump-be32-bytes.d
new file mode 100644
index 00000000000..49dc3451e16
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-be32-bytes.d
@@ -0,0 +1,10 @@ 
+#source: table-jump-rv32.s
+#as: -march=rv32ic_zcmt -mbig-endian
+#ld: -melf32briscv -Ttable-jump.ld --relax-zcmt
+#objdump: -s -j .riscv.jvt
+
+#...
+.*:     file format elf32-bigriscv
+#...
+Contents of section .riscv.jvt:
+[ 	]+0+[0-9a-f]*[ 	]+0000400e[ 	]+0000400c.*
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-be32-text.d b/ld/testsuite/ld-riscv-elf/table-jump-be32-text.d
new file mode 100644
index 00000000000..a7a00bf3e39
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-be32-text.d
@@ -0,0 +1,13 @@ 
+#source: table-jump-rv32.s
+#as: -march=rv32ic_zcmt -mbig-endian
+#ld: -melf32briscv -Ttable-jump.ld --relax-zcmt
+#objdump: -d
+
+#...
+.*:     file format elf32-bigriscv
+#...
+Disassembly of section .text:
+#...
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # [0-9a-f]+ <foo_local>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+1 # [0-9a-f]+ <foo_global>
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-be64-bytes.d b/ld/testsuite/ld-riscv-elf/table-jump-be64-bytes.d
new file mode 100644
index 00000000000..7dbc2d1a30e
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-be64-bytes.d
@@ -0,0 +1,10 @@ 
+#source: table-jump.s
+#as: -march=rv64ic_zcmt --defsym TEST_CM_JT=1 -mbig-endian
+#ld: -melf64briscv -Ttable-jump.ld --relax-zcmt
+#objdump: -s -j .riscv.jvt
+
+#...
+.*:     file format elf64-bigriscv
+#...
+Contents of section .riscv.jvt:
+[ 	]+0+[0-9a-f]*[ 	]+00000000[ 	]+00004016[ 	]+00000000[ 	]+00004014.*
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-be64-text.d b/ld/testsuite/ld-riscv-elf/table-jump-be64-text.d
new file mode 100644
index 00000000000..7477201f199
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-be64-text.d
@@ -0,0 +1,13 @@ 
+#source: table-jump.s
+#as: -march=rv64ic_zcmt --defsym TEST_CM_JT=1 -mbig-endian
+#ld: -melf64briscv -Ttable-jump.ld --relax-zcmt
+#objdump: -d
+
+#...
+.*:     file format elf64-bigriscv
+#...
+Disassembly of section .text:
+#...
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # [0-9a-f]+ <foo_local>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+1 # [0-9a-f]+ <foo_global>
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-benefit.d b/ld/testsuite/ld-riscv-elf/table-jump-benefit.d
new file mode 100644
index 00000000000..2fe5c787605
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-benefit.d
@@ -0,0 +1,17 @@ 
+#source: table-jump-benefit.s
+#as: -march=rv64ic_zcmt
+#ld: -Ttable-jump.ld --relax-zcmt
+#objdump: -d
+#...
+Disassembly of section .riscv.jvt:
+0+[0-9a-f]+ <__jvt_base\$>:
+[ 	]+...
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jalt\[32\]:[ 	]+func_d
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jalt\[33\]:[ 	]+func_c
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jalt\[34\]:[ 	]+func_b
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jalt\[35\]:[ 	]+func_a
+#...
+Disassembly of section .text:
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+32 # [0-9a-f]+ <func_d>
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-benefit.s b/ld/testsuite/ld-riscv-elf/table-jump-benefit.s
new file mode 100644
index 00000000000..0450fef000f
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-benefit.s
@@ -0,0 +1,51 @@ 
+	.section .text
+
+	.globl _start
+	.type	_start, %function
+_start:
+	# Test benefit ranking: higher frequency functions should
+	# be prioritized for JVT entry allocation.
+	# Use reverse alphabetical order to verify sorting by benefit,
+	# not by name.
+
+	# func_d called 80 times - highest benefit
+	.rept 80
+	call func_d
+	.endr
+
+	# func_c called 50 times - medium benefit
+	.rept 50
+	call func_c
+	.endr
+
+	# func_b called 20 times - lower benefit
+	.rept 20
+	call func_b
+	.endr
+
+	# func_a called 5 times - lowest benefit
+	.rept 5
+	call func_a
+	.endr
+
+	ret
+
+	.globl func_a
+	.type	func_a, %function
+func_a:
+	ret
+
+	.globl func_b
+	.type	func_b, %function
+func_b:
+	ret
+
+	.globl func_c
+	.type	func_c, %function
+func_c:
+	ret
+
+	.globl func_d
+	.type	func_d, %function
+func_d:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-default-jvt-relocs.d b/ld/testsuite/ld-riscv-elf/table-jump-default-jvt-relocs.d
new file mode 100644
index 00000000000..9b3079f24b3
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-default-jvt-relocs.d
@@ -0,0 +1,9 @@ 
+#source: table-jump.s
+#as: -march=rv64ic_zcmt --defsym TEST_CM_JT_JALT=1
+#ld: --relax-zcmt
+#readelf: -rW
+
+#failif
+#...
+.*R_RISCV_TABLE_JUMP.*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-default-jvt-symbol.d b/ld/testsuite/ld-riscv-elf/table-jump-default-jvt-symbol.d
new file mode 100644
index 00000000000..5dd5e43c680
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-default-jvt-symbol.d
@@ -0,0 +1,10 @@ 
+#source: table-jump.s
+#as: -march=rv64ic_zcmt --defsym TEST_CM_JT_JALT=1
+#ld: --relax-zcmt
+#readelf: -SWs
+
+#...
+.*\.riscv\.jvt[ 	]+PROGBITS[ 	]+[0-9a-f]+[ 	]+[0-9a-f]+[ 	]+[0-9a-f]+.*
+#...
+[ 	]+[0-9]+:[ 	]+0+[0-9a-f]+[ 	]+0[ 	]+NOTYPE[ 	]+LOCAL[ 	]+DEFAULT[ 	]+[0-9]+[ 	]+__jvt_base\$
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-default-jvt.d b/ld/testsuite/ld-riscv-elf/table-jump-default-jvt.d
new file mode 100644
index 00000000000..96c9efaa29d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-default-jvt.d
@@ -0,0 +1,12 @@ 
+#source: table-jump.s
+#as: -march=rv64ic_zcmt --defsym TEST_CM_JT_JALT=1
+#ld: --relax-zcmt
+#objdump: -d
+
+#...
+Disassembly of section .text:
+#...
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # [0-9a-f]+ <foo_global>
+#...
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+32 # [0-9a-f]+ <foo_global>
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-default-no-jvt.d b/ld/testsuite/ld-riscv-elf/table-jump-default-no-jvt.d
new file mode 100644
index 00000000000..1ebad80ec12
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-default-no-jvt.d
@@ -0,0 +1,15 @@ 
+#source: table-jump-no-benefit.s
+#as: -march=rv64ic_zcmt
+#ld: --relax-zcmt
+#objdump: -d
+
+#...
+Disassembly of section .text:
+#...
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+j[ 	]+[0-9a-f]+ <target>
+.*:[ 	]+[0-9a-f]+[ 	]+j[ 	]+[0-9a-f]+ <target>
+#...
+0+[0-9a-f]+ <target>:
+.*:[ 	]+[0-9a-f]+[ 	]+ret
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-dump-unknown.d b/ld/testsuite/ld-riscv-elf/table-jump-dump-unknown.d
new file mode 100644
index 00000000000..53e9097603c
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-dump-unknown.d
@@ -0,0 +1,25 @@ 
+#source: table-jump.s
+#as: -march=rv64ic_zcmt --defsym TEST_CM_JT=1
+#ld: -Ttable-jump.ld --strip-all --relax-zcmt
+#objdump: -d
+#...
+Disassembly of section .riscv.jvt:
+0+[0-9a-f]+ <.riscv.jvt>:
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jt\[0\]:[ 	]+<unknown>
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jt\[1\]:[ 	]+<unknown>
+#...
+Disassembly of section .text:
+0+[0-9a-f]+ <.text>:
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+1
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+1
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+1
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+1
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+1
+#...
+.*:[ 	]+[0-9a-f]+[ 	]+ret
+.*:[ 	]+[0-9a-f]+[ 	]+ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-emit-relocs.d b/ld/testsuite/ld-riscv-elf/table-jump-emit-relocs.d
new file mode 100644
index 00000000000..32c69c727ae
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-emit-relocs.d
@@ -0,0 +1,9 @@ 
+#source: table-jump.s
+#as: -march=rv64ic_zcmt --defsym TEST_CM_JALT=1
+#ld: -Ttable-jump.ld --emit-relocs --relax-zcmt
+#readelf: -rW
+
+#failif
+#...
+.*R_RISCV_(TABLE_JUMP|DELETE|DELETE_AND_RELAX).*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-equal-benefit.d b/ld/testsuite/ld-riscv-elf/table-jump-equal-benefit.d
new file mode 100644
index 00000000000..6f4ed2e2682
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-equal-benefit.d
@@ -0,0 +1,18 @@ 
+#source: table-jump-equal-benefit.s
+#as: -march=rv64ic_zcmt
+#ld: -Ttable-jump.ld --relax-zcmt
+#objdump: -d
+
+#...
+Disassembly of section .riscv.jvt:
+0+[0-9a-f]+ <__jvt_base\$>:
+[ 	]+...
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jalt\[32\]:[ 	]+func_b
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jalt\[33\]:[ 	]+func_a
+#...
+Disassembly of section .text:
+#...
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+32 # [0-9a-f]+ <func_b>
+#...
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+33 # [0-9a-f]+ <func_a>
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-equal-benefit.s b/ld/testsuite/ld-riscv-elf/table-jump-equal-benefit.s
new file mode 100644
index 00000000000..13dc4200569
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-equal-benefit.s
@@ -0,0 +1,19 @@ 
+	.section .text
+
+	.globl _start
+	.type _start, %function
+_start:
+	.rept 70
+	call func_b
+	call func_a
+	.endr
+
+	.globl func_a
+	.type func_a, %function
+func_a:
+	ret
+
+	.globl func_b
+	.type func_b, %function
+func_b:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-gc-comdat-symbol.d b/ld/testsuite/ld-riscv-elf/table-jump-gc-comdat-symbol.d
new file mode 100644
index 00000000000..0b8ebee90bc
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-gc-comdat-symbol.d
@@ -0,0 +1,9 @@ 
+#source: table-jump-gc-comdat.s
+#as: -march=rv64ic_zcmt
+#ld: --gc-sections --relax-zcmt -e _start
+#readelf: -SWs
+
+#failif
+#...
+.*(__jvt_base\$|\.riscv\.jvt).*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-gc-comdat.d b/ld/testsuite/ld-riscv-elf/table-jump-gc-comdat.d
new file mode 100644
index 00000000000..6a5e01c8ae9
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-gc-comdat.d
@@ -0,0 +1,11 @@ 
+#source: table-jump-gc-comdat.s
+#as: -march=rv64ic_zcmt
+#ld: --gc-sections --relax-zcmt -e _start
+#objdump: -d
+
+#...
+Disassembly of section .text:
+#...
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+ret
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-gc-comdat.s b/ld/testsuite/ld-riscv-elf/table-jump-gc-comdat.s
new file mode 100644
index 00000000000..591d7c47e29
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-gc-comdat.s
@@ -0,0 +1,18 @@ 
+	.section .text.live, "ax"
+
+	.globl _start
+	.type _start, %function
+_start:
+	ret
+
+	.section .text.dead_group, "axG", @progbits, dead_group, comdat
+	.type dead_group_start, %function
+dead_group_start:
+	.rept 70
+	call dead_group_target
+	.endr
+	ret
+
+	.type dead_group_target, %function
+dead_group_target:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-gc-dead-symbol.d b/ld/testsuite/ld-riscv-elf/table-jump-gc-dead-symbol.d
new file mode 100644
index 00000000000..b8a14241f12
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-gc-dead-symbol.d
@@ -0,0 +1,9 @@ 
+#source: table-jump-gc-dead.s
+#as: -march=rv64ic_zcmt
+#ld: --gc-sections --relax-zcmt -e _start
+#readelf: -SWs
+
+#failif
+#...
+.*(__jvt_base\$|\.riscv\.jvt).*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-gc-dead.d b/ld/testsuite/ld-riscv-elf/table-jump-gc-dead.d
new file mode 100644
index 00000000000..1c95f06c7e0
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-gc-dead.d
@@ -0,0 +1,11 @@ 
+#source: table-jump-gc-dead.s
+#as: -march=rv64ic_zcmt
+#ld: --gc-sections --relax-zcmt -e _start
+#objdump: -d
+
+#...
+Disassembly of section .text:
+#...
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+ret
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-gc-dead.s b/ld/testsuite/ld-riscv-elf/table-jump-gc-dead.s
new file mode 100644
index 00000000000..0030a207897
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-gc-dead.s
@@ -0,0 +1,18 @@ 
+	.section .text.live, "ax"
+
+	.globl _start
+	.type _start, %function
+_start:
+	ret
+
+	.section .text.dead, "ax"
+	.type dead_start, %function
+dead_start:
+	.rept 70
+	call dead_target
+	.endr
+	ret
+
+	.type dead_target, %function
+dead_target:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-jalt.d b/ld/testsuite/ld-riscv-elf/table-jump-jalt.d
new file mode 100644
index 00000000000..098e65fc403
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-jalt.d
@@ -0,0 +1,18 @@ 
+#source: table-jump.s
+#as: -march=rv64ic_zcmt --defsym TEST_CM_JALT=1
+#ld: -Ttable-jump.ld --relax-zcmt
+#objdump: -d
+#...
+Disassembly of section .riscv.jvt:
+0+[0-9a-f]+ <__jvt_base\$>:
+[ 	]+...
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jalt\[32\]:[ 	]+foo_global
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jalt\[33\]:[ 	]+foo_local
+#...
+Disassembly of section .text:
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+32 # [0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+33 # [0-9a-f]+ <foo_local>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+32 # [0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+33 # [0-9a-f]+ <foo_local>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-jt.d b/ld/testsuite/ld-riscv-elf/table-jump-jt.d
new file mode 100644
index 00000000000..0b0250ee104
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-jt.d
@@ -0,0 +1,23 @@ 
+#source: table-jump.s
+#as: -march=rv64ic_zcmt --defsym TEST_CM_JT=1
+#ld: -Ttable-jump.ld --relax-zcmt
+#objdump: -d
+#...
+Disassembly of section .riscv.jvt:
+0+[0-9a-f]+ <__jvt_base\$>:
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jt\[0\]:[ 	]+foo_local
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jt\[1\]:[ 	]+foo_global
+#...
+Disassembly of section .text:
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # [0-9a-f]+ <foo_local>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+1 # [0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # [0-9a-f]+ <foo_local>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+1 # [0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # [0-9a-f]+ <foo_local>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+1 # [0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # [0-9a-f]+ <foo_local>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+1 # [0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # [0-9a-f]+ <foo_local>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+1 # [0-9a-f]+ <foo_global>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-multiple-local-sym-2.s b/ld/testsuite/ld-riscv-elf/table-jump-multiple-local-sym-2.s
new file mode 100644
index 00000000000..bf106b88f2d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-multiple-local-sym-2.s
@@ -0,0 +1,12 @@ 
+    .section .text
+    .type   bar, %function
+bar:
+    .rept 70
+    call foo_local
+    call foo_global
+    .endr
+    ret
+
+    .type   foo_local, %function
+foo_local:
+    ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-multiple-local-sym.d b/ld/testsuite/ld-riscv-elf/table-jump-multiple-local-sym.d
new file mode 100644
index 00000000000..07dbd3978be
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-multiple-local-sym.d
@@ -0,0 +1,16 @@ 
+#source: table-jump-multiple-local-sym.s
+#source: table-jump-multiple-local-sym-2.s
+#as: -march=rv64ic_zcmt
+#ld: -Ttable-jump.ld --relax-zcmt
+#objdump: -d
+
+#...
+Disassembly of section .riscv.jvt:
+0+[0-9a-f]+ <__jvt_base\$>:
+[ 	]+...
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jalt\[32\]:[ 	]+foo_global
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jalt\[33\]:[ 	]+foo_local
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jalt\[34\]:[ 	]+foo_local
+
+Disassembly of section .text:
+#...
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-multiple-local-sym.s b/ld/testsuite/ld-riscv-elf/table-jump-multiple-local-sym.s
new file mode 100644
index 00000000000..ccc2525e2f7
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-multiple-local-sym.s
@@ -0,0 +1,18 @@ 
+    .section .text
+
+    .globl _start
+    .type   _start, %function
+_start:
+    .rept 70
+    call foo_global
+    call foo_local
+    .endr
+
+    .globl  foo_global
+    .type   foo_global, %function
+foo_global:
+    ret
+
+    .type   foo_local, %function
+foo_local:
+    ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-no-benefit-jvt-ref.d b/ld/testsuite/ld-riscv-elf/table-jump-no-benefit-jvt-ref.d
new file mode 100644
index 00000000000..3b392efa45c
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-no-benefit-jvt-ref.d
@@ -0,0 +1,4 @@ 
+#source: table-jump-no-benefit-jvt-ref.s
+#as: -march=rv64ic_zcmt
+#ld: --relax-zcmt
+#error: .*undefined reference to `__jvt_base\$'
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-no-benefit-jvt-ref.s b/ld/testsuite/ld-riscv-elf/table-jump-no-benefit-jvt-ref.s
new file mode 100644
index 00000000000..3665215f92d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-no-benefit-jvt-ref.s
@@ -0,0 +1,16 @@ 
+	.section .text
+
+	.globl _start
+	.type _start, %function
+_start:
+	.option push
+	.option norelax
+	la a0, __jvt_base$
+	.option pop
+	tail target
+	tail target
+	.space 4096
+
+	.type target, %function
+target:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-no-benefit-section.d b/ld/testsuite/ld-riscv-elf/table-jump-no-benefit-section.d
new file mode 100644
index 00000000000..74363ad11c6
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-no-benefit-section.d
@@ -0,0 +1,9 @@ 
+#source: table-jump-no-benefit.s
+#as: -march=rv64ic_zcmt
+#ld: --relax-zcmt
+#readelf: -SW
+
+#failif
+#...
+.*\.riscv\.jvt.*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-no-benefit-symbol.d b/ld/testsuite/ld-riscv-elf/table-jump-no-benefit-symbol.d
new file mode 100644
index 00000000000..6d5eb9a35b0
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-no-benefit-symbol.d
@@ -0,0 +1,9 @@ 
+#source: table-jump-no-benefit.s
+#as: -march=rv64ic_zcmt
+#ld: --relax-zcmt
+#readelf: -sW
+
+#failif
+#...
+.*__jvt_base\$.*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-no-benefit.s b/ld/testsuite/ld-riscv-elf/table-jump-no-benefit.s
new file mode 100644
index 00000000000..2292cc43b3e
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-no-benefit.s
@@ -0,0 +1,12 @@ 
+	.section .text
+
+	.globl _start
+	.type _start, %function
+_start:
+	tail target
+	tail target
+	.space 4096
+
+	.type target, %function
+target:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-no-relax-zcmt.d b/ld/testsuite/ld-riscv-elf/table-jump-no-relax-zcmt.d
new file mode 100644
index 00000000000..a551a05c97e
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-no-relax-zcmt.d
@@ -0,0 +1,17 @@ 
+#source: table-jump.s
+#as: -march=rv64ic_zcmt --defsym TEST_CM_JT_JALT=1
+#ld: -Ttable-jump.ld --no-relax-zcmt
+#objdump: -d
+#...
+Disassembly of section .text:
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+j[ 	]+[0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+j[ 	]+[0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+j[ 	]+[0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+j[ 	]+[0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+j[ 	]+[0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+[0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+[0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+[0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+[0-9a-f]+ <foo_global>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-no-relo-1.s b/ld/testsuite/ld-riscv-elf/table-jump-no-relo-1.s
new file mode 100644
index 00000000000..d3e9b7ce4dd
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-no-relo-1.s
@@ -0,0 +1,7 @@ 
+    .section .text
+
+    .globl _start
+    .type	_start, %function
+_start:
+L0: j L0
+    ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-no-relo-2.s b/ld/testsuite/ld-riscv-elf/table-jump-no-relo-2.s
new file mode 100644
index 00000000000..f68cdaf423b
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-no-relo-2.s
@@ -0,0 +1,12 @@ 
+    .section .text
+
+    .globl foo
+    .type  foo, %function
+foo:
+    call bar
+
+
+    .globl bar
+    .type  bar, %function
+bar:
+    ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-no-relo.d b/ld/testsuite/ld-riscv-elf/table-jump-no-relo.d
new file mode 100644
index 00000000000..9ff45cb2616
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-no-relo.d
@@ -0,0 +1,16 @@ 
+#source: table-jump-no-relo-1.s
+#source: table-jump-no-relo-2.s
+#as: -march=rv64ic_zcmt
+#ld: -Ttable-jump.ld --relax-zcmt
+#objdump: -d
+#...
+Disassembly of section .text:
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+j[ 	]+[0-9a-f]+ <_start>
+.*:[ 	]+[0-9a-f]+[ 	]+ret
+
+0+[0-9a-f]+ <foo>:
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+[0-9a-f]+ <bar>
+
+0+[0-9a-f]+ <bar>:
+.*:[ 	]+[0-9a-f]+[ 	]+ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-no-zcmt.d b/ld/testsuite/ld-riscv-elf/table-jump-no-zcmt.d
new file mode 100644
index 00000000000..5b23e81e2c8
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-no-zcmt.d
@@ -0,0 +1,16 @@ 
+#source: table-jump-no-zcmt.s
+#as: -march=rv64ic
+#ld: -Ttable-jump.ld --relax-zcmt
+#objdump: -d
+#...
+Disassembly of section .text:
+0+[0-9a-f]+ <_start>:
+# All calls should remain as jal (not converted to cm.jalt)
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+[0-9a-f]+ <foo>
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+[0-9a-f]+ <foo>
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+[0-9a-f]+ <foo>
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+[0-9a-f]+ <foo>
+#...
+0+[0-9a-f]+ <foo>:
+.*:[ 	]+[0-9a-f]+[ 	]+ret
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-no-zcmt.s b/ld/testsuite/ld-riscv-elf/table-jump-no-zcmt.s
new file mode 100644
index 00000000000..3e483757ba5
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-no-zcmt.s
@@ -0,0 +1,16 @@ 
+	.section .text
+
+	.globl _start
+	.type	_start, %function
+_start:
+	# This file is assembled without Zcmt extension.
+	# The linker should NOT generate JVT section
+	.rept 50
+	call foo
+	.endr
+	ret
+
+	.globl foo
+	.type	foo, %function
+foo:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-norelax.d b/ld/testsuite/ld-riscv-elf/table-jump-norelax.d
new file mode 100644
index 00000000000..8621ad8550b
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-norelax.d
@@ -0,0 +1,15 @@ 
+#source: table-jump-norelax.s
+#as: -march=rv64ic_zcmt
+#ld: -Ttable-jump.ld --relax-zcmt
+#objdump: -d
+#...
+Disassembly of section .text:
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+ra,0x1
+.*:[ 	]+[0-9a-f]+[ 	]+jalr[ 	]+[0-9]+\(ra\) # [0-9a-f]+ <target>
+.*:[ 	]+[0-9a-f]+[ 	]+auipc[ 	]+t[0-9],0x1
+.*:[ 	]+[0-9a-f]+[ 	]+jr[ 	]+[0-9]+\(t[0-9]\) # [0-9a-f]+ <target>
+.*:[ 	]+[0-9a-f]+[ 	]+j[ 	]+[0-9a-f]+ <target>
+#...
+0+[0-9a-f]+ <target>:
+.*:[ 	]+[0-9a-f]+[ 	]+ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-norelax.s b/ld/testsuite/ld-riscv-elf/table-jump-norelax.s
new file mode 100644
index 00000000000..0880f79a5c5
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-norelax.s
@@ -0,0 +1,17 @@ 
+	.section .text
+
+	.globl _start
+	.type _start, %function
+_start:
+	.option push
+	.option norelax
+	call target
+	tail target
+	jal x0, target
+	.option pop
+
+	.space 4096
+
+	.type target, %function
+target:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-overflow.d b/ld/testsuite/ld-riscv-elf/table-jump-overflow.d
new file mode 100644
index 00000000000..4b5e36005a9
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-overflow.d
@@ -0,0 +1,11 @@ 
+#source: table-jump-overflow.s
+#as: -march=rv64ic_zcmt
+#ld: -Ttable-jump.ld --relax-zcmt
+#objdump: -d
+#...
+Disassembly of section .text:
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+[0-9a-f]+ <func_top>
+#...
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+[0-9a-f]+ <func_overflow>
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-overflow.s b/ld/testsuite/ld-riscv-elf/table-jump-overflow.s
new file mode 100644
index 00000000000..5aae80f24c6
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-overflow.s
@@ -0,0 +1,49 @@ 
+	.altmacro
+	.section .text
+
+	# Test JVT overflow: cm.jalt has 224 entries (index 32-255)
+	# func_top: called 10x (highest benefit), guaranteed jvt.jalt[32]
+	# func_0 to func_222: called 2x each, fill remaining 223 JVT slots
+	# func_overflow: called 1x (lowest benefit), overflows, remains jal
+
+	.macro call2 n
+	call func_\n
+	call func_\n
+	.endm
+
+	.macro defn n
+	.type func_\n, %function
+func_\n:	ret
+	.endm
+
+	.globl _start
+	.type	_start, %function
+_start:
+	# func_top: highest benefit, guaranteed first JVT entry
+	.rept 10
+	call func_top
+	.endr
+
+	# 223 functions with 2 calls each (fill remaining JVT entries)
+	.set i, 0
+	.rept 223
+	call2 %i
+	.set i, i+1
+	.endr
+
+	# func_overflow: lowest benefit, will not fit in JVT
+	call func_overflow
+	ret
+
+	# Define functions
+	.type func_top, %function
+func_top:	ret
+
+	.set i, 0
+	.rept 223
+	defn %i
+	.set i, i+1
+	.endr
+
+	.type func_overflow, %function
+func_overflow:	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-pie-relocs.d b/ld/testsuite/ld-riscv-elf/table-jump-pie-relocs.d
new file mode 100644
index 00000000000..407acf5fcc6
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-pie-relocs.d
@@ -0,0 +1,9 @@ 
+#source: table-jump-pie.s
+#as: -march=rv64ic_zcmt -fpic
+#ld: -pie --relax-zcmt
+#readelf: -rW
+
+#failif
+#...
+.*R_RISCV_(TABLE_JUMP|DELETE|DELETE_AND_RELAX).*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-pie-text.d b/ld/testsuite/ld-riscv-elf/table-jump-pie-text.d
new file mode 100644
index 00000000000..8cb52d4a5df
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-pie-text.d
@@ -0,0 +1,9 @@ 
+#source: table-jump-pie.s
+#as: -march=rv64ic_zcmt -fpic
+#ld: -pie --relax-zcmt
+#objdump: -d
+
+#failif
+#...
+.*cm\.j(al)?t.*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-pie.d b/ld/testsuite/ld-riscv-elf/table-jump-pie.d
new file mode 100644
index 00000000000..d8a8a6a532d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-pie.d
@@ -0,0 +1,9 @@ 
+#source: table-jump-pie.s
+#as: -march=rv64ic_zcmt -fpic
+#ld: -pie --relax-zcmt
+#readelf: -SWs
+
+#failif
+#...
+.*(__jvt_base\$|\.riscv\.jvt).*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-pie.s b/ld/testsuite/ld-riscv-elf/table-jump-pie.s
new file mode 100644
index 00000000000..3d5fe7759bd
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-pie.s
@@ -0,0 +1,13 @@ 
+	.section .text
+
+	.globl _start
+	.type _start, %function
+_start:
+	.rept 70
+	call target
+	.endr
+	ret
+
+	.type target, %function
+target:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-relax-zcmt.d b/ld/testsuite/ld-riscv-elf/table-jump-relax-zcmt.d
new file mode 100644
index 00000000000..1adb1b289ce
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-relax-zcmt.d
@@ -0,0 +1,23 @@ 
+#source: table-jump.s
+#as: -march=rv64ic_zcmt --defsym TEST_CM_JT_JALT=1
+#ld: -Ttable-jump.ld --relax-zcmt
+#objdump: -d
+#...
+Disassembly of section .riscv.jvt:
+0+[0-9a-f]+ <__jvt_base\$>:
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jt\[0\]:[ 	]+foo_global
+[ 	]+...
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jalt\[32\]:[ 	]+foo_global
+#...
+Disassembly of section .text:
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # [0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # [0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # [0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # [0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # [0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+32 # [0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+32 # [0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+32 # [0-9a-f]+ <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+32 # [0-9a-f]+ <foo_global>
+#...
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-rv32.d b/ld/testsuite/ld-riscv-elf/table-jump-rv32.d
new file mode 100644
index 00000000000..ef6bbcdd526
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-rv32.d
@@ -0,0 +1,15 @@ 
+#source: table-jump-rv32.s
+#as: -march=rv32ic_zcmt
+#ld: -Ttable-jump.ld --relax-zcmt -melf32lriscv
+#objdump: -d
+#...
+Disassembly of section .riscv.jvt:
+0+[0-9a-f]+ <__jvt_base\$>:
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jt\[0\]:[ 	]+foo_local
+.*:[ 	]+[0-9a-f]+[ 	]+jvt.jt\[1\]:[ 	]+foo_global
+#...
+Disassembly of section .text:
+0+[0-9a-f]+ <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # [0-9a-f]+ <foo_local>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+1 # [0-9a-f]+ <foo_global>
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-rv32.s b/ld/testsuite/ld-riscv-elf/table-jump-rv32.s
new file mode 100644
index 00000000000..274c3b03f8b
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-rv32.s
@@ -0,0 +1,25 @@ 
+	.section .text
+
+	# Test RV32 ZCMT relaxation for cm.jt
+	# jal x0 (no link) -> cm.jt (index 0-31)
+	# JVT entry cost: 4 bytes (RV32), benefit = 2 per call
+	# Threshold: 3*2=6 > 4 (just profitable)
+
+	.globl _start
+	.type	_start, %function
+_start:
+	.rept 3
+	tail foo_local
+	tail foo_global
+	.endr
+
+	.space 4096
+
+	.globl  foo_global
+	.type	foo_global, %function
+foo_global:
+	ret
+
+	.type	foo_local, %function
+foo_local:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-shared-relocs.d b/ld/testsuite/ld-riscv-elf/table-jump-shared-relocs.d
new file mode 100644
index 00000000000..1c879fff883
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-shared-relocs.d
@@ -0,0 +1,9 @@ 
+#source: table-jump-shared.s
+#as: -march=rv64ic_zcmt -fpic
+#ld: -shared --relax-zcmt
+#readelf: -rW
+
+#failif
+#...
+.*R_RISCV_(TABLE_JUMP|DELETE|DELETE_AND_RELAX).*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-shared-symbol.d b/ld/testsuite/ld-riscv-elf/table-jump-shared-symbol.d
new file mode 100644
index 00000000000..a82763c2f8c
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-shared-symbol.d
@@ -0,0 +1,9 @@ 
+#source: table-jump-shared.s
+#as: -march=rv64ic_zcmt -fpic
+#ld: -shared --relax-zcmt
+#readelf: -SWs
+
+#failif
+#...
+.*(__jvt_base\$|\.riscv\.jvt).*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-shared-text.d b/ld/testsuite/ld-riscv-elf/table-jump-shared-text.d
new file mode 100644
index 00000000000..262d4c59643
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-shared-text.d
@@ -0,0 +1,9 @@ 
+#source: table-jump-shared.s
+#as: -march=rv64ic_zcmt -fpic
+#ld: -shared --relax-zcmt
+#objdump: -d
+
+#failif
+#...
+.*cm\.j(al)?t.*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-shared.d b/ld/testsuite/ld-riscv-elf/table-jump-shared.d
new file mode 100644
index 00000000000..1fec141e6b6
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-shared.d
@@ -0,0 +1,12 @@ 
+#source: table-jump-shared.s
+#as: -march=rv64ic_zcmt -fpic
+#ld: -shared --relax-zcmt
+#objdump: -d
+#...
+Disassembly of section .text:
+#...
+0+[0-9a-f]+ <foo>:
+#  Calls should NOT be cm.jalt in shared library (should be jal to PLT)
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+[0-9a-f]+ <bar@plt>
+.*:[ 	]+[0-9a-f]+[ 	]+jal[ 	]+[0-9a-f]+ <bar@plt>
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-shared.s b/ld/testsuite/ld-riscv-elf/table-jump-shared.s
new file mode 100644
index 00000000000..0bd853d1ae6
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-shared.s
@@ -0,0 +1,15 @@ 
+	.section .text
+
+	.globl foo
+	.type	foo, %function
+foo:
+	# Shared library: JVT relaxation should be disabled
+	.rept 50
+	call bar
+	.endr
+	ret
+
+	.globl bar
+	.type	bar, %function
+bar:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-strip-symbol.d b/ld/testsuite/ld-riscv-elf/table-jump-strip-symbol.d
new file mode 100644
index 00000000000..39b1d99e93e
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-strip-symbol.d
@@ -0,0 +1,9 @@ 
+#source: table-jump.s
+#as: -march=rv64ic_zcmt --defsym TEST_CM_JALT=1
+#ld: -Ttable-jump.ld --strip-all --relax-zcmt
+#readelf: -sW
+
+#failif
+#...
+.*__jvt_base\$.*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-symbol-policy.d b/ld/testsuite/ld-riscv-elf/table-jump-symbol-policy.d
new file mode 100644
index 00000000000..acd152a93be
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-symbol-policy.d
@@ -0,0 +1,8 @@ 
+#source: table-jump.s
+#as: -march=rv64ic_zcmt --defsym TEST_CM_JALT=1
+#ld: -Ttable-jump.ld --relax-zcmt
+#readelf: -sW
+
+#...
+[ 	]+[0-9]+:[ 	]+0+[0-9a-f]+[ 	]+0[ 	]+NOTYPE[ 	]+LOCAL[ 	]+DEFAULT[ 	]+[0-9]+[ 	]+__jvt_base\$
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-vma-lma.d b/ld/testsuite/ld-riscv-elf/table-jump-vma-lma.d
new file mode 100644
index 00000000000..222d546df8b
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-vma-lma.d
@@ -0,0 +1,16 @@ 
+#source: table-jump.s
+#as: -march=rv64ic_zcmt --defsym TEST_CM_JALT=1
+#ld: -Ttable-jump-vma-lma.ld --relax-zcmt
+#objdump: -d
+
+#...
+Disassembly of section .riscv.jvt:
+#...
+.*:[ 	]+0000000000003118[ 	]+jvt.jalt\[32\]:[ 	]+foo_global
+.*:[ 	]+000000000000311a[ 	]+jvt.jalt\[33\]:[ 	]+foo_local
+#...
+Disassembly of section .text:
+#...
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+32 # 3118 <foo_global>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jalt[ 	]+33 # 311a <foo_local>
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-vma-lma.ld b/ld/testsuite/ld-riscv-elf/table-jump-vma-lma.ld
new file mode 100644
index 00000000000..a8202afb381
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-vma-lma.ld
@@ -0,0 +1,10 @@ 
+ENTRY(_start)
+SECTIONS
+{
+	.riscv.jvt 0x1000 : AT(0x8000) {
+		*(.riscv.jvt)
+	}
+	.text 0x3000 : AT(0x9000) {
+		*(.text)
+	}
+}
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-vma-zero.d b/ld/testsuite/ld-riscv-elf/table-jump-vma-zero.d
new file mode 100644
index 00000000000..c93a1c6c4f2
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-vma-zero.d
@@ -0,0 +1,15 @@ 
+#source: table-jump-vma-zero.s
+#as: -march=rv64ic_zcmt
+#ld: -Ttable-jump-vma-zero.ld --relax-zcmt
+#objdump: -d -s -j .riscv.jvt -j .text
+
+#...
+Contents of section .riscv.jvt:
+[ 	]+1000[ 	]+00000000[ 	]+00000000.*
+#...
+Disassembly of section .text:
+#...
+0+3000 <_start>:
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # 0 <target>
+.*:[ 	]+[0-9a-f]+[ 	]+cm.jt[ 	]+0 # 0 <target>
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-vma-zero.ld b/ld/testsuite/ld-riscv-elf/table-jump-vma-zero.ld
new file mode 100644
index 00000000000..6db8ac52006
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-vma-zero.ld
@@ -0,0 +1,13 @@ 
+ENTRY(_start)
+SECTIONS
+{
+	.text.target 0x0 : {
+		*(.text.target)
+	}
+	.riscv.jvt 0x1000 : {
+		*(.riscv.jvt)
+	}
+	.text 0x3000 : {
+		*(.text.start)
+	}
+}
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-vma-zero.s b/ld/testsuite/ld-riscv-elf/table-jump-vma-zero.s
new file mode 100644
index 00000000000..742a0ee23fe
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-vma-zero.s
@@ -0,0 +1,14 @@ 
+	.section .text.start, "ax"
+
+	.globl _start
+	.type _start, %function
+_start:
+	.rept 5
+	tail target
+	.endr
+	ret
+
+	.section .text.target, "ax"
+	.type target, %function
+target:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-zcd-conflict.d b/ld/testsuite/ld-riscv-elf/table-jump-zcd-conflict.d
new file mode 100644
index 00000000000..ed70f3fd94e
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-zcd-conflict.d
@@ -0,0 +1,9 @@ 
+#source: table-jump.s
+#as: -march=rv64imafd_zicsr_zifencei_zca_zcd_zcmt --defsym TEST_CM_JT_JALT=1
+#ld: -Ttable-jump.ld --relax-zcmt
+#readelf: -SWs
+
+#failif
+#...
+.*(__jvt_base\$|\.riscv\.jvt).*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zca.d b/ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zca.d
new file mode 100644
index 00000000000..9554df1aa91
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zca.d
@@ -0,0 +1,9 @@ 
+#source: table-jump-zcmt-no-zca.s
+#as: -march=rv64i -mno-arch-attr
+#ld: --relax-zcmt
+#readelf: -SWs
+
+#failif
+#...
+.*(__jvt_base\$|\.riscv\.jvt).*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zca.s b/ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zca.s
new file mode 100644
index 00000000000..91aa8f07ed3
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zca.s
@@ -0,0 +1,24 @@ 
+	.section .riscv.attributes,"",@0x70000003
+	.byte 0x41
+0:
+	.4byte 1f-0b
+	.asciz "riscv"
+2:
+	.byte 1
+	.4byte 1f-2b
+	.byte 5
+	.asciz "rv64i2p1_zicsr2p0_zcmt1p0"
+1:
+
+	.section .text
+	.globl _start
+	.type _start, %function
+_start:
+	.rept 70
+	call target
+	.endr
+	ret
+
+	.type target, %function
+target:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zicsr.d b/ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zicsr.d
new file mode 100644
index 00000000000..981e5933097
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zicsr.d
@@ -0,0 +1,9 @@ 
+#source: table-jump-zcmt-no-zicsr.s
+#as: -march=rv64i -mno-arch-attr
+#ld: --relax-zcmt
+#readelf: -SWs
+
+#failif
+#...
+.*(__jvt_base\$|\.riscv\.jvt).*
+#pass
diff --git a/ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zicsr.s b/ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zicsr.s
new file mode 100644
index 00000000000..768a6fb32fa
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump-zcmt-no-zicsr.s
@@ -0,0 +1,24 @@ 
+	.section .riscv.attributes,"",@0x70000003
+	.byte 0x41
+0:
+	.4byte 1f-0b
+	.asciz "riscv"
+2:
+	.byte 1
+	.4byte 1f-2b
+	.byte 5
+	.asciz "rv64i2p1_zca1p0_zcmt1p0"
+1:
+
+	.section .text
+	.globl _start
+	.type _start, %function
+_start:
+	.rept 70
+	call target
+	.endr
+	ret
+
+	.type target, %function
+target:
+	ret
diff --git a/ld/testsuite/ld-riscv-elf/table-jump.ld b/ld/testsuite/ld-riscv-elf/table-jump.ld
new file mode 100644
index 00000000000..ab84fdcb772
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump.ld
@@ -0,0 +1,16 @@ 
+ENTRY(_start)
+SECTIONS
+{
+	.got  0x1000 : {
+		*(.got)
+	}
+	.riscv.jvt : {
+		*(.riscv.jvt)
+	}
+	.data 0x2000: {
+		*(.data)
+	}
+	.text 0x3000 : {
+		*(.text)
+	}
+}
diff --git a/ld/testsuite/ld-riscv-elf/table-jump.s b/ld/testsuite/ld-riscv-elf/table-jump.s
new file mode 100644
index 00000000000..75574a69223
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/table-jump.s
@@ -0,0 +1,46 @@ 
+    .section .text
+
+    .globl _start
+	.type	_start, %function
+_start:
+.ifdef TEST_CM_JT
+    .rept 5
+    tail foo_local
+    tail foo_global
+    .endr
+.endif
+
+.ifdef TEST_CM_JALT
+    .rept 70
+    call foo_global
+    call foo_local
+    .endr
+.endif
+
+.ifdef TEST_CM_JT_JALT
+    .rept 5
+    tail foo_global
+    .endr
+    .rept 140
+    call foo_global
+    .endr
+.endif
+
+	# Keep paired JAL candidates out of C.J range.  Zcmt profiling runs
+	# after ordinary shortening, so near paired JALs should become C.J
+	# and no longer be Zcmt candidates.
+.ifdef TEST_CM_JT
+    .space 4096
+.endif
+.ifdef TEST_CM_JT_JALT
+    .space 4096
+.endif
+
+    .globl  foo_global
+	.type	foo_global, %function
+foo_global:
+    ret
+
+	.type	foo_local, %function
+foo_local:
+    ret