Get comparison sizes right.
[smatch.git] / example.c
blob5894fc91e08a41a33ff7110fabca1e969a7be2dc
1 /*
2 * Example of how to write a compiler with sparse
3 */
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <stdarg.h>
7 #include <string.h>
8 #include <assert.h>
10 #include "symbol.h"
11 #include "expression.h"
12 #include "linearize.h"
13 #include "flow.h"
14 #include "storage.h"
16 static const char* opcodes[] = {
17 [OP_BADOP] = "bad_op",
19 /* Fn entrypoint */
20 [OP_ENTRY] = "<entry-point>",
22 /* Terminator */
23 [OP_RET] = "ret",
24 [OP_BR] = "br",
25 [OP_SWITCH] = "switch",
26 [OP_INVOKE] = "invoke",
27 [OP_COMPUTEDGOTO] = "jmp *",
28 [OP_UNWIND] = "unwind",
30 /* Binary */
31 [OP_ADD] = "add",
32 [OP_SUB] = "sub",
33 [OP_MUL] = "mul",
34 [OP_DIV] = "div",
35 [OP_MOD] = "mod",
36 [OP_SHL] = "shl",
37 [OP_SHR] = "shr",
39 /* Logical */
40 [OP_AND] = "and",
41 [OP_OR] = "or",
42 [OP_XOR] = "xor",
43 [OP_AND_BOOL] = "and-bool",
44 [OP_OR_BOOL] = "or-bool",
46 /* Binary comparison */
47 [OP_SET_EQ] = "seteq",
48 [OP_SET_NE] = "setne",
49 [OP_SET_LE] = "setle",
50 [OP_SET_GE] = "setge",
51 [OP_SET_LT] = "setlt",
52 [OP_SET_GT] = "setgt",
53 [OP_SET_B] = "setb",
54 [OP_SET_A] = "seta",
55 [OP_SET_BE] = "setbe",
56 [OP_SET_AE] = "setae",
58 /* Uni */
59 [OP_NOT] = "not",
60 [OP_NEG] = "neg",
62 /* Special three-input */
63 [OP_SEL] = "select",
65 /* Memory */
66 [OP_MALLOC] = "malloc",
67 [OP_FREE] = "free",
68 [OP_ALLOCA] = "alloca",
69 [OP_LOAD] = "load",
70 [OP_STORE] = "store",
71 [OP_SETVAL] = "set",
72 [OP_GET_ELEMENT_PTR] = "getelem",
74 /* Other */
75 [OP_PHI] = "phi",
76 [OP_PHISOURCE] = "phisrc",
77 [OP_CAST] = "cast",
78 [OP_PTRCAST] = "ptrcast",
79 [OP_CALL] = "call",
80 [OP_VANEXT] = "va_next",
81 [OP_VAARG] = "va_arg",
82 [OP_SLICE] = "slice",
83 [OP_SNOP] = "snop",
84 [OP_LNOP] = "lnop",
85 [OP_NOP] = "nop",
86 [OP_DEATHNOTE] = "dead",
87 [OP_ASM] = "asm",
89 /* Sparse tagging (line numbers, context, whatever) */
90 [OP_CONTEXT] = "context",
93 static int last_reg, stack_offset;
95 struct hardreg {
96 const char *name;
97 struct pseudo_list *contains;
98 unsigned busy:16,
99 dead:8,
100 used:1;
103 #define TAG_DEAD 1
104 #define TAG_DIRTY 2
106 /* Our "switch" generation is very very stupid. */
107 #define SWITCH_REG (1)
109 static void output_bb(struct basic_block *bb, unsigned long generation);
112 * We only know about the caller-clobbered registers
113 * right now.
115 static struct hardreg hardregs[] = {
116 { .name = "%eax" },
117 { .name = "%edx" },
118 { .name = "%ecx" },
119 { .name = "%ebx" },
120 { .name = "%esi" },
121 { .name = "%edi" },
123 #define REGNO (sizeof(hardregs)/sizeof(struct hardreg))
125 struct bb_state {
126 struct position pos;
127 struct storage_hash_list *inputs;
128 struct storage_hash_list *outputs;
129 struct storage_hash_list *internal;
131 /* CC cache.. */
132 int cc_opcode, cc_dead;
133 pseudo_t cc_target;
136 static struct storage_hash *find_storage_hash(pseudo_t pseudo, struct storage_hash_list *list)
138 struct storage_hash *entry;
139 FOR_EACH_PTR(list, entry) {
140 if (entry->pseudo == pseudo)
141 return entry;
142 } END_FOR_EACH_PTR(entry);
143 return NULL;
146 static struct storage_hash *find_or_create_hash(pseudo_t pseudo, struct storage_hash_list **listp)
148 struct storage_hash *entry;
150 entry = find_storage_hash(pseudo, *listp);
151 if (!entry) {
152 entry = alloc_storage_hash(alloc_storage());
153 entry->pseudo = pseudo;
154 add_ptr_list(listp, entry);
156 return entry;
159 /* Eventually we should just build it up in memory */
160 static void output_line(struct bb_state *state, const char *fmt, ...)
162 va_list args;
164 va_start(args, fmt);
165 vprintf(fmt, args);
166 va_end(args);
169 static void output_label(struct bb_state *state, const char *fmt, ...)
171 static char buffer[512];
172 va_list args;
174 va_start(args, fmt);
175 vsnprintf(buffer, sizeof(buffer), fmt, args);
176 va_end(args);
178 output_line(state, "%s:\n", buffer);
181 static void output_insn(struct bb_state *state, const char *fmt, ...)
183 static char buffer[512];
184 va_list args;
186 va_start(args, fmt);
187 vsnprintf(buffer, sizeof(buffer), fmt, args);
188 va_end(args);
190 output_line(state, "\t%s\n", buffer);
193 static void output_comment(struct bb_state *state, const char *fmt, ...)
195 static char buffer[512];
196 va_list args;
198 if (!verbose)
199 return;
200 va_start(args, fmt);
201 vsnprintf(buffer, sizeof(buffer), fmt, args);
202 va_end(args);
204 output_line(state, "\t# %s\n", buffer);
207 static const char *show_memop(struct storage *storage)
209 static char buffer[1000];
211 if (!storage)
212 return "undef";
213 switch (storage->type) {
214 case REG_FRAME:
215 sprintf(buffer, "%d(FP)", storage->offset);
216 break;
217 case REG_STACK:
218 sprintf(buffer, "%d(SP)", storage->offset);
219 break;
220 case REG_REG:
221 return hardregs[storage->regno].name;
222 default:
223 return show_storage(storage);
225 return buffer;
228 static void alloc_stack(struct bb_state *state, struct storage *storage)
230 storage->type = REG_STACK;
231 storage->offset = stack_offset;
232 stack_offset += 4;
236 * Can we re-generate the pseudo, so that we don't need to
237 * flush it to memory? We can regenerate:
238 * - immediates and symbol addresses
239 * - pseudos we got as input in non-registers
240 * - pseudos we've already saved off earlier..
242 static int can_regenerate(struct bb_state *state, pseudo_t pseudo)
244 struct storage_hash *in;
246 switch (pseudo->type) {
247 case PSEUDO_VAL:
248 case PSEUDO_SYM:
249 return 1;
251 default:
252 in = find_storage_hash(pseudo, state->inputs);
253 if (in && in->storage->type != REG_REG)
254 return 1;
255 in = find_storage_hash(pseudo, state->internal);
256 if (in)
257 return 1;
259 return 0;
262 static void flush_one_pseudo(struct bb_state *state, struct hardreg *hardreg, pseudo_t pseudo)
264 struct storage_hash *out;
265 struct storage *storage;
267 if (can_regenerate(state, pseudo))
268 return;
270 output_comment(state, "flushing %s from %s", show_pseudo(pseudo), hardreg->name);
271 out = find_storage_hash(pseudo, state->internal);
272 if (!out) {
273 out = find_storage_hash(pseudo, state->outputs);
274 if (!out)
275 out = find_or_create_hash(pseudo, &state->internal);
277 storage = out->storage;
278 switch (storage->type) {
279 default:
281 * Aieee - the next user wants it in a register, but we
282 * need to flush it to memory in between. Which means that
283 * we need to allocate an internal one, dammit..
285 out = find_or_create_hash(pseudo, &state->internal);
286 storage = out->storage;
287 /* Fall through */
288 case REG_UDEF:
289 alloc_stack(state, storage);
290 /* Fall through */
291 case REG_STACK:
292 output_insn(state, "movl %s,%s", hardreg->name, show_memop(storage));
293 break;
297 /* Flush a hardreg out to the storage it has.. */
298 static void flush_reg(struct bb_state *state, struct hardreg *hardreg)
300 pseudo_t pseudo;
302 if (!hardreg->busy)
303 return;
304 hardreg->busy = 0;
305 hardreg->dead = 0;
306 hardreg->used = 1;
307 FOR_EACH_PTR(hardreg->contains, pseudo) {
308 if (CURRENT_TAG(pseudo) & TAG_DEAD)
309 continue;
310 if (!(CURRENT_TAG(pseudo) & TAG_DIRTY))
311 continue;
312 flush_one_pseudo(state, hardreg, pseudo);
313 } END_FOR_EACH_PTR(pseudo);
314 free_ptr_list(&hardreg->contains);
317 static struct storage_hash *find_pseudo_storage(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
319 struct storage_hash *src;
321 src = find_storage_hash(pseudo, state->internal);
322 if (!src) {
323 src = find_storage_hash(pseudo, state->inputs);
324 if (!src) {
325 src = find_storage_hash(pseudo, state->outputs);
326 /* Undefined? Screw it! */
327 if (!src)
328 return NULL;
331 * If we found output storage, it had better be local stack
332 * that we flushed to earlier..
334 if (src->storage->type != REG_STACK)
335 return NULL;
340 * Incoming pseudo with out any pre-set storage allocation?
341 * We can make up our own, and obviously prefer to get it
342 * in the register we already selected (if it hasn't been
343 * used yet).
345 if (src->storage->type == REG_UDEF) {
346 if (reg && !reg->used) {
347 src->storage->type = REG_REG;
348 src->storage->regno = reg - hardregs;
349 return NULL;
351 alloc_stack(state, src->storage);
353 return src;
356 static void mark_reg_dead(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
358 pseudo_t p;
360 FOR_EACH_PTR(reg->contains, p) {
361 if (p != pseudo)
362 continue;
363 if (CURRENT_TAG(p) & TAG_DEAD)
364 continue;
365 output_comment(state, "marking pseudo %s in reg %s dead", show_pseudo(pseudo), reg->name);
366 TAG_CURRENT(p, TAG_DEAD);
367 reg->dead++;
368 } END_FOR_EACH_PTR(p);
371 static void add_pseudo_reg(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
373 output_comment(state, "added pseudo %s to reg %s", show_pseudo(pseudo), reg->name);
374 add_ptr_list_tag(&reg->contains, pseudo, TAG_DIRTY);
375 reg->busy++;
378 static struct hardreg *preferred_reg(struct bb_state *state, pseudo_t target)
380 struct storage_hash *dst;
382 dst = find_storage_hash(target, state->outputs);
383 if (dst) {
384 struct storage *storage = dst->storage;
385 if (storage->type == REG_REG)
386 return hardregs + storage->regno;
388 return NULL;
391 static struct hardreg *empty_reg(struct bb_state *state)
393 int i;
394 struct hardreg *reg = hardregs;
396 for (i = 0; i < REGNO; i++, reg++) {
397 if (!reg->busy)
398 return reg;
400 return NULL;
403 static struct hardreg *target_reg(struct bb_state *state, pseudo_t pseudo, pseudo_t target)
405 int i;
406 struct hardreg *reg;
408 /* First, see if we have a preferred target register.. */
409 reg = preferred_reg(state, target);
410 if (reg && !reg->busy)
411 goto found;
413 reg = empty_reg(state);
414 if (reg)
415 goto found;
417 i = last_reg+1;
418 if (i >= REGNO)
419 i = 0;
420 last_reg = i;
421 reg = hardregs + i;
422 flush_reg(state, reg);
424 found:
425 add_pseudo_reg(state, pseudo, reg);
426 return reg;
429 static struct hardreg *find_in_reg(struct bb_state *state, pseudo_t pseudo)
431 int i;
432 struct hardreg *reg;
434 for (i = 0; i < REGNO; i++) {
435 pseudo_t p;
437 reg = hardregs + i;
438 FOR_EACH_PTR(reg->contains, p) {
439 if (p == pseudo) {
440 last_reg = i;
441 output_comment(state, "found pseudo %s in reg %s", show_pseudo(pseudo), reg->name);
442 return reg;
444 } END_FOR_EACH_PTR(p);
446 return NULL;
449 static void flush_cc_cache_to_reg(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
451 int opcode = state->cc_opcode;
453 state->cc_opcode = 0;
454 state->cc_target = NULL;
455 output_insn(state, "%s %s", opcodes[opcode], reg->name);
458 static void flush_cc_cache(struct bb_state *state)
460 pseudo_t pseudo = state->cc_target;
462 if (pseudo) {
463 struct hardreg *dst;
465 state->cc_target = NULL;
467 if (!state->cc_dead) {
468 dst = target_reg(state, pseudo, pseudo);
469 flush_cc_cache_to_reg(state, pseudo, dst);
474 static void add_cc_cache(struct bb_state *state, int opcode, pseudo_t pseudo)
476 assert(!state->cc_target);
477 state->cc_target = pseudo;
478 state->cc_opcode = opcode;
479 state->cc_dead = 0;
480 output_comment(state, "caching %s", opcodes[opcode]);
483 /* Fill a hardreg with the pseudo it has */
484 static struct hardreg *fill_reg(struct bb_state *state, struct hardreg *hardreg, pseudo_t pseudo)
486 struct storage_hash *src;
487 struct instruction *def;
489 if (state->cc_target == pseudo) {
490 flush_cc_cache_to_reg(state, pseudo, hardreg);
491 return hardreg;
494 switch (pseudo->type) {
495 case PSEUDO_VAL:
496 output_insn(state, "movl $%lld,%s", pseudo->value, hardreg->name);
497 break;
498 case PSEUDO_SYM:
499 output_insn(state, "movl $<%s>,%s", show_pseudo(pseudo), hardreg->name);
500 break;
501 case PSEUDO_ARG:
502 case PSEUDO_REG:
503 def = pseudo->def;
504 if (def->opcode == OP_SETVAL) {
505 output_insn(state, "movl $<%s>,%s", show_pseudo(def->target), hardreg->name);
506 break;
508 src = find_pseudo_storage(state, pseudo, hardreg);
509 if (!src)
510 break;
511 if (src->flags & TAG_DEAD)
512 mark_reg_dead(state, pseudo, hardreg);
513 output_insn(state, "mov.%d %s,%s", 32, show_memop(src->storage), hardreg->name);
514 break;
515 default:
516 output_insn(state, "reload %s from %s", hardreg->name, show_pseudo(pseudo));
517 break;
519 return hardreg;
522 static struct hardreg *getreg(struct bb_state *state, pseudo_t pseudo, pseudo_t target)
524 struct hardreg *reg;
526 reg = find_in_reg(state, pseudo);
527 if (reg)
528 return reg;
529 reg = target_reg(state, pseudo, target);
530 return fill_reg(state, reg, pseudo);
533 static void move_reg(struct bb_state *state, struct hardreg *src, struct hardreg *dst)
535 output_insn(state, "movl %s,%s", src->name, dst->name);
538 static struct hardreg *copy_reg(struct bb_state *state, struct hardreg *src, pseudo_t target)
540 int i;
541 struct hardreg *reg;
543 if (!src->busy)
544 return src;
546 reg = preferred_reg(state, target);
547 if (reg && !reg->busy) {
548 output_comment(state, "copying %s to preferred target %s", show_pseudo(target), reg->name);
549 move_reg(state, src, reg);
550 return reg;
553 for (i = 0; i < REGNO; i++) {
554 struct hardreg *reg = hardregs + i;
555 if (!reg->busy) {
556 output_comment(state, "copying %s to %s", show_pseudo(target), reg->name);
557 output_insn(state, "movl %s,%s", src->name, reg->name);
558 return reg;
562 flush_reg(state, src);
563 return src;
566 static const char *generic(struct bb_state *state, pseudo_t pseudo)
568 struct hardreg *reg;
569 struct storage_hash *src;
571 switch (pseudo->type) {
572 case PSEUDO_SYM:
573 case PSEUDO_VAL:
574 return show_pseudo(pseudo);
575 default:
576 reg = find_in_reg(state, pseudo);
577 if (reg)
578 return reg->name;
579 src = find_pseudo_storage(state, pseudo, NULL);
580 if (!src)
581 return "undef";
582 return show_memop(src->storage);
586 static const char *address(struct bb_state *state, struct instruction *memop)
588 struct symbol *sym;
589 struct hardreg *base;
590 static char buffer[100];
591 pseudo_t addr = memop->src;
593 switch(addr->type) {
594 case PSEUDO_SYM:
595 sym = addr->sym;
596 if (sym->ctype.modifiers & MOD_NONLOCAL) {
597 sprintf(buffer, "%s+%d", show_ident(sym->ident), memop->offset);
598 return buffer;
600 sprintf(buffer, "%d+%s(SP)", memop->offset, show_ident(sym->ident));
601 return buffer;
602 default:
603 base = getreg(state, addr, NULL);
604 sprintf(buffer, "%d(%s)", memop->offset, base->name);
605 return buffer;
609 static const char *reg_or_imm(struct bb_state *state, pseudo_t pseudo)
611 switch(pseudo->type) {
612 case PSEUDO_VAL:
613 return show_pseudo(pseudo);
614 default:
615 return getreg(state, pseudo, NULL)->name;
619 static void kill_dead_reg(struct hardreg *reg)
621 if (reg->dead) {
622 pseudo_t p;
624 FOR_EACH_PTR(reg->contains, p) {
625 if (CURRENT_TAG(p) & TAG_DEAD) {
626 DELETE_CURRENT_PTR(p);
627 reg->busy--;
628 reg->dead--;
630 } END_FOR_EACH_PTR(p);
631 PACK_PTR_LIST(&reg->contains);
632 assert(!reg->dead);
636 static struct hardreg *target_copy_reg(struct bb_state *state, struct hardreg *src, pseudo_t target)
638 kill_dead_reg(src);
639 return copy_reg(state, src, target);
642 static void do_binop(struct bb_state *state, struct instruction *insn, pseudo_t val1, pseudo_t val2)
644 const char *op = opcodes[insn->opcode];
645 struct hardreg *src = getreg(state, val1, insn->target);
646 const char *src2 = generic(state, val2);
647 struct hardreg *dst;
649 dst = target_copy_reg(state, src, insn->target);
650 output_insn(state, "%s.%d %s,%s", op, insn->size, src2, dst->name);
651 add_pseudo_reg(state, insn->target, dst);
654 static void generate_binop(struct bb_state *state, struct instruction *insn)
656 flush_cc_cache(state);
657 do_binop(state, insn, insn->src1, insn->src2);
660 static int is_dead_reg(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
662 pseudo_t p;
663 FOR_EACH_PTR(reg->contains, p) {
664 if (p == pseudo)
665 return CURRENT_TAG(p) & TAG_DEAD;
666 } END_FOR_EACH_PTR(p);
667 return 0;
671 * Commutative binops are much more flexible, since we can switch the
672 * sources around to satisfy the target register, or to avoid having
673 * to load one of them into a register..
675 static void generate_commutative_binop(struct bb_state *state, struct instruction *insn)
677 pseudo_t src1, src2;
678 struct hardreg *reg1, *reg2;
680 flush_cc_cache(state);
681 src1 = insn->src1;
682 src2 = insn->src2;
683 reg2 = find_in_reg(state, src2);
684 if (!reg2)
685 goto dont_switch;
686 reg1 = find_in_reg(state, src1);
687 if (!reg1)
688 goto do_switch;
689 if (!is_dead_reg(state, src2, reg2))
690 goto dont_switch;
691 if (!is_dead_reg(state, src1, reg1))
692 goto do_switch;
694 /* Both are dead. Is one preferrable? */
695 if (reg2 != preferred_reg(state, insn->target))
696 goto dont_switch;
698 do_switch:
699 src1 = src2;
700 src2 = insn->src1;
701 dont_switch:
702 do_binop(state, insn, src1, src2);
706 * This marks a pseudo dead. It still stays on the hardreg list (the hardreg
707 * still has its value), but it's scheduled to be killed after the next
708 * "sequence point" when we call "kill_read_pseudos()"
710 static void mark_pseudo_dead(struct bb_state *state, pseudo_t pseudo)
712 int i;
713 struct storage_hash *src;
715 if (state->cc_target == pseudo)
716 state->cc_dead = 1;
717 src = find_pseudo_storage(state, pseudo, NULL);
718 if (src)
719 src->flags |= TAG_DEAD;
720 for (i = 0; i < REGNO; i++)
721 mark_reg_dead(state, pseudo, hardregs + i);
724 static void kill_dead_pseudos(struct bb_state *state)
726 int i;
728 for (i = 0; i < REGNO; i++) {
729 kill_dead_reg(hardregs + i);
734 * A PHI source can define a pseudo that we already
735 * have in another register. We need to invalidate the
736 * old register so that we don't end up with the same
737 * pseudo in "two places".
739 static void remove_pseudo_reg(struct bb_state *state, pseudo_t pseudo)
741 int i;
743 output_comment(state, "pseudo %s died", show_pseudo(pseudo));
744 for (i = 0; i < REGNO; i++) {
745 struct hardreg *reg = hardregs + i;
746 pseudo_t p;
747 FOR_EACH_PTR(reg->contains, p) {
748 if (p != pseudo)
749 continue;
750 if (CURRENT_TAG(p) & TAG_DEAD)
751 reg->dead--;
752 reg->busy--;
753 DELETE_CURRENT_PTR(p);
754 output_comment(state, "removed pseudo %s from reg %s", show_pseudo(pseudo), reg->name);
755 } END_FOR_EACH_PTR(p);
756 PACK_PTR_LIST(&reg->contains);
760 static void generate_store(struct instruction *insn, struct bb_state *state)
762 output_insn(state, "mov.%d %s,%s", insn->size, reg_or_imm(state, insn->target), address(state, insn));
765 static void generate_load(struct instruction *insn, struct bb_state *state)
767 const char *input = address(state, insn);
768 struct hardreg *dst;
770 kill_dead_pseudos(state);
771 dst = target_reg(state, insn->target, NULL);
772 output_insn(state, "mov.%d %s,%s", insn->size, input, dst->name);
775 static void generate_phisource(struct instruction *insn, struct bb_state *state)
777 struct instruction *user;
778 struct hardreg *reg;
780 /* Mark all the target pseudos dead first */
781 FOR_EACH_PTR(insn->phi_users, user) {
782 mark_pseudo_dead(state, user->target);
783 } END_FOR_EACH_PTR(user);
785 reg = NULL;
786 FOR_EACH_PTR(insn->phi_users, user) {
787 if (!reg)
788 reg = getreg(state, insn->phi_src, user->target);
789 remove_pseudo_reg(state, user->target);
790 add_pseudo_reg(state, user->target, reg);
791 } END_FOR_EACH_PTR(user);
794 static void generate_cast(struct bb_state *state, struct instruction *insn)
796 struct hardreg *src = getreg(state, insn->src, insn->target);
797 struct hardreg *dst;
798 unsigned int old = insn->orig_type ? insn->orig_type->bit_size : 0;
799 unsigned int new = insn->size;
802 * Cast to smaller type? Ignore the high bits, we
803 * just keep both pseudos in the same register.
805 if (old >= new) {
806 add_pseudo_reg(state, insn->target, src);
807 return;
810 dst = target_copy_reg(state, src, insn->target);
812 if (insn->orig_type && (insn->orig_type->ctype.modifiers & MOD_SIGNED)) {
813 output_insn(state, "sext.%d.%d %s", old, new, dst->name);
814 } else {
815 unsigned long long mask;
816 mask = ~(~0ULL << old);
817 mask &= ~(~0ULL << new);
818 output_insn(state, "andl.%d $%#llx,%s", insn->size, mask, dst->name);
820 add_pseudo_reg(state, insn->target, dst);
823 static void generate_output_storage(struct bb_state *state);
825 static const char *conditional[] = {
826 [OP_SET_EQ] = "e",
827 [OP_SET_NE] = "ne",
828 [OP_SET_LE] = "le",
829 [OP_SET_GE] = "ge",
830 [OP_SET_LT] = "lt",
831 [OP_SET_GT] = "gt",
832 [OP_SET_B] = "b",
833 [OP_SET_A] = "a",
834 [OP_SET_BE] = "be",
835 [OP_SET_AE] = "ae"
839 static void generate_branch(struct bb_state *state, struct instruction *br)
841 const char *cond = "XXX";
842 struct basic_block *target;
844 if (br->cond) {
845 if (state->cc_target == br->cond) {
846 cond = conditional[state->cc_opcode];
847 } else {
848 struct hardreg *reg = getreg(state, br->cond, NULL);
849 output_insn(state, "testl %s,%s", reg->name, reg->name);
850 cond = "ne";
853 generate_output_storage(state);
854 target = br->bb_true;
855 if (br->cond) {
856 output_insn(state, "j%s .L%p", cond, target);
857 target = br->bb_false;
859 output_insn(state, "jmp .L%p", target);
862 /* We've made sure that there is a dummy reg live for the output */
863 static void generate_switch(struct bb_state *state, struct instruction *insn)
865 struct hardreg *reg = hardregs + SWITCH_REG;
867 generate_output_storage(state);
868 output_insn(state, "switch on %s", reg->name);
869 output_insn(state, "unimplemented: %s", show_instruction(insn));
872 static void generate_ret(struct bb_state *state, struct instruction *ret)
874 if (ret->src && ret->src != VOID) {
875 struct hardreg *wants = hardregs+0;
876 struct hardreg *reg = getreg(state, ret->src, NULL);
877 if (reg != wants)
878 output_insn(state, "movl %s,%s", reg->name, wants->name);
880 output_insn(state, "ret");
884 * Fake "call" linearization just as a taster..
886 static void generate_call(struct bb_state *state, struct instruction *insn)
888 int offset = 0;
889 pseudo_t arg;
891 FOR_EACH_PTR(insn->arguments, arg) {
892 output_insn(state, "pushl %s", generic(state, arg));
893 offset += 4;
894 } END_FOR_EACH_PTR(arg);
895 flush_reg(state, hardregs+0);
896 flush_reg(state, hardregs+1);
897 flush_reg(state, hardregs+2);
898 output_insn(state, "call %s", show_pseudo(insn->func));
899 if (offset)
900 output_insn(state, "addl $%d,%%esp", offset);
901 add_pseudo_reg(state, insn->target, hardregs+0);
904 static void generate_select(struct bb_state *state, struct instruction *insn)
906 const char *cond;
907 struct hardreg *src1, *src2, *dst;
909 src1 = getreg(state, insn->src2, NULL);
910 dst = copy_reg(state, src1, insn->target);
911 add_pseudo_reg(state, insn->target, dst);
912 src2 = getreg(state, insn->src3, insn->target);
914 if (state->cc_target == insn->src1) {
915 cond = conditional[state->cc_opcode];
916 } else {
917 struct hardreg *reg = getreg(state, insn->src1, NULL);
918 output_insn(state, "testl %s,%s", reg->name, reg->name);
919 cond = "ne";
922 output_insn(state, "sel%s %s,%s", cond, src2->name, dst->name);
925 struct asm_arg {
926 const struct ident *name;
927 const char *value;
928 pseudo_t pseudo;
929 struct hardreg *reg;
932 static void replace_asm_arg(char **dst_p, struct asm_arg *arg)
934 char *dst = *dst_p;
935 int len = strlen(arg->value);
937 memcpy(dst, arg->value, len);
938 *dst_p = dst + len;
941 static void replace_asm_percent(const char **src_p, char **dst_p, struct asm_arg *args, int nr)
943 const char *src = *src_p;
944 char c;
945 int index;
947 c = *src++;
948 switch (c) {
949 case '0' ... '9':
950 index = c - '0';
951 if (index < nr)
952 replace_asm_arg(dst_p, args+index);
953 break;
955 *src_p = src;
956 return;
959 static void replace_asm_named(const char **src_p, char **dst_p, struct asm_arg *args, int nr)
961 const char *src = *src_p;
962 const char *end = src;
964 for(;;) {
965 char c = *end++;
966 if (!c)
967 return;
968 if (c == ']') {
969 int i;
971 *src_p = end;
972 for (i = 0; i < nr; i++) {
973 const struct ident *ident = args[i].name;
974 int len;
975 if (!ident)
976 continue;
977 len = ident->len;
978 if (memcmp(src, ident->name, len))
979 continue;
980 replace_asm_arg(dst_p, args+i);
981 return;
987 static const char *replace_asm_args(const char *str, struct asm_arg *args, int nr)
989 static char buffer[1000];
990 char *p = buffer;
992 for (;;) {
993 char c = *str;
994 *p = c;
995 if (!c)
996 return buffer;
997 str++;
998 switch (c) {
999 case '%':
1000 if (*str == '%') {
1001 str++;
1002 p++;
1003 continue;
1005 replace_asm_percent(&str, &p, args, nr);
1006 continue;
1007 case '[':
1008 replace_asm_named(&str, &p, args, nr);
1009 continue;
1010 default:
1011 break;
1013 p++;
1017 #define MAX_ASM_ARG (50)
1018 static struct asm_arg asm_arguments[MAX_ASM_ARG];
1020 static struct asm_arg *generate_asm_inputs(struct bb_state *state, struct asm_constraint_list *list, struct asm_arg *arg)
1022 struct asm_constraint *entry;
1024 FOR_EACH_PTR(list, entry) {
1025 const char *constraint = entry->constraint;
1026 pseudo_t pseudo = entry->pseudo;
1027 struct hardreg *reg, *orig;
1028 const char *string;
1029 int index;
1031 string = "undef";
1032 switch (*constraint) {
1033 case 'r':
1034 string = getreg(state, pseudo, NULL)->name;
1035 break;
1036 case '0' ... '9':
1037 index = *constraint - '0';
1038 reg = asm_arguments[index].reg;
1039 orig = find_in_reg(state, pseudo);
1040 if (orig)
1041 move_reg(state, orig, reg);
1042 else
1043 fill_reg(state, reg, pseudo);
1044 string = reg->name;
1045 break;
1046 default:
1047 string = generic(state, pseudo);
1048 break;
1051 output_insn(state, "# asm input \"%s\": %s : %s", constraint, show_pseudo(pseudo), string);
1053 arg->name = entry->ident;
1054 arg->value = string;
1055 arg->pseudo = NULL;
1056 arg->reg = NULL;
1057 arg++;
1058 } END_FOR_EACH_PTR(entry);
1059 return arg;
1062 static struct asm_arg *generate_asm_outputs(struct bb_state *state, struct asm_constraint_list *list, struct asm_arg *arg)
1064 struct asm_constraint *entry;
1066 FOR_EACH_PTR(list, entry) {
1067 const char *constraint = entry->constraint;
1068 pseudo_t pseudo = entry->pseudo;
1069 struct hardreg *reg;
1070 const char *string;
1072 while (*constraint == '=' || *constraint == '+')
1073 constraint++;
1075 string = "undef";
1076 switch (*constraint) {
1077 case 'r':
1078 default:
1079 reg = target_reg(state, pseudo, NULL);
1080 arg->pseudo = pseudo;
1081 arg->reg = reg;
1082 string = reg->name;
1083 break;
1086 output_insn(state, "# asm output \"%s\": %s : %s", constraint, show_pseudo(pseudo), string);
1088 arg->name = entry->ident;
1089 arg->value = string;
1090 arg++;
1091 } END_FOR_EACH_PTR(entry);
1092 return arg;
1095 static void generate_asm(struct bb_state *state, struct instruction *insn)
1097 const char *str = insn->string;
1099 if (insn->asm_rules->outputs || insn->asm_rules->inputs) {
1100 struct asm_arg *arg;
1102 arg = generate_asm_outputs(state, insn->asm_rules->outputs, asm_arguments);
1103 arg = generate_asm_inputs(state, insn->asm_rules->inputs, arg);
1104 str = replace_asm_args(str, asm_arguments, arg - asm_arguments);
1106 output_insn(state, "%s", str);
1109 static void generate_compare(struct bb_state *state, struct instruction *insn)
1111 struct hardreg *src;
1112 const char *src2;
1113 int opcode;
1115 flush_cc_cache(state);
1116 opcode = insn->opcode;
1119 * We should try to switch these around if necessary,
1120 * and update the opcode to match..
1122 src = getreg(state, insn->src1, insn->target);
1123 src2 = generic(state, insn->src2);
1125 output_insn(state, "cmp.%d %s,%s", insn->size, src2, src->name);
1127 add_cc_cache(state, opcode, insn->target);
1130 static void generate_one_insn(struct instruction *insn, struct bb_state *state)
1132 if (verbose)
1133 output_comment(state, "%s", show_instruction(insn));
1135 switch (insn->opcode) {
1136 case OP_ENTRY: {
1137 struct symbol *sym = insn->bb->ep->name;
1138 const char *name = show_ident(sym->ident);
1139 if (sym->ctype.modifiers & MOD_STATIC)
1140 printf("\n\n%s:\n", name);
1141 else
1142 printf("\n\n.globl %s\n%s:\n", name, name);
1143 break;
1147 * OP_PHI doesn't actually generate any code. It has been
1148 * done by the storage allocator and the OP_PHISOURCE.
1150 case OP_PHI:
1151 break;
1153 case OP_PHISOURCE:
1154 generate_phisource(insn, state);
1155 break;
1158 * OP_SETVAL likewise doesn't actually generate any
1159 * code. On use, the "def" of the pseudo will be
1160 * looked up.
1162 case OP_SETVAL:
1163 break;
1165 case OP_STORE:
1166 generate_store(insn, state);
1167 break;
1169 case OP_LOAD:
1170 generate_load(insn, state);
1171 break;
1173 case OP_DEATHNOTE:
1174 mark_pseudo_dead(state, insn->target);
1175 return;
1177 case OP_ADD: case OP_MUL:
1178 case OP_AND: case OP_OR: case OP_XOR:
1179 case OP_AND_BOOL: case OP_OR_BOOL:
1180 generate_commutative_binop(state, insn);
1181 break;
1183 case OP_SUB: case OP_DIV: case OP_MOD:
1184 case OP_SHL: case OP_SHR:
1185 generate_binop(state, insn);
1186 break;
1188 case OP_BINCMP ... OP_BINCMP_END:
1189 generate_compare(state, insn);
1190 break;
1192 case OP_CAST: case OP_PTRCAST:
1193 generate_cast(state, insn);
1194 break;
1196 case OP_SEL:
1197 generate_select(state, insn);
1198 break;
1200 case OP_BR:
1201 generate_branch(state, insn);
1202 break;
1204 case OP_SWITCH:
1205 generate_switch(state, insn);
1206 break;
1208 case OP_CALL:
1209 generate_call(state, insn);
1210 break;
1212 case OP_RET:
1213 generate_ret(state, insn);
1214 break;
1216 case OP_ASM:
1217 generate_asm(state, insn);
1218 break;
1220 default:
1221 output_insn(state, "unimplemented: %s", show_instruction(insn));
1222 break;
1224 kill_dead_pseudos(state);
1227 #define VERY_BUSY 1000
1228 #define REG_FIXED 2000
1230 static void write_reg_to_storage(struct bb_state *state, struct hardreg *reg, pseudo_t pseudo, struct storage *storage)
1232 int i;
1233 struct hardreg *out;
1235 switch (storage->type) {
1236 case REG_REG:
1237 out = hardregs + storage->regno;
1238 if (reg == out)
1239 return;
1240 output_insn(state, "movl %s,%s", reg->name, out->name);
1241 return;
1242 case REG_UDEF:
1243 if (reg->busy < VERY_BUSY) {
1244 storage->type = REG_REG;
1245 storage->regno = reg - hardregs;
1246 reg->busy = REG_FIXED;
1247 return;
1250 /* Try to find a non-busy register.. */
1251 for (i = 0; i < REGNO; i++) {
1252 out = hardregs + i;
1253 if (out->busy)
1254 continue;
1255 output_insn(state, "movl %s,%s", reg->name, out->name);
1256 storage->type = REG_REG;
1257 storage->regno = i;
1258 out->busy = REG_FIXED;
1259 return;
1262 /* Fall back on stack allocation ... */
1263 alloc_stack(state, storage);
1264 /* Fallthroigh */
1265 default:
1266 output_insn(state, "movl %s,%s", reg->name, show_memop(storage));
1267 return;
1271 static void write_val_to_storage(struct bb_state *state, pseudo_t src, struct storage *storage)
1273 struct hardreg *out;
1275 switch (storage->type) {
1276 case REG_UDEF:
1277 alloc_stack(state, storage);
1278 default:
1279 output_insn(state, "movl %s,%s", show_pseudo(src), show_memop(storage));
1280 break;
1281 case REG_REG:
1282 out = hardregs + storage->regno;
1283 output_insn(state, "movl %s,%s", show_pseudo(src), out->name);
1287 static void fill_output(struct bb_state *state, pseudo_t pseudo, struct storage *out)
1289 int i;
1290 struct storage_hash *in;
1291 struct instruction *def;
1293 /* Is that pseudo a constant value? */
1294 switch (pseudo->type) {
1295 case PSEUDO_VAL:
1296 write_val_to_storage(state, pseudo, out);
1297 return;
1298 case PSEUDO_REG:
1299 def = pseudo->def;
1300 if (def->opcode == OP_SETVAL) {
1301 write_val_to_storage(state, pseudo, out);
1302 return;
1304 default:
1305 break;
1308 /* See if we have that pseudo in a register.. */
1309 for (i = 0; i < REGNO; i++) {
1310 struct hardreg *reg = hardregs + i;
1311 pseudo_t p;
1313 FOR_EACH_PTR(reg->contains, p) {
1314 if (p == pseudo) {
1315 write_reg_to_storage(state, reg, pseudo, out);
1316 return;
1318 } END_FOR_EACH_PTR(p);
1321 /* Do we have it in another storage? */
1322 in = find_storage_hash(pseudo, state->internal);
1323 if (!in) {
1324 in = find_storage_hash(pseudo, state->inputs);
1325 /* Undefined? */
1326 if (!in)
1327 return;
1329 switch (out->type) {
1330 case REG_UDEF:
1331 *out = *in->storage;
1332 break;
1333 case REG_REG:
1334 output_insn(state, "movl %s,%s", show_memop(in->storage), hardregs[out->regno].name);
1335 break;
1336 default:
1337 if (out == in->storage)
1338 break;
1339 if (out->type == in->storage->type == out->regno == in->storage->regno)
1340 break;
1341 output_insn(state, "movl %s,%s", show_memop(in->storage), show_memop(out));
1342 break;
1344 return;
1347 static int final_pseudo_flush(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
1349 struct storage_hash *hash;
1350 struct storage *out;
1351 struct hardreg *dst;
1354 * Since this pseudo is live at exit, we'd better have output
1355 * storage for it..
1357 hash = find_storage_hash(pseudo, state->outputs);
1358 if (!hash)
1359 return 1;
1360 out = hash->storage;
1362 /* If the output is in a register, try to get it there.. */
1363 if (out->type == REG_REG) {
1364 dst = hardregs + out->regno;
1366 * Two good cases: nobody is using the right register,
1367 * or we've already set it aside for output..
1369 if (!dst->busy || dst->busy > VERY_BUSY)
1370 goto copy_to_dst;
1372 /* Aiee. Try to keep it in a register.. */
1373 dst = empty_reg(state);
1374 if (dst)
1375 goto copy_to_dst;
1377 return 0;
1380 /* If the output is undefined, let's see if we can put it in a register.. */
1381 if (out->type == REG_UDEF) {
1382 dst = empty_reg(state);
1383 if (dst) {
1384 out->type = REG_REG;
1385 out->regno = dst - hardregs;
1386 goto copy_to_dst;
1388 /* Uhhuh. Not so good. No empty registers right now */
1389 return 0;
1392 /* If we know we need to flush it, just do so already .. */
1393 output_insn(state, "movl %s,%s", reg->name, show_memop(out));
1394 return 1;
1396 copy_to_dst:
1397 if (reg == dst)
1398 return 1;
1399 output_insn(state, "movl %s,%s", reg->name, dst->name);
1400 add_pseudo_reg(state, pseudo, dst);
1401 return 1;
1405 * This tries to make sure that we put all the pseudos that are
1406 * live on exit into the proper storage
1408 static void generate_output_storage(struct bb_state *state)
1410 struct storage_hash *entry;
1412 /* Go through the fixed outputs, making sure we have those regs free */
1413 FOR_EACH_PTR(state->outputs, entry) {
1414 struct storage *out = entry->storage;
1415 if (out->type == REG_REG) {
1416 struct hardreg *reg = hardregs + out->regno;
1417 pseudo_t p;
1418 int flushme = 0;
1420 reg->busy = REG_FIXED;
1421 FOR_EACH_PTR(reg->contains, p) {
1422 if (p == entry->pseudo) {
1423 flushme = -100;
1424 continue;
1426 if (CURRENT_TAG(p) & TAG_DEAD)
1427 continue;
1429 /* Try to write back the pseudo to where it should go ... */
1430 if (final_pseudo_flush(state, p, reg)) {
1431 DELETE_CURRENT_PTR(p);
1432 reg->busy--;
1433 continue;
1435 flushme++;
1436 } END_FOR_EACH_PTR(p);
1437 PACK_PTR_LIST(&reg->contains);
1438 if (flushme > 0)
1439 flush_reg(state, reg);
1441 } END_FOR_EACH_PTR(entry);
1443 FOR_EACH_PTR(state->outputs, entry) {
1444 fill_output(state, entry->pseudo, entry->storage);
1445 } END_FOR_EACH_PTR(entry);
1448 static void generate(struct basic_block *bb, struct bb_state *state)
1450 int i;
1451 struct storage_hash *entry;
1452 struct instruction *insn;
1454 for (i = 0; i < REGNO; i++) {
1455 free_ptr_list(&hardregs[i].contains);
1456 hardregs[i].busy = 0;
1457 hardregs[i].dead = 0;
1458 hardregs[i].used = 0;
1461 FOR_EACH_PTR(state->inputs, entry) {
1462 struct storage *storage = entry->storage;
1463 const char *name = show_storage(storage);
1464 output_comment(state, "incoming %s in %s", show_pseudo(entry->pseudo), name);
1465 if (storage->type == REG_REG) {
1466 int regno = storage->regno;
1467 add_pseudo_reg(state, entry->pseudo, hardregs + regno);
1468 name = hardregs[regno].name;
1470 } END_FOR_EACH_PTR(entry);
1472 output_label(state, ".L%p", bb);
1473 FOR_EACH_PTR(bb->insns, insn) {
1474 if (!insn->bb)
1475 continue;
1476 generate_one_insn(insn, state);
1477 } END_FOR_EACH_PTR(insn);
1479 if (verbose) {
1480 output_comment(state, "--- in ---");
1481 FOR_EACH_PTR(state->inputs, entry) {
1482 output_comment(state, "%s <- %s", show_pseudo(entry->pseudo), show_storage(entry->storage));
1483 } END_FOR_EACH_PTR(entry);
1484 output_comment(state, "--- spill ---");
1485 FOR_EACH_PTR(state->internal, entry) {
1486 output_comment(state, "%s <-> %s", show_pseudo(entry->pseudo), show_storage(entry->storage));
1487 } END_FOR_EACH_PTR(entry);
1488 output_comment(state, "--- out ---");
1489 FOR_EACH_PTR(state->outputs, entry) {
1490 output_comment(state, "%s -> %s", show_pseudo(entry->pseudo), show_storage(entry->storage));
1491 } END_FOR_EACH_PTR(entry);
1493 printf("\n");
1496 static void generate_list(struct basic_block_list *list, unsigned long generation)
1498 struct basic_block *bb;
1499 FOR_EACH_PTR(list, bb) {
1500 if (bb->generation == generation)
1501 continue;
1502 output_bb(bb, generation);
1503 } END_FOR_EACH_PTR(bb);
1507 * Mark all the output registers of all the parents
1508 * as being "used" - this does not mean that we cannot
1509 * re-use them, but it means that we cannot ask the
1510 * parents to pass in another pseudo in one of those
1511 * registers that it already uses for another child.
1513 static void mark_used_registers(struct basic_block *bb, struct bb_state *state)
1515 struct basic_block *parent;
1517 FOR_EACH_PTR(bb->parents, parent) {
1518 struct storage_hash_list *outputs = gather_storage(parent, STOR_OUT);
1519 struct storage_hash *entry;
1521 FOR_EACH_PTR(outputs, entry) {
1522 struct storage *s = entry->storage;
1523 if (s->type == REG_REG) {
1524 struct hardreg *reg = hardregs + s->regno;
1525 reg->used = 1;
1527 } END_FOR_EACH_PTR(entry);
1528 } END_FOR_EACH_PTR(parent);
1531 static void output_bb(struct basic_block *bb, unsigned long generation)
1533 struct bb_state state;
1535 bb->generation = generation;
1537 /* Make sure all parents have been generated first */
1538 generate_list(bb->parents, generation);
1540 state.pos = bb->pos;
1541 state.inputs = gather_storage(bb, STOR_IN);
1542 state.outputs = gather_storage(bb, STOR_OUT);
1543 state.internal = NULL;
1544 state.cc_opcode = 0;
1545 state.cc_target = NULL;
1547 /* Mark incoming registers used */
1548 mark_used_registers(bb, &state);
1550 generate(bb, &state);
1552 free_ptr_list(&state.inputs);
1553 free_ptr_list(&state.outputs);
1555 /* Generate all children... */
1556 generate_list(bb->children, generation);
1559 static void set_up_arch_entry(struct entrypoint *ep, struct instruction *entry)
1561 int i;
1562 pseudo_t arg;
1565 * We should set up argument sources here..
1567 * Things like "first three arguments in registers" etc
1568 * are all for this place.
1570 i = 0;
1571 FOR_EACH_PTR(entry->arg_list, arg) {
1572 struct storage *in = lookup_storage(entry->bb, arg, STOR_IN);
1573 if (!in) {
1574 in = alloc_storage();
1575 add_storage(in, entry->bb, arg, STOR_IN);
1577 if (i < 3) {
1578 in->type = REG_REG;
1579 in->regno = i;
1580 } else {
1581 in->type = REG_FRAME;
1582 in->offset = (i-3)*4;
1584 i++;
1585 } END_FOR_EACH_PTR(arg);
1589 * Set up storage information for "return"
1591 * Not strictly necessary, since the code generator will
1592 * certainly move the return value to the right register,
1593 * but it can help register allocation if the allocator
1594 * sees that the target register is going to return in %eax.
1596 static void set_up_arch_exit(struct basic_block *bb, struct instruction *ret)
1598 pseudo_t pseudo = ret->src;
1600 if (pseudo && pseudo != VOID) {
1601 struct storage *out = lookup_storage(bb, pseudo, STOR_OUT);
1602 if (!out) {
1603 out = alloc_storage();
1604 add_storage(out, bb, pseudo, STOR_OUT);
1606 out->type = REG_REG;
1607 out->regno = 0;
1612 * Set up dummy/silly output storage information for a switch
1613 * instruction. We need to make sure that a register is available
1614 * when we generate code for switch, so force that by creating
1615 * a dummy output rule.
1617 static void set_up_arch_switch(struct basic_block *bb, struct instruction *insn)
1619 pseudo_t pseudo = insn->cond;
1620 struct storage *out = lookup_storage(bb, pseudo, STOR_OUT);
1621 if (!out) {
1622 out = alloc_storage();
1623 add_storage(out, bb, pseudo, STOR_OUT);
1625 out->type = REG_REG;
1626 out->regno = SWITCH_REG;
1629 static void arch_set_up_storage(struct entrypoint *ep)
1631 struct basic_block *bb;
1633 /* Argument storage etc.. */
1634 set_up_arch_entry(ep, ep->entry);
1636 FOR_EACH_PTR(ep->bbs, bb) {
1637 struct instruction *insn = last_instruction(bb->insns);
1638 if (!insn)
1639 continue;
1640 switch (insn->opcode) {
1641 case OP_RET:
1642 set_up_arch_exit(bb, insn);
1643 break;
1644 case OP_SWITCH:
1645 set_up_arch_switch(bb, insn);
1646 break;
1647 default:
1648 /* nothing */;
1650 } END_FOR_EACH_PTR(bb);
1653 static void output(struct entrypoint *ep)
1655 unsigned long generation = ++bb_generation;
1657 last_reg = -1;
1658 stack_offset = 0;
1660 /* Set up initial inter-bb storage links */
1661 set_up_storage(ep);
1663 /* Architecture-specific storage rules.. */
1664 arch_set_up_storage(ep);
1666 /* Show the results ... */
1667 output_bb(ep->entry->bb, generation);
1669 /* Clear the storage hashes for the next function.. */
1670 free_storage();
1673 static int compile(struct symbol_list *list)
1675 struct symbol *sym;
1676 FOR_EACH_PTR(list, sym) {
1677 struct entrypoint *ep;
1678 expand_symbol(sym);
1679 ep = linearize_symbol(sym);
1680 if (ep)
1681 output(ep);
1682 } END_FOR_EACH_PTR(sym);
1684 return 0;
1687 int main(int argc, char **argv)
1689 return compile(sparse(argc, argv));