@@ -136,9 +136,84 @@ public void foldsLiteralStringConcatenation() throws Exception {
136136 AwkProgram tuples = new Awk ().compile (script );
137137 List <Opcode > opcodes = collectOpcodes (tuples );
138138 assertFalse ("Literal concatenation should eliminate CONCAT tuple" , opcodes .contains (Opcode .CONCAT ));
139+ assertFalse ("Literal concatenation should eliminate MULTI_CONCAT tuple" , opcodes .contains (Opcode .MULTI_CONCAT ));
139140 assertTrue ("Expected folded literal push of foobar" , hasLiteralPush (tuples , "foobar" ));
140141 }
141142
143+ @ Test
144+ public void foldsChainedLiteralStringConcatenation () throws Exception {
145+ String script = "BEGIN { print \" foo\" \" bar\" \" baz\" \" qux\" }\n " ;
146+ AwkTestSupport
147+ .awkTest ("folds chained literal string concatenation" )
148+ .script (script )
149+ .expect ("foobarbazqux\n " )
150+ .runAndAssert ();
151+
152+ AwkProgram tuples = new Awk ().compile (script );
153+ List <Opcode > opcodes = collectOpcodes (tuples );
154+ assertFalse ("Chained literal concatenation should eliminate CONCAT tuple" , opcodes .contains (Opcode .CONCAT ));
155+ assertFalse (
156+ "Chained literal concatenation should eliminate MULTI_CONCAT tuple" ,
157+ opcodes .contains (Opcode .MULTI_CONCAT ));
158+ assertTrue ("Expected folded literal push of foobarbazqux" , hasLiteralPush (tuples , "foobarbazqux" ));
159+ }
160+
161+ @ Test
162+ public void optimizesChainedStringConcatenationAsSingleMultiConcat () throws Exception {
163+ String script = "BEGIN { s1 = \" alpha\" ; s2 = \" beta\" ; print s1 \" -\" s2 \" :\" }\n " ;
164+ AwkTestSupport
165+ .awkTest ("counted chained string concatenation" )
166+ .script (script )
167+ .expect ("alpha-beta:\n " )
168+ .runAndAssert ();
169+
170+ AwkProgram tuples = new Awk ().compile (script );
171+ assertEquals (
172+ "Expected one counted MULTI_CONCAT for the mixed chain" ,
173+ 1 ,
174+ countOpcodeWithCount (tuples , Opcode .MULTI_CONCAT , 4 ));
175+ assertEquals ("Optimized mixed chain should not keep binary CONCAT tuples" , 0 , countOpcode (tuples , Opcode .CONCAT ));
176+ }
177+
178+ @ Test
179+ public void keepsParserConcatenationBinaryWhenOptimizationDisabled () throws Exception {
180+ String script = "BEGIN { s1 = \" alpha\" ; s2 = \" beta\" ; print s1 \" -\" s2 \" :\" }\n " ;
181+ AwkProgram tuples = new Awk ().compile (script , true );
182+
183+ assertEquals (
184+ "Unoptimized parser output should keep one binary CONCAT per expression pair" ,
185+ 3 ,
186+ countOpcode (tuples , Opcode .CONCAT ));
187+ assertEquals (
188+ "Unoptimized parser output should not emit counted chain MULTI_CONCAT" ,
189+ 0 ,
190+ countOpcode (tuples , Opcode .MULTI_CONCAT ));
191+ }
192+
193+ @ Test
194+ public void keepsConcatRunWhenFirstConcatIsBranchTarget () {
195+ AwkTuples tuples = new AwkTuples ();
196+ tuples .pushSourceLineNumber (1 );
197+ Address concatTarget = tuples .createAddress ("concat-target" );
198+
199+ tuples .dereference (1 , false , true );
200+ tuples .ifFalse (concatTarget );
201+ tuples .dereference (2 , false , true );
202+ tuples .dereference (3 , false , true );
203+ tuples .address (concatTarget );
204+ tuples .concat ();
205+ tuples .dereference (4 , false , true );
206+ tuples .concat ();
207+
208+ tuples .optimize ();
209+
210+ assertEquals ("Targeted CONCAT run should remain binary" , 2 , countOpcode (tuples , Opcode .CONCAT ));
211+ assertEquals (
212+ "Targeted CONCAT run should not be folded into MULTI_CONCAT" ,
213+ 0 ,
214+ countOpcode (tuples , Opcode .MULTI_CONCAT ));
215+ }
216+
142217 @ Test
143218 public void foldsScalarAssignmentPopIntoNonPushingAssignment () throws Exception {
144219 String script = "BEGIN { a = -2; b = 2; c = 4; print a + b + c }\n " ;
@@ -204,6 +279,11 @@ public void doesNotFoldNumericConcatenation() throws Exception {
204279 AwkProgram tuples = new Awk ().compile (script );
205280 List <Opcode > opcodes = collectOpcodes (tuples );
206281 assertTrue ("Numeric literal concatenation should preserve CONCAT tuple" , opcodes .contains (Opcode .CONCAT ));
282+ assertEquals ("Numeric literal concatenation should remain binary" , 1 , countOpcode (tuples , Opcode .CONCAT ));
283+ assertEquals (
284+ "Binary numeric literal concatenation should not use MULTI_CONCAT" ,
285+ 0 ,
286+ countOpcode (tuples , Opcode .MULTI_CONCAT ));
207287 assertFalse ("Optimizer should not fold numeric/string concatenation" , hasLiteralPush (tuples , "1x" ));
208288 }
209289
@@ -559,8 +639,12 @@ private static boolean hasAddressTargetWithPredecessor(AwkProgram tuples, Opcode
559639 }
560640
561641 private static int countOpcode (AwkProgram tuples , Opcode opcode ) {
642+ return countOpcode (rawTuples (tuples ), opcode );
643+ }
644+
645+ private static int countOpcode (AwkTuples tuples , Opcode opcode ) {
562646 int count = 0 ;
563- PositionTracker tracker = rawTuples ( tuples ) .top ();
647+ PositionTracker tracker = tuples .top ();
564648 while (!tracker .isEOF ()) {
565649 if (tracker .opcode () == opcode ) {
566650 count ++;
@@ -570,6 +654,20 @@ private static int countOpcode(AwkProgram tuples, Opcode opcode) {
570654 return count ;
571655 }
572656
657+ private static int countOpcodeWithCount (AwkProgram tuples , Opcode opcode , long expectedCount ) {
658+ int count = 0 ;
659+ PositionTracker tracker = rawTuples (tuples ).top ();
660+ while (!tracker .isEOF ()) {
661+ if (tracker .opcode () == opcode
662+ && tracker .current () instanceof Tuple .CountTuple
663+ && ((Tuple .CountTuple ) tracker .current ()).getCount () == expectedCount ) {
664+ count ++;
665+ }
666+ tracker .next ();
667+ }
668+ return count ;
669+ }
670+
573671 private static String dumpTuples (AwkProgram tuples ) throws Exception {
574672 ByteArrayOutputStream out = new ByteArrayOutputStream ();
575673 try (PrintStream ps = new PrintStream (out , true , StandardCharsets .UTF_8 .name ())) {
0 commit comments