Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jextract-swift: allow $direct access to properties when we know their offset/layout, and no side effects #30

Open
ktoso opened this issue Oct 1, 2024 · 2 comments
Labels
good first issue Good for newcomers help wanted Extra attention is needed jextract-swift Issues related to jextract-swift performance

Comments

@ktoso
Copy link
Collaborator

ktoso commented Oct 1, 2024

This is a follow up to the "call the accessors" #24 part of properties.

We may consider implementing direct field access when we'd know the layout / offset / size of a field. These are direct memory access so could be more efficient than calling the Swift accessor methods, however they can only be safe to call when the fields have no.

This is likely a "later" task, as it'd only be a specific performance optimization vs. accessing by accessors, but it's worth exploring eventually.


    // --------------------------------------------------------------------------------------------------------
    // ==== len

    private static final OfLong len$LAYOUT = (OfLong)$LAYOUT.select(groupElement("len"));

    private static class len$property {
        public static final FunctionDescriptor DESC_GET = FunctionDescriptor.of(
                /* -> */ManualJavaKitExample.SWIFT_INT,
                /* self = */ ManualJavaKitExample.SWIFT_POINTER
        );
        public static final FunctionDescriptor DESC_SET = FunctionDescriptor.ofVoid(
                /* self = */ ManualJavaKitExample.SWIFT_POINTER,
                ManualJavaKitExample.SWIFT_INT
        );

        private static final String BASE_NAME = "$s14JavaKitExample12MySwiftClassC3lenSiv";
        public static final MemorySegment ADDR_GET = ManualJavaKitExample.findOrThrow(BASE_NAME + "g");
        public static final MemorySegment ADDR_SET = ManualJavaKitExample.findOrThrow(BASE_NAME + "s");

        public static final MethodHandle HANDLE_GET = Linker.nativeLinker().downcallHandle(ADDR_GET, DESC_GET);
        public static final MethodHandle HANDLE_SET = Linker.nativeLinker().downcallHandle(ADDR_SET, DESC_SET);
    }

    public static final OfLong len$layout() {
        return len$LAYOUT;
    }

    private static final long len$OFFSET = 8; // FIXME: we don't know yet

    public static final long len$offset() {
        return len$OFFSET;
    }

    public static FunctionDescriptor len$get$descriptor() {
        return len$property.DESC_GET;
    }
    public static MethodHandle len$get$handle() {
        return len$property.HANDLE_GET;
    }
    public static MemorySegment len$get$address() {
        return len$property.ADDR_GET;
    }

    public static long getLen(MemorySegment self) {
        var mh$ = len$property.HANDLE_GET;
        try {
            if (TRACE_DOWNCALLS) {
                traceDowncall("len$getter", self);
            }
            return (long) mh$.invokeExact(self);
        } catch (Throwable ex$) {
            throw new AssertionError("should not reach here", ex$);
        }
    }

    public static void getLen$direct(MemorySegment self) {
        // FIXME: we don't know the right offset yet, we need to get told in the .swiftinterface
        self.get(len$LAYOUT, len$OFFSET);
    }

    public static long getLen$direct(MemorySegment self) {
        return self.get(len$LAYOUT, len$OFFSET);
    }


    public static FunctionDescriptor len$set$descriptor() {
        return len$property.DESC_SET;
    }
    public static MethodHandle len$set$handle() {
        return len$property.HANDLE_SET;
    }
    public static MemorySegment len$set$address() {
        return len$property.ADDR_SET;
    }


    /**
     * Setter for field:
     * {@snippet lang = Swift :
     * var len: Int { set }
     * }
     */
    public static void setLen(MemorySegment self, long fieldValue) {
        var mh$ = len$property.HANDLE_SET;
        try {
            if (TRACE_DOWNCALLS) {
                traceDowncall("len$setter", self, fieldValue);
            }
            mh$.invokeExact(self, fieldValue);
        } catch (Throwable ex$) {
            throw new AssertionError("should not reach here", ex$);
        }
    }
    public static void setLen$direct(MemorySegment self, long fieldValue) {
        // FIXME: we don't know the right offset yet, we need to get told in the .swiftinterface
        self.set(len$LAYOUT, len$OFFSET, fieldValue);
    }
@DougGregor
Copy link
Member

There a different way to implement this that I believe is both easier and will work better than looking for this information in the .swiftinterface. The Swift runtime has the ability to describe the offsets of stored properties in a type via runtime calls. There are two options:

  • Query the offset of a stored property via MemoryLayout<T>.offset(of:). For example MemoryLayout<MyPoint>.offset(of: \.y).
  • Use the longstanding@_spi function _forEachField that enumerates the names, types, and offsets of the stored properties within a type.

By using the runtime, we'll be able to deal with types that don't have statically-known layouts, such as generic and resilient types. Plus, we don't need any compiler changes for either of these approaches to work: they can be done entirely within jextract-swift.

@ktoso
Copy link
Collaborator Author

ktoso commented Oct 10, 2024

Very nice, thanks for the idea here 👍

@ktoso ktoso added good first issue Good for newcomers help wanted Extra attention is needed labels Oct 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers help wanted Extra attention is needed jextract-swift Issues related to jextract-swift performance
Projects
None yet
Development

No branches or pull requests

2 participants