mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 16:02:37 +00:00
shader: decode FLAT, GLOBAL and SCRATCH D16 high-half loads (#1924)
FLAT-family opcode 0x25 (FLAT/GLOBAL/SCRATCH_LOAD_SHORT_D16_HI) loads an unsigned halfword into bits [31:16] of the destination and keeps bits [15:0]. Decode it beside the low-half variant and let it select the high half of the existing partial destination write, so FLAT_LOAD needs no new path. Ghost of Yotei CS 0x79b0066f0cd1f3cd stopped at global_load_short_d16_hi v16, v18, s[0:1] offset:196 (0xdc9480c4 0x10000012).
This commit is contained in:
1 parent
f90a8b1589
commit
986721f5bc
6 files changed
+358
-37
No files matched your search
@@ -95,14 +95,14 @@ constexpr MemoryOpcodeInfo MTBUF_OPCODE_LIST[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constexpr MemoryOpcodeInfo FLAT_OPCODE_LIST[] = {
|
constexpr MemoryOpcodeInfo FLAT_OPCODE_LIST[] = {
|
||||||
{0x08u, Opcode::FLAT_LOAD_UBYTE, 1, 8}, {0x09u, Opcode::FLAT_LOAD_SBYTE, 1, 8, true},
|
{0x08u, Opcode::FLAT_LOAD_UBYTE, 1, 8}, {0x09u, Opcode::FLAT_LOAD_SBYTE, 1, 8, true},
|
||||||
{0x0au, Opcode::FLAT_LOAD_USHORT, 1, 16}, {0x0bu, Opcode::FLAT_LOAD_SSHORT, 1, 16, true},
|
{0x0au, Opcode::FLAT_LOAD_USHORT, 1, 16}, {0x0bu, Opcode::FLAT_LOAD_SSHORT, 1, 16, true},
|
||||||
{0x0cu, Opcode::FLAT_LOAD_DWORD, 1, 32}, {0x0du, Opcode::FLAT_LOAD_DWORDX2, 2, 32},
|
{0x0cu, Opcode::FLAT_LOAD_DWORD, 1, 32}, {0x0du, Opcode::FLAT_LOAD_DWORDX2, 2, 32},
|
||||||
{0x0eu, Opcode::FLAT_LOAD_DWORDX4, 4, 32}, {0x0fu, Opcode::FLAT_LOAD_DWORDX3, 3, 32},
|
{0x0eu, Opcode::FLAT_LOAD_DWORDX4, 4, 32}, {0x0fu, Opcode::FLAT_LOAD_DWORDX3, 3, 32},
|
||||||
{0x18u, Opcode::FLAT_STORE_BYTE, 1, 8}, {0x1au, Opcode::FLAT_STORE_SHORT, 1, 16},
|
{0x18u, Opcode::FLAT_STORE_BYTE, 1, 8}, {0x1au, Opcode::FLAT_STORE_SHORT, 1, 16},
|
||||||
{0x1cu, Opcode::FLAT_STORE_DWORD, 1, 32}, {0x1du, Opcode::FLAT_STORE_DWORDX2, 2, 32},
|
{0x1cu, Opcode::FLAT_STORE_DWORD, 1, 32}, {0x1du, Opcode::FLAT_STORE_DWORDX2, 2, 32},
|
||||||
{0x1eu, Opcode::FLAT_STORE_DWORDX4, 4, 32}, {0x1fu, Opcode::FLAT_STORE_DWORDX3, 3, 32},
|
{0x1eu, Opcode::FLAT_STORE_DWORDX4, 4, 32}, {0x1fu, Opcode::FLAT_STORE_DWORDX3, 3, 32},
|
||||||
{0x24u, Opcode::FLAT_LOAD_SHORT_D16, 1, 16},
|
{0x24u, Opcode::FLAT_LOAD_SHORT_D16, 1, 16}, {0x25u, Opcode::FLAT_LOAD_SHORT_D16_HI, 1, 16},
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr MemoryOpcodeInfo DS_OPCODE_LIST[] = {
|
constexpr MemoryOpcodeInfo DS_OPCODE_LIST[] = {
|
||||||
@@ -419,9 +419,10 @@ void DecodeFlat(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
|
|||||||
}
|
}
|
||||||
|
|
||||||
DecodeVectorGpr(IsFlatStoreOpcode(inst.opcode) ? data : vdst, inst.dst);
|
DecodeVectorGpr(IsFlatStoreOpcode(inst.opcode) ? data : vdst, inst.dst);
|
||||||
if (inst.opcode == Opcode::FLAT_LOAD_SHORT_D16) {
|
if (inst.opcode == Opcode::FLAT_LOAD_SHORT_D16 ||
|
||||||
// D16 loads reuse partial destinations to preserve the untouched high half.
|
inst.opcode == Opcode::FLAT_LOAD_SHORT_D16_HI) {
|
||||||
inst.dst.sdwa_sel = 4u;
|
// D16 loads reuse partial destinations to preserve the untouched half.
|
||||||
|
inst.dst.sdwa_sel = inst.opcode == Opcode::FLAT_LOAD_SHORT_D16_HI ? 5u : 4u;
|
||||||
}
|
}
|
||||||
DecodeVectorGpr(addr, inst.src0);
|
DecodeVectorGpr(addr, inst.src0);
|
||||||
inst.src_count = 1;
|
inst.src_count = 1;
|
||||||
|
|||||||
@@ -701,6 +701,7 @@ std::string InstructionToString(const Instruction& inst) {
|
|||||||
case Opcode::FLAT_LOAD_USHORT:
|
case Opcode::FLAT_LOAD_USHORT:
|
||||||
case Opcode::FLAT_LOAD_SSHORT:
|
case Opcode::FLAT_LOAD_SSHORT:
|
||||||
case Opcode::FLAT_LOAD_SHORT_D16:
|
case Opcode::FLAT_LOAD_SHORT_D16:
|
||||||
|
case Opcode::FLAT_LOAD_SHORT_D16_HI:
|
||||||
case Opcode::FLAT_LOAD_DWORD:
|
case Opcode::FLAT_LOAD_DWORD:
|
||||||
case Opcode::FLAT_LOAD_DWORDX2:
|
case Opcode::FLAT_LOAD_DWORDX2:
|
||||||
case Opcode::FLAT_LOAD_DWORDX3:
|
case Opcode::FLAT_LOAD_DWORDX3:
|
||||||
|
|||||||
@@ -612,6 +612,7 @@ enum class Opcode {
|
|||||||
FLAT_LOAD_USHORT,
|
FLAT_LOAD_USHORT,
|
||||||
FLAT_LOAD_SSHORT,
|
FLAT_LOAD_SSHORT,
|
||||||
FLAT_LOAD_SHORT_D16,
|
FLAT_LOAD_SHORT_D16,
|
||||||
|
FLAT_LOAD_SHORT_D16_HI,
|
||||||
FLAT_LOAD_DWORD,
|
FLAT_LOAD_DWORD,
|
||||||
FLAT_LOAD_DWORDX2,
|
FLAT_LOAD_DWORDX2,
|
||||||
FLAT_LOAD_DWORDX3,
|
FLAT_LOAD_DWORDX3,
|
||||||
|
|||||||
@@ -1146,6 +1146,7 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) {
|
|||||||
case Decoder::Opcode::FLAT_LOAD_USHORT:
|
case Decoder::Opcode::FLAT_LOAD_USHORT:
|
||||||
case Decoder::Opcode::FLAT_LOAD_SSHORT:
|
case Decoder::Opcode::FLAT_LOAD_SSHORT:
|
||||||
case Decoder::Opcode::FLAT_LOAD_SHORT_D16:
|
case Decoder::Opcode::FLAT_LOAD_SHORT_D16:
|
||||||
|
case Decoder::Opcode::FLAT_LOAD_SHORT_D16_HI:
|
||||||
case Decoder::Opcode::FLAT_LOAD_DWORD:
|
case Decoder::Opcode::FLAT_LOAD_DWORD:
|
||||||
case Decoder::Opcode::FLAT_LOAD_DWORDX2:
|
case Decoder::Opcode::FLAT_LOAD_DWORDX2:
|
||||||
case Decoder::Opcode::FLAT_LOAD_DWORDX3:
|
case Decoder::Opcode::FLAT_LOAD_DWORDX3:
|
||||||
|
|||||||
@@ -22667,6 +22667,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode,
|
|||||||
case Opcode::FLAT_LOAD_USHORT:
|
case Opcode::FLAT_LOAD_USHORT:
|
||||||
case Opcode::FLAT_LOAD_SSHORT:
|
case Opcode::FLAT_LOAD_SSHORT:
|
||||||
case Opcode::FLAT_LOAD_SHORT_D16:
|
case Opcode::FLAT_LOAD_SHORT_D16:
|
||||||
|
case Opcode::FLAT_LOAD_SHORT_D16_HI:
|
||||||
case Opcode::FLAT_LOAD_DWORD:
|
case Opcode::FLAT_LOAD_DWORD:
|
||||||
case Opcode::FLAT_LOAD_DWORDX2:
|
case Opcode::FLAT_LOAD_DWORDX2:
|
||||||
case Opcode::FLAT_LOAD_DWORDX3:
|
case Opcode::FLAT_LOAD_DWORDX3:
|
||||||
@@ -35765,7 +35766,17 @@ TestCase FlatSubdwordLoadsApplyByteOffset() {
|
|||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCase GlobalLoadShortD16Captured(u32 wave_size) {
|
// D16_HI loads move the raw halfword into bits [31:16] and keep the old low half.
|
||||||
|
constexpr u32 MergeD16Hi(u32 old_value, u32 halfword) {
|
||||||
|
return (old_value & 0xffffu) | ((halfword & 0xffffu) << 16u);
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(MergeD16Hi(0xa5a51234u, 0x0000u) == 0x00001234u &&
|
||||||
|
MergeD16Hi(0xa5a51234u, 0x7fffu) == 0x7fff1234u &&
|
||||||
|
MergeD16Hi(0xa5a51234u, 0x8000u) == 0x80001234u &&
|
||||||
|
MergeD16Hi(0xa5a51234u, 0xffffu) == 0xffff1234u);
|
||||||
|
|
||||||
|
TestCase GlobalLoadShortD16Captured(u32 wave_size, bool high = false) {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
constexpr uint64_t GuestBase = 0x0000000110000000ull;
|
constexpr uint64_t GuestBase = 0x0000000110000000ull;
|
||||||
constexpr u32 Marker = 0xa5a50000u;
|
constexpr u32 Marker = 0xa5a50000u;
|
||||||
@@ -35773,16 +35784,32 @@ TestCase GlobalLoadShortD16Captured(u32 wave_size) {
|
|||||||
constexpr uint64_t ExecMask = 0xaaaaaaaa55555555ull;
|
constexpr uint64_t ExecMask = 0xaaaaaaaa55555555ull;
|
||||||
constexpr u32 Values[] = {0u, 0x7fffu, 0x8000u, 0xffffu};
|
constexpr u32 Values[] = {0u, 0x7fffu, 0x8000u, 0xffffu};
|
||||||
TestCase test;
|
TestCase test;
|
||||||
test.name = wave_size == 32u ? "GlobalLoadShortD16CapturedWave32"
|
if (high) {
|
||||||
: "GlobalLoadShortD16CapturedWave64";
|
test.name = wave_size == 32u ? "GlobalLoadShortD16HiCapturedWave32"
|
||||||
test.initial.resize(2u * wave_size + wave_size / 2u);
|
: "GlobalLoadShortD16HiCapturedWave64";
|
||||||
test.expected.resize(2u * wave_size);
|
} else {
|
||||||
|
test.name = wave_size == 32u ? "GlobalLoadShortD16CapturedWave32"
|
||||||
|
: "GlobalLoadShortD16CapturedWave64";
|
||||||
|
}
|
||||||
|
// Pass 0 runs with the full EXEC, pass 1 with a partial EXEC and pass 2 (high half only) with
|
||||||
|
// EXEC=0 and an unmapped address, which must neither touch memory nor change the destination.
|
||||||
|
// Pass 2 reads its zero EXEC from the buffer so that the guard is exercised at run time rather
|
||||||
|
// than folded away together with the load.
|
||||||
|
const u32 passes = high ? 3u : 2u;
|
||||||
|
const u32 zero_dword = passes * wave_size + wave_size / 2u;
|
||||||
|
test.initial.resize(zero_dword + (high ? 1u : 0u));
|
||||||
|
test.expected.resize(passes * wave_size);
|
||||||
for (u32 lane = 0; lane < wave_size; ++lane) {
|
for (u32 lane = 0; lane < wave_size; ++lane) {
|
||||||
const u32 value = Values[lane % std::size(Values)];
|
const u32 value = Values[lane % std::size(Values)];
|
||||||
test.initial[2u * wave_size + lane / 2u] |= value << (16u * (lane % 2u));
|
test.initial[passes * wave_size + lane / 2u] |= value << (16u * (lane % 2u));
|
||||||
test.expected[lane] = Marker | value;
|
for (u32 pass = 0; pass < passes; ++pass) {
|
||||||
test.expected[wave_size + lane] = Marker |
|
const bool active =
|
||||||
((ExecMask & (uint64_t{1} << lane)) != 0u ? value : lane * 2u);
|
pass == 0 || (pass == 1 && (ExecMask & (uint64_t{1} << lane)) != 0);
|
||||||
|
const u32 old_value = Marker | (lane * 2u);
|
||||||
|
test.expected[pass * wave_size + lane] =
|
||||||
|
!active ? old_value
|
||||||
|
: (high ? MergeD16Hi(old_value, value) : Marker | value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
auto &code = test.code;
|
auto &code = test.code;
|
||||||
AppendSMovLiteral(&code, 2, static_cast<u32>(ScalarBase));
|
AppendSMovLiteral(&code, 2, static_cast<u32>(ScalarBase));
|
||||||
@@ -35790,20 +35817,35 @@ TestCase GlobalLoadShortD16Captured(u32 wave_size) {
|
|||||||
code.push_back(EncodeVop2(0x1a, 6, InlineU32(1), 0));
|
code.push_back(EncodeVop2(0x1a, 6, InlineU32(1), 0));
|
||||||
AppendVMovLiteral(&code, 5, Marker);
|
AppendVMovLiteral(&code, 5, Marker);
|
||||||
code.push_back(EncodeVop2(0x25, 6, Vgpr(5), 6));
|
code.push_back(EncodeVop2(0x25, 6, Vgpr(5), 6));
|
||||||
for (bool masked : {false, true}) {
|
for (u32 pass = 0; pass < passes; ++pass) {
|
||||||
|
const bool masked = pass != 0;
|
||||||
code.push_back(EncodeVop1(0x01, 5, Vgpr(6)));
|
code.push_back(EncodeVop1(0x01, 5, Vgpr(6)));
|
||||||
if (masked) {
|
if (masked) {
|
||||||
code.push_back(EncodeSop1(0x04, 8, 126));
|
code.push_back(EncodeSop1(0x04, 8, 126));
|
||||||
AppendSMovLiteral(&code, 126, static_cast<u32>(ExecMask));
|
if (pass == 1) {
|
||||||
AppendSMovLiteral(&code, 127, static_cast<u32>(ExecMask >> 32u));
|
AppendSMovLiteral(&code, 126, static_cast<u32>(ExecMask));
|
||||||
|
AppendSMovLiteral(&code, 127, static_cast<u32>(ExecMask >> 32u));
|
||||||
|
} else {
|
||||||
|
// s[2:3] hold the scalar base, so read through the output buffer descriptor s[48:51].
|
||||||
|
AppendVMovU32(&code, 9, zero_dword * 4u);
|
||||||
|
code.push_back(EncodeMubuf0(0x0cu));
|
||||||
|
code.push_back(EncodeMubuf1(9, 12, 9));
|
||||||
|
code.push_back(EncodeVop1(0x02, 20, Vgpr(9)));
|
||||||
|
code.push_back(EncodeVop1(0x02, 21, Vgpr(9)));
|
||||||
|
code.push_back(EncodeSop1(0x04, 126, 20));
|
||||||
|
AppendSMovLiteral(&code, 2, 0u);
|
||||||
|
AppendSMovLiteral(&code, 3, 0u);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Captured GLOBAL_LOAD_SHORT_D16: v5 supplies the byte offset and destination.
|
// Captured GLOBAL_LOAD_SHORT_D16 (the high variant only switches the opcode to D16_HI):
|
||||||
code.insert(code.end(), {0xdc908070u, 0x05020005u});
|
// v5 supplies the byte offset and destination.
|
||||||
|
code.insert(code.end(), {high ? 0xdc948070u : 0xdc908070u, 0x05020005u});
|
||||||
if (masked) code.push_back(EncodeSop1(0x04, 126, 8));
|
if (masked) code.push_back(EncodeSop1(0x04, 126, 8));
|
||||||
AppendStoreVgprAtLaneDwordOffset(&code, 5, 0, masked ? wave_size : 0u);
|
AppendStoreVgprAtLaneDwordOffset(&code, 5, 0, pass * wave_size);
|
||||||
}
|
}
|
||||||
AppendEnd(&code);
|
AppendEnd(&code);
|
||||||
test.bda_mappings = {{GuestBase, 8u * wave_size}};
|
test.bda_mappings = {{GuestBase, 4u * passes * wave_size}};
|
||||||
|
if (high) test.fault_free_address = uint64_t{Marker} + 0x70u;
|
||||||
test.compute_info.threads_num[0] = wave_size;
|
test.compute_info.threads_num[0] = wave_size;
|
||||||
test.compute_info.threads_num[1] = 1;
|
test.compute_info.threads_num[1] = 1;
|
||||||
test.compute_info.threads_num[2] = 1;
|
test.compute_info.threads_num[2] = 1;
|
||||||
@@ -35811,13 +35853,21 @@ TestCase GlobalLoadShortD16Captured(u32 wave_size) {
|
|||||||
test.compute_info.wave_size = wave_size;
|
test.compute_info.wave_size = wave_size;
|
||||||
test.has_compute_info = true;
|
test.has_compute_info = true;
|
||||||
test.opcodes = {O::S_MOV_B32, O::S_MOV_B64, O::V_MOV_B32, O::V_LSHLREV_B32,
|
test.opcodes = {O::S_MOV_B32, O::S_MOV_B64, O::V_MOV_B32, O::V_LSHLREV_B32,
|
||||||
O::V_ADD_NC_U32, O::FLAT_LOAD_SHORT_D16,
|
O::V_ADD_NC_U32,
|
||||||
|
high ? O::FLAT_LOAD_SHORT_D16_HI : O::FLAT_LOAD_SHORT_D16,
|
||||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||||
test.decoded_counts = {{"FLAT_LOAD_SHORT_D16", 2}};
|
test.decoded_counts = {
|
||||||
|
{high ? "FLAT_LOAD_SHORT_D16_HI" : "FLAT_LOAD_SHORT_D16", passes}};
|
||||||
|
if (high) {
|
||||||
|
test.opcodes.insert(test.opcodes.end(),
|
||||||
|
{O::BUFFER_LOAD_DWORD, O::V_READFIRSTLANE_B32});
|
||||||
|
// Every pass, including the EXEC=0 one, must keep its load.
|
||||||
|
test.ir_counts = {{"LoadAddressU16", passes}};
|
||||||
|
}
|
||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
TestCase FlatLoadShortD16AddressSegments() {
|
TestCase FlatLoadShortD16AddressSegments(bool high = false) {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
constexpr uint64_t GuestBase = 0x0000000110000000ull;
|
constexpr uint64_t GuestBase = 0x0000000110000000ull;
|
||||||
std::vector<u32> code;
|
std::vector<u32> code;
|
||||||
@@ -35825,7 +35875,8 @@ TestCase FlatLoadShortD16AddressSegments() {
|
|||||||
AppendVMovLiteral(&code, 21, static_cast<u32>(GuestBase >> 32u));
|
AppendVMovLiteral(&code, 21, static_cast<u32>(GuestBase >> 32u));
|
||||||
for (u32 segment : {0u, 2u}) {
|
for (u32 segment : {0u, 2u}) {
|
||||||
AppendVMovLiteral(&code, 3, 0x12345678u);
|
AppendVMovLiteral(&code, 3, 0x12345678u);
|
||||||
code.push_back(EncodeFlat0(0x24, segment, segment == 0u ? 0u : 0xffeu));
|
code.push_back(EncodeFlat0(high ? 0x25 : 0x24, segment,
|
||||||
|
segment == 0u ? 0u : 0xffeu));
|
||||||
code.push_back(EncodeFlat1(3, 0x7d, 0, 20));
|
code.push_back(EncodeFlat1(3, 0x7d, 0, 20));
|
||||||
AppendStoreVgpr(&code, 3, segment == 0u ? 0u : 1u);
|
AppendStoreVgpr(&code, 3, segment == 0u ? 0u : 1u);
|
||||||
}
|
}
|
||||||
@@ -35834,20 +35885,121 @@ TestCase FlatLoadShortD16AddressSegments() {
|
|||||||
code.push_back(EncodeFlat0(0x1c, 1, 0));
|
code.push_back(EncodeFlat0(0x1c, 1, 0));
|
||||||
code.push_back(EncodeFlat1(0, 0x7d, 3, 20));
|
code.push_back(EncodeFlat1(0, 0x7d, 3, 20));
|
||||||
AppendVMovLiteral(&code, 3, 0x98761234u);
|
AppendVMovLiteral(&code, 3, 0x98761234u);
|
||||||
code.push_back(EncodeFlat0(0x24, 1, 2));
|
code.push_back(EncodeFlat0(high ? 0x25 : 0x24, 1, 2));
|
||||||
code.push_back(EncodeFlat1(3, 0x7d, 0, 20));
|
code.push_back(EncodeFlat1(3, 0x7d, 0, 20));
|
||||||
AppendStoreVgpr(&code, 3, 2);
|
AppendStoreVgpr(&code, 3, 2);
|
||||||
AppendEnd(&code);
|
AppendEnd(&code);
|
||||||
TestCase test{"FlatLoadShortD16AddressSegments", code,
|
TestCase test{high ? "FlatLoadShortD16HiAddressSegments"
|
||||||
|
: "FlatLoadShortD16AddressSegments",
|
||||||
|
code,
|
||||||
{0, 0, 0, 0, 0x80017fffu},
|
{0, 0, 0, 0, 0x80017fffu},
|
||||||
{0x12348001u, 0x12347fffu, 0x9876beefu},
|
high ? std::vector<u32>{0x80015678u, 0x7fff5678u, 0xbeef1234u}
|
||||||
{O::V_MOV_B32, O::FLAT_LOAD_SHORT_D16, O::FLAT_STORE_DWORD,
|
: std::vector<u32>{0x12348001u, 0x12347fffu, 0x9876beefu},
|
||||||
O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
{O::V_MOV_B32,
|
||||||
|
high ? O::FLAT_LOAD_SHORT_D16_HI : O::FLAT_LOAD_SHORT_D16,
|
||||||
|
O::FLAT_STORE_DWORD, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
|
||||||
test.bda_mappings = {{GuestBase, 16}};
|
test.bda_mappings = {{GuestBase, 16}};
|
||||||
test.compute_info.scratch_size_dwords = 1;
|
test.compute_info.scratch_size_dwords = 1;
|
||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestCase GlobalLoadShortD16HiMergesOldDestination(u32 wave_size) {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
constexpr uint64_t GuestBase = 0x0000000110000000ull;
|
||||||
|
constexpr uint64_t ScalarBase = GuestBase - 196u;
|
||||||
|
constexpr u32 Values[] = {0u, 0x7fffu, 0x8000u, 0xffffu};
|
||||||
|
// The destination does not alias the address. Changing only the old high half must not change
|
||||||
|
// the result, and changing only the old low half must change only the low half of the result.
|
||||||
|
constexpr u32 OldValues[] = {0xa5a51234u, 0x5a5a1234u, 0xa5a5abcdu};
|
||||||
|
constexpr u32 Neighbor = 0x0badf00du;
|
||||||
|
constexpr u32 Passes = static_cast<u32>(std::size(OldValues));
|
||||||
|
TestCase test;
|
||||||
|
test.name = wave_size == 32u ? "GlobalLoadShortD16HiMergesOldDestinationWave32"
|
||||||
|
: "GlobalLoadShortD16HiMergesOldDestinationWave64";
|
||||||
|
const u32 data_dword = (Passes + 1u) * wave_size;
|
||||||
|
test.initial.resize(data_dword + wave_size / 2u);
|
||||||
|
test.expected.resize(data_dword);
|
||||||
|
for (u32 lane = 0; lane < wave_size; ++lane) {
|
||||||
|
const u32 value = Values[lane % std::size(Values)];
|
||||||
|
test.initial[data_dword + lane / 2u] |= value << (16u * (lane % 2u));
|
||||||
|
for (u32 pass = 0; pass < Passes; ++pass) {
|
||||||
|
test.expected[pass * wave_size + lane] = MergeD16Hi(OldValues[pass], value);
|
||||||
|
}
|
||||||
|
test.expected[Passes * wave_size + lane] = Neighbor;
|
||||||
|
}
|
||||||
|
auto &code = test.code;
|
||||||
|
AppendSMovLiteral(&code, 0, static_cast<u32>(ScalarBase));
|
||||||
|
AppendSMovLiteral(&code, 1, static_cast<u32>(ScalarBase >> 32u));
|
||||||
|
code.push_back(EncodeVop2(0x1a, 18, InlineU32(1), 0));
|
||||||
|
AppendVMovLiteral(&code, 17, Neighbor);
|
||||||
|
for (u32 pass = 0; pass < Passes; ++pass) {
|
||||||
|
AppendVMovLiteral(&code, 16, OldValues[pass]);
|
||||||
|
// Captured from Ghost of Yotei CS 0x79b0066f0cd1f3cd:
|
||||||
|
// global_load_short_d16_hi v16, v18, s[0:1] offset:196
|
||||||
|
code.insert(code.end(), {0xdc9480c4u, 0x10000012u});
|
||||||
|
AppendStoreVgprAtLaneDwordOffset(&code, 16, 0, pass * wave_size);
|
||||||
|
}
|
||||||
|
AppendStoreVgprAtLaneDwordOffset(&code, 17, 0, Passes * wave_size);
|
||||||
|
AppendEnd(&code);
|
||||||
|
test.bda_mappings = {{GuestBase, 4u * data_dword}};
|
||||||
|
test.compute_info.threads_num[0] = wave_size;
|
||||||
|
test.compute_info.threads_num[1] = 1;
|
||||||
|
test.compute_info.threads_num[2] = 1;
|
||||||
|
test.compute_info.thread_ids_num = 1;
|
||||||
|
test.compute_info.wave_size = wave_size;
|
||||||
|
test.has_compute_info = true;
|
||||||
|
test.opcodes = {O::S_MOV_B32, O::V_MOV_B32,
|
||||||
|
O::V_LSHLREV_B32, O::V_ADD_NC_U32,
|
||||||
|
O::FLAT_LOAD_SHORT_D16_HI, O::BUFFER_STORE_DWORD,
|
||||||
|
O::S_ENDPGM};
|
||||||
|
test.decoded_counts = {{"FLAT_LOAD_SHORT_D16_HI", Passes}};
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestCase FlatLoadShortD16HiIntoAddressHighVgpr(u32 wave_size) {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
constexpr uint64_t GuestBase = 0x0000000110000000ull;
|
||||||
|
constexpr u32 Values[] = {0u, 0x7fffu, 0x8000u, 0xffffu};
|
||||||
|
constexpr u32 AddressHigh = static_cast<u32>(GuestBase >> 32u);
|
||||||
|
TestCase test;
|
||||||
|
test.name = wave_size == 32u ? "FlatLoadShortD16HiIntoAddressHighVgprWave32"
|
||||||
|
: "FlatLoadShortD16HiIntoAddressHighVgprWave64";
|
||||||
|
const u32 data_dword = 2u * wave_size;
|
||||||
|
test.initial.resize(data_dword + wave_size / 2u);
|
||||||
|
test.expected.resize(data_dword);
|
||||||
|
for (u32 lane = 0; lane < wave_size; ++lane) {
|
||||||
|
const u32 value = Values[lane % std::size(Values)];
|
||||||
|
test.initial[data_dword + lane / 2u] |= value << (16u * (lane % 2u));
|
||||||
|
// The 64-bit address is read before v4 is replaced, so the old high address dword supplies
|
||||||
|
// the preserved low half and the low address dword stays as it was.
|
||||||
|
test.expected[lane] = MergeD16Hi(AddressHigh, value);
|
||||||
|
test.expected[wave_size + lane] = static_cast<u32>(GuestBase) + lane * 2u;
|
||||||
|
}
|
||||||
|
auto &code = test.code;
|
||||||
|
code.push_back(EncodeVop2(0x1a, 3, InlineU32(1), 0));
|
||||||
|
AppendVMovLiteral(&code, 9, static_cast<u32>(GuestBase));
|
||||||
|
code.push_back(EncodeVop2(0x25, 3, Vgpr(9), 3));
|
||||||
|
AppendVMovLiteral(&code, 4, AddressHigh);
|
||||||
|
// flat_load_short_d16_hi v4, v[3:4]
|
||||||
|
code.push_back(EncodeFlat0(0x25, 0, 0));
|
||||||
|
code.push_back(EncodeFlat1(4, 0x7d, 0, 3));
|
||||||
|
AppendStoreVgprAtLaneDwordOffset(&code, 4, 0, 0);
|
||||||
|
AppendStoreVgprAtLaneDwordOffset(&code, 3, 0, wave_size);
|
||||||
|
AppendEnd(&code);
|
||||||
|
test.bda_mappings = {{GuestBase, 4u * data_dword}};
|
||||||
|
test.compute_info.threads_num[0] = wave_size;
|
||||||
|
test.compute_info.threads_num[1] = 1;
|
||||||
|
test.compute_info.threads_num[2] = 1;
|
||||||
|
test.compute_info.thread_ids_num = 1;
|
||||||
|
test.compute_info.wave_size = wave_size;
|
||||||
|
test.has_compute_info = true;
|
||||||
|
test.opcodes = {O::V_MOV_B32, O::V_LSHLREV_B32,
|
||||||
|
O::V_ADD_NC_U32, O::FLAT_LOAD_SHORT_D16_HI,
|
||||||
|
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||||
|
test.decoded_counts = {{"FLAT_LOAD_SHORT_D16_HI", 1}};
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
TestCase BranchVccnzUsesCarryProducedWaveMask() {
|
TestCase BranchVccnzUsesCarryProducedWaveMask() {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
|
|
||||||
@@ -45002,7 +45154,14 @@ std::vector<TestCase> MakeCases() {
|
|||||||
AddCase(FlatSubdwordLoadsApplyByteOffset);
|
AddCase(FlatSubdwordLoadsApplyByteOffset);
|
||||||
cases.push_back(GlobalLoadShortD16Captured(32));
|
cases.push_back(GlobalLoadShortD16Captured(32));
|
||||||
cases.push_back(GlobalLoadShortD16Captured(64));
|
cases.push_back(GlobalLoadShortD16Captured(64));
|
||||||
AddCase(FlatLoadShortD16AddressSegments);
|
cases.push_back(GlobalLoadShortD16Captured(32, true));
|
||||||
|
cases.push_back(GlobalLoadShortD16Captured(64, true));
|
||||||
|
cases.push_back(FlatLoadShortD16AddressSegments());
|
||||||
|
cases.push_back(FlatLoadShortD16AddressSegments(true));
|
||||||
|
cases.push_back(GlobalLoadShortD16HiMergesOldDestination(32));
|
||||||
|
cases.push_back(GlobalLoadShortD16HiMergesOldDestination(64));
|
||||||
|
cases.push_back(FlatLoadShortD16HiIntoAddressHighVgpr(32));
|
||||||
|
cases.push_back(FlatLoadShortD16HiIntoAddressHighVgpr(64));
|
||||||
AddCase(FlatVirtualAddressRebasesGuestAllocation);
|
AddCase(FlatVirtualAddressRebasesGuestAllocation);
|
||||||
AddCase(GlobalSignedImmediateRebasesBeforeSaddr);
|
AddCase(GlobalSignedImmediateRebasesBeforeSaddr);
|
||||||
AddCase(FlatSegmentIgnoresSaddrAndMasksOffsetMsb);
|
AddCase(FlatSegmentIgnoresSaddrAndMasksOffsetMsb);
|
||||||
@@ -51501,7 +51660,14 @@ int main(int argc, char **argv) {
|
|||||||
RunCase(&vulkan, FlatLoadDlcCaptured(64));
|
RunCase(&vulkan, FlatLoadDlcCaptured(64));
|
||||||
RunCase(&vulkan, GlobalLoadShortD16Captured(32));
|
RunCase(&vulkan, GlobalLoadShortD16Captured(32));
|
||||||
RunCase(&vulkan, GlobalLoadShortD16Captured(64));
|
RunCase(&vulkan, GlobalLoadShortD16Captured(64));
|
||||||
|
RunCase(&vulkan, GlobalLoadShortD16Captured(32, true));
|
||||||
|
RunCase(&vulkan, GlobalLoadShortD16Captured(64, true));
|
||||||
RunCase(&vulkan, FlatLoadShortD16AddressSegments());
|
RunCase(&vulkan, FlatLoadShortD16AddressSegments());
|
||||||
|
RunCase(&vulkan, FlatLoadShortD16AddressSegments(true));
|
||||||
|
for (const u32 wave : {32u, 64u}) {
|
||||||
|
RunCase(&vulkan, GlobalLoadShortD16HiMergesOldDestination(wave));
|
||||||
|
RunCase(&vulkan, FlatLoadShortD16HiIntoAddressHighVgpr(wave));
|
||||||
|
}
|
||||||
RunCase(&vulkan, FlatSubdwordLoadsApplyByteOffset());
|
RunCase(&vulkan, FlatSubdwordLoadsApplyByteOffset());
|
||||||
RunCase(&vulkan, FlatVirtualAddressRebasesGuestAllocation());
|
RunCase(&vulkan, FlatVirtualAddressRebasesGuestAllocation());
|
||||||
RunCase(&vulkan, GlobalSignedImmediateRebasesBeforeSaddr());
|
RunCase(&vulkan, GlobalSignedImmediateRebasesBeforeSaddr());
|
||||||
|
|||||||
@@ -16130,6 +16130,153 @@ void TestCapturedBufferLoadUbyteD16Decode() {
|
|||||||
"captured BUFFER_LOAD_UBYTE_D16 lost its preserved half, address, or coherence flags");
|
"captured BUFFER_LOAD_UBYTE_D16 lost its preserved half, address, or coherence flags");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestCapturedGlobalLoadShortD16HiDecode() {
|
||||||
|
namespace Decoder = ShaderRecompiler::Decoder;
|
||||||
|
// Ghost of Yotei CS 0x79b0066f0cd1f3cd: global_load_short_d16_hi v16, v18, s[0:1] offset:196.
|
||||||
|
const uint32_t code[] = {0xdc9480c4u, 0x10000012u};
|
||||||
|
Decoder::Instruction inst;
|
||||||
|
Decoder::DecodeInstruction(code, 0, inst);
|
||||||
|
Check(inst.opcode == Decoder::Opcode::FLAT_LOAD_SHORT_D16_HI &&
|
||||||
|
inst.family == Decoder::Family::FLAT && inst.word_count == 2 &&
|
||||||
|
inst.data_dwords == 1 && inst.data_bits == 16 && !inst.data_signed &&
|
||||||
|
inst.memory_segment == 2 && inst.offset == 196 && !inst.glc && !inst.slc &&
|
||||||
|
!inst.dlc,
|
||||||
|
"captured GLOBAL_LOAD_SHORT_D16_HI lost its halfword payload or address fields");
|
||||||
|
Check(inst.dst.kind == Decoder::OperandKind::Vgpr && inst.dst.reg == 16 &&
|
||||||
|
inst.dst.sdwa_sel == 5 && inst.dst.sdwa_dst_unused == 2,
|
||||||
|
"captured GLOBAL_LOAD_SHORT_D16_HI must write only the high half of v16");
|
||||||
|
Check(inst.src0.kind == Decoder::OperandKind::Vgpr && inst.src0.reg == 18 &&
|
||||||
|
inst.src1.kind == Decoder::OperandKind::Sgpr && inst.src1.reg == 0,
|
||||||
|
"captured GLOBAL_LOAD_SHORT_D16_HI lost its VGPR offset or s[0:1] base");
|
||||||
|
Check(Decoder::InstructionToString(inst).find("FLAT_LOAD_SHORT_D16_HI") != std::string::npos,
|
||||||
|
"captured GLOBAL_LOAD_SHORT_D16_HI diagnostic text lost the opcode name");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestFlatLoadShortD16HiLlvmEncodingAnchor() {
|
||||||
|
namespace Decoder = ShaderRecompiler::Decoder;
|
||||||
|
// llvm/test/MC/AMDGPU/flat-gfx10.s: flat_load_short_d16_hi v1, v[3:4]
|
||||||
|
// encodes as [0x00,0x00,0x94,0xdc,0x03,0x00,0x7d,0x01].
|
||||||
|
const uint32_t code[] = {0xdc940000u, 0x017d0003u};
|
||||||
|
Decoder::Instruction inst;
|
||||||
|
Decoder::DecodeInstruction(code, 0, inst);
|
||||||
|
Check(inst.opcode == Decoder::Opcode::FLAT_LOAD_SHORT_D16_HI && inst.word_count == 2 &&
|
||||||
|
inst.memory_segment == 0 && inst.offset == 0 && inst.data_dwords == 1 &&
|
||||||
|
inst.data_bits == 16 && !inst.data_signed,
|
||||||
|
"LLVM flat_load_short_d16_hi encoding did not decode as a FLAT halfword load");
|
||||||
|
Check(inst.dst.kind == Decoder::OperandKind::Vgpr && inst.dst.reg == 1 &&
|
||||||
|
inst.dst.sdwa_sel == 5 && inst.dst.sdwa_dst_unused == 2 &&
|
||||||
|
inst.src0.kind == Decoder::OperandKind::Vgpr && inst.src0.reg == 3 &&
|
||||||
|
inst.src1.kind == Decoder::OperandKind::Vgpr && inst.src1.reg == 4,
|
||||||
|
"LLVM flat_load_short_d16_hi v1, v[3:4] lost its destination half or address pair");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestFlatLoadShortD16HiCompilesAndValidates() {
|
||||||
|
// The captured high-half load followed by a buffer store of the merged register so that the
|
||||||
|
// load stays live: global_load_short_d16_hi v16, v18, s[0:1] offset:196.
|
||||||
|
const uint32_t shader[] = {
|
||||||
|
0xdc9480c4u, 0x10000012u,
|
||||||
|
EncodeMubuf0(0x1c, 0, false), EncodeMubuf1(16, 12, 0),
|
||||||
|
EncodeSopp(0x01),
|
||||||
|
};
|
||||||
|
auto options = MakeCompileOptions(ShaderType::Compute);
|
||||||
|
options.dump_ir = true;
|
||||||
|
auto result = RecompileForTest(shader, options);
|
||||||
|
Check(result.decoded_dump.find("FLAT_LOAD_SHORT_D16_HI") != std::string::npos &&
|
||||||
|
result.decoded_dump.find("unsupported family=") == std::string::npos,
|
||||||
|
"GLOBAL_LOAD_SHORT_D16_HI did not reach the decoded program");
|
||||||
|
Check(result.ir_dump.find("LoadAddressU16") != std::string::npos &&
|
||||||
|
result.ir_dump.find("BitFieldInsert") != std::string::npos,
|
||||||
|
"GLOBAL_LOAD_SHORT_D16_HI did not become a 16-bit load merged into the old register");
|
||||||
|
CheckSpirvBinaryValidates(result.spirv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestFlatLoadShortD16HiAddressFields() {
|
||||||
|
namespace Decoder = ShaderRecompiler::Decoder;
|
||||||
|
// Modifier bits of the first FLAT-family word.
|
||||||
|
constexpr uint32_t DlcBit = 1u << 12u;
|
||||||
|
constexpr uint32_t LdsBit = 1u << 13u;
|
||||||
|
constexpr uint32_t GlcBit = 1u << 16u;
|
||||||
|
constexpr uint32_t SlcBit = 1u << 17u;
|
||||||
|
constexpr uint32_t SegFlat = 0;
|
||||||
|
constexpr uint32_t SegScratch = 1;
|
||||||
|
constexpr uint32_t SegGlobal = 2;
|
||||||
|
constexpr uint32_t SegReserved = 3;
|
||||||
|
constexpr uint32_t SaddrOff = 0x7du;
|
||||||
|
const auto decode = [](uint32_t seg, uint32_t offset_field, uint32_t saddr, uint32_t flags = 0) {
|
||||||
|
const uint32_t code[] = {EncodeFlat0(0x25, seg, offset_field) | flags,
|
||||||
|
EncodeFlat1(7, saddr, 0, 9)};
|
||||||
|
Decoder::Instruction inst;
|
||||||
|
Decoder::DecodeInstruction(code, 0, inst);
|
||||||
|
return inst;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OffsetCase {
|
||||||
|
uint32_t seg;
|
||||||
|
uint32_t field;
|
||||||
|
int32_t expected;
|
||||||
|
};
|
||||||
|
const OffsetCase offsets[] = {
|
||||||
|
// FLAT ignores OFFSET[11] and uses an unsigned 11-bit byte offset.
|
||||||
|
{SegFlat, 0x000, 0},
|
||||||
|
{SegFlat, 0x7ff, 2047},
|
||||||
|
{SegFlat, 0x800, 0},
|
||||||
|
{SegFlat, 0xfff, 2047},
|
||||||
|
// GLOBAL and SCRATCH sign-extend the 12-bit field.
|
||||||
|
{SegGlobal, 0x000, 0},
|
||||||
|
{SegGlobal, 0x7ff, 2047},
|
||||||
|
{SegGlobal, 0x800, -2048},
|
||||||
|
{SegGlobal, 0xfff, -1},
|
||||||
|
{SegScratch, 0x000, 0},
|
||||||
|
{SegScratch, 0x7ff, 2047},
|
||||||
|
{SegScratch, 0x800, -2048},
|
||||||
|
{SegScratch, 0xfff, -1},
|
||||||
|
};
|
||||||
|
for (const auto &test : offsets) {
|
||||||
|
const auto inst = decode(test.seg, test.field, SaddrOff);
|
||||||
|
Check(inst.opcode == Decoder::Opcode::FLAT_LOAD_SHORT_D16_HI &&
|
||||||
|
inst.memory_segment == test.seg &&
|
||||||
|
inst.offset == static_cast<uint32_t>(test.expected),
|
||||||
|
"FLAT-family D16 high-half load decoded the wrong byte offset for its segment");
|
||||||
|
Check(inst.dst.kind == Decoder::OperandKind::Vgpr && inst.dst.reg == 7 &&
|
||||||
|
inst.dst.sdwa_sel == 5 && inst.dst.sdwa_dst_unused == 2 &&
|
||||||
|
inst.src0.kind == Decoder::OperandKind::Vgpr && inst.src0.reg == 9,
|
||||||
|
"FLAT-family D16 high-half load lost its destination half or address VGPR");
|
||||||
|
}
|
||||||
|
|
||||||
|
// FLAT takes a VGPR pair whatever the SADDR field says; GLOBAL takes either an SGPR base plus
|
||||||
|
// a VGPR offset or, with SADDR off, a VGPR pair.
|
||||||
|
const auto flat = decode(SegFlat, 0, 2);
|
||||||
|
Check(flat.opcode == Decoder::Opcode::FLAT_LOAD_SHORT_D16_HI &&
|
||||||
|
flat.src1.kind == Decoder::OperandKind::Vgpr && flat.src1.reg == 10,
|
||||||
|
"FLAT D16 high-half load must use the VGPR pair and ignore SADDR");
|
||||||
|
const auto global_sgpr = decode(SegGlobal, 0, 2);
|
||||||
|
Check(global_sgpr.opcode == Decoder::Opcode::FLAT_LOAD_SHORT_D16_HI &&
|
||||||
|
global_sgpr.src_count == 2 && global_sgpr.src0.kind == Decoder::OperandKind::Vgpr &&
|
||||||
|
global_sgpr.src0.reg == 9 && global_sgpr.src1.kind == Decoder::OperandKind::Sgpr &&
|
||||||
|
global_sgpr.src1.reg == 2,
|
||||||
|
"GLOBAL D16 high-half load with SADDR s[2:3] lost its scalar base");
|
||||||
|
const auto global_pair = decode(SegGlobal, 0, SaddrOff);
|
||||||
|
Check(global_pair.opcode == Decoder::Opcode::FLAT_LOAD_SHORT_D16_HI &&
|
||||||
|
global_pair.src_count == 2 && global_pair.src0.kind == Decoder::OperandKind::Vgpr &&
|
||||||
|
global_pair.src0.reg == 9 && global_pair.src1.kind == Decoder::OperandKind::Vgpr &&
|
||||||
|
global_pair.src1.reg == 10,
|
||||||
|
"GLOBAL D16 high-half load with SADDR off must use the VGPR pair");
|
||||||
|
|
||||||
|
// DLC is only a cache hint and stays visible; LDS, GLC and SLC on a load and the reserved
|
||||||
|
// segment are not implemented and must keep failing loudly.
|
||||||
|
const auto dlc = decode(SegGlobal, 4, 2, DlcBit);
|
||||||
|
Check(dlc.opcode == Decoder::Opcode::FLAT_LOAD_SHORT_D16_HI && dlc.dlc && !dlc.glc && !dlc.slc,
|
||||||
|
"GLOBAL D16 high-half load lost its DLC hint");
|
||||||
|
Check(decode(SegGlobal, 0, 2, LdsBit).opcode == Decoder::Opcode::UNSUPPORTED,
|
||||||
|
"FLAT-family D16 high-half load accepted LDS=1");
|
||||||
|
Check(decode(SegGlobal, 0, 2, GlcBit).opcode == Decoder::Opcode::UNSUPPORTED,
|
||||||
|
"FLAT-family D16 high-half load accepted GLC=1");
|
||||||
|
Check(decode(SegGlobal, 0, 2, SlcBit).opcode == Decoder::Opcode::UNSUPPORTED,
|
||||||
|
"FLAT-family D16 high-half load accepted SLC=1 on a load");
|
||||||
|
Check(decode(SegReserved, 0, 2).opcode == Decoder::Opcode::UNSUPPORTED,
|
||||||
|
"FLAT-family D16 high-half load accepted the reserved segment");
|
||||||
|
}
|
||||||
|
|
||||||
void TestCapturedBufferStoreShortD16HiDecode() {
|
void TestCapturedBufferStoreShortD16HiDecode() {
|
||||||
namespace Decoder = ShaderRecompiler::Decoder;
|
namespace Decoder = ShaderRecompiler::Decoder;
|
||||||
const uint32_t code[] = {0xe06c6000u, 0x80010326u};
|
const uint32_t code[] = {0xe06c6000u, 0x80010326u};
|
||||||
@@ -17017,6 +17164,10 @@ int main() {
|
|||||||
TestPixelProgramCacheBindingIdentity();
|
TestPixelProgramCacheBindingIdentity();
|
||||||
TestGraphicsPushConstantPlacement();
|
TestGraphicsPushConstantPlacement();
|
||||||
TestCapturedBufferLoadUbyteD16Decode();
|
TestCapturedBufferLoadUbyteD16Decode();
|
||||||
|
TestCapturedGlobalLoadShortD16HiDecode();
|
||||||
|
TestFlatLoadShortD16HiLlvmEncodingAnchor();
|
||||||
|
TestFlatLoadShortD16HiAddressFields();
|
||||||
|
TestFlatLoadShortD16HiCompilesAndValidates();
|
||||||
TestCapturedBufferStoreShortD16HiDecode();
|
TestCapturedBufferStoreShortD16HiDecode();
|
||||||
TestCapturedBufferStoreFormatD16HiXDecode();
|
TestCapturedBufferStoreFormatD16HiXDecode();
|
||||||
TestCapturedBufferLoadD16XyDecode();
|
TestCapturedBufferLoadD16XyDecode();
|
||||||
|
|||||||
Reference in new issue
Block a user