Struct types are represented at runtime as either a OutlineTypedObject or an InlineTypedObject depending on whether the struct type can fit entirely inside a JSObject allocation .
When this is combined with prefix subtyping (a struct subtype may have more fields, possibly making it outline), we don't know at any given struct accessor whether the object was allocated inline/outline and have to do a branch. This complicates codegen and is slow.
There seem to be three options here in roughly increasing complexity:
- Add a data pointer in InlineTypedObject that points to it's inline memory, making it representation compatible with OutlineTypedObject. Every accessor can than dereference this pointer to get the either inline/outline memory.
- Always allocate as many fields as we can inline, before appending a footer word that points to the remainder fields in malloc memory. Every accessor is for a static field and can tell whether it can get it inline or if it has to find the footer word to find the malloc'ed area.
- Add a 'large object area' and remove the JSObject size limit (at least for struct objects) so that struct objects always have data inline. I've been told this is very difficult.
A discarded idea was to use a shared implementation limit on the number of struct fields to guarantee data is always inlined. V8 uses a limit of 999 fields. With v128 types, this is worst case ~15KiB, and likely far beyond what we'd want to support.
I believe that (2) is the sweet spot in complexity and runtime performance. It has the added benefit that struct and array objects will use different classes, and we can start to specialize them.