diff --git a/remill/Arch/Runtime/Intrinsics.cpp b/remill/Arch/Runtime/Intrinsics.cpp index 2a4763e1..25a91964 100644 --- a/remill/Arch/Runtime/Intrinsics.cpp +++ b/remill/Arch/Runtime/Intrinsics.cpp @@ -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_ diff --git a/remill/Arch/Runtime/Intrinsics.h b/remill/Arch/Runtime/Intrinsics.h index 73254172..f74c1c28 100644 --- a/remill/Arch/Runtime/Intrinsics.h +++ b/remill/Arch/Runtime/Intrinsics.h @@ -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); diff --git a/remill/Arch/Runtime/Operators.h b/remill/Arch/Runtime/Operators.h index cd7d6cc9..32daf382 100644 --- a/remill/Arch/Runtime/Operators.h +++ b/remill/Arch/Runtime/Operators.h @@ -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( \ + __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(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 op, \ + Memory *memory, MnW 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(&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(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 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 -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::ST { return static_cast::ST>(val); } - // Zero-extend an integer to twice its current width. template ALWAYS_INLINE static @@ -560,6 +536,95 @@ auto TruncTo(T val) -> typename IntegerType
::BT { return static_cast::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(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 \ 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 \ 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 \ 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 \ T prefix ## InsertV ## size(T vec, size_t n, base_type val) { \ static_assert( \ sizeof(base_type) == sizeof(typename VectorType::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 -ALWAYS_INLINE constexpr T _EmptyVec(void) { +template +ALWAYS_INLINE constexpr T _ZeroVec(void) { + static_assert(std::is_same::BT>::value, + "Vector type and base don't match."); return {}; } -#define _ClearV(...) _EmptyVec() +#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() +#define UClearV16(...) _ZeroVec() +#define UClearV32(...) _ZeroVec() +#define UClearV64(...) _ZeroVec() +#define UClearV128(...) _ZeroVec() + +#define SClearV8(...) _ZeroVec() +#define SClearV16(...) _ZeroVec() +#define SClearV32(...) _ZeroVec() +#define SClearV64(...) _ZeroVec() +#define SClearV128(...) _ZeroVec() + +#define FClearV32(...) _ZeroVec() +#define FClearV64(...) _ZeroVec() + +// 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 \ @@ -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_ diff --git a/remill/Arch/Runtime/Types.h b/remill/Arch/Runtime/Types.h index f58af4d7..83a8c005 100644 --- a/remill/Arch/Runtime/Types.h +++ b/remill/Arch/Runtime/Types.h @@ -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 : public IntegerType {}; - -template -struct Tag; - -template -struct Tag : public Tag {}; - -template -struct Tag : public Tag {}; - -template -struct Tag> : public Tag {}; - -template -struct Tag> : public Tag {}; - -template -struct Tag> : public Tag {}; - -template -struct Tag> : public Tag {}; - -template -struct Tag> : public Tag {}; - -template -struct Tag> : public Tag {}; - -template -struct Tag> : public Tag {}; - -template -struct Tag> : public Tag {}; - -struct VectorTag {}; -struct NumberTag {}; - -#define MAKE_TAG(prefix, size, tag) \ - template <> \ - struct Tag { \ - 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(value); } diff --git a/remill/Arch/X86/Semantics/CONVERT.h b/remill/Arch/X86/Semantics/CONVERT.h index 3afd7855..523adc32 100644 --- a/remill/Arch/X86/Semantics/CONVERT.h +++ b/remill/Arch/X86/Semantics/CONVERT.h @@ -39,20 +39,20 @@ namespace { template 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 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 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 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 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)); }