@@ -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+
13691652bool 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