-
Notifications
You must be signed in to change notification settings - Fork 5
Description
There are cases where a generic type is represented exactly the same way for multiple instantiations, or a function's generated code becomes identical for multiple monomorphisations of it.
We can detect at least some of these cases and monomorphise these types/functions based on memory representation of type parameters.
For example, if I have a type that just adds a layer of indirection to allow sharing and mutation of value types:
type MutCell[t](
_value: t
)
MutCell.get(self: MutCell[t]) t:
self._value
MutCell.set(self: MutCell[t], newValue: t) t:
let oldValue = self._value
self._value = newValue
oldValue
MutCell[U32], MutCell[I32], and any other wrappers of U32 and I32 like value type FooId(U32) will have the same memory layout and all operations will compile to the same code.
Another example is arrays: the same thing applies to Array[U32], Array[I32], Array[FooId], ...
Similar to #266, we should analyze type parameters for whether they can be made "representation types", from concrete types. The analysis should follow the same framework described in #266: create dep. tree of SCCs, start with "representation" for every type parameter, turn type parameters into "concrete" as we see operations that requires knowing the exact type (e.g. a trait method call).
I think we could have just one analysis for both #266 and this, using the three states "unused" (starting point), "representational", "concrete" (the current state of all type params) and the obvious transitions from one to the other based on how they're used.
Note that types used type parameters in types will always be monomorphised by representation. Only function can have "concrete" type parameters.