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  * @modules jdk.incubator.code
 27  * @run junit TestNormalizeBlocksTransformer
 28  */
 29 
 30 import jdk.incubator.code.Op;
 31 import jdk.incubator.code.dialect.core.NormalizeBlocksTransformer;
 32 import jdk.incubator.code.dialect.java.JavaOp;
 33 import jdk.incubator.code.extern.OpParser;
 34 import org.junit.jupiter.api.Assertions;
 35 import org.junit.jupiter.params.ParameterizedTest;
 36 import org.junit.jupiter.params.provider.MethodSource;
 37 
 38 import java.util.stream.Stream;
 39 
 40 public class TestNormalizeBlocksTransformer {
 41     static final String TEST1_INPUT = """
 42             func @"f" (%0 : java.type:"int")java.type:"int" -> {
 43                 %1 : java.type:"int" = invoke @java.ref:"C::m():int";
 44                 branch ^block_1;
 45 
 46               ^block_1:
 47                 %2 : java.type:"int" = invoke %1 @java.ref:"C::m(int):int";
 48                 branch ^block_2(%2);
 49 
 50               ^block_2(%3: java.type:"int"):
 51                 %4 : java.type:"int" = invoke %2 %3 @java.ref:"C::m(int, int):int";
 52                 branch ^block_3(%3);
 53 
 54               ^block_3(%5: java.type:"int"):
 55                 %6 : java.type:"int" = invoke %4 %3 %5 @java.ref:"C::m(int, int, int):int";
 56                 branch ^block_4;
 57 
 58               ^block_4:
 59                 return %6;
 60             };
 61             """;
 62     static final String TEST1_EXPECTED = """
 63             func @"f" (%0 : java.type:"int")java.type:"int" -> {
 64                 %1 : java.type:"int" = invoke @java.ref:"C::m():int";
 65                 %2 : java.type:"int" = invoke %1 @java.ref:"C::m(int):int";
 66                 %3 : java.type:"int" = invoke %2 %2 @java.ref:"C::m(int, int):int";
 67                 %4 : java.type:"int" = invoke %3 %2 %2 @java.ref:"C::m(int, int, int):int";
 68                 return %4;
 69             };
 70             """;
 71 
 72     static final String TEST2_INPUT = """
 73             func @"f" (%0 : java.type:"java.lang.Object")java.type:"void" -> {
 74                 %1 : Var<java.type:"java.lang.Object"> = var %0 @"o";
 75                 exception.region.enter ^block_1 ^block_8 ^block_3;
 76 
 77               ^block_1:
 78                 %3 : java.type:"int" = invoke @java.ref:"A::try_():int";
 79                 branch ^block_2;
 80 
 81               ^block_2:
 82                 exception.region.exit ^block_6 ^block_3 ^block_8;
 83 
 84               ^block_3(%4 : java.type:"java.lang.RuntimeException"):
 85                 exception.region.enter ^block_4 ^block_8;
 86 
 87               ^block_4:
 88                 %6 : Var<java.type:"java.lang.RuntimeException"> = var %4 @"e";
 89                 branch ^block_5;
 90 
 91               ^block_5:
 92                 exception.region.exit ^block_6 ^block_8;
 93 
 94               ^block_6:
 95                 %7 : java.type:"int" = invoke @java.ref:"A::finally_():int";
 96                 branch ^block_7;
 97 
 98               ^block_7:
 99                 return;
100 
101               ^block_8(%8 : java.type:"java.lang.Throwable"):
102                 %9 : java.type:"int" = invoke @java.ref:"A::finally_():int";
103                 throw %8;
104             };
105             """;
106     static final String TEST2_EXPECTED = """
107             func @"f" (%0 : java.type:"java.lang.Object")java.type:"void" -> {
108                 %1 : Var<java.type:"java.lang.Object"> = var %0 @"o";
109                 exception.region.enter ^block_1 ^block_5 ^block_2;
110 
111               ^block_1:
112                 %3 : java.type:"int" = invoke @java.ref:"A::try_():int";
113                 exception.region.exit ^block_4 ^block_2 ^block_5;
114 
115               ^block_2(%4 : java.type:"java.lang.RuntimeException"):
116                 exception.region.enter ^block_3 ^block_5;
117 
118               ^block_3:
119                 %6 : Var<java.type:"java.lang.RuntimeException"> = var %4 @"e";
120                 exception.region.exit ^block_4 ^block_5;
121 
122               ^block_4:
123                 %7 : java.type:"int" = invoke @java.ref:"A::finally_():int";
124                 return;
125 
126               ^block_5(%8 : java.type:"java.lang.Throwable"):
127                 %9 : java.type:"int" = invoke @java.ref:"A::finally_():int";
128                 throw %8;
129             };""";
130 
131     static final String TEST3_INPUT = """
132             func @"f" (%0 : java.type:"int")java.type:"int" -> {
133                 %1 : java.type:"int" = constant @0;
134                 %2 : java.type:"boolean" = gt %0 %1;
135                 cbranch %2 ^block_1 ^block_2;
136 
137               ^block_1:
138                 %3 : java.type:"int" = constant @1;
139                 branch ^block_1_1;
140 
141               ^block_1_1:
142                 branch ^block_3(%3);
143 
144               ^block_2:
145                 %4 : java.type:"int" = constant @-1;
146                 branch ^block_2_1;
147 
148               ^block_2_1:
149                 branch ^block_3(%4);
150 
151               ^block_3(%5 : java.type:"int"):
152                 return %5;
153             };""";
154     static final String TEST3_EXPECTED = """
155             func @"f" (%0 : java.type:"int")java.type:"int" -> {
156                 %1 : java.type:"int" = constant @0;
157                 %2 : java.type:"boolean" = gt %0 %1;
158                 cbranch %2 ^block_1 ^block_2;
159 
160               ^block_1:
161                 %3 : java.type:"int" = constant @1;
162                 branch ^block_3(%3);
163 
164               ^block_2:
165                 %4 : java.type:"int" = constant @-1;
166                 branch ^block_3(%4);
167 
168               ^block_3(%5 : java.type:"int"):
169                 return %5;
170             };
171             """;
172 
173     static final String TEST4_INPUT = """
174             func @"f" (%0 : java.type:"int")java.type:"int" -> {
175                 %1 : java.type:"int" = constant @0;
176                 %2 : java.type:"boolean" = gt %0 %1;
177                 cbranch %2 ^block_1 ^block_2;
178 
179               ^block_1:
180                 %3 : java.type:"int" = constant @1;
181                 branch ^block_1_1;
182 
183               ^block_1_1:
184                 branch ^block_3(%0, %3, %1);
185 
186               ^block_2:
187                 %4 : java.type:"int" = constant @-1;
188                 branch ^block_2_1;
189 
190               ^block_2_1:
191                 branch ^block_3(%0, %4, %1);
192 
193               ^block_3(%unused_1 : java.type:"int", %5 : java.type:"int", %unused_2 : java.type:"int"):
194                 return %5;
195             };""";
196     static final String TEST4_EXPECTED = """
197             func @"f" (%0 : java.type:"int")java.type:"int" -> {
198                 %1 : java.type:"int" = constant @0;
199                 %2 : java.type:"boolean" = gt %0 %1;
200                 cbranch %2 ^block_1 ^block_2;
201 
202               ^block_1:
203                 %3 : java.type:"int" = constant @1;
204                 branch ^block_3(%3);
205 
206               ^block_2:
207                 %4 : java.type:"int" = constant @-1;
208                 branch ^block_3(%4);
209 
210               ^block_3(%5 : java.type:"int"):
211                 return %5;
212             };
213             """;
214 
215     static final String TEST5_INPUT = """
216             func @"f" ()java.type:"void" -> {
217                 exception.region.enter ^block_1 ^block_4;
218 
219               ^block_1:
220                 invoke @java.ref:"A::m():void";
221                 branch ^block_2;
222 
223               ^block_2:
224                 exception.region.exit ^block_3 ^block_4;
225 
226               ^block_3:
227                 branch ^block_5;
228 
229               ^block_4(%1 : java.type:"java.lang.Throwable"):
230                 branch ^block_5;
231 
232               ^block_5:
233                 return;
234             };
235             """;
236     static final String TEST5_EXPECTED = """
237             func @"f" ()java.type:"void" -> {
238                 exception.region.enter ^block_1 ^block_3;
239 
240               ^block_1:
241                 invoke @java.ref:"A::m():void";
242                 exception.region.exit ^block_2 ^block_3;
243 
244               ^block_2:
245                 branch ^block_4;
246 
247               ^block_3(%1 : java.type:"java.lang.Throwable"):
248                 branch ^block_4;
249 
250               ^block_4:
251                 return;
252             };
253             """;
254 
255     static final String TEST6_INPUT = """
256             func @"m" (%0 : java.type:"java.lang.Object")java.type:"int" -> {
257                 %1 : java.type:"java.lang.Object" = constant @null;
258                 %2 : java.type:"boolean" = invoke %0 %1 @java.ref:"java.util.Objects::equals(java.lang.Object, java.lang.Object):boolean";
259                 cbranch %2 ^block_1 ^block_2;
260 
261               ^block_1:
262                 %3 : java.type:"java.lang.NullPointerException" = new @java.ref:"java.lang.NullPointerException::()";
263                 throw %3;
264 
265               ^block_2:
266                 %4 : java.type:"boolean" = instanceof %0 @java.type:"java.util.List";
267                 cbranch %4 ^block_3 ^block_5;
268 
269               ^block_3:
270                 %5 : java.type:"java.util.List" = cast %0 @java.type:"java.util.List";
271                 branch ^block_4;
272 
273               ^block_4:
274                 %6 : java.type:"boolean" = constant @true;
275                 branch ^block_6(%6);
276 
277               ^block_5:
278                 %7 : java.type:"boolean" = constant @false;
279                 branch ^block_6(%7);
280 
281               ^block_6(%8 : java.type:"boolean"):
282                 cbranch %8 ^block_7 ^block_8;
283 
284               ^block_7:
285                 %9 : java.type:"int" = constant @1;
286                 branch ^block_21(%9);
287 
288               ^block_8:
289                 %10 : java.type:"boolean" = instanceof %0 @java.type:"java.lang.String";
290                 cbranch %10 ^block_9 ^block_11;
291 
292               ^block_9:
293                 %11 : java.type:"java.lang.String" = cast %0 @java.type:"java.lang.String";
294                 branch ^block_10;
295 
296               ^block_10:
297                 %12 : java.type:"boolean" = constant @true;
298                 branch ^block_12(%12);
299 
300               ^block_11:
301                 %13 : java.type:"boolean" = constant @false;
302                 branch ^block_12(%13);
303 
304               ^block_12(%14 : java.type:"boolean"):
305                 cbranch %14 ^block_13 ^block_14;
306 
307               ^block_13:
308                 %15 : java.type:"int" = constant @2;
309                 branch ^block_21(%15);
310 
311               ^block_14:
312                 %16 : java.type:"boolean" = instanceof %0 @java.type:"java.util.Map";
313                 cbranch %16 ^block_15 ^block_17;
314 
315               ^block_15:
316                 %17 : java.type:"java.util.Map" = cast %0 @java.type:"java.util.Map";
317                 branch ^block_16;
318 
319               ^block_16:
320                 %18 : java.type:"boolean" = constant @true;
321                 branch ^block_18(%18);
322 
323               ^block_17:
324                 %19 : java.type:"boolean" = constant @false;
325                 branch ^block_18(%19);
326 
327               ^block_18(%20 : java.type:"boolean"):
328                 cbranch %20 ^block_19 ^block_20;
329 
330               ^block_19:
331                 %21 : java.type:"int" = constant @3;
332                 branch ^block_21(%21);
333 
334               ^block_20:
335                 %22 : java.type:"int" = constant @-1;
336                 branch ^block_21(%22);
337 
338               ^block_21(%23 : java.type:"int"):
339                 return %23;
340             };
341             """;
342     static final String TEST6_EXPECTED = """
343             func @"m" (%0 : java.type:"java.lang.Object")java.type:"int" -> {
344                 %1 : java.type:"java.lang.Object" = constant @null;
345                 %2 : java.type:"boolean" = invoke %0 %1 @java.ref:"java.util.Objects::equals(java.lang.Object, java.lang.Object):boolean";
346                 cbranch %2 ^block_1 ^block_2;
347 
348               ^block_1:
349                 %3 : java.type:"java.lang.NullPointerException" = new @java.ref:"java.lang.NullPointerException::()";
350                 throw %3;
351 
352               ^block_2:
353                 %4 : java.type:"boolean" = instanceof %0 @java.type:"java.util.List";
354                 cbranch %4 ^block_3 ^block_4;
355 
356               ^block_3:
357                 %5 : java.type:"java.util.List" = cast %0 @java.type:"java.util.List";
358                 %6 : java.type:"int" = constant @1;
359                 branch ^block_9(%6);
360 
361               ^block_4:
362                 %7 : java.type:"boolean" = instanceof %0 @java.type:"java.lang.String";
363                 cbranch %7 ^block_5 ^block_6;
364 
365               ^block_5:
366                 %8 : java.type:"java.lang.String" = cast %0 @java.type:"java.lang.String";
367                 %9 : java.type:"int" = constant @2;
368                 branch ^block_9(%9);
369 
370               ^block_6:
371                 %10 : java.type:"boolean" = instanceof %0 @java.type:"java.util.Map";
372                 cbranch %10 ^block_7 ^block_8;
373 
374               ^block_7:
375                 %11 : java.type:"java.util.Map" = cast %0 @java.type:"java.util.Map";
376                 %12 : java.type:"int" = constant @3;
377                 branch ^block_9(%12);
378 
379               ^block_8:
380                 %13 : java.type:"int" = constant @-1;
381                 branch ^block_9(%13);
382 
383               ^block_9(%14 : java.type:"int"):
384                 return %14;
385             };
386             """;
387     static Object[][] testModels() {
388         return new Object[][]{
389                 parse(TEST1_INPUT, TEST1_EXPECTED),
390                 parse(TEST2_INPUT, TEST2_EXPECTED),
391                 parse(TEST3_INPUT, TEST3_EXPECTED),
392                 parse(TEST4_INPUT, TEST4_EXPECTED),
393                 parse(TEST5_INPUT, TEST5_EXPECTED),
394                 parse(TEST6_INPUT, TEST6_EXPECTED),
395         };
396     }
397 
398     static Object[] parse(String... models) {
399         return Stream.of(models).map(s -> OpParser.fromString(JavaOp.JAVA_DIALECT_FACTORY, s).getFirst())
400                 .toArray(Object[]::new);
401     }
402 
403     @ParameterizedTest
404     @MethodSource("testModels")
405     public void test(Op input, Op expected) {
406         Op actual = NormalizeBlocksTransformer.transform(input);
407         Assertions.assertEquals(expected.toText(), actual.toText());
408     }
409 }