Merge pull request #2409 from ReinUsesLisp/half-floats

shader_ir/decode: Miscellaneous fixes to half-float decompilation
This commit is contained in:
bunnei 2019-04-19 21:31:52 -04:00 committed by GitHub
commit 650d9b1044
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 181 additions and 136 deletions

View file

@ -119,14 +119,10 @@ std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
/// Returns true if an object has to be treated as precise /// Returns true if an object has to be treated as precise
bool IsPrecise(Operation operand) { bool IsPrecise(Operation operand) {
const auto& meta = operand.GetMeta(); const auto& meta{operand.GetMeta()};
if (const auto arithmetic = std::get_if<MetaArithmetic>(&meta)) { if (const auto arithmetic = std::get_if<MetaArithmetic>(&meta)) {
return arithmetic->precise; return arithmetic->precise;
} }
if (const auto half_arithmetic = std::get_if<MetaHalfArithmetic>(&meta)) {
return half_arithmetic->precise;
}
return false; return false;
} }
@ -627,28 +623,7 @@ private:
} }
std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) { std::string VisitOperand(Operation operation, std::size_t operand_index, Type type) {
std::string value = VisitOperand(operation, operand_index); return CastOperand(VisitOperand(operation, operand_index), type);
switch (type) {
case Type::HalfFloat: {
const auto half_meta = std::get_if<MetaHalfArithmetic>(&operation.GetMeta());
if (!half_meta) {
value = "toHalf2(" + value + ')';
}
switch (half_meta->types.at(operand_index)) {
case Tegra::Shader::HalfType::H0_H1:
return "toHalf2(" + value + ')';
case Tegra::Shader::HalfType::F32:
return "vec2(" + value + ')';
case Tegra::Shader::HalfType::H0_H0:
return "vec2(toHalf2(" + value + ")[0])";
case Tegra::Shader::HalfType::H1_H1:
return "vec2(toHalf2(" + value + ")[1])";
}
}
default:
return CastOperand(value, type);
}
} }
std::string CastOperand(const std::string& value, Type type) const { std::string CastOperand(const std::string& value, Type type) const {
@ -662,9 +637,7 @@ private:
case Type::Uint: case Type::Uint:
return "ftou(" + value + ')'; return "ftou(" + value + ')';
case Type::HalfFloat: case Type::HalfFloat:
// Can't be handled as a stand-alone value return "toHalf2(" + value + ')';
UNREACHABLE();
return value;
} }
UNREACHABLE(); UNREACHABLE();
return value; return value;
@ -1083,13 +1056,40 @@ private:
return BitwiseCastResult(value, Type::HalfFloat); return BitwiseCastResult(value, Type::HalfFloat);
} }
std::string HClamp(Operation operation) {
const std::string value = VisitOperand(operation, 0, Type::HalfFloat);
const std::string min = VisitOperand(operation, 1, Type::Float);
const std::string max = VisitOperand(operation, 2, Type::Float);
const std::string clamped = "clamp(" + value + ", vec2(" + min + "), vec2(" + max + "))";
return ApplyPrecise(operation, BitwiseCastResult(clamped, Type::HalfFloat));
}
std::string HUnpack(Operation operation) {
const std::string operand{VisitOperand(operation, 0, Type::HalfFloat)};
const auto value = [&]() -> std::string {
switch (std::get<Tegra::Shader::HalfType>(operation.GetMeta())) {
case Tegra::Shader::HalfType::H0_H1:
return operand;
case Tegra::Shader::HalfType::F32:
return "vec2(fromHalf2(" + operand + "))";
case Tegra::Shader::HalfType::H0_H0:
return "vec2(" + operand + "[0])";
case Tegra::Shader::HalfType::H1_H1:
return "vec2(" + operand + "[1])";
}
UNREACHABLE();
return "0";
}();
return "fromHalf2(" + value + ')';
}
std::string HMergeF32(Operation operation) { std::string HMergeF32(Operation operation) {
return "float(toHalf2(" + Visit(operation[0]) + ")[0])"; return "float(toHalf2(" + Visit(operation[0]) + ")[0])";
} }
std::string HMergeH0(Operation operation) { std::string HMergeH0(Operation operation) {
return "fromHalf2(vec2(toHalf2(" + Visit(operation[0]) + ")[1], toHalf2(" + return "fromHalf2(vec2(toHalf2(" + Visit(operation[1]) + ")[0], toHalf2(" +
Visit(operation[1]) + ")[0]))"; Visit(operation[0]) + ")[1]))";
} }
std::string HMergeH1(Operation operation) { std::string HMergeH1(Operation operation) {
@ -1189,34 +1189,46 @@ private:
return GenerateUnary(operation, "any", Type::Bool, Type::Bool2); return GenerateUnary(operation, "any", Type::Bool, Type::Bool2);
} }
template <bool with_nan>
std::string GenerateHalfComparison(Operation operation, std::string compare_op) {
std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2,
Type::HalfFloat, Type::HalfFloat)};
if constexpr (!with_nan) {
return comparison;
}
return "halfFloatNanComparison(" + comparison + ", " +
VisitOperand(operation, 0, Type::HalfFloat) + ", " +
VisitOperand(operation, 1, Type::HalfFloat) + ')';
}
template <bool with_nan>
std::string Logical2HLessThan(Operation operation) { std::string Logical2HLessThan(Operation operation) {
return GenerateBinaryCall(operation, "lessThan", Type::Bool2, Type::HalfFloat, return GenerateHalfComparison<with_nan>(operation, "lessThan");
Type::HalfFloat);
} }
template <bool with_nan>
std::string Logical2HEqual(Operation operation) { std::string Logical2HEqual(Operation operation) {
return GenerateBinaryCall(operation, "equal", Type::Bool2, Type::HalfFloat, return GenerateHalfComparison<with_nan>(operation, "equal");
Type::HalfFloat);
} }
template <bool with_nan>
std::string Logical2HLessEqual(Operation operation) { std::string Logical2HLessEqual(Operation operation) {
return GenerateBinaryCall(operation, "lessThanEqual", Type::Bool2, Type::HalfFloat, return GenerateHalfComparison<with_nan>(operation, "lessThanEqual");
Type::HalfFloat);
} }
template <bool with_nan>
std::string Logical2HGreaterThan(Operation operation) { std::string Logical2HGreaterThan(Operation operation) {
return GenerateBinaryCall(operation, "greaterThan", Type::Bool2, Type::HalfFloat, return GenerateHalfComparison<with_nan>(operation, "greaterThan");
Type::HalfFloat);
} }
template <bool with_nan>
std::string Logical2HNotEqual(Operation operation) { std::string Logical2HNotEqual(Operation operation) {
return GenerateBinaryCall(operation, "notEqual", Type::Bool2, Type::HalfFloat, return GenerateHalfComparison<with_nan>(operation, "notEqual");
Type::HalfFloat);
} }
template <bool with_nan>
std::string Logical2HGreaterEqual(Operation operation) { std::string Logical2HGreaterEqual(Operation operation) {
return GenerateBinaryCall(operation, "greaterThanEqual", Type::Bool2, Type::HalfFloat, return GenerateHalfComparison<with_nan>(operation, "greaterThanEqual");
Type::HalfFloat);
} }
std::string Texture(Operation operation) { std::string Texture(Operation operation) {
@ -1505,6 +1517,8 @@ private:
&GLSLDecompiler::Fma<Type::HalfFloat>, &GLSLDecompiler::Fma<Type::HalfFloat>,
&GLSLDecompiler::Absolute<Type::HalfFloat>, &GLSLDecompiler::Absolute<Type::HalfFloat>,
&GLSLDecompiler::HNegate, &GLSLDecompiler::HNegate,
&GLSLDecompiler::HClamp,
&GLSLDecompiler::HUnpack,
&GLSLDecompiler::HMergeF32, &GLSLDecompiler::HMergeF32,
&GLSLDecompiler::HMergeH0, &GLSLDecompiler::HMergeH0,
&GLSLDecompiler::HMergeH1, &GLSLDecompiler::HMergeH1,
@ -1541,12 +1555,18 @@ private:
&GLSLDecompiler::LogicalNotEqual<Type::Uint>, &GLSLDecompiler::LogicalNotEqual<Type::Uint>,
&GLSLDecompiler::LogicalGreaterEqual<Type::Uint>, &GLSLDecompiler::LogicalGreaterEqual<Type::Uint>,
&GLSLDecompiler::Logical2HLessThan, &GLSLDecompiler::Logical2HLessThan<false>,
&GLSLDecompiler::Logical2HEqual, &GLSLDecompiler::Logical2HEqual<false>,
&GLSLDecompiler::Logical2HLessEqual, &GLSLDecompiler::Logical2HLessEqual<false>,
&GLSLDecompiler::Logical2HGreaterThan, &GLSLDecompiler::Logical2HGreaterThan<false>,
&GLSLDecompiler::Logical2HNotEqual, &GLSLDecompiler::Logical2HNotEqual<false>,
&GLSLDecompiler::Logical2HGreaterEqual, &GLSLDecompiler::Logical2HGreaterEqual<false>,
&GLSLDecompiler::Logical2HLessThan<true>,
&GLSLDecompiler::Logical2HEqual<true>,
&GLSLDecompiler::Logical2HLessEqual<true>,
&GLSLDecompiler::Logical2HGreaterThan<true>,
&GLSLDecompiler::Logical2HNotEqual<true>,
&GLSLDecompiler::Logical2HGreaterEqual<true>,
&GLSLDecompiler::Texture, &GLSLDecompiler::Texture,
&GLSLDecompiler::TextureLod, &GLSLDecompiler::TextureLod,
@ -1647,6 +1667,12 @@ std::string GetCommonDeclarations() {
"}\n\n" "}\n\n"
"vec2 toHalf2(float value) {\n" "vec2 toHalf2(float value) {\n"
" return unpackHalf2x16(ftou(value));\n" " return unpackHalf2x16(ftou(value));\n"
"}\n\n"
"bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {\n"
" bvec2 is_nan1 = isnan(pair1);\n"
" bvec2 is_nan2 = isnan(pair2);\n"
" return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || "
"is_nan2.y);\n"
"}\n"; "}\n";
} }

View file

@ -76,14 +76,10 @@ constexpr u32 GetGenericAttributeLocation(Attribute::Index attribute) {
/// Returns true if an object has to be treated as precise /// Returns true if an object has to be treated as precise
bool IsPrecise(Operation operand) { bool IsPrecise(Operation operand) {
const auto& meta = operand.GetMeta(); const auto& meta{operand.GetMeta()};
if (std::holds_alternative<MetaArithmetic>(meta)) { if (std::holds_alternative<MetaArithmetic>(meta)) {
return std::get<MetaArithmetic>(meta).precise; return std::get<MetaArithmetic>(meta).precise;
} }
if (std::holds_alternative<MetaHalfArithmetic>(meta)) {
return std::get<MetaHalfArithmetic>(meta).precise;
}
return false; return false;
} }
@ -746,6 +742,16 @@ private:
return {}; return {};
} }
Id HClamp(Operation operation) {
UNIMPLEMENTED();
return {};
}
Id HUnpack(Operation operation) {
UNIMPLEMENTED();
return {};
}
Id HMergeF32(Operation operation) { Id HMergeF32(Operation operation) {
UNIMPLEMENTED(); UNIMPLEMENTED();
return {}; return {};
@ -1218,6 +1224,8 @@ private:
&SPIRVDecompiler::Ternary<&Module::OpFma, Type::HalfFloat>, &SPIRVDecompiler::Ternary<&Module::OpFma, Type::HalfFloat>,
&SPIRVDecompiler::Unary<&Module::OpFAbs, Type::HalfFloat>, &SPIRVDecompiler::Unary<&Module::OpFAbs, Type::HalfFloat>,
&SPIRVDecompiler::HNegate, &SPIRVDecompiler::HNegate,
&SPIRVDecompiler::HClamp,
&SPIRVDecompiler::HUnpack,
&SPIRVDecompiler::HMergeF32, &SPIRVDecompiler::HMergeF32,
&SPIRVDecompiler::HMergeH0, &SPIRVDecompiler::HMergeH0,
&SPIRVDecompiler::HMergeH1, &SPIRVDecompiler::HMergeH1,
@ -1254,6 +1262,13 @@ private:
&SPIRVDecompiler::Binary<&Module::OpINotEqual, Type::Bool, Type::Uint>, &SPIRVDecompiler::Binary<&Module::OpINotEqual, Type::Bool, Type::Uint>,
&SPIRVDecompiler::Binary<&Module::OpUGreaterThanEqual, Type::Bool, Type::Uint>, &SPIRVDecompiler::Binary<&Module::OpUGreaterThanEqual, Type::Bool, Type::Uint>,
&SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool, Type::HalfFloat>,
&SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool, Type::HalfFloat>,
&SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool, Type::HalfFloat>,
&SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThan, Type::Bool, Type::HalfFloat>,
&SPIRVDecompiler::Binary<&Module::OpFOrdNotEqual, Type::Bool, Type::HalfFloat>,
&SPIRVDecompiler::Binary<&Module::OpFOrdGreaterThanEqual, Type::Bool, Type::HalfFloat>,
// TODO(Rodrigo): Should these use the OpFUnord* variants?
&SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool, Type::HalfFloat>, &SPIRVDecompiler::Binary<&Module::OpFOrdLessThan, Type::Bool, Type::HalfFloat>,
&SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool, Type::HalfFloat>, &SPIRVDecompiler::Binary<&Module::OpFOrdEqual, Type::Bool, Type::HalfFloat>,
&SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool, Type::HalfFloat>, &SPIRVDecompiler::Binary<&Module::OpFOrdLessThanEqual, Type::Bool, Type::HalfFloat>,

View file

@ -18,7 +18,9 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) {
if (opcode->get().GetId() == OpCode::Id::HADD2_C || if (opcode->get().GetId() == OpCode::Id::HADD2_C ||
opcode->get().GetId() == OpCode::Id::HADD2_R) { opcode->get().GetId() == OpCode::Id::HADD2_R) {
UNIMPLEMENTED_IF(instr.alu_half.ftz != 0); if (instr.alu_half.ftz != 0) {
LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName());
}
} }
UNIMPLEMENTED_IF_MSG(instr.alu_half.saturate != 0, "Half float saturation not implemented"); UNIMPLEMENTED_IF_MSG(instr.alu_half.saturate != 0, "Half float saturation not implemented");
@ -27,9 +29,8 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) {
const bool negate_b = const bool negate_b =
opcode->get().GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0; opcode->get().GetId() != OpCode::Id::HMUL2_C && instr.alu_half.negate_b != 0;
const Node op_a = GetOperandAbsNegHalf(GetRegister(instr.gpr8), instr.alu_half.abs_a, negate_a); Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half.type_a);
op_a = GetOperandAbsNegHalf(op_a, instr.alu_half.abs_a, negate_a);
// instr.alu_half.type_a
Node op_b = [&]() { Node op_b = [&]() {
switch (opcode->get().GetId()) { switch (opcode->get().GetId()) {
@ -44,17 +45,17 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) {
return Immediate(0); return Immediate(0);
} }
}(); }();
op_b = UnpackHalfFloat(op_b, instr.alu_half.type_b);
op_b = GetOperandAbsNegHalf(op_b, instr.alu_half.abs_b, negate_b); op_b = GetOperandAbsNegHalf(op_b, instr.alu_half.abs_b, negate_b);
Node value = [&]() { Node value = [&]() {
MetaHalfArithmetic meta{true, {instr.alu_half_imm.type_a, instr.alu_half.type_b}};
switch (opcode->get().GetId()) { switch (opcode->get().GetId()) {
case OpCode::Id::HADD2_C: case OpCode::Id::HADD2_C:
case OpCode::Id::HADD2_R: case OpCode::Id::HADD2_R:
return Operation(OperationCode::HAdd, meta, op_a, op_b); return Operation(OperationCode::HAdd, PRECISE, op_a, op_b);
case OpCode::Id::HMUL2_C: case OpCode::Id::HMUL2_C:
case OpCode::Id::HMUL2_R: case OpCode::Id::HMUL2_R:
return Operation(OperationCode::HMul, meta, op_a, op_b); return Operation(OperationCode::HMul, PRECISE, op_a, op_b);
default: default:
UNIMPLEMENTED_MSG("Unhandled half float instruction: {}", opcode->get().GetName()); UNIMPLEMENTED_MSG("Unhandled half float instruction: {}", opcode->get().GetName());
return Immediate(0); return Immediate(0);

View file

@ -17,34 +17,33 @@ u32 ShaderIR::DecodeArithmeticHalfImmediate(NodeBlock& bb, u32 pc) {
const auto opcode = OpCode::Decode(instr); const auto opcode = OpCode::Decode(instr);
if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) { if (opcode->get().GetId() == OpCode::Id::HADD2_IMM) {
UNIMPLEMENTED_IF(instr.alu_half_imm.ftz != 0); if (instr.alu_half_imm.ftz != 0) {
LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName());
}
} else { } else {
UNIMPLEMENTED_IF(instr.alu_half_imm.precision != Tegra::Shader::HalfPrecision::None); UNIMPLEMENTED_IF(instr.alu_half_imm.precision != Tegra::Shader::HalfPrecision::None);
} }
UNIMPLEMENTED_IF_MSG(instr.alu_half_imm.saturate != 0,
"Half float immediate saturation not implemented");
Node op_a = GetRegister(instr.gpr8); Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half_imm.type_a);
op_a = GetOperandAbsNegHalf(op_a, instr.alu_half_imm.abs_a, instr.alu_half_imm.negate_a); op_a = GetOperandAbsNegHalf(op_a, instr.alu_half_imm.abs_a, instr.alu_half_imm.negate_a);
const Node op_b = UnpackHalfImmediate(instr, true); const Node op_b = UnpackHalfImmediate(instr, true);
Node value = [&]() { Node value = [&]() {
MetaHalfArithmetic meta{true, {instr.alu_half_imm.type_a}};
switch (opcode->get().GetId()) { switch (opcode->get().GetId()) {
case OpCode::Id::HADD2_IMM: case OpCode::Id::HADD2_IMM:
return Operation(OperationCode::HAdd, meta, op_a, op_b); return Operation(OperationCode::HAdd, PRECISE, op_a, op_b);
case OpCode::Id::HMUL2_IMM: case OpCode::Id::HMUL2_IMM:
return Operation(OperationCode::HMul, meta, op_a, op_b); return Operation(OperationCode::HMul, PRECISE, op_a, op_b);
default: default:
UNREACHABLE(); UNREACHABLE();
return Immediate(0); return Immediate(0);
} }
}(); }();
value = GetSaturatedHalfFloat(value, instr.alu_half_imm.saturate);
value = HalfMerge(GetRegister(instr.gpr0), value, instr.alu_half_imm.merge); value = HalfMerge(GetRegister(instr.gpr0), value, instr.alu_half_imm.merge);
SetRegister(bb, instr.gpr0, value); SetRegister(bb, instr.gpr0, value);
return pc; return pc;
} }

