229 lines
8.6 KiB
C++
229 lines
8.6 KiB
C++
#include "ControlFlow/GraphBuilder.hpp"
|
|
#include "IntermediateRepresentation/IrProgram.hpp"
|
|
#include "RdnaDecoder/RdnaScalarOpDecoder.hpp"
|
|
#include "Translation/TranslationContext.hpp"
|
|
#include <array>
|
|
#include <cstdio>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
using namespace ShaderRecompiler;
|
|
static void Require(bool value) { if (!value) throw std::runtime_error("scalar swappc regression"); }
|
|
|
|
template <typename Fn>
|
|
static void ExpectThrow(const char* needle, Fn fn) {
|
|
try {
|
|
fn();
|
|
} catch (const std::exception& error) {
|
|
if (std::string(error.what()).find(needle) == std::string::npos) {
|
|
throw std::runtime_error(std::string("wrong error for \"") + needle + "\": " + error.what());
|
|
}
|
|
return;
|
|
}
|
|
throw std::runtime_error(std::string("expected throw containing \"") + needle + "\"");
|
|
}
|
|
|
|
static RdnaInstruction Sop1(std::uint32_t pc, std::uint32_t sdst, std::uint32_t opcode, std::uint32_t ssrc0) {
|
|
const std::array<std::uint32_t, 2> words{0x80000000u | (0x7du << 23u) | (sdst << 16u) | (opcode << 8u) | ssrc0, 0u};
|
|
return DecodeRdnaSop1(pc, words, 0u);
|
|
}
|
|
|
|
static RdnaInstruction Sop2Literal(std::uint32_t pc, std::uint32_t sdst, std::uint32_t opcode, std::uint32_t ssrc0, std::uint32_t literal) {
|
|
const std::array<std::uint32_t, 2> words{0x80000000u | (opcode << 23u) | (sdst << 16u) | (0xffu << 8u) | ssrc0, literal};
|
|
return DecodeRdnaSop2(pc, words, 0u);
|
|
}
|
|
|
|
static RdnaInstruction Sopp(std::uint32_t pc, std::uint32_t opcode) {
|
|
const std::array<std::uint32_t, 1> words{0x80000000u | (0x7fu << 23u) | (opcode << 16u)};
|
|
return DecodeRdnaSopp(pc, words, 0u);
|
|
}
|
|
|
|
static RdnaInstruction Sopk(std::uint32_t pc, std::uint32_t opcode, std::uint32_t sdst, std::uint32_t simm16) {
|
|
const std::array<std::uint32_t, 1> words{0x80000000u | ((0x60u + opcode) << 23u) | (sdst << 16u) | (simm16 & 0xffffu)};
|
|
return DecodeRdnaSopk(pc, words, 0u);
|
|
}
|
|
|
|
static RdnaProgram Program(std::initializer_list<RdnaInstruction> instructions) {
|
|
RdnaProgram program;
|
|
program.instructions.assign(instructions);
|
|
return program;
|
|
}
|
|
|
|
static ControlFlowGraph Build(const RdnaProgram& program, const SwappcInfo* swappc = nullptr) {
|
|
return GraphBuilder{}.Build(program, swappc);
|
|
}
|
|
|
|
static void CheckDecodeIdentity() {
|
|
const std::array<std::uint32_t, 2> words{0x80000000u | (0x7du << 23u) | (8u << 16u) | (0x21u << 8u) | 4u, 0u};
|
|
const RdnaInstruction instruction = DecodeRdnaSop1(0x24u, words, 0u);
|
|
Require(instruction.op == RdnaOpcode::SSwappcB64);
|
|
Require(instruction.family == RdnaInstructionFamily::SOP1);
|
|
Require(instruction.programCounter == 0x24u);
|
|
Require(instruction.dataDwordCount == 2u);
|
|
Require(instruction.sourceCount == 1u);
|
|
Require(instruction.source0.kind == RdnaOperandKind::ScalarRegister && instruction.source0.reg == 4u);
|
|
Require(instruction.destination.kind == RdnaOperandKind::ScalarRegister && instruction.destination.reg == 8u);
|
|
|
|
const std::array<std::uint32_t, 2> pcWords{0x80000000u | (0x7du << 23u) | (125u << 16u) | (0x21u << 8u) | 4u, 0u};
|
|
Require(DecodeRdnaSop1(0u, pcWords, 0u).op == RdnaOpcode::SSetpcB64);
|
|
|
|
|
|
const RdnaInstruction add = Sop2Literal(0x4u, 4u, 0x00u, 4u, 0x18u);
|
|
Require(add.op == RdnaOpcode::SAddU32);
|
|
Require(add.wordCount == 2u);
|
|
}
|
|
|
|
static std::string TranslateDump(const RdnaInstruction& instruction) {
|
|
IrProgram program;
|
|
auto& block = program.CreateBlock();
|
|
program.SetEntryBlock(block);
|
|
program.BlockOrder() = {&block};
|
|
program.Metadata().blockInfo.resize(1);
|
|
TranslationContext context(program, block, 256);
|
|
context.TranslateInstruction(instruction);
|
|
return ProgramToString(program);
|
|
}
|
|
|
|
static void CheckLinkModelsNextInstructionAddress() {
|
|
const std::array<std::uint32_t, 2> getpcWords{0x80000000u | (0x7du << 23u) | (2u << 16u) | (0x1fu << 8u), 0u};
|
|
const std::array<std::uint32_t, 2> swappcWords{0x80000000u | (0x7du << 23u) | (2u << 16u) | (0x21u << 8u) | 4u, 0u};
|
|
const RdnaInstruction getpc = DecodeRdnaSop1(0x10u, getpcWords, 0u);
|
|
const RdnaInstruction swappc = DecodeRdnaSop1(0x10u, swappcWords, 0u);
|
|
Require(!TranslateDump(getpc).empty());
|
|
Require(TranslateDump(getpc) == TranslateDump(swappc));
|
|
}
|
|
|
|
static void CheckStaticCallRegion() {
|
|
const auto program = Program({
|
|
Sop1(0x00u, 4u, 0x1fu, 0u),
|
|
Sop2Literal(0x04u, 4u, 0x00u, 4u, 0x18u),
|
|
Sop2Literal(0x0cu, 5u, 0x04u, 5u, 0u),
|
|
Sop1(0x14u, 8u, 0x21u, 4u),
|
|
Sopp(0x18u, 0x01u),
|
|
Sopp(0x1cu, 0x00u),
|
|
Sop1(0x20u, 0u, 0x20u, 8u),
|
|
});
|
|
const auto cfg = Build(program);
|
|
Require(cfg.FindBlockByProgramCounter(0x00u).terminator.kind == TerminatorKind::Branch);
|
|
Require(cfg.FindBlockByProgramCounter(0x00u).terminator.trueBlock == cfg.FindBlockByProgramCounter(0x1cu).id);
|
|
Require(cfg.FindBlockByProgramCounter(0x1cu).terminator.kind == TerminatorKind::Branch);
|
|
Require(cfg.FindBlockByProgramCounter(0x1cu).terminator.trueBlock == cfg.FindBlockByProgramCounter(0x18u).id);
|
|
}
|
|
|
|
static void CheckFetchCallAtEntry() {
|
|
SwappcInfo info;
|
|
info.fetchCallAllowed = true;
|
|
info.userDataBaseRegister = 16u;
|
|
info.userDataCount = 8u;
|
|
const auto program = Program({
|
|
Sopp(0x00u, 0x00u),
|
|
Sop1(0x04u, 2u, 0x21u, 16u),
|
|
Sopp(0x08u, 0x01u),
|
|
});
|
|
const auto cfg = Build(program, &info);
|
|
Require(cfg.hasFetchCall);
|
|
Require(cfg.fetchCallProgramCounter == 0x04u);
|
|
}
|
|
|
|
static void CheckThrows() {
|
|
ExpectThrow("computed/data-dependent s_swappc_b64", [] {
|
|
Build(Program({
|
|
Sop1(0x00u, 8u, 0x21u, 4u),
|
|
Sopp(0x04u, 0x01u),
|
|
}));
|
|
});
|
|
ExpectThrow("escapes the constant-offset call/return model", [] {
|
|
Build(Program({
|
|
Sop1(0x00u, 4u, 0x1fu, 0u),
|
|
Sop2Literal(0x04u, 4u, 0x00u, 4u, 0x18u),
|
|
Sop2Literal(0x0cu, 5u, 0x04u, 5u, 0u),
|
|
Sop1(0x14u, 8u, 0x21u, 4u),
|
|
Sop1(0x18u, 8u, 0x03u, 8u),
|
|
Sopp(0x1cu, 0x01u),
|
|
Sopp(0x20u, 0x00u),
|
|
Sop1(0x24u, 0u, 0x20u, 8u),
|
|
}));
|
|
});
|
|
ExpectThrow("no paired s_setpc_b64 return", [] {
|
|
Build(Program({
|
|
Sop1(0x00u, 4u, 0x1fu, 0u),
|
|
Sop2Literal(0x04u, 4u, 0x00u, 4u, 0x18u),
|
|
Sop2Literal(0x0cu, 5u, 0x04u, 5u, 0u),
|
|
Sop1(0x14u, 8u, 0x21u, 4u),
|
|
Sopp(0x18u, 0x01u),
|
|
Sopp(0x1cu, 0x00u),
|
|
}));
|
|
});
|
|
ExpectThrow("multiple s_setpc_b64 returns", [] {
|
|
Build(Program({
|
|
Sop1(0x00u, 4u, 0x1fu, 0u),
|
|
Sop2Literal(0x04u, 4u, 0x00u, 4u, 0x18u),
|
|
Sop2Literal(0x0cu, 5u, 0x04u, 5u, 0u),
|
|
Sop1(0x14u, 8u, 0x21u, 4u),
|
|
Sopp(0x18u, 0x01u),
|
|
Sopp(0x1cu, 0x00u),
|
|
Sop1(0x20u, 0u, 0x20u, 8u),
|
|
Sop1(0x24u, 0u, 0x20u, 8u),
|
|
}));
|
|
});
|
|
ExpectThrow("is shared with another call", [] {
|
|
Build(Program({
|
|
Sop1(0x00u, 4u, 0x1fu, 0u),
|
|
Sop2Literal(0x04u, 4u, 0x00u, 4u, 0x18u),
|
|
Sop2Literal(0x0cu, 5u, 0x04u, 5u, 0u),
|
|
Sop1(0x14u, 8u, 0x21u, 4u),
|
|
Sop1(0x18u, 8u, 0x21u, 4u),
|
|
Sopp(0x1cu, 0x01u),
|
|
Sopp(0x20u, 0x00u),
|
|
Sop1(0x24u, 0u, 0x20u, 8u),
|
|
}));
|
|
});
|
|
ExpectThrow("recursive s_swappc_b64 call", [] {
|
|
Build(Program({
|
|
Sop1(0x00u, 4u, 0x1fu, 0u),
|
|
Sop2Literal(0x04u, 4u, 0x00u, 4u, 0x10u),
|
|
Sop2Literal(0x0cu, 5u, 0x04u, 5u, 0u),
|
|
Sop1(0x14u, 8u, 0x21u, 4u),
|
|
Sopp(0x18u, 0x01u),
|
|
Sop1(0x1cu, 0u, 0x20u, 8u),
|
|
}));
|
|
});
|
|
ExpectThrow("computed/data-dependent s_swappc_b64", [] {
|
|
SwappcInfo info;
|
|
info.fetchCallAllowed = true;
|
|
info.userDataBaseRegister = 16u;
|
|
info.userDataCount = 8u;
|
|
Build(Program({
|
|
Sopp(0x00u, 0x00u),
|
|
Sop1(0x04u, 2u, 0x21u, 4u),
|
|
Sopp(0x08u, 0x01u),
|
|
}), &info);
|
|
});
|
|
ExpectThrow("must live in the fetch-shader code", [] {
|
|
SwappcInfo info;
|
|
info.fetchCallAllowed = true;
|
|
info.userDataBaseRegister = 16u;
|
|
info.userDataCount = 8u;
|
|
Build(Program({
|
|
Sopp(0x00u, 0x00u),
|
|
Sop1(0x04u, 2u, 0x21u, 16u),
|
|
Sopp(0x08u, 0x01u),
|
|
Sop1(0x0cu, 0u, 0x20u, 2u),
|
|
}), &info);
|
|
});
|
|
}
|
|
|
|
int main() {
|
|
try {
|
|
CheckDecodeIdentity();
|
|
CheckLinkModelsNextInstructionAddress();
|
|
CheckStaticCallRegion();
|
|
CheckFetchCallAtEntry();
|
|
CheckThrows();
|
|
} catch (const std::exception& error) {
|
|
std::fprintf(stderr, "scalar swappc regression: %s\n", error.what());
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|