| 
									
										
										
										
											2018-12-20 19:09:21 -03:00
										 |  |  | // Copyright 2018 yuzu Emulator Project
 | 
					
						
							|  |  |  | // Licensed under GPLv2 or any later version
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "common/assert.h"
 | 
					
						
							|  |  |  | #include "common/common_types.h"
 | 
					
						
							|  |  |  | #include "video_core/engines/shader_bytecode.h"
 | 
					
						
							| 
									
										
										
										
											2019-06-04 22:44:06 -03:00
										 |  |  | #include "video_core/shader/node_helper.h"
 | 
					
						
							| 
									
										
										
										
											2018-12-20 19:09:21 -03:00
										 |  |  | #include "video_core/shader/shader_ir.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace VideoCommon::Shader { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | using Tegra::Shader::Instruction; | 
					
						
							|  |  |  | using Tegra::Shader::OpCode; | 
					
						
							| 
									
										
										
										
											2018-12-15 17:32:51 -03:00
										 |  |  | using Tegra::Shader::Register; | 
					
						
							| 
									
										
										
										
											2018-12-20 19:09:21 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-30 02:09:40 -03:00
										 |  |  | u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) { | 
					
						
							| 
									
										
										
										
											2018-12-20 19:09:21 -03:00
										 |  |  |     const Instruction instr = {program_code[pc]}; | 
					
						
							|  |  |  |     const auto opcode = OpCode::Decode(instr); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-15 17:32:51 -03:00
										 |  |  |     switch (opcode->get().GetId()) { | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |     case OpCode::Id::I2I_R: | 
					
						
							|  |  |  |     case OpCode::Id::I2I_C: | 
					
						
							|  |  |  |     case OpCode::Id::I2I_IMM: { | 
					
						
							| 
									
										
										
										
											2019-08-27 23:39:38 -03:00
										 |  |  |         UNIMPLEMENTED_IF(instr.conversion.selector.Value()); | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |         UNIMPLEMENTED_IF(instr.conversion.dst_size != Register::Size::Word); | 
					
						
							|  |  |  |         UNIMPLEMENTED_IF(instr.alu.saturate_d); | 
					
						
							| 
									
										
										
										
											2018-12-17 21:14:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const bool input_signed = instr.conversion.is_input_signed; | 
					
						
							|  |  |  |         const bool output_signed = instr.conversion.is_output_signed; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |         Node value = [&]() { | 
					
						
							|  |  |  |             switch (opcode->get().GetId()) { | 
					
						
							|  |  |  |             case OpCode::Id::I2I_R: | 
					
						
							|  |  |  |                 return GetRegister(instr.gpr20); | 
					
						
							|  |  |  |             case OpCode::Id::I2I_C: | 
					
						
							|  |  |  |                 return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); | 
					
						
							|  |  |  |             case OpCode::Id::I2I_IMM: | 
					
						
							|  |  |  |                 return Immediate(instr.alu.GetSignedImm20_20()); | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |                 UNREACHABLE(); | 
					
						
							|  |  |  |                 return Immediate(0); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }(); | 
					
						
							| 
									
										
										
										
											2018-12-17 21:14:25 -03:00
										 |  |  |         value = ConvertIntegerSize(value, instr.conversion.src_size, input_signed); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         value = GetOperandAbsNegInteger(value, instr.conversion.abs_a, instr.conversion.negate_a, | 
					
						
							|  |  |  |                                         input_signed); | 
					
						
							|  |  |  |         if (input_signed != output_signed) { | 
					
						
							|  |  |  |             value = SignedOperation(OperationCode::ICastUnsigned, output_signed, NO_PRECISE, value); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-27 16:50:36 -03:00
										 |  |  |         SetInternalFlagsFromInteger(bb, value, instr.generates_cc); | 
					
						
							| 
									
										
										
										
											2018-12-17 21:14:25 -03:00
										 |  |  |         SetRegister(bb, instr.gpr0, value); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-12-21 01:53:05 -03:00
										 |  |  |     case OpCode::Id::I2F_R: | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |     case OpCode::Id::I2F_C: | 
					
						
							|  |  |  |     case OpCode::Id::I2F_IMM: { | 
					
						
							| 
									
										
										
										
											2019-08-27 23:39:38 -03:00
										 |  |  |         UNIMPLEMENTED_IF(instr.conversion.selector.Value()); | 
					
						
							| 
									
										
										
										
											2019-07-20 17:38:25 -04:00
										 |  |  |         UNIMPLEMENTED_IF(instr.conversion.dst_size == Register::Size::Long); | 
					
						
							| 
									
										
										
										
											2018-12-21 01:53:05 -03:00
										 |  |  |         UNIMPLEMENTED_IF_MSG(instr.generates_cc, | 
					
						
							|  |  |  |                              "Condition codes generation in I2F is not implemented"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Node value = [&]() { | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |             switch (opcode->get().GetId()) { | 
					
						
							|  |  |  |             case OpCode::Id::I2F_R: | 
					
						
							| 
									
										
										
										
											2018-12-21 01:53:05 -03:00
										 |  |  |                 return GetRegister(instr.gpr20); | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |             case OpCode::Id::I2F_C: | 
					
						
							| 
									
										
										
										
											2019-01-28 18:11:23 -03:00
										 |  |  |                 return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |             case OpCode::Id::I2F_IMM: | 
					
						
							|  |  |  |                 return Immediate(instr.alu.GetSignedImm20_20()); | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |                 UNREACHABLE(); | 
					
						
							|  |  |  |                 return Immediate(0); | 
					
						
							| 
									
										
										
										
											2018-12-21 01:53:05 -03:00
										 |  |  |             } | 
					
						
							|  |  |  |         }(); | 
					
						
							|  |  |  |         const bool input_signed = instr.conversion.is_input_signed; | 
					
						
							|  |  |  |         value = ConvertIntegerSize(value, instr.conversion.src_size, input_signed); | 
					
						
							|  |  |  |         value = GetOperandAbsNegInteger(value, instr.conversion.abs_a, false, input_signed); | 
					
						
							|  |  |  |         value = SignedOperation(OperationCode::FCastInteger, input_signed, PRECISE, value); | 
					
						
							|  |  |  |         value = GetOperandAbsNegFloat(value, false, instr.conversion.negate_a); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-27 16:50:36 -03:00
										 |  |  |         SetInternalFlagsFromFloat(bb, value, instr.generates_cc); | 
					
						
							| 
									
										
										
										
											2019-07-20 17:38:25 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (instr.conversion.dst_size == Register::Size::Short) { | 
					
						
							|  |  |  |             value = Operation(OperationCode::HCastFloat, PRECISE, value); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-21 01:53:05 -03:00
										 |  |  |         SetRegister(bb, instr.gpr0, value); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-12-17 21:42:59 -03:00
										 |  |  |     case OpCode::Id::F2F_R: | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |     case OpCode::Id::F2F_C: | 
					
						
							|  |  |  |     case OpCode::Id::F2F_IMM: { | 
					
						
							| 
									
										
										
										
											2019-08-27 23:39:38 -03:00
										 |  |  |         UNIMPLEMENTED_IF(instr.conversion.selector.Value()); | 
					
						
							| 
									
										
										
										
											2019-07-20 17:38:25 -04:00
										 |  |  |         UNIMPLEMENTED_IF(instr.conversion.dst_size == Register::Size::Long); | 
					
						
							|  |  |  |         UNIMPLEMENTED_IF(instr.conversion.src_size == Register::Size::Long); | 
					
						
							| 
									
										
										
										
											2018-12-15 17:32:51 -03:00
										 |  |  |         UNIMPLEMENTED_IF_MSG(instr.generates_cc, | 
					
						
							|  |  |  |                              "Condition codes generation in F2F is not implemented"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-17 21:42:59 -03:00
										 |  |  |         Node value = [&]() { | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |             switch (opcode->get().GetId()) { | 
					
						
							|  |  |  |             case OpCode::Id::F2F_R: | 
					
						
							| 
									
										
										
										
											2018-12-17 21:42:59 -03:00
										 |  |  |                 return GetRegister(instr.gpr20); | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |             case OpCode::Id::F2F_C: | 
					
						
							| 
									
										
										
										
											2019-01-28 18:11:23 -03:00
										 |  |  |                 return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |             case OpCode::Id::F2F_IMM: | 
					
						
							|  |  |  |                 return GetImmediate19(instr); | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |                 UNREACHABLE(); | 
					
						
							|  |  |  |                 return Immediate(0); | 
					
						
							| 
									
										
										
										
											2018-12-17 21:42:59 -03:00
										 |  |  |             } | 
					
						
							|  |  |  |         }(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-20 17:38:25 -04:00
										 |  |  |         if (instr.conversion.src_size == Register::Size::Short) { | 
					
						
							|  |  |  |             // TODO: figure where extract is sey in the encoding
 | 
					
						
							|  |  |  |             value = Operation(OperationCode::FCastHalf0, PRECISE, value); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-15 17:32:51 -03:00
										 |  |  |         value = GetOperandAbsNegFloat(value, instr.conversion.abs_a, instr.conversion.negate_a); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         value = [&]() { | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |             switch (instr.conversion.f2f.GetRoundingMode()) { | 
					
						
							| 
									
										
										
										
											2018-12-15 17:32:51 -03:00
										 |  |  |             case Tegra::Shader::F2fRoundingOp::None: | 
					
						
							|  |  |  |                 return value; | 
					
						
							|  |  |  |             case Tegra::Shader::F2fRoundingOp::Round: | 
					
						
							|  |  |  |                 return Operation(OperationCode::FRoundEven, PRECISE, value); | 
					
						
							|  |  |  |             case Tegra::Shader::F2fRoundingOp::Floor: | 
					
						
							|  |  |  |                 return Operation(OperationCode::FFloor, PRECISE, value); | 
					
						
							|  |  |  |             case Tegra::Shader::F2fRoundingOp::Ceil: | 
					
						
							|  |  |  |                 return Operation(OperationCode::FCeil, PRECISE, value); | 
					
						
							|  |  |  |             case Tegra::Shader::F2fRoundingOp::Trunc: | 
					
						
							|  |  |  |                 return Operation(OperationCode::FTrunc, PRECISE, value); | 
					
						
							| 
									
										
										
										
											2019-04-03 04:33:36 -03:00
										 |  |  |             default: | 
					
						
							|  |  |  |                 UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}", | 
					
						
							|  |  |  |                                   static_cast<u32>(instr.conversion.f2f.rounding.Value())); | 
					
						
							| 
									
										
										
										
											2019-07-20 17:38:25 -04:00
										 |  |  |                 return value; | 
					
						
							| 
									
										
										
										
											2018-12-15 17:32:51 -03:00
										 |  |  |             } | 
					
						
							|  |  |  |         }(); | 
					
						
							|  |  |  |         value = GetSaturatedFloat(value, instr.alu.saturate_d); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-27 16:50:36 -03:00
										 |  |  |         SetInternalFlagsFromFloat(bb, value, instr.generates_cc); | 
					
						
							| 
									
										
										
										
											2019-07-20 17:38:25 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (instr.conversion.dst_size == Register::Size::Short) { | 
					
						
							|  |  |  |             value = Operation(OperationCode::HCastFloat, PRECISE, value); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-15 17:32:51 -03:00
										 |  |  |         SetRegister(bb, instr.gpr0, value); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-12-21 01:57:13 -03:00
										 |  |  |     case OpCode::Id::F2I_R: | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |     case OpCode::Id::F2I_C: | 
					
						
							|  |  |  |     case OpCode::Id::F2I_IMM: { | 
					
						
							| 
									
										
										
										
											2019-07-20 17:38:25 -04:00
										 |  |  |         UNIMPLEMENTED_IF(instr.conversion.src_size == Register::Size::Long); | 
					
						
							| 
									
										
										
										
											2018-12-21 01:57:13 -03:00
										 |  |  |         UNIMPLEMENTED_IF_MSG(instr.generates_cc, | 
					
						
							|  |  |  |                              "Condition codes generation in F2I is not implemented"); | 
					
						
							|  |  |  |         Node value = [&]() { | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |             switch (opcode->get().GetId()) { | 
					
						
							|  |  |  |             case OpCode::Id::F2I_R: | 
					
						
							| 
									
										
										
										
											2018-12-21 01:57:13 -03:00
										 |  |  |                 return GetRegister(instr.gpr20); | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |             case OpCode::Id::F2I_C: | 
					
						
							| 
									
										
										
										
											2019-01-28 18:11:23 -03:00
										 |  |  |                 return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset()); | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |             case OpCode::Id::F2I_IMM: | 
					
						
							|  |  |  |                 return GetImmediate19(instr); | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |                 UNREACHABLE(); | 
					
						
							|  |  |  |                 return Immediate(0); | 
					
						
							| 
									
										
										
										
											2018-12-21 01:57:13 -03:00
										 |  |  |             } | 
					
						
							|  |  |  |         }(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-20 17:38:25 -04:00
										 |  |  |         if (instr.conversion.src_size == Register::Size::Short) { | 
					
						
							| 
									
										
										
										
											2019-08-27 23:39:38 -03:00
										 |  |  |             const OperationCode cast = [instr] { | 
					
						
							|  |  |  |                 switch (instr.conversion.selector) { | 
					
						
							|  |  |  |                 case 0: | 
					
						
							|  |  |  |                     return OperationCode::FCastHalf0; | 
					
						
							|  |  |  |                 case 1: | 
					
						
							|  |  |  |                     return OperationCode::FCastHalf1; | 
					
						
							|  |  |  |                 default: | 
					
						
							|  |  |  |                     UNREACHABLE_MSG("Invalid selector={}", | 
					
						
							|  |  |  |                                     static_cast<u32>(instr.conversion.selector)); | 
					
						
							|  |  |  |                     return OperationCode::FCastHalf0; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }(); | 
					
						
							|  |  |  |             value = Operation(cast, NO_PRECISE, std::move(value)); | 
					
						
							| 
									
										
										
										
											2019-07-20 17:38:25 -04:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-21 01:57:13 -03:00
										 |  |  |         value = GetOperandAbsNegFloat(value, instr.conversion.abs_a, instr.conversion.negate_a); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         value = [&]() { | 
					
						
							|  |  |  |             switch (instr.conversion.f2i.rounding) { | 
					
						
							| 
									
										
										
										
											2019-02-11 18:46:45 -04:00
										 |  |  |             case Tegra::Shader::F2iRoundingOp::RoundEven: | 
					
						
							|  |  |  |                 return Operation(OperationCode::FRoundEven, PRECISE, value); | 
					
						
							| 
									
										
										
										
											2018-12-21 01:57:13 -03:00
										 |  |  |             case Tegra::Shader::F2iRoundingOp::Floor: | 
					
						
							|  |  |  |                 return Operation(OperationCode::FFloor, PRECISE, value); | 
					
						
							|  |  |  |             case Tegra::Shader::F2iRoundingOp::Ceil: | 
					
						
							|  |  |  |                 return Operation(OperationCode::FCeil, PRECISE, value); | 
					
						
							|  |  |  |             case Tegra::Shader::F2iRoundingOp::Trunc: | 
					
						
							|  |  |  |                 return Operation(OperationCode::FTrunc, PRECISE, value); | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |                 UNIMPLEMENTED_MSG("Unimplemented F2I rounding mode {}", | 
					
						
							|  |  |  |                                   static_cast<u32>(instr.conversion.f2i.rounding.Value())); | 
					
						
							| 
									
										
										
										
											2018-12-21 18:47:22 -03:00
										 |  |  |                 return Immediate(0); | 
					
						
							| 
									
										
										
										
											2018-12-21 01:57:13 -03:00
										 |  |  |             } | 
					
						
							|  |  |  |         }(); | 
					
						
							|  |  |  |         const bool is_signed = instr.conversion.is_output_signed; | 
					
						
							|  |  |  |         value = SignedOperation(OperationCode::ICastFloat, is_signed, PRECISE, value); | 
					
						
							| 
									
										
										
										
											2019-04-15 19:04:33 -04:00
										 |  |  |         value = ConvertIntegerSize(value, instr.conversion.dst_size, is_signed); | 
					
						
							| 
									
										
										
										
											2018-12-21 01:57:13 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |         SetRegister(bb, instr.gpr0, value); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-12-15 17:32:51 -03:00
										 |  |  |     default: | 
					
						
							|  |  |  |         UNIMPLEMENTED_MSG("Unhandled conversion instruction: {}", opcode->get().GetName()); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-12-20 19:09:21 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return pc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-11 18:46:45 -04:00
										 |  |  | } // namespace VideoCommon::Shader
 |