+/* The options to use for inexact and arithmetic exceptions */
+#define FOLD_ROUNDING SFLOAT_ROUND_NEAREST_EVEN
+#define FOLD_TINYNESS SFLOAT_TBEFORE
+
+/*
+ * The constant folder is also responsible for validating if the constant
+ * expressions produce valid results. We cannot trust the FPU control
+ * unit for these exceptions because setting FPU control words might not
+ * work. Systems can set and enforce FPU modes of operation. It's also valid
+ * for libc's to simply ignore FPU exceptions. For instance ARM CPUs in
+ * glibc. We implement some trivial and IEE 754 conformant functions which
+ * emulate those operations. This is an entierly optional compiler feature
+ * which shouldn't be enabled for anything other than performing strict
+ * passes on constant expressions since it's quite slow.
+ */
+typedef uint32_t sfloat_t;
+
+typedef union {
+ qcfloat_t f;
+ sfloat_t s;
+} sfloat_cast_t;
+
+typedef enum {
+ SFLOAT_NOEXCEPT = 0,
+ SFLOAT_INVALID = 1,
+ SFLOAT_DIVBYZERO = 4,
+ SFLOAT_OVERFLOW = 8,
+ SFLOAT_UNDERFLOW = 16,
+ SFLOAT_INEXACT = 32
+} sfloat_exceptionflags_t;
+
+typedef enum {
+ SFLOAT_ROUND_NEAREST_EVEN,
+ SFLOAT_ROUND_DOWN,
+ SFLOAT_ROUND_UP,
+ SFLOAT_ROUND_TO_ZERO
+} sfloat_roundingmode_t;
+
+typedef enum {
+ SFLOAT_TAFTER,
+ SFLOAT_TBEFORE
+} sfloat_tdetect_t;
+
+typedef struct {
+ sfloat_roundingmode_t roundingmode;
+ sfloat_exceptionflags_t exceptionflags;
+ sfloat_tdetect_t tiny;
+} sfloat_state_t;
+
+/* Count of leading zero bits before the most-significand 1 bit. */
+#ifdef _MSC_VER
+/* MSVC has an intrinsic for this */
+ static GMQCC_INLINE uint32_t sfloat_clz(uint32_t x) {
+ int r = 0;
+ _BitScanForward(&r, x);
+ return r;
+ }
+# define SFLOAT_CLZ(X, SUB) \
+ (sfloat_clz((X)) - (SUB))
+#elif defined(__GNUC__) || defined(__CLANG__)
+/* Clang and GCC have a builtin for this */
+# define SFLOAT_CLZ(X, SUB) \
+ (__builtin_clz((X)) - (SUB))
+#else
+/* Native fallback */
+ static GMQCC_INLINE uint32_t sfloat_popcnt(uint32_t x) {
+ x -= ((x >> 1) & 0x55555555);
+ x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
+ x = (((x >> 4) + x) & 0x0F0F0F0F);
+ x += x >> 8;
+ x += x >> 16;
+ return x & 0x0000003F;
+ }
+ static GMQCC_INLINE uint32_t sfloat_clz(uint32_t x) {
+ x |= (x >> 1);
+ x |= (x >> 2);
+ x |= (x >> 4);
+ x |= (x >> 8);
+ x |= (x >> 16);
+ return 32 - sfloat_popcnt(x);
+ }
+# define SFLOAT_CLZ(X, SUB) \
+ (sfloat_clz((X) - (SUB)))
+#endif
+
+/* The value of a NaN */
+#define SFLOAT_NAN 0xFFC00000
+/* Test if NaN */
+#define SFLOAT_ISNAN(A) \
+ (0xFF000000 < (uint32_t)((A) << 1))
+/* Test if signaling NaN */
+#define SFLOAT_ISSNAN(A) \
+ (((((A) >> 22) & 0x1FF) == 0x1FE) && ((A) & 0x003FFFFF))
+/* Raise exception */
+#define SFLOAT_RAISE(STATE, FLAGS) \
+ ((STATE)->exceptionflags = (sfloat_exceptionflags_t)((STATE)->exceptionflags | (FLAGS)))
+/*
+ * Shifts `A' right `COUNT' bits. Non-zero bits are stored in LSB. Size
+ * sets the arbitrarly-large limit.
+ */
+#define SFLOAT_SHIFT(SIZE, A, COUNT, Z) \
+ *(Z) = ((COUNT) == 0) \
+ ? 1 \
+ : (((COUNT) < (SIZE)) \
+ ? ((A) >> (COUNT)) | (((A) << ((-(COUNT)) & ((SIZE) - 1))) != 0) \
+ : ((A) != 0))
+/* Extract fractional component */
+#define SFLOAT_EXTRACT_FRAC(X) \
+ ((uint32_t)((X) & 0x007FFFFF))
+/* Extract exponent component */
+#define SFLOAT_EXTRACT_EXP(X) \
+ ((int16_t)((X) >> 23) & 0xFF)
+/* Extract sign bit */
+#define SFLOAT_EXTRACT_SIGN(X) \
+ ((X) >> 31)
+/* Normalize a subnormal */
+#define SFLOAT_SUBNORMALIZE(SA, Z, SZ) \
+ (void)(*(SZ) = (SA) << SFLOAT_CLZ((SA), 8), *(SZ) = 1 - SFLOAT_CLZ((SA), 8))
+/*
+ * Pack sign, exponent and significand and produce a float.
+ *
+ * Integer portions of the significand are added to the exponent. The
+ * exponent input should be one less than the result exponent whenever
+ * the significand is normalized since normalized significand will
+ * always have an integer portion of value one.
+ */
+#define SFLOAT_PACK(SIGN, EXP, SIG) \
+ (sfloat_t)((((uint32_t)(SIGN)) << 31) + (((uint32_t)(EXP)) << 23) + (SIG))
+
+/* Calculate NaN. If either operands are signaling then raise invalid */
+static sfloat_t sfloat_propagate_nan(sfloat_state_t *state, sfloat_t a, sfloat_t b) {
+ bool isnan_a = SFLOAT_ISNAN(a);
+ bool issnan_a = SFLOAT_ISSNAN(a);
+ bool isnan_b = SFLOAT_ISNAN(b);
+ bool issnan_b = SFLOAT_ISSNAN(b);
+
+ a |= 0x00400000;
+ b |= 0x00400000;
+
+ if (issnan_a | issnan_b)
+ SFLOAT_RAISE(state, SFLOAT_INEXACT);
+ if (issnan_a) {
+ if (issnan_b)
+ goto larger;
+ return isnan_b ? b : a;
+ } else if (isnan_a) {
+ if (issnan_b | !isnan_b)
+ return a;
+larger:
+ if ((uint32_t)(a << 1) < (uint32_t)(b << 1)) return b;
+ if ((uint32_t)(b << 1) < (uint32_t)(a << 1)) return a;
+ return (a < b) ? a : b;
+ }
+ return b;
+}
+
+/* Round and pack */
+static sfloat_t SFLOAT_PACK_round(sfloat_state_t *state, bool sign_z, int16_t exp_z, uint32_t sig_z) {
+ sfloat_roundingmode_t mode = state->roundingmode;
+ bool even = !!(mode == SFLOAT_ROUND_NEAREST_EVEN);
+ unsigned char increment = 0x40;
+ unsigned char bits = sig_z & 0x7F;
+
+ if (!even) {
+ if (mode == SFLOAT_ROUND_TO_ZERO)
+ increment = 0;
+ else {
+ increment = 0x7F;
+ if (sign_z) {
+ if (mode == SFLOAT_ROUND_UP)
+ increment = 0;
+ } else {
+ if (mode == SFLOAT_ROUND_DOWN)
+ increment = 0;
+ }
+ }
+ }
+
+ if (0xFD <= (uint16_t)exp_z) {
+ if ((0xFD < exp_z) || ((exp_z == 0xFD) && ((int32_t)(sig_z + increment) < 0))) {
+ SFLOAT_RAISE(state, SFLOAT_OVERFLOW | SFLOAT_INEXACT);
+ return SFLOAT_PACK(sign_z, 0xFF, 0) - (increment == 0);
+ }
+ if (exp_z < 0) {
+ /* Check for underflow */
+ bool tiny = (state->tiny == SFLOAT_TBEFORE) || (exp_z < -1) || (sig_z + increment < 0x80000000);
+ SFLOAT_SHIFT(32, sig_z, -exp_z, &sig_z);
+ exp_z = 0;
+ bits = sig_z & 0x7F;
+ if (tiny && bits)
+ SFLOAT_RAISE(state, SFLOAT_UNDERFLOW);
+ }
+ }
+
+ /*
+ * Significand has point between bits 30 and 29, 7 bits to the left of
+ * the usual place. This shifted significand has to be normalized
+ * or smaller, if it isn't the exponent must be zero, in which case
+ * no rounding occurs since the result will be a subnormal.
+ */
+ if (bits)
+ SFLOAT_RAISE(state, SFLOAT_INEXACT);
+ sig_z = (sig_z + increment) >> 7;
+ sig_z &= ~(((bits ^ 0x40) == 0) & even);
+ if (sig_z == 0)
+ exp_z = 0;
+ return SFLOAT_PACK(sign_z, exp_z, sig_z);
+}
+
+/* Normalized round and pack */
+static sfloat_t SFLOAT_PACK_normal(sfloat_state_t *state, bool sign_z, int16_t exp_z, uint32_t sig_z) {
+ unsigned char c = SFLOAT_CLZ(sig_z, 1);
+ return SFLOAT_PACK_round(state, sign_z, exp_z - c, sig_z << c);
+}
+
+static sfloat_t sfloat_add_impl(sfloat_state_t *state, sfloat_t a, sfloat_t b, bool sign_z) {
+ int16_t exp_a = SFLOAT_EXTRACT_EXP(a);
+ int16_t exp_b = SFLOAT_EXTRACT_EXP(b);
+ int16_t exp_z = 0;
+ int16_t exp_d = exp_a - exp_b;
+ uint32_t sig_a = SFLOAT_EXTRACT_FRAC(a) << 6;
+ uint32_t sig_b = SFLOAT_EXTRACT_FRAC(b) << 6;
+ uint32_t sig_z = 0;
+
+ if (0 < exp_d) {
+ if (exp_a == 0xFF)
+ return sig_a ? sfloat_propagate_nan(state, a, b) : a;
+ if (exp_b == 0)
+ --exp_d;
+ else
+ sig_b |= 0x20000000;
+ SFLOAT_SHIFT(32, sig_b, exp_d, &sig_b);
+ exp_z = exp_a;
+ } else if (exp_d < 0) {
+ if (exp_b == 0xFF)
+ return sig_b ? sfloat_propagate_nan(state, a, b) : SFLOAT_PACK(sign_z, 0xFF, 0);
+ if (exp_a == 0)
+ ++exp_d;
+ else
+ sig_a |= 0x20000000;
+ SFLOAT_SHIFT(32, sig_a, -exp_d, &sig_a);
+ exp_z = exp_b;
+ } else {
+ if (exp_a == 0xFF)
+ return (sig_a | sig_b) ? sfloat_propagate_nan(state, a, b) : a;
+ if (exp_a == 0)
+ return SFLOAT_PACK(sign_z, 0, (sig_a + sig_b) >> 6);
+ sig_z = 0x40000000 + sig_a + sig_b;
+ exp_z = exp_a;
+ goto end;
+ }
+ sig_a |= 0x20000000;
+ sig_z = (sig_a + sig_b) << 1;
+ --exp_z;
+ if ((int32_t)sig_z < 0) {
+ sig_z = sig_a + sig_b;
+ ++exp_z;
+ }
+end:
+ return SFLOAT_PACK_round(state, sign_z, exp_z, sig_z);
+}
+
+static sfloat_t sfloat_sub_impl(sfloat_state_t *state, sfloat_t a, sfloat_t b, bool sign_z) {
+ int16_t exp_a = SFLOAT_EXTRACT_EXP(a);
+ int16_t exp_b = SFLOAT_EXTRACT_EXP(b);
+ int16_t exp_z = 0;
+ int16_t exp_d = exp_a - exp_b;
+ uint32_t sig_a = SFLOAT_EXTRACT_FRAC(a) << 7;
+ uint32_t sig_b = SFLOAT_EXTRACT_FRAC(b) << 7;
+ uint32_t sig_z = 0;
+
+ if (0 < exp_d) goto exp_greater_a;
+ if (exp_d < 0) goto exp_greater_b;
+
+ if (exp_a == 0xFF) {
+ if (sig_a | sig_b)
+ return sfloat_propagate_nan(state, a, b);
+ SFLOAT_RAISE(state, SFLOAT_INVALID);
+ return SFLOAT_NAN;
+ }
+
+ if (exp_a == 0)
+ exp_a = exp_b = 1;
+
+ if (sig_b < sig_a) goto greater_a;
+ if (sig_a < sig_b) goto greater_b;
+
+ return SFLOAT_PACK(state->roundingmode == SFLOAT_ROUND_DOWN, 0, 0);
+
+exp_greater_b:
+ if (exp_b == 0xFF)
+ return (sig_b) ? sfloat_propagate_nan(state, a, b) : SFLOAT_PACK(sign_z ^ 1, 0xFF, 0);
+ if (exp_a == 0)
+ ++exp_d;
+ else
+ sig_a |= 0x40000000;
+ SFLOAT_SHIFT(32, sig_a, -exp_d, &sig_a);
+ sig_b |= 0x40000000;
+greater_b:
+ sig_z = sig_b - sig_a;
+ exp_z = exp_b;
+ sign_z ^= 1;
+ goto end;
+
+exp_greater_a:
+ if (exp_a == 0xFF)
+ return (sig_a) ? sfloat_propagate_nan(state, a, b) : a;
+ if (exp_b == 0)
+ --exp_d;
+ else
+ sig_b |= 0x40000000;
+ SFLOAT_SHIFT(32, sig_b, exp_d, &sig_b);
+ sig_a |= 0x40000000;
+greater_a:
+ sig_z = sig_a - sig_b;
+ exp_z = exp_a;
+
+end:
+ --exp_z;
+ return SFLOAT_PACK_normal(state, sign_z, exp_z, sig_z);
+}
+
+static GMQCC_INLINE sfloat_t sfloat_add(sfloat_state_t *state, sfloat_t a, sfloat_t b) {
+ bool sign_a = SFLOAT_EXTRACT_SIGN(a);
+ bool sign_b = SFLOAT_EXTRACT_SIGN(b);
+ return (sign_a == sign_b) ? sfloat_add_impl(state, a, b, sign_a)
+ : sfloat_sub_impl(state, a, b, sign_a);
+}
+
+static GMQCC_INLINE sfloat_t sfloat_sub(sfloat_state_t *state, sfloat_t a, sfloat_t b) {
+ bool sign_a = SFLOAT_EXTRACT_SIGN(a);
+ bool sign_b = SFLOAT_EXTRACT_SIGN(b);
+ return (sign_a == sign_b) ? sfloat_sub_impl(state, a, b, sign_a)
+ : sfloat_add_impl(state, a, b, sign_a);
+}
+
+static sfloat_t sfloat_mul(sfloat_state_t *state, sfloat_t a, sfloat_t b) {
+ int16_t exp_a = SFLOAT_EXTRACT_EXP(a);
+ int16_t exp_b = SFLOAT_EXTRACT_EXP(b);
+ int16_t exp_z = 0;
+ uint32_t sig_a = SFLOAT_EXTRACT_FRAC(a);
+ uint32_t sig_b = SFLOAT_EXTRACT_FRAC(b);
+ uint32_t sig_z = 0;
+ uint64_t sig_z64 = 0;
+ bool sign_a = SFLOAT_EXTRACT_SIGN(a);
+ bool sign_b = SFLOAT_EXTRACT_SIGN(b);
+ bool sign_z = sign_a ^ sign_b;
+
+ if (exp_a == 0xFF) {
+ if (sig_a || ((exp_b == 0xFF) && sig_b))
+ return sfloat_propagate_nan(state, a, b);
+ if ((exp_b | sig_b) == 0) {
+ SFLOAT_RAISE(state, SFLOAT_INVALID);
+ return SFLOAT_NAN;
+ }
+ return SFLOAT_PACK(sign_z, 0xFF, 0);
+ }
+ if (exp_b == 0xFF) {
+ if (sig_b)
+ return sfloat_propagate_nan(state, a, b);
+ if ((exp_a | sig_a) == 0) {
+ SFLOAT_RAISE(state, SFLOAT_INVALID);
+ return SFLOAT_NAN;
+ }
+ return SFLOAT_PACK(sign_z, 0xFF, 0);
+ }
+ if (exp_a == 0) {
+ if (sig_a == 0)
+ return SFLOAT_PACK(sign_z, 0, 0);
+ SFLOAT_SUBNORMALIZE(sig_a, &exp_a, &sig_a);
+ }
+ if (exp_b == 0) {
+ if (sig_b == 0)
+ return SFLOAT_PACK(sign_z, 0, 0);
+ SFLOAT_SUBNORMALIZE(sig_b, &exp_b, &sig_b);
+ }
+ exp_z = exp_a + exp_b - 0x7F;
+ sig_a = (sig_a | 0x00800000) << 7;
+ sig_b = (sig_b | 0x00800000) << 8;
+ SFLOAT_SHIFT(64, ((uint64_t)sig_a) * sig_b, 32, &sig_z64);
+ sig_z = sig_z64;
+ if (0 <= (int32_t)(sig_z << 1)) {
+ sig_z <<= 1;
+ --exp_z;
+ }
+ return SFLOAT_PACK_round(state, sign_z, exp_z, sig_z);
+}
+
+static sfloat_t sfloat_div(sfloat_state_t *state, sfloat_t a, sfloat_t b) {
+ int16_t exp_a = SFLOAT_EXTRACT_EXP(a);
+ int16_t exp_b = SFLOAT_EXTRACT_EXP(b);
+ int16_t exp_z = 0;
+ uint32_t sig_a = SFLOAT_EXTRACT_FRAC(a);
+ uint32_t sig_b = SFLOAT_EXTRACT_FRAC(b);
+ uint32_t sig_z = 0;
+ bool sign_a = SFLOAT_EXTRACT_SIGN(a);
+ bool sign_b = SFLOAT_EXTRACT_SIGN(b);
+ bool sign_z = sign_a ^ sign_b;
+
+ if (exp_a == 0xFF) {
+ if (sig_a)
+ return sfloat_propagate_nan(state, a, b);
+ if (exp_b == 0xFF) {
+ if (sig_b)
+ return sfloat_propagate_nan(state, a, b);
+ SFLOAT_RAISE(state, SFLOAT_INVALID);
+ return SFLOAT_NAN;
+ }
+ return SFLOAT_PACK(sign_z, 0xFF, 0);
+ }
+ if (exp_b == 0xFF)
+ return (sig_b) ? sfloat_propagate_nan(state, a, b) : SFLOAT_PACK(sign_z, 0, 0);
+ if (exp_b == 0) {
+ if (sig_b == 0) {
+ if ((exp_a | sig_a) == 0) {
+ SFLOAT_RAISE(state, SFLOAT_INVALID);
+ return SFLOAT_NAN;
+ }
+ SFLOAT_RAISE(state, SFLOAT_DIVBYZERO);
+ return SFLOAT_PACK(sign_z, 0xFF, 0);
+ }
+ SFLOAT_SUBNORMALIZE(sig_b, &exp_b, &sig_b);
+ }
+ if (exp_a == 0) {
+ if (sig_a == 0)
+ return SFLOAT_PACK(sign_z, 0, 0);
+ SFLOAT_SUBNORMALIZE(sig_a, &exp_a, &sig_a);
+ }
+ exp_z = exp_a - exp_b + 0x7D;
+ sig_a = (sig_a | 0x00800000) << 7;
+ sig_b = (sig_b | 0x00800000) << 8;
+ if (sig_b <= (sig_a + sig_a)) {
+ sig_a >>= 1;
+ ++exp_z;
+ }
+ sig_z = (((uint64_t)sig_a) << 32) / sig_b;
+ if ((sig_z & 0x3F) == 0)
+ sig_z |= ((uint64_t)sig_b * sig_z != ((uint64_t)sig_a) << 32);
+ return SFLOAT_PACK_round(state, sign_z, exp_z, sig_z);
+}
+
+static GMQCC_INLINE void sfloat_check(lex_ctx_t ctx, sfloat_state_t *state, const char *vec) {
+ /* Exception comes from vector component */
+ if (vec) {
+ if (state->exceptionflags & SFLOAT_DIVBYZERO)
+ compile_error(ctx, "division by zero in `%s' component", vec);
+ if (state->exceptionflags & SFLOAT_INVALID)
+ compile_error(ctx, "undefined (inf) in `%s' component", vec);
+ if (state->exceptionflags & SFLOAT_OVERFLOW)
+ compile_error(ctx, "arithmetic overflow in `%s' component", vec);
+ if (state->exceptionflags & SFLOAT_UNDERFLOW)
+ compile_error(ctx, "arithmetic underflow in `%s' component", vec);
+ return;
+ }
+ if (state->exceptionflags & SFLOAT_DIVBYZERO)
+ compile_error(ctx, "division by zero");
+ if (state->exceptionflags & SFLOAT_INVALID)
+ compile_error(ctx, "undefined (inf)");
+ if (state->exceptionflags & SFLOAT_OVERFLOW)
+ compile_error(ctx, "arithmetic overflow");
+ if (state->exceptionflags & SFLOAT_UNDERFLOW)
+ compile_error(ctx, "arithmetic underflow");
+}
+
+static GMQCC_INLINE void sfloat_init(sfloat_state_t *state) {
+ state->exceptionflags = SFLOAT_NOEXCEPT;
+ state->roundingmode = FOLD_ROUNDING;
+ state->tiny = FOLD_TINYNESS;
+}
+