119 lines
4.7 KiB
C++
119 lines
4.7 KiB
C++
#include "IntermediateRepresentation/IrBuilder.hpp"
|
|
#include "Optimization/ConstantFolder.hpp"
|
|
#include "Optimization/DeadCodeEliminator.hpp"
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <exception>
|
|
#include <initializer_list>
|
|
|
|
using namespace ShaderRecompiler;
|
|
|
|
namespace {
|
|
|
|
enum class Bitcast { None, F32, F16 };
|
|
|
|
struct RoundTrip {
|
|
const char* name;
|
|
unsigned bits;
|
|
Bitcast bitcast;
|
|
};
|
|
|
|
IrValue& convert(IrBuilder& builder, IrValue& source, const RoundTrip& test) {
|
|
if (test.bitcast == Bitcast::F16) {
|
|
return builder.BitCastU32FromF16(builder.BitCastF16(source));
|
|
}
|
|
auto& input = test.bitcast == Bitcast::F32 ? builder.BitCastU32(builder.BitCastF32(source)) : source;
|
|
auto& narrowed = builder.Emit(test.bits == 16 ? IrOpcode::ConvertU16U32 : IrOpcode::ConvertU8U32,
|
|
test.bits == 16 ? IrType::U16 : IrType::U8, {&input});
|
|
auto& widened = builder.Emit(test.bits == 16 ? IrOpcode::ConvertU32U16 : IrOpcode::ConvertU32U8,
|
|
IrType::U32, {&narrowed});
|
|
return test.bitcast == Bitcast::F32 ? builder.BitCastU32(builder.BitCastF32(widened)) : widened;
|
|
}
|
|
|
|
void fold(IrProgram& program, bool cleanup) {
|
|
ConstantFolder().Fold(program);
|
|
if (cleanup) {
|
|
DeadCodeEliminator().RemoveIdentities(program);
|
|
DeadCodeEliminator().Eliminate(program);
|
|
ConstantFolder().Fold(program);
|
|
DeadCodeEliminator().RemoveIdentities(program);
|
|
DeadCodeEliminator().Eliminate(program);
|
|
}
|
|
ValidateProgram(program, true);
|
|
}
|
|
|
|
bool checkRoundTrip(const RoundTrip& test, std::uint32_t pattern, std::uint32_t laneValue, bool dynamic, bool cleanup) {
|
|
IrProgram program;
|
|
auto& block = program.CreateBlock();
|
|
program.SetEntryBlock(block);
|
|
program.BlockOrder().push_back(&block);
|
|
BlockInfo info;
|
|
info.id = 0;
|
|
program.Metadata().blockInfo.push_back(info);
|
|
IrBuilder builder(program);
|
|
builder.SetInsertionPoint(block);
|
|
IrValue* lane = dynamic ? &builder.Emit(IrOpcode::LaneId, IrType::U32, {}) : nullptr;
|
|
auto& source = dynamic ? builder.Emit(IrOpcode::IAdd32, IrType::U32, {lane, &builder.Constant(pattern)})
|
|
: builder.Constant(pattern);
|
|
auto& result = convert(builder, source, test);
|
|
auto& user = builder.Emit(IrOpcode::ReferenceU32, IrType::Void, {&result});
|
|
static_cast<void>(builder.Emit(IrOpcode::Return, IrType::Void, {}));
|
|
ValidateProgram(program, true);
|
|
fold(program, cleanup);
|
|
if (dynamic) {
|
|
if (user.Argument(0)->Resolve()->HasImmediate() || !lane->HasUses()) {
|
|
std::fprintf(stderr, "%s: optimized result must still depend on LaneId\n", test.name);
|
|
return false;
|
|
}
|
|
lane->ReplaceUsesWith(&builder.Constant(laneValue), true);
|
|
fold(program, cleanup);
|
|
}
|
|
const auto expected = (pattern + laneValue) & (test.bits == 16 ? 0xffffu : 0xffu);
|
|
const auto* actual = user.Argument(0)->Resolve();
|
|
if (!actual->HasImmediate() || actual->Type() != IrType::U32 || actual->ImmediateU32() != expected) {
|
|
std::fprintf(stderr, "%s: pattern=%08x lane=%u dynamic=%d cleanup=%d expected=%08x actual=%08x immediate=%d\n",
|
|
test.name, pattern, laneValue, dynamic, cleanup, expected,
|
|
actual->HasImmediate() ? actual->ImmediateU32() : 0u, actual->HasImmediate());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
try {
|
|
constexpr RoundTrip cases[] = {
|
|
{"U16 round trip", 16, Bitcast::None},
|
|
{"U8 round trip", 8, Bitcast::None},
|
|
{"U16 with F32 bitcasts", 16, Bitcast::F32},
|
|
{"U8 with F32 bitcasts", 8, Bitcast::F32},
|
|
{"F16 bitcast round trip", 16, Bitcast::F16},
|
|
};
|
|
constexpr std::uint32_t patterns[] = {
|
|
0u, 1u, 0x7fu, 0x80u, 0xffu, 0x100u, 0x7fffu, 0x8000u,
|
|
0xffffu, 0x10000u, 0x12345678u, 0x80000000u, 0xffff0000u, 0xffffff00u, 0xffffffffu,
|
|
};
|
|
constexpr std::uint32_t lanes[] = {0u, 1u, 7u, 31u, 32u, 63u};
|
|
unsigned total = 0;
|
|
unsigned failures = 0;
|
|
for (const auto& test : cases) {
|
|
for (auto pattern : patterns) {
|
|
++total;
|
|
failures += !checkRoundTrip(test, pattern, 0u, false, true);
|
|
for (auto lane : lanes) {
|
|
for (bool cleanup : {false, true}) {
|
|
++total;
|
|
failures += !checkRoundTrip(test, pattern, lane, true, cleanup);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
std::printf("Integer narrowing: %u cases, %u failures\n", total, failures);
|
|
return failures == 0 ? 0 : 1;
|
|
} catch (const std::exception& error) {
|
|
std::fprintf(stderr, "%s\n", error.what());
|
|
return 1;
|
|
}
|
|
}
|