mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-22 19:06:15 +08:00
864cbb0fa0
* [Core] Add POSIX native execution and PS5 SELF support Extend the native backend, guest TLS, fixed-address memory, and loader paths needed by PS5 titles on Windows, Linux, and macOS. Keep workstation GC so high-core-count hosts do not reserve over fixed guest image bases. * [HLE] Expand PS5 service and media compatibility Add the kernel, threading, save-data, networking, audio, video-codec, font, dialog, and service exports required by newer PS5 software. Preserve every SysAbi NID currently registered by main while adding the compatibility surface used by ASTRO BOT. * [AGC/Vulkan] Extend Gen5 shader and presentation support Expand PM4 handling, Gen5 shader translation, MRT and packed export support, guest image tracking, depth initialization, texture aliasing, and Vulkan presentation. Add the performance overlay and address-filtered diagnostics used to validate ASTRO BOT with original shaders. * [Core] Align static TLS reservation across hosts * [Pad] Align primary user ID with UserService * [Gpu] Preserve runtime scalar buffers across renderer seam * [AGC] Restore omitted command helper exports * [Vulkan] Reuse primary views for promoted MRT targets * [Vulkan] Preserve scratch storage bindings in compute dispatches
3337 lines
132 KiB
C#
3337 lines
132 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using SharpEmu.ShaderCompiler;
|
|
|
|
namespace SharpEmu.ShaderCompiler.Vulkan;
|
|
|
|
public 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.Control is Gen5SdwaControl sdwa &&
|
|
(sdwa.Source0Select == 7 ||
|
|
sdwa.Source1Select == 7 ||
|
|
sdwa.DestinationSelect == 7 ||
|
|
sdwa.DestinationUnused == 3))
|
|
{
|
|
error = $"reserved SDWA selector/modifier in {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
if (instruction.Control is Gen5DppControl dppControl &&
|
|
!IsSupportedDppControl(dppControl.Control))
|
|
{
|
|
error = $"unsupported DPP16 control 0x{dppControl.Control:X3}";
|
|
return false;
|
|
}
|
|
|
|
if (instruction.Opcode.StartsWith("VCmp", StringComparison.Ordinal))
|
|
{
|
|
return TryEmitVectorCompare(instruction, out error);
|
|
}
|
|
|
|
if (instruction.Opcode == "VReadfirstlaneB32")
|
|
{
|
|
if (instruction.Destinations.Count == 0 ||
|
|
instruction.Destinations[0].Kind != Gen5OperandKind.ScalarRegister ||
|
|
instruction.Sources.Count == 0)
|
|
{
|
|
error = "invalid read-first-lane operands";
|
|
return false;
|
|
}
|
|
|
|
var value = GetRawSource(instruction, 0);
|
|
if (_subgroupInvocationIdInput != 0)
|
|
{
|
|
if (_emulateWave64)
|
|
{
|
|
value = BroadcastFirstWave64Active(value);
|
|
}
|
|
else
|
|
{
|
|
// SPIR-V's BroadcastFirst uses the first host-active
|
|
// invocation. Guest EXEC is modeled as data, so obtain the
|
|
// guest-active mask explicitly and broadcast from its first
|
|
// set lane instead. This also updates the private SGPR copy
|
|
// for lanes that are currently disabled and may be restored
|
|
// by a later saveexec sequence.
|
|
var activeLanes = _module.AddInstruction(
|
|
SpirvOp.GroupNonUniformBallot,
|
|
_uvec4Type,
|
|
UInt(3),
|
|
Load(_boolType, _exec));
|
|
var activeLow = _module.AddInstruction(
|
|
SpirvOp.CompositeExtract,
|
|
_uintType,
|
|
activeLanes,
|
|
0);
|
|
var firstActiveLane = Ext(73, _uintType, activeLow);
|
|
value = _module.AddInstruction(
|
|
SpirvOp.GroupNonUniformBroadcast,
|
|
_uintType,
|
|
UInt(3),
|
|
value,
|
|
firstActiveLane);
|
|
}
|
|
}
|
|
|
|
StoreS(instruction.Destinations[0].Value, value);
|
|
return true;
|
|
}
|
|
|
|
if (instruction.Opcode == "VReadlaneB32")
|
|
{
|
|
return TryEmitReadlane(instruction, out error);
|
|
}
|
|
|
|
if (!TryGetVectorDestination(instruction, out var destination))
|
|
{
|
|
error = "missing vector destination";
|
|
return false;
|
|
}
|
|
|
|
uint result;
|
|
switch (instruction.Opcode)
|
|
{
|
|
case "VMovB32":
|
|
result = GetRawSource(instruction, 0);
|
|
break;
|
|
case "VWritelaneB32":
|
|
{
|
|
// vdst[lane(src1)] = src0
|
|
// Per-lane: if current lane == src1, write src0, else keep old value.
|
|
var oldValue = LoadV(destination);
|
|
var src0 = GetRawSource(instruction, 0);
|
|
var laneSelect = GetRawSource(instruction, 1);
|
|
var currentLane = _subgroupInvocationIdInput != 0
|
|
? BitwiseAnd(
|
|
Load(_uintType, _subgroupInvocationIdInput),
|
|
UInt(RdnaWaveLaneCount - 1))
|
|
: UInt(0);
|
|
var isTargetLane = _module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
currentLane,
|
|
laneSelect);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
isTargetLane,
|
|
src0,
|
|
oldValue);
|
|
// Writelane writes to a specific lane regardless of exec mask.
|
|
StoreV(destination, result, guardWithExec: false);
|
|
return true;
|
|
}
|
|
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 "VXnorB32":
|
|
{
|
|
var xor = EmitIntegerBinary(instruction, SpirvOp.BitwiseXor);
|
|
result = _module.AddInstruction(SpirvOp.Not, _uintType, xor);
|
|
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":
|
|
case "VAddCoCiU32":
|
|
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 = Bitcast(
|
|
_uintType,
|
|
_module.AddInstruction(
|
|
SpirvOp.SConvert,
|
|
_intType,
|
|
_module.AddInstruction(
|
|
SpirvOp.ShiftRightArithmetic,
|
|
_longType,
|
|
product,
|
|
_module.Constant64(_longType, 32))));
|
|
break;
|
|
}
|
|
case "VBcntU32B32":
|
|
result = IAdd(
|
|
_module.AddInstruction(
|
|
SpirvOp.BitCount,
|
|
_uintType,
|
|
GetRawSource(instruction, 0)),
|
|
GetRawSource(instruction, 1));
|
|
break;
|
|
case "VMbcntHiU32B32":
|
|
{
|
|
var guestLane = GuestWaveLane();
|
|
var lane = BitwiseAnd(guestLane, UInt(31));
|
|
var partialMask = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
ShiftLeftLogical(UInt(1), lane),
|
|
UInt(1));
|
|
var countedBits = _module.AddInstruction(
|
|
SpirvOp.BitCount,
|
|
_uintType,
|
|
BitwiseAnd(GetRawSource(instruction, 0), partialMask));
|
|
var isUpperHalf = _module.AddInstruction(
|
|
SpirvOp.UGreaterThanEqual,
|
|
_boolType,
|
|
guestLane,
|
|
UInt(32));
|
|
result = IAdd(
|
|
GetRawSource(instruction, 1),
|
|
_module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
isUpperHalf,
|
|
countedBits,
|
|
UInt(0)));
|
|
break;
|
|
}
|
|
case "VMbcntLoU32B32":
|
|
{
|
|
var guestLane = GuestWaveLane();
|
|
var lane = BitwiseAnd(guestLane, UInt(31));
|
|
var partialMask = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
ShiftLeftLogical(UInt(1), lane),
|
|
UInt(1));
|
|
var lowBitsMask = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
_module.AddInstruction(
|
|
SpirvOp.UGreaterThanEqual,
|
|
_boolType,
|
|
guestLane,
|
|
UInt(32)),
|
|
UInt(uint.MaxValue),
|
|
partialMask);
|
|
result = IAdd(
|
|
GetRawSource(instruction, 1),
|
|
_module.AddInstruction(
|
|
SpirvOp.BitCount,
|
|
_uintType,
|
|
BitwiseAnd(GetRawSource(instruction, 0), lowBitsMask)));
|
|
break;
|
|
}
|
|
case "VBfmB32":
|
|
{
|
|
var width = BitwiseAnd(GetRawSource(instruction, 0), UInt(31));
|
|
var lowMask = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
ShiftLeftLogical(UInt(1), width),
|
|
UInt(1));
|
|
result = ShiftLeftLogical(lowMask, 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 "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 "VMadU64U32":
|
|
{
|
|
// V_MAD_U64_U32 writes a 64-bit VGPR pair. The first two
|
|
// sources are 32-bit factors; the third is a 64-bit addend
|
|
// held in a VGPR or SGPR pair. Its SDST receives the carry
|
|
// mask for the unsigned 64-bit addition.
|
|
var wideLeft = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
GetRawSource(instruction, 0));
|
|
var wideRight = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
GetRawSource(instruction, 1));
|
|
var product = _module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_ulongType,
|
|
wideLeft,
|
|
wideRight);
|
|
var addend = GetRawSource64(instruction, 2);
|
|
var wideResult = _module.AddInstruction(
|
|
SpirvOp.IAdd,
|
|
_ulongType,
|
|
product,
|
|
addend);
|
|
var carry = _module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
wideResult,
|
|
addend);
|
|
result = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
wideResult);
|
|
var high = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
ShiftRightLogical64(
|
|
wideResult,
|
|
_module.Constant64(_ulongType, 32)));
|
|
StoreV(destination + 1, high);
|
|
StoreCarryOut(instruction, carry);
|
|
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);
|
|
StorePackedHalf(
|
|
destination,
|
|
vector);
|
|
break;
|
|
}
|
|
case "VCvtPknormI16F32":
|
|
case "VCvtPknormU16F32":
|
|
{
|
|
var vector = _module.AddInstruction(
|
|
SpirvOp.CompositeConstruct,
|
|
_vec2Type,
|
|
GetFloatSource(instruction, 0),
|
|
GetFloatSource(instruction, 1));
|
|
// GLSL.std.450 PackSnorm2x16 / PackUnorm2x16 match the
|
|
// RDNA saturated normalized conversion and packing rules.
|
|
result = Ext(
|
|
instruction.Opcode == "VCvtPknormI16F32" ? 56u : 57u,
|
|
_uintType,
|
|
vector);
|
|
break;
|
|
}
|
|
default:
|
|
error = $"unsupported vector opcode {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
if (instruction.Control is Gen5DppControl dpp)
|
|
{
|
|
result = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
IsDppWriteEnabled(dpp),
|
|
result,
|
|
LoadV(destination));
|
|
}
|
|
|
|
if (instruction.Control is Gen5SdwaControl destinationControl &&
|
|
destinationControl.ScalarDestination is null)
|
|
{
|
|
result = ApplySdwaDestination(
|
|
destinationControl,
|
|
result,
|
|
LoadV(destination));
|
|
}
|
|
|
|
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")
|
|
{
|
|
var left = GetFloatSource(instruction, 0);
|
|
var right = GetFloatSource(instruction, 1);
|
|
var unordered = _module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
_module.AddInstruction(SpirvOp.IsNan, _boolType, left),
|
|
_module.AddInstruction(SpirvOp.IsNan, _boolType, right));
|
|
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,
|
|
"VCmpNltF32" or "VCmpxNltF32" => SpirvOp.FUnordGreaterThanEqual,
|
|
"VCmpNleF32" or "VCmpxNleF32" => SpirvOp.FUnordGreaterThan,
|
|
"VCmpNgtF32" or "VCmpxNgtF32" => SpirvOp.FUnordLessThanEqual,
|
|
"VCmpNgeF32" or "VCmpxNgeF32" => SpirvOp.FUnordLessThan,
|
|
"VCmpNlgF32" or "VCmpxNlgF32" => SpirvOp.FUnordEqual,
|
|
_ => 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);
|
|
}
|
|
|
|
if (_state.Program.Address == 0x0000000500781200ul &&
|
|
((instruction.Pc == 0x4D4 &&
|
|
Environment.GetEnvironmentVariable(
|
|
"SHARPEMU_FORCE_TITLE_COMPARE_4D4") == "1") ||
|
|
(instruction.Pc == 0x540 &&
|
|
Environment.GetEnvironmentVariable(
|
|
"SHARPEMU_FORCE_TITLE_COMPARE_540") == "1")))
|
|
{
|
|
condition = _module.ConstantBool(true);
|
|
}
|
|
|
|
// Vector compares fully overwrite the destination mask, but only
|
|
// lanes enabled by EXEC can pass the test: VCC = EXEC & condition.
|
|
// Balloting the raw condition leaks results from disabled lanes
|
|
// into later saveexec/branch sequences.
|
|
var activeCondition = _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
Load(_boolType, _exec),
|
|
condition);
|
|
if (instruction.Control is Gen5DppControl compareDpp)
|
|
{
|
|
activeCondition = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_boolType,
|
|
IsDppWriteEnabled(compareDpp),
|
|
activeCondition,
|
|
Load(_boolType, _vcc));
|
|
}
|
|
|
|
if (opcode.StartsWith("VCmpx", StringComparison.Ordinal))
|
|
{
|
|
// GFX10 VCMPX is EXEC-only. The SDWA bits that older
|
|
// generations exposed as an explicit scalar destination must
|
|
// not overwrite VCC/an SGPR pair here. Guest shaders rely on
|
|
// those registers retaining a saved EXEC mask for later
|
|
// reconvergence.
|
|
StoreWaveMask(126, activeCondition);
|
|
}
|
|
else
|
|
{
|
|
var compareDestination = instruction.Control is Gen5SdwaControl
|
|
{ ScalarDestination: { } scalarDestination }
|
|
? scalarDestination
|
|
: 106u;
|
|
StoreWaveMask(compareDestination, activeCondition);
|
|
}
|
|
|
|
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);
|
|
if (instruction.Opcode.EndsWith("SaveexecB32", StringComparison.Ordinal))
|
|
{
|
|
var oldExec64 = BooleanToWaveMask(Load(_boolType, _exec));
|
|
var oldExec = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
oldExec64);
|
|
var notLeft = _module.AddInstruction(SpirvOp.Not, _uintType, left);
|
|
var notOldExec = _module.AddInstruction(SpirvOp.Not, _uintType, oldExec);
|
|
var newExec = instruction.Opcode switch
|
|
{
|
|
"SAndSaveexecB32" => BitwiseAnd(oldExec, left),
|
|
"SOrSaveexecB32" => BitwiseOr(oldExec, left),
|
|
"SXorSaveexecB32" => _module.AddInstruction(
|
|
SpirvOp.BitwiseXor, _uintType, oldExec, left),
|
|
"SAndn1SaveexecB32" => BitwiseAnd(notLeft, oldExec),
|
|
"SAndn2SaveexecB32" => BitwiseAnd(left, notOldExec),
|
|
"SOrn1SaveexecB32" => BitwiseOr(notLeft, oldExec),
|
|
"SOrn2SaveexecB32" => BitwiseOr(left, notOldExec),
|
|
"SNandSaveexecB32" => _module.AddInstruction(
|
|
SpirvOp.Not, _uintType, BitwiseAnd(left, oldExec)),
|
|
"SNorSaveexecB32" => _module.AddInstruction(
|
|
SpirvOp.Not, _uintType, BitwiseOr(left, oldExec)),
|
|
"SXnorSaveexecB32" => _module.AddInstruction(
|
|
SpirvOp.Not,
|
|
_uintType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseXor,
|
|
_uintType,
|
|
left,
|
|
oldExec)),
|
|
_ => 0u,
|
|
};
|
|
if (newExec == 0)
|
|
{
|
|
error = $"unsupported scalar 32-bit saveexec opcode {instruction.Opcode}";
|
|
return false;
|
|
}
|
|
|
|
StoreS(destination, oldExec);
|
|
// B32 saveexec is the Wave32 form; EXEC_HI is zero.
|
|
var newExec64 = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
newExec);
|
|
StoreS64(126, newExec64);
|
|
Store(_scc, IsNotZero(newExec));
|
|
return true;
|
|
}
|
|
|
|
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 product = _module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_ulongType,
|
|
_module.AddInstruction(SpirvOp.UConvert, _ulongType, left),
|
|
_module.AddInstruction(SpirvOp.UConvert, _ulongType, right));
|
|
result = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
ShiftRightLogical64(
|
|
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 "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;
|
|
}
|
|
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;
|
|
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 = BooleanToWaveMask(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;
|
|
}
|
|
|
|
if (instruction.Opcode == "SBfmB64")
|
|
{
|
|
if (instruction.Sources.Count < 2)
|
|
{
|
|
error = "missing scalar 64-bit bitfield-mask source";
|
|
return false;
|
|
}
|
|
|
|
var width = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
BitwiseAnd(GetRawSource(instruction, 0), UInt(63)));
|
|
var offset = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
BitwiseAnd(GetRawSource(instruction, 1), UInt(63)));
|
|
// Width is masked to 0..63, so (1 << width) never invokes an
|
|
// undefined 64-bit shift. This naturally yields zero for a
|
|
// zero-width mask and avoids OpBitFieldInsert, which the
|
|
// MoltenVK/SPIRV-Cross path rejects for 64-bit integers.
|
|
var lowMask = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_ulongType,
|
|
ShiftLeftLogical64(
|
|
_module.Constant64(_ulongType, 1),
|
|
width),
|
|
_module.Constant64(_ulongType, 1));
|
|
var maskValue = ShiftLeftLogical64(lowMask, offset);
|
|
StoreS64(destination, maskValue);
|
|
Store(_scc, IsNotZero64(maskValue));
|
|
return true;
|
|
}
|
|
|
|
uint value;
|
|
if (instruction.Opcode == "SMovB64")
|
|
{
|
|
value = left;
|
|
}
|
|
else if (instruction.Opcode == "SWqmB64")
|
|
{
|
|
var quadAny = _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_ulongType,
|
|
left,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_ulongType,
|
|
ShiftRightLogical64(left, _module.Constant64(_ulongType, 1)),
|
|
_module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_ulongType,
|
|
ShiftRightLogical64(left, _module.Constant64(_ulongType, 2)),
|
|
ShiftRightLogical64(left, _module.Constant64(_ulongType, 3)))));
|
|
quadAny = _module.AddInstruction(
|
|
SpirvOp.BitwiseAnd,
|
|
_ulongType,
|
|
quadAny,
|
|
_module.Constant64(_ulongType, 0x1111_1111_1111_1111UL));
|
|
value = _module.AddInstruction(
|
|
SpirvOp.IMul,
|
|
_ulongType,
|
|
quadAny,
|
|
_module.Constant64(_ulongType, 0xFUL));
|
|
}
|
|
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,
|
|
bool applySdwaIntegerModifiers = true)
|
|
{
|
|
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 operand.Value == 251 =>
|
|
_module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
LogicalNot(SubgroupAny(Load(_boolType, _vcc))),
|
|
UInt(1),
|
|
UInt(0)),
|
|
Gen5OperandKind.EncodedConstant when operand.Value == 252 =>
|
|
_module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
LogicalNot(SubgroupAny(Load(_boolType, _exec))),
|
|
UInt(1),
|
|
UInt(0)),
|
|
Gen5OperandKind.EncodedConstant when operand.Value == 253 =>
|
|
_module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
Load(_boolType, _scc),
|
|
UInt(1),
|
|
UInt(0)),
|
|
Gen5OperandKind.EncodedConstant when TryDecodeInlineConstant(
|
|
operand.Value,
|
|
out var inline) => UInt(inline),
|
|
_ => throw new InvalidOperationException($"unsupported source {operand}"),
|
|
};
|
|
|
|
// DPP16 remaps src0 across lanes before the VALU operation. The IR
|
|
// has always preserved this control, but treating it as an ordinary
|
|
// local VGPR read breaks every wave reduction used by the XPR
|
|
// renderer (min/max/OR scans become value-with-self operations).
|
|
if (sourceIndex == 0 &&
|
|
instruction.Control is Gen5DppControl dpp)
|
|
{
|
|
value = ApplyDppSource(dpp, value);
|
|
}
|
|
else if (sourceIndex == 0 &&
|
|
instruction.Control is Gen5Dpp8Control dpp8)
|
|
{
|
|
value = ApplyDpp8Source(dpp8, value);
|
|
}
|
|
|
|
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,
|
|
};
|
|
var signExtend = sourceIndex switch
|
|
{
|
|
0 => sdwa.Source0SignExtend,
|
|
1 => sdwa.Source1SignExtend,
|
|
_ => false,
|
|
};
|
|
if (signExtend && selector != 6)
|
|
{
|
|
var width = selector <= 3 ? 8u : 16u;
|
|
value = Bitcast(
|
|
_uintType,
|
|
_module.AddInstruction(
|
|
SpirvOp.BitFieldSExtract,
|
|
_intType,
|
|
Bitcast(_intType, value),
|
|
UInt(0),
|
|
UInt(width)));
|
|
}
|
|
|
|
if (applySdwaIntegerModifiers)
|
|
{
|
|
if ((sdwa.AbsoluteMask & (1u << sourceIndex)) != 0)
|
|
{
|
|
value = Bitcast(
|
|
_uintType,
|
|
Ext(5, _intType, Bitcast(_intType, value)));
|
|
}
|
|
|
|
if ((sdwa.NegateMask & (1u << sourceIndex)) != 0)
|
|
{
|
|
value = _module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
UInt(0),
|
|
value);
|
|
}
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
private uint ApplyDpp8Source(Gen5Dpp8Control control, uint value)
|
|
{
|
|
var lane = GuestWaveLane();
|
|
var laneInGroup = BitwiseAnd(lane, UInt(7));
|
|
var selector = UInt(control.LaneSelectors & 7);
|
|
for (var index = 1u; index < 8; index++)
|
|
{
|
|
selector = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
_module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
laneInGroup,
|
|
UInt(index)),
|
|
UInt((control.LaneSelectors >> checked((int)(index * 3))) & 7),
|
|
selector);
|
|
}
|
|
|
|
var targetLane = IAdd(BitwiseAnd(lane, UInt(0xFFFF_FFF8)), selector);
|
|
targetLane = BitwiseAnd(targetLane, UInt(31));
|
|
var shuffled = _module.AddInstruction(
|
|
SpirvOp.GroupNonUniformShuffle,
|
|
_uintType,
|
|
UInt(3),
|
|
value,
|
|
targetLane);
|
|
if (control.FetchInactive)
|
|
{
|
|
return shuffled;
|
|
}
|
|
|
|
var activeWord = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
Load(_boolType, _exec),
|
|
UInt(1),
|
|
UInt(0));
|
|
var sourceActive = IsNotZero(
|
|
_module.AddInstruction(
|
|
SpirvOp.GroupNonUniformShuffle,
|
|
_uintType,
|
|
UInt(3),
|
|
activeWord,
|
|
targetLane));
|
|
return _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
sourceActive,
|
|
shuffled,
|
|
UInt(0));
|
|
}
|
|
|
|
private uint ApplyDppSource(Gen5DppControl control, uint value)
|
|
{
|
|
GetDppSourceLane(control, out var targetLane, out var inRange);
|
|
var lane = GuestWaveLane();
|
|
var safeTarget = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
inRange,
|
|
targetLane,
|
|
lane);
|
|
safeTarget = BitwiseAnd(safeTarget, UInt(31));
|
|
var shuffled = _module.AddInstruction(
|
|
SpirvOp.GroupNonUniformShuffle,
|
|
_uintType,
|
|
UInt(3),
|
|
value,
|
|
safeTarget);
|
|
|
|
var sourceAvailable = inRange;
|
|
if (!control.FetchInactive)
|
|
{
|
|
var activeWord = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
Load(_boolType, _exec),
|
|
UInt(1),
|
|
UInt(0));
|
|
var shuffledActive = _module.AddInstruction(
|
|
SpirvOp.GroupNonUniformShuffle,
|
|
_uintType,
|
|
UInt(3),
|
|
activeWord,
|
|
safeTarget);
|
|
sourceAvailable = _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
sourceAvailable,
|
|
IsNotZero(shuffledActive));
|
|
}
|
|
|
|
return _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
sourceAvailable,
|
|
shuffled,
|
|
UInt(0));
|
|
}
|
|
|
|
private uint ApplySdwaDestination(
|
|
Gen5SdwaControl control,
|
|
uint value,
|
|
uint previous)
|
|
{
|
|
var (shift, width) = control.DestinationSelect switch
|
|
{
|
|
0 => (0u, 8u),
|
|
1 => (8u, 8u),
|
|
2 => (16u, 8u),
|
|
3 => (24u, 8u),
|
|
4 => (0u, 16u),
|
|
5 => (16u, 16u),
|
|
_ => (0u, 32u),
|
|
};
|
|
if (width == 32)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
var lowMask = width == 8 ? 0xFFu : 0xFFFFu;
|
|
var fieldMask = lowMask << checked((int)shift);
|
|
var upperStart = shift + width;
|
|
var upperMask = upperStart == 32
|
|
? 0u
|
|
: uint.MaxValue << checked((int)upperStart);
|
|
var positioned = ShiftLeftLogical(
|
|
BitwiseAnd(value, UInt(lowMask)),
|
|
UInt(shift));
|
|
return control.DestinationUnused switch
|
|
{
|
|
0 => positioned,
|
|
1 => BitwiseOr(
|
|
positioned,
|
|
_module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
IsNotZero(BitwiseAnd(positioned, UInt(1u << checked((int)(shift + width - 1))))),
|
|
UInt(upperMask),
|
|
UInt(0))),
|
|
2 => BitwiseOr(
|
|
BitwiseAnd(previous, UInt(~fieldMask)),
|
|
positioned),
|
|
_ => throw new InvalidOperationException("reserved SDWA destination-unused mode"),
|
|
};
|
|
}
|
|
|
|
private static bool IsSupportedDppControl(uint control) =>
|
|
control <= 0xFF ||
|
|
control is >= 0x101 and <= 0x10F or
|
|
>= 0x111 and <= 0x11F or
|
|
>= 0x121 and <= 0x12F or
|
|
0x140 or 0x141 or
|
|
>= 0x150 and <= 0x15F or
|
|
>= 0x160 and <= 0x16F;
|
|
|
|
private void GetDppSourceLane(
|
|
Gen5DppControl control,
|
|
out uint targetLane,
|
|
out uint inRange)
|
|
{
|
|
var lane = GuestWaveLane();
|
|
var rowBase = BitwiseAnd(lane, UInt(0xFFFF_FFF0));
|
|
var rowLane = BitwiseAnd(lane, UInt(15));
|
|
var dpp = control.Control;
|
|
inRange = _module.ConstantBool(true);
|
|
|
|
if (dpp <= 0xFF)
|
|
{
|
|
var quadLane = BitwiseAnd(lane, UInt(3));
|
|
var selected = UInt(dpp & 3);
|
|
for (var index = 1u; index < 4; index++)
|
|
{
|
|
selected = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
_module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
quadLane,
|
|
UInt(index)),
|
|
UInt((dpp >> checked((int)(index * 2))) & 3),
|
|
selected);
|
|
}
|
|
|
|
targetLane = IAdd(BitwiseAnd(lane, UInt(0xFFFF_FFFC)), selected);
|
|
return;
|
|
}
|
|
|
|
if (dpp is >= 0x101 and <= 0x10F)
|
|
{
|
|
var shift = UInt(dpp & 15);
|
|
var shifted = IAdd(rowLane, shift);
|
|
inRange = _module.AddInstruction(
|
|
SpirvOp.ULessThan,
|
|
_boolType,
|
|
shifted,
|
|
UInt(16));
|
|
targetLane = IAdd(rowBase, BitwiseAnd(shifted, UInt(15)));
|
|
return;
|
|
}
|
|
|
|
if (dpp is >= 0x111 and <= 0x11F)
|
|
{
|
|
var shift = UInt(dpp & 15);
|
|
inRange = _module.AddInstruction(
|
|
SpirvOp.UGreaterThanEqual,
|
|
_boolType,
|
|
rowLane,
|
|
shift);
|
|
targetLane = IAdd(
|
|
rowBase,
|
|
BitwiseAnd(
|
|
_module.AddInstruction(SpirvOp.ISub, _uintType, rowLane, shift),
|
|
UInt(15)));
|
|
return;
|
|
}
|
|
|
|
if (dpp is >= 0x121 and <= 0x12F)
|
|
{
|
|
targetLane = IAdd(
|
|
rowBase,
|
|
BitwiseAnd(
|
|
_module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
rowLane,
|
|
UInt(dpp & 15)),
|
|
UInt(15)));
|
|
return;
|
|
}
|
|
|
|
targetLane = dpp switch
|
|
{
|
|
0x140 => IAdd(rowBase, _module.AddInstruction(
|
|
SpirvOp.ISub, _uintType, UInt(15), rowLane)),
|
|
0x141 => IAdd(
|
|
BitwiseAnd(lane, UInt(0xFFFF_FFF8)),
|
|
_module.AddInstruction(
|
|
SpirvOp.ISub,
|
|
_uintType,
|
|
UInt(7),
|
|
BitwiseAnd(lane, UInt(7)))),
|
|
>= 0x150 and <= 0x15F => IAdd(rowBase, UInt(dpp & 15)),
|
|
>= 0x160 and <= 0x16F => IAdd(
|
|
rowBase,
|
|
BitwiseXor(rowLane, UInt(dpp & 15))),
|
|
_ => lane,
|
|
};
|
|
}
|
|
|
|
private uint IsDppWriteEnabled(Gen5DppControl control)
|
|
{
|
|
GetDppSourceLane(control, out _, out var inRange);
|
|
var lane = GuestWaveLane();
|
|
var row = ShiftRightLogical(lane, UInt(4));
|
|
var bank = BitwiseAnd(lane, UInt(3));
|
|
var rowEnabled = IsNotZero(BitwiseAnd(
|
|
UInt(control.RowMask),
|
|
ShiftLeftLogical(UInt(1), row)));
|
|
var bankEnabled = IsNotZero(BitwiseAnd(
|
|
UInt(control.BankMask),
|
|
ShiftLeftLogical(UInt(1), bank)));
|
|
var sourceAllowsWrite = control.BoundControl
|
|
? _module.ConstantBool(true)
|
|
: inRange;
|
|
return _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
rowEnabled,
|
|
_module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
bankEnabled,
|
|
sourceAllowsWrite));
|
|
}
|
|
|
|
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,
|
|
applySdwaIntegerModifiers: false));
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
if (operand.Kind == Gen5OperandKind.VectorRegister)
|
|
{
|
|
var vectorLow = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
LoadV(operand.Value));
|
|
var high = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_ulongType,
|
|
LoadV(operand.Value + 1));
|
|
high = ShiftLeftLogical64(
|
|
high,
|
|
_module.Constant64(_ulongType, 32));
|
|
return _module.AddInstruction(
|
|
SpirvOp.BitwiseOr,
|
|
_ulongType,
|
|
vectorLow,
|
|
high);
|
|
}
|
|
|
|
// Scalar inline negative constants are signed immediates. B64
|
|
// consumers sign-extend them, so -1 denotes a full 64-bit mask.
|
|
if (operand.Kind == Gen5OperandKind.EncodedConstant &&
|
|
operand.Value is >= 193 and <= 208)
|
|
{
|
|
var signed = -(long)(operand.Value - 192);
|
|
return _module.Constant64(_ulongType, unchecked((ulong)signed));
|
|
}
|
|
|
|
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 carryMask = instruction.Sources.Count > 2
|
|
? IsCurrentLaneSet(GetRawSource64(instruction, 2))
|
|
: Load(_boolType, _vcc);
|
|
var carryIn = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
carryMask,
|
|
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));
|
|
StoreCarryOut(instruction, 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 borrowMask = instruction.Sources.Count > 2
|
|
? IsCurrentLaneSet(GetRawSource64(instruction, 2))
|
|
: Load(_boolType, _vcc);
|
|
var borrowIn = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
borrowMask,
|
|
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));
|
|
StoreCarryOut(instruction, borrow);
|
|
return result;
|
|
}
|
|
|
|
private uint BroadcastFirstWave64Active(uint value)
|
|
{
|
|
var lane = GuestWaveLane();
|
|
EmitConditional(
|
|
_module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
lane,
|
|
UInt(0)),
|
|
() => Store(WaveBroadcastScratchPointer(), UInt(0)));
|
|
EmitWave64Barrier();
|
|
|
|
var activeMask = BooleanToWaveMask(Load(_boolType, _exec));
|
|
var lowMask = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
activeMask);
|
|
var highMask = _module.AddInstruction(
|
|
SpirvOp.UConvert,
|
|
_uintType,
|
|
ShiftRightLogical64(
|
|
activeMask,
|
|
_module.Constant64(_ulongType, 32)));
|
|
var hasLow = IsNotZero(lowMask);
|
|
var hasHigh = IsNotZero(highMask);
|
|
var firstLow = Ext(73, _uintType, lowMask);
|
|
var firstHigh = IAdd(UInt(32), Ext(73, _uintType, highMask));
|
|
var firstLane = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
hasLow,
|
|
firstLow,
|
|
_module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
hasHigh,
|
|
firstHigh,
|
|
UInt(0)));
|
|
var isFirst = _module.AddInstruction(
|
|
SpirvOp.IEqual,
|
|
_boolType,
|
|
lane,
|
|
firstLane);
|
|
EmitConditional(
|
|
_module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
isFirst,
|
|
_module.AddInstruction(
|
|
SpirvOp.LogicalOr,
|
|
_boolType,
|
|
hasLow,
|
|
hasHigh)),
|
|
() => Store(WaveBroadcastScratchPointer(), value));
|
|
EmitWave64Barrier();
|
|
var result = Load(_uintType, WaveBroadcastScratchPointer());
|
|
EmitWave64Barrier();
|
|
return result;
|
|
}
|
|
|
|
private void StoreCarryOut(
|
|
Gen5ShaderInstruction instruction,
|
|
uint carry)
|
|
{
|
|
var activeCarry = _module.AddInstruction(
|
|
SpirvOp.LogicalAnd,
|
|
_boolType,
|
|
Load(_boolType, _exec),
|
|
carry);
|
|
if (instruction.Control is Gen5Vop3Control { ScalarDestination: { } register })
|
|
{
|
|
StoreWaveMask(register, activeCarry);
|
|
return;
|
|
}
|
|
|
|
StoreWaveMask(106, activeCarry);
|
|
}
|
|
|
|
private bool TryEmitReadlane(
|
|
Gen5ShaderInstruction instruction,
|
|
out string error)
|
|
{
|
|
error = string.Empty;
|
|
if (instruction.Destinations.Count == 0 ||
|
|
instruction.Destinations[0].Kind != Gen5OperandKind.ScalarRegister)
|
|
{
|
|
error = "VReadlaneB32 expects scalar destination";
|
|
return false;
|
|
}
|
|
|
|
var destination = instruction.Destinations[0].Value;
|
|
var src0 = GetRawSource(instruction, 0);
|
|
|
|
if (_subgroupInvocationIdInput != 0)
|
|
{
|
|
// sdst = vsrc0[lane(src1)] — broadcast from the specified lane.
|
|
var laneSelect = GetRawSource(instruction, 1);
|
|
var broadcast = _module.AddInstruction(
|
|
SpirvOp.GroupNonUniformBroadcast,
|
|
_uintType,
|
|
UInt(3), // Subgroup scope
|
|
src0,
|
|
laneSelect);
|
|
StoreS(destination, broadcast);
|
|
}
|
|
else
|
|
{
|
|
// Fallback: no subgroup ops, read current lane's value.
|
|
StoreS(destination, src0);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private uint EmitPermlane16(
|
|
Gen5ShaderInstruction instruction,
|
|
bool exchangeRows)
|
|
{
|
|
if (instruction.Control is not Gen5Vop3Control control ||
|
|
(control.OperandSelect & ~3u) != 0 ||
|
|
control.AbsoluteMask != 0 ||
|
|
control.NegateMask != 0 ||
|
|
control.OutputModifier != 0 ||
|
|
control.Clamp)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"invalid permlane modifiers for {instruction.Opcode}");
|
|
}
|
|
|
|
var value = GetRawSource(instruction, 0);
|
|
var selectorLow = GetRawSource(instruction, 1);
|
|
var selectorHigh = GetRawSource(instruction, 2);
|
|
var lane = GuestWaveLane();
|
|
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);
|
|
targetLane = BitwiseAnd(targetLane, UInt(31));
|
|
var shuffled = _module.AddInstruction(
|
|
SpirvOp.GroupNonUniformShuffle,
|
|
_uintType,
|
|
UInt(3),
|
|
value,
|
|
targetLane);
|
|
var fetchInactive = (control.OperandSelect & 1) != 0;
|
|
if (fetchInactive)
|
|
{
|
|
return shuffled;
|
|
}
|
|
|
|
var activeWord = _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
Load(_boolType, _exec),
|
|
UInt(1),
|
|
UInt(0));
|
|
var sourceActive = IsNotZero(
|
|
_module.AddInstruction(
|
|
SpirvOp.GroupNonUniformShuffle,
|
|
_uintType,
|
|
UInt(3),
|
|
activeWord,
|
|
targetLane));
|
|
return _module.AddInstruction(
|
|
SpirvOp.Select,
|
|
_uintType,
|
|
sourceActive,
|
|
shuffled,
|
|
UInt(0));
|
|
}
|
|
|
|
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) =>
|
|
Gen5InlineConstants.TryDecode(encoded, out value);
|
|
}
|
|
}
|