Support for unary minus
[mw/milkymist.git] / software / libfpvm / fpvm.c
1 /*
2  * Milkymist VJ SoC (Software)
3  * Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3 of the License.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <string.h>
19 #include <stdio.h>
20 #include <base/version.h>
21
22 #include <fpvm/is.h>
23 #include <fpvm/fpvm.h>
24
25 #include "ast.h"
26 #include "parser_helper.h"
27
28 const char *fpvm_version()
29 {
30         return VERSION;
31 }
32
33 void fpvm_init(struct fpvm_fragment *fragment, int vector_mode)
34 {
35         fragment->last_error[0] = 0;
36
37         fragment->nbindings = 3;
38         fragment->bindings[0].isvar = 1;
39         fragment->bindings[0].b.v[0] = '_';
40         fragment->bindings[0].b.v[1] = 'X';
41         fragment->bindings[0].b.v[2] = 'i';
42         fragment->bindings[0].b.v[3] = 0;
43         fragment->bindings[1].isvar = 1;
44         fragment->bindings[1].b.v[0] = '_';
45         fragment->bindings[1].b.v[1] = 'Y';
46         fragment->bindings[1].b.v[2] = 'i';
47         fragment->bindings[1].b.v[3] = 0;
48         /* Prevent binding of R2 (we need it for "if") */
49         fragment->bindings[2].isvar = 1;
50         fragment->bindings[2].b.v[0] = 0;
51
52         fragment->ntbindings = 2;
53         fragment->tbindings[0].reg = -1;
54         fragment->tbindings[0].sym[0] = '_';
55         fragment->tbindings[0].sym[1] = 'X';
56         fragment->tbindings[0].sym[2] = 'o';
57         fragment->tbindings[0].sym[3] = 0;
58         fragment->tbindings[1].reg = -2;
59         fragment->tbindings[1].sym[0] = '_';
60         fragment->tbindings[1].sym[1] = 'Y';
61         fragment->tbindings[1].sym[2] = 'o';
62         fragment->tbindings[1].sym[3] = 0;
63
64         fragment->nrenamings = 0;
65         
66         fragment->next_sur = -3;
67         fragment->ninstructions = 0;
68
69         fragment->bind_mode = 0;
70         fragment->vector_mode = vector_mode;
71 }
72
73 int fpvm_bind(struct fpvm_fragment *fragment, const char *sym)
74 {
75         if(fragment->nbindings == FPVM_MAXBINDINGS) {
76                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Failed to allocate register for variable: %s", sym);
77                 return FPVM_INVALID_REG;
78         }
79         fragment->bindings[fragment->nbindings].isvar = 1;
80         strcpy(fragment->bindings[fragment->nbindings].b.v, sym);
81         return fragment->nbindings++;
82 }
83
84 void fpvm_set_xin(struct fpvm_fragment *fragment, const char *sym)
85 {
86         strcpy(fragment->bindings[0].b.v, sym);
87 }
88
89 void fpvm_set_yin(struct fpvm_fragment *fragment, const char *sym)
90 {
91         strcpy(fragment->bindings[1].b.v, sym);
92 }
93
94 void fpvm_set_xout(struct fpvm_fragment *fragment, const char *sym)
95 {
96         strcpy(fragment->tbindings[0].sym, sym);
97 }
98
99 void fpvm_set_yout(struct fpvm_fragment *fragment, const char *sym)
100 {
101         strcpy(fragment->tbindings[1].sym, sym);
102 }
103
104 static int lookup(struct fpvm_fragment *fragment, const char *sym)
105 {
106         int i;
107
108         for(i=0;i<fragment->nrenamings;i++)
109                 if(strcmp(sym, fragment->renamings[i].sym) == 0)
110                         return fragment->renamings[i].reg;
111         for(i=0;i<fragment->nbindings;i++)
112                 if(fragment->bindings[i].isvar &&
113                         (strcmp(sym, fragment->bindings[i].b.v) == 0))
114                         return i;
115         for(i=0;i<fragment->ntbindings;i++)
116                 if(strcmp(sym, fragment->tbindings[i].sym) == 0)
117                         return fragment->tbindings[i].reg;
118         return FPVM_INVALID_REG;
119 }
120
121 static int tbind(struct fpvm_fragment *fragment, const char *sym)
122 {
123         if(fragment->ntbindings == FPVM_MAXTBINDINGS) {
124                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Failed to allocate register for variable: %s", sym);
125                 return FPVM_INVALID_REG;
126         }
127         fragment->tbindings[fragment->ntbindings].reg = fragment->next_sur;
128         strcpy(fragment->tbindings[fragment->ntbindings].sym, sym);
129         fragment->ntbindings++;
130         return fragment->next_sur--;
131 }
132
133 static int rename(struct fpvm_fragment *fragment, const char *sym, int reg)
134 {
135         int i;
136         
137         for(i=0;i<fragment->nrenamings;i++)
138                 if(strcmp(sym, fragment->renamings[i].sym) == 0) {
139                         fragment->renamings[i].reg = reg;
140                         return 1;
141                 }
142         if(fragment->nrenamings == FPVM_MAXRENAMINGS) {
143                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Failed to allocate renamed register for variable: %s", sym);
144                 return 0;
145         }
146         fragment->renamings[fragment->nrenamings].reg = reg;
147         strcpy(fragment->renamings[fragment->nrenamings].sym, sym);
148         fragment->nrenamings++;
149         return 1;
150 }
151
152 static int sym_to_reg(struct fpvm_fragment *fragment, const char *sym, int *created)
153 {
154         int r;
155         if(created) *created = 0;
156         r = lookup(fragment, sym);
157         if(r == FPVM_INVALID_REG) {
158                 if(created) *created = 1;
159                 if(fragment->bind_mode)
160                         r = fpvm_bind(fragment, sym);
161                 else
162                         r = tbind(fragment, sym);
163         }
164         return r;
165 }
166
167 static int const_to_reg(struct fpvm_fragment *fragment, float c)
168 {
169         int i;
170
171         for(i=0;i<fragment->nbindings;i++) {
172                 if(!fragment->bindings[i].isvar &&
173                         (fragment->bindings[i].b.c == c))
174                         return i;
175         }
176         /* not already bound */
177         if(fragment->nbindings == FPVM_MAXBINDINGS) {
178                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Failed to allocate register for constant");
179                 return FPVM_INVALID_REG;
180         }
181         fragment->bindings[fragment->nbindings].isvar = 0;
182         fragment->bindings[fragment->nbindings].b.c = c;
183         return fragment->nbindings++;
184 }
185
186 static int find_negative_constant(struct fpvm_fragment *fragment)
187 {
188         int i;
189
190         for(i=0;i<fragment->nbindings;i++) {
191                 if(!fragment->bindings[i].isvar &&
192                         (fragment->bindings[i].b.c < 0.0))
193                         return i;
194         }
195         return const_to_reg(fragment, -1.0);
196 }
197
198 static int add_isn(struct fpvm_fragment *fragment, int opcode, int opa, int opb, int dest)
199 {
200         int len;
201
202         len = fragment->ninstructions;
203         if(len >= FPVM_MAXCODELEN) {
204                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Ran out of program space");
205                 return 0;
206         }
207
208         fragment->code[len].opa = opa;
209         fragment->code[len].opb = opb;
210         fragment->code[len].opcode = opcode;
211         fragment->code[len].dest = dest;
212
213         fragment->ninstructions++;
214         return 1;
215 }
216
217 static int operator2opcode(const char *operator)
218 {
219         if(strcmp(operator, "+") == 0) return FPVM_OPCODE_FADD;
220         if(strcmp(operator, "-") == 0) return FPVM_OPCODE_FSUB;
221         if(strcmp(operator, "*") == 0) return FPVM_OPCODE_FMUL;
222         if(strcmp(operator, "abs") == 0) return FPVM_OPCODE_FABS;
223         if(strcmp(operator, "isin") == 0) return FPVM_OPCODE_SIN;
224         if(strcmp(operator, "icos") == 0) return FPVM_OPCODE_COS;
225         if(strcmp(operator, "above") == 0) return FPVM_OPCODE_ABOVE;
226         if(strcmp(operator, "equal") == 0) return FPVM_OPCODE_EQUAL;
227         if(strcmp(operator, "i2f") == 0) return FPVM_OPCODE_I2F;
228         if(strcmp(operator, "f2i") == 0) return FPVM_OPCODE_F2I;
229         if(strcmp(operator, "if") == 0) return FPVM_OPCODE_IF;
230         if(strcmp(operator, "tsign") == 0) return FPVM_OPCODE_TSIGN;
231         if(strcmp(operator, "quake") == 0) return FPVM_OPCODE_QUAKE;
232         else return -1;
233 }
234
235 static int add_inv_sqrt_step(struct fpvm_fragment *fragment, int reg_y, int reg_x, int reg_out)
236 {
237         int reg_onehalf;
238         int reg_twohalf;
239         int reg_yy;
240         int reg_hx;
241         int reg_hxyy;
242         int reg_sub;
243
244         reg_yy = fragment->next_sur--;
245         reg_hx = fragment->next_sur--;
246         reg_hxyy = fragment->next_sur--;
247         reg_sub = fragment->next_sur--;
248
249         reg_onehalf = const_to_reg(fragment, 0.5f);
250         if(reg_onehalf == FPVM_INVALID_REG) return 0;
251         reg_twohalf = const_to_reg(fragment, 1.5f);
252         if(reg_twohalf == FPVM_INVALID_REG) return 0;
253
254         if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_y, reg_y, reg_yy)) return 0;
255         if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_onehalf, reg_x, reg_hx)) return 0;
256         if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_hx, reg_yy, reg_hxyy)) return 0;
257         if(!add_isn(fragment, FPVM_OPCODE_FSUB, reg_twohalf, reg_hxyy, reg_sub)) return 0;
258         if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_sub, reg_y, reg_out)) return 0;
259
260         return 1;
261 }
262
263 static int add_inv_sqrt(struct fpvm_fragment *fragment, int reg_in, int reg_out)
264 {
265         int reg_y, reg_y2;
266
267         reg_y = fragment->next_sur--;
268         reg_y2 = fragment->next_sur--;
269         
270         if(!add_isn(fragment, FPVM_OPCODE_QUAKE, reg_in, 0, reg_y)) return 0;
271         if(!add_inv_sqrt_step(fragment, reg_y, reg_in, reg_y2)) return 0;
272         if(!add_inv_sqrt_step(fragment, reg_y2, reg_in, reg_out)) return 0;
273         
274         return 1;
275 }
276
277 static int add_int(struct fpvm_fragment *fragment, int reg_in, int reg_out)
278 {
279         int reg_i;
280         
281         reg_i = fragment->next_sur--;
282         if(!add_isn(fragment, FPVM_OPCODE_F2I, reg_in, 0, reg_i)) return FPVM_INVALID_REG;
283         if(!add_isn(fragment, FPVM_OPCODE_I2F, reg_i, 0, reg_out)) return FPVM_INVALID_REG;
284         return 1;
285 }
286
287 /*
288  * Compiles a node.
289  * Returns the register the result of the node gets written to,
290  * and FPVM_INVALID_REG in case of error.
291  * If reg != FPVM_INVALID_REG,
292  * it forces the result to be written to this particular register.
293  */
294 static int compile(struct fpvm_fragment *fragment, int reg, struct ast_node *node)
295 {
296         int opa, opb;
297         int opcode;
298
299         if(node->label[0] == 0) {
300                 /* AST node is a constant */
301                 opa = const_to_reg(fragment, node->contents.constant);
302                 if(opa == FPVM_INVALID_REG) return FPVM_INVALID_REG;
303                 if(reg != FPVM_INVALID_REG) {
304                         if(!add_isn(fragment, FPVM_OPCODE_COPY, opa, 0, reg)) return FPVM_INVALID_REG;
305                 } else
306                         reg = opa;
307                 return reg;
308         }
309         if(node->contents.branches.a == NULL) {
310                 /* AST node is a variable */
311                 if(fragment->bind_mode) {
312                         opa = sym_to_reg(fragment, node->label, NULL);
313                         if(opa == FPVM_INVALID_REG) return FPVM_INVALID_REG;
314                 } else {
315                         opa = lookup(fragment, node->label);
316                         if((opa == FPVM_INVALID_REG)||(opa == fragment->final_dest)) {
317                                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Reading unbound variable: %s", node->label);
318                                 return FPVM_INVALID_REG;
319                         }
320                 }
321                 if(reg != FPVM_INVALID_REG) {
322                         if(!add_isn(fragment, FPVM_OPCODE_COPY, opa, 0, reg)) return FPVM_INVALID_REG;
323                 } else
324                         reg = opa;
325                 return reg;
326         }
327         if((strcmp(node->label, "!") == 0) && (node->contents.branches.a->label[0] == 0)) {
328                 /* Node is a negative constant */
329                 struct ast_node *n;
330
331                 n = node->contents.branches.a;
332                 opa = const_to_reg(fragment, -n->contents.constant);
333                 if(opa == FPVM_INVALID_REG) return FPVM_INVALID_REG;
334                 if(reg != FPVM_INVALID_REG) {
335                         if(!add_isn(fragment, FPVM_OPCODE_COPY, opa, 0, reg)) return FPVM_INVALID_REG;
336                 } else
337                         reg = opa;
338                 return reg;
339         }
340         /* AST node is an operator or function */
341         if(strcmp(node->label, "if") == 0) {
342                 /*
343                  * "if" must receive a special treatment.
344                  * It is implemented as a ternary function,
345                  * but its first parameter is hardwired to R2 (FPVM_REG_IFB) and implicit.
346                  * We must compute the other parameters first, as they may clobber R2.
347                  */
348                 opa = compile(fragment, FPVM_INVALID_REG, node->contents.branches.b);
349                 if(opa == FPVM_INVALID_REG) return FPVM_INVALID_REG;
350                 opb = compile(fragment, FPVM_INVALID_REG, node->contents.branches.c);
351                 if(opb == FPVM_INVALID_REG) return FPVM_INVALID_REG;
352                 if(compile(fragment, FPVM_REG_IFB, node->contents.branches.a) == FPVM_INVALID_REG)
353                         return FPVM_INVALID_REG;
354         } else {
355                 opa = compile(fragment, FPVM_INVALID_REG, node->contents.branches.a);
356                 if(opa == FPVM_INVALID_REG) return FPVM_INVALID_REG;
357                 opb = 0;
358                 if(node->contents.branches.b != NULL) {
359                         opb = compile(fragment, FPVM_INVALID_REG, node->contents.branches.b);
360                         if(opb == FPVM_INVALID_REG) return FPVM_INVALID_REG;
361                 }
362         }
363
364         if(reg == FPVM_INVALID_REG) reg = fragment->next_sur--;
365         if(strcmp(node->label, "below") == 0) {
366                 /*
367                  * "below" is like "above", but with reversed operands.
368                  */
369                 if(!add_isn(fragment, FPVM_OPCODE_ABOVE, opb, opa, reg)) return FPVM_INVALID_REG;
370         } else if((strcmp(node->label, "sin") == 0)||(strcmp(node->label, "cos") == 0)) {
371                 /*
372                  * Trigo functions are implemented with several instructions.
373                  * We must convert the floating point argument in radians
374                  * to an integer expressed in 1/8192 turns for FPVM.
375                  */
376                 int reg_const;
377                 int reg_mul;
378                 int reg_f2i;
379
380                 if(strcmp(node->label, "sin") == 0)
381                         opcode = FPVM_OPCODE_SIN;
382                 else
383                         opcode = FPVM_OPCODE_COS;
384
385                 reg_const = const_to_reg(fragment, FPVM_TRIG_CONV);
386                 if(reg_const == FPVM_INVALID_REG) return FPVM_INVALID_REG;
387                 reg_mul = fragment->next_sur--;
388                 reg_f2i = fragment->next_sur--;
389
390                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_const, opa, reg_mul)) return FPVM_INVALID_REG;
391                 if(!add_isn(fragment, FPVM_OPCODE_F2I, reg_mul, 0, reg_f2i)) return FPVM_INVALID_REG;
392                 if(!add_isn(fragment, opcode, reg_f2i, 0, reg)) return FPVM_INVALID_REG;
393         } else if(strcmp(node->label, "sqrt") == 0) {
394                 /*
395                  * Square root is implemented with a variant of the Quake III algorithm.
396                  * See http://en.wikipedia.org/wiki/Fast_inverse_square_root
397                  * sqrt(x) = x*(1/sqrt(x))
398                  */
399                 int reg_invsqrt;
400                 reg_invsqrt = fragment->next_sur--;
401                 if(!add_inv_sqrt(fragment, opa, reg_invsqrt)) return FPVM_INVALID_REG;
402                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, opa, reg_invsqrt, reg)) return FPVM_INVALID_REG;
403         } else if(strcmp(node->label, "invsqrt") == 0) {
404                 if(!add_inv_sqrt(fragment, opa, reg)) return FPVM_INVALID_REG;
405         } else if(strcmp(node->label, "/") == 0) {
406                 /*
407                  * Floating point division is implemented as
408                  * a/b = a*(1/sqrt(b))*(1/sqrt(b))
409                  */
410                 int reg_a2;
411                 int reg_b2;
412                 int reg_invsqrt;
413                 int reg_invsqrt2;
414                 
415                 reg_a2 = fragment->next_sur--;
416                 reg_b2 = fragment->next_sur--;
417                 reg_invsqrt = fragment->next_sur--;
418                 reg_invsqrt2 = fragment->next_sur--;
419
420                 /* Transfer the sign of the result to a and make b positive */
421                 if(!add_isn(fragment, FPVM_OPCODE_TSIGN, opa, opb, reg_a2)) return FPVM_INVALID_REG;
422                 if(!add_isn(fragment, FPVM_OPCODE_FABS, opb, 0, reg_b2)) return FPVM_INVALID_REG;
423                 
424                 if(!add_inv_sqrt(fragment, reg_b2, reg_invsqrt)) return FPVM_INVALID_REG;
425                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_invsqrt, reg_invsqrt, reg_invsqrt2)) return FPVM_INVALID_REG;
426                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_invsqrt2, reg_a2, reg)) return FPVM_INVALID_REG;
427         } else if(strcmp(node->label, "%") == 0) {
428                 int reg_invsqrt;
429                 int reg_invsqrt2;
430                 int reg_div;
431                 int reg_idiv;
432                 int reg_bidiv;
433
434                 reg_invsqrt = fragment->next_sur--;
435                 reg_invsqrt2 = fragment->next_sur--;
436                 reg_div = fragment->next_sur--;
437                 reg_idiv = fragment->next_sur--;
438                 reg_bidiv = fragment->next_sur--;
439
440                 if(!add_inv_sqrt(fragment, opb, reg_invsqrt)) return FPVM_INVALID_REG;
441                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_invsqrt, reg_invsqrt, reg_invsqrt2)) return FPVM_INVALID_REG;
442                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_invsqrt2, opa, reg_div)) return FPVM_INVALID_REG;
443                 if(!add_int(fragment, reg_div, reg_idiv)) return FPVM_INVALID_REG;
444                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, opb, reg_idiv, reg_bidiv)) return FPVM_INVALID_REG;
445                 if(!add_isn(fragment, FPVM_OPCODE_FSUB, opa, reg_bidiv, reg)) return FPVM_INVALID_REG;
446         } else if(strcmp(node->label, "min") == 0) {
447                 if(!add_isn(fragment, FPVM_OPCODE_ABOVE, opa, opb, FPVM_REG_IFB)) return FPVM_INVALID_REG;
448                 if(!add_isn(fragment, FPVM_OPCODE_IF, opb, opa, reg)) return FPVM_INVALID_REG;
449         } else if(strcmp(node->label, "max") == 0) {
450                 if(!add_isn(fragment, FPVM_OPCODE_ABOVE, opa, opb, FPVM_REG_IFB)) return FPVM_INVALID_REG;
451                 if(!add_isn(fragment, FPVM_OPCODE_IF, opa, opb, reg)) return FPVM_INVALID_REG;
452         } else if(strcmp(node->label, "sqr") == 0) {
453                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, opa, opa, reg)) return FPVM_INVALID_REG;
454         } else if(strcmp(node->label, "int") == 0) {
455                 if(!add_int(fragment, opa, reg)) return FPVM_INVALID_REG;
456         } else if(strcmp(node->label, "!") == 0) {
457                 opb = find_negative_constant(fragment);
458                 if(opb == FPVM_INVALID_REG) return FPVM_INVALID_REG;
459                 if(!add_isn(fragment, FPVM_OPCODE_TSIGN, opa, opb, reg)) return FPVM_INVALID_REG;
460         } else {
461                 /* Normal case */
462                 opcode = operator2opcode(node->label);
463                 if(opcode < 0) {
464                         snprintf(fragment->last_error, FPVM_MAXERRLEN, "Operation not supported: %s", node->label);
465                         return FPVM_INVALID_REG;
466                 }
467                 if(!add_isn(fragment, opcode, opa, opb, reg)) return FPVM_INVALID_REG;
468         }
469
470         return reg;
471 }
472
473 struct fpvm_backup {
474         int ntbindings;
475         int next_sur;
476         int ninstructions;
477 };
478
479 static void fragment_backup(struct fpvm_fragment *fragment, struct fpvm_backup *backup)
480 {
481         backup->ntbindings = fragment->ntbindings;
482         backup->next_sur = fragment->next_sur;
483         backup->ninstructions = fragment->ninstructions;
484 }
485
486 static void fragment_restore(struct fpvm_fragment *fragment, struct fpvm_backup *backup)
487 {
488         fragment->ntbindings = backup->ntbindings;
489         fragment->next_sur = backup->next_sur;
490         fragment->ninstructions = backup->ninstructions;
491 }
492
493 int fpvm_assign(struct fpvm_fragment *fragment, const char *dest, const char *expr)
494 {
495         struct ast_node *n;
496         int dest_reg;
497         struct fpvm_backup backup;
498         int created;
499         int use_renaming;
500
501         n = fpvm_parse(expr);
502         if(n == NULL) {
503                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Parse error");
504                 return 0;
505         }
506
507         fragment_backup(fragment, &backup);
508
509         use_renaming = fragment->vector_mode
510                 && (strcmp(dest, fragment->tbindings[0].sym) != 0) /* do not rename output X and Y */
511                 && (strcmp(dest, fragment->tbindings[1].sym) != 0);
512         if(use_renaming) {
513                 dest_reg = fragment->next_sur;
514                 fragment->next_sur--;
515                 created = 1;
516         } else
517                 dest_reg = sym_to_reg(fragment, dest, &created);
518         if(dest_reg == FPVM_INVALID_REG) {
519                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Failed to allocate register for destination");
520                 fpvm_parse_free(n);
521                 fragment_restore(fragment, &backup);
522                 return 0;
523         }
524
525         if(created)
526                 fragment->final_dest = dest_reg;
527         else
528                 fragment->final_dest = FPVM_INVALID_REG;
529         if(compile(fragment, dest_reg, n) == FPVM_INVALID_REG) {
530                 fpvm_parse_free(n);
531                 fragment_restore(fragment, &backup);
532                 return 0;
533         }
534         if(use_renaming) {
535                 if(!rename(fragment, dest, dest_reg)) {
536                         fpvm_parse_free(n);
537                         fragment_restore(fragment, &backup);
538                         return 0;
539                 }
540         }
541
542         fpvm_parse_free(n);
543         return 1;
544 }
545
546 void fpvm_get_references(struct fpvm_fragment *fragment, int *references)
547 {
548         int i;
549
550         for(i=0;i<FPVM_MAXBINDINGS;i++)
551                 references[i] = 0;
552         for(i=0;i<fragment->ninstructions;i++) {
553                 if(fragment->code[i].opcode == FPVM_OPCODE_IF)
554                         references[2] = 1;
555                 if(fragment->code[i].opa > 0)
556                         references[fragment->code[i].opa] = 1;
557                 if(fragment->code[i].opb > 0)
558                         references[fragment->code[i].opb] = 1;
559                 if(fragment->code[i].dest > 0)
560                         references[fragment->code[i].dest] = 1;
561         }
562 }
563
564 int fpvm_finalize(struct fpvm_fragment *fragment)
565 {
566         if(fragment->vector_mode)
567                 return add_isn(fragment, FPVM_OPCODE_VECTOUT, -1, -2, 0);
568         else
569                 return 1;
570 }
571
572 void fpvm_print_opcode(int opcode)
573 {
574         switch(opcode) {
575                 case FPVM_OPCODE_NOP:     printf("NOP     "); break;
576                 case FPVM_OPCODE_FADD:    printf("FADD    "); break;
577                 case FPVM_OPCODE_FSUB:    printf("FSUB    "); break;
578                 case FPVM_OPCODE_FMUL:    printf("FMUL    "); break;
579                 case FPVM_OPCODE_FABS:    printf("FABS    "); break;
580                 case FPVM_OPCODE_F2I:     printf("F2I     "); break;
581                 case FPVM_OPCODE_I2F:     printf("I2F     "); break;
582                 case FPVM_OPCODE_VECTOUT: printf("VECTOUT "); break;
583                 case FPVM_OPCODE_SIN:     printf("SIN     "); break;
584                 case FPVM_OPCODE_COS:     printf("COS     "); break;
585                 case FPVM_OPCODE_ABOVE:   printf("ABOVE   "); break;
586                 case FPVM_OPCODE_EQUAL:   printf("EQUAL   "); break;
587                 case FPVM_OPCODE_COPY:    printf("COPY    "); break;
588                 case FPVM_OPCODE_IF:      printf("IF<R2>  "); break;
589                 case FPVM_OPCODE_TSIGN:   printf("TSIGN   "); break;
590                 case FPVM_OPCODE_QUAKE:   printf("QUAKE   "); break;
591                 default:                  printf("XXX     "); break;
592         }
593 }
594
595 int fpvm_get_arity(int opcode)
596 {
597         switch(opcode) {
598                 case FPVM_OPCODE_IF:
599                         return 3;
600                 case FPVM_OPCODE_FADD:
601                 case FPVM_OPCODE_FSUB:
602                 case FPVM_OPCODE_FMUL:
603                 case FPVM_OPCODE_VECTOUT:
604                 case FPVM_OPCODE_EQUAL:
605                 case FPVM_OPCODE_ABOVE:
606                 case FPVM_OPCODE_TSIGN:
607                         return 2;
608                 case FPVM_OPCODE_FABS:
609                 case FPVM_OPCODE_F2I:
610                 case FPVM_OPCODE_I2F:
611                 case FPVM_OPCODE_SIN:
612                 case FPVM_OPCODE_COS:
613                 case FPVM_OPCODE_COPY:
614                 case FPVM_OPCODE_QUAKE:
615                         return 1;
616                 default:
617                         return 0;
618         }
619 }
620
621 void fpvm_dump(struct fpvm_fragment *fragment)
622 {
623         int i;
624         
625         printf("== Permanent bindings:\n");
626         for(i=0;i<fragment->nbindings;i++) {
627                 printf("R%04d ", i);
628                 if(fragment->bindings[i].isvar)
629                         printf("%s\n", fragment->bindings[i].b.v);
630                 else
631                         #ifdef PRINTF_FLOAT
632                         printf("%f\n", fragment->bindings[i].b.c);
633                         #else
634                         printf("%f\n", &fragment->bindings[i].b.c);
635                         #endif
636         }
637         printf("== Transient bindings:\n");
638         for(i=0;i<fragment->ntbindings;i++) {
639                 printf("R%04d ", fragment->tbindings[i].reg);
640                 printf("%s\n", fragment->tbindings[i].sym);
641         }
642         printf("== Code:\n");
643         for(i=0;i<fragment->ninstructions;i++) {
644                 printf("%04d: ", i);
645                 fpvm_print_opcode(fragment->code[i].opcode);
646                 switch(fpvm_get_arity(fragment->code[i].opcode)) {
647                         case 3:
648                         case 2:
649                                 printf("R%04d,R%04d ", fragment->code[i].opa, fragment->code[i].opb);
650                                 break;
651                         case 1:
652                                 printf("R%04d       ", fragment->code[i].opa);
653                                 break;
654                         case 0:
655                                 printf("            ");
656                                 break;
657                 }
658                 if(fragment->code[i].dest != 0)
659                         printf("-> R%04d", fragment->code[i].dest);
660                 printf("\n");
661         }
662 }