https://github.com/MacDue created 
https://github.com/llvm/llvm-project/pull/153565

The vector granule (AArch64 DWARF register 46) is a pseudo-register that 
contains the available size in bits of SVE vector registers in the current call 
frame, divided by 64. The vector granule can be used in DWARF expressions to 
describe SVE/SME stack frame layouts (e.g., the location of SVE callee-saves).

The first time VG is evaluated (if not already set), it is initialized to the 
result of evaluating a "CNTD" instruction (this assumes SVE is available).

To support SME, the value of VG can change per call frame; this is currently 
handled like any other callee-save and is intended to support the unwind 
information implemented in #152283. This limits how VG is used in the CFI 
information of functions with "streaming-mode changes" (mode changes that 
change the SVE vector length), to make the unwinder's job easier.

>From c4d1c6dad4edd2ce1263de982f141519346621bc Mon Sep 17 00:00:00 2001
From: Benjamin Maxwell <benjamin.maxw...@arm.com>
Date: Tue, 12 Aug 2025 08:27:47 +0000
Subject: [PATCH] [libunwind] Add support for the AArch64 "Vector Granule" (VG)
 register

The vector granule (AArch64 DWARF register 46) is a pseudo-register that
contains the available size in bits of SVE vector registers in the
current call frame, divided by 64. The vector granule can be used in
DWARF expressions to describe SVE/SME stack frame layouts (e.g., the
location of SVE callee-saves).

The first time VG is evaluated (if not already set), it is initialized
to the result of evaluating a "CNTD" instruction (this assumes SVE is
available).

To support SME, the value of VG can change per call frame; this is
currently handled like any other callee-save and is intended to support
the unwind information implemented in #152283. This limits how VG is
used in the CFI information of functions with "streaming-mode changes"
(mode changes that change the SVE vector length), to make the
unwinder's job easier.
---
 libunwind/include/__libunwind_config.h    |  4 +-
 libunwind/include/libunwind.h             |  1 +
 libunwind/src/Registers.hpp               | 36 ++++++++++++-
 libunwind/test/aarch64_vg_unwind.pass.cpp | 66 +++++++++++++++++++++++
 4 files changed, 103 insertions(+), 4 deletions(-)
 create mode 100644 libunwind/test/aarch64_vg_unwind.pass.cpp

diff --git a/libunwind/include/__libunwind_config.h 
b/libunwind/include/__libunwind_config.h
index bb7fe4c83a3c1..544b3ec96216a 100644
--- a/libunwind/include/__libunwind_config.h
+++ b/libunwind/include/__libunwind_config.h
@@ -73,11 +73,11 @@
 #  define _LIBUNWIND_HIGHEST_DWARF_REGISTER 
_LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC
 # elif defined(__aarch64__)
 #  define _LIBUNWIND_TARGET_AARCH64 1
-#  define _LIBUNWIND_CONTEXT_SIZE 66
+#define _LIBUNWIND_CONTEXT_SIZE 67
 #  if defined(__SEH__)
 #    define _LIBUNWIND_CURSOR_SIZE 164
 #  else
-#    define _LIBUNWIND_CURSOR_SIZE 78
+#define _LIBUNWIND_CURSOR_SIZE 79
 #  endif
 #  define _LIBUNWIND_HIGHEST_DWARF_REGISTER 
_LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64
 # elif defined(__arm__)
