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