@@ -1795,6 +1795,202 @@ bool eval_const_unary(insn_t *insn)
17951795 return true;
17961796}
17971797
1798+ /* SSA-level constant folding with algebraic simplifications.
1799+ * Only handle cases where we have known constants.
1800+ * Non-constant patterns (x-x, x^x, etc.) are handled by peephole optimizer.
1801+ */
1802+ bool eval_algebraic (insn_t * insn )
1803+ {
1804+ if (!insn || !insn -> rd )
1805+ return false;
1806+
1807+ /* Only optimize when we have actual constants */
1808+ bool has_const = (insn -> rs1 && insn -> rs1 -> is_const ) ||
1809+ (insn -> rs2 && insn -> rs2 -> is_const );
1810+ if (!has_const )
1811+ return false;
1812+
1813+ switch (insn -> opcode ) {
1814+ case OP_add :
1815+ /* x + 0 = x or 0 + x = x */
1816+ if ((insn -> rs2 && insn -> rs2 -> is_const && insn -> rs2 -> init_val == 0 )) {
1817+ insn -> opcode = OP_assign ;
1818+ insn -> rs2 = NULL ;
1819+ return true;
1820+ }
1821+ if ((insn -> rs1 && insn -> rs1 -> is_const && insn -> rs1 -> init_val == 0 )) {
1822+ insn -> opcode = OP_assign ;
1823+ insn -> rs1 = insn -> rs2 ;
1824+ insn -> rs2 = NULL ;
1825+ return true;
1826+ }
1827+ break ;
1828+
1829+ case OP_sub :
1830+ /* x - 0 = x */
1831+ if (insn -> rs2 && insn -> rs2 -> is_const && insn -> rs2 -> init_val == 0 ) {
1832+ insn -> opcode = OP_assign ;
1833+ insn -> rs2 = NULL ;
1834+ return true;
1835+ }
1836+ /* 0 - x = -x */
1837+ if (insn -> rs1 && insn -> rs1 -> is_const && insn -> rs1 -> init_val == 0 ) {
1838+ insn -> opcode = OP_negate ;
1839+ insn -> rs1 = insn -> rs2 ;
1840+ insn -> rs2 = NULL ;
1841+ return true;
1842+ }
1843+ break ;
1844+
1845+ case OP_mul :
1846+ /* x * 0 = 0 or 0 * x = 0 */
1847+ if ((insn -> rs1 && insn -> rs1 -> is_const && insn -> rs1 -> init_val == 0 ) ||
1848+ (insn -> rs2 && insn -> rs2 -> is_const && insn -> rs2 -> init_val == 0 )) {
1849+ insn -> opcode = OP_load_constant ;
1850+ insn -> rd -> is_const = true;
1851+ insn -> rd -> init_val = 0 ;
1852+ insn -> rs1 = NULL ;
1853+ insn -> rs2 = NULL ;
1854+ return true;
1855+ }
1856+ /* x * 1 = x */
1857+ if (insn -> rs2 && insn -> rs2 -> is_const && insn -> rs2 -> init_val == 1 ) {
1858+ insn -> opcode = OP_assign ;
1859+ insn -> rs2 = NULL ;
1860+ return true;
1861+ }
1862+ /* 1 * x = x */
1863+ if (insn -> rs1 && insn -> rs1 -> is_const && insn -> rs1 -> init_val == 1 ) {
1864+ insn -> opcode = OP_assign ;
1865+ insn -> rs1 = insn -> rs2 ;
1866+ insn -> rs2 = NULL ;
1867+ return true;
1868+ }
1869+ /* Strength reduction: x * power-of-2 → x << shift */
1870+ if (insn -> rs2 && insn -> rs2 -> is_const ) {
1871+ int val = insn -> rs2 -> init_val ;
1872+ if (val > 0 && (val & (val - 1 )) == 0 ) {
1873+ int shift = 0 ;
1874+ while ((1 << shift ) != val )
1875+ shift ++ ;
1876+ insn -> opcode = OP_lshift ;
1877+ insn -> rs2 -> init_val = shift ;
1878+ return true;
1879+ }
1880+ }
1881+ break ;
1882+
1883+ case OP_div :
1884+ /* x / 1 = x */
1885+ if (insn -> rs2 && insn -> rs2 -> is_const && insn -> rs2 -> init_val == 1 ) {
1886+ insn -> opcode = OP_assign ;
1887+ insn -> rs2 = NULL ;
1888+ return true;
1889+ }
1890+ /* Strength reduction: x / power-of-2 → x >> shift (unsigned) */
1891+ if (insn -> rs2 && insn -> rs2 -> is_const ) {
1892+ int val = insn -> rs2 -> init_val ;
1893+ if (val > 0 && (val & (val - 1 )) == 0 ) {
1894+ int shift = 0 ;
1895+ while ((1 << shift ) != val )
1896+ shift ++ ;
1897+ insn -> opcode = OP_rshift ;
1898+ insn -> rs2 -> init_val = shift ;
1899+ return true;
1900+ }
1901+ }
1902+ break ;
1903+
1904+ case OP_bit_and :
1905+ /* x & 0 = 0 */
1906+ if ((insn -> rs1 && insn -> rs1 -> is_const && insn -> rs1 -> init_val == 0 ) ||
1907+ (insn -> rs2 && insn -> rs2 -> is_const && insn -> rs2 -> init_val == 0 )) {
1908+ insn -> opcode = OP_load_constant ;
1909+ insn -> rd -> is_const = true;
1910+ insn -> rd -> init_val = 0 ;
1911+ insn -> rs1 = NULL ;
1912+ insn -> rs2 = NULL ;
1913+ return true;
1914+ }
1915+ /* x & -1 = x */
1916+ if (insn -> rs2 && insn -> rs2 -> is_const && insn -> rs2 -> init_val == -1 ) {
1917+ insn -> opcode = OP_assign ;
1918+ insn -> rs2 = NULL ;
1919+ return true;
1920+ }
1921+ /* -1 & x = x */
1922+ if (insn -> rs1 && insn -> rs1 -> is_const && insn -> rs1 -> init_val == -1 ) {
1923+ insn -> opcode = OP_assign ;
1924+ insn -> rs1 = insn -> rs2 ;
1925+ insn -> rs2 = NULL ;
1926+ return true;
1927+ }
1928+ break ;
1929+
1930+ case OP_bit_or :
1931+ /* x | 0 = x */
1932+ if (insn -> rs2 && insn -> rs2 -> is_const && insn -> rs2 -> init_val == 0 ) {
1933+ insn -> opcode = OP_assign ;
1934+ insn -> rs2 = NULL ;
1935+ return true;
1936+ }
1937+ /* 0 | x = x */
1938+ if (insn -> rs1 && insn -> rs1 -> is_const && insn -> rs1 -> init_val == 0 ) {
1939+ insn -> opcode = OP_assign ;
1940+ insn -> rs1 = insn -> rs2 ;
1941+ insn -> rs2 = NULL ;
1942+ return true;
1943+ }
1944+ /* x | -1 = -1 or -1 | x = -1 */
1945+ if ((insn -> rs1 && insn -> rs1 -> is_const && insn -> rs1 -> init_val == -1 ) ||
1946+ (insn -> rs2 && insn -> rs2 -> is_const && insn -> rs2 -> init_val == -1 )) {
1947+ insn -> opcode = OP_load_constant ;
1948+ insn -> rd -> is_const = true;
1949+ insn -> rd -> init_val = -1 ;
1950+ insn -> rs1 = NULL ;
1951+ insn -> rs2 = NULL ;
1952+ return true;
1953+ }
1954+ break ;
1955+
1956+ case OP_bit_xor :
1957+ /* x ^ 0 = x */
1958+ if (insn -> rs2 && insn -> rs2 -> is_const && insn -> rs2 -> init_val == 0 ) {
1959+ insn -> opcode = OP_assign ;
1960+ insn -> rs2 = NULL ;
1961+ return true;
1962+ }
1963+ /* 0 ^ x = x */
1964+ if (insn -> rs1 && insn -> rs1 -> is_const && insn -> rs1 -> init_val == 0 ) {
1965+ insn -> opcode = OP_assign ;
1966+ insn -> rs1 = insn -> rs2 ;
1967+ insn -> rs2 = NULL ;
1968+ return true;
1969+ }
1970+ break ;
1971+
1972+ case OP_lshift :
1973+ case OP_rshift :
1974+ /* x << 0 = x or x >> 0 = x */
1975+ if (insn -> rs2 && insn -> rs2 -> is_const && insn -> rs2 -> init_val == 0 ) {
1976+ insn -> opcode = OP_assign ;
1977+ insn -> rs2 = NULL ;
1978+ return true;
1979+ }
1980+ break ;
1981+
1982+ default :
1983+ break ;
1984+ }
1985+
1986+ return false;
1987+ }
1988+
1989+ /* NOTE: Self-operation patterns (x-x, x^x, x&x, x|x) are better handled
1990+ * at peephole level after register allocation, where we can see actual
1991+ * register usage patterns. Removed from SSA level.
1992+ */
1993+
17981994bool const_folding (insn_t * insn )
17991995{
18001996 if (mark_const (insn ))
@@ -1803,6 +1999,9 @@ bool const_folding(insn_t *insn)
18031999 return true;
18042000 if (eval_const_unary (insn ))
18052001 return true;
2002+ if (eval_algebraic (insn ))
2003+ return true;
2004+
18062005 return false;
18072006}
18082007
0 commit comments