|
32 | 32 | import java.util.HashMap; |
33 | 33 | import java.util.HashSet; |
34 | 34 | import java.util.IdentityHashMap; |
| 35 | +import java.util.List; |
35 | 36 | import java.util.Map; |
36 | 37 | import java.util.Set; |
37 | 38 | import java.util.function.Supplier; |
@@ -69,7 +70,7 @@ public class AwkTuples implements Serializable { |
69 | 70 | * can be serialized and patched efficiently. A linked list would make every |
70 | 71 | * lookup O(n) and complicate address reassignment. |
71 | 72 | */ |
72 | | - private java.util.List<Tuple> queue = new ArrayList<Tuple>(100) { |
| 73 | + private List<Tuple> queue = new ArrayList<Tuple>(100) { |
73 | 74 | private static final long serialVersionUID = -6334362156408598578L; |
74 | 75 |
|
75 | 76 | @Override |
@@ -296,11 +297,25 @@ public void length(int numExprs) { |
296 | 297 |
|
297 | 298 | /** |
298 | 299 | * <p> |
299 | | - * concat. |
| 300 | + * Concatenates two stack items. |
300 | 301 | * </p> |
301 | 302 | */ |
302 | 303 | public void concat() { |
303 | | - queue.add(new Tuple.NoOperandTuple(Opcode.CONCAT)); |
| 304 | + concat(2); |
| 305 | + } |
| 306 | + |
| 307 | + /** |
| 308 | + * <p> |
| 309 | + * Concatenates the requested number of stack items. |
| 310 | + * </p> |
| 311 | + * |
| 312 | + * @param count the number of stack items to concatenate |
| 313 | + */ |
| 314 | + public void concat(int count) { |
| 315 | + if (count < 2) { |
| 316 | + throw new IllegalArgumentException("CONCAT requires at least two stack items"); |
| 317 | + } |
| 318 | + queue.add(new Tuple.CountTuple(Opcode.CONCAT, count)); |
304 | 319 | } |
305 | 320 |
|
306 | 321 | /** |
@@ -1878,10 +1893,10 @@ private boolean peepholeOptimizePass() { |
1878 | 1893 | return false; |
1879 | 1894 | } |
1880 | 1895 |
|
1881 | | - java.util.List<Tuple> original = new ArrayList<Tuple>(queue); |
| 1896 | + List<Tuple> original = new ArrayList<Tuple>(queue); |
1882 | 1897 | int[] indexMapping = new int[originalSize]; |
1883 | 1898 | Arrays.fill(indexMapping, -1); |
1884 | | - java.util.List<Tuple> optimizedQueue = new ArrayList<Tuple>(originalSize); |
| 1899 | + List<Tuple> optimizedQueue = new ArrayList<Tuple>(originalSize); |
1885 | 1900 | boolean[] isAddressTarget = addressTargets(original, originalSize); |
1886 | 1901 |
|
1887 | 1902 | boolean modified = false; |
@@ -1929,6 +1944,20 @@ private boolean peepholeOptimizePass() { |
1929 | 1944 | continue; |
1930 | 1945 | } |
1931 | 1946 | } |
| 1947 | + Tuple countedStringConcat = foldCountedStringConcat(original, oldIndex); |
| 1948 | + if (countedStringConcat != null) { |
| 1949 | + int count = countStringPushes(original, oldIndex); |
| 1950 | + // Fold a counted literal-only concatenation into one string push, |
| 1951 | + // e.g. PUSH_STRING "a", PUSH_STRING "b", CONCAT 2 -> PUSH_STRING |
| 1952 | + // "ab". Numeric literals are deliberately not folded here because |
| 1953 | + // their string representation depends on runtime formatting state. |
| 1954 | + optimizedQueue.add(countedStringConcat); |
| 1955 | + mapFoldedRange(indexMapping, oldIndex, count + 1, newIndex); |
| 1956 | + oldIndex += count + 1; |
| 1957 | + newIndex++; |
| 1958 | + modified = true; |
| 1959 | + continue; |
| 1960 | + } |
1932 | 1961 | if ((oldIndex + 2) < originalSize) { |
1933 | 1962 | Tuple nextTuple = original.get(oldIndex + 1); |
1934 | 1963 | Tuple opTuple = original.get(oldIndex + 2); |
@@ -1987,7 +2016,7 @@ private boolean peepholeOptimizePass() { |
1987 | 2016 | return true; |
1988 | 2017 | } |
1989 | 2018 |
|
1990 | | - private boolean[] addressTargets(java.util.List<Tuple> tuples, int tupleCount) { |
| 2019 | + private boolean[] addressTargets(List<Tuple> tuples, int tupleCount) { |
1991 | 2020 | boolean[] targets = new boolean[tupleCount]; |
1992 | 2021 | for (Tuple tuple : tuples) { |
1993 | 2022 | Address address = tuple.getAddress(); |
@@ -2020,6 +2049,47 @@ private Object literalValue(Tuple tuple) { |
2020 | 2049 | } |
2021 | 2050 | } |
2022 | 2051 |
|
| 2052 | + private Tuple foldCountedStringConcat(List<Tuple> original, int oldIndex) { |
| 2053 | + Tuple firstTuple = original.get(oldIndex); |
| 2054 | + if (firstTuple.getOpcode() != Opcode.PUSH_STRING) { |
| 2055 | + return null; |
| 2056 | + } |
| 2057 | + |
| 2058 | + int tupleCount = original.size(); |
| 2059 | + StringBuilder folded = new StringBuilder(); |
| 2060 | + int itemCount = 0; |
| 2061 | + int currentIndex = oldIndex; |
| 2062 | + while (currentIndex < tupleCount && original.get(currentIndex).getOpcode() == Opcode.PUSH_STRING) { |
| 2063 | + folded.append(((Tuple.PushStringTuple) original.get(currentIndex)).getValue()); |
| 2064 | + itemCount++; |
| 2065 | + currentIndex++; |
| 2066 | + } |
| 2067 | + |
| 2068 | + if (itemCount < 2 || currentIndex >= tupleCount) { |
| 2069 | + return null; |
| 2070 | + } |
| 2071 | + |
| 2072 | + Tuple operation = original.get(currentIndex); |
| 2073 | + if (operation.getOpcode() != Opcode.CONCAT || !(operation instanceof Tuple.CountTuple)) { |
| 2074 | + return null; |
| 2075 | + } |
| 2076 | + |
| 2077 | + long count = ((Tuple.CountTuple) operation).getCount(); |
| 2078 | + if (count != itemCount) { |
| 2079 | + return null; |
| 2080 | + } |
| 2081 | + |
| 2082 | + return createLiteralPush(folded.toString(), firstTuple.getLineNumber()); |
| 2083 | + } |
| 2084 | + |
| 2085 | + private int countStringPushes(List<Tuple> original, int oldIndex) { |
| 2086 | + int currentIndex = oldIndex; |
| 2087 | + while (currentIndex < original.size() && original.get(currentIndex).getOpcode() == Opcode.PUSH_STRING) { |
| 2088 | + currentIndex++; |
| 2089 | + } |
| 2090 | + return currentIndex - oldIndex; |
| 2091 | + } |
| 2092 | + |
2023 | 2093 | private Object foldBinary(Object left, Object right, Tuple operation) { |
2024 | 2094 | Opcode opcode = operation.getOpcode(); |
2025 | 2095 | if (opcode == null) { |
|
0 commit comments