Fixes
[xonotic/gmqcc.git] / fold.c
1 /*
2  * Copyright (C) 2012, 2013
3  *     Dale Weiler
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9  * of the Software, and to permit persons to whom the Software is furnished to do
10  * so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  */
23 #include <string.h>
24 #include <math.h>
25
26 #include "ast.h"
27 #include "parser.h"
28
29 #define FOLD_STRING_UNTRANSLATE_HTSIZE 1024
30 #define FOLD_STRING_DOTRANSLATE_HTSIZE 1024
31
32 /*
33  * There is two stages to constant folding in GMQCC: there is the parse
34  * stage constant folding, where, witht he help of the AST, operator
35  * usages can be constant folded. Then there is the constant folding
36  * in the IR for things like eliding if statements, can occur.
37  * 
38  * This file is thus, split into two parts.
39  */
40
41 #define isfloat(X)      (((ast_expression*)(X))->vtype == TYPE_FLOAT)
42 #define isvector(X)     (((ast_expression*)(X))->vtype == TYPE_VECTOR)
43 #define isstring(X)     (((ast_expression*)(X))->vtype == TYPE_STRING)
44 #define isfloats(X,Y)   (isfloat  (X) && isfloat (Y))
45
46 /*
47  * Implementation of basic vector math for vec3_t, for trivial constant
48  * folding.
49  * 
50  * TODO: gcc/clang hinting for autovectorization
51  */
52 static GMQCC_INLINE vec3_t vec3_add(vec3_t a, vec3_t b) {
53     vec3_t out;
54     out.x = a.x + b.x;
55     out.y = a.y + b.y;
56     out.z = a.z + b.z;
57     return out;
58 }
59
60 static GMQCC_INLINE vec3_t vec3_sub(vec3_t a, vec3_t b) {
61     vec3_t out;
62     out.x = a.x + b.x;
63     out.y = a.y + b.y;
64     out.z = a.z + b.z;
65     return out;
66 }
67
68 static GMQCC_INLINE vec3_t vec3_neg(vec3_t a) {
69     vec3_t out;
70     out.x = -a.x;
71     out.y = -a.y;
72     out.z = -a.z;
73     return out;
74 }
75
76 static GMQCC_INLINE vec3_t vec3_xor(vec3_t a, vec3_t b) {
77     vec3_t out;
78     out.x = (qcfloat_t)(((qcint_t)a.x) ^ ((qcint_t)b.x));
79     out.y = (qcfloat_t)(((qcint_t)a.y) ^ ((qcint_t)b.y));
80     out.z = (qcfloat_t)(((qcint_t)a.z) ^ ((qcint_t)b.z));
81     return out;
82 }
83
84 static GMQCC_INLINE vec3_t vec3_xorvf(vec3_t a, qcfloat_t b) {
85     vec3_t out;
86     out.x = (qcfloat_t)(((qcint_t)a.x) ^ ((qcint_t)b));
87     out.y = (qcfloat_t)(((qcint_t)a.y) ^ ((qcint_t)b));
88     out.z = (qcfloat_t)(((qcint_t)a.z) ^ ((qcint_t)b));
89     return out;
90 }
91
92 static GMQCC_INLINE qcfloat_t vec3_mulvv(vec3_t a, vec3_t b) {
93     return (a.x * b.x + a.y * b.y + a.z * b.z);
94 }
95
96 static GMQCC_INLINE vec3_t vec3_mulvf(vec3_t a, qcfloat_t b) {
97     vec3_t out;
98     out.x = a.x * b;
99     out.y = a.y * b;
100     out.z = a.z * b;
101     return out;
102 }
103
104 static GMQCC_INLINE bool vec3_cmp(vec3_t a, vec3_t b) {
105     return a.x == b.x &&
106            a.y == b.y &&
107            a.z == b.z;
108 }
109
110 static GMQCC_INLINE vec3_t vec3_create(float x, float y, float z) {
111     vec3_t out;
112     out.x = x;
113     out.y = y;
114     out.z = z;
115     return out;
116 }
117
118 static GMQCC_INLINE qcfloat_t vec3_notf(vec3_t a) {
119     return (!a.x && !a.y && !a.z);
120 }
121
122 static GMQCC_INLINE bool vec3_pbool(vec3_t a) {
123     return (a.x && a.y && a.z);
124 }
125
126 static GMQCC_INLINE bool fold_can_1(const ast_value *val) {
127     return (ast_istype(((ast_expression*)(val)), ast_value) && val->hasvalue && (val->cvq == CV_CONST) &&
128               ((ast_expression*)(val))->vtype != TYPE_FUNCTION);
129 }
130
131 static GMQCC_INLINE bool fold_can_2(const ast_value *v1, const ast_value *v2) {
132     return fold_can_1(v1) && fold_can_1(v2);
133 }
134
135 static lex_ctx_t fold_ctx(fold_t *fold) {
136     lex_ctx_t ctx;
137     if (fold->parser->lex)
138         return parser_ctx(fold->parser);
139
140     memset(&ctx, 0, sizeof(ctx));
141     return ctx;
142 }
143
144 static GMQCC_INLINE bool fold_immediate_true(fold_t *fold, ast_value *v) {
145     switch (v->expression.vtype) {
146         case TYPE_FLOAT:
147             return !!v->constval.vfloat;
148         case TYPE_INTEGER:
149             return !!v->constval.vint;
150         case TYPE_VECTOR: 
151             if (OPTS_FLAG(CORRECT_LOGIC))
152                 return vec3_pbool(v->constval.vvec);
153             return !!(v->constval.vvec.x);
154         case TYPE_STRING:
155             if (!v->constval.vstring)
156                 return false;
157             if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
158                 return true;
159             return !!v->constval.vstring[0];
160         default:
161             compile_error(fold_ctx(fold), "internal error: fold_immediate_true on invalid type");
162             break;
163     }
164     return !!v->constval.vfunc;
165 }
166
167 #define fold_immvalue_float(E)  ((E)->constval.vfloat)
168 #define fold_immvalue_vector(E) ((E)->constval.vvec)
169 #define fold_immvalue_string(E) ((E)->constval.vstring)
170
171 fold_t *fold_init(parser_t *parser) {
172     fold_t *fold                 = (fold_t*)mem_a(sizeof(fold_t));
173     fold->parser                 = parser;
174     fold->imm_float              = NULL;
175     fold->imm_vector             = NULL;
176     fold->imm_string             = NULL;
177     fold->imm_string_untranslate = util_htnew(FOLD_STRING_UNTRANSLATE_HTSIZE);
178     fold->imm_string_dotranslate = util_htnew(FOLD_STRING_DOTRANSLATE_HTSIZE);
179
180     /*
181      * prime the tables with common constant values at constant
182      * locations.
183      */
184     (void)fold_constgen_float (fold,  0.0f);
185     (void)fold_constgen_float (fold,  1.0f);
186     (void)fold_constgen_float (fold, -1.0f);
187
188     (void)fold_constgen_vector(fold, vec3_create(0.0f, 0.0f, 0.0f));
189
190     return fold;
191 }
192
193 bool fold_generate(fold_t *fold, ir_builder *ir) {
194     /* generate globals for immediate folded values */
195     size_t     i;
196     ast_value *cur;
197
198     for (i = 0; i < vec_size(fold->imm_float);   ++i)
199         if (!ast_global_codegen ((cur = fold->imm_float[i]), ir, false)) goto err;
200     for (i = 0; i < vec_size(fold->imm_vector);  ++i)
201         if (!ast_global_codegen((cur = fold->imm_vector[i]), ir, false)) goto err;
202     for (i = 0; i < vec_size(fold->imm_string);  ++i)
203         if (!ast_global_codegen((cur = fold->imm_string[i]), ir, false)) goto err;
204
205     return true;
206
207 err:
208     con_out("failed to generate global %s\n", cur->name);
209     ir_builder_delete(ir);
210     return false;
211 }
212
213 void fold_cleanup(fold_t *fold) {
214     size_t i;
215
216     for (i = 0; i < vec_size(fold->imm_float);  ++i) ast_delete(fold->imm_float[i]);
217     for (i = 0; i < vec_size(fold->imm_vector); ++i) ast_delete(fold->imm_vector[i]);
218     for (i = 0; i < vec_size(fold->imm_string); ++i) ast_delete(fold->imm_string[i]);
219
220     vec_free(fold->imm_float);
221     vec_free(fold->imm_vector);
222     vec_free(fold->imm_string);
223
224     util_htdel(fold->imm_string_untranslate);
225     util_htdel(fold->imm_string_dotranslate);
226
227     mem_d(fold);
228 }
229
230 ast_expression *fold_constgen_float(fold_t *fold, qcfloat_t value) {
231     ast_value  *out = NULL;
232     size_t      i;
233
234     for (i = 0; i < vec_size(fold->imm_float); i++) {
235         if (fold->imm_float[i]->constval.vfloat == value)
236             return (ast_expression*)fold->imm_float[i];
237     }
238
239     out                  = ast_value_new(fold_ctx(fold), "#IMMEDIATE", TYPE_FLOAT);
240     out->cvq             = CV_CONST;
241     out->hasvalue        = true;
242     out->constval.vfloat = value;
243
244     vec_push(fold->imm_float, out);
245
246     return (ast_expression*)out;
247 }
248
249 ast_expression *fold_constgen_vector(fold_t *fold, vec3_t value) {
250     ast_value *out;
251     size_t     i;
252
253     for (i = 0; i < vec_size(fold->imm_vector); i++) {
254         if (vec3_cmp(fold->imm_vector[i]->constval.vvec, value))
255             return (ast_expression*)fold->imm_vector[i];
256     }
257
258     out                = ast_value_new(fold_ctx(fold), "#IMMEDIATE", TYPE_VECTOR);
259     out->cvq           = CV_CONST;
260     out->hasvalue      = true;
261     out->constval.vvec = value;
262
263     vec_push(fold->imm_vector, out);
264
265     return (ast_expression*)out;
266 }
267
268 ast_expression *fold_constgen_string(fold_t *fold, const char *str, bool translate) {
269     hash_table_t *table = (translate) ? fold->imm_string_untranslate : fold->imm_string_dotranslate;
270     ast_value    *out   = NULL;
271     size_t        hash  = util_hthash(table, str);
272
273     if ((out = (ast_value*)util_htgeth(table, str, hash)))
274         return (ast_expression*)out;
275
276     if (translate) {
277         char name[32];
278         util_snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(fold->parser->translated++));
279         out                    = ast_value_new(parser_ctx(fold->parser), name, TYPE_STRING);
280         out->expression.flags |= AST_FLAG_INCLUDE_DEF; /* def needs to be included for translatables */
281     } else
282         out                    = ast_value_new(fold_ctx(fold), "#IMMEDIATE", TYPE_STRING);
283
284     out->cvq              = CV_CONST;
285     out->hasvalue         = true;
286     out->isimm            = true;
287     out->constval.vstring = parser_strdup(str);
288
289     vec_push(fold->imm_string, out);
290     util_htseth(table, str, hash, out);
291
292     return (ast_expression*)out;
293 }
294
295
296 static GMQCC_INLINE ast_expression *fold_op_mul_vec(fold_t *fold, vec3_t vec, ast_value *sel, const char *set) {
297     /*
298      * vector-component constant folding works by matching the component sets
299      * to eliminate expensive operations on whole-vectors (3 components at runtime).
300      * to achive this effect in a clean manner this function generalizes the 
301      * values through the use of a set paramater, which is used as an indexing method
302      * for creating the elided ast binary expression.
303      *
304      * Consider 'n 0 0' where y, and z need to be tested for 0, and x is
305      * used as the value in a binary operation generating an INSTR_MUL instruction
306      * to acomplish the indexing of the correct component value we use set[0], set[1], set[2]
307      * as x, y, z, where the values of those operations return 'x', 'y', 'z'. Because
308      * of how ASCII works we can easily deliniate:
309      * vec.z is the same as set[2]-'x' for when set[2] is 'z', 'z'-'x' results in a
310      * literal value of 2, using this 2, we know that taking the address of vec->x (float)
311      * and indxing it with this literal will yeild the immediate address of that component
312      * 
313      * Of course more work needs to be done to generate the correct index for the ast_member_new
314      * call, which is no problem: set[0]-'x' suffices that job.
315      */
316     qcfloat_t x = (&vec.x)[set[0]-'x'];
317     qcfloat_t y = (&vec.x)[set[1]-'x'];
318     qcfloat_t z = (&vec.x)[set[2]-'x'];
319
320     if (!y && !z) {
321         ast_expression *out;
322         ++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
323         out                        = (ast_expression*)ast_member_new(fold_ctx(fold), (ast_expression*)sel, set[0]-'x', NULL);
324         out->node.keep             = false;
325         ((ast_member*)out)->rvalue = true;
326         if (x != -1)
327             return (ast_expression*)ast_binary_new(fold_ctx(fold), INSTR_MUL_F, fold_constgen_float(fold, x), out);
328     }
329     return NULL;
330 }
331
332
333 static GMQCC_INLINE ast_expression *fold_op_neg(fold_t *fold, ast_value *a) {
334     if (isfloat(a)) {
335         if (fold_can_1(a))
336             return fold_constgen_float(fold, -fold_immvalue_float(a));
337     } else if (isvector(a)) {
338         if (fold_can_1(a))
339             return fold_constgen_vector(fold, vec3_neg(fold_immvalue_vector(a)));
340     }
341     return NULL;
342 }
343
344 static GMQCC_INLINE ast_expression *fold_op_not(fold_t *fold, ast_value *a) {
345     if (isfloat(a)) {
346         if (fold_can_1(a))
347             return fold_constgen_float(fold, !fold_immvalue_float(a));
348     } else if (isvector(a)) {
349         if (fold_can_1(a))
350             return fold_constgen_float(fold, vec3_notf(fold_immvalue_vector(a)));
351     } else if (isstring(a)) {
352         if (fold_can_1(a)) {
353             if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
354                 return fold_constgen_float(fold, !fold_immvalue_string(a));
355             else
356                 return fold_constgen_float(fold, !fold_immvalue_string(a) || !*fold_immvalue_string(a));
357         }
358     }
359     return NULL;
360 }
361
362 static GMQCC_INLINE ast_expression *fold_op_add(fold_t *fold, ast_value *a, ast_value *b) {
363     if (isfloat(a)) {
364         if (fold_can_2(a, b))
365             return fold_constgen_float(fold, fold_immvalue_float(a) + fold_immvalue_float(b));
366     } else if (isvector(a)) {
367         if (fold_can_2(a, b))
368             return fold_constgen_vector(fold, vec3_add(fold_immvalue_vector(a), fold_immvalue_vector(b)));
369     }
370     return NULL;
371 }
372
373 static GMQCC_INLINE ast_expression *fold_op_sub(fold_t *fold, ast_value *a, ast_value *b) {
374     if (isfloat(a)) {
375         if (fold_can_2(a, b))
376             return fold_constgen_float(fold, fold_immvalue_float(a) - fold_immvalue_float(b));
377     } else if (isvector(a)) {
378         if (fold_can_2(a, b))
379             return fold_constgen_vector(fold, vec3_sub(fold_immvalue_vector(a), fold_immvalue_vector(b)));
380     }
381     return NULL;
382 }
383
384 static GMQCC_INLINE ast_expression *fold_op_mul(fold_t *fold, ast_value *a, ast_value *b) {
385     if (isfloat(a)) {
386         if (isfloat(b)) {
387             if (fold_can_2(a, b))
388                 return fold_constgen_vector(fold, vec3_mulvf(fold_immvalue_vector(b), fold_immvalue_float(a)));
389         } else {
390             if (fold_can_2(a, b))
391                 return fold_constgen_float(fold, fold_immvalue_float(a) * fold_immvalue_float(b));
392         }
393     } else if (isvector(a)) {
394         if (isfloat(b)) {
395             if (fold_can_2(a, b))
396                 return fold_constgen_vector(fold, vec3_mulvf(fold_immvalue_vector(a), fold_immvalue_float(b)));
397         } else {
398             if (fold_can_2(a, b)) {
399                 return fold_constgen_float(fold, vec3_mulvv(fold_immvalue_vector(a), fold_immvalue_vector(b)));
400             } else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && fold_can_1(a)) {
401                 ast_expression *out;
402                 if ((out = fold_op_mul_vec(fold, fold_immvalue_vector(a), b, "xyz"))) return out;
403                 if ((out = fold_op_mul_vec(fold, fold_immvalue_vector(a), b, "yxz"))) return out;
404                 if ((out = fold_op_mul_vec(fold, fold_immvalue_vector(a), b, "zxy"))) return out;
405             } else if (OPTS_OPTIMIZATION(OPTIM_VECTOR_COMPONENTS) && fold_can_1(b)) {
406                 ast_expression *out;
407                 if ((out = fold_op_mul_vec(fold, fold_immvalue_vector(b), a, "xyz"))) return out;
408                 if ((out = fold_op_mul_vec(fold, fold_immvalue_vector(b), a, "yxz"))) return out;
409                 if ((out = fold_op_mul_vec(fold, fold_immvalue_vector(b), a, "zxy"))) return out;
410             }
411         }
412     }
413     return NULL;
414 }
415
416 static GMQCC_INLINE ast_expression *fold_op_div(fold_t *fold, ast_value *a, ast_value *b) {
417     if (isfloat(a)) {
418         if (fold_can_2(a, b))
419             return fold_constgen_float(fold, fold_immvalue_float(a) / fold_immvalue_float(b));
420     } else if (isvector(a)) {
421         if (fold_can_2(a, b))
422             return fold_constgen_vector(fold, vec3_mulvf(fold_immvalue_vector(a), 1.0f / fold_immvalue_float(b)));
423         else {
424             return (ast_expression*)ast_binary_new(
425                 fold_ctx(fold),
426                 INSTR_MUL_VF,
427                 (ast_expression*)a,
428                 (fold_can_1(b))
429                     ? (ast_expression*)fold_constgen_float(fold, 1.0f / fold_immvalue_float(b))
430                     : (ast_expression*)ast_binary_new(
431                                             fold_ctx(fold),
432                                             INSTR_DIV_F,
433                                             (ast_expression*)fold->imm_float[1],
434                                             (ast_expression*)b
435                     )
436             );
437         }
438     }
439     return NULL;
440 }
441
442 static GMQCC_INLINE ast_expression *fold_op_mod(fold_t *fold, ast_value *a, ast_value *b) {
443     if (fold_can_2(a, b))
444         return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) % ((qcint_t)fold_immvalue_float(b))));
445     return NULL;
446 }
447
448 static GMQCC_INLINE ast_expression *fold_op_bor(fold_t *fold, ast_value *a, ast_value *b) {
449     if (fold_can_2(a, b))
450         return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) | ((qcint_t)fold_immvalue_float(b))));
451     return NULL;
452 }
453
454 static GMQCC_INLINE ast_expression *fold_op_band(fold_t *fold, ast_value *a, ast_value *b) {
455     if (fold_can_2(a, b))
456         return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) & ((qcint_t)fold_immvalue_float(b))));
457     return NULL;
458 }
459
460 static GMQCC_INLINE ast_expression *fold_op_xor(fold_t *fold, ast_value *a, ast_value *b) {
461     if (isfloat(a)) {
462         if (fold_can_2(a, b))
463             return fold_constgen_float(fold, (qcfloat_t)(((qcint_t)fold_immvalue_float(a)) ^ ((qcint_t)fold_immvalue_float(b))));
464     } else {
465         if (isvector(b)) {
466             if (fold_can_2(a, b))
467                 return fold_constgen_vector(fold, vec3_xor(fold_immvalue_vector(a), fold_immvalue_vector(b)));
468         } else {
469             if (fold_can_2(a, b))
470                 return fold_constgen_vector(fold, vec3_xorvf(fold_immvalue_vector(a), fold_immvalue_float(b)));
471         }
472     }
473     return NULL;
474 }
475
476 static GMQCC_INLINE ast_expression *fold_op_lshift(fold_t *fold, ast_value *a, ast_value *b) {
477     if (fold_can_2(a, b) && isfloats(a, b))
478         return fold_constgen_float(fold, (qcfloat_t)((qcuint_t)(fold_immvalue_float(a)) << (qcuint_t)(fold_immvalue_float(b))));
479     return NULL;
480 }
481
482 static GMQCC_INLINE ast_expression *fold_op_rshift(fold_t *fold, ast_value *a, ast_value *b) {
483     if (fold_can_2(a, b) && isfloats(a, b))
484         return fold_constgen_float(fold, (qcfloat_t)((qcuint_t)(fold_immvalue_float(a)) >> (qcuint_t)(fold_immvalue_float(b))));
485     return NULL;
486 }
487
488 static GMQCC_INLINE ast_expression *fold_op_andor(fold_t *fold, ast_value *a, ast_value *b, float or) {
489     if (fold_can_2(a, b)) {
490         if (OPTS_FLAG(PERL_LOGIC)) {
491             if (fold_immediate_true(fold, a))
492                 return (ast_expression*)b;
493         } else {
494             return fold_constgen_float (
495                 fold, 
496                 ((or) ? (fold_immediate_true(fold, a) || fold_immediate_true(fold, b))
497                       : (fold_immediate_true(fold, a) && fold_immediate_true(fold, b)))
498                             ? 1
499                             : 0
500             );
501         }
502     }
503     return NULL;
504 }
505
506 static GMQCC_INLINE ast_expression *fold_op_tern(fold_t *fold, ast_value *a, ast_value *b, ast_value *c) {
507     if (fold_can_1(a)) {
508         return fold_immediate_true(fold, a)
509                     ? (ast_expression*)b
510                     : (ast_expression*)c;
511     }
512     return NULL;
513 }
514
515 static GMQCC_INLINE ast_expression *fold_op_exp(fold_t *fold, ast_value *a, ast_value *b) {
516     if (fold_can_2(a, b))
517         return fold_constgen_float(fold, (qcfloat_t)powf(fold_immvalue_float(a), fold_immvalue_float(b)));
518     return NULL;
519 }
520
521 static GMQCC_INLINE ast_expression *fold_op_lteqgt(fold_t *fold, ast_value *a, ast_value *b) {
522     if (fold_can_2(a,b)) {
523         if (fold_immvalue_float(a) <  fold_immvalue_float(b)) return (ast_expression*)fold->imm_float[2];
524         if (fold_immvalue_float(a) == fold_immvalue_float(b)) return (ast_expression*)fold->imm_float[0];
525         if (fold_immvalue_float(a) >  fold_immvalue_float(b)) return (ast_expression*)fold->imm_float[1];
526     }
527     return NULL;
528 }
529
530 static GMQCC_INLINE ast_expression *fold_op_cmp(fold_t *fold, ast_value *a, ast_value *b, bool ne) {
531     if (fold_can_2(a, b)) {
532         return fold_constgen_float(
533                     fold,
534                     (ne) ? (fold_immvalue_float(a) != fold_immvalue_float(b))
535                          : (fold_immvalue_float(a) == fold_immvalue_float(b))
536                 );
537     }
538     return NULL;
539 }
540
541 static GMQCC_INLINE ast_expression *fold_op_bnot(fold_t *fold, ast_value *a) {
542     if (fold_can_1(a))
543         return fold_constgen_float(fold, ~((qcint_t)fold_immvalue_float(a)));
544     return NULL;
545 }
546
547 ast_expression *fold_op(fold_t *fold, const oper_info *info, ast_expression **opexprs) {
548     ast_value *a = (ast_value*)opexprs[0];
549     ast_value *b = (ast_value*)opexprs[1];
550     ast_value *c = (ast_value*)opexprs[2];
551
552     /* can a fold operation be applied to this operator usage? */
553     if (!info->folds)
554         return NULL;
555
556     switch(info->operands) {
557         case 3: if(!c) return NULL;
558         case 2: if(!b) return NULL;
559         case 1:
560         if(!a) {
561             compile_error(fold_ctx(fold), "interal error: fold_op no operands to fold\n");
562             return NULL;
563         }
564     }
565
566     switch(info->id) {
567         case opid2('-','P'):     return fold_op_neg    (fold, a);
568         case opid2('!','P'):     return fold_op_not    (fold, a);
569         case opid1('+'):         return fold_op_add    (fold, a, b);
570         case opid1('-'):         return fold_op_sub    (fold, a, b);
571         case opid1('*'):         return fold_op_mul    (fold, a, b);
572         case opid1('/'):         return fold_op_div    (fold, a, b);
573         case opid1('%'):         return fold_op_mod    (fold, a, b);
574         case opid1('|'):         return fold_op_bor    (fold, a, b);
575         case opid1('&'):         return fold_op_band   (fold, a, b);
576         case opid1('^'):         return fold_op_xor    (fold, a, b);
577         case opid2('<','<'):     return fold_op_lshift (fold, a, b);
578         case opid2('>','>'):     return fold_op_rshift (fold, a, b);
579         case opid2('|','|'):     return fold_op_andor  (fold, a, b, true);
580         case opid2('&','&'):     return fold_op_andor  (fold, a, b, false);
581         case opid2('?',':'):     return fold_op_tern   (fold, a, b, c);
582         case opid2('*','*'):     return fold_op_exp    (fold, a, b);
583         case opid3('<','=','>'): return fold_op_lteqgt (fold, a, b);
584         case opid2('!','='):     return fold_op_cmp    (fold, a, b, true);
585         case opid2('=','='):     return fold_op_cmp    (fold, a, b, false);
586         case opid2('~','P'):     return fold_op_bnot   (fold, a);
587     }
588     return NULL;
589 }