Skip to content

Commit 5ee2dea

Browse files
[lldb][AArch64][Linux] Add Floating Point Mode Register (#106695)
Otherwise known as FEAT_FPMR. This register controls the behaviour of floating point operations. https://developer.arm.com/documentation/ddi0601/2024-06/AArch64-Registers/FPMR--Floating-point-Mode-Register As the current floating point register contexts are fixed size, this has been placed in a new set. Linux kernel patches have landed already, so you can cross check with those. To simplify testing we're not going to do any floating point operations, just read and write from the program and debugger to make sure each sees the other's values correctly.
1 parent 02f46d7 commit 5ee2dea

File tree

10 files changed

+252
-2
lines changed

10 files changed

+252
-2
lines changed

lldb/packages/Python/lldbsuite/test/lldbtest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,6 +1370,9 @@ def isAArch64PAuth(self):
13701370
return True
13711371
return self.isAArch64() and "paca" in self.getCPUInfo()
13721372

1373+
def isAArch64FPMR(self):
1374+
return self.isAArch64() and "fpmr" in self.getCPUInfo()
1375+
13731376
def isAArch64Windows(self):
13741377
"""Returns true if the architecture is AArch64 and platform windows."""
13751378
if self.getPlatform() == "windows":

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

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,16 @@
6060
#define NT_ARM_TAGGED_ADDR_CTRL 0x409 /* Tagged address control register */
6161
#endif
6262

63+
#ifndef NT_ARM_FPMR
64+
#define NT_ARM_FPMR 0x40e /* Floating point mode register */
65+
#endif
66+
6367
#define HWCAP_PACA (1 << 30)
6468

6569
#define HWCAP2_MTE (1 << 18)
6670

71+
#define HWCAP2_FPMR (1UL << 48)
72+
6773
using namespace lldb;
6874
using namespace lldb_private;
6975
using namespace lldb_private::process_linux;
@@ -139,8 +145,12 @@ NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
139145

140146
std::optional<uint64_t> auxv_at_hwcap2 =
141147
process.GetAuxValue(AuxVector::AUXV_AT_HWCAP2);
142-
if (auxv_at_hwcap2 && (*auxv_at_hwcap2 & HWCAP2_MTE))
143-
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskMTE);
148+
if (auxv_at_hwcap2) {
149+
if (*auxv_at_hwcap2 & HWCAP2_MTE)
150+
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskMTE);
151+
if (*auxv_at_hwcap2 & HWCAP2_FPMR)
152+
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskFPMR);
153+
}
144154

145155
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskTLS);
146156

@@ -186,6 +196,7 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
186196
std::fill(m_zt_reg.begin(), m_zt_reg.end(), 0);
187197

188198
m_mte_ctrl_reg = 0;
199+
m_fpmr_reg = 0;
189200

190201
// 16 is just a maximum value, query hardware for actual watchpoint count
191202
m_max_hwp_supported = 16;
@@ -201,6 +212,7 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
201212
m_mte_ctrl_is_valid = false;
202213
m_tls_is_valid = false;
203214
m_zt_buffer_is_valid = false;
215+
m_fpmr_is_valid = false;
204216

205217
// SME adds the tpidr2 register
206218
m_tls_size = GetRegisterInfo().IsSSVEPresent() ? sizeof(m_tls_regs)
@@ -413,6 +425,14 @@ NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info,
413425
assert(offset < GetSMEPseudoBufferSize());
414426
src = (uint8_t *)GetSMEPseudoBuffer() + offset;
415427
}
428+
} else if (IsFPMR(reg)) {
429+
error = ReadFPMR();
430+
if (error.Fail())
431+
return error;
432+
433+
offset = reg_info->byte_offset - GetRegisterInfo().GetFPMROffset();
434+
assert(offset < GetFPMRBufferSize());
435+
src = (uint8_t *)GetFPMRBuffer() + offset;
416436
} else
417437
return Status::FromErrorString(
418438
"failed - register wasn't recognized to be a GPR or an FPR, "
@@ -626,6 +646,17 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
626646
} else
627647
return Status::FromErrorString(
628648
"Writing to SVG or SVCR is not supported.");
649+
} else if (IsFPMR(reg)) {
650+
error = ReadFPMR();
651+
if (error.Fail())
652+
return error;
653+
654+
offset = reg_info->byte_offset - GetRegisterInfo().GetFPMROffset();
655+
assert(offset < GetFPMRBufferSize());
656+
dst = (uint8_t *)GetFPMRBuffer() + offset;
657+
::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
658+
659+
return WriteFPMR();
629660
}
630661

