Skip to content

Commit d19f238

Browse files
committed
Add copy propagation optimization
This commit implements copy propagation to eliminate redundant variable assignments by tracking copy relationships, reducing unnecessary data movement in the generated code. 1. Local (intra-block) copy propagation: - Backward search within basic blocks to find copy sources - Configurable search limit (COPY_PROP_SEARCH_LIMIT) for performance - Transitive copy chain following (a=b, b=c => use a for c) 2. Cross-block copy propagation: - Uses dominator tree information for safe inter-block optimization - Conservative search depth to maintain compilation performance - SSA-aware to prevent incorrect propagation across versions 3. PHI node optimization: - Propagates copies into PHI operands - Enables optimization across control flow merges
1 parent ee97fef commit d19f238

File tree

2 files changed

+291
-2
lines changed

2 files changed

+291
-2
lines changed

src/defs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@
3939
#define MAX_OPERAND_STACK_SIZE 32
4040
#define MAX_ANALYSIS_STACK_SIZE 800
4141

42+
/* Max instructions to search backward for copy sources */
43+
#define COPY_PROP_SEARCH_LIMIT 10
44+
4245
/* Default capacities for common data structures */
4346
/* Arena sizes optimized based on typical usage patterns */
4447
#define DEFAULT_ARENA_SIZE 262144 /* 256 KiB - standard default */

src/ssa.c

Lines changed: 288 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,6 +1366,289 @@ bool cse(insn_t *insn, basic_block_t *bb)
13661366
return false;
13671367
}
13681368

