|
|
|
@ -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); |
|
|
|
} |
|
|
|
|