Merge pull request #3400 from AchrafReyani/carry/pipodi-3349
fix(recompiler): evaluate an out-of-range constant buffer read as zero
This commit is contained in:
commit
6cee0bb02a
|
|
@ -464,6 +464,63 @@ void verifySignedSrtComparison() {
|
|||
}
|
||||
}
|
||||
|
||||
void verifyOutOfRangeConstantReads() {
|
||||
std::vector<std::uint32_t> code{0xf40c0200u, 0xfa000000u, 0xf4080500u, 0xfa000020u, 0xf4080600u, 0xfa000040u, 0xf4000400u, 0xfa000050u, 0xf4080a00u, 0xfa000080u, 0xbf8cc07fu, 0xf42c0814u, 0xfa000000u, 0xbf8cc07fu, 0xbf068010u, 0xbf850008u};
|
||||
for (std::uint32_t dword = 0; dword < 8u; dword++) {
|
||||
code.push_back(0xbe800300u | ((8u + dword) << 16u) | (32u + dword));
|
||||
}
|
||||
code.insert(code.end(), {0xf09c0f08u, 0x00a20000u, 0xbf8c3f70u, 0xe0700000u, 0x80060000u, 0xbf810000u});
|
||||
using namespace ShaderRecompiler;
|
||||
constexpr std::uint32_t Format8888UNorm = 56;
|
||||
constexpr std::uint32_t Type2D = 9;
|
||||
struct alignas(256) Texture { std::array<std::uint8_t, 256> bytes{}; };
|
||||
static Texture textures[2];
|
||||
const auto imageDescriptor = [&](const Texture& texture) {
|
||||
const auto base = static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(texture.bytes.data()));
|
||||
return std::array<std::uint32_t, 8>{static_cast<std::uint32_t>(base >> 8u), static_cast<std::uint32_t>((base >> 40u) & 0xffu) | (Format8888UNorm << 20u) | (3u << 30u), 3u << 14u, 0xfacu | (Type2D << 28u), 0u, 0u, 0u, 0u};
|
||||
};
|
||||
std::array<std::uint32_t, 4> output{};
|
||||
const auto outputAddress = static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(output.data()));
|
||||
std::array<std::uint32_t, 36> srt{};
|
||||
const auto first = imageDescriptor(textures[0]);
|
||||
const auto second = imageDescriptor(textures[1]);
|
||||
std::copy(first.begin(), first.end(), srt.begin());
|
||||
const std::array<std::uint32_t, 4> outputV{static_cast<std::uint32_t>(outputAddress), static_cast<std::uint32_t>((outputAddress >> 32u) & 0xffffu), 16u, 0xfacu};
|
||||
std::copy(outputV.begin(), outputV.end(), srt.begin() + 16);
|
||||
srt[20] = 1u;
|
||||
std::copy(second.begin(), second.end(), srt.begin() + 24);
|
||||
const auto srtAddress = static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(srt.data()));
|
||||
const std::array<std::uint32_t, 2> userData{static_cast<std::uint32_t>(srtAddress), static_cast<std::uint32_t>(srtAddress >> 32u)};
|
||||
const std::array<std::uint32_t, 4> capabilities{29u, spv::CapabilityInt64, spv::CapabilityPhysicalStorageBufferAddresses, spv::CapabilityStorageBuffer8BitAccess};
|
||||
const std::array<std::string_view, 2> extensions{"SPV_KHR_physical_storage_buffer", "SPV_KHR_8bit_storage"};
|
||||
RecompileRequest request{};
|
||||
request.shader = {ShaderStage::Compute, 0x23000u, code, 0, {}};
|
||||
request.context.waveSize = 32u;
|
||||
request.context.userDataBaseRegister = 0;
|
||||
request.context.userData = userData;
|
||||
request.context.compute = ShaderComputeStageInfo{{32u, 1u, 1u}, 0u, {false, false, false}, false, 1u};
|
||||
request.target.vulkanVersion = 0x00401000u;
|
||||
request.target.spirvVersion = 0x00010300u;
|
||||
request.target.subgroupSize = 32;
|
||||
request.target.supportedCapabilities = capabilities;
|
||||
request.target.supportedExtensions = extensions;
|
||||
request.target.bdaAbiVersion = BdaAbi::Version;
|
||||
request.target.fragmentShaderBarycentricEnabled = false;
|
||||
request.layout.pushConstantSizeBytes = 128;
|
||||
const auto plan = GetResourcePlan(request);
|
||||
require(plan->info.images.size() == 2u && plan->info.samplers.size() == 1u, "out-of-range s_buffer_load: the two T#s were not planned as one image each");
|
||||
AgcDriver::ShaderMemory memory({});
|
||||
const auto capture = memory.Capture(request);
|
||||
request.context.memory = memory.Regions();
|
||||
static_cast<void>(Recompile(request, *capture));
|
||||
const auto holds = [&](const std::array<std::uint32_t, 8>& words) {
|
||||
return std::ranges::any_of(capture->snapshot.images, [&](const DescriptorValue& value) {
|
||||
return std::equal(words.begin(), words.end(), value.dwords.begin());
|
||||
});
|
||||
};
|
||||
require(holds(first) && holds(std::array<std::uint32_t, 8>{}), "out-of-range s_buffer_load: a T# read through a null V# was not evaluated as zero");
|
||||
}
|
||||
|
||||
// The pure flat slots of a hand-built plan (Detail::ComputePureFlatSlots): a slot is pure unless
|
||||
// a descriptor dword, a condition, a uniform value or another slot's address cone reaches it.
|
||||
void verifyPureFlatSlots() {
|
||||
|
|
@ -2711,6 +2768,7 @@ int main(int argc, char** argv) {
|
|||
verifyEvaluatedValues();
|
||||
verifyFrontendPair();
|
||||
verifySignedSrtComparison();
|
||||
verifyOutOfRangeConstantReads();
|
||||
verifySignedMinimum();
|
||||
verifyPureFlatSlots();
|
||||
verifyBindlessTable();
|
||||
|
|
|
|||
|
|
@ -143,7 +143,8 @@ bool Evaluator::EvaluateRawRead(IrValue& inst, std::uint64_t& result) {
|
|||
const auto stride = (static_cast<std::uint32_t>(high) >> 16u) & 0x3fffu;
|
||||
const auto size = stride == 0u ? static_cast<std::uint64_t>(static_cast<std::uint32_t>(records)) : static_cast<std::uint64_t>(stride) * static_cast<std::uint32_t>(records);
|
||||
if (aligned > size || size - aligned < sizeof(std::uint32_t)) {
|
||||
return false;
|
||||
result = 0;
|
||||
return true;
|
||||
}
|
||||
address = ((base & ~std::uint64_t {3}) + byteOffset) & ~std::uint64_t {3};
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in New Issue