2 * Example of how to write a compiler with sparse
9 #include "expression.h"
10 #include "linearize.h"
22 static void output_bb(struct basic_block
*bb
, unsigned long generation
);
25 * We only know about the caller-clobbered registers
28 static struct hardreg hardregs
[] = {
36 #define REGNO (sizeof(hardregs)/sizeof(struct hardreg))
39 struct basic_block
*bb
;
40 unsigned long stack_offset
;
41 struct storage_hash_list
*inputs
;
42 struct storage_hash_list
*outputs
;
43 struct storage_hash_list
*internal
;
46 static struct storage_hash
*find_storage_hash(pseudo_t pseudo
, struct storage_hash_list
*list
)
48 struct storage_hash
*entry
;
49 FOR_EACH_PTR(list
, entry
) {
50 if (entry
->pseudo
== pseudo
)
52 } END_FOR_EACH_PTR(entry
);
56 static struct storage_hash
*find_or_create_hash(pseudo_t pseudo
, struct storage_hash_list
**listp
)
58 struct storage_hash
*entry
;
60 entry
= find_storage_hash(pseudo
, *listp
);
62 entry
= alloc_storage_hash(alloc_storage());
63 entry
->pseudo
= pseudo
;
64 add_ptr_list(listp
, entry
);
69 static const char *show_memop(struct storage
*storage
)
71 static char buffer
[1000];
72 switch (storage
->type
) {
74 sprintf(buffer
, "%d(FP)", storage
->regno
* 4);
77 sprintf(buffer
, "%d(SP)", storage
->offset
);
80 return hardregs
[storage
->regno
].name
;
82 return show_storage(storage
);
87 static void alloc_stack(struct bb_state
*state
, struct storage
*storage
)
89 storage
->type
= REG_STACK
;
90 storage
->offset
= state
->stack_offset
;
91 state
->stack_offset
+= 4;
94 /* Flush a hardreg out to the storage it has.. */
95 static void flush_reg(struct bb_state
*state
, struct hardreg
*hardreg
)
98 struct storage_hash
*out
;
99 struct storage
*storage
;
101 if (!hardreg
->busy
|| !hardreg
->dirty
)
106 pseudo
= hardreg
->contains
;
109 if (pseudo
->type
!= PSEUDO_REG
&& pseudo
->type
!= PSEUDO_ARG
)
112 out
= find_storage_hash(pseudo
, state
->internal
);
114 out
= find_storage_hash(pseudo
, state
->outputs
);
116 out
= find_or_create_hash(pseudo
, &state
->internal
);
118 storage
= out
->storage
;
119 switch (storage
->type
) {
122 * Aieee - the next user wants it in a register, but we
123 * need to flush it to memory in between. Which means that
124 * we need to allocate an internal one, dammit..
126 out
= find_or_create_hash(pseudo
, &state
->internal
);
127 storage
= out
->storage
;
130 alloc_stack(state
, storage
);
133 printf("\tmovl %s,%s\n", hardreg
->name
, show_memop(storage
));
138 /* Fill a hardreg with the pseudo it has */
139 static struct hardreg
*fill_reg(struct bb_state
*state
, struct hardreg
*hardreg
, pseudo_t pseudo
)
141 struct storage_hash
*src
;
142 struct instruction
*def
;
144 switch (pseudo
->type
) {
146 printf("\tmovl $%lld,%s\n", pseudo
->value
, hardreg
->name
);
149 printf("\tmovl $<%s>,%s\n", show_pseudo(pseudo
), hardreg
->name
);
154 if (def
->opcode
== OP_SETVAL
) {
155 printf("\tmovl $<%s>,%s\n", show_pseudo(def
->symbol
), hardreg
->name
);
158 src
= find_storage_hash(pseudo
, state
->internal
);
160 src
= find_storage_hash(pseudo
, state
->inputs
);
162 src
= find_storage_hash(pseudo
, state
->outputs
);
163 /* Undefined? Screw it! */
165 printf("\tundef %s ??\n", show_pseudo(pseudo
));
170 if (src
->storage
->type
== REG_UDEF
) {
171 if (!hardreg
->used
) {
172 src
->storage
->type
= REG_REG
;
173 src
->storage
->regno
= hardreg
- hardregs
;
176 alloc_stack(state
, src
->storage
);
178 printf("\tmov.%d %s,%s\n", 32, show_memop(src
->storage
), hardreg
->name
);
181 printf("\treload %s from %s\n", hardreg
->name
, show_pseudo(pseudo
));
189 static struct hardreg
*target_reg(struct bb_state
*state
, pseudo_t pseudo
, pseudo_t target
)
193 struct storage_hash
*dst
;
195 /* First, see if we have a preferred target register.. */
196 dst
= find_storage_hash(target
, state
->outputs
);
198 struct storage
*storage
= dst
->storage
;
199 if (storage
->type
== REG_REG
) {
200 reg
= hardregs
+ storage
->regno
;
211 flush_reg(state
, reg
);
214 reg
->contains
= pseudo
;
220 static struct hardreg
*getreg(struct bb_state
*state
, pseudo_t pseudo
, pseudo_t target
)
225 for (i
= 0; i
< REGNO
; i
++) {
226 if (hardregs
[i
].contains
== pseudo
) {
231 reg
= target_reg(state
, pseudo
, target
);
232 return fill_reg(state
, reg
, pseudo
);
235 static const char *address(struct bb_state
*state
, struct instruction
*memop
)
238 struct hardreg
*base
;
239 static char buffer
[100];
240 pseudo_t addr
= memop
->src
;
245 if (sym
->ctype
.modifiers
& MOD_NONLOCAL
) {
246 sprintf(buffer
, "%s+%d", show_ident(sym
->ident
), memop
->offset
);
249 sprintf(buffer
, "%d+%s(SP)", memop
->offset
, show_ident(sym
->ident
));
252 base
= getreg(state
, addr
, NULL
);
253 sprintf(buffer
, "%d(%s)", memop
->offset
, base
->name
);
258 static const char *reg_or_imm(struct bb_state
*state
, pseudo_t pseudo
)
260 switch(pseudo
->type
) {
262 return show_pseudo(pseudo
);
264 return getreg(state
, pseudo
, NULL
)->name
;
268 static void generate_store(struct instruction
*insn
, struct bb_state
*state
)
270 printf("\tmov.%d %s,%s\n", insn
->size
, reg_or_imm(state
, insn
->target
), address(state
, insn
));
273 static void generate_load(struct instruction
*insn
, struct bb_state
*state
)
275 const char *input
= address(state
, insn
);
276 printf("\tmov.%d %s,%s\n", insn
->size
, input
, target_reg(state
, insn
->target
, NULL
)->name
);
279 static const char* opcodes
[] = {
280 [OP_BADOP
] = "bad_op",
283 [OP_ENTRY
] = "<entry-point>",
288 [OP_SWITCH
] = "switch",
289 [OP_INVOKE
] = "invoke",
290 [OP_COMPUTEDGOTO
] = "jmp *",
291 [OP_UNWIND
] = "unwind",
306 [OP_AND_BOOL
] = "and-bool",
307 [OP_OR_BOOL
] = "or-bool",
309 /* Binary comparison */
310 [OP_SET_EQ
] = "seteq",
311 [OP_SET_NE
] = "setne",
312 [OP_SET_LE
] = "setle",
313 [OP_SET_GE
] = "setge",
314 [OP_SET_LT
] = "setlt",
315 [OP_SET_GT
] = "setgt",
318 [OP_SET_BE
] = "setbe",
319 [OP_SET_AE
] = "setae",
325 /* Special three-input */
329 [OP_MALLOC
] = "malloc",
331 [OP_ALLOCA
] = "alloca",
333 [OP_STORE
] = "store",
335 [OP_GET_ELEMENT_PTR
] = "getelem",
339 [OP_PHISOURCE
] = "phisrc",
341 [OP_PTRCAST
] = "ptrcast",
343 [OP_VANEXT
] = "va_next",
344 [OP_VAARG
] = "va_arg",
345 [OP_SLICE
] = "slice",
349 [OP_DEATHNOTE
] = "dead",
352 /* Sparse tagging (line numbers, context, whatever) */
353 [OP_CONTEXT
] = "context",
357 static struct hardreg
*copy_reg(struct bb_state
*state
, struct hardreg
*src
)
364 for (i
= 0; i
< REGNO
; i
++) {
365 struct hardreg
*reg
= hardregs
+ i
;
367 printf("\tmovl %s,%s\n", src
->name
, reg
->name
);
372 flush_reg(state
, src
);
376 static void generate_binop(struct bb_state
*state
, struct instruction
*insn
)
378 const char *op
= opcodes
[insn
->opcode
];
379 struct hardreg
*src
= getreg(state
, insn
->src1
, NULL
);
380 struct hardreg
*dst
= copy_reg(state
, src
);
382 printf("\t%s.%d %s,%s\n", op
, insn
->size
, reg_or_imm(state
, insn
->src2
), dst
->name
);
383 dst
->contains
= insn
->target
;
388 static void mark_pseudo_dead(pseudo_t pseudo
)
392 for (i
= 0; i
< REGNO
; i
++) {
393 if (hardregs
[i
].contains
== pseudo
) {
394 hardregs
[i
].busy
= 0;
395 hardregs
[i
].dirty
= 0;
400 static void generate_phisource(struct instruction
*insn
, struct bb_state
*state
)
402 struct instruction
*user
= first_instruction(insn
->phi_users
);
407 reg
= getreg(state
, insn
->phi_src
, user
->target
);
409 flush_reg(state
, reg
);
410 reg
->contains
= user
->target
;
415 static void generate_output_storage(struct bb_state
*state
);
417 static void generate_branch(struct bb_state
*state
, struct instruction
*br
)
420 struct hardreg
*reg
= getreg(state
, br
->cond
, NULL
);
421 printf("\ttestl %s,%s\n", reg
->name
, reg
->name
);
423 generate_output_storage(state
);
425 printf("\tje .L%p\n", br
->bb_false
);
426 printf("\tjmp .L%p\n", br
->bb_true
);
429 static void generate_one_insn(struct instruction
*insn
, struct bb_state
*state
)
431 switch (insn
->opcode
) {
433 struct symbol
*sym
= insn
->bb
->ep
->name
;
434 const char *name
= show_ident(sym
->ident
);
435 if (sym
->ctype
.modifiers
& MOD_STATIC
)
436 printf("\n\n%s:\n", name
);
438 printf("\n\n.globl %s\n%s:\n", name
, name
);
443 * OP_PHI doesn't actually generate any code. It has been
444 * done by the storage allocator and the OP_PHISOURCE.
450 generate_phisource(insn
, state
);
454 * OP_SETVAL likewise doesn't actually generate any
455 * code. On use, the "def" of the pseudo will be
462 generate_store(insn
, state
);
466 generate_load(insn
, state
);
470 mark_pseudo_dead(insn
->target
);
473 case OP_BINARY
... OP_BINARY_END
:
474 case OP_BINCMP
... OP_BINCMP_END
:
475 generate_binop(state
, insn
);
479 generate_branch(state
, insn
);
483 show_instruction(insn
);
488 static void write_reg_to_storage(struct bb_state
*state
, struct hardreg
*reg
, struct storage
*storage
)
492 switch (storage
->type
) {
494 storage
->type
= REG_REG
;
495 storage
->regno
= reg
- hardregs
;
498 out
= hardregs
+ storage
->regno
;
501 assert(!out
->contains
);
502 printf("\tmovl %s,%s\n", reg
->name
, out
->name
);
505 printf("\tmovl %s,%s\n", reg
->name
, show_memop(storage
));
510 static void write_val_to_storage(struct bb_state
*state
, pseudo_t src
, struct storage
*storage
)
514 switch (storage
->type
) {
516 alloc_stack(state
, storage
);
518 printf("\tmovl %s,%s\n", show_pseudo(src
), show_memop(storage
));
521 out
= hardregs
+ storage
->regno
;
522 printf("\tmovl %s,%s\n", show_pseudo(src
), out
->name
);
526 static void fill_output(struct bb_state
*state
, pseudo_t pseudo
, struct storage
*out
)
529 struct storage_hash
*in
;
530 struct instruction
*def
;
532 /* Is that pseudo a constant value? */
533 switch (pseudo
->type
) {
535 write_val_to_storage(state
, pseudo
, out
);
539 if (def
->opcode
== OP_SETVAL
) {
540 write_val_to_storage(state
, def
->symbol
, out
);
547 /* See if we have that pseudo in a register.. */
548 for (i
= 0; i
< REGNO
; i
++) {
549 struct hardreg
*reg
= hardregs
+ i
;
550 if (reg
->contains
!= pseudo
)
552 write_reg_to_storage(state
, reg
, out
);
556 /* Do we have it in another storage? */
557 in
= find_storage_hash(pseudo
, state
->internal
);
559 in
= find_storage_hash(pseudo
, state
->inputs
);
569 printf("\tmovl %s,%s\n", show_memop(in
->storage
), hardregs
[out
->regno
].name
);
572 printf("\tmovl %s,tmp\n", show_memop(in
->storage
));
573 printf("\tmovl tmp,%s\n", show_memop(out
));
580 * This tries to make sure that we put all the pseudos that are
581 * live on exit into the proper storage
583 static void generate_output_storage(struct bb_state
*state
)
585 struct storage_hash
*entry
;
587 /* Go through the fixed outputs, making sure we have those regs free */
588 FOR_EACH_PTR(state
->outputs
, entry
) {
589 struct storage
*out
= entry
->storage
;
590 if (out
->type
== REG_REG
) {
591 struct hardreg
*reg
= hardregs
+ out
->regno
;
592 if (reg
->contains
!= entry
->pseudo
) {
593 flush_reg(state
, reg
);
594 reg
->contains
= NULL
;
597 } END_FOR_EACH_PTR(entry
);
599 FOR_EACH_PTR(state
->outputs
, entry
) {
600 fill_output(state
, entry
->pseudo
, entry
->storage
);
601 } END_FOR_EACH_PTR(entry
);
604 static void generate(struct basic_block
*bb
, struct bb_state
*state
)
607 struct storage_hash
*entry
;
608 struct instruction
*insn
;
610 for (i
= 0; i
< REGNO
; i
++) {
611 hardregs
[i
].contains
= NULL
;
612 hardregs
[i
].busy
= 0;
613 hardregs
[i
].dirty
= 0;
614 hardregs
[i
].used
= 0;
617 FOR_EACH_PTR(state
->inputs
, entry
) {
618 struct storage
*storage
= entry
->storage
;
619 const char *name
= show_storage(storage
);
620 if (storage
->type
== REG_REG
) {
621 int regno
= storage
->regno
;
622 hardregs
[regno
].contains
= entry
->pseudo
;
623 hardregs
[regno
].busy
= 1;
624 hardregs
[regno
].dirty
= 1;
625 name
= hardregs
[regno
].name
;
627 } END_FOR_EACH_PTR(entry
);
629 printf(".L%p:\n", bb
);
630 FOR_EACH_PTR(bb
->insns
, insn
) {
633 generate_one_insn(insn
, state
);
634 } END_FOR_EACH_PTR(insn
);
638 FOR_EACH_PTR(state
->inputs
, entry
) {
639 printf("\t%s <- %s\n", show_pseudo(entry
->pseudo
), show_storage(entry
->storage
));
640 } END_FOR_EACH_PTR(entry
);
642 FOR_EACH_PTR(state
->internal
, entry
) {
643 printf("\t%s <-> %s\n", show_pseudo(entry
->pseudo
), show_storage(entry
->storage
));
644 } END_FOR_EACH_PTR(entry
);
646 FOR_EACH_PTR(state
->outputs
, entry
) {
647 printf("\t%s -> %s\n", show_pseudo(entry
->pseudo
), show_storage(entry
->storage
));
648 } END_FOR_EACH_PTR(entry
);
653 static void generate_list(struct basic_block_list
*list
, unsigned long generation
)
655 struct basic_block
*bb
;
656 FOR_EACH_PTR(list
, bb
) {
657 if (bb
->generation
== generation
)
659 output_bb(bb
, generation
);
660 } END_FOR_EACH_PTR(bb
);
663 static void output_bb(struct basic_block
*bb
, unsigned long generation
)
665 struct bb_state state
;
667 bb
->generation
= generation
;
669 /* Make sure all parents have been generated first */
670 generate_list(bb
->parents
, generation
);
672 state
.inputs
= gather_storage(bb
, STOR_IN
);
673 state
.outputs
= gather_storage(bb
, STOR_OUT
);
674 state
.internal
= NULL
;
675 state
.stack_offset
= 0;
677 generate(bb
, &state
);
679 free_ptr_list(&state
.inputs
);
680 free_ptr_list(&state
.outputs
);
682 /* Generate all children... */
683 generate_list(bb
->children
, generation
);
686 static void output(struct entrypoint
*ep
)
688 unsigned long generation
= ++bb_generation
;
692 /* Set up initial inter-bb storage links */
695 /* Show the results ... */
696 output_bb(ep
->entry
->bb
, generation
);
698 /* Clear the storage hashes for the next function.. */
702 static int compile(struct symbol_list
*list
)
705 FOR_EACH_PTR(list
, sym
) {
706 struct entrypoint
*ep
;
708 ep
= linearize_symbol(sym
);
711 } END_FOR_EACH_PTR(sym
);
716 int main(int argc
, char **argv
)
718 return compile(sparse(argc
, argv
));