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 }