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
793 lines
34 KiB
C++
793 lines
34 KiB
C++
// Copyright Advanced Micro Devices, Inc., or its affiliates.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include <cmath>
|
|
#include <memory>
|
|
|
|
#include "CustomMatchers.hpp"
|
|
#include "CustomSections.hpp"
|
|
#include "TestContext.hpp"
|
|
#include "TestKernels.hpp"
|
|
|
|
#include <common/SourceMatcher.hpp>
|
|
#include <common/TestValues.hpp>
|
|
|
|
#include <rocRoller/AssemblyKernel.hpp>
|
|
#include <rocRoller/CodeGen/ArgumentLoader.hpp>
|
|
#include <rocRoller/CodeGen/CopyGenerator.hpp>
|
|
#include <rocRoller/CodeGen/Instruction.hpp>
|
|
#include <rocRoller/CodeGen/MemoryInstructions.hpp>
|
|
#include <rocRoller/Expression.hpp>
|
|
#include <rocRoller/ExpressionTransformations.hpp>
|
|
|
|
#include <catch2/catch_template_test_macros.hpp>
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <catch2/matchers/catch_matchers.hpp>
|
|
#include <catch2/matchers/catch_matchers_contains.hpp>
|
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
|
#include <catch2/matchers/catch_matchers_string.hpp>
|
|
|
|
using namespace rocRoller;
|
|
|
|
namespace HazardObserverTest
|
|
{
|
|
using Catch::Matchers::ContainsSubstring;
|
|
|
|
void peekAndSchedule(TestContext& context, Instruction& inst, uint expectedNops = 0)
|
|
{
|
|
auto peeked = context->observer()->peek(inst);
|
|
CHECK(peeked.nops == expectedNops);
|
|
context->schedule(inst);
|
|
}
|
|
|
|
std::vector<rocRoller::Register::ValuePtr>
|
|
createRegisters(TestContext& context,
|
|
rocRoller::Register::Type const regType,
|
|
rocRoller::DataType const dataType,
|
|
size_t const amount,
|
|
int const regCount = 1,
|
|
rocRoller::Register::AllocationOptions allocOptions = {})
|
|
{
|
|
std::vector<rocRoller::Register::ValuePtr> regs;
|
|
for(size_t i = 0; i < amount; i++)
|
|
{
|
|
auto reg = std::make_shared<rocRoller::Register::Value>(
|
|
context.get(), regType, dataType, regCount, allocOptions);
|
|
try
|
|
{
|
|
reg->allocateNow();
|
|
}
|
|
catch(...)
|
|
{
|
|
std::cout << i << std::endl;
|
|
throw;
|
|
}
|
|
regs.push_back(reg);
|
|
}
|
|
return regs;
|
|
}
|
|
|
|
TEST_CASE("VALUWriteVCC", "[observer]")
|
|
{
|
|
SUPPORTED_ARCH_SECTION(arch)
|
|
{
|
|
if(!arch.isCDNAGPU() || arch.isCDNA5GPU())
|
|
{
|
|
SKIP("Architecture " + arch.toString()
|
|
+ " does not meet requirements for this observer");
|
|
}
|
|
|
|
SECTION("v_readlane (2nd op) read as laneselect")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 5);
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::UInt32, 2);
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction(
|
|
"v_readlane_b32", {s[0]}, {v[0], Register::Value::Literal(0)}, {}, ""),
|
|
Instruction("v_readlane_b32", {s[1]}, {v[1], s[0]}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 4);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 3"));
|
|
}
|
|
|
|
SECTION("v_* (2nd op) read SGPR as constant")
|
|
{
|
|
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 5);
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::UInt32, 2);
|
|
|
|
std::vector<Instruction> insts = {
|
|
Instruction(
|
|
"v_readlane_b32", {s[0]}, {v[0], Register::Value::Literal(0)}, {}, ""),
|
|
Instruction("v_cndmask_b32", {v[0]}, {s[0], v[3], context->getVCC()}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
if(arch.isCDNA1GPU() || arch.isCDNA2GPU())
|
|
{
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 1);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 0"));
|
|
}
|
|
else
|
|
{
|
|
// NOPs are required on 94X arch
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 2);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 1"));
|
|
}
|
|
}
|
|
|
|
SECTION("v_addc* reading VCC as carry")
|
|
{
|
|
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 6);
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction("v_add_co_u32", {v[0], context->getVCC()}, {v[1], v[2]}, {}, ""),
|
|
Instruction("v_addc_co_u32",
|
|
{v[5], context->getVCC()},
|
|
{v[3], v[4], context->getVCC()},
|
|
{},
|
|
""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
|
|
CHECK_THAT(context.output(), !(ContainsSubstring("s_nop")));
|
|
}
|
|
|
|
SECTION("v_subb* reading VCC as carry")
|
|
{
|
|
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 6);
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction("v_sub_co_u32", {v[0], context->getVCC()}, {v[1], v[2]}, {}, ""),
|
|
Instruction("v_subb_co_u32",
|
|
{v[5], context->getVCC()},
|
|
{v[3], v[4], context->getVCC()},
|
|
{},
|
|
""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
|
|
CHECK_THAT(context.output(), !(ContainsSubstring("s_nop")));
|
|
}
|
|
|
|
SECTION("v_addc* reading VCC as non-carry")
|
|
{
|
|
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 6);
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction("v_add_co_u32", {v[0], context->getVCC()}, {v[1], v[2]}, {}, ""),
|
|
Instruction("v_addc_co_u32",
|
|
{v[5], context->getVCC()},
|
|
{v[3], context->getVCC(), context->getVCC()},
|
|
{},
|
|
""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 1);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 0"));
|
|
}
|
|
|
|
SECTION("v_addc* reading SGPR as non-carry")
|
|
{
|
|
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 6);
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::UInt32, 2);
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction("v_add_co_u32", {s[0], context->getVCC()}, {v[1], v[2]}, {}, ""),
|
|
Instruction("v_addc_co_u32",
|
|
{v[5], context->getVCC()},
|
|
{v[3], s[0], context->getVCC()},
|
|
{},
|
|
""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
if(arch.isCDNA1GPU() || arch.isCDNA2GPU())
|
|
{
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 1);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 0"));
|
|
}
|
|
else
|
|
{
|
|
// NOPs are required on 94X arch
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 2);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 1"));
|
|
}
|
|
}
|
|
|
|
SECTION("v_addc* reading SGPR as non-carry")
|
|
{
|
|
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 6);
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::UInt32, 2);
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction("v_add_co_u32", {s[0], context->getVCC()}, {v[1], v[2]}, {}, ""),
|
|
Instruction("v_addc_co_u32",
|
|
{v[5], context->getVCC()},
|
|
{v[3], s[0], context->getVCC()},
|
|
{},
|
|
""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
if(arch.isCDNA1GPU() || arch.isCDNA2GPU())
|
|
{
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 1);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 0"));
|
|
}
|
|
else
|
|
{
|
|
// NOPs are required on 94X arch
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 2);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 1"));
|
|
}
|
|
}
|
|
|
|
SECTION("This example can be found in basic I32 division")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 5);
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::UInt32, 2);
|
|
|
|
std::vector<Instruction> insts = {
|
|
Instruction("v_cmp_ge_u32", {context->getVCC()}, {v[0], v[1]}, {}, ""),
|
|
Instruction("v_cndmask_b32", {v[0]}, {v[2], v[3], context->getVCC()}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 1);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 0"));
|
|
}
|
|
|
|
// TODO Fix implicit VCC observer and enable this test
|
|
#if 0
|
|
SECTION("Implicit VCC write")
|
|
{
|
|
std::vector<Instruction> insts
|
|
= {Instruction("v_add_co_u32", {v[3]}, {v[0], v[1]}, {}, ""),
|
|
Instruction("v_cndmask_b32", {v[4]}, {v[4], v[3], context->getVCC()}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context,insts[0]);
|
|
peekAndSchedule(context,insts[1], 2);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 1"));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
TEST_CASE("VALUTrans94X", "[observer]")
|
|
{
|
|
SUPPORTED_ARCH_SECTION(arch)
|
|
{
|
|
if(!arch.isCDNAGPU() || arch.isCDNA5GPU())
|
|
{
|
|
SKIP("This observer only applies to CDNA4 or earlier archictectures");
|
|
}
|
|
|
|
SECTION("Has hazard with 2nd op (non-trans) accessing the same register")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::Float, 2);
|
|
|
|
std::vector<Instruction> insts = {
|
|
Instruction("v_rcp_iflag_f32", {v[0]}, {v[0]}, {}, ""),
|
|
Instruction(
|
|
"v_mul_f32", {v[0]}, {Register::Value::Literal(0x4f7ffffe), v[0]}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
if(arch.isCDNA3GPU() || arch.isCDNA4GPU())
|
|
{
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 1);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 0"));
|
|
}
|
|
else
|
|
{
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
|
|
CHECK_THAT(context.output(), !(ContainsSubstring("s_nop")));
|
|
}
|
|
}
|
|
|
|
SECTION("No hazard (2nd non-trans op accessing different VGPR)")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::Float, 2);
|
|
|
|
std::vector<Instruction> insts = {
|
|
Instruction("v_rcp_iflag_f32", {v[0]}, {v[0]}, {}, ""),
|
|
Instruction(
|
|
"v_mul_f32", {v[1]}, {Register::Value::Literal(0x4f7ffffe), v[1]}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
|
|
CHECK_THAT(context.output(), !(ContainsSubstring("s_nop")));
|
|
}
|
|
|
|
SECTION("No hazard (2nd op is a trans op)")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::Float, 2);
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction("v_rcp_iflag_f32", {v[0]}, {v[0]}, {}, ""),
|
|
Instruction("v_rcp_f32", {v[0]}, {v[0]}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
|
|
CHECK_THAT(context.output(), !(ContainsSubstring("s_nop")));
|
|
}
|
|
|
|
SECTION("No hazard")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::Float, 2);
|
|
auto vu = createRegisters(context, Register::Type::Vector, DataType::UInt32, 2);
|
|
|
|
std::vector<Instruction> insts = {
|
|
Instruction("v_rcp_iflag_f32", {v[0]}, {v[0]}, {}, ""),
|
|
Instruction("v_add_u32", {vu[0]}, {vu[0], vu[1]}, {}, ""),
|
|
Instruction(
|
|
"v_mul_f32", {v[0]}, {Register::Value::Literal(0x4f7ffffe), v[0]}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
peekAndSchedule(context, insts[2]);
|
|
|
|
CHECK_THAT(context.output(), !(ContainsSubstring("s_nop")));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("VALUWriteReadlane94X", "[observer]")
|
|
{
|
|
SUPPORTED_ARCH_SECTION(arch)
|
|
{
|
|
if(!arch.isCDNAGPU() || arch.isCDNA5GPU())
|
|
{
|
|
SKIP("This observer only applies to CDNA4 or earlier archictectures");
|
|
}
|
|
|
|
SECTION("Hazard with VALU write followed by a readlane or permlane")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 9);
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::UInt32, 2);
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction("v_subrev_u32", {v[0]}, {s[0], v[0]}, {}, ""),
|
|
Instruction(
|
|
"v_readlane_b32", {s[1]}, {v[0], Register::Value::Literal(0)}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
if(arch.isCDNA3GPU() || arch.isCDNA4GPU())
|
|
{
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 1);
|
|
peekAndSchedule(context, insts[2]);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 0"));
|
|
}
|
|
else
|
|
{
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
peekAndSchedule(context, insts[2]);
|
|
|
|
CHECK_THAT(context.output(), !(ContainsSubstring("s_nop")));
|
|
}
|
|
}
|
|
|
|
SECTION("No hazard (accessing different VGPRs)")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 3);
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::UInt32, 2);
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction("v_subrev_u32", {v[0]}, {s[0], v[0]}, {}, ""),
|
|
Instruction(
|
|
"v_readlane_b32", {s[1]}, {v[1], Register::Value::Literal(0)}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
peekAndSchedule(context, insts[2]);
|
|
|
|
CHECK_THAT(context.output(), !(ContainsSubstring("s_nop")));
|
|
}
|
|
|
|
SECTION("Tests for v_permlane*, only on GFX950")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
if(!context->targetArchitecture().HasCapability(GPUCapability::HasPermLanes16)
|
|
&& !context->targetArchitecture().HasCapability(GPUCapability::HasPermLanes32))
|
|
{
|
|
SKIP("Architecture " + arch.toString() + " does not support permlanes.");
|
|
}
|
|
|
|
SECTION("Hazard with 2nd op accessing the same register")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 9);
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::UInt32, 2);
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction("v_subrev_u32", {v[0]}, {s[0], v[0]}, {}, ""),
|
|
Instruction::InoutInstruction(
|
|
"v_permlane32_swap_b32", {v[0], v[1]}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 2);
|
|
peekAndSchedule(context, insts[2]);
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 1"));
|
|
}
|
|
|
|
SECTION("No hazard. Access different registers.")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 9);
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::UInt32, 2);
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction::InoutInstruction(
|
|
"v_permlane16_swap_b32", {v[3], v[4]}, {}, ""),
|
|
Instruction::InoutInstruction(
|
|
"v_permlane32_swap_b32", {v[5], v[6]}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
peekAndSchedule(context, insts[2]);
|
|
|
|
CHECK_THAT(context.output(), !(ContainsSubstring("s_nop")));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("VCMPX_EXEC94X", "[observer]")
|
|
{
|
|
|
|
SUPPORTED_ARCH_SECTION(arch)
|
|
{
|
|
if(!arch.isCDNAGPU() || arch.isCDNA5GPU())
|
|
{
|
|
SKIP("Architecture " + arch.toString()
|
|
+ " does not meet requirements for this observer");
|
|
}
|
|
|
|
SECTION("Hazard on 94X with 2nd op is v_readlane")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 3);
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::UInt32, 2);
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction("v_cmpx_eq_u32", {}, {s[0], v[0]}, {}, ""),
|
|
Instruction(
|
|
"v_readlane_b32", {s[1]}, {v[0], Register::Value::Literal(0)}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
if(arch.isCDNA1GPU() || arch.isCDNA2GPU())
|
|
{
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
|
|
CHECK_THAT(context.output(), !(ContainsSubstring("s_nop")));
|
|
}
|
|
else
|
|
{
|
|
// NOPs are required on 94X arch
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 4);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 3"));
|
|
}
|
|
}
|
|
|
|
SECTION("Hazard on 94X with 2nd op reads EXEC")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 3);
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::UInt32, 2);
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction("v_cmpx_eq_u32", {}, {s[0], v[0]}, {}, ""),
|
|
Instruction("v_xor_b32", {v[1]}, {v[0], context->getEXEC()}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
if(arch.isCDNA1GPU() || arch.isCDNA2GPU())
|
|
{
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
|
|
CHECK_THAT(context.output(), !(ContainsSubstring("s_nop")));
|
|
}
|
|
else
|
|
{
|
|
// NOPs are required on 94X arch
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 2);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 1"));
|
|
}
|
|
}
|
|
|
|
SECTION("No hazard on with 2nd op not reading EXEC")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 3);
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::UInt32, 2);
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction("v_cmpx_eq_u32", {}, {s[0], v[0]}, {}, ""),
|
|
Instruction("v_xor_b32", {v[1]}, {v[0], v[1]}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
|
|
CHECK_THAT(context.output(), !(ContainsSubstring("s_nop")));
|
|
}
|
|
|
|
SECTION("Test v_permlane, only on GFX950 ")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
if(!context->targetArchitecture().HasCapability(GPUCapability::HasPermLanes16)
|
|
&& !context->targetArchitecture().HasCapability(GPUCapability::HasPermLanes32))
|
|
{
|
|
SKIP("Architecture " + arch.toString() + " does not support permlanes.");
|
|
}
|
|
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 3);
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::UInt32, 2);
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction("v_cmpx_eq_u32", {}, {s[0], v[0]}, {}, ""),
|
|
Instruction::InoutInstruction("v_permlane32_swap_b32", {v[1], v[2]}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 4);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 3"));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("OPSELxSDWA94X", "[observer]")
|
|
{
|
|
SUPPORTED_ARCH_SECTION(arch)
|
|
{
|
|
if(!arch.isCDNAGPU() || arch.isCDNA5GPU())
|
|
{
|
|
SKIP("This observer only applies to CDNA4 or earlier archictectures");
|
|
}
|
|
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 2);
|
|
|
|
// OPSEL
|
|
std::vector<Instruction> insts
|
|
= {Instruction("v_fma_f16", {v[1]}, {v[0], v[0], v[0]}, {"op_sel:[0,1,1]"}, ""),
|
|
Instruction("v_mov_b32", {v[0]}, {v[1]}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
if(arch.isCDNA3GPU() || arch.isCDNA4GPU())
|
|
{
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 1);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 0"));
|
|
}
|
|
else
|
|
{
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
|
|
CHECK_THAT(context.output(), !(ContainsSubstring("s_nop")));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_CASE("VALUWriteSGPRVMEM", "[observer]")
|
|
{
|
|
SUPPORTED_ARCH_SECTION(arch)
|
|
{
|
|
if(!arch.isCDNAGPU() || arch.isCDNA5GPU())
|
|
{
|
|
SKIP("Architecture " + arch.toString()
|
|
+ " does not meet requirements for this observer");
|
|
}
|
|
|
|
auto context = TestContext::ForTarget(arch);
|
|
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::UInt32, 2);
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::UInt32, 1);
|
|
|
|
std::vector<Instruction> insts = {
|
|
Instruction("v_readlane_b32", {s[0]}, {v[0], Register::Value::Literal(0)}, {}, ""),
|
|
Instruction(
|
|
"buffer_load_dword", {v[1]}, {s[0], Register::Value::Literal(0)}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1], 5);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop 4"));
|
|
}
|
|
}
|
|
|
|
TEST_CASE("BufferStoreWrite", "[observer]")
|
|
{
|
|
SUPPORTED_ARCH_SECTION(arch)
|
|
{
|
|
if(!TestContext::ForTarget(arch)->targetArchitecture().HasCapability(
|
|
GPUCapability::HasAccCD))
|
|
{
|
|
SKIP("Architecture " + arch.toString() + " does not use Accumulator registers.");
|
|
}
|
|
|
|
SECTION("NOPs added for buffer_store_dwordx4 followed by VALU")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::Float, 1, 4)[0];
|
|
auto addr = createRegisters(context, Register::Type::Vector, DataType::Raw32, 1)[0];
|
|
auto a = createRegisters(
|
|
context, Register::Type::Accumulator, DataType::Float, 1, 4)[0];
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::Raw32, 1, 4)[0];
|
|
|
|
std::vector<Instruction> insts
|
|
= {Instruction("buffer_store_dwordx4",
|
|
{},
|
|
{v, addr, s, Register::Value::Literal(0)},
|
|
{"offen"},
|
|
""),
|
|
Instruction("v_accvgpr_read", {v->subset({0})}, {a->subset({0})}, {}, ""),
|
|
Instruction("v_accvgpr_read", {v->subset({1})}, {a->subset({1})}, {}, ""),
|
|
Instruction("v_accvgpr_read", {v->subset({2})}, {a->subset({2})}, {}, ""),
|
|
Instruction("v_accvgpr_read", {v->subset({3})}, {a->subset({3})}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(
|
|
context, insts[1], (arch.isCDNA1GPU() || arch.isCDNA2GPU()) ? 1 : 2);
|
|
peekAndSchedule(context, insts[2]);
|
|
peekAndSchedule(context, insts[3]);
|
|
peekAndSchedule(context, insts[4]);
|
|
peekAndSchedule(context, insts[5]);
|
|
|
|
CHECK_THAT(context.output(), ContainsSubstring("s_nop"));
|
|
}
|
|
|
|
SECTION("No NOPs for buffer_store_dwordx4 followed by VALU if vdata not accessed")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::Float, 1, 4)[0];
|
|
auto addr = createRegisters(context, Register::Type::Vector, DataType::Raw32, 1)[0];
|
|
auto a = createRegisters(
|
|
context, Register::Type::Accumulator, DataType::Float, 1, 4)[0];
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::Raw32, 1, 4)[0];
|
|
|
|
std::vector<Instruction> insts = {
|
|
Instruction("buffer_store_dwordx4",
|
|
{},
|
|
{v, addr, s, Register::Value::Literal(0)},
|
|
{"offen"},
|
|
""),
|
|
Instruction("v_add_u32", {addr}, {addr, Register::Value::Literal(0)}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
peekAndSchedule(context, insts[2]);
|
|
|
|
CHECK_THAT(context.output(), !ContainsSubstring("s_nop"));
|
|
}
|
|
|
|
SECTION("No NOPs when soffset is an SGPR")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v = createRegisters(context, Register::Type::Vector, DataType::Float, 1, 4)[0];
|
|
auto addr = createRegisters(context, Register::Type::Vector, DataType::Raw32, 1)[0];
|
|
auto a = createRegisters(
|
|
context, Register::Type::Accumulator, DataType::Float, 1, 4)[0];
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::Raw32, 1, 4)[0];
|
|
auto soffset
|
|
= createRegisters(context, Register::Type::Scalar, DataType::Raw32, 1, 1)[0];
|
|
|
|
std::vector<Instruction> insts = {
|
|
Instruction("buffer_store_dwordx4", {}, {v, addr, s, soffset}, {"offen"}, ""),
|
|
Instruction("v_accvgpr_read", {v->subset({0})}, {a->subset({0})}, {}, ""),
|
|
Instruction("v_accvgpr_read", {v->subset({1})}, {a->subset({1})}, {}, ""),
|
|
Instruction("v_accvgpr_read", {v->subset({2})}, {a->subset({2})}, {}, ""),
|
|
Instruction("v_accvgpr_read", {v->subset({3})}, {a->subset({3})}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
peekAndSchedule(context, insts[2]);
|
|
peekAndSchedule(context, insts[3]);
|
|
peekAndSchedule(context, insts[4]);
|
|
peekAndSchedule(context, insts[5]);
|
|
|
|
CHECK_THAT(context.output(), !ContainsSubstring("s_nop"));
|
|
}
|
|
|
|
SECTION("No NOPs after buffer_store_dwordx4")
|
|
{
|
|
auto context = TestContext::ForTarget(arch);
|
|
auto v1
|
|
= createRegisters(context, Register::Type::Vector, DataType::Float, 1, 4)[0];
|
|
auto v2
|
|
= createRegisters(context, Register::Type::Vector, DataType::Float, 1, 4)[0];
|
|
auto addr = createRegisters(context, Register::Type::Vector, DataType::Raw32, 1)[0];
|
|
auto a = createRegisters(
|
|
context, Register::Type::Accumulator, DataType::Float, 1, 4)[0];
|
|
auto s = createRegisters(context, Register::Type::Scalar, DataType::Raw32, 1, 4)[0];
|
|
auto soffset
|
|
= createRegisters(context, Register::Type::Scalar, DataType::Raw32, 1, 1)[0];
|
|
|
|
std::vector<Instruction> insts = {
|
|
Instruction("buffer_store_dwordx4", {}, {v1, addr, s, soffset}, {"offen"}, ""),
|
|
Instruction("v_accvgpr_read", {v2->subset({0})}, {a->subset({0})}, {}, ""),
|
|
Instruction("v_accvgpr_read", {v2->subset({1})}, {a->subset({1})}, {}, ""),
|
|
Instruction("v_accvgpr_read", {v2->subset({2})}, {a->subset({2})}, {}, ""),
|
|
Instruction("v_accvgpr_read", {v2->subset({3})}, {a->subset({3})}, {}, ""),
|
|
Instruction("s_endpgm", {}, {}, {}, "")};
|
|
|
|
peekAndSchedule(context, insts[0]);
|
|
peekAndSchedule(context, insts[1]);
|
|
peekAndSchedule(context, insts[2]);
|
|
peekAndSchedule(context, insts[3]);
|
|
peekAndSchedule(context, insts[4]);
|
|
peekAndSchedule(context, insts[5]);
|
|
|
|
CHECK_THAT(context.output(), !ContainsSubstring("s_nop"));
|
|
}
|
|
}
|
|
}
|
|
}
|