#pragma once #include "common/assert.h" #include "graphics/shader/recompiler/ir/Reg.h" #include "graphics/shader/recompiler/ir/opcodes/ValueOpcodes.h" #include #include #include #include #include #include namespace Libs::Graphics::ShaderRecompiler::IR { class Block; class Inst; class Value { public: Value() = default; explicit Value(Inst* value); explicit Value(ScalarReg value); explicit Value(VectorReg value); explicit Value(bool value); explicit Value(uint8_t value); explicit Value(uint16_t value); explicit Value(uint32_t value); explicit Value(uint64_t value); static Value F16(uint16_t bits); static Value F32(float value); [[nodiscard]] bool IsEmpty() const; [[nodiscard]] bool IsImmediate() const; [[nodiscard]] bool IsIdentity() const; [[nodiscard]] bool IsPhi() const; [[nodiscard]] Type GetType() const; [[nodiscard]] Inst* Instruction() const; [[nodiscard]] Inst* TryInstruction() const; [[nodiscard]] Inst* ResolveInstruction() const; [[nodiscard]] Value Resolve() const; [[nodiscard]] ScalarReg ScalarRegister() const; [[nodiscard]] VectorReg VectorRegister() const; [[nodiscard]] bool U1() const; [[nodiscard]] uint8_t U8() const; [[nodiscard]] uint16_t U16() const; [[nodiscard]] uint32_t U32() const; [[nodiscard]] uint64_t U64() const; [[nodiscard]] uint16_t F16Bits() const; [[nodiscard]] float F32Value() const; bool operator==(const Value& other) const; private: Type type = Type::Void; union { Inst* inst; ScalarReg scalar_reg; VectorReg vector_reg; bool imm_u1; uint8_t imm_u8; uint16_t imm_u16; uint32_t imm_u32; uint64_t imm_u64; }; explicit Value(Type type, uint64_t bits); }; static_assert(std::is_trivially_copyable_v); template class TypedValue: public Value { public: TypedValue() = default; template requires(TypesOverlap(type_, other_type)) TypedValue(const TypedValue& value): Value(value) {} explicit TypedValue(Value value): Value(value) { EXIT_IF(!AreTypesCompatible(value.GetType(), type_)); } }; using U1 = TypedValue; using U8 = TypedValue; using U16 = TypedValue; using U32 = TypedValue; using U64 = TypedValue; using F16 = TypedValue; using F32 = TypedValue; using U32F32 = TypedValue; struct Use { Inst* user = nullptr; size_t operand = 0; bool operator==(const Use&) const = default; }; class Inst { public: explicit Inst(ValueOpcode opcode, uint64_t flags = 0); ~Inst(); Inst(const Inst&) = delete; Inst& operator=(const Inst&) = delete; Inst(Inst&&) = delete; Inst& operator=(Inst&&) = delete; [[nodiscard]] ValueOpcode GetOpcode() const; [[nodiscard]] Type GetType() const; [[nodiscard]] bool MayHaveSideEffects() const; [[nodiscard]] bool HasUses() const; [[nodiscard]] size_t UseCount() const; [[nodiscard]] size_t NumArgs() const; [[nodiscard]] size_t NumPhiBlocks() const; [[nodiscard]] Value Arg(size_t index) const; [[nodiscard]] Block* PhiBlock(size_t index) const; [[nodiscard]] Block* Parent() const; [[nodiscard]] const std::vector& Uses() const; // Runtime indices belong to the resource plan that owns this instruction. [[nodiscard]] uint32_t EvaluationIndex(uint32_t& count) const { if (evaluation_index == UINT32_MAX) { evaluation_index = count++; } return evaluation_index; } void SetParent(Block* block); void SetArg(size_t index, Value value); void AddPhiOperand(Block* predecessor, Value value); void ReplaceUsesWith(Value replacement, bool preserve = true); void ReplaceOpcode(ValueOpcode opcode); void Invalidate(); template requires(sizeof(T) <= sizeof(uint64_t) && std::is_trivially_copyable_v) [[nodiscard]] T Flags() const { T result {}; std::memcpy(&result, &flags, sizeof(result)); return result; } template requires(sizeof(T) <= sizeof(uint64_t) && std::is_trivially_copyable_v) void SetFlags(T value) { flags = 0; std::memcpy(&flags, &value, sizeof(value)); } private: void AddUse(Inst* used, size_t operand); void RemoveUse(Inst* used, size_t operand); void ClearArgs(); ValueOpcode opcode; uint64_t flags; Block* parent = nullptr; std::vector args; std::vector phi_blocks; std::vector uses; mutable uint32_t evaluation_index = UINT32_MAX; }; } // namespace Libs::Graphics::ShaderRecompiler::IR