Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
66 changes: 66 additions & 0 deletions libunwind/test/aarch64_vg_unwind.pass.cpp
Original file line number Diff line number Diff line change
@@ -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(); }
Loading