// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include #include "common/assert.h" #include "shader_recompiler/exception.h" #include "shader_recompiler/ir/type.h" #include "shader_recompiler/ir/value.h" namespace Shader::IR { class ImmValue { public: ImmValue() noexcept = default; explicit ImmValue(const IR::Value& value) noexcept; explicit ImmValue(bool value) noexcept; explicit ImmValue(u8 value) noexcept; explicit ImmValue(s8 value) noexcept; explicit ImmValue(u16 value) noexcept; explicit ImmValue(s16 value) noexcept; explicit ImmValue(u32 value) noexcept; explicit ImmValue(s32 value) noexcept; explicit ImmValue(f32 value) noexcept; explicit ImmValue(u64 value) noexcept; explicit ImmValue(s64 value) noexcept; explicit ImmValue(f64 value) noexcept; ImmValue(u32 value1, u32 value2) noexcept; ImmValue(u32 value1, u32 value2, u32 value3) noexcept; ImmValue(u32 value1, u32 value2, u32 value3, u32 value4) noexcept; ImmValue(s32 value1, s32 value2) noexcept; ImmValue(s32 value1, s32 value2, s32 value3) noexcept; ImmValue(s32 value1, s32 value2, s32 value3, s32 value4) noexcept; ImmValue(f32 value1, f32 value2) noexcept; ImmValue(f32 value1, f32 value2, f32 value3) noexcept; ImmValue(f32 value1, f32 value2, f32 value3, f32 value4) noexcept; ImmValue(f64 value1, f64 value2) noexcept; ImmValue(f64 value1, f64 value2, f64 value3) noexcept; ImmValue(f64 value1, f64 value2, f64 value3, f64 value4) noexcept; [[nodiscard]] bool IsEmpty() const noexcept; [[nodiscard]] IR::Type Type() const noexcept; [[nodiscard]] IR::Type BaseType() const noexcept; [[nodiscard]] u32 Dimensions() const noexcept; [[nodiscard]] bool IsSigned() const noexcept; void SetSigned(bool signed_) noexcept; void SameSignAs(const ImmValue& other) noexcept; [[nodiscard]] bool U1() const; [[nodiscard]] u8 U8() const; [[nodiscard]] s8 S8() const; [[nodiscard]] u16 U16() const; [[nodiscard]] s16 S16() const; [[nodiscard]] u32 U32() const; [[nodiscard]] s32 S32() const; [[nodiscard]] f32 F32() const; [[nodiscard]] u64 U64() const; [[nodiscard]] s64 S64() const; [[nodiscard]] f64 F64() const; [[nodiscard]] std::tuple U32x2() const; [[nodiscard]] std::tuple U32x3() const; [[nodiscard]] std::tuple U32x4() const; [[nodiscard]] std::tuple S32x2() const; [[nodiscard]] std::tuple S32x3() const; [[nodiscard]] std::tuple S32x4() const; [[nodiscard]] std::tuple F32x2() const; [[nodiscard]] std::tuple F32x3() const; [[nodiscard]] std::tuple F32x4() const; [[nodiscard]] std::tuple F64x2() const; [[nodiscard]] std::tuple F64x3() const; [[nodiscard]] std::tuple F64x4() const; [[nodiscard]] bool operator==(const ImmValue& other) const noexcept; [[nodiscard]] bool operator!=(const ImmValue& other) const noexcept; [[nodiscard]] bool operator<(const ImmValue& other) const noexcept; [[nodiscard]] bool operator>(const ImmValue& other) const noexcept; [[nodiscard]] bool operator<=(const ImmValue& other) const noexcept; [[nodiscard]] bool operator>=(const ImmValue& other) const noexcept; [[nodiscard]] ImmValue operator+(const ImmValue& other) const noexcept; [[nodiscard]] ImmValue operator-(const ImmValue& other) const noexcept; [[nodiscard]] ImmValue operator*(const ImmValue& other) const noexcept; [[nodiscard]] ImmValue operator/(const ImmValue& other) const; [[nodiscard]] ImmValue operator%(const ImmValue& other) const noexcept; [[nodiscard]] ImmValue operator&(const ImmValue& other) const noexcept; [[nodiscard]] ImmValue operator|(const ImmValue& other) const noexcept; [[nodiscard]] ImmValue operator^(const ImmValue& other) const noexcept; [[nodiscard]] ImmValue operator<<(const ImmValue& other) const noexcept; [[nodiscard]] ImmValue operator>>(const ImmValue& other) const noexcept; [[nodiscard]] ImmValue operator~() const noexcept; [[nodiscard]] ImmValue operator++(int) noexcept; [[nodiscard]] ImmValue operator--(int) noexcept; ImmValue& operator++() noexcept; ImmValue& operator--() noexcept; [[nodiscard]] ImmValue operator-() const noexcept; [[nodiscard]] ImmValue operator+() const noexcept; ImmValue& operator+=(const ImmValue& other) noexcept; ImmValue& operator-=(const ImmValue& other) noexcept; ImmValue& operator*=(const ImmValue& other) noexcept; ImmValue& operator/=(const ImmValue& other); ImmValue& operator%=(const ImmValue& other) noexcept; ImmValue& operator&=(const ImmValue& other) noexcept; ImmValue& operator|=(const ImmValue& other) noexcept; ImmValue& operator^=(const ImmValue& other) noexcept; ImmValue& operator<<=(const ImmValue& other) noexcept; ImmValue& operator>>=(const ImmValue& other) noexcept; private: union Value { bool imm_u1; u8 imm_u8; s8 imm_s8; u16 imm_u16; s16 imm_s16; u32 imm_u32; s32 imm_s32; f32 imm_f32; u64 imm_u64; s64 imm_s64; f64 imm_f64; }; IR::Type type{}; bool is_signed{}; std::array imm_values; friend class std::hash; }; static_assert(std::is_trivially_copyable_v); template class TypedImmValue : public ImmValue { public: inline static constexpr IR::Type static_type = type_; inline static constexpr bool static_is_signed = is_signed_; TypedImmValue() = default; template requires((other_type & type_) != IR::Type::Void && other_signed == is_signed_) explicit(false) TypedImmValue(const TypedImmValue& other) : ImmValue(other) {} explicit TypedImmValue(const ImmValue& value) : ImmValue(value) { if ((value.Type() & type_) == IR::Type::Void && value.IsSigned() == is_signed_) { throw InvalidArgument("Incompatible types {} {} and {} {}", is_signed_ ? "signed" : "unsigned", type_, value.Type(), value.IsSigned() ? "signed" : "unsigned"); } } }; using ImmU1 = TypedImmValue; using ImmU8 = TypedImmValue; using ImmS8 = TypedImmValue; using ImmU16 = TypedImmValue; using ImmS16 = TypedImmValue; using ImmU32 = TypedImmValue; using ImmS32 = TypedImmValue; using ImmF32 = TypedImmValue; using ImmU64 = TypedImmValue; using ImmS64 = TypedImmValue; using ImmF64 = TypedImmValue; using ImmS32F32 = TypedImmValue; using ImmS64F64 = TypedImmValue; using ImmU32U64 = TypedImmValue; using ImmS32S64 = TypedImmValue; using ImmU16U32U64 = TypedImmValue; using ImmS16S32S64 = TypedImmValue; using ImmF32F64 = TypedImmValue; using ImmUAny = TypedImmValue; using ImmSAny = TypedImmValue; using ImmU32x2 = TypedImmValue; using ImmU32x3 = TypedImmValue; using ImmU32x4 = TypedImmValue; using ImmS32x2 = TypedImmValue; using ImmS32x3 = TypedImmValue; using ImmS32x4 = TypedImmValue; using ImmF32x2 = TypedImmValue; using ImmF32x3 = TypedImmValue; using ImmF32x4 = TypedImmValue; using ImmF64x2 = TypedImmValue; using ImmF64x3 = TypedImmValue; using ImmF64x4 = TypedImmValue; using ImmS32F32x2 = TypedImmValue; using ImmS32F32x3 = TypedImmValue; using ImmS32F32x4 = TypedImmValue; using ImmF32F64x2 = TypedImmValue; using ImmF32F64x3 = TypedImmValue; using ImmF32F64x4 = TypedImmValue; using ImmU32xAny = TypedImmValue; using ImmS32xAny = TypedImmValue; using ImmF32xAny = TypedImmValue; using ImmF64xAny = TypedImmValue; using ImmS32F32xAny = TypedImmValue; using ImmF32F64xAny = TypedImmValue; inline bool ImmValue::IsEmpty() const noexcept { return type == Type::Void; } inline IR::Type ImmValue::Type() const noexcept { return type; } inline bool ImmValue::U1() const { ASSERT(type == Type::U1 && !is_signed); return imm_values[0].imm_u1; } inline u8 ImmValue::U8() const { ASSERT(type == Type::U8 && !is_signed); return imm_values[0].imm_u8; } inline s8 ImmValue::S8() const { ASSERT(type == Type::U8 && is_signed); return imm_values[0].imm_s8; } inline u16 ImmValue::U16() const { ASSERT(type == Type::U16 && !is_signed); return imm_values[0].imm_u16; } inline s16 ImmValue::S16() const { ASSERT(type == Type::U16 && is_signed); return imm_values[0].imm_s16; } inline u32 ImmValue::U32() const { ASSERT(type == Type::U32 && !is_signed); return imm_values[0].imm_u32; } inline s32 ImmValue::S32() const { ASSERT(type == Type::U32 && is_signed); return imm_values[0].imm_s32; } inline f32 ImmValue::F32() const { ASSERT(type == Type::F32 && is_signed); return imm_values[0].imm_f32; } inline u64 ImmValue::U64() const { ASSERT(type == Type::U64 && !is_signed); return imm_values[0].imm_u64; } inline s64 ImmValue::S64() const { ASSERT(type == Type::U64 && is_signed); return imm_values[0].imm_s64; } inline f64 ImmValue::F64() const { ASSERT(type == Type::F64 && is_signed); return imm_values[0].imm_f64; } inline std::tuple ImmValue::U32x2() const { ASSERT(type == Type::U32x2 && !is_signed); return {imm_values[0].imm_u32, imm_values[1].imm_u32}; } inline std::tuple ImmValue::U32x3() const { ASSERT(type == Type::U32x3 && !is_signed); return {imm_values[0].imm_u32, imm_values[1].imm_u32, imm_values[2].imm_u32}; } inline std::tuple ImmValue::U32x4() const { ASSERT(type == Type::U32x4 && !is_signed); return {imm_values[0].imm_u32, imm_values[1].imm_u32, imm_values[2].imm_u32, imm_values[3].imm_u32}; } inline std::tuple ImmValue::S32x2() const { ASSERT(type == Type::U32x2 && is_signed); return {imm_values[0].imm_s32, imm_values[1].imm_s32}; } inline std::tuple ImmValue::S32x3() const { ASSERT(type == Type::U32x3 && is_signed); return {imm_values[0].imm_s32, imm_values[1].imm_s32, imm_values[2].imm_s32}; } inline std::tuple ImmValue::S32x4() const { ASSERT(type == Type::U32x4 && is_signed); return {imm_values[0].imm_s32, imm_values[1].imm_s32, imm_values[2].imm_s32, imm_values[3].imm_s32}; } inline std::tuple ImmValue::F32x2() const { ASSERT(type == Type::F32x2 && is_signed); return {imm_values[0].imm_f32, imm_values[1].imm_f32}; } inline std::tuple ImmValue::F32x3() const { ASSERT(type == Type::F32x3 && is_signed); return {imm_values[0].imm_f32, imm_values[1].imm_f32, imm_values[2].imm_f32}; } inline std::tuple ImmValue::F32x4() const { ASSERT(type == Type::F32x4 && is_signed); return {imm_values[0].imm_f32, imm_values[1].imm_f32, imm_values[2].imm_f32, imm_values[3].imm_f32}; } inline std::tuple ImmValue::F64x2() const { ASSERT(type == Type::F64x2 && is_signed); return {imm_values[0].imm_f64, imm_values[1].imm_f64}; } inline std::tuple ImmValue::F64x3() const { ASSERT(type == Type::F64x3 && is_signed); return {imm_values[0].imm_f64, imm_values[1].imm_f64, imm_values[2].imm_f64}; } inline std::tuple ImmValue::F64x4() const { ASSERT(type == Type::F64x4 && is_signed); return {imm_values[0].imm_f64, imm_values[1].imm_f64, imm_values[2].imm_f64, imm_values[3].imm_f64}; } } // namespace Shader::IR namespace std { template <> struct hash { std::size_t operator()(const Shader::IR::ImmValue& value) const; }; } // namespace std