Perliminary work on arithmetic exception handling in the constant evaluator. We can...
authorDale Weiler <weilercdale@gmail.com>
Sat, 24 May 2014 13:53:38 +0000 (09:53 -0400)
committerDale Weiler <weilercdale@gmail.com>
Sat, 24 May 2014 13:53:38 +0000 (09:53 -0400)
ast.c
ast.h
doc/gmqcc.1
fold.c
gmqcc.ini.example
intrin.c
opts.c
opts.def
parser.c
parser.h

diff --git a/ast.c b/ast.c
index d7e3d7a..5c38fa9 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -359,6 +359,7 @@ ast_value* ast_value_new(lex_ctx_t ctx, const char *name, int t)
     self->cvq      = CV_NONE;
     self->hasvalue = false;
     self->isimm    = false;
+    self->inexact  = false;
     self->uses     = 0;
     memset(&self->constval, 0, sizeof(self->constval));
     self->initlist = NULL;
diff --git a/ast.h b/ast.h
index 7cc7a6c..4720b40 100644 (file)
--- a/ast.h
+++ b/ast.h
@@ -214,6 +214,7 @@ struct ast_value_s
     bool isfield; /* this declares a field */
     bool isimm;   /* an immediate, not just const */
     bool hasvalue;
+    bool inexact; /* inexact coming from folded expression */
     basic_value_t constval;
     /* for TYPE_ARRAY we have an optional vector
      * of constants when an initializer list
index b5d39ea..e88acd9 100644 (file)
@@ -344,6 +344,10 @@ will search its intrinsics table for something that matches that
 function name by appending "__builtin_" to it. This behaviour may
 be unexpected, so enabling this will produce a diagnostic when
 such a function is resolved to a builtin.
+.It Fl W Ns Cm inexact-compares
+When comparing an inexact value such as `1.0/3.0' the result is
+pathologically wrong. Enabling this will trigger a compiler warning
+on such expressions.
 .El
 .Sh COMPILE FLAGS
 .Bl -tag -width Ds
diff --git a/fold.c b/fold.c
index af37c4c..1cd8dfa 100644 (file)
--- a/fold.c
+++ b/fold.c
 #define FOLD_STRING_DOTRANSLATE_HTSIZE 1024
 
 /*
+ * 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_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;
+
+/* The value of a NaN */
+#define SFLOAT_NAN 0xFFC00000
+/* Count of leading zero bits before the most-significand 1 bit. */
+#define SFLOAT_CLZ(X, SUB) \
+    (__builtin_clz((X)) - (SUB))
+/* 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 |= (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);
+}
+
+/*
  * There is two stages to constant folding in GMQCC: there is the parse
  * stage constant folding, where, witht he help of the AST, operator
  * usages can be constant folded. Then there is the constant folding
@@ -227,10 +630,10 @@ fold_t *fold_init(parser_t *parser) {
      * prime the tables with common constant values at constant
      * locations.
      */
-    (void)fold_constgen_float (fold,  0.0f);
-    (void)fold_constgen_float (fold,  1.0f);
-    (void)fold_constgen_float (fold, -1.0f);
-    (void)fold_constgen_float (fold,  2.0f);
+    (void)fold_constgen_float (fold,  0.0f, false);
+    (void)fold_constgen_float (fold,  1.0f, false);
+    (void)fold_constgen_float (fold, -1.0f, false);
+    (void)fold_constgen_float (fold,  2.0f, false);
 
     (void)fold_constgen_vector(fold, vec3_create(0.0f, 0.0f, 0.0f));
     (void)fold_constgen_vector(fold, vec3_create(-1.0f, -1.0f, -1.0f));
@@ -275,7 +678,7 @@ void fold_cleanup(fold_t *fold) {
     mem_d(fold);
 }
 