diff --git a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h
index b2dae8feed9a3..94928f436025a 100644
--- a/libunwind/include/libunwind.h
+++ b/libunwind/include/libunwind.h
@@ -532,6 +532,7 @@ enum {
   UNW_AARCH64_X31 = 31,
   UNW_AARCH64_SP = 31,
   UNW_AARCH64_PC = 32,
+  UNW_AARCH64_VG = 46,
 
   // reserved block
   UNW_AARCH64_RA_SIGN_STATE = 34,
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index 2c3bfb7e8428a..8b3055f5cd7cd 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -1851,6 +1851,8 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
   void      setFP(uint64_t value) { _registers.__fp = value; }
 
 private:
+  uint64_t lazyGetVG() const;
+
   struct GPRs {
     uint64_t __x[29]; // x0-x28
     uint64_t __fp;    // Frame pointer x29
@@ -1860,12 +1862,22 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
     uint64_t __ra_sign_state; // RA sign state register
   };
 
-  GPRs    _registers;
-  double  _vectorHalfRegisters[32];
+  struct Misc {
+    mutable uint64_t __vg = 0; // Vector Granule
+  };
+
+  GPRs _registers;
   // Currently only the lower double in 128-bit vectore registers
   // is perserved during unwinding.  We could define new register
   // numbers (> 96) which mean whole vector registers, then this
   // struct would need to change to contain whole vector registers.
+  double _vectorHalfRegisters[32];
+
+  // Miscellaneous/virtual registers. These are stored below the GPRs and FPRs
+  // as they do not correspond to physical registers, so do not need to be
+  // saved/restored in UnwindRegistersRestore.S and UnwindRegistersSave.S, and
+  // we don't want to modify the existing offsets for GPRs and FPRs.
+  Misc _misc_registers;
 };
 
 inline Registers_arm64::Registers_arm64(const void *registers) {
@@ -1895,11 +1907,27 @@ inline bool Registers_arm64::validRegister(int regNum) 
const {
     return false;
   if (regNum == UNW_AARCH64_RA_SIGN_STATE)
     return true;
+  if (regNum == UNW_AARCH64_VG)
+    return true;
   if ((regNum > 32) && (regNum < 64))
     return false;
   return true;
 }
 
+inline uint64_t Registers_arm64::lazyGetVG() const {
+  if (!_misc_registers.__vg) {
+#if defined(__aarch64__)
+    register uint64_t vg asm("x0");
+    asm(".inst 0x04e0e3e0" // CNTD x0
+        : "=r"(vg));
+    _misc_registers.__vg = vg;
+#else
+    _LIBUNWIND_ABORT("arm64 VG undefined");
+#endif
+  }
+  return _misc_registers.__vg;
+}
+
 inline uint64_t Registers_arm64::getRegister(int regNum) const {
   if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC)
     return _registers.__pc;
@@ -1911,6 +1939,8 @@ inline uint64_t Registers_arm64::getRegister(int regNum) 
const {
     return _registers.__fp;
   if (regNum == UNW_AARCH64_LR)
     return _registers.__lr;
+  if (regNum == UNW_AARCH64_VG)
+    return lazyGetVG();
   if ((regNum >= 0) && (regNum < 29))
     return _registers.__x[regNum];
   _LIBUNWIND_ABORT("unsupported arm64 register");
@@ -1927,6 +1957,8 @@ inline void Registers_arm64::setRegister(int regNum, 
uint64_t value) {
     _registers.__fp = value;
   else if (regNum == UNW_AARCH64_LR)
     _registers.__lr = value;
+  else if (regNum == UNW_AARCH64_VG)
+    _misc_registers.__vg = value;
   else if ((regNum >= 0) && (regNum < 29))
     _registers.__x[regNum] = value;
   else
diff --git a/libunwind/test/aarch64_vg_unwind.pass.cpp 
b/libunwind/test/aarch64_vg_unwind.pass.cpp
new file mode 100644
index 0000000000000..73ef941997f28
--- /dev/null
+++ b/libunwind/test/aarch64_vg_unwind.pass.cpp
@@ -0,0 +1,66 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: linux && target={{aarch64-.+}}
+
+#include <libunwind.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Basic test of VG (Vector Granule) unwinding. This is meant to mimic SVE/SME
+// unwind info without requiring those features for this test.
+
+#define VG_REGNUM 46
+
+__attribute__((noinline)) void baz() {
+  // The previous value of VG is 2
+  asm volatile(".cfi_escape 0x16, 0x2e, 0x01, 0x32");
+
+  unw_context_t context;
+  unw_cursor_t cursor;
+  unw_getcontext(&context);
+  unw_init_local(&cursor, &context);
+
+  // Note: At this point VG is not defined (until we unw_step).
+
+  uint16_t expected_vgs[]{/*qux*/ 2, /*bar*/ 2, /*foo*/ 8, /*main*/ 2};
+  for (uint16_t expected_vg : expected_vgs) {
+    unw_step(&cursor);
+    unw_word_t vg;
+    unw_get_reg(&cursor, VG_REGNUM, &vg);
+    if (vg != expected_vg)
+      exit(1);
+  }
+  exit(0);
+}
+
+__attribute__((noinline)) void qux() { baz(); }
+
+__attribute__((noinline)) void bar() {
+  // The previous value of VG is 8
+  asm volatile(".cfi_escape 0x16, 0x2e, 0x01, 0x38");
+  // The previous value of W21 is VG (used to force an evaluation of VG).
+  asm volatile(".cfi_escape 0x16, 0x15, 0x03, 0x92, 0x2e, 0x00");
+
+  // smstop sm
+  qux();
+  // smstart sm
+}
+__attribute__((noinline)) void foo() {
+  // The previous value of VG is 2
+  asm volatile(".cfi_escape 0x16, 0x2e, 0x01, 0x32");
+  // The previous value of W21 is VG (used to force an evaluation of VG).
+  asm volatile(".cfi_escape 0x16, 0x15, 0x03, 0x92, 0x2e, 0x00");
+
+  // smstart sm
+  bar();
+  // smstop sm
+}
+
+int main() { foo(); }

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to