631662
return Status::FromErrorString("Failed to write register value");
@@ -640,6 +671,7 @@ enum RegisterSetType : uint32_t {
640671
TLS,
641672
SME, // ZA only, because SVCR and SVG are pseudo registers.
642673
SME2, // ZT only.
674+
FPMR,
643675
};
644676

645677
static uint8_t *AddRegisterSetType(uint8_t *dst,
@@ -720,6 +752,13 @@ NativeRegisterContextLinux_arm64::CacheAllRegisters(uint32_t &cached_size) {
720752
return error;
721753
}
722754

755+
if (GetRegisterInfo().IsFPMRPresent()) {
756+
cached_size += sizeof(RegisterSetType) + GetFPMRBufferSize();
757+
error = ReadFPMR();
758+
if (error.Fail())
759+
return error;
760+
}
761+
723762
// tpidr is always present but tpidr2 depends on SME.
724763
cached_size += sizeof(RegisterSetType) + GetTLSBufferSize();
725764
error = ReadTLS();
@@ -823,6 +862,11 @@ Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
823862
GetMTEControlSize());
824863
}
825864

865+
if (GetRegisterInfo().IsFPMRPresent()) {
866+
dst = AddSavedRegisters(dst, RegisterSetType::FPMR, GetFPMRBuffer(),
867+
GetFPMRBufferSize());
868+
}
869+
826870
dst = AddSavedRegisters(dst, RegisterSetType::TLS, GetTLSBuffer(),
827871
GetTLSBufferSize());
828872

@@ -971,6 +1015,11 @@ Status NativeRegisterContextLinux_arm64::WriteAllRegisterValues(
9711015
GetZTBuffer(), &src, GetZTBufferSize(), m_zt_buffer_is_valid,
9721016
std::bind(&NativeRegisterContextLinux_arm64::WriteZT, this));
9731017
break;
1018+
case RegisterSetType::FPMR:
1019+
error = RestoreRegisters(
1020+
GetFPMRBuffer(), &src, GetFPMRBufferSize(), m_fpmr_is_valid,
1021+
std::bind(&NativeRegisterContextLinux_arm64::WriteFPMR, this));
1022+
break;
9741023
}
9751024

9761025
if (error.Fail())
@@ -1014,6 +1063,10 @@ bool NativeRegisterContextLinux_arm64::IsTLS(unsigned reg) const {
10141063
return GetRegisterInfo().IsTLSReg(reg);
10151064
}
10161065

