Skip to content

Commit f1342c7

Browse files
committed
[LLDB] AArch64 SVE restore SVE registers after expression
This patch fixes register save/restore on expression call to also include SVE registers. This will fix expression calls like: re re p1 <Register Value P1 before expression> p <var-name or function call> re re p1 <Register Value P1 after expression> In above example register P1 should remain the same before and after the expression evaluation. Reviewed By: DavidSpickett Differential Revision: https://reviews.llvm.org/D108739
1 parent fbb8b41 commit f1342c7

File tree

5 files changed

+190
-67
lines changed

5 files changed

+190
-67
lines changed

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp

Lines changed: 115 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@
4646
#define HWCAP_PACA (1 << 30)
4747
#define HWCAP2_MTE (1 << 18)
4848

49-
#define REG_CONTEXT_SIZE (GetGPRSize() + GetFPRSize())
50-
5149
using namespace lldb;
5250
using namespace lldb_private;
5351
using namespace lldb_private::process_linux;
@@ -452,45 +450,80 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
452450

453451
Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
454452
lldb::DataBufferSP &data_sp) {
455-
Status error;
453+
// AArch64 register data must contain GPRs, either FPR or SVE registers
454+
// and optional MTE register. Pointer Authentication (PAC) registers are
455+
// read-only and will be skiped.
456456

457-
data_sp.reset(new DataBufferHeap(REG_CONTEXT_SIZE, 0));
457+
// In order to create register data checkpoint we first read all register
458+
// values if not done already and calculate total size of register set data.
459+
// We store all register values in data_sp by copying full PTrace data that
460+
// corresponds to register sets enabled by current register context.
458461

462+
Status error;
463+
uint32_t reg_data_byte_size = GetGPRBufferSize();
459464
error = ReadGPR();
460465
if (error.Fail())
461466
return error;
462467

463-
error = ReadFPR();
468+
// If SVE is enabled we need not copy FPR separately.
469+
if (GetRegisterInfo().IsSVEEnabled()) {
470+
reg_data_byte_size += GetSVEBufferSize();
471+
error = ReadAllSVE();
472+
} else {
473+
reg_data_byte_size += GetFPRSize();
474+
error = ReadFPR();
475+
}
464476
if (error.Fail())
465477
return error;
466478

479+
if (GetRegisterInfo().IsMTEEnabled()) {
480+
reg_data_byte_size += GetMTEControlSize();
481+
error = ReadMTEControl();
482+
if (error.Fail())
483+
return error;
484+
}
485+
486+
data_sp.reset(new DataBufferHeap(reg_data_byte_size, 0));
467487
uint8_t *dst = data_sp->GetBytes();
468-
::memcpy(dst, GetGPRBuffer(), GetGPRSize());
469-
dst += GetGPRSize();
470-
::memcpy(dst, GetFPRBuffer(), GetFPRSize());
488+
489+
::memcpy(dst, GetGPRBuffer(), GetGPRBufferSize());
490+
dst += GetGPRBufferSize();
491+
492+
if (GetRegisterInfo().IsSVEEnabled()) {
493+
::memcpy(dst, GetSVEBuffer(), GetSVEBufferSize());
494+
dst += GetSVEBufferSize();
495+
} else {
496+
::memcpy(dst, GetFPRBuffer(), GetFPRSize());
497+
dst += GetFPRSize();
498+
}
499+
500+
if (GetRegisterInfo().IsMTEEnabled())
501+
::memcpy(dst, GetMTEControl(), GetMTEControlSize());
471502

472503
return error;
473504
}
474505

