> On Jul 29, 2017, at 7:35 PM, Slava Pestov <spes...@apple.com> wrote: >> On Jul 29, 2017, at 12:53 PM, John McCall via swift-dev <swift-dev@swift.org >> <mailto:swift-dev@swift.org>> wrote: >>> On Jul 29, 2017, at 12:48 AM, Andrew Trick <atr...@apple.com >>> <mailto:atr...@apple.com>> wrote: >>>> On Jul 28, 2017, at 8:13 PM, John McCall <rjmcc...@apple.com >>>> <mailto:rjmcc...@apple.com>> wrote: >>>>> On Jul 28, 2017, at 11:11 PM, John McCall via swift-dev >>>>> <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote: >>>>>> On Jul 28, 2017, at 10:38 PM, Andrew Trick <atr...@apple.com >>>>>> <mailto:atr...@apple.com>> wrote: >>>>>>> On Jul 28, 2017, at 3:15 PM, John McCall <rjmcc...@apple.com >>>>>>> <mailto:rjmcc...@apple.com>> wrote: >>>>>>>> On Jul 28, 2017, at 6:02 PM, Andrew Trick via swift-dev >>>>>>>> <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote: >>>>>>>> >>>>>>>> >>>>>>>>> On Jul 28, 2017, at 2:20 PM, Joe Groff via swift-dev >>>>>>>>> <swift-dev@swift.org <mailto:swift-dev@swift.org>> wrote: >>>>>>>>> >>>>>>>>> The Swift runtime currently maintains globally unique pointer >>>>>>>>> identities for type metadata and protocol conformances. This makes >>>>>>>>> checking type equivalence a trivial pointer equality comparison, but >>>>>>>>> most operations on generic values do not really care about exact type >>>>>>>>> identity and only need to invoke value or protocol witness methods or >>>>>>>>> consult other data in the type metadata structure. I think it's worth >>>>>>>>> reevaluating whether having globally unique type metadata objects is >>>>>>>>> the correct design choice. Maintaining global uniqueness of metadata >>>>>>>>> instances carries a number of costs. Any code that wants type >>>>>>>>> metadata for an instance of a generic type, even a fully concrete >>>>>>>>> one, must make a potentially expensive runtime call to get the >>>>>>>>> canonical metadata instance. This also greatly complicates our >>>>>>>>> ability to emit specializations of type metadata, value witness >>>>>>>>> tables, or protocol witness tables for concrete instances of generic >>>>>>>>> types, since specializations would need to be registered with the >>>>>>>>> runtime as canonical metadata objects, and it would be difficult to >>>>>>>>> do this lazily and still reliably favor specializations over more >>>>>>>>> generic witnesses. The lack of witness table specializations leaves >>>>>>>>> an obnoxious performance cliff for instances of generic types that >>>>>>>>> end up inside existential containers or cross into unspecialized >>>>>>>>> code. The runtime also obligates binaries to provide the canonical >>>>>>>>> metadata for all of their public types, along with all the dependent >>>>>>>>> value witnesses, class methods, and protocol witness tables, meaning >>>>>>>>> a type abstraction can never be completely "zero-cost" across modules. >>>>>>>>> >>>>>>>>> On the other hand, if type metadata did not need to be unique, then >>>>>>>>> the compiler would be free to emit specialized type metadata and >>>>>>>>> protocol witness tables for fully concrete non-concrete value types >>>>>>>>> without consulting the runtime. This would let us avoid runtime calls >>>>>>>>> to fetch metadata in specialized code, and would make it much easier >>>>>>>>> for us to implement witness specialization. It would also give us the >>>>>>>>> ability to potentially extend the "inlinable" concept to public >>>>>>>>> fragile types, making it a client's responsibility to emit metadata >>>>>>>>> for the type when needed and keeping the type from affecting its home >>>>>>>>> module's ABI. This could significantly reduce the size and ABI >>>>>>>>> surface area of the standard library, since the standard library >>>>>>>>> contains a lot of generic lightweight adapter types for collections >>>>>>>>> and other abstractions that are intended to be optimized away in most >>>>>>>>> use cases. >>>>>>>>> >>>>>>>>> There are of course benefits to globally unique metadata objects that >>>>>>>>> we would lose if we gave up uniqueness. Operations that do check type >>>>>>>>> identity, such as comparison, hashing, and dynamic casting, would >>>>>>>>> have to perform more expensive checks, and nonunique metadata objects >>>>>>>>> would need to carry additional information to enable those checks. It >>>>>>>>> is likely that class objects would have to remain globally unique, if >>>>>>>>> for no other reason than that the Objective-C runtime requires it on >>>>>>>>> Apple platforms. Having multiple equivalent copies of type metadata >>>>>>>>> has the potential to increase the working set of an app in some >>>>>>>>> situations, although it's likely that redundant compiler-emitted >>>>>>>>> copies of value type metadata would at least be able to live in >>>>>>>>> constant pages mapped from disk instead of getting dynamically >>>>>>>>> instantiated by the runtime like everything is today. There could >>>>>>>>> also be subtle source-breaking behavior for code that bitcasts >>>>>>>>> metatype values to integers or pointers and expects bit-level >>>>>>>>> equality to indicate type equality. It's unlikely to me that giving >>>>>>>>> up uniqueness would buy us any simplification to the runtime, since >>>>>>>>> the runtime would still need to be able to instantiate metadata for >>>>>>>>> unspecialized code, and we would still want to unique >>>>>>>>> runtime-instantiated metadata objects as an optimization. >>>>>>>>> >>>>>>>>> Overall, my intuition is that the tradeoffs come out in favor for >>>>>>>>> nonunique metadata objects, but what do you all think? Is there >>>>>>>>> anything I'm missing? >>>>>>>>> >>>>>>>>> -Joe >>>>>>>> >>>>>>>> In a premature proposal two years ago, we agreed to ditch unique >>>>>>>> protocol conformances but install the canonical address as the first >>>>>>>> entry in each specialized table. >>>>>>> >>>>>>> This would be a reference to (unique) global data about the >>>>>>> conformance, not a reference to some canonical version of the protocol >>>>>>> witness table. We do not rely on having a canonical protocol witness >>>>>>> table. The only reason we unique them (when we do need to instantiate) >>>>>>> is because we don't want to track their lifetimes. >>>>>>> >>>>>>>> That would mitigate the disadvantages that you pointed to. But, we >>>>>>>> would also lose the ability to emit specialized metadata/conformances >>>>>>>> in constant pages. How do you feel about that tradeoff? >>>>>>> >>>>>>> Note that, per above, it's only specialized constant type metadata that >>>>>>> we would lose. >>>>>>> >>>>>>> I continue to feel that having to do structural equality tests on type >>>>>>> metadata would be a huge loss. >>>>>>> >>>>>>> John. >>>>>> >>>>>> My question was really, are we going to runtime-initialize the >>>>>> specialized metadata and specialized witness tables in order to install >>>>>> the unique identifier, rather than requiring a runtime call whenever we >>>>>> need the unique ID. I think the answer is “yes”, we want to install the >>>>>> ID at initialization time for fast type comparison, hashing and casting. >>>>> >>>>> Sorry, by "(unique) global data about the conformance" I meant that we >>>>> would emit a global conformance descriptor in constant data for the >>>>> conformance declaration. There would be one of these, no matter how many >>>>> it was instantiated; it would therefore uniquely identify a possible >>>>> generic conformance the same way that a nominal type descriptor uniquely >>>>> identifies a possibly generic type. The reference to it would just be an >>>>> ordinary symbol reference. >>>> >>>> Naturally, eagerly emitting one of those has the same advantages and >>>> disadvantages as eagerly emitting type metadata and everything else, and >>>> can be solved in the same way. >>>> >>>> John. >>> >>> Sure, for witness tables each constant specialized conformance can refer to >>> a unique constant nominal conformance, resolved at link-time. >>> >>> Whereas we expect specialized type metadata to always need some runtime >>> initialization because we want to unique some canonical entity for each >>> instantiation and possibly compress VWTs. >> >> Oh, I missed that you were talking about both, sorry. If we wanted to emit >> specialized type metadata, I think it would have to be an explicit goal that >> they could be emitted without any sort of dynamic initialization, which >> implies that they're non-unique. > > I was wondering about that. I’m still having trouble filling in the details, > but it seems that if non-unique type metadata never ‘escapes’ from a > function, we could stack-allocate ‘structural’ metadata, for example if you > have > > func foo<T>(_: T) {} > > func bar<T>(x: T, y: Y) { > foo((x, y)) > } > > You would be able to compile bar() without any runtime calls at all, building > the tuple type metadata ‘from scratch’ on the stack and passing it to foo(). > Perhaps generic nominal types could also be constructed non-uniquely without > a runtime call.
Okay. What would be required to prove that type metadata never escapes from a function? John. > >> Remember that we can only do this for types not expressed in terms of >> archetypes to begin with, and we can already use caller-side caches to >> optimize access such to those metadata anyway. >> >> John. >> _______________________________________________ >> swift-dev mailing list >> swift-dev@swift.org <mailto:swift-dev@swift.org> >> https://lists.swift.org/mailman/listinfo/swift-dev >> <https://lists.swift.org/mailman/listinfo/swift-dev>
_______________________________________________ swift-dev mailing list swift-dev@swift.org https://lists.swift.org/mailman/listinfo/swift-dev