123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- /*
- * Register - track pseudo usage, maybe eventually try to do register
- * allocation.
- *
- * Copyright (C) 2004 Linus Torvalds
- */
- #include <assert.h>
- #include "liveness.h"
- #include "parse.h"
- #include "expression.h"
- #include "linearize.h"
- #include "flow.h"
- static void phi_defines(struct instruction * phi_node, pseudo_t target,
- void (*defines)(struct basic_block *, pseudo_t))
- {
- pseudo_t phi;
- FOR_EACH_PTR(phi_node->phi_list, phi) {
- struct instruction *def;
- if (phi == VOID)
- continue;
- def = phi->def;
- if (!def || !def->bb)
- continue;
- defines(def->bb, target);
- } END_FOR_EACH_PTR(phi);
- }
- static void asm_liveness(struct basic_block *bb, struct instruction *insn,
- void (*def)(struct basic_block *, pseudo_t),
- void (*use)(struct basic_block *, pseudo_t))
- {
- struct asm_constraint *entry;
- FOR_EACH_PTR(insn->asm_rules->inputs, entry) {
- use(bb, entry->pseudo);
- } END_FOR_EACH_PTR(entry);
-
- FOR_EACH_PTR(insn->asm_rules->outputs, entry) {
- if (entry->is_memory)
- use(bb, entry->pseudo);
- else
- def(bb, entry->pseudo);
- } END_FOR_EACH_PTR(entry);
- }
- static void track_instruction_usage(struct basic_block *bb, struct instruction *insn,
- void (*def)(struct basic_block *, pseudo_t),
- void (*use)(struct basic_block *, pseudo_t))
- {
- pseudo_t pseudo;
- #define USES(x) use(bb, insn->x)
- #define DEFINES(x) def(bb, insn->x)
- switch (insn->opcode) {
- case OP_RET:
- case OP_COMPUTEDGOTO:
- USES(src);
- break;
- case OP_CBR:
- case OP_SWITCH:
- USES(cond);
- break;
- /* Binary */
- case OP_BINARY ... OP_BINARY_END:
- case OP_FPCMP ... OP_FPCMP_END:
- case OP_BINCMP ... OP_BINCMP_END:
- USES(src1); USES(src2); DEFINES(target);
- break;
- /* Uni */
- case OP_UNOP ... OP_UNOP_END:
- case OP_SYMADDR:
- USES(src1); DEFINES(target);
- break;
- case OP_SEL:
- USES(src1); USES(src2); USES(src3); DEFINES(target);
- break;
-
- /* Memory */
- case OP_LOAD:
- USES(src); DEFINES(target);
- break;
- case OP_STORE:
- USES(src); USES(target);
- break;
- case OP_SETVAL:
- case OP_SETFVAL:
- DEFINES(target);
- break;
- /* Other */
- case OP_PHI:
- /* Phi-nodes are "backwards" nodes. Their def doesn't matter */
- phi_defines(insn, insn->target, def);
- break;
- case OP_PHISOURCE:
- /*
- * We don't care about the phi-source define, they get set
- * up and expanded by the OP_PHI
- */
- USES(phi_src);
- break;
- case OP_CALL:
- USES(func);
- if (insn->target != VOID)
- DEFINES(target);
- FOR_EACH_PTR(insn->arguments, pseudo) {
- use(bb, pseudo);
- } END_FOR_EACH_PTR(pseudo);
- break;
- case OP_SLICE:
- USES(base); DEFINES(target);
- break;
- case OP_ASM:
- asm_liveness(bb, insn, def, use);
- break;
- case OP_RANGE:
- USES(src1); USES(src2); USES(src3);
- break;
- case OP_BADOP:
- case OP_NOP:
- case OP_CONTEXT:
- break;
- }
- }
- static int liveness_changed;
- static void add_pseudo_exclusive(struct pseudo_list **list, pseudo_t pseudo)
- {
- if (!pseudo_in_list(*list, pseudo)) {
- liveness_changed = 1;
- add_pseudo(list, pseudo);
- }
- }
- static inline int trackable_pseudo(pseudo_t pseudo)
- {
- return pseudo && (pseudo->type == PSEUDO_REG || pseudo->type == PSEUDO_ARG);
- }
- static void insn_uses(struct basic_block *bb, pseudo_t pseudo)
- {
- if (trackable_pseudo(pseudo)) {
- struct instruction *def = pseudo->def;
- if (pseudo->type != PSEUDO_REG || def->bb != bb || def->opcode == OP_PHI)
- add_pseudo_exclusive(&bb->needs, pseudo);
- }
- }
- static void insn_defines(struct basic_block *bb, pseudo_t pseudo)
- {
- assert(trackable_pseudo(pseudo));
- add_pseudo(&bb->defines, pseudo);
- }
- static void track_bb_liveness(struct basic_block *bb)
- {
- pseudo_t needs;
- FOR_EACH_PTR(bb->needs, needs) {
- struct basic_block *parent;
- FOR_EACH_PTR(bb->parents, parent) {
- if (!pseudo_in_list(parent->defines, needs)) {
- add_pseudo_exclusive(&parent->needs, needs);
- }
- } END_FOR_EACH_PTR(parent);
- } END_FOR_EACH_PTR(needs);
- }
- /*
- * We need to clear the liveness information if we
- * are going to re-run it.
- */
- void clear_liveness(struct entrypoint *ep)
- {
- struct basic_block *bb;
- FOR_EACH_PTR(ep->bbs, bb) {
- free_ptr_list(&bb->needs);
- free_ptr_list(&bb->defines);
- } END_FOR_EACH_PTR(bb);
- }
- /*
- * Track inter-bb pseudo liveness. The intra-bb case
- * is purely local information.
- */
- void track_pseudo_liveness(struct entrypoint *ep)
- {
- struct basic_block *bb;
- /* Add all the bb pseudo usage */
- FOR_EACH_PTR(ep->bbs, bb) {
- struct instruction *insn;
- FOR_EACH_PTR(bb->insns, insn) {
- if (!insn->bb)
- continue;
- assert(insn->bb == bb);
- track_instruction_usage(bb, insn, insn_defines, insn_uses);
- } END_FOR_EACH_PTR(insn);
- } END_FOR_EACH_PTR(bb);
- /* Calculate liveness.. */
- do {
- liveness_changed = 0;
- FOR_EACH_PTR_REVERSE(ep->bbs, bb) {
- track_bb_liveness(bb);
- } END_FOR_EACH_PTR_REVERSE(bb);
- } while (liveness_changed);
- /* Remove the pseudos from the "defines" list that are used internally */
- FOR_EACH_PTR(ep->bbs, bb) {
- pseudo_t def;
- FOR_EACH_PTR(bb->defines, def) {
- struct basic_block *child;
- FOR_EACH_PTR(bb->children, child) {
- if (pseudo_in_list(child->needs, def))
- goto is_used;
- } END_FOR_EACH_PTR(child);
- DELETE_CURRENT_PTR(def);
- is_used:
- ;
- } END_FOR_EACH_PTR(def);
- PACK_PTR_LIST(&bb->defines);
- } END_FOR_EACH_PTR(bb);
- }
- static void merge_pseudo_list(struct pseudo_list *src, struct pseudo_list **dest)
- {
- pseudo_t pseudo;
- FOR_EACH_PTR(src, pseudo) {
- add_pseudo_exclusive(dest, pseudo);
- } END_FOR_EACH_PTR(pseudo);
- }
- static void track_phi_uses(struct instruction *insn)
- {
- pseudo_t phi;
- FOR_EACH_PTR(insn->phi_list, phi) {
- struct instruction *def;
- if (phi == VOID || !phi->def)
- continue;
- def = phi->def;
- assert(def->opcode == OP_PHISOURCE);
- add_ptr_list(&def->phi_users, insn);
- } END_FOR_EACH_PTR(phi);
- }
- static void track_bb_phi_uses(struct basic_block *bb)
- {
- struct instruction *insn;
- FOR_EACH_PTR(bb->insns, insn) {
- if (insn->bb && insn->opcode == OP_PHI)
- track_phi_uses(insn);
- } END_FOR_EACH_PTR(insn);
- }
- static struct pseudo_list **live_list;
- static struct pseudo_list *dead_list;
- static void death_def(struct basic_block *bb, pseudo_t pseudo)
- {
- }
- static void death_use(struct basic_block *bb, pseudo_t pseudo)
- {
- if (trackable_pseudo(pseudo) && !pseudo_in_list(*live_list, pseudo)) {
- add_pseudo(&dead_list, pseudo);
- add_pseudo(live_list, pseudo);
- }
- }
- static void track_pseudo_death_bb(struct basic_block *bb)
- {
- struct pseudo_list *live = NULL;
- struct basic_block *child;
- struct instruction *insn;
- FOR_EACH_PTR(bb->children, child) {
- merge_pseudo_list(child->needs, &live);
- } END_FOR_EACH_PTR(child);
- live_list = &live;
- FOR_EACH_PTR_REVERSE(bb->insns, insn) {
- if (!insn->bb)
- continue;
- dead_list = NULL;
- track_instruction_usage(bb, insn, death_def, death_use);
- if (dead_list) {
- pseudo_t dead;
- FOR_EACH_PTR(dead_list, dead) {
- struct instruction *deathnote = __alloc_instruction(0);
- deathnote->bb = bb;
- deathnote->opcode = OP_DEATHNOTE;
- deathnote->target = dead;
- INSERT_CURRENT(deathnote, insn);
- } END_FOR_EACH_PTR(dead);
- free_ptr_list(&dead_list);
- }
- } END_FOR_EACH_PTR_REVERSE(insn);
- free_ptr_list(&live);
- }
- void track_pseudo_death(struct entrypoint *ep)
- {
- struct basic_block *bb;
- FOR_EACH_PTR(ep->bbs, bb) {
- track_bb_phi_uses(bb);
- } END_FOR_EACH_PTR(bb);
- FOR_EACH_PTR(ep->bbs, bb) {
- track_pseudo_death_bb(bb);
- } END_FOR_EACH_PTR(bb);
- }
|