fix(shader): give the frontend's call analysis the dispatch's wave size

AcquireFrontend built the frontend's SwappcInfo with a wave size of 0, while the
driver's LinkUserDataCallees passes the dispatch's wave size. A wave64 v_cmp_e64
that reuses a call's link pair after the return, followed by a v_cndmask_b32_e64
that reads it, was refused as escaping the call/return model. The frontend now
receives the dispatch's wave size, and a frontend built for one wave size is not
reused for another.
This commit is contained in:
capmare committed 2026-10-21 14:13:06 +02:00
1 parent 11bb53a347
commit 9863f8b641
2 files changed
+27 -4

No files matched your search

@@ -168,7 +168,6 @@ void verifyFrontendIdentity() {
require((preparation.AcquireFrontend(partial) == first) == reuse, "frontend pair: partial-thread state changed the frontend identity");
auto target = request;
target.target.subgroupSize = 64u;
target.context.waveSize = 32u;
target.context.compute->numThreads = {32u, 2u, 1u};
require((preparation.AcquireFrontend(target) == first) == reuse, "frontend pair: translation-only state changed the frontend identity");
const std::array<std::uint32_t, 2> changedUserData{1u, 2u};
@@ -197,6 +196,10 @@ void verifyFrontendIdentity() {
changed.context.userDataBaseRegister = 4u;
verifyMiss(changed, "frontend pair: changed user-data base reused the frontend");
changed = request;
changed.context.waveSize = 32u;
changed.context.compute->numThreads = {32u, 2u, 1u};
verifyMiss(changed, "frontend pair: a different wave size reused the frontend");
changed = request;
changed.context.userData = std::span<const std::uint32_t>(userData).first(1u);
verifyMiss(changed, "frontend pair: changed user-data count reused the frontend");
changed = request;
@@ -222,6 +225,25 @@ void verifyFrontendIdentity() {
require((preparation.AcquireFrontend(request) == first) == reuse, "frontend pair: a failed miss replaced the prior frontend");
}
void verifyFrontendWaveSizeCalls() {
using namespace ShaderRecompiler;
const std::vector<std::uint32_t> code{
0xbb0e0005u,
0xd4c2000eu, 0x00020300u,
0xd5010002u, 0x003a0300u,
0xbf820001u,
0xbe80200eu,
0xbf810000u};
const std::array<std::uint32_t, 2> userData{};
for (const std::uint32_t waveSize : {32u, 64u}) {
auto request = FrontendRequest(code, userData);
request.context.waveSize = waveSize;
request.context.compute->numThreads = {waveSize, 1u, 1u};
ShaderPreparationContext preparation;
require(preparation.AcquireFrontend(request) != nullptr, "frontend wave size: a mask write that reuses the link pair and its v_cndmask read were refused");
}
}
void verifyFrontendProgram(const ShaderRecompiler::IrProgram& reused, const ShaderRecompiler::IrProgram& fresh) {
using namespace ShaderRecompiler;
require(ProgramToString(reused) == ProgramToString(fresh), "frontend pair: reused frontend changed the IR");
@@ -256,6 +278,7 @@ void verifyFrontendProgram(const ShaderRecompiler::IrProgram& reused, const Shad
void verifyFrontendPair() {
using namespace ShaderRecompiler;
verifyFrontendIdentity();
verifyFrontendWaveSizeCalls();
const bool reuse = std::getenv("APS5_NO_PERF_FRONTEND_PAIR") == nullptr;
const std::vector<std::uint32_t> code{0xe0700000u, 0x80000000u, 0xbf810000u};
const std::array<std::uint32_t, 4> buffer{0x10000000u, 0u, 0x40u, 0x00027facu};
@@ -318,7 +341,7 @@ void verifyFrontendPair() {
const auto freshChanged = PrepareResourceProgram(translated);
verifyFrontendProgram(changed, freshChanged);
require(changed.WaveSize() != freshFull.WaveSize(), "frontend pair: changed translation inputs were ignored");
require((preparation.AcquireFrontend(request) == retained) == reuse, "frontend pair: translating a variant replaced the frontend");
require(preparation.AcquireFrontend(request) != retained, "frontend pair: a variant with another wave size reused the frontend");
}
for (const bool partialFirst : {false, true}) {
+2 -2
View File
@@ -169,7 +169,7 @@ struct PreparedControlFlow {
bool Matches(const RecompileRequest& request, const SwappcInfo& swappcInfo) const {
return stage == request.shader.stage && mergedHull == MergedHullStage(request) && swappc.fetchCallAllowed == swappcInfo.fetchCallAllowed &&
swappc.userDataBaseRegister == swappcInfo.userDataBaseRegister && swappc.userDataCount == swappcInfo.userDataCount &&
swappc.userDataBaseRegister == swappcInfo.userDataBaseRegister && swappc.userDataCount == swappcInfo.userDataCount && swappc.waveSize == swappcInfo.waveSize &&
std::ranges::equal(fallbacks, swappcInfo.linkedCallFallbacks) && std::ranges::equal(code, request.shader.code);
}
@@ -189,7 +189,7 @@ struct PreparedControlFlow {
std::shared_ptr<const PreparedControlFlow> ShaderPreparationContext::AcquireFrontend(const RecompileRequest& request) {
const auto inputInfo = RequestInputInfo(request);
const SwappcInfo swappc{inputInfo.vertex != nullptr, request.context.userDataBaseRegister, static_cast<std::uint32_t>(request.context.userData.size()), 0u, request.context.linkedCallFallbacks};
const SwappcInfo swappc{inputInfo.vertex != nullptr, request.context.userDataBaseRegister, static_cast<std::uint32_t>(request.context.userData.size()), request.context.waveSize, request.context.linkedCallFallbacks};
static const bool reuse = std::getenv("APS5_NO_PERF_FRONTEND_PAIR") == nullptr;
if (reuse && frontend != nullptr && frontend->Matches(request, swappc)) {
return frontend;