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
1001 lines
27 KiB
C++
1001 lines
27 KiB
C++
// MIT License
|
|
//
|
|
// Copyright (c) 2024-2026 Advanced Micro Devices, Inc. All rights reserved.
|
|
//
|
|
// 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 <array>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <vector>
|
|
#include "gfx10/gfx10parser.h"
|
|
#include "gfx10/gfx10token.h"
|
|
#include "gfx11/gfx11parser.h"
|
|
#include "gfx11/gfx11token.h"
|
|
#include "gfx12/gfx12parser.h"
|
|
#include "gfx12/gfx12token.h"
|
|
#include "mi400/mi400token.h"
|
|
#include "quick_scan_export.hpp"
|
|
|
|
// Helper class to build token streams bit by bit
|
|
class TokenStreamBuilder
|
|
{
|
|
public:
|
|
TokenStreamBuilder() : bit_pos(0) {}
|
|
|
|
// Write bits LSB first (matching the token encoding format)
|
|
void writeBits(uint64_t value, int num_bits)
|
|
{
|
|
for (int i = 0; i < num_bits; i++)
|
|
{
|
|
size_t byte_idx = bit_pos / 8;
|
|
size_t bit_idx = bit_pos % 8;
|
|
|
|
// Ensure buffer is large enough
|
|
if (byte_idx >= buffer.size()) buffer.resize(byte_idx + 1, 0);
|
|
|
|
// Write bit (LSB first)
|
|
if (value & (1ull << i)) buffer[byte_idx] |= (1 << bit_idx);
|
|
|
|
bit_pos++;
|
|
}
|
|
}
|
|
|
|
// Pad to next byte boundary
|
|
void padToByteBoundary()
|
|
{
|
|
while (bit_pos % 8 != 0)
|
|
{
|
|
size_t byte_idx = bit_pos / 8;
|
|
if (byte_idx >= buffer.size()) buffer.resize(byte_idx + 1, 0);
|
|
bit_pos++;
|
|
}
|
|
}
|
|
|
|
const uint8_t* data() const { return buffer.data(); }
|
|
size_t size() const { return buffer.size(); }
|
|
|
|
// Finalize the buffer - pad to byte boundary
|
|
void finalize() { padToByteBoundary(); }
|
|
|
|
private:
|
|
std::vector<uint8_t> buffer;
|
|
size_t bit_pos;
|
|
};
|
|
|
|
// Bit encodings for gfx10 tokens (LSB first)
|
|
// VALU_INST: {1,1,0} = 0b011 (3 bits)
|
|
// TIME: {0,0,0,1} = 0b1000 (4 bits)
|
|
// NOP: {0,0,0,0} = 0b0000 (4 bits)
|
|
// WAVE_START: {0,0,1,1,0} = 0b01100 (5 bits)
|
|
// WAVE_END: {1,0,1,0,1} = 0b10101 (5 bits)
|
|
// INST: {0,1,0} = 0b010 (3 bits)
|
|
|
|
//=============================================================================
|
|
// GFX10 Token Stream Tests
|
|
// These tests verify token decoding with exact buffer sizes (no byte padding).
|
|
// The parser uses readOne_unsafe() when 64+ bits of padding remain, and
|
|
// readOne_safe() when near buffer end (returns 0 for out-of-bounds reads).
|
|
//=============================================================================
|
|
|
|
TEST(Gfx10TokenStreamTest, DecodesSingleValuInst)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
// VALU_INST encoding: {1,1,0} = 0b011, token length = 12 bits
|
|
uint64_t token_data = 0;
|
|
token_data |= 0b011; // header (3 bits): VALU_INST
|
|
token_data |= (0b101 << 3); // tm (3 bits): time delta = 5
|
|
token_data |= (0 << 6); // w64h (1 bit)
|
|
token_data |= (7 << 7); // wid (5 bits): wave id = 7
|
|
|
|
builder.writeBits(token_data, 12);
|
|
builder.finalize(); // Align to byte for valid buffer
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::VALU_INST);
|
|
|
|
valu_inst_type valu{.raw = token.contents};
|
|
EXPECT_EQ(valu.wid, 7);
|
|
}
|
|
|
|
TEST(Gfx10TokenStreamTest, DecodesMultipleTokens)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
// Token 1: VALU_INST (12 bits)
|
|
uint64_t valu_data = 0;
|
|
valu_data |= 0b011; // header: VALU_INST
|
|
valu_data |= (0b010 << 3); // tm = 2
|
|
valu_data |= (0 << 6); // w64h
|
|
valu_data |= (5 << 7); // wid = 5
|
|
builder.writeBits(valu_data, 12);
|
|
|
|
// Token 2: IMM_ONE (12 bits)
|
|
uint64_t imm_data = 0;
|
|
imm_data |= 0b1101; // header: IMM_ONE
|
|
imm_data |= (0b010 << 4); // tm = 2
|
|
imm_data |= (13 << 7); // wid = 13
|
|
builder.writeBits(imm_data, 12);
|
|
|
|
builder.finalize();
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token1 = gen.next();
|
|
EXPECT_EQ(token1.type, RdnaType::VALU_INST);
|
|
valu_inst_type valu{.raw = token1.contents};
|
|
EXPECT_EQ(valu.wid, 5);
|
|
|
|
auto token2 = gen.next();
|
|
EXPECT_EQ(token2.type, RdnaType::IMM_ONE);
|
|
immed_one_type imm{.raw = token2.contents};
|
|
EXPECT_EQ(imm.wid, 13);
|
|
}
|
|
|
|
TEST(Gfx10TokenStreamTest, DecodesWaveStartToken)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
// WAVE_START: 32 bits total
|
|
uint64_t ws_data = 0;
|
|
ws_data |= 0b01100; // header: WAVE_START (5 bits)
|
|
ws_data |= (0b11 << 5); // tm = 3 (2 bits)
|
|
ws_data |= (1ull << 7); // sa = 1
|
|
ws_data |= (2ull << 8); // simd = 2 (2 bits)
|
|
ws_data |= (5ull << 10); // wgp = 5 (3 bits)
|
|
ws_data |= (10ull << 13); // wid = 10 (5 bits)
|
|
ws_data |= (1ull << 18); // queue = 1 (3 bits)
|
|
ws_data |= (2ull << 21); // pipe = 2 (2 bits)
|
|
ws_data |= (1ull << 23); // me = 1
|
|
ws_data |= (0ull << 24); // dispatcher = 0
|
|
ws_data |= (15ull << 25); // count = 15 (7 bits)
|
|
|
|
builder.writeBits(ws_data, 32);
|
|
// 32 bits = 4 bytes, already aligned
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::WAVE_START);
|
|
|
|
gfx10::wstart_type ws{.raw = token.contents};
|
|
EXPECT_EQ(ws.sa, 1);
|
|
EXPECT_EQ(ws.simd, 2);
|
|
EXPECT_EQ(ws.wgp, 5);
|
|
EXPECT_EQ(ws.wid, 10);
|
|
EXPECT_EQ(ws.pipe, 2);
|
|
EXPECT_EQ(ws.me, 1);
|
|
}
|
|
|
|
TEST(Gfx10TokenStreamTest, DecodesWaveEndToken)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
// WAVE_END: 20 bits
|
|
uint64_t we_data = 0;
|
|
we_data |= 0b10101; // header: WAVE_END (5 bits)
|
|
we_data |= (0b011 << 5); // tm = 3 (3 bits)
|
|
we_data |= (0ull << 8); // sa = 0
|
|
we_data |= (3ull << 9); // simd = 3 (2 bits)
|
|
we_data |= (7ull << 11); // wgp = 7 (3 bits)
|
|
we_data |= (0ull << 14); // unused
|
|
we_data |= (12ull << 15); // wid = 12 (5 bits)
|
|
|
|
builder.writeBits(we_data, 20);
|
|
builder.finalize(); // 20 bits -> 3 bytes
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::WAVE_END);
|
|
|
|
gfx10::wend_type we{.raw = token.contents};
|
|
EXPECT_EQ(we.sa, 0);
|
|
EXPECT_EQ(we.simd, 3);
|
|
EXPECT_EQ(we.wgp, 7);
|
|
EXPECT_EQ(we.wid, 12);
|
|
}
|
|
|
|
TEST(Gfx10TokenStreamTest, DecodesInstToken)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
// INST: 20 bits
|
|
uint64_t inst_data = 0;
|
|
inst_data |= 0b010; // header: INST (3 bits)
|
|
inst_data |= (0ull << 3); // unused (1 bit)
|
|
inst_data |= (0b101 << 4); // tm = 5 (3 bits)
|
|
inst_data |= (1ull << 7); // w64h = 1
|
|
inst_data |= (15ull << 8); // wid = 15 (5 bits)
|
|
inst_data |= (42ull << 13); // inst = 42 (7 bits)
|
|
|
|
builder.writeBits(inst_data, 20);
|
|
builder.finalize();
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::INST);
|
|
|
|
gfx10::inst_type inst{.raw = token.contents};
|
|
EXPECT_EQ(inst.w64h, 1);
|
|
EXPECT_EQ(inst.wid, 15);
|
|
EXPECT_EQ(inst.inst, 42);
|
|
}
|
|
|
|
TEST(Gfx10TokenStreamTest, SkipsNopTokens)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
// NOP: 4 bits
|
|
builder.writeBits(0b0000, 4);
|
|
|
|
// VALU_INST: 12 bits
|
|
uint64_t valu_data = 0;
|
|
valu_data |= 0b011;
|
|
valu_data |= (0b001 << 3);
|
|
valu_data |= (0 << 6);
|
|
valu_data |= (3 << 7);
|
|
builder.writeBits(valu_data, 12);
|
|
|
|
builder.finalize();
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::VALU_INST);
|
|
valu_inst_type valu{.raw = token.contents};
|
|
EXPECT_EQ(valu.wid, 3);
|
|
}
|
|
|
|
TEST(Gfx10TokenStreamTest, TimeTokenUpdatesGlobalTime)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
// TIME: 8 bits
|
|
uint64_t time_data = 0;
|
|
time_data |= 0b1000;
|
|
time_data |= (0b1010 << 4);
|
|
builder.writeBits(time_data, 8);
|
|
|
|
// VALU_INST: 12 bits
|
|
uint64_t valu_data = 0;
|
|
valu_data |= 0b011;
|
|
valu_data |= (0b000 << 3);
|
|
valu_data |= (0 << 6);
|
|
valu_data |= (1 << 7);
|
|
builder.writeBits(valu_data, 12);
|
|
|
|
builder.finalize();
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::VALU_INST);
|
|
EXPECT_GT(token.time, 0);
|
|
}
|
|
|
|
//=============================================================================
|
|
// GFX11 Token Stream Tests
|
|
//=============================================================================
|
|
|
|
TEST(Gfx11TokenStreamTest, DecodesSingleValuInst)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
uint64_t token_data = 0;
|
|
token_data |= 0b011;
|
|
token_data |= (0b100 << 3);
|
|
token_data |= (1 << 6);
|
|
token_data |= (9 << 7);
|
|
|
|
builder.writeBits(token_data, 12);
|
|
builder.finalize();
|
|
|
|
gfx11::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::VALU_INST);
|
|
|
|
valu_inst_type valu{.raw = token.contents};
|
|
EXPECT_EQ(valu.wid, 9);
|
|
EXPECT_EQ(valu.w64h, 1);
|
|
}
|
|
|
|
TEST(Gfx11TokenStreamTest, DecodesMultipleTokensSequence)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
// WAVE_START (32 bits)
|
|
uint64_t ws_data = 0;
|
|
ws_data |= 0b01100;
|
|
ws_data |= (0b10 << 5);
|
|
ws_data |= (0ull << 7);
|
|
ws_data |= (1ull << 8);
|
|
ws_data |= (3ull << 10);
|
|
ws_data |= (4ull << 13);
|
|
ws_data |= (0ull << 18);
|
|
ws_data |= (1ull << 21);
|
|
ws_data |= (0ull << 23);
|
|
ws_data |= (0ull << 24);
|
|
ws_data |= (20ull << 25);
|
|
builder.writeBits(ws_data, 32);
|
|
|
|
// INST (20 bits)
|
|
uint64_t inst_data = 0;
|
|
inst_data |= 0b010;
|
|
inst_data |= (0ull << 3);
|
|
inst_data |= (0b011 << 4);
|
|
inst_data |= (0ull << 7);
|
|
inst_data |= (4ull << 8);
|
|
inst_data |= (10ull << 13);
|
|
builder.writeBits(inst_data, 20);
|
|
|
|
// WAVE_END (20 bits)
|
|
uint64_t we_data = 0;
|
|
we_data |= 0b10101;
|
|
we_data |= (0b010 << 5);
|
|
we_data |= (0ull << 8);
|
|
we_data |= (1ull << 9);
|
|
we_data |= (3ull << 11);
|
|
we_data |= (0ull << 14);
|
|
we_data |= (4ull << 15);
|
|
builder.writeBits(we_data, 20);
|
|
|
|
builder.finalize();
|
|
|
|
gfx11::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token1 = gen.next();
|
|
EXPECT_EQ(token1.type, RdnaType::WAVE_START);
|
|
gfx10::wstart_type ws{.raw = token1.contents};
|
|
EXPECT_EQ(ws.wid, 4);
|
|
EXPECT_EQ(ws.wgp, 3);
|
|
|
|
auto token2 = gen.next();
|
|
EXPECT_EQ(token2.type, RdnaType::INST);
|
|
gfx10::inst_type inst{.raw = token2.contents};
|
|
EXPECT_EQ(inst.wid, 4);
|
|
EXPECT_EQ(inst.inst, 10);
|
|
|
|
auto token3 = gen.next();
|
|
EXPECT_EQ(token3.type, RdnaType::WAVE_END);
|
|
gfx10::wend_type we{.raw = token3.contents};
|
|
EXPECT_EQ(we.wid, 4);
|
|
}
|
|
|
|
//=============================================================================
|
|
// GFX12 Token Stream Tests
|
|
//=============================================================================
|
|
|
|
TEST(Gfx12TokenStreamTest, DecodesSingleValuInst)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
uint64_t token_data = 0;
|
|
token_data |= 0b011;
|
|
token_data |= (0b110 << 3);
|
|
token_data |= (0 << 6);
|
|
token_data |= (11 << 7);
|
|
|
|
builder.writeBits(token_data, 12);
|
|
builder.finalize();
|
|
|
|
gfx12::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::VALU_INST);
|
|
|
|
valu_inst_type valu{.raw = token.contents};
|
|
EXPECT_EQ(valu.wid, 11);
|
|
}
|
|
|
|
#if ROCPROF_TRACE_DECODER_QUICK_SCAN_HAS_SIMD
|
|
TEST(Gfx12QuickScanTest, CapturesRareTokenByteOffset)
|
|
{
|
|
if (!quick_scan::avx512_available()) GTEST_SKIP() << "quick_scan requires AVX-512";
|
|
|
|
TokenStreamBuilder builder;
|
|
header_type header{};
|
|
header.header = 0b0010001;
|
|
header.version = 4;
|
|
for (size_t i = 0; i < 4; ++i) builder.writeBits(header.raw, 64);
|
|
|
|
builder.writeBits(0, 4);
|
|
builder.writeBits(0, 4);
|
|
|
|
gfx10::reg_init_type reg{};
|
|
reg.header = 0b1110001;
|
|
builder.writeBits(reg.raw, 64);
|
|
builder.finalize();
|
|
|
|
std::array<quick_scan::QuickToken, 4> out{};
|
|
const size_t n = quick_scan::scan_gfx12(builder.data(), builder.size(), out.data(), out.size());
|
|
|
|
ASSERT_EQ(n, 1);
|
|
EXPECT_EQ(out[0].type, RdnaType::REG_INIT);
|
|
EXPECT_EQ(out[0].offset, 33);
|
|
}
|
|
|
|
TEST(MI400QuickScanTest, CapturesRareTokenByteOffset)
|
|
{
|
|
if (!quick_scan::avx512_available()) GTEST_SKIP() << "quick_scan requires AVX-512";
|
|
|
|
gfx10::reg_init_type reg{};
|
|
reg.header = 0b1110001;
|
|
|
|
std::array<uint8_t, 9> buffer{};
|
|
std::memcpy(buffer.data() + 1, ®.raw, sizeof(reg.raw));
|
|
|
|
std::array<quick_scan::QuickToken, 4> out{};
|
|
const size_t n = quick_scan::scan_mi400(buffer.data(), buffer.size(), out.data(), out.size());
|
|
|
|
ASSERT_EQ(n, 1);
|
|
EXPECT_EQ(out[0].type, RdnaType::REG_INIT);
|
|
EXPECT_EQ(out[0].offset, 1);
|
|
}
|
|
|
|
static void expectBuildStandalonePrependsHeader(uint64_t header)
|
|
{
|
|
if (!quick_scan::avx512_available()) GTEST_SKIP() << "quick_scan requires AVX-512";
|
|
|
|
std::array<uint8_t, 24> data{};
|
|
std::memcpy(data.data(), &header, sizeof(header));
|
|
|
|
rocprof_trace_decoder_handle_t handle{};
|
|
ASSERT_EQ(rocprof_trace_decoder_create_handle(&handle), ROCPROFILER_THREAD_TRACE_DECODER_STATUS_SUCCESS);
|
|
|
|
std::array<uint8_t, 512> out{};
|
|
uint64_t out_size = out.size();
|
|
const auto status = rocprof_trace_decoder_build_standalone(
|
|
handle, 0, data.data(), data.size(), 8, data.size(), out.data(), &out_size
|
|
);
|
|
|
|
EXPECT_EQ(status, ROCPROFILER_THREAD_TRACE_DECODER_STATUS_SUCCESS);
|
|
ASSERT_GE(out_size, sizeof(header));
|
|
EXPECT_EQ(std::memcmp(out.data(), &header, sizeof(header)), 0);
|
|
ASSERT_GE(out_size, sizeof(header) + (data.size() - 8));
|
|
EXPECT_EQ(std::memcmp(out.data() + out_size - (data.size() - 8), data.data() + 8, data.size() - 8), 0);
|
|
|
|
EXPECT_EQ(rocprof_trace_decoder_destroy_handle(handle), ROCPROFILER_THREAD_TRACE_DECODER_STATUS_SUCCESS);
|
|
}
|
|
|
|
TEST(Gfx12BuildStandaloneTest, PrependsInputHeader)
|
|
{
|
|
header_type header{};
|
|
header.header = 0b0010001;
|
|
header.version = 4;
|
|
expectBuildStandalonePrependsHeader(header.raw);
|
|
}
|
|
|
|
TEST(MI400BuildStandaloneTest, PrependsInputHeader)
|
|
{
|
|
mi400::header_type header{};
|
|
header.header = 0b0010001;
|
|
header.version = 5;
|
|
expectBuildStandalonePrependsHeader(header.raw);
|
|
}
|
|
#endif
|
|
|
|
TEST(Gfx12TokenStreamTest, DecodesWaveStartGfx12)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
uint64_t ws_data = 0;
|
|
ws_data |= 0b01100;
|
|
ws_data |= (0b01 << 5);
|
|
ws_data |= (1ull << 7);
|
|
ws_data |= (0ull << 8);
|
|
ws_data |= (6ull << 10);
|
|
ws_data |= (0ull << 14);
|
|
ws_data |= (8ull << 15);
|
|
ws_data |= (3ull << 20);
|
|
ws_data |= (25ull << 25);
|
|
|
|
builder.writeBits(ws_data, 32);
|
|
|
|
gfx12::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::WAVE_START);
|
|
|
|
gfx12::wstart_type ws{.raw = token.contents};
|
|
EXPECT_EQ(ws.sa, 1);
|
|
EXPECT_EQ(ws.simd, 0);
|
|
EXPECT_EQ(ws.wgp, 6);
|
|
EXPECT_EQ(ws.wid, 8);
|
|
}
|
|
|
|
TEST(Gfx12TokenStreamTest, DecodesInstGfx12)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
uint64_t inst_data = 0;
|
|
inst_data |= 0b010;
|
|
inst_data |= (0b100 << 3);
|
|
inst_data |= (1ull << 6);
|
|
inst_data |= (20ull << 7);
|
|
inst_data |= (100ull << 12);
|
|
|
|
builder.writeBits(inst_data, 20);
|
|
builder.finalize();
|
|
|
|
gfx12::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::INST);
|
|
|
|
gfx12::inst_type inst{.raw = token.contents};
|
|
EXPECT_EQ(inst.w64h, 1);
|
|
EXPECT_EQ(inst.wid, 20);
|
|
EXPECT_EQ(inst.inst, 100);
|
|
}
|
|
|
|
TEST(Gfx12TokenStreamTest, DecodesCompleteWaveLifecycle)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
// WAVE_START (32 bits)
|
|
uint64_t ws_data = 0;
|
|
ws_data |= 0b01100;
|
|
ws_data |= (0b01 << 5);
|
|
ws_data |= (0ull << 7);
|
|
ws_data |= (2ull << 8);
|
|
ws_data |= (4ull << 10);
|
|
ws_data |= (0ull << 14);
|
|
ws_data |= (5ull << 15);
|
|
ws_data |= (0ull << 20);
|
|
ws_data |= (10ull << 25);
|
|
builder.writeBits(ws_data, 32);
|
|
|
|
// 3x INST (20 bits each)
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
uint64_t inst_data = 0;
|
|
inst_data |= 0b010;
|
|
inst_data |= ((i + 1) << 3);
|
|
inst_data |= (0ull << 6);
|
|
inst_data |= (5ull << 7);
|
|
inst_data |= ((i * 10) << 12);
|
|
builder.writeBits(inst_data, 20);
|
|
}
|
|
|
|
// WAVE_END (20 bits)
|
|
uint64_t we_data = 0;
|
|
we_data |= 0b10101;
|
|
we_data |= (0b001 << 5);
|
|
we_data |= (0ull << 8);
|
|
we_data |= (2ull << 9);
|
|
we_data |= (4ull << 11);
|
|
we_data |= (0ull << 14);
|
|
we_data |= (5ull << 15);
|
|
builder.writeBits(we_data, 20);
|
|
|
|
builder.finalize();
|
|
|
|
gfx12::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::WAVE_START);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::INST);
|
|
}
|
|
|
|
token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::WAVE_END);
|
|
}
|
|
|
|
//=============================================================================
|
|
// Edge Cases - Basic
|
|
//=============================================================================
|
|
|
|
TEST(TokenStreamEdgeCaseTest, HandlesNopPadding)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
// Multiple NOPs (4 bits each) followed by a real token
|
|
for (int i = 0; i < 5; i++) builder.writeBits(0b0000, 4);
|
|
|
|
// VALU_INST (12 bits)
|
|
uint64_t valu_data = 0;
|
|
valu_data |= 0b011;
|
|
valu_data |= (0b111 << 3);
|
|
valu_data |= (0 << 6);
|
|
valu_data |= (1 << 7);
|
|
builder.writeBits(valu_data, 12);
|
|
|
|
builder.finalize();
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::VALU_INST);
|
|
}
|
|
|
|
TEST(TokenStreamEdgeCaseTest, TimeAccumulatesCorrectly)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
// Two VALU_INST tokens (12 bits each)
|
|
uint64_t valu1 = 0;
|
|
valu1 |= 0b011;
|
|
valu1 |= (0b000 << 3);
|
|
valu1 |= (0 << 6);
|
|
valu1 |= (1 << 7);
|
|
builder.writeBits(valu1, 12);
|
|
|
|
uint64_t valu2 = 0;
|
|
valu2 |= 0b011;
|
|
valu2 |= (0b101 << 3);
|
|
valu2 |= (0 << 6);
|
|
valu2 |= (2 << 7);
|
|
builder.writeBits(valu2, 12);
|
|
|
|
builder.finalize();
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token1 = gen.next();
|
|
auto token2 = gen.next();
|
|
|
|
EXPECT_GT(token2.time, token1.time);
|
|
}
|
|
|
|
TEST(TokenStreamEdgeCaseTest, AllNopBuffer)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
// Buffer full of NOPs (4 bits each)
|
|
for (int i = 0; i < 32; i++) builder.writeBits(0b0000, 4);
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
int iterations = 0;
|
|
while (gen.nextValid() && iterations < 100)
|
|
{
|
|
gen.next();
|
|
iterations++;
|
|
}
|
|
EXPECT_LT(iterations, 100);
|
|
}
|
|
|
|
TEST(TokenStreamEdgeCaseTest, MaxBitfieldValues)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
// VALU_INST with max values
|
|
uint64_t valu = 0;
|
|
valu |= 0b011;
|
|
valu |= (0b111 << 3);
|
|
valu |= (1 << 6);
|
|
valu |= (31 << 7);
|
|
builder.writeBits(valu, 12);
|
|
|
|
builder.finalize();
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::VALU_INST);
|
|
|
|
valu_inst_type parsed{.raw = token.contents};
|
|
EXPECT_EQ(parsed.wid, 31);
|
|
EXPECT_EQ(parsed.w64h, 1);
|
|
EXPECT_EQ(parsed.tm, 7);
|
|
}
|
|
|
|
TEST(TokenStreamEdgeCaseTest, AlternatingTokenTypes)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
// VALU_INST (12 bits)
|
|
uint64_t valu = 0;
|
|
valu |= 0b011;
|
|
valu |= (i << 3);
|
|
valu |= (0 << 6);
|
|
valu |= (i << 7);
|
|
builder.writeBits(valu, 12);
|
|
|
|
// INST (20 bits)
|
|
uint64_t inst = 0;
|
|
inst |= 0b010;
|
|
inst |= (0 << 3);
|
|
inst |= (i << 4);
|
|
inst |= (0 << 7);
|
|
inst |= (i << 8);
|
|
inst |= (i << 13);
|
|
builder.writeBits(inst, 20);
|
|
}
|
|
|
|
builder.finalize();
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
auto valu_token = gen.next();
|
|
EXPECT_EQ(valu_token.type, RdnaType::VALU_INST);
|
|
|
|
auto inst_token = gen.next();
|
|
EXPECT_EQ(inst_token.type, RdnaType::INST);
|
|
}
|
|
}
|
|
|
|
TEST(Gfx10TokenStreamEdgeCaseTest, TimestampToken)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
// TIMESTAMP (64 bits)
|
|
uint64_t timestamp_value = 0x123456789ABCull;
|
|
uint64_t ts_data = 0;
|
|
ts_data |= 0b0000001;
|
|
ts_data |= (timestamp_value << 7);
|
|
builder.writeBits(ts_data, 64);
|
|
|
|
// VALU_INST (12 bits)
|
|
uint64_t valu = 0;
|
|
valu |= 0b011;
|
|
valu |= (0 << 3);
|
|
valu |= (0 << 6);
|
|
valu |= (1 << 7);
|
|
builder.writeBits(valu, 12);
|
|
|
|
builder.finalize();
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_GE(token.time, 0);
|
|
}
|
|
|
|
TEST(Gfx12TokenStreamEdgeCaseTest, Gfx12SpecificInst)
|
|
{
|
|
TokenStreamBuilder builder;
|
|
|
|
// INST with 8-bit inst field (20 bits)
|
|
uint64_t inst_data = 0;
|
|
inst_data |= 0b010;
|
|
inst_data |= (0b011 << 3);
|
|
inst_data |= (1ull << 6);
|
|
inst_data |= (25ull << 7);
|
|
inst_data |= (200ull << 12);
|
|
|
|
builder.writeBits(inst_data, 20);
|
|
builder.finalize();
|
|
|
|
gfx12::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::INST);
|
|
|
|
gfx12::inst_type inst{.raw = token.contents};
|
|
EXPECT_EQ(inst.inst, 200);
|
|
}
|
|
|
|
//=============================================================================
|
|
// Safe/Unsafe Reader Boundary Tests
|
|
// The parser uses readOne_unsafe() when 64+ bits of buffer remain (bufferPadded).
|
|
// When near buffer end, it uses readOne_safe() which returns 0 for OOB reads.
|
|
// Minimum buffer size is 8 bytes (64 bits) for the read window.
|
|
//=============================================================================
|
|
|
|
TEST(TokenStreamBoundaryTest, SmallTokenInMinBuffer)
|
|
{
|
|
// A 12-bit VALU_INST in a small buffer (no padding needed)
|
|
TokenStreamBuilder builder;
|
|
|
|
uint64_t valu = 0;
|
|
valu |= 0b011;
|
|
valu |= (0b011 << 3);
|
|
valu |= (0 << 6);
|
|
valu |= (7 << 7);
|
|
builder.writeBits(valu, 12);
|
|
builder.finalize();
|
|
|
|
EXPECT_EQ(builder.size(), 2u); // 12 bits -> 2 bytes
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::VALU_INST);
|
|
valu_inst_type parsed{.raw = token.contents};
|
|
EXPECT_EQ(parsed.wid, 7);
|
|
}
|
|
|
|
TEST(TokenStreamBoundaryTest, MultipleSmallTokensInMinBuffer)
|
|
{
|
|
// Multiple 12-bit tokens in a small buffer (no padding needed)
|
|
TokenStreamBuilder builder;
|
|
|
|
// 3 VALU_INST tokens (12 bits each = 36 bits)
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
uint64_t valu = 0;
|
|
valu |= 0b011;
|
|
valu |= (i << 3);
|
|
valu |= (0 << 6);
|
|
valu |= ((i + 1) << 7);
|
|
builder.writeBits(valu, 12);
|
|
}
|
|
builder.finalize();
|
|
|
|
EXPECT_EQ(builder.size(), 5u); // 36 bits -> 5 bytes
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::VALU_INST);
|
|
valu_inst_type parsed{.raw = token.contents};
|
|
EXPECT_EQ(parsed.wid, (uint64_t) (i + 1));
|
|
}
|
|
}
|
|
|
|
TEST(TokenStreamBoundaryTest, Max64BitToken)
|
|
{
|
|
// A 64-bit TIMESTAMP fills exactly 8 bytes
|
|
TokenStreamBuilder builder;
|
|
|
|
uint64_t ts = 0;
|
|
ts |= 0b0000001; // TIMESTAMP header
|
|
ts |= (0xABCDEF123456ull << 7);
|
|
builder.writeBits(ts, 64);
|
|
|
|
EXPECT_GE(builder.size(), 8u);
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
// TIMESTAMP is consumed internally - verify no crash
|
|
while (gen.nextValid()) gen.next();
|
|
}
|
|
|
|
TEST(TokenStreamBoundaryTest, TokenCrossingSafeUnsafeBoundary)
|
|
{
|
|
// >8 bytes buffer where first token uses unsafe, second uses safe reader
|
|
TokenStreamBuilder builder;
|
|
|
|
// 64-bit TIMESTAMP (fills initial 8 bytes, uses unsafe reader)
|
|
uint64_t ts = 0;
|
|
ts |= 0b0000001;
|
|
ts |= (0x12345ull << 7);
|
|
builder.writeBits(ts, 64);
|
|
|
|
// 12-bit VALU_INST (at bit 64, uses safe reader as <64 bits remain)
|
|
uint64_t valu = 0;
|
|
valu |= 0b011;
|
|
valu |= (0b111 << 3);
|
|
valu |= (1 << 6);
|
|
valu |= (20 << 7);
|
|
builder.writeBits(valu, 12);
|
|
|
|
builder.finalize();
|
|
|
|
// 76 bits -> 10 bytes (padded to byte boundary)
|
|
EXPECT_EQ(builder.size(), 10u);
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
// TIMESTAMP consumed internally, VALU_INST returned
|
|
auto token = gen.next();
|
|
EXPECT_EQ(token.type, RdnaType::VALU_INST);
|
|
valu_inst_type parsed{.raw = token.contents};
|
|
EXPECT_EQ(parsed.wid, 20);
|
|
}
|
|
|
|
TEST(TokenStreamBoundaryTest, LargerBufferWithMixedTokens)
|
|
{
|
|
// A larger buffer (>16 bytes) with multiple token types
|
|
TokenStreamBuilder builder;
|
|
|
|
// WAVE_START (32 bits)
|
|
uint64_t ws = 0;
|
|
ws |= 0b01100;
|
|
ws |= (0b01 << 5);
|
|
ws |= (1ull << 7);
|
|
ws |= (1ull << 8);
|
|
ws |= (3ull << 10);
|
|
ws |= (15ull << 13);
|
|
ws |= (0ull << 18);
|
|
ws |= (1ull << 21);
|
|
ws |= (0ull << 23);
|
|
ws |= (0ull << 24);
|
|
ws |= (5ull << 25);
|
|
builder.writeBits(ws, 32);
|
|
|
|
// INST (20 bits)
|
|
uint64_t inst = 0;
|
|
inst |= 0b010;
|
|
inst |= (0ull << 3);
|
|
inst |= (0b011 << 4);
|
|
inst |= (0ull << 7);
|
|
inst |= (15ull << 8);
|
|
inst |= (42ull << 13);
|
|
builder.writeBits(inst, 20);
|
|
|
|
// WAVE_END (20 bits)
|
|
uint64_t we = 0;
|
|
we |= 0b10101;
|
|
we |= (0b010 << 5);
|
|
we |= (1ull << 8);
|
|
we |= (1ull << 9);
|
|
we |= (3ull << 11);
|
|
we |= (0ull << 14);
|
|
we |= (15ull << 15);
|
|
builder.writeBits(we, 20);
|
|
|
|
builder.finalize();
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token1 = gen.next();
|
|
EXPECT_EQ(token1.type, RdnaType::WAVE_START);
|
|
gfx10::wstart_type ws_parsed{.raw = token1.contents};
|
|
EXPECT_EQ(ws_parsed.wid, 15);
|
|
EXPECT_EQ(ws_parsed.sa, 1);
|
|
|
|
auto token2 = gen.next();
|
|
EXPECT_EQ(token2.type, RdnaType::INST);
|
|
gfx10::inst_type inst_parsed{.raw = token2.contents};
|
|
EXPECT_EQ(inst_parsed.wid, 15);
|
|
EXPECT_EQ(inst_parsed.inst, 42);
|
|
|
|
auto token3 = gen.next();
|
|
EXPECT_EQ(token3.type, RdnaType::WAVE_END);
|
|
gfx10::wend_type we_parsed{.raw = token3.contents};
|
|
EXPECT_EQ(we_parsed.wid, 15);
|
|
}
|
|
|
|
TEST(TokenStreamBoundaryTest, GeneratorTerminatesCleanly)
|
|
{
|
|
// Verify generator terminates properly when buffer exhausted
|
|
TokenStreamBuilder builder;
|
|
|
|
uint64_t valu = 0;
|
|
valu |= 0b011;
|
|
valu |= (0b001 << 3);
|
|
valu |= (0 << 6);
|
|
valu |= (5 << 7);
|
|
builder.writeBits(valu, 12);
|
|
builder.finalize();
|
|
|
|
gfx10::TokenGenerator gen(builder.data(), builder.size(), 0, 0);
|
|
|
|
auto token1 = gen.next();
|
|
EXPECT_EQ(token1.type, RdnaType::VALU_INST);
|
|
|
|
// Consume remaining (should be NOPs from padding)
|
|
int count = 0;
|
|
while (gen.nextValid() && count < 100)
|
|
{
|
|
gen.next();
|
|
count++;
|
|
}
|
|
EXPECT_LT(count, 100); // Should terminate, not infinite loop
|
|
}
|