475506
Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
476507
const lldb::DataBufferSP &data_sp) {
477-
Status error;
508+
// AArch64 register data must contain GPRs, either FPR or SVE registers
509+
// and optional MTE register. Pointer Authentication (PAC) registers are
510+
// read-only and will be skiped.
511+
512+
// We store all register values in data_sp by copying full PTrace data that
513+
// corresponds to register sets enabled by current register context. In order
514+
// to restore from register data checkpoint we will first restore GPRs, based
515+
// on size of remaining register data either SVE or FPRs should be restored
516+
// next. SVE is not enabled if we have register data size less than or equal
517+
// to size of GPR + FPR + MTE.
478518

519+
Status error;
479520
if (!data_sp) {
480521
error.SetErrorStringWithFormat(
481-
"NativeRegisterContextLinux_x86_64::%s invalid data_sp provided",
522+
"NativeRegisterContextLinux_arm64::%s invalid data_sp provided",
482523
__FUNCTION__);
483524
return error;
484525
}
485526

486-
if (data_sp->GetByteSize() != REG_CONTEXT_SIZE) {
487-
error.SetErrorStringWithFormat(
488-
"NativeRegisterContextLinux_x86_64::%s data_sp contained mismatched "
489-
"data size, expected %" PRIu64 ", actual %" PRIu64,
490-
__FUNCTION__, REG_CONTEXT_SIZE, data_sp->GetByteSize());
491-
return error;
492-
}
493-
494527
uint8_t *src = data_sp->GetBytes();
495528
if (src == nullptr) {
496529
error.SetErrorStringWithFormat("NativeRegisterContextLinux_x86_64::%s "
@@ -499,19 +532,79 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
499532
__FUNCTION__);
500533
return error;
501534
}
502-
::memcpy(GetGPRBuffer(), src, GetRegisterInfoInterface().GetGPRSize());
535+
536+
uint64_t reg_data_min_size = GetGPRBufferSize() + GetFPRSize();
537+
if (data_sp->GetByteSize() < reg_data_min_size) {
538+
error.SetErrorStringWithFormat(
539+
"NativeRegisterContextLinux_arm64::%s data_sp contained insufficient "
540+
"register data bytes, expected at least %" PRIu64 ", actual %" PRIu64,
541+
__FUNCTION__, reg_data_min_size, data_sp->GetByteSize());
542+
return error;
543+
}
544+
545+
// Register data starts with GPRs
546+
::memcpy(GetGPRBuffer(), src, GetGPRBufferSize());
547+
m_gpr_is_valid = true;
503548

504549
error = WriteGPR();
505550
if (error.Fail())
506551
return error;
507552

508-
src += GetRegisterInfoInterface().GetGPRSize();
509-
::memcpy(GetFPRBuffer(), src, GetFPRSize());
553+
src += GetGPRBufferSize();
554+
555+
// Verify if register data may contain SVE register values.
556+
bool contains_sve_reg_data =
557+
(data_sp->GetByteSize() > (reg_data_min_size + GetSVEHeaderSize()));
558+
559+
if (contains_sve_reg_data) {
560+
// We have SVE register data first write SVE header.
561+
::memcpy(GetSVEHeader(), src, GetSVEHeaderSize());
562+
if (!sve_vl_valid(m_sve_header.vl)) {
563+
m_sve_header_is_valid = false;
564+
error.SetErrorStringWithFormat("NativeRegisterContextLinux_arm64::%s "
565+
"Invalid SVE header in data_sp",
566+
__FUNCTION__);
567+
return error;
568+
}
569+
m_sve_header_is_valid = true;
570+
error = WriteSVEHeader();
571+
if (error.Fail())
572+
return error;
573+
574+
// SVE header has been written configure SVE vector length if needed.
575+
ConfigureRegisterContext();
576+
577+
// Make sure data_sp contains sufficient data to write all SVE registers.
578+
reg_data_min_size = GetGPRBufferSize() + GetSVEBufferSize();
579+
if (data_sp->GetByteSize() < reg_data_min_size) {
580+
error.SetErrorStringWithFormat(
581+
"NativeRegisterContextLinux_arm64::%s data_sp contained insufficient "
582+
"register data bytes, expected %" PRIu64 ", actual %" PRIu64,
583+
__FUNCTION__, reg_data_min_size, data_sp->GetByteSize());
584+
return error;
585+
}
586+
587+
::memcpy(GetSVEBuffer(), src, GetSVEBufferSize());
588+
m_sve_buffer_is_valid = true;
589+
error = WriteAllSVE();
590+
src += GetSVEBufferSize();
591+
} else {
592+
::memcpy(GetFPRBuffer(), src, GetFPRSize());
593+
m_fpu_is_valid = true;
594+
error = WriteFPR();
595+
src += GetFPRSize();
596+
}
510597

511-
error = WriteFPR();
512598
if (error.Fail())
513599
return error;
514600

601+
if (GetRegisterInfo().IsMTEEnabled() &&
602+
data_sp->GetByteSize() > reg_data_min_size) {
603+
::memcpy(GetMTEControl(), src, GetMTEControlSize());
604+
m_mte_ctrl_is_valid = true;
605+
error = WriteMTEControl();
606+
}
607+
515608
return error;
516609
}
517610

