Joe_Groff
(Joe Groff)
February 26, 2020, 10:59pm
23
Regarding key paths, their weird calling convention is also an issue for optimization, so we may introduce a SIL convention for key path accessors, which would be another marker you could use to lower them to a consistent LLVM function type for wasm:
apple:master
← NobodyNada:master
opened 10:57PM - 15 Dec 19 UTC
We have an optimization in SILCombiner (introduced in #24929) that inlines comp… ile-time constant key paths by performing the property access directly instead of calling a runtime function (leading to huge performance gains e.g. for heavy use of `@dynamicMemberLookup`). However, this optimization previously only supported key paths which solely access stored properties, so computed properties, optional chaining, etc. still had to call a runtime function. This commit generalizes the optimization to support all types of key paths.
This is my first time contributing to any sort of compiler development, so it's likely I've overlooked something. The previous iteration of this optimzation essentially computed the stored property offsets and returned the resulting address. I still used with a design that projects the key path to an address since that's what's returned by the runtime functions that we're replacing, but my implementation is more complex because projections for most types of key path components need both setup and teardown logic to e.g. deallocate temporaries, call getters and setters, or re-wrap optionals.
I created an abstract class called `KeyPathProjector`. It has a method called `project`, which accepts an access type (get, set, or modify) and a callback lambda. This method inserts SIL instructions to project the key path to an address, invokes the callback, and then inserts instructions to tear down the projection. A complete key path is handled (internally to `KeyPathProjector.cpp`) by creating a "chained" projector: each projector in the chain is responsible for projecting a single component on top of the previously projected result. For each type of key path component, there is one subclass of `KeyPathProjector` (again, all of these are implementation details of `KeyPathProjector.cpp`).
There are a few special cases (such as access enforcement and optional chaining), but I think my logic works, and it passes all the tests I've tried. I do have a couple concerns though:
- Subscripts in key paths are treated as computed properties with indices, and they require an additional context pointer to be passed to the property accessor. The key path projector needs to create this context object with the correct memory layout; I implemented this by creating a tuple which contains the context operands. Is a tuple guaranteed to have the correct memory layout? Will this work in all cases, and is it guaranteed to work in the future?
- Some code in IRGen, along with the key path ABI documentation, seemed to make references to including a "generic environment" along with these context pointers. However, SILVerifier [doesn't seem to know about this](https://github.com/apple/swift/blob/master/lib/SIL/SILVerifier.cpp#L248), and I haven't been able to figure out anything more. Is this something I need to account for? Under what circumstances would I need to pass a generic environment to an accessor, and what exactly would that involve?
1 Like