So it passes a pointer for the return value as sret, i64 is the index, %0 is the subvalue (why isn't this swiftself?) and the subtype is passed as last arg. Can anyone explain why the subtype is used and why it's passed last instead of 1 before last, and why SwiftSelf is missing?
import Foundation;
public struct StructTest<T> {
public var x: T
public func Fred(_ a: Int, _ b: StructTest<T>) {}
}
public func Wiliam() {
var q = StructTest<Double>(x: 15);
q.Fred(15, q);
}
The ctor call for StructTest get typeof Double passed, while Fred gets typeof StructTest<Double> passed?
Not sure what the confusion is. In your specification, StructTest, when instantiated, requires a Double value to initialize x. StructTest.Fred takes an integer and a StructTest as parameters, exactly what the compiler is doing.
My confusion is when to use a full instantisted type, and when to pass generic parameters separately. Both for array, the original case and the next case the compiler does different things in different cases. I'm trying to find out what the rule is so I can do the same.
You can look at the PolymorphicConvention class in the Swift compiler:
The compiler tries to avoid passing metadata for things it can derive from the standard parameters to the function, as you can see by the various consider* calls in the PolymorphicConvention constructor. For indirect generic arguments, it also introduces a metadata argument for the type of the value being passed, since it is likely the callee will need that metadata in order to manipulate values of the type:
Thanks that helps a lot. The only thing I can't infer from this is the array logic though:
call swiftcc void @"$sSayxSicig"(%swift.opaque* noalias nocapture sret %50, i64 0, %swift.bridge* %41, %swift.type* @"$sSiN")
that's "myarray[0]" for type [Int], but whatever I try to do:
public struct StructTest<T> {
var q: T
}
public extension StructTest {
public subscript(index: Int) -> T { return q; }
For my own subscripts it always like (even in an extension): call swiftcc void @"$s5carlo10StructTestVyxSicig"(%swift.opaque* noalias nocapture sret %26, i64 0, %swift.type* %28, %T5carlo10StructTestV* noalias nocapture swiftself %29)
So whatever I do, I'm getting the self as "swiftself", but array doesn't.
Your struct's layout is dependent on its generic argument, because the q: T property is stored in-line. This means that, in unspecialized code, it gets passed indirectly, and so we add the GenericLValue type argument to the parameter list of the function. For Array, its layout is always fixed, since regardless of the element type the representation is a pointer to the array buffer, so we don't add the argument for the whole type metadata. Since there are no other arguments to get the Element type metadata from, we fall back to adding a new type argument for it.
If you changed StructTest so that its layout was not dependent on the type argument, say by changing q's type to UnsafePointer<T> or something like that, then you should see it have similar behavior to Array's methods.
Am I the only one who finds it insane/scary that none of this is cleanly spec'ed somewhere, for a compiler that's ostensibly to be used for production-work development on one of the worlds dominant computing platforms?
You’re not the only one who would like more formal specifications — but to be fair, Swift hasn’t undergone any kind of formal standardisation process like ANSI C/ISO C(++)/ECMAScript. So there isn’t any standard for 3rd-party compiler implementors to follow, beyond what the official compiler does.
FWIW, I couldn’t find a 3rd party Rust compiler, either. You might find similarly underspecified holes if you tried to write a compiler for that language, too.
The big difference with Rust is that there aren't new platform apis written in Rust only for iOS / MacOS so any other compiler for iOS / MacOS would be required to interact with it.
Also I'm not looking for the standard for the language itself. But how to interact with it.
Right. it's not so much about the language being standardized, this about the ABI, which — by definition — is a specification of how the binary interface between separate pieces of software compiled for it works. You'd expect that to be formally or informally documented beyond "it's whatever the current t compiler code does". Because sooner or later someone's gonna change that code.
There may not be a written spec yet, but the calling convention code was designed and implemented the way it is intentionally, and we’re not going to change it on existing platforms.
Ah, if you have library evolution enabled, then because StructTest is public it's going to be assumed to be ABI-resilient by default and still passed indirectly. Marking it as @frozen, making it internal, or disabling library evolution should make it use the direct calling convention.
Practically speaking, even if there were a spec, the code would define the behavior, because we can't break existing binaries compiled with older compilers. For instance, Clang has x86_64 C ABI bugs that are intentionally not fixed on Darwin to maintain ABI compatibility.