mirror of
https://github.com/intel/llvm.git
synced 2026-01-16 05:32:28 +08:00
[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.
This commit is contained in:
3
lldb/test/API/linux/aarch64/fpmr/Makefile
Normal file
3
lldb/test/API/linux/aarch64/fpmr/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
C_SOURCES := main.c
|
||||
|
||||
include Makefile.rules
|
||||
58
lldb/test/API/linux/aarch64/fpmr/TestAArch64LinuxFPMR.py
Normal file
58
lldb/test/API/linux/aarch64/fpmr/TestAArch64LinuxFPMR.py
Normal file
@@ -0,0 +1,58 @@
|
||||
"""
|
||||
Test lldb's ability to read and write the AArch64 FPMR register.
|
||||
"""
|
||||
|
||||
import lldb
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test import lldbutil
|
||||
|
||||
|
||||
class AArch64LinuxFPMR(TestBase):
|
||||
NO_DEBUG_INFO_TESTCASE = True
|
||||
|
||||
@skipUnlessArch("aarch64")
|
||||
@skipUnlessPlatform(["linux"])
|
||||
def test_fpmr_register(self):
|
||||
if not self.isAArch64FPMR():
|
||||
self.skipTest("FPMR must be present.")
|
||||
|
||||
self.build()
|
||||
self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
|
||||
|
||||
lldbutil.run_break_set_by_file_and_line(
|
||||
self,
|
||||
"main.c",
|
||||
line_number("main.c", "// Set break point at this line."),
|
||||
num_expected_locations=1,
|
||||
)
|
||||
|
||||
self.runCmd("run", RUN_SUCCEEDED)
|
||||
|
||||
if self.process().GetState() == lldb.eStateExited:
|
||||
self.fail("Test program failed to run.")
|
||||
|
||||
self.expect(
|
||||
"thread list",
|
||||
STOPPED_DUE_TO_BREAKPOINT,
|
||||
substrs=["stopped", "stop reason = breakpoint"],
|
||||
)
|
||||
|
||||
# This has been set by the program.
|
||||
expected_fpmr = (0b101010 << 32) | 0b101
|
||||
self.expect(
|
||||
"register read --all",
|
||||
substrs=["Floating Point Mode Register", f"fpmr = {expected_fpmr:#018x}"],
|
||||
)
|
||||
|
||||
# Write a value for the program to find. Same fields but with bit values
|
||||
# inverted.
|
||||
new_fpmr = (0b010101 << 32) | 0b010
|
||||
self.runCmd(f"register write fpmr {new_fpmr:#x}")
|
||||
|
||||
# This value should be saved and restored after expressions.
|
||||
self.runCmd("p expr_func()")
|
||||
self.expect("register read fpmr", substrs=[f"fpmr = {new_fpmr:#018x}"])
|
||||
|
||||
# 0 means the program found the new value in the sysreg as expected.
|
||||
self.expect("continue", substrs=["exited with status = 0"])
|
||||
41
lldb/test/API/linux/aarch64/fpmr/main.c
Normal file
41
lldb/test/API/linux/aarch64/fpmr/main.c
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <asm/hwcap.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/auxv.h>
|
||||
|
||||
#ifndef HWCAP2_FPMR
|
||||
#define HWCAP2_FPMR (1UL << 48)
|
||||
#endif
|
||||
|
||||
uint64_t get_fpmr(void) {
|
||||
uint64_t fpmr = 0;
|
||||
__asm__ volatile("mrs %0, s3_3_c4_c4_2" : "=r"(fpmr));
|
||||
return fpmr;
|
||||
}
|
||||
|
||||
void set_fpmr(uint64_t value) {
|
||||
__asm__ volatile("msr s3_3_c4_c4_2, %0" ::"r"(value));
|
||||
}
|
||||
|
||||
// Set F8S1 (bits 0-2) and LSCALE2 (bits 37-32) (to prove we treat fpmr as 64
|
||||
// bit).
|
||||
const uint64_t original_fpmr = (uint64_t)0b101010 << 32 | (uint64_t)0b101;
|
||||
|
||||
void expr_func() { set_fpmr(original_fpmr); }
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (!(getauxval(AT_HWCAP2) & HWCAP2_FPMR))
|
||||
return 1;
|
||||
|
||||
// As FPMR controls a bunch of floating point options that are quite
|
||||
// extensive, we're not going to run any floating point ops here. Instead just
|
||||
// update the value from the debugger and check it from this program, and vice
|
||||
// versa.
|
||||
set_fpmr(original_fpmr);
|
||||
|
||||
// Here the debugger checks it read back the value above, then writes in a new
|
||||
// value. Note that the bits are flipped in the new value.
|
||||
uint64_t new_fpmr = get_fpmr(); // Set break point at this line.
|
||||
uint64_t expected_fpmr = ((uint64_t)0b010101 << 32) | (uint64_t)0b010;
|
||||
|
||||
return new_fpmr == expected_fpmr ? 0 : 1;
|
||||
}
|
||||
Reference in New Issue
Block a user