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