llvm-project/libunwind/test/aarch64_vg_unwind.pass.cpp
Benjamin Maxwell bfab8085af
[libunwind] Add support for the AArch64 "Vector Granule" (VG) register (#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.
2025-08-21 10:01:40 +01:00

66 lines
1.8 KiB
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(".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(".cfi_escape 0x16, 0x2e, 0x01, 0x38");
// The previous value of W21 is VG (used to force an evaluation of VG).
asm(".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(".cfi_escape 0x16, 0x2e, 0x01, 0x32");
// The previous value of W21 is VG (used to force an evaluation of VG).
asm(".cfi_escape 0x16, 0x15, 0x03, 0x92, 0x2e, 0x00");
// smstart sm
bar();
// smstop sm
}
int main() { foo(); }