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 }