1 /*
  2  * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /*
 25  * @test
 26  * @run testng TestNormalizeBlocksTransformer
 27  */
 28 
 29 import org.testng.Assert;
 30 import org.testng.annotations.DataProvider;
 31 import org.testng.annotations.Test;
 32 
 33 import java.lang.reflect.code.Op;
 34 import java.lang.reflect.code.analysis.NormalizeBlocksTransformer;
 35 import java.lang.reflect.code.op.ExtendedOp;
 36 import java.lang.reflect.code.parser.OpParser;
 37 import java.util.stream.Stream;
 38 
 39 public class TestNormalizeBlocksTransformer {
 40     static final String TEST1_INPUT = """
 41             func @"f" (%0 : int)int -> {
 42                 %1 : int = invoke @"C::m()int";
 43                 branch ^block_1;
 44 
 45               ^block_1:
 46                 %2 : int = invoke %1 @"C::m(int)int";
 47                 branch ^block_2(%2);
 48 
 49               ^block_2(%3: int):
 50                 %4 : int = invoke %2 %3 @"C::m(int, int)int";
 51                 branch ^block_3(%3);
 52 
 53               ^block_3(%5: int):
 54                 %6 : int = invoke %4 %3 %5 @"C::m(int, int, int)int";
 55                 branch ^block_4;
 56 
 57               ^block_4:
 58                 return %6;
 59             };
 60             """;
 61     static final String TEST1_EXPECTED = """
 62             func @"f" (%0 : int)int -> {
 63                 %1 : int = invoke @"C::m()int";
 64                 %2 : int = invoke %1 @"C::m(int)int";
 65                 %3 : int = invoke %2 %2 @"C::m(int, int)int";
 66                 %4 : int = invoke %3 %2 %2 @"C::m(int, int, int)int";
 67                 return %4;
 68             };
 69             """;
 70 
 71     static final String TEST2_INPUT = """
 72             func @"f" (%0 : java.lang.Object)void -> {
 73                 %1 : Var<java.lang.Object> = var %0 @"o";
 74                 %2 : java.lang.reflect.code.op.CoreOp$ExceptionRegion = exception.region.enter ^block_1 ^block_3 ^block_8;
 75 
 76               ^block_1:
 77                 %3 : int = invoke @"A::try_()int";
 78                 branch ^block_2;
 79 
 80               ^block_2:
 81                 exception.region.exit %2 ^block_6;
 82 
 83               ^block_3(%4 : java.lang.RuntimeException):
 84                 %5 : java.lang.reflect.code.op.CoreOp$ExceptionRegion = exception.region.enter ^block_4 ^block_8;
 85 
 86               ^block_4:
 87                 %6 : Var<java.lang.RuntimeException> = var %4 @"e";
 88                 branch ^block_5;
 89 
 90               ^block_5:
 91                 exception.region.exit %5 ^block_6;
 92 
 93               ^block_6:
 94                 %7 : int = invoke @"A::finally_()int";
 95                 branch ^block_7;
 96 
 97               ^block_7:
 98                 return;
 99 
100               ^block_8(%8 : java.lang.Throwable):
101                 %9 : int = invoke @"A::finally_()int";
102                 throw %8;
103             };
104             """;
105     static final String TEST2_EXPECTED = """
106             func @"f" (%0 : java.lang.Object)void -> {
107                 %1 : Var<java.lang.Object> = var %0 @"o";
108                 %2 : java.lang.reflect.code.op.CoreOp$ExceptionRegion = exception.region.enter ^block_1 ^block_2 ^block_5;
109 
110               ^block_1:
111                 %3 : int = invoke @"A::try_()int";
112                 exception.region.exit %2 ^block_4;
113 
114               ^block_2(%4 : java.lang.RuntimeException):
115                 %5 : java.lang.reflect.code.op.CoreOp$ExceptionRegion = exception.region.enter ^block_3 ^block_5;
116 
117               ^block_3:
118                 %6 : Var<java.lang.RuntimeException> = var %4 @"e";
119                 exception.region.exit %5 ^block_4;
120 
121               ^block_4:
122                 %7 : int = invoke @"A::finally_()int";
123                 return;
124 
125               ^block_5(%8 : java.lang.Throwable):
126                 %9 : int = invoke @"A::finally_()int";
127                 throw %8;
128             };""";
129 
130     static final String TEST3_INPUT = """
131             func @"f" (%0 : int)int -> {
132                 %1 : int = constant @"0";
133                 %2 : boolean = gt %0 %1;
134                 cbranch %2 ^block_1 ^block_2;
135 
136               ^block_1:
137                 %3 : int = constant @"1";
138                 branch ^block_1_1;
139 
140               ^block_1_1:
141                 branch ^block_3(%3);
142 
143               ^block_2:
144                 %4 : int = constant @"-1";
145                 branch ^block_2_1;
146 
147               ^block_2_1:
148                 branch ^block_3(%4);
149 
150               ^block_3(%5 : int):
151                 return %5;
152             };""";
153     static final String TEST3_EXPECTED = """
154             func @"f" (%0 : int)int -> {
155                 %1 : int = constant @"0";
156                 %2 : boolean = gt %0 %1;
157                 cbranch %2 ^block_1 ^block_2;
158 
159               ^block_1:
160                 %3 : int = constant @"1";
161                 branch ^block_3(%3);
162 
163               ^block_2:
164                 %4 : int = constant @"-1";
165                 branch ^block_3(%4);
166 
167               ^block_3(%5 : int):
168                 return %5;
169             };
170             """;
171 
172     static final String TEST4_INPUT = """
173             func @"f" (%0 : int)int -> {
174                 %1 : int = constant @"0";
175                 %2 : boolean = gt %0 %1;
176                 cbranch %2 ^block_1 ^block_2;
177 
178               ^block_1:
179                 %3 : int = constant @"1";
180                 branch ^block_1_1;
181 
182               ^block_1_1:
183                 branch ^block_3(%0, %3, %1);
184 
185               ^block_2:
186                 %4 : int = constant @"-1";
187                 branch ^block_2_1;
188 
189               ^block_2_1:
190                 branch ^block_3(%0, %4, %1);
191 
192               ^block_3(%unused_1 : int, %5 : int, %unused_2 : int):
193                 return %5;
194             };""";
195     static final String TEST4_EXPECTED = """
196             func @"f" (%0 : int)int -> {
197                 %1 : int = constant @"0";
198                 %2 : boolean = gt %0 %1;
199                 cbranch %2 ^block_1 ^block_2;
200 
201               ^block_1:
202                 %3 : int = constant @"1";
203                 branch ^block_3(%3);
204 
205               ^block_2:
206                 %4 : int = constant @"-1";
207                 branch ^block_3(%4);
208 
209               ^block_3(%5 : int):
210                 return %5;
211             };
212             """;
213 
214     static final String TEST5_INPUT = """
215             func @"f" ()void -> {
216                 %0 : java.lang.reflect.code.op.CoreOp$ExceptionRegion = exception.region.enter ^block_1 ^block_4;
217 
218               ^block_1:
219                 invoke @"A::m()void";
220                 branch ^block_2;
221 
222               ^block_2:
223                 exception.region.exit %0 ^block_3;
224 
225               ^block_3:
226                 branch ^block_5;
227 
228               ^block_4(%1 : java.lang.Throwable):
229                 branch ^block_5;
230 
231               ^block_5:
232                 return;
233             };
234             """;
235     static final String TEST5_EXPECTED = """
236             func @"f" ()void -> {
237                 %0 : java.lang.reflect.code.op.CoreOp$ExceptionRegion = exception.region.enter ^block_1 ^block_3;
238 
239               ^block_1:
240                 invoke @"A::m()void";
241                 exception.region.exit %0 ^block_2;
242 
243               ^block_2:
244                 branch ^block_4;
245 
246               ^block_3(%1 : java.lang.Throwable):
247                 branch ^block_4;
248 
249               ^block_4:
250                 return;
251             };
252             """;
253 
254     @DataProvider
255     static Object[][] testModels() {
256         return new Object[][]{
257                 parse(TEST1_INPUT, TEST1_EXPECTED),
258                 parse(TEST2_INPUT, TEST2_EXPECTED),
259                 parse(TEST3_INPUT, TEST3_EXPECTED),
260                 parse(TEST4_INPUT, TEST4_EXPECTED),
261                 parse(TEST5_INPUT, TEST5_EXPECTED),
262         };
263     }
264 
265     static Object[] parse(String... models) {
266         return Stream.of(models).map(s -> OpParser.fromString(ExtendedOp.FACTORY, s).getFirst())
267                 .toArray(Object[]::new);
268     }
269 
270     @Test(dataProvider = "testModels")
271     public void test(Op input, Op expected) {
272         Op actual = NormalizeBlocksTransformer.transform(input);
273         Assert.assertEquals(actual.toText(), expected.toText());
274     }
275 }