1066+
bool NativeRegisterContextLinux_arm64::IsFPMR(unsigned reg) const {
1067+
return GetRegisterInfo().IsFPMRReg(reg);
1068+
}
1069+
10171070
llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() {
10181071
if (!m_refresh_hwdebug_info) {
10191072
return llvm::Error::success();
@@ -1161,6 +1214,7 @@ void NativeRegisterContextLinux_arm64::InvalidateAllRegisters() {
11611214
m_mte_ctrl_is_valid = false;
11621215
m_tls_is_valid = false;
11631216
m_zt_buffer_is_valid = false;
1217+
m_fpmr_is_valid = false;
11641218

11651219
// Update SVE and ZA registers in case there is change in configuration.
11661220
ConfigureRegisterContext();
@@ -1440,6 +1494,40 @@ Status NativeRegisterContextLinux_arm64::WriteZT() {
14401494
return WriteRegisterSet(&ioVec, GetZTBufferSize(), NT_ARM_ZT);
14411495
}
14421496

1497+
Status NativeRegisterContextLinux_arm64::ReadFPMR() {
1498+
Status error;
1499+
1500+
if (m_fpmr_is_valid)
1501+
return error;
1502+
1503+
struct iovec ioVec;
1504+
ioVec.iov_base = GetFPMRBuffer();
1505+
ioVec.iov_len = GetFPMRBufferSize();
1506+
1507+
error = ReadRegisterSet(&ioVec, GetFPMRBufferSize(), NT_ARM_FPMR);
1508+
1509+
if (error.Success())
1510+
m_fpmr_is_valid = true;
1511+
1512+
return error;
1513+
}
1514+
1515+
Status NativeRegisterContextLinux_arm64::WriteFPMR() {
1516+
Status error;
1517+
1518+
error = ReadFPMR();
1519+
if (error.Fail())
1520+
return error;
1521+
1522+
struct iovec ioVec;
1523+
ioVec.iov_base = GetFPMRBuffer();
1524+
ioVec.iov_len = GetFPMRBufferSize();
1525+
1526+
m_fpmr_is_valid = false;
1527+
1528+
return WriteRegisterSet(&ioVec, GetFPMRBufferSize(), NT_ARM_FPMR);
1529+
}
1530+
14431531
void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() {
14441532
// ConfigureRegisterContext gets called from InvalidateAllRegisters
14451533
// on every stop and configures SVE vector length and whether we are in

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class NativeRegisterContextLinux_arm64
8484
bool m_sve_buffer_is_valid;
8585
bool m_mte_ctrl_is_valid;
8686
bool m_zt_buffer_is_valid;
87+
bool m_fpmr_is_valid;
8788

8889
bool m_sve_header_is_valid;
8990
bool m_za_buffer_is_valid;
@@ -133,6 +134,8 @@ class NativeRegisterContextLinux_arm64
133134
// SME2's ZT is a 512 bit register.
134135
std::array<uint8_t, 64> m_zt_reg;
135136

137+
uint64_t m_fpmr_reg;
138+
136139
bool IsGPR(unsigned reg) const;
137140

138141
bool IsFPR(unsigned reg) const;
@@ -174,11 +177,16 @@ class NativeRegisterContextLinux_arm64
174177
// SVCR is a pseudo register and we do not allow writes to it.
175178
Status ReadSMEControl();
176179

180+
Status ReadFPMR();
181+
182+
Status WriteFPMR();
183+
177184
bool IsSVE(unsigned reg) const;
178185
bool IsSME(unsigned reg) const;
179186
bool IsPAuth(unsigned reg) const;
180187
bool IsMTE(unsigned reg) const;
181188
bool IsTLS(unsigned reg) const;
189+
bool IsFPMR(unsigned reg) const;
182190

183191
uint64_t GetSVERegVG() { return m_sve_header.vl / 8; }
184192

@@ -202,6 +210,8 @@ class NativeRegisterContextLinux_arm64
202210

203211
void *GetSVEBuffer() { return m_sve_ptrace_payload.data(); }
204212

213+
void *GetFPMRBuffer() { return &m_fpmr_reg; }
214+
205215
size_t GetSVEHeaderSize() { return sizeof(m_sve_header); }
206216

207217
size_t GetPACMaskSize() { return sizeof(m_pac_mask); }
@@ -222,6 +232,8 @@ class NativeRegisterContextLinux_arm64
222232

223233
size_t GetZTBufferSize() { return m_zt_reg.size(); }
224234

235+
size_t GetFPMRBufferSize() { return sizeof(m_fpmr_reg); }
236+
225237
llvm::Error ReadHardwareDebugInfo() override;
226238

227239
llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override;

lldb/source/Plugins/Process/Utility/RegisterContextPOSIX_arm64.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ bool RegisterContextPOSIX_arm64::IsMTE(unsigned reg) const {
5959
return m_register_info_up->IsMTEReg(reg);
6060
}
6161

62+
bool RegisterContextPOSIX_arm64::IsFPMR(unsigned reg) const {
63+
return m_register_info_up->IsFPMRReg(reg);
64+
}
65+
6266
RegisterContextPOSIX_arm64::RegisterContextPOSIX_arm64(
6367
lldb_private::Thread &thread,
6468
std::unique_ptr<RegisterInfoPOSIX_arm64> register_info)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class RegisterContextPOSIX_arm64 : public lldb_private::RegisterContext {
5858
bool IsTLS(unsigned reg) const;
5959
bool IsSME(unsigned reg) const;
6060
bool IsMTE(unsigned reg) const;
61+
bool IsFPMR(unsigned reg) const;
6162

6263
bool IsSVEZ(unsigned reg) const { return m_register_info_up->IsSVEZReg(reg); }
6364
bool IsSVEP(unsigned reg) const { return m_register_info_up->IsSVEPReg(reg); }

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ static lldb_private::RegisterInfo g_register_infos_sme2[] = {
9494
{"zt0", nullptr, 64, 0, lldb::eEncodingVector, lldb::eFormatVectorOfUInt8,
9595
KIND_ALL_INVALID, nullptr, nullptr, nullptr}};
9696

97+
static lldb_private::RegisterInfo g_register_infos_fpmr[] = {
98+
DEFINE_EXTENSION_REG(fpmr)};
99+
97100
// Number of register sets provided by this context.
98101
enum {
99102
k_num_gpr_registers = gpr_w28 - gpr_x0 + 1,
@@ -105,6 +108,7 @@ enum {
105108
// SME2's ZT0 will also be added to this set if present. So this number is
106109
// only for SME1 registers.
107110
k_num_sme_register = 3,
111+
k_num_fpmr_register = 1,
108112
k_num_register_sets_default = 2,
109113
k_num_register_sets = 3
110114
};
@@ -214,6 +218,9 @@ static const lldb_private::RegisterSet g_reg_set_mte_arm64 = {
214218
static const lldb_private::RegisterSet g_reg_set_sme_arm64 = {
215219
"Scalable Matrix Extension Registers", "sme", k_num_sme_register, nullptr};
216220

221+
static const lldb_private::RegisterSet g_reg_set_fpmr_arm64 = {
222+
"Floating Point Mode Register", "fpmr", k_num_fpmr_register, nullptr};
223+
217224
RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
218225
const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets)
219226
: lldb_private::RegisterInfoAndSetInterface(target_arch),
@@ -263,6 +270,9 @@ RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
263270
if (m_opt_regsets.AnySet(eRegsetMaskSSVE))
264271
AddRegSetSME(m_opt_regsets.AnySet(eRegsetMaskZT));
265272

273+
if (m_opt_regsets.AllSet(eRegsetMaskFPMR))
274+
AddRegSetFPMR();
275+
266276
m_register_info_count = m_dynamic_reg_infos.size();
267277
m_register_info_p = m_dynamic_reg_infos.data();
268278
m_register_set_p = m_dynamic_reg_sets.data();
@@ -409,6 +419,21 @@ void RegisterInfoPOSIX_arm64::AddRegSetSME(bool has_zt) {
409419
m_dynamic_reg_infos[GetRegNumSVEVG()].invalidate_regs = vg_invalidates;
410420
}
411421

422+
void RegisterInfoPOSIX_arm64::AddRegSetFPMR() {
423+
uint32_t fpmr_regnum = m_dynamic_reg_infos.size();
424+
m_fpmr_regnum_collection.push_back(fpmr_regnum);
425+
m_dynamic_reg_infos.push_back(g_register_infos_fpmr[0]);
426+
m_dynamic_reg_infos[fpmr_regnum].byte_offset =
427+
m_dynamic_reg_infos[fpmr_regnum - 1].byte_offset +
428+
m_dynamic_reg_infos[fpmr_regnum - 1].byte_size;
429+
m_dynamic_reg_infos[fpmr_regnum].kinds[lldb::eRegisterKindLLDB] = fpmr_regnum;
430+
431+
m_per_regset_regnum_range[m_register_set_count] =
432+
std::make_pair(fpmr_regnum, fpmr_regnum + 1);
433+
m_dynamic_reg_sets.push_back(g_reg_set_fpmr_arm64);
434+
m_dynamic_reg_sets.back().registers = m_fpmr_regnum_collection.data();
435+
}
436+
412437
uint32_t RegisterInfoPOSIX_arm64::ConfigureVectorLengthSVE(uint32_t sve_vq) {
413438
// sve_vq contains SVE Quad vector length in context of AArch64 SVE.
414439
// SVE register infos if enabled cannot be disabled by selecting sve_vq = 0.
@@ -532,6 +557,10 @@ bool RegisterInfoPOSIX_arm64::IsSMEReg(unsigned reg) const {
532557
return llvm::is_contained(m_sme_regnum_collection, reg);
533558
}
534559

560+
bool RegisterInfoPOSIX_arm64::IsFPMRReg(unsigned reg) const {
561+
return llvm::is_contained(m_fpmr_regnum_collection, reg);
562+
}
563+
535564
uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEZ0() const { return sve_z0; }
536565

537566
uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEFFR() const { return sve_ffr; }
@@ -561,3 +590,7 @@ uint32_t RegisterInfoPOSIX_arm64::GetTLSOffset() const {
561590
uint32_t RegisterInfoPOSIX_arm64::GetSMEOffset() const {
562591
return m_register_info_p[m_sme_regnum_collection[0]].byte_offset;
563592
}
593+
594+
uint32_t RegisterInfoPOSIX_arm64::GetFPMROffset() const {
595+
return m_register_info_p[m_fpmr_regnum_collection[0]].byte_offset;
596+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class RegisterInfoPOSIX_arm64
3232
eRegsetMaskTLS = 16,
3333
eRegsetMaskZA = 32,
3434
eRegsetMaskZT = 64,
35+
eRegsetMaskFPMR = 128,
3536
eRegsetMaskDynamic = ~1,
3637
};
3738

@@ -110,6 +111,8 @@ class RegisterInfoPOSIX_arm64
110111

111112
void AddRegSetSME(bool has_zt);
112113

114+
void AddRegSetFPMR();
115+
113116
uint32_t ConfigureVectorLengthSVE(uint32_t sve_vq);
114117

115118
void ConfigureVectorLengthZA(uint32_t za_vq);
@@ -128,6 +131,7 @@ class RegisterInfoPOSIX_arm64
128131
bool IsPAuthPresent() const { return m_opt_regsets.AnySet(eRegsetMaskPAuth); }
129132
bool IsMTEPresent() const { return m_opt_regsets.AnySet(eRegsetMaskMTE); }
130133
bool IsTLSPresent() const { return m_opt_regsets.AnySet(eRegsetMaskTLS); }
134+
bool IsFPMRPresent() const { return m_opt_regsets.AnySet(eRegsetMaskFPMR); }
131135

132136
bool IsSVEReg(unsigned reg) const;
133137
bool IsSVEZReg(unsigned reg) const;
@@ -139,6 +143,7 @@ class RegisterInfoPOSIX_arm64
139143
bool IsSMEReg(unsigned reg) const;
140144
bool IsSMERegZA(unsigned reg) const;
141145
bool IsSMERegZT(unsigned reg) const;
146+
bool IsFPMRReg(unsigned reg) const;
142147

143148
uint32_t GetRegNumSVEZ0() const;
144149
uint32_t GetRegNumSVEFFR() const;
@@ -150,6 +155,7 @@ class RegisterInfoPOSIX_arm64
150155
uint32_t GetMTEOffset() const;
151156
uint32_t GetTLSOffset() const;
152157
uint32_t GetSMEOffset() const;
158+
uint32_t GetFPMROffset() const;
153159

154160
private:
155161
typedef std::map<uint32_t, std::vector<lldb_private::RegisterInfo>>
@@ -181,6 +187,7 @@ class RegisterInfoPOSIX_arm64
181187
std::vector<uint32_t> m_mte_regnum_collection;
182188
std::vector<uint32_t> m_tls_regnum_collection;
183189
std::vector<uint32_t> m_sme_regnum_collection;
190+
std::vector<uint32_t> m_fpmr_regnum_collection;
184191
};
185192

186193
#endif

0 commit comments

Comments
 (0)