From f9ed719c6a46569811df519faabcdd5ffb9b212e Mon Sep 17 00:00:00 2001 From: Christoph Pillmayer Date: Tue, 10 Jun 2025 15:12:42 +0000 Subject: [PATCH] panvk: Add tests for ls tracker behavior in cs_loop Reviewed-by: Boris Brezillon Part-of: --- src/panfrost/genxml/test/cs_builder.cpp | 197 ++++++++++++++++++++++++ 1 file changed, 197 insertions(+) diff --git a/src/panfrost/genxml/test/cs_builder.cpp b/src/panfrost/genxml/test/cs_builder.cpp index 27e7f2a19ca..d0990ad1743 100644 --- a/src/panfrost/genxml/test/cs_builder.cpp +++ b/src/panfrost/genxml/test/cs_builder.cpp @@ -1,5 +1,6 @@ /* * Copyright 2025 Collabora Ltd + * Copyright (C) 2025 Arm Ltd. * SPDX-License-Identifier: MIT */ @@ -138,3 +139,199 @@ TEST_F(CsBuilderTest, maybe_early_patch) EXPECT_EQ(b.root_chunk.size, ARRAY_SIZE(expected_patched)); EXPECT_U64_ARRAY_EQUAL(output, expected_patched, ARRAY_SIZE(expected_patched)); } + +/* If inside the loop no register is used that is getting loaded at the moment, + * do not emit a WAIT on continue / going back to start. */ +TEST_F(CsBuilderTest, loop_ls_tracker_unrelated_inside) +{ + struct cs_index r0 = cs_reg32(&b, 0); + struct cs_index r1 = cs_reg32(&b, 1); + struct cs_index addr = cs_reg64(&b, 10); + + cs_load32_to(&b, r0, addr, 0x0); + cs_while(&b, MALI_CS_CONDITION_ALWAYS, cs_undef()) { + cs_add32(&b, r1, r1, 0x0); + cs_break(&b); + } + cs_add32(&b, r0, r0, 0xab); + cs_finish(&b); + + uint64_t expected_patched[] = { + 0x14000a0000010000, /* LOAD_MULTIPLE r0, addr, #0x0 */ + 0x1001010000000000, /* ADD32 r1, r1, #0x0 */ + 0x1600000060000001, /* BRANCH al, r0, #1 */ + 0x160000006000fffd, /* BRANCH al, r0, #-3 */ + 0x0300000000010000, /* WAIT #0x1 */ + 0x10000000000000ab, /* ADD32 r0, r0, #0xab */ + }; + EXPECT_EQ(b.root_chunk.size, ARRAY_SIZE(expected_patched)); + EXPECT_U64_ARRAY_EQUAL(output, expected_patched, + ARRAY_SIZE(expected_patched)); +} + +/* If a load is started inside the loop it has to be waited for after the loop. */ +TEST_F(CsBuilderTest, loop_ls_tracker_load_only_inside_if) +{ + struct cs_index r0 = cs_reg32(&b, 0); + struct cs_index addr = cs_reg64(&b, 10); + + cs_while(&b, MALI_CS_CONDITION_ALWAYS, cs_undef()) { + cs_if(&b, MALI_CS_CONDITION_LESS, r0) { + cs_load32_to(&b, r0, addr, 0x0); + } + cs_break(&b); + } + cs_add32(&b, r0, r0, 0xab); + cs_finish(&b); + + uint64_t expected_patched[] = { + 0x1600000050000001, /* BRANCH ge, r0, #1 */ + 0x14000a0000010000, /* LOAD_MULTIPLE r0, addr, #0x0 */ + 0x1600000060000002, /* BRANCH al, r0, #2 */ + /* This WAIT is unnecessary because the loop body doesn't use r0. */ + 0x0300000000010000, /* WAIT #0x1 */ + 0x160000006000fffb, /* BRANCH al, r0, #-5 */ + 0x0300000000010000, /* WAIT #0x1 */ + 0x10000000000000ab, /* ADD32 r0, r0, #0xab */ + }; + EXPECT_EQ(b.root_chunk.size, ARRAY_SIZE(expected_patched)); + EXPECT_U64_ARRAY_EQUAL(output, expected_patched, + ARRAY_SIZE(expected_patched)); +} + +/* If a load is started inside the loop with a continue in the if, it has to be + * waited for on continue. */ +TEST_F(CsBuilderTest, loop_ls_tracker_load_only_continue_inside_if) +{ + struct cs_index r0 = cs_reg32(&b, 0); + struct cs_index addr = cs_reg64(&b, 10); + + cs_add32(&b, r0, r0, 0x0); + cs_while(&b, MALI_CS_CONDITION_ALWAYS, cs_undef()) { + cs_if(&b, MALI_CS_CONDITION_LESS, cs_reg32(&b, 1)) { + cs_load32_to(&b, r0, addr, 0x0); + cs_continue(&b); + } + cs_break(&b); + } + cs_add32(&b, r0, r0, 0xab); + cs_finish(&b); + + uint64_t expected_patched[] = { + 0x1000000000000000, /* ADD32 r0, r0, #0x0 */ + 0x1600010050000003, /* BRANCH ge, r1, #3 */ + 0x14000a0000010000, /* LOAD_MULTIPLE r0, addr, #0x0 */ + 0x0300000000010000, /* WAIT #0x1 */ + 0x160000006000fffc, /* BRANCH al, r0, #-4 */ + 0x1600000060000001, /* BRANCH al, r1, #1 */ + 0x160000006000fffa, /* BRANCH al, r0, #-6 */ + 0x10000000000000ab, /* ADD32 r0, r0, #0xab */ + }; + EXPECT_EQ(b.root_chunk.size, ARRAY_SIZE(expected_patched)); + EXPECT_U64_ARRAY_EQUAL(output, expected_patched, + ARRAY_SIZE(expected_patched)); +} + +/* If a load is started inside the loop with a break in the if, it has to be + * waited for after the loop. */ +TEST_F(CsBuilderTest, loop_ls_tracker_load_only_break_inside_if) +{ + struct cs_index r0 = cs_reg32(&b, 0); + struct cs_index addr = cs_reg64(&b, 10); + + cs_add32(&b, r0, r0, 0x0); + cs_while(&b, MALI_CS_CONDITION_ALWAYS, cs_undef()) { + cs_if(&b, MALI_CS_CONDITION_LESS, cs_reg32(&b, 1)) { + cs_load32_to(&b, r0, addr, 0x0); + cs_break(&b); + } + } + cs_add32(&b, r0, r0, 0xab); + cs_finish(&b); + + uint64_t expected_patched[] = { + 0x1000000000000000, /* ADD32 r0, r0, #0x0 */ + 0x1600010050000002, /* BRANCH ge, r1, #2 */ + 0x14000a0000010000, /* LOAD_MULTIPLE r0, addr, #0x0 */ + 0x1600000060000002, /* BRANCH al, r0, #2 */ + 0x0300000000010000, /* WAIT #0x1 */ + 0x160000006000fffb, /* BRANCH al, r0, #-5 */ + 0x0300000000010000, /* WAIT #0x1 */ + 0x10000000000000ab, /* ADD32 r0, r0, #0xab */ + }; + EXPECT_EQ(b.root_chunk.size, ARRAY_SIZE(expected_patched)); + EXPECT_U64_ARRAY_EQUAL(output, expected_patched, + ARRAY_SIZE(expected_patched)); +} + +/* If a register is loaded inside the loop, that was already getting loaded + * when the loop began, there is no need to add a WAIT on continue. If that + * register is used again after the loop, a WAIT has to be added. */ +TEST_F(CsBuilderTest, loop_ls_tracker_load_same_inside) +{ + struct cs_index r0 = cs_reg32(&b, 0); + struct cs_index addr = cs_reg64(&b, 10); + + cs_load32_to(&b, r0, addr, 0x0); + cs_while(&b, MALI_CS_CONDITION_ALWAYS, cs_undef()) { + cs_add32(&b, r0, r0, 0x0); + cs_load32_to(&b, r0, addr, 0x0); + cs_if(&b, MALI_CS_CONDITION_LESS, cs_reg32(&b, 1)) { + cs_break(&b); + } + } + cs_add32(&b, r0, r0, 0xab); + cs_finish(&b); + + uint64_t expected_patched[] = { + 0x14000a0000010000, /* LOAD_MULTIPLE r0, addr, #0x0 */ + 0x0300000000010000, /* WAIT #0x1 */ + 0x1000000000000000, /* ADD32 r0, r0, #0x0 */ + 0x14000a0000010000, /* LOAD_MULTIPLE r0, addr, #0x0 */ + 0x1600010050000001, /* BRANCH ge, r1, #1 */ + 0x1600000060000001, /* BRANCH al, r0, #1 */ + 0x160000006000fffa, /* BRANCH al, r0, #-6 */ + 0x0300000000010000, /* WAIT #0x1 */ + 0x10000000000000ab, /* ADD32 r0, r0, #0xab */ + }; + EXPECT_EQ(b.root_chunk.size, ARRAY_SIZE(expected_patched)); + EXPECT_U64_ARRAY_EQUAL(output, expected_patched, + ARRAY_SIZE(expected_patched)); +} + +/* If the register that is used and loaded in the loop body is also used as the + * condition, we need to WAIT on continue because the WAIT for the condition is + * emitted before the loop body. */ +TEST_F(CsBuilderTest, loop_ls_tracker_load_same_inside_use_as_cond) +{ + struct cs_index r0 = cs_reg32(&b, 0); + struct cs_index addr = cs_reg64(&b, 10); + + cs_load32_to(&b, r0, addr, 0x0); + cs_while(&b, MALI_CS_CONDITION_LESS, r0) { + cs_add32(&b, r0, r0, 0x0); + cs_load32_to(&b, r0, addr, 0x0); + cs_if(&b, MALI_CS_CONDITION_LESS, cs_reg32(&b, 1)) { + cs_break(&b); + } + } + cs_add32(&b, r0, r0, 0xab); + cs_finish(&b); + + uint64_t expected_patched[] = { + 0x14000a0000010000, /* LOAD_MULTIPLE r0, addr, #0x0 */ + 0x0300000000010000, /* WAIT #0x1 */ + 0x1600000050000006, /* BRANCH ge, r0, #6 */ + 0x1000000000000000, /* ADD32 r0, r0, #0x0 */ + 0x14000a0000010000, /* LOAD_MULTIPLE r0, addr, #0x0 */ + 0x1600010050000001, /* BRANCH ge, r1, #1 */ + 0x1600000060000002, /* BRANCH al, r0, #2 */ + 0x0300000000010000, /* WAIT #0x1 */ + 0x160000004000fffa, /* BRANCH lt, r0, #-6 */ + 0x0300000000010000, /* WAIT #0x1 */ + 0x10000000000000ab, /* ADD32 r0, r0, #0xab */ + }; + EXPECT_EQ(b.root_chunk.size, ARRAY_SIZE(expected_patched)); + EXPECT_U64_ARRAY_EQUAL(output, expected_patched, + ARRAY_SIZE(expected_patched)); +}