[llvm] [AArch64] Fallback to PRFUM for PRFM with negative or unaligned offset (PR #166756)

Cullen Rhodes via llvm-commits llvm-commits at lists.llvm.org
Thu Nov 6 03:49:43 PST 2025


https://github.com/c-rhodes created https://github.com/llvm/llvm-project/pull/166756

Section C3.2.2 (quoted below) in the ARMARM makes this a requirement of assemblers for load/stores with unscaled offset. It makes no mention of PRFM so I don't consider this to be a bug, although I can see why we would want to extend this behaviour to the unscaled variants of these instructions as well, as GCC does. This patch adds an alias for this.

C3.2.2 Load/store register (unscaled offset)

  The load/store register instructions with an unscaled offset support
  only one addressing mode:

      Base plus an unscaled 9-bit signed immediate offset.

  See Load/store addressing modes.

  The load/store register (unscaled offset) instructions are required to
  disambiguate this instruction class from the load/store register
  instruction forms that support an addressing mode of base plus a scaled,
  unsigned 12-bit immediate offset, because that can represent some offset
  values in the same range.

  The ambiguous immediate offsets are byte offsets that are both:

      In the range 0-255, inclusive.

      Naturally aligned to the access size.

  Other byte offsets in the range -256 to 255 inclusive are unambiguous.
  An assembler program translating a load/store instruction, for example
  LDR, is required to encode an unambiguous offset using the unscaled
  9-bit offset form, and to encode an ambiguous offset using the scaled
  12-bit offset form. A programmer might force the generation of the
  unscaled 9-bit form by using one of the mnemonics in Table C.3.21. Arm
  recommends that a disassembler outputs all unscaled 9-bit offset forms
  using one of these mnemonics, but unambiguous offsets can be output
  using a load/store single register mnemonic, for example, LDR.

Fixes #83226.

>From 5b447363b63915ed430725926d34ba7c2e003f72 Mon Sep 17 00:00:00 2001
From: Cullen Rhodes <cullen.rhodes at arm.com>
Date: Thu, 6 Nov 2025 11:12:33 +0000
Subject: [PATCH] [AArch64] Fallback to PRFUM for PRFM with negative or
 unaligned offset

Section C3.2.2 (quoted below) in the ARMARM makes this a requirement of
assemblers for load/stores with unscaled offset. It makes no mention of
PRFM so I don't consider this to be a bug, although I can see why we
would want to extend this behaviour to the unscaled variants of these
instructions as well, as GCC does. This patch adds an alias for this.

C3.2.2 Load/store register (unscaled offset)

  The load/store register instructions with an unscaled offset support
  only one addressing mode:

      Base plus an unscaled 9-bit signed immediate offset.

  See Load/store addressing modes.

  The load/store register (unscaled offset) instructions are required to
  disambiguate this instruction class from the load/store register
  instruction forms that support an addressing mode of base plus a scaled,
  unsigned 12-bit immediate offset, because that can represent some offset
  values in the same range.

  The ambiguous immediate offsets are byte offsets that are both:

      In the range 0-255, inclusive.

      Naturally aligned to the access size.

  Other byte offsets in the range -256 to 255 inclusive are unambiguous.
  An assembler program translating a load/store instruction, for example
  LDR, is required to encode an unambiguous offset using the unscaled
  9-bit offset form, and to encode an ambiguous offset using the scaled
  12-bit offset form. A programmer might force the generation of the
  unscaled 9-bit form by using one of the mnemonics in Table C.3.21. Arm
  recommends that a disassembler outputs all unscaled 9-bit offset forms
  using one of these mnemonics, but unambiguous offsets can be output
  using a load/store single register mnemonic, for example, LDR.

Fixes #83226.
---
 llvm/lib/Target/AArch64/AArch64InstrInfo.td |  5 +++
 llvm/test/MC/AArch64/prfum.s                | 44 +++++++++++++++++++++
 2 files changed, 49 insertions(+)
 create mode 100644 llvm/test/MC/AArch64/prfum.s

diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 2871a20e28b65..5a608ef80230f 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -4444,6 +4444,11 @@ defm PRFUM : PrefetchUnscaled<0b11, 0, 0b10, "prfum",
                   [(AArch64Prefetch timm:$Rt,
                                   (am_unscaled64 GPR64sp:$Rn, simm9:$offset))]>;
 
+// PRFM falls back to PRFUM for negative or unaligned offsets (not a multiple
+// of 8).
+def : InstAlias<"prfm $Rt, [$Rn, $offset]",
+                (PRFUMi prfop:$Rt, GPR64sp:$Rn, simm9_offset_fb64:$offset), 0>;
+
 //---
 // (unscaled immediate, unprivileged)
 defm LDTRX : LoadUnprivileged<0b11, 0, 0b01, GPR64, "ldtr">;
diff --git a/llvm/test/MC/AArch64/prfum.s b/llvm/test/MC/AArch64/prfum.s
new file mode 100644
index 0000000000000..81a864a694325
--- /dev/null
+++ b/llvm/test/MC/AArch64/prfum.s
@@ -0,0 +1,44 @@
+// RUN: llvm-mc -triple=aarch64 -show-encoding --print-imm-hex=false < %s \
+// RUN:        | FileCheck %s --check-prefixes=CHECK-ENCODING,CHECK-INST
+// RUN: llvm-mc -triple=aarch64 -filetype=obj < %s \
+// RUN:        | llvm-objdump -d --print-imm-hex=false - | FileCheck %s --check-prefix=CHECK-INST
+// Disassemble encoding and check the re-encoding (-show-encoding) matches.
+// RUN: llvm-mc -triple=aarch64 -show-encoding < %s \
+// RUN:        | sed '/.text/d' | sed 's/.*encoding: //g' \
+// RUN:        | llvm-mc -triple=aarch64 -disassemble -show-encoding --print-imm-hex=false \
+// RUN:        | FileCheck %s --check-prefixes=CHECK-ENCODING,CHECK-INST
+
+// PRFM falls back to PRFUM for negative or unaligned offsets (not a multiple
+// of 8).
+
+prfm pldl1keep, [x0, #-256]
+// CHECK-INST: prfum pldl1keep, [x0, #-256]
+// CHECK-ENCODING: [0x00,0x00,0x90,0xf8]
+
+prfm pldl1keep, [x0, #-8]
+// CHECK-INST: prfum pldl1keep, [x0, #-8]
+// CHECK-ENCODING: [0x00,0x80,0x9f,0xf8]
+
+prfm pldl1keep, [x0, #-1]
+// CHECK-INST: prfum pldl1keep, [x0, #-1]
+// CHECK-ENCODING: [0x00,0xf0,0x9f,0xf8]
+
+prfm pldl1keep, [x0, #0]
+// CHECK-INST: prfm pldl1keep, [x0]
+// CHECK-ENCODING: [0x00,0x00,0x80,0xf9]
+
+prfm pldl1keep, [x0, #1]
+// CHECK-INST: prfum pldl1keep, [x0, #1]
+// CHECK-ENCODING: [0x00,0x10,0x80,0xf8]
+
+prfm pldl1keep, [x0, #8]
+// CHECK-INST: prfm pldl1keep, [x0, #8]
+// CHECK-ENCODING: [0x00,0x04,0x80,0xf9]
+
+prfm pldl1keep, [x0, #255]
+// CHECK-INST: prfum pldl1keep, [x0, #255]
+// CHECK-ENCODING: [0x00,0xf0,0x8f,0xf8]
+
+prfm pldl1keep, [x0, #256]
+// CHECK-INST: prfm pldl1keep, [x0, #256]
+// CHECK-ENCODING: [0x00,0x80,0x80,0xf9]



More information about the llvm-commits mailing list