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 }