Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions libunwind/include/__libunwind_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand Down
1 change: 1 addition & 0 deletions libunwind/include/libunwind.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
36 changes: 34 additions & 2 deletions libunwind/src/Registers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Comment on lines +1920 to +1923
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if there's a nicer way to do this, LLVM's compiler-rt provides __arm_get_current_vg (which would be a safer alternative), but depending on how libunwind is built, it's not necessarily going to be available.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libunwind is more lower level than compiler-rt. It's best not to call compiler-rt functions.

#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;
Expand All @@ -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");
Expand All @@ -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
Expand Down
65 changes: 65 additions & 0 deletions libunwind/test/aarch64_vg_unwind.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//===----------------------------------------------------------------------===//
//
// 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(); }
Loading