[x64] Implement missing byte-swap store/load paths

Fill in all unimplemented LOAD_STORE_BYTE_SWAP code paths in the x64
backend that previously hit assert_false or assert_always and add
tests
This commit is contained in:
Herman S.
2026-03-16 02:52:26 +09:00
parent 18fc1cd03c
commit 5e636ac335
2 changed files with 366 additions and 30 deletions

View File

@@ -1349,11 +1349,15 @@ struct STORE_OFFSET_I16
static void Emit(X64Emitter& e, const EmitArgType& i) {
auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2);
if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) {
assert_false(i.src3.is_constant);
if (e.IsFeatureEnabled(kX64EmitMovbe)) {
if (i.src3.is_constant) {
e.mov(e.word[addr],
xe::byte_swap(static_cast<uint16_t>(i.src3.constant())));
} else if (e.IsFeatureEnabled(kX64EmitMovbe)) {
e.movbe(e.word[addr], i.src3);
} else {
assert_always("not implemented");
e.movzx(e.ecx, i.src3);
e.ror(e.cx, 8);
e.mov(e.word[addr], e.cx);
}
} else {
if (i.src3.is_constant) {
@@ -1399,11 +1403,15 @@ struct STORE_OFFSET_I32
} else {
auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2);
if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) {
assert_false(i.src3.is_constant);
if (e.IsFeatureEnabled(kX64EmitMovbe)) {
if (i.src3.is_constant) {
e.mov(e.dword[addr],
xe::byte_swap(static_cast<uint32_t>(i.src3.constant())));
} else if (e.IsFeatureEnabled(kX64EmitMovbe)) {
e.movbe(e.dword[addr], i.src3);
} else {
assert_always("not implemented");
e.mov(e.ecx, i.src3);
e.bswap(e.ecx);
e.mov(e.dword[addr], e.ecx);
}
} else {
if (i.src3.is_constant) {
@@ -1426,11 +1434,15 @@ struct STORE_OFFSET_I64
static void Emit(X64Emitter& e, const EmitArgType& i) {
auto addr = ComputeMemoryAddressOffset(e, i.src1, i.src2);
if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) {
assert_false(i.src3.is_constant);
if (e.IsFeatureEnabled(kX64EmitMovbe)) {
if (i.src3.is_constant) {
e.MovMem64(addr,
xe::byte_swap(static_cast<uint64_t>(i.src3.constant())));
} else if (e.IsFeatureEnabled(kX64EmitMovbe)) {
e.movbe(e.qword[addr], i.src3);
} else {
assert_always("not implemented");
e.mov(e.rcx, i.src3);
e.bswap(e.rcx);
e.mov(e.qword[addr], e.rcx);
}
} else {
if (i.src3.is_constant) {
@@ -1537,9 +1549,13 @@ struct LOAD_I64 : Sequence<LOAD_I64, I<OPCODE_LOAD, I64Op, I64Op>> {
struct LOAD_F32 : Sequence<LOAD_F32, I<OPCODE_LOAD, F32Op, I64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
auto addr = ComputeMemoryAddress(e, i.src1);
e.vmovss(i.dest, e.dword[addr]);
if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) {
assert_always("not implemented yet");
// Load as integer, byte-swap, move to XMM.
e.mov(e.eax, e.dword[addr]);
e.bswap(e.eax);
e.vmovd(i.dest, e.eax);
} else {
e.vmovss(i.dest, e.dword[addr]);
}
if (IsTracingData()) {
e.lea(e.GetNativeParam(1), e.dword[addr]);
@@ -1551,9 +1567,13 @@ struct LOAD_F32 : Sequence<LOAD_F32, I<OPCODE_LOAD, F32Op, I64Op>> {
struct LOAD_F64 : Sequence<LOAD_F64, I<OPCODE_LOAD, F64Op, I64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
auto addr = ComputeMemoryAddress(e, i.src1);
e.vmovsd(i.dest, e.qword[addr]);
if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) {
assert_always("not implemented yet");
// Load as integer, byte-swap, move to XMM.
e.mov(e.rax, e.qword[addr]);
e.bswap(e.rax);
e.vmovq(i.dest, e.rax);
} else {
e.vmovsd(i.dest, e.qword[addr]);
}
if (IsTracingData()) {
e.lea(e.GetNativeParam(1), e.qword[addr]);
@@ -1605,11 +1625,15 @@ struct STORE_I16 : Sequence<STORE_I16, I<OPCODE_STORE, VoidOp, I64Op, I16Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
auto addr = ComputeMemoryAddress(e, i.src1);
if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) {
assert_false(i.src2.is_constant);
if (e.IsFeatureEnabled(kX64EmitMovbe)) {
if (i.src2.is_constant) {
e.mov(e.word[addr],
xe::byte_swap(static_cast<uint16_t>(i.src2.constant())));
} else if (e.IsFeatureEnabled(kX64EmitMovbe)) {
e.movbe(e.word[addr], i.src2);
} else {
assert_always("not implemented");
e.movzx(e.ecx, i.src2);
e.ror(e.cx, 8);
e.mov(e.word[addr], e.cx);
}
} else {
if (i.src2.is_constant) {
@@ -1649,11 +1673,15 @@ struct STORE_I32 : Sequence<STORE_I32, I<OPCODE_STORE, VoidOp, I64Op, I32Op>> {
} else {
auto addr = ComputeMemoryAddress(e, i.src1);
if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) {
assert_false(i.src2.is_constant);
if (e.IsFeatureEnabled(kX64EmitMovbe)) {
if (i.src2.is_constant) {
e.mov(e.dword[addr],
xe::byte_swap(static_cast<uint32_t>(i.src2.constant())));
} else if (e.IsFeatureEnabled(kX64EmitMovbe)) {
e.movbe(e.dword[addr], i.src2);
} else {
assert_always("not implemented");
e.mov(e.ecx, i.src2);
e.bswap(e.ecx);
e.mov(e.dword[addr], e.ecx);
}
} else {
if (i.src2.is_constant) {
@@ -1674,11 +1702,16 @@ struct STORE_I64 : Sequence<STORE_I64, I<OPCODE_STORE, VoidOp, I64Op, I64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
auto addr = ComputeMemoryAddress(e, i.src1);
if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) {
assert_false(i.src2.is_constant);
if (e.IsFeatureEnabled(kX64EmitMovbe)) {
if (i.src2.is_constant) {
// MovMem64 avoids clobbering rax (used by ComputeMemoryAddress).
e.MovMem64(addr,
xe::byte_swap(static_cast<uint64_t>(i.src2.constant())));
} else if (e.IsFeatureEnabled(kX64EmitMovbe)) {
e.movbe(e.qword[addr], i.src2);
} else {
assert_always("not implemented");
e.mov(e.rcx, i.src2);
e.bswap(e.rcx);
e.mov(e.qword[addr], e.rcx);
}
} else {
if (i.src2.is_constant) {
@@ -1699,8 +1732,14 @@ struct STORE_F32 : Sequence<STORE_F32, I<OPCODE_STORE, VoidOp, I64Op, F32Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
auto addr = ComputeMemoryAddress(e, i.src1);
if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) {
assert_false(i.src2.is_constant);
assert_always("not yet implemented");
if (i.src2.is_constant) {
e.mov(e.dword[addr],
xe::byte_swap(static_cast<uint32_t>(i.src2.value->constant.i32)));
} else {
e.vmovd(e.ecx, i.src2);
e.bswap(e.ecx);
e.mov(e.dword[addr], e.ecx);
}
} else {
if (i.src2.is_constant) {
e.mov(e.dword[addr], i.src2.value->constant.i32);
@@ -1720,8 +1759,15 @@ struct STORE_F64 : Sequence<STORE_F64, I<OPCODE_STORE, VoidOp, I64Op, F64Op>> {
static void Emit(X64Emitter& e, const EmitArgType& i) {
auto addr = ComputeMemoryAddress(e, i.src1);
if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) {
assert_false(i.src2.is_constant);
assert_always("not yet implemented");
if (i.src2.is_constant) {
e.MovMem64(
addr,
xe::byte_swap(static_cast<uint64_t>(i.src2.value->constant.i64)));
} else {
e.vmovq(e.rcx, i.src2);
e.bswap(e.rcx);
e.mov(e.qword[addr], e.rcx);
}
} else {
if (i.src2.is_constant) {
e.MovMem64(addr, i.src2.value->constant.i64);
@@ -1742,10 +1788,12 @@ struct STORE_V128
static void Emit(X64Emitter& e, const EmitArgType& i) {
auto addr = ComputeMemoryAddress(e, i.src1);
if (i.instr->flags & LoadStoreFlags::LOAD_STORE_BYTE_SWAP) {
assert_false(i.src2.is_constant);
e.vpshufb(e.xmm0, i.src2, e.GetXmmConstPtr(XMMByteSwapMask));
// changed from vmovaps, the penalty on the vpshufb is unavoidable but
// we dont need to incur another here too
if (i.src2.is_constant) {
e.LoadConstantXmm(e.xmm0, i.src2.constant());
e.vpshufb(e.xmm0, e.xmm0, e.GetXmmConstPtr(XMMByteSwapMask));
} else {
e.vpshufb(e.xmm0, i.src2, e.GetXmmConstPtr(XMMByteSwapMask));
}
e.vmovdqa(e.ptr[addr], e.xmm0);
} else {
if (i.src2.is_constant) {