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