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.analysis.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 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(JavaOp.JAVA_DIALECT_FACTORY, s).getFirst())
267 .toArray(Object[]::new);
268 }
269
270 @ParameterizedTest
271 @MethodSource("testModels")
272 public void test(Op input, Op expected) {
273 Op actual = NormalizeBlocksTransformer.transform(input);
274 Assertions.assertEquals(expected.toText(), actual.toText());
275 }
276 }