Files
TheRock/rocm-libraries/shared/stinkytofu/tests/unit/asm/LoopRegionRemarkPassTest.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

224 lines
7.5 KiB
C++

/* ************************************************************************
* Copyright (C) 2026 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* ************************************************************************ */
#include <gtest/gtest.h>
#include <cstdint>
#include <sstream>
#include <string>
#include "TestHelpers.hpp"
#include "stinkytofu/analysis/AnalysisRegistration.hpp"
#include "stinkytofu/core/PassManager.hpp"
#include "stinkytofu/transforms/asm/LoopRegionRemarkPass.hpp"
using namespace stinkytofu;
using namespace stinkytofu::test;
static StinkyRegister litInt(int32_t v) {
StinkyRegister r;
r.dataType = StinkyRegister::Type::LiteralInt;
r.literalInt = v;
return r;
}
class LoopRegionRemarkPassTest : public ::testing::Test {
protected:
GfxArchID arch = GfxArchID::Gfx1250;
GemmTileConfig config;
std::unique_ptr<Function> func;
std::unique_ptr<Pass> pass;
AnalysisManager am;
void SetUp() override {
config.arch[0] = 12;
config.arch[1] = 5;
config.arch[2] = 0;
func = std::make_unique<Function>("loop_region_test");
setFunctionArch(*func, arch);
pass = createLoopRegionRemarkPass();
registerAllAnalyses(am);
}
// Run pass with remarks enabled, capture stderr output
std::string runPassWithRemarks() {
PassContext ctx;
ctx.setGemmTileConfig(config);
ctx.setRemarksEnabled(true);
std::ostringstream captured;
auto* origBuf = std::cerr.rdbuf(captured.rdbuf());
pass->run(*func, ctx, am);
std::cerr.rdbuf(origBuf);
return captured.str();
}
// Run pass without remarks — should produce no output
std::string runPassWithoutRemarks() {
PassContext ctx;
ctx.setGemmTileConfig(config);
ctx.setRemarksEnabled(false);
std::ostringstream captured;
auto* origBuf = std::cerr.rdbuf(captured.rdbuf());
pass->run(*func, ctx, am);
std::cerr.rdbuf(origBuf);
return captured.str();
}
// Build a self-loop BB
BasicBlock* buildLoopBB(const char* label = "loop_body") {
BasicBlock* body = func->createBasicBlock(label);
body->addSuccessor(body);
return body;
}
};
// No loops: pass should produce no output
TEST_F(LoopRegionRemarkPassTest, NoLoopsNoOutput) {
func->createBasicBlock("entry");
std::string output = runPassWithRemarks();
EXPECT_TRUE(output.empty());
}
// Remarks disabled: pass should produce no output even with loops
TEST_F(LoopRegionRemarkPassTest, RemarksDisabledNoOutput) {
BasicBlock* body = buildLoopBB("loop_body");
createVAddInBlock(body, arch, 0, 1, 2);
createVAddInBlock(body, arch, 3, 4, 5);
std::string output = runPassWithoutRemarks();
EXPECT_TRUE(output.empty());
}
// Simple loop with no side effects: should report 1 region
TEST_F(LoopRegionRemarkPassTest, SingleRegionLoop) {
BasicBlock* body = buildLoopBB("loop_body");
createVAddInBlock(body, arch, 0, 1, 2);
createVAddInBlock(body, arch, 3, 4, 5);
std::string output = runPassWithRemarks();
EXPECT_NE(output.find("loop 'loop_body' summary"), std::string::npos);
EXPECT_NE(output.find("1 regions"), std::string::npos);
EXPECT_NE(output.find("0 s_nop"), std::string::npos);
}
// Loop with a wait instruction: should report 2 regions and a [wait] boundary
TEST_F(LoopRegionRemarkPassTest, WaitCreatesRegionBoundary) {
BasicBlock* body = buildLoopBB("loop_body");
createVAddInBlock(body, arch, 0, 1, 2);
// Insert s_waitcnt — this is a non-movable side effect
{
AsmIRBuilder builder(*body, arch);
StinkyInstruction* waitInst = builder.create(getMCIDByUOp(GFX::s_waitcnt, arch));
waitInst->addSrcReg(litInt(0));
}
createVAddInBlock(body, arch, 3, 4, 5);
std::string output = runPassWithRemarks();
EXPECT_NE(output.find("2 regions"), std::string::npos);
EXPECT_NE(output.find("[wait]"), std::string::npos);
}
// Loop with untokenized ds_load (no LDS pseudo-regs): should report as boundary
TEST_F(LoopRegionRemarkPassTest, UntokenizedDsLoadCreatesBoundary) {
BasicBlock* body = buildLoopBB("loop_body");
createVAddInBlock(body, arch, 0, 1, 2);
// ds_load without LDS pseudo-register — non-movable
createDsReadB128InBlock(body, arch, 10, 20);
createVAddInBlock(body, arch, 3, 4, 5);
std::string output = runPassWithRemarks();
EXPECT_NE(output.find("2 regions"), std::string::npos);
EXPECT_NE(output.find("[untokenized_mem]"), std::string::npos);
EXPECT_NE(output.find("no MemTokenData"), std::string::npos);
}
// Loop with tokenized ds_load (has LDS pseudo-reg): should NOT be a boundary
TEST_F(LoopRegionRemarkPassTest, TokenizedDsLoadNotBoundary) {
BasicBlock* body = buildLoopBB("loop_body");
createVAddInBlock(body, arch, 0, 1, 2);
// ds_load WITH LDS pseudo-register — movable, not a boundary
{
StinkyInstruction* inst = createDsReadB128InBlock(body, arch, 10, 20);
inst->addSrcReg(StinkyRegister(RegType::LDS, 0, 1));
}
createVAddInBlock(body, arch, 3, 4, 5);
std::string output = runPassWithRemarks();
EXPECT_NE(output.find("1 regions"), std::string::npos);
}
// Loop with s_nop: should report wasted cycles
TEST_F(LoopRegionRemarkPassTest, SnopDetection) {
BasicBlock* body = buildLoopBB("loop_body");
createVAddInBlock(body, arch, 0, 1, 2);
// Insert s_nop 3 (wastes 4 cycles)
{
AsmIRBuilder builder(*body, arch);
StinkyInstruction* nopInst = builder.create(getMCIDByUOp(GFX::s_nop, arch));
nopInst->addSrcReg(litInt(3));
}
createVAddInBlock(body, arch, 3, 4, 5);
std::string output = runPassWithRemarks();
EXPECT_NE(output.find("1 s_nop"), std::string::npos);
EXPECT_NE(output.find("4 wasted cycles"), std::string::npos);
}
// Loop with multiple boundaries: should report correct count
TEST_F(LoopRegionRemarkPassTest, MultipleBoundaries) {
BasicBlock* body = buildLoopBB("loop_body");
createVAddInBlock(body, arch, 0, 1, 2);
// wait
{
AsmIRBuilder builder(*body, arch);
StinkyInstruction* inst = builder.create(getMCIDByUOp(GFX::s_waitcnt, arch));
inst->addSrcReg(litInt(0));
}
createVAddInBlock(body, arch, 3, 4, 5);
// untokenized ds_load
createDsReadB128InBlock(body, arch, 10, 20);
createVAddInBlock(body, arch, 6, 7, 8);
std::string output = runPassWithRemarks();
EXPECT_NE(output.find("3 regions"), std::string::npos);
EXPECT_NE(output.find("2 boundaries"), std::string::npos);
}