From eebd557efc9fba5411ae54d2613a1c64a27ca93f Mon Sep 17 00:00:00 2001 From: Lander Gallastegi Date: Thu, 6 Mar 2025 01:02:39 +0100 Subject: [PATCH] ImmValue --- CMakeLists.txt | 2 + .../ir/compute_value/imm_value.cpp | 1106 +++++++++++++++++ .../ir/compute_value/imm_value.h | 345 +++++ 3 files changed, 1453 insertions(+) create mode 100644 src/shader_recompiler/ir/compute_value/imm_value.cpp create mode 100644 src/shader_recompiler/ir/compute_value/imm_value.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e1b8c2920..041642840 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -843,6 +843,8 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp src/shader_recompiler/ir/passes/shared_memory_to_storage_pass.cpp src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp + src/shader_recompiler/ir/compute_value/imm_value.cpp + src/shader_recompiler/ir/compute_value/imm_value.h src/shader_recompiler/ir/abstract_syntax_list.cpp src/shader_recompiler/ir/abstract_syntax_list.h src/shader_recompiler/ir/attribute.cpp diff --git a/src/shader_recompiler/ir/compute_value/imm_value.cpp b/src/shader_recompiler/ir/compute_value/imm_value.cpp new file mode 100644 index 000000000..f222ea009 --- /dev/null +++ b/src/shader_recompiler/ir/compute_value/imm_value.cpp @@ -0,0 +1,1106 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/hash.h" +#include "shader_recompiler/ir/compute_value/imm_value.h" + +namespace Shader::IR { + +ImmValue::ImmValue(const IR::Value& value) noexcept { + IR::Value resolved = value.Resolve(); + type = resolved.Type(); + switch (type) { + case Type::U1: + imm_values[0].imm_u1 = resolved.U1(); + break; + case Type::U8: + imm_values[0].imm_u8 = resolved.U8(); + break; + case Type::U16: + imm_values[0].imm_u16 = resolved.U16(); + break; + case Type::U32: + imm_values[0].imm_u32 = resolved.U32(); + break; + case Type::F32: + imm_values[0].imm_f32 = resolved.F32(); + break; + case Type::U64: + imm_values[0].imm_u64 = resolved.U64(); + break; + case Type::F64: + imm_values[0].imm_f64 = resolved.F64(); + break; + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +ImmValue::ImmValue(bool value) noexcept : type{Type::U1}, is_signed{false} { + imm_values[0].imm_u1 = value; +} + +ImmValue::ImmValue(u8 value) noexcept : type{Type::U8}, is_signed{false} { + imm_values[0].imm_u8 = value; +} + +ImmValue::ImmValue(s8 value) noexcept : type{Type::U8}, is_signed{true} { + imm_values[0].imm_s8 = value; +} + +ImmValue::ImmValue(u16 value) noexcept : type{Type::U16}, is_signed{false} { + imm_values[0].imm_u16 = value; +} + +ImmValue::ImmValue(s16 value) noexcept : type{Type::U16}, is_signed{true} { + imm_values[0].imm_s16 = value; +} + +ImmValue::ImmValue(u32 value) noexcept : type{Type::U32}, is_signed{false} { + imm_values[0].imm_u32 = value; +} + +ImmValue::ImmValue(s32 value) noexcept : type{Type::U32}, is_signed{true} { + imm_values[0].imm_s32 = value; +} + +ImmValue::ImmValue(f32 value) noexcept : type{Type::F32}, is_signed{true} { + imm_values[0].imm_f32 = value; +} + +ImmValue::ImmValue(u64 value) noexcept : type{Type::U64}, is_signed{false} { + imm_values[0].imm_u64 = value; +} + +ImmValue::ImmValue(s64 value) noexcept : type{Type::U64}, is_signed{true} { + imm_values[0].imm_s64 = value; +} + +ImmValue::ImmValue(f64 value) noexcept : type{Type::F64}, is_signed{true} { + imm_values[0].imm_f64 = value; +} + +ImmValue::ImmValue(u32 value1, u32 value2) noexcept : type{Type::U32x2}, is_signed{false} { + imm_values[0].imm_u32 = value1; + imm_values[1].imm_u32 = value2; +} + +ImmValue::ImmValue(u32 value1, u32 value2, u32 value3) noexcept + : type{Type::U32x3}, is_signed{false} { + imm_values[0].imm_u32 = value1; + imm_values[1].imm_u32 = value2; + imm_values[2].imm_u32 = value3; +} + +ImmValue::ImmValue(u32 value1, u32 value2, u32 value3, u32 value4) noexcept + : type{Type::U32x4}, is_signed{false} { + imm_values[0].imm_u32 = value1; + imm_values[1].imm_u32 = value2; + imm_values[2].imm_u32 = value3; + imm_values[3].imm_u32 = value4; +} + +ImmValue::ImmValue(s32 value1, s32 value2) noexcept : type{Type::U32x2}, is_signed{true} { + imm_values[0].imm_s32 = value1; + imm_values[1].imm_s32 = value2; +} + +ImmValue::ImmValue(s32 value1, s32 value2, s32 value3) noexcept + : type{Type::U32x3}, is_signed{true} { + imm_values[0].imm_s32 = value1; + imm_values[1].imm_s32 = value2; + imm_values[2].imm_s32 = value3; +} + +ImmValue::ImmValue(s32 value1, s32 value2, s32 value3, s32 value4) noexcept + : type{Type::U32x4}, is_signed{true} { + imm_values[0].imm_s32 = value1; + imm_values[1].imm_s32 = value2; + imm_values[2].imm_s32 = value3; + imm_values[3].imm_s32 = value4; +} + +ImmValue::ImmValue(f32 value1, f32 value2) noexcept : type{Type::F32x2}, is_signed{true} { + imm_values[0].imm_f32 = value1; + imm_values[1].imm_f32 = value2; +} + +ImmValue::ImmValue(f32 value1, f32 value2, f32 value3) noexcept + : type{Type::F32x3}, is_signed{true} { + imm_values[0].imm_f32 = value1; + imm_values[1].imm_f32 = value2; + imm_values[2].imm_f32 = value3; +} + +ImmValue::ImmValue(f32 value1, f32 value2, f32 value3, f32 value4) noexcept + : type{Type::F32x4}, is_signed{true} { + imm_values[0].imm_f32 = value1; + imm_values[1].imm_f32 = value2; + imm_values[2].imm_f32 = value3; + imm_values[3].imm_f32 = value4; +} + +ImmValue::ImmValue(f64 value1, f64 value2) noexcept : type{Type::F64x2}, is_signed{true} { + imm_values[0].imm_f64 = value1; + imm_values[1].imm_f64 = value2; +} + +ImmValue::ImmValue(f64 value1, f64 value2, f64 value3) noexcept + : type{Type::F64x3}, is_signed{true} { + imm_values[0].imm_f64 = value1; + imm_values[1].imm_f64 = value2; + imm_values[2].imm_f64 = value3; +} + +ImmValue::ImmValue(f64 value1, f64 value2, f64 value3, f64 value4) noexcept + : type{Type::F64x4}, is_signed{true} { + imm_values[0].imm_f64 = value1; + imm_values[1].imm_f64 = value2; + imm_values[2].imm_f64 = value3; + imm_values[3].imm_f64 = value4; +} + +IR::Type ImmValue::BaseType() const noexcept { + switch (type) { + case Type::U1: + return Type::U1; + case Type::U8: + return Type::U8; + case Type::U16: + return Type::U16; + case Type::U32: + case Type::U32x2: + case Type::U32x3: + case Type::U32x4: + return Type::U32; + case Type::U64: + return Type::U64; + case Type::F32: + case Type::F32x2: + case Type::F32x3: + case Type::F32x4: + return Type::F32; + case Type::F64: + case Type::F64x2: + case Type::F64x3: + case Type::F64x4: + return Type::F64; + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +u32 ImmValue::Dimensions() const noexcept { + switch (type) { + case Type::U1: + case Type::U8: + case Type::U16: + case Type::U32: + case Type::U64: + case Type::F32: + case Type::F64: + return 1; + case Type::U32x2: + case Type::F32x2: + case Type::F64x2: + return 2; + case Type::U32x3: + case Type::F32x3: + case Type::F64x3: + return 3; + case Type::U32x4: + case Type::F32x4: + case Type::F64x4: + return 4; + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +bool ImmValue::IsSigned() const noexcept { + return is_signed; +} + +void ImmValue::SetSigned(bool signed_) noexcept { + is_signed = signed_; +} + +void ImmValue::SameSignAs(const ImmValue& other) noexcept { + SetSigned(other.IsSigned()); +} + +bool ImmValue::operator==(const ImmValue& other) const noexcept { + if (type != other.type) { + return false; + } + switch (type) { + case Type::U1: + return imm_values[0].imm_u1 == other.imm_values[0].imm_u1; + case Type::U8: + return imm_values[0].imm_u8 == other.imm_values[0].imm_u8; + case Type::U16: + return imm_values[0].imm_u16 == other.imm_values[0].imm_u16; + case Type::U32: + case Type::F32: + return imm_values[0].imm_u32 == other.imm_values[0].imm_u32; + case Type::U64: + case Type::F64: + return imm_values[0].imm_u64 == other.imm_values[0].imm_u64; + case Type::U32x2: + case Type::F32x2: + case Type::F64x2: + return imm_values[0].imm_u32 == other.imm_values[0].imm_u32 && + imm_values[1].imm_u32 == other.imm_values[1].imm_u32; + case Type::U32x3: + case Type::F32x3: + case Type::F64x3: + return imm_values[0].imm_u32 == other.imm_values[0].imm_u32 && + imm_values[1].imm_u32 == other.imm_values[1].imm_u32 && + imm_values[2].imm_u32 == other.imm_values[2].imm_u32; + case Type::U32x4: + case Type::F32x4: + case Type::F64x4: + return imm_values[0].imm_u32 == other.imm_values[0].imm_u32 && + imm_values[1].imm_u32 == other.imm_values[1].imm_u32 && + imm_values[2].imm_u32 == other.imm_values[2].imm_u32 && + imm_values[3].imm_u32 == other.imm_values[3].imm_u32; + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +bool ImmValue::operator!=(const ImmValue& other) const noexcept { + return !operator==(other); +} + +bool ImmValue::operator<(const ImmValue& other) const noexcept { + ASSERT(type == other.type); + switch (type) { + case Type::U8: + return is_signed && other.is_signed ? imm_values[0].imm_s8 < other.imm_values[0].imm_s8 + : imm_values[0].imm_u8 < other.imm_values[0].imm_u8; + case Type::U16: + return is_signed && other.is_signed ? imm_values[0].imm_s16 < other.imm_values[0].imm_s16 + : imm_values[0].imm_u16 < other.imm_values[0].imm_u16; + case Type::U32: + return is_signed && other.is_signed ? imm_values[0].imm_s32 < other.imm_values[0].imm_s32 + : imm_values[0].imm_u32 < other.imm_values[0].imm_u32; + case Type::F32: + return imm_values[0].imm_f32 < other.imm_values[0].imm_f32; + case Type::U64: + return is_signed && other.is_signed ? imm_values[0].imm_s64 < other.imm_values[0].imm_s64 + : imm_values[0].imm_u64 < other.imm_values[0].imm_u64; + case Type::F64: + return imm_values[0].imm_f64 < other.imm_values[0].imm_f64; + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +bool ImmValue::operator>(const ImmValue& other) const noexcept { + ASSERT(type == other.type); + switch (type) { + case Type::U8: + return is_signed && other.is_signed ? imm_values[0].imm_s8 > other.imm_values[0].imm_s8 + : imm_values[0].imm_u8 > other.imm_values[0].imm_u8; + case Type::U16: + return is_signed && other.is_signed ? imm_values[0].imm_s16 > other.imm_values[0].imm_s16 + : imm_values[0].imm_u16 > other.imm_values[0].imm_u16; + case Type::U32: + return is_signed && other.is_signed ? imm_values[0].imm_s32 > other.imm_values[0].imm_s32 + : imm_values[0].imm_u32 > other.imm_values[0].imm_u32; + case Type::F32: + return imm_values[0].imm_f32 > other.imm_values[0].imm_f32; + case Type::U64: + return is_signed && other.is_signed ? imm_values[0].imm_s64 > other.imm_values[0].imm_s64 + : imm_values[0].imm_u64 > other.imm_values[0].imm_u64; + case Type::F64: + return imm_values[0].imm_f64 > other.imm_values[0].imm_f64; + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +bool ImmValue::operator<=(const ImmValue& other) const noexcept { + return !operator>(other); +} + +bool ImmValue::operator>=(const ImmValue& other) const noexcept { + return !operator<(other); +} + +ImmValue ImmValue::operator+(const ImmValue& other) const noexcept { + ASSERT(type == other.type); + switch (type) { + case Type::U8: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s8 + other.imm_values[0].imm_s8) + : ImmValue(imm_values[0].imm_u8 + other.imm_values[0].imm_u8); + case Type::U16: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s16 + other.imm_values[0].imm_s16) + : ImmValue(imm_values[0].imm_u16 + other.imm_values[0].imm_u16); + case Type::U32: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 + other.imm_values[0].imm_s32) + : ImmValue(imm_values[0].imm_u32 + other.imm_values[0].imm_u32); + case Type::F32: + return ImmValue(imm_values[0].imm_f32 + other.imm_values[0].imm_f32); + case Type::U32x2: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 + other.imm_values[0].imm_s32, + imm_values[1].imm_s32 + other.imm_values[1].imm_s32) + : ImmValue(imm_values[0].imm_u32 + other.imm_values[0].imm_u32, + imm_values[1].imm_u32 + other.imm_values[1].imm_u32); + case Type::F32x2: + return ImmValue(imm_values[0].imm_f32 + other.imm_values[0].imm_f32, + imm_values[1].imm_f32 + other.imm_values[1].imm_f32); + case Type::U32x3: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 + other.imm_values[0].imm_s32, + imm_values[1].imm_s32 + other.imm_values[1].imm_s32, + imm_values[2].imm_s32 + other.imm_values[2].imm_s32) + : ImmValue(imm_values[0].imm_u32 + other.imm_values[0].imm_u32, + imm_values[1].imm_u32 + other.imm_values[1].imm_u32, + imm_values[2].imm_u32 + other.imm_values[2].imm_u32); + case Type::F32x3: + return ImmValue(imm_values[0].imm_f32 + other.imm_values[0].imm_f32, + imm_values[1].imm_f32 + other.imm_values[1].imm_f32, + imm_values[2].imm_f32 + other.imm_values[2].imm_f32); + case Type::U32x4: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 + other.imm_values[0].imm_s32, + imm_values[1].imm_s32 + other.imm_values[1].imm_s32, + imm_values[2].imm_s32 + other.imm_values[2].imm_s32, + imm_values[3].imm_s32 + other.imm_values[3].imm_s32) + : ImmValue(imm_values[0].imm_u32 + other.imm_values[0].imm_u32, + imm_values[1].imm_u32 + other.imm_values[1].imm_u32, + imm_values[2].imm_u32 + other.imm_values[2].imm_u32, + imm_values[3].imm_u32 + other.imm_values[3].imm_u32); + case Type::F32x4: + return ImmValue(imm_values[0].imm_f32 + other.imm_values[0].imm_f32, + imm_values[1].imm_f32 + other.imm_values[1].imm_f32, + imm_values[2].imm_f32 + other.imm_values[2].imm_f32, + imm_values[3].imm_f32 + other.imm_values[3].imm_f32); + case Type::U64: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s64 + other.imm_values[0].imm_s64) + : ImmValue(imm_values[0].imm_u64 + other.imm_values[0].imm_u64); + case Type::F64: + return ImmValue(imm_values[0].imm_f64 + other.imm_values[0].imm_f64); + case Type::F64x2: + return ImmValue(imm_values[0].imm_f64 + other.imm_values[0].imm_f64, + imm_values[1].imm_f64 + other.imm_values[1].imm_f64); + case Type::F64x3: + return ImmValue(imm_values[0].imm_f64 + other.imm_values[0].imm_f64, + imm_values[1].imm_f64 + other.imm_values[1].imm_f64, + imm_values[2].imm_f64 + other.imm_values[2].imm_f64); + case Type::F64x4: + return ImmValue(imm_values[0].imm_f64 + other.imm_values[0].imm_f64, + imm_values[1].imm_f64 + other.imm_values[1].imm_f64, + imm_values[2].imm_f64 + other.imm_values[2].imm_f64, + imm_values[3].imm_f64 + other.imm_values[3].imm_f64); + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +ImmValue ImmValue::operator-(const ImmValue& other) const noexcept { + ASSERT(type == other.type); + switch (type) { + case Type::U8: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s8 - other.imm_values[0].imm_s8) + : ImmValue(imm_values[0].imm_u8 - other.imm_values[0].imm_u8); + case Type::U16: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s16 - other.imm_values[0].imm_s16) + : ImmValue(imm_values[0].imm_u16 - other.imm_values[0].imm_u16); + case Type::U32: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 - other.imm_values[0].imm_s32) + : ImmValue(imm_values[0].imm_u32 - other.imm_values[0].imm_u32); + case Type::F32: + return ImmValue(imm_values[0].imm_f32 - other.imm_values[0].imm_f32); + case Type::U32x2: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 - other.imm_values[0].imm_s32, + imm_values[1].imm_s32 - other.imm_values[1].imm_s32) + : ImmValue(imm_values[0].imm_u32 - other.imm_values[0].imm_u32, + imm_values[1].imm_u32 - other.imm_values[1].imm_u32); + case Type::F32x2: + return ImmValue(imm_values[0].imm_f32 - other.imm_values[0].imm_f32, + imm_values[1].imm_f32 - other.imm_values[1].imm_f32); + case Type::U32x3: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 - other.imm_values[0].imm_s32, + imm_values[1].imm_s32 - other.imm_values[1].imm_s32, + imm_values[2].imm_s32 - other.imm_values[2].imm_s32) + : ImmValue(imm_values[0].imm_u32 - other.imm_values[0].imm_u32, + imm_values[1].imm_u32 - other.imm_values[1].imm_u32, + imm_values[2].imm_u32 - other.imm_values[2].imm_u32); + case Type::F32x3: + return ImmValue(imm_values[0].imm_f32 - other.imm_values[0].imm_f32, + imm_values[1].imm_f32 - other.imm_values[1].imm_f32, + imm_values[2].imm_f32 - other.imm_values[2].imm_f32); + case Type::U32x4: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 - other.imm_values[0].imm_s32, + imm_values[1].imm_s32 - other.imm_values[1].imm_s32, + imm_values[2].imm_s32 - other.imm_values[2].imm_s32, + imm_values[3].imm_s32 - other.imm_values[3].imm_s32) + : ImmValue(imm_values[0].imm_u32 - other.imm_values[0].imm_u32, + imm_values[1].imm_u32 - other.imm_values[1].imm_u32, + imm_values[2].imm_u32 - other.imm_values[2].imm_u32, + imm_values[3].imm_u32 - other.imm_values[3].imm_u32); + case Type::F32x4: + return ImmValue(imm_values[0].imm_f32 - other.imm_values[0].imm_f32, + imm_values[1].imm_f32 - other.imm_values[1].imm_f32, + imm_values[2].imm_f32 - other.imm_values[2].imm_f32, + imm_values[3].imm_f32 - other.imm_values[3].imm_f32); + case Type::U64: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s64 - other.imm_values[0].imm_s64) + : ImmValue(imm_values[0].imm_u64 - other.imm_values[0].imm_u64); + case Type::F64: + return ImmValue(imm_values[0].imm_f64 - other.imm_values[0].imm_f64); + case Type::F64x2: + return ImmValue(imm_values[0].imm_f64 - other.imm_values[0].imm_f64, + imm_values[1].imm_f64 - other.imm_values[1].imm_f64); + case Type::F64x3: + return ImmValue(imm_values[0].imm_f64 - other.imm_values[0].imm_f64, + imm_values[1].imm_f64 - other.imm_values[1].imm_f64, + imm_values[2].imm_f64 - other.imm_values[2].imm_f64); + case Type::F64x4: + return ImmValue(imm_values[0].imm_f64 - other.imm_values[0].imm_f64, + imm_values[1].imm_f64 - other.imm_values[1].imm_f64, + imm_values[2].imm_f64 - other.imm_values[2].imm_f64, + imm_values[3].imm_f64 - other.imm_values[3].imm_f64); + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +ImmValue ImmValue::operator*(const ImmValue& other) const noexcept { + ASSERT(BaseType() == other.BaseType()); + const ImmValue* vector; + const ImmValue* scalar; + if (Dimensions() == 1) { + scalar = this; + vector = &other; + } else if (other.Dimensions() == 1) { + scalar = &other; + vector = this; + } else { + UNREACHABLE_MSG("Unspecified behavior for vector * vector multiplication"); + } + switch (vector->type) { + case Type::U8: + return is_signed && scalar->is_signed + ? ImmValue(scalar->imm_values[0].imm_s8 * vector->imm_values[0].imm_s8) + : ImmValue(scalar->imm_values[0].imm_u8 * vector->imm_values[0].imm_u8); + case Type::U16: + return is_signed && scalar->is_signed + ? ImmValue(scalar->imm_values[0].imm_s16 * vector->imm_values[0].imm_s16) + : ImmValue(scalar->imm_values[0].imm_u16 * vector->imm_values[0].imm_u16); + case Type::U32: + return is_signed && scalar->is_signed + ? ImmValue(scalar->imm_values[0].imm_s32 * vector->imm_values[0].imm_s32) + : ImmValue(scalar->imm_values[0].imm_u32 * vector->imm_values[0].imm_u32); + case Type::F32: + return ImmValue(scalar->imm_values[0].imm_f32 * vector->imm_values[0].imm_f32); + case Type::U32x2: + return is_signed && scalar->is_signed + ? ImmValue(scalar->imm_values[0].imm_s32 * vector->imm_values[0].imm_s32, + scalar->imm_values[0].imm_s32 * vector->imm_values[1].imm_s32) + : ImmValue(scalar->imm_values[0].imm_u32 * vector->imm_values[0].imm_u32, + scalar->imm_values[0].imm_u32 * vector->imm_values[1].imm_u32); + case Type::F32x2: + return ImmValue(scalar->imm_values[0].imm_f32 * vector->imm_values[0].imm_f32, + scalar->imm_values[0].imm_f32 * vector->imm_values[1].imm_f32); + case Type::U32x3: + return is_signed && scalar->is_signed + ? ImmValue(scalar->imm_values[0].imm_s32 * vector->imm_values[0].imm_s32, + scalar->imm_values[0].imm_s32 * vector->imm_values[1].imm_s32, + scalar->imm_values[0].imm_s32 * vector->imm_values[2].imm_s32) + : ImmValue(scalar->imm_values[0].imm_u32 * vector->imm_values[0].imm_u32, + scalar->imm_values[0].imm_u32 * vector->imm_values[1].imm_u32, + scalar->imm_values[0].imm_u32 * vector->imm_values[2].imm_u32); + case Type::F32x3: + return ImmValue(scalar->imm_values[0].imm_f32 * vector->imm_values[0].imm_f32, + scalar->imm_values[0].imm_f32 * vector->imm_values[1].imm_f32, + scalar->imm_values[0].imm_f32 * vector->imm_values[2].imm_f32); + case Type::U32x4: + return is_signed && scalar->is_signed + ? ImmValue(scalar->imm_values[0].imm_s32 * vector->imm_values[0].imm_s32, + scalar->imm_values[0].imm_s32 * vector->imm_values[1].imm_s32, + scalar->imm_values[0].imm_s32 * vector->imm_values[2].imm_s32, + scalar->imm_values[0].imm_s32 * vector->imm_values[3].imm_s32) + : ImmValue(scalar->imm_values[0].imm_u32 * vector->imm_values[0].imm_u32, + scalar->imm_values[0].imm_u32 * vector->imm_values[1].imm_u32, + scalar->imm_values[0].imm_u32 * vector->imm_values[2].imm_u32, + scalar->imm_values[0].imm_u32 * vector->imm_values[3].imm_u32); + case Type::F32x4: + return ImmValue(scalar->imm_values[0].imm_f32 * vector->imm_values[0].imm_f32, + scalar->imm_values[0].imm_f32 * vector->imm_values[1].imm_f32, + scalar->imm_values[0].imm_f32 * vector->imm_values[2].imm_f32, + scalar->imm_values[0].imm_f32 * vector->imm_values[3].imm_f32); + case Type::U64: + return is_signed && scalar->is_signed + ? ImmValue(scalar->imm_values[0].imm_s64 * vector->imm_values[0].imm_s64) + : ImmValue(scalar->imm_values[0].imm_u64 * vector->imm_values[0].imm_u64); + case Type::F64: + return ImmValue(scalar->imm_values[0].imm_f64 * vector->imm_values[0].imm_f64); + case Type::F64x2: + return ImmValue(scalar->imm_values[0].imm_f64 * vector->imm_values[0].imm_f64, + scalar->imm_values[0].imm_f64 * vector->imm_values[1].imm_f64); + case Type::F64x3: + return ImmValue(scalar->imm_values[0].imm_f64 * vector->imm_values[0].imm_f64, + scalar->imm_values[0].imm_f64 * vector->imm_values[1].imm_f64, + scalar->imm_values[0].imm_f64 * vector->imm_values[2].imm_f64); + case Type::F64x4: + return ImmValue(scalar->imm_values[0].imm_f64 * vector->imm_values[0].imm_f64, + scalar->imm_values[0].imm_f64 * vector->imm_values[1].imm_f64, + scalar->imm_values[0].imm_f64 * vector->imm_values[2].imm_f64, + scalar->imm_values[0].imm_f64 * vector->imm_values[3].imm_f64); + default: + UNREACHABLE_MSG("Invalid type {}", vector->type); + } +} + +ImmValue ImmValue::operator/(const ImmValue& other) const { + ASSERT(BaseType() == other.BaseType() && other.Dimensions() == 1); + switch (type) { + case Type::U8: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s8 / other.imm_values[0].imm_s8) + : ImmValue(imm_values[0].imm_u8 / other.imm_values[0].imm_u8); + case Type::U16: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s16 / other.imm_values[0].imm_s16) + : ImmValue(imm_values[0].imm_u16 / other.imm_values[0].imm_u16); + case Type::U32: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 / other.imm_values[0].imm_s32) + : ImmValue(imm_values[0].imm_u32 / other.imm_values[0].imm_u32); + case Type::F32: + return ImmValue(imm_values[0].imm_f32 / other.imm_values[0].imm_f32); + case Type::U32x2: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 / other.imm_values[0].imm_s32, + imm_values[1].imm_s32 / other.imm_values[0].imm_s32) + : ImmValue(imm_values[0].imm_u32 / other.imm_values[0].imm_u32, + imm_values[1].imm_u32 / other.imm_values[0].imm_u32); + case Type::F32x2: + return ImmValue(imm_values[0].imm_f32 / other.imm_values[0].imm_f32, + imm_values[1].imm_f32 / other.imm_values[0].imm_f32); + case Type::U32x3: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 / other.imm_values[0].imm_s32, + imm_values[1].imm_s32 / other.imm_values[0].imm_s32, + imm_values[2].imm_s32 / other.imm_values[0].imm_s32) + : ImmValue(imm_values[0].imm_u32 / other.imm_values[0].imm_u32, + imm_values[1].imm_u32 / other.imm_values[0].imm_u32, + imm_values[2].imm_u32 / other.imm_values[0].imm_u32); + case Type::F32x3: + return ImmValue(imm_values[0].imm_f32 / other.imm_values[0].imm_f32, + imm_values[1].imm_f32 / other.imm_values[0].imm_f32, + imm_values[2].imm_f32 / other.imm_values[0].imm_f32); + case Type::U32x4: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 / other.imm_values[0].imm_s32, + imm_values[1].imm_s32 / other.imm_values[0].imm_s32, + imm_values[2].imm_s32 / other.imm_values[0].imm_s32, + imm_values[3].imm_s32 / other.imm_values[0].imm_s32) + : ImmValue(imm_values[0].imm_u32 / other.imm_values[0].imm_u32, + imm_values[1].imm_u32 / other.imm_values[0].imm_u32, + imm_values[2].imm_u32 / other.imm_values[0].imm_u32, + imm_values[3].imm_u32 / other.imm_values[0].imm_u32); + case Type::F32x4: + return ImmValue(imm_values[0].imm_f32 / other.imm_values[0].imm_f32, + imm_values[1].imm_f32 / other.imm_values[0].imm_f32, + imm_values[2].imm_f32 / other.imm_values[0].imm_f32, + imm_values[3].imm_f32 / other.imm_values[0].imm_f32); + case Type::U64: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s64 / other.imm_values[0].imm_s64) + : ImmValue(imm_values[0].imm_u64 / other.imm_values[0].imm_u64); + case Type::F64: + return ImmValue(imm_values[0].imm_f64 / other.imm_values[0].imm_f64); + case Type::F64x2: + return ImmValue(imm_values[0].imm_f64 / other.imm_values[0].imm_f64, + imm_values[1].imm_f64 / other.imm_values[0].imm_f64); + case Type::F64x3: + return ImmValue(imm_values[0].imm_f64 / other.imm_values[0].imm_f64, + imm_values[1].imm_f64 / other.imm_values[0].imm_f64, + imm_values[2].imm_f64 / other.imm_values[0].imm_f64); + case Type::F64x4: + return ImmValue(imm_values[0].imm_f64 / other.imm_values[0].imm_f64, + imm_values[1].imm_f64 / other.imm_values[0].imm_f64, + imm_values[2].imm_f64 / other.imm_values[0].imm_f64, + imm_values[3].imm_f64 / other.imm_values[0].imm_f64); + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +ImmValue ImmValue::operator%(const ImmValue& other) const noexcept { + ASSERT(type == other.type); + switch (type) { + case Type::U8: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s8 % other.imm_values[0].imm_s8) + : ImmValue(imm_values[0].imm_u8 % other.imm_values[0].imm_u8); + case Type::U16: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s16 % other.imm_values[0].imm_s16) + : ImmValue(imm_values[0].imm_u16 % other.imm_values[0].imm_u16); + case Type::U32: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 % other.imm_values[0].imm_s32) + : ImmValue(imm_values[0].imm_u32 % other.imm_values[0].imm_u32); + case Type::U64: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s64 % other.imm_values[0].imm_s64) + : ImmValue(imm_values[0].imm_u64 % other.imm_values[0].imm_u64); + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +ImmValue ImmValue::operator&(const ImmValue& other) const noexcept { + ASSERT(type == other.type); + switch (type) { + case Type::U1: + return ImmValue(imm_values[0].imm_u1 & other.imm_values[0].imm_u1); + case Type::U8: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s8 & other.imm_values[0].imm_s8) + : ImmValue(imm_values[0].imm_u8 & other.imm_values[0].imm_u8); + case Type::U16: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s16 & other.imm_values[0].imm_s16) + : ImmValue(imm_values[0].imm_u16 & other.imm_values[0].imm_u16); + case Type::U32: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 & other.imm_values[0].imm_s32) + : ImmValue(imm_values[0].imm_u32 & other.imm_values[0].imm_u32); + case Type::U64: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s64 & other.imm_values[0].imm_s64) + : ImmValue(imm_values[0].imm_u64 & other.imm_values[0].imm_u64); + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +ImmValue ImmValue::operator|(const ImmValue& other) const noexcept { + ASSERT(type == other.type); + switch (type) { + case Type::U1: + return ImmValue(imm_values[0].imm_u1 | other.imm_values[0].imm_u1); + case Type::U8: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s8 | other.imm_values[0].imm_s8) + : ImmValue(imm_values[0].imm_u8 | other.imm_values[0].imm_u8); + case Type::U16: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s16 | other.imm_values[0].imm_s16) + : ImmValue(imm_values[0].imm_u16 | other.imm_values[0].imm_u16); + case Type::U32: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 | other.imm_values[0].imm_s32) + : ImmValue(imm_values[0].imm_u32 | other.imm_values[0].imm_u32); + case Type::U64: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s64 | other.imm_values[0].imm_s64) + : ImmValue(imm_values[0].imm_u64 | other.imm_values[0].imm_u64); + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +ImmValue ImmValue::operator^(const ImmValue& other) const noexcept { + ASSERT(type == other.type); + switch (type) { + case Type::U1: + return ImmValue(imm_values[0].imm_u1 ^ other.imm_values[0].imm_u1); + case Type::U8: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s8 ^ other.imm_values[0].imm_s8) + : ImmValue(imm_values[0].imm_u8 ^ other.imm_values[0].imm_u8); + case Type::U16: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s16 ^ other.imm_values[0].imm_s16) + : ImmValue(imm_values[0].imm_u16 ^ other.imm_values[0].imm_u16); + case Type::U32: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 ^ other.imm_values[0].imm_s32) + : ImmValue(imm_values[0].imm_u32 ^ other.imm_values[0].imm_u32); + case Type::U64: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s64 ^ other.imm_values[0].imm_s64) + : ImmValue(imm_values[0].imm_u64 ^ other.imm_values[0].imm_u64); + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +ImmValue ImmValue::operator<<(const ImmValue& other) const noexcept { + ASSERT(type == other.type); + switch (type) { + case Type::U1: + return ImmValue(imm_values[0].imm_u1 << other.imm_values[0].imm_u1); + case Type::U8: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s8 << other.imm_values[0].imm_s8) + : ImmValue(imm_values[0].imm_u8 << other.imm_values[0].imm_u8); + case Type::U16: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s16 << other.imm_values[0].imm_s16) + : ImmValue(imm_values[0].imm_u16 << other.imm_values[0].imm_u16); + case Type::U32: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 << other.imm_values[0].imm_s32) + : ImmValue(imm_values[0].imm_u32 << other.imm_values[0].imm_u32); + case Type::U64: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s64 << other.imm_values[0].imm_s64) + : ImmValue(imm_values[0].imm_u64 << other.imm_values[0].imm_u64); + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +ImmValue ImmValue::operator>>(const ImmValue& other) const noexcept { + ASSERT(type == other.type); + switch (type) { + case Type::U1: + return ImmValue(imm_values[0].imm_u1 >> other.imm_values[0].imm_u1); + case Type::U8: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s8 >> other.imm_values[0].imm_s8) + : ImmValue(imm_values[0].imm_u8 >> other.imm_values[0].imm_u8); + case Type::U16: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s16 >> other.imm_values[0].imm_s16) + : ImmValue(imm_values[0].imm_u16 >> other.imm_values[0].imm_u16); + case Type::U32: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s32 >> other.imm_values[0].imm_s32) + : ImmValue(imm_values[0].imm_u32 >> other.imm_values[0].imm_u32); + case Type::U64: + return is_signed && other.is_signed + ? ImmValue(imm_values[0].imm_s64 >> other.imm_values[0].imm_s64) + : ImmValue(imm_values[0].imm_u64 >> other.imm_values[0].imm_u64); + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +ImmValue ImmValue::operator~() const noexcept { + switch (type) { + case Type::U1: + return ImmValue(~imm_values[0].imm_u1); + case Type::U8: + return is_signed ? ImmValue(imm_values[0].imm_s8) : ImmValue(imm_values[0].imm_u8); + case Type::U16: + return is_signed ? ImmValue(imm_values[0].imm_s16) : ImmValue(imm_values[0].imm_u16); + case Type::U32: + return is_signed ? ImmValue(imm_values[0].imm_s32) : ImmValue(imm_values[0].imm_u32); + case Type::U64: + return is_signed ? ImmValue(imm_values[0].imm_s64) : ImmValue(imm_values[0].imm_u64); + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +ImmValue ImmValue::operator++(int) noexcept { + switch (type) { + case Type::U8: + return is_signed ? ImmValue(imm_values[0].imm_s8++) : ImmValue(imm_values[0].imm_u8++); + case Type::U16: + return is_signed ? ImmValue(imm_values[0].imm_s16++) : ImmValue(imm_values[0].imm_u16++); + case Type::U32: + return is_signed ? ImmValue(imm_values[0].imm_s32++) : ImmValue(imm_values[0].imm_u32++); + case Type::U64: + return is_signed ? ImmValue(imm_values[0].imm_s64++) : ImmValue(imm_values[0].imm_u64++); + case Type::F32: + return ImmValue(imm_values[0].imm_f32++); + case Type::F64: + return ImmValue(imm_values[0].imm_f64++); + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +ImmValue ImmValue::operator--(int) noexcept { + switch (type) { + case Type::U8: + return is_signed ? ImmValue(imm_values[0].imm_s8--) : ImmValue(imm_values[0].imm_u8--); + case Type::U16: + return is_signed ? ImmValue(imm_values[0].imm_s16--) : ImmValue(imm_values[0].imm_u16--); + case Type::U32: + return is_signed ? ImmValue(imm_values[0].imm_s32--) : ImmValue(imm_values[0].imm_u32--); + case Type::U64: + return is_signed ? ImmValue(imm_values[0].imm_s64--) : ImmValue(imm_values[0].imm_u64--); + case Type::F32: + return ImmValue(imm_values[0].imm_f32--); + case Type::F64: + return ImmValue(imm_values[0].imm_f64--); + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +ImmValue& ImmValue::operator++() noexcept { + switch (type) { + case Type::U8: + if (is_signed) { + imm_values[0].imm_s8++; + } else { + imm_values[0].imm_u8++; + } + break; + case Type::U16: + if (is_signed) { + imm_values[0].imm_s16++; + } else { + imm_values[0].imm_u16++; + } + break; + case Type::U32: + if (is_signed) { + imm_values[0].imm_s32++; + } else { + imm_values[0].imm_u32++; + } + break; + case Type::U64: + if (is_signed) { + imm_values[0].imm_s64++; + } else { + imm_values[0].imm_u64++; + } + break; + case Type::F32: + imm_values[0].imm_f32++; + break; + case Type::F64: + imm_values[0].imm_f64++; + break; + default: + UNREACHABLE_MSG("Invalid type {}", type); + } + return *this; +} + +ImmValue& ImmValue::operator--() noexcept { + switch (type) { + case Type::U8: + if (is_signed) { + imm_values[0].imm_s8--; + } else { + imm_values[0].imm_u8--; + } + break; + case Type::U16: + if (is_signed) { + imm_values[0].imm_s16--; + } else { + imm_values[0].imm_u16--; + } + break; + case Type::U32: + if (is_signed) { + imm_values[0].imm_s32--; + } else { + imm_values[0].imm_u32--; + } + break; + case Type::U64: + if (is_signed) { + imm_values[0].imm_s64--; + } else { + imm_values[0].imm_u64--; + } + break; + case Type::F32: + imm_values[0].imm_f32--; + break; + case Type::F64: + imm_values[0].imm_f64--; + break; + default: + UNREACHABLE_MSG("Invalid type {}", type); + } + return *this; +} + +ImmValue ImmValue::operator-() const noexcept { + switch (type) { + case Type::U8: + return is_signed ? ImmValue(-imm_values[0].imm_s8) : ImmValue(-imm_values[0].imm_u8); + case Type::U16: + return is_signed ? ImmValue(-imm_values[0].imm_s16) : ImmValue(-imm_values[0].imm_u16); + case Type::U32: + return is_signed ? ImmValue(-imm_values[0].imm_s32) : ImmValue(-imm_values[0].imm_u32); + case Type::U32x2: + return is_signed ? ImmValue(-imm_values[0].imm_s32, -imm_values[1].imm_s32) + : ImmValue(-imm_values[0].imm_u32, -imm_values[1].imm_u32); + case Type::U32x3: + return is_signed ? ImmValue(-imm_values[0].imm_s32, -imm_values[1].imm_s32, + -imm_values[2].imm_s32) + : ImmValue(-imm_values[0].imm_u32, -imm_values[1].imm_u32, + -imm_values[2].imm_u32); + case Type::U32x4: + return is_signed ? ImmValue(-imm_values[0].imm_s32, -imm_values[1].imm_s32, + -imm_values[2].imm_s32, -imm_values[3].imm_s32) + : ImmValue(-imm_values[0].imm_u32, -imm_values[1].imm_u32, + -imm_values[2].imm_u32, -imm_values[3].imm_u32); + case Type::U64: + return is_signed ? ImmValue(-imm_values[0].imm_s64) : ImmValue(-imm_values[0].imm_u64); + case Type::F32: + return ImmValue(-imm_values[0].imm_f32); + case Type::F32x2: + return ImmValue(-imm_values[0].imm_f32, -imm_values[1].imm_f32); + case Type::F32x3: + return ImmValue(-imm_values[0].imm_f32, -imm_values[1].imm_f32, -imm_values[2].imm_f32); + case Type::F32x4: + return ImmValue(-imm_values[0].imm_f32, -imm_values[1].imm_f32, -imm_values[2].imm_f32, + -imm_values[3].imm_f32); + case Type::F64: + return ImmValue(-imm_values[0].imm_f64); + case Type::F64x2: + return ImmValue(-imm_values[0].imm_f64, -imm_values[1].imm_f64); + case Type::F64x3: + return ImmValue(-imm_values[0].imm_f64, -imm_values[1].imm_f64, -imm_values[2].imm_f64); + case Type::F64x4: + return ImmValue(-imm_values[0].imm_f64, -imm_values[1].imm_f64, -imm_values[2].imm_f64, + -imm_values[3].imm_f64); + default: + UNREACHABLE_MSG("Invalid type {}", type); + } +} + +ImmValue ImmValue::operator+() const noexcept { + return *this; +} + +// this is not the best way + +ImmValue& ImmValue::operator+=(const ImmValue& other) noexcept { + ImmValue result = *this + other; + *this = result; + return *this; +} + +ImmValue& ImmValue::operator-=(const ImmValue& other) noexcept { + ImmValue result = *this - other; + *this = result; + return *this; +} + +ImmValue& ImmValue::operator*=(const ImmValue& other) noexcept { + ImmValue result = *this * other; + *this = result; + return *this; +} + +ImmValue& ImmValue::operator/=(const ImmValue& other) { + ImmValue result = *this / other; + *this = result; + return *this; +} + +ImmValue& ImmValue::operator%=(const ImmValue& other) noexcept { + ImmValue result = *this % other; + *this = result; + return *this; +} + +ImmValue& ImmValue::operator&=(const ImmValue& other) noexcept { + ImmValue result = *this & other; + *this = result; + return *this; +} + +ImmValue& ImmValue::operator|=(const ImmValue& other) noexcept { + ImmValue result = *this | other; + *this = result; + return *this; +} + +ImmValue& ImmValue::operator^=(const ImmValue& other) noexcept { + ImmValue result = *this ^ other; + *this = result; + return *this; +} + +ImmValue& ImmValue::operator<<=(const ImmValue& other) noexcept { + ImmValue result = *this << other; + *this = result; + return *this; +} + +ImmValue& ImmValue::operator>>=(const ImmValue& other) noexcept { + ImmValue result = *this >> other; + *this = result; + return *this; +} + +} // namespace Shader::IR + +namespace std { + +std::size_t hash::operator()(const Shader::IR::ImmValue& value) const { + using namespace Shader::IR; + + u64 h = HashCombine(static_cast(value.Type()), 0ULL); + + switch (value.Type()) { + case Type::U1: + return HashCombine(static_cast(value.imm_values[0].imm_u1), h); + case Type::U8: + return HashCombine(static_cast(value.imm_values[0].imm_u8), h); + case Type::U16: + return HashCombine(static_cast(value.imm_values[0].imm_u16), h); + case Type::U32: + case Type::F32: + return HashCombine(static_cast(value.imm_values[0].imm_u32), h); + case Type::U64: + case Type::F64: + return HashCombine(static_cast(value.imm_values[0].imm_u64), h); + case Type::U32x2: + case Type::F32x2: + h = HashCombine(static_cast(value.imm_values[0].imm_u32), h); + return HashCombine(static_cast(value.imm_values[1].imm_u32), h); + case Type::F64x2: + h = HashCombine(static_cast(value.imm_values[0].imm_f64), h); + return HashCombine(static_cast(value.imm_values[1].imm_f64), h); + case Type::U32x3: + case Type::F32x3: + h = HashCombine(static_cast(value.imm_values[0].imm_u32), h); + h = HashCombine(static_cast(value.imm_values[1].imm_u32), h); + return HashCombine(static_cast(value.imm_values[2].imm_u32), h); + case Type::F64x3: + h = HashCombine(static_cast(value.imm_values[0].imm_f64), h); + h = HashCombine(static_cast(value.imm_values[1].imm_f64), h); + return HashCombine(static_cast(value.imm_values[2].imm_f64), h); + case Type::U32x4: + case Type::F32x4: + h = HashCombine(static_cast(value.imm_values[0].imm_u32), h); + h = HashCombine(static_cast(value.imm_values[1].imm_u32), h); + h = HashCombine(static_cast(value.imm_values[2].imm_u32), h); + return HashCombine(static_cast(value.imm_values[3].imm_u32), h); + case Type::F64x4: + h = HashCombine(static_cast(value.imm_values[0].imm_f64), h); + h = HashCombine(static_cast(value.imm_values[1].imm_f64), h); + h = HashCombine(static_cast(value.imm_values[2].imm_f64), h); + return HashCombine(static_cast(value.imm_values[3].imm_f64), h); + default: + UNREACHABLE_MSG("Invalid type {}", value.Type()); + } +} + +} // namespace std diff --git a/src/shader_recompiler/ir/compute_value/imm_value.h b/src/shader_recompiler/ir/compute_value/imm_value.h new file mode 100644 index 000000000..1a304a4af --- /dev/null +++ b/src/shader_recompiler/ir/compute_value/imm_value.h @@ -0,0 +1,345 @@ +// 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 \ No newline at end of file