Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "rocjitsu/isa/arch/amdgpu/shared/alu_exceptions.h"
#include "rocjitsu/isa/instruction.h"
#include "rocjitsu/vm/amdgpu/mem_state.h"
#include "rocjitsu/vm/amdgpu/register_access.h"
#include "util/except.h"
#include "util/log.h"

Expand Down Expand Up @@ -812,10 +813,9 @@ void ComputeUnitCore::issue_instruction(Wavefront *active) {
auto mn = std::string_view(inst->mnemonic());
if (mn.find("s_setpc") != std::string_view::npos ||
mn.find("s_swappc") != std::string_view::npos) {
uint32_t ssrc0_idx = words[0] & 0x7F;
uint32_t sb = active->sgpr_alloc().base;
uint64_t target = static_cast<uint64_t>(read_sgpr(sb + ssrc0_idx)) |
(static_cast<uint64_t>(read_sgpr(sb + ssrc0_idx + 1)) << 32);
const Operand *target_operand = inst->src_operand(0);
assert(target_operand && "indirect PC instruction must have a target operand");
uint64_t target = RegisterAccess(*active).read_scalar64(*target_operand);
if (target == 0) {
active->halt();
delete inst;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "rocjitsu/vm/plugins/race_detector/plugin.h"

#include "rocjitsu/isa/arch/amdgpu/shared/scalar_operand_read.h"
#include "rocjitsu/isa/instruction.h"
#include "rocjitsu/vm/amdgpu/compute_unit.h"
#include "rocjitsu/vm/amdgpu/lds.h"
Expand Down Expand Up @@ -337,11 +338,19 @@ void RaceDetectorPlugin::onAmdgpuRouteMemoryInstruction(const Instruction &inst,
if (inst.data()->tag() == amdgpu::SCALAR_MEM) {
auto &d = *inst.data_as<amdgpu::ScalarMemState>();
if (d.is_load) {
uint32_t logicalBase = d.dst_reg_base - wf.sgpr_alloc().base;
std::vector<uint32_t> registers(d.num_dwords);
for (uint32_t i = 0; i < d.num_dwords; ++i)
registers[i] = logicalBase + i;
rs->registerEvent(wf.pc, MemoryEventType::GLOBAL_TO_SGPR, std::move(registers), wf.exec());
// TTMP destinations use the wave-private trap file. They do not produce
// ordinary-SGPR read callbacks, so registering a GLOBAL_TO_SGPR event for
// them would leave an event that no matching consumer can diagnose.
// Modeling pending scalar loads into TTMP is separate race-detector work.
std::vector<uint32_t> registers;
registers.reserve(d.num_dwords);
for (uint32_t i = 0; i < d.num_dwords; ++i) {
const uint32_t selector = d.dst_selector + i;
if (selector < amdgpu::kTtmpSelectorFirst || selector > amdgpu::kTtmpSelectorLast)
registers.push_back(selector);
}
if (!registers.empty())
rs->registerEvent(wf.pc, MemoryEventType::GLOBAL_TO_SGPR, std::move(registers), wf.exec());
}
}
}
Expand Down
41 changes: 41 additions & 0 deletions emulation/rocjitsu/tests/amdgpu_vm_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "embedded_schema.h"
#include "rocjitsu/code/amdgpu_elf.h"
#include "rocjitsu/code/builders/instruction_builder.h"
#include "rocjitsu/code/kernel_descriptor_scan.h"
#include "rocjitsu/code/kernel_symbol.h"
#include "rocjitsu/config/config_loader.h"
Expand Down Expand Up @@ -2120,6 +2121,46 @@ TEST_P(IsaTest, DispatchWfReturnsNullWhenSlotsExhausted) {
EXPECT_EQ(cu->dispatch_wf(0, 0x1040, 104, 256), nullptr);
}

TEST(TrapRegisterPcTest, SetpcPrecheckReadsDecodedTtmpPair) {
amdgpu::GpuMemory mem("cdna4_setpc_ttmp_mem");
amdgpu::L2Cache l2("cdna4_setpc_ttmp_l2");
amdgpu::ComputeUnitCore::Config cfg{};
cfg.arch = ROCJITSU_CODE_ARCH_CDNA4;
cfg.num_wf_slots = 2;
cfg.sgprs_per_wf = 104;
cfg.vgprs_per_wf = 16;
cfg.lds_size_kb = 64;
auto cu = amdgpu::ComputeUnitCore::create("cdna4_setpc_ttmp_cu", cfg, &mem, &l2);
ASSERT_NE(cu, nullptr);

test::HaltSnapshotPlugin *snapshot = nullptr;
cu->set_plugin_group(test::make_halt_snapshot_group(&snapshot));
ASSERT_NE(snapshot, nullptr);

constexpr uint64_t kStartPc = 0x1000;
constexpr uint64_t kTargetPc = kStartPc + 2 * sizeof(uint32_t);
constexpr uint32_t kTtmp0Selector = 108;
const std::array<uint32_t, 4> code = {
build_s_setpc_b64(kTtmp0Selector, ROCJITSU_CODE_ARCH_CDNA4),
build_s_endpgm(ROCJITSU_CODE_ARCH_CDNA4),
build_s_mov_b32(/*sdst=*/0, scalar_positive_inline_u32(1), ROCJITSU_CODE_ARCH_CDNA4),
build_s_endpgm(ROCJITSU_CODE_ARCH_CDNA4),
};
mem.load_image(reinterpret_cast<const uint8_t *>(code.data()), sizeof(code), kStartPc);

auto *wavefront = cu->dispatch_wf(/*wg_id=*/0, kStartPc, cfg.sgprs_per_wf, cfg.vgprs_per_wf);
ASSERT_NE(wavefront, nullptr);
wavefront->set_ttmp(/*TTMP0=*/0, static_cast<uint32_t>(kTargetPc));
wavefront->set_ttmp(/*TTMP1=*/1, static_cast<uint32_t>(kTargetPc >> 32));

for (uint32_t step = 0; step < code.size() && cu->has_active_wfs(); ++step)
static_cast<void>(cu->step());

ASSERT_FALSE(cu->has_active_wfs());
ASSERT_EQ(snapshot->snapshots().size(), 1u);
EXPECT_EQ(snapshot->snapshots()[0].sgpr(0), 1u);
}

TEST(CommandProcessorTest, DispatchToQuiescedDebugHaltedCuReactivatesEventLoop) {
VmFixture f("cdna4", 2, 2);
test::AqlQueue queue(f.mem(), f.cp());
Expand Down
41 changes: 41 additions & 0 deletions emulation/rocjitsu/tests/execution_plugin_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "rocjitsu/isa/arch/amdgpu/shared/dpp_sdwa_ops.h"
#include "rocjitsu/isa/arch/amdgpu/shared/instruction_encoding.h"
#include "rocjitsu/isa/arch/amdgpu/shared/mma_exec.h"
#include "rocjitsu/isa/arch/amdgpu/shared/scalar_operand_read.h"
#include "rocjitsu/isa/decoder.h"
#include "rocjitsu/isa/instruction.h"
#include "rocjitsu/kmd/linux/kfd_process.h"
Expand Down Expand Up @@ -2640,6 +2641,46 @@ TEST(RaceDetectorPluginTest, D16LoadTracksFullDwordWhenSramEccEnabled) {
EXPECT_FALSE(opposite_half_read_reports_race("rdna4", /*wavefront_size=*/32));
}

TEST(RaceDetectorPluginTest, ScalarLoadToTtmpDoesNotRegisterOrdinarySgprHazard) {
PluginFixture f(/*num_wf_slots=*/2);
PluginSinkConfig sink_config;
sink_config.emplace<StringSink>();
f.plugin_group_ = std::make_shared<ExecutionPluginGroup>(std::move(sink_config));
auto detector_plugin = std::make_unique<RaceDetectorPlugin>();
auto *detector_plugin_ptr = detector_plugin.get();
ASSERT_TRUE(f.plugin_group_->add(std::move(detector_plugin)));
f.soc->set_plugin_group(f.plugin_group_);
f.plugin_group_->onInit();

auto *cu = f.cu();
auto *wf = cu->dispatch_wf(/*wg_id=*/0, /*pc=*/0, /*sgprs=*/104, /*vgprs=*/256);
ASSERT_NE(wf, nullptr);
wf->set_exec(1u);
std::array<amdgpu::Wavefront *, 1> waves{wf};
f.plugin_group_->onAmdgpuWorkgroupDispatched(
/*dispatch_id=*/1, /*wg_id=*/0, /*physical_vgpr_count=*/256,
/*physical_sgpr_count=*/104, waves);

constexpr uint32_t kTtmp0Selector = amdgpu::kTtmpSelectorFirst;
auto state = std::make_unique<ScalarMemState>();
state->is_load = true;
state->num_dwords = 1;
state->dst_selector = kTtmp0Selector;
state->dst_reg_base = wf->sgpr_alloc().base + state->dst_selector;
TestMemoryInstruction load(std::move(state));
f.plugin_group_->onAmdgpuRouteMemoryInstruction(load, *wf);

auto *plugin_state =
static_cast<RaceWavefrontState *>(wf->plugin_state(detector_plugin_ptr->slot_index()));
ASSERT_NE(plugin_state, nullptr);
ASSERT_NE(plugin_state->race_state, nullptr);
const auto &events = plugin_state->race_state->getWaveMemoryEvents();
const auto &registry = plugin_state->race_state->getDetector()->events();
EXPECT_TRUE(std::none_of(events.begin(), events.end(), [&](EventId event) {
return registry.type(event) == MemoryEventType::GLOBAL_TO_SGPR;
}));
}

TEST(ExecutionPluginTest, F64SourceReadObservationReportsBothHalves) {
if constexpr (!util::has_stdx_simd) {
GTEST_SKIP() << "<experimental/simd> unavailable";
Expand Down
Loading