Skip to content

Commit e96adfd

Browse files
committed
[lldb][AArch64] Add testing for SME's ZA and SVG registers
An SME enabled program has the following extra state: * Streaming mode or non-streaming mode. * ZA enabled or disabled. * The active vector length. Covering the transition between all possible states and all other possible states is not viable, therefore the testing added here is a cross section of that, all of which found real bugs in LLDB and the Linux Kernel during development. Many of those transitions will not be possible via LLDB (e.g. disabling ZA) and many more are possible but unlikely to be used in normal use. Added testing: * TestSVEThreadedDynamic now checks for correct SVG values. * New test TestZAThreadedDynamic creates 3 threads with different ZA sizes and states and switches between them verifying the register value (derived from the existing threaded SVE test). * New test TestZARegisterSaveRestore starts in a given SME state, runs a set of expressions in various orders, then checks that the original state has been restored. * TestArm64DynamicRegsets has ZA and SVG checks added, including writing to ZA to enable it. Running these tests will as usual require QEMU as there is no real SME hardware available at this time, and a very recent kernel. Reviewed By: omjavaid Differential Revision: https://reviews.llvm.org/D159505
1 parent 46b961f commit e96adfd

File tree

8 files changed

+927
-33
lines changed

8 files changed

+927
-33
lines changed

lldb/test/API/commands/register/register/aarch64_dynamic_regset/TestArm64DynamicRegsets.py

Lines changed: 73 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,13 @@ def sve_regs_read_dynamic(self, sve_registers):
7070
self.runCmd("register write ffr " + "'" + p_regs_value + "'")
7171
self.expect("register read ffr", substrs=[p_regs_value])
7272

73-
@no_debug_info_test
74-
@skipIf(archs=no_match(["aarch64"]))
75-
@skipIf(oslist=no_match(["linux"]))
76-
def test_aarch64_dynamic_regset_config(self):
77-
"""Test AArch64 Dynamic Register sets configuration."""
73+
def setup_register_config_test(self, run_args=None):
7874
self.build()
7975
self.line = line_number("main.c", "// Set a break point here.")
8076

8177
exe = self.getBuildArtifact("a.out")
78+
if run_args is not None:
79+
self.runCmd("settings set target.run-args " + run_args)
8280
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
8381

8482
lldbutil.run_break_set_by_file_and_line(
@@ -92,12 +90,16 @@ def test_aarch64_dynamic_regset_config(self):
9290
substrs=["stop reason = breakpoint 1."],
9391
)
9492

95-
target = self.dbg.GetSelectedTarget()
96-
process = target.GetProcess()
97-
thread = process.GetThreadAtIndex(0)
98-
currentFrame = thread.GetFrameAtIndex(0)
93+
return self.thread().GetSelectedFrame().GetRegisters()
94+
95+
@no_debug_info_test
96+
@skipIf(archs=no_match(["aarch64"]))
97+
@skipIf(oslist=no_match(["linux"]))
98+
def test_aarch64_dynamic_regset_config(self):
99+
"""Test AArch64 Dynamic Register sets configuration."""
100+
register_sets = self.setup_register_config_test()
99101

100-
for registerSet in currentFrame.GetRegisters():
102+
for registerSet in register_sets:
101103
if "Scalable Vector Extension Registers" in registerSet.GetName():
102104
self.assertTrue(
103105
self.isAArch64SVE(),
@@ -120,6 +122,19 @@ def test_aarch64_dynamic_regset_config(self):
120122
)
121123
self.expect("register read data_mask", substrs=["data_mask = 0x"])
122124
self.expect("register read code_mask", substrs=["code_mask = 0x"])
125+
if "Scalable Matrix Extension Registers" in registerSet.GetName():
126+
self.assertTrue(
127+
self.isAArch64SME(),
128+
"LLDB Enabled SME register set when it was disabled by target",
129+
)
130+
131+
def make_za_value(self, vl, generator):
132+
# Generate a vector value string "{0x00 0x01....}".
133+
rows = []
134+
for row in range(vl):
135+
byte = "0x{:02x}".format(generator(row))
136+
rows.append(" ".join([byte] * vl))
137+
return "{" + " ".join(rows) + "}"
123138

124139
@no_debug_info_test
125140
@skipIf(archs=no_match(["aarch64"]))
@@ -130,28 +145,58 @@ def test_aarch64_dynamic_regset_config_sme(self):
130145
if not self.isAArch64SME():
131146
self.skipTest("SME must be present.")
132147

133-
self.build()
134-
self.line = line_number("main.c", "// Set a break point here.")
148+
register_sets = self.setup_register_config_test("sme")
135149

