diff --git a/libunwind/include/__libunwind_config.h b/libunwind/include/__libunwind_config.h index bb7fe4c83a3c..544b3ec96216 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 b2dae8feed9a..94928f436025 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 2c3bfb7e8428..8b3055f5cd7c 100644 --- a/libunwind/src/Registers.hpp +++ b/libunwind/src/Registers.hpp @@ -1851,6 +1851,8 @@ public: 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 @@ private: 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 000000000000..b6bb9693c1e0 --- /dev/null +++ b/libunwind/test/aarch64_vg_unwind.pass.cpp @@ -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 +#include +#include + +// 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(); }