diff --git a/src/opt-sccp.c b/src/opt-sccp.c new file mode 100644 index 00000000..81f4e405 --- /dev/null +++ b/src/opt-sccp.c @@ -0,0 +1,156 @@ +/* + * shecc - Self-Hosting and Educational C Compiler. + * + * shecc is freely redistributable under the BSD 2 clause license. See the + * file "LICENSE" for information on usage and redistribution of this file. + */ + +/* SCCP (Sparse Conditional Constant Propagation) Optimization Pass + * + * This optimization pass performs: + * - Constant propagation through assignments + * - Constant folding for arithmetic and comparison operations + * - Branch folding when conditions are compile-time constants + * - Dead code elimination through unreachable branch removal + */ + +/* Simple constant propagation within basic blocks */ +bool simple_sccp(func_t *func) +{ + if (!func || !func->bbs) + return false; + + bool changed = false; + + /* Iterate through basic blocks */ + for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) { + /* Process instructions in the block */ + for (insn_t *insn = bb->insn_list.head; insn; insn = insn->next) { + /* Skip if no destination */ + if (!insn->rd) + continue; + + /* Handle simple constant propagation */ + switch (insn->opcode) { + case OP_assign: + /* Propagate constants through assignments */ + if (insn->rs1 && insn->rs1->is_const && !insn->rd->is_const) { + insn->rd->is_const = true; + insn->rd->init_val = insn->rs1->init_val; + insn->opcode = OP_load_constant; + insn->rs1 = NULL; + changed = true; + } + break; + + case OP_add: + case OP_sub: + case OP_mul: + /* Fold binary operations with constants */ + if (insn->rs1 && insn->rs1->is_const && insn->rs2 && + insn->rs2->is_const && + !insn->rd->is_global) { /* Don't modify globals */ + int result = 0; + int l = insn->rs1->init_val, r = insn->rs2->init_val; + + switch (insn->opcode) { + case OP_add: + result = l + r; + break; + case OP_sub: + result = l - r; + break; + case OP_mul: + result = l * r; + break; + default: + continue; + } + + /* Convert to constant load */ + insn->opcode = OP_load_constant; + insn->rd->is_const = true; + insn->rd->init_val = result; + insn->rs1 = NULL; + insn->rs2 = NULL; + changed = true; + } + break; + + case OP_eq: + case OP_neq: + case OP_lt: + case OP_leq: + case OP_gt: + case OP_geq: + /* Fold comparison operations */ + if (insn->rs1 && insn->rs1->is_const && insn->rs2 && + insn->rs2->is_const && + !insn->rd->is_global) { /* Don't modify globals */ + int result = 0; + int l = insn->rs1->init_val; + int r = insn->rs2->init_val; + + switch (insn->opcode) { + case OP_eq: + result = (l == r); + break; + case OP_neq: + result = (l != r); + break; + case OP_lt: + result = (l < r); + break; + case OP_leq: + result = (l <= r); + break; + case OP_gt: + result = (l > r); + break; + case OP_geq: + result = (l >= r); + break; + default: + continue; + } + + /* Convert to constant load */ + insn->opcode = OP_load_constant; + insn->rd->is_const = true; + insn->rd->init_val = result; + insn->rs1 = NULL; + insn->rs2 = NULL; + changed = true; + } + break; + + default: + /* Other opcodes - no optimization */ + break; + } + } + + /* Simple constant branch folding */ + insn_t *last = bb->insn_list.tail; + if (last && last->opcode == OP_branch) { + if (last->rs1 && last->rs1->is_const) { + /* Convert to unconditional jump */ + last->opcode = OP_jump; + + if (last->rs1->init_val != 0) { + /* Take then branch */ + bb->else_ = NULL; + } else { + /* Take else branch */ + bb->then_ = bb->else_; + bb->else_ = NULL; + } + + last->rs1 = NULL; + changed = true; + } + } + } + + return changed; +} diff --git a/src/ssa.c b/src/ssa.c index 2ea068ef..7a9b6b2b 100644 --- a/src/ssa.c +++ b/src/ssa.c @@ -11,6 +11,13 @@ #include "defs.h" #include "globals.c" +/* SCCP (Sparse Conditional Constant Propagation) optimization */ +#include "opt-sccp.c" + +/* Configuration constants - replace magic numbers */ +#define PHI_WORKLIST_SIZE 64 +#define DCE_WORKLIST_SIZE 2048 + /* cfront does not accept structure as an argument, pass pointer */ void bb_forward_traversal(bb_traversal_args_t *args) { @@ -198,10 +205,8 @@ bool dom_connect(basic_block_t *pred, basic_block_t *succ) break; } - if (i > MAX_BB_DOM_SUCC - 1) { - printf("Error: too many predecessors\n"); - abort(); - } + if (i > MAX_BB_DOM_SUCC - 1) + fatal("Too many predecessors in dominator tree"); pred->dom_next[i++] = succ; succ->dom_prev = pred; @@ -298,15 +303,12 @@ void build_r_idom(void) pred = bb->then_; } - if (bb->next && bb->next != pred && bb->next->r_idom) { + if (bb->next && bb->next != pred && bb->next->r_idom) pred = reverse_intersect(bb->next, pred); - } - if (bb->else_ && bb->else_ != pred && bb->else_->r_idom) { + if (bb->else_ && bb->else_ != pred && bb->else_->r_idom) pred = reverse_intersect(bb->else_, pred); - } - if (bb->then_ && bb->then_ != pred && bb->then_->r_idom) { + if (bb->then_ && bb->then_ != pred && bb->then_->r_idom) pred = reverse_intersect(bb->then_, pred); - } if (bb->r_idom != pred) { bb->r_idom = pred; changed = true; @@ -320,6 +322,7 @@ bool rdom_connect(basic_block_t *pred, basic_block_t *succ) { if (succ->rdom_prev) return false; + int i; for (i = 0; i < MAX_BB_RDOM_SUCC; i++) { if (pred->rdom_next[i] == succ) @@ -328,10 +331,8 @@ bool rdom_connect(basic_block_t *pred, basic_block_t *succ) break; } - if (i > MAX_BB_RDOM_SUCC - 1) { - printf("Error: too many predecessors\n"); - abort(); - } + if (i > MAX_BB_RDOM_SUCC - 1) + fatal("Too many predecessors in reverse dominator tree"); pred->rdom_next[i++] = succ; succ->rdom_prev = pred; @@ -605,12 +606,15 @@ void solve_phi_insertion(void) for (symbol_t *sym = func->global_sym_list.head; sym; sym = sym->next) { var_t *var = sym->var; - basic_block_t *work_list[64]; + basic_block_t *work_list[PHI_WORKLIST_SIZE]; int work_list_idx = 0; for (ref_block_t *ref = var->ref_block_list.head; ref; - ref = ref->next) + ref = ref->next) { + if (work_list_idx >= PHI_WORKLIST_SIZE - 1) + fatal("PHI worklist overflow"); work_list[work_list_idx++] = ref->bb; + } for (int i = 0; i < work_list_idx; i++) { basic_block_t *bb = work_list[i]; @@ -649,13 +653,17 @@ void solve_phi_insertion(void) if (var->is_ternary_ret || var->is_logical_ret) continue; - for (int l = 0; l < work_list_idx; l++) + for (int l = 0; l < work_list_idx; l++) { if (work_list[l] == df) { found = true; break; } - if (!found) + } + if (!found) { + if (work_list_idx >= PHI_WORKLIST_SIZE - 1) + fatal("PHI worklist overflow"); work_list[work_list_idx++] = df; + } } } } @@ -694,7 +702,8 @@ var_t *get_stack_top_subscript_var(var_t *var) return var->base->subscripts[i]; } - abort(); + fatal("Failed to find subscript variable on rename stack"); + return NULL; /* unreachable, but silences compiler warning */ } void rename_var(var_t **var) @@ -888,7 +897,7 @@ void bb_dump_connection(FILE *fd, str = "%s_%p:se->%s_%p:n\n"; break; default: - abort(); + fatal("Unknown basic block connection type"); } char *pred; @@ -955,8 +964,8 @@ char *get_insn_op(insn_t *insn) case OP_log_or: return "||"; default: - printf("Unknown opcode"); - abort(); + fatal("Unknown opcode in operator string conversion"); + return ""; /* unreachable, but silences compiler warning */ } } @@ -1125,8 +1134,7 @@ void bb_dump(FILE *fd, func_t *func, basic_block_t *bb) insn->rs1->var_name, insn->rs1->subscript); break; default: - printf("Unknown opcode\n"); - abort(); + fatal("Unknown opcode in instruction dump"); } fprintf(fd, "insn_%p [label=%s]\n", insn, str); } @@ -1222,82 +1230,140 @@ void ssa_build(void) unwind_phi(); } +/* Check if operation can be subject to CSE */ +bool is_cse_candidate(insn_t *insn) +{ + switch (insn->opcode) { + case OP_add: + case OP_sub: + case OP_mul: + case OP_div: + case OP_mod: + case OP_lshift: + case OP_rshift: + case OP_bit_and: + case OP_bit_or: + case OP_bit_xor: + case OP_log_and: + case OP_log_or: + case OP_eq: + case OP_neq: + case OP_lt: + case OP_leq: + case OP_gt: + case OP_geq: + return true; + default: + return false; + } +} + /* Common Subexpression Elimination (CSE) */ -/* TODO: release detached insns node */ +/* Enhanced to support general binary operations */ bool cse(insn_t *insn, basic_block_t *bb) { - if (insn->opcode != OP_read) - return false; + /* Handle array access pattern: add + read */ + if (insn->opcode == OP_read) { + insn_t *prev = insn->prev; + if (!prev) + return false; + if (prev->opcode != OP_add) + return false; + if (prev->rd != insn->rs1) + return false; - insn_t *prev = insn->prev; - if (!prev) - return false; - if (prev->opcode != OP_add) + var_t *def = insn->rd, *base = prev->rs1, *idx = prev->rs2; + if (base->is_global || idx->is_global) + return false; + + /* Look for identical add+read patterns */ + for (use_chain_t *user = base->users_head; user; user = user->next) { + insn_t *i = user->insn; + if (i == prev) + continue; + if (i->opcode != OP_add) + continue; + if (!i->next) + continue; + if (i->next->opcode != OP_read) + continue; + if (i->rs1 != base || i->rs2 != idx) + continue; + + /* Check dominance */ + basic_block_t *i_bb = i->belong_to; + bool check_dom = false; + for (;; i_bb = i_bb->idom) { + if (i_bb == bb) { + check_dom = true; + break; + } + if (i_bb == i_bb->idom) + break; + } + if (!check_dom) + continue; + + /* Replace with assignment */ + i->next->opcode = OP_assign; + i->next->rs1 = def; + if (i->prev) { + i->prev->next = i->next; + i->next->prev = i->prev; + } else { + i->belong_to->insn_list.head = i->next; + i->next->prev = NULL; + } + } + return true; + } + + /* Handle general binary operations */ + if (!is_cse_candidate(insn)) return false; - if (prev->rd != insn->rs1) + + if (!insn->rs1 || !insn->rs2 || !insn->rd) return false; - var_t *def = insn->rd, *base = prev->rs1, *idx = prev->rs2; - if (base->is_global || idx->is_global) + /* Don't CSE operations with global variables */ + if (insn->rs1->is_global || insn->rs2->is_global) return false; - use_chain_t *rs1_delete_user = NULL; - use_chain_t *rs2_delete_user = NULL; - for (use_chain_t *user = base->users_head; user; user = user->next) { - insn_t *i = user->insn; + /* Look for identical binary operations */ + for (insn_t *other = bb->insn_list.head; other; other = other->next) { + if (other == insn) + break; /* Only consider earlier instructions */ - /* Delete the use chain nodes found in the last loop */ - if (rs1_delete_user) { - use_chain_delete(rs1_delete_user, rs1_delete_user->insn->rs1); - rs1_delete_user = NULL; - } - if (rs2_delete_user) { - use_chain_delete(rs2_delete_user, rs2_delete_user->insn->rs2); - rs2_delete_user = NULL; - } - if (i == prev) - continue; - if (i->opcode != OP_add) + if (other->opcode != insn->opcode) continue; - if (!i->next) + if (!other->rs1 || !other->rs2 || !other->rd) continue; - if (i->next->opcode != OP_read) - continue; - if (i->rs1 != base || i->rs2 != idx) - continue; - basic_block_t *i_bb = i->belong_to; - bool check_dom = 0; - /* Check if the instructions are under the same dominate tree */ - for (;; i_bb = i_bb->idom) { - if (i_bb == bb) { - check_dom = true; - break; + + /* Check if operands match */ + bool operands_match = false; + if (other->rs1 == insn->rs1 && other->rs2 == insn->rs2) { + operands_match = true; + } else if (insn->opcode == OP_add || insn->opcode == OP_mul || + insn->opcode == OP_bit_and || insn->opcode == OP_bit_or || + insn->opcode == OP_bit_xor || insn->opcode == OP_log_and || + insn->opcode == OP_log_or || insn->opcode == OP_eq || + insn->opcode == OP_neq) { + /* Commutative operations */ + if (other->rs1 == insn->rs2 && other->rs2 == insn->rs1) { + operands_match = true; } - if (i_bb == i_bb->idom) - break; } - if (!check_dom) - continue; - i->next->opcode = OP_assign; - i->next->rs1 = def; - if (i->prev) { - i->prev->next = i->next; - i->next->prev = i->prev; - } else { - i->belong_to->insn_list.head = i->next; - i->next->prev = NULL; + if (operands_match) { + /* Replace current instruction with assignment */ + insn->opcode = OP_assign; + insn->rs1 = other->rd; + insn->rs2 = NULL; + return true; } - i->next->opcode = OP_assign; - i->next->rs1 = def; - /* Prepare information for deleting use chain nodes */ - rs1_delete_user = user; - for (rs2_delete_user = i->rs2->users_head; - rs2_delete_user->insn != rs1_delete_user->insn; - rs2_delete_user = rs2_delete_user->next) - ; } - return true; + + return false; } bool mark_const(insn_t *insn) @@ -1355,11 +1421,54 @@ bool eval_const_arithmetic(insn_t *insn) res = l * r; break; case OP_div: + if (r == 0) + return false; /* avoid division by zero */ res = l / r; break; case OP_mod: + if (r == 0) + return false; /* avoid modulo by zero */ res = l % r; break; + case OP_lshift: + res = l << r; + break; + case OP_rshift: + res = l >> r; + break; + case OP_bit_and: + res = l & r; + break; + case OP_bit_or: + res = l | r; + break; + case OP_bit_xor: + res = l ^ r; + break; + case OP_log_and: + res = l && r; + break; + case OP_log_or: + res = l || r; + break; + case OP_eq: + res = l == r; + break; + case OP_neq: + res = l != r; + break; + case OP_lt: + res = l < r; + break; + case OP_leq: + res = l <= r; + break; + case OP_gt: + res = l > r; + break; + case OP_geq: + res = l >= r; + break; default: return false; } @@ -1372,26 +1481,123 @@ bool eval_const_arithmetic(insn_t *insn) return true; } +bool eval_const_unary(insn_t *insn) +{ + if (!insn->rs1) + return false; + if (!insn->rs1->is_const) + return false; + + int res; + int val = insn->rs1->init_val; + + switch (insn->opcode) { + case OP_negate: + res = -val; + break; + case OP_bit_not: + res = ~val; + break; + case OP_log_not: + res = !val; + break; + default: + return false; + } + + insn->rs1 = NULL; + insn->rd->is_const = 1; + insn->rd->init_val = res; + insn->opcode = OP_load_constant; + return true; +} + bool const_folding(insn_t *insn) { if (mark_const(insn)) return true; if (eval_const_arithmetic(insn)) return true; + if (eval_const_unary(insn)) + return true; return false; } +/* Check if a basic block is unreachable */ +bool is_block_unreachable(basic_block_t *bb) +{ + if (!bb) + return true; + + /* Entry block is always reachable */ + if (!bb->idom && bb->belong_to && bb == bb->belong_to->bbs) + return false; + + /* If block has no immediate dominator and it is not the entry block, it is + * unreachable + */ + if (!bb->idom) + return true; + + /* If block was never visited during dominator tree construction, it is + * unreachable + */ + if (!bb->visited) + return true; + + return false; +} + +/* Check if a variable escapes (is used outside the function) */ +bool var_escapes(var_t *var) +{ + if (!var) + return true; /* conservative: assume it escapes */ + + /* Global variables always escape */ + if (var->is_global) + return true; + + /* Function definitions escape */ + if (var->is_func) + return true; + + /* Conservative approach - assume all variables escape to avoid issues */ + /* This ensures we don't eliminate stores that might be needed */ + return true; +} + /* initial mark useful instruction */ int dce_init_mark(insn_t *insn, insn_t *work_list[], int work_list_idx) { int mark_num = 0; - /* - * mark instruction "useful" if it sets a return value, affects the value in + /* mark instruction "useful" if it sets a return value, affects the value in * a storage location, or it is a function call. */ switch (insn->opcode) { case OP_return: + insn->useful = true; + insn->belong_to->useful = true; + work_list[work_list_idx + mark_num] = insn; + mark_num++; + break; case OP_write: + case OP_store: + /* Only mark stores/writes to escaping variables as useful */ + if (!insn->rd || var_escapes(insn->rd)) { + insn->useful = true; + insn->belong_to->useful = true; + work_list[work_list_idx + mark_num] = insn; + mark_num++; + } + break; + case OP_global_store: + /* Global stores always escape */ + insn->useful = true; + insn->belong_to->useful = true; + work_list[work_list_idx + mark_num] = insn; + mark_num++; + break; case OP_address_of: case OP_unwound_phi: case OP_allocat: @@ -1409,7 +1615,7 @@ int dce_init_mark(insn_t *insn, insn_t *work_list[], int work_list_idx) /* mark precall and postreturn sequences at calls */ if (insn->next && insn->next->opcode == OP_func_ret) { insn->next->useful = true; - work_list[work_list_idx + mark_num] = insn; + work_list[work_list_idx + mark_num] = insn->next; mark_num++; } while (insn->prev && insn->prev->opcode == OP_push) { @@ -1437,38 +1643,67 @@ int dce_init_mark(insn_t *insn, insn_t *work_list[], int work_list_idx) /* Dead Code Elimination (DCE) */ void dce_insn(basic_block_t *bb) { - insn_t *work_list[2048]; + insn_t *work_list[DCE_WORKLIST_SIZE]; int work_list_idx = 0; - /* initially analyze current bb*/ + /* initially analyze current bb */ for (insn_t *insn = bb->insn_list.head; insn; insn = insn->next) { int mark_num = dce_init_mark(insn, work_list, work_list_idx); work_list_idx += mark_num; - if (work_list_idx > 2048 - 1) { - printf("size of work_list in DCE is not enough\n"); - abort(); - } + if (work_list_idx > DCE_WORKLIST_SIZE - 1) + fatal("DCE worklist size exceeded"); } + /* Process worklist - marking dependencies as useful */ while (work_list_idx != 0) { insn_t *curr = work_list[--work_list_idx]; - insn_t *rs1_insn, *rs2_insn; - /* trace back where rs1 and rs2 are assigned values */ + /* Skip if already processed to avoid redundant work */ + if (!curr) + continue; + + /* Mark instruction as useful and add to worklist */ + insn_t *dep_insn = NULL; + + /* trace back where rs1 is assigned */ if (curr->rs1 && curr->rs1->last_assign) { - rs1_insn = curr->rs1->last_assign; - if (!rs1_insn->useful) { - rs1_insn->useful = true; - rs1_insn->belong_to->useful = true; - work_list[work_list_idx++] = rs1_insn; + dep_insn = curr->rs1->last_assign; + if (!dep_insn->useful) { + dep_insn->useful = true; + dep_insn->belong_to->useful = true; + if (work_list_idx < DCE_WORKLIST_SIZE - 1) + work_list[work_list_idx++] = dep_insn; + else + fatal("DCE worklist overflow"); } } + + /* trace back where rs2 is assigned */ if (curr->rs2 && curr->rs2->last_assign) { - rs2_insn = curr->rs2->last_assign; - if (!rs2_insn->useful) { - rs2_insn->useful = true; - rs2_insn->belong_to->useful = true; - work_list[work_list_idx++] = rs2_insn; + dep_insn = curr->rs2->last_assign; + if (!dep_insn->useful) { + dep_insn->useful = true; + dep_insn->belong_to->useful = true; + if (work_list_idx < DCE_WORKLIST_SIZE - 1) + work_list[work_list_idx++] = dep_insn; + else + fatal("DCE worklist overflow"); + } + } + + /* For phi nodes, mark all operands as useful */ + if (curr->opcode == OP_phi && curr->useful) { + for (phi_operand_t *phi_op = curr->phi_ops; phi_op; + phi_op = phi_op->next) { + if (phi_op->var && phi_op->var->last_assign && + !phi_op->var->last_assign->useful) { + phi_op->var->last_assign->useful = true; + phi_op->var->last_assign->belong_to->useful = true; + if (work_list_idx < DCE_WORKLIST_SIZE - 1) + work_list[work_list_idx++] = phi_op->var->last_assign; + else + fatal("DCE worklist overflow"); + } } } @@ -1478,10 +1713,12 @@ void dce_insn(basic_block_t *bb) if (!rdf) break; insn_t *tail = rdf->insn_list.tail; - if (tail->opcode == OP_branch && !tail->useful) { + if (tail && tail->opcode == OP_branch && !tail->useful) { tail->useful = true; rdf->useful = true; work_list[work_list_idx++] = tail; + if (work_list_idx > DCE_WORKLIST_SIZE - 1) + fatal("DCE worklist overflow"); } } } @@ -1489,38 +1726,58 @@ void dce_insn(basic_block_t *bb) void dce_sweep(void) { + int total_eliminated = 0; /* Track effectiveness */ + for (func_t *func = FUNC_LIST.head; func; func = func->next) { for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) { - for (insn_t *insn = bb->insn_list.head; insn; insn = insn->next) { - if (insn->useful) - continue; - /* - * If a branch instruction is useless, redirect to the - * reverse immediate dominator of this basic block and - * remove the branch instruction. Later, register allocation - * will insert a jump instruction. - */ - if (insn->opcode == OP_branch) { - basic_block_t *jump_bb = bb->r_idom; - bb_disconnect(bb, bb->then_); - bb_disconnect(bb, bb->else_); - while (jump_bb != bb->belong_to->exit) { - if (jump_bb->useful) { - bb_connect(bb, jump_bb, NEXT); - break; + /* Skip unreachable blocks entirely */ + if (is_block_unreachable(bb)) { + /* Count instructions being eliminated */ + for (insn_t *insn = bb->insn_list.head; insn; + insn = insn->next) { + if (!insn->useful) + total_eliminated++; + insn->useful = false; + } + /* Mark entire block as dead */ + bb->useful = false; + continue; + } + + insn_t *insn = bb->insn_list.head; + while (insn) { + insn_t *next = insn->next; + if (!insn->useful) { + total_eliminated++; + /* If a branch instruction is useless, redirect to the + * reverse immediate dominator of this basic block and + * remove the branch instruction. Later, register allocation + * will insert a jump instruction. + */ + if (insn->opcode == OP_branch) { + basic_block_t *jump_bb = bb->r_idom; + bb_disconnect(bb, bb->then_); + bb_disconnect(bb, bb->else_); + while (jump_bb != bb->belong_to->exit) { + if (jump_bb->useful) { + bb_connect(bb, jump_bb, NEXT); + break; + } + jump_bb = jump_bb->r_idom; } - jump_bb = jump_bb->r_idom; } + + /* remove useless instructions */ + if (insn->next) + insn->next->prev = insn->prev; + else + bb->insn_list.tail = insn->prev; + if (insn->prev) + insn->prev->next = insn->next; + else + bb->insn_list.head = insn->next; } - /* remove useless instructions */ - if (insn->next) - insn->next->prev = insn->prev; - else - bb->insn_list.tail = insn->prev; - if (insn->prev) - insn->prev->next = insn->next; - else - bb->insn_list.head = insn->next; + insn = next; } } } @@ -1538,6 +1795,10 @@ void optimize(void) use_chain_build(); + /* Run SCCP optimization first */ + for (func_t *func = FUNC_LIST.head; func; func = func->next) + simple_sccp(func); + for (func_t *func = FUNC_LIST.head; func; func = func->next) { /* basic block level (control flow) optimizations */ @@ -1547,20 +1808,61 @@ void optimize(void) /* record the instruction assigned value to rd */ if (insn->rd) insn->rd->last_assign = insn; - if (cse(insn, bb)) + + /* Apply optimizations in order */ + if (const_folding(insn)) /* First: fold constants */ + continue; + if (cse(insn, bb)) /* Then: eliminate common subexpressions */ continue; - if (const_folding(insn)) + + /* Eliminate redundant assignments: x = x */ + if (insn->opcode == OP_assign && insn->rd && insn->rs1 && + insn->rd == insn->rs1) { + /* Convert to no-op that DCE will remove */ + insn->rd = NULL; + insn->rs1 = NULL; continue; + } + + /* Dead store elimination - conservative */ + if (insn->opcode == OP_store || insn->opcode == OP_write) { + /* Only eliminate if target is local and immediately + * overwritten + */ + if (insn->rd && !insn->rd->is_global && insn->next) { + insn_t *next_insn = insn->next; + + /* Check for immediate overwrite with no intervening + * instructions + */ + if ((next_insn->opcode == OP_store || + next_insn->opcode == OP_write) && + next_insn->rd == insn->rd) { + /* Eliminate only immediate overwrites */ + insn->rd = NULL; + insn->rs1 = NULL; + insn->rs2 = NULL; + } + } + } + + /* TODO: Dead load elimination */ + /* more optimizations */ } } } + /* TODO: Phi node optimization */ + + /* Mark useful instructions */ for (func_t *func = FUNC_LIST.head; func; func = func->next) { for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) { dce_insn(bb); } } + + /* Eliminate dead instructions */ dce_sweep(); }