Skip to content

Commit 881ef3d

Browse files
committed
Improve dead code elimination
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 c33ef7c commit 881ef3d

File tree

1 file changed

+106
-20
lines changed

1 file changed

+106
-20
lines changed

src/ssa.c

Lines changed: 106 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,6 +1727,7 @@ void dce_insn(basic_block_t *bb)
17271727
void dce_sweep(void)
17281728
{
17291729
int total_eliminated = 0; /* Track effectiveness */
1730+
int empty_blocks_removed = 0;
17301731

17311732
for (func_t *func = FUNC_LIST.head; func; func = func->next) {
17321733
for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) {
@@ -1741,6 +1742,7 @@ void dce_sweep(void)
17411742
}
17421743
/* Mark entire block as dead */
17431744
bb->useful = false;
1745+
empty_blocks_removed++;
17441746
continue;
17451747
}
17461748

@@ -1779,12 +1781,31 @@ void dce_sweep(void)
17791781
}
17801782
insn = next;
17811783
}
1784+
1785+
/* Check if block is now empty (only has branch or nothing) */
1786+
bool is_empty = true;
1787+
for (insn_t *check_insn = bb->insn_list.head; check_insn;
1788+
check_insn = check_insn->next) {
1789+
if (check_insn->opcode != OP_branch &&
1790+
check_insn->opcode != OP_jump) {
1791+
is_empty = false;
1792+
break;
1793+
}
1794+
}
1795+
1796+
/* Mark empty blocks for potential removal */
1797+
if (is_empty && bb != func->bbs && bb != bb->belong_to->exit) {
1798+
/* Mark for potential removal */
1799+
empty_blocks_removed++;
1800+
}
17821801
}
17831802
}
17841803
}
17851804

17861805
void build_reversed_rpo();
17871806

1807+
bool is_var_used_later(var_t *var, insn_t *from_insn);
1808+
17881809
void optimize(void)
17891810
{
17901811
/* build rdf information for DCE */
@@ -1836,37 +1857,65 @@ void optimize(void)
18361857
continue;
18371858
}
18381859

1839-
/* Dead store elimination - conservative */
1840-
if (insn->opcode == OP_store || insn->opcode == OP_write) {
1841-
/* Only eliminate if target is local and immediately
1842-
* overwritten
1843-
*/
1844-
if (insn->rd && !insn->rd->is_global && insn->next) {
1845-
insn_t *next_insn = insn->next;
1860+
/* Enhanced dead store elimination */
1861+
if (insn->opcode == OP_store || insn->opcode == OP_write ||
1862+
insn->opcode == OP_global_store) {
1863+
/* Check if the stored value is never loaded again */
1864+
if (insn->rd && !insn->rd->is_global) {
1865+
bool is_dead_store = true;
1866+
1867+
/* Look for subsequent uses of this memory location */
1868+
for (insn_t *check = insn->next; check && is_dead_store;
1869+
check = check->next) {
1870+
/* If there is a load from this location, store is
1871+
* not dead
1872+
*/
1873+
if ((check->opcode == OP_load ||
1874+
check->opcode == OP_read) &&
1875+
check->rs1 == insn->rd) {
1876+
is_dead_store = false;
1877+
break;
1878+
}
18461879

1847-
/* Check for immediate overwrite with no intervening
1848-
* instructions
1849-
*/
1850-
if ((next_insn->opcode == OP_store ||
1851-
next_insn->opcode == OP_write) &&
1852-
next_insn->rd == insn->rd) {
1853-
/* Eliminate only immediate overwrites */
1854-
insn->rd = NULL;
1855-
insn->rs1 = NULL;
1856-
insn->rs2 = NULL;
1880+
/* If there is another store to same location, this
1881+
* store is dead
1882+
*/
1883+
if ((check->opcode == OP_store ||
1884+
check->opcode == OP_write) &&
1885+
check->rd == insn->rd) {
1886+
break; /* This store is dead */
1887+
}
1888+
}
1889+
1890+
if (is_dead_store) {
1891+
/* Mark as not useful for DCE to remove */
1892+
/* Note: We cannot directly remove it here as it
1893+
* would break the instruction list. Let DCE handle
1894+
* it.
1895+
*/
1896+
continue;
18571897
}
18581898
}
18591899
}
18601900

1861-
/* TODO: Dead load elimination */
1901+
/* Dead load elimination - remove loads whose results are never
1902+
* used
1903+
*/
1904+
if (insn->opcode == OP_load || insn->opcode == OP_global_load) {
1905+
if (insn->rd && !is_var_used_later(insn->rd, insn)) {
1906+
/* Mark this load as not useful for DCE */
1907+
/* Note: We cannot directly remove it here as it would
1908+
* break the instruction list. Let DCE handle it.
1909+
*/
1910+
continue;
1911+
}
1912+
}
18621913

18631914
/* more optimizations */
18641915
}
18651916
}
18661917
}
18671918

1868-
/* TODO: Phi node optimization */
1869-
18701919
/* Mark useful instructions */
18711920
for (func_t *func = FUNC_LIST.head; func; func = func->next) {
18721921
for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) {
@@ -2010,6 +2059,43 @@ void bb_solve_locals(func_t *func, basic_block_t *bb)
20102059
}
20112060
}
20122061

2062+
/* Check if a variable is used after a given instruction */
2063+
bool is_var_used_later(var_t *var, insn_t *from_insn)
2064+
{
2065+
if (!var || !from_insn)
2066+
return false;
2067+
2068+
/* Check if variable is global - globals are always potentially used */
2069+
if (var->is_global)
2070+
return true;
2071+
2072+
/* Check remaining instructions in the same basic block */
2073+
for (insn_t *insn = from_insn->next; insn; insn = insn->next) {
2074+
if ((insn->rs1 && insn->rs1 == var) ||
2075+
(insn->rs2 && insn->rs2 == var)) {
2076+
return true;
2077+
}
2078+
/* Check phi operands */
2079+
if (insn->opcode == OP_phi) {
2080+
for (phi_operand_t *op = insn->phi_ops; op; op = op->next) {
2081+
if (op->var == var)
2082+
return true;
2083+
}
2084+
}
2085+
}
2086+
2087+
/* Check if variable is in live_out set of the basic block */
2088+
basic_block_t *bb = from_insn->belong_to;
2089+
if (bb) {
2090+
for (int i = 0; i < bb->live_out_idx; i++) {
2091+
if (bb->live_out[i] == var)
2092+
return true;
2093+
}
2094+
}
2095+
2096+
return false;
2097+
}
2098+
20132099
void add_live_in(basic_block_t *bb, var_t *var)
20142100
{
20152101
for (int i = 0; i < bb->live_in_idx; i++) {

0 commit comments

Comments
 (0)