Files
TheRock/rocm-libraries/shared/rocroller/test/unit/MFMAHazardObserverTest.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

1093 lines
42 KiB
C++

// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <rocRoller/DataTypes/DataTypes.hpp>
#include <rocRoller/InstructionValues/Register.hpp>
#include "GenericContextFixture.hpp"
#include "SourceMatcher.hpp"
using namespace rocRoller;
using ::testing::HasSubstr;
namespace rocRollerTest
{
class MFMA90aObserverTest : public GenericContextFixture
{
protected:
GPUArchitectureTarget targetArchitecture() override
{
return {GPUArchitectureGFX::GFX90A};
}
void peekAndSchedule(Instruction& inst, uint expectedNops = 0)
{
auto peeked = m_context->observer()->peek(inst);
EXPECT_EQ(peeked.nops, expectedNops) << inst.toString(LogLevel::Debug);
m_context->schedule(inst);
}
};
class MFMA908ObserverTest : public GenericContextFixture
{
protected:
GPUArchitectureTarget targetArchitecture() override
{
return {GPUArchitectureGFX::GFX908};
}
void peekAndSchedule(Instruction inst, uint expectedNops = 0)
{
auto peeked = m_context->peek(inst);
EXPECT_EQ(peeked.nops, expectedNops) << inst.toString(LogLevel::Debug);
m_context->schedule(inst);
}
};
class MFMA942ObserverTest : public GenericContextFixture
{
protected:
GPUArchitectureTarget targetArchitecture() override
{
return {GPUArchitectureGFX::GFX942};
}
void peekAndSchedule(Instruction& inst, uint expectedNops = 0)
{
auto peeked = m_context->observer()->peek(inst);
EXPECT_EQ(peeked.nops, expectedNops) << inst.toString(LogLevel::Debug);
m_context->schedule(inst);
}
};
class MFMA950ObserverTest : public GenericContextFixture
{
protected:
GPUArchitectureTarget targetArchitecture()
{
return GPUArchitectureTarget{GPUArchitectureGFX::GFX950};
}
void peekAndSchedule(Instruction& inst, uint expectedNops = 0)
{
auto peeked = m_context->observer()->peek(inst);
EXPECT_EQ(peeked.nops, expectedNops) << inst.toString(LogLevel::Debug);
m_context->schedule(inst);
}
};
TEST_F(MFMA90aObserverTest, NoWaitStates)
{
auto s = createRegisters(Register::Type::Scalar,
DataType::Float,
3,
2,
Register::AllocationOptions::FullyContiguous());
auto zero = Register::Value::Literal(0);
{
std::vector<Instruction> insts
= {Instruction("s_load_dwordx2", {s[1]}, {s[0], zero}, {}, ""),
Instruction("s_load_dwordx2", {s[2]}, {s[0], zero}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
for(auto inst : insts)
{
peekAndSchedule(inst);
}
std::string expected = R"(
s_load_dwordx2 s[2:3], s[0:1], 0
s_load_dwordx2 s[4:5], s[0:1], 0
s_endpgm
)";
EXPECT_EQ(NormalizedSource(output()), NormalizedSource(expected));
}
}
TEST_F(MFMA90aObserverTest, CMPXWaitStates)
{
auto v_i64 = createRegisters(Register::Type::Vector, DataType::Int64, 1);
auto v_f32 = createRegisters(Register::Type::Vector, DataType::Float, 3);
auto v_f16 = createRegisters(Register::Type::Vector, DataType::Half, 2);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1);
{
std::vector<Instruction> insts
= {Instruction("v_cmpx_lt_f32", {v_f32[2]}, {v_f32[0], v_f32[1]}, {}, ""),
Instruction("v_mfma_f32_32x32x8f16", {a[0]}, {v_f16[0], v_f16[1], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 4);
EXPECT_THAT(output(), HasSubstr("s_nop 3"));
clearOutput();
}
// No hazard if v_cmp doesn't write to exec or a VGPR that is later read
{
std::vector<Instruction> insts
= {Instruction("v_cmp_lt_f32", {v_i64[0]}, {v_f32[0], v_f32[1]}, {}, ""),
Instruction("v_mfma_f32_32x32x8f16", {a[0]}, {v_f16[0], v_f16[1], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1]);
EXPECT_THAT(output(), Not(HasSubstr("s_nop")));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, VALUThenMFMA)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 6);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1);
{
std::vector<Instruction> insts
= {Instruction("v_or_b32", {v[2]}, {v[0], v[1]}, {}, ""),
Instruction("v_mfma_f32_16x16x4f32", {a[0]}, {v[0], v[2], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 2);
EXPECT_THAT(output(), HasSubstr("s_nop 1"));
clearOutput();
}
// No hazard if second instruction doesn't read same VGPRs
{
std::vector<Instruction> insts
= {Instruction("v_or_b32", {v[2]}, {v[0], v[1]}, {}, ""),
Instruction("v_mfma_f32_16x16x4f32", {a[0]}, {v[3], v[4], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 0);
EXPECT_THAT(output(), Not(HasSubstr("s_nop")));
clearOutput();
}
{
std::vector<Instruction> insts
= {Instruction("v_or_b32", {v[2]}, {v[0], v[1]}, {}, ""),
Instruction("v_or_b32", {v[5]}, {v[3], v[4]}, {}, ""), // Unrelated
Instruction("v_mfma_f32_16x16x4f32", {a[0]}, {v[0], v[2], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1]);
peekAndSchedule(insts[2], 1);
EXPECT_THAT(output(), HasSubstr("s_nop 0"));
clearOutput();
}
{
std::vector<Instruction> insts
= {Instruction("v_or_b32", {v[2]}, {v[0], v[1]}, {}, ""),
Instruction("v_or_b32", {v[5]}, {v[3], v[4]}, {}, ""), // Unrelated
Instruction("v_mfma_f32_16x16x4f32", {a[0]}, {v[2], v[5], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1]);
peekAndSchedule(insts[2], 2);
EXPECT_THAT(output(), HasSubstr("s_nop 1"));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, XDLOPThenMFMA_SrcCExact)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 4);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1, 4);
// No hazard if SrcC is exactly overlapped
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4f32", {a[0]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_mfma_f32_16x16x4f32", {a[0]}, {v[2], v[3], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1]);
EXPECT_THAT(output(), Not(HasSubstr("s_nop")));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, XDLOPThenMFMA_Different)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 4);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1, 4);
// No register overlap should not be a hazard
{
std::vector<Instruction> insts = {Instruction("v_mfma_f32_16x16x4f32",
{a[0]->subset({0, 1})},
{v[0], v[1], a[0]->subset({0, 1})},
{},
""),
Instruction("v_mfma_f32_16x16x4f32",
{a[0]->subset({2, 3})},
{v[2], v[3], a[0]->subset({2, 3})},
{},
""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1]);
EXPECT_THAT(output(), Not(HasSubstr("s_nop")));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, XDLOPThenMFMA_SrcCOverlap)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 4);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1, 4);
// If SrcC is partially overlapped
{
std::vector<Instruction> insts = {Instruction("v_mfma_f32_16x16x4f32",
{a[0]->subset({0, 1})},
{v[0], v[1], a[0]->subset({0, 1})},
{},
""),
Instruction("v_mfma_f32_16x16x4f32",
{a[0]->subset({1, 2})},
{v[2], v[3], a[0]->subset({1, 2})},
{},
""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 8);
EXPECT_THAT(output(), HasSubstr("s_nop 7"));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, XDLOPThenMFMA_ReadA)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4f32", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_mfma_f32_16x16x4f32", {a[1]}, {v[2], v[3], a[1]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 11);
EXPECT_THAT(output(), HasSubstr("s_nop 10"));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, XDLOPThenFlat)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4f32", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("flat_store_dword", {}, {v[2], v[3]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 11);
EXPECT_THAT(output(), HasSubstr("s_nop 10"));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, XDLOPThenVALU_Read)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4f32", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_or_b32", {v[4]}, {v[2], v[3]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 11);
EXPECT_THAT(output(), HasSubstr("s_nop 10"));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, XDLOPThenVALU_Write)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4f32", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_or_b32", {v[2]}, {v[3], v[4]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 11);
EXPECT_THAT(output(), HasSubstr("s_nop 10"));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, XDLOPThenVALU_WAR)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 6);
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4f32", {v[2]}, {v[0], v[1], v[3]}, {}, ""),
Instruction("v_or_b32", {v[3]}, {v[4], v[5]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 11);
EXPECT_THAT(output(), HasSubstr("s_nop 10"));
}
TEST_F(MFMA90aObserverTest, XDLOPThenVALU_WAR_Partial)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 6, 4);
std::vector<Instruction> insts
= {Instruction(
"v_mfma_f32_16x16x4f32", {v[2]}, {v[0], v[1], v[3]->subset({2, 3})}, {}, ""),
Instruction("v_or_b32", {v[3]->subset({1, 2})}, {v[4], v[5]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 11);
EXPECT_THAT(output(), HasSubstr("s_nop 10"));
clearOutput();
}
TEST_F(MFMA90aObserverTest, DLOPWriteScrC)
{
auto v = createRegisters(Register::Type::Vector, DataType::Half, 7);
{
std::vector<Instruction> insts
= {Instruction("v_dot2_f32_f16", {v[3]}, {v[0], v[1], v[2]}, {}, ""),
Instruction("v_dot2_f32_f16", {v[6]}, {v[4], v[5], v[3]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1]);
EXPECT_THAT(output(), Not(HasSubstr("s_nop")));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, DLOPWriteSrcA)
{
auto v = createRegisters(Register::Type::Vector, DataType::Half, 7);
{
std::vector<Instruction> insts
= {Instruction("v_dot2_f32_f16", {v[3]}, {v[0], v[1], v[2]}, {}, ""),
Instruction("v_dot2_f32_f16", {v[6]}, {v[3], v[4], v[5]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 3);
EXPECT_THAT(output(), HasSubstr("s_nop 2"));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, DLOPWriteSrcB)
{
auto v = createRegisters(Register::Type::Vector, DataType::Half, 7);
{
std::vector<Instruction> insts
= {Instruction("v_dot2_f32_f16", {v[3]}, {v[0], v[1], v[2]}, {}, ""),
Instruction("v_dot2_f32_f16", {v[6]}, {v[4], v[3], v[5]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 3);
EXPECT_THAT(output(), HasSubstr("s_nop 2"));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, DLOPWriteDifferent)
{
auto v = createRegisters(Register::Type::Vector, DataType::Half, 7);
{
std::vector<Instruction> insts
= {Instruction("v_dot2_f32_f16", {v[3]}, {v[0], v[1], v[2]}, {}, ""),
Instruction("v_dot2c_f32_f16", {v[6]}, {v[0], v[1], v[3]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 3);
EXPECT_THAT(output(), HasSubstr("s_nop 2"));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, DGEMM16x16x4ThenMFMA_SrcCExact)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 4);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1, 4);
// No hazard if SrcC is exactly overlapped
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f64_16x16x4f64", {a[0]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_mfma_f64_16x16x4f64", {a[0]}, {v[2], v[3], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1]);
EXPECT_THAT(output(), Not(HasSubstr("s_nop")));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, DGEMM16x16x4ThenDGEMM_Different)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 4);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1, 4);
// No register overlap should not be a hazard
{
std::vector<Instruction> insts = {Instruction("v_mfma_f64_16x16x4f64",
{a[0]->subset({0, 1})},
{v[0], v[1], a[0]->subset({0, 1})},
{},
""),
Instruction("v_mfma_f64_16x16x4f64",
{a[0]->subset({2, 3})},
{v[2], v[3], a[0]->subset({2, 3})},
{},
""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1]);
EXPECT_THAT(output(), Not(HasSubstr("s_nop")));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, DGEMM16x16x4ThenDGEMM_SrcCOverlap)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 4);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1, 4);
// If SrcC is partially overlapped
{
std::vector<Instruction> insts = {Instruction("v_mfma_f64_16x16x4f64",
{a[0]->subset({0, 1})},
{v[0], v[1], a[0]->subset({0, 1})},
{},
""),
Instruction("v_mfma_f64_16x16x4f64",
{a[0]->subset({1, 2})},
{v[2], v[3], a[0]->subset({1, 2})},
{},
""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 9);
EXPECT_THAT(output(), HasSubstr("s_nop 8"));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, DGEMM16x16x4ThenMFMA_ReadA)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f64_16x16x4f64", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_mfma_f64_16x16x4f64", {a[1]}, {v[2], v[3], a[1]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 11);
EXPECT_THAT(output(), HasSubstr("s_nop 10"));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, DGEMM16x16x4ThenACCRead)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f64_16x16x4f64", {a[1]}, {v[0], v[1], a[0]}, {}, ""),
Instruction::Comment("Comments shouldn't change NOP counts"),
Instruction("v_accvgpr_read", {v[2]}, {a[1]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1]);
peekAndSchedule(insts[2], 11);
EXPECT_THAT(output(), HasSubstr("s_nop 10"));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, DGEMM16x16x4ThenFlat)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f64_16x16x4f64", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("flat_store_dword", {}, {v[2], v[3]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 18);
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, DGEMM16x16x4ThenVALU_Read)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f64_16x16x4f64", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_or_b32", {v[4]}, {v[2], v[3]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 11);
EXPECT_THAT(output(), HasSubstr("s_nop 10"));
clearOutput();
}
}
TEST_F(MFMA90aObserverTest, DGEMM16x16x4ThenVALU_Write)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f64_16x16x4f64", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_or_b32", {v[2]}, {v[3], v[4]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 11);
EXPECT_THAT(output(), HasSubstr("s_nop 10"));
clearOutput();
}
}
TEST_F(MFMA908ObserverTest, CMPXWaitStates)
{
auto v_i64 = createRegisters(Register::Type::Vector, DataType::Int64, 1);
auto v_f32 = createRegisters(Register::Type::Vector, DataType::Float, 3);
auto v_f16 = createRegisters(Register::Type::Vector, DataType::Half, 2);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1);
{
std::vector<Instruction> insts
= {Instruction("v_cmpx_lt_f32", {v_f32[2]}, {v_f32[0], v_f32[1]}, {}, ""),
Instruction("v_mfma_f32_32x32x8f16", {a[0]}, {v_f16[0], v_f16[1], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 4);
EXPECT_THAT(output(), HasSubstr("s_nop 3"));
clearOutput();
}
// No hazard if v_cmp doesn't write to exec or a VGPR that is later read
{
std::vector<Instruction> insts
= {Instruction("v_cmp_lt_f32", {v_i64[0]}, {v_f32[0], v_f32[1]}, {}, ""),
Instruction("v_mfma_f32_32x32x8f16", {a[0]}, {v_f16[0], v_f16[1], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1]);
EXPECT_THAT(output(), Not(HasSubstr("s_nop")));
clearOutput();
}
}
TEST_F(MFMA908ObserverTest, CMPXThenACCVGPRWrite)
{
auto v_f32 = createRegisters(Register::Type::Vector, DataType::Float, 3);
auto v_f16 = createRegisters(Register::Type::Vector, DataType::Half, 2);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1);
{
std::vector<Instruction> insts
= {Instruction("v_cmpx_lt_f32", {v_f32[2]}, {v_f32[0], v_f32[1]}, {}, ""),
Instruction("v_accvgpr_write", {a[0]}, {v_f16[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 4);
EXPECT_THAT(output(), HasSubstr("s_nop 3"));
clearOutput();
}
}
TEST_F(MFMA908ObserverTest, VALUThenMFMA)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 6);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1);
{
std::vector<Instruction> insts
= {Instruction("v_or_b32", {v[2]}, {v[0], v[1]}, {}, ""),
Instruction("v_mfma_f32_16x16x4f32", {a[0]}, {v[0], v[2], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 2);
EXPECT_THAT(output(), HasSubstr("s_nop 1"));
clearOutput();
}
// No hazard if second instruction doesn't read same VGPRs
{
std::vector<Instruction> insts
= {Instruction("v_or_b32", {v[2]}, {v[0], v[1]}, {}, ""),
Instruction("v_mfma_f32_16x16x4f32", {a[0]}, {v[3], v[4], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 0);
EXPECT_THAT(output(), Not(HasSubstr("s_nop")));
clearOutput();
}
{
std::vector<Instruction> insts
= {Instruction("v_or_b32", {v[2]}, {v[0], v[1]}, {}, ""),
Instruction("v_or_b32", {v[5]}, {v[3], v[4]}, {}, ""), // Unrelated
Instruction("v_mfma_f32_16x16x4f32", {a[0]}, {v[0], v[2], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1]);
peekAndSchedule(insts[2], 1);
EXPECT_THAT(output(), HasSubstr("s_nop 0"));
clearOutput();
}
}
TEST_F(MFMA908ObserverTest, VALUThenACCVGPRWrite)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 6);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1);
{
std::vector<Instruction> insts
= {Instruction("v_or_b32", {v[2]}, {v[0], v[1]}, {}, ""),
Instruction("v_accvgpr_write", {a[0]}, {v[2]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 2);
EXPECT_THAT(output(), HasSubstr("s_nop 1"));
clearOutput();
}
}
TEST_F(MFMA942ObserverTest, XDLOPThenMFMA_SrcCExact)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 4);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1, 1);
// No hazard if SrcC is exactly overlapped
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4_f32", {a[0]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_mfma_f32_16x16x4_f32", {a[0]}, {v[2], v[3], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1]);
EXPECT_THAT(output(), Not(HasSubstr("s_nop")));
clearOutput();
}
}
TEST_F(MFMA942ObserverTest, XDLOPThenMFMA_SrcCExact2Pass)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 4);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1, 1);
// Exception: Hazard for 2 pass MFMAs
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_4x4x1f32", {a[0]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_mfma_f32_4x4x1f32", {a[0]}, {v[2], v[3], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 2);
EXPECT_THAT(output(), HasSubstr("s_nop 1"));
clearOutput();
}
}
TEST_F(MFMA942ObserverTest, XDLOPThenMFMA_Different)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 4);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1, 4);
// No register overlap should not be a hazard
{
std::vector<Instruction> insts = {Instruction("v_mfma_f32_16x16x4_f32",
{a[0]->subset({0, 1})},
{v[0], v[1], a[0]->subset({0, 1})},
{},
""),
Instruction("v_mfma_f32_16x16x4_f32",
{a[0]->subset({2, 3})},
{v[2], v[3], a[0]->subset({2, 3})},
{},
""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1]);
EXPECT_THAT(output(), Not(HasSubstr("s_nop")));
clearOutput();
}
}
TEST_F(MFMA942ObserverTest, XDLOPThenMFMA_SrcCOverlap)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 4);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1, 4);
// If SrcC is partially overlapped
{
std::vector<Instruction> insts = {Instruction("v_mfma_f32_16x16x4_f32",
{a[0]->subset({0, 1})},
{v[0], v[1], a[0]->subset({0, 1})},
{},
""),
Instruction("v_mfma_f32_16x16x4_f32",
{a[0]->subset({1, 2})},
{v[2], v[3], a[0]->subset({1, 2})},
{},
""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 9);
EXPECT_THAT(output(), HasSubstr("s_nop 8"));
clearOutput();
}
}
TEST_F(MFMA942ObserverTest, XDLOPThenMFMA_ReadA)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4_f32", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_mfma_f32_16x16x4_f32", {a[1]}, {v[2], v[3], a[1]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 11);
EXPECT_THAT(output(), HasSubstr("s_nop 10"));
clearOutput();
}
}
TEST_F(MFMA942ObserverTest, XDLOPThenFlat)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4_f32", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("flat_store_dword", {}, {v[2], v[3]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 11);
EXPECT_THAT(output(), HasSubstr("s_nop 10"));
clearOutput();
}
}
TEST_F(MFMA942ObserverTest, XDLOPThenVALU_Read)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4_f32", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_or_b32", {v[4]}, {v[2], v[3]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 11);
EXPECT_THAT(output(), HasSubstr("s_nop 10"));
clearOutput();
}
}
TEST_F(MFMA942ObserverTest, XDLOPThenVALU_Write)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4_f32", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_or_b32", {v[2]}, {v[3], v[4]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 11);
EXPECT_THAT(output(), HasSubstr("s_nop 10"));
clearOutput();
}
}
TEST_F(MFMA942ObserverTest, XDLOPThenVALU_WAR)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 6);
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4_f32", {v[2]}, {v[0], v[1], v[3]}, {}, ""),
Instruction("v_or_b32", {v[3]}, {v[4], v[5]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 7);
EXPECT_THAT(output(), HasSubstr("s_nop 6"));
}
TEST_F(MFMA942ObserverTest, XDLOPThenVALU_WAR_Partial)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 6, 4);
std::vector<Instruction> insts
= {Instruction(
"v_mfma_f32_16x16x4_f32", {v[2]}, {v[0], v[1], v[3]->subset({2, 3})}, {}, ""),
Instruction("v_or_b32", {v[3]->subset({1, 2})}, {v[4], v[5]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 7);
EXPECT_THAT(output(), HasSubstr("s_nop 6"));
clearOutput();
}
TEST_F(MFMA950ObserverTest, XDLOPThenMFMA_SrcCOverlap)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 4);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1, 4);
// If SrcC is partially overlapped
{
std::vector<Instruction> insts = {Instruction("v_mfma_f32_16x16x4_f32",
{a[0]->subset({0, 1})},
{v[0], v[1], a[0]->subset({0, 1})},
{},
""),
Instruction("v_mfma_f32_16x16x4_f32",
{a[0]->subset({1, 2})},
{v[2], v[3], a[0]->subset({1, 2})},
{},
""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 10);
EXPECT_THAT(output(), HasSubstr("s_nop 9"));
clearOutput();
}
}
TEST_F(MFMA950ObserverTest, XDLOPThenMFMA_SrcCExact)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 4);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1, 1);
// No hazard if SrcC is exactly overlapped
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4_f32", {a[0]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_mfma_f32_16x16x4_f32", {a[0]}, {v[2], v[3], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1]);
EXPECT_THAT(output(), Not(HasSubstr("s_nop")));
clearOutput();
}
}
TEST_F(MFMA950ObserverTest, XDLOPThenMFMA_SrcCExact2Pass)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 4);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 1, 1);
// Exception: Hazard for 2 pass MFMAs
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_4x4x1f32", {a[0]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_mfma_f32_4x4x1f32", {a[0]}, {v[2], v[3], a[0]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 2);
EXPECT_THAT(output(), HasSubstr("s_nop 1"));
clearOutput();
}
}
TEST_F(MFMA950ObserverTest, XDLOPThenMFMA_ReadA)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4_f32", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_mfma_f32_16x16x4_f32", {a[1]}, {v[2], v[3], a[1]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 12);
EXPECT_THAT(output(), HasSubstr("s_nop 11"));
clearOutput();
}
}
TEST_F(MFMA950ObserverTest, XDLOPThenFlat)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4_f32", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("flat_store_dword", {}, {v[2], v[3]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 12);
EXPECT_THAT(output(), HasSubstr("s_nop 11"));
clearOutput();
}
}
TEST_F(MFMA950ObserverTest, XDLOPThenVALU_Read)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4_f32", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_or_b32", {v[4]}, {v[2], v[3]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 12);
EXPECT_THAT(output(), HasSubstr("s_nop 11"));
clearOutput();
}
}
TEST_F(MFMA950ObserverTest, XDLOPThenVALU_Write)
{
auto v = createRegisters(Register::Type::Vector, DataType::Float, 5);
auto a = createRegisters(Register::Type::Accumulator, DataType::Float, 2, 4);
{
std::vector<Instruction> insts
= {Instruction("v_mfma_f32_16x16x4_f32", {v[2]}, {v[0], v[1], a[0]}, {}, ""),
Instruction("v_or_b32", {v[2]}, {v[3], v[4]}, {}, ""),
Instruction("s_endpgm", {}, {}, {}, "")};
peekAndSchedule(insts[0]);
peekAndSchedule(insts[1], 12);
EXPECT_THAT(output(), HasSubstr("s_nop 11"));
clearOutput();
}
}
}