Files
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

147 lines
5.7 KiB
C++

// Copyright (c) 2026 Advanced Micro Devices, Inc.
// SPDX-License-Identifier: MIT
// Synthetic, always-on coverage for the callee-body clobber summary.
// ProbeCallable is a plain struct, so these tests construct it directly from
// body words instead of parsing an ELF.
//
// Instruction encodings below are gfx90a ground truth captured from
// `llvm-mc -arch=amdgcn -mcpu=gfx90a -show-encoding`.
#include "rocjitsu/code/patch/probe_callable.h"
#include "rocjitsu/code/patch/probe_clobber.h"
#include "rocjitsu/code/rj_code.h"
#include "rocjitsu/isa/register_set.h"
#include <gtest/gtest.h>
#include <cstdint>
#include <string>
#include <vector>
namespace rocjitsu {
namespace {
// gfx90a instruction encodings (single 32-bit word).
constexpr uint32_t kSMovS5_0 = 0xbe850080; // s_mov_b32 s5, 0
constexpr uint32_t kSWaitcnt0 = 0xbf8c0000; // s_waitcnt 0
constexpr uint32_t kSNop0 = 0xbf800000; // s_nop 0
constexpr uint32_t kSSetpcS30S31 = 0xbe801d1e; // s_setpc_b64 s[30:31]
// Explicit special-state writes. to_register_ref() currently returns nullopt
// for these operand forms, so the summary must recognize them via the operand
// name.
constexpr uint32_t kSMovExecLo_0 = 0xbefe0080; // s_mov_b32 exec_lo, 0
constexpr uint32_t kSMovM0_0 = 0xbefc0080; // s_mov_b32 m0, 0
constexpr uint32_t kSMovVccLo_0 = 0xbeea0080; // s_mov_b32 vcc_lo, 0
constexpr uint32_t kSMovFlatScrLo_0 = 0xbee60080; // s_mov_b32 flat_scratch_lo, 0
constexpr rj_code_arch_t kArch = ROCJITSU_CODE_ARCH_CDNA2;
ProbeCallable make_callable(std::vector<uint32_t> body) {
ProbeCallable callable;
callable.symbol = "rj_nop_probe";
callable.arch = kArch;
callable.body_words = std::move(body);
callable.cc = ProbeCallingConvention::AmdGpuFuncNoArgsReturnS30S31;
return callable;
}
bool has_sgpr(const RegisterSet &set, uint16_t index) {
return set.contains(RegisterRef{RegClass::SGPR, index, 1});
}
// A no-op probe body's clobber summary is empty: no ordinary registers, no
// special state, no private-segment use.
TEST(ProbeClobber, NopProbeSummaryIsEmpty) {
const auto callable = make_callable({kSWaitcnt0, kSSetpcS30S31});
std::string err;
const auto summary = build_probe_clobber_summary(callable, &err);
ASSERT_TRUE(summary.has_value()) << err;
EXPECT_TRUE(summary->ordinary_clobbers.none());
EXPECT_FALSE(summary->touches_exec);
EXPECT_FALSE(summary->touches_vcc);
EXPECT_FALSE(summary->touches_scc);
EXPECT_FALSE(summary->touches_m0);
EXPECT_FALSE(summary->touches_flat_scratch);
EXPECT_FALSE(summary->uses_private_segment);
}
// The summary is decode-derived, not declared: a body that writes s5 reports s5
// (and only s5) as an ordinary clobber.
TEST(ProbeClobber, DerivesOrdinaryClobberFromBody) {
const auto callable = make_callable({kSMovS5_0, kSSetpcS30S31});
std::string err;
const auto summary = build_probe_clobber_summary(callable, &err);
ASSERT_TRUE(summary.has_value()) << err;
EXPECT_TRUE(has_sgpr(summary->ordinary_clobbers, 5));
EXPECT_FALSE(has_sgpr(summary->ordinary_clobbers, 6));
}
// The return-link use (s_setpc_b64 s[30:31]) is a use, not a def, so it must not
// appear in the probe body's ordinary clobbers; the call envelope owns the link
// pair instead.
TEST(ProbeClobber, ReturnLinkIsNotAProbeClobber) {
const auto callable = make_callable({kSNop0, kSSetpcS30S31});
std::string err;
const auto summary = build_probe_clobber_summary(callable, &err);
ASSERT_TRUE(summary.has_value()) << err;
EXPECT_FALSE(has_sgpr(summary->ordinary_clobbers, 30));
EXPECT_FALSE(has_sgpr(summary->ordinary_clobbers, 31));
}
// A body that writes EXEC via an explicit operand must set touches_exec, even
// though to_register_ref() does not map the OPR_SDST_EXEC operand form. This is
// the decoder-gap regression guard: if the name fallback ever silently breaks,
// touches_exec goes false and this fails.
TEST(ProbeClobber, DetectsExplicitExecWrite) {
const auto callable = make_callable({kSMovExecLo_0, kSSetpcS30S31});
std::string err;
const auto summary = build_probe_clobber_summary(callable, &err);
ASSERT_TRUE(summary.has_value()) << err;
EXPECT_TRUE(summary->touches_exec);
EXPECT_FALSE(summary->touches_vcc);
EXPECT_FALSE(summary->touches_m0);
EXPECT_FALSE(summary->touches_flat_scratch);
}
// A body that writes M0 via an explicit operand must set touches_m0.
TEST(ProbeClobber, DetectsExplicitM0Write) {
const auto callable = make_callable({kSMovM0_0, kSSetpcS30S31});
std::string err;
const auto summary = build_probe_clobber_summary(callable, &err);
ASSERT_TRUE(summary.has_value()) << err;
EXPECT_TRUE(summary->touches_m0);
EXPECT_FALSE(summary->touches_exec);
EXPECT_FALSE(summary->touches_vcc);
EXPECT_FALSE(summary->touches_flat_scratch);
}
// A body that writes VCC via an explicit operand must set touches_vcc.
TEST(ProbeClobber, DetectsExplicitVccWrite) {
const auto callable = make_callable({kSMovVccLo_0, kSSetpcS30S31});
std::string err;
const auto summary = build_probe_clobber_summary(callable, &err);
ASSERT_TRUE(summary.has_value()) << err;
EXPECT_TRUE(summary->touches_vcc);
EXPECT_FALSE(summary->touches_exec);
EXPECT_FALSE(summary->touches_m0);
EXPECT_FALSE(summary->touches_flat_scratch);
}
// A body that writes FLAT_SCRATCH via an explicit operand must set
// touches_flat_scratch.
TEST(ProbeClobber, DetectsExplicitFlatScratchWrite) {
const auto callable = make_callable({kSMovFlatScrLo_0, kSSetpcS30S31});
std::string err;
const auto summary = build_probe_clobber_summary(callable, &err);
ASSERT_TRUE(summary.has_value()) << err;
EXPECT_TRUE(summary->touches_flat_scratch);
EXPECT_FALSE(summary->touches_exec);
EXPECT_FALSE(summary->touches_vcc);
EXPECT_FALSE(summary->touches_m0);
}
} // namespace
} // namespace rocjitsu