diff options
author | Nemanja Ivanovic <nemanja.i.ibm@gmail.com> | 2023-02-16 13:37:31 -0500 |
---|---|---|
committer | Nemanja Ivanovic <nemanja.i.ibm@gmail.com> | 2023-02-16 13:37:58 -0500 |
commit | 372820bf571c8d32c8165cfc74b0439c7bb397f9 (patch) | |
tree | c2e4b16dd904305331d306f46abdd7c0c612089b /libunwind | |
parent | fbe210dc7a6ad87a30e5ffe928a168e621f6fcc5 (diff) | |
download | llvm-372820bf571c8d32c8165cfc74b0439c7bb397f9.tar.gz |
[libunwind][PowerPC] Fix saving/restoring VSX registers on LE systems
Currently, libunwind just uses stxvd2x/lxvd2x to save/restore
VSX registers respectively. This puts the registers in
doubleword-reversed order into memory on little endian systems.
If both the save and restore are done the same way, this
isn't a problem. However if the unwinder is just restoring
a callee-saved register, it will restore it in the wrong
order (since function prologues save them in the correct order).
This patch adds the necessary swaps before the saves and after
the restores.
Differential revision: https://reviews.llvm.org/D137599
Diffstat (limited to 'libunwind')
-rw-r--r-- | libunwind/src/UnwindRegistersRestore.S | 18 | ||||
-rw-r--r-- | libunwind/src/UnwindRegistersSave.S | 11 |
2 files changed, 29 insertions, 0 deletions
diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S index 2a472be943f3..543b19f7e72a 100644 --- a/libunwind/src/UnwindRegistersRestore.S +++ b/libunwind/src/UnwindRegistersRestore.S @@ -194,9 +194,20 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind15Registers_ppc646jumptoEv) addi 4, 3, PPC64_OFFS_FP // load VS register +#ifdef __LITTLE_ENDIAN__ +// For little-endian targets, we need a swap since lxvd2x will load the register +// in the incorrect doubleword order. +// FIXME: when supporting targets older than Power9 on LE is no longer required, +// this can be changed to simply `lxv n, (16 * n)(4)`. #define PPC64_LVS(n) \ lxvd2x n, 0, 4 ;\ + xxswapd n, n ;\ addi 4, 4, 16 +#else +#define PPC64_LVS(n) \ + lxvd2x n, 0, 4 ;\ + addi 4, 4, 16 +#endif // restore the first 32 VS regs (and also all floating point regs) PPC64_LVS(0) @@ -232,9 +243,16 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind15Registers_ppc646jumptoEv) PPC64_LVS(30) PPC64_LVS(31) +#ifdef __LITTLE_ENDIAN__ +#define PPC64_CLVS_RESTORE(n) \ + addi 4, 3, PPC64_OFFS_FP + n * 16 ;\ + lxvd2x n, 0, 4 ;\ + xxswapd n, n +#else #define PPC64_CLVS_RESTORE(n) \ addi 4, 3, PPC64_OFFS_FP + n * 16 ;\ lxvd2x n, 0, 4 +#endif #if !defined(_AIX) // use VRSAVE to conditionally restore the remaining VS regs, that are diff --git a/libunwind/src/UnwindRegistersSave.S b/libunwind/src/UnwindRegistersSave.S index 6c26b79877f6..79f5696a9888 100644 --- a/libunwind/src/UnwindRegistersSave.S +++ b/libunwind/src/UnwindRegistersSave.S @@ -351,9 +351,20 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) addi 4, 3, PPC64_OFFS_FP // store VS register +#ifdef __LITTLE_ENDIAN__ +// For little-endian targets, we need a swap since stxvd2x will store the +// register in the incorrect doubleword order. +// FIXME: when supporting targets older than Power9 on LE is no longer required +// this can be changed to simply `stxv n, 16 * n(4)`. #define PPC64_STVS(n) \ + xxswapd n, n ;\ stxvd2x n, 0, 4 ;\ addi 4, 4, 16 +#else +#define PPC64_STVS(n) \ + stxvd2x n, 0, 4 ;\ + addi 4, 4, 16 +#endif PPC64_STVS(0) PPC64_STVS(1) |