136-
exe = self.getBuildArtifact("a.out")
137-
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
138-
139-
lldbutil.run_break_set_by_file_and_line(
140-
self, "main.c", self.line, num_expected_locations=1
150+
ssve_registers = register_sets.GetFirstValueByName(
151+
"Scalable Vector Extension Registers"
141152
)
142-
self.runCmd("settings set target.run-args sme")
143-
self.runCmd("run", RUN_SUCCEEDED)
153+
self.assertTrue(ssve_registers.IsValid())
154+
self.sve_regs_read_dynamic(ssve_registers)
144155

145-
self.expect(
146-
"thread backtrace",
147-
STOPPED_DUE_TO_BREAKPOINT,
148-
substrs=["stop reason = breakpoint 1."],
156+
sme_registers = register_sets.GetFirstValueByName(
157+
"Scalable Matrix Extension Registers"
149158
)
159+
self.assertTrue(sme_registers.IsValid())
150160

151-
register_sets = self.thread().GetSelectedFrame().GetRegisters()
161+
vg = ssve_registers.GetChildMemberWithName("vg").GetValueAsUnsigned()
162+
vl = vg * 8
163+
# When first enabled it is all 0s.
164+
self.expect("register read za", substrs=[self.make_za_value(vl, lambda r: 0)])
165+
za_value = self.make_za_value(vl, lambda r: r + 1)
166+
self.runCmd("register write za '{}'".format(za_value))
167+
self.expect("register read za", substrs=[za_value])
152168

153-
ssve_registers = register_sets.GetFirstValueByName(
154-
"Scalable Vector Extension Registers"
169+
# SVG should match VG because we're in streaming mode.
170+
171+
self.assertTrue(sme_registers.IsValid())
172+
svg = sme_registers.GetChildMemberWithName("svg").GetValueAsUnsigned()
173+
self.assertEqual(vg, svg)
174+
175+
@no_debug_info_test
176+
@skipIf(archs=no_match(["aarch64"]))
177+
@skipIf(oslist=no_match(["linux"]))
178+
def test_aarch64_dynamic_regset_config_sme_za_disabled(self):
179+
"""Test that ZA shows as 0s when disabled and can be enabled by writing
180+
to it."""
181+
if not self.isAArch64SME():
182+
self.skipTest("SME must be present.")
183+
184+
# No argument, so ZA will be disabled when we break.
185+
register_sets = self.setup_register_config_test()
186+
187+
# vg is the non-streaming vg as we are in non-streaming mode, so we need
188+
# to use svg.
189+
sme_registers = register_sets.GetFirstValueByName(
190+
"Scalable Matrix Extension Registers"
155191
)
156-
self.assertTrue(ssve_registers.IsValid())
157-
self.sve_regs_read_dynamic(ssve_registers)
192+
self.assertTrue(sme_registers.IsValid())
193+
svg = sme_registers.GetChildMemberWithName("svg").GetValueAsUnsigned()
194+
195+
svl = svg * 8
196+
# A disabled ZA is shown as all 0s.
197+
self.expect("register read za", substrs=[self.make_za_value(svl, lambda r: 0)])
198+
za_value = self.make_za_value(svl, lambda r: r + 1)
199+
# Writing to it enables ZA, so the value should be there when we read
200+
# it back.
201+
self.runCmd("register write za '{}'".format(za_value))
202+
self.expect("register read za", substrs=[za_value])

lldb/test/API/commands/register/register/aarch64_sve_registers/rw_access_dynamic_resize/TestSVEThreadedDynamic.py

Lines changed: 98 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,19 +98,21 @@ def check_sve_registers(self, vg_test_value):
9898

9999
self.expect("register read ffr", substrs=[p_regs_value])
100100

101+
def build_for_mode(self, mode):
102+
cflags = "-march=armv8-a+sve -lpthread"
103+
if mode == Mode.SSVE:
104+
cflags += " -DUSE_SSVE"
105+
self.build(dictionary={"CFLAGS_EXTRAS": cflags})
106+
101107
def run_sve_test(self, mode):
102108
if (mode == Mode.SVE) and not self.isAArch64SVE():
103109
self.skipTest("SVE registers must be supported.")
104110

105111
if (mode == Mode.SSVE) and not self.isAArch64SME():
106112
self.skipTest("Streaming SVE registers must be supported.")
107113

108-
cflags = "-march=armv8-a+sve -lpthread"
109-
if mode == Mode.SSVE:
110-
cflags += " -DUSE_SSVE"
111-
self.build(dictionary={"CFLAGS_EXTRAS": cflags})
114+
self.build_for_mode(mode)
112115

113-
self.build()
114116
supported_vg = self.get_supported_vg()
115117

116118
if not (2 in supported_vg and 4 in supported_vg):
@@ -196,3 +198,94 @@ def test_sve_registers_dynamic_config(self):
196198
def test_ssve_registers_dynamic_config(self):
197199
"""Test AArch64 SSVE registers multi-threaded dynamic resize."""
198200
self.run_sve_test(Mode.SSVE)
201+
202+
def setup_svg_test(self, mode):
203+
# Even when running in SVE mode, we need access to SVG for these tests.
204+
if not self.isAArch64SME():
205+
self.skipTest("Streaming SVE registers must be present.")
206+
207+
self.build_for_mode(mode)
208+
209+
supported_vg = self.get_supported_vg()
210+
211+
main_thread_stop_line = line_number("main.c", "// Break in main thread")
212+
lldbutil.run_break_set_by_file_and_line(self, "main.c", main_thread_stop_line)
213+
214+
self.runCmd("run", RUN_SUCCEEDED)
215+
216+
self.expect(
217+
"thread info 1",
218+
STOPPED_DUE_TO_BREAKPOINT,
219+
substrs=["stop reason = breakpoint"],
220+
)
221+
222+
target = self.dbg.GetSelectedTarget()
223+
process = target.GetProcess()
224+
225+
return process, supported_vg
226+
227+
def read_reg(self, process, regset, reg):
228+
registerSets = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters()
229+
sve_registers = registerSets.GetFirstValueByName(regset)
230+
return sve_registers.GetChildMemberWithName(reg).GetValueAsUnsigned()
231+
232+
def read_vg(self, process):
233+
return self.read_reg(process, "Scalable Vector Extension Registers", "vg")
234+
235+
def read_svg(self, process):
236+
return self.read_reg(process, "Scalable Matrix Extension Registers", "svg")
237+
238+
def do_svg_test(self, process, vgs, expected_svgs):
239+
for vg, svg in zip(vgs, expected_svgs):
240+
self.runCmd("register write vg {}".format(vg))
241+
self.assertEqual(svg, self.read_svg(process))
242+
243+
@no_debug_info_test
244+
@skipIf(archs=no_match(["aarch64"]))
245+
@skipIf(oslist=no_match(["linux"]))
246+
def test_svg_sve_mode(self):
247+
"""When in SVE mode, svg should remain constant as we change vg."""
248+
process, supported_vg = self.setup_svg_test(Mode.SVE)
249+
svg = self.read_svg(process)
250+
self.do_svg_test(process, supported_vg, [svg] * len(supported_vg))
251+
252+
@no_debug_info_test
253+
@skipIf(archs=no_match(["aarch64"]))
254+
@skipIf(oslist=no_match(["linux"]))
255+
def test_svg_ssve_mode(self):
256+
"""When in SSVE mode, changing vg should change svg to the same value."""
257+
process, supported_vg = self.setup_svg_test(Mode.SSVE)
258+
self.do_svg_test(process, supported_vg, supported_vg)
259+
260+
@no_debug_info_test
261+
@skipIf(archs=no_match(["aarch64"]))
262+
@skipIf(oslist=no_match(["linux"]))
263+
def test_sme_not_present(self):
264+
"""When there is no SME, we should not show the SME register sets."""
265+
if self.isAArch64SME():
266+
self.skipTest("Streaming SVE registers must not be present.")
267+
268+
self.build_for_mode(Mode.SVE)
269+
270+
exe = self.getBuildArtifact("a.out")
271+
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
272+
273+
# This test may run on a non-sve system, but we'll stop before any
274+
# SVE instruction would be run.
275+
self.runCmd("b main")
276+
self.runCmd("run", RUN_SUCCEEDED)
277+
278+
self.expect(
279+
"thread info 1",
280+
STOPPED_DUE_TO_BREAKPOINT,
281+
substrs=["stop reason = breakpoint"],
282+
)
283+
284+
target = self.dbg.GetSelectedTarget()
285+
process = target.GetProcess()
286+
287+
registerSets = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters()
288+
sme_registers = registerSets.GetFirstValueByName(
289+
"Scalable Matrix Extension Registers"
290+
)
291+
self.assertFalse(sme_registers.IsValid())
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
C_SOURCES := main.c
2+
3+
CFLAGS_EXTRAS := -march=armv8-a+sve+sme -lpthread
4+
5+
include Makefile.rules

0 commit comments

Comments
 (0)