mirror of
https://github.com/lifting-bits/remill
synced 2026-06-21 13:56:07 +00:00
Cleanups to Operators.h. Specifically, to the signed versions of vector operations. They now more strictly enforce type safety. This caught a few bugs. Operators.h is arguable simpler as a result.
This commit is contained in:
@@ -93,20 +93,4 @@ extern "C" void __remill_mark_as_used(const void *);
|
||||
USED(__remill_read_cpu_features);
|
||||
}
|
||||
|
||||
// TODO(pag): Assumes little-endian.
|
||||
uint128_t __remill_read_memory_128(Memory *mem, addr_t addr) {
|
||||
uint128_t low_qword = ZExt(__remill_read_memory_64(mem, addr));
|
||||
uint128_t high_qword = ZExt(__remill_read_memory_64(mem, addr + 8));
|
||||
return UOr(UShl(high_qword, 64), low_qword);
|
||||
}
|
||||
|
||||
// TODO(pag): Assumes little-endian.
|
||||
Memory *__remill_write_memory_128(Memory *mem, addr_t addr, uint128_t val) {
|
||||
uint64_t low_qword = Trunc(val);
|
||||
uint64_t high_qword = Trunc(UShr(val, 64));
|
||||
mem = __remill_write_memory_64(mem, addr, low_qword);
|
||||
mem = __remill_write_memory_64(mem, addr + 8, high_qword);
|
||||
return mem;
|
||||
}
|
||||
|
||||
#endif // REMILL_ARCH_SEMANTICS_INSTRINSICS_CPP_
|
||||
|
||||
@@ -170,22 +170,6 @@ extern Memory *__remill_atomic_end(Memory *);
|
||||
[[gnu::used]]
|
||||
extern void __remill_read_cpu_features(State &, Memory *, addr_t addr);
|
||||
|
||||
// "Fake" intrinsics, implemented in terms of other intrinsics. Why use these
|
||||
// fake intrinsics? When we go to bitcode, we don't want LLVM to introduce
|
||||
// struct returns (i.e. passing a pointer to the function that will act
|
||||
// as the destination of the return value). We don't really want that because
|
||||
// it means that some of the intrinsics will be non-uniform, and special
|
||||
// cases are annoying.
|
||||
[[gnu::used, gnu::const]]
|
||||
uint128_t __remill_read_memory_128(Memory *, addr_t);
|
||||
|
||||
|
||||
[[gnu::used, gnu::const]]
|
||||
Memory *__remill_write_memory_128(Memory *, addr_t, uint128_t);
|
||||
|
||||
#define __remill_barrier_compiler()
|
||||
// __asm__ __volatile__ ("" ::: "memory")
|
||||
|
||||
[[gnu::used]]
|
||||
extern void __remill_intrinsics(void);
|
||||
|
||||
|
||||
+266
-159
@@ -6,19 +6,36 @@
|
||||
struct Memory;
|
||||
struct State;
|
||||
|
||||
// Something has gone terribly wrong and we need to stop because there is
|
||||
// an error.
|
||||
//
|
||||
// TODO(pag): What happens if there's a signal handler? How should we
|
||||
// communicate the error class?
|
||||
#define StopFailure() \
|
||||
do { \
|
||||
__remill_error(state, memory, Read(REG_XIP)); \
|
||||
__builtin_unreachable(); \
|
||||
} while (false)
|
||||
|
||||
namespace {
|
||||
|
||||
ALWAYS_INLINE static
|
||||
uint128_t __remill_read_memory_128(Memory *mem, addr_t addr);
|
||||
|
||||
ALWAYS_INLINE static
|
||||
Memory *__remill_write_memory_128(Memory *mem, addr_t addr, uint128_t val);
|
||||
|
||||
|
||||
#define MAKE_SIGNED_MEM_ACCESS(size) \
|
||||
ALWAYS_INLINE static \
|
||||
int ## size ## _t __remill_read_memory_s ## size( \
|
||||
Memory *mem, addr_t addr) { \
|
||||
return static_cast<int ## size ## _t>( \
|
||||
__remill_read_memory_ ## size(mem, addr)); \
|
||||
} \
|
||||
\
|
||||
ALWAYS_INLINE static \
|
||||
Memory * __remill_write_memory_s ## size( \
|
||||
Memory *mem, addr_t addr, int ## size ## _t val) { \
|
||||
return __remill_write_memory_ ## size( \
|
||||
mem, addr, static_cast<uint ## size ## _t>(val)); \
|
||||
}
|
||||
|
||||
MAKE_SIGNED_MEM_ACCESS(8)
|
||||
MAKE_SIGNED_MEM_ACCESS(16)
|
||||
MAKE_SIGNED_MEM_ACCESS(32)
|
||||
MAKE_SIGNED_MEM_ACCESS(64)
|
||||
MAKE_SIGNED_MEM_ACCESS(128)
|
||||
|
||||
// Read a value directly.
|
||||
ALWAYS_INLINE static bool _Read(Memory *, bool val) {
|
||||
return val;
|
||||
@@ -80,6 +97,7 @@ MAKE_MREAD(8, 8, uint, 8)
|
||||
MAKE_MREAD(16, 16, uint, 16)
|
||||
MAKE_MREAD(32, 32, uint, 32)
|
||||
MAKE_MREAD(64, 64, uint, 64)
|
||||
|
||||
MAKE_MREAD(32, 32, float, f32)
|
||||
MAKE_MREAD(64, 64, float, f64)
|
||||
MAKE_MREAD(80, 64, float, f80)
|
||||
@@ -113,22 +131,23 @@ MAKE_RWRITE(float64_t)
|
||||
#undef MAKE_RWRITE
|
||||
|
||||
// Make write operators for writing values to memory.
|
||||
#define MAKE_MWRITE(size, write_size, type_prefix, access_suffix) \
|
||||
#define MAKE_MWRITE(size, write_size, mem_prefix, type_prefix, access_suffix) \
|
||||
ALWAYS_INLINE static \
|
||||
Memory *_Write( \
|
||||
Memory *memory, MnW<type_prefix ## size ## _t> op, \
|
||||
Memory *memory, MnW<mem_prefix ## size ## _t> op, \
|
||||
type_prefix ## write_size ## _t val) { \
|
||||
return __remill_write_memory_ ## access_suffix (\
|
||||
memory, op.addr, val); \
|
||||
}
|
||||
|
||||
MAKE_MWRITE(8, 8, uint, 8)
|
||||
MAKE_MWRITE(16, 16, uint, 16)
|
||||
MAKE_MWRITE(32, 32, uint, 32)
|
||||
MAKE_MWRITE(64, 64, uint, 64)
|
||||
MAKE_MWRITE(32, 32, float, f32)
|
||||
MAKE_MWRITE(64, 64, float, f64)
|
||||
MAKE_MWRITE(80, 64, float, f80)
|
||||
MAKE_MWRITE(8, 8, uint, uint, 8)
|
||||
MAKE_MWRITE(16, 16, uint, uint, 16)
|
||||
MAKE_MWRITE(32, 32, uint, uint, 32)
|
||||
MAKE_MWRITE(64, 64, uint, uint, 64)
|
||||
|
||||
MAKE_MWRITE(32, 32, float, float, f32)
|
||||
MAKE_MWRITE(64, 64, float, float, f64)
|
||||
MAKE_MWRITE(80, 64, float, float, f80)
|
||||
|
||||
#undef MAKE_MWRITE
|
||||
|
||||
@@ -145,12 +164,18 @@ MAKE_MWRITE(80, 64, float, f80)
|
||||
return reinterpret_cast<const T *>(&vec.val)->accessor; \
|
||||
}
|
||||
|
||||
MAKE_READRV(U, 8, bytes, uint)
|
||||
MAKE_READRV(U, 16, words, uint)
|
||||
MAKE_READRV(U, 32, dwords, uint)
|
||||
MAKE_READRV(U, 64, qwords, uint)
|
||||
MAKE_READRV(F, 32, floats, float)
|
||||
MAKE_READRV(F, 64, doubles, float)
|
||||
MAKE_READRV(U, 8, bytes, uint8_t)
|
||||
MAKE_READRV(U, 16, words, uint16_t)
|
||||
MAKE_READRV(U, 32, dwords, uint32_t)
|
||||
MAKE_READRV(U, 64, qwords, uint64_t)
|
||||
|
||||
MAKE_READRV(S, 8, bytes, int8_t)
|
||||
MAKE_READRV(S, 16, words, int16_t)
|
||||
MAKE_READRV(S, 32, dwords, int32_t)
|
||||
MAKE_READRV(S, 64, qwords, int64_t)
|
||||
|
||||
MAKE_READRV(F, 32, floats, float32_t)
|
||||
MAKE_READRV(F, 64, doubles, float64_t)
|
||||
|
||||
#undef MAKE_READRV
|
||||
|
||||
@@ -172,6 +197,13 @@ MAKE_READV(U, 16, words)
|
||||
MAKE_READV(U, 32, dwords)
|
||||
MAKE_READV(U, 64, qwords)
|
||||
MAKE_READV(U, 128, dqwords)
|
||||
|
||||
MAKE_READV(S, 8, sbytes)
|
||||
MAKE_READV(S, 16, swords)
|
||||
MAKE_READV(S, 32, sdwords)
|
||||
MAKE_READV(S, 64, sqwords)
|
||||
MAKE_READV(S, 128, sdqwords)
|
||||
|
||||
MAKE_READV(F, 32, floats)
|
||||
MAKE_READV(F, 64, doubles)
|
||||
|
||||
@@ -207,6 +239,13 @@ MAKE_MREADV(U, 16, words, 16)
|
||||
MAKE_MREADV(U, 32, dwords, 32)
|
||||
MAKE_MREADV(U, 64, qwords, 64)
|
||||
MAKE_MREADV(U, 128, dqwords, 128)
|
||||
|
||||
MAKE_MREADV(S, 8, sbytes, s8)
|
||||
MAKE_MREADV(S, 16, swords, s16)
|
||||
MAKE_MREADV(S, 32, sdwords, s32)
|
||||
MAKE_MREADV(S, 64, sqwords, s64)
|
||||
MAKE_MREADV(S, 128, sdqwords, s128)
|
||||
|
||||
MAKE_MREADV(F, 32, floats, f32)
|
||||
MAKE_MREADV(F, 64, doubles, f64)
|
||||
|
||||
@@ -251,6 +290,13 @@ MAKE_WRITEV(U, 16, words, VnW, uint16_t)
|
||||
MAKE_WRITEV(U, 32, dwords, VnW, uint32_t)
|
||||
MAKE_WRITEV(U, 64, qwords, VnW, uint64_t)
|
||||
MAKE_WRITEV(U, 128, dqwords, VnW, uint128_t)
|
||||
|
||||
MAKE_WRITEV(S, 8, sbytes, VnW, int8_t)
|
||||
MAKE_WRITEV(S, 16, swords, VnW, int16_t)
|
||||
MAKE_WRITEV(S, 32, sdwords, VnW, int32_t)
|
||||
MAKE_WRITEV(S, 64, sqwords, VnW, int64_t)
|
||||
MAKE_WRITEV(S, 128, sdqwords, VnW, int128_t)
|
||||
|
||||
MAKE_WRITEV(F, 32, floats, VnW, float32_t)
|
||||
MAKE_WRITEV(F, 64, doubles, VnW, float64_t)
|
||||
|
||||
@@ -258,6 +304,12 @@ MAKE_WRITEV(U, 8, bytes, RVnW, uint8_t)
|
||||
MAKE_WRITEV(U, 16, words, RVnW, uint16_t)
|
||||
MAKE_WRITEV(U, 32, dwords, RVnW, uint32_t)
|
||||
MAKE_WRITEV(U, 64, qwords, RVnW, uint64_t)
|
||||
|
||||
MAKE_WRITEV(S, 8, sbytes, RVnW, int8_t)
|
||||
MAKE_WRITEV(S, 16, swords, RVnW, int16_t)
|
||||
MAKE_WRITEV(S, 32, sdwords, RVnW, int32_t)
|
||||
MAKE_WRITEV(S, 64, sqwords, RVnW, int64_t)
|
||||
|
||||
MAKE_WRITEV(F, 32, floats, RVnW, float32_t)
|
||||
MAKE_WRITEV(F, 64, doubles, RVnW, float64_t)
|
||||
|
||||
@@ -303,6 +355,13 @@ MAKE_MWRITEV(U, 16, words, 16, uint16_t)
|
||||
MAKE_MWRITEV(U, 32, dwords, 32, uint32_t)
|
||||
MAKE_MWRITEV(U, 64, qwords, 64, uint64_t)
|
||||
MAKE_MWRITEV(U, 128, dqwords, 128, uint128_t)
|
||||
|
||||
MAKE_MWRITEV(S, 8, sbytes, s8, int8_t)
|
||||
MAKE_MWRITEV(S, 16, swords, s16, int16_t)
|
||||
MAKE_MWRITEV(S, 32, sdwords, s32, int32_t)
|
||||
MAKE_MWRITEV(S, 64, sqwords, s64, int64_t)
|
||||
MAKE_MWRITEV(S, 128, sdqwords, s128, int128_t)
|
||||
|
||||
MAKE_MWRITEV(F, 32, floats, f32, float32_t)
|
||||
MAKE_MWRITEV(F, 64, doubles, f64, float64_t)
|
||||
|
||||
@@ -330,7 +389,7 @@ MAKE_WRITE_REF(float64_t)
|
||||
#define Read(op) _Read(memory, op)
|
||||
|
||||
// Write a source value to a destination operand, where the sizes of the
|
||||
// valyes must match.
|
||||
// values must match.
|
||||
#define Write(op, val) \
|
||||
do { \
|
||||
static_assert( \
|
||||
@@ -339,84 +398,6 @@ MAKE_WRITE_REF(float64_t)
|
||||
memory = _Write(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
// Handle writes of N-bit values to M-bit values with N <= M. If N < M then the
|
||||
// source value will be zero-extended to the dest value type. This is useful
|
||||
// on x86-64 where writes to 32-bit registers zero-extend to 64-bits. In a
|
||||
// 64-bit build of Remill, the `R32W` type used in the X86 architecture
|
||||
// runtime actually aliases `R64W`.
|
||||
#define WriteZExt(op, val) \
|
||||
do { \
|
||||
Write(op, ZExtTo<decltype(op)>(val)); \
|
||||
} while (false)
|
||||
|
||||
#define UWriteV8 WriteV8
|
||||
#define SWriteV8 WriteV8
|
||||
#define WriteV8(op, val) \
|
||||
do { \
|
||||
memory = _UWriteV8(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define UWriteV16 WriteV16
|
||||
#define SWriteV16 WriteV16
|
||||
#define WriteV16(op, val) \
|
||||
do { \
|
||||
memory = _UWriteV16(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define UWriteV32 WriteV32
|
||||
#define SWriteV32 WriteV32
|
||||
#define WriteV32(op, val) \
|
||||
do { \
|
||||
memory = _UWriteV32(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define UWriteV64 WriteV64
|
||||
#define SWriteV64 WriteV64
|
||||
#define WriteV64(op, val) \
|
||||
do { \
|
||||
memory = _UWriteV64(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define UWriteV128 WriteV128
|
||||
#define SWriteV128 WriteV128
|
||||
#define WriteV128(op, val) \
|
||||
do { \
|
||||
memory = _UWriteV128(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define FWriteV32(op, val) \
|
||||
do { \
|
||||
memory = _FWriteV32(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define FWriteV64(op, val) \
|
||||
do { \
|
||||
memory = _FWriteV64(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
|
||||
#define UReadV8 ReadV8
|
||||
#define SReadV8 ReadV8
|
||||
#define ReadV8(op) _UReadV8(memory, op)
|
||||
|
||||
#define UReadV16 ReadV16
|
||||
#define SReadV16 ReadV16
|
||||
#define ReadV16(op) _UReadV16(memory, op)
|
||||
|
||||
#define UReadV32 ReadV32
|
||||
#define SReadV32 ReadV32
|
||||
#define ReadV32(op) _UReadV32(memory, op)
|
||||
|
||||
#define UReadV64 ReadV64
|
||||
#define SReadV64 ReadV64
|
||||
#define ReadV64(op) _UReadV64(memory, op)
|
||||
|
||||
#define UReadV128 ReadV128
|
||||
#define SReadV128 ReadV128
|
||||
#define ReadV128(op) _UReadV128(memory, op)
|
||||
|
||||
#define FReadV32(op) _FReadV32(memory, op)
|
||||
#define FReadV64(op) _FReadV64(memory, op)
|
||||
|
||||
template <typename T>
|
||||
ALWAYS_INLINE static constexpr
|
||||
@@ -481,11 +462,7 @@ MAKE_CONVERT(float64_t, Float64)
|
||||
// Return the value as-is. This is useful when making many accessors using
|
||||
// macros, because it lets us decide to pull out values as-is, as unsigned
|
||||
// integers, or as signed integers.
|
||||
template <typename T>
|
||||
ALWAYS_INLINE static
|
||||
T Identity(T val) {
|
||||
return val;
|
||||
}
|
||||
#define Identity(...) __VA_ARGS__
|
||||
|
||||
// Convert an integer to some other type. This is important for
|
||||
// integer literals, whose type are `int`.
|
||||
@@ -507,7 +484,6 @@ auto SLiteral(U val) -> typename IntegerType<T>::ST {
|
||||
return static_cast<typename IntegerType<T>::ST>(val);
|
||||
}
|
||||
|
||||
|
||||
// Zero-extend an integer to twice its current width.
|
||||
template <typename T>
|
||||
ALWAYS_INLINE static
|
||||
@@ -560,6 +536,95 @@ auto TruncTo(T val) -> typename IntegerType<DT>::BT {
|
||||
return static_cast<typename IntegerType<DT>::BT>(val);
|
||||
}
|
||||
|
||||
// Handle writes of N-bit values to M-bit values with N <= M. If N < M then the
|
||||
// source value will be zero-extended to the dest value type. This is useful
|
||||
// on x86-64 where writes to 32-bit registers zero-extend to 64-bits. In a
|
||||
// 64-bit build of Remill, the `R32W` type used in the X86 architecture
|
||||
// runtime actually aliases `R64W`.
|
||||
#define WriteZExt(op, val) \
|
||||
do { \
|
||||
Write(op, ZExtTo<decltype(op)>(val)); \
|
||||
} while (false)
|
||||
|
||||
#define SWriteV8(op, val) \
|
||||
do { \
|
||||
memory = _SWriteV8(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define UWriteV8(op, val) \
|
||||
do { \
|
||||
memory = _UWriteV8(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define SWriteV16(op, val) \
|
||||
do { \
|
||||
memory = _SWriteV16(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define UWriteV16(op, val) \
|
||||
do { \
|
||||
memory = _UWriteV16(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define SWriteV32(op, val) \
|
||||
do { \
|
||||
memory = _SWriteV32(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define UWriteV32(op, val) \
|
||||
do { \
|
||||
memory = _UWriteV32(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define SWriteV64(op, val) \
|
||||
do { \
|
||||
memory = _SWriteV64(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define UWriteV64(op, val) \
|
||||
do { \
|
||||
memory = _UWriteV64(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define SWriteV128(op, val) \
|
||||
do { \
|
||||
memory = _SWriteV128(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define UWriteV128(op, val) \
|
||||
do { \
|
||||
memory = _UWriteV128(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define FWriteV32(op, val) \
|
||||
do { \
|
||||
memory = _FWriteV32(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
#define FWriteV64(op, val) \
|
||||
do { \
|
||||
memory = _FWriteV64(memory, op, (val)); \
|
||||
} while (false)
|
||||
|
||||
|
||||
#define SReadV8 _SReadV8
|
||||
#define UReadV8(op) _UReadV8(memory, op)
|
||||
|
||||
#define SReadV16(op) _SReadV16(memory, op)
|
||||
#define UReadV16(op) _UReadV16(memory, op)
|
||||
|
||||
#define SReadV32(op) _SReadV32(memory, op)
|
||||
#define UReadV32(op) _UReadV32(memory, op)
|
||||
|
||||
#define SReadV64(op) _SReadV64(memory, op)
|
||||
#define UReadV64(op) _UReadV64(memory, op)
|
||||
|
||||
#define SReadV128(op) _SReadV128(memory, op)
|
||||
#define UReadV128(op) _UReadV128(memory, op)
|
||||
|
||||
#define FReadV32(op) _FReadV32(memory, op)
|
||||
#define FReadV64(op) _FReadV64(memory, op)
|
||||
|
||||
// Useful for stubbing out an operator.
|
||||
#define MAKE_NOP(...)
|
||||
|
||||
@@ -645,43 +710,42 @@ ALWAYS_INLINE static bool BNot(bool a) {
|
||||
}
|
||||
|
||||
// Binary broadcast operator.
|
||||
#define MAKE_BIN_BROADCAST(op, size, accessor, in, out) \
|
||||
#define MAKE_BIN_BROADCAST(op, size, accessor) \
|
||||
template <typename T> \
|
||||
ALWAYS_INLINE static \
|
||||
T op ## V ## size(const T &L, const T &R) { \
|
||||
T ret{}; \
|
||||
_Pragma("unroll") \
|
||||
for (auto i = 0UL; i < NumVectorElems(L); ++i) { \
|
||||
ret.elems[i] = out(op(in(L.elems[i]), \
|
||||
in(R.elems[i]))); \
|
||||
ret.elems[i] = op(L.elems[i], R.elems[i]); \
|
||||
} \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
// Unary broadcast operator.
|
||||
#define MAKE_UN_BROADCAST(op, size, accessor, in, out) \
|
||||
#define MAKE_UN_BROADCAST(op, size, accessor) \
|
||||
template <typename T> \
|
||||
ALWAYS_INLINE static \
|
||||
T op ## V ## size(const T &R) { \
|
||||
T ret{}; \
|
||||
_Pragma("unroll") \
|
||||
for (auto i = 0UL; i < NumVectorElems(R); ++i) { \
|
||||
ret.elems[i] = out(op(in(R.elems[i]))); \
|
||||
ret.elems[i] = op(R.elems[i]); \
|
||||
} \
|
||||
return ret; \
|
||||
}
|
||||
|
||||
#define MAKE_BROADCASTS(op, make_int_broadcast, make_float_broadcast) \
|
||||
make_int_broadcast(U ## op, 8, bytes, Unsigned, Unsigned) \
|
||||
make_int_broadcast(U ## op, 16, words, Unsigned, Unsigned) \
|
||||
make_int_broadcast(U ## op, 32, dwords, Unsigned, Unsigned) \
|
||||
make_int_broadcast(U ## op, 64, qwords, Unsigned, Unsigned) \
|
||||
make_int_broadcast(S ## op, 8, bytes, Signed, Unsigned) \
|
||||
make_int_broadcast(S ## op, 16, words, Signed, Unsigned) \
|
||||
make_int_broadcast(S ## op, 32, dwords, Signed, Unsigned) \
|
||||
make_int_broadcast(S ## op, 64, qwords, Signed, Unsigned) \
|
||||
make_float_broadcast(F ## op, 32, floats, Identity, Identity) \
|
||||
make_float_broadcast(F ## op, 64, doubles, Identity, Identity) \
|
||||
make_int_broadcast(U ## op, 8, bytes) \
|
||||
make_int_broadcast(U ## op, 16, words) \
|
||||
make_int_broadcast(U ## op, 32, dwords) \
|
||||
make_int_broadcast(U ## op, 64, qwords) \
|
||||
make_int_broadcast(S ## op, 8, sbytes) \
|
||||
make_int_broadcast(S ## op, 16, swords) \
|
||||
make_int_broadcast(S ## op, 32, sdwords) \
|
||||
make_int_broadcast(S ## op, 64, sqwords) \
|
||||
make_float_broadcast(F ## op, 32, floats) \
|
||||
make_float_broadcast(F ## op, 64, doubles) \
|
||||
|
||||
MAKE_BROADCASTS(Add, MAKE_BIN_BROADCAST, MAKE_BIN_BROADCAST)
|
||||
MAKE_BROADCASTS(Sub, MAKE_BIN_BROADCAST, MAKE_BIN_BROADCAST)
|
||||
@@ -701,15 +765,15 @@ MAKE_BROADCASTS(Not, MAKE_UN_BROADCAST, MAKE_NOP)
|
||||
#undef MAKE_UN_BROADCAST
|
||||
|
||||
// Binary broadcast operator.
|
||||
#define MAKE_ACCUMULATE(op, size, accessor, in, out) \
|
||||
#define MAKE_ACCUMULATE(op, size, accessor) \
|
||||
template <typename T> \
|
||||
ALWAYS_INLINE static \
|
||||
auto Accumulate ## op ## V ## size(T R) \
|
||||
-> decltype(out(R.elems[0])) { \
|
||||
auto L = in(R.elems[0]); \
|
||||
-> decltype(R.elems[0] | R.elems[1]) { \
|
||||
auto L = R.elems[0]; \
|
||||
_Pragma("unroll") \
|
||||
for (auto i = 1UL; i < NumVectorElems(R); ++i) { \
|
||||
L = out(op(L, in(R.elems[i]))); \
|
||||
L = op(L, R.elems[i]); \
|
||||
} \
|
||||
return L; \
|
||||
}
|
||||
@@ -757,50 +821,67 @@ MAKE_EXTRACTV(64, float64_t, doubles, Identity, F)
|
||||
#undef MAKE_EXTRACTV
|
||||
|
||||
// Access the Nth element of an aggregate vector.
|
||||
#define MAKE_INSERTV(size, base_type, accessor, in, prefix) \
|
||||
#define MAKE_INSERTV(prefix, size, base_type, accessor) \
|
||||
template <typename T> \
|
||||
T prefix ## InsertV ## size(T vec, size_t n, base_type val) { \
|
||||
static_assert( \
|
||||
sizeof(base_type) == sizeof(typename VectorType<T>::BT), \
|
||||
"Invalid extract"); \
|
||||
vec.elems[n] = in(val); \
|
||||
vec.elems[n] = val; \
|
||||
return vec; \
|
||||
}
|
||||
|
||||
MAKE_INSERTV(8, uint8_t, bytes, Unsigned, U)
|
||||
MAKE_INSERTV(16, uint16_t, words, Unsigned, U)
|
||||
MAKE_INSERTV(32, uint32_t, dwords, Unsigned, U)
|
||||
MAKE_INSERTV(64, uint64_t, qwords, Unsigned, U)
|
||||
MAKE_INSERTV(128, uint128_t, dqwords, Unsigned, U)
|
||||
MAKE_INSERTV(8, int8_t, bytes, Unsigned, S)
|
||||
MAKE_INSERTV(16, int16_t, words, Unsigned, S)
|
||||
MAKE_INSERTV(32, int32_t, dwords, Unsigned, S)
|
||||
MAKE_INSERTV(64, int64_t, qwords, Unsigned, S)
|
||||
MAKE_INSERTV(128, int128_t, dqwords, Unsigned, S)
|
||||
MAKE_INSERTV(32, float32_t, floats, Identity, F)
|
||||
MAKE_INSERTV(64, float64_t, doubles, Identity, F)
|
||||
MAKE_INSERTV(U, 8, uint8_t, bytes)
|
||||
MAKE_INSERTV(U, 16, uint16_t, words)
|
||||
MAKE_INSERTV(U, 32, uint32_t, dwords)
|
||||
MAKE_INSERTV(U, 64, uint64_t, qwords)
|
||||
MAKE_INSERTV(U, 128, uint128_t, dqwords)
|
||||
|
||||
MAKE_INSERTV(S, 8, int8_t, sbytes)
|
||||
MAKE_INSERTV(S, 16, int16_t, swords)
|
||||
MAKE_INSERTV(S, 32, int32_t, sdwords)
|
||||
MAKE_INSERTV(S, 64, int64_t, sqwords)
|
||||
MAKE_INSERTV(S, 128, int128_t, sdqwords)
|
||||
|
||||
MAKE_INSERTV(F, 32, float32_t, floats)
|
||||
MAKE_INSERTV(F, 64, float64_t, doubles)
|
||||
|
||||
#undef MAKE_INSERTV
|
||||
|
||||
template <typename T>
|
||||
ALWAYS_INLINE constexpr T _EmptyVec(void) {
|
||||
template <typename U, typename T>
|
||||
ALWAYS_INLINE constexpr T _ZeroVec(void) {
|
||||
static_assert(std::is_same<U, typename VectorType<T>::BT>::value,
|
||||
"Vector type and base don't match.");
|
||||
return {};
|
||||
}
|
||||
|
||||
#define _ClearV(...) _EmptyVec<decltype(__VA_ARGS__)>()
|
||||
#define _ClearV(base_type, ...)
|
||||
|
||||
#define UClearV8 _ClearV
|
||||
#define UClearV16 _ClearV
|
||||
#define UClearV32 _ClearV
|
||||
#define UClearV64 _ClearV
|
||||
#define UClearV128 _ClearV
|
||||
#define SClearV8 _ClearV
|
||||
#define SClearV16 _ClearV
|
||||
#define SClearV32 _ClearV
|
||||
#define SClearV64 _ClearV
|
||||
#define SClearV128 _ClearV
|
||||
#define FClearV32 _ClearV
|
||||
#define FClearV64 _ClearV
|
||||
#define UClearV8(...) _ZeroVec<uint8_t, decltype(__VA_ARGS__)>()
|
||||
#define UClearV16(...) _ZeroVec<uint16_t, decltype(__VA_ARGS__)>()
|
||||
#define UClearV32(...) _ZeroVec<uint32_t, decltype(__VA_ARGS__)>()
|
||||
#define UClearV64(...) _ZeroVec<uint64_t, decltype(__VA_ARGS__)>()
|
||||
#define UClearV128(...) _ZeroVec<uint128_t, decltype(__VA_ARGS__)>()
|
||||
|
||||
#define SClearV8(...) _ZeroVec<int8_t, decltype(__VA_ARGS__)>()
|
||||
#define SClearV16(...) _ZeroVec<int16_t, decltype(__VA_ARGS__)>()
|
||||
#define SClearV32(...) _ZeroVec<int32_t, decltype(__VA_ARGS__)>()
|
||||
#define SClearV64(...) _ZeroVec<int64_t, decltype(__VA_ARGS__)>()
|
||||
#define SClearV128(...) _ZeroVec<int128_t, decltype(__VA_ARGS__)>()
|
||||
|
||||
#define FClearV32(...) _ZeroVec<float32_t, decltype(__VA_ARGS__)>()
|
||||
#define FClearV64(...) _ZeroVec<float64_t, decltype(__VA_ARGS__)>()
|
||||
|
||||
// Something has gone terribly wrong and we need to stop because there is
|
||||
// an error.
|
||||
//
|
||||
// TODO(pag): What happens if there's a signal handler? How should we
|
||||
// communicate the error class?
|
||||
#define StopFailure() \
|
||||
do { \
|
||||
__remill_error(state, memory, Read(REG_XIP)); \
|
||||
__builtin_unreachable(); \
|
||||
} while (false)
|
||||
|
||||
// Esthetically pleasing names that hide the implicit small-step semantics
|
||||
// of the memory pointer.
|
||||
@@ -824,6 +905,13 @@ ALWAYS_INLINE constexpr T _EmptyVec(void) {
|
||||
memory = __remill_barrier_store_store(memory); \
|
||||
} while (false)
|
||||
|
||||
// A 'compiler' barrier that prevents reordering of instructions across the
|
||||
// barrier.
|
||||
#define BarrierReorder() \
|
||||
do { \
|
||||
__asm__ __volatile__ ("" ::: "memory"); \
|
||||
} while (false)
|
||||
|
||||
// Make a predicate for querying the type of an operand.
|
||||
#define MAKE_PRED(name, X, val) \
|
||||
template <typename T> \
|
||||
@@ -934,6 +1022,25 @@ ALWAYS_INLINE static T Select(bool cond, T if_true, T if_false) {
|
||||
#define UUndefined32 __remill_undefined_32
|
||||
#define UUndefined64 __remill_undefined_64
|
||||
|
||||
|
||||
// TODO(pag): Assumes little-endian.
|
||||
ALWAYS_INLINE static
|
||||
uint128_t __remill_read_memory_128(Memory *mem, addr_t addr) {
|
||||
uint128_t low_qword = ZExt(__remill_read_memory_64(mem, addr));
|
||||
uint128_t high_qword = ZExt(__remill_read_memory_64(mem, addr + 8));
|
||||
return UOr(UShl(high_qword, 64), low_qword);
|
||||
}
|
||||
|
||||
// TODO(pag): Assumes little-endian.
|
||||
ALWAYS_INLINE static
|
||||
Memory *__remill_write_memory_128(Memory *mem, addr_t addr, uint128_t val) {
|
||||
uint64_t low_qword = Trunc(val);
|
||||
uint64_t high_qword = Trunc(UShr(val, 64));
|
||||
mem = __remill_write_memory_64(mem, addr, low_qword);
|
||||
mem = __remill_write_memory_64(mem, addr + 8, high_qword);
|
||||
return mem;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif // REMILL_ARCH_RUNTIME_OPERATORS_H_
|
||||
|
||||
+68
-100
@@ -136,6 +136,37 @@ MAKE_VECTOR(uint128_t, uint128, 1, 128, 16)
|
||||
MAKE_VECTOR(uint128_t, uint128, 2, 256, 32)
|
||||
MAKE_VECTOR(uint128_t, uint128, 4, 512, 64)
|
||||
|
||||
MAKE_VECTOR(int8_t, int8, 1, 8, 1)
|
||||
MAKE_VECTOR(int8_t, int8, 2, 16, 2)
|
||||
MAKE_VECTOR(int8_t, int8, 4, 32, 4)
|
||||
MAKE_VECTOR(int8_t, int8, 8, 64, 8)
|
||||
MAKE_VECTOR(int8_t, int8, 16, 128, 16)
|
||||
MAKE_VECTOR(int8_t, int8, 32, 256, 32)
|
||||
MAKE_VECTOR(int8_t, int8, 64, 512, 64)
|
||||
|
||||
MAKE_VECTOR(int16_t, int16, 1, 16, 2)
|
||||
MAKE_VECTOR(int16_t, int16, 2, 32, 4)
|
||||
MAKE_VECTOR(int16_t, int16, 4, 64, 8)
|
||||
MAKE_VECTOR(int16_t, int16, 8, 128, 16)
|
||||
MAKE_VECTOR(int16_t, int16, 16, 256, 32)
|
||||
MAKE_VECTOR(int16_t, int16, 32, 512, 64)
|
||||
|
||||
MAKE_VECTOR(int32_t, int32, 1, 32, 4)
|
||||
MAKE_VECTOR(int32_t, int32, 2, 64, 8)
|
||||
MAKE_VECTOR(int32_t, int32, 4, 128, 16)
|
||||
MAKE_VECTOR(int32_t, int32, 8, 256, 32)
|
||||
MAKE_VECTOR(int32_t, int32, 16, 512, 64)
|
||||
|
||||
MAKE_VECTOR(int64_t, int64, 1, 64, 8)
|
||||
MAKE_VECTOR(int64_t, int64, 2, 128, 16)
|
||||
MAKE_VECTOR(int64_t, int64, 4, 256, 32)
|
||||
MAKE_VECTOR(int64_t, int64, 8, 512, 64)
|
||||
|
||||
//MAKE_VECTOR(int128_t, int128, 0, 64, 8);
|
||||
MAKE_VECTOR(int128_t, int128, 1, 128, 16)
|
||||
MAKE_VECTOR(int128_t, int128, 2, 256, 32)
|
||||
MAKE_VECTOR(int128_t, int128, 4, 512, 64)
|
||||
|
||||
MAKE_VECTOR(float, float32, 1, 32, 4)
|
||||
MAKE_VECTOR(float, float32, 2, 64, 8)
|
||||
MAKE_VECTOR(float, float32, 4, 128, 16)
|
||||
@@ -155,20 +186,21 @@ MAKE_VECTOR(double, float64, 8, 512, 64);
|
||||
|
||||
union vec8_t final {
|
||||
uint8v1_t bytes;
|
||||
int8v1_t sbytes;
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(1 == sizeof(vec8_t) &&
|
||||
1 == sizeof(vec8_t().bytes),
|
||||
static_assert(1 == sizeof(vec8_t),
|
||||
"Invalid structure packing of `vec8_t`.");
|
||||
|
||||
union vec16_t final {
|
||||
uint8v2_t bytes;
|
||||
uint16v1_t words;
|
||||
|
||||
int8v2_t sbytes;
|
||||
int16v1_t swords;
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(2 == sizeof(vec16_t) &&
|
||||
2 == sizeof(vec16_t().bytes) &&
|
||||
2 == sizeof(vec16_t().words),
|
||||
static_assert(2 == sizeof(vec16_t),
|
||||
"Invalid structure packing of `vec16_t`.");
|
||||
|
||||
union vec32_t final {
|
||||
@@ -176,13 +208,13 @@ union vec32_t final {
|
||||
uint16v2_t words;
|
||||
uint32v1_t dwords;
|
||||
float32v1_t floats;
|
||||
|
||||
int8v4_t sbytes;
|
||||
int16v2_t swords;
|
||||
int32v1_t sdwords;
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(4 == sizeof(vec32_t) &&
|
||||
4 == sizeof(vec32_t().bytes) &&
|
||||
4 == sizeof(vec32_t().words) &&
|
||||
4 == sizeof(vec32_t().dwords) &&
|
||||
4 == sizeof(vec32_t().floats),
|
||||
static_assert(4 == sizeof(vec32_t),
|
||||
"Invalid structure packing of `vec32_t`.");
|
||||
|
||||
union vec64_t final {
|
||||
@@ -192,17 +224,16 @@ union vec64_t final {
|
||||
uint64v1_t qwords;
|
||||
float32v2_t floats;
|
||||
float64v1_t doubles;
|
||||
|
||||
int8v8_t sbytes;
|
||||
int16v4_t swords;
|
||||
int32v2_t sdwords;
|
||||
int64v1_t sqwords;
|
||||
} __attribute__((packed));
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
static_assert(8 == sizeof(vec64_t) &&
|
||||
8 == sizeof(vec64_t().bytes) &&
|
||||
8 == sizeof(vec64_t().words) &&
|
||||
8 == sizeof(vec64_t().dwords) &&
|
||||
8 == sizeof(vec64_t().qwords) &&
|
||||
8 == sizeof(vec64_t().floats) &&
|
||||
8 == sizeof(vec64_t().doubles),
|
||||
static_assert(8 == sizeof(vec64_t),
|
||||
"Invalid structure packing of `vec64_t`.");
|
||||
|
||||
union vec128_t final {
|
||||
@@ -213,16 +244,15 @@ union vec128_t final {
|
||||
uint128v1_t dqwords;
|
||||
float32v4_t floats;
|
||||
float64v2_t doubles;
|
||||
|
||||
int8v16_t sbytes;
|
||||
int16v8_t swords;
|
||||
int32v4_t sdwords;
|
||||
int64v2_t sqwords;
|
||||
int128v1_t sdqwords;
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(16 == sizeof(vec128_t) &&
|
||||
16 == sizeof(vec128_t().bytes) &&
|
||||
16 == sizeof(vec128_t().words) &&
|
||||
16 == sizeof(vec128_t().dwords) &&
|
||||
16 == sizeof(vec128_t().qwords) &&
|
||||
16 == sizeof(vec128_t().dqwords) &&
|
||||
16 == sizeof(vec128_t().floats) &&
|
||||
16 == sizeof(vec128_t().doubles),
|
||||
static_assert(16 == sizeof(vec128_t),
|
||||
"Invalid structure packing of `vec128_t`.");
|
||||
|
||||
union vec256_t final {
|
||||
@@ -233,16 +263,15 @@ union vec256_t final {
|
||||
uint128v2_t dqwords;
|
||||
float32v8_t floats;
|
||||
float64v4_t doubles;
|
||||
|
||||
int8v32_t sbytes;
|
||||
int16v16_t swords;
|
||||
int32v8_t sdwords;
|
||||
int64v4_t sqwords;
|
||||
int128v2_t sdqwords;
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(32 == sizeof(vec256_t) &&
|
||||
32 == sizeof(vec256_t().bytes) &&
|
||||
32 == sizeof(vec256_t().words) &&
|
||||
32 == sizeof(vec256_t().dwords) &&
|
||||
32 == sizeof(vec256_t().qwords) &&
|
||||
32 == sizeof(vec256_t().dqwords) &&
|
||||
32 == sizeof(vec256_t().floats) &&
|
||||
32 == sizeof(vec256_t().doubles),
|
||||
static_assert(32 == sizeof(vec256_t),
|
||||
"Invalid structure packing of `vec256_t`.");
|
||||
|
||||
union vec512_t final {
|
||||
@@ -253,6 +282,12 @@ union vec512_t final {
|
||||
uint128v4_t dqwords;
|
||||
float32v16_t floats;
|
||||
float64v8_t doubles;
|
||||
|
||||
int8v64_t sbytes;
|
||||
int16v32_t swords;
|
||||
int32v16_t sdwords;
|
||||
int64v8_t sqwords;
|
||||
int128v4_t sdqwords;
|
||||
} __attribute__((packed));
|
||||
|
||||
static_assert(64 == sizeof(vec512_t) &&
|
||||
@@ -532,73 +567,6 @@ struct IntegerType {
|
||||
template <>
|
||||
struct IntegerType<bool> : public IntegerType<uint8_t> {};
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct Tag;
|
||||
|
||||
template <typename T>
|
||||
struct Tag<T &> : public Tag<T> {};
|
||||
|
||||
template <typename T>
|
||||
struct Tag<T *> : public Tag<T> {};
|
||||
|
||||
template <typename T>
|
||||
struct Tag<Rn<T>> : public Tag<T> {};
|
||||
|
||||
template <typename T>
|
||||
struct Tag<RVn<T>> : public Tag<T> {};
|
||||
|
||||
template <typename T>
|
||||
struct Tag<RnW<T>> : public Tag<T> {};
|
||||
|
||||
template <typename T>
|
||||
struct Tag<Mn<T>> : public Tag<T> {};
|
||||
|
||||
template <typename T>
|
||||
struct Tag<MnW<T>> : public Tag<T> {};
|
||||
|
||||
template <typename T>
|
||||
struct Tag<In<T>> : public Tag<T> {};
|
||||
|
||||
template <typename T>
|
||||
struct Tag<Vn<T>> : public Tag<T> {};
|
||||
|
||||
template <typename T>
|
||||
struct Tag<VnW<T>> : public Tag<T> {};
|
||||
|
||||
struct VectorTag {};
|
||||
struct NumberTag {};
|
||||
|
||||
#define MAKE_TAG(prefix, size, tag) \
|
||||
template <> \
|
||||
struct Tag<prefix ## size ## _t> { \
|
||||
typedef tag Type; \
|
||||
};
|
||||
|
||||
MAKE_TAG(vec, 8, VectorTag)
|
||||
MAKE_TAG(vec, 16, VectorTag)
|
||||
MAKE_TAG(vec, 32, VectorTag)
|
||||
MAKE_TAG(vec, 64, VectorTag)
|
||||
MAKE_TAG(vec, 128, VectorTag)
|
||||
MAKE_TAG(vec, 256, VectorTag)
|
||||
MAKE_TAG(vec, 512, VectorTag)
|
||||
|
||||
MAKE_TAG(uint, 8, NumberTag)
|
||||
MAKE_TAG(uint, 16, NumberTag)
|
||||
MAKE_TAG(uint, 32, NumberTag)
|
||||
MAKE_TAG(uint, 64, NumberTag)
|
||||
MAKE_TAG(uint, 128, NumberTag)
|
||||
|
||||
MAKE_TAG(int, 8, NumberTag)
|
||||
MAKE_TAG(int, 16, NumberTag)
|
||||
MAKE_TAG(int, 32, NumberTag)
|
||||
MAKE_TAG(int, 64, NumberTag)
|
||||
MAKE_TAG(int, 128, NumberTag)
|
||||
|
||||
MAKE_TAG(float, 32, NumberTag)
|
||||
MAKE_TAG(float, 64, NumberTag)
|
||||
#undef MAKE_TAG
|
||||
|
||||
inline uint8_t operator "" _u8(unsigned long long value) {
|
||||
return static_cast<uint8_t>(value);
|
||||
}
|
||||
|
||||
@@ -39,20 +39,20 @@ namespace {
|
||||
|
||||
template <typename D, typename S1>
|
||||
DEF_SEM(CVTDQ2PD, D dst, S1 src) {
|
||||
auto src_vec = UReadV32(src);
|
||||
auto a = Float64(Signed(UExtractV32(src_vec, 0)));
|
||||
auto b = Float64(Signed(UExtractV32(src_vec, 1)));
|
||||
auto src_vec = SReadV32(src);
|
||||
auto a = Float64(SExtractV32(src_vec, 0));
|
||||
auto b = Float64(SExtractV32(src_vec, 1));
|
||||
auto dst_vec = FClearV64(FReadV64(dst));
|
||||
FWriteV64(dst, FInsertV64(FInsertV64(dst_vec, 0, a), 1, b));
|
||||
}
|
||||
|
||||
template <typename D, typename S1>
|
||||
DEF_SEM(CVTDQ2PD_AVX, D dst, S1 src) {
|
||||
auto src_vec = UReadV32(src);
|
||||
auto src_vec = SReadV32(src);
|
||||
auto dst_vec = FClearV64(FReadV64(dst));
|
||||
_Pragma("unroll")
|
||||
for (size_t i = 0; i < 4UL; ++i) {
|
||||
auto entry = Float64(Signed(UExtractV32(src_vec, i)));
|
||||
auto entry = Float64(SExtractV32(src_vec, i));
|
||||
dst_vec = FInsertV64(dst_vec, i, entry);
|
||||
}
|
||||
FWriteV64(dst, dst_vec);
|
||||
@@ -72,22 +72,22 @@ namespace {
|
||||
template <typename D, typename S1>
|
||||
DEF_SEM(CVTTPD2DQ, D dst, S1 src) {
|
||||
auto src_vec = FReadV64(src);
|
||||
auto a = Unsigned(Int32(FExtractV64(src_vec, 0)));
|
||||
auto b = Unsigned(Int32(FExtractV64(src_vec, 1)));
|
||||
auto dst_vec = UClearV32(UReadV32(dst));
|
||||
UWriteV32(dst, UInsertV32(UInsertV32(dst_vec, 0, a), 1, b));
|
||||
auto a = Int32(FExtractV64(src_vec, 0));
|
||||
auto b = Int32(FExtractV64(src_vec, 1));
|
||||
auto dst_vec = SClearV32(SReadV32(dst));
|
||||
SWriteV32(dst, SInsertV32(SInsertV32(dst_vec, 0, a), 1, b));
|
||||
}
|
||||
|
||||
template <typename D, typename S1>
|
||||
DEF_SEM(CVTTPD2DQ_AVX, D dst, S1 src) {
|
||||
auto src_vec = FReadV64(src);
|
||||
auto dst_vec = UClearV32(UReadV32(dst));
|
||||
auto dst_vec = SClearV32(SReadV32(dst));
|
||||
_Pragma("unroll")
|
||||
for (size_t i = 0; i < 4UL; ++i) {
|
||||
auto entry = Unsigned(Int32(FExtractV64(src_vec, i)));
|
||||
dst_vec = UInsertV32(dst_vec, i, entry);
|
||||
auto entry = Int32(FExtractV64(src_vec, i));
|
||||
dst_vec = SInsertV32(dst_vec, i, entry);
|
||||
}
|
||||
UWriteV32(dst, dst_vec);
|
||||
SWriteV32(dst, dst_vec);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -162,8 +162,8 @@ namespace {
|
||||
template <typename S>
|
||||
DEF_SEM(CVTPI2PD, V128W dst, S src) {
|
||||
auto src_vec = UReadV32(src);
|
||||
auto a = Float64(Signed(UExtractV32(src_vec, 0)));
|
||||
auto b = Float64(Signed(UExtractV32(src_vec, 1)));
|
||||
auto a = Float64(SExtractV32(src_vec, 0));
|
||||
auto b = Float64(SExtractV32(src_vec, 1));
|
||||
FWriteV64(dst, FInsertV64(FInsertV64(FReadV64(dst), 0, a), 1, b));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user