mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-25 20:28:48 +08:00
1f09de8896
* [AGC] Complete gfx10 v_cmpx_f32 decode and emit ordered/unordered float compares Add the missing v_cmpx_*_f32 VOPC decode entries (0x17-0x1C, 0x1F) and emission for the ordered/unordered predicates: nlg maps to OpFUnordEqual, while o/u are lowered from OpIsNan (unordered = isnan(a) || isnan(b), ordered = !unordered) because SPIR-V's OpOrdered/OpUnordered require the Kernel capability and are invalid in Vulkan shader modules. Opcode numbers cross-checked against LLVM's llvm-mc regression tests (llvm/test/MC/AMDGPU/gfx10_asm_vopc.s, gfx10_asm_vopcx.s); emitted lowering validated with spirv-val --target-env vulkan1.1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * [AGC] Write VCC only for non-X vector compares On gfx10 the VCmpx encodings have no sdst and define EXEC only, so the unconditional VCC store clobbered VCC on every VCmpx. Move the VCC store to the non-X path; EXEC keeps the existing old-EXEC & condition update. Addresses review feedback on #122. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: tensorcrush <tensorcrush@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2538 lines
100 KiB
C#
2538 lines
100 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
namespace SharpEmu.Libs.Agc;
|
|
|
|
internal static partial class Gen5SpirvTranslator
|
|
{
|
|
private sealed partial class CompilationContext
|
|
{
|
|
private bool TryEmitVectorAlu(
|
|
Gen5ShaderInstruction instruction,
|
|
out string error)
|
|
{
|
|
error = string.Empty;
|
|
if (instruction.Opcode == "VNop")
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (instruction.Opcode.StartsWith("VCmp", StringComparison.Ordinal))
|
|
{
|
|
return TryEmitVectorCompare(instruction, out error);
|
|
}
|
|
|
|
if (!TryGetVectorDestination(instruction, out var destination))
|
|
{
|
|
error = "missing vector destination";
|
|
return false;
|
|
}
|
|
|
|
uint result;
|
|
switch (instruction.Opcode)
|
|
{
|
|
case "VMovB32":
|
|
case "VReadlaneB32":
|
|
case "VReadfirstlaneB32":
|
|
result = GetRawSource(instruction, 0);
|
|
break;
|
|
case "VCndmaskB32":
|
|
{
|
|
var condition = instruction.Sources.Count > 2
|
|
? IsCurrentLaneSet(GetRawSource64(instruction, 2))
|
|
: Load(_boolType, _vcc);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
condition,
|
|
GetRawSource(instruction, 1),
|
|
GetRawSource(instruction, 0));
|
|
break;
|
|
}
|
|
case "VCvtU32F32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.ConvertFToU,
|
|
_uintType,
|
|
GetFloatSource(instruction, 0));
|
|
break;
|
|
case "VCvtI32F32":
|
|
case "VCvtRpiI32F32":
|
|
case "VCvtFlrI32F32":
|
|
{
|
|
var source = GetFloatSource(instruction, 0);
|
|
if (instruction.Opcode == "VCvtRpiI32F32")
|
|
{
|
|
source = Ext(9, _floatType, source);
|
|
}
|
|
else if (instruction.Opcode == "VCvtFlrI32F32")
|
|
{
|
|
source = Ext(8, _floatType, source);
|
|
}
|
|
|
|
result = Bitcast(
|
|
_uintType,
|
|
_module.AddInstruction(SpirvOp.ConvertFToS, _intType, source));
|
|
break;
|
|
}
|
|
case "VCvtF32I32":
|
|
{
|
|
var signed = Bitcast(_intType, GetRawSource(instruction, 0));
|
|
result = Bitcast(
|
|
_uintType,
|
|
_module.AddInstruction(SpirvOp.ConvertSToF, _floatType, signed));
|
|
break;
|
|
}
|
|
case "VCvtF32U32":
|
|
result = Bitcast(
|
|
_uintType,
|
|
_module.AddInstruction(
|
|
SpirvOp.ConvertUToF,
|
|
_floatType,
|
|
GetRawSource(instruction, 0)));
|
|
break;
|
|
case "VCvtF32Ubyte0":
|
|
case "VCvtF32Ubyte1":
|
|
case "VCvtF32Ubyte2":
|
|
case "VCvtF32Ubyte3":
|
|
{
|
|
var shift = (uint)(instruction.Opcode[^1] - '0') * 8;
|
|
var raw = ShiftRightLogical(GetRawSource(instruction, 0), UInt(shift));
|
|
raw = BitwiseAnd(raw, UInt(0xFF));
|
|
result = Bitcast(
|
|
_uintType,
|
|
_module.AddInstruction(SpirvOp.ConvertUToF, _floatType, raw));
|
|
break;
|
|
}
|
|
case "VCvtF16F32":
|
|
{
|
|
var vector = _module.AddInstruction(
|
|
SpirvOp.CompositeConstruct,
|
|
_vec2Type,
|
|
GetFloatSource(instruction, 0),
|
|
Float(0));
|
|
result = BitwiseAnd(Ext(58, _uintType, vector), UInt(0xFFFF));
|
|
break;
|
|
}
|
|
case "VCvtF32F16":
|
|
{
|
|
var unpacked = Ext(62, _vec2Type, GetRawSource(instruction, 0));
|
|
var value = _module.AddInstruction(
|
|
SpirvOp.CompositeExtract,
|
|
_floatType,
|
|
unpacked,
|
|
0);
|
|
result = Bitcast(_uintType, value);
|
|
break;
|
|
}
|
|
case "VCvtOffF32I4":
|
|
result = EmitCvtOffF32I4(instruction);
|
|
break;
|
|
case "VCvtPkU8F32":
|
|
{
|
|
var converted = _module.AddInstruction(
|
|
SpirvOp.ConvertFToU,
|
|
_uintType,
|
|
GetFloatSource(instruction, 0));
|
|
var offset = ShiftLeftLogical(
|
|
BitwiseAnd(GetRawSource(instruction, 1), UInt(3)),
|
|
UInt(3));
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitFieldInsert,
|
|
_uintType,
|
|
GetRawSource(instruction, 2),
|
|
converted,
|
|
offset,
|
|
UInt(8));
|
|
break;
|
|
}
|
|
case "VRcpF32":
|
|
case "VRcpIflagF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
_module.AddInstruction(
|
|
SpirvOp.FDiv,
|
|
_floatType,
|
|
Float(1),
|
|
GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VLogF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(30, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VLdexpF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(
|
|
53,
|
|
_floatType,
|
|
GetFloatSource(instruction, 0),
|
|
Bitcast(_intType, GetRawSource(instruction, 1))));
|
|
break;
|
|
case "VExpF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(29, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VRsqF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(32, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VFractF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(10, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VTruncF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(3, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VCeilF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(9, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VRndneF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(2, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VFloorF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(8, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VSqrtF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(31, _floatType, GetFloatSource(instruction, 0)));
|
|
break;
|
|
case "VSinF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(
|
|
13,
|
|
_floatType,
|
|
_module.AddInstruction(
|
|
SpirvOp.FMul,
|
|
_floatType,
|
|
GetFloatSource(instruction, 0),
|
|
Float(MathF.Tau))));
|
|
break;
|
|
case "VCosF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(
|
|
14,
|
|
_floatType,
|
|
_module.AddInstruction(
|
|
SpirvOp.FMul,
|
|
_floatType,
|
|
GetFloatSource(instruction, 0),
|
|
Float(MathF.Tau))));
|
|
break;
|
|
case "VAddF32":
|
|
result = EmitFloatBinary(instruction, SpirvOp.FAdd);
|
|
break;
|
|
case "VSubF32":
|
|
result = EmitFloatBinary(instruction, SpirvOp.FSub);
|
|
break;
|
|
case "VSubrevF32":
|
|
result = EmitFloatBinary(instruction, SpirvOp.FSub, reverse: true);
|
|
break;
|
|
case "VMulF32":
|
|
result = EmitFloatBinary(instruction, SpirvOp.FMul);
|
|
break;
|
|
case "VMinF32":
|
|
result = EmitFloatExtBinary(instruction, 37);
|
|
break;
|
|
case "VMaxF32":
|
|
result = EmitFloatExtBinary(instruction, 40);
|
|
break;
|
|
case "VMadF32":
|
|
case "VFmaF32":
|
|
case "VMadMkF32":
|
|
case "VMadAkF32":
|
|
case "VFmamkF32":
|
|
case "VFmaakF32":
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(
|
|
50,
|
|
_floatType,
|
|
GetFloatSource(instruction, 0),
|
|
GetFloatSource(instruction, 1),
|
|
GetFloatSource(instruction, 2)));
|
|
break;
|
|
case "VMacF32":
|
|
case "VFmacF32":
|
|
{
|
|
var addend = Bitcast(_floatType, LoadV(destination));
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(
|
|
50,
|
|
_floatType,
|
|
GetFloatSource(instruction, 0),
|
|
GetFloatSource(instruction, 1),
|
|
addend));
|
|
break;
|
|
}
|
|
case "VMin3F32":
|
|
result = EmitFloatTernaryExt(instruction, 37);
|
|
break;
|
|
case "VMax3F32":
|
|
result = EmitFloatTernaryExt(instruction, 40);
|
|
break;
|
|
case "VAndB32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.BitwiseAnd);
|
|
break;
|
|
case "VOrB32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.BitwiseOr);
|
|
break;
|
|
case "VXorB32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.BitwiseXor);
|
|
break;
|
|
case "VNotB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_uintType,
|
|
GetRawSource(instruction, 0));
|
|
break;
|
|
case "VBfrevB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitReverse,
|
|
_uintType,
|
|
GetRawSource(instruction, 0));
|
|
break;
|
|
case "VFfblB32":
|
|
result = Bitcast(
|
|
_uintType,
|
|
Ext(
|
|
73,
|
|
_intType,
|
|
Bitcast(_intType, GetRawSource(instruction, 0))));
|
|
break;
|
|
case "VAddI32":
|
|
case "VAddU32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.IAdd);
|
|
break;
|
|
case "VAddcU32":
|
|
result = EmitAddWithCarry(instruction);
|
|
break;
|
|
case "VSubI32":
|
|
case "VSubU32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.ISub);
|
|
break;
|
|
case "VSubrevI32":
|
|
case "VSubrevU32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.ISub, reverse: true);
|
|
break;
|
|
case "VSubbU32":
|
|
result = EmitSubtractWithBorrow(instruction, reverse: false);
|
|
break;
|
|
case "VSubbrevU32":
|
|
result = EmitSubtractWithBorrow(instruction, reverse: true);
|
|
break;
|
|
case "VMulLoU32":
|
|
case "VMulLoI32":
|
|
case "VMulU32U24":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.IMul);
|
|
break;
|
|
case "VMulHiU32":
|
|
case "VMulHiU32U24":
|
|
{
|
|
var left = GetRawSource(instruction, 0);
|
|
var right = GetRawSource(instruction, 1);
|
|
if (instruction.Opcode == "VMulHiU32U24")
|
|
{
|
|
left = BitwiseAnd(left, UInt(0x00FF_FFFF));
|
|
right = BitwiseAnd(right, UInt(0x00FF_FFFF));
|
|
}
|
|
|
|
var wideLeft = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
left);
|
|
var wideRight = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
right);
|
|
var product = _module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_ulongType,
|
|
wideLeft,
|
|
wideRight);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
ShiftRightLogical64(
|
|
product,
|
|
_module.Constant64(_ulongType, 32)));
|
|
break;
|
|
}
|
|
case "VMulHiI32":
|
|
{
|
|
var wideLeft = _module.AddInstruction(
|
|
SpirvOp.SConvert,
|
|
_longType,
|
|
Bitcast(_intType, GetRawSource(instruction, 0)));
|
|
var wideRight = _module.AddInstruction(
|
|
SpirvOp.SConvert,
|
|
_longType,
|
|
Bitcast(_intType, GetRawSource(instruction, 1)));
|
|
var product = _module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_longType,
|
|
wideLeft,
|
|
wideRight);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
ShiftRightLogical64(
|
|
Bitcast(_ulongType, product),
|
|
_module.Constant64(_ulongType, 32)));
|
|
break;
|
|
}
|
|
case "VBcntU32B32":
|
|
result = IAdd(
|
|
_module.AddInstruction(
|
|
SpirvOp.BitCount,
|
|
_uintType,
|
|
GetRawSource(instruction, 0)),
|
|
GetRawSource(instruction, 1));
|
|
break;
|
|
case "VMadU32U24":
|
|
{
|
|
var left = BitwiseAnd(
|
|
GetRawSource(instruction, 0),
|
|
UInt(0x00FF_FFFF));
|
|
var right = BitwiseAnd(
|
|
GetRawSource(instruction, 1),
|
|
UInt(0x00FF_FFFF));
|
|
result = IAdd(
|
|
_module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_uintType,
|
|
left,
|
|
right),
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
}
|
|
case "VMadU32U16":
|
|
{
|
|
var left = BitwiseAnd(
|
|
GetRawSource(instruction, 0),
|
|
UInt(0xFFFF));
|
|
var right = BitwiseAnd(
|
|
GetRawSource(instruction, 1),
|
|
UInt(0xFFFF));
|
|
result = IAdd(
|
|
_module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_uintType,
|
|
left,
|
|
right),
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
}
|
|
case "VLshrB32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.ShiftRightLogical);
|
|
break;
|
|
case "VLshrrevB32":
|
|
result = EmitIntegerBinary(
|
|
instruction,
|
|
SpirvOp.ShiftRightLogical,
|
|
reverse: true);
|
|
break;
|
|
case "VLshlB32":
|
|
result = EmitIntegerBinary(instruction, SpirvOp.ShiftLeftLogical);
|
|
break;
|
|
case "VLshlrevB32":
|
|
result = EmitIntegerBinary(
|
|
instruction,
|
|
SpirvOp.ShiftLeftLogical,
|
|
reverse: true);
|
|
break;
|
|
case "VAshrI32":
|
|
case "VAshrrevI32":
|
|
{
|
|
var reverse = instruction.Opcode == "VAshrrevI32";
|
|
var left = GetRawSource(instruction, reverse ? 1 : 0);
|
|
var right = GetRawSource(instruction, reverse ? 0 : 1);
|
|
result = ShiftRightArithmetic(left, right);
|
|
break;
|
|
}
|
|
case "VLshlAddU32":
|
|
{
|
|
var shifted = ShiftLeftLogical(
|
|
GetRawSource(instruction, 0),
|
|
BitwiseAnd(GetRawSource(instruction, 1), UInt(31)));
|
|
result = IAdd(shifted, GetRawSource(instruction, 2));
|
|
break;
|
|
}
|
|
case "VLshlOrU32":
|
|
{
|
|
var shifted = ShiftLeftLogical(
|
|
GetRawSource(instruction, 0),
|
|
BitwiseAnd(GetRawSource(instruction, 1), UInt(31)));
|
|
result = BitwiseOr(
|
|
shifted,
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
}
|
|
case "VAndOrB32":
|
|
result = BitwiseOr(
|
|
BitwiseAnd(
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1)),
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
case "VOr3U32":
|
|
result = BitwiseOr(
|
|
BitwiseOr(
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1)),
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
case "VPermlane16B32":
|
|
result = EmitPermlane16(instruction, exchangeRows: false);
|
|
break;
|
|
case "VPermlanex16B32":
|
|
result = EmitPermlane16(instruction, exchangeRows: true);
|
|
break;
|
|
case "VAddLshlU32":
|
|
{
|
|
var added = IAdd(
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1));
|
|
result = ShiftLeftLogical(added, GetRawSource(instruction, 2));
|
|
break;
|
|
}
|
|
case "VAdd3U32":
|
|
result = IAdd(
|
|
IAdd(
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1)),
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
case "VMinU32":
|
|
result = Ext(
|
|
38,
|
|
_uintType,
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1));
|
|
break;
|
|
case "VMaxU32":
|
|
result = Ext(
|
|
41,
|
|
_uintType,
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1));
|
|
break;
|
|
case "VMin3U32":
|
|
result = Ext(
|
|
38,
|
|
_uintType,
|
|
Ext(
|
|
38,
|
|
_uintType,
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1)),
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
case "VMax3U32":
|
|
result = Ext(
|
|
41,
|
|
_uintType,
|
|
Ext(
|
|
41,
|
|
_uintType,
|
|
GetRawSource(instruction, 0),
|
|
GetRawSource(instruction, 1)),
|
|
GetRawSource(instruction, 2));
|
|
break;
|
|
case "VMinI32":
|
|
case "VMaxI32":
|
|
{
|
|
var signedResult = Ext(
|
|
instruction.Opcode == "VMinI32" ? 39u : 42u,
|
|
_intType,
|
|
Bitcast(_intType, GetRawSource(instruction, 0)),
|
|
Bitcast(_intType, GetRawSource(instruction, 1)));
|
|
result = Bitcast(_uintType, signedResult);
|
|
break;
|
|
}
|
|
case "VMin3I32":
|
|
case "VMax3I32":
|
|
{
|
|
var operation = instruction.Opcode == "VMin3I32" ? 39u : 42u;
|
|
var left = Bitcast(
|
|
_intType,
|
|
GetRawSource(instruction, 0));
|
|
var middle = Bitcast(
|
|
_intType,
|
|
GetRawSource(instruction, 1));
|
|
var right = Bitcast(
|
|
_intType,
|
|
GetRawSource(instruction, 2));
|
|
result = Bitcast(
|
|
_uintType,
|
|
Ext(
|
|
operation,
|
|
_intType,
|
|
Ext(operation, _intType, left, middle),
|
|
right));
|
|
break;
|
|
}
|
|
case "VMed3U32":
|
|
{
|
|
var left = GetRawSource(instruction, 0);
|
|
var middle = GetRawSource(instruction, 1);
|
|
var right = GetRawSource(instruction, 2);
|
|
var low = Ext(38, _uintType, left, middle);
|
|
var high = Ext(41, _uintType, left, middle);
|
|
result = Ext(
|
|
41,
|
|
_uintType,
|
|
low,
|
|
Ext(38, _uintType, high, right));
|
|
break;
|
|
}
|
|
case "VSadU32":
|
|
{
|
|
var left = GetRawSource(instruction, 0);
|
|
var right = GetRawSource(instruction, 1);
|
|
var difference = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
Ext(41, _uintType, left, right),
|
|
Ext(38, _uintType, left, right));
|
|
result = IAdd(difference, GetRawSource(instruction, 2));
|
|
break;
|
|
}
|
|
case "VMed3I32":
|
|
{
|
|
var left = Bitcast(_intType, GetRawSource(instruction, 0));
|
|
var middle = Bitcast(_intType, GetRawSource(instruction, 1));
|
|
var right = Bitcast(_intType, GetRawSource(instruction, 2));
|
|
var low = Ext(39, _intType, left, middle);
|
|
var high = Ext(42, _intType, left, middle);
|
|
result = Bitcast(
|
|
_uintType,
|
|
Ext(
|
|
42,
|
|
_intType,
|
|
low,
|
|
Ext(39, _intType, high, right)));
|
|
break;
|
|
}
|
|
case "VMed3F32":
|
|
{
|
|
var left = GetFloatSource(instruction, 0);
|
|
var middle = GetFloatSource(instruction, 1);
|
|
var right = GetFloatSource(instruction, 2);
|
|
var low = Ext(37, _floatType, left, middle);
|
|
var high = Ext(40, _floatType, left, middle);
|
|
result = EmitFloatResult(
|
|
instruction,
|
|
Ext(
|
|
40,
|
|
_floatType,
|
|
low,
|
|
Ext(37, _floatType, high, right)));
|
|
break;
|
|
}
|
|
case "VCubeidF32":
|
|
result = EmitCubeCoordinate(instruction, CubeCoordinate.Id);
|
|
break;
|
|
case "VCubescF32":
|
|
result = EmitCubeCoordinate(instruction, CubeCoordinate.Sc);
|
|
break;
|
|
case "VCubetcF32":
|
|
result = EmitCubeCoordinate(instruction, CubeCoordinate.Tc);
|
|
break;
|
|
case "VCubemaF32":
|
|
result = EmitCubeCoordinate(instruction, CubeCoordinate.Ma);
|
|
break;
|
|
case "VAddCoU32":
|
|
{
|
|
var left = GetRawSource(instruction, 0);
|
|
var right = GetRawSource(instruction, 1);
|
|
result = IAdd(left, right);
|
|
var carry = _module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
result,
|
|
left);
|
|
StoreCarryOut(instruction, carry);
|
|
break;
|
|
}
|
|
case "VSubCoU32":
|
|
case "VSubrevCoU32":
|
|
{
|
|
var reverse = instruction.Opcode == "VSubrevCoU32";
|
|
var left = GetRawSource(instruction, reverse ? 1 : 0);
|
|
var right = GetRawSource(instruction, reverse ? 0 : 1);
|
|
result = _module.AddInstruction(SpirvOp.ISub, _uintType, left, right);
|
|
var borrow = _module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
left,
|
|
right);
|
|
StoreCarryOut(instruction, borrow);
|
|
break;
|
|
}
|
|
case "VBfeU32":
|
|
{
|
|
var width = BitwiseAnd(GetRawSource(instruction, 2), UInt(31));
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitFieldUExtract,
|
|
_uintType,
|
|
GetRawSource(instruction, 0),
|
|
BitwiseAnd(GetRawSource(instruction, 1), UInt(31)),
|
|
width);
|
|
break;
|
|
}
|
|
case "VBfiB32":
|
|
{
|
|
var mask = GetRawSource(instruction, 0);
|
|
var insert = GetRawSource(instruction, 1);
|
|
var source = GetRawSource(instruction, 2);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_uintType,
|
|
BitwiseAnd(mask, insert),
|
|
BitwiseAnd(
|
|
_module.AddInstruction(SpirvOp.Not, _uintType, mask),
|
|
source));
|
|
break;
|
|
}
|
|
case "VCvtPkrtzF16F32":
|
|
{
|
|
var first = TruncateFloat32ForPack(GetFloatSource(instruction, 0));
|
|
var second = TruncateFloat32ForPack(GetFloatSource(instruction, 1));
|
|
var vector = _module.AddInstruction(
|
|
SpirvOp.CompositeConstruct,
|
|
_vec2Type,
|
|
first,
|
|
second);
|
|
result = Ext(58, _uintType, vector);
|
|
break;
|
|
}
|
|
case "VCvtPkU16U32":
|
|
case "VCvtPkI16I32":
|
|
result = BitwiseOr(
|
|
BitwiseAnd(GetRawSource(instruction, 0), UInt(0xFFFF)),
|
|
ShiftLeftLogical(
|
|
BitwiseAnd(GetRawSource(instruction, 1), UInt(0xFFFF)),
|
|
UInt(16)));
|
|
break;
|
|
default:
|
|
error = $"unsupported vector opcode {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
StoreV(destination, result);
|
|
return true;
|
|
}
|
|
|
|
private bool TryEmitVectorCompare(
|
|
Gen5ShaderInstruction instruction,
|
|
out string error)
|
|
{
|
|
error = string.Empty;
|
|
uint condition = _module.ConstantBool(false);
|
|
var opcode = instruction.Opcode;
|
|
if (opcode is "VCmpClassF32" or "VCmpxClassF32")
|
|
{
|
|
var source = GetFloatSource(instruction, 0);
|
|
var raw = GetRawSource(instruction, 0);
|
|
var mask = GetRawSource(instruction, 1);
|
|
var negative = IsNotZero(BitwiseAnd(raw, UInt(0x8000_0000)));
|
|
var positive = _module.AddInstruction(
|
|
SpirvOp.LogicalNot,
|
|
_boolType,
|
|
negative);
|
|
var nan = _module.AddInstruction(SpirvOp.IsNan, _boolType, source);
|
|
var infinity =
|
|
_module.AddInstruction(SpirvOp.IsInf, _boolType, source);
|
|
var zero = _module.AddInstruction(
|
|
SpirvOp.FOrdEqual,
|
|
_boolType,
|
|
source,
|
|
Float(0));
|
|
var absolute = Ext(4, _floatType, source);
|
|
var nonzero = _module.AddInstruction(
|
|
SpirvOp.FOrdGreaterThan,
|
|
_boolType,
|
|
absolute,
|
|
Float(0));
|
|
var belowNormal = _module.AddInstruction(
|
|
SpirvOp.FOrdLessThan,
|
|
_boolType,
|
|
absolute,
|
|
Bitcast(_floatType, UInt(0x0080_0000)));
|
|
var subnormal = _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
nonzero,
|
|
belowNormal);
|
|
var special = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
nan,
|
|
_module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
infinity,
|
|
_module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
zero,
|
|
subnormal)));
|
|
var normal = _module.AddInstruction(
|
|
SpirvOp.LogicalNot,
|
|
_boolType,
|
|
special);
|
|
|
|
uint MaskedClass(uint bits, uint value)
|
|
{
|
|
var enabled = IsNotZero(BitwiseAnd(mask, UInt(bits)));
|
|
return _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
enabled,
|
|
value);
|
|
}
|
|
|
|
uint SignedClass(uint negativeBit, uint positiveBit, uint value)
|
|
{
|
|
var negativeClass = MaskedClass(
|
|
negativeBit,
|
|
_module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
negative,
|
|
value));
|
|
var positiveClass = MaskedClass(
|
|
positiveBit,
|
|
_module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
positive,
|
|
value));
|
|
return _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
negativeClass,
|
|
positiveClass);
|
|
}
|
|
|
|
condition = MaskedClass(0x003, nan);
|
|
condition = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
condition,
|
|
SignedClass(0x004, 0x200, infinity));
|
|
condition = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
condition,
|
|
SignedClass(0x008, 0x100, normal));
|
|
condition = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
condition,
|
|
SignedClass(0x010, 0x080, subnormal));
|
|
condition = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
condition,
|
|
SignedClass(0x020, 0x040, zero));
|
|
}
|
|
else if (opcode is "VCmpFF32" or "VCmpxFF32" or "VCmpFI32" or "VCmpFU32")
|
|
{
|
|
condition = _module.ConstantBool(false);
|
|
}
|
|
else if (opcode is "VCmpTruF32" or "VCmpxTruF32" or "VCmpTI32" or "VCmpTU32")
|
|
{
|
|
condition = _module.ConstantBool(true);
|
|
}
|
|
else if (opcode is "VCmpOF32" or "VCmpxOF32" or "VCmpUF32" or "VCmpxUF32")
|
|
{
|
|
// The ordered/unordered predicates only test whether either
|
|
// operand is NaN. SPIR-V's OpOrdered/OpUnordered are Kernel-only,
|
|
// so build the same result from OpIsNan, which needs no extra
|
|
// capability: unordered = isnan(a) || isnan(b), ordered = !that.
|
|
var left = GetFloatSource(instruction, 0);
|
|
var right = GetFloatSource(instruction, 1);
|
|
var nanLeft = _module.AddInstruction(SpirvOp.IsNan, _boolType, left);
|
|
var nanRight = _module.AddInstruction(SpirvOp.IsNan, _boolType, right);
|
|
var unordered = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
nanLeft,
|
|
nanRight);
|
|
condition = opcode is "VCmpUF32" or "VCmpxUF32"
|
|
? unordered
|
|
: _module.AddInstruction(SpirvOp.LogicalNot, _boolType, unordered);
|
|
}
|
|
else if (opcode is not ("VCmpClassF32" or "VCmpxClassF32") &&
|
|
opcode.EndsWith("F32", StringComparison.Ordinal))
|
|
{
|
|
var left = GetFloatSource(instruction, 0);
|
|
var right = GetFloatSource(instruction, 1);
|
|
var operation = opcode switch
|
|
{
|
|
"VCmpLtF32" or "VCmpxLtF32" => SpirvOp.FOrdLessThan,
|
|
"VCmpEqF32" or "VCmpxEqF32" => SpirvOp.FOrdEqual,
|
|
"VCmpLeF32" or "VCmpxLeF32" => SpirvOp.FOrdLessThanEqual,
|
|
"VCmpGtF32" or "VCmpxGtF32" => SpirvOp.FOrdGreaterThan,
|
|
"VCmpLgF32" or "VCmpxLgF32" => SpirvOp.FOrdNotEqual,
|
|
"VCmpGeF32" or "VCmpxGeF32" => SpirvOp.FOrdGreaterThanEqual,
|
|
"VCmpNeqF32" or "VCmpxNeqF32" => SpirvOp.FUnordNotEqual,
|
|
"VCmpNlgF32" or "VCmpxNlgF32" => SpirvOp.FUnordEqual,
|
|
"VCmpNltF32" or "VCmpxNltF32" => SpirvOp.FUnordGreaterThanEqual,
|
|
"VCmpNleF32" or "VCmpxNleF32" => SpirvOp.FUnordGreaterThan,
|
|
"VCmpNgtF32" or "VCmpxNgtF32" => SpirvOp.FUnordLessThanEqual,
|
|
"VCmpNgeF32" or "VCmpxNgeF32" => SpirvOp.FUnordLessThan,
|
|
_ => SpirvOp.Nop,
|
|
};
|
|
if (operation == SpirvOp.Nop)
|
|
{
|
|
error = $"unsupported float compare {opcode}";
|
|
return false;
|
|
}
|
|
|
|
condition = _module.AddInstruction(operation, _boolType, left, right);
|
|
}
|
|
else if (opcode is not ("VCmpClassF32" or "VCmpxClassF32"))
|
|
{
|
|
var left = GetRawSource(instruction, 0);
|
|
var right = GetRawSource(instruction, 1);
|
|
var signed = opcode.EndsWith("I32", StringComparison.Ordinal);
|
|
if (signed)
|
|
{
|
|
left = Bitcast(_intType, left);
|
|
right = Bitcast(_intType, right);
|
|
}
|
|
|
|
var operation = opcode switch
|
|
{
|
|
"VCmpEqI32" or "VCmpxEqI32" or
|
|
"VCmpEqU32" or "VCmpxEqU32" => SpirvOp.IEqual,
|
|
"VCmpNeI32" or "VCmpxNeI32" or
|
|
"VCmpNeU32" or "VCmpxNeU32" => SpirvOp.INotEqual,
|
|
"VCmpLtI32" or "VCmpxLtI32" => SpirvOp.SLessThan,
|
|
"VCmpLeI32" or "VCmpxLeI32" => SpirvOp.SLessThanEqual,
|
|
"VCmpGtI32" or "VCmpxGtI32" => SpirvOp.SGreaterThan,
|
|
"VCmpGeI32" or "VCmpxGeI32" => SpirvOp.SGreaterThanEqual,
|
|
"VCmpLtU32" or "VCmpxLtU32" => SpirvOp.ULessThan,
|
|
"VCmpLeU32" or "VCmpxLeU32" => SpirvOp.ULessThanEqual,
|
|
"VCmpGtU32" or "VCmpxGtU32" => SpirvOp.UGreaterThan,
|
|
"VCmpGeU32" or "VCmpxGeU32" => SpirvOp.UGreaterThanEqual,
|
|
_ => SpirvOp.Nop,
|
|
};
|
|
if (operation == SpirvOp.Nop)
|
|
{
|
|
error = $"unsupported integer compare {opcode}";
|
|
return false;
|
|
}
|
|
|
|
condition = _module.AddInstruction(operation, _boolType, left, right);
|
|
}
|
|
|
|
// On gfx10, VCmpx writes EXEC only and preserves VCC; the sdst
|
|
// operand was removed from the cmpx encodings on this generation.
|
|
if (opcode.StartsWith("VCmpx", StringComparison.Ordinal))
|
|
{
|
|
var active = _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
Load(_boolType, _exec),
|
|
condition);
|
|
StoreWaveMask(126, active);
|
|
}
|
|
else
|
|
{
|
|
StoreWaveMask(106, condition);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool TryEmitScalarAlu(
|
|
Gen5ShaderInstruction instruction,
|
|
out string error)
|
|
{
|
|
error = string.Empty;
|
|
if (instruction.Encoding == Gen5ShaderEncoding.Sopc)
|
|
{
|
|
return TryEmitScalarCompare(instruction, out error);
|
|
}
|
|
|
|
if (instruction.Destinations.Count == 0 ||
|
|
instruction.Destinations[0].Kind != Gen5OperandKind.ScalarRegister)
|
|
{
|
|
error = "missing scalar destination";
|
|
return false;
|
|
}
|
|
|
|
var destination = instruction.Destinations[0].Value;
|
|
if (instruction.Encoding == Gen5ShaderEncoding.Sopk)
|
|
{
|
|
var immediate = unchecked((uint)(short)(instruction.Words[0] & 0xFFFF));
|
|
if (instruction.Opcode.StartsWith("SCmpk", StringComparison.Ordinal))
|
|
{
|
|
return TryEmitScalarCompareK(instruction, destination, immediate, out error);
|
|
}
|
|
|
|
var current = LoadS(destination);
|
|
var value = instruction.Opcode switch
|
|
{
|
|
"SMovkI32" => UInt(immediate),
|
|
"SAddkI32" => IAdd(current, UInt(immediate)),
|
|
"SMulkI32" => _module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_uintType,
|
|
current,
|
|
UInt(immediate)),
|
|
_ => 0u,
|
|
};
|
|
if (value == 0)
|
|
{
|
|
error = $"unsupported scalar immediate {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
StoreS(destination, value);
|
|
return true;
|
|
}
|
|
|
|
if (instruction.Opcode == "SGetpcB64")
|
|
{
|
|
var pc = _state.Program.Address +
|
|
instruction.Pc +
|
|
(ulong)(instruction.Words.Count * sizeof(uint));
|
|
StoreS(destination, UInt((uint)pc));
|
|
StoreS(destination + 1, UInt((uint)(pc >> 32)));
|
|
return true;
|
|
}
|
|
|
|
if (instruction.Opcode.EndsWith("B64", StringComparison.Ordinal) ||
|
|
instruction.Opcode is "SWqmB64" or "SBfeU64" or "SBfeI64")
|
|
{
|
|
return TryEmitScalar64(instruction, destination, out error);
|
|
}
|
|
|
|
var left = GetRawSource(instruction, 0);
|
|
uint result;
|
|
switch (instruction.Opcode)
|
|
{
|
|
case "SMovB32":
|
|
result = left;
|
|
break;
|
|
case "SNotB32":
|
|
result = _module.AddInstruction(SpirvOp.Not, _uintType, left);
|
|
StoreS(destination, result);
|
|
Store(_scc, IsNotZero(result));
|
|
return true;
|
|
case "SBrevB32":
|
|
result = _module.AddInstruction(SpirvOp.BitReverse, _uintType, left);
|
|
StoreS(destination, result);
|
|
Store(_scc, IsNotZero(result));
|
|
return true;
|
|
case "SBcnt1I32B32":
|
|
result = _module.AddInstruction(SpirvOp.BitCount, _uintType, left);
|
|
StoreS(destination, result);
|
|
Store(_scc, IsNotZero(result));
|
|
return true;
|
|
case "SFF1I32B32":
|
|
result = Ext(73, _uintType, left);
|
|
StoreS(destination, result);
|
|
Store(_scc, IsNotZero(result));
|
|
return true;
|
|
case "SBitset1B32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitFieldInsert,
|
|
_uintType,
|
|
LoadS(destination),
|
|
UInt(1),
|
|
BitwiseAnd(left, UInt(31)),
|
|
UInt(1));
|
|
StoreS(destination, result);
|
|
return true;
|
|
default:
|
|
{
|
|
if (instruction.Sources.Count < 2)
|
|
{
|
|
error = $"missing scalar source for {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
var right = GetRawSource(instruction, 1);
|
|
switch (instruction.Opcode)
|
|
{
|
|
case "SAddU32":
|
|
result = IAdd(left, right);
|
|
Store(_scc, _module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
result,
|
|
left));
|
|
break;
|
|
case "SSubU32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
left,
|
|
right);
|
|
Store(_scc, _module.AddInstruction(
|
|
SpirvOp.UGreaterThan,
|
|
_boolType,
|
|
right,
|
|
left));
|
|
break;
|
|
case "SAddI32":
|
|
result = IAdd(left, right);
|
|
Store(_scc, SignedAddOverflow(left, right, result));
|
|
break;
|
|
case "SSubI32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
left,
|
|
right);
|
|
Store(_scc, SignedSubOverflow(left, right, result));
|
|
break;
|
|
case "SAddcU32":
|
|
{
|
|
var carryIn = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
Load(_boolType, _scc),
|
|
UInt(1),
|
|
UInt(0));
|
|
var partial = IAdd(left, right);
|
|
result = IAdd(partial, carryIn);
|
|
var firstCarry = _module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
partial,
|
|
left);
|
|
var secondCarry = _module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
result,
|
|
partial);
|
|
Store(
|
|
_scc,
|
|
_module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
firstCarry,
|
|
secondCarry));
|
|
break;
|
|
}
|
|
case "SSubbU32":
|
|
{
|
|
var borrow = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
Load(_boolType, _scc),
|
|
UInt(1),
|
|
UInt(0));
|
|
var partial = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
left,
|
|
right);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
partial,
|
|
borrow);
|
|
var firstBorrow = _module.AddInstruction(
|
|
SpirvOp.UGreaterThan,
|
|
_boolType,
|
|
right,
|
|
left);
|
|
var secondBorrow = _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
_module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
borrow,
|
|
UInt(1)),
|
|
_module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
right,
|
|
left));
|
|
Store(
|
|
_scc,
|
|
_module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
firstBorrow,
|
|
secondBorrow));
|
|
break;
|
|
}
|
|
case "SMulI32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_uintType,
|
|
left,
|
|
right);
|
|
break;
|
|
case "SMulHiU32":
|
|
{
|
|
var wideLeft = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
left);
|
|
var wideRight = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
right);
|
|
var product = _module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_ulongType,
|
|
wideLeft,
|
|
wideRight);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
ShiftRightLogical64(
|
|
product,
|
|
_module.Constant64(_ulongType, 32)));
|
|
break;
|
|
}
|
|
case "SMulHiI32":
|
|
{
|
|
var wideLeft = _module.AddInstruction(
|
|
SpirvOp.SConvert,
|
|
_longType,
|
|
Bitcast(_intType, left));
|
|
var wideRight = _module.AddInstruction(
|
|
SpirvOp.SConvert,
|
|
_longType,
|
|
Bitcast(_intType, right));
|
|
var product = _module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_longType,
|
|
wideLeft,
|
|
wideRight);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
ShiftRightLogical64(
|
|
Bitcast(_ulongType, product),
|
|
_module.Constant64(_ulongType, 32)));
|
|
break;
|
|
}
|
|
case "SAndB32":
|
|
result = BitwiseAnd(left, right);
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SOrB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_uintType,
|
|
left,
|
|
right);
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SXorB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitwiseXor,
|
|
_uintType,
|
|
left,
|
|
right);
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SAndn2B32":
|
|
result = BitwiseAnd(
|
|
left,
|
|
_module.AddInstruction(SpirvOp.Not, _uintType, right));
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SOrn2B32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_uintType,
|
|
left,
|
|
_module.AddInstruction(SpirvOp.Not, _uintType, right));
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SNandB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_uintType,
|
|
BitwiseAnd(left, right));
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SNorB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_uintType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_uintType,
|
|
left,
|
|
right));
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SXnorB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_uintType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseXor,
|
|
_uintType,
|
|
left,
|
|
right));
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SLshlB32":
|
|
result = ShiftLeftLogical(left, right);
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SLshrB32":
|
|
result = ShiftRightLogical(
|
|
left,
|
|
BitwiseAnd(right, UInt(31)));
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SAshrI32":
|
|
result = ShiftRightArithmetic(left, right);
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
case "SBfmB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.BitFieldInsert,
|
|
_uintType,
|
|
UInt(0),
|
|
UInt(uint.MaxValue),
|
|
BitwiseAnd(right, UInt(31)),
|
|
BitwiseAnd(left, UInt(31)));
|
|
break;
|
|
case "SBfeU32":
|
|
case "SBfeI32":
|
|
{
|
|
var offset = BitwiseAnd(right, UInt(31));
|
|
var requestedWidth = BitwiseAnd(
|
|
ShiftRightLogical(right, UInt(16)),
|
|
UInt(0x7F));
|
|
var remaining = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
UInt(32),
|
|
offset);
|
|
var width = Ext(
|
|
38,
|
|
_uintType,
|
|
requestedWidth,
|
|
remaining);
|
|
result = instruction.Opcode == "SBfeI32"
|
|
? Bitcast(
|
|
_uintType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitFieldSExtract,
|
|
_intType,
|
|
Bitcast(_intType, left),
|
|
offset,
|
|
width))
|
|
: _module.AddInstruction(
|
|
SpirvOp.BitFieldUExtract,
|
|
_uintType,
|
|
left,
|
|
offset,
|
|
width);
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
}
|
|
case "SAbsdiffI32":
|
|
{
|
|
var wideLeft = _module.AddInstruction(
|
|
SpirvOp.SConvert,
|
|
_longType,
|
|
Bitcast(_intType, left));
|
|
var wideRight = _module.AddInstruction(
|
|
SpirvOp.SConvert,
|
|
_longType,
|
|
Bitcast(_intType, right));
|
|
var difference = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_longType,
|
|
wideLeft,
|
|
wideRight);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
Ext(5, _longType, difference));
|
|
Store(_scc, IsNotZero(result));
|
|
break;
|
|
}
|
|
case "SPackLlB32B16":
|
|
result = BitwiseOr(
|
|
BitwiseAnd(left, UInt(0xFFFF)),
|
|
ShiftLeftLogical(right, UInt(16)));
|
|
break;
|
|
case "SPackLhB32B16":
|
|
result = BitwiseOr(
|
|
BitwiseAnd(left, UInt(0xFFFF)),
|
|
BitwiseAnd(right, UInt(0xFFFF0000)));
|
|
break;
|
|
case "SPackHhB32B16":
|
|
result = BitwiseOr(
|
|
ShiftRightLogical(left, UInt(16)),
|
|
BitwiseAnd(right, UInt(0xFFFF0000)));
|
|
break;
|
|
case "SCselectB32":
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
Load(_boolType, _scc),
|
|
left,
|
|
right);
|
|
break;
|
|
case "SMinU32":
|
|
result = Ext(38, _uintType, left, right);
|
|
Store(
|
|
_scc,
|
|
_module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
left,
|
|
right));
|
|
break;
|
|
case "SMinI32":
|
|
result = Bitcast(
|
|
_uintType,
|
|
Ext(39, _intType, Bitcast(_intType, left), Bitcast(_intType, right)));
|
|
Store(
|
|
_scc,
|
|
_module.AddInstruction(
|
|
SpirvOp.SLessThan,
|
|
_boolType,
|
|
Bitcast(_intType, left),
|
|
Bitcast(_intType, right)));
|
|
break;
|
|
case "SMaxU32":
|
|
result = Ext(41, _uintType, left, right);
|
|
Store(
|
|
_scc,
|
|
_module.AddInstruction(
|
|
SpirvOp.UGreaterThan,
|
|
_boolType,
|
|
left,
|
|
right));
|
|
break;
|
|
case "SMaxI32":
|
|
result = Bitcast(
|
|
_uintType,
|
|
Ext(42, _intType, Bitcast(_intType, left), Bitcast(_intType, right)));
|
|
Store(
|
|
_scc,
|
|
_module.AddInstruction(
|
|
SpirvOp.SGreaterThan,
|
|
_boolType,
|
|
Bitcast(_intType, left),
|
|
Bitcast(_intType, right)));
|
|
break;
|
|
case "SLshl1AddU32":
|
|
case "SLshl2AddU32":
|
|
case "SLshl3AddU32":
|
|
case "SLshl4AddU32":
|
|
{
|
|
var shift = (uint)(instruction.Opcode[5] - '0');
|
|
result = IAdd(
|
|
ShiftLeftLogical(left, UInt(shift)),
|
|
right);
|
|
break;
|
|
}
|
|
default:
|
|
error = $"unsupported scalar opcode {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
StoreS(destination, result);
|
|
return true;
|
|
}
|
|
|
|
private bool TryEmitScalarCompare(
|
|
Gen5ShaderInstruction instruction,
|
|
out string error)
|
|
{
|
|
error = string.Empty;
|
|
if (instruction.Sources.Count < 2)
|
|
{
|
|
error = "missing scalar compare source";
|
|
return false;
|
|
}
|
|
|
|
var left = GetRawSource(instruction, 0);
|
|
var right = GetRawSource(instruction, 1);
|
|
if (instruction.Opcode is "SBitcmp0B32" or "SBitcmp1B32")
|
|
{
|
|
var shifted = ShiftRightLogical(
|
|
left,
|
|
BitwiseAnd(right, UInt(31)));
|
|
var isSet = IsNotZero(BitwiseAnd(shifted, UInt(1)));
|
|
Store(
|
|
_scc,
|
|
instruction.Opcode == "SBitcmp1B32"
|
|
? isSet
|
|
: _module.AddInstruction(
|
|
SpirvOp.LogicalNot,
|
|
_boolType,
|
|
isSet));
|
|
return true;
|
|
}
|
|
|
|
var operation = instruction.Opcode switch
|
|
{
|
|
"SCmpEqI32" or "SCmpEqU32" => SpirvOp.IEqual,
|
|
"SCmpLgI32" or "SCmpLgU32" => SpirvOp.INotEqual,
|
|
"SCmpGtI32" => SpirvOp.SGreaterThan,
|
|
"SCmpGeI32" => SpirvOp.SGreaterThanEqual,
|
|
"SCmpLtI32" => SpirvOp.SLessThan,
|
|
"SCmpLeI32" => SpirvOp.SLessThanEqual,
|
|
"SCmpGtU32" => SpirvOp.UGreaterThan,
|
|
"SCmpGeU32" => SpirvOp.UGreaterThanEqual,
|
|
"SCmpLtU32" => SpirvOp.ULessThan,
|
|
"SCmpLeU32" => SpirvOp.ULessThanEqual,
|
|
_ => SpirvOp.Nop,
|
|
};
|
|
if (operation == SpirvOp.Nop)
|
|
{
|
|
error = $"unsupported scalar compare {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
if (instruction.Opcode.EndsWith("I32", StringComparison.Ordinal))
|
|
{
|
|
left = Bitcast(_intType, left);
|
|
right = Bitcast(_intType, right);
|
|
}
|
|
|
|
Store(_scc, _module.AddInstruction(operation, _boolType, left, right));
|
|
return true;
|
|
}
|
|
|
|
private bool TryEmitScalarCompareK(
|
|
Gen5ShaderInstruction instruction,
|
|
uint destination,
|
|
uint immediate,
|
|
out string error)
|
|
{
|
|
error = string.Empty;
|
|
var left = LoadS(destination);
|
|
var right = UInt(immediate);
|
|
var operation = instruction.Opcode switch
|
|
{
|
|
"SCmpkEqI32" or "SCmpkEqU32" => SpirvOp.IEqual,
|
|
"SCmpkLgI32" or "SCmpkLgU32" => SpirvOp.INotEqual,
|
|
"SCmpkGtI32" => SpirvOp.SGreaterThan,
|
|
"SCmpkGeI32" => SpirvOp.SGreaterThanEqual,
|
|
"SCmpkLtI32" => SpirvOp.SLessThan,
|
|
"SCmpkLeI32" => SpirvOp.SLessThanEqual,
|
|
"SCmpkGtU32" => SpirvOp.UGreaterThan,
|
|
"SCmpkGeU32" => SpirvOp.UGreaterThanEqual,
|
|
"SCmpkLtU32" => SpirvOp.ULessThan,
|
|
"SCmpkLeU32" => SpirvOp.ULessThanEqual,
|
|
_ => SpirvOp.Nop,
|
|
};
|
|
if (operation == SpirvOp.Nop)
|
|
{
|
|
error = $"unsupported scalar immediate compare {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
if (instruction.Opcode.EndsWith("I32", StringComparison.Ordinal))
|
|
{
|
|
left = Bitcast(_intType, left);
|
|
right = Bitcast(_intType, right);
|
|
}
|
|
|
|
Store(_scc, _module.AddInstruction(operation, _boolType, left, right));
|
|
return true;
|
|
}
|
|
|
|
private bool TryEmitScalar64(
|
|
Gen5ShaderInstruction instruction,
|
|
uint destination,
|
|
out string error)
|
|
{
|
|
error = string.Empty;
|
|
var left = GetRawSource64(instruction, 0);
|
|
if (instruction.Opcode.EndsWith("SaveexecB64", StringComparison.Ordinal))
|
|
{
|
|
var oldExec = BooleanToLaneMask(Load(_boolType, _exec));
|
|
var notLeft = _module.AddInstruction(SpirvOp.Not, _ulongType, left);
|
|
var newExec = instruction.Opcode switch
|
|
{
|
|
"SAndSaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseAnd, _ulongType, oldExec, left),
|
|
"SOrSaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseOr, _ulongType, oldExec, left),
|
|
"SXorSaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseXor, _ulongType, oldExec, left),
|
|
"SAndn2SaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseAnd,
|
|
_ulongType,
|
|
left,
|
|
_module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
oldExec)),
|
|
"SAndn1SaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseAnd,
|
|
_ulongType,
|
|
notLeft,
|
|
oldExec),
|
|
"SOrn1SaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_ulongType,
|
|
notLeft,
|
|
oldExec),
|
|
"SOrn2SaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_ulongType,
|
|
left,
|
|
_module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
oldExec)),
|
|
"SNandSaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseAnd,
|
|
_ulongType,
|
|
left,
|
|
oldExec)),
|
|
"SNorSaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_ulongType,
|
|
left,
|
|
oldExec)),
|
|
"SXnorSaveexecB64" => _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseXor,
|
|
_ulongType,
|
|
left,
|
|
oldExec)),
|
|
_ => 0u,
|
|
};
|
|
if (newExec == 0)
|
|
{
|
|
error =
|
|
$"unsupported scalar 64-bit opcode {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
StoreS64(destination, oldExec);
|
|
StoreS64(126, newExec);
|
|
Store(_scc, IsNotZero64(newExec));
|
|
return true;
|
|
}
|
|
|
|
if (instruction.Opcode is "SLshlB64" or "SLshrB64")
|
|
{
|
|
if (instruction.Sources.Count < 2)
|
|
{
|
|
error = "missing scalar 64-bit shift source";
|
|
return false;
|
|
}
|
|
|
|
var shift = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
GetRawSource(instruction, 1));
|
|
var shiftedValue = instruction.Opcode == "SLshlB64"
|
|
? ShiftLeftLogical64(left, shift)
|
|
: ShiftRightLogical64(left, shift);
|
|
StoreS64(destination, shiftedValue);
|
|
Store(_scc, IsNotZero64(shiftedValue));
|
|
return true;
|
|
}
|
|
|
|
if (instruction.Opcode is "SBfeU64" or "SBfeI64")
|
|
{
|
|
if (instruction.Sources.Count < 2)
|
|
{
|
|
error = "missing scalar 64-bit bitfield source";
|
|
return false;
|
|
}
|
|
|
|
var control = GetRawSource(instruction, 1);
|
|
var offset = BitwiseAnd(control, UInt(63));
|
|
var requestedWidth = BitwiseAnd(
|
|
ShiftRightLogical(control, UInt(16)),
|
|
UInt(0x7F));
|
|
var remaining = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
UInt(64),
|
|
offset);
|
|
var width = Ext(
|
|
38,
|
|
_uintType,
|
|
requestedWidth,
|
|
remaining);
|
|
var offset64 = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
offset);
|
|
var width64 = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
width);
|
|
var one64 = _module.Constant64(_ulongType, 1);
|
|
var shifted = ShiftRightLogical64(left, offset64);
|
|
var partialMask = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_ulongType,
|
|
ShiftLeftLogical64(one64, width64),
|
|
one64);
|
|
var fullWidth = _module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
width,
|
|
UInt(64));
|
|
var mask = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_ulongType,
|
|
fullWidth,
|
|
_module.Constant64(_ulongType, ulong.MaxValue),
|
|
partialMask);
|
|
var extracted = _module.AddInstruction(
|
|
SpirvOp.BitwiseAnd,
|
|
_ulongType,
|
|
shifted,
|
|
mask);
|
|
if (instruction.Opcode == "SBfeI64")
|
|
{
|
|
var signShift = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
width,
|
|
UInt(1));
|
|
var signBit = ShiftLeftLogical64(
|
|
one64,
|
|
_module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
signShift));
|
|
var signExtended = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseXor,
|
|
_ulongType,
|
|
extracted,
|
|
signBit),
|
|
signBit);
|
|
extracted = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
width,
|
|
UInt(0)),
|
|
_module.Constant64(_ulongType, 0),
|
|
signExtended);
|
|
}
|
|
|
|
StoreS64(destination, extracted);
|
|
Store(_scc, IsNotZero64(extracted));
|
|
return true;
|
|
}
|
|
|
|
uint value;
|
|
if (instruction.Opcode is "SMovB64" or "SWqmB64")
|
|
{
|
|
value = left;
|
|
}
|
|
else if (instruction.Opcode == "SNotB64")
|
|
{
|
|
value = _module.AddInstruction(SpirvOp.Not, _ulongType, left);
|
|
}
|
|
else
|
|
{
|
|
if (instruction.Sources.Count < 2)
|
|
{
|
|
error = "missing scalar 64-bit source";
|
|
return false;
|
|
}
|
|
|
|
var right = GetRawSource64(instruction, 1);
|
|
value = instruction.Opcode switch
|
|
{
|
|
"SAndB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseAnd, _ulongType, left, right),
|
|
"SOrB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseOr, _ulongType, left, right),
|
|
"SXorB64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseXor, _ulongType, left, right),
|
|
"SNandB64" => _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseAnd, _ulongType, left, right)),
|
|
"SNorB64" => _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseOr, _ulongType, left, right)),
|
|
"SXnorB64" => _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_ulongType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseXor, _ulongType, left, right)),
|
|
"SAndn1B64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseAnd,
|
|
_ulongType,
|
|
_module.AddInstruction(SpirvOp.Not, _ulongType, left),
|
|
right),
|
|
"SAndn2B64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseAnd,
|
|
_ulongType,
|
|
left,
|
|
_module.AddInstruction(SpirvOp.Not, _ulongType, right)),
|
|
"SOrn1B64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_ulongType,
|
|
_module.AddInstruction(SpirvOp.Not, _ulongType, left),
|
|
right),
|
|
"SOrn2B64" => _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_ulongType,
|
|
left,
|
|
_module.AddInstruction(SpirvOp.Not, _ulongType, right)),
|
|
"SCselectB64" => _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_ulongType,
|
|
Load(_boolType, _scc),
|
|
left,
|
|
right),
|
|
_ => 0,
|
|
};
|
|
if (value == 0)
|
|
{
|
|
error = $"unsupported scalar 64-bit opcode {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
StoreS64(destination, value);
|
|
if (instruction.Opcode is
|
|
"SNotB64" or
|
|
"SAndB64" or
|
|
"SOrB64" or
|
|
"SXorB64" or
|
|
"SAndn1B64" or
|
|
"SAndn2B64" or
|
|
"SOrn1B64" or
|
|
"SOrn2B64" or
|
|
"SNandB64" or
|
|
"SNorB64" or
|
|
"SXnorB64")
|
|
{
|
|
Store(_scc, IsNotZero64(value));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private uint GetRawSource(
|
|
Gen5ShaderInstruction instruction,
|
|
int sourceIndex)
|
|
{
|
|
if ((uint)sourceIndex >= instruction.Sources.Count)
|
|
{
|
|
throw new InvalidOperationException($"missing source {sourceIndex}");
|
|
}
|
|
|
|
var operand = instruction.Sources[sourceIndex];
|
|
uint value = operand.Kind switch
|
|
{
|
|
Gen5OperandKind.VectorRegister => LoadV(operand.Value),
|
|
Gen5OperandKind.ScalarRegister => LoadS(operand.Value),
|
|
Gen5OperandKind.LiteralConstant => UInt(operand.Value),
|
|
Gen5OperandKind.EncodedConstant when TryDecodeInlineConstant(
|
|
operand.Value,
|
|
out var inline) => UInt(inline),
|
|
_ => throw new InvalidOperationException($"unsupported source {operand}"),
|
|
};
|
|
|
|
if (instruction.Control is Gen5SdwaControl sdwa)
|
|
{
|
|
var selector = sourceIndex switch
|
|
{
|
|
0 => sdwa.Source0Select,
|
|
1 => sdwa.Source1Select,
|
|
_ => 6u,
|
|
};
|
|
value = selector switch
|
|
{
|
|
0 => BitwiseAnd(value, UInt(0xFF)),
|
|
1 => BitwiseAnd(ShiftRightLogical(value, UInt(8)), UInt(0xFF)),
|
|
2 => BitwiseAnd(ShiftRightLogical(value, UInt(16)), UInt(0xFF)),
|
|
3 => BitwiseAnd(ShiftRightLogical(value, UInt(24)), UInt(0xFF)),
|
|
4 => BitwiseAnd(value, UInt(0xFFFF)),
|
|
5 => BitwiseAnd(ShiftRightLogical(value, UInt(16)), UInt(0xFFFF)),
|
|
_ => value,
|
|
};
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
private uint GetFloatSource(
|
|
Gen5ShaderInstruction instruction,
|
|
int sourceIndex)
|
|
{
|
|
var operand = instruction.Sources[sourceIndex];
|
|
uint value;
|
|
if (operand.Kind == Gen5OperandKind.EncodedConstant &&
|
|
operand.Value is >= 128 and <= 192)
|
|
{
|
|
value = Float(operand.Value - 128);
|
|
}
|
|
else if (operand.Kind == Gen5OperandKind.EncodedConstant &&
|
|
operand.Value is >= 193 and <= 208)
|
|
{
|
|
value = Float(-(operand.Value - 192));
|
|
}
|
|
else
|
|
{
|
|
value = Bitcast(_floatType, GetRawSource(instruction, sourceIndex));
|
|
}
|
|
|
|
uint absoluteMask = 0;
|
|
uint negateMask = 0;
|
|
switch (instruction.Control)
|
|
{
|
|
case Gen5Vop3Control control:
|
|
absoluteMask = control.AbsoluteMask;
|
|
negateMask = control.NegateMask;
|
|
break;
|
|
case Gen5SdwaControl control:
|
|
absoluteMask = control.AbsoluteMask;
|
|
negateMask = control.NegateMask;
|
|
break;
|
|
case Gen5DppControl control:
|
|
absoluteMask = control.AbsoluteMask;
|
|
negateMask = control.NegateMask;
|
|
break;
|
|
}
|
|
|
|
if ((absoluteMask & (1u << sourceIndex)) != 0)
|
|
{
|
|
value = Ext(4, _floatType, value);
|
|
}
|
|
|
|
if ((negateMask & (1u << sourceIndex)) != 0)
|
|
{
|
|
value = _module.AddInstruction(SpirvOp.FNegate, _floatType, value);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
private uint GetRawSource64(
|
|
Gen5ShaderInstruction instruction,
|
|
int sourceIndex)
|
|
{
|
|
var operand = instruction.Sources[sourceIndex];
|
|
if (operand.Kind == Gen5OperandKind.ScalarRegister)
|
|
{
|
|
return LoadS64(operand.Value);
|
|
}
|
|
|
|
var low = GetRawSource(instruction, sourceIndex);
|
|
return _module.AddInstruction(SpirvOp.UConvert, _ulongType, low);
|
|
}
|
|
|
|
private uint LoadS64(uint register)
|
|
{
|
|
var low = _module.AddInstruction(SpirvOp.UConvert, _ulongType, LoadS(register));
|
|
var high = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
LoadS(register + 1));
|
|
high = ShiftLeftLogical64(high, _module.Constant64(_ulongType, 32));
|
|
return _module.AddInstruction(SpirvOp.BitwiseOr, _ulongType, low, high);
|
|
}
|
|
|
|
private void StoreS64(uint register, uint value)
|
|
{
|
|
StoreS(
|
|
register,
|
|
_module.AddInstruction(SpirvOp.UConvert, _uintType, value));
|
|
var high = ShiftRightLogical64(
|
|
value,
|
|
_module.Constant64(_ulongType, 32));
|
|
StoreS(
|
|
register + 1,
|
|
_module.AddInstruction(SpirvOp.UConvert, _uintType, high));
|
|
}
|
|
|
|
private uint EmitFloatBinary(
|
|
Gen5ShaderInstruction instruction,
|
|
SpirvOp operation,
|
|
bool reverse = false)
|
|
{
|
|
var left = GetFloatSource(instruction, reverse ? 1 : 0);
|
|
var right = GetFloatSource(instruction, reverse ? 0 : 1);
|
|
return EmitFloatResult(
|
|
instruction,
|
|
_module.AddInstruction(operation, _floatType, left, right));
|
|
}
|
|
|
|
private uint EmitFloatExtBinary(
|
|
Gen5ShaderInstruction instruction,
|
|
uint operation) =>
|
|
EmitFloatResult(
|
|
instruction,
|
|
Ext(
|
|
operation,
|
|
_floatType,
|
|
GetFloatSource(instruction, 0),
|
|
GetFloatSource(instruction, 1)));
|
|
|
|
private uint EmitFloatTernaryExt(
|
|
Gen5ShaderInstruction instruction,
|
|
uint operation)
|
|
{
|
|
var first = Ext(
|
|
operation,
|
|
_floatType,
|
|
GetFloatSource(instruction, 0),
|
|
GetFloatSource(instruction, 1));
|
|
return EmitFloatResult(
|
|
instruction,
|
|
Ext(operation, _floatType, first, GetFloatSource(instruction, 2)));
|
|
}
|
|
|
|
private uint EmitIntegerBinary(
|
|
Gen5ShaderInstruction instruction,
|
|
SpirvOp operation,
|
|
bool reverse = false)
|
|
{
|
|
var left = GetRawSource(instruction, reverse ? 1 : 0);
|
|
var right = GetRawSource(instruction, reverse ? 0 : 1);
|
|
if (operation == SpirvOp.ShiftLeftLogical)
|
|
{
|
|
return ShiftLeftLogical(left, right);
|
|
}
|
|
|
|
if (operation == SpirvOp.ShiftRightLogical)
|
|
{
|
|
return ShiftRightLogical(left, right);
|
|
}
|
|
|
|
if (operation == SpirvOp.ShiftRightArithmetic)
|
|
{
|
|
return ShiftRightArithmetic(left, right);
|
|
}
|
|
|
|
return _module.AddInstruction(operation, _uintType, left, right);
|
|
}
|
|
|
|
private enum CubeCoordinate
|
|
{
|
|
Id,
|
|
Sc,
|
|
Tc,
|
|
Ma,
|
|
}
|
|
|
|
private uint EmitCvtOffF32I4(Gen5ShaderInstruction instruction)
|
|
{
|
|
var index = BitwiseAnd(GetRawSource(instruction, 0), UInt(15));
|
|
ReadOnlySpan<float> table =
|
|
[
|
|
0.0f,
|
|
0.0625f,
|
|
0.1250f,
|
|
0.1875f,
|
|
0.2500f,
|
|
0.3125f,
|
|
0.3750f,
|
|
0.4375f,
|
|
-0.5000f,
|
|
-0.4375f,
|
|
-0.3750f,
|
|
-0.3125f,
|
|
-0.2500f,
|
|
-0.1875f,
|
|
-0.1250f,
|
|
-0.0625f,
|
|
];
|
|
|
|
var result = UInt(BitConverter.SingleToUInt32Bits(table[^1]));
|
|
for (var tableIndex = table.Length - 2; tableIndex >= 0; tableIndex--)
|
|
{
|
|
var matches = _module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
index,
|
|
UInt((uint)tableIndex));
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
matches,
|
|
UInt(BitConverter.SingleToUInt32Bits(table[tableIndex])),
|
|
result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private uint EmitCubeCoordinate(
|
|
Gen5ShaderInstruction instruction,
|
|
CubeCoordinate coordinate)
|
|
{
|
|
var x = GetFloatSource(instruction, 0);
|
|
var y = GetFloatSource(instruction, 1);
|
|
var z = GetFloatSource(instruction, 2);
|
|
var nx = _module.AddInstruction(SpirvOp.FNegate, _floatType, x);
|
|
var ny = _module.AddInstruction(SpirvOp.FNegate, _floatType, y);
|
|
var nz = _module.AddInstruction(SpirvOp.FNegate, _floatType, z);
|
|
var ax = Ext(4, _floatType, x);
|
|
var ay = Ext(4, _floatType, y);
|
|
var az = Ext(4, _floatType, z);
|
|
var amaxXY = Ext(40, _floatType, ax, ay);
|
|
var amax = Ext(40, _floatType, az, amaxXY);
|
|
var ma = _module.AddInstruction(
|
|
SpirvOp.FMul,
|
|
_floatType,
|
|
Float(2),
|
|
amax);
|
|
if (coordinate == CubeCoordinate.Ma)
|
|
{
|
|
return EmitFloatResult(instruction, ma);
|
|
}
|
|
|
|
var isZMax = _module.AddInstruction(
|
|
SpirvOp.FOrdGreaterThanEqual,
|
|
_boolType,
|
|
az,
|
|
amaxXY);
|
|
var yGreaterOrEqualX = _module.AddInstruction(
|
|
SpirvOp.FOrdGreaterThanEqual,
|
|
_boolType,
|
|
ay,
|
|
ax);
|
|
var isYMax = _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
_module.AddInstruction(SpirvOp.LogicalNot, _boolType, isZMax),
|
|
yGreaterOrEqualX);
|
|
if (coordinate == CubeCoordinate.Id)
|
|
{
|
|
var isZNeg = _module.AddInstruction(
|
|
SpirvOp.FOrdLessThan,
|
|
_boolType,
|
|
z,
|
|
Float(0));
|
|
var isYNeg = _module.AddInstruction(
|
|
SpirvOp.FOrdLessThan,
|
|
_boolType,
|
|
y,
|
|
Float(0));
|
|
var isXNeg = _module.AddInstruction(
|
|
SpirvOp.FOrdLessThan,
|
|
_boolType,
|
|
x,
|
|
Float(0));
|
|
var zCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isZNeg,
|
|
Float(5),
|
|
Float(4));
|
|
var yCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isYNeg,
|
|
Float(3),
|
|
Float(2));
|
|
var xCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isXNeg,
|
|
Float(1),
|
|
Float(0));
|
|
var xyCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
yGreaterOrEqualX,
|
|
yCase,
|
|
xCase);
|
|
return EmitFloatResult(
|
|
instruction,
|
|
_module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isZMax,
|
|
zCase,
|
|
xyCase));
|
|
}
|
|
|
|
if (coordinate == CubeCoordinate.Sc)
|
|
{
|
|
var isZNeg = _module.AddInstruction(
|
|
SpirvOp.FOrdLessThan,
|
|
_boolType,
|
|
z,
|
|
Float(0));
|
|
var isXNeg = _module.AddInstruction(
|
|
SpirvOp.FOrdLessThan,
|
|
_boolType,
|
|
x,
|
|
Float(0));
|
|
var zCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isZNeg,
|
|
nx,
|
|
x);
|
|
var xCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isXNeg,
|
|
z,
|
|
nz);
|
|
var nonZCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isYMax,
|
|
x,
|
|
xCase);
|
|
return EmitFloatResult(
|
|
instruction,
|
|
_module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isZMax,
|
|
zCase,
|
|
nonZCase));
|
|
}
|
|
|
|
var tcIsYNeg = _module.AddInstruction(
|
|
SpirvOp.FOrdLessThan,
|
|
_boolType,
|
|
y,
|
|
Float(0));
|
|
var tcYCase = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
tcIsYNeg,
|
|
nz,
|
|
z);
|
|
return EmitFloatResult(
|
|
instruction,
|
|
_module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_floatType,
|
|
isYMax,
|
|
tcYCase,
|
|
ny));
|
|
}
|
|
|
|
private uint EmitAddWithCarry(Gen5ShaderInstruction instruction)
|
|
{
|
|
var left = GetRawSource(instruction, 0);
|
|
var right = GetRawSource(instruction, 1);
|
|
var carryIn = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
Load(_boolType, _vcc),
|
|
UInt(1),
|
|
UInt(0));
|
|
var partial = IAdd(left, right);
|
|
var result = IAdd(partial, carryIn);
|
|
var carry = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
_module.AddInstruction(SpirvOp.ULessThan, _boolType, partial, left),
|
|
_module.AddInstruction(SpirvOp.ULessThan, _boolType, result, partial));
|
|
StoreWaveMask(106, carry);
|
|
return result;
|
|
}
|
|
|
|
private uint EmitSubtractWithBorrow(
|
|
Gen5ShaderInstruction instruction,
|
|
bool reverse)
|
|
{
|
|
var left = GetRawSource(instruction, reverse ? 1 : 0);
|
|
var right = GetRawSource(instruction, reverse ? 0 : 1);
|
|
var borrowIn = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
Load(_boolType, _vcc),
|
|
UInt(1),
|
|
UInt(0));
|
|
var partial = _module.AddInstruction(SpirvOp.ISub, _uintType, left, right);
|
|
var result = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
partial,
|
|
borrowIn);
|
|
var borrow = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
_module.AddInstruction(SpirvOp.ULessThan, _boolType, left, right),
|
|
_module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
partial,
|
|
borrowIn));
|
|
StoreWaveMask(106, borrow);
|
|
return result;
|
|
}
|
|
|
|
private void StoreCarryOut(
|
|
Gen5ShaderInstruction instruction,
|
|
uint carry)
|
|
{
|
|
if (instruction.Control is Gen5Vop3Control { ScalarDestination: { } register })
|
|
{
|
|
StoreS(
|
|
register,
|
|
_module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
carry,
|
|
UInt(1),
|
|
UInt(0)));
|
|
if (register == 106)
|
|
{
|
|
StoreWaveMask(106, carry);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
StoreWaveMask(106, carry);
|
|
}
|
|
|
|
private uint EmitPermlane16(
|
|
Gen5ShaderInstruction instruction,
|
|
bool exchangeRows)
|
|
{
|
|
var value = GetRawSource(instruction, 0);
|
|
var selectorLow = GetRawSource(instruction, 1);
|
|
var selectorHigh = GetRawSource(instruction, 2);
|
|
var lane = Load(_uintType, _subgroupInvocationIdInput);
|
|
var localLane = BitwiseAnd(lane, UInt(15));
|
|
var lowHalf = _module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
localLane,
|
|
UInt(8));
|
|
var lowShift = ShiftLeftLogical(localLane, UInt(2));
|
|
var highLane = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
localLane,
|
|
UInt(8));
|
|
var highShift = ShiftLeftLogical(highLane, UInt(2));
|
|
var lowSelector = BitwiseAnd(
|
|
ShiftRightLogical(selectorLow, lowShift),
|
|
UInt(15));
|
|
var highSelector = BitwiseAnd(
|
|
ShiftRightLogical(selectorHigh, highShift),
|
|
UInt(15));
|
|
var selector = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
lowHalf,
|
|
lowSelector,
|
|
highSelector);
|
|
var rowBase = BitwiseAnd(lane, UInt(0xFFFF_FFF0));
|
|
if (exchangeRows)
|
|
{
|
|
rowBase = BitwiseXor(rowBase, UInt(16));
|
|
}
|
|
|
|
var targetLane = IAdd(rowBase, selector);
|
|
return _module.AddInstruction(
|
|
SpirvOp.GroupNonUniformShuffle,
|
|
_uintType,
|
|
UInt(3),
|
|
value,
|
|
targetLane);
|
|
}
|
|
|
|
private uint EmitFloatResult(
|
|
Gen5ShaderInstruction instruction,
|
|
uint value)
|
|
{
|
|
uint outputModifier = 0;
|
|
var clamp = false;
|
|
switch (instruction.Control)
|
|
{
|
|
case Gen5Vop3Control control:
|
|
outputModifier = control.OutputModifier;
|
|
clamp = control.Clamp;
|
|
break;
|
|
case Gen5SdwaControl control:
|
|
outputModifier = control.OutputModifier;
|
|
clamp = control.Clamp;
|
|
break;
|
|
}
|
|
|
|
value = outputModifier switch
|
|
{
|
|
1 => _module.AddInstruction(SpirvOp.FMul, _floatType, value, Float(2)),
|
|
2 => _module.AddInstruction(SpirvOp.FMul, _floatType, value, Float(4)),
|
|
3 => _module.AddInstruction(SpirvOp.FMul, _floatType, value, Float(0.5f)),
|
|
_ => value,
|
|
};
|
|
if (clamp)
|
|
{
|
|
value = Ext(43, _floatType, value, Float(0), Float(1));
|
|
}
|
|
|
|
return Bitcast(_uintType, value);
|
|
}
|
|
|
|
private uint TruncateFloat32ForPack(uint value)
|
|
{
|
|
var raw = BitwiseAnd(
|
|
Bitcast(_uintType, value),
|
|
UInt(0xFFFF_E000));
|
|
return Bitcast(_floatType, raw);
|
|
}
|
|
|
|
private uint Ext(uint operation, uint resultType, params uint[] operands)
|
|
{
|
|
var values = new uint[2 + operands.Length];
|
|
values[0] = _glsl;
|
|
values[1] = operation;
|
|
operands.CopyTo(values, 2);
|
|
return _module.AddInstruction(SpirvOp.ExtInst, resultType, values);
|
|
}
|
|
|
|
private uint IsNotZero(uint value) =>
|
|
_module.AddInstruction(SpirvOp.INotEqual, _boolType, value, UInt(0));
|
|
|
|
private uint IsNotZero64(uint value) =>
|
|
_module.AddInstruction(
|
|
SpirvOp.INotEqual,
|
|
_boolType,
|
|
value,
|
|
_module.Constant64(_ulongType, 0));
|
|
|
|
private uint SignBit(uint value) =>
|
|
ShiftRightLogical(value, UInt(31));
|
|
|
|
private uint SignedAddOverflow(uint left, uint right, uint result)
|
|
{
|
|
var leftSign = SignBit(left);
|
|
var rightSign = SignBit(right);
|
|
var resultSign = SignBit(result);
|
|
var sameSourceSign = _module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
leftSign,
|
|
rightSign);
|
|
var resultSignChanged = _module.AddInstruction(
|
|
SpirvOp.INotEqual,
|
|
_boolType,
|
|
leftSign,
|
|
resultSign);
|
|
return _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
sameSourceSign,
|
|
resultSignChanged);
|
|
}
|
|
|
|
private uint SignedSubOverflow(uint left, uint right, uint result)
|
|
{
|
|
var leftSign = SignBit(left);
|
|
var rightSign = SignBit(right);
|
|
var resultSign = SignBit(result);
|
|
var differentSourceSign = _module.AddInstruction(
|
|
SpirvOp.INotEqual,
|
|
_boolType,
|
|
leftSign,
|
|
rightSign);
|
|
var resultSignChanged = _module.AddInstruction(
|
|
SpirvOp.INotEqual,
|
|
_boolType,
|
|
leftSign,
|
|
resultSign);
|
|
return _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
differentSourceSign,
|
|
resultSignChanged);
|
|
}
|
|
|
|
private static bool TryDecodeInlineConstant(uint encoded, out uint value)
|
|
{
|
|
if (encoded == 125)
|
|
{
|
|
value = 0;
|
|
return true;
|
|
}
|
|
|
|
if (encoded is >= 128 and <= 192)
|
|
{
|
|
value = encoded - 128;
|
|
return true;
|
|
}
|
|
|
|
if (encoded is >= 193 and <= 208)
|
|
{
|
|
value = unchecked((uint)-(int)(encoded - 192));
|
|
return true;
|
|
}
|
|
|
|
var floatingPoint = encoded switch
|
|
{
|
|
240 => 0.5f,
|
|
241 => -0.5f,
|
|
242 => 1.0f,
|
|
243 => -1.0f,
|
|
244 => 2.0f,
|
|
245 => -2.0f,
|
|
246 => 4.0f,
|
|
247 => -4.0f,
|
|
248 => 1.0f / (2.0f * MathF.PI),
|
|
_ => float.NaN,
|
|
};
|
|
if (float.IsNaN(floatingPoint))
|
|
{
|
|
value = 0;
|
|
return false;
|
|
}
|
|
|
|
value = BitConverter.SingleToUInt32Bits(floatingPoint);
|
|
return true;
|
|
}
|
|
}
|
|
}
|