New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(ivy): add support for short-circuiting #24039
Conversation
@@ -494,11 +546,12 @@ describe('compiler compliance', () => { | |||
if (rf & 1) { | |||
$r3$.ɵE(0, 'my-comp'); | |||
$r3$.ɵe(); | |||
i0.ɵrS(10); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i0
-> $r3$
- everywhere updated
* Binding for pure functions are store after the LNodes in the data array but before the binding. | ||
* | ||
* --------------------------------------------------------------------------- | ||
* | LNodes ... | pure function bindings | regular binding / interpolations | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reg bindingS
You can preview e81bc71 at https://pr24039-e81bc71.ngbuilds.io/. |
28e7953
to
f399591
Compare
You can preview 28e7953 at https://pr24039-28e7953.ngbuilds.io/. |
} | ||
if (rf & 2) { | ||
// TODO(vicb): Shouldn't bind be the outer call ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mhevery the code needs to be updated here, right ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
correct, it needs to be wrapped in bind
You can preview f399591 at https://pr24039-f399591.ngbuilds.io/. |
You can preview 8662643 at https://pr24039-8662643.ngbuilds.io/. |
i0.ɵp(0, 'ternary', i0.ɵb((ctx.cond? i0.ɵf1(2, _c0, ctx.a): _c1))); | ||
i0.ɵp(0, 'pipe', i0.ɵb(i0.ɵpb3(6, 1, ctx.value, 1, 2))); | ||
i0.ɵp(0, 'and', i0.ɵb((ctx.cond && i0.ɵf1(4, _c0, ctx.b)))); | ||
i0.ɵp(0, 'or', i0.ɵb((ctx.cond || i0.ɵf1(6, _c0, ctx.c)))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not look right to me. 6
is already used 2 lines above. Are we double using the slots?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 lines above is i0.ɵpb3(6, 1, ctx.value, 1, 2)
- 6 is the pipe
- 1 is the offset of the reserved slot <- this one compares to this 6. No error here.
However i0
should be $r3$
. I'll update that part.
} | ||
if (rf & 2) { | ||
$r3$.ɵp( | ||
0, 'names', | ||
$r3$.ɵb($r3$.ɵfV($e0_ff$, ctx.n0, ctx.n1, ctx.n2, ctx.n3, ctx.n4, ctx.n5, ctx.n6, ctx.n7, ctx.n8))); | ||
$r3$.ɵb($r3$.ɵfV(10, $e0_ff$, ctx.n0, ctx.n1, ctx.n2, ctx.n3, ctx.n4, ctx.n5, ctx.n6, ctx.n7, ctx.n8))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems wrong. ɵfV(10, $e0_ff$, [ctx.n0, ctx.n1, ctx.n2, ctx.n3, ctx.n4, ctx.n5, ctx.n6, ctx.n7, ctx.n8]))
(note that args should be in array)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think at some point Chuck used that to prevent building an array. But good catch. I'll update (compiler + test).
This error is not introduced by the current PR but was here before
@@ -64,6 +64,8 @@ export { | |||
text as T, | |||
textBinding as t, | |||
|
|||
reserveSlots as rS, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am starting to think that maybe we should have a general instruction
function reserve(
elementCount: number,
bindingCount: number,
pureFnCount?: number,
pipeCount?: number,
): void;
This instruction would be always required as first instruction and it would allocate the data. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be.
I think we discussed reserving the bindingCount
part today (current issue with undefined
not being detected as a change).
Should be designed/agreed, I would tend to do this in a follow up PR. WDYT ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM
export function pureFunctionV(pureFn: (...v: any[]) => any, exps: any[], thisArg?: any): any { | ||
let different = false; | ||
export function pureFunctionV( | ||
slotOffset: number, pureFn: (...v: any[]) => any, exps: any[], thisArg?: any): any { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note exps
is any[]
hence pureFunctionV
must take args as [arg0, arg1, ...]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, will fix (this code was not changed by this PR).
} | ||
if (rf & 2) { | ||
// TODO(vicb): Shouldn't bind be the outer call ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
correct, it needs to be wrapped in bind
$r3$.ɵt(0, $r3$.ɵi1('', $r3$.ɵpb2(1, $r3$.ɵpb2(2, ctx.name, ctx.size), ctx.size), '')); | ||
$r3$.ɵt( | ||
0, | ||
$r3$.ɵi1('', $r3$.ɵpb2(1, 6, $r3$.ɵpb2(2, 3, ctx.name, ctx.size), ctx.size), '')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here, missing bind
.
Thinking more about it, I think we should just inline bind into text
, property
, attribute
instruction. This way it will be less code to generate. Maybe follow up PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is actually correct. interpolationX
returns NO_CHANGE
.
I don't want to change the behavior or textBinding
, ... in this PR. Out of scope.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mhevery Thanks for the review. I'll update this PR first thing tomorrow morning. |
You can preview a973202 at https://pr24039-a973202.ngbuilds.io/. |
You can preview fd9a07a at https://pr24039-fd9a07a.ngbuilds.io/. |
You can preview 4adff90 at https://pr24039-4adff90.ngbuilds.io/. |
Short-circuitable expressions (using ternary & binary operators) could not use the regular binding mechanism as it relies on the bindings being checked every single time - the index is incremented as part of checking the bindings. Then for pure function kind of bindings we use a different mechanism with a fixed index. As such short circuiting a binding check does not mess with the expected binding index. Note that all pure function bindings are handled the same wether or not they actually are short-circuitable. This allows to keep the compiler and compiled code simple - and there is no runtime perf cost anyway.
You can preview bb679b3 at https://pr24039-bb679b3.ngbuilds.io/. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI found some minor typos (probably for a follow-up, since this already has a merge label).
} | ||
|
||
/** | ||
* Sets up the binding index before execute any `pureFunctionX` instructions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
before execute
-> before executing
/** | ||
* Reserves slots for pure functions (`pureFunctionX` instructions) | ||
* | ||
* Binding for pure functions are store after the LNodes in the data array but before the binding. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Binding for pure functions
-> Bindings for pure functions
are store
-> are stored
*/ | ||
export function reserveSlots(numSlots: number) { | ||
// Init the slots with a unique `NO_CHANGE` value so that the first change is always detected | ||
// whether is happens or not during the first change detection pass - pure functions checks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is happens
-> it happens
@@ -2365,6 +2414,22 @@ function assertDataNext(index: number, arr?: any[]) { | |||
arr.length, index, `index ${index} expected to be at the end of arr (length ${arr.length})`); | |||
} | |||
|
|||
/** | |||
* On the first template pass the reserved slots should be set `NO_CHANGE`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the first template pass the reserved slots
-> On the first template pass, the reserved slots
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Short-circuitable expressions (using ternary & binary operators) could not use
the regular binding mechanism as it relies on the bindings being checked every
single time - the index is incremented as part of checking the bindings.
Then for pure function kind of bindings we use a different mechanism with a
fixed index. As such short circuiting a binding check does not mess with the
expected binding index.
Note that all pure function bindings are handled the same wether or not they
actually are short-circuitable. This allows to keep the compiler and compiled
code simple - and there is no runtime perf cost anyway.