<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">I like the idea; it makes more sense to me than our current model (which feels more like a plain callback than a continuation to me). Some things that occurred to me when reading this:</div><div class=""><br class=""></div><div class="">- This seems like it'll be much simpler to check for invalid concurrent access to the same location (inout violations) in debug builds. (I don't think it's actually any more or less possible, but it is simpler.)</div><div class=""><br class=""></div><div class="">- This will look funny but work fine for multiple inout parameters: foo(&amp;x, &amp;y)</div><div class=""><br class=""></div><div class="">- OTOH, this does break up basic blocks in the caller context, making LLVM analysis less effective. Since the materializeForSet calls were already either inlined or opaque, though, we're probably fine there.</div><div class=""><br class=""></div><div class="">- Does this help us with the nested dictionary CoW problem? `foo["bar"]["baz"] += 1`</div><div class=""><br class=""></div><div class="">Jordan</div><div class=""><br class=""></div><div class=""><br class=""></div><br class=""><div><blockquote type="cite" class=""><div class="">On Oct 31, 2016, at 12:22, Joe Groff via swift-dev &lt;<a href="mailto:swift-dev@swift.org" class="">swift-dev@swift.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">We currently abstract over mutable property accesses using what I’ll call a&nbsp;<em style="line-height: 1;" class="">continuation-based</em>&nbsp;model–the&nbsp;<code style="line-height: 1;" class="">materializeForSet</code>&nbsp;accessor is called before an&nbsp;<code style="line-height: 1;" class="">inout</code>&nbsp;access, and returns a continuation callback that must be called when the&nbsp;<code style="line-height: 1;" class="">inout</code>&nbsp;access is completed. I think a&nbsp;<em style="line-height: 1;" class="">nested-function-based</em>&nbsp;model, if designed correctly, has the potential to be more efficient and easier to implement.</p><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">As background for anyone intimately familiar with the implementation,&nbsp;Swift’s language model for mutable properties requires a reconciliation step after a mutation occurs. For example, in an&nbsp;<code style="line-height: 1;" class="">inout</code>&nbsp;access like this:</p><pre style="margin-top: 21px; margin-bottom: 21px; tab-size: 4; color: rgb(17, 17, 17); font-size: 15px; background-color: rgb(248, 248, 248); height: 36px;" class=""><code class="swift hljs" style="line-height: inherit; display: block; padding: 0.5em; color: rgb(51, 51, 51); height: auto;">foo(&amp;x.y.z)</code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">the&nbsp;<code style="line-height: 1;" class="">y</code>&nbsp;or&nbsp;<code style="line-height: 1;" class="">z</code>&nbsp;property may be computed, requiring a getter call before the call to&nbsp;<code style="line-height: 1;" class="">foo</code>&nbsp;and a setter call afterward, or either property may have&nbsp;<code style="line-height: 1;" class="">willSet</code>&nbsp;or&nbsp;<code style="line-height: 1;" class="">didSet</code>&nbsp;observers that fire after&nbsp;<code style="line-height: 1;" class="">foo</code>&nbsp;finishes. If the base&nbsp;<code style="line-height: 1;" class="">x</code>&nbsp;is a class, protocol, or generic value, the implementation of&nbsp;<code style="line-height: 1;" class="">y</code>&nbsp;can`t be statically known, so we need an abstract interface for mutating the property, projecting out the value at the start of the access and committing the mutated value back at the end. Ideally, the interface should accommodate efficient access to both stored and computed properties, avoiding unnecessary copying of stored properties to avoid inducing CoW copies or, in the near future, violating the constraints of move-only types or the borrow model. There are two broad approaches to doing this:</p><h2 id="continuation-basedaccess" style="color: rgb(17, 17, 17); font-size: 27px; line-height: 42px; margin-top: 42px; margin-bottom: 21px; font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;" class="">Continuation-based access</h2><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">The accessor that begins the formal access can return a continuation closure to call when the access is completed. This is the approach Swift currently uses with its&nbsp;<code style="line-height: 1;" class="">materializeForSet</code>&nbsp;accessor pattern. A property under this model compiles to something like this C code:</p><pre style="margin-top: 21px; margin-bottom: 21px; tab-size: 4; color: rgb(17, 17, 17); font-size: 15px; background-color: rgb(248, 248, 248); height: 729px;" class=""><code class="c cpp hljs" style="line-height: inherit; display: block; padding: 0.5em; color: rgb(51, 51, 51); height: auto;"><span class="hljs-keyword" style="font-weight: bold;">struct</span> X {
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// getter for Y</span>
  Y (*getY)(<span class="hljs-keyword" style="font-weight: bold;">const</span> X *self);
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// setter for Y</span>
  <span class="hljs-keyword" style="font-weight: bold;">void</span> (*setY)(X *self, Y newValue);
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// materializeForSet for Y</span>
  <span class="hljs-keyword" style="font-weight: bold;">struct</span> MaterializeForSetReturn {
    <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// The projected field</span>
    Y *y;
    <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// The function that finishes the access, or null</span>
    <span class="hljs-keyword" style="font-weight: bold;">void</span> (*finish)(X *self, <span class="hljs-keyword" style="font-weight: bold;">void</span> *buffer);
  }; 
  <span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">struct</span> <span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">MaterializeForSetReturn</span> <span class="hljs-params">(*materalizeForSettingY)</span><span class="hljs-params">(X *self, <span class="hljs-keyword" style="font-weight: bold;">void</span> *buffer)</span></span>;
};

<span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">void</span> <span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">callFooOnXDotYDotZ</span><span class="hljs-params">(X *x)</span> </span>{
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// To compile:</span>
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">//   foo(&amp;x.y.z)</span>
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// </span>
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// - call the materializeForSet accessor to begin the access to y, giving it a buffer</span>
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">//   to store a computed value or capture state to pass to the end of the access</span>
  <span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">char</span> <span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">alignas</span><span class="hljs-params">(Y)</span> bufferY[<span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">sizeof</span><span class="hljs-params">(Y)</span> + 3*<span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">sizeof</span><span class="hljs-params">(<span class="hljs-keyword" style="font-weight: bold;">void</span>*)</span>]</span>;
  <span class="hljs-keyword" style="font-weight: bold;">auto</span> materializedY = x-&gt;materializeForSettingY(x, bufferY);
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// - call materializeForSet to begin access to z</span>
  <span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">char</span> <span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">alignas</span><span class="hljs-params">(Z)</span> bufferZ[<span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">sizeof</span><span class="hljs-params">(Z)</span> + 3*<span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">sizeof</span><span class="hljs-params">(<span class="hljs-keyword" style="font-weight: bold;">void</span>*)</span>]</span>;
  autom materializedZ = materializedY.y-&gt;materializeForSettingZ(materializedY.y, bufferZ);
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// - perform the access</span>
  foo(materializedZ.z);
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// - finish the accesses, if we were given a finishing callback</span>
  <span class="hljs-keyword" style="font-weight: bold;">if</span> (materializedZ.finish)
    materializedZ.finish(materializedY.y, bufferZ);
  <span class="hljs-keyword" style="font-weight: bold;">if</span> (materializedY.finish)
    materializedY.finish(x, bufferY);
}</code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">A stored property can be exposed through this interface by trivially returning the projected address of the subobject, with no continuation:</p><pre style="margin-top: 21px; margin-bottom: 21px; tab-size: 4; color: rgb(17, 17, 17); font-size: 15px; background-color: rgb(248, 248, 248); height: 99px;" class=""><code class="c cpp hljs" style="line-height: inherit; display: block; padding: 0.5em; color: rgb(51, 51, 51); height: auto;"><span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">struct</span> MaterializeForSetReturn
<span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">materializeForSettingStoredY</span><span class="hljs-params">(X *self, <span class="hljs-keyword" style="font-weight: bold;">void</span> *buffer)</span> </span>{
  <span class="hljs-keyword" style="font-weight: bold;">return</span> (<span class="hljs-keyword" style="font-weight: bold;">struct</span> MaterializeForSetReturn){&amp;self-&gt;y, <span class="hljs-literal">NULL</span>};
}</code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">whereas a computed property can call the getter, store the computed value into the buffer, and return a continuation function that calls the setter:</p><pre style="margin-top: 21px; margin-bottom: 21px; tab-size: 4; color: rgb(17, 17, 17); font-size: 15px; background-color: rgb(248, 248, 248); height: 246px;" class=""><code class="c cpp hljs" style="line-height: inherit; display: block; padding: 0.5em; color: rgb(51, 51, 51); height: auto;"><span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">struct</span> MaterializeForSetReturn
<span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">materializeForSettingComputedY</span><span class="hljs-params">(X *self, <span class="hljs-keyword" style="font-weight: bold;">void</span> *buffer)</span> </span>{
  Y oldValue = getY(self);
  <span class="hljs-built_in" style="color: rgb(0, 134, 179);">memcpy</span>(buffer, &amp;oldValue, <span class="hljs-keyword" style="font-weight: bold;">sizeof</span>(Y));
  <span class="hljs-keyword" style="font-weight: bold;">return</span> (<span class="hljs-keyword" style="font-weight: bold;">struct</span> MaterializeForSetReturn){(Y *)buffer, finishSettingComputedY};
}
<span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">void</span> <span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">finishSettingComputedY</span><span class="hljs-params">(X *self, <span class="hljs-keyword" style="font-weight: bold;">void</span> *buffer)</span> </span>{
  Y newValue;
  <span class="hljs-built_in" style="color: rgb(0, 134, 179);">memcpy</span>(&amp;newValue, buffer, <span class="hljs-keyword" style="font-weight: bold;">sizeof</span>(Y));
  setY(self, newValue);
}</code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">A benefit of this approach is that it maintains the integrity of the source function in the compiled code. On the other hand, in order for the property implementation to transfer state from the start to the completion of the access, the caller must preallocate some stack space; if it’s too much, the space is wasted, and if it’s not enough, the property implementation must use malloc to get more space for itself. (It’s theoretically possible to avoid this with some clever stack pointer accounting.) There’s also a code size impact on the caller, which needs to bracket the&nbsp;<code style="line-height: 1;" class="">inout</code>&nbsp;access with a call on each side. The underlying CPU has to execute three or five branches per segment of the access path (calling and returning from&nbsp;<code style="line-height: 1;" class="">materializeForSet</code>, testing for&nbsp;<code style="line-height: 1;" class="">null</code>, and potentially calling and returning from the continuation if there is one).</p><h2 id="nestedfunctions" style="color: rgb(17, 17, 17); font-size: 27px; line-height: 42px; margin-top: 42px; margin-bottom: 21px; font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;" class="">Nested functions</h2><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">Alternatively, we can implement the formal access pattern as a series of nested functions. A property under this model compiles to something like this C code:</p><pre style="margin-top: 21px; margin-bottom: 21px; tab-size: 4; color: rgb(17, 17, 17); font-size: 15px; background-color: rgb(248, 248, 248); height: 498px;" class=""><code class="c cpp hljs" style="line-height: inherit; display: block; padding: 0.5em; color: rgb(51, 51, 51); height: auto;"><span class="hljs-keyword" style="font-weight: bold;">struct</span> X {
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// getter for Y</span>
  Y (*getY)(<span class="hljs-keyword" style="font-weight: bold;">const</span> X *self);
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// setter for Y</span>
  <span class="hljs-keyword" style="font-weight: bold;">void</span> (*setY)(X *self, Y newValue);
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// mutator for Y</span>
  <span class="hljs-keyword" style="font-weight: bold;">void</span> *(*mutateY)(X *self, <span class="hljs-keyword" style="font-weight: bold;">void</span> *context, <span class="hljs-keyword" style="font-weight: bold;">void</span> *(*innerMutation)(Y *y, <span class="hljs-keyword" style="font-weight: bold;">void</span> *context));
};

<span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">void</span> <span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">callFooOnXDotYDotZ</span><span class="hljs-params">(X *x)</span> </span>{
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// To compile:</span>
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">//   foo(&amp;x.y)</span>
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// - call the mutate accessor for y, with the inner access as a</span>
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">//   function whose pointer gets passed in</span>
  x-&gt;mutateY(x, <span class="hljs-literal">NULL</span>, callFooOnXDotY_inner_1);
}
<span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">void</span> <span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">callFooOnXDotYDotZ_inner_1</span><span class="hljs-params">(Y *y, <span class="hljs-keyword" style="font-weight: bold;">void</span> *context)</span> </span>{
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// - call the mutate accessor for z</span>
  y-&gt;mutateZ(y, context, callFooOnXDotY_inner_2);
}
<span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">void</span> <span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">callFooOnXDotYDotZ_inner_2</span><span class="hljs-params">(Z *z, <span class="hljs-keyword" style="font-weight: bold;">void</span> *context)</span> </span>{
  foo(z);
}</code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">A stored property can implement&nbsp;<code style="line-height: 1;" class="">mutate</code>&nbsp;by projecting the physical address of the subobject, tail-calling the subsequent inner function:</p><pre style="margin-top: 21px; margin-bottom: 21px; tab-size: 4; color: rgb(17, 17, 17); font-size: 15px; background-color: rgb(248, 248, 248); height: 78px;" class=""><code class="c cpp hljs" style="line-height: inherit; display: block; padding: 0.5em; color: rgb(51, 51, 51); height: auto;"><span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">void</span> *<span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">mutateStoredY</span><span class="hljs-params">(X *self, <span class="hljs-keyword" style="font-weight: bold;">void</span> *context, <span class="hljs-keyword" style="font-weight: bold;">void</span> *(*innerMutation)</span><span class="hljs-params">(Y *y, <span class="hljs-keyword" style="font-weight: bold;">void</span> *context)</span>) </span>{
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">/*tail*/</span> <span class="hljs-keyword" style="font-weight: bold;">return</span> innerMutation(&amp;self-&gt;y, context);
}</code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">and a computed property can bracket the inner mutation in getter and setter calls:</p><pre style="margin-top: 21px; margin-bottom: 21px; tab-size: 4; color: rgb(17, 17, 17); font-size: 15px; background-color: rgb(248, 248, 248); height: 162px;" class=""><code class="c cpp hljs" style="line-height: inherit; display: block; padding: 0.5em; color: rgb(51, 51, 51); height: auto;"><span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">void</span> *<span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">mutateComputedY</span><span class="hljs-params">(X *self, <span class="hljs-keyword" style="font-weight: bold;">void</span> *context, <span class="hljs-keyword" style="font-weight: bold;">void</span> *(*innerMutation)</span><span class="hljs-params">(Y *y, <span class="hljs-keyword" style="font-weight: bold;">void</span> *context)</span>) </span>{
  Y value = getY(self);
  <span class="hljs-keyword" style="font-weight: bold;">void</span> *result = innerMutation(&amp;value, context);
  setY(self, value);
  <span class="hljs-keyword" style="font-weight: bold;">return</span> result;
}</code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">This makes things easier for the property implementation, which can easily save as much state as it needs to on the stack instead of relying on a preallocated buffer handed down from the caller. There’s one fewer branch necessary per segment of the access path compared to the continuation-based model, either three if the accessor has no reconciliation necessary and can tail-call the inner function, or four if it does a normal call. On the other hand, the caller function has to be broken up into many smaller subfunctions, potentially one for every segment of an access path.</p><h2 id="lettingnestedfunctionswalktheaccesspath" style="color: rgb(17, 17, 17); font-size: 27px; line-height: 42px; margin-top: 42px; margin-bottom: 21px; font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;" class="">Letting nested functions walk the access path</h2><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">We can reduce the number of nested functions that are needed for a nested access path by designing a convention that allows each mutator to directly call the mutator for the next segment of access. For example, the caller could construct the entire access path as an array of pointers to mutation functions:</p><pre style="margin-top: 21px; margin-bottom: 21px; tab-size: 4; color: rgb(17, 17, 17); font-size: 15px; background-color: rgb(248, 248, 248); height: 225px;" class=""><code class="c cpp hljs" style="line-height: inherit; display: block; padding: 0.5em; color: rgb(51, 51, 51); height: auto;"><span class="hljs-keyword" style="font-weight: bold;">typedef</span> <span class="hljs-keyword" style="font-weight: bold;">void</span> *(*MutatorFunction)(<span class="hljs-keyword" style="font-weight: bold;">void</span> *base, MutatorFunction *accessPath, <span class="hljs-keyword" style="font-weight: bold;">void</span> *context);
<span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">void</span> <span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">callFooOnXDotYDotZDotW</span><span class="hljs-params">(X *x)</span> </span>{
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// foo(&amp;x.y.z.w)</span>
  MutatorFunction accessPath[] = {
    (MutatorFunction)mutateZ,
    (MutatorFunction)mutateW,
    (MutatorFunction)callFooOnXDotYDotZDotW_inner
  };
  mutateY(x, accessPath, <span class="hljs-literal">NULL</span>);
}</code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">Each property accessor function then loads the next step of the access out of the path, and advances the pointer forward for the nested access:</p><pre style="margin-top: 21px; margin-bottom: 21px; tab-size: 4; color: rgb(17, 17, 17); font-size: 15px; background-color: rgb(248, 248, 248); height: 141px;" class=""><code class="c cpp hljs" style="line-height: inherit; display: block; padding: 0.5em; color: rgb(51, 51, 51); height: auto;"><span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">void</span> *<span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">mutateY</span><span class="hljs-params">(X *self, MutatorFunction *accessPath, <span class="hljs-keyword" style="font-weight: bold;">void</span> *context)</span> </span>{
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// Project out y</span>
  Y *y = self-&gt;y;
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// Call the next step of the access path, passing the remainder of the access path</span>
  <span class="hljs-keyword" style="font-weight: bold;">return</span> (*accessPath)(y, accessPath + <span class="hljs-number" style="color: rgb(0, 153, 153);">1</span>, context);
}</code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">And the inner function at the end of the access path ignores the access path parameter and performs the access:</p><pre style="margin-top: 21px; margin-bottom: 21px; tab-size: 4; color: rgb(17, 17, 17); font-size: 15px; background-color: rgb(248, 248, 248); height: 78px;" class=""><code class="c cpp hljs" style="line-height: inherit; display: block; padding: 0.5em; color: rgb(51, 51, 51); height: auto;"><span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">void</span> *<span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">callFooOnXDotYDotZDotW_inner</span><span class="hljs-params">(W *w, MutatorFunction *_ignored, <span class="hljs-keyword" style="font-weight: bold;">void</span> *context)</span> </span>{
  foo(w);
}</code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">In this model, only one inner function needs to be broken out of the caller for the innermost inout access, at least for simple property projections. There are only two branches needed per segment, or one for segments that can tail-call the inner access. Parameterized segments such as subscripts can be supported by storing the parameters in the access path buffer as well, and having the subscript accessor advance past the parameters when it consumes them. For example:</p><pre style="margin-top: 21px; margin-bottom: 21px; tab-size: 4; color: rgb(17, 17, 17); font-size: 15px; background-color: rgb(248, 248, 248); height: 435px;" class=""><code class="c cpp hljs" style="line-height: inherit; display: block; padding: 0.5em; color: rgb(51, 51, 51); height: auto;"><span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">void</span> <span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">callFooOnXDotYSubIDotZ</span><span class="hljs-params">(X *x, intptr_t i)</span> </span>{
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// foo(&amp;x.y[i].z)</span>
  <span class="hljs-keyword" style="font-weight: bold;">uintptr_t</span> accessPath[] = {
    (<span class="hljs-keyword" style="font-weight: bold;">uintptr_t</span>)mutateYSubscript,
    (<span class="hljs-keyword" style="font-weight: bold;">uintptr_t</span>)i,
    (<span class="hljs-keyword" style="font-weight: bold;">uintptr_t</span>)mutateZ,
    (<span class="hljs-keyword" style="font-weight: bold;">uintptr_t</span>)callFooOnXDotYSubIDotZ_inner
  };
  
  mutateY(x, accessPath, <span class="hljs-literal">NULL</span>);
}

<span class="hljs-function"><span class="hljs-keyword" style="font-weight: bold;">void</span> *<span class="hljs-title" style="color: rgb(153, 0, 0); font-weight: bold;">mutateYSubscript</span><span class="hljs-params">(Y *y, uintptr_t *accessPath, <span class="hljs-keyword" style="font-weight: bold;">void</span> *context)</span> </span>{
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// Get the subscript index out of the access path</span>
  <span class="hljs-keyword" style="font-weight: bold;">intptr_t</span> i = (<span class="hljs-keyword" style="font-weight: bold;">intptr_t</span>)*accessPath++;
  <span class="hljs-comment" style="color: rgb(153, 153, 136); font-style: italic;">// Get the next step of the access out of the access path</span>
  MutatorFunction next = (MutatorFunction)*accessPath++;
  
  <span class="hljs-keyword" style="font-weight: bold;">return</span> next(&amp;y-&gt;elements[i], accessPath, context);
}</code></pre><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">On the other hand, this does push the inner access functions and subscript arguments onto the stack, which is a small amount of overhead. Furthermore, the caller function has to push any state it needs to pass from outside the access to inside on the stack as well, if it isn’t able to cram it otherwise into a single context pointer argument.</p><h2 id="machine-levelcallingconventiontricks" style="color: rgb(17, 17, 17); font-size: 27px; line-height: 42px; margin-top: 42px; margin-bottom: 21px; font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif;" class="">Machine-level calling convention tricks</h2><p style="color: rgb(17, 17, 17); font-family: 'Helvetica Neue', Helvetica, Arial, Verdana, sans-serif; word-wrap: break-word; margin: 1.3125em 0px; font-size: 1.1429em; line-height: 1.3125em;" class="">To be able to feed a return value from the inner access back to the outer caller, nested mutators would need to preserve all of the return registers potentially used by the inner access on the way out of the access. To minimize the impact on the outer caller, the convention for mutators could also specify that they need to preserve the contents of callee-save registers when they call down to nested accesses. This should allow as much state to be kept in registers between the outer caller and inner access as if they were naturally in the same function, with at most a function call in between.</p><div class="">With these sorts of optimizations, I think the nested function approach has the potential to be competitive with, or even more efficient than, our current continuation-based materializeForSet design.</div><div class=""><br class=""></div><div class="">-Joe</div></div>_______________________________________________<br class="">swift-dev mailing list<br class=""><a href="mailto:swift-dev@swift.org" class="">swift-dev@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-dev<br class=""></div></blockquote></div><br class=""></body></html>