// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/common_types.h"
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/maxwell/opcode.h"
#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
namespace Shader::Maxwell {
namespace {
enum class DestFormat : u64 {
Invalid,
I16,
I32,
I64,
};
enum class SrcFormat : u64 {
Invalid,
F16,
F32,
F64,
};
enum class Rounding : u64 {
Round,
Floor,
Ceil,
Trunc,
};
union F2I {
u64 raw;
BitField<0, 8, IR::Reg> dest_reg;
BitField<8, 2, DestFormat> dest_format;
BitField<10, 2, SrcFormat> src_format;
BitField<12, 1, u64> is_signed;
BitField<39, 1, Rounding> rounding;
BitField<49, 1, u64> half;
BitField<44, 1, u64> ftz;
BitField<45, 1, u64> abs;
BitField<47, 1, u64> cc;
BitField<49, 1, u64> neg;
};
size_t BitSize(DestFormat dest_format) {
switch (dest_format) {
case DestFormat::I16:
return 16;
case DestFormat::I32:
return 32;
case DestFormat::I64:
return 64;
default:
throw NotImplementedException("Invalid destination format {}", dest_format);
}
}
void TranslateF2I(TranslatorVisitor& v, u64 insn, const IR::U16U32U64& op_a) {
// F2I is used to convert from a floating point value to an integer
const F2I f2i{insn};
const IR::U16U32U64 float_value{v.ir.FPAbsNeg(op_a, f2i.abs != 0, f2i.neg != 0)};
const IR::U16U32U64 rounded_value{[&] {
switch (f2i.rounding) {
case Rounding::Round:
return v.ir.FPRoundEven(float_value);
case Rounding::Floor:
return v.ir.FPFloor(float_value);
case Rounding::Ceil:
return v.ir.FPCeil(float_value);
case Rounding::Trunc:
return v.ir.FPTrunc(float_value);
default:
throw NotImplementedException("Invalid F2I rounding {}", f2i.rounding.Value());
}
}()};
// TODO: Handle out of bounds conversions.
// 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)};
v.X(f2i.dest_reg, result);
if (f2i.cc != 0) {
v.SetZFlag(v.ir.GetZeroFromOp(result));
if (is_signed) {
v.SetSFlag(v.ir.GetSignFromOp(result));
} else {
v.ResetSFlag();
}
v.ResetCFlag();
// TODO: Investigate if out of bound conversions sets the overflow flag
v.ResetOFlag();
}
}
} // Anonymous namespace
void TranslatorVisitor::F2I_reg(u64 insn) {
union {
F2I base;
BitField<20, 8, IR::Reg> src_reg;
} const f2i{insn};
const IR::U16U32U64 op_a{[&]() -> IR::U16U32U64 {
switch (f2i.base.src_format) {
case SrcFormat::F16:
return ir.CompositeExtract(ir.UnpackFloat2x16(X(f2i.src_reg)), f2i.base.half);
case SrcFormat::F32:
return X(f2i.src_reg);
case SrcFormat::F64:
return ir.PackDouble2x32(ir.CompositeConstruct(X(f2i.src_reg), X(f2i.src_reg + 1)));
default:
throw NotImplementedException("Invalid F2I source format {}",
f2i.base.src_format.Value());
}
}()};
TranslateF2I(*this, insn, op_a);
}
void TranslatorVisitor::F2I_cbuf(u64) {
throw NotImplementedException("{}", Opcode::F2I_cbuf);
}
void TranslatorVisitor::F2I_imm(u64) {
throw NotImplementedException("{}", Opcode::F2I_imm);
}
} // namespace Shader::Maxwell