/* * CSE - walk the linearized instruction flow, and * see if we can simplify it and apply CSE on it. * * Copyright (C) 2004 Linus Torvalds */ #include #include #include #include #include #include #include "parse.h" #include "expression.h" #include "linearize.h" #include "flow.h" #define INSN_HASH_SIZE 256 static struct instruction_list *insn_hash_table[INSN_HASH_SIZE]; int repeat_phase; static int phi_compare(pseudo_t phi1, pseudo_t phi2) { const struct instruction *def1 = phi1->def; const struct instruction *def2 = phi2->def; if (def1->src1 != def2->src1) return def1->src1 < def2->src1 ? -1 : 1; if (def1->bb != def2->bb) return def1->bb < def2->bb ? -1 : 1; return 0; } static void clean_up_one_instruction(struct basic_block *bb, struct instruction *insn) { unsigned long hash; if (!insn->bb) return; assert(insn->bb == bb); repeat_phase |= simplify_instruction(insn); hash = (insn->opcode << 3) + (insn->size >> 3); switch (insn->opcode) { case OP_SEL: hash += hashval(insn->src3); /* Fall through */ /* Binary arithmetic */ case OP_ADD: case OP_SUB: case OP_MULU: case OP_MULS: case OP_DIVU: case OP_DIVS: case OP_MODU: case OP_MODS: case OP_SHL: case OP_LSR: case OP_ASR: case OP_AND: case OP_OR: /* Binary logical */ case OP_XOR: case OP_AND_BOOL: case OP_OR_BOOL: /* Binary comparison */ case OP_SET_EQ: case OP_SET_NE: case OP_SET_LE: case OP_SET_GE: case OP_SET_LT: case OP_SET_GT: case OP_SET_B: case OP_SET_A: case OP_SET_BE: case OP_SET_AE: hash += hashval(insn->src2); /* Fall through */ /* Unary */ case OP_NOT: case OP_NEG: hash += hashval(insn->src1); break; case OP_SETVAL: hash += hashval(insn->val); break; case OP_SYMADDR: hash += hashval(insn->symbol); break; case OP_CAST: case OP_SCAST: case OP_PTRCAST: /* * This is crap! Many "orig_types" are the * same as far as casts go, we should generate * some kind of "type hash" that is identical * for identical casts */ hash += hashval(insn->orig_type); hash += hashval(insn->src); break; /* Other */ case OP_PHI: { pseudo_t phi; FOR_EACH_PTR(insn->phi_list, phi) { struct instruction *def; if (phi == VOID || !phi->def) continue; def = phi->def; hash += hashval(def->src1); hash += hashval(def->bb); } END_FOR_EACH_PTR(phi); break; } default: /* * Nothing to do, don't even bother hashing them, * we're not going to try to CSE them */ return; } hash += hash >> 16; hash &= INSN_HASH_SIZE-1; add_instruction(insn_hash_table + hash, insn); } static void clean_up_insns(struct entrypoint *ep) { struct basic_block *bb; FOR_EACH_PTR(ep->bbs, bb) { struct instruction *insn; FOR_EACH_PTR(bb->insns, insn) { clean_up_one_instruction(bb, insn); } END_FOR_EACH_PTR(insn); } END_FOR_EACH_PTR(bb); } /* Compare two (sorted) phi-lists */ static int phi_list_compare(struct pseudo_list *l1, struct pseudo_list *l2) { pseudo_t phi1, phi2; PREPARE_PTR_LIST(l1, phi1); PREPARE_PTR_LIST(l2, phi2); for (;;) { int cmp; while (phi1 && (phi1 == VOID || !phi1->def)) NEXT_PTR_LIST(phi1); while (phi2 && (phi2 == VOID || !phi2->def)) NEXT_PTR_LIST(phi2); if (!phi1) return phi2 ? -1 : 0; if (!phi2) return phi1 ? 1 : 0; cmp = phi_compare(phi1, phi2); if (cmp) return cmp; NEXT_PTR_LIST(phi1); NEXT_PTR_LIST(phi2); } /* Not reached, but we need to make the nesting come out right */ FINISH_PTR_LIST(phi2); FINISH_PTR_LIST(phi1); } static int insn_compare(const void *_i1, const void *_i2) { const struct instruction *i1 = _i1; const struct instruction *i2 = _i2; if (i1->opcode != i2->opcode) return i1->opcode < i2->opcode ? -1 : 1; switch (i1->opcode) { case OP_SEL: if (i1->src3 != i2->src3) return i1->src3 < i2->src3 ? -1 : 1; /* Fall-through to binops */ /* Binary arithmetic */ case OP_ADD: case OP_SUB: case OP_MULU: case OP_MULS: case OP_DIVU: case OP_DIVS: case OP_MODU: case OP_MODS: case OP_SHL: case OP_LSR: case OP_ASR: case OP_AND: case OP_OR: /* Binary logical */ case OP_XOR: case OP_AND_BOOL: case OP_OR_BOOL: /* Binary comparison */ case OP_SET_EQ: case OP_SET_NE: case OP_SET_LE: case OP_SET_GE: case OP_SET_LT: case OP_SET_GT: case OP_SET_B: case OP_SET_A: case OP_SET_BE: case OP_SET_AE: if (i1->src2 != i2->src2) return i1->src2 < i2->src2 ? -1 : 1; /* Fall through to unops */ /* Unary */ case OP_NOT: case OP_NEG: if (i1->src1 != i2->src1) return i1->src1 < i2->src1 ? -1 : 1; break; case OP_SYMADDR: if (i1->symbol != i2->symbol) return i1->symbol < i2->symbol ? -1 : 1; break; case OP_SETVAL: if (i1->val != i2->val) return i1->val < i2->val ? -1 : 1; break; /* Other */ case OP_PHI: return phi_list_compare(i1->phi_list, i2->phi_list); case OP_CAST: case OP_SCAST: case OP_PTRCAST: /* * This is crap! See the comments on hashing. */ if (i1->orig_type != i2->orig_type) return i1->orig_type < i2->orig_type ? -1 : 1; if (i1->src != i2->src) return i1->src < i2->src ? -1 : 1; break; default: warning(i1->pos, "bad instruction on hash chain"); } if (i1->size != i2->size) return i1->size < i2->size ? -1 : 1; return 0; } static void sort_instruction_list(struct instruction_list **list) { sort_list((struct ptr_list **)list , insn_compare); } static struct instruction * cse_one_instruction(struct instruction *insn, struct instruction *def) { convert_instruction_target(insn, def->target); if (insn->opcode == OP_PHI) { /* Remove the instruction from PHI users */ pseudo_t phi; FOR_EACH_PTR(insn->phi_list, phi) { struct pseudo_user *pu; FOR_EACH_PTR(phi->users, pu) { if (pu->insn == insn) DELETE_CURRENT_PTR(pu); } END_FOR_EACH_PTR(pu); } END_FOR_EACH_PTR(phi); } insn->opcode = OP_NOP; insn->bb = NULL; repeat_phase |= REPEAT_CSE; return def; } /* * Does "bb1" dominate "bb2"? */ static int bb_dominates(struct entrypoint *ep, struct basic_block *bb1, struct basic_block *bb2, unsigned long generation) { struct basic_block *parent; /* Nothing dominates the entrypoint.. */ if (bb2 == ep->entry->bb) return 0; FOR_EACH_PTR(bb2->parents, parent) { if (parent == bb1) continue; if (parent->generation == generation) continue; parent->generation = generation; if (!bb_dominates(ep, bb1, parent, generation)) return 0; } END_FOR_EACH_PTR(parent); return 1; } static struct basic_block *trivial_common_parent(struct basic_block *bb1, struct basic_block *bb2) { struct basic_block *parent; if (bb_list_size(bb1->parents) != 1) return NULL; parent = first_basic_block(bb1->parents); if (bb_list_size(bb2->parents) != 1) return NULL; if (first_basic_block(bb2->parents) != parent) return NULL; return parent; } static inline void remove_instruction(struct instruction_list **list, struct instruction *insn, int count) { delete_ptr_list_entry((struct ptr_list **)list, insn, count); } static void add_instruction_to_end(struct instruction *insn, struct basic_block *bb) { struct instruction *br = delete_last_instruction(&bb->insns); insn->bb = bb; add_instruction(&bb->insns, insn); add_instruction(&bb->insns, br); } static struct instruction * try_to_cse(struct entrypoint *ep, struct instruction *i1, struct instruction *i2) { struct basic_block *b1, *b2, *common; /* * OK, i1 and i2 are the same instruction, modulo "target". * We should now see if we can combine them. */ b1 = i1->bb; b2 = i2->bb; /* * Currently we only handle the uninteresting degenerate case where * the CSE is inside one basic-block. */ if (b1 == b2) { struct instruction *insn; FOR_EACH_PTR(b1->insns, insn) { if (insn == i1) return cse_one_instruction(i2, i1); if (insn == i2) return cse_one_instruction(i1, i2); } END_FOR_EACH_PTR(insn); warning(b1->pos, "Whaa? unable to find CSE instructions"); return i1; } if (bb_dominates(ep, b1, b2, ++bb_generation)) return cse_one_instruction(i2, i1); if (bb_dominates(ep, b2, b1, ++bb_generation)) return cse_one_instruction(i1, i2); /* No direct dominance - but we could try to find a common ancestor.. */ common = trivial_common_parent(b1, b2); if (common) { i1 = cse_one_instruction(i2, i1); remove_instruction(&b1->insns, i1, 1); add_instruction_to_end(i1, common); } return i1; } void cleanup_and_cse(struct entrypoint *ep) { int i; simplify_memops(ep); repeat: repeat_phase = 0; clean_up_insns(ep); for (i = 0; i < INSN_HASH_SIZE; i++) { struct instruction_list **list = insn_hash_table + i; if (*list) { if (instruction_list_size(*list) > 1) { struct instruction *insn, *last; sort_instruction_list(list); last = NULL; FOR_EACH_PTR(*list, insn) { if (!insn->bb) continue; if (last) { if (!insn_compare(last, insn)) insn = try_to_cse(ep, last, insn); } last = insn; } END_FOR_EACH_PTR(insn); } free_ptr_list((struct ptr_list **)list); } } if (repeat_phase & REPEAT_SYMBOL_CLEANUP) simplify_memops(ep); if (repeat_phase & REPEAT_CSE) goto repeat; }