fix(shader): fold BitReverse32 of an immediate

A descriptor dword built by S_BREV_B32, V_BFREV_B32 or S_BREV_B64 from a
constant stayed a runtime value, so the resource could not be resolved
and the shader failed to prepare. The constant folder now reverses the
bits of an immediate the way the hardware does (D[31-i] = S0[i]), like
the existing BitCount32 fold. ConstantBitTest covers the fold.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
Alex 2026-10-11 04:23:07 -04:00
parent 09bd4aa1a3
commit d809958b27
2 changed files with 34 additions and 0 deletions

View File

@ -28,6 +28,22 @@ bool foldsTo(std::uint32_t word, bool expected, bool maskedShift) {
return result->HasImmediate() && result->ImmediateBool() == expected;
}
bool reversesTo(std::uint32_t word, std::uint32_t expected) {
IrProgram program;
auto& block = program.CreateBlock();
program.SetEntryBlock(block);
program.BlockOrder().push_back(&block);
IrBuilder builder(program);
builder.SetInsertionPoint(block);
auto& reversed = builder.Emit(IrOpcode::BitReverse32, IrType::U32, {&builder.Constant(word)});
auto& test = builder.Emit(IrOpcode::IEqual32, IrType::Bool, {&reversed, &builder.Constant(expected)});
auto& user = builder.Emit(IrOpcode::Reference, IrType::Void, {&test});
static_cast<void>(builder.Emit(IrOpcode::Return, IrType::Void, {}));
ConstantFolder().Fold(program);
const IrValue* result = user.Argument(0)->Resolve();
return result->HasImmediate() && result->ImmediateBool();
}
bool run(const char* name, bool passed) {
if (!passed) std::fprintf(stderr, "%s\n", name);
return passed;
@ -42,6 +58,11 @@ int main() {
passed &= run("an all-one word must fold the lane bit test to true", foldsTo(0xffffffffu, true, true));
passed &= run("a mixed word must keep the lane bit test", foldsTo(0x0000ffffu, false, true));
passed &= run("an unmasked lane number must keep the lane bit test", foldsTo(0xffffffffu, true, false));
passed &= run("a reversed zero must fold to zero", reversesTo(0x00000000u, 0x00000000u));
passed &= run("a reversed single bit must fold to the opposite end", reversesTo(0x00000001u, 0x80000000u));
passed &= run("a reversed high bit must fold to bit zero", reversesTo(0x80000000u, 0x00000001u));
passed &= run("a reversed mixed word must fold to its mirror image", reversesTo(0x12345678u, 0x1e6a2c48u));
passed &= run("a reversed descriptor dword must fold to its mirror image", reversesTo(0xdeadbeefu, 0xf77db57bu));
return passed ? 0 : 1;
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());

View File

@ -623,6 +623,19 @@ bool ConstantFolder::tryFoldValue(IrProgram& program, IrValue& value) const {
replaceWith(value, builder.Constant(static_cast<std::uint32_t>(std::popcount(operand.ImmediateU64()))));
return true;
}
case IrOpcode::BitReverse32: {
auto& operand = resolveArg(value, 0);
if (!isImmediate(operand, IrType::U32)) {
return false;
}
const std::uint32_t word = operand.ImmediateU32();
std::uint32_t reversed = 0;
for (std::uint32_t bit = 0; bit < 32; ++bit) {
reversed |= ((word >> bit) & 1u) << (31u - bit);
}
replaceWith(value, builder.Constant(reversed));
return true;
}
case IrOpcode::SMin32:
case IrOpcode::SMax32: {
const auto opcode = value.Opcode();