Skip to content

Commit 8a80823

Browse files
nemanjaitru
authored andcommitted
[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 (cherry picked from commit 372820b)
1 parent c5b23ab commit 8a80823

File tree

3 files changed

+119
-0
lines changed

3 files changed

+119
-0
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// Check that the PowerPC vector registers are restored properly during
10+
// unwinding.
11+
12+
// REQUIRES: target=powerpc{{(64)?}}le-unknown-linux-gnu
13+
// UNSUPPORTED: no-exceptions
14+
15+
// Callee-saved VSR's 62 and 63 (vr30, vr31 respectively) are set to 16 bytes
16+
// with values 1, 2 respectively in main. In order to ensure the two doublewords
17+
// in each register are different, they are merged. Then they are reset to 16
18+
// bytes with values 9 and 12 respectively in a callee and an exception is
19+
// thrown. When catching an exception in main, the values in the two registers
20+
// need to be the original ones (including the correct doubleword order).
21+
22+
#include <cassert>
23+
#include <cstdlib>
24+
25+
int __attribute__((noinline)) test2(int i) {
26+
if (i > 3)
27+
throw i;
28+
srand(i);
29+
return rand();
30+
}
31+
32+
int __attribute__((noinline)) test(int i) {
33+
// Clobber VS63 and VS62 in the function body.
34+
// Set VS63 to 16 bytes each with value 9
35+
asm volatile("vspltisb 31, 9" : : : "v31");
36+
37+
// Set VS62 to 16 bytes each with value 12
38+
asm volatile("vspltisb 30, 12" : : : "v30");
39+
return test2(i);
40+
}
41+
42+
#define cmpVS63(vec, result) \
43+
{ \
44+
vector unsigned char gbg; \
45+
asm volatile("vcmpequb. %[gbg], 31, %[veca];" \
46+
"mfocrf %[res], 2;" \
47+
"rlwinm %[res], %[res], 25, 31, 31" \
48+
: [res] "=r"(result), [gbg] "=v"(gbg) \
49+
: [veca] "v"(vec) \
50+
: "cr6"); \
51+
}
52+
53+
#define cmpVS62(vec, result) \
54+
{ \
55+
vector unsigned char gbg; \
56+
asm volatile("vcmpequb. %[gbg], 30, %[veca];" \
57+
"mfocrf %[res], 2;" \
58+
"rlwinm %[res], %[res], 25, 31, 31" \
59+
: [res] "=r"(result), [gbg] "=v"(gbg) \
60+
: [veca] "v"(vec) \
61+
: "cr6"); \
62+
}
63+
64+
int main(int, char **) {
65+
// Set VS63 to 16 bytes each with value 1.
66+
asm volatile("vspltisb 31, 1" : : : "v31");
67+
68+
// Set VS62 to 16 bytes each with value 2.
69+
asm volatile("vspltisb 30, 2" : : : "v30");
70+
71+
// Mix doublewords for both VS62 and VS63.
72+
asm volatile("xxmrghd 63, 63, 62");
73+
asm volatile("xxmrghd 62, 63, 62");
74+
75+
vector unsigned long long expectedVS63Value = {0x202020202020202,
76+
0x101010101010101};
77+
vector unsigned long long expectedVS62Value = {0x202020202020202,
78+
0x101010101010101};
79+
try {
80+
test(4);
81+
} catch (int num) {
82+
// If the unwinder restores VS63 and VS62 correctly, they should contain
83+
// 0x01's and 0x02's respectively instead of 0x09's and 0x12's.
84+
bool isEqualVS63, isEqualVS62;
85+
cmpVS63(expectedVS63Value, isEqualVS63);
86+
cmpVS62(expectedVS62Value, isEqualVS62);
87+
assert(isEqualVS63 && isEqualVS62);
88+
}
89+
return 0;
90+
}

libunwind/src/UnwindRegistersRestore.S

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,20 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind15Registers_ppc646jumptoEv)
194194
addi 4, 3, PPC64_OFFS_FP
195195

196196
// load VS register
197+
#ifdef __LITTLE_ENDIAN__
198+
// For little-endian targets, we need a swap since lxvd2x will load the register
199+
// in the incorrect doubleword order.
200+
// FIXME: when supporting targets older than Power9 on LE is no longer required,
201+
// this can be changed to simply `lxv n, (16 * n)(4)`.
197202
#define PPC64_LVS(n) \
198203
lxvd2x n, 0, 4 ;\
204+
xxswapd n, n ;\
199205
addi 4, 4, 16
206+
#else
207+
#define PPC64_LVS(n) \
208+
lxvd2x n, 0, 4 ;\
209+
addi 4, 4, 16
210+
#endif
200211

201212
// restore the first 32 VS regs (and also all floating point regs)
202213
PPC64_LVS(0)
@@ -232,9 +243,16 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind15Registers_ppc646jumptoEv)
232243
PPC64_LVS(30)
233244
PPC64_LVS(31)
234245

246+
#ifdef __LITTLE_ENDIAN__
247+
#define PPC64_CLVS_RESTORE(n) \
248+
addi 4, 3, PPC64_OFFS_FP + n * 16 ;\
249+
lxvd2x n, 0, 4 ;\
250+
xxswapd n, n
251+
#else
235252
#define PPC64_CLVS_RESTORE(n) \
236253
addi 4, 3, PPC64_OFFS_FP + n * 16 ;\
237254
lxvd2x n, 0, 4
255+
#endif
238256

239257
#if !defined(_AIX)
240258
// use VRSAVE to conditionally restore the remaining VS regs, that are

libunwind/src/UnwindRegistersSave.S

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,9 +351,20 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
351351
addi 4, 3, PPC64_OFFS_FP
352352

353353
// store VS register
354+
#ifdef __LITTLE_ENDIAN__
355+
// For little-endian targets, we need a swap since stxvd2x will store the
356+
// register in the incorrect doubleword order.
357+
// FIXME: when supporting targets older than Power9 on LE is no longer required
358+
// this can be changed to simply `stxv n, 16 * n(4)`.
354359
#define PPC64_STVS(n) \
360+
xxswapd n, n ;\
355361
stxvd2x n, 0, 4 ;\
356362
addi 4, 4, 16
363+
#else
364+
#define PPC64_STVS(n) \
365+
stxvd2x n, 0, 4 ;\
366+
addi 4, 4, 16
367+
#endif
357368

358369
PPC64_STVS(0)
359370
PPC64_STVS(1)

0 commit comments

Comments
 (0)