Merge pull request #7325 from Cleverdee/fix/image-table-nested-addend

fix(recompiler): fold chained immediate addends of image-table record offsets
This commit is contained in:
Dean authored and GitHub committed 2026-10-18 13:30:46 +13:00
commit f1af6c5915
2 files changed
+44 -4

No files matched your search

@@ -1904,6 +1904,40 @@ void verifyPointerTableOffsetCarry() {
#endif
}
void verifyChainedTableOffset() {
using namespace ShaderRecompiler;
namespace Abi = ImageTableAbi;
static TableTexture textures[2];
alignas(256) static std::array<std::uint32_t, 0x3000> root{};
static std::array<std::uint32_t, 8> keys{};
static std::array<std::uint32_t, 4> output{};
const auto keysV = TableV(keys.data(), 0u, static_cast<std::uint32_t>(sizeof(keys)));
const auto outputV = TableV(output.data(), 0u, static_cast<std::uint32_t>(sizeof(output)));
std::copy(keysV.begin(), keysV.end(), root.begin());
std::copy(outputV.begin(), outputV.end(), root.begin() + 4);
const auto at = [&](std::uint32_t offset, std::array<std::uint32_t, 8> words) {
std::copy(words.begin(), words.end(), root.begin() + offset / 4u);
};
at(0x60u + 3u * 32u, TableT(textures[0].bytes.data(), TableFormat8888UNorm, TableType2D));
at(0x60u + 5u * 32u, TableT(textures[1].bytes.data(), TableFormat32UInt, TableType2D));
const auto base = static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(root.data()));
const std::array<std::uint32_t, 2> userData{static_cast<std::uint32_t>(base), static_cast<std::uint32_t>(base >> 32u)};
const std::vector<std::uint32_t> code{0xf4080200u, 0xfa000000u, 0xf4080700u, 0xfa000010u, 0xf4080800u, 0xfa000020u, 0x7e200500u, 0x8f108210u, 0xf4200444u,
0x20000000u, 0x8711ff11u, 0x000000ffu, 0x8f128511u, 0x8113a012u, 0x81149013u, 0xf4080900u, 0x26000040u, 0xf4080a00u, 0x28000040u, 0xf09c0f08u,
0x01090000u, 0xe0700000u, 0x80070000u, 0xbf810000u};
auto request = TableRequest(code, userData);
request.shader.codeAddress = 0x37000u;
const auto plan = GetResourcePlan(request);
require(plan->info.images.size() == 1u && plan->info.images[0].table == 0u, "chained table offset: a T# whose second load offset adds to the first one's was not planned as a table");
const auto& column = *plan->descriptorSources.at(plan->info.images[0].source).tableColumn;
require(column.address && column.stride == 32u && column.addend == 0x20u && column.offset == 0x40u && column.maxKey == 0xffu && column.foldedAddend == 0x20u && column.foldedCarry == 16u, "chained table offset: the column is wrong");
const auto compiled = CompileTable(request);
const auto& snapshot = compiled.capture->snapshot.tables.tables.at(0);
const auto& words = compiled.capture->snapshot.tables.words;
require(snapshot.keys == 256u && (snapshot.codes[3] & Abi::PoisonFlag) == 0u && (snapshot.codes[5] & Abi::PoisonFlag) == 0u, "chained table offset: the records were not resolved");
require(words.at(snapshot.codes[3]).dwords[0] == root[(0x60u + 3u * 32u) / 4u] && words.at(snapshot.codes[3]).dwords[7] == root[(0x60u + 3u * 32u) / 4u + 7u] && words.at(snapshot.codes[5]).dwords[0] == root[(0x60u + 5u * 32u) / 4u] && snapshot.codes[3] != snapshot.codes[5], "chained table offset: a record was read from the wrong offset");
}
void verifyDescriptorPhis() {
using namespace ShaderRecompiler;
constexpr std::uint32_t Format8888UNorm = 56;
@@ -5307,6 +5341,7 @@ int main(int argc, char** argv) {
verifyScalarAddressRule();
verifyPointerImageTable();
verifyPointerTableOffsetCarry();
verifyChainedTableOffset();
std::cout << "Image table keys, maps, records and registration passed\n";
return 0;
}
@@ -5338,6 +5373,7 @@ int main(int argc, char** argv) {
verifyScalarAddressRule();
verifyPointerImageTable();
verifyPointerTableOffsetCarry();
verifyChainedTableOffset();
verifyDescriptorPhis();
verifyUniformTableImage();
verifyLaneSelectedDescriptors();
@@ -542,11 +542,15 @@ private:
static IrValue* SplitImmediateAddend(IrValue* value, std::uint32_t& addend) {
value = value->Resolve();
addend = 0;
if (value->Opcode() == IrOpcode::IAdd32 && value->ArgumentCount() == 2u) {
if (immediateU32(value->Argument(0), addend)) return value->Argument(1)->Resolve();
if (immediateU32(value->Argument(1), addend)) return value->Argument(0)->Resolve();
while (value->Opcode() == IrOpcode::IAdd32 && value->ArgumentCount() == 2u) {
std::uint32_t immediate = 0;
IrValue* rest = nullptr;
if (immediateU32(value->Argument(0), immediate)) rest = value->Argument(1)->Resolve();
else if (immediateU32(value->Argument(1), immediate)) rest = value->Argument(0)->Resolve();
else break;
addend += immediate;
value = rest;
}
addend = 0;
return value;
}