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

Add swift backend? #32

Open
Gankra opened this issue Jun 30, 2024 · 4 comments
Open

Add swift backend? #32

Gankra opened this issue Jun 30, 2024 · 4 comments
Labels
enhancement New feature or request question Further information is requested

Comments

@Gankra
Copy link
Owner

Gankra commented Jun 30, 2024

I'm not sure how much work this is but it sure would be neat!

@Gankra Gankra added enhancement New feature or request question Further information is requested labels Jun 30, 2024
@belkadan
Copy link
Contributor

belkadan commented Jul 7, 2024

Dumping some thoughts here in case you or someone else ever gets to this:

  • As of Swift 5.10, Swift files can't formally declare structs with C-compatible layouts. However, in practice tuples have nearly-C-compatible layouts, and since tuples are part of the stable ABI on macOS and iOS, that's unlikely to change even if general struct layout changes. The "nearly" comes because Swift does not automatically tail-pad structures (unlike C and Rust, but kind of like C++ base classes), instead choosing to make a size/stride distinction. If a struct's size is a multiple of its alignment already, however, this shouldn't be a problem.

    Still, the currently-recommended approach for this is to declare a C header and import it. But there's a bunch more scaffolding needed for that than just calling swiftc (two generated input files??). Arguably this is also a different test, but possibly the more useful one since it's the one that's actually supported.

  • Swift does not have fixed-sized arrays yet. Imitating this with a manually-expanded tuple is equivalent in all cases where size == stride. (It's unclear whether a native Swift array would have tail padding on its last element, because Swift does not have fixed-sized arrays yet.)

    There's one other exception to this: unlike Rust, in Swift a zero-sized type always has stride = 1. This includes Void/(). So ((), (), (), ()) has total size 0 and stride 1, but the buffer inside [(), (), (), ()] (a heap-allocated array in Swift, closer to Vec) is 4 bytes long.

  • Swift does not have a way to define unions, or explicitly set alignment, or declare an opaque type. I can't think of good hacks for any of those.

  • C-like enums are supported, you just have to mark them @objc and give them an underlying type. There's no direct equivalent to repr(C), but in practice the underlying type CInt ought to be equivalent to repr(C) as long as the enum values don't exceed the limits of CInt. (This is based on how C++ picks the underlying type for its equivalent feature.)

  • Swift generic functions are not monomorphized, but generic types effectively are. (Which you could probably figure out for yourself, since otherwise Optional<UnsafeRawPointer> couldn't be a single pointer.) This does mean you can't get a C-compatible function pointer from a generic function.

  • The unstable @_cdecl attribute is how you mark a function as C-compatible in Swift 5.10. However, this also restricts the argument types to what the Swift compiler knows how to express in C, so if you want to do anything with generics or custom structs, you have to go through an opaque UnsafeRawPointer or something.

  • Swift doesn't have references. Don't try to use the ownership stuff as equivalent to &T, it doesn't map 1:1 with ABI like that. Just use UnsafePointer<T> and refer to .pointee or [0] everywhere it's used.

  • In that vein, remember that Swift pointer types aren't nullable. abi-cafe's ptr should be Unsafe[Mutable]RawPointer?, with the trailing ? being syntactic sugar for Optional.

  • All Swift structs are effectively repr-transparent. I don't think there's an equivalent to C's "wrapping this in a struct causes the ABI to change", but also who ordered that anyway?

@belkadan
Copy link
Contributor

belkadan commented Jul 7, 2024

  • Obviously Swift has its own ABI and someone could try to interoperate with it directly. But that sounds like a lot more work.

@Gankra
Copy link
Owner Author

Gankra commented Jul 7, 2024

As of #39 we've made a pretty significant shift in the """semantics""" of KDLScript to make it more useful for testing non-C-ABIs and layouts. We now take a single type declaration and use it to test both C reprs and Rust reprs, as well as C conventions and Rust conventions. (this is useful for i.e. cranelift which should ideally be ABI-compatible with the llvm-based rustc backend).

Basically at this point it's basically giving you the shape of types and interfaces, and the harness is free to take that shape and apply modifiers like "...with $LANG-style repr" or "...with $LANG calling convention".

So yeah we could have a repr(Swift) and Convention::Swift which reflect what happens when you just Write Some Normal Swift Code and ask it to generate two (swift?)staticlibs that get linked into one (swift?)dylib. And if anyone ever has a wild project to add Swift interop to Rust or whatever then hey we're ready to deal with that!

It's interesting to hear that the header importer is still the only decent way to ask Swift to give things C-style layout, but we could maybe handle that..? I was sure one day we'd need to deal with a staticlib needing multiple source files, but adding that is still gonna be a pain in the ass.

@Gankra
Copy link
Owner Author

Gankra commented Jul 7, 2024

There's one other exception to this: unlike Rust, in Swift a zero-sized type always has stride = 1. This includes Void/(). So ((), (), (), ()) has total size 0 and stride 1, but the buffer inside [(), (), (), ()] (a heap-allocated array in Swift, closer to Vec) is 4 bytes long.

FWIW this kind of thing is actually awesome/great, for abi-cafe to slam face first into without adjustment. The original problem statement of the tool was to demonstrate these kinds of mismatches for decision-makers! It's still an open question as to what level we should mark these kinds of issues as "known bad" on (as we ran into with the new f16/f128 support), but I think it's broadly desirable to be able to ask for them and have them blow up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants