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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package experiments; 26 27 28 import hat.ifacemapper.Schema; 29 import hat.buffer.Buffer; 30 import hat.ifacemapper.MappableIface; 31 32 import java.lang.constant.ClassDesc; 33 import java.lang.foreign.*; 34 import java.lang.invoke.MethodHandles; 35 import java.lang.reflect.Field; 36 import java.lang.reflect.Method; 37 import jdk.incubator.code.*; 38 import jdk.incubator.code.analysis.SSA; 39 import jdk.incubator.code.extern.ExternalizedTypeElement; 40 import jdk.incubator.code.extern.OpFactory; 41 import jdk.incubator.code.dialect.core.CoreOp; 42 import jdk.incubator.code.CodeReflection; 43 import jdk.incubator.code.dialect.core.CoreType; 44 import jdk.incubator.code.dialect.core.FunctionType; 45 import jdk.incubator.code.dialect.java.JavaOp; 46 import jdk.incubator.code.dialect.java.JavaType; 47 import jdk.incubator.code.dialect.java.PrimitiveType; 48 49 import java.util.*; 50 import java.util.stream.Stream; 51 52 public class LayoutExample { 53 54 /* 55 struct { 56 StructTwo struct; 57 int i; 58 } 59 */ 60 61 public interface Outer extends Buffer { 62 interface Inner extends Struct { 63 int i(); 64 65 void i(int v); 66 67 float f(); 68 69 void f(float v); 70 71 // Schema schema = Schema.of(Inner.class, b->b.primitive("i").primitive("f")); 72 } 73 74 Inner right(); 75 Inner left(); 76 int i(); 77 void i(int v); 78 79 80 Schema schema = Schema.of(Outer.class, b->b 81 .struct("left", left->left 82 .field("i") 83 .field("f") 84 ) 85 // .struct("right", Inner.schema) 86 .field("i") 87 ); 88 } 89 90 91 @CodeReflection 92 static float m(Outer s1) { 93 // StructOne* s1 94 // s1 -> i 95 int i = s1.i(); 96 // s1 -> *s2 97 Outer.Inner s2 = s1.left(); 98 // s2 -> i 99 i += s2.i(); 100 // s2 -> f 101 float f = s2.f(); 102 return i + f; 103 } 104 105 106 public static void main(String[] args) { 107 var lookup = MethodHandles.lookup(); 108 Optional<Method> om = Stream.of(LayoutExample.class.getDeclaredMethods()) 109 .filter(m -> m.getName().equals("m")) 110 .findFirst(); 111 112 Method m = om.orElseThrow(); 113 CoreOp.FuncOp f= Op.ofMethod(m).orElseThrow(); 114 f = SSA.transform(f); 115 System.out.println(f.toText()); 116 FunctionType functionType = transformStructClassToPtr(lookup, f); 117 System.out.println(f.toText()); 118 CoreOp.FuncOp pm = transformInvokesToPtrs(lookup, f, functionType); 119 System.out.println(pm.toText()); 120 } 121 static FunctionType transformStructClassToPtr(MethodHandles.Lookup l, 122 CoreOp.FuncOp f) { 123 List<TypeElement> pTypes = new ArrayList<>(); 124 for (Block.Parameter p : f.parameters()) { 125 pTypes.add(transformStructClassToPtr(l, p.type())); 126 } 127 return CoreType.functionType( 128 transformStructClassToPtr(l, f.invokableType().returnType()), pTypes); 129 } 130 131 static CoreOp.FuncOp transformInvokesToPtrs(MethodHandles.Lookup l, 132 CoreOp.FuncOp f, FunctionType functionType) { 133 134 var builder= CoreOp.func(f.funcName(), functionType); 135 136 var funcOp = builder.body(funcBlock -> { 137 funcBlock.transformBody(f.body(), funcBlock.parameters(), (b, op) -> { 138 if (op instanceof JavaOp.InvokeOp invokeOp 139 && invokeOp.hasReceiver() 140 && invokeOp.operands().getFirst() instanceof Value receiver) { 141 if (bufferOrBufferChildClass(l, receiver.type()) != null) { 142 Value ptr = b.context().getValue(receiver); 143 PtrToMember ptrToMemberOp = new PtrToMember(ptr, invokeOp.invokeDescriptor().name()); 144 Op.Result memberPtr = b.op(ptrToMemberOp); 145 146 if (invokeOp.operands().size() == 1) { 147 // Pointer access and (possibly) value load 148 if (ptrToMemberOp.resultType().layout() instanceof ValueLayout) { 149 Op.Result v = b.op(new PtrLoadValue(memberPtr)); 150 b.context().mapValue(invokeOp.result(), v); 151 } else { 152 b.context().mapValue(invokeOp.result(), memberPtr); 153 } 154 } else { 155 // @@@ 156 // Value store 157 throw new UnsupportedOperationException(); 158 } 159 } else { 160 b.op(op); 161 } 162 } else { 163 b.op(op); 164 } 165 return b; 166 }); 167 }); 168 return funcOp; 169 } 170 171 172 173 static boolean isBufferOrBufferChild(Class<?> maybeIface) { 174 return maybeIface.isInterface() && ( 175 MappableIface.class.isAssignableFrom(maybeIface) 176 ); 177 178 } 179 static Schema bufferOrBufferChildSchema(MethodHandles.Lookup l, Class<?> maybeBufferOrBufferChild) { 180 if (isBufferOrBufferChild(maybeBufferOrBufferChild)) { 181 throw new IllegalArgumentException(); 182 } 183 Field schemaField; 184 try { 185 schemaField = maybeBufferOrBufferChild.getField("schema"); 186 return (Schema)schemaField.get(null); 187 } catch (NoSuchFieldException | IllegalAccessException e) { 188 throw new RuntimeException(e); 189 } 190 } 191 static Class<?> bufferOrBufferChildClass(MethodHandles.Lookup l, TypeElement t) { 192 try { 193 if (!(t instanceof JavaType jt) || !(jt.resolve(l) instanceof Class<?> c)) { 194 return null; 195 } 196 return isBufferOrBufferChild(c) ? c : null; 197 } catch (ReflectiveOperationException e) { 198 throw new RuntimeException(e); 199 } 200 } 201 static TypeElement transformStructClassToPtr(MethodHandles.Lookup l, TypeElement type) { 202 if (bufferOrBufferChildClass(l, type) instanceof Class<?> sc) { 203 return new PtrType(bufferOrBufferChildSchema(l, sc)); 204 } else { 205 return type; 206 } 207 } 208 209 public static final class PtrType implements TypeElement { 210 static final String NAME = "ptr"; 211 MemoryLayout layout; 212 Schema schema; 213 final JavaType returnType; 214 215 public PtrType(MemoryLayout layout) { 216 this.layout = layout; 217 this.returnType = switch (layout) { 218 case StructLayout _ -> JavaType.type(ClassDesc.of(layout.name().orElseThrow())); 219 case AddressLayout _ -> throw new UnsupportedOperationException("Unsupported member layout: " + layout); 220 case ValueLayout valueLayout -> JavaType.type(valueLayout.carrier()); 221 default -> throw new UnsupportedOperationException("Unsupported member layout: " + layout); 222 }; 223 } 224 public PtrType(Schema schema) { 225 this.schema = schema; 226 this.layout= null;//schema.layout(); 227 this.returnType = switch (layout) { 228 case StructLayout _ -> JavaType.type(ClassDesc.of(layout.name().orElseThrow())); 229 case AddressLayout _ -> throw new UnsupportedOperationException("Unsupported member layout: " + layout); 230 case ValueLayout valueLayout -> JavaType.type(valueLayout.carrier()); 231 default -> throw new UnsupportedOperationException("Unsupported member layout: " + layout); 232 }; 233 } 234 235 public JavaType returnType() { 236 return returnType; 237 } 238 239 public MemoryLayout layout() { 240 return layout; 241 } 242 public Schema schema() { 243 return schema; 244 } 245 246 @Override 247 public boolean equals(Object o) { 248 if (this == o) return true; 249 if (o == null || getClass() != o.getClass()) return false; 250 PtrType ptrType = (PtrType) o; 251 return Objects.equals(layout, ptrType.layout); 252 } 253 254 @Override 255 public int hashCode() { 256 return Objects.hash(layout); 257 } 258 259 @Override 260 public ExternalizedTypeElement externalize() { 261 return new ExternalizedTypeElement(NAME, List.of(returnType.externalize())); 262 } 263 264 @Override 265 public String toString() { 266 return externalize().toString(); 267 } 268 } 269 270 public static final class PtrToMember extends Op { 271 public static final String NAME = "ptr.to.member"; 272 public static final String ATTRIBUTE_OFFSET = "offset"; 273 public static final String ATTRIBUTE_NAME = "name"; 274 275 final String simpleMemberName; 276 final long memberOffset; 277 final PtrType resultType; 278 279 PtrToMember(PtrToMember that, CopyContext cc) { 280 super(that, cc); 281 this.simpleMemberName = that.simpleMemberName; 282 this.memberOffset = that.memberOffset; 283 this.resultType = that.resultType; 284 } 285 286 @Override 287 public PtrToMember transform(CopyContext cc, OpTransformer ot) { 288 return new PtrToMember(this, cc); 289 } 290 291 public PtrToMember(Value ptr, String simpleMemberName) { 292 super(NAME, List.of(ptr)); 293 this.simpleMemberName = simpleMemberName; 294 295 if (!(ptr.type() instanceof PtrType ptrType)) { 296 throw new IllegalArgumentException("Pointer value is not of pointer type: " + ptr.type()); 297 } 298 // @@@ Support group layout 299 if (!(ptrType.layout() instanceof StructLayout structLayout)) { 300 throw new IllegalArgumentException("Pointer type layout is not a struct layout: " + ptrType.layout()); 301 } 302 303 // Find the actual member name from the simple member name 304 String memberName = findMemberName(structLayout, simpleMemberName); 305 MemoryLayout.PathElement p = MemoryLayout.PathElement.groupElement(memberName); 306 this.memberOffset = structLayout.byteOffset(p); 307 MemoryLayout memberLayout = structLayout.select(p); 308 // Remove any simple member name from the layout 309 MemoryLayout ptrLayout = memberLayout instanceof StructLayout 310 ? memberLayout.withName(className(memberName)) 311 : memberLayout.withoutName(); 312 this.resultType = new PtrType(ptrLayout); 313 } 314 315 // @@@ Change to return member index 316 static String findMemberName(StructLayout sl, String simpleMemberName) { 317 for (MemoryLayout layout : sl.memberLayouts()) { 318 String memberName = layout.name().orElseThrow(); 319 if (simpleMemberName(memberName).equals(simpleMemberName)) { 320 return memberName; 321 } 322 } 323 throw new NoSuchElementException("No member found: " + simpleMemberName + " " + sl); 324 } 325 326 static String simpleMemberName(String memberName) { 327 int i = memberName.indexOf("::"); 328 return i != -1 329 ? memberName.substring(i + 2) 330 : memberName; 331 } 332 333 static String className(String memberName) { 334 int i = memberName.indexOf("::"); 335 return i != -1 336 ? memberName.substring(0, i) 337 : null; 338 } 339 340 @Override 341 public PtrType resultType() { 342 return resultType; 343 } 344 345 @Override 346 public Map<String, Object> externalize() { 347 return Map.of( 348 "", simpleMemberName, 349 ATTRIBUTE_OFFSET, memberOffset); 350 } 351 352 public String simpleMemberName() { 353 return simpleMemberName; 354 } 355 356 public long memberOffset() { 357 return memberOffset; 358 } 359 360 public Value ptrValue() { 361 return operands().get(0); 362 } 363 } 364 365 366 public static final class PtrAddOffset extends Op { 367 public static final String NAME = "ptr.add.offset"; 368 369 PtrAddOffset(PtrAddOffset that, CopyContext cc) { 370 super(that, cc); 371 } 372 373 @Override 374 public PtrAddOffset transform(CopyContext cc, OpTransformer ot) { 375 return new PtrAddOffset(this, cc); 376 } 377 378 public PtrAddOffset(Value ptr, Value offset) { 379 super(NAME, List.of(ptr, offset)); 380 381 if (!(ptr.type() instanceof PtrType)) { 382 throw new IllegalArgumentException("Pointer value is not of pointer type: " + ptr.type()); 383 } 384 if (!(offset.type() instanceof PrimitiveType pt && pt.equals(JavaType.LONG))) { 385 throw new IllegalArgumentException("Offset value is not of primitve long type: " + offset.type()); 386 } 387 } 388 389 @Override 390 public TypeElement resultType() { 391 return ptrValue().type(); 392 } 393 394 public Value ptrValue() { 395 return operands().get(0); 396 } 397 398 public Value offsetValue() { 399 return operands().get(1); 400 } 401 } 402 403 public static final class PtrLoadValue extends Op { 404 public static final String NAME = "ptr.load.value"; 405 406 final JavaType resultType; 407 408 PtrLoadValue(PtrLoadValue that, CopyContext cc) { 409 super(that, cc); 410 this.resultType = that.resultType; 411 } 412 413 @Override 414 public PtrLoadValue transform(CopyContext cc, OpTransformer ot) { 415 return new PtrLoadValue(this, cc); 416 } 417 418 public PtrLoadValue(Value ptr) { 419 super(NAME, List.of(ptr)); 420 421 if (!(ptr.type() instanceof PtrType ptrType)) { 422 throw new IllegalArgumentException("Pointer value is not of pointer type: " + ptr.type()); 423 } 424 if (!(ptrType.layout() instanceof ValueLayout)) { 425 throw new IllegalArgumentException("Pointer type layout is not a value layout: " + ptrType.layout()); 426 } 427 this.resultType = ptrType.returnType(); 428 } 429 430 @Override 431 public TypeElement resultType() { 432 return resultType; 433 } 434 435 public Value ptrValue() { 436 return operands().get(0); 437 } 438 } 439 440 @OpFactory.OpDeclaration(PtrToMember.NAME) 441 public static final class PtrStoreValue extends Op { 442 public static final String NAME = "ptr.store.value"; 443 444 PtrStoreValue(PtrStoreValue that, CopyContext cc) { 445 super(that, cc); 446 } 447 448 @Override 449 public PtrStoreValue transform(CopyContext cc, OpTransformer ot) { 450 return new PtrStoreValue(this, cc); 451 } 452 453 public PtrStoreValue(Value ptr, Value v) { 454 super(NAME, List.of(ptr)); 455 456 if (!(ptr.type() instanceof PtrType ptrType)) { 457 throw new IllegalArgumentException("Pointer value is not of pointer type: " + ptr.type()); 458 } 459 if (!(ptrType.layout() instanceof ValueLayout)) { 460 throw new IllegalArgumentException("Pointer type layout is not a value layout: " + ptrType.layout()); 461 } 462 if (!(ptrType.returnType().equals(v.type()))) { 463 throw new IllegalArgumentException("Pointer reference type is not same as value to store type: " 464 + ptrType.returnType() + " " + v.type()); 465 } 466 } 467 468 @Override 469 public TypeElement resultType() { 470 return JavaType.VOID; 471 } 472 473 public Value ptrValue() { 474 return operands().get(0); 475 } 476 } 477 } 478