/* code from https://github.com/gwsystems/silverfish/blob/master/runtime/runtime.c */ #include #include // TODO: Throughout here we use `assert` for error conditions, which isn't optimal // Instead we should use `unlikely` branches to a single trapping function (which should optimize better) // The below functions are for implementing WASM instructions // ROTL and ROTR helper functions INLINE u32 rotl_u32(u32 n, u32 c_u32) { // WASM requires a modulus here (usually a single bitwise op, but it means we need no assert) unsigned int c = c_u32 % (CHAR_BIT * sizeof(n)); const unsigned int mask = (CHAR_BIT * sizeof(n) - 1); // assumes width is a power of 2. c &= mask; return (n << c) | (n >> ((-c) & mask)); } INLINE u32 rotr_u32(u32 n, u32 c_u32) { // WASM requires a modulus here (usually a single bitwise op, but it means we need no assert) unsigned int c = c_u32 % (CHAR_BIT * sizeof(n)); const unsigned int mask = (CHAR_BIT * sizeof(n) - 1); c &= mask; return (n >> c) | (n << ((-c) & mask)); } INLINE u64 rotl_u64(u64 n, u64 c_u64) { // WASM requires a modulus here (usually a single bitwise op, but it means we need no assert) unsigned int c = c_u64 % (CHAR_BIT * sizeof(n)); const unsigned int mask = (CHAR_BIT * sizeof(n) - 1); // assumes width is a power of 2. c &= mask; return (n << c) | (n >> ((-c) & mask)); } INLINE u64 rotr_u64(u64 n, u64 c_u64) { // WASM requires a modulus here (usually a single bitwise op, but it means we need no assert) unsigned int c = c_u64 % (CHAR_BIT * sizeof(n)); const unsigned int mask = (CHAR_BIT * sizeof(n) - 1); c &= mask; return (n >> c) | (n << ((-c) & mask)); } // Now safe division and remainder INLINE u32 u32_div(u32 a, u32 b) { assert(b); return a / b; } INLINE u32 u32_rem(u32 a, u32 b) { assert(b); return a % b; } INLINE i32 i32_div(i32 a, i32 b) { assert(b && (a != INT32_MIN || b != -1)); return a / b; } INLINE i32 i32_rem(i32 a, i32 b) { assert(b && (a != INT32_MIN || b != -1)); return a % b; } INLINE u64 u64_div(u64 a, u64 b) { assert(b); return a / b; } INLINE u64 u64_rem(u64 a, u64 b) { assert(b); return a % b; } INLINE i64 i64_div(i64 a, i64 b) { assert(b && (a != INT64_MIN || b != -1)); return a / b; } INLINE i64 i64_rem(i64 a, i64 b) { assert(b && (a != INT64_MIN || b != -1)); return a % b; } // float to integer conversion methods // In C, float => int conversions always truncate // If a int2float(int::min_value) <= float <= int2float(int::max_value), it must always be safe to truncate it u32 u32_trunc_f32(float f) { assert(0 <= f && f <= UINT32_MAX); return (u32)f; } i32 i32_trunc_f32(float f) { assert(INT32_MIN <= f && f <= INT32_MAX); return (i32)f; } u32 u32_trunc_f64(double f) { assert(0 <= f && f <= UINT32_MAX); return (u32)f; } i32 i32_trunc_f64(double f) { assert(INT32_MIN <= f && f <= INT32_MAX); return (i32)f; } u64 u64_trunc_f32(float f) { assert(0 <= f && f <= UINT64_MAX); return (u64)f; } i64 i64_trunc_f32(float f) { assert(INT64_MIN <= f && f <= INT64_MAX); return (i64)f; } u64 u64_trunc_f64(double f) { assert(0 <= f && f <= UINT64_MAX); return (u64)f; } i64 i64_trunc_f64(double f) { assert(INT64_MIN <= f && f <= INT64_MAX); return (i64)f; } // Float => Float truncation functions INLINE float f32_trunc_f32(float f) { return trunc(f); } INLINE float f32_min(float a, float b) { return a < b ? a : b; } INLINE float f32_max(float a, float b) { return a > b ? a : b; } INLINE float f32_floor(float a) { return floor(a); } INLINE double f64_min(double a, double b) { return a < b ? a : b; } INLINE double f64_max(double a, double b) { return a > b ? a : b; } INLINE double f64_floor(double a) { return floor(a); }