mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-23 17:13:13 -05:00
lowerPackedAncillary follows the selects, phis and identities that forward the packed ancillary word to its readers by recursing once per link (collectReaders), with a linear scan of the visited list at each step. A long forwarding chain overflowed the stack of the thread that compiles the shader: PPSA01317 crashes about 30 s into boot in that recursion on a guest job thread, with a 0xc00000fd stack overflow inside malloc. The readers are now collected with an explicit stack and a visited set, in the same order. AI-assisted: yes
137 lines
5.6 KiB
C++
137 lines
5.6 KiB
C++
#include "IntermediateRepresentation/IrBuilder.hpp"
|
|
#include "Optimization/ConstantFolder.hpp"
|
|
#include "Optimization/DeadCodeEliminator.hpp"
|
|
#include <cstdio>
|
|
#include <pthread.h>
|
|
#include <string>
|
|
#include <stdexcept>
|
|
|
|
using namespace ShaderRecompiler;
|
|
|
|
namespace {
|
|
|
|
void Require(bool condition, const char* message) {
|
|
if (!condition) throw std::runtime_error(message);
|
|
}
|
|
|
|
void CheckDiamond(unsigned mode) {
|
|
IrProgram program;
|
|
for (std::uint32_t id = 0; id < 4u; ++id) {
|
|
auto& block = program.CreateBlock();
|
|
program.BlockOrder().push_back(&block);
|
|
BlockInfo info;
|
|
info.id = id;
|
|
info.terminator.kind = TerminatorKind::Return;
|
|
program.Metadata().blockInfo.push_back(info);
|
|
}
|
|
auto& entry = *program.BlockOrder()[0];
|
|
auto& left = *program.BlockOrder()[1];
|
|
auto& right = *program.BlockOrder()[2];
|
|
auto& merge = *program.BlockOrder()[3];
|
|
program.SetEntryBlock(entry);
|
|
entry.AddBranch(&left);
|
|
entry.AddBranch(&right);
|
|
left.AddBranch(&merge);
|
|
right.AddBranch(&merge);
|
|
auto& entryInfo = program.Metadata().blockInfo[0];
|
|
entryInfo.terminator.kind = TerminatorKind::ConditionalBranch;
|
|
entryInfo.terminator.trueBlock = 1u;
|
|
entryInfo.terminator.falseBlock = 2u;
|
|
for (std::uint32_t id = 1; id < 3u; ++id) {
|
|
program.Metadata().blockInfo[id].terminator.kind = TerminatorKind::Branch;
|
|
program.Metadata().blockInfo[id].terminator.trueBlock = 3u;
|
|
}
|
|
IrBuilder builder(program);
|
|
builder.SetInsertionPoint(entry);
|
|
auto& lane = builder.Emit(IrOpcode::LaneId, IrType::U32, {});
|
|
auto& limit = builder.Constant(16u);
|
|
auto& condition = builder.Emit(IrOpcode::ULessThan32, IrType::Bool, {&lane, &limit});
|
|
entryInfo.condition = &condition;
|
|
auto& one = builder.Constant(1u);
|
|
auto& two = builder.Constant(2u);
|
|
IrValue* first = &lane;
|
|
IrValue* second = &lane;
|
|
if (mode == 0u || mode == 1u) {
|
|
builder.SetInsertionPoint(left);
|
|
first = mode == 0u ? &builder.Emit(IrOpcode::SelectU32, IrType::U32, {&condition, &one, &two}) : &builder.Emit(IrOpcode::LaneId, IrType::U32, {});
|
|
builder.SetInsertionPoint(right);
|
|
second = mode == 0u ? &builder.Emit(IrOpcode::SelectU32, IrType::U32, {&condition, &one, &two}) : &builder.Emit(IrOpcode::LaneId, IrType::U32, {});
|
|
} else if (mode == 3u || mode == 4u) {
|
|
first = &one;
|
|
second = &program.CreateValue(IrOpcode::Void, IrType::U32);
|
|
second->SetImmediateU32(mode == 3u ? 1u : 2u);
|
|
}
|
|
builder.SetInsertionPoint(merge);
|
|
auto& phi = builder.Emit(IrOpcode::Phi, IrType::U32, {});
|
|
phi.AddPhiOperand(&left, first);
|
|
phi.AddPhiOperand(&right, second);
|
|
auto& reference = builder.Emit(IrOpcode::ReferenceU32, IrType::Void, {&phi});
|
|
ValidateProgram(program, true);
|
|
ConstantFolder{}.Fold(program);
|
|
DeadCodeEliminator{}.RemoveIdentities(program);
|
|
DeadCodeEliminator{}.Eliminate(program);
|
|
ValidateProgram(program, true);
|
|
const auto* result = reference.Argument(0)->Resolve();
|
|
if (mode == 0u || mode == 1u || mode == 4u) {
|
|
Require(result == &phi && phi.Argument(0) == first && phi.Argument(1) == second, "phi lost distinct branch definitions or constants");
|
|
} else {
|
|
Require(result == first, "phi did not fold identical definitions or equal constants");
|
|
}
|
|
}
|
|
|
|
void CheckLongAncillaryForwarding() {
|
|
IrProgram program;
|
|
auto& block = program.CreateBlock();
|
|
program.BlockOrder().push_back(&block);
|
|
BlockInfo info;
|
|
info.id = 0u;
|
|
info.terminator.kind = TerminatorKind::Return;
|
|
program.Metadata().blockInfo.push_back(info);
|
|
program.SetEntryBlock(block);
|
|
IrBuilder builder(program);
|
|
builder.SetInsertionPoint(block);
|
|
auto& ancillary = builder.Emit(IrOpcode::GetBuiltin, IrType::U32, {&builder.Constant(static_cast<std::uint32_t>(StageInputKind::PackedAncillary)), &builder.Constant(0u)});
|
|
auto& lane = builder.Emit(IrOpcode::LaneId, IrType::U32, {});
|
|
auto& condition = builder.Emit(IrOpcode::ULessThan32, IrType::Bool, {&lane, &builder.Constant(16u)});
|
|
auto& zero = builder.Constant(0u);
|
|
IrValue* forwarded = &ancillary;
|
|
for (std::uint32_t link = 0; link < 20000u; ++link) forwarded = &builder.Emit(IrOpcode::SelectU32, IrType::U32, {&condition, &zero, forwarded});
|
|
auto& field = builder.Emit(IrOpcode::BitFieldUExtract, IrType::U32, {forwarded, &builder.Constant(8u), &builder.Constant(4u)});
|
|
builder.Emit(IrOpcode::ReferenceU32, IrType::Void, {&field});
|
|
struct Run {
|
|
IrProgram& program;
|
|
std::string error;
|
|
} run{program, {}};
|
|
pthread_attr_t attributes;
|
|
pthread_attr_init(&attributes);
|
|
pthread_attr_setstacksize(&attributes, 256u * 1024u);
|
|
pthread_t thread;
|
|
const auto body = +[](void* argument) -> void* {
|
|
auto& state = *static_cast<Run*>(argument);
|
|
try {
|
|
ConstantFolder{}.Fold(state.program);
|
|
} catch (const std::exception& error) {
|
|
state.error = error.what();
|
|
}
|
|
return nullptr;
|
|
};
|
|
Require(pthread_create(&thread, &attributes, body, &run) == 0, "long ancillary forwarding: cannot start the folding thread");
|
|
pthread_join(thread, nullptr);
|
|
pthread_attr_destroy(&attributes);
|
|
Require(run.error.empty(), "long ancillary forwarding: folding failed");
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
try {
|
|
for (unsigned mode = 0; mode < 5u; ++mode) CheckDiamond(mode);
|
|
CheckLongAncillaryForwarding();
|
|
std::puts("constant phi dominance tests passed");
|
|
return 0;
|
|
} catch (const std::exception& error) {
|
|
std::fprintf(stderr, "%s\n", error.what());
|
|
return 1;
|
|
}
|
|
}
|