Delete src/shader_recompiler directory

This commit is contained in:
Kravickas authored and GitHub committed 2026-10-09 08:01:25 +02:00
1 parent 6bb777f100
commit 46adccd0ef
17 files changed
-15443

No files matched your search

@@ -1,493 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <sirit/sirit.h>
#include "common/types.h"
namespace Shader::IR {
enum class Attribute : u64;
enum class ScalarReg : u32;
enum class Patch : u64;
class Inst;
class Value;
} // namespace Shader::IR
namespace Shader::Backend::SPIRV {
using Sirit::Id;
class EmitContext;
// Microinstruction emitters
Id EmitPhi(EmitContext& ctx, IR::Inst* inst);
void EmitVoid(EmitContext& ctx);
Id EmitConditionRef(EmitContext& ctx, const IR::Value& value);
void EmitGetScc(EmitContext& ctx);
void EmitGetExec(EmitContext& ctx);
void EmitGetVcc(EmitContext& ctx);
void EmitGetVccLo(EmitContext& ctx);
void EmitGetVccHi(EmitContext& ctx);
void EmitGetM0(EmitContext& ctx);
void EmitSetScc(EmitContext& ctx);
void EmitSetExec(EmitContext& ctx);
void EmitSetVcc(EmitContext& ctx);
void EmitSetVccLo(EmitContext& ctx);
void EmitSetVccHi(EmitContext& ctx);
void EmitSetM0(EmitContext& ctx);
void EmitFPCmpClass32(EmitContext& ctx);
void EmitPrologue(EmitContext& ctx);
void EmitEpilogue(EmitContext& ctx);
void EmitDiscard(EmitContext& ctx);
void EmitDiscardCond(EmitContext& ctx, Id condition);
void EmitDebugPrint(EmitContext& ctx, IR::Inst* inst, Id arg0, Id arg1, Id arg2, Id arg3, Id arg4);
Id EmitMemtime(EmitContext& ctx);
void EmitBarrier(EmitContext& ctx);
void EmitWorkgroupMemoryBarrier(EmitContext& ctx);
void EmitDeviceMemoryBarrier(EmitContext& ctx);
Id EmitGetUserData(EmitContext& ctx, IR::ScalarReg reg);
void EmitGetThreadBitScalarReg(EmitContext& ctx);
void EmitSetThreadBitScalarReg(EmitContext& ctx);
void EmitGetScalarRegister(EmitContext& ctx);
void EmitSetScalarRegister(EmitContext& ctx);
void EmitGetVectorRegister(EmitContext& ctx);
void EmitSetVectorRegister(EmitContext& ctx);
void EmitSetVirtualRegister(EmitContext& ctx);
void EmitGetVirtualRegister(EmitContext& ctx);
void EmitSetGotoVariable(EmitContext& ctx);
void EmitGetGotoVariable(EmitContext& ctx);
void EmitSetMaskLaneVariable(EmitContext& ctx);
void EmitGetMaskLaneVariable(EmitContext& ctx);
void EmitSetScc(EmitContext& ctx);
Id EmitReadConst(EmitContext& ctx, IR::Inst* inst, Id addr, Id offset);
Id EmitReadConstBuffer(EmitContext& ctx, u32 handle, Id index);
Id EmitLoadBufferU8(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferU16(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferU32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferU32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferU64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferF32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferF32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferF32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitLoadBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
void EmitStoreBufferU8(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferU16(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferU32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferU32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferU32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferU64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferF32x2(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferF32x3(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferF32x4(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
void EmitStoreBufferFormatF32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicIAdd64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicISub32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicSMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicSMin64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicUMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicUMin64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicFMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicSMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicSMax64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicUMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicUMax64(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicFMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicInc32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitBufferAtomicDec32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address);
Id EmitBufferAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicOr32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicCmpSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
Id cmp_value);
Id EmitBufferAtomicFCmpSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
Id cmp_value);
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, u32 index);
Id EmitGetAttributeU1(EmitContext& ctx, IR::Attribute attr, u32 comp);
Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp);
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 comp);
Id EmitGetTessGenericAttribute(EmitContext& ctx, Id vertex_index, Id attr_index, Id comp_index);
void EmitSetTcsGenericAttribute(EmitContext& ctx, Id value, Id attr_index, Id comp_index);
Id EmitReadTcsGenericOuputAttribute(EmitContext& ctx, Id vertex_index, Id attr_index,
Id comp_index);
Id EmitGetPcLo(EmitContext& ctx, const IR::Value& value);
Id EmitGetPatch(EmitContext& ctx, IR::Patch patch);
void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value);
Id EmitWorkgroupId(EmitContext& ctx);
Id EmitLocalInvocationId(EmitContext& ctx);
Id EmitInvocationId(EmitContext& ctx);
Id EmitInvocationInfo(EmitContext& ctx);
Id EmitUndefU1(EmitContext& ctx);
Id EmitUndefU8(EmitContext& ctx);
Id EmitUndefU16(EmitContext& ctx);
Id EmitUndefU32(EmitContext& ctx);
Id EmitUndefU32x2(EmitContext& ctx);
Id EmitUndefU32x3(EmitContext& ctx);
Id EmitUndefU32x4(EmitContext& ctx);
Id EmitUndefF32(EmitContext& ctx);
Id EmitUndefU64(EmitContext& ctx);
Id EmitLoadSharedU16(EmitContext& ctx, Id offset);
Id EmitLoadSharedU32(EmitContext& ctx, Id offset);
Id EmitLoadSharedU64(EmitContext& ctx, Id offset);
void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value);
void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value);
void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicIAdd32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicIAdd64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicUMax32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicUMax64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicSMax32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicSMax64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicUMin32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicUMin64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicSMin32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicSMin64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicAnd32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicAnd64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicOr32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicOr64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicXor32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicXor64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicInc32(EmitContext& ctx, Id offset);
Id EmitSharedAtomicInc64(EmitContext& ctx, Id offset);
Id EmitSharedAtomicDec32(EmitContext& ctx, Id offset);
Id EmitSharedAtomicDec64(EmitContext& ctx, Id offset);
Id EmitSharedAtomicISub32(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicISub64(EmitContext& ctx, Id offset, Id value);
Id EmitSharedAtomicCmpSwap32(EmitContext& ctx, Id offset, Id value, Id cmp_value);
Id EmitSharedAtomicCmpSwap64(EmitContext& ctx, Id offset, Id value, Id cmp_value);
Id EmitCompositeConstructU32x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2);
Id EmitCompositeConstructU32x3(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3);
Id EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3, Id e4);
Id EmitCompositeConstructU32x2x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2);
Id EmitCompositeExtractU32x2(EmitContext& ctx, Id composite, u32 index);
Id EmitCompositeExtractU32x3(EmitContext& ctx, Id composite, u32 index);
Id EmitCompositeExtractU32x4(EmitContext& ctx, Id composite, u32 index);
Id EmitCompositeInsertU32x2(EmitContext& ctx, Id composite, Id object, u32 index);
Id EmitCompositeInsertU32x3(EmitContext& ctx, Id composite, Id object, u32 index);
Id EmitCompositeInsertU32x4(EmitContext& ctx, Id composite, Id object, u32 index);
Id EmitCompositeShuffleU32x2(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1);
Id EmitCompositeShuffleU32x3(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1,
u32 comp2);
Id EmitCompositeShuffleU32x4(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1,
u32 comp2, u32 comp3);
Id EmitCompositeConstructF32x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2);
Id EmitCompositeConstructF32x3(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3);
Id EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2, Id e3, Id e4);
Id EmitCompositeConstructF32x2x2(EmitContext& ctx, IR::Inst* inst, Id e1, Id e2);
Id EmitCompositeExtractF32x2(EmitContext& ctx, Id composite, u32 index);
Id EmitCompositeExtractF32x3(EmitContext& ctx, Id composite, u32 index);
Id EmitCompositeExtractF32x4(EmitContext& ctx, Id composite, u32 index);
Id EmitCompositeInsertF32x2(EmitContext& ctx, Id composite, Id object, u32 index);
Id EmitCompositeInsertF32x3(EmitContext& ctx, Id composite, Id object, u32 index);
Id EmitCompositeInsertF32x4(EmitContext& ctx, Id composite, Id object, u32 index);
Id EmitCompositeShuffleF32x2(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1);
Id EmitCompositeShuffleF32x3(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1,
u32 comp2);
Id EmitCompositeShuffleF32x4(EmitContext& ctx, Id composite1, Id composite2, u32 comp0, u32 comp1,
u32 comp2, u32 comp3);
Id EmitSelectU1(EmitContext& ctx, Id cond, Id true_value, Id false_value);
Id EmitSelectU32(EmitContext& ctx, Id cond, Id true_value, Id false_value);
Id EmitSelectF32(EmitContext& ctx, Id cond, Id true_value, Id false_value);
Id EmitSelectU64(EmitContext& ctx, Id cond, Id true_value, Id false_value);
Id EmitBitCastU16F16(EmitContext& ctx, Id value);
Id EmitBitCastU32F32(EmitContext& ctx, Id value);
Id EmitBitCastF16U16(EmitContext& ctx, Id value);
Id EmitBitCastF32U32(EmitContext& ctx, Id value);
Id EmitPackUint2x32(EmitContext& ctx, Id value);
Id EmitUnpackUint2x32(EmitContext& ctx, Id value);
Id EmitPackDouble2x32(EmitContext& ctx, Id value);
Id EmitUnpackDouble2x32(EmitContext& ctx, Id value);
Id EmitPackUnorm2x16(EmitContext& ctx, Id value);
Id EmitUnpackUnorm2x16(EmitContext& ctx, Id value);
Id EmitPackSnorm2x16(EmitContext& ctx, Id value);
Id EmitUnpackSnorm2x16(EmitContext& ctx, Id value);
Id EmitPackUint2x16(EmitContext& ctx, Id value);
Id EmitUnpackUint2x16(EmitContext& ctx, Id value);
Id EmitPackSint2x16(EmitContext& ctx, Id value);
Id EmitUnpackSint2x16(EmitContext& ctx, Id value);
Id EmitPackHalf2x16(EmitContext& ctx, Id value);
Id EmitUnpackHalf2x16(EmitContext& ctx, Id value);
Id EmitPackUnorm4x8(EmitContext& ctx, Id value);
Id EmitUnpackUnorm4x8(EmitContext& ctx, Id value);
Id EmitPackSnorm4x8(EmitContext& ctx, Id value);
Id EmitUnpackSnorm4x8(EmitContext& ctx, Id value);
Id EmitPackUint4x8(EmitContext& ctx, Id value);
Id EmitUnpackUint4x8(EmitContext& ctx, Id value);
Id EmitPackSint4x8(EmitContext& ctx, Id value);
Id EmitUnpackSint4x8(EmitContext& ctx, Id value);
Id EmitPackUfloat10_11_11(EmitContext& ctx, Id value);
Id EmitUnpackUfloat10_11_11(EmitContext& ctx, Id value);
Id EmitPackUnorm2_10_10_10(EmitContext& ctx, Id value);
Id EmitUnpackUnorm2_10_10_10(EmitContext& ctx, Id value);
Id EmitPackSnorm2_10_10_10(EmitContext& ctx, Id value);
Id EmitUnpackSnorm2_10_10_10(EmitContext& ctx, Id value);
Id EmitPackUint2_10_10_10(EmitContext& ctx, Id value);
Id EmitUnpackUint2_10_10_10(EmitContext& ctx, Id value);
Id EmitPackSint2_10_10_10(EmitContext& ctx, Id value);
Id EmitUnpackSint2_10_10_10(EmitContext& ctx, Id value);
Id EmitFPAbs32(EmitContext& ctx, Id value);
Id EmitFPAbs64(EmitContext& ctx, Id value);
Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
Id EmitFPSub32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c);
Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c);
Id EmitFPMax32(EmitContext& ctx, Id a, Id b);
Id EmitFPMax64(EmitContext& ctx, Id a, Id b);
Id EmitFPMin32(EmitContext& ctx, Id a, Id b);
Id EmitFPMin64(EmitContext& ctx, Id a, Id b);
Id EmitFPMinTri32(EmitContext& ctx, Id a, Id b, Id c);
Id EmitFPMaxTri32(EmitContext& ctx, Id a, Id b, Id c);
Id EmitFPMedTri32(EmitContext& ctx, Id a, Id b, Id c);
Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
Id EmitFPDiv32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
Id EmitFPDiv64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
Id EmitFPNeg32(EmitContext& ctx, Id value);
Id EmitFPNeg64(EmitContext& ctx, Id value);
Id EmitFPSin(EmitContext& ctx, Id value);
Id EmitFPCos(EmitContext& ctx, Id value);
Id EmitFPExp2(EmitContext& ctx, Id value);
Id EmitFPPow(EmitContext& ctx, Id x, Id y);
Id EmitFPLdexp(EmitContext& ctx, Id value, Id exp);
Id EmitFPLog2(EmitContext& ctx, Id value);
Id EmitFPRecip32(EmitContext& ctx, Id value);
Id EmitFPRecip64(EmitContext& ctx, Id value);
Id EmitFPRecipSqrt32(EmitContext& ctx, Id value);
Id EmitFPRecipSqrt64(EmitContext& ctx, Id value);
Id EmitFPSqrt(EmitContext& ctx, Id value);
Id EmitFPSaturate32(EmitContext& ctx, Id value);
Id EmitFPSaturate64(EmitContext& ctx, Id value);
Id EmitFPClamp32(EmitContext& ctx, Id value, Id min_value, Id max_value);
Id EmitFPClamp64(EmitContext& ctx, Id value, Id min_value, Id max_value);
Id EmitFPRoundEven32(EmitContext& ctx, Id value);
Id EmitFPRoundEven64(EmitContext& ctx, Id value);
Id EmitFPFloor32(EmitContext& ctx, Id value);
Id EmitFPFloor64(EmitContext& ctx, Id value);
Id EmitFPCeil32(EmitContext& ctx, Id value);
Id EmitFPCeil64(EmitContext& ctx, Id value);
Id EmitFPTrunc32(EmitContext& ctx, Id value);
Id EmitFPTrunc64(EmitContext& ctx, Id value);
Id EmitFPFract32(EmitContext& ctx, Id value);
Id EmitFPFract64(EmitContext& ctx, Id value);
Id EmitFPFrexpSig32(EmitContext& ctx, Id value);
Id EmitFPFrexpSig64(EmitContext& ctx, Id value);
Id EmitFPFrexpExp32(EmitContext& ctx, Id value);
Id EmitFPFrexpExp64(EmitContext& ctx, Id value);
Id EmitFPOrdEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPOrdEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPUnordEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPUnordEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPOrdNotEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPOrdNotEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPUnordNotEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPUnordNotEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPOrdLessThan32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPOrdLessThan64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPUnordLessThan32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPUnordLessThan64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPOrdGreaterThan32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPOrdGreaterThan64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPUnordGreaterThan32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPUnordGreaterThan64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPOrdLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPOrdLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPUnordLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPUnordLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPOrdGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPOrdGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPUnordGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPUnordGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitFPIsNan32(EmitContext& ctx, Id value);
Id EmitFPIsNan64(EmitContext& ctx, Id value);
Id EmitFPIsInf32(EmitContext& ctx, Id value);
Id EmitFPIsInf64(EmitContext& ctx, Id value);
Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
Id EmitIAdd64(EmitContext& ctx, Id a, Id b);
Id EmitISub32(EmitContext& ctx, Id a, Id b);
Id EmitISub64(EmitContext& ctx, Id a, Id b);
Id EmitSMulHi(EmitContext& ctx, Id a, Id b);
Id EmitUMulHi(EmitContext& ctx, Id a, Id b);
Id EmitIMul32(EmitContext& ctx, Id a, Id b);
Id EmitIMul64(EmitContext& ctx, Id a, Id b);
Id EmitSDiv32(EmitContext& ctx, Id a, Id b);
Id EmitUDiv32(EmitContext& ctx, Id a, Id b);
Id EmitSMod32(EmitContext& ctx, Id a, Id b);
Id EmitUMod32(EmitContext& ctx, Id a, Id b);
Id EmitINeg32(EmitContext& ctx, Id value);
Id EmitINeg64(EmitContext& ctx, Id value);
Id EmitIAbs32(EmitContext& ctx, Id value);
Id EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift);
Id EmitShiftLeftLogical64(EmitContext& ctx, Id base, Id shift);
Id EmitShiftRightLogical32(EmitContext& ctx, Id base, Id shift);
Id EmitShiftRightLogical64(EmitContext& ctx, Id base, Id shift);
Id EmitShiftRightArithmetic32(EmitContext& ctx, Id base, Id shift);
Id EmitShiftRightArithmetic64(EmitContext& ctx, Id base, Id shift);
Id EmitBitwiseAnd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
Id EmitBitwiseAnd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
Id EmitBitwiseOr32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
Id EmitBitwiseOr64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
Id EmitBitwiseXor32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
Id EmitBitwiseXor64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
Id EmitBitFieldInsert32(EmitContext& ctx, Id base, Id insert, Id offset, Id count);
Id EmitBitFieldInsert64(EmitContext& ctx, Id base, Id insert, Id offset, Id count);
Id EmitBitFieldSExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count);
Id EmitBitFieldUExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count);
Id EmitBitReverse32(EmitContext& ctx, Id value);
Id EmitBitCount32(EmitContext& ctx, Id value);
Id EmitBitCount64(EmitContext& ctx, Id value);
Id EmitBitwiseNot32(EmitContext& ctx, Id value);
Id EmitBitwiseNot64(EmitContext& ctx, Id value);
Id EmitMaskedBitCount32(EmitContext& ctx, Id value, Id addend, bool hi);
Id EmitFindSMsb32(EmitContext& ctx, Id value);
Id EmitFindUMsb32(EmitContext& ctx, Id value);
Id EmitFindUMsb64(EmitContext& ctx, Id value);
Id EmitFindILsb32(EmitContext& ctx, Id value);
Id EmitFindILsb64(EmitContext& ctx, Id value);
Id EmitSMin32(EmitContext& ctx, Id a, Id b);
Id EmitUMin32(EmitContext& ctx, Id a, Id b);
Id EmitSMax32(EmitContext& ctx, Id a, Id b);
Id EmitUMax32(EmitContext& ctx, Id a, Id b);
Id EmitSMinTri32(EmitContext& ctx, Id a, Id b, Id c);
Id EmitUMinTri32(EmitContext& ctx, Id a, Id b, Id c);
Id EmitSMaxTri32(EmitContext& ctx, Id a, Id b, Id c);
Id EmitUMaxTri32(EmitContext& ctx, Id a, Id b, Id c);
Id EmitSMedTri32(EmitContext& ctx, Id a, Id b, Id c);
Id EmitUMedTri32(EmitContext& ctx, Id a, Id b, Id c);
Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max);
Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max);
Id EmitSLessThan32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSLessThan64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitULessThan32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitULessThan64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitIEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitIEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitULessThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitULessThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSGreaterThan32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSGreaterThan64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitUGreaterThan32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitUGreaterThan64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitINotEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitINotEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitSGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitUGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs);
Id EmitUGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs);
Id EmitLogicalOr(EmitContext& ctx, Id a, Id b);
Id EmitLogicalAnd(EmitContext& ctx, Id a, Id b);
Id EmitLogicalXor(EmitContext& ctx, Id a, Id b);
Id EmitLogicalNot(EmitContext& ctx, Id value);
Id EmitConvertS16F32(EmitContext& ctx, Id value);
Id EmitConvertS16F64(EmitContext& ctx, Id value);
Id EmitConvertS32F32(EmitContext& ctx, Id value);
Id EmitConvertS32F64(EmitContext& ctx, Id value);
Id EmitConvertS64F32(EmitContext& ctx, Id value);
Id EmitConvertS64F64(EmitContext& ctx, Id value);
Id EmitConvertU16F32(EmitContext& ctx, Id value);
Id EmitConvertU16F64(EmitContext& ctx, Id value);
Id EmitConvertU32F32(EmitContext& ctx, Id value);
Id EmitConvertU32F64(EmitContext& ctx, Id value);
Id EmitConvertU64F32(EmitContext& ctx, Id value);
Id EmitConvertU64F64(EmitContext& ctx, Id value);
Id EmitConvertU64U32(EmitContext& ctx, Id value);
Id EmitConvertU32U64(EmitContext& ctx, Id value);
Id EmitConvertF16F32(EmitContext& ctx, Id value);
Id EmitConvertF32F16(EmitContext& ctx, Id value);
Id EmitConvertF32F64(EmitContext& ctx, Id value);
Id EmitConvertF64F32(EmitContext& ctx, Id value);
Id EmitConvertF32S8(EmitContext& ctx, Id value);
Id EmitConvertF32S16(EmitContext& ctx, Id value);
Id EmitConvertF32S32(EmitContext& ctx, Id value);
Id EmitConvertF32S64(EmitContext& ctx, Id value);
Id EmitConvertF32U8(EmitContext& ctx, Id value);
Id EmitConvertF32U16(EmitContext& ctx, Id value);
Id EmitConvertF32U32(EmitContext& ctx, Id value);
Id EmitConvertF32U64(EmitContext& ctx, Id value);
Id EmitConvertF64S8(EmitContext& ctx, Id value);
Id EmitConvertF64S16(EmitContext& ctx, Id value);
Id EmitConvertF64S32(EmitContext& ctx, Id value);
Id EmitConvertF64S64(EmitContext& ctx, Id value);
Id EmitConvertF64U8(EmitContext& ctx, Id value);
Id EmitConvertF64U16(EmitContext& ctx, Id value);
Id EmitConvertF64U32(EmitContext& ctx, Id value);
Id EmitConvertF64U64(EmitContext& ctx, Id value);
Id EmitConvertU16U32(EmitContext& ctx, Id value);
Id EmitConvertU32U16(EmitContext& ctx, Id value);
Id EmitConvertU8U32(EmitContext& ctx, Id value);
Id EmitConvertU32U8(EmitContext& ctx, Id value);
Id EmitConvertS32S8(EmitContext& ctx, Id value);
Id EmitConvertS32S16(EmitContext& ctx, Id value);
Id EmitImageHandle(EmitContext& ctx, Id, Id);
Id EmitImageSampleRaw(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address1, Id address2,
Id address3, Id address4);
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id bias,
const IR::Value& offset);
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod,
const IR::Value& offset);
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id dref,
Id bias, const IR::Value& offset);
Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id dref,
Id lod, const IR::Value& offset);
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords,
const IR::Value& offset);
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords,
const IR::Value& offset, Id dref);
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod, bool skip_mips);
Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords);
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id derivatives_dx,
Id derivatives_dy, const IR::Value& offset, const IR::Value& lod_clamp);
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id ms);
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, Id ms,
Id color);
Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicSMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicUMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicSMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicUMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicFMax32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicFMin32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicInc32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicDec32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicCmpSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
Id cmp_value);
Id EmitCubeFaceIndex(EmitContext& ctx, IR::Inst* inst, Id x, Id y, Id z);
Id EmitCubeFaceCoordS(EmitContext& ctx, IR::Inst* inst, Id x, Id y, Id z);
Id EmitCubeFaceCoordT(EmitContext& ctx, IR::Inst* inst, Id x, Id y, Id z);
Id EmitCubeFaceMajorAxis(EmitContext& ctx, IR::Inst* inst, Id x, Id y, Id z);
Id EmitLaneId(EmitContext& ctx);
Id EmitWarpId(EmitContext& ctx);
Id EmitQuadBroadcast(EmitContext& ctx, Id value, Id index);
Id EmitShuffle(EmitContext& ctx, Id value, Id index);
Id EmitShuffleXor(EmitContext& ctx, Id value, Id mask);
Id EmitReadFirstLane(EmitContext& ctx, Id value, Id exec);
Id EmitReadLane(EmitContext& ctx, Id value, Id lane);
Id EmitWriteLane(EmitContext& ctx, Id value, Id write_value, u32 lane);
Id EmitBallot(EmitContext& ctx, Id bit);
Id EmitBallotFindLsb(EmitContext& ctx, Id mask);
Id EmitInverseBallot(EmitContext& ctx, Id mask);
Id EmitGroupAny(EmitContext& ctx, Id bit);
Id EmitDataAppend(EmitContext& ctx, Id gds_dw_offset, Id exec);
Id EmitDataConsume(EmitContext& ctx, Id gds_dw_offset, Id exec);
void EmitEmitVertex(EmitContext& ctx);
void EmitEmitPrimitive(EmitContext& ctx);
} // namespace Shader::Backend::SPIRV
@@ -1,461 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
namespace {
void SetZeroFlag(EmitContext& ctx, IR::Inst* inst, Id result) {
// IR::Inst* const zero{inst->GetAssociatedPseudoOperation(IR::Opcode::GetZeroFromOp)};
// if (!zero) {
// return;
// }
// zero->SetDefinition(ctx.OpIEqual(ctx.U1[1], result, ctx.u32_zero_value));
// zero->Invalidate();
}
void SetSignFlag(EmitContext& ctx, IR::Inst* inst, Id result) {
// IR::Inst* const sign{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSignFromOp)};
// if (!sign) {
// return;
// }
// sign->SetDefinition(ctx.OpSLessThan(ctx.U1[1], result, ctx.u32_zero_value));
// sign->Invalidate();
}
} // Anonymous namespace
Id EmitIAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
return ctx.OpIAdd(ctx.U32[1], a, b);
// Id result{};
// if (IR::Inst* const carry{inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp)}) {
// const Id carry_type{ctx.TypeStruct(ctx.U32[1], ctx.U32[1])};
// const Id carry_result{ctx.OpIAddCarry(carry_type, a, b)};
// result = ctx.OpCompositeExtract(ctx.U32[1], carry_result, 0U);
// const Id carry_value{ctx.OpCompositeExtract(ctx.U32[1], carry_result, 1U)};
// carry->SetDefinition(ctx.OpINotEqual(ctx.U1[1][1], carry_value, ctx.u32_zero_value));
// carry->Invalidate();
//} else {
// result = ctx.OpIAdd(ctx.U32[1], a, b);
//}
// SetZeroFlag(ctx, inst, result);
// SetSignFlag(ctx, inst, result);
// if (IR::Inst * overflow{inst->GetAssociatedPseudoOperation(IR::Opcode::GetOverflowFromOp)}) {
// // https://stackoverflow.com/questions/55468823/how-to-detect-integer-overflow-in-c
// constexpr u32 s32_max{static_cast<u32>(std::numeric_limits<s32>::max())};
// const Id is_positive{ctx.OpSGreaterThanEqual(ctx.U1[1], a, ctx.u32_zero_value)};
// const Id sub_a{ctx.OpISub(ctx.U32[1], ctx.Const(s32_max), a)};
// const Id positive_test{ctx.OpSGreaterThan(ctx.U1[1], b, sub_a)};
// const Id negative_test{ctx.OpSLessThan(ctx.U1[1], b, sub_a)};
// const Id carry_flag{ctx.OpSelect(ctx.U1[1], is_positive, positive_test, negative_test)};
// overflow->SetDefinition(carry_flag);
// overflow->Invalidate();
//}
// return result;
}
Id EmitIAdd64(EmitContext& ctx, Id a, Id b) {
return ctx.OpIAdd(ctx.U64, a, b);
}
Id EmitISub32(EmitContext& ctx, Id a, Id b) {
return ctx.OpISub(ctx.U32[1], a, b);
}
Id EmitISub64(EmitContext& ctx, Id a, Id b) {
return ctx.OpISub(ctx.U64, a, b);
}
Id EmitSMulHi(EmitContext& ctx, Id a, Id b) {
const auto signed_a{ctx.OpBitcast(ctx.S32[1], a)};
const auto signed_b{ctx.OpBitcast(ctx.S32[1], b)};
const auto mul_ext{ctx.OpSMulExtended(ctx.full_result_i32x2, signed_a, signed_b)};
const auto signed_hi{ctx.OpCompositeExtract(ctx.S32[1], mul_ext, 1)};
return ctx.OpBitcast(ctx.U32[1], signed_hi);
}
Id EmitUMulHi(EmitContext& ctx, Id a, Id b) {
const auto mul_ext{ctx.OpUMulExtended(ctx.full_result_u32x2, a, b)};
return ctx.OpCompositeExtract(ctx.U32[1], mul_ext, 1);
}
Id EmitIMul32(EmitContext& ctx, Id a, Id b) {
return ctx.OpIMul(ctx.U32[1], a, b);
}
Id EmitIMul64(EmitContext& ctx, Id a, Id b) {
return ctx.OpIMul(ctx.U64, a, b);
}
Id EmitSDiv32(EmitContext& ctx, Id a, Id b) {
return ctx.OpSDiv(ctx.U32[1], a, b);
}
Id EmitUDiv32(EmitContext& ctx, Id a, Id b) {
return ctx.OpUDiv(ctx.U32[1], a, b);
}
Id EmitSMod32(EmitContext& ctx, Id a, Id b) {
return ctx.OpSMod(ctx.U32[1], a, b);
}
Id EmitUMod32(EmitContext& ctx, Id a, Id b) {
return ctx.OpUMod(ctx.U32[1], a, b);
}
Id EmitINeg32(EmitContext& ctx, Id value) {
return ctx.OpSNegate(ctx.U32[1], value);
}
Id EmitINeg64(EmitContext& ctx, Id value) {
return ctx.OpSNegate(ctx.U64, value);
}
Id EmitIAbs32(EmitContext& ctx, Id value) {
return ctx.OpSAbs(ctx.U32[1], value);
}
Id EmitShiftLeftLogical32(EmitContext& ctx, Id base, Id shift) {
return ctx.OpShiftLeftLogical(ctx.U32[1], base, shift);
}
Id EmitShiftLeftLogical64(EmitContext& ctx, Id base, Id shift) {
return ctx.OpShiftLeftLogical(ctx.U64, base, shift);
}
Id EmitShiftRightLogical32(EmitContext& ctx, Id base, Id shift) {
return ctx.OpShiftRightLogical(ctx.U32[1], base, shift);
}
Id EmitShiftRightLogical64(EmitContext& ctx, Id base, Id shift) {
return ctx.OpShiftRightLogical(ctx.U64, base, shift);
}
Id EmitShiftRightArithmetic32(EmitContext& ctx, Id base, Id shift) {
return ctx.OpShiftRightArithmetic(ctx.U32[1], base, shift);
}
Id EmitShiftRightArithmetic64(EmitContext& ctx, Id base, Id shift) {
return ctx.OpShiftRightArithmetic(ctx.U64, base, shift);
}
Id EmitBitwiseAnd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
const Id result{ctx.OpBitwiseAnd(ctx.U32[1], a, b)};
SetZeroFlag(ctx, inst, result);
SetSignFlag(ctx, inst, result);
return result;
}
Id EmitBitwiseAnd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
const Id result{ctx.OpBitwiseAnd(ctx.U64, a, b)};
SetZeroFlag(ctx, inst, result);
SetSignFlag(ctx, inst, result);
return result;
}
Id EmitBitwiseOr32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
const Id result{ctx.OpBitwiseOr(ctx.U32[1], a, b)};
SetZeroFlag(ctx, inst, result);
SetSignFlag(ctx, inst, result);
return result;
}
Id EmitBitwiseOr64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
const Id result{ctx.OpBitwiseOr(ctx.U64, a, b)};
SetZeroFlag(ctx, inst, result);
SetSignFlag(ctx, inst, result);
return result;
}
Id EmitBitwiseXor32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
const Id result{ctx.OpBitwiseXor(ctx.U32[1], a, b)};
SetZeroFlag(ctx, inst, result);
SetSignFlag(ctx, inst, result);
return result;
}
Id EmitBitwiseXor64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
const Id result{ctx.OpBitwiseXor(ctx.U64, a, b)};
SetZeroFlag(ctx, inst, result);
SetSignFlag(ctx, inst, result);
return result;
}
Id EmitBitFieldInsert32(EmitContext& ctx, Id base, Id insert, Id offset, Id count) {
return ctx.OpBitFieldInsert(ctx.U32[1], base, insert, offset, count);
}
Id EmitBitFieldInsert64(EmitContext& ctx, Id base, Id insert, Id offset, Id count) {
// Vulkan restricts OpBitFieldInsert to a 32-bit base, so build the field mask with
// 64-bit shifts and insert with bitwise operations.
const Id all_ones{ctx.Constant(ctx.U64, ~0ULL)};
const Id ones{ctx.OpShiftRightLogical(ctx.U64, all_ones,
ctx.OpISub(ctx.U32[1], ctx.ConstU32(64U), count))};
const Id is_empty{ctx.OpIEqual(ctx.U1[1], count, ctx.u32_zero_value)};
const Id field{ctx.OpSelect(ctx.U64, is_empty, ctx.u64_zero_value, ones)};
const Id mask{ctx.OpShiftLeftLogical(ctx.U64, field, offset)};
const Id kept{ctx.OpBitwiseAnd(ctx.U64, base, ctx.OpNot(ctx.U64, mask))};
const Id inserted{
ctx.OpBitwiseAnd(ctx.U64, ctx.OpShiftLeftLogical(ctx.U64, insert, offset), mask)};
return ctx.OpBitwiseOr(ctx.U64, kept, inserted);
}
Id EmitBitFieldSExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count) {
const Id result{ctx.OpBitFieldSExtract(ctx.U32[1], base, offset, count)};
SetZeroFlag(ctx, inst, result);
SetSignFlag(ctx, inst, result);
return result;
}
Id EmitBitFieldUExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count) {
const Id result{ctx.OpBitFieldUExtract(ctx.U32[1], base, offset, count)};
SetZeroFlag(ctx, inst, result);
SetSignFlag(ctx, inst, result);
return result;
}
Id EmitBitReverse32(EmitContext& ctx, Id value) {
return ctx.OpBitReverse(ctx.U32[1], value);
}
Id EmitBitCount32(EmitContext& ctx, Id value) {
return ctx.OpBitCount(ctx.U32[1], value);
}
Id EmitBitCount64(EmitContext& ctx, Id value) {
// Vulkan restricts some bitwise operations to 32-bit only, so decompose into
// two 32-bit values and add the result.
const Id unpacked{ctx.OpBitcast(ctx.U32[2], value)};
const Id lo{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 0U)};
const Id hi{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 1U)};
const Id lo_count{ctx.OpBitCount(ctx.U32[1], lo)};
const Id hi_count{ctx.OpBitCount(ctx.U32[1], hi)};
return ctx.OpIAdd(ctx.U32[1], lo_count, hi_count);
}
Id EmitBitwiseNot32(EmitContext& ctx, Id value) {
return ctx.OpNot(ctx.U32[1], value);
}
Id EmitBitwiseNot64(EmitContext& ctx, Id value) {
return ctx.OpNot(ctx.U64, value);
}
Id EmitFindSMsb32(EmitContext& ctx, Id value) {
return ctx.OpFindSMsb(ctx.U32[1], value);
}
Id EmitFindUMsb32(EmitContext& ctx, Id value) {
return ctx.OpFindUMsb(ctx.U32[1], value);
}
Id EmitFindUMsb64(EmitContext& ctx, Id value) {
// Vulkan restricts some bitwise operations to 32-bit only, so decompose into
// two 32-bit values and select the correct result.
const Id unpacked{ctx.OpBitcast(ctx.U32[2], value)};
const Id hi{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 1U)};
const Id lo{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 0U)};
const Id hi_msb{ctx.OpFindUMsb(ctx.U32[1], hi)};
const Id lo_msb{ctx.OpFindUMsb(ctx.U32[1], lo)};
const Id found_hi{ctx.OpINotEqual(ctx.U1[1], hi_msb, ctx.ConstU32(u32(-1)))};
const Id shifted_hi{ctx.OpIAdd(ctx.U32[1], hi_msb, ctx.ConstU32(32u))};
// value == 0 case is checked in IREmitter
return ctx.OpSelect(ctx.U32[1], found_hi, shifted_hi, lo_msb);
}
Id EmitFindILsb32(EmitContext& ctx, Id value) {
return ctx.OpFindILsb(ctx.U32[1], value);
}
Id EmitFindILsb64(EmitContext& ctx, Id value) {
// Vulkan restricts some bitwise operations to 32-bit only, so decompose into
// two 32-bit values and select the correct result.
const Id unpacked{ctx.OpBitcast(ctx.U32[2], value)};
const Id lo{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 0U)};
const Id hi{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 1U)};
const Id lo_lsb{ctx.OpFindILsb(ctx.U32[1], lo)};
const Id hi_lsb{ctx.OpFindILsb(ctx.U32[1], hi)};
const Id not_found{ctx.ConstU32(u32(-1))};
const Id found_lo{ctx.OpINotEqual(ctx.U1[1], lo_lsb, not_found)};
const Id found_hi{ctx.OpINotEqual(ctx.U1[1], hi_lsb, not_found)};
// The high half holds bits 32..63; -1 when no bit is set at all.
const Id hi_pos{ctx.OpSelect(ctx.U32[1], found_hi,
ctx.OpIAdd(ctx.U32[1], hi_lsb, ctx.ConstU32(32U)), not_found)};
return ctx.OpSelect(ctx.U32[1], found_lo, lo_lsb, hi_pos);
}
Id EmitSMin32(EmitContext& ctx, Id a, Id b) {
return ctx.OpSMin(ctx.U32[1], a, b);
}
Id EmitUMin32(EmitContext& ctx, Id a, Id b) {
return ctx.OpUMin(ctx.U32[1], a, b);
}
Id EmitSMax32(EmitContext& ctx, Id a, Id b) {
return ctx.OpSMax(ctx.U32[1], a, b);
}
Id EmitUMax32(EmitContext& ctx, Id a, Id b) {
return ctx.OpUMax(ctx.U32[1], a, b);
}
Id EmitSMinTri32(EmitContext& ctx, Id a, Id b, Id c) {
if (ctx.profile.supports_trinary_minmax) {
return ctx.OpSMin3AMD(ctx.U32[1], a, b, c);
}
return ctx.OpSMin(ctx.U32[1], a, ctx.OpSMin(ctx.U32[1], b, c));
}
Id EmitUMinTri32(EmitContext& ctx, Id a, Id b, Id c) {
if (ctx.profile.supports_trinary_minmax) {
return ctx.OpUMin3AMD(ctx.U32[1], a, b, c);
}
return ctx.OpUMin(ctx.U32[1], a, ctx.OpUMin(ctx.U32[1], b, c));
}
Id EmitSMaxTri32(EmitContext& ctx, Id a, Id b, Id c) {
if (ctx.profile.supports_trinary_minmax) {
return ctx.OpSMax3AMD(ctx.U32[1], a, b, c);
}
return ctx.OpSMax(ctx.U32[1], a, ctx.OpSMax(ctx.U32[1], b, c));
}
Id EmitUMaxTri32(EmitContext& ctx, Id a, Id b, Id c) {
if (ctx.profile.supports_trinary_minmax) {
return ctx.OpUMax3AMD(ctx.U32[1], a, b, c);
}
return ctx.OpUMax(ctx.U32[1], a, ctx.OpUMax(ctx.U32[1], b, c));
}
Id EmitSMedTri32(EmitContext& ctx, Id a, Id b, Id c) {
if (ctx.profile.supports_trinary_minmax) {
return ctx.OpSMid3AMD(ctx.U32[1], a, b, c);
}
const Id mmx{ctx.OpSMin(ctx.U32[1], ctx.OpSMax(ctx.U32[1], a, b), c)};
return ctx.OpSMax(ctx.U32[1], ctx.OpSMin(ctx.U32[1], a, b), mmx);
}
Id EmitUMedTri32(EmitContext& ctx, Id a, Id b, Id c) {
if (ctx.profile.supports_trinary_minmax) {
return ctx.OpUMid3AMD(ctx.U32[1], a, b, c);
}
const Id mmx{ctx.OpUMin(ctx.U32[1], ctx.OpUMax(ctx.U32[1], a, b), c)};
return ctx.OpUMax(ctx.U32[1], ctx.OpUMin(ctx.U32[1], a, b), mmx);
}
Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
Id result{};
if (ctx.profile.has_broken_spirv_clamp) {
value = ctx.OpBitcast(ctx.S32[1], value);
min = ctx.OpBitcast(ctx.S32[1], min);
max = ctx.OpBitcast(ctx.S32[1], max);
result = ctx.OpSMax(ctx.S32[1], ctx.OpSMin(ctx.S32[1], value, max), min);
result = ctx.OpBitcast(ctx.U32[1], result);
} else {
result = ctx.OpSClamp(ctx.U32[1], value, min, max);
}
SetZeroFlag(ctx, inst, result);
SetSignFlag(ctx, inst, result);
return result;
}
Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
Id result{};
if (ctx.profile.has_broken_spirv_clamp) {
result = ctx.OpUMax(ctx.U32[1], ctx.OpUMin(ctx.U32[1], value, max), min);
} else {
result = ctx.OpUClamp(ctx.U32[1], value, min, max);
}
SetZeroFlag(ctx, inst, result);
SetSignFlag(ctx, inst, result);
return result;
}
Id EmitSLessThan32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpSLessThan(ctx.U1[1], lhs, rhs);
}
Id EmitSLessThan64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpSLessThan(ctx.U1[1], lhs, rhs);
}
Id EmitULessThan32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpULessThan(ctx.U1[1], lhs, rhs);
}
Id EmitULessThan64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpULessThan(ctx.U1[1], lhs, rhs);
}
Id EmitIEqual32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpIEqual(ctx.U1[1], lhs, rhs);
}
Id EmitIEqual64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpIEqual(ctx.U1[1], lhs, rhs);
}
Id EmitSLessThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpSLessThanEqual(ctx.U1[1], lhs, rhs);
}
Id EmitSLessThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpSLessThanEqual(ctx.U1[1], lhs, rhs);
}
Id EmitULessThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpULessThanEqual(ctx.U1[1], lhs, rhs);
}
Id EmitULessThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpULessThanEqual(ctx.U1[1], lhs, rhs);
}
Id EmitSGreaterThan32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpSGreaterThan(ctx.U1[1], lhs, rhs);
}
Id EmitSGreaterThan64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpSGreaterThan(ctx.U1[1], lhs, rhs);
}
Id EmitUGreaterThan32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpUGreaterThan(ctx.U1[1], lhs, rhs);
}
Id EmitUGreaterThan64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpUGreaterThan(ctx.U1[1], lhs, rhs);
}
Id EmitINotEqual32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpINotEqual(ctx.U1[1], lhs, rhs);
}
Id EmitINotEqual64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpINotEqual(ctx.U1[1], lhs, rhs);
}
Id EmitSGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpSGreaterThanEqual(ctx.U1[1], lhs, rhs);
}
Id EmitSGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpSGreaterThanEqual(ctx.U1[1], lhs, rhs);
}
Id EmitUGreaterThanEqual32(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpUGreaterThanEqual(ctx.U1[1], lhs, rhs);
}
Id EmitUGreaterThanEqual64(EmitContext& ctx, Id lhs, Id rhs) {
return ctx.OpUGreaterThanEqual(ctx.U1[1], lhs, rhs);
}
Id EmitMaskedBitCount32(EmitContext& ctx, Id value, Id addend, bool hi) {
UNREACHABLE_MSG("Unreachable instruction");
}
} // namespace Shader::Backend::SPIRV
@@ -1,45 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
Id EmitUndefU1(EmitContext& ctx) {
return ctx.OpUndef(ctx.U1[1]);
}
Id EmitUndefU8(EmitContext&) {
UNREACHABLE_MSG("SPIR-V Instruction");
}
Id EmitUndefU16(EmitContext&) {
UNREACHABLE_MSG("SPIR-V Instruction");
}
Id EmitUndefU32(EmitContext& ctx) {
return ctx.OpUndef(ctx.U32[1]);
}
Id EmitUndefU32x2(EmitContext& ctx) {
return ctx.OpUndef(ctx.U32[2]);
}
Id EmitUndefU32x3(EmitContext& ctx) {
return ctx.OpUndef(ctx.U32[3]);
}
Id EmitUndefU32x4(EmitContext& ctx) {
return ctx.OpUndef(ctx.U32[4]);
}
Id EmitUndefF32(EmitContext& ctx) {
return ctx.OpUndef(ctx.F32[1]);
}
Id EmitUndefU64(EmitContext& ctx) {
return ctx.OpUndef(ctx.U64);
}
} // namespace Shader::Backend::SPIRV
@@ -1,75 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "shader_recompiler/backend/spirv/emit_spirv_instructions.h"
#include "shader_recompiler/backend/spirv/spirv_emit_context.h"
namespace Shader::Backend::SPIRV {
Id SubgroupScope(EmitContext& ctx) {
return ctx.ConstU32(static_cast<u32>(spv::Scope::Subgroup));
}
Id EmitWarpId(EmitContext& ctx) {
UNREACHABLE();
}
Id EmitLaneId(EmitContext& ctx) {
return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id);
}
Id EmitQuadBroadcast(EmitContext& ctx, Id value, Id index) {
return ctx.OpGroupNonUniformQuadBroadcast(ctx.U32[1], SubgroupScope(ctx), value, index);
}
Id EmitReadFirstLane(EmitContext& ctx, Id value, Id exec) {
// The first lane with EXEC set, lane 0 when EXEC is empty.
const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), exec)};
const Id lsb{ctx.OpGroupNonUniformBallotFindLSB(ctx.U32[1], SubgroupScope(ctx), ballot)};
const Id any{
ctx.OpINotEqual(ctx.U1[1],
ctx.OpBitwiseOr(ctx.U32[1], ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U),
ctx.OpCompositeExtract(ctx.U32[1], ballot, 1U)),
ctx.u32_zero_value)};
const Id lane{ctx.OpSelect(ctx.U32[1], any, lsb, ctx.u32_zero_value)};
return ctx.OpGroupNonUniformBroadcast(ctx.U32[1], SubgroupScope(ctx), value, lane);
}
Id EmitShuffle(EmitContext& ctx, Id value, Id index) {
return ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, index);
}
Id EmitShuffleXor(EmitContext& ctx, Id value, Id mask) {
return ctx.OpGroupNonUniformShuffleXor(ctx.U32[1], SubgroupScope(ctx), value, mask);
}
Id EmitReadLane(EmitContext& ctx, Id value, Id lane) {
return ctx.OpGroupNonUniformBroadcast(ctx.U32[1], SubgroupScope(ctx), value, lane);
}
Id EmitWriteLane(EmitContext& ctx, Id value, Id write_value, u32 lane) {
return ctx.u32_zero_value;
}
Id EmitBallot(EmitContext& ctx, Id bit) {
const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), bit)};
return ctx.OpBitcast(ctx.U64, ctx.OpVectorShuffle(ctx.U32[2], ballot, ballot, 0, 1));
}
Id EmitBallotFindLsb(EmitContext& ctx, Id mask) {
const Id value{ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpBitcast(ctx.U32[2], mask),
ctx.u32_zero_value, ctx.u32_zero_value)};
return ctx.OpGroupNonUniformBallotFindLSB(ctx.U32[1], SubgroupScope(ctx), value);
}
Id EmitInverseBallot(EmitContext& ctx, Id mask) {
const Id value{ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpBitcast(ctx.U32[2], mask),
ctx.u32_zero_value, ctx.u32_zero_value)};
return ctx.OpGroupNonUniformInverseBallot(ctx.U1[1], SubgroupScope(ctx), value);
}
Id EmitGroupAny(EmitContext& ctx, Id bit) {
return ctx.OpGroupNonUniformAny(ctx.U1[1], SubgroupScope(ctx), bit);
}
} // namespace Shader::Backend::SPIRV
@@ -1,650 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <map>
#include <optional>
#include <unordered_map>
#include "common/assert.h"
#include "common/logging/log.h"
#include "shader_recompiler/frontend/control_flow_graph.h"
#include "shader_recompiler/ir/condition.h"
namespace Shader::Gcn {
struct Compare {
bool operator()(const Block& lhs, u32 rhs) const noexcept {
return lhs.begin < rhs;
}
bool operator()(u32 lhs, const Block& rhs) const noexcept {
return lhs < rhs.begin;
}
bool operator()(const Block& lhs, const Block& rhs) const noexcept {
return lhs.begin < rhs.begin;
}
};
static IR::Condition MakeCondition(const GcnInst& inst) {
if (inst.IsCmpx()) {
return IR::Condition::Execnz;
}
switch (inst.opcode) {
case Opcode::S_CBRANCH_SCC0:
return IR::Condition::Scc0;
case Opcode::S_CBRANCH_SCC1:
return IR::Condition::Scc1;
case Opcode::S_CBRANCH_VCCZ:
return IR::Condition::Vccz;
case Opcode::S_CBRANCH_VCCNZ:
return IR::Condition::Vccnz;
case Opcode::S_CBRANCH_EXECZ:
return IR::Condition::Execz;
case Opcode::S_CBRANCH_EXECNZ:
return IR::Condition::Execnz;
default:
return IR::Condition::True;
}
}
static bool IgnoresExecMask(const GcnInst& inst) {
// EXEC mask does not affect scalar instructions or branches. Exports run in every lane, a
// fragment export discards the lanes with EXEC clear.
switch (inst.category) {
case InstCategory::ScalarALU:
case InstCategory::ScalarMemory:
case InstCategory::FlowControl:
case InstCategory::Export:
return true;
default:
break;
}
// Compares run in every lane, they clear the bits of the lanes with EXEC clear themselves.
if (inst.opcode >= Opcode::V_CMP_F_F32 && inst.opcode <= Opcode::V_CMPX_T_U64) {
return true;
}
// Read/Write Lane instructions are not affected either.
switch (inst.opcode) {
case Opcode::V_READLANE_B32:
case Opcode::V_WRITELANE_B32:
case Opcode::V_READFIRSTLANE_B32:
return true;
default:
break;
}
return false;
}
static std::optional<u32> ResolveSetPcTarget(std::span<const GcnInst> list, u32 setpc_index,
std::span<const u32> pc_map) {
if (setpc_index < 3) {
return std::nullopt;
}
const auto& getpc = list[setpc_index - 3];
const auto& arith = list[setpc_index - 2];
const auto& setpc = list[setpc_index];
if (getpc.opcode != Opcode::S_GETPC_B64 ||
!(arith.opcode == Opcode::S_ADD_U32 || arith.opcode == Opcode::S_SUB_U32) ||
setpc.opcode != Opcode::S_SETPC_B64)
return std::nullopt;
if (getpc.dst[0].code != setpc.src[0].code || arith.dst[0].code != setpc.src[0].code)
return std::nullopt;
if (arith.src_count < 2 || arith.src[1].field != OperandField::LiteralConst)
return std::nullopt;
const u32 imm = arith.src[1].code;
const s32 signed_offset =
(arith.opcode == Opcode::S_ADD_U32) ? static_cast<s32>(imm) : -static_cast<s32>(imm);
const u32 base_pc = pc_map[setpc_index - 3] + getpc.length;
const u32 result_pc = static_cast<u32>(static_cast<s32>(base_pc) + signed_offset);
LOG_DEBUG(Render_Recompiler, "SetPC target: {} + {} = {}", base_pc, signed_offset, result_pc);
return result_pc & ~0x3u;
}
static constexpr size_t LabelReserveSize = 32;
CFG::CFG(Common::ObjectPool<Block>& block_pool_, std::span<const GcnInst> inst_list_)
: block_pool{block_pool_}, inst_list{inst_list_} {
index_to_pc.resize(inst_list.size() + 1);
labels.reserve(LabelReserveSize);
EmitLabels();
EmitBlocks();
LinkBlocks();
SplitDivergenceScopes();
RemoveUnreachableBlocks();
}
void CFG::EmitLabels() {
// Always set a label at entry point.
u32 pc = 0;
AddLabel(pc);
// Iterate instruction list and add labels to branch targets.
for (u32 i = 0; i < inst_list.size(); i++) {
index_to_pc[i] = pc;
const GcnInst inst = inst_list[i];
if (inst.IsUnconditionalBranch()) {
u32 target = inst.BranchTarget(pc);
if (inst.opcode == Opcode::S_SETPC_B64) {
if (auto t = ResolveSetPcTarget(inst_list, i, index_to_pc)) {
target = *t;
} else {
ASSERT_MSG(
false,
"S_SETPC_B64 without a resolvable offset at PC {:#x} (Index {}): Involved "
"instructions not recognized or invalid pattern",
pc, i);
}
}
AddLabel(target);
// Emit this label so that the block ends with the branching instruction
AddLabel(pc + inst.length);
} else if (inst.IsConditionalBranch()) {
const u32 true_label = inst.BranchTarget(pc);
const u32 false_label = pc + inst.length;
if (true_label != false_label) {
AddLabel(true_label);
AddLabel(false_label);
}
} else if (inst.opcode == Opcode::S_ENDPGM) {
const u32 next_label = pc + inst.length;
AddLabel(next_label);
}
pc += inst.length;
}
index_to_pc[inst_list.size()] = pc;
// Sort labels to make sure block insertion is correct.
std::ranges::sort(labels);
}
// Instructions without any effect, they can stay inside an EXEC scope.
static bool IsExecNeutral(const GcnInst& inst) {
switch (inst.opcode) {
case Opcode::S_NOP:
case Opcode::S_WAITCNT:
case Opcode::S_SETPRIO:
return true;
default:
return false;
}
}
static bool IsSaveExec(const GcnInst& inst) {
return inst.opcode >= Opcode::S_AND_SAVEEXEC_B64 && inst.opcode <= Opcode::S_XNOR_SAVEEXEC_B64;
}
static bool WritesExec(const GcnInst& inst) {
// S_WQM_B64 is translated as a no-op, it does not change the EXEC of an invocation.
if (inst.opcode == Opcode::S_WQM_B64) {
return false;
}
return IsSaveExec(inst) || inst.IsCmpx() ||
(inst.dst_count > 0 && (inst.dst[0].field == OperandField::ExecLo ||
inst.dst[0].field == OperandField::ExecHi));
}
// Registers written by a scalar memory instruction.
static u32 ScalarLoadWidth(Opcode opcode) {
switch (opcode) {
case Opcode::S_LOAD_DWORD:
case Opcode::S_BUFFER_LOAD_DWORD:
return 1;
case Opcode::S_LOAD_DWORDX2:
case Opcode::S_BUFFER_LOAD_DWORDX2:
case Opcode::S_MEMTIME:
return 2;
case Opcode::S_LOAD_DWORDX4:
case Opcode::S_BUFFER_LOAD_DWORDX4:
return 4;
case Opcode::S_LOAD_DWORDX8:
case Opcode::S_BUFFER_LOAD_DWORDX8:
return 8;
default:
return 16;
}
}
namespace {
// What is known about EXEC at a program point: whether every lane that executes it has its EXEC
// bit set, and which registers hold a half of a copy of such a full mask. A tag is
// (copy id << 1) | half; the copy id is the index of the instruction that saved it.
struct ExecState {
bool full{};
std::array<u32, 128> sgpr_tag{}; // SGPRs and VCC (106, 107)
std::map<u32, u32> lane_tag{}; // VGPR lanes holding a spilled half: vgpr * 64 + lane
bool operator==(const ExecState&) const = default;
bool HoldsFullCopy(u32 reg) const {
return reg + 1 < sgpr_tag.size() && sgpr_tag[reg] != 0 && (sgpr_tag[reg] & 1) == 0 &&
sgpr_tag[reg + 1] == sgpr_tag[reg] + 1;
}
static ExecState Merge(const ExecState& a, const ExecState& b) {
ExecState merged{.full = a.full && b.full};
for (u32 i = 0; i < merged.sgpr_tag.size(); ++i) {
merged.sgpr_tag[i] = a.sgpr_tag[i] == b.sgpr_tag[i] ? a.sgpr_tag[i] : 0;
}
for (const auto& [key, tag] : a.lane_tag) {
const auto it = b.lane_tag.find(key);
if (it != b.lane_tag.end() && it->second == tag) {
merged.lane_tag.emplace(key, tag);
}
}
return merged;
}
};
} // Anonymous namespace
// The value of an inline integer constant operand, if it is one.
static std::optional<u32> InlineInt(const InstOperand& op) {
if (op.field == OperandField::ConstZero) {
return 0;
}
if (op.field == OperandField::SignedConstIntPos) {
return op.code - static_cast<u32>(OperandField::ConstZero);
}
return std::nullopt;
}
static void ExecTransfer(const GcnInst& inst, u32 index, ExecState& state) {
const bool full_before = state.full;
const auto is_full_copy = [&](const InstOperand& op) {
return (op.field == OperandField::ScalarGPR || op.field == OperandField::VccLo) &&
state.HoldsFullCopy(op.code);
};
if (WritesExec(inst)) {
// Restoring a copy of the full mask, s_mov_b64 exec, -1 and the s_or_b64 exec, exec, copy
// that ends an if/else make EXEC full again; any other write can leave lanes off.
bool full = false;
if (inst.opcode == Opcode::S_MOV_B64) {
full =
is_full_copy(inst.src[0]) ||
(inst.src[0].field == OperandField::SignedConstIntNeg && inst.src[0].code == 193);
} else if (inst.opcode == Opcode::S_OR_B64) {
full = is_full_copy(inst.src[0]) || is_full_copy(inst.src[1]);
} else if (inst.opcode == Opcode::S_ORN2_SAVEEXEC_B64) {
// EXEC = src | ~EXEC: with src = EXEC every lane is on.
full = inst.src[0].field == OperandField::ExecLo || is_full_copy(inst.src[0]);
} else if (inst.opcode == Opcode::S_AND_B64) {
// Ending whole quad mode: a full EXEC and a copy of the full mask stay full.
const auto is_exec = [](const InstOperand& op) {
return op.field == OperandField::ExecLo;
};
full = full_before && ((is_exec(inst.src[0]) && is_full_copy(inst.src[1])) ||
(is_exec(inst.src[1]) && is_full_copy(inst.src[0])));
}
state.full = full;
}
// A half of a copy moved through a VGPR lane: v_writelane / v_readlane with a constant lane.
if (inst.opcode == Opcode::V_READLANE_B32 && inst.dst[0].field == OperandField::ScalarGPR &&
inst.dst[0].code < 128) {
const auto lane = InlineInt(inst.src[1]);
u32 tag = 0;
if (lane && inst.src[0].field == OperandField::VectorGPR) {
const auto it = state.lane_tag.find(inst.src[0].code * 64 + *lane);
tag = it != state.lane_tag.end() ? it->second : 0;
}
state.sgpr_tag[inst.dst[0].code] = tag;
return;
}
if (inst.opcode == Opcode::V_WRITELANE_B32 && inst.dst[0].field == OperandField::VectorGPR) {
const auto lane = InlineInt(inst.src[1]);
if (!lane) {
std::erase_if(state.lane_tag,
[&](const auto& entry) { return entry.first / 64 == inst.dst[0].code; });
return;
}
const u32 key = inst.dst[0].code * 64 + *lane;
const bool from_sgpr = inst.src[0].field == OperandField::ScalarGPR ||
inst.src[0].field == OperandField::VccLo ||
inst.src[0].field == OperandField::VccHi;
const u32 tag = from_sgpr && inst.src[0].code < 128 ? state.sgpr_tag[inst.src[0].code] : 0;
if (tag != 0) {
state.lane_tag[key] = tag;
} else {
state.lane_tag.erase(key);
}
return;
}
// Registers written here no longer hold a half of a copy. Every destination slot is checked:
// an implicit VCC write is stored in dst[1] without being counted in dst_count.
for (const InstOperand& dst : inst.dst) {
if (dst.field == OperandField::VccLo || dst.field == OperandField::VccHi) {
state.sgpr_tag[static_cast<u32>(OperandField::VccLo)] = 0;
state.sgpr_tag[static_cast<u32>(OperandField::VccHi)] = 0;
continue;
}
if (dst.field == OperandField::VectorGPR) {
// Vector memory writes up to four VGPRs (dwordx4, four image components, read2_b64).
u32 vwidth = 1;
if (inst.category == InstCategory::VectorMemory ||
inst.category == InstCategory::DataShare) {
vwidth = 4;
} else if (dst.type == ScalarType::Uint64 || dst.type == ScalarType::Sint64 ||
dst.type == ScalarType::Float64) {
vwidth = 2;
}
std::erase_if(state.lane_tag, [&](const auto& entry) {
return entry.first / 64 >= dst.code && entry.first / 64 < dst.code + vwidth;
});
continue;
}
if (dst.field != OperandField::ScalarGPR) {
continue;
}
u32 width = 2;
if (inst.category == InstCategory::ScalarMemory) {
width = ScalarLoadWidth(inst.opcode);
} else if (dst.type == ScalarType::Uint32 || dst.type == ScalarType::Sint32 ||
dst.type == ScalarType::Float32 || dst.type == ScalarType::Uint16 ||
dst.type == ScalarType::Sint16 || dst.type == ScalarType::Float16) {
width = 1;
}
for (u32 reg = dst.code; reg < std::min<u32>(dst.code + width, 128); ++reg) {
state.sgpr_tag[reg] = 0;
}
}
// A save of the full mask: saveexec stores the old EXEC, as does s_mov_b64 sdst, exec.
if (full_before && inst.dst_count > 0 &&
(inst.dst[0].field == OperandField::ScalarGPR ||
inst.dst[0].field == OperandField::VccLo) &&
inst.dst[0].code + 1 < 128 &&
(IsSaveExec(inst) ||
(inst.opcode == Opcode::S_MOV_B64 && inst.src[0].field == OperandField::ExecLo))) {
state.sgpr_tag[inst.dst[0].code] = (index + 1) << 1;
state.sgpr_tag[inst.dst[0].code + 1] = ((index + 1) << 1) | 1;
}
}
void CFG::SplitDivergenceScopes() {
// Which instructions can run with lanes off in EXEC: forward over the CFG from the entry,
// where EXEC is full; paths merge to "partial" when any of them is.
std::unordered_map<const Block*, ExecState> block_in;
block_in[&*blocks.begin()] = ExecState{.full = true};
for (bool changed = true; changed;) {
changed = false;
for (const Block& blk : blocks) {
const auto it = block_in.find(&blk);
if (it == block_in.end()) {
continue;
}
ExecState state = it->second;
for (u32 i = blk.begin_index; i <= blk.end_index; ++i) {
ExecTransfer(inst_list[i], i, state);
}
for (const Block* succ : {blk.branch_true, blk.branch_false}) {
if (!succ) {
continue;
}
// A per-lane EXEC branch sends the lanes with EXEC set one way: EXEC is set in
// every lane on that edge, and clear in every lane on the other.
ExecState edge = state;
if (blk.cond == IR::Condition::Execz || blk.cond == IR::Condition::Execnz) {
const bool exec_set_edge =
(blk.cond == IR::Condition::Execnz) == (succ == blk.branch_true);
edge.full = exec_set_edge;
}
const auto [succ_it, inserted] = block_in.try_emplace(succ, edge);
if (inserted) {
changed = true;
continue;
}
const ExecState merged = ExecState::Merge(succ_it->second, edge);
if (merged != succ_it->second) {
succ_it->second = merged;
changed = true;
}
}
}
}
std::vector<bool> exec_partial(inst_list.size(), false);
for (const Block& blk : blocks) {
const auto it = block_in.find(&blk);
if (it == block_in.end()) {
continue;
}
ExecState state = it->second;
for (u32 i = blk.begin_index; i <= blk.end_index; ++i) {
exec_partial[i] = !state.full;
ExecTransfer(inst_list[i], i, state);
}
}
const auto is_masked = [&](u32 i) { return exec_partial[i] && !IgnoresExecMask(inst_list[i]); };
// EXEC masks the vector instructions only. Each run of them that can execute with lanes off
// goes into its own block, which only the lanes with EXEC set enter; scalar instructions and
// branches stay outside and run in every lane.
for (auto blk = blocks.begin(); blk != blocks.end(); blk++) {
if (blk->is_exec_scope) {
continue;
}
u32 first = blk->begin_index;
while (first <= blk->end_index && !is_masked(first)) {
++first;
}
if (first > blk->end_index) {
continue;
}
u32 last = first;
while (last < blk->end_index &&
(is_masked(last + 1) || IsExecNeutral(inst_list[last + 1]))) {
++last;
}
while (IsExecNeutral(inst_list[last])) {
--last;
}
const auto next_blk = std::next(blk);
// Create a new block for the run.
Block* block = block_pool.Create();
block->begin = index_to_pc[first];
block->end = index_to_pc[last + 1];
block->begin_index = first;
block->end_index = last;
block->end_inst = inst_list[last];
block->num_predecessors = 1;
block->is_exec_scope = true;
block->cond = IR::Condition::True;
block->end_class = EndClass::Branch;
blocks.insert_before(next_blk, *block);
if (last != blk->end_index) {
// The rest of the parent block follows the run, the lanes that skipped it join there.
Block* epi_block = block_pool.Create();
epi_block->begin = index_to_pc[last + 1];
epi_block->end = blk->end;
epi_block->begin_index = last + 1;
epi_block->end_index = blk->end_index;
epi_block->end_inst = blk->end_inst;
epi_block->cond = blk->cond;
epi_block->end_class = blk->end_class;
epi_block->branch_true = blk->branch_true;
epi_block->branch_false = blk->branch_false;
epi_block->num_predecessors = 2;
blocks.insert_before(next_blk, *epi_block);
block->branch_true = epi_block;
blk->branch_false = epi_block;
} else {
// The run ends the parent block, both paths continue at its successor.
ASSERT(blk->cond == IR::Condition::True && blk->branch_true);
block->branch_true = blk->branch_true;
blk->branch_false = blk->branch_true;
blk->branch_true->num_predecessors++;
}
// The parent block ends right before the run (it is empty when the run starts it) and
// enters the run with the lane's EXEC bit.
blk->end = index_to_pc[first];
blk->end_index = first - 1;
if (first != blk->begin_index) {
blk->end_inst = inst_list[first - 1];
}
blk->cond = IR::Condition::Execnz;
blk->end_class = EndClass::Branch;
blk->branch_true = block;
}
}
void CFG::EmitBlocks() {
for (auto it = labels.cbegin(); it != labels.cend(); ++it) {
const Label start = *it;
const auto next_it = std::next(it);
const bool is_last = (next_it == labels.cend());
if (is_last) {
// Last label is special.
return;
}
// The end label is the start instruction of next block.
// The end instruction of this block is the previous one.
const Label end = *next_it;
const size_t end_index = GetIndex(end) - 1;
const auto& end_inst = inst_list[end_index];
// Insert block between the labels using the last instruction
// as an indicator for branching type.
Block* block = block_pool.Create();
block->begin = start;
block->end = end;
block->begin_index = GetIndex(start);
block->end_index = end_index;
block->end_inst = end_inst;
block->cond = MakeCondition(end_inst);
blocks.insert(*block);
}
}
void CFG::LinkBlocks() {
const auto get_block = [this](u32 address) {
auto it = blocks.find(address, Compare{});
ASSERT_MSG(it != blocks.cend() && it->begin == address);
return &*it;
};
for (auto it = blocks.begin(); it != blocks.end(); it++) {
auto& block = *it;
const auto end_inst{block.end_inst};
// If the block doesn't end with a branch we simply
// need to link with the next block.
if (!end_inst.IsTerminateInstruction()) {
auto* next_block = get_block(block.end);
block.branch_true = next_block;
block.end_class = EndClass::Branch;
next_block->num_predecessors++;
continue;
}
// Find the branch targets from the instruction and link the blocks.
// Note: Block end address is one instruction after end_inst.
const u32 branch_pc = block.end - end_inst.length;
u32 target_pc = 0;
if (end_inst.opcode == Opcode::S_SETPC_B64) {
auto tgt = ResolveSetPcTarget(inst_list, block.end_index, index_to_pc);
ASSERT_MSG(tgt,
"S_SETPC_B64 without a resolvable offset at PC {:#x} (Index {}): Involved "
"instructions not recognized or invalid pattern",
branch_pc, block.end_index);
target_pc = *tgt;
} else {
target_pc = end_inst.BranchTarget(branch_pc);
}
if (end_inst.IsUnconditionalBranch()) {
auto* target_block = get_block(target_pc);
block.branch_true = target_block;
block.end_class = EndClass::Branch;
target_block->num_predecessors++;
} else if (end_inst.IsConditionalBranch()) {
auto* end_block = get_block(block.end);
block.end_class = EndClass::Branch;
if (target_pc != block.end) {
auto* target_block = get_block(target_pc);
block.branch_true = target_block;
block.branch_false = end_block;
target_block->num_predecessors++;
} else {
block.branch_true = end_block;
block.cond = IR::Condition::True;
}
end_block->num_predecessors++;
} else if (end_inst.opcode == Opcode::S_ENDPGM) {
block.end_class = EndClass::Exit;
} else {
UNREACHABLE();
}
}
}
void CFG::RemoveUnreachableBlocks() {
for (auto it = std::next(blocks.begin()); it != blocks.end();) {
if (it->num_predecessors == 0) {
LOG_WARNING(Render_Recompiler, "Removing unreachable block begin={:#x}", it->begin);
it = blocks.erase(it);
} else {
it++;
}
}
}
std::string CFG::Dot() const {
int node_uid{0};
const auto name_of = [](const Block& block) { return fmt::format("\"{:#x}\"", block.begin); };
std::string dot{"digraph shader {\n"};
dot += fmt::format("\tsubgraph cluster_{} {{\n", 0);
dot += fmt::format("\t\tnode [style=filled];\n");
for (const Block& block : blocks) {
const std::string name{name_of(block)};
const auto add_branch = [&](Block* branch, bool add_label) {
dot += fmt::format("\t\t{}->{}", name, name_of(*branch));
if (add_label && block.cond != IR::Condition::True &&
block.cond != IR::Condition::False) {
dot += fmt::format(" [label=\"{}\"]", block.cond);
}
dot += '\n';
};
dot += fmt::format("\t\t{};\n", name);
switch (block.end_class) {
case EndClass::Branch:
if (block.cond != IR::Condition::False) {
add_branch(block.branch_true, true);
}
if (block.cond != IR::Condition::True) {
add_branch(block.branch_false, false);
}
break;
case EndClass::Exit:
dot += fmt::format("\t\t{}->N{};\n", name, node_uid);
dot +=
fmt::format("\t\tN{} [label=\"Exit\"][shape=square][style=stripped];\n", node_uid);
++node_uid;
break;
}
}
dot += "\t\tlabel = \"main\";\n\t}\n";
if (blocks.empty()) {
dot += "Start;\n";
} else {
dot += fmt::format("\tStart -> {};\n", name_of(*blocks.begin()));
}
dot += fmt::format("\tStart [shape=diamond];\n");
dot += "}\n";
return dot;
}
} // namespace Shader::Gcn
@@ -1,104 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <algorithm>
#include <span>
#include <string>
#include <boost/container/small_vector.hpp>
#include <boost/intrusive/set.hpp>
#include "common/assert.h"
#include "common/object_pool.h"
#include "common/types.h"
#include "shader_recompiler/frontend/instruction.h"
#include "shader_recompiler/ir/condition.h"
namespace Shader::IR {
class Block;
}
namespace Shader::Gcn {
using Hook =
boost::intrusive::set_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>;
enum class EndClass {
Branch, ///< Block ends with a (un)conditional branch.
Exit, ///< Block ends with an exit instruction.
};
/// A block represents a linear range of instructions.
struct Block : Hook {
[[nodiscard]] bool Contains(u32 pc) const noexcept;
bool operator<(const Block& rhs) const noexcept {
// An empty block ahead of an EXEC scope shares its begin, it sorts first.
return begin < rhs.begin || (begin == rhs.begin && end < rhs.end);
}
u32 begin;
u32 end;
u32 begin_index;
u32 end_index;
u32 num_predecessors{};
IR::Condition cond{};
GcnInst end_inst{};
EndClass end_class{};
Block* branch_true{};
Block* branch_false{};
IR::Block* ir_block{};
bool is_dummy{};
bool is_exec_scope{};
};
class CFG {
using Label = u32;
using BlockList = boost::intrusive::set<Block>;
using iterator = BlockList::iterator;
public:
explicit CFG(Common::ObjectPool<Block>& block_pool, std::span<const GcnInst> inst_list);
[[nodiscard]] iterator begin() {
return blocks.begin();
}
[[nodiscard]] iterator end() {
return blocks.end();
}
[[nodiscard]] std::string Dot() const;
private:
void EmitLabels();
void EmitBlocks();
void LinkBlocks();
void SplitDivergenceScopes();
void RemoveUnreachableBlocks();
void AddLabel(Label address) {
const auto it = std::ranges::find(labels, address);
if (it == labels.end()) {
labels.push_back(address);
}
}
size_t GetIndex(Label label) {
if (label == 0) {
return 0ULL;
}
const auto it_index = std::ranges::lower_bound(index_to_pc, label);
ASSERT(it_index != index_to_pc.end() || label > index_to_pc.back());
return std::distance(index_to_pc.begin(), it_index);
}
public:
Common::ObjectPool<Block>& block_pool;
std::span<const GcnInst> inst_list;
std::vector<u32> index_to_pc;
boost::container::small_vector<Label, 16> labels;
BlockList blocks;
};
} // namespace Shader::Gcn
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,353 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "shader_recompiler/frontend/translate/translate.h"
#include "shader_recompiler/ir/reg.h"
#include "shader_recompiler/runtime_info.h"
namespace Shader::Gcn {
void Translator::EmitDataShare(const GcnInst& inst) {
switch (inst.opcode) {
// DS
case Opcode::DS_ADD_U32:
return DS_OP(inst, AtomicOp::Add, false);
case Opcode::DS_ADD_U64:
return DS_OP<IR::U64>(inst, AtomicOp::Add, false);
case Opcode::DS_SUB_U32:
return DS_OP(inst, AtomicOp::Sub, false);
case Opcode::DS_INC_U32:
return DS_OP(inst, AtomicOp::Inc, false);
case Opcode::DS_DEC_U32:
return DS_OP(inst, AtomicOp::Dec, false);
case Opcode::DS_MIN_I32:
return DS_OP(inst, AtomicOp::Smin, false);
case Opcode::DS_MAX_I32:
return DS_OP(inst, AtomicOp::Smax, false);
case Opcode::DS_MIN_U32:
return DS_OP(inst, AtomicOp::Umin, false);
case Opcode::DS_MAX_U32:
return DS_OP(inst, AtomicOp::Umax, false);
case Opcode::DS_AND_B32:
return DS_OP(inst, AtomicOp::And, false);
case Opcode::DS_OR_B32:
return DS_OP(inst, AtomicOp::Or, false);
case Opcode::DS_XOR_B32:
return DS_OP(inst, AtomicOp::Xor, false);
case Opcode::DS_WRITE_B32:
return DS_WRITE(32, false, false, false, inst);
case Opcode::DS_WRITE2_B32:
return DS_WRITE(32, false, true, false, inst);
case Opcode::DS_WRITE2ST64_B32:
return DS_WRITE(32, false, true, true, inst);
case Opcode::DS_ADD_RTN_U32:
return DS_OP(inst, AtomicOp::Add, true);
case Opcode::DS_SUB_RTN_U32:
return DS_OP(inst, AtomicOp::Sub, true);
case Opcode::DS_MIN_RTN_U32:
return DS_OP(inst, AtomicOp::Umin, true);
case Opcode::DS_MAX_RTN_U32:
return DS_OP(inst, AtomicOp::Umax, true);
case Opcode::DS_AND_RTN_B32:
return DS_OP(inst, AtomicOp::And, true);
case Opcode::DS_OR_RTN_B32:
return DS_OP(inst, AtomicOp::Or, true);
case Opcode::DS_XOR_RTN_B32:
return DS_OP(inst, AtomicOp::Xor, true);
case Opcode::DS_CMPST_B32:
return DS_CMPST(32, false, inst);
case Opcode::DS_CMPST_RTN_B32:
return DS_CMPST(32, true, inst);
case Opcode::DS_CMPST_B64:
return DS_CMPST(64, false, inst);
case Opcode::DS_CMPST_RTN_B64:
return DS_CMPST(64, true, inst);
case Opcode::DS_SWIZZLE_B32:
return DS_SWIZZLE_B32(inst);
case Opcode::DS_READ_B32:
return DS_READ(32, false, false, false, inst);
case Opcode::DS_READ2_B32:
return DS_READ(32, false, true, false, inst);
case Opcode::DS_READ2ST64_B32:
return DS_READ(32, false, true, true, inst);
case Opcode::DS_READ_U16:
return DS_READ(16, false, false, false, inst);
case Opcode::DS_CONSUME:
return DS_CONSUME(inst);
case Opcode::DS_APPEND:
return DS_APPEND(inst);
case Opcode::DS_WRITE_B16:
return DS_WRITE(16, false, false, false, inst);
case Opcode::DS_WRITE_B64:
return DS_WRITE(64, false, false, false, inst);
case Opcode::DS_WRITE2_B64:
return DS_WRITE(64, false, true, false, inst);
case Opcode::DS_WRITE2ST64_B64:
return DS_WRITE(64, false, true, true, inst);
case Opcode::DS_READ_B64:
return DS_READ(64, false, false, false, inst);
case Opcode::DS_READ2_B64:
return DS_READ(64, false, true, false, inst);
case Opcode::DS_READ2ST64_B64:
return DS_READ(64, false, true, true, inst);
default:
LogMissingOpcode(inst);
}
}
// VOP2
void Translator::V_READFIRSTLANE_B32(const GcnInst& inst) {
const IR::U32 value{GetSrc(inst.src[0])};
SetDst(inst.dst[0], ir.ReadFirstLane(value, ir.GetExec()));
}
void Translator::V_READLANE_B32(const GcnInst& inst) {
ASSERT(inst.src[0].field == OperandField::VectorGPR);
const IR::U32 value{GetSrc(inst.src[0])};
const IR::U32 lane{GetSrc(inst.src[1])};
SetDst(inst.dst[0], ir.ReadLane(value, lane));
}
void Translator::V_WRITELANE_B32(const GcnInst& inst) {
const IR::VectorReg dst{inst.dst[0].code};
const IR::U32 value{GetSrc(inst.src[0])};
const IR::U32 lane{GetSrc(inst.src[1])};
const IR::U32 old_value{GetSrc(inst.dst[0])};
ir.SetVectorReg(dst, ir.WriteLane(old_value, value, lane));
}
// DS
template <typename T>
void Translator::DS_OP(const GcnInst& inst, AtomicOp op, bool rtn) {
const bool is_gds = inst.control.ds.gds;
const IR::U32 addr{GetSrc(inst.src[0])};
const T data = [&] {
if (op == AtomicOp::Inc || op == AtomicOp::Dec) {
return T{};
}
if constexpr (std::is_same_v<T, IR::U32>) {
return GetSrc(inst.src[1]);
} else {
return GetSrc64(inst.src[1]);
}
}();
const IR::U32 offset =
ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0));
const IR::U32 addr_offset = ir.IAdd(addr, offset);
const T original_val = [&] -> T {
switch (op) {
case AtomicOp::Add:
return ir.SharedAtomicIAdd(addr_offset, data, is_gds);
case AtomicOp::Umin:
return ir.SharedAtomicIMin(addr_offset, data, false, is_gds);
case AtomicOp::Smin:
return ir.SharedAtomicIMin(addr_offset, data, true, is_gds);
case AtomicOp::Umax:
return ir.SharedAtomicIMax(addr_offset, data, false, is_gds);
case AtomicOp::Smax:
return ir.SharedAtomicIMax(addr_offset, data, true, is_gds);
case AtomicOp::And:
return ir.SharedAtomicAnd(addr_offset, data, is_gds);
case AtomicOp::Or:
return ir.SharedAtomicOr(addr_offset, data, is_gds);
case AtomicOp::Xor:
return ir.SharedAtomicXor(addr_offset, data, is_gds);
case AtomicOp::Sub:
return ir.SharedAtomicISub(addr_offset, data, is_gds);
case AtomicOp::Inc:
return ir.SharedAtomicInc<T>(addr_offset, is_gds);
case AtomicOp::Dec:
return ir.SharedAtomicDec<T>(addr_offset, is_gds);
default:
UNREACHABLE();
}
}();
if (rtn) {
if constexpr (std::is_same_v<T, IR::U32>) {
SetDst(inst.dst[0], original_val);
} else {
SetDst64(inst.dst[0], original_val);
}
}
}
void Translator::DS_CMPST(int bit_size, bool rtn, const GcnInst& inst) {
const bool is_gds = inst.control.ds.gds;
const IR::U32 addr{GetSrc(inst.src[0])};
const IR::U32 offset =
ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0));
const IR::U32 addr_offset = ir.IAdd(addr, offset);
if (bit_size == 64) {
const IR::U64 cmp_value{GetSrc64(inst.src[1])};
const IR::U64 write_value{GetSrc64(inst.src[2])};
const IR::U64 original_val =
ir.SharedAtomicCmpSwap(addr_offset, write_value, cmp_value, is_gds);
if (rtn) {
SetDst64(inst.dst[0], original_val);
}
} else {
const IR::U32 cmp_value{GetSrc(inst.src[1])};
const IR::U32 write_value{GetSrc(inst.src[2])};
const IR::U32 original_val =
ir.SharedAtomicCmpSwap(addr_offset, write_value, cmp_value, is_gds);
if (rtn) {
SetDst(inst.dst[0], original_val);
}
}
}
void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool stride64,
const GcnInst& inst) {
const bool is_gds = inst.control.ds.gds;
const IR::U32 addr{ir.GetVectorReg(IR::VectorReg(inst.src[0].code))};
const IR::VectorReg data0{inst.src[1].code};
const IR::VectorReg data1{inst.src[2].code};
const u32 offset = (inst.control.ds.offset1 << 8u) + inst.control.ds.offset0;
if (info.hw_stage == HwStage::Fragment) {
ASSERT_MSG(!is_pair && bit_size == 32 && offset % 256 == 0,
"Unexpected shared memory offset alignment: {}", offset);
ir.SetVectorReg(GetScratchVgpr(offset), ir.GetVectorReg(data0));
return;
}
if (is_pair) {
const u32 adj = (bit_size == 32 ? 4 : 8) * (stride64 ? 64 : 1);
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0 * adj)));
if (bit_size == 64) {
ir.WriteShared(64,
ir.PackUint2x32(ir.CompositeConstruct(ir.GetVectorReg(data0),
ir.GetVectorReg(data0 + 1))),
addr0, is_gds);
} else if (bit_size == 32) {
ir.WriteShared(32, ir.GetVectorReg(data0), addr0, is_gds);
} else if (bit_size == 16) {
ir.WriteShared(16, ir.UConvert(16, ir.GetVectorReg(data0)), addr0, is_gds);
}
const IR::U32 addr1 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset1 * adj)));
if (bit_size == 64) {
ir.WriteShared(64,
ir.PackUint2x32(ir.CompositeConstruct(ir.GetVectorReg(data1),
ir.GetVectorReg(data1 + 1))),
addr1, is_gds);
} else if (bit_size == 32) {
ir.WriteShared(32, ir.GetVectorReg(data1), addr1, is_gds);
} else if (bit_size == 16) {
ir.WriteShared(16, ir.UConvert(16, ir.GetVectorReg(data1)), addr1, is_gds);
}
} else {
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(offset));
if (bit_size == 64) {
const IR::Value data =
ir.CompositeConstruct(ir.GetVectorReg(data0), ir.GetVectorReg(data0 + 1));
ir.WriteShared(bit_size, ir.PackUint2x32(data), addr0, is_gds);
} else if (bit_size == 32) {
ir.WriteShared(bit_size, ir.GetVectorReg(data0), addr0, is_gds);
} else if (bit_size == 16) {
ir.WriteShared(bit_size, ir.UConvert(16, ir.GetVectorReg(data0)), addr0, is_gds);
}
}
}
void Translator::DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride64,
const GcnInst& inst) {
const bool is_gds = inst.control.ds.gds;
const IR::U32 addr{ir.GetVectorReg(IR::VectorReg(inst.src[0].code))};
IR::VectorReg dst_reg{inst.dst[0].code};
const u32 offset = (inst.control.ds.offset1 << 8u) + inst.control.ds.offset0;
if (info.hw_stage == HwStage::Fragment) {
ASSERT_MSG(!is_pair && bit_size == 32 && offset % 256 == 0,
"Unexpected shared memory offset alignment: {}", offset);
ir.SetVectorReg(dst_reg, ir.GetVectorReg(GetScratchVgpr(offset)));
return;
}
if (is_pair) {
// Pair loads are either 32 or 64-bit
const u32 adj = (bit_size == 32 ? 4 : 8) * (stride64 ? 64 : 1);
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0 * adj)));
const IR::Value data0 = ir.LoadShared(bit_size, is_signed, addr0, is_gds);
if (bit_size == 64) {
const auto vector = ir.UnpackUint2x32(IR::U64{data0});
ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(vector, 0)});
ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(vector, 1)});
} else if (bit_size == 32) {
ir.SetVectorReg(dst_reg++, IR::U32{data0});
} else if (bit_size == 16) {
ir.SetVectorReg(dst_reg++, IR::U32{ir.UConvert(32, IR::U16{data0})});
}
const IR::U32 addr1 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset1 * adj)));
const IR::Value data1 = ir.LoadShared(bit_size, is_signed, addr1, is_gds);
if (bit_size == 64) {
const auto vector = ir.UnpackUint2x32(IR::U64{data1});
ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(vector, 0)});
ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(vector, 1)});
} else if (bit_size == 32) {
ir.SetVectorReg(dst_reg++, IR::U32{data1});
} else if (bit_size == 16) {
ir.SetVectorReg(dst_reg++, IR::U32{ir.UConvert(32, IR::U16{data1})});
}
} else {
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(offset));
const IR::Value data = ir.LoadShared(bit_size, is_signed, addr0, is_gds);
if (bit_size == 64) {
const auto vector = ir.UnpackUint2x32(IR::U64{data});
ir.SetVectorReg(dst_reg, IR::U32{ir.CompositeExtract(vector, 0)});
ir.SetVectorReg(dst_reg + 1, IR::U32{ir.CompositeExtract(vector, 1)});
} else if (bit_size == 32) {
ir.SetVectorReg(dst_reg, IR::U32{data});
} else if (bit_size == 16) {
ir.SetVectorReg(dst_reg++, IR::U32{ir.UConvert(32, IR::U16{data})});
}
}
}
void Translator::DS_SWIZZLE_B32(const GcnInst& inst) {
const u8 offset0 = inst.control.ds.offset0;
const u8 offset1 = inst.control.ds.offset1;
const IR::U32 src{GetSrc(inst.src[0])};
if (offset1 & 0x80) {
if (offset0 == 0x0 || offset0 == 0x55 || offset0 == 0xAA || offset0 == 0xFF) {
SetDst(inst.dst[0], ir.QuadBroadcast(src, ir.Imm32(offset0 & 3)));
} else {
const IR::U32 lane_id = ir.LaneId();
const IR::U32 id_in_quad = ir.BitwiseAnd(lane_id, ir.Imm32(3));
const IR::U32 base = ir.ShiftLeftLogical(id_in_quad, ir.Imm32(1));
const IR::U32 sel = ir.BitFieldExtract(ir.Imm32(offset0), base, ir.Imm32(2));
const IR::U32 quad_base = ir.BitwiseAnd(lane_id, ir.Imm32(~3u));
SetDst(inst.dst[0], ir.Shuffle(src, ir.BitwiseOr(quad_base, sel)));
}
} else {
const u8 and_mask = (offset0 & 0x1f) | (~u8{0} << 5);
const u8 or_mask = (offset0 >> 5) | ((offset1 & 0x3) << 3);
const u8 xor_mask = offset1 >> 2;
if (or_mask == 0u && and_mask == 255u) {
SetDst(inst.dst[0], ir.ShuffleXor(src, ir.Imm32(xor_mask)));
} else {
const IR::U32 lane_id = ir.LaneId();
const IR::U32 local = ir.BitwiseAnd(lane_id, ir.Imm32(31u));
const IR::U32 half = ir.BitwiseAnd(lane_id, ir.Imm32(~31u));
const IR::U32 index = ir.BitwiseXor(
ir.BitwiseOr(ir.BitwiseAnd(local, ir.Imm32(and_mask)), ir.Imm32(or_mask)),
ir.Imm32(xor_mask));
SetDst(inst.dst[0], ir.Shuffle(src, ir.BitwiseOr(index, half)));
}
}
}
void Translator::DS_APPEND(const GcnInst& inst) {
const u32 inst_offset = (u32(inst.control.ds.offset1) << 8u) + inst.control.ds.offset0;
const IR::U32 base = ir.BitFieldExtract(ir.GetM0(), ir.Imm32(16), ir.Imm32(16));
const IR::U32 gds_offset = ir.IAdd(base, ir.Imm32(inst_offset));
const IR::U32 prev = ir.DataAppend(ir.ShiftRightLogical(gds_offset, ir.Imm32(2u)));
SetDst(inst.dst[0], prev);
}
void Translator::DS_CONSUME(const GcnInst& inst) {
const u32 inst_offset = (u32(inst.control.ds.offset1) << 8u) + inst.control.ds.offset0;
const IR::U32 base = ir.BitFieldExtract(ir.GetM0(), ir.Imm32(16), ir.Imm32(16));
const IR::U32 gds_offset = ir.IAdd(base, ir.Imm32(inst_offset));
const IR::U32 prev = ir.DataConsume(ir.ShiftRightLogical(gds_offset, ir.Imm32(2u)));
SetDst(inst.dst[0], prev);
}
} // namespace Shader::Gcn
@@ -1,696 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <magic_enum/magic_enum.hpp>
#include "common/assert.h"
#include "shader_recompiler/frontend/translate/translate.h"
namespace Shader::Gcn {
void Translator::EmitScalarAlu(const GcnInst& inst) {
switch (inst.encoding) {
case InstEncoding::SOPC: {
EmitSOPC(inst);
break;
}
case InstEncoding::SOPK: {
EmitSOPK(inst);
break;
}
default:
switch (inst.opcode) {
// SOP2
case Opcode::S_ADD_U32:
return S_ADD_U32(inst);
case Opcode::S_SUB_U32:
return S_SUB_U32(inst);
case Opcode::S_ADD_I32:
return S_ADD_I32(inst);
case Opcode::S_SUB_I32:
return S_SUB_I32(inst);
case Opcode::S_ADDC_U32:
return S_ADDC_U32(inst);
case Opcode::S_SUBB_U32:
return S_SUBB_U32(inst);
case Opcode::S_MIN_I32:
return S_MIN_U32(true, inst);
case Opcode::S_MIN_U32:
return S_MIN_U32(false, inst);
case Opcode::S_MAX_I32:
return S_MAX_U32(true, inst);
case Opcode::S_MAX_U32:
return S_MAX_U32(false, inst);
case Opcode::S_CSELECT_B32:
return S_CSELECT_B32(inst);
case Opcode::S_CSELECT_B64:
return S_CSELECT_B64(inst);
case Opcode::S_AND_B32:
return S_AND_B32(NegateMode::None, inst);
case Opcode::S_AND_B64:
return S_AND_B64(NegateMode::None, inst);
case Opcode::S_OR_B32:
return S_OR_B32(inst);
case Opcode::S_OR_B64:
return S_OR_B64(NegateMode::None, false, inst);
case Opcode::S_XOR_B32:
return S_XOR_B32(inst);
case Opcode::S_NOT_B32:
return S_NOT_B32(inst);
case Opcode::S_XOR_B64:
return S_OR_B64(NegateMode::None, true, inst);
case Opcode::S_ANDN2_B32:
return S_AND_B32(NegateMode::Src1, inst);
case Opcode::S_ANDN2_B64:
return S_AND_B64(NegateMode::Src1, inst);
case Opcode::S_ORN2_B64:
return S_OR_B64(NegateMode::Src1, false, inst);
case Opcode::S_NAND_B32:
return S_AND_B32(NegateMode::Result, inst);
case Opcode::S_NAND_B64:
return S_AND_B64(NegateMode::Result, inst);
case Opcode::S_NOR_B64:
return S_OR_B64(NegateMode::Result, false, inst);
case Opcode::S_XNOR_B64:
return S_OR_B64(NegateMode::Result, true, inst);
case Opcode::S_LSHL_B32:
return S_LSHL_B32(inst);
case Opcode::S_LSHL_B64:
return S_LSHL_B64(inst);
case Opcode::S_LSHR_B32:
return S_LSHR_B32(inst);
case Opcode::S_ASHR_I32:
return S_ASHR_I32(inst);
case Opcode::S_ASHR_I64:
return S_ASHR_I64(inst);
case Opcode::S_BFM_B32:
return S_BFM_B32(inst);
case Opcode::S_BFM_B64:
return S_BFM_B64(inst);
case Opcode::S_MUL_I32:
return S_MUL_I32(inst);
case Opcode::S_BFE_I32:
return S_BFE(inst, true);
case Opcode::S_BFE_U32:
return S_BFE(inst, false);
case Opcode::S_ABSDIFF_I32:
return S_ABSDIFF_I32(inst);
// SOP1
case Opcode::S_MOV_B32:
return S_MOV(inst);
case Opcode::S_MOV_B64:
return S_MOV_B64(inst);
case Opcode::S_NOT_B64:
return S_NOT_B64(inst);
case Opcode::S_WQM_B64:
break;
case Opcode::S_BREV_B32:
return S_BREV_B32(inst);
case Opcode::S_BCNT1_I32_B32:
return S_BCNT1_I32_B32(inst);
case Opcode::S_BCNT1_I32_B64:
return S_BCNT1_I32_B64(inst);
case Opcode::S_FF1_I32_B32:
return S_FF1_I32_B32(inst);
case Opcode::S_FF1_I32_B64:
return S_FF1_I32_B64(inst);
case Opcode::S_FLBIT_I32_B32:
return S_FLBIT_I32_B32(inst);
case Opcode::S_FLBIT_I32_B64:
return S_FLBIT_I32_B64(inst);
case Opcode::S_BITSET0_B32:
return S_BITSET_B32(inst, 0);
case Opcode::S_BITSET1_B32:
return S_BITSET_B32(inst, 1);
case Opcode::S_BITSET0_B64:
return S_BITSET_B64(inst, 0);
case Opcode::S_BITSET1_B64:
return S_BITSET_B64(inst, 1);
case Opcode::S_AND_SAVEEXEC_B64:
return S_SAVEEXEC_B64(NegateMode::None, false, inst);
case Opcode::S_ORN2_SAVEEXEC_B64:
return S_SAVEEXEC_B64(NegateMode::Src1, true, inst);
case Opcode::S_ABS_I32:
return S_ABS_I32(inst);
default:
LogMissingOpcode(inst);
}
break;
}
}
void Translator::EmitSOPC(const GcnInst& inst) {
switch (inst.opcode) {
case Opcode::S_CMP_EQ_I32:
return S_CMP(ConditionOp::EQ, true, inst);
case Opcode::S_CMP_LG_I32:
return S_CMP(ConditionOp::LG, true, inst);
case Opcode::S_CMP_GT_I32:
return S_CMP(ConditionOp::GT, true, inst);
case Opcode::S_CMP_GE_I32:
return S_CMP(ConditionOp::GE, true, inst);
case Opcode::S_CMP_LT_I32:
return S_CMP(ConditionOp::LT, true, inst);
case Opcode::S_CMP_LE_I32:
return S_CMP(ConditionOp::LE, true, inst);
case Opcode::S_CMP_EQ_U32:
return S_CMP(ConditionOp::EQ, false, inst);
case Opcode::S_CMP_LG_U32:
return S_CMP(ConditionOp::LG, false, inst);
case Opcode::S_CMP_GT_U32:
return S_CMP(ConditionOp::GT, false, inst);
case Opcode::S_CMP_GE_U32:
return S_CMP(ConditionOp::GE, false, inst);
case Opcode::S_CMP_LT_U32:
return S_CMP(ConditionOp::LT, false, inst);
case Opcode::S_CMP_LE_U32:
return S_CMP(ConditionOp::LE, false, inst);
case Opcode::S_BITCMP0_B32:
return S_BITCMP(false, 32, inst);
case Opcode::S_BITCMP1_B32:
return S_BITCMP(true, 32, inst);
case Opcode::S_BITCMP0_B64:
return S_BITCMP(false, 64, inst);
case Opcode::S_BITCMP1_B64:
return S_BITCMP(true, 64, inst);
default:
LogMissingOpcode(inst);
}
}
void Translator::EmitSOPK(const GcnInst& inst) {
switch (inst.opcode) {
// SOPK
case Opcode::S_MOVK_I32:
return S_MOVK(inst, false);
case Opcode::S_CMOVK_I32:
return S_MOVK(inst, true);
case Opcode::S_CMPK_EQ_I32:
return S_CMPK(ConditionOp::EQ, true, inst);
case Opcode::S_CMPK_LG_I32:
return S_CMPK(ConditionOp::LG, true, inst);
case Opcode::S_CMPK_GT_I32:
return S_CMPK(ConditionOp::GT, true, inst);
case Opcode::S_CMPK_GE_I32:
return S_CMPK(ConditionOp::GE, true, inst);
case Opcode::S_CMPK_LT_I32:
return S_CMPK(ConditionOp::LT, true, inst);
case Opcode::S_CMPK_LE_I32:
return S_CMPK(ConditionOp::LE, true, inst);
case Opcode::S_CMPK_EQ_U32:
return S_CMPK(ConditionOp::EQ, false, inst);
case Opcode::S_CMPK_LG_U32:
return S_CMPK(ConditionOp::LG, false, inst);
case Opcode::S_CMPK_GT_U32:
return S_CMPK(ConditionOp::GT, false, inst);
case Opcode::S_CMPK_GE_U32:
return S_CMPK(ConditionOp::GE, false, inst);
case Opcode::S_CMPK_LT_U32:
return S_CMPK(ConditionOp::LT, false, inst);
case Opcode::S_CMPK_LE_U32:
return S_CMPK(ConditionOp::LE, false, inst);
case Opcode::S_ADDK_I32:
return S_ADDK_I32(inst);
case Opcode::S_MULK_I32:
return S_MULK_I32(inst);
default:
LogMissingOpcode(inst);
}
}
// SOP2
void Translator::S_ADD_U32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 result{ir.IAdd(src0, src1)};
SetDst(inst.dst[0], result);
// SCC = tmp >= 0x100000000ULL ? 1'1U : 1'0U;
// The above assumes tmp is a 64-bit value.
// It should be enough however to test that the truncated result is less than at least one
// of the operands. In unsigned addition the result is always bigger than both the operands,
// except in the case of overflow where the truncated result is less than both.
ir.SetScc(ir.ILessThan(result, src0, false));
}
void Translator::S_SUB_U32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
SetDst(inst.dst[0], ir.ISub(src0, src1));
// SCC = S1.u > S0.u ? 1'1U : 1'0U;
ir.SetScc(ir.IGreaterThan(src1, src0, false));
}
void Translator::S_SUBB_U32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 borrow{ir.Select(ir.GetScc(), ir.Imm32(1U), ir.Imm32(0U))};
const IR::U32 difference{ir.ISub(src0, src1)};
const IR::U32 result{ir.ISub(difference, borrow)};
SetDst(inst.dst[0], result);
// SCC = (S1.u + SCC > S0.u) as a 33-bit compare.
const IR::U1 underflow{ir.IGreaterThan(src1, src0, false)};
const IR::U1 borrow_underflow{ir.IGreaterThan(borrow, difference, false)};
ir.SetScc(ir.LogicalOr(underflow, borrow_underflow));
}
void Translator::S_ADD_I32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 result{ir.IAdd(src0, src1)};
SetDst(inst.dst[0], result);
// SCC = ((S0.u[31] == S1.u[31]) && (S0.u[31] != Result.u[31]));
const IR::U32 shift{ir.Imm32(31)};
const IR::U32 sign0{ir.ShiftRightLogical(src0, shift)};
const IR::U32 sign1{ir.ShiftRightLogical(src1, shift)};
const IR::U32 signr{ir.ShiftRightLogical(result, shift)};
ir.SetScc(ir.LogicalAnd(ir.IEqual(sign0, sign1), ir.INotEqual(sign0, signr)));
}
void Translator::S_SUB_I32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 result{ir.ISub(src0, src1)};
SetDst(inst.dst[0], result);
// SCC = ((S0.u[31] != S1.u[31]) && (S0.u[31] != tmp.u[31]));
const IR::U32 shift{ir.Imm32(31)};
const IR::U32 sign0{ir.ShiftRightLogical(src0, shift)};
const IR::U32 sign1{ir.ShiftRightLogical(src1, shift)};
const IR::U32 signr{ir.ShiftRightLogical(result, shift)};
ir.SetScc(ir.LogicalAnd(ir.INotEqual(sign0, sign1), ir.INotEqual(sign0, signr)));
}
void Translator::S_ADDC_U32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 carry{ir.Select(ir.GetScc(), ir.Imm32(1U), ir.Imm32(0U))};
const IR::U32 result1{ir.IAdd(src0, src1)};
const IR::U32 result2{ir.IAdd(result1, carry)};
const IR::U1 carry_out1{ir.ILessThan(result1, src0, false)};
const IR::U1 carry_out2{ir.ILessThan(result2, result1, false)};
SetDst(inst.dst[0], result2);
ir.SetScc(ir.LogicalOr(carry_out1, carry_out2));
}
void Translator::S_MIN_U32(bool is_signed, const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 result = ir.IMin(src0, src1, is_signed);
SetDst(inst.dst[0], result);
// SCC is set only when S0 is strictly smaller, equal operands clear it.
ir.SetScc(ir.ILessThan(src0, src1, is_signed));
}
void Translator::S_MAX_U32(bool is_signed, const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 result = ir.IMax(src0, src1, is_signed);
SetDst(inst.dst[0], result);
// SCC is set only when S0 is strictly greater, equal operands clear it.
ir.SetScc(ir.IGreaterThan(src0, src1, is_signed));
}
void Translator::S_CSELECT_B32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
SetDst(inst.dst[0], IR::U32{ir.Select(ir.GetScc(), src0, src1)});
}
void Translator::S_CSELECT_B64(const GcnInst& inst) {
const IR::U64 src0{GetSrc64(inst.src[0])};
const IR::U64 src1{GetSrc64(inst.src[1])};
const IR::U64 result{ir.Select(ir.GetScc(), src0, src1)};
SetDst64(inst.dst[0], result);
}
void Translator::S_AND_B32(NegateMode negate, const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
IR::U32 src1{GetSrc(inst.src[1])};
if (negate == NegateMode::Src1) {
src1 = ir.BitwiseNot(src1);
}
IR::U32 result{ir.BitwiseAnd(src0, src1)};
if (negate == NegateMode::Result) {
result = ir.BitwiseNot(result);
}
SetDst(inst.dst[0], result);
ir.SetScc(ir.INotEqual(result, ir.Imm32(0)));
}
void Translator::S_AND_B64(NegateMode negate, const GcnInst& inst) {
const IR::U64 src0{GetSrc64(inst.src[0])};
IR::U64 src1{GetSrc64(inst.src[1])};
if (negate == NegateMode::Src1) {
src1 = ir.BitwiseNot(src1);
}
IR::U64 result = ir.BitwiseAnd(src0, src1);
if (negate == NegateMode::Result) {
result = ir.BitwiseNot(result);
}
ir.SetScc(ir.INotEqual(result, ir.Imm64(u64(0))));
SetDst64(inst.dst[0], result);
}
void Translator::S_OR_B32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 result{ir.BitwiseOr(src0, src1)};
SetDst(inst.dst[0], result);
ir.SetScc(ir.INotEqual(result, ir.Imm32(0)));
}
void Translator::S_OR_B64(NegateMode negate, bool is_xor, const GcnInst& inst) {
const IR::U64 src0{GetSrc64(inst.src[0])};
IR::U64 src1{GetSrc64(inst.src[1])};
if (negate == NegateMode::Src1) {
src1 = ir.BitwiseNot(src1);
}
IR::U64 result = is_xor ? ir.BitwiseXor(src0, src1) : ir.BitwiseOr(src0, src1);
if (negate == NegateMode::Result) {
result = ir.BitwiseNot(result);
}
ir.SetScc(ir.INotEqual(result, ir.Imm64(u64(0))));
SetDst64(inst.dst[0], result);
}
void Translator::S_XOR_B32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 result{ir.BitwiseXor(src0, src1)};
SetDst(inst.dst[0], result);
ir.SetScc(ir.INotEqual(result, ir.Imm32(0)));
}
void Translator::S_NOT_B32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 result{ir.BitwiseNot(src0)};
SetDst(inst.dst[0], result);
ir.SetScc(ir.INotEqual(result, ir.Imm32(0)));
}
void Translator::S_LSHL_B32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 result = ir.ShiftLeftLogical(src0, ir.BitwiseAnd(src1, ir.Imm32(0x1F)));
SetDst(inst.dst[0], result);
ir.SetScc(ir.INotEqual(result, ir.Imm32(0)));
}
void Translator::S_LSHL_B64(const GcnInst& inst) {
const IR::U64 src0{GetSrc64(inst.src[0])};
const IR::U64 src1{GetSrc64(inst.src[1])};
const IR::U64 result = ir.ShiftLeftLogical(src0, ir.BitwiseAnd(src1, ir.Imm64(u64(0x3F))));
SetDst64(inst.dst[0], result);
ir.SetScc(ir.INotEqual(result, ir.Imm64(u64(0))));
}
void Translator::S_LSHR_B32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 shift_amt = ir.BitwiseAnd(src1, ir.Imm32(0x1F));
const IR::U32 result = ir.ShiftRightLogical(src0, shift_amt);
SetDst(inst.dst[0], result);
ir.SetScc(ir.INotEqual(result, ir.Imm32(0)));
}
void Translator::S_ASHR_I32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 result{ir.ShiftRightArithmetic(src0, ir.BitwiseAnd(src1, ir.Imm32(0x1F)))};
SetDst(inst.dst[0], result);
ir.SetScc(ir.INotEqual(result, ir.Imm32(0)));
}
void Translator::S_ASHR_I64(const GcnInst& inst) {
const IR::U64 src0{GetSrc64(inst.src[0])};
const IR::U64 src1{GetSrc64(inst.src[1])};
const IR::U64 result{ir.ShiftRightArithmetic(src0, ir.BitwiseAnd(src1, ir.Imm64(u64(0x3F))))};
SetDst64(inst.dst[0], result);
ir.SetScc(ir.INotEqual(result, ir.Imm64(u64(0))));
}
void Translator::S_BFM_B32(const GcnInst& inst) {
const IR::U32 src0{ir.BitwiseAnd(GetSrc(inst.src[0]), ir.Imm32(0x1F))};
const IR::U32 src1{ir.BitwiseAnd(GetSrc(inst.src[1]), ir.Imm32(0x1F))};
const IR::U32 mask{ir.ISub(ir.ShiftLeftLogical(ir.Imm32(1u), src0), ir.Imm32(1))};
SetDst(inst.dst[0], ir.ShiftLeftLogical(mask, src1));
}
void Translator::S_BFM_B64(const GcnInst& inst) {
// 32-bit sources, 6-bit width and offset; SCC is not written.
const IR::U32 src0{ir.BitwiseAnd(GetSrc(inst.src[0]), ir.Imm32(0x3F))};
const IR::U32 src1{ir.BitwiseAnd(GetSrc(inst.src[1]), ir.Imm32(0x3F))};
const IR::U64 mask{ir.ISub(ir.ShiftLeftLogical(ir.Imm64(u64(1)), src0), ir.Imm64(u64(1)))};
SetDst64(inst.dst[0], ir.ShiftLeftLogical(mask, src1));
}
void Translator::S_MUL_I32(const GcnInst& inst) {
SetDst(inst.dst[0], ir.IMul(GetSrc(inst.src[0]), GetSrc(inst.src[1])));
}
void Translator::S_BFE(const GcnInst& inst, bool is_signed) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 offset{ir.BitwiseAnd(src1, ir.Imm32(0x1F))};
const IR::U32 count{ir.BitFieldExtract(src1, ir.Imm32(16), ir.Imm32(7))};
const IR::U32 result{ir.BitFieldExtract(src0, offset, count, is_signed)};
SetDst(inst.dst[0], result);
ir.SetScc(ir.INotEqual(result, ir.Imm32(0)));
}
void Translator::S_ABSDIFF_I32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 src1{GetSrc(inst.src[1])};
const IR::U32 result{ir.IAbs(ir.ISub(src0, src1))};
SetDst(inst.dst[0], result);
ir.SetScc(ir.INotEqual(result, ir.Imm32(0)));
}
// SOPK
void Translator::S_MOVK(const GcnInst& inst, bool is_conditional) {
const s16 simm16 = inst.control.sopk.simm;
// do the sign extension
const s32 simm32 = static_cast<s32>(simm16);
IR::U32 val = ir.Imm32(simm32);
if (is_conditional) {
// if !SCC its a NOP
val = IR::U32{ir.Select(ir.GetScc(), val, GetSrc(inst.dst[0]))};
}
SetDst(inst.dst[0], val);
}
void Translator::S_CMPK(ConditionOp cond, bool is_signed, const GcnInst& inst) {
const s32 simm16 = inst.control.sopk.simm;
const IR::U32 lhs = GetSrc(inst.dst[0]);
const IR::U32 rhs = ir.Imm32(simm16);
const IR::U1 result = [&] {
switch (cond) {
case ConditionOp::EQ:
return ir.IEqual(lhs, rhs);
case ConditionOp::LG:
return ir.INotEqual(lhs, rhs);
case ConditionOp::GT:
return ir.IGreaterThan(lhs, rhs, is_signed);
case ConditionOp::GE:
return ir.IGreaterThanEqual(lhs, rhs, is_signed);
case ConditionOp::LT:
return ir.ILessThan(lhs, rhs, is_signed);
case ConditionOp::LE:
return ir.ILessThanEqual(lhs, rhs, is_signed);
default:
UNREACHABLE();
}
}();
ir.SetScc(result);
}
void Translator::S_ADDK_I32(const GcnInst& inst) {
const s32 simm16 = inst.control.sopk.simm;
const IR::U32 src0{GetSrc(inst.dst[0])};
const IR::U32 src1{ir.Imm32(simm16)};
const IR::U32 result{ir.IAdd(src0, src1)};
SetDst(inst.dst[0], result);
const IR::U32 shift{ir.Imm32(31)};
const IR::U32 sign0{ir.ShiftRightLogical(src0, shift)};
const IR::U32 sign1{ir.ShiftRightLogical(src1, shift)};
const IR::U32 signr{ir.ShiftRightLogical(result, shift)};
ir.SetScc(ir.LogicalAnd(ir.IEqual(sign0, sign1), ir.INotEqual(sign0, signr)));
}
void Translator::S_MULK_I32(const GcnInst& inst) {
const s32 simm16 = inst.control.sopk.simm;
SetDst(inst.dst[0], ir.IMul(GetSrc(inst.dst[0]), ir.Imm32(simm16)));
}
// SOP1
void Translator::S_MOV(const GcnInst& inst) {
SetDst(inst.dst[0], GetSrc(inst.src[0]));
}
void Translator::S_MOV_B64(const GcnInst& inst) {
SetDst64(inst.dst[0], GetSrc64(inst.src[0]));
}
void Translator::S_NOT_B64(const GcnInst& inst) {
const IR::U64 src0{GetSrc64(inst.src[0])};
const IR::U64 result = ir.BitwiseNot(src0);
ir.SetScc(ir.INotEqual(result, ir.Imm64(u64(0))));
SetDst64(inst.dst[0], result);
}
void Translator::S_BREV_B32(const GcnInst& inst) {
SetDst(inst.dst[0], ir.BitReverse(GetSrc(inst.src[0])));
}
void Translator::S_BCNT1_I32_B32(const GcnInst& inst) {
const IR::U32 result = ir.BitCount(GetSrc(inst.src[0]));
SetDst(inst.dst[0], result);
ir.SetScc(ir.INotEqual(result, ir.Imm32(0)));
}
void Translator::S_BCNT1_I32_B64(const GcnInst& inst) {
const IR::U32 result = ir.BitCount(GetSrc64(inst.src[0]));
SetDst(inst.dst[0], result);
ir.SetScc(ir.INotEqual(result, ir.Imm32(0)));
}
void Translator::S_FF1_I32_B32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
const IR::U32 result{ir.FindILsb(src0)};
SetDst(inst.dst[0], result);
}
void Translator::S_FF1_I32_B64(const GcnInst& inst) {
// Integer find-first-one over all 64 bits, -1 when none is set.
const IR::U64 src0{GetSrc64(inst.src[0])};
SetDst(inst.dst[0], ir.FindILsb(src0));
}
void Translator::S_FLBIT_I32_B32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
// Gcn wants the MSB position counting from the left, but SPIR-V counts from the rightmost (LSB)
// position
const IR::U32 msb_pos = ir.FindUMsb(src0);
const IR::U32 pos_from_left = ir.ISub(ir.Imm32(31), msb_pos);
// Select 0xFFFFFFFF if src0 was 0
const IR::U1 cond = ir.INotEqual(src0, ir.Imm32(0));
SetDst(inst.dst[0], IR::U32{ir.Select(cond, pos_from_left, ir.Imm32(~0U))});
}
void Translator::S_FLBIT_I32_B64(const GcnInst& inst) {
const IR::U64 src0{GetSrc64(inst.src[0])};
// Gcn wants the MSB position counting from the left, but SPIR-V counts from the rightmost (LSB)
// position
const IR::U32 msb_pos = ir.FindUMsb(src0);
const IR::U32 pos_from_left = ir.ISub(ir.Imm32(63), msb_pos);
// Select 0xFFFFFFFF if src0 was 0
const IR::U1 cond = ir.INotEqual(src0, ir.Imm64(u64(0u)));
SetDst(inst.dst[0], IR::U32{ir.Select(cond, pos_from_left, ir.Imm32(~0U))});
}
void Translator::S_BITSET_B32(const GcnInst& inst, u32 bit_value) {
const IR::U32 old_value{GetSrc(inst.dst[0])};
const IR::U32 offset{ir.BitFieldExtract(GetSrc(inst.src[0]), ir.Imm32(0U), ir.Imm32(5U))};
const IR::U32 result{ir.BitFieldInsert(old_value, ir.Imm32(bit_value), offset, ir.Imm32(1U))};
SetDst(inst.dst[0], result);
}
void Translator::S_BITSET_B64(const GcnInst& inst, u32 bit_value) {
const IR::U64 old_value{GetSrc64(inst.dst[0])};
const IR::U32 offset{ir.BitwiseAnd(GetSrc(inst.src[0]), ir.Imm32(0x3F))};
const IR::U64 result{
ir.BitFieldInsert(old_value, ir.Imm64(u64(bit_value)), offset, ir.Imm32(1U))};
SetDst64(inst.dst[0], result);
}
void Translator::S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst) {
IR::U64 exec{ir.Ballot(ir.GetExec())};
const IR::U64 src{GetSrc64(inst.src[0])};
SetDst64(inst.dst[0], exec);
// Update EXEC.
if (negate == NegateMode::Src1) {
exec = ir.BitwiseNot(exec);
}
IR::U64 result = is_or ? ir.BitwiseOr(exec, src) : ir.BitwiseAnd(exec, src);
if (negate == NegateMode::Result) {
result = ir.BitwiseNot(result);
}
ir.SetExec(ir.InverseBallot(result));
ir.SetScc(ir.INotEqual(result, ir.Imm64(u64(0))));
}
void Translator::S_ABS_I32(const GcnInst& inst) {
const auto result = ir.IAbs(GetSrc(inst.src[0]));
SetDst(inst.dst[0], result);
ir.SetScc(ir.INotEqual(result, ir.Imm32(0)));
}
// SOPC
void Translator::S_CMP(ConditionOp cond, bool is_signed, const GcnInst& inst) {
const IR::U32 lhs = GetSrc(inst.src[0]);
const IR::U32 rhs = GetSrc(inst.src[1]);
const IR::U1 result = [&] {
switch (cond) {
case ConditionOp::EQ:
return ir.IEqual(lhs, rhs);
case ConditionOp::LG:
return ir.INotEqual(lhs, rhs);
case ConditionOp::GT:
return ir.IGreaterThan(lhs, rhs, is_signed);
case ConditionOp::GE:
return ir.IGreaterThanEqual(lhs, rhs, is_signed);
case ConditionOp::LT:
return ir.ILessThan(lhs, rhs, is_signed);
case ConditionOp::LE:
return ir.ILessThanEqual(lhs, rhs, is_signed);
default:
UNREACHABLE();
}
}();
ir.SetScc(result);
}
void Translator::S_BITCMP(bool compare_mode, u32 bits, const GcnInst& inst) {
const IR::U1 result = [&] {
const IR::U32 src1 = GetSrc(inst.src[1]);
const IR::U32 mask = ir.Imm32(bits == 64 ? 0x3f : 0x1f);
const IR::U32 bitpos{ir.BitwiseAnd(src1, mask)};
const IR::U32 bittest = [&]() -> IR::U32 {
if (bits == 64) {
const IR::U64 src0 = GetSrc64(inst.src[0]);
const IR::U64 bit{
ir.BitwiseAnd(ir.ShiftRightLogical(src0, bitpos), ir.Imm64(u64(1)))};
return IR::U32{ir.CompositeExtract(ir.UnpackUint2x32(bit), 0)};
}
if (bits != 32) {
UNREACHABLE();
}
const IR::U32 src0 = GetSrc(inst.src[0]);
return ir.BitwiseAnd(ir.ShiftRightLogical(src0, bitpos), ir.Imm32(1));
}();
if (!compare_mode) {
return ir.IEqual(bittest, ir.Imm32(0));
} else {
return ir.IEqual(bittest, ir.Imm32(1));
}
}();
ir.SetScc(result);
}
} // namespace Shader::Gcn
File diff suppressed because it is too large. Load diff
@@ -1,401 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <span>
#include <unordered_map>
#include "shader_recompiler/frontend/fetch_shader.h"
#include "shader_recompiler/frontend/instruction.h"
#include "shader_recompiler/info.h"
#include "shader_recompiler/ir/basic_block.h"
#include "shader_recompiler/ir/condition.h"
#include "shader_recompiler/ir/ir_emitter.h"
namespace Shader {
struct Info;
struct Profile;
} // namespace Shader
namespace Shader::Gcn {
enum class ConditionOp : u32 {
F,
EQ,
LG, // NE
GT,
GE,
LT,
LE,
TRU,
U,
};
enum class AtomicOp : u32 {
Swap,
CmpSwap,
Add,
Sub,
Smin,
Umin,
Smax,
Umax,
And,
Or,
Xor,
Inc,
Dec,
FCmpSwap,
Fmin,
Fmax,
};
enum class NegateMode : u32 {
None,
Src1,
Result,
};
template <typename T>
using pk_type = std::pair<T, T>;
static constexpr size_t MaxInterpVgpr = 16;
class Translator {
public:
explicit Translator(Info& info, const RuntimeInfo& runtime_info, const Profile& profile);
void Translate(IR::Block* block, u32 pc, IR::Condition cond,
std::span<const GcnInst> inst_list);
void TranslateInstruction(const GcnInst& inst);
// Instruction categories
void EmitPrologue(IR::Block* first_block);
void EmitFetch(const GcnInst& inst);
void EmitExport(const GcnInst& inst);
void EmitFlowControl(const GcnInst& inst);
void EmitScalarAlu(const GcnInst& inst);
void EmitScalarMemory(const GcnInst& inst);
void EmitVectorAlu(const GcnInst& inst);
void EmitVectorInterpolation(const GcnInst& inst);
void EmitDataShare(const GcnInst& inst);
void EmitVectorMemory(const GcnInst& inst);
// Instruction encodings
void EmitSOPC(const GcnInst& inst);
void EmitSOPK(const GcnInst& inst);
// Scalar ALU
// SOP2
void S_ADD_U32(const GcnInst& inst);
void S_SUB_U32(const GcnInst& inst);
void S_SUBB_U32(const GcnInst& inst);
void S_ADD_I32(const GcnInst& inst);
void S_SUB_I32(const GcnInst& inst);
void S_ADDC_U32(const GcnInst& inst);
void S_MIN_U32(bool is_signed, const GcnInst& inst);
void S_MAX_U32(bool is_signed, const GcnInst& inst);
void S_CSELECT_B32(const GcnInst& inst);
void S_CSELECT_B64(const GcnInst& inst);
void S_AND_B32(NegateMode negate, const GcnInst& inst);
void S_AND_B64(NegateMode negate, const GcnInst& inst);
void S_OR_B32(const GcnInst& inst);
void S_OR_B64(NegateMode negate, bool is_xor, const GcnInst& inst);
void S_XOR_B32(const GcnInst& inst);
void S_LSHL_B32(const GcnInst& inst);
void S_LSHL_B64(const GcnInst& inst);
void S_LSHR_B32(const GcnInst& inst);
void S_ASHR_I32(const GcnInst& inst);
void S_ASHR_I64(const GcnInst& inst);
void S_BFM_B32(const GcnInst& inst);
void S_BFM_B64(const GcnInst& inst);
void S_MUL_I32(const GcnInst& inst);
void S_BFE(const GcnInst& inst, bool is_signed);
void S_BFE_I32(const GcnInst& inst);
void S_ABSDIFF_I32(const GcnInst& inst);
void S_NOT_B32(const GcnInst& inst);
// SOPK
void S_MOVK(const GcnInst& inst, bool is_conditional);
void S_CMPK(ConditionOp cond, bool is_signed, const GcnInst& inst);
void S_ADDK_I32(const GcnInst& inst);
void S_MULK_I32(const GcnInst& inst);
// SOP1
void S_MOV(const GcnInst& inst);
void S_MOV_B64(const GcnInst& inst);
void S_NOT_B64(const GcnInst& inst);
void S_BREV_B32(const GcnInst& inst);
void S_BCNT1_I32_B32(const GcnInst& inst);
void S_BCNT1_I32_B64(const GcnInst& inst);
void S_FF1_I32_B32(const GcnInst& inst);
void S_FF1_I32_B64(const GcnInst& inst);
void S_FLBIT_I32_B32(const GcnInst& inst);
void S_FLBIT_I32_B64(const GcnInst& inst);
void S_BITSET_B32(const GcnInst& inst, u32 bit_value);
void S_BITSET_B64(const GcnInst& inst, u32 bit_value);
void S_GETPC_B64(const GcnInst& inst);
void S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst);
void S_ABS_I32(const GcnInst& inst);
// SOPC
void S_CMP(ConditionOp cond, bool is_signed, const GcnInst& inst);
void S_BITCMP(bool compare_mode, u32 bits, const GcnInst& inst);
// SOPP
void S_BARRIER();
void S_SENDMSG(const GcnInst& inst);
// Scalar Memory
// SMRD
void S_LOAD_DWORD(int num_dwords, const GcnInst& inst);
void S_BUFFER_LOAD_DWORD(int num_dwords, const GcnInst& inst);
void S_MEMTIME(const GcnInst& inst);
// Vector ALU
// VOP2
void V_CNDMASK_B32(const GcnInst& inst);
void V_READLANE_B32(const GcnInst& inst);
void V_WRITELANE_B32(const GcnInst& inst);
void V_ADD_F32(const GcnInst& inst);
void V_ADD_F64(const GcnInst& inst);
void V_SUB_F32(const GcnInst& inst);
void V_SUBREV_F32(const GcnInst& inst);
void V_MUL_F32(const GcnInst& inst);
void V_MUL_LEGACY_F32(const GcnInst& inst);
void V_MUL_I32_I24(const GcnInst& inst, bool is_signed);
void V_MIN_F32(const GcnInst& inst, bool is_legacy = false);
void V_MAX_F32(const GcnInst& inst, bool is_legacy = false);
void V_MIN_I32(const GcnInst& inst);
void V_MIN_U32(const GcnInst& inst);
void V_MAX_U32(bool is_signed, const GcnInst& inst);
void V_LSHR_B32(const GcnInst& inst);
void V_LSHRREV_B32(const GcnInst& inst);
void V_ASHR_I32(const GcnInst& inst);
void V_ASHRREV_I32(const GcnInst& inst);
void V_LSHL_B32(const GcnInst& inst);
void V_LSHLREV_B32(const GcnInst& inst);
void V_AND_B32(const GcnInst& inst);
void V_OR_B32(bool is_xor, const GcnInst& inst);
void V_BFM_B32(const GcnInst& inst);
void V_MAC_F32(const GcnInst& inst);
void V_MADMK_F32(const GcnInst& inst);
void V_BCNT_U32_B32(const GcnInst& inst);
void V_MBCNT_U32_B32(bool hi, const GcnInst& inst);
void V_ADD_I32(const GcnInst& inst);
void V_SUB_I32(const GcnInst& inst);
void V_SUBREV_I32(const GcnInst& inst);
void V_ADDC_U32(const GcnInst& inst);
void V_SUBB_U32(const GcnInst& inst);
void V_SUBBREV_U32(const GcnInst& inst);
void V_LDEXP_F32(const GcnInst& inst);
void V_CVT_PKNORM_U16_F32(const GcnInst& inst);
void V_CVT_PKNORM_I16_F32(const GcnInst& inst);
void V_CVT_PKRTZ_F16_F32(const GcnInst& inst);
void V_ADD_F16(const GcnInst& inst);
void V_SUB_F16(const GcnInst& inst);
void V_MUL_F16(const GcnInst& inst);
void V_MAX_F16(const GcnInst& inst);
void V_MIN_F16(const GcnInst& inst);
// VOP1
void V_MOV(const GcnInst& inst);
void V_READFIRSTLANE_B32(const GcnInst& inst);
void V_CVT_I32_F64(const GcnInst& inst);
void V_CVT_F64_I32(const GcnInst& inst);
void V_CVT_F64_U32(const GcnInst& inst);
void V_CVT_F32_I32(const GcnInst& inst);
void V_CVT_F32_U32(const GcnInst& inst);
void V_CVT_U32_F32(const GcnInst& inst);
void V_CVT_I32_F32(const GcnInst& inst);
void V_CVT_F16_F32(const GcnInst& inst);
void V_CVT_F32_F16(const GcnInst& inst);
void V_CVT_RPI_I32_F32(const GcnInst& inst);
void V_CVT_FLR_I32_F32(const GcnInst& inst);
void V_CVT_OFF_F32_I4(const GcnInst& inst);
void V_CVT_F32_F64(const GcnInst& inst);
void V_CVT_F64_F32(const GcnInst& inst);
void V_CVT_F32_UBYTE(u32 index, const GcnInst& inst);
void V_TRUNC_F64(const GcnInst& inst);
void V_FLOOR_F64(const GcnInst& inst);
void V_FRACT_F32(const GcnInst& inst);
void V_TRUNC_F32(const GcnInst& inst);
void V_CEIL_F32(const GcnInst& inst);
void V_RNDNE_F32(const GcnInst& inst);
void V_FLOOR_F32(const GcnInst& inst);
void V_EXP_F32(const GcnInst& inst);
void V_LOG_F32(const GcnInst& inst);
void V_RCP_F32(const GcnInst& inst);
void V_RCP_LEGACY_F32(const GcnInst& inst);
void V_RCP_F64(const GcnInst& inst);
void V_RSQ_F32(const GcnInst& inst);
void V_SQRT_F32(const GcnInst& inst);
void V_SIN_F32(const GcnInst& inst);
void V_COS_F32(const GcnInst& inst);
void V_NOT_B32(const GcnInst& inst);
void V_BFREV_B32(const GcnInst& inst);
void V_FFBH_U32(const GcnInst& inst);
void V_FFBH_I32(const GcnInst& inst);
void V_FFBL_B32(const GcnInst& inst);
void V_FREXP_EXP_I32_F64(const GcnInst& inst);
void V_FREXP_MANT_F64(const GcnInst& inst);
void V_FRACT_F64(const GcnInst& inst);
void V_FREXP_EXP_I32_F32(const GcnInst& inst);
void V_FREXP_MANT_F32(const GcnInst& inst);
void V_MOVRELD_B32(const GcnInst& inst);
void V_MOVRELS_B32(const GcnInst& inst);
void V_MOVRELSD_B32(const GcnInst& inst);
// VOPC
void V_CMP_F32(ConditionOp op, bool set_exec, const GcnInst& inst);
void V_CMP_F64(ConditionOp op, bool set_exec, const GcnInst& inst);
void V_CMP_U32(ConditionOp op, bool is_signed, bool set_exec, const GcnInst& inst);
void V_CMP_U64(ConditionOp op, bool is_signed, bool set_exec, const GcnInst& inst);
void V_CMP_CLASS_F32(const GcnInst& inst);
// VOP3a
void V_MAD_F32(const GcnInst& inst);
void V_MAD_I32_I24(const GcnInst& inst, bool is_signed = true);
void V_MAD_U32_U24(const GcnInst& inst);
void V_CUBEID_F32(const GcnInst& inst);
void V_CUBESC_F32(const GcnInst& inst);
void V_CUBETC_F32(const GcnInst& inst);
void V_CUBEMA_F32(const GcnInst& inst);
void V_BFE_U32(bool is_signed, const GcnInst& inst);
void V_BFI_B32(const GcnInst& inst);
void V_FMA_F32(const GcnInst& inst);
void V_FMA_F64(const GcnInst& inst);
void V_MIN3_F32(const GcnInst& inst);
void V_MIN3_U32(bool is_signed, const GcnInst& inst);
void V_MAX3_F32(const GcnInst& inst);
void V_MAX3_U32(bool is_signed, const GcnInst& inst);
void V_MED3_F32(const GcnInst& inst);
void V_MED3_U32(bool is_signed, const GcnInst& inst);
void V_SAD(const GcnInst& inst);
void V_SAD_U32(const GcnInst& inst);
void V_CVT_PK_U16_U32(const GcnInst& inst);
void V_CVT_PK_I16_I32(const GcnInst& inst);
void V_CVT_PK_U8_F32(const GcnInst& inst);
void V_LSHL_B64(const GcnInst& inst);
void V_LSHR_B64(const GcnInst& inst);
void V_ASHR_I64(const GcnInst& inst);
void V_ALIGNBIT_B32(const GcnInst& inst);
void V_ALIGNBYTE_B32(const GcnInst& inst);
void V_MUL_F64(const GcnInst& inst);
void V_MIN_F64(const GcnInst& inst);
void V_MAX_F64(const GcnInst& inst);
void V_MUL_LO_U32(const GcnInst& inst);
void V_MUL_HI_U32(bool is_signed, const GcnInst& inst);
void V_MAD_U64_U32(const GcnInst& inst);
void V_LSHRREV_B16(const GcnInst& inst);
void V_ASHRREV_I16(const GcnInst& inst);
void V_LSHLREV_B16(const GcnInst& inst);
void V_LSHL_ADD_U32(const GcnInst& inst);
void V_ADD_LSHL_U32(const GcnInst& inst);
void V_MIN3_F16(const GcnInst& inst);
void V_MAX3_F16(const GcnInst& inst);
void V_MED3_F16(const GcnInst& inst);
void V_ADD3_U32(const GcnInst& inst);
void V_LSHL_OR_B32(const GcnInst& inst);
void V_AND_OR_B32(const GcnInst& inst);
void V_OR3_B32(const GcnInst& inst);
// VOP3P
void V_PK_MUL_LO_U16(const GcnInst& inst);
void V_PK_ADD_I16(const GcnInst& inst);
void V_PK_SUB_I16(const GcnInst& inst);
void V_PK_LSHLREV_B16(const GcnInst& inst);
void V_PK_LSHRREV_B16(const GcnInst& inst);
void V_PK_MAD_U16(const GcnInst& inst);
void V_PK_ADD_U16(const GcnInst& inst);
void V_PK_SUB_U16(const GcnInst& inst);
void V_PK_MAX_U16(const GcnInst& inst);
void V_PK_MIN_U16(const GcnInst& inst);
void V_PK_FMA_F16(const GcnInst& inst);
void V_PK_ADD_F16(const GcnInst& inst);
void V_PK_MUL_F16(const GcnInst& inst);
void V_PK_MIN_F16(const GcnInst& inst);
void V_PK_MAX_F16(const GcnInst& inst);
void V_MAD_MIX_F32(const GcnInst& inst);
void V_MAD_MIXLO_F16(const GcnInst& inst);
void V_MAD_MIXHI_F16(const GcnInst& inst);
// Vector interpolation
// VINTRP
void V_INTERP_P1_F32(const GcnInst& inst);
void V_INTERP_P2_F32(const GcnInst& inst);
void V_INTERP_MOV_F32(const GcnInst& inst);
// Data share
// DS
template <typename T = IR::U32>
void DS_OP(const GcnInst& inst, AtomicOp op, bool rtn);
void DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool stride64, const GcnInst& inst);
void DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride64, const GcnInst& inst);
void DS_SWIZZLE_B32(const GcnInst& inst);
void DS_APPEND(const GcnInst& inst);
void DS_CONSUME(const GcnInst& inst);
void DS_CMPST(int bit_size, bool rtn, const GcnInst& inst);
// Buffer Memory
// MUBUF / MTBUF
void BUFFER_LOAD(u32 num_dwords, bool is_inst_typed, bool is_buffer_typed, const GcnInst& inst,
u32 scalar_width = 32, bool is_signed = false);
void BUFFER_STORE(u32 num_dwords, bool is_inst_typed, bool is_buffer_typed, const GcnInst& inst,
u32 scalar_width = 32);
template <typename T = IR::U32>
void BUFFER_ATOMIC(AtomicOp op, const GcnInst& inst);
// Image Memory
// MIMG
void IMAGE_LOAD(bool has_mip, const GcnInst& inst);
void IMAGE_STORE(bool has_mip, const GcnInst& inst);
void IMAGE_GET_RESINFO(const GcnInst& inst);
void IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst);
void IMAGE_SAMPLE(const GcnInst& inst);
void IMAGE_GATHER(const GcnInst& inst);
void IMAGE_GET_LOD(const GcnInst& inst);
private:
template <typename T = IR::U32>
[[nodiscard]] T GetSrc(const InstOperand& operand);
template <typename T = IR::U32, bool is_signed = false>
[[nodiscard]] T GetSrc16(const InstOperand& operand);
template <typename T = IR::U64>
[[nodiscard]] T GetSrc64(const InstOperand& operand);
[[nodiscard]] IR::F32 GetSrcMix(const InstOperand& operand);
template <typename T = IR::U32, bool is_signed = false>
[[nodiscard]] pk_type<T> GetSrcPk(const InstOperand& operand);
void SetDst(const InstOperand& operand, const IR::U32F32& value);
template <bool is_signed = false>
void SetDst16(const InstOperand& operand, const IR::U32F32& value);
void SetDst64(const InstOperand& operand, const IR::U64F64& value_raw);
template <typename T = IR::U32, bool is_signed = false>
void SetDstPk(const InstOperand& operand, const pk_type<T>& value);
// Vector ALU Helpers
IR::U32 GetCarryIn(const GcnInst& inst);
void SetCarryOut(const GcnInst& inst, const IR::U1& carry);
IR::U32 VMovRelSHelper(u32 src_vgprno, const IR::U32 m0);
void VMovRelDHelper(u32 dst_vgprno, const IR::U32 src_val, const IR::U32 m0);
void ExportRenderTarget(const GcnInst& inst);
void ExportDepth(const GcnInst& inst);
void LogMissingOpcode(const GcnInst& inst);
IR::VectorReg GetScratchVgpr(u32 offset);
private:
IR::IREmitter ir;
Info& info;
const RuntimeInfo& runtime_info;
const Profile& profile;
u32 next_vgpr_num;
std::unordered_map<u32, IR::VectorReg> vgpr_map;
std::array<IR::Attribute, MaxInterpVgpr> vgpr_to_interp{};
FetchShaderData fetch_data{};
bool opcode_missing = false;
u32 pc{};
};
} // namespace Shader::Gcn
File diff suppressed because it is too large. Load diff
-52
View File
@@ -1,52 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <fmt/format.h>
#include "common/types.h"
namespace Shader::IR {
enum class Condition : u32 {
False,
True,
Scc0,
Scc1,
Vccz,
Vccnz,
Execz,
Execnz,
};
constexpr std::string_view NameOf(Condition condition) {
switch (condition) {
case Condition::False:
return "False";
case Condition::True:
return "True";
case Condition::Scc0:
return "Scc0";
case Condition::Scc1:
return "Scc1";
case Condition::Vccz:
return "Vccz";
case Condition::Vccnz:
return "Vccnz";
case Condition::Execz:
return "Execz";
case Condition::Execnz:
return "Execnz";
default:
UNREACHABLE();
}
}
} // namespace Shader::IR
template <>
struct fmt::formatter<Shader::IR::Condition> : formatter<std::string_view> {
auto format(const Shader::IR::Condition cond, format_context& ctx) const {
return formatter<string_view>::format(NameOf(cond), ctx);
}
};
File diff suppressed because it is too large. Load diff
-449
View File
@@ -1,449 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstring>
#include <type_traits>
#include "shader_recompiler/ir/attribute.h"
#include "shader_recompiler/ir/basic_block.h"
#include "shader_recompiler/ir/condition.h"
#include "shader_recompiler/ir/patch.h"
#include "shader_recompiler/ir/value.h"
namespace Shader::IR {
class IREmitter {
public:
explicit IREmitter() = default;
explicit IREmitter(Block& block_) : block{&block_}, insertion_point{block->end()} {}
explicit IREmitter(Block& block_, Block::iterator insertion_point_)
: block{&block_}, insertion_point{insertion_point_} {}
[[nodiscard]] U1 Imm1(bool value) const;
[[nodiscard]] U8 Imm8(u8 value) const;
[[nodiscard]] U16 Imm16(u16 value) const;
[[nodiscard]] U32 Imm32(u32 value) const;
[[nodiscard]] U32 Imm32(s32 value) const;
[[nodiscard]] F32 Imm32(f32 value) const;
[[nodiscard]] U64 Imm64(u64 value) const;
[[nodiscard]] U64 Imm64(s64 value) const;
[[nodiscard]] F64 Imm64(f64 value) const;
template <typename Dest, typename Source>
[[nodiscard]] Dest BitCast(const Source& value);
U1 ConditionRef(const U1& value);
void Prologue();
void Epilogue();
void Discard();
void Discard(const U1& cond);
void DebugPrint(const char* fmt, boost::container::small_vector<Value, 5> args);
void Barrier();
void WorkgroupMemoryBarrier();
void DeviceMemoryBarrier();
[[nodiscard]] U32 GetUserData(IR::ScalarReg reg);
template <typename T = U32>
[[nodiscard]] T GetScalarReg(IR::ScalarReg reg);
template <typename T = U32>
[[nodiscard]] T GetVectorReg(IR::VectorReg reg);
void SetScalarReg(IR::ScalarReg reg, const U32F32& value);
void SetVectorReg(IR::VectorReg reg, const U32F32& value);
[[nodiscard]] Value GetVirtualReg(IR::VirtualReg reg);
void SetVirtualReg(IR::VirtualReg reg, const Value& value);
[[nodiscard]] U1 GetGotoVariable(u32 id);
void SetGotoVariable(u32 id, const U1& value);
[[nodiscard]] U1 GetScc();
[[nodiscard]] U1 GetExec();
[[nodiscard]] U32 GetVccLo();
[[nodiscard]] U32 GetVccHi();
[[nodiscard]] U32 GetM0();
void SetScc(const U1& value);
void SetExec(const U1& value);
void SetVccLo(const U32& value);
void SetVccHi(const U32& value);
void SetM0(const U32& value);
[[nodiscard]] U1 Condition(IR::Condition cond);
[[nodiscard]] F32 GetAttribute(Attribute attribute, u32 comp = 0, u32 index = 0);
[[nodiscard]] U1 GetAttributeU1(Attribute attribute, u32 comp = 0);
[[nodiscard]] U32 GetAttributeU32(Attribute attribute, u32 comp = 0);
void SetAttribute(Attribute attribute, const F32& value, u32 comp = 0);
[[nodiscard]] F32 GetTessGenericAttribute(const U32& vertex_index, const U32& attr_index,
const U32& comp_index);
void SetTcsGenericAttribute(const F32& value, const U32& attr_index, const U32& comp_index);
[[nodiscard]] F32 ReadTcsGenericOuputAttribute(const U32& vertex_index, const U32& attr_index,
const U32& comp_index);
[[nodiscard]] U32 GetPcLo(const U32& pc);
[[nodiscard]] F32 GetPatch(Patch patch);
void SetPatch(Patch patch, const F32& value);
[[nodiscard]] Value LoadShared(int bit_size, bool is_signed, const U32& offset,
bool is_gds = false);
void WriteShared(int bit_size, const Value& value, const U32& offset, bool is_gds = false);
[[nodiscard]] U32U64 SharedAtomicIAdd(const U32& address, const U32U64& data, bool is_gds);
[[nodiscard]] U32U64 SharedAtomicISub(const U32& address, const U32U64& data, bool is_gds);
[[nodiscard]] U32U64 SharedAtomicIMin(const U32& address, const U32U64& data, bool is_signed,
bool is_gds);
[[nodiscard]] U32U64 SharedAtomicIMax(const U32& address, const U32U64& data, bool is_signed,
bool is_gds);
[[nodiscard]] U32U64 SharedAtomicAnd(const U32& address, const U32U64& data, bool is_gds);
[[nodiscard]] U32U64 SharedAtomicOr(const U32& address, const U32U64& data, bool is_gds);
[[nodiscard]] U32U64 SharedAtomicXor(const U32& address, const U32U64& data, bool is_gds);
[[nodiscard]] U32U64 SharedAtomicCmpSwap(const U32& address, const U32U64& value,
const U32U64& cmp_value, bool is_gds);
template <typename T = U32>
[[nodiscard]] T SharedAtomicInc(const U32& address, bool is_gds);
template <typename T = U32>
[[nodiscard]] T SharedAtomicDec(const U32& address, bool is_gds);
[[nodiscard]] U32 ReadConst(const Value& base, const U32& offset);
[[nodiscard]] U32 ReadConstBuffer(const Value& handle, const U32& index, BufferInstInfo info);
[[nodiscard]] U8 LoadBufferU8(const Value& handle, const Value& address, BufferInstInfo info);
[[nodiscard]] U16 LoadBufferU16(const Value& handle, const Value& address, BufferInstInfo info);
[[nodiscard]] Value LoadBufferU32(int num_dwords, const Value& handle, const Value& address,
BufferInstInfo info);
[[nodiscard]] U64 LoadBufferU64(const Value& handle, const Value& address, BufferInstInfo info);
[[nodiscard]] Value LoadBufferF32(int num_dwords, const Value& handle, const Value& address,
BufferInstInfo info);
[[nodiscard]] Value LoadBufferFormat(const Value& handle, const Value& address,
BufferInstInfo info);
void StoreBufferU8(const Value& handle, const Value& address, const U8& data,
BufferInstInfo info);
void StoreBufferU16(const Value& handle, const Value& address, const U16& data,
BufferInstInfo info);
void StoreBufferU32(int num_dwords, const Value& handle, const Value& address,
const Value& data, BufferInstInfo info);
void StoreBufferU64(const Value& handle, const Value& address, const U64& data,
BufferInstInfo info);
void StoreBufferF32(int num_dwords, const Value& handle, const Value& address,
const Value& data, BufferInstInfo info);
void StoreBufferFormat(const Value& handle, const Value& address, const Value& data,
BufferInstInfo info);
[[nodiscard]] Value BufferAtomicIAdd(const Value& handle, const Value& address,
const Value& value, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicISub(const Value& handle, const Value& address,
const Value& value, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicIMin(const Value& handle, const Value& address,
const Value& value, bool is_signed, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicFMin(const Value& handle, const Value& address,
const Value& value, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicIMax(const Value& handle, const Value& address,
const Value& value, bool is_signed, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicFMax(const Value& handle, const Value& address,
const Value& value, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicInc(const Value& handle, const Value& address,
BufferInstInfo info);
[[nodiscard]] Value BufferAtomicDec(const Value& handle, const Value& address,
BufferInstInfo info);
[[nodiscard]] Value BufferAtomicAnd(const Value& handle, const Value& address,
const Value& value, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicOr(const Value& handle, const Value& address,
const Value& value, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicXor(const Value& handle, const Value& address,
const Value& value, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicSwap(const Value& handle, const Value& address,
const Value& value, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicCmpSwap(const Value& handle, const Value& address,
const Value& value, const Value& cmp_value,
BufferInstInfo info);
[[nodiscard]] Value BufferAtomicFCmpSwap(const Value& handle, const Value& address,
const Value& value, const Value& cmp_value,
BufferInstInfo info);
[[nodiscard]] U32 DataAppend(const U32& gds_dw_offset);
[[nodiscard]] U32 DataConsume(const U32& gds_dw_offset);
[[nodiscard]] U32 LaneId();
[[nodiscard]] U32 WarpId();
[[nodiscard]] U32 QuadBroadcast(const U32& value, const U32& index);
[[nodiscard]] U32 Shuffle(const U32& value, const U32& index);
[[nodiscard]] U32 ShuffleXor(const U32& value, const U32& mask);
[[nodiscard]] U32 ReadFirstLane(const U32& value, const U1& exec);
[[nodiscard]] U32 ReadLane(const U32& value, const U32& lane);
[[nodiscard]] U32 WriteLane(const U32& value, const U32& write_value, const U32& lane);
[[nodiscard]] U64 Ballot(const U1& bit);
[[nodiscard]] U32 BallotFindLsb(const U64& mask);
[[nodiscard]] U1 InverseBallot(const U64& mask);
[[nodiscard]] U1 GroupAny(const U1& bit);
[[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2);
[[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2, const Value& e3);
[[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2, const Value& e3,
const Value& e4);
[[nodiscard]] Value CompositeConstruct(std::span<const Value> values);
[[nodiscard]] Value CompositeExtract(const Value& vector, size_t element);
[[nodiscard]] Value CompositeInsert(const Value& vector, const Value& object, size_t element);
[[nodiscard]] Value CompositeShuffle(const Value& vector1, const Value& vector2, size_t comp0,
size_t comp1);
[[nodiscard]] Value CompositeShuffle(const Value& vector1, const Value& vector2, size_t comp0,
size_t comp1, size_t comp2);
[[nodiscard]] Value CompositeShuffle(const Value& vector1, const Value& vector2, size_t comp0,
size_t comp1, size_t comp2, size_t comp3);
[[nodiscard]] Value Select(const U1& condition, const Value& true_value,
const Value& false_value);
[[nodiscard]] U64 PackUint2x32(const Value& vector);
[[nodiscard]] Value UnpackUint2x32(const U64& value);
[[nodiscard]] F64 PackDouble2x32(const Value& vector);
[[nodiscard]] Value UnpackDouble2x32(const F64& value);
[[nodiscard]] U32 Pack2x16(AmdGpu::NumberFormat number_format, const Value& vector);
[[nodiscard]] Value Unpack2x16(AmdGpu::NumberFormat number_format, const U32& value);
[[nodiscard]] U32 Pack4x8(AmdGpu::NumberFormat number_format, const Value& vector);
[[nodiscard]] Value Unpack4x8(AmdGpu::NumberFormat number_format, const U32& value);
[[nodiscard]] U32 Pack10_11_11(AmdGpu::NumberFormat number_format, const Value& vector);
[[nodiscard]] Value Unpack10_11_11(AmdGpu::NumberFormat number_format, const U32& value);
[[nodiscard]] U32 Pack2_10_10_10(AmdGpu::NumberFormat number_format, const Value& vector);
[[nodiscard]] Value Unpack2_10_10_10(AmdGpu::NumberFormat number_format, const U32& value);
[[nodiscard]] F32F64 FPAdd(const F32F64& a, const F32F64& b);
[[nodiscard]] F32F64 FPSub(const F32F64& a, const F32F64& b);
[[nodiscard]] F32F64 FPMul(const F32F64& a, const F32F64& b);
[[nodiscard]] F32F64 FPDiv(const F32F64& a, const F32F64& b);
[[nodiscard]] F32F64 FPFma(const F32F64& a, const F32F64& b, const F32F64& c);
[[nodiscard]] F32F64 FPAbs(const F32F64& value);
[[nodiscard]] F32F64 FPNeg(const F32F64& value);
[[nodiscard]] F32F64 FPAbsNeg(const F32F64& value, bool abs, bool neg);
[[nodiscard]] F32 FPCos(const F32& value);
[[nodiscard]] F32 FPSin(const F32& value);
[[nodiscard]] F32 FPExp2(const F32& value);
[[nodiscard]] F32 FPLog2(const F32& value);
[[nodiscard]] F32 FPPow(const F32& x, const F32& y);
[[nodiscard]] F32 FPLdexp(const F32& value, const U32& exp);
[[nodiscard]] F32F64 FPRecip(const F32F64& value);
[[nodiscard]] F32F64 FPRecipSqrt(const F32F64& value);
[[nodiscard]] F32 FPSqrt(const F32& value);
[[nodiscard]] F32F64 FPSaturate(const F32F64& value);
[[nodiscard]] F32F64 FPClamp(const F32F64& value, const F32F64& min_value,
const F32F64& max_value);
[[nodiscard]] F32F64 FPRoundEven(const F32F64& value);
[[nodiscard]] F32F64 FPFloor(const F32F64& value);
[[nodiscard]] F32F64 FPCeil(const F32F64& value);
[[nodiscard]] F32F64 FPTrunc(const F32F64& value);
[[nodiscard]] F32F64 FPFract(const F32F64& value);
[[nodiscard]] F32F64 FPFrexpSig(const F32F64& value);
[[nodiscard]] U32 FPFrexpExp(const F32F64& value);
[[nodiscard]] U1 FPEqual(const F32F64& lhs, const F32F64& rhs, bool ordered = true);
[[nodiscard]] U1 FPNotEqual(const F32F64& lhs, const F32F64& rhs, bool ordered = true);
[[nodiscard]] U1 FPLessThanEqual(const F32F64& lhs, const F32F64& rhs, bool ordered = true);
[[nodiscard]] U1 FPGreaterThanEqual(const F32F64& lhs, const F32F64& rhs, bool ordered = true);
[[nodiscard]] U1 FPLessThan(const F32F64& lhs, const F32F64& rhs, bool ordered = true);
[[nodiscard]] U1 FPGreaterThan(const F32F64& lhs, const F32F64& rhs, bool ordered = true);
[[nodiscard]] U1 FPIsNan(const F32F64& value);
[[nodiscard]] U1 FPIsInf(const F32F64& value);
[[nodiscard]] U1 FPCmpClass32(const F32& value, const U32& op);
[[nodiscard]] U1 FPOrdered(const F32F64& lhs, const F32F64& rhs);
[[nodiscard]] U1 FPUnordered(const F32F64& lhs, const F32F64& rhs);
[[nodiscard]] F32F64 FPMax(const F32F64& lhs, const F32F64& rhs);
[[nodiscard]] F32F64 FPMin(const F32F64& lhs, const F32F64& rhs);
[[nodiscard]] F32F64 FPMinTri(const F32F64& a, const F32F64& b, const F32F64& c);
[[nodiscard]] F32F64 FPMaxTri(const F32F64& a, const F32F64& b, const F32F64& c);
[[nodiscard]] F32F64 FPMedTri(const F32F64& a, const F32F64& b, const F32F64& c);
[[nodiscard]] U32U64 IAdd(const U32U64& a, const U32U64& b);
[[nodiscard]] U32U64 ISub(const U32U64& a, const U32U64& b);
[[nodiscard]] U32 IMulHi(const U32& a, const U32& b, bool is_signed = false);
[[nodiscard]] U32U64 IMul(const U32U64& a, const U32U64& b);
[[nodiscard]] U32 IDiv(const U32& a, const U32& b, bool is_signed = false);
[[nodiscard]] U32 IMod(const U32& a, const U32& b, bool is_signed = false);
[[nodiscard]] U32U64 INeg(const U32U64& value);
[[nodiscard]] U32 IAbs(const U32& value);
[[nodiscard]] U32U64 ShiftLeftLogical(const U32U64& base, const U32& shift);
[[nodiscard]] U32U64 ShiftRightLogical(const U32U64& base, const U32& shift);
[[nodiscard]] U32U64 ShiftRightArithmetic(const U32U64& base, const U32& shift);
[[nodiscard]] U32U64 BitwiseAnd(const U32U64& a, const U32U64& b);
[[nodiscard]] U32U64 BitwiseOr(const U32U64& a, const U32U64& b);
[[nodiscard]] U32U64 BitwiseXor(const U32U64& a, const U32U64& b);
[[nodiscard]] U32U64 BitFieldInsert(const U32U64& base, const U32U64& insert, const U32& offset,
const U32& count);
[[nodiscard]] U32 BitFieldExtract(const U32& base, const U32& offset, const U32& count,
bool is_signed = false);
[[nodiscard]] U32 BitReverse(const U32& value);
[[nodiscard]] U32 BitCount(const U32U64& value);
[[nodiscard]] U32U64 BitwiseNot(const U32U64& value);
[[nodiscard]] U32 MaskedBitCount(const U32& value, const U32& addend, bool hi);
[[nodiscard]] U32 FindSMsb(const U32& value);
[[nodiscard]] U32 FindUMsb(const U32U64& value);
[[nodiscard]] U32 FindILsb(const U32U64& value);
[[nodiscard]] U32 SMin(const U32& a, const U32& b);
[[nodiscard]] U32 UMin(const U32& a, const U32& b);
[[nodiscard]] U32 IMin(const U32& a, const U32& b, bool is_signed);
[[nodiscard]] U32 SMax(const U32& a, const U32& b);
[[nodiscard]] U32 UMax(const U32& a, const U32& b);
[[nodiscard]] U32 IMax(const U32& a, const U32& b, bool is_signed);
[[nodiscard]] U32 SMinTri(const U32& a, const U32& b, const U32& c);
[[nodiscard]] U32 UMinTri(const U32& a, const U32& b, const U32& c);
[[nodiscard]] U32 IMinTri(const U32& a, const U32& b, const U32& c, bool is_signed);
[[nodiscard]] U32 SMaxTri(const U32& a, const U32& b, const U32& c);
[[nodiscard]] U32 UMaxTri(const U32& a, const U32& b, const U32& c);
[[nodiscard]] U32 IMaxTri(const U32& a, const U32& b, const U32& c, bool is_signed);
[[nodiscard]] U32 SMedTri(const U32& a, const U32& b, const U32& c);
[[nodiscard]] U32 UMedTri(const U32& a, const U32& b, const U32& c);
[[nodiscard]] U32 IMedTri(const U32& a, const U32& b, const U32& c, bool is_signed);
[[nodiscard]] U32 SClamp(const U32& value, const U32& min, const U32& max);
[[nodiscard]] U32 UClamp(const U32& value, const U32& min, const U32& max);
[[nodiscard]] U1 ILessThan(const U32U64& lhs, const U32U64& rhs, bool is_signed);
[[nodiscard]] U1 IEqual(const U32U64& lhs, const U32U64& rhs);
[[nodiscard]] U1 ILessThanEqual(const U32U64& lhs, const U32U64& rhs, bool is_signed);
[[nodiscard]] U1 IGreaterThan(const U32U64& lhs, const U32U64& rhs, bool is_signed);
[[nodiscard]] U1 INotEqual(const U32U64& lhs, const U32U64& rhs);
[[nodiscard]] U1 IGreaterThanEqual(const U32U64& lhs, const U32U64& rhs, bool is_signed);
[[nodiscard]] U1 LogicalOr(const U1& a, const U1& b);
[[nodiscard]] U1 LogicalAnd(const U1& a, const U1& b);
[[nodiscard]] U1 LogicalXor(const U1& a, const U1& b);
[[nodiscard]] U1 LogicalNot(const U1& value);
[[nodiscard]] U32U64 ConvertFToS(size_t bitsize, const F32F64& value);
[[nodiscard]] U32U64 ConvertFToU(size_t bitsize, const F32F64& value);
[[nodiscard]] U32U64 ConvertFToI(size_t bitsize, bool is_signed, const F32F64& value);
[[nodiscard]] F32F64 ConvertSToF(size_t dest_bitsize, size_t src_bitsize, const Value& value);
[[nodiscard]] F32F64 ConvertUToF(size_t dest_bitsize, size_t src_bitsize, const Value& value);
[[nodiscard]] F32F64 ConvertIToF(size_t dest_bitsize, size_t src_bitsize, bool is_signed,
const Value& value);
[[nodiscard]] U8U16U32U64 UConvert(size_t result_bitsize, const U8U16U32U64& value);
[[nodiscard]] U8U16U32U64 SConvert(size_t result_bitsize, const U8U16U32U64& value);
[[nodiscard]] F16F32F64 FPConvert(size_t result_bitsize, const F16F32F64& value);
[[nodiscard]] Value ImageAtomicIAdd(const Value& handle, const Value& coords,
const Value& value, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicSMin(const Value& handle, const Value& coords,
const Value& value, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicUMin(const Value& handle, const Value& coords,
const Value& value, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicIMin(const Value& handle, const Value& coords,
const Value& value, bool is_signed, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicSMax(const Value& handle, const Value& coords,
const Value& value, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicUMax(const Value& handle, const Value& coords,
const Value& value, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicFMax(const Value& handle, const Value& coords,
const Value& value, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicFMin(const Value& handle, const Value& coords,
const Value& value, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicIMax(const Value& handle, const Value& coords,
const Value& value, bool is_signed, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicInc(const Value& handle, const Value& coords, const Value& value,
TextureInstInfo info);
[[nodiscard]] Value ImageAtomicDec(const Value& handle, const Value& coords, const Value& value,
TextureInstInfo info);
[[nodiscard]] Value ImageAtomicAnd(const Value& handle, const Value& coords, const Value& value,
TextureInstInfo info);
[[nodiscard]] Value ImageAtomicOr(const Value& handle, const Value& coords, const Value& value,
TextureInstInfo info);
[[nodiscard]] Value ImageAtomicXor(const Value& handle, const Value& coords, const Value& value,
TextureInstInfo info);
[[nodiscard]] Value ImageAtomicExchange(const Value& handle, const Value& coords,
const Value& value, TextureInstInfo info);
[[nodiscard]] Value ImageAtomicCmpSwap(const Value& handle, const Value& coords,
const Value& value, const Value& cmp_value,
TextureInstInfo info);
[[nodiscard]] Value ImageHandle(const Value& tsharp_low, const Value& tsharp_high);
[[nodiscard]] Value ImageSampleRaw(const Value& handle, const Value& sampler_handle,
const Value& address1, const Value& address2,
const Value& address3, const Value& address4,
TextureInstInfo info);
[[nodiscard]] Value ImageSampleImplicitLod(const Value& handle, const Value& body,
const F32& bias, const Value& offset,
TextureInstInfo info);
[[nodiscard]] Value ImageSampleExplicitLod(const Value& handle, const Value& body,
const F32& lod, const Value& offset,
TextureInstInfo info);
[[nodiscard]] Value ImageSampleDrefImplicitLod(const Value& handle, const Value& body,
const F32& dref, const F32& bias,
const Value& offset, TextureInstInfo info);
[[nodiscard]] Value ImageSampleDrefExplicitLod(const Value& handle, const Value& body,
const F32& dref, const F32& lod,
const Value& offset, TextureInstInfo info);
[[nodiscard]] Value ImageQueryDimension(const Value& handle, const U32& lod,
const U1& skip_mips, TextureInstInfo info);
[[nodiscard]] Value ImageQueryLod(const Value& handle, const Value& coords,
TextureInstInfo info);
[[nodiscard]] Value ImageGather(const Value& handle, const Value& coords, const Value& offset,
TextureInstInfo info);
[[nodiscard]] Value ImageGatherDref(const Value& handle, const Value& coords,
const Value& offset, const F32& dref, TextureInstInfo info);
[[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords,
const Value& derivatives_dx, const Value& derivatives_dy,
const Value& offset, const F32& lod_clamp,
TextureInstInfo info);
[[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, const U32& lod,
const U32& multisampling, TextureInstInfo info);
void ImageWrite(const Value& handle, const Value& coords, const U32& lod,
const U32& multisampling, const Value& color, TextureInstInfo info);
[[nodiscard]] F32 CubeFaceIndex(const Value& x, const Value& y, const Value& z);
[[nodiscard]] F32 CubeFaceCoordS(const Value& x, const Value& y, const Value& z);
[[nodiscard]] F32 CubeFaceCoordT(const Value& x, const Value& y, const Value& z);
[[nodiscard]] F32 CubeFaceMajorAxis(const Value& x, const Value& y, const Value& z);
void EmitVertex();
void EmitPrimitive();
U64 Memtime();
private:
Block* block;
IR::Block::iterator insertion_point;
template <typename T = Value, typename... Args>
T Inst(Opcode op, Args... args) {
auto it{block->PrependNewInst(insertion_point, op, {Value{args}...})};
return T{Value{&*it}};
}
template <typename T>
requires(sizeof(T) <= sizeof(u64) && std::is_trivially_copyable_v<T>)
struct Flags {
Flags() = default;
Flags(T proxy_) : proxy{proxy_} {}
T proxy;
};
template <typename T = Value, typename FlagType, typename... Args>
T Inst(Opcode op, Flags<FlagType> flags, Args... args) {
u64 raw_flags{};
std::memcpy(&raw_flags, &flags.proxy, sizeof(flags.proxy));
auto it{block->PrependNewInst(insertion_point, op, {Value{args}...}, raw_flags)};
return T{Value{&*it}};
}
};
} // namespace Shader::IR
-474
View File
@@ -1,474 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
// opcode name, return type, arg1 type, arg2 type, arg3 type, arg4 type, arg4 type, ...
OPCODE(Phi, Opaque, )
OPCODE(Void, Void, )
OPCODE(ConditionRef, U1, U1, )
// Special operations
OPCODE(Prologue, Void, )
OPCODE(Epilogue, Void, )
OPCODE(Discard, Void, )
OPCODE(DiscardCond, Void, U1, )
OPCODE(DebugPrint, Void, StringLiteral, Opaque, Opaque, Opaque, Opaque, )
// Constant memory operations
OPCODE(ReadConst, U32, U32x2, U32, )
OPCODE(ReadConstBuffer, U32, Opaque, U32, )
// Barriers
OPCODE(Barrier, Void, )
OPCODE(WorkgroupMemoryBarrier, Void, )
OPCODE(DeviceMemoryBarrier, Void, )
// Geometry shader specific
OPCODE(EmitVertex, Void, )
OPCODE(EmitPrimitive, Void, )
// Shared memory operations
OPCODE(LoadSharedU16, U16, U32, )
OPCODE(LoadSharedU32, U32, U32, )
OPCODE(LoadSharedU64, U64, U32, )
OPCODE(WriteSharedU16, Void, U32, U16, )
OPCODE(WriteSharedU32, Void, U32, U32, )
OPCODE(WriteSharedU64, Void, U32, U64, )
// Shared atomic operations
OPCODE(SharedAtomicIAdd32, U32, U32, U32, )
OPCODE(SharedAtomicIAdd64, U64, U32, U64, )
OPCODE(SharedAtomicISub32, U32, U32, U32, )
OPCODE(SharedAtomicISub64, U64, U32, U64, )
OPCODE(SharedAtomicSMin32, U32, U32, U32, )
OPCODE(SharedAtomicSMin64, U64, U32, U64, )
OPCODE(SharedAtomicUMin32, U32, U32, U32, )
OPCODE(SharedAtomicUMin64, U64, U32, U64, )
OPCODE(SharedAtomicSMax32, U32, U32, U32, )
OPCODE(SharedAtomicSMax64, U64, U32, U64, )
OPCODE(SharedAtomicUMax32, U32, U32, U32, )
OPCODE(SharedAtomicUMax64, U64, U32, U64, )
OPCODE(SharedAtomicInc32, U32, U32, )
OPCODE(SharedAtomicInc64, U64, U32, )
OPCODE(SharedAtomicDec32, U32, U32, )
OPCODE(SharedAtomicDec64, U64, U32, )
OPCODE(SharedAtomicAnd32, U32, U32, U32, )
OPCODE(SharedAtomicAnd64, U64, U32, U64, )
OPCODE(SharedAtomicOr32, U32, U32, U32, )
OPCODE(SharedAtomicOr64, U64, U32, U64, )
OPCODE(SharedAtomicXor32, U32, U32, U32, )
OPCODE(SharedAtomicXor64, U64, U32, U64, )
OPCODE(SharedAtomicCmpSwap32, U32, U32, U32, U32, )
OPCODE(SharedAtomicCmpSwap64, U64, U32, U64, U64, )
// Context getters/setters
OPCODE(GetUserData, U32, ScalarReg, )
OPCODE(GetScalarRegister, U32, ScalarReg, )
OPCODE(SetScalarRegister, Void, ScalarReg, U32, )
OPCODE(GetVectorRegister, U32, VectorReg, )
OPCODE(SetVectorRegister, Void, VectorReg, U32, )
OPCODE(GetVirtualRegister, Opaque, VirtualReg, )
OPCODE(SetVirtualRegister, Void, VirtualReg, Opaque, )
OPCODE(GetGotoVariable, U1, U32, )
OPCODE(SetGotoVariable, Void, U32, U1, )
OPCODE(GetAttribute, F32, Attribute, U32, U32, )
OPCODE(GetAttributeU1, U1, Attribute, U32, )
OPCODE(GetAttributeU32, U32, Attribute, U32, )
OPCODE(SetAttribute, Void, Attribute, F32, U32, )
OPCODE(GetPatch, F32, Patch, )
OPCODE(SetPatch, Void, Patch, F32, )
OPCODE(GetTessGenericAttribute, F32, U32, U32, U32, )
OPCODE(SetTcsGenericAttribute, Void, F32, U32, U32, )
OPCODE(ReadTcsGenericOuputAttribute, F32, U32, U32, U32, )
OPCODE(GetPcLo, U32, U32, )
// Flags
OPCODE(GetScc, U1, Void, )
OPCODE(GetExec, U1, Void, )
OPCODE(GetVcc, U1, Void, )
OPCODE(GetVccLo, U32, Void, )
OPCODE(GetVccHi, U32, Void, )
OPCODE(GetM0, U32, Void, )
OPCODE(SetScc, Void, U1, )
OPCODE(SetExec, Void, U1, )
OPCODE(SetVcc, Void, U1, )
OPCODE(SetVccLo, Void, U32, )
OPCODE(SetVccHi, Void, U32, )
OPCODE(SetM0, Void, U32, )
// Undefined
OPCODE(UndefU1, U1, )
OPCODE(UndefU8, U8, )
OPCODE(UndefU16, U16, )
OPCODE(UndefU32, U32, )
OPCODE(UndefU32x2, U32x2, )
OPCODE(UndefU32x3, U32x3, )
OPCODE(UndefU32x4, U32x4, )
OPCODE(UndefF32, F32, )
OPCODE(UndefU64, U64, )
// Buffer operations
OPCODE(LoadBufferU8, U8, Opaque, Opaque, )
OPCODE(LoadBufferU16, U16, Opaque, Opaque, )
OPCODE(LoadBufferU32, U32, Opaque, Opaque, )
OPCODE(LoadBufferU32x2, U32x2, Opaque, Opaque, )
OPCODE(LoadBufferU32x3, U32x3, Opaque, Opaque, )
OPCODE(LoadBufferU32x4, U32x4, Opaque, Opaque, )
OPCODE(LoadBufferU64, U64, Opaque, Opaque, )
OPCODE(LoadBufferF32, F32, Opaque, Opaque, )
OPCODE(LoadBufferF32x2, F32x2, Opaque, Opaque, )
OPCODE(LoadBufferF32x3, F32x3, Opaque, Opaque, )
OPCODE(LoadBufferF32x4, F32x4, Opaque, Opaque, )
OPCODE(LoadBufferFormatF32, F32x4, Opaque, Opaque, )
OPCODE(StoreBufferU8, Void, Opaque, Opaque, U8, )
OPCODE(StoreBufferU16, Void, Opaque, Opaque, U16, )
OPCODE(StoreBufferU32, Void, Opaque, Opaque, U32, )
OPCODE(StoreBufferU32x2, Void, Opaque, Opaque, U32x2, )
OPCODE(StoreBufferU32x3, Void, Opaque, Opaque, U32x3, )
OPCODE(StoreBufferU32x4, Void, Opaque, Opaque, U32x4, )
OPCODE(StoreBufferU64, Void, Opaque, Opaque, U64, )
OPCODE(StoreBufferF32, Void, Opaque, Opaque, F32, )
OPCODE(StoreBufferF32x2, Void, Opaque, Opaque, F32x2, )
OPCODE(StoreBufferF32x3, Void, Opaque, Opaque, F32x3, )
OPCODE(StoreBufferF32x4, Void, Opaque, Opaque, F32x4, )
OPCODE(StoreBufferFormatF32, Void, Opaque, Opaque, F32x4, )
// Buffer atomic operations
OPCODE(BufferAtomicIAdd32, U32, Opaque, Opaque, U32 )
OPCODE(BufferAtomicIAdd64, U64, Opaque, Opaque, U64 )
OPCODE(BufferAtomicISub32, U32, Opaque, Opaque, U32 )
OPCODE(BufferAtomicSMin32, U32, Opaque, Opaque, U32 )
OPCODE(BufferAtomicSMin64, U64, Opaque, Opaque, U64 )
OPCODE(BufferAtomicUMin32, U32, Opaque, Opaque, U32 )
OPCODE(BufferAtomicUMin64, U64, Opaque, Opaque, U64 )
OPCODE(BufferAtomicFMin32, F32, Opaque, Opaque, F32 )
OPCODE(BufferAtomicSMax32, U32, Opaque, Opaque, U32 )
OPCODE(BufferAtomicSMax64, U64, Opaque, Opaque, U64 )
OPCODE(BufferAtomicUMax32, U32, Opaque, Opaque, U32 )
OPCODE(BufferAtomicUMax64, U64, Opaque, Opaque, U64 )
OPCODE(BufferAtomicFMax32, F32, Opaque, Opaque, F32 )
OPCODE(BufferAtomicInc32, U32, Opaque, Opaque, )
OPCODE(BufferAtomicDec32, U32, Opaque, Opaque, )
OPCODE(BufferAtomicAnd32, U32, Opaque, Opaque, U32, )
OPCODE(BufferAtomicOr32, U32, Opaque, Opaque, U32, )
OPCODE(BufferAtomicXor32, U32, Opaque, Opaque, U32, )
OPCODE(BufferAtomicSwap32, U32, Opaque, Opaque, U32, )
OPCODE(BufferAtomicCmpSwap32, U32, Opaque, Opaque, U32, U32, )
OPCODE(BufferAtomicFCmpSwap32, F32, Opaque, Opaque, F32, F32, )
// Vector utility
OPCODE(CompositeConstructU32x2, U32x2, U32, U32, )
OPCODE(CompositeConstructU32x3, U32x3, U32, U32, U32, )
OPCODE(CompositeConstructU32x4, U32x4, U32, U32, U32, U32, )
OPCODE(CompositeConstructU32x2x2, U32x4, U32x2, U32x2, )
OPCODE(CompositeExtractU32x2, U32, U32x2, U32, )
OPCODE(CompositeExtractU32x3, U32, U32x3, U32, )
OPCODE(CompositeExtractU32x4, U32, U32x4, U32, )
OPCODE(CompositeInsertU32x2, U32x2, U32x2, U32, U32, )
OPCODE(CompositeInsertU32x3, U32x3, U32x3, U32, U32, )
OPCODE(CompositeInsertU32x4, U32x4, U32x4, U32, U32, )
OPCODE(CompositeShuffleU32x2, U32x2, U32x2, U32x2, U32, U32, )
OPCODE(CompositeShuffleU32x3, U32x3, U32x3, U32x3, U32, U32, U32, )
OPCODE(CompositeShuffleU32x4, U32x4, U32x4, U32x4, U32, U32, U32, U32, )
OPCODE(CompositeConstructF32x2, F32x2, F32, F32, )
OPCODE(CompositeConstructF32x3, F32x3, F32, F32, F32, )
OPCODE(CompositeConstructF32x4, F32x4, F32, F32, F32, F32, )
OPCODE(CompositeConstructF32x2x2, F32x4, F32x2, F32x2, )
OPCODE(CompositeExtractF32x2, F32, F32x2, U32, )
OPCODE(CompositeExtractF32x3, F32, F32x3, U32, )
OPCODE(CompositeExtractF32x4, F32, F32x4, U32, )
OPCODE(CompositeInsertF32x2, F32x2, F32x2, F32, U32, )
OPCODE(CompositeInsertF32x3, F32x3, F32x3, F32, U32, )
OPCODE(CompositeInsertF32x4, F32x4, F32x4, F32, U32, )
OPCODE(CompositeShuffleF32x2, F32x2, F32x2, F32x2, U32, U32, )
OPCODE(CompositeShuffleF32x3, F32x3, F32x3, F32x3, U32, U32, U32, )
OPCODE(CompositeShuffleF32x4, F32x4, F32x4, F32x4, U32, U32, U32, U32, )
// Select operations
OPCODE(SelectU1, U1, U1, U1, U1, )
OPCODE(SelectU32, U32, U1, U32, U32, )
OPCODE(SelectF32, F32, U1, F32, F32, )
OPCODE(SelectU64, U64, U1, U64, U64, )
// Bitwise conversions
OPCODE(BitCastU16F16, U16, F16, )
OPCODE(BitCastU32F32, U32, F32, )
OPCODE(BitCastF16U16, F16, U16, )
OPCODE(BitCastF32U32, F32, U32, )
OPCODE(PackUint2x32, U64, U32x2, )
OPCODE(UnpackUint2x32, U32x2, U64, )
OPCODE(PackDouble2x32, F64, U32x2, )
OPCODE(UnpackDouble2x32, U32x2, F64, )
OPCODE(PackUnorm2x16, U32, F32x2, )
OPCODE(UnpackUnorm2x16, F32x2, U32, )
OPCODE(PackSnorm2x16, U32, F32x2, )
OPCODE(UnpackSnorm2x16, F32x2, U32, )
OPCODE(PackUint2x16, U32, F32x2, )
OPCODE(UnpackUint2x16, F32x2, U32, )
OPCODE(PackSint2x16, U32, F32x2, )
OPCODE(UnpackSint2x16, F32x2, U32, )
OPCODE(PackHalf2x16, U32, F32x2, )
OPCODE(UnpackHalf2x16, F32x2, U32, )
OPCODE(PackUnorm4x8, U32, F32x4, )
OPCODE(UnpackUnorm4x8, F32x4, U32, )
OPCODE(PackSnorm4x8, U32, F32x4, )
OPCODE(UnpackSnorm4x8, F32x4, U32, )
OPCODE(PackUint4x8, U32, F32x4, )
OPCODE(UnpackUint4x8, F32x4, U32, )
OPCODE(PackSint4x8, U32, F32x4, )
OPCODE(UnpackSint4x8, F32x4, U32, )
OPCODE(PackUfloat10_11_11, U32, F32x3, )
OPCODE(UnpackUfloat10_11_11, F32x3, U32, )
OPCODE(PackUnorm2_10_10_10, U32, F32x4, )
OPCODE(UnpackUnorm2_10_10_10, F32x4, U32, )
OPCODE(PackSnorm2_10_10_10, U32, F32x4, )
OPCODE(UnpackSnorm2_10_10_10, F32x4, U32, )
OPCODE(PackUint2_10_10_10, U32, F32x4, )
OPCODE(UnpackUint2_10_10_10, F32x4, U32, )
OPCODE(PackSint2_10_10_10, U32, F32x4, )
OPCODE(UnpackSint2_10_10_10, F32x4, U32, )
// Floating-point operations
OPCODE(FPAbs32, F32, F32, )
OPCODE(FPAbs64, F64, F64, )
OPCODE(FPAdd32, F32, F32, F32, )
OPCODE(FPAdd64, F64, F64, F64, )
OPCODE(FPSub32, F32, F32, F32, )
OPCODE(FPFma32, F32, F32, F32, F32, )
OPCODE(FPFma64, F64, F64, F64, F64, )
OPCODE(FPMax32, F32, F32, F32, )
OPCODE(FPMax64, F64, F64, F64, )
OPCODE(FPMin32, F32, F32, F32, )
OPCODE(FPMin64, F64, F64, F64, )
OPCODE(FPMinTri32, F32, F32, F32, F32, )
OPCODE(FPMaxTri32, F32, F32, F32, F32, )
OPCODE(FPMedTri32, F32, F32, F32, F32, )
OPCODE(FPMul32, F32, F32, F32, )
OPCODE(FPMul64, F64, F64, F64, )
OPCODE(FPDiv32, F32, F32, F32, )
OPCODE(FPDiv64, F64, F64, F64, )
OPCODE(FPNeg32, F32, F32, )
OPCODE(FPNeg64, F64, F64, )
OPCODE(FPRecip32, F32, F32, )
OPCODE(FPRecip64, F64, F64, )
OPCODE(FPRecipSqrt32, F32, F32, )
OPCODE(FPRecipSqrt64, F64, F64, )
OPCODE(FPSqrt, F32, F32, )
OPCODE(FPSin, F32, F32, )
OPCODE(FPExp2, F32, F32, )
OPCODE(FPPow, F32, F32, F32, )
OPCODE(FPLdexp, F32, F32, U32, )
OPCODE(FPCos, F32, F32, )
OPCODE(FPLog2, F32, F32, )
OPCODE(FPSaturate32, F32, F32, )
OPCODE(FPSaturate64, F64, F64, )
OPCODE(FPClamp32, F32, F32, F32, F32, )
OPCODE(FPClamp64, F64, F64, F64, F64, )
OPCODE(FPRoundEven32, F32, F32, )
OPCODE(FPRoundEven64, F64, F64, )
OPCODE(FPFloor32, F32, F32, )
OPCODE(FPFloor64, F64, F64, )
OPCODE(FPCeil32, F32, F32, )
OPCODE(FPCeil64, F64, F64, )
OPCODE(FPTrunc32, F32, F32, )
OPCODE(FPTrunc64, F64, F64, )
OPCODE(FPFract32, F32, F32, )
OPCODE(FPFract64, F64, F64, )
OPCODE(FPFrexpSig32, F32, F32, )
OPCODE(FPFrexpSig64, F64, F64, )
OPCODE(FPFrexpExp32, U32, F32, )
OPCODE(FPFrexpExp64, U32, F64, )
OPCODE(FPOrdEqual32, U1, F32, F32, )
OPCODE(FPOrdEqual64, U1, F64, F64, )
OPCODE(FPUnordEqual32, U1, F32, F32, )
OPCODE(FPUnordEqual64, U1, F64, F64, )
OPCODE(FPOrdNotEqual32, U1, F32, F32, )
OPCODE(FPOrdNotEqual64, U1, F64, F64, )
OPCODE(FPUnordNotEqual32, U1, F32, F32, )
OPCODE(FPUnordNotEqual64, U1, F64, F64, )
OPCODE(FPOrdLessThan32, U1, F32, F32, )
OPCODE(FPOrdLessThan64, U1, F64, F64, )
OPCODE(FPUnordLessThan32, U1, F32, F32, )
OPCODE(FPUnordLessThan64, U1, F64, F64, )
OPCODE(FPOrdGreaterThan32, U1, F32, F32, )
OPCODE(FPOrdGreaterThan64, U1, F64, F64, )
OPCODE(FPUnordGreaterThan32, U1, F32, F32, )
OPCODE(FPUnordGreaterThan64, U1, F64, F64, )
OPCODE(FPOrdLessThanEqual32, U1, F32, F32, )
OPCODE(FPOrdLessThanEqual64, U1, F64, F64, )
OPCODE(FPUnordLessThanEqual32, U1, F32, F32, )
OPCODE(FPUnordLessThanEqual64, U1, F64, F64, )
OPCODE(FPOrdGreaterThanEqual32, U1, F32, F32, )
OPCODE(FPOrdGreaterThanEqual64, U1, F64, F64, )
OPCODE(FPUnordGreaterThanEqual32, U1, F32, F32, )
OPCODE(FPUnordGreaterThanEqual64, U1, F64, F64, )
OPCODE(FPIsNan32, U1, F32, )
OPCODE(FPIsNan64, U1, F64, )
OPCODE(FPIsInf32, U1, F32, )
OPCODE(FPIsInf64, U1, F64, )
OPCODE(FPCmpClass32, U1, F32, U32 )
// Integer operations
OPCODE(IAdd32, U32, U32, U32, )
OPCODE(IAdd64, U64, U64, U64, )
OPCODE(ISub32, U32, U32, U32, )
OPCODE(ISub64, U64, U64, U64, )
OPCODE(IMul32, U32, U32, U32, )
OPCODE(IMul64, U64, U64, U64, )
OPCODE(SMulHi, U32, U32, U32, )
OPCODE(UMulHi, U32, U32, U32, )
OPCODE(SDiv32, U32, U32, U32, )
OPCODE(UDiv32, U32, U32, U32, )
OPCODE(SMod32, U32, U32, U32, )
OPCODE(UMod32, U32, U32, U32, )
OPCODE(INeg32, U32, U32, )
OPCODE(INeg64, U64, U64, )
OPCODE(IAbs32, U32, U32, )
OPCODE(ShiftLeftLogical32, U32, U32, U32, )
OPCODE(ShiftLeftLogical64, U64, U64, U32, )
OPCODE(ShiftRightLogical32, U32, U32, U32, )
OPCODE(ShiftRightLogical64, U64, U64, U32, )
OPCODE(ShiftRightArithmetic32, U32, U32, U32, )
OPCODE(ShiftRightArithmetic64, U64, U64, U32, )
OPCODE(BitwiseAnd32, U32, U32, U32, )
OPCODE(BitwiseAnd64, U64, U64, U64, )
OPCODE(BitwiseOr32, U32, U32, U32, )
OPCODE(BitwiseOr64, U64, U64, U64, )
OPCODE(BitwiseXor32, U32, U32, U32, )
OPCODE(BitwiseXor64, U64, U64, U64, )
OPCODE(BitFieldInsert32, U32, U32, U32, U32, U32, )
OPCODE(BitFieldInsert64, U64, U64, U64, U32, U32, )
OPCODE(BitFieldSExtract, U32, U32, U32, U32, )
OPCODE(BitFieldUExtract, U32, U32, U32, U32, )
OPCODE(BitReverse32, U32, U32, )
OPCODE(BitCount32, U32, U32, )
OPCODE(BitCount64, U32, U64, )
OPCODE(BitwiseNot32, U32, U32, )
OPCODE(BitwiseNot64, U64, U64, )
OPCODE(MaskedBitCount32, U32, U32, U32, U1, )
OPCODE(FindSMsb32, U32, U32, )
OPCODE(FindUMsb32, U32, U32, )
OPCODE(FindUMsb64, U32, U64, )
OPCODE(FindILsb32, U32, U32, )
OPCODE(FindILsb64, U32, U64, )
OPCODE(SMin32, U32, U32, U32, )
OPCODE(UMin32, U32, U32, U32, )
OPCODE(SMax32, U32, U32, U32, )
OPCODE(UMax32, U32, U32, U32, )
OPCODE(SMinTri32, U32, U32, U32, U32, )
OPCODE(UMinTri32, U32, U32, U32, U32, )
OPCODE(SMaxTri32, U32, U32, U32, U32, )
OPCODE(UMaxTri32, U32, U32, U32, U32, )
OPCODE(SMedTri32, U32, U32, U32, U32, )
OPCODE(UMedTri32, U32, U32, U32, U32, )
OPCODE(SClamp32, U32, U32, U32, U32, )
OPCODE(UClamp32, U32, U32, U32, U32, )
OPCODE(SLessThan32, U1, U32, U32, )
OPCODE(SLessThan64, U1, U64, U64, )
OPCODE(ULessThan32, U1, U32, U32, )
OPCODE(ULessThan64, U1, U64, U64, )
OPCODE(IEqual32, U1, U32, U32, )
OPCODE(IEqual64, U1, U64, U64, )
OPCODE(SLessThanEqual32, U1, U32, U32, )
OPCODE(SLessThanEqual64, U1, U64, U64, )
OPCODE(ULessThanEqual32, U1, U32, U32, )
OPCODE(ULessThanEqual64, U1, U64, U64, )
OPCODE(SGreaterThan32, U1, U32, U32, )
OPCODE(SGreaterThan64, U1, U64, U64, )
OPCODE(UGreaterThan32, U1, U32, U32, )
OPCODE(UGreaterThan64, U1, U64, U64, )
OPCODE(INotEqual32, U1, U32, U32, )
OPCODE(INotEqual64, U1, U64, U64, )
OPCODE(SGreaterThanEqual32, U1, U32, U32, )
OPCODE(SGreaterThanEqual64, U1, U64, U64, )
OPCODE(UGreaterThanEqual32, U1, U32, U32, )
OPCODE(UGreaterThanEqual64, U1, U64, U64, )
// Logical operations
OPCODE(LogicalOr, U1, U1, U1, )
OPCODE(LogicalAnd, U1, U1, U1, )
OPCODE(LogicalXor, U1, U1, U1, )
OPCODE(LogicalNot, U1, U1, )
// Conversion operations
OPCODE(ConvertS32F32, U32, F32, )
OPCODE(ConvertS32F64, U32, F64, )
OPCODE(ConvertU32F32, U32, F32, )
OPCODE(ConvertF16F32, F16, F32, )
OPCODE(ConvertF32F16, F32, F16, )
OPCODE(ConvertF32F64, F32, F64, )
OPCODE(ConvertF64F32, F64, F32, )
OPCODE(ConvertF32S32, F32, U32, )
OPCODE(ConvertF32U32, F32, U32, )
OPCODE(ConvertF64S32, F64, U32, )
OPCODE(ConvertF64U32, F64, U32, )
OPCODE(ConvertF32U16, F32, U16, )
OPCODE(ConvertU16U32, U16, U32, )
OPCODE(ConvertU32U16, U32, U16, )
OPCODE(ConvertU64U32, U64, U32, )
OPCODE(ConvertU8U32, U8, U32, )
OPCODE(ConvertU32U8, U32, U8, )
OPCODE(ConvertS32S8, U32, U8, )
OPCODE(ConvertS32S16, U32, U16, )
// Image operations
OPCODE(ImageHandle, Opaque, U32x4, U32x4, )
OPCODE(ImageSampleRaw, F32x4, Opaque, Opaque, F32x4, F32x4, F32x4, F32, )
OPCODE(ImageSampleImplicitLod, F32x4, Opaque, F32x4, F32, Opaque, )
OPCODE(ImageSampleExplicitLod, F32x4, Opaque, Opaque, F32, Opaque, )
OPCODE(ImageSampleDrefImplicitLod, F32x4, Opaque, Opaque, F32, F32, Opaque, )
OPCODE(ImageSampleDrefExplicitLod, F32x4, Opaque, Opaque, F32, F32, Opaque, )
OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, )
OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, F32, )
OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, U1, )
OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, )
OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, F32, )
OPCODE(ImageRead, F32x4, Opaque, Opaque, U32, U32, )
OPCODE(ImageWrite, Void, Opaque, Opaque, U32, U32, F32x4, )
// Image atomic operations
OPCODE(ImageAtomicIAdd32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicSMin32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicUMin32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicSMax32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicUMax32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicFMax32, F32, Opaque, Opaque, F32, )
OPCODE(ImageAtomicFMin32, F32, Opaque, Opaque, F32, )
OPCODE(ImageAtomicInc32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicDec32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicAnd32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicOr32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicXor32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicExchange32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicCmpSwap32, U32, Opaque, Opaque, U32, U32, )
// Cube operations - optional, usable if profile.supports_native_cube_calc
OPCODE(CubeFaceIndex, F32, F32, F32, F32, )
OPCODE(CubeFaceCoordS, F32, F32, F32, F32, )
OPCODE(CubeFaceCoordT, F32, F32, F32, F32, )
OPCODE(CubeFaceMajorAxis, F32, F32, F32, F32, )
// Warp operations
OPCODE(LaneId, U32, )
OPCODE(WarpId, U32, )
OPCODE(QuadBroadcast, U32, U32, U32 )
OPCODE(Shuffle, U32, U32, U32 )
OPCODE(ShuffleXor, U32, U32, U32, )
OPCODE(ReadFirstLane, U32, U32, U1, )
OPCODE(ReadLane, U32, U32, U32 )
OPCODE(WriteLane, U32, U32, U32, U32 )
OPCODE(Ballot, U64, U1, )
OPCODE(BallotFindLsb, U32, U64, )
OPCODE(InverseBallot, U1, U64, )
OPCODE(DataAppend, U32, U32, U1 )
OPCODE(DataConsume, U32, U32, U1 )
OPCODE(GroupAny, U1, U1, )
// Time operations
OPCODE(Memtime, U64, )