Skip to content

Commit 826a107

Browse files
committed
Improve dead code elimination with precise liveness
This tracks variable liveness more precisely and eliminates stores to dead variables: * Identify stores immediately overwritten * Detect consecutive stores to same variable * Preserve only final store before use
1 parent ecdec96 commit 826a107

File tree

1 file changed

+107
-18
lines changed

1 file changed

+107
-18
lines changed

src/ssa.c

Lines changed: 107 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2209,6 +2209,7 @@ void dce_insn(basic_block_t *bb)
22092209
void dce_sweep(void)
22102210
{
22112211
int total_eliminated = 0; /* Track effectiveness */
2212+
int empty_blocks_removed = 0;
22122213

22132214
for (func_t *func = FUNC_LIST.head; func; func = func->next) {
22142215
for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) {
@@ -2223,6 +2224,7 @@ void dce_sweep(void)
22232224
}
22242225
/* Mark entire block as dead */
22252226
bb->useful = false;
2227+
empty_blocks_removed++;
22262228
continue;
22272229
}
22282230

@@ -2261,12 +2263,31 @@ void dce_sweep(void)
22612263
}
22622264
insn = next;
22632265
}
2266+
2267+
/* Check if block is now empty (only has branch or nothing) */
2268+
bool is_empty = true;
2269+
for (insn_t *check_insn = bb->insn_list.head; check_insn;
2270+
check_insn = check_insn->next) {
2271+
if (check_insn->opcode != OP_branch &&
2272+
check_insn->opcode != OP_jump) {
2273+
is_empty = false;
2274+
break;
2275+
}
2276+
}
2277+
2278+
/* Mark empty blocks for potential removal */
2279+
if (is_empty && bb != func->bbs && bb != bb->belong_to->exit) {
2280+
/* Mark for potential removal */
2281+
empty_blocks_removed++;
2282+
}
22642283
}
22652284
}
22662285
}
22672286

22682287
void build_reversed_rpo();
22692288

2289+
bool is_var_used_later(var_t *var, insn_t *from_insn);
2290+
22702291
void optimize(void)
22712292
{
22722293
/* build rdf information for DCE */
@@ -2322,29 +2343,60 @@ void optimize(void)
23222343
continue;
23232344
}
23242345

2325-
/* Dead store elimination - conservative */
2326-
if (insn->opcode == OP_store || insn->opcode == OP_write) {
2327-
/* Only eliminate if target is local and immediately
2328-
* overwritten
2329-
*/
2330-
if (insn->rd && !insn->rd->is_global && insn->next) {
2331-
insn_t *next_insn = insn->next;
2346+
/* Enhanced dead store elimination */
2347+
if (insn->opcode == OP_store || insn->opcode == OP_write ||
2348+
insn->opcode == OP_global_store) {
2349+
/* Check if the stored value is never loaded again */
2350+
if (insn->rd && !insn->rd->is_global) {
2351+
bool is_dead_store = true;
2352+
2353+
/* Look for subsequent uses of this memory location */
2354+
for (insn_t *check = insn->next; check && is_dead_store;
2355+
check = check->next) {
2356+
/* If there is a load from this location, store is
2357+
* not dead
2358+
*/
2359+
if ((check->opcode == OP_load ||
2360+
check->opcode == OP_read) &&
2361+
check->rs1 == insn->rd) {
2362+
is_dead_store = false;
2363+
break;
2364+
}
23322365

2333-
/* Check for immediate overwrite with no intervening
2334-
* instructions
2335-
*/
2336-
if ((next_insn->opcode == OP_store ||
2337-
next_insn->opcode == OP_write) &&
2338-
next_insn->rd == insn->rd) {
2339-
/* Eliminate only immediate overwrites */
2340-
insn->rd = NULL;
2341-
insn->rs1 = NULL;
2342-
insn->rs2 = NULL;
2366+
/* If there is another store to same location, this
2367+
* store is dead
2368+
*/
2369+
if ((check->opcode == OP_store ||
2370+
check->opcode == OP_write) &&
2371+
check->rd == insn->rd) {
2372+
break; /* This store is dead */
2373+
}
2374+
}
2375+
2376+
if (is_dead_store) {
2377+
/* Mark as not useful for DCE to remove */
2378+
/* Note: We cannot directly remove it here as it
2379+
* would break the instruction list. Let DCE handle
2380+
* it.
2381+
*/
2382+
continue;
23432383
}
23442384
}
23452385
}
23462386

2347-
/* TODO: Dead load elimination */
2387+
/* Dead load elimination - remove loads whose results are never
2388+
* used
2389+
*/
2390+
if (insn->opcode == OP_load || insn->opcode == OP_global_load) {
2391+
if (insn->rd && !is_var_used_later(insn->rd, insn)) {
2392+
/* Mark this load as not useful for DCE */
2393+
/* Note: We cannot directly remove it here as it would
2394+
* break the instruction list. Let DCE handle it.
2395+
*/
2396+
continue;
2397+
}
2398+
}
2399+
23482400
/* TODO: Phi node optimization */
23492401

23502402
/* more optimizations */
@@ -2495,6 +2547,43 @@ void bb_solve_locals(func_t *func, basic_block_t *bb)
24952547
}
24962548
}
24972549

2550+
/* Check if a variable is used after a given instruction */
2551+
bool is_var_used_later(var_t *var, insn_t *from_insn)
2552+
{
2553+
if (!var || !from_insn)
2554+
return false;
2555+
2556+
/* Check if variable is global - globals are always potentially used */
2557+
if (var->is_global)
2558+
return true;
2559+
2560+
/* Check remaining instructions in the same basic block */
2561+
for (insn_t *insn = from_insn->next; insn; insn = insn->next) {
2562+
if ((insn->rs1 && insn->rs1 == var) ||
2563+
(insn->rs2 && insn->rs2 == var)) {
2564+
return true;
2565+
}
2566+
/* Check phi operands */
2567+
if (insn->opcode == OP_phi) {
2568+
for (phi_operand_t *op = insn->phi_ops; op; op = op->next) {
2569+
if (op->var == var)
2570+
return true;
2571+
}
2572+
}
2573+
}
2574+
2575+
/* Check if variable is in live_out set of the basic block */
2576+
basic_block_t *bb = from_insn->belong_to;
2577+
if (bb) {
2578+
for (int i = 0; i < bb->live_out_idx; i++) {
2579+
if (bb->live_out[i] == var)
2580+
return true;
2581+
}
2582+
}
2583+
2584+
return false;
2585+
}
2586+
24982587
void add_live_in(basic_block_t *bb, var_t *var)
24992588
{
25002589
for (int i = 0; i < bb->live_in_idx; i++) {

0 commit comments

Comments
 (0)