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 testng TestNormalizeBlocksTransformer
 28  */
 29 
 30 import org.testng.Assert;
 31 import org.testng.annotations.DataProvider;
 32 import org.testng.annotations.Test;
 33 
 34 import jdk.incubator.code.Op;
 35 import jdk.incubator.code.analysis.NormalizeBlocksTransformer;
 36 import jdk.incubator.code.op.ExtendedOp;
 37 import jdk.incubator.code.parser.OpParser;
 38 import java.util.stream.Stream;
 39 
 40 public class TestNormalizeBlocksTransformer {
 41     static final String TEST1_INPUT = """
 42             func @"f" (%0 : int)int -> {
 43                 %1 : int = invoke @"C::m()int";
 44                 branch ^block_1;
 45 
 46               ^block_1:
 47                 %2 : int = invoke %1 @"C::m(int)int";
 48                 branch ^block_2(%2);
 49 
 50               ^block_2(%3: int):
 51                 %4 : int = invoke %2 %3 @"C::m(int, int)int";
 52                 branch ^block_3(%3);
 53 
 54               ^block_3(%5: int):
 55                 %6 : int = invoke %4 %3 %5 @"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 : int)int -> {
 64                 %1 : int = invoke @"C::m()int";
 65                 %2 : int = invoke %1 @"C::m(int)int";
 66                 %3 : int = invoke %2 %2 @"C::m(int, int)int";
 67                 %4 : int = invoke %3 %2 %2 @"C::m(int, int, int)int";
 68                 return %4;
 69             };
 70             """;
 71 
 72     static final String TEST2_INPUT = """
 73             func @"f" (%0 : java.lang.Object)void -> {
 74                 %1 : Var<java.lang.Object> = var %0 @"o";
 75                 exception.region.enter ^block_1 ^block_8 ^block_3;
 76 
 77               ^block_1:
 78                 %3 : int = invoke @"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.lang.RuntimeException):
 85                 exception.region.enter ^block_4 ^block_8;
 86 
 87               ^block_4:
 88                 %6 : Var<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 : int = invoke @"A::finally_()int";
 96                 branch ^block_7;
 97 
 98               ^block_7:
 99                 return;
100 
101               ^block_8(%8 : java.lang.Throwable):
102                 %9 : int = invoke @"A::finally_()int";
103                 throw %8;
104             };
105             """;
106     static final String TEST2_EXPECTED = """
107             func @"f" (%0 : java.lang.Object)void -> {
108                 %1 : Var<java.lang.Object> = var %0 @"o";
109                 exception.region.enter ^block_1 ^block_5 ^block_2;
110 
111               ^block_1:
112                 %3 : int = invoke @"A::try_()int";
113                 exception.region.exit ^block_4 ^block_2 ^block_5;
114 
115               ^block_2(%4 : java.lang.RuntimeException):
116                 exception.region.enter ^block_3 ^block_5;
117 
118               ^block_3:
119                 %6 : Var<java.lang.RuntimeException> = var %4 @"e";
120                 exception.region.exit ^block_4 ^block_5;
121 
122               ^block_4:
123                 %7 : int = invoke @"A::finally_()int";
124                 return;
125 
126               ^block_5(%8 : java.lang.Throwable):
127                 %9 : int = invoke @"A::finally_()int";
128                 throw %8;
129             };""";
130 
131     static final String TEST3_INPUT = """
132             func @"f" (%0 : int)int -> {
133                 %1 : int = constant @"0";
134                 %2 : boolean = gt %0 %1;
135                 cbranch %2 ^block_1 ^block_2;
136 
137               ^block_1:
138                 %3 : int = constant @"1";
139                 branch ^block_1_1;
140 
141               ^block_1_1:
142                 branch ^block_3(%3);
143 
144               ^block_2:
145                 %4 : int = constant @"-1";
146                 branch ^block_2_1;
147 
148               ^block_2_1:
149                 branch ^block_3(%4);
150 
151               ^block_3(%5 : int):
152                 return %5;
153             };""";
154     static final String TEST3_EXPECTED = """
155             func @"f" (%0 : int)int -> {
156                 %1 : int = constant @"0";
157                 %2 : boolean = gt %0 %1;
158                 cbranch %2 ^block_1 ^block_2;
159 
160               ^block_1:
161                 %3 : int = constant @"1";
162                 branch ^block_3(%3);
163 
164               ^block_2:
165                 %4 : int = constant @"-1";
166                 branch ^block_3(%4);
167 
168               ^block_3(%5 : int):
169                 return %5;
170             };
171             """;
172 
173     static final String TEST4_INPUT = """
174             func @"f" (%0 : int)int -> {
175                 %1 : int = constant @"0";
176                 %2 : boolean = gt %0 %1;
177                 cbranch %2 ^block_1 ^block_2;
178 
179               ^block_1:
180                 %3 : 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 : 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 : int, %5 : int, %unused_2 : int):
194                 return %5;
195             };""";
196     static final String TEST4_EXPECTED = """
197             func @"f" (%0 : int)int -> {
198                 %1 : int = constant @"0";
199                 %2 : boolean = gt %0 %1;
200                 cbranch %2 ^block_1 ^block_2;
201 
202               ^block_1:
203                 %3 : int = constant @"1";
204                 branch ^block_3(%3);
205 
206               ^block_2:
207                 %4 : int = constant @"-1";
208                 branch ^block_3(%4);
209 
210               ^block_3(%5 : int):
211                 return %5;
212             };
213             """;
214 
215     static final String TEST5_INPUT = """
216             func @"f" ()void -> {
217                 exception.region.enter ^block_1 ^block_4;
218 
219               ^block_1:
220                 invoke @"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.lang.Throwable):
230                 branch ^block_5;
231 
232               ^block_5:
233                 return;
234             };
235             """;
236     static final String TEST5_EXPECTED = """
237             func @"f" ()void -> {
238                 exception.region.enter ^block_1 ^block_3;
239 
240               ^block_1:
241                 invoke @"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.lang.Throwable):
248                 branch ^block_4;
249 
250               ^block_4:
251                 return;
252             };
253             """;
254 
255     @DataProvider
256     static Object[][] testModels() {
257         return new Object[][]{
258                 parse(TEST1_INPUT, TEST1_EXPECTED),
259                 parse(TEST2_INPUT, TEST2_EXPECTED),
260                 parse(TEST3_INPUT, TEST3_EXPECTED),
261                 parse(TEST4_INPUT, TEST4_EXPECTED),
262                 parse(TEST5_INPUT, TEST5_EXPECTED),
263         };
264     }
265 
266     static Object[] parse(String... models) {
267         return Stream.of(models).map(s -> OpParser.fromString(ExtendedOp.FACTORY, s).getFirst())
268                 .toArray(Object[]::new);
269     }
270 
271     @Test(dataProvider = "testModels")
272     public void test(Op input, Op expected) {
273         Op actual = NormalizeBlocksTransformer.transform(input);
274         Assert.assertEquals(actual.toText(), expected.toText());
275     }
276 }