View file

@ -18,11 +18,13 @@ u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) {
const Instruction instr = {program_code[pc]}; const Instruction instr = {program_code[pc]};
const auto opcode = OpCode::Decode(instr); const auto opcode = OpCode::Decode(instr);
UNIMPLEMENTED_IF(instr.hset2.ftz != 0); if (instr.hset2.ftz != 0) {
LOG_WARNING(HW_GPU, "{} FTZ not implemented", opcode->get().GetName());
}
Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hset2.type_a);
op_a = GetOperandAbsNegHalf(op_a, instr.hset2.abs_a, instr.hset2.negate_a);
// instr.hset2.type_a
// instr.hset2.type_b
Node op_a = GetRegister(instr.gpr8);
Node op_b = [&]() { Node op_b = [&]() {
switch (opcode->get().GetId()) { switch (opcode->get().GetId()) {
case OpCode::Id::HSET2_R: case OpCode::Id::HSET2_R:
@ -32,14 +34,12 @@ u32 ShaderIR::DecodeHalfSet(NodeBlock& bb, u32 pc) {
return Immediate(0); return Immediate(0);
} }
}(); }();
op_b = UnpackHalfFloat(op_b, instr.hset2.type_b);
op_a = GetOperandAbsNegHalf(op_a, instr.hset2.abs_a, instr.hset2.negate_a);
op_b = GetOperandAbsNegHalf(op_b, instr.hset2.abs_b, instr.hset2.negate_b); op_b = GetOperandAbsNegHalf(op_b, instr.hset2.abs_b, instr.hset2.negate_b);
const Node second_pred = GetPredicate(instr.hset2.pred39, instr.hset2.neg_pred); const Node second_pred = GetPredicate(instr.hset2.pred39, instr.hset2.neg_pred);
MetaHalfArithmetic meta{false, {instr.hset2.type_a, instr.hset2.type_b}}; const Node comparison_pair = GetPredicateComparisonHalf(instr.hset2.cond, op_a, op_b);
const Node comparison_pair = GetPredicateComparisonHalf(instr.hset2.cond, meta, op_a, op_b);
const OperationCode combiner = GetPredicateCombiner(instr.hset2.op); const OperationCode combiner = GetPredicateCombiner(instr.hset2.op);

View file

@ -19,10 +19,10 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) {
UNIMPLEMENTED_IF(instr.hsetp2.ftz != 0); UNIMPLEMENTED_IF(instr.hsetp2.ftz != 0);
Node op_a = GetRegister(instr.gpr8); Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hsetp2.type_a);
op_a = GetOperandAbsNegHalf(op_a, instr.hsetp2.abs_a, instr.hsetp2.negate_a); op_a = GetOperandAbsNegHalf(op_a, instr.hsetp2.abs_a, instr.hsetp2.negate_a);
const Node op_b = [&]() { Node op_b = [&]() {
switch (opcode->get().GetId()) { switch (opcode->get().GetId()) {
case OpCode::Id::HSETP2_R: case OpCode::Id::HSETP2_R:
return GetOperandAbsNegHalf(GetRegister(instr.gpr20), instr.hsetp2.abs_a, return GetOperandAbsNegHalf(GetRegister(instr.gpr20), instr.hsetp2.abs_a,
@ -32,6 +32,7 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) {
return Immediate(0); return Immediate(0);
} }
}(); }();
op_b = UnpackHalfFloat(op_b, instr.hsetp2.type_b);
// We can't use the constant predicate as destination. // We can't use the constant predicate as destination.
ASSERT(instr.hsetp2.pred3 != static_cast<u64>(Pred::UnusedIndex)); ASSERT(instr.hsetp2.pred3 != static_cast<u64>(Pred::UnusedIndex));
@ -42,8 +43,7 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) {
const OperationCode pair_combiner = const OperationCode pair_combiner =
instr.hsetp2.h_and ? OperationCode::LogicalAll2 : OperationCode::LogicalAny2; instr.hsetp2.h_and ? OperationCode::LogicalAll2 : OperationCode::LogicalAny2;
MetaHalfArithmetic meta = {false, {instr.hsetp2.type_a, instr.hsetp2.type_b}}; const Node comparison = GetPredicateComparisonHalf(instr.hsetp2.cond, op_a, op_b);
const Node comparison = GetPredicateComparisonHalf(instr.hsetp2.cond, meta, op_a, op_b);
const Node first_pred = Operation(pair_combiner, comparison); const Node first_pred = Operation(pair_combiner, comparison);
// Set the primary predicate to the result of Predicate OP SecondPredicate // Set the primary predicate to the result of Predicate OP SecondPredicate

View file

@ -27,10 +27,6 @@ u32 ShaderIR::DecodeHfma2(NodeBlock& bb, u32 pc) {
} }
constexpr auto identity = HalfType::H0_H1; constexpr auto identity = HalfType::H0_H1;
const HalfType type_a = instr.hfma2.type_a;
const Node op_a = GetRegister(instr.gpr8);
bool neg_b{}, neg_c{}; bool neg_b{}, neg_c{};
auto [saturate, type_b, op_b, type_c, auto [saturate, type_b, op_b, type_c,
op_c] = [&]() -> std::tuple<bool, HalfType, Node, HalfType, Node> { op_c] = [&]() -> std::tuple<bool, HalfType, Node, HalfType, Node> {
@ -62,11 +58,11 @@ u32 ShaderIR::DecodeHfma2(NodeBlock& bb, u32 pc) {
}(); }();
UNIMPLEMENTED_IF_MSG(saturate, "HFMA2 saturation is not implemented"); UNIMPLEMENTED_IF_MSG(saturate, "HFMA2 saturation is not implemented");
op_b = GetOperandAbsNegHalf(op_b, false, neg_b); const Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.hfma2.type_a);
op_c = GetOperandAbsNegHalf(op_c, false, neg_c); op_b = GetOperandAbsNegHalf(UnpackHalfFloat(op_b, type_b), false, neg_b);
op_c = GetOperandAbsNegHalf(UnpackHalfFloat(op_c, type_c), false, neg_c);
MetaHalfArithmetic meta{true, {type_a, type_b, type_c}}; Node value = Operation(OperationCode::HFma, PRECISE, op_a, op_b, op_c);
Node value = Operation(OperationCode::HFma, meta, op_a, op_b, op_c);
value = HalfMerge(GetRegister(instr.gpr0), value, instr.hfma2.merge); value = HalfMerge(GetRegister(instr.gpr0), value, instr.hfma2.merge);
SetRegister(bb, instr.gpr0, value); SetRegister(bb, instr.gpr0, value);

View file

@ -189,7 +189,11 @@ Node ShaderIR::UnpackHalfImmediate(Instruction instr, bool has_negation) {
const Node first_negate = GetPredicate(instr.half_imm.first_negate != 0); const Node first_negate = GetPredicate(instr.half_imm.first_negate != 0);
const Node second_negate = GetPredicate(instr.half_imm.second_negate != 0); const Node second_negate = GetPredicate(instr.half_imm.second_negate != 0);
return Operation(OperationCode::HNegate, HALF_NO_PRECISE, value, first_negate, second_negate); return Operation(OperationCode::HNegate, NO_PRECISE, value, first_negate, second_negate);
}
Node ShaderIR::UnpackHalfFloat(Node value, Tegra::Shader::HalfType type) {
return Operation(OperationCode::HUnpack, type, value);
} }
Node ShaderIR::HalfMerge(Node dest, Node src, Tegra::Shader::HalfMerge merge) { Node ShaderIR::HalfMerge(Node dest, Node src, Tegra::Shader::HalfMerge merge) {
@ -209,17 +213,26 @@ Node ShaderIR::HalfMerge(Node dest, Node src, Tegra::Shader::HalfMerge merge) {
Node ShaderIR::GetOperandAbsNegHalf(Node value, bool absolute, bool negate) { Node ShaderIR::GetOperandAbsNegHalf(Node value, bool absolute, bool negate) {
if (absolute) { if (absolute) {
value = Operation(OperationCode::HAbsolute, HALF_NO_PRECISE, value); value = Operation(OperationCode::HAbsolute, NO_PRECISE, value);
} }
if (negate) { if (negate) {
value = Operation(OperationCode::HNegate, HALF_NO_PRECISE, value, GetPredicate(true), value = Operation(OperationCode::HNegate, NO_PRECISE, value, GetPredicate(true),
GetPredicate(true)); GetPredicate(true));
} }
return value; return value;
} }
Node ShaderIR::GetSaturatedHalfFloat(Node value, bool saturate) {
if (!saturate) {
return value;
}
const Node positive_zero = Immediate(std::copysignf(0, 1));
const Node positive_one = Immediate(1.0f);
return Operation(OperationCode::HClamp, NO_PRECISE, value, positive_zero, positive_one);
}
Node ShaderIR::GetPredicateComparisonFloat(PredCondition condition, Node op_a, Node op_b) { Node ShaderIR::GetPredicateComparisonFloat(PredCondition condition, Node op_a, Node op_b) {
static const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = { const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = {
{PredCondition::LessThan, OperationCode::LogicalFLessThan}, {PredCondition::LessThan, OperationCode::LogicalFLessThan},
{PredCondition::Equal, OperationCode::LogicalFEqual}, {PredCondition::Equal, OperationCode::LogicalFEqual},
{PredCondition::LessEqual, OperationCode::LogicalFLessEqual}, {PredCondition::LessEqual, OperationCode::LogicalFLessEqual},
@ -255,7 +268,7 @@ Node ShaderIR::GetPredicateComparisonFloat(PredCondition condition, Node op_a, N
Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_signed, Node op_a, Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_signed, Node op_a,
Node op_b) { Node op_b) {
static const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = { const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = {
{PredCondition::LessThan, OperationCode::LogicalILessThan}, {PredCondition::LessThan, OperationCode::LogicalILessThan},
{PredCondition::Equal, OperationCode::LogicalIEqual}, {PredCondition::Equal, OperationCode::LogicalIEqual},
{PredCondition::LessEqual, OperationCode::LogicalILessEqual}, {PredCondition::LessEqual, OperationCode::LogicalILessEqual},
@ -283,40 +296,32 @@ Node ShaderIR::GetPredicateComparisonInteger(PredCondition condition, bool is_si
return predicate; return predicate;
} }
Node ShaderIR::GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, Node ShaderIR::GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, Node op_a,
const MetaHalfArithmetic& meta, Node op_a, Node op_b) { Node op_b) {
const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = {
UNIMPLEMENTED_IF_MSG(condition == PredCondition::LessThanWithNan ||
condition == PredCondition::NotEqualWithNan ||
condition == PredCondition::LessEqualWithNan ||
condition == PredCondition::GreaterThanWithNan ||
condition == PredCondition::GreaterEqualWithNan,
"Unimplemented NaN comparison for half floats");
static const std::unordered_map<PredCondition, OperationCode> PredicateComparisonTable = {
{PredCondition::LessThan, OperationCode::Logical2HLessThan}, {PredCondition::LessThan, OperationCode::Logical2HLessThan},
{PredCondition::Equal, OperationCode::Logical2HEqual}, {PredCondition::Equal, OperationCode::Logical2HEqual},
{PredCondition::LessEqual, OperationCode::Logical2HLessEqual}, {PredCondition::LessEqual, OperationCode::Logical2HLessEqual},
{PredCondition::GreaterThan, OperationCode::Logical2HGreaterThan}, {PredCondition::GreaterThan, OperationCode::Logical2HGreaterThan},
{PredCondition::NotEqual, OperationCode::Logical2HNotEqual}, {PredCondition::NotEqual, OperationCode::Logical2HNotEqual},
{PredCondition::GreaterEqual, OperationCode::Logical2HGreaterEqual}, {PredCondition::GreaterEqual, OperationCode::Logical2HGreaterEqual},
{PredCondition::LessThanWithNan, OperationCode::Logical2HLessThan}, {PredCondition::LessThanWithNan, OperationCode::Logical2HLessThanWithNan},
{PredCondition::NotEqualWithNan, OperationCode::Logical2HNotEqual}, {PredCondition::NotEqualWithNan, OperationCode::Logical2HNotEqualWithNan},
{PredCondition::LessEqualWithNan, OperationCode::Logical2HLessEqual}, {PredCondition::LessEqualWithNan, OperationCode::Logical2HLessEqualWithNan},
{PredCondition::GreaterThanWithNan, OperationCode::Logical2HGreaterThan}, {PredCondition::GreaterThanWithNan, OperationCode::Logical2HGreaterThanWithNan},
{PredCondition::GreaterEqualWithNan, OperationCode::Logical2HGreaterEqual}}; {PredCondition::GreaterEqualWithNan, OperationCode::Logical2HGreaterEqualWithNan}};
const auto comparison{PredicateComparisonTable.find(condition)}; const auto comparison{PredicateComparisonTable.find(condition)};
UNIMPLEMENTED_IF_MSG(comparison == PredicateComparisonTable.end(), UNIMPLEMENTED_IF_MSG(comparison == PredicateComparisonTable.end(),
"Unknown predicate comparison operation"); "Unknown predicate comparison operation");
const Node predicate = Operation(comparison->second, meta, op_a, op_b); const Node predicate = Operation(comparison->second, NO_PRECISE, op_a, op_b);
return predicate; return predicate;
} }
OperationCode ShaderIR::GetPredicateCombiner(PredOperation operation) { OperationCode ShaderIR::GetPredicateCombiner(PredOperation operation) {
static const std::unordered_map<PredOperation, OperationCode> PredicateOperationTable = { const std::unordered_map<PredOperation, OperationCode> PredicateOperationTable = {
{PredOperation::And, OperationCode::LogicalAnd}, {PredOperation::And, OperationCode::LogicalAnd},
{PredOperation::Or, OperationCode::LogicalOr}, {PredOperation::Or, OperationCode::LogicalOr},
{PredOperation::Xor, OperationCode::LogicalXor}, {PredOperation::Xor, OperationCode::LogicalXor},

View file

@ -109,11 +109,13 @@ enum class OperationCode {
UBitfieldExtract, /// (MetaArithmetic, uint value, int offset, int offset) -> uint UBitfieldExtract, /// (MetaArithmetic, uint value, int offset, int offset) -> uint
UBitCount, /// (MetaArithmetic, uint) -> uint UBitCount, /// (MetaArithmetic, uint) -> uint
HAdd, /// (MetaHalfArithmetic, f16vec2 a, f16vec2 b) -> f16vec2 HAdd, /// (MetaArithmetic, f16vec2 a, f16vec2 b) -> f16vec2
HMul, /// (MetaHalfArithmetic, f16vec2 a, f16vec2 b) -> f16vec2 HMul, /// (MetaArithmetic, f16vec2 a, f16vec2 b) -> f16vec2
HFma, /// (MetaHalfArithmetic, f16vec2 a, f16vec2 b, f16vec2 c) -> f16vec2 HFma, /// (MetaArithmetic, f16vec2 a, f16vec2 b, f16vec2 c) -> f16vec2
HAbsolute, /// (f16vec2 a) -> f16vec2 HAbsolute, /// (f16vec2 a) -> f16vec2
HNegate, /// (f16vec2 a, bool first, bool second) -> f16vec2 HNegate, /// (f16vec2 a, bool first, bool second) -> f16vec2
HClamp, /// (f16vec2 src, float min, float max) -> f16vec2
HUnpack, /// (Tegra::Shader::HalfType, T value) -> f16vec2
HMergeF32, /// (f16vec2 src) -> float HMergeF32, /// (f16vec2 src) -> float
HMergeH0, /// (f16vec2 dest, f16vec2 src) -> f16vec2 HMergeH0, /// (f16vec2 dest, f16vec2 src) -> f16vec2
HMergeH1, /// (f16vec2 dest, f16vec2 src) -> f16vec2 HMergeH1, /// (f16vec2 dest, f16vec2 src) -> f16vec2
@ -150,12 +152,18 @@ enum class OperationCode {
LogicalUNotEqual, /// (uint a, uint b) -> bool LogicalUNotEqual, /// (uint a, uint b) -> bool
LogicalUGreaterEqual, /// (uint a, uint b) -> bool LogicalUGreaterEqual, /// (uint a, uint b) -> bool
Logical2HLessThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 Logical2HLessThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
Logical2HEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 Logical2HEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
Logical2HLessEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 Logical2HLessEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
Logical2HGreaterThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 Logical2HGreaterThan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
Logical2HNotEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 Logical2HNotEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
Logical2HGreaterEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2 Logical2HGreaterEqual, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
Logical2HLessThanWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
Logical2HEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
Logical2HLessEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
Logical2HGreaterThanWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
Logical2HNotEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
Logical2HGreaterEqualWithNan, /// (MetaHalfArithmetic, f16vec2 a, f16vec2) -> bool2
Texture, /// (MetaTexture, float[N] coords) -> float4 Texture, /// (MetaTexture, float[N] coords) -> float4
TextureLod, /// (MetaTexture, float[N] coords) -> float4 TextureLod, /// (MetaTexture, float[N] coords) -> float4
@ -308,13 +316,6 @@ struct MetaArithmetic {
bool precise{}; bool precise{};
}; };
struct MetaHalfArithmetic {
bool precise{};
std::array<Tegra::Shader::HalfType, 3> types = {Tegra::Shader::HalfType::H0_H1,
Tegra::Shader::HalfType::H0_H1,
Tegra::Shader::HalfType::H0_H1};
};
struct MetaTexture { struct MetaTexture {
const Sampler& sampler; const Sampler& sampler;
Node array{}; Node array{};
@ -326,11 +327,10 @@ struct MetaTexture {
u32 element{}; u32 element{};
}; };
constexpr MetaArithmetic PRECISE = {true}; inline constexpr MetaArithmetic PRECISE = {true};
constexpr MetaArithmetic NO_PRECISE = {false}; inline constexpr MetaArithmetic NO_PRECISE = {false};
constexpr MetaHalfArithmetic HALF_NO_PRECISE = {false};
using Meta = std::variant<MetaArithmetic, MetaHalfArithmetic, MetaTexture>; using Meta = std::variant<MetaArithmetic, MetaTexture, Tegra::Shader::HalfType>;
/// Holds any kind of operation that can be done in the IR /// Holds any kind of operation that can be done in the IR
class OperationNode final { class OperationNode final {
@ -734,10 +734,14 @@ private:
/// Unpacks a half immediate from an instruction /// Unpacks a half immediate from an instruction
Node UnpackHalfImmediate(Tegra::Shader::Instruction instr, bool has_negation); Node UnpackHalfImmediate(Tegra::Shader::Instruction instr, bool has_negation);
/// Unpacks a binary value into a half float pair with a type format
Node UnpackHalfFloat(Node value, Tegra::Shader::HalfType type);
/// Merges a half pair into another value /// Merges a half pair into another value
Node HalfMerge(Node dest, Node src, Tegra::Shader::HalfMerge merge); Node HalfMerge(Node dest, Node src, Tegra::Shader::HalfMerge merge);
/// Conditionally absolute/negated half float pair. Absolute is applied first /// Conditionally absolute/negated half float pair. Absolute is applied first
Node GetOperandAbsNegHalf(Node value, bool absolute, bool negate); Node GetOperandAbsNegHalf(Node value, bool absolute, bool negate);
/// Conditionally saturates a half float pair
Node GetSaturatedHalfFloat(Node value, bool saturate = true);
/// Returns a predicate comparing two floats /// Returns a predicate comparing two floats
Node GetPredicateComparisonFloat(Tegra::Shader::PredCondition condition, Node op_a, Node op_b); Node GetPredicateComparisonFloat(Tegra::Shader::PredCondition condition, Node op_a, Node op_b);
@ -745,8 +749,7 @@ private:
Node GetPredicateComparisonInteger(Tegra::Shader::PredCondition condition, bool is_signed, Node GetPredicateComparisonInteger(Tegra::Shader::PredCondition condition, bool is_signed,
Node op_a, Node op_b); Node op_a, Node op_b);
/// Returns a predicate comparing two half floats. meta consumes how both pairs will be compared /// Returns a predicate comparing two half floats. meta consumes how both pairs will be compared
Node GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, Node GetPredicateComparisonHalf(Tegra::Shader::PredCondition condition, Node op_a, Node op_b);
const MetaHalfArithmetic& meta, Node op_a, Node op_b);
/// Returns a predicate combiner operation /// Returns a predicate combiner operation
OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation); OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation);