341 lines
12 KiB
Plaintext
341 lines
12 KiB
Plaintext
// Generated by SharpEmu Gen5MslTranslator.
|
|
#include <metal_stdlib>
|
|
|
|
using namespace metal;
|
|
|
|
struct Gen5AddressRange
|
|
{
|
|
ulong first_page;
|
|
ulong page_count;
|
|
device uint* base;
|
|
};
|
|
|
|
// Finds the mapped range of a guest address; a miss records the page in the fault bitmap.
|
|
static inline device uint* sharpemu_resolve_device_address(device const Gen5AddressRange* ranges, uint range_count, device uint* faults, uint fault_words, ulong address, thread bool& valid)
|
|
{
|
|
ulong masked = address & 0xFFFFFFFFFFFFul;
|
|
ulong page = masked >> 14u;
|
|
uint low = 0u;
|
|
uint high = range_count;
|
|
for (uint step = 0u; step < 7u; step++)
|
|
{
|
|
uint middle = low + ((high - low) >> 1u);
|
|
bool move_up = low < high && ranges[middle].first_page <= page;
|
|
low = move_up ? middle + 1u : low;
|
|
high = move_up ? high : middle;
|
|
}
|
|
valid = false;
|
|
if (low != 0u)
|
|
{
|
|
Gen5AddressRange range = ranges[low - 1u];
|
|
if (page - range.first_page < range.page_count)
|
|
{
|
|
valid = true;
|
|
ulong byte_offset = ((page - range.first_page) << 14u) + (masked & 0x3FFFul);
|
|
return (device uint*)((device uchar*)range.base + byte_offset);
|
|
}
|
|
}
|
|
uint page_index = (uint)page;
|
|
uint word = page_index >> 5u;
|
|
if (word < fault_words)
|
|
{
|
|
atomic_fetch_or_explicit((device atomic_uint*)(faults + word), 1u << (page_index & 31u), memory_order_relaxed);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static inline uint sharpemu_load_device_dword(device const Gen5AddressRange* ranges, uint range_count, device uint* faults, uint fault_words, ulong address)
|
|
{
|
|
bool valid = false;
|
|
device uint* word = sharpemu_resolve_device_address(ranges, range_count, faults, fault_words, address, valid);
|
|
return valid ? *word : 0u;
|
|
}
|
|
|
|
// An unaligned value assembled from the two aligned dwords it spans.
|
|
static inline uint sharpemu_load_device_unaligned(device const Gen5AddressRange* ranges, uint range_count, device uint* faults, uint fault_words, ulong address, uint count)
|
|
{
|
|
uint alignment = (uint)(address & 3ul);
|
|
ulong aligned = address & ~3ul;
|
|
uint shift = alignment * 8u;
|
|
uint low = sharpemu_load_device_dword(ranges, range_count, faults, fault_words, aligned);
|
|
uint high = (alignment + count > 4u) ? sharpemu_load_device_dword(ranges, range_count, faults, fault_words, aligned + 4ul) : 0u;
|
|
return (low >> shift) | (shift == 0u ? 0u : (high << (32u - shift)));
|
|
}
|
|
|
|
static inline void sharpemu_store_device_dword(device const Gen5AddressRange* ranges, uint range_count, device uint* faults, uint fault_words, ulong address, uint value)
|
|
{
|
|
bool valid = false;
|
|
device uint* word = sharpemu_resolve_device_address(ranges, range_count, faults, fault_words, address, valid);
|
|
if (valid)
|
|
{
|
|
*word = value;
|
|
}
|
|
}
|
|
|
|
static inline void sharpemu_store_device_masked_word(device const Gen5AddressRange* ranges, uint range_count, device uint* faults, uint fault_words, ulong address, uint value, uint mask)
|
|
{
|
|
if (mask == 0u)
|
|
{
|
|
return;
|
|
}
|
|
bool valid = false;
|
|
device uint* word = sharpemu_resolve_device_address(ranges, range_count, faults, fault_words, address, valid);
|
|
if (!valid)
|
|
{
|
|
return;
|
|
}
|
|
if (mask == 0xFFFFFFFFu)
|
|
{
|
|
*word = value;
|
|
return;
|
|
}
|
|
device atomic_uint* atomic_word = (device atomic_uint*)word;
|
|
uint observed = atomic_load_explicit(atomic_word, memory_order_relaxed);
|
|
while (!atomic_compare_exchange_weak_explicit(atomic_word, &observed, (observed & ~mask) | value, memory_order_relaxed, memory_order_relaxed))
|
|
{
|
|
}
|
|
}
|
|
|
|
// A sub-dword store merges into the dwords it touches.
|
|
static inline void sharpemu_store_device_bytes(device const Gen5AddressRange* ranges, uint range_count, device uint* faults, uint fault_words, ulong address, uint value, uint count)
|
|
{
|
|
uint element_mask = count == 1u ? 0xFFu : 0xFFFFu;
|
|
uint alignment = (uint)(address & 3ul);
|
|
ulong aligned = address & ~3ul;
|
|
uint shift = alignment * 8u;
|
|
uint carry_shift = 32u - shift;
|
|
bool crosses = alignment + count > 4u;
|
|
uint masked = value & element_mask;
|
|
sharpemu_store_device_masked_word(ranges, range_count, faults, fault_words, aligned, masked << shift, element_mask << shift);
|
|
sharpemu_store_device_masked_word(ranges, range_count, faults, fault_words, aligned + 4ul, crosses ? (masked >> carry_shift) : 0u, crosses ? (element_mask >> carry_shift) : 0u);
|
|
}
|
|
|
|
static inline uint sharpemu_atomic_step(device atomic_uint* word, bool increment)
|
|
{
|
|
uint observed = atomic_load_explicit(word, memory_order_relaxed);
|
|
while (!atomic_compare_exchange_weak_explicit(word, &observed, increment ? observed + 1u : observed - 1u, memory_order_relaxed, memory_order_relaxed))
|
|
{
|
|
}
|
|
return observed;
|
|
}
|
|
|
|
// Compare-and-swap that retries only spurious failures; returns the value observed.
|
|
static inline uint sharpemu_atomic_compare_swap(device atomic_uint* word, uint compare, uint value)
|
|
{
|
|
uint expected = compare;
|
|
while (!atomic_compare_exchange_weak_explicit(word, &expected, value, memory_order_relaxed, memory_order_relaxed) && expected == compare)
|
|
{
|
|
}
|
|
return expected;
|
|
}
|
|
|
|
struct Gen5StageResources
|
|
{
|
|
array<device uint*, 1> buffers [[id(0)]];
|
|
array<uint, 1> buffer_bytes [[id(1)]];
|
|
};
|
|
|
|
static inline uint sharpemu_load_word(device uint* b, uint bytes, uint addr)
|
|
{
|
|
if ((addr & 3u) == 0u)
|
|
{
|
|
return addr + 4u <= bytes ? b[addr >> 2] : 0u;
|
|
}
|
|
uint value = 0u;
|
|
device const uchar* p = (device const uchar*)b;
|
|
for (uint i = 0u; i < 4u; i++)
|
|
{
|
|
if (addr + i < bytes)
|
|
{
|
|
value |= (uint)p[addr + i] << (i * 8u);
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static inline uint sharpemu_load_bytes(device uint* b, uint bytes, uint addr, uint count, bool signExtend)
|
|
{
|
|
uint value = 0u;
|
|
device const uchar* p = (device const uchar*)b;
|
|
for (uint i = 0u; i < count; i++)
|
|
{
|
|
if (addr + i < bytes)
|
|
{
|
|
value |= (uint)p[addr + i] << (i * 8u);
|
|
}
|
|
}
|
|
if (signExtend && count < 4u)
|
|
{
|
|
uint shift = 32u - (count * 8u);
|
|
value = (uint)(((int)(value << shift)) >> shift);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static inline void sharpemu_store_bytes(device uint* b, uint bytes, uint addr, uint value, uint count)
|
|
{
|
|
device uchar* p = (device uchar*)b;
|
|
for (uint i = 0u; i < count; i++)
|
|
{
|
|
if (addr + i < bytes)
|
|
{
|
|
p[addr + i] = (uchar)((value >> (i * 8u)) & 0xFFu);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void sharpemu_store_masked_word(device uint* b, uint bytes, uint index, uint value, uint mask)
|
|
{
|
|
if (mask == 0u || index >= (bytes >> 2))
|
|
{
|
|
return;
|
|
}
|
|
if (mask == 0xFFFFFFFFu)
|
|
{
|
|
b[index] = value;
|
|
return;
|
|
}
|
|
// The other bits of the word survive stores from neighbouring invocations.
|
|
device atomic_uint* word = (device atomic_uint*)(b + index);
|
|
uint observed = atomic_load_explicit(word, memory_order_relaxed);
|
|
while (!atomic_compare_exchange_weak_explicit(word, &observed, (observed & ~mask) | value, memory_order_relaxed, memory_order_relaxed))
|
|
{
|
|
}
|
|
}
|
|
|
|
static inline void sharpemu_store_element(device uint* b, uint bytes, uint addr, uint count, uint4 values, uint4 masks)
|
|
{
|
|
uint shift = (addr & 3u) * 8u;
|
|
uint index = addr >> 2;
|
|
uint carryValue = 0u;
|
|
uint carryMask = 0u;
|
|
for (uint k = 0u; k <= count; k++)
|
|
{
|
|
uint value = carryValue;
|
|
uint mask = carryMask;
|
|
if (k < count)
|
|
{
|
|
uint element = values[k] & masks[k];
|
|
value |= element << shift;
|
|
mask |= masks[k] << shift;
|
|
carryValue = shift == 0u ? 0u : element >> (32u - shift);
|
|
carryMask = shift == 0u ? 0u : masks[k] >> (32u - shift);
|
|
}
|
|
sharpemu_store_masked_word(b, bytes, index + k, value, mask);
|
|
}
|
|
}
|
|
|
|
static inline uint sharpemu_ballot(bool value)
|
|
{
|
|
return (uint)(uint64_t)simd_ballot(value);
|
|
}
|
|
|
|
static constant float sharpemu_off_i4_table[16] =
|
|
{
|
|
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,
|
|
};
|
|
|
|
kernel void gen5_cs(
|
|
constant Gen5StageResources& sharpemu_resources [[buffer(0)]],
|
|
constant uint* sharpemu_push_data [[buffer(1)]],
|
|
uint3 sharpemu_local_id [[thread_position_in_threadgroup]],
|
|
uint3 sharpemu_group_id [[threadgroup_position_in_grid]],
|
|
uint sharpemu_lane [[thread_index_in_simdgroup]])
|
|
{
|
|
uint s[128] = {};
|
|
uint v[256] = {};
|
|
bool exec = true;
|
|
bool vcc = false;
|
|
bool scc = false;
|
|
uint pc = 0u;
|
|
bool active = true;
|
|
uint steps = 0u;
|
|
device uint* b0 = sharpemu_resources.buffers[0];
|
|
uint bias[1] = {};
|
|
s[0] = sharpemu_push_data[0];
|
|
s[8] = sharpemu_push_data[1];
|
|
s[9] = sharpemu_push_data[2];
|
|
s[10] = sharpemu_push_data[3];
|
|
s[11] = sharpemu_push_data[4];
|
|
bias[0] = (sharpemu_push_data[5] >> 0u) & 0xFFu;
|
|
v[0] = sharpemu_local_id.x;
|
|
v[1] = sharpemu_local_id.y;
|
|
v[2] = sharpemu_local_id.z;
|
|
active = (sharpemu_group_id.x * 32u + sharpemu_local_id.x) < 0xFFFFFFFFu
|
|
&& (sharpemu_group_id.y * 1u + sharpemu_local_id.y) < 0xFFFFFFFFu
|
|
&& (sharpemu_group_id.z * 1u + sharpemu_local_id.z) < 0xFFFFFFFFu;
|
|
s[106] = 0u;
|
|
s[107] = 0u;
|
|
s[126] = sharpemu_ballot(true);
|
|
s[127] = 0u;
|
|
|
|
while (active)
|
|
{
|
|
switch (pc)
|
|
{
|
|
case 0u:
|
|
{
|
|
uint t0 = 0x3FC00000u;
|
|
if (exec) { v[0] = t0; }
|
|
uint t1 = 0x40100000u;
|
|
if (exec) { v[1] = t1; }
|
|
uint t2 = 0x41200000u;
|
|
if (exec) { v[2] = t2; }
|
|
uint t3 = as_type<uint>((fma(as_type<float>(v[0]), as_type<float>(v[1]), as_type<float>(v[2]))));
|
|
if (exec) { v[2] = t3; }
|
|
uint t4 = 0x7FFFFFFFu;
|
|
if (exec) { v[3] = t4; }
|
|
uint t5 = 0x10003u;
|
|
if (exec) { v[4] = t5; }
|
|
uint t6 = as_type<uint>(mulhi(as_type<int>(v[3]), as_type<int>(v[4])));
|
|
if (exec) { v[5] = t6; }
|
|
uint t7 = ((v[3]) * (v[4]));
|
|
if (exec) { v[6] = t7; }
|
|
uint t8 = ((0x0u + 0u + 0u + (0u * 0u)) + bias[0]);
|
|
if (exec)
|
|
{
|
|
sharpemu_store_bytes(b0, sharpemu_resources.buffer_bytes[0], t8 + 0u, v[2], 4u);
|
|
}
|
|
uint t9 = ((0x4u + 0u + 0u + (0u * 0u)) + bias[0]);
|
|
if (exec)
|
|
{
|
|
sharpemu_store_bytes(b0, sharpemu_resources.buffer_bytes[0], t9 + 0u, v[5], 4u);
|
|
}
|
|
uint t10 = ((0x8u + 0u + 0u + (0u * 0u)) + bias[0]);
|
|
if (exec)
|
|
{
|
|
sharpemu_store_bytes(b0, sharpemu_resources.buffer_bytes[0], t10 + 0u, v[6], 4u);
|
|
}
|
|
uint t11 = 0u;
|
|
uint t12 = t11;
|
|
s[126] = t12;
|
|
exec = ((t12) >> sharpemu_lane & 1u) != 0u;
|
|
uint t13 = ((0xCu + 0u + 0u + (0u * 0u)) + bias[0]);
|
|
if (exec)
|
|
{
|
|
sharpemu_store_bytes(b0, sharpemu_resources.buffer_bytes[0], t13 + 0u, v[2], 4u);
|
|
}
|
|
uint t14 = 0xFFFFFFFFu;
|
|
uint t15 = t14;
|
|
s[126] = t15;
|
|
exec = ((t15) >> sharpemu_lane & 1u) != 0u;
|
|
uint t16 = ((0x10u + 0u + 0u + (0u * 0u)) + bias[0]);
|
|
if (exec)
|
|
{
|
|
sharpemu_store_bytes(b0, sharpemu_resources.buffer_bytes[0], t16 + 0u, v[0], 4u);
|
|
}
|
|
active = false;
|
|
}
|
|
break;
|
|
default:
|
|
active = false;
|
|
break;
|
|
}
|
|
if (++steps >= 100000u)
|
|
{
|
|
active = false;
|
|
}
|
|
}
|
|
}
|