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