|
|
@@ -2,6 +2,8 @@
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
+#include <limits>
|
|
|
+
|
|
|
#include "common/common_types.h"
|
|
|
#include "shader_recompiler/exception.h"
|
|
|
#include "shader_recompiler/frontend/maxwell/opcodes.h"
|
|
|
@@ -55,6 +57,37 @@ size_t BitSize(DestFormat dest_format) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+std::pair<f64, f64> ClampBounds(DestFormat format, bool is_signed) {
|
|
|
+ if (is_signed) {
|
|
|
+ switch (format) {
|
|
|
+ case DestFormat::I16:
|
|
|
+ return {static_cast<f64>(std::numeric_limits<s16>::max()),
|
|
|
+ static_cast<f64>(std::numeric_limits<s16>::min())};
|
|
|
+ case DestFormat::I32:
|
|
|
+ return {static_cast<f64>(std::numeric_limits<s32>::max()),
|
|
|
+ static_cast<f64>(std::numeric_limits<s32>::min())};
|
|
|
+ case DestFormat::I64:
|
|
|
+ return {static_cast<f64>(std::numeric_limits<s64>::max()),
|
|
|
+ static_cast<f64>(std::numeric_limits<s64>::min())};
|
|
|
+ default: {}
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ switch (format) {
|
|
|
+ case DestFormat::I16:
|
|
|
+ return {static_cast<f64>(std::numeric_limits<u16>::max()),
|
|
|
+ static_cast<f64>(std::numeric_limits<u16>::min())};
|
|
|
+ case DestFormat::I32:
|
|
|
+ return {static_cast<f64>(std::numeric_limits<u32>::max()),
|
|
|
+ static_cast<f64>(std::numeric_limits<u32>::min())};
|
|
|
+ case DestFormat::I64:
|
|
|
+ return {static_cast<f64>(std::numeric_limits<u64>::max()),
|
|
|
+ static_cast<f64>(std::numeric_limits<u64>::min())};
|
|
|
+ default: {}
|
|
|
+ }
|
|
|
+ }
|
|
|
+ throw NotImplementedException("Invalid destination format {}", format);
|
|
|
+}
|
|
|
+
|
|
|
IR::F64 UnpackCbuf(TranslatorVisitor& v, u64 insn) {
|
|
|
union {
|
|
|
u64 raw;
|
|
|
@@ -112,13 +145,58 @@ void TranslateF2I(TranslatorVisitor& v, u64 insn, const IR::F16F32F64& src_a) {
|
|
|
// For example converting F32 65537.0 to U16, the expected value is 0xffff,
|
|
|
|
|
|
const bool is_signed{f2i.is_signed != 0};
|
|
|
- const size_t bitsize{BitSize(f2i.dest_format)};
|
|
|
- const IR::U16U32U64 result{v.ir.ConvertFToI(bitsize, is_signed, rounded_value)};
|
|
|
+ const auto [max_bound, min_bound] = ClampBounds(f2i.dest_format, is_signed);
|
|
|
+
|
|
|
+ IR::F16F32F64 intermediate;
|
|
|
+ switch (f2i.src_format) {
|
|
|
+ case SrcFormat::F16: {
|
|
|
+ const IR::F16 max_val{v.ir.FPConvert(16, v.ir.Imm32(static_cast<f32>(max_bound)))};
|
|
|
+ const IR::F16 min_val{v.ir.FPConvert(16, v.ir.Imm32(static_cast<f32>(min_bound)))};
|
|
|
+ intermediate = v.ir.FPClamp(rounded_value, min_val, max_val);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case SrcFormat::F32: {
|
|
|
+ const IR::F32 max_val{v.ir.Imm32(static_cast<f32>(max_bound))};
|
|
|
+ const IR::F32 min_val{v.ir.Imm32(static_cast<f32>(min_bound))};
|
|
|
+ intermediate = v.ir.FPClamp(rounded_value, min_val, max_val);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case SrcFormat::F64: {
|
|
|
+ const IR::F64 max_val{v.ir.Imm64(max_bound)};
|
|
|
+ const IR::F64 min_val{v.ir.Imm64(min_bound)};
|
|
|
+ intermediate = v.ir.FPClamp(rounded_value, min_val, max_val);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ throw NotImplementedException("Invalid destination format {}", f2i.dest_format.Value());
|
|
|
+ }
|
|
|
+
|
|
|
+ const size_t bitsize{std::max<size_t>(32, BitSize(f2i.dest_format))};
|
|
|
+ IR::U16U32U64 result{v.ir.ConvertFToI(bitsize, is_signed, intermediate)};
|
|
|
+
|
|
|
+ bool handled_special_case = false;
|
|
|
+ const bool special_nan_cases =
|
|
|
+ (f2i.src_format == SrcFormat::F64) != (f2i.dest_format == DestFormat::I64);
|
|
|
+ if (special_nan_cases) {
|
|
|
+ if (f2i.dest_format == DestFormat::I32) {
|
|
|
+ handled_special_case = true;
|
|
|
+ result = IR::U32{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm32(0x8000'0000U), result)};
|
|
|
+ } else if (f2i.dest_format == DestFormat::I64) {
|
|
|
+ handled_special_case = true;
|
|
|
+ result = IR::U64{
|
|
|
+ v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm64(0x8000'0000'0000'0000ULL), result)};
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!handled_special_case && is_signed) {
|
|
|
+ if (bitsize != 64) {
|
|
|
+ result = IR::U32{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm32(0U), result)};
|
|
|
+ } else {
|
|
|
+ result = IR::U64{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm64(0ULL), result)};
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
if (bitsize == 64) {
|
|
|
- const IR::Value vector{v.ir.UnpackUint2x32(result)};
|
|
|
- v.X(f2i.dest_reg + 0, IR::U32{v.ir.CompositeExtract(vector, 0)});
|
|
|
- v.X(f2i.dest_reg + 1, IR::U32{v.ir.CompositeExtract(vector, 1)});
|
|
|
+ v.L(f2i.dest_reg, result);
|
|
|
} else {
|
|
|
v.X(f2i.dest_reg, result);
|
|
|
}
|