-ast_expression *fold_constgen_float(fold_t *fold, qcfloat_t value) {
+ast_expression *fold_constgen_float(fold_t *fold, qcfloat_t value, bool inexact) {
     ast_value  *out = NULL;
     size_t      i;
 
@@ -287,6 +690,7 @@ ast_expression *fold_constgen_float(fold_t *fold, qcfloat_t value) {
     out                  = ast_value_new(fold_ctx(fold), "#IMMEDIATE", TYPE_FLOAT);
     out->cvq             = CV_CONST;
     out->hasvalue        = true;
+    out->inexact         = inexact;
     out->constval.vfloat = value;
 
     vec_push(fold->imm_float, out);
@@ -372,7 +776,7 @@ static GMQCC_INLINE ast_expression *fold_op_mul_vec(fold_t *fold, vec3_t vec, as
         out->node.keep             = false;
         ((ast_member*)out)->rvalue = true;
         if (x != -1.0f)
-            return (ast_expression*)ast_binary_new(fold_ctx(fold), INSTR_MUL_F, fold_constgen_float(fold, x), out);
+            return (ast_expression*)ast_binary_new(fold_ctx(fold), INSTR_MUL_F, fold_constgen_float(fold, x, false), out);
     }
     return NULL;
 }
@@ -381,7 +785,7 @@ static GMQCC_INLINE ast_expression *fold_op_mul_vec(fold_t *fold, vec3_t vec, as
 static GMQCC_INLINE ast_expression *fold_op_neg(fold_t *fold, ast_value *a) {
     if (isfloat(a)) {
         if (fold_can_1(a))
-            return fold_constgen_float(fold, -fold_immvalue_float(a));
+            return fold_constgen_float(fold, -fold_immvalue_float(a), false);
     } else if (isvector(a)) {
         if (fold_can_1(a))
             return fold_constgen_vector(fold, vec3_neg(fold_immvalue_vector(a)));
@@ -392,25 +796,72 @@ static GMQCC_INLINE ast_expression *fold_op_neg(fold_t *fold, ast_value *a) {
 static GMQCC_INLINE ast_expression *fold_op_not(fold_t *fold, ast_value *a) {
     if (isfloat(a)) {
         if (fold_can_1(a))
-            return fold_constgen_float(fold, !fold_immvalue_float(a));
+            return fold_constgen_float(fold, !fold_immvalue_float(a), false);
     } else if (isvector(a)) {
         if (fold_can_1(a))
-            return fold_constgen_float(fold, vec3_notf(fold_immvalue_vector(a)));
+            return fold_constgen_float(fold, vec3_notf(fold_immvalue_vector(a)), false);
     } else if (isstring(a)) {
         if (fold_can_1(a)) {
             if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
-                return fold_constgen_float(fold, !fold_immvalue_string(a));
+                return fold_constgen_float(fold, !fold_immvalue_string(a), false);
             else
-                return fold_constgen_float(fold, !fold_immvalue_string(a) || !*fold_immvalue_string(a));
+                return fold_constgen_float(fold, !fold_immvalue_string(a) || !*fold_immvalue_string(a), false);
         }
     }
     return NULL;
 }
 
+static bool fold_check_except_float(sfloat_t (*callback)(sfloat_state_t *, sfloat_t, sfloat_t),
+                                    fold_t    *fold,
+                                    ast_value *a,
+                                    ast_value *b)
+{
+    sfloat_state_t s;
+    sfloat_cast_t ca;
+    sfloat_cast_t cb;
+
+    s.roundingmode   = SFLOAT_ROUND_NEAREST_EVEN;
+    s.tiny           = SFLOAT_TBEFORE;
+    s.exceptionflags = 0;
+    ca.f             = fold_immvalue_float(a);
+    cb.f             = fold_immvalue_float(b);
+
+    callback(&s, ca.s, cb.s);
+    if (s.exceptionflags == 0)
+        return false;
+
+    if (s.exceptionflags & SFLOAT_DIVBYZERO)
+        compile_error(fold_ctx(fold), "division by zero");
+#if 0
+    /*
+     * To be enabled once softfloat implementations for stuff like sqrt()
+     * exist
+     */
+    if (s.exceptionflags & SFLOAT_INVALID)
+        compile_error(fold_ctx(fold), "invalid argument");
+#endif
+
+    if (s.exceptionflags & SFLOAT_OVERFLOW)
+        compile_error(fold_ctx(fold), "arithmetic overflow");
+    if (s.exceptionflags & SFLOAT_UNDERFLOW)
+        compile_error(fold_ctx(fold), "arithmetic underflow");
+
+    return s.exceptionflags == SFLOAT_INEXACT;
+}
+
+static bool fold_check_inexact_float(fold_t *fold, ast_value *a, ast_value *b) {
+    lex_ctx_t ctx = fold_ctx(fold);
+    if (!a->inexact && !b->inexact)
+        return false;
+    return compile_warning(ctx, WARN_INEXACT_COMPARES, "inexact value in comparison");
+}
+
 static GMQCC_INLINE ast_expression *fold_op_add(fold_t *fold, ast_value *a, ast_value *b) {
     if (isfloat(a)) {
-        if (fold_can_2(a, b))
-            return fold_constgen_float(fold, fold_immvalue_float(a) + fold_immvalue_float(b));
+        if (fold_can_2(a, b)) {
+            bool inexact = fold_check_except_float(&sfloat_add, fold, a, b);
+            return fold_constgen_float(fold, fold_immvalue_float(a) + fold_immvalue_float(b), inexact);
+        }
     } else if (isvector(a)) {
         if (fold_can_2(a, b))
             return fold_constgen_vector(fold, vec3_add(fold_immvalue_vector(a), fold_immvalue_vector(b)));
@@ -420,8 +871,10 @@ static GMQCC_INLINE ast_expression *fold_op_add(fold_t *fold, ast_value *a, ast_
 
 static GMQCC_INLINE ast_expression *fold_op_sub(fold_t *fold, ast_value *a, ast_value *b) {
     if (isfloat(a)) {
-        if (fold_can_2(a, b))
-            return fold_constgen_float(fold, fold_immvalue_float(a) - fold_immvalue_float(b));
+        if (fold_can_2(a, b)) {
+            bool inexact = fold_check_except_float(&sfloat_sub, fold, a, b);
+            return fold_constgen_float(fold, fold_immvalue_float(a) - fold_immvalue_float(b), inexact);
+        }
     } else if (isvector(a)) {
         if (fold_can_2(a, b))
             return fold_constgen_vector(fold, vec3_sub(fold_immvalue_vector(a), fold_immvalue_vector(b)));
@@ -435,8 +888,10 @@ static GMQCC_INLINE ast_expression *fold_op_mul(fold_t *fold, ast_value *a, ast_
             if (fold_can_2(a, b))
                 return fold_constgen_vector(fold, vec3_mulvf(fold_immvalue_vector(b), fold_immvalue_float(a)));
         } else {
-            if (fold_can_2(a, b))
-                return fold_constgen_float(fold, fold_immvalue_float(a) * fold_immvalue_float(b));
+            if (fold_can_2(a, b)) {
+                bool inexact = fold_check_except_float(&sfloat_mul, fold, a, b);
+                return fold_constgen_float(fold, fold_immvalue_float(a) * fold_immvalue_float(b), inexact);
+            }
         }
     } else if (isvector(a)) {
         if (isfloat(b)) {
@@ -444,7 +899,7 @@ static GMQCC_INLINE ast_expression *fold_op_mul(fold_t *fold, ast_value *a, ast_
                 return fold_constgen_vector(fold, vec3_mulvf(fold_immvalue_vector(a), fold_immvalue_float(b)));
         } else {
             if (fold_can_2(a, b)) {
-                return fold_constgen_float(fold, vec3_mulvv(fold_immvalue_vector(a), fold_immvalue_vector(b)));
+                return fold_constgen_float(fold, vec3_mulvv(fold_immvalue_vector(a), fold_immvalue_vector(b)), false);
             } else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && fold_can_1(a)) {
                 ast_expression *out;
                 if ((out = fold_op_mul_vec(fold, fold_immvalue_vector(a), b, "xyz"))) return out;
@@ -464,13 +919,14 @@ static GMQCC_INLINE ast_expression *fold_op_mul(fold_t *fold, ast_value *a, ast_
 static GMQCC_INLINE ast_expression *fold_op_div(fold_t *fold, ast_value *a, ast_value *b) {
     if (isfloat(a)) {
         if (fold_can_2(a, b)) {
-            return fold_constgen_float(fold, fold_immvalue_float(a) / fold_immvalue_float(b));
+            bool inexact = fold_check_except_float(&sfloat_div, fold, a, b);
+            return fold_constgen_float(fold, fold_immvalue_float(a) / fold_immvalue_float(b), inexact);
         } else if (fold_can_1(b)) {
             return (ast_expression*)ast_binary_new(
                 fold_ctx(fold),
                 INSTR_MUL_F,
                 (ast_expression*)a,
-                fold_constgen_float(fold, 1.0f / fold_immvalue_float(b))
+                fold_constgen_float(fold, 1.0f / fold_immvalue_float(b), false)
             );
         }
     } else if (isvector(a)) {
@@ -482,7 +938,7 @@ static GMQCC_INLINE ast_expression *fold_op_div(fold_t *fold, ast_value *a, ast_
                 INSTR_MUL_VF,
                 (ast_expression*)a,
                 (fold_can_1(b))
-                    ? (ast_expression*)fold_constgen_float(fold, 1.0f / fold_immvalue_float(b))
+                    ? (ast_expression*)fold_constgen_float(fold, 1.0f / fold_immvalue_float(b), false)
                     : (ast_expression*)ast_binary_new(
                                             fold_ctx(fold),
                                             INSTR_DIV_F,
@@ -497,14 +953,14 @@ static GMQCC_INLINE ast_expression *fold_op_div(fold_t *fold, ast_value *a, ast_
 
 static GMQCC_INLINE ast_expression *fold_op_mod(fold_t *fold, ast_value *a, ast_value *b) {
     return (fold_can_2(a, b))
-                ? fold_constgen_float(fold, fmod(fold_immvalue_float(a), fold_immvalue_float(b)))
+                ? fold_constgen_float(fold, fmod(fold_immvalue_float(a), fold_immvalue_float(b)), false)
                 : NULL;
 }
 
 static GMQCC_INLINE ast_expression *fold_op_bor(fold_t *fold, ast_value *a, ast_value *b) {
     if (isfloat(a)) {
         if (fold_can_2(a, b))
-            return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) | ((qcint_t)fold_immvalue_float(b))));
+            return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) | ((qcint_t)fold_immvalue_float(b))), false);
     } else {
         if (isvector(b)) {
             if (fold_can_2(a, b))
@@ -520,7 +976,7 @@ static GMQCC_INLINE ast_expression *fold_op_bor(fold_t *fold, ast_value *a, ast_
 static GMQCC_INLINE ast_expression *fold_op_band(fold_t *fold, ast_value *a, ast_value *b) {
     if (isfloat(a)) {
         if (fold_can_2(a, b))
-            return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) & ((qcint_t)fold_immvalue_float(b))));
+            return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) & ((qcint_t)fold_immvalue_float(b))), false);
     } else {
         if (isvector(b)) {
             if (fold_can_2(a, b))
@@ -536,7 +992,7 @@ static GMQCC_INLINE ast_expression *fold_op_band(fold_t *fold, ast_value *a, ast
 static GMQCC_INLINE ast_expression *fold_op_xor(fold_t *fold, ast_value *a, ast_value *b) {
     if (isfloat(a)) {
         if (fold_can_2(a, b))
-            return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) ^ ((qcint_t)fold_immvalue_float(b))));
+            return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) ^ ((qcint_t)fold_immvalue_float(b))), false);
     } else {
         if (fold_can_2(a, b)) {
             if (isvector(b))
@@ -550,13 +1006,13 @@ static GMQCC_INLINE ast_expression *fold_op_xor(fold_t *fold, ast_value *a, ast_
 
 static GMQCC_INLINE ast_expression *fold_op_lshift(fold_t *fold, ast_value *a, ast_value *b) {
     if (fold_can_2(a, b) && isfloats(a, b))
-        return fold_constgen_float(fold, (qcfloat_t)floorf(fold_immvalue_float(a) * powf(2.0f, fold_immvalue_float(b))));
+        return fold_constgen_float(fold, (qcfloat_t)floorf(fold_immvalue_float(a) * powf(2.0f, fold_immvalue_float(b))), false);
     return NULL;
 }
 
 static GMQCC_INLINE ast_expression *fold_op_rshift(fold_t *fold, ast_value *a, ast_value *b) {
     if (fold_can_2(a, b) && isfloats(a, b))
-        return fold_constgen_float(fold, (qcfloat_t)floorf(fold_immvalue_float(a) / powf(2.0f, fold_immvalue_float(b))));
+        return fold_constgen_float(fold, (qcfloat_t)floorf(fold_immvalue_float(a) / powf(2.0f, fold_immvalue_float(b))), false);
     return NULL;
 }
 
@@ -573,7 +1029,8 @@ static GMQCC_INLINE ast_expression *fold_op_andor(fold_t *fold, ast_value *a, as
                 ((expr) ? (fold_immediate_true(fold, a) || fold_immediate_true(fold, b))
                         : (fold_immediate_true(fold, a) && fold_immediate_true(fold, b)))
                             ? 1
-                            : 0
+                            : 0,
+                false
             );
         }
     }
@@ -591,12 +1048,13 @@ static GMQCC_INLINE ast_expression *fold_op_tern(fold_t *fold, ast_value *a, ast
 
 static GMQCC_INLINE ast_expression *fold_op_exp(fold_t *fold, ast_value *a, ast_value *b) {
     if (fold_can_2(a, b))
-        return fold_constgen_float(fold, (qcfloat_t)powf(fold_immvalue_float(a), fold_immvalue_float(b)));
+        return fold_constgen_float(fold, (qcfloat_t)powf(fold_immvalue_float(a), fold_immvalue_float(b)), false);
     return NULL;
 }
 
 static GMQCC_INLINE ast_expression *fold_op_lteqgt(fold_t *fold, ast_value *a, ast_value *b) {
     if (fold_can_2(a,b)) {
+        fold_check_inexact_float(fold, a, b);
         if (fold_immvalue_float(a) <  fold_immvalue_float(b)) return (ast_expression*)fold->imm_float[2];
         if (fold_immvalue_float(a) == fold_immvalue_float(b)) return (ast_expression*)fold->imm_float[0];
         if (fold_immvalue_float(a) >  fold_immvalue_float(b)) return (ast_expression*)fold->imm_float[1];
@@ -604,11 +1062,21 @@ static GMQCC_INLINE ast_expression *fold_op_lteqgt(fold_t *fold, ast_value *a, a
     return NULL;
 }
 
+static GMQCC_INLINE ast_expression *fold_op_ltgt(fold_t *fold, ast_value *a, ast_value *b, bool lt) {
+    if (fold_can_2(a, b)) {
+        fold_check_inexact_float(fold, a, b);
+        return (lt) ? (ast_expression*)fold->imm_float[!!(fold_immvalue_float(a) < fold_immvalue_float(b))]
+                    : (ast_expression*)fold->imm_float[!!(fold_immvalue_float(a) > fold_immvalue_float(b))];
+    }
+    return NULL;
+}
+
 static GMQCC_INLINE ast_expression *fold_op_cmp(fold_t *fold, ast_value *a, ast_value *b, bool ne) {
     if (fold_can_2(a, b)) {
         if (isfloat(a) && isfloat(b)) {
             float la = fold_immvalue_float(a);
             float lb = fold_immvalue_float(b);
+            fold_check_inexact_float(fold, a, b);
             return (ast_expression*)fold->imm_float[!(ne ? la == lb : la != lb)];
         } if (isvector(a) && isvector(b)) {
             vec3_t la = fold_immvalue_vector(a);
@@ -622,7 +1090,7 @@ static GMQCC_INLINE ast_expression *fold_op_cmp(fold_t *fold, ast_value *a, ast_
 static GMQCC_INLINE ast_expression *fold_op_bnot(fold_t *fold, ast_value *a) {
     if (isfloat(a)) {
         if (fold_can_1(a))
-            return fold_constgen_float(fold, -1-fold_immvalue_float(a));
+            return fold_constgen_float(fold, -1-fold_immvalue_float(a), false);
     } else {
         if (isvector(a)) {
             if (fold_can_1(a))
@@ -685,6 +1153,8 @@ ast_expression *fold_op(fold_t *fold, const oper_info *info, ast_expression **op
         fold_op_case(1, ('|'),         bor,    (fold, a, b));
         fold_op_case(1, ('&'),         band,   (fold, a, b));
         fold_op_case(1, ('^'),         xor,    (fold, a, b));
+        fold_op_case(1, ('<'),         ltgt,   (fold, a, b, true));
+        fold_op_case(1, ('>'),         ltgt,   (fold, a, b, false));
         fold_op_case(2, ('<', '<'),    lshift, (fold, a, b));
         fold_op_case(2, ('>', '>'),    rshift, (fold, a, b));
         fold_op_case(2, ('|', '|'),    andor,  (fold, a, b, true));
@@ -708,46 +1178,46 @@ ast_expression *fold_op(fold_t *fold, const oper_info *info, ast_expression **op
  * and a generic selection function.
  */
 static GMQCC_INLINE ast_expression *fold_intrin_isfinite(fold_t *fold, ast_value *a) {
-    return fold_constgen_float(fold, isfinite(fold_immvalue_float(a)));
+    return fold_constgen_float(fold, isfinite(fold_immvalue_float(a)), false);
 }
 static GMQCC_INLINE ast_expression *fold_intrin_isinf(fold_t *fold, ast_value *a) {
-    return fold_constgen_float(fold, isinf(fold_immvalue_float(a)));
+    return fold_constgen_float(fold, isinf(fold_immvalue_float(a)), false);
 }
 static GMQCC_INLINE ast_expression *fold_intrin_isnan(fold_t *fold, ast_value *a) {
-    return fold_constgen_float(fold, isnan(fold_immvalue_float(a)));
+    return fold_constgen_float(fold, isnan(fold_immvalue_float(a)), false);
 }
 static GMQCC_INLINE ast_expression *fold_intrin_isnormal(fold_t *fold, ast_value *a) {
-    return fold_constgen_float(fold, isnormal(fold_immvalue_float(a)));
+    return fold_constgen_float(fold, isnormal(fold_immvalue_float(a)), false);
 }
 static GMQCC_INLINE ast_expression *fold_intrin_signbit(fold_t *fold, ast_value *a) {
-    return fold_constgen_float(fold, signbit(fold_immvalue_float(a)));
+    return fold_constgen_float(fold, signbit(fold_immvalue_float(a)), false);
 }
 static GMQCC_INLINE ast_expression *fold_intirn_acosh(fold_t *fold, ast_value *a) {
-    return fold_constgen_float(fold, acoshf(fold_immvalue_float(a)));
+    return fold_constgen_float(fold, acoshf(fold_immvalue_float(a)), false);
 }
 static GMQCC_INLINE ast_expression *fold_intrin_asinh(fold_t *fold, ast_value *a) {
-    return fold_constgen_float(fold, asinhf(fold_immvalue_float(a)));
+    return fold_constgen_float(fold, asinhf(fold_immvalue_float(a)), false);
 }
 static GMQCC_INLINE ast_expression *fold_intrin_atanh(fold_t *fold, ast_value *a) {
-    return fold_constgen_float(fold, (float)atanh(fold_immvalue_float(a)));
+    return fold_constgen_float(fold, (float)atanh(fold_immvalue_float(a)), false);
 }
 static GMQCC_INLINE ast_expression *fold_intrin_exp(fold_t *fold, ast_value *a) {
-    return fold_constgen_float(fold, expf(fold_immvalue_float(a)));
+    return fold_constgen_float(fold, expf(fold_immvalue_float(a)), false);
 }
 static GMQCC_INLINE ast_expression *fold_intrin_exp2(fold_t *fold, ast_value *a) {
-    return fold_constgen_float(fold, exp2f(fold_immvalue_float(a)));
+    return fold_constgen_float(fold, exp2f(fold_immvalue_float(a)), false);
 }
 static GMQCC_INLINE ast_expression *fold_intrin_expm1(fold_t *fold, ast_value *a) {
-    return fold_constgen_float(fold, expm1f(fold_immvalue_float(a)));
+    return fold_constgen_float(fold, expm1f(fold_immvalue_float(a)), false);
 }
 static GMQCC_INLINE ast_expression *fold_intrin_mod(fold_t *fold, ast_value *lhs, ast_value *rhs) {
-    return fold_constgen_float(fold, fmodf(fold_immvalue_float(lhs), fold_immvalue_float(rhs)));
+    return fold_constgen_float(fold, fmodf(fold_immvalue_float(lhs), fold_immvalue_float(rhs)), false);
 }
 static GMQCC_INLINE ast_expression *fold_intrin_pow(fold_t *fold, ast_value *lhs, ast_value *rhs) {
-    return fold_constgen_float(fold, powf(fold_immvalue_float(lhs), fold_immvalue_float(rhs)));
+    return fold_constgen_float(fold, powf(fold_immvalue_float(lhs), fold_immvalue_float(rhs)), false);
 }
 static GMQCC_INLINE ast_expression *fold_intrin_fabs(fold_t *fold, ast_value *a) {
-    return fold_constgen_float(fold, fabsf(fold_immvalue_float(a)));
+    return fold_constgen_float(fold, fabsf(fold_immvalue_float(a)), false);
 }
 
 
index 20680d3..a12628e 100644 (file)
     BUILTINS = true
 
 
+    #When comparing an inexact value such as `1.0/3.0' the result is
+    #pathologically wrong. Enabling this will trigger a compiler warning
+    #on such expressions.
+    INEXACT_COMPARES = true
+
+
 [optimizations]
     #Some general peephole optimizations. For instance the code `a = b
     #+ c` typically generates 2 instructions, an ADD and a STORE. This
index 8c78ba7..c1b84ed 100644 (file)
--- a/intrin.c
+++ b/intrin.c
@@ -422,7 +422,7 @@ static ast_expression *intrin_atanh(intrin_t *intrin) {
         (ast_expression*)ast_binary_new(
             intrin_ctx(intrin),
             INSTR_MUL_F,
-            (ast_expression*)fold_constgen_float(intrin->fold, 0.5),
+            (ast_expression*)fold_constgen_float(intrin->fold, 0.5, false),
             (ast_expression*)calllog
         )
     );
@@ -496,7 +496,7 @@ static ast_expression *intrin_exp(intrin_t *intrin) {
                 intrin_ctx(intrin),
                 INSTR_LT,
                 (ast_expression*)i,
-                (ast_expression*)fold_constgen_float(intrin->fold, 200.0f)
+                (ast_expression*)fold_constgen_float(intrin->fold, 200.0f, false)
             ),
             false,
             NULL,
@@ -1027,7 +1027,7 @@ static ast_expression *intrin_pow(intrin_t *intrin) {
                 intrin_ctx(intrin),
                 INSTR_GT,
                 (ast_expression*)callfabs,
-                (ast_expression*)fold_constgen_float(intrin->fold, QC_POW_EPSILON)
+                (ast_expression*)fold_constgen_float(intrin->fold, QC_POW_EPSILON, false)
             ),
             /* pre not */
             false,
@@ -1911,7 +1911,7 @@ static ast_expression *intrin_log_variant(intrin_t *intrin, const char *name, fl
     vec_push(value->expression.params, arg1);
 
     vec_push(callln->params, (ast_expression*)arg1);
-    vec_push(callln->params, (ast_expression*)fold_constgen_float(intrin->fold, base));
+    vec_push(callln->params, (ast_expression*)fold_constgen_float(intrin->fold, base, false));
 
     vec_push(body->exprs,
         (ast_expression*)ast_return_new(
diff --git a/opts.c b/opts.c
index 7998024..ddb48f7 100644 (file)
--- a/opts.c
+++ b/opts.c
@@ -93,6 +93,7 @@ static void opts_setdefault(void) {
     opts_set(opts.warn,  WARN_CONST_OVERWRITE,           true);
     opts_set(opts.warn,  WARN_DIRECTIVE_INMACRO,         true);
     opts_set(opts.warn,  WARN_BUILTINS,                  true);
+    opts_set(opts.warn,  WARN_INEXACT_COMPARES,          true);
 
     /* flags */
     opts_set(opts.flags, ADJUST_VECTOR_FIELDS,           true);
index b229245..5aa52ab 100644 (file)
--- a/opts.def
+++ b/opts.def
@@ -98,6 +98,7 @@
     GMQCC_DEFINE_FLAG(CONST_OVERWRITE)
     GMQCC_DEFINE_FLAG(DIRECTIVE_INMACRO)
     GMQCC_DEFINE_FLAG(BUILTINS)
+    GMQCC_DEFINE_FLAG(INEXACT_COMPARES)
 #endif
 
 #ifdef GMQCC_TYPE_OPTIMIZATIONS
index eaa1490..354ad03 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -1293,7 +1293,7 @@ static bool parser_close_call(parser_t *parser, shunt *sy)
         if ((fun->flags & AST_FLAG_VARIADIC) &&
             !(/*funval->cvq == CV_CONST && */ funval->hasvalue && funval->constval.vfunc->builtin))
         {
-            call->va_count = (ast_expression*)fold_constgen_float(parser->fold, (qcfloat_t)paramcount);
+            call->va_count = (ast_expression*)fold_constgen_float(parser->fold, (qcfloat_t)paramcount, false);
         }
     }
 
@@ -1548,14 +1548,14 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
         return true;
     }
     else if (parser->tok == TOKEN_FLOATCONST) {
-        ast_expression *val = fold_constgen_float(parser->fold, (parser_token(parser)->constval.f));
+        ast_expression *val = fold_constgen_float(parser->fold, (parser_token(parser)->constval.f), false);
         if (!val)
             return false;
         vec_push(sy->out, syexp(parser_ctx(parser), val));
         return true;
     }
     else if (parser->tok == TOKEN_INTCONST || parser->tok == TOKEN_CHARCONST) {
-        ast_expression *val = fold_constgen_float(parser->fold, (qcfloat_t)(parser_token(parser)->constval.i));
+        ast_expression *val = fold_constgen_float(parser->fold, (qcfloat_t)(parser_token(parser)->constval.i), false);
         if (!val)
             return false;
         vec_push(sy->out, syexp(parser_ctx(parser), val));
@@ -4030,7 +4030,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
         self_think     = (ast_expression*)ast_entfield_new(ctx, gbl_self, fld_think);
 
         time_plus_1    = (ast_expression*)ast_binary_new(ctx, INSTR_ADD_F,
-                         gbl_time, (ast_expression*)fold_constgen_float(parser->fold, 0.1f));
+                         gbl_time, (ast_expression*)fold_constgen_float(parser->fold, 0.1f, false));
 
         if (!self_frame || !self_nextthink || !self_think || !time_plus_1) {
             if (self_frame)     ast_delete(self_frame);
@@ -4155,7 +4155,7 @@ static bool parse_function_body(parser_t *parser, ast_value *var)
             goto enderrfn;
         }
         func->varargs     = varargs;
-        func->fixedparams = (ast_value*)fold_constgen_float(parser->fold, vec_size(var->expression.params));
+        func->fixedparams = (ast_value*)fold_constgen_float(parser->fold, vec_size(var->expression.params), false);
     }
 
     parser->function = func;
@@ -4213,7 +4213,7 @@ static ast_expression *array_accessor_split(
 
     cmp = ast_binary_new(ctx, INSTR_LT,
                          (ast_expression*)index,
-                         (ast_expression*)fold_constgen_float(parser->fold, middle));
+                         (ast_expression*)fold_constgen_float(parser->fold, middle, false));
     if (!cmp) {
         ast_delete(left);
         ast_delete(right);
@@ -4246,7 +4246,7 @@ static ast_expression *array_setter_node(parser_t *parser, ast_value *array, ast
         if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR)
             assignop = INSTR_STORE_V;
 
-        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from));
+        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from, false));
         if (!subscript)
             return NULL;
 
@@ -4312,7 +4312,7 @@ static ast_expression *array_field_setter_node(
         if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR)
             assignop = INSTR_STOREP_V;
 
-        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from));
+        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from, false));
         if (!subscript)
             return NULL;
 
@@ -4375,7 +4375,7 @@ static ast_expression *array_getter_node(parser_t *parser, ast_value *array, ast
         ast_return      *ret;
         ast_array_index *subscript;
 
-        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from));
+        subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)fold_constgen_float(parser->fold, from, false));
         if (!subscript)
             return NULL;
 
index 51140be..eb77eff 100644 (file)
--- a/parser.h
+++ b/parser.h
@@ -127,7 +127,7 @@ ast_expression *parser_find_global(parser_t *parser, const char *name);
 /* fold.c */
 fold_t         *fold_init           (parser_t *);
 void            fold_cleanup        (fold_t *);
-ast_expression *fold_constgen_float (fold_t *, qcfloat_t);
+ast_expression *fold_constgen_float (fold_t *, qcfloat_t, bool);
 ast_expression *fold_constgen_vector(fold_t *, vec3_t);
 ast_expression *fold_constgen_string(fold_t *, const char *, bool);
 bool            fold_generate       (fold_t *, ir_builder *);