1*1422f720SNicholas Piggin // SPDX-License-Identifier: GPL-2.0-only
2*1422f720SNicholas Piggin /*
3*1422f720SNicholas Piggin * Test some powerpc instructions
4*1422f720SNicholas Piggin *
5*1422f720SNicholas Piggin * Copyright 2024 Nicholas Piggin, IBM Corp.
6*1422f720SNicholas Piggin */
7*1422f720SNicholas Piggin #include <stdint.h>
8*1422f720SNicholas Piggin #include <libcflat.h>
9*1422f720SNicholas Piggin #include <migrate.h>
10*1422f720SNicholas Piggin #include <asm/processor.h>
11*1422f720SNicholas Piggin #include <asm/time.h>
12*1422f720SNicholas Piggin #include <asm/atomic.h>
13*1422f720SNicholas Piggin #include <asm/setup.h>
14*1422f720SNicholas Piggin #include <asm/barrier.h>
15*1422f720SNicholas Piggin #include <asm/smp.h>
16*1422f720SNicholas Piggin
17*1422f720SNicholas Piggin static bool do_migrate;
18*1422f720SNicholas Piggin static bool do_record;
19*1422f720SNicholas Piggin
20*1422f720SNicholas Piggin #define RSV_SIZE 128
21*1422f720SNicholas Piggin
22*1422f720SNicholas Piggin static uint8_t granule[RSV_SIZE] __attribute((__aligned__(RSV_SIZE)));
23*1422f720SNicholas Piggin
spin_lock(unsigned int * lock)24*1422f720SNicholas Piggin static void spin_lock(unsigned int *lock)
25*1422f720SNicholas Piggin {
26*1422f720SNicholas Piggin unsigned int old;
27*1422f720SNicholas Piggin
28*1422f720SNicholas Piggin asm volatile ("1:"
29*1422f720SNicholas Piggin "lwarx %0,0,%2;"
30*1422f720SNicholas Piggin "cmpwi %0,0;"
31*1422f720SNicholas Piggin "bne 1b;"
32*1422f720SNicholas Piggin "stwcx. %1,0,%2;"
33*1422f720SNicholas Piggin "bne- 1b;"
34*1422f720SNicholas Piggin "lwsync;"
35*1422f720SNicholas Piggin : "=&r"(old) : "r"(1), "r"(lock) : "cr0", "memory");
36*1422f720SNicholas Piggin }
37*1422f720SNicholas Piggin
spin_unlock(unsigned int * lock)38*1422f720SNicholas Piggin static void spin_unlock(unsigned int *lock)
39*1422f720SNicholas Piggin {
40*1422f720SNicholas Piggin asm volatile("lwsync;"
41*1422f720SNicholas Piggin "stw %1,%0;"
42*1422f720SNicholas Piggin : "+m"(*lock) : "r"(0) : "memory");
43*1422f720SNicholas Piggin }
44*1422f720SNicholas Piggin
45*1422f720SNicholas Piggin static volatile bool got_interrupt;
46*1422f720SNicholas Piggin static volatile struct pt_regs recorded_regs;
47*1422f720SNicholas Piggin
interrupt_handler(struct pt_regs * regs,void * opaque)48*1422f720SNicholas Piggin static void interrupt_handler(struct pt_regs *regs, void *opaque)
49*1422f720SNicholas Piggin {
50*1422f720SNicholas Piggin assert(!got_interrupt);
51*1422f720SNicholas Piggin got_interrupt = true;
52*1422f720SNicholas Piggin memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
53*1422f720SNicholas Piggin regs_advance_insn(regs);
54*1422f720SNicholas Piggin }
55*1422f720SNicholas Piggin
test_lwarx_stwcx(int argc,char * argv[])56*1422f720SNicholas Piggin static void test_lwarx_stwcx(int argc, char *argv[])
57*1422f720SNicholas Piggin {
58*1422f720SNicholas Piggin unsigned int *var = (unsigned int *)granule;
59*1422f720SNicholas Piggin unsigned int old;
60*1422f720SNicholas Piggin unsigned int result;
61*1422f720SNicholas Piggin
62*1422f720SNicholas Piggin *var = 0;
63*1422f720SNicholas Piggin asm volatile ("1:"
64*1422f720SNicholas Piggin "lwarx %0,0,%2;"
65*1422f720SNicholas Piggin "stwcx. %1,0,%2;"
66*1422f720SNicholas Piggin "bne- 1b;"
67*1422f720SNicholas Piggin : "=&r"(old) : "r"(1), "r"(var) : "cr0", "memory");
68*1422f720SNicholas Piggin report(old == 0 && *var == 1, "simple update");
69*1422f720SNicholas Piggin
70*1422f720SNicholas Piggin *var = 0;
71*1422f720SNicholas Piggin asm volatile ("li %0,0;"
72*1422f720SNicholas Piggin "stwcx. %1,0,%2;"
73*1422f720SNicholas Piggin "stwcx. %1,0,%2;"
74*1422f720SNicholas Piggin "bne- 1f;"
75*1422f720SNicholas Piggin "li %0,1;"
76*1422f720SNicholas Piggin "1:"
77*1422f720SNicholas Piggin : "=&r"(result)
78*1422f720SNicholas Piggin : "r"(1), "r"(var) : "cr0", "memory");
79*1422f720SNicholas Piggin report(result == 0 && *var == 0, "failed stwcx. (no reservation)");
80*1422f720SNicholas Piggin
81*1422f720SNicholas Piggin *var = 0;
82*1422f720SNicholas Piggin asm volatile ("li %0,0;"
83*1422f720SNicholas Piggin "lwarx %1,0,%4;"
84*1422f720SNicholas Piggin "stw %3,0(%4);"
85*1422f720SNicholas Piggin "stwcx. %2,0,%4;"
86*1422f720SNicholas Piggin "bne- 1f;"
87*1422f720SNicholas Piggin "li %0,1;"
88*1422f720SNicholas Piggin "1:"
89*1422f720SNicholas Piggin : "=&r"(result), "=&r"(old)
90*1422f720SNicholas Piggin : "r"(1), "r"(2), "r"(var) : "cr0", "memory");
91*1422f720SNicholas Piggin /* This is implementation specific, so don't fail */
92*1422f720SNicholas Piggin if (result == 0 && *var == 2)
93*1422f720SNicholas Piggin report(true, "failed stwcx. (intervening store)");
94*1422f720SNicholas Piggin else
95*1422f720SNicholas Piggin report(true, "succeeded stwcx. (intervening store)");
96*1422f720SNicholas Piggin
97*1422f720SNicholas Piggin handle_exception(0x600, interrupt_handler, NULL);
98*1422f720SNicholas Piggin handle_exception(0x700, interrupt_handler, NULL);
99*1422f720SNicholas Piggin
100*1422f720SNicholas Piggin /* Implementations may not necessarily invoke the alignment interrupt */
101*1422f720SNicholas Piggin old = 10;
102*1422f720SNicholas Piggin *var = 0;
103*1422f720SNicholas Piggin asm volatile (
104*1422f720SNicholas Piggin "lwarx %0,0,%1;"
105*1422f720SNicholas Piggin : "+&r"(old) : "r"((char *)var + 1));
106*1422f720SNicholas Piggin report(old == 10 && got_interrupt && recorded_regs.trap == 0x600,
107*1422f720SNicholas Piggin "unaligned lwarx causes fault");
108*1422f720SNicholas Piggin got_interrupt = false;
109*1422f720SNicholas Piggin
110*1422f720SNicholas Piggin /*
111*1422f720SNicholas Piggin * Unaligned stwcx. is more difficult to test, at least under QEMU,
112*1422f720SNicholas Piggin * the store does not proceed if there is no matching reservation, so
113*1422f720SNicholas Piggin * the alignment handler does not get invoked. This is okay according
114*1422f720SNicholas Piggin * to the Power ISA (unalignment does not necessarily invoke the
115*1422f720SNicholas Piggin * alignment interrupt). But POWER CPUs do cause alignment interrupt.
116*1422f720SNicholas Piggin */
117*1422f720SNicholas Piggin *var = 0;
118*1422f720SNicholas Piggin asm volatile (
119*1422f720SNicholas Piggin "lwarx %0,0,%2;"
120*1422f720SNicholas Piggin "stwcx. %1,0,%3;"
121*1422f720SNicholas Piggin : "=&r"(old) : "r"(1), "r"(var), "r"((char *)var+1)
122*1422f720SNicholas Piggin : "cr0", "memory");
123*1422f720SNicholas Piggin /*
124*1422f720SNicholas Piggin * An unaligned larx/stcx. is not required by the ISA to cause an
125*1422f720SNicholas Piggin * exception, and in TCG the stcx does not though it does on POWER CPUs.
126*1422f720SNicholas Piggin */
127*1422f720SNicholas Piggin report_kfail(host_is_tcg, old == 0 && *var == 0 &&
128*1422f720SNicholas Piggin got_interrupt && recorded_regs.trap == 0x600,
129*1422f720SNicholas Piggin "unaligned stwcx. causes fault");
130*1422f720SNicholas Piggin got_interrupt = false;
131*1422f720SNicholas Piggin
132*1422f720SNicholas Piggin handle_exception(0x600, NULL, NULL);
133*1422f720SNicholas Piggin
134*1422f720SNicholas Piggin }
135*1422f720SNicholas Piggin
test_lqarx_stqcx(int argc,char * argv[])136*1422f720SNicholas Piggin static void test_lqarx_stqcx(int argc, char *argv[])
137*1422f720SNicholas Piggin {
138*1422f720SNicholas Piggin union {
139*1422f720SNicholas Piggin __int128_t var;
140*1422f720SNicholas Piggin struct {
141*1422f720SNicholas Piggin #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
142*1422f720SNicholas Piggin unsigned long var1;
143*1422f720SNicholas Piggin unsigned long var2;
144*1422f720SNicholas Piggin #else
145*1422f720SNicholas Piggin unsigned long var2;
146*1422f720SNicholas Piggin unsigned long var1;
147*1422f720SNicholas Piggin #endif
148*1422f720SNicholas Piggin };
149*1422f720SNicholas Piggin } var __attribute__((aligned(16)));
150*1422f720SNicholas Piggin register unsigned long new1 asm("r8");
151*1422f720SNicholas Piggin register unsigned long new2 asm("r9");
152*1422f720SNicholas Piggin register unsigned long old1 asm("r10");
153*1422f720SNicholas Piggin register unsigned long old2 asm("r11");
154*1422f720SNicholas Piggin unsigned int result;
155*1422f720SNicholas Piggin
156*1422f720SNicholas Piggin var.var1 = 1;
157*1422f720SNicholas Piggin var.var2 = 2;
158*1422f720SNicholas Piggin
159*1422f720SNicholas Piggin (void)new2;
160*1422f720SNicholas Piggin (void)old2;
161*1422f720SNicholas Piggin
162*1422f720SNicholas Piggin old1 = 0;
163*1422f720SNicholas Piggin old2 = 0;
164*1422f720SNicholas Piggin new1 = 3;
165*1422f720SNicholas Piggin new2 = 4;
166*1422f720SNicholas Piggin asm volatile ("1:"
167*1422f720SNicholas Piggin "lqarx %0,0,%4;"
168*1422f720SNicholas Piggin "stqcx. %2,0,%4;"
169*1422f720SNicholas Piggin "bne- 1b;"
170*1422f720SNicholas Piggin : "=&r"(old1), "=&r"(old2)
171*1422f720SNicholas Piggin : "r"(new1), "r"(new2), "r"(&var)
172*1422f720SNicholas Piggin : "cr0", "memory");
173*1422f720SNicholas Piggin
174*1422f720SNicholas Piggin report(old1 == 2 && old2 == 1 && var.var1 == 4 && var.var2 == 3,
175*1422f720SNicholas Piggin "simple update");
176*1422f720SNicholas Piggin
177*1422f720SNicholas Piggin var.var1 = 1;
178*1422f720SNicholas Piggin var.var2 = 2;
179*1422f720SNicholas Piggin new1 = 3;
180*1422f720SNicholas Piggin new2 = 4;
181*1422f720SNicholas Piggin asm volatile ("li %0,0;"
182*1422f720SNicholas Piggin "stqcx. %1,0,%3;"
183*1422f720SNicholas Piggin "stqcx. %1,0,%3;"
184*1422f720SNicholas Piggin "bne- 1f;"
185*1422f720SNicholas Piggin "li %0,1;"
186*1422f720SNicholas Piggin "1:"
187*1422f720SNicholas Piggin : "=&r"(result)
188*1422f720SNicholas Piggin : "r"(new1), "r"(new2), "r"(&var)
189*1422f720SNicholas Piggin : "cr0", "memory");
190*1422f720SNicholas Piggin report(result == 0 && var.var1 == 1 && var.var2 == 2,
191*1422f720SNicholas Piggin "failed stqcx. (no reservation)");
192*1422f720SNicholas Piggin
193*1422f720SNicholas Piggin var.var1 = 1;
194*1422f720SNicholas Piggin var.var2 = 2;
195*1422f720SNicholas Piggin new1 = 3;
196*1422f720SNicholas Piggin new2 = 4;
197*1422f720SNicholas Piggin asm volatile ("li %0,0;"
198*1422f720SNicholas Piggin "lqarx %1,0,%6;"
199*1422f720SNicholas Piggin "std %5,0(%6);"
200*1422f720SNicholas Piggin "stqcx. %3,0,%6;"
201*1422f720SNicholas Piggin "bne- 1f;"
202*1422f720SNicholas Piggin "li %0,1;"
203*1422f720SNicholas Piggin "1:"
204*1422f720SNicholas Piggin : "=&r"(result), "=&r"(old1), "=&r"(old2)
205*1422f720SNicholas Piggin : "r"(new1), "r"(new2), "r"(0), "r"(&var)
206*1422f720SNicholas Piggin : "cr0", "memory");
207*1422f720SNicholas Piggin /* This is implementation specific, so don't fail */
208*1422f720SNicholas Piggin if (result == 0 && (var.var1 == 0 || var.var2 == 0))
209*1422f720SNicholas Piggin report(true, "failed stqcx. (intervening store)");
210*1422f720SNicholas Piggin else
211*1422f720SNicholas Piggin report(true, "succeeded stqcx. (intervening store)");
212*1422f720SNicholas Piggin }
213*1422f720SNicholas Piggin
test_migrate_reserve(int argc,char * argv[])214*1422f720SNicholas Piggin static void test_migrate_reserve(int argc, char *argv[])
215*1422f720SNicholas Piggin {
216*1422f720SNicholas Piggin unsigned int *var = (unsigned int *)granule;
217*1422f720SNicholas Piggin unsigned int old;
218*1422f720SNicholas Piggin int i;
219*1422f720SNicholas Piggin int succeed = 0;
220*1422f720SNicholas Piggin
221*1422f720SNicholas Piggin if (!do_migrate)
222*1422f720SNicholas Piggin return;
223*1422f720SNicholas Piggin
224*1422f720SNicholas Piggin for (i = 0; i < 10; i++) {
225*1422f720SNicholas Piggin *var = 0x12345;
226*1422f720SNicholas Piggin asm volatile ("lwarx %0,0,%1" : "=&r"(old) : "r"(var) : "memory");
227*1422f720SNicholas Piggin migrate_quiet();
228*1422f720SNicholas Piggin asm volatile ("stwcx. %0,0,%1" : : "r"(0xf00d), "r"(var) : "cr0", "memory");
229*1422f720SNicholas Piggin if (*var == 0xf00d)
230*1422f720SNicholas Piggin succeed++;
231*1422f720SNicholas Piggin }
232*1422f720SNicholas Piggin
233*1422f720SNicholas Piggin if (do_record) {
234*1422f720SNicholas Piggin /*
235*1422f720SNicholas Piggin * Running under TCG record-replay, reservations must not
236*1422f720SNicholas Piggin * be lost by migration
237*1422f720SNicholas Piggin */
238*1422f720SNicholas Piggin report(succeed > 0, "migrated reservation is not lost");
239*1422f720SNicholas Piggin } else {
240*1422f720SNicholas Piggin report(succeed == 0, "migrated reservation is lost");
241*1422f720SNicholas Piggin }
242*1422f720SNicholas Piggin
243*1422f720SNicholas Piggin report_prefix_pop();
244*1422f720SNicholas Piggin }
245*1422f720SNicholas Piggin
246*1422f720SNicholas Piggin #define ITERS 10000000
247*1422f720SNicholas Piggin static int test_counter = 0;
test_inc_perf(int argc,char * argv[])248*1422f720SNicholas Piggin static void test_inc_perf(int argc, char *argv[])
249*1422f720SNicholas Piggin {
250*1422f720SNicholas Piggin int i;
251*1422f720SNicholas Piggin uint64_t tb1, tb2;
252*1422f720SNicholas Piggin
253*1422f720SNicholas Piggin tb1 = get_tb();
254*1422f720SNicholas Piggin for (i = 0; i < ITERS; i++)
255*1422f720SNicholas Piggin __atomic_fetch_add(&test_counter, 1, __ATOMIC_RELAXED);
256*1422f720SNicholas Piggin tb2 = get_tb();
257*1422f720SNicholas Piggin report(true, "atomic add takes %ldns",
258*1422f720SNicholas Piggin (tb2 - tb1) * 1000000000 / ITERS / tb_hz);
259*1422f720SNicholas Piggin
260*1422f720SNicholas Piggin tb1 = get_tb();
261*1422f720SNicholas Piggin for (i = 0; i < ITERS; i++)
262*1422f720SNicholas Piggin __atomic_fetch_add(&test_counter, 1, __ATOMIC_SEQ_CST);
263*1422f720SNicholas Piggin tb2 = get_tb();
264*1422f720SNicholas Piggin report(true, "sequentially conssistent atomic add takes %ldns",
265*1422f720SNicholas Piggin (tb2 - tb1) * 1000000000 / ITERS / tb_hz);
266*1422f720SNicholas Piggin }
267*1422f720SNicholas Piggin
268*1422f720SNicholas Piggin static long smp_inc_counter = 0;
269*1422f720SNicholas Piggin static int smp_inc_started;
270*1422f720SNicholas Piggin
smp_inc_fn(int cpu_id)271*1422f720SNicholas Piggin static void smp_inc_fn(int cpu_id)
272*1422f720SNicholas Piggin {
273*1422f720SNicholas Piggin long i;
274*1422f720SNicholas Piggin
275*1422f720SNicholas Piggin atomic_fetch_inc(&smp_inc_started);
276*1422f720SNicholas Piggin while (smp_inc_started < nr_cpus_present)
277*1422f720SNicholas Piggin cpu_relax();
278*1422f720SNicholas Piggin
279*1422f720SNicholas Piggin for (i = 0; i < ITERS; i++)
280*1422f720SNicholas Piggin atomic_fetch_inc(&smp_inc_counter);
281*1422f720SNicholas Piggin atomic_fetch_dec(&smp_inc_started);
282*1422f720SNicholas Piggin }
283*1422f720SNicholas Piggin
test_smp_inc(int argc,char ** argv)284*1422f720SNicholas Piggin static void test_smp_inc(int argc, char **argv)
285*1422f720SNicholas Piggin {
286*1422f720SNicholas Piggin if (nr_cpus_present < 2)
287*1422f720SNicholas Piggin return;
288*1422f720SNicholas Piggin
289*1422f720SNicholas Piggin if (!start_all_cpus(smp_inc_fn))
290*1422f720SNicholas Piggin report_abort("Failed to start secondary cpus");
291*1422f720SNicholas Piggin
292*1422f720SNicholas Piggin while (smp_inc_started < nr_cpus_present - 1)
293*1422f720SNicholas Piggin cpu_relax();
294*1422f720SNicholas Piggin smp_inc_fn(smp_processor_id());
295*1422f720SNicholas Piggin while (smp_inc_started > 0)
296*1422f720SNicholas Piggin cpu_relax();
297*1422f720SNicholas Piggin
298*1422f720SNicholas Piggin stop_all_cpus();
299*1422f720SNicholas Piggin
300*1422f720SNicholas Piggin report(smp_inc_counter == nr_cpus_present * ITERS,
301*1422f720SNicholas Piggin "counter lost no increments");
302*1422f720SNicholas Piggin }
303*1422f720SNicholas Piggin
304*1422f720SNicholas Piggin static long smp_lock_counter __attribute__((aligned(128))) = 0;
305*1422f720SNicholas Piggin static unsigned int smp_lock __attribute__((aligned(128)));
306*1422f720SNicholas Piggin static int smp_lock_started;
307*1422f720SNicholas Piggin
smp_lock_fn(int cpu_id)308*1422f720SNicholas Piggin static void smp_lock_fn(int cpu_id)
309*1422f720SNicholas Piggin {
310*1422f720SNicholas Piggin long i;
311*1422f720SNicholas Piggin
312*1422f720SNicholas Piggin atomic_fetch_inc(&smp_lock_started);
313*1422f720SNicholas Piggin while (smp_lock_started < nr_cpus_present)
314*1422f720SNicholas Piggin cpu_relax();
315*1422f720SNicholas Piggin
316*1422f720SNicholas Piggin for (i = 0; i < ITERS; i++) {
317*1422f720SNicholas Piggin spin_lock(&smp_lock);
318*1422f720SNicholas Piggin smp_lock_counter++;
319*1422f720SNicholas Piggin spin_unlock(&smp_lock);
320*1422f720SNicholas Piggin }
321*1422f720SNicholas Piggin atomic_fetch_dec(&smp_lock_started);
322*1422f720SNicholas Piggin }
323*1422f720SNicholas Piggin
test_smp_lock(int argc,char ** argv)324*1422f720SNicholas Piggin static void test_smp_lock(int argc, char **argv)
325*1422f720SNicholas Piggin {
326*1422f720SNicholas Piggin if (nr_cpus_present < 2)
327*1422f720SNicholas Piggin return;
328*1422f720SNicholas Piggin
329*1422f720SNicholas Piggin if (!start_all_cpus(smp_lock_fn))
330*1422f720SNicholas Piggin report_abort("Failed to start secondary cpus");
331*1422f720SNicholas Piggin
332*1422f720SNicholas Piggin while (smp_lock_started < nr_cpus_present - 1)
333*1422f720SNicholas Piggin cpu_relax();
334*1422f720SNicholas Piggin smp_lock_fn(smp_processor_id());
335*1422f720SNicholas Piggin while (smp_lock_started > 0)
336*1422f720SNicholas Piggin cpu_relax();
337*1422f720SNicholas Piggin
338*1422f720SNicholas Piggin stop_all_cpus();
339*1422f720SNicholas Piggin
340*1422f720SNicholas Piggin report(smp_lock_counter == nr_cpus_present * ITERS,
341*1422f720SNicholas Piggin "counter lost no increments");
342*1422f720SNicholas Piggin }
343*1422f720SNicholas Piggin
344*1422f720SNicholas Piggin struct {
345*1422f720SNicholas Piggin const char *name;
346*1422f720SNicholas Piggin void (*func)(int argc, char **argv);
347*1422f720SNicholas Piggin } hctests[] = {
348*1422f720SNicholas Piggin { "lwarx/stwcx", test_lwarx_stwcx },
349*1422f720SNicholas Piggin { "lqarx/stqcx", test_lqarx_stqcx },
350*1422f720SNicholas Piggin { "migration", test_migrate_reserve },
351*1422f720SNicholas Piggin { "performance", test_inc_perf },
352*1422f720SNicholas Piggin { "SMP-atomic", test_smp_inc },
353*1422f720SNicholas Piggin { "SMP-lock", test_smp_lock },
354*1422f720SNicholas Piggin { NULL, NULL }
355*1422f720SNicholas Piggin };
356*1422f720SNicholas Piggin
main(int argc,char ** argv)357*1422f720SNicholas Piggin int main(int argc, char **argv)
358*1422f720SNicholas Piggin {
359*1422f720SNicholas Piggin int i;
360*1422f720SNicholas Piggin int all;
361*1422f720SNicholas Piggin
362*1422f720SNicholas Piggin all = argc == 1 || !strcmp(argv[1], "all");
363*1422f720SNicholas Piggin
364*1422f720SNicholas Piggin for (i = 1; i < argc; i++) {
365*1422f720SNicholas Piggin if (strcmp(argv[i], "-r") == 0) {
366*1422f720SNicholas Piggin do_record = true;
367*1422f720SNicholas Piggin }
368*1422f720SNicholas Piggin if (strcmp(argv[i], "-m") == 0) {
369*1422f720SNicholas Piggin do_migrate = true;
370*1422f720SNicholas Piggin }
371*1422f720SNicholas Piggin }
372*1422f720SNicholas Piggin
373*1422f720SNicholas Piggin report_prefix_push("atomics");
374*1422f720SNicholas Piggin
375*1422f720SNicholas Piggin for (i = 0; hctests[i].name != NULL; i++) {
376*1422f720SNicholas Piggin if (all || strcmp(argv[1], hctests[i].name) == 0) {
377*1422f720SNicholas Piggin report_prefix_push(hctests[i].name);
378*1422f720SNicholas Piggin hctests[i].func(argc, argv);
379*1422f720SNicholas Piggin report_prefix_pop();
380*1422f720SNicholas Piggin }
381*1422f720SNicholas Piggin }
382*1422f720SNicholas Piggin
383*1422f720SNicholas Piggin report_prefix_pop();
384*1422f720SNicholas Piggin
385*1422f720SNicholas Piggin return report_summary();
386*1422f720SNicholas Piggin }
387