@@ -864,13 +957,6 @@ uint32_t NativeRegisterContextLinux_arm64::CalculateSVEOffset(
864957
return sve_reg_offset;
865958
}
866959

867-
void *NativeRegisterContextLinux_arm64::GetSVEBuffer() {
868-
if (m_sve_state == SVEState::FPSIMD)
869-
return m_sve_ptrace_payload.data() + sve::ptrace_fpsimd_offset;
870-
871-
return m_sve_ptrace_payload.data();
872-
}
873-
874960
std::vector<uint32_t> NativeRegisterContextLinux_arm64::GetExpeditedRegisters(
875961
ExpeditedRegs expType) const {
876962
std::vector<uint32_t> expedited_reg_nums =

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ class NativeRegisterContextLinux_arm64
139139

140140
void *GetMTEControl() { return &m_mte_ctrl_reg; }
141141

142-
void *GetSVEBuffer();
142+
void *GetSVEBuffer() { return m_sve_ptrace_payload.data(); };
143143

144144
size_t GetSVEHeaderSize() { return sizeof(m_sve_header); }
145145

lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ class RegisterInfoPOSIX_arm64
110110

111111
bool IsSVEEnabled() const { return m_opt_regsets.AnySet(eRegsetMaskSVE); }
112112
bool IsPAuthEnabled() const { return m_opt_regsets.AnySet(eRegsetMaskPAuth); }
113+
bool IsMTEEnabled() const { return m_opt_regsets.AnySet(eRegsetMaskMTE); }
113114

114115
bool IsSVEReg(unsigned reg) const;
115116
bool IsSVEZReg(unsigned reg) const;

lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/TestSVERegisters.py

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,51 @@ def check_sve_register_size(self, set, name, expected):
1717
self.assertEqual(reg_value.GetByteSize(), expected,
1818
'Verify "%s" == %i' % (name, expected))
1919

20+
def check_sve_regs_read(self, z_reg_size):
21+
p_reg_size = int(z_reg_size / 8)
22+
23+
for i in range(32):
24+
z_regs_value = '{' + \
25+
' '.join('0x{:02x}'.format(i + 1)
26+
for _ in range(z_reg_size)) + '}'
27+
self.expect("register read " + 'z%i' %
28+
(i), substrs=[z_regs_value])
29+
30+
p_value_bytes = ['0xff', '0x55', '0x11', '0x01', '0x00']
31+
for i in range(16):
32+
p_regs_value = '{' + \
33+
' '.join(p_value_bytes[i % 5] for _ in range(p_reg_size)) + '}'
34+
self.expect("register read " + 'p%i' % (i), substrs=[p_regs_value])
35+
36+
self.expect("register read ffr", substrs=[p_regs_value])
37+
38+
def check_sve_regs_read_after_write(self, z_reg_size):
39+
p_reg_size = int(z_reg_size / 8)
40+
41+
z_regs_value = '{' + \
42+
' '.join(('0x9d' for _ in range(z_reg_size))) + '}'
43+
44+
p_regs_value = '{' + \
45+
' '.join(('0xee' for _ in range(p_reg_size))) + '}'
46+
47+
for i in range(32):
48+
self.runCmd('register write ' + 'z%i' %
49+
(i) + " '" + z_regs_value + "'")
50+
51+
for i in range(32):
52+
self.expect("register read " + 'z%i' % (i), substrs=[z_regs_value])
53+
54+
for i in range(16):
55+
self.runCmd('register write ' + 'p%i' %
56+
(i) + " '" + p_regs_value + "'")
57+
58+
for i in range(16):
59+
self.expect("register read " + 'p%i' % (i), substrs=[p_regs_value])
60+
61+
self.runCmd('register write ' + 'ffr ' + "'" + p_regs_value + "'")
62+
63+
self.expect("register read " + 'ffr', substrs=[p_regs_value])
64+
2065
mydir = TestBase.compute_mydir(__file__)
2166

2267
@no_debug_info_test
@@ -117,43 +162,17 @@ def test_sve_registers_read_write(self):
117162

118163
z_reg_size = vg_reg_value * 8
119164

120-
p_reg_size = int(z_reg_size / 8)
121-
122-
for i in range(32):
123-
z_regs_value = '{' + \
124-
' '.join('0x{:02x}'.format(i + 1)
125-
for _ in range(z_reg_size)) + '}'
126-
self.expect("register read " + 'z%i' %
127-
(i), substrs=[z_regs_value])
165+
self.check_sve_regs_read(z_reg_size)
128166

129-
p_value_bytes = ['0xff', '0x55', '0x11', '0x01', '0x00']
130-
for i in range(16):
131-
p_regs_value = '{' + \
132-
' '.join(p_value_bytes[i % 5] for _ in range(p_reg_size)) + '}'
133-
self.expect("register read " + 'p%i' % (i), substrs=[p_regs_value])
167+
# Evaluate simple expression and print function expr_eval_func address.
168+
self.expect("p expr_eval_func", substrs=["= 0x"])
134169

135-
self.expect("register read ffr", substrs=[p_regs_value])
170+
# Evaluate expression call function expr_eval_func.
171+
self.expect_expr("expr_eval_func()",
172+
result_type="int", result_value="1")
136173

137-
z_regs_value = '{' + \
138-
' '.join(('0x9d' for _ in range(z_reg_size))) + '}'
174+
# We called a jitted function above which must not have changed SVE
175+
# vector length or register values.
176+
self.check_sve_regs_read(z_reg_size)
139177

140-
p_regs_value = '{' + \
141-
' '.join(('0xee' for _ in range(p_reg_size))) + '}'
142-
143-
for i in range(32):
144-
self.runCmd('register write ' + 'z%i' %
145-
(i) + " '" + z_regs_value + "'")
146-
147-
for i in range(32):
148-
self.expect("register read " + 'z%i' % (i), substrs=[z_regs_value])
149-
150-
for i in range(16):
151-
self.runCmd('register write ' + 'p%i' %
152-
(i) + " '" + p_regs_value + "'")
153-
154-
for i in range(16):
155-
self.expect("register read " + 'p%i' % (i), substrs=[p_regs_value])
156-
157-
self.runCmd('register write ' + 'ffr ' + "'" + p_regs_value + "'")
158-
159-
self.expect("register read " + 'ffr', substrs=[p_regs_value])
178+
self.check_sve_regs_read_after_write(z_reg_size)

lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_static_config/main.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
int main() {
1+
#include <sys/prctl.h>
2+
3+
void write_sve_regs() {
24
asm volatile("setffr\n\t");
35
asm volatile("ptrue p0.b\n\t");
46
asm volatile("ptrue p1.h\n\t");
@@ -49,5 +51,20 @@ int main() {
4951
asm volatile("cpy z29.b, p5/z, #30\n\t");
5052
asm volatile("cpy z30.b, p10/z, #31\n\t");
5153
asm volatile("cpy z31.b, p15/z, #32\n\t");
54+
}
55+
56+
// This function will be called using jitted expression call. We change vector
57+
// length and write SVE registers. Our program context should restore to
58+
// orignal vector length and register values after expression evaluation.
59+
int expr_eval_func() {
60+
prctl(PR_SVE_SET_VL, 8 * 2);
61+
write_sve_regs();
62+
prctl(PR_SVE_SET_VL, 8 * 4);
63+
write_sve_regs();
64+
return 1;
65+
}
66+
67+
int main() {
68+
write_sve_regs();
5269
return 0; // Set a break point here.
5370
}

0 commit comments

Comments
 (0)