mirror of
https://github.com/Kravickas/PS4-apps.git
synced 2026-10-23 09:01:04 +02:00
Delete src/shader_recompiler directory
This commit is contained in:
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
@@ -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
@@ -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
|
||||
@@ -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, )
|
||||
Reference in new issue
Block a user