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 import jdk.incubator.code.Block;
25 import jdk.incubator.code.Body;
26 import jdk.incubator.code.Op;
27 import jdk.incubator.code.Quotable;
28 import jdk.incubator.code.analysis.SSA;
29 import jdk.incubator.code.dialect.java.JavaOp;
30 import org.junit.jupiter.api.Assertions;
31 import org.junit.jupiter.api.Test;
32
33 import java.util.function.IntBinaryOperator;
34
35 import static jdk.incubator.code.dialect.core.CoreOp.*;
36 import static jdk.incubator.code.dialect.core.CoreType.FUNCTION_TYPE_VOID;
37 import static jdk.incubator.code.dialect.core.CoreType.functionType;
38 import static jdk.incubator.code.dialect.java.JavaType.INT;
39 import static jdk.incubator.code.dialect.java.JavaType.type;
40
41 /*
42 * @test
43 * @modules jdk.incubator.code
44 * @run junit TestBuild
45 * @run junit/othervm -Dbabylon.ssa=cytron TestBuild
46 */
47
48 public class TestBuild {
49
50 public JavaOp.LambdaOp f() {
51 IntBinaryOperator ibo = (IntBinaryOperator & Quotable) (a, b) -> a + b;
52 Quotable iboq = (Quotable) ibo;
53 return SSA.transform((JavaOp.LambdaOp) Op.ofQuotable(iboq).get().op());
54 }
55
56 @Test
57 public void testBoundValueAsOperand() {
58 JavaOp.LambdaOp f = f();
59
60 var body = Body.Builder.of(null, f.invokableType());
61 var block = body.entryBlock();
62
63 var a = f.body().entryBlock().parameters().get(0);
64 var b = f.body().entryBlock().parameters().get(1);
65 // Passing bound values as operands to a new unbound operation
66 var addop = JavaOp.add(a, b);
67
68 Assertions.assertThrows(IllegalStateException.class, () -> block.op(addop));
69 }
70
71 @Test
72 public void testBoundValueAsHeaderArgument() {
73 JavaOp.LambdaOp f = f();
74
75 var body = Body.Builder.of(null, f.invokableType());
76 var block = body.entryBlock();
77 var anotherBlock = block.block(INT, INT);
78
79 var a = f.body().entryBlock().parameters().get(0);
80 var b = f.body().entryBlock().parameters().get(1);
81 // Passing bound values as header arguments of a header
82 // that is the successor of a terminal operation
83 var brop = branch(anotherBlock.successor(a, b));
84
85 Assertions.assertThrows(IllegalStateException.class, () -> block.op(brop));
86 }
87
88 @Test
89 public void testUnmappedBoundValue() {
90 JavaOp.LambdaOp f = f();
91
92 var body = Body.Builder.of(null, f.invokableType());
93 var block = body.entryBlock();
94
95 var freturnOp = f.body().entryBlock().terminatingOp();
96 // Unmapped bound value that is operand of the bound return op
97 Assertions.assertThrows(IllegalArgumentException.class, () -> block.op(freturnOp));
98 }
99
100 @Test
101 public void testMappingToBoundValue() {
102 JavaOp.LambdaOp f = f();
103
104 var body = Body.Builder.of(null, f.invokableType());
105 var block = body.entryBlock();
106
107 var result = f.body().entryBlock().firstOp().result();
108 // Mapping to a bound value
109 Assertions.assertThrows(IllegalArgumentException.class, () -> block.context().mapValue(result, result));
110 }
111
112 @Test
113 public void testMappedBoundValue() {
114 JavaOp.LambdaOp f = f();
115
116 var body = Body.Builder.of(null, f.invokableType());
117 var block = body.entryBlock();
118
119 var a = block.parameters().get(0);
120 var b = block.parameters().get(1);
121 var result = block.op(JavaOp.add(a, b));
122 // Map the bound value used as the operand to the bound return op to
123 // the above value
124 block.context().mapValue(f.body().entryBlock().firstOp().result(), result);
125
126 var freturnOp = f.body().entryBlock().terminatingOp();
127 // No error since values (operands) are mapped
128 block.op(freturnOp);
129 }
130
131 @Test
132 public void testPartiallyConstructedValueAccess() {
133 var body = Body.Builder.of(null, functionType(INT, INT, INT));
134 var block = body.entryBlock();
135
136 Block.Parameter a = block.parameters().get(0);
137 Block.Parameter b = block.parameters().get(1);
138 Op.Result result = block.op(JavaOp.add(a, b));
139
140 // Access the declaring block of values before the block and its body are
141 // constructed
142 Assertions.assertThrows(IllegalStateException.class, a::declaringBlock);
143 Assertions.assertThrows(IllegalStateException.class, result::declaringBlock);
144 // Access to parent block/body of operation result before they are constructed
145 Assertions.assertThrows(IllegalStateException.class, result.op()::ancestorBlock);
146 Assertions.assertThrows(IllegalStateException.class, result.op()::ancestorBody);
147 // Access to set of users before constructed
148 Assertions.assertThrows(IllegalStateException.class, a::uses);
149
150 block.op(return_(result));
151
152 var f = func("f", body);
153
154 Assertions.assertNotNull(a.declaringBlock());
155 Assertions.assertNotNull(result.declaringBlock());
156 Assertions.assertNotNull(result.op().ancestorBlock());
157 Assertions.assertNotNull(result.op().ancestorBody());
158 Assertions.assertNotNull(a.uses());
159 }
160
161 @Test
162 public void testPartiallyConstructedHeaderAccess() {
163 var body = Body.Builder.of(null, functionType(INT, INT, INT));
164 var block = body.entryBlock();
165 var anotherBlock = block.block(INT, INT);
166
167 var a = block.parameters().get(0);
168 var b = block.parameters().get(1);
169 Block.Reference successor = anotherBlock.successor(a, b);
170 // Access to target block before constructed
171 Assertions.assertThrows(IllegalStateException.class, successor::targetBlock);
172 block.op(branch(anotherBlock.successor(a, b)));
173
174 a = anotherBlock.parameters().get(0);
175 b = anotherBlock.parameters().get(1);
176 var result = anotherBlock.op(JavaOp.add(a, b));
177 anotherBlock.op(return_(result));
178
179 var f = func("f", body);
180
181 Assertions.assertNotNull(successor.targetBlock());
182 }
183
184 @Test
185 public void testValueUseFromOtherModel() {
186 var abody = Body.Builder.of(null, functionType(INT, INT, INT));
187 var ablock = abody.entryBlock();
188 var aa = ablock.parameters().get(0);
189 var ab = ablock.parameters().get(1);
190
191 var bbody = Body.Builder.of(null, abody.bodyType());
192 var bblock = bbody.entryBlock();
193
194 // Operation uses values from another model
195 var addOp = JavaOp.add(aa, ab);
196 Assertions.assertThrows(IllegalStateException.class, () -> bblock.op(addOp));
197 }
198
199 @Test
200 public void testHeaderFromOtherBody() {
201 var abody = Body.Builder.of(null, FUNCTION_TYPE_VOID);
202 var ablock = abody.entryBlock().block();
203
204 var bbody = Body.Builder.of(null, FUNCTION_TYPE_VOID);
205 var bblock = bbody.entryBlock();
206
207 // Operation uses header with target block from another model
208 var brOp = branch(ablock.successor());
209 Assertions.assertThrows(IllegalStateException.class, () -> bblock.op(brOp));
210 }
211
212 @Test
213 public void testHeaderFromEntryBlock() {
214 var body = Body.Builder.of(null, FUNCTION_TYPE_VOID);
215 var block = body.entryBlock();
216 Assertions.assertThrows(IllegalStateException.class, block::successor);
217 }
218
219 @Test
220 public void testBuiltBodyBuilder() {
221 var body = Body.Builder.of(null, FUNCTION_TYPE_VOID);
222 var block = body.entryBlock();
223 block.op(return_());
224 func("f", body);
225
226 // Body is built
227 Assertions.assertThrows(IllegalStateException.class, () -> func("f", body));
228 }
229
230 @Test
231 public void testBodyBuilderWithBuiltAncestor() {
232 var body = Body.Builder.of(null, FUNCTION_TYPE_VOID);
233 var block = body.entryBlock();
234 block.op(return_());
235 func("f", body);
236
237 // ancestor body is built
238 Assertions.assertThrows(IllegalStateException.class, () -> Body.Builder.of(body, FUNCTION_TYPE_VOID));
239 }
240
241 @Test
242 public void testBodyBuilderWithUnbuiltChildren() {
243 var body = Body.Builder.of(null, FUNCTION_TYPE_VOID);
244 var block = body.entryBlock();
245 block.op(return_());
246
247 Body.Builder.of(body, FUNCTION_TYPE_VOID);
248
249 // Great-grandchild body is not built
250 Assertions.assertThrows(IllegalStateException.class, () -> func("f", body));
251 }
252
253 @Test
254 public void testMistmatchedBody() {
255 var body1 = Body.Builder.of(null, FUNCTION_TYPE_VOID);
256 var block1 = body1.entryBlock();
257
258 var anotherBody = Body.Builder.of(null, FUNCTION_TYPE_VOID);
259
260 var body2 = Body.Builder.of(anotherBody, FUNCTION_TYPE_VOID);
261 var block2 = body2.entryBlock();
262 block2.op(return_());
263 var lambdaOp = JavaOp.lambda(type(Runnable.class), body2);
264
265 // Op's grandparent body is not parent body of block1
266 Assertions.assertThrows(IllegalStateException.class, () -> block1.op(lambdaOp));
267 }
268
269 @Test
270 public void testAppendAfterTerminatingOperation() {
271 var body = Body.Builder.of(null, FUNCTION_TYPE_VOID);
272 var block = body.entryBlock();
273 block.op(return_());
274
275 // Append operation after terminating operation
276 Assertions.assertThrows(IllegalStateException.class, () -> block.op(return_()));
277 }
278
279 @Test
280 public void testNoTerminatingOperation() {
281 var body = Body.Builder.of(null, FUNCTION_TYPE_VOID);
282 var block = body.entryBlock();
283 block.op(constant(INT, 0));
284
285 // No terminating operation
286 Assertions.assertThrows(IllegalStateException.class, () -> func("f", body));
287 }
288
289 @Test
290 public void testUnreferencedBlocksRemoved() {
291 var body = Body.Builder.of(null, FUNCTION_TYPE_VOID);
292 var block = body.entryBlock();
293 block.op(return_());
294
295 // Create empty blocks
296 block.block();
297 block.block();
298 block.block();
299
300 FuncOp f = func("f", body);
301 Assertions.assertEquals(1, f.body().blocks().size());
302 }
303
304 @Test
305 public void testEmptyEntryBlock() {
306 var body = Body.Builder.of(null, FUNCTION_TYPE_VOID);
307 var block = body.entryBlock();
308
309 Assertions.assertThrows(IllegalStateException.class, () -> func("f", body));
310 }
311
312 @Test
313 public void testNonEmptyEntryBlockNoTerminatingOp() {
314 var body = Body.Builder.of(null, FUNCTION_TYPE_VOID);
315 var block = body.entryBlock();
316 // No terminating op
317 block.op(constant(INT, 0));
318
319 Assertions.assertThrows(IllegalStateException.class, () -> func("f", body));
320 }
321
322 @Test
323 public void testEmptyBlockWithPredecessor() {
324 var body = Body.Builder.of(null, FUNCTION_TYPE_VOID);
325 var entryBlock = body.entryBlock();
326 // Create empty block
327 var block = entryBlock.block();
328 // Branch to empty block
329 entryBlock.op(branch(block.successor()));
330
331 Assertions.assertThrows(IllegalStateException.class, () -> func("f", body));
332 }
333
334 @Test
335 public void testNonEmptyBlockNoTerminatingOp() {
336 var body = Body.Builder.of(null, FUNCTION_TYPE_VOID);
337 var entryBlock = body.entryBlock();
338 // Create empty block
339 var block = entryBlock.block();
340 // Branch to empty block
341 entryBlock.op(branch(block.successor()));
342 // No terminating op
343 block.op(constant(INT, 0));
344
345 Assertions.assertThrows(IllegalStateException.class, () -> func("f", body));
346 }
347 }