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