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