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