1369+
/* Copy propagation: track and propagate variable copies
1370+
* This optimization eliminates redundant assignments by tracking
1371+
* copy relationships and replacing uses with original sources.
1372+
*
1373+
* Features:
1374+
* - Intra-block copy propagation with configurable search limits
1375+
* - Cross-block copy propagation using dominator information
1376+
* - Transitive copy chain following (a=b, b=c => a=c)
1377+
*
1378+
* TODO: Handle copy propagation through memory operations (loads/stores)
1379+
* TODO: Integrate with value numbering for more aggressive optimization
1380+
*/
1381+
1382+
/* Find if a variable is a simple copy from another variable */
1383+
var_t *find_simple_copy_source(var_t *var, insn_t *use_insn, basic_block_t *bb)
1384+
{
1385+
if (!var || !use_insn || !bb)
1386+
return var;
1387+
1388+
/* Don't propagate globals, functions, or constants */
1389+
if (var->is_global || var->is_func || var->is_const)
1390+
return var;
1391+
1392+
/* NOTE: Cross-block propagation is now handled separately using
1393+
* dominance information in find_cross_block_copy_source() */
1394+
1395+
/* Use configurable search limit from defs.h */
1396+
int search_limit = COPY_PROP_SEARCH_LIMIT;
1397+
insn_t *insn = use_insn->prev;
1398+
1399+
while (insn && search_limit > 0) {
1400+
if (!insn->rd) {
1401+
insn = insn->prev;
1402+
search_limit--;
1403+
continue;
1404+
}
1405+
1406+
/* Found the definition of our variable */
1407+
if (insn->rd == var) {
1408+
/* If it's a simple copy, return the source */
1409+
if (insn->opcode == OP_assign && insn->rs1) {
1410+
/* Don't propagate if source is global or function */
1411+
if (insn->rs1->is_global || insn->rs1->is_func)
1412+
return var;
1413+
/* Check SSA safety - don't propagate across SSA versions */
1414+
if (var->base && insn->rs1->base &&
1415+
var->base != insn->rs1->base)
1416+
return var;
1417+
/* Don't create cycles */
1418+
if (insn->rs1 == var)
1419+
return var;
1420+
1421+
/* Follow copy chains iteratively for transitive propagation */
1422+
/* Limit chain length to prevent infinite loops */
1423+
var_t *source = insn->rs1;
1424+
int chain_limit = 3;
1425+
1426+
while (chain_limit > 0 && source) {
1427+
/* Look for another copy of this source */
1428+
insn_t *prev = insn->prev;
1429+
int inner_limit = COPY_PROP_SEARCH_LIMIT;
1430+
bool found_copy = false;
1431+
1432+
while (prev && inner_limit > 0) {
1433+
if (prev->rd == source && prev->opcode == OP_assign &&
1434+
prev->rs1) {
1435+
/* Found another copy, continue chain */
1436+
if (prev->rs1->is_global || prev->rs1->is_func)
1437+
return source;
1438+
if (source->base && prev->rs1->base &&
1439+
source->base != prev->rs1->base)
1440+
return source;
1441+
if (prev->rs1 == var) /* Avoid cycles */
1442+
return source;
1443+
source = prev->rs1;
1444+
found_copy = true;
1445+
break;
1446+
}
1447+
prev = prev->prev;
1448+
inner_limit--;
1449+
}
1450+
1451+
if (!found_copy)
1452+
break;
1453+
chain_limit--;
1454+
}
1455+
1456+
return source;
1457+
}
1458+
/* Not a copy assignment, stop here */
1459+
return var;
1460+
}
1461+
1462+
/* Stop at control flow instructions for local search.
1463+
* Cross-block propagation is handled separately using dominance */
1464+
if (insn->opcode == OP_branch || insn->opcode == OP_call ||
1465+
insn->opcode == OP_func_ret || insn->opcode == OP_phi) {
1466+
return var;
1467+
}
1468+
1469+
insn = insn->prev;
1470+
search_limit--;
1471+
}
1472+
1473+
return var;
1474+
}
1475+
1476+
/* Apply copy propagation to a single instruction */
1477+
bool propagate_copies_insn(insn_t *insn, basic_block_t *bb)
1478+
{
1479+
if (!insn || !bb)
1480+
return false;
1481+
1482+
bool changed = false;
1483+
1484+
/* Handle PHI nodes specially */
1485+
if (insn->opcode == OP_phi) {
1486+
/* Propagate copies into PHI operands */
1487+
bool phi_changed = false;
1488+
for (phi_operand_t *phi_op = insn->phi_ops; phi_op;
1489+
phi_op = phi_op->next) {
1490+
if (phi_op->var && !phi_op->var->is_global &&
1491+
!phi_op->var->is_func) {
1492+
var_t *source = find_simple_copy_source(phi_op->var, insn, bb);
1493+
if (source != phi_op->var) {
1494+
phi_op->var = source;
1495+
phi_changed = true;
1496+
}
1497+
}
1498+
}
1499+
return phi_changed;
1500+
}
1501+
1502+
/* Propagate copies in source operands */
1503+
if (insn->rs1 && !insn->rs1->is_global && !insn->rs1->is_func) {
1504+
var_t *source = find_simple_copy_source(insn->rs1, insn, bb);
1505+
if (source != insn->rs1) {
1506+
insn->rs1 = source;
1507+
changed = true;
1508+
}
1509+
}
1510+
1511+
if (insn->rs2 && !insn->rs2->is_func && !insn->rs2->is_global) {
1512+
var_t *source = find_simple_copy_source(insn->rs2, insn, bb);
1513+
if (source != insn->rs2) {
1514+
insn->rs2 = source;
1515+
changed = true;
1516+
}
1517+
}
1518+
1519+
/* Eliminate self-assignments after propagation */
1520+
if (insn->opcode == OP_assign && insn->rd && insn->rs1) {
1521+
if (insn->rd == insn->rs1) {
1522+
/* Self-assignment - convert to no-op for DCE to remove */
1523+
insn->opcode = OP_assign;
1524+
insn->rd = NULL;
1525+
insn->rs1 = NULL;
1526+
insn->rs2 = NULL;
1527+
/* Mark as not useful so DCE will remove it */
1528+
insn->useful = false;
1529+
changed = true;
1530+
}
1531+
}
1532+
1533+
return changed;
1534+
}
1535+
1536+
/* Apply copy propagation to an entire basic block */
1537+
void copy_propagation_bb(basic_block_t *bb)
1538+
{
1539+
if (!bb)
1540+
return;
1541+
1542+
/* Single pass: propagate copies and eliminate redundant assignments */
1543+
for (insn_t *insn = bb->insn_list.head; insn; insn = insn->next) {
1544+
/* First propagate copies in this instruction */
1545+
propagate_copies_insn(insn, bb);
1546+
1547+
/* Then check if this instruction itself is a redundant copy */
1548+
if (insn->opcode == OP_assign && insn->rd && insn->rs1) {
1549+
/* Check if this is now a self-copy after propagation */
1550+
if (insn->rs1 == insn->rd) {
1551+
/* Self-copy, mark for DCE removal */
1552+
insn->rd = NULL;
1553+
insn->rs1 = NULL;
1554+
insn->rs2 = NULL;
1555+
insn->useful = false;
1556+
}
1557+
}
1558+
}
1559+
}
1560+
1561+
/* Find copy source across basic blocks using dominance */
1562+
var_t *find_cross_block_copy_source(var_t *var,
1563+
basic_block_t *use_bb,
1564+
func_t *func)
1565+
{
1566+
if (!var || !use_bb || !func || var->is_global || var->is_func)
1567+
return var;
1568+
1569+
/* Search for copy definitions in dominating blocks */
1570+
basic_block_t *dom_bb = use_bb;
1571+
int search_depth = 3; /* Limit search depth */
1572+
1573+
while (dom_bb && search_depth > 0) {
1574+
/* Search within the dominating block */
1575+
for (insn_t *insn = dom_bb->insn_list.tail; insn; insn = insn->prev) {
1576+
if (insn->rd == var && insn->opcode == OP_assign && insn->rs1) {
1577+
/* Found a copy definition */
1578+
if (!insn->rs1->is_global && !insn->rs1->is_func) {
1579+
/* Check SSA safety */
1580+
if (var->base && insn->rs1->base &&
1581+
var->base != insn->rs1->base)
1582+
return var;
1583+
return insn->rs1;
1584+
}
1585+
}
1586+
/* Stop if we find any other definition of var */
1587+
if (insn->rd == var && insn->opcode != OP_assign)
1588+
return var;
1589+
}
1590+
1591+
/* Move to immediate dominator */
1592+
if (dom_bb->idom && dom_bb->idom != dom_bb) {
1593+
dom_bb = dom_bb->idom;
1594+
search_depth--;
1595+
} else {
1596+
break;
1597+
}
1598+
}
1599+
1600+
return var;
1601+
}
1602+
1603+
/* Apply copy propagation to an entire function */
1604+
void copy_propagation_pass(func_t *func)
1605+
{
1606+
if (!func)
1607+
return;
1608+
1609+
/* Process all basic blocks - local propagation first */
1610+
for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) {
1611+
/* Apply copy propagation multiple times for better results */
1612+
bool changed = true;
1613+
int iterations = 0;
1614+
while (changed && iterations < 3) {
1615+
changed = false;
1616+
for (insn_t *insn = bb->insn_list.head; insn; insn = insn->next) {
1617+
if (propagate_copies_insn(insn, bb))
1618+
changed = true;
1619+
}
1620+
iterations++;
1621+
}
1622+
1623+
/* Final cleanup pass */
1624+
copy_propagation_bb(bb);
1625+
}
1626+
1627+
/* Second pass: cross-block copy propagation using dominance */
1628+
/* Only do this if dominator information is available */
1629+
if (func->bbs && func->bbs->idom) {
1630+
for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) {
1631+
for (insn_t *insn = bb->insn_list.head; insn; insn = insn->next) {
1632+
/* Try cross-block propagation for operands */
1633+
if (insn->rs1 && !insn->rs1->is_global && !insn->rs1->is_func) {
1634+
var_t *source =
1635+
find_cross_block_copy_source(insn->rs1, bb, func);
1636+
if (source != insn->rs1) {
1637+
insn->rs1 = source;
1638+
}
1639+
}
1640+
if (insn->rs2 && !insn->rs2->is_global && !insn->rs2->is_func) {
1641+
var_t *source =
1642+
find_cross_block_copy_source(insn->rs2, bb, func);
1643+
if (source != insn->rs2) {
1644+
insn->rs2 = source;
1645+
}
1646+
}
1647+
}
1648+
}
1649+
}
1650+
}
1651+
13691652
bool mark_const(insn_t *insn)
13701653
{
13711654
if (insn->opcode == OP_load_constant) {
@@ -1811,6 +2094,10 @@ void optimize(void)
18112094
for (func_t *func = FUNC_LIST.head; func; func = func->next)
18122095
optimize_constant_casts(func);
18132096

2097+
/* Run copy propagation pass to eliminate redundant copies */
2098+
for (func_t *func = FUNC_LIST.head; func; func = func->next)
2099+
copy_propagation_pass(func);
2100+
18142101
for (func_t *func = FUNC_LIST.head; func; func = func->next) {
18152102
/* basic block level (control flow) optimizations */
18162103

@@ -1859,14 +2146,13 @@ void optimize(void)
18592146
}
18602147

18612148
/* TODO: Dead load elimination */
2149+
/* TODO: Phi node optimization */
18622150

18632151
/* more optimizations */
18642152
}
18652153
}
18662154
}
18672155

1868-
/* TODO: Phi node optimization */
1869-
18702156
/* Mark useful instructions */
18712157
for (func_t *func = FUNC_LIST.head; func; func = func->next) {
18722158
for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) {

0 commit comments

Comments
 (0)