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