@samitouri / QOS-React-1 / commits / 691cad6b51

Optimization for scope merging when no deps

Addresses T168684688 (#2242). MergeReactiveScopesThatInvalidateTogether does not merge scopes if their output is not guaranteed to change when their inputs do. So for example a case such as `{session_id: bar(props.bar)}` will not merge the scopes for `t0 = bar(props.bar)` and `t1 = {session_id: t0}`, because t0 isn't guaranteed to change when `props.bar` does, and we want to avoid recreating the t1 object unless it semantically changes. But there's a special case: if a scope has no dependencies, then we'll never execute it again anyway. So it doesn't matter what kind of value it produces and it's safe to merge with subsequent scopes: ```javascript return {session_id: bar()} ``` Without the reactive input, `bar()` will always return the same value since we'll only ever call it once anyway. So it's safe to then merge with the scope for the outer object literal.

Joe Savona committed Nov 9, 2023 at 16:21 UTC 691cad6b51b03e8844104b9a55116903da33d3e9
20 files changed +173 -248
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/MergeReactiveScopesThatInvalidateTogether.ts
+20 -5
@@ -232,7 +232,7 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
232 */
233 current.lvalues.clear();
234
235 - if (!scopeAlwaysInvalidatesOnDependencyChanges(instr)) {
235 + if (!scopeIsEligibleForMerging(instr)) {
236 /*
237 * The subsequent scope that we just merged isn't guaranteed to invalidate if its
238 * inputs change, so it is not a candidate for future merging
@@ -252,7 +252,7 @@ class Transform extends ReactiveFunctionTransform<ReactiveScopeDependencies | nu
252 reset();
253 }
254 // Only set a new merge candidate if the scope is guaranteed to invalidate on changes
255 - if (scopeAlwaysInvalidatesOnDependencyChanges(instr)) {
255 + if (scopeIsEligibleForMerging(instr)) {
256 current = {
257 scope: instr,
258 from: i,
@@ -432,9 +432,24 @@ function areEqualPaths(a: Array<string>, b: Array<string>): boolean {
432 return a.length === b.length && a.every((item, ix) => item === b[ix]);
433 }
434
435 -function scopeAlwaysInvalidatesOnDependencyChanges(
436 - scope: ReactiveScopeBlock
437 -): boolean {
435 +/**
436 + * Is this scope eligible for merging with subsequent scopes? In general this
437 + * is only true if the scope's output values are guaranteed to change when its
438 + * input changes. When the output may not change, it's better to avoid merging
439 + * with subsequent scopes so that they can compare the input and avoid updating
440 + * when there are no changes.
441 + *
442 + * A special-case is if the scope has no dependencies, then its output will
443 + * *never* change and it's also eligible for merging.
444 + */
445 +function scopeIsEligibleForMerging(scope: ReactiveScopeBlock): boolean {
446 + if (scope.scope.dependencies.size === 0) {
447 + /*
448 + * Regardless of the type of value produced, if the scope has no dependencies
449 + * then its value will never change.
450 + */
451 + return true;
452 + }
453 const visitor = new DeclarationTypeVisitor(scope.scope);
454 visitor.visitScope(scope, undefined);
455 return visitor.alwaysInvalidatesOnInputChange;
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/alias-capture-in-method-receiver-and-mutate.expect.md
+6 -15
@@ -31,28 +31,19 @@ import { unstable_useMemoCache as useMemoCache } from "react";
31 import { makeObject_Primitives, mutate } from "shared-runtime";
32
33 function Component() {
34 - const $ = useMemoCache(3);
35 - let x;
36 - let a;
34 + const $ = useMemoCache(1);
35 + let t0;
36 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
38 - a = makeObject_Primitives();
37 + const a = makeObject_Primitives();
38
40 - x = [];
39 + const x = [];
40 x.push(a);
41
42 mutate(x);
44 - $[0] = x;
45 - $[1] = a;
46 - } else {
47 - x = $[0];
48 - a = $[1];
49 - }
50 - let t0;
51 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
43 t0 = [x, a];
53 - $[2] = t0;
44 + $[0] = t0;
45 } else {
55 - t0 = $[2];
46 + t0 = $[0];
47 }
48 return t0;
49 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/alias-capture-in-method-receiver.expect.md
+6 -11
@@ -20,7 +20,7 @@ function Component() {
20 ```javascript
21 import { unstable_useMemoCache as useMemoCache } from "react";
22 function Component() {
23 - const $ = useMemoCache(3);
23 + const $ = useMemoCache(2);
24 let t0;
25 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
26 t0 = someObj();
@@ -29,20 +29,15 @@ function Component() {
29 t0 = $[0];
30 }
31 const a = t0;
32 - let x;
32 + let t1;
33 if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
34 - x = [];
34 + const x = [];
35 x.push(a);
36 - $[1] = x;
37 - } else {
38 - x = $[1];
39 - }
40 - let t1;
41 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
36 +
37 t1 = [x, a];
43 - $[2] = t1;
38 + $[1] = t1;
39 } else {
45 - t1 = $[2];
40 + t1 = $[1];
41 }
42 return t1;
43 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/alias-nested-member-path.expect.md
+5 -11
@@ -24,7 +24,7 @@ export const FIXTURE_ENTRYPOINT = {
24 ```javascript
25 import { unstable_useMemoCache as useMemoCache } from "react";
26 function component() {
27 - const $ = useMemoCache(3);
27 + const $ = useMemoCache(2);
28 let t0;
29 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
30 t0 = [];
@@ -33,21 +33,15 @@ function component() {
33 t0 = $[0];
34 }
35 const z = t0;
36 - let y;
36 + let x;
37 if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
38 - y = {};
38 + const y = {};
39 y.z = z;
40 - $[1] = y;
41 - } else {
42 - y = $[1];
43 - }
44 - let x;
45 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
40 x = {};
41 x.y = y;
48 - $[2] = x;
42 + $[1] = x;
43 } else {
50 - x = $[2];
44 + x = $[1];
45 }
46 return x;
47 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/builtin-jsx-tag-lowered-between-mutations.expect.md
+3 -10
@@ -14,23 +14,16 @@ function Component(props) {
14 ```javascript
15 import { unstable_useMemoCache as useMemoCache } from "react";
16 function Component(props) {
17 - const $ = useMemoCache(2);
17 + const $ = useMemoCache(1);
18 let t0;
19 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
20 const maybeMutable = new MaybeMutable();
21 - t0 = maybeMutate(maybeMutable);
21 + t0 = <div>{maybeMutate(maybeMutable)}</div>;
22 $[0] = t0;
23 } else {
24 t0 = $[0];
25 }
26 - let t1;
27 - if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
28 - t1 = <div>{t0}</div>;
29 - $[1] = t1;
30 - } else {
31 - t1 = $[1];
32 - }
33 - return t1;
26 + return t0;
27 }
28
29 ```
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/call.expect.md
+6 -15
@@ -22,27 +22,18 @@ import { unstable_useMemoCache as useMemoCache } from "react";
22 function foo() {}
23
24 function Component(props) {
25 - const $ = useMemoCache(3);
26 - let a;
27 - let b;
25 + const $ = useMemoCache(1);
26 + let t0;
27 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
29 - a = [];
30 - b = {};
28 + const a = [];
29 + const b = {};
30 foo(a, b);
31
32 foo(b);
34 - $[0] = a;
35 - $[1] = b;
36 - } else {
37 - a = $[0];
38 - b = $[1];
39 - }
40 - let t0;
41 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
33 t0 = <div a={a} b={b} />;
43 - $[2] = t0;
34 + $[0] = t0;
35 } else {
45 - t0 = $[2];
36 + t0 = $[0];
37 }
38 return t0;
39 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constructor.expect.md
+6 -15
@@ -22,26 +22,17 @@ import { unstable_useMemoCache as useMemoCache } from "react";
22 function Foo() {}
23
24 function Component(props) {
25 - const $ = useMemoCache(3);
26 - let a;
27 - let b;
25 + const $ = useMemoCache(1);
26 + let t0;
27 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
29 - a = [];
30 - b = {};
28 + const a = [];
29 + const b = {};
30 new Foo(a, b);
31 new Foo(b);
33 - $[0] = a;
34 - $[1] = b;
35 - } else {
36 - a = $[0];
37 - b = $[1];
38 - }
39 - let t0;
40 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
32 t0 = <div a={a} b={b} />;
42 - $[2] = t0;
33 + $[0] = t0;
34 } else {
44 - t0 = $[2];
35 + t0 = $[0];
36 }
37 return t0;
38 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/for-of-mutate.expect.md
+6 -11
@@ -28,24 +28,19 @@ import { unstable_useMemoCache as useMemoCache } from "react";
28 import { makeObject_Primitives, mutate, Stringify } from "shared-runtime";
29
30 function Component(_props) {
31 - const $ = useMemoCache(2);
32 - let results;
31 + const $ = useMemoCache(1);
32 + let t0;
33 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
34 const collection = [makeObject_Primitives()];
35 - results = [];
35 + const results = [];
36 for (const item of collection) {
37 results.push(<div key={Stringify(item)}>{Stringify(mutate(item))}</div>);
38 }
39 - $[0] = results;
40 - } else {
41 - results = $[0];
42 - }
43 - let t0;
44 - if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
39 +
40 t0 = <div>{results}</div>;
46 - $[1] = t0;
41 + $[0] = t0;
42 } else {
48 - t0 = $[1];
43 + t0 = $[0];
44 }
45 return t0;
46 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/global-jsx-tag-lowered-between-mutations.expect.md
+6 -17
@@ -25,28 +25,17 @@ function Component(props) {
25 ```javascript
26 import { unstable_useMemoCache as useMemoCache } from "react";
27 function Component(props) {
28 - const $ = useMemoCache(3);
29 - let T0;
30 - let t1;
28 + const $ = useMemoCache(1);
29 + let t0;
30 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
31 const maybeMutable = new MaybeMutable();
32
34 - T0 = View;
35 - t1 = maybeMutate(maybeMutable);
36 - $[0] = T0;
37 - $[1] = t1;
33 + t0 = <View>{maybeMutate(maybeMutable)}</View>;
34 + $[0] = t0;
35 } else {
39 - T0 = $[0];
40 - t1 = $[1];
36 + t0 = $[0];
37 }
42 - let t2;
43 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
44 - t2 = <T0>{t1}</T0>;
45 - $[2] = t2;
46 - } else {
47 - t2 = $[2];
48 - }
49 - return t2;
38 + return t0;
39 }
40
41 ```
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/hoisting-simple-const-declaration.expect.md
+5 -11
@@ -24,23 +24,17 @@ export const FIXTURE_ENTRYPOINT = {
24 ```javascript
25 import { unstable_useMemoCache as useMemoCache } from "react";
26 function hoisting() {
27 - const $ = useMemoCache(2);
28 - let foo;
27 + const $ = useMemoCache(1);
28 + let t0;
29 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
30 - foo = () => bar + baz;
30 + const foo = () => bar + baz;
31
32 const bar = 3;
33 const baz = 2;
34 - $[0] = foo;
35 - } else {
36 - foo = $[0];
37 - }
38 - let t0;
39 - if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
34 t0 = foo();
41 - $[1] = t0;
35 + $[0] = t0;
36 } else {
43 - t0 = $[1];
37 + t0 = $[0];
38 }
39 return t0;
40 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/jsx-member-expression-tag-grouping.expect.md
+6 -17
@@ -14,27 +14,16 @@ function Component(props) {
14 ```javascript
15 import { unstable_useMemoCache as useMemoCache } from "react";
16 function Component(props) {
17 - const $ = useMemoCache(3);
18 - let T0;
19 - let t1;
17 + const $ = useMemoCache(1);
18 + let t0;
19 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
20 const maybeMutable = new MaybeMutable();
22 - T0 = Foo.Bar;
23 - t1 = maybeMutate(maybeMutable);
24 - $[0] = T0;
25 - $[1] = t1;
21 + t0 = <Foo.Bar>{maybeMutate(maybeMutable)}</Foo.Bar>;
22 + $[0] = t0;
23 } else {
27 - T0 = $[0];
28 - t1 = $[1];
24 + t0 = $[0];
25 }
30 - let t2;
31 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
32 - t2 = <T0>{t1}</T0>;
33 - $[2] = t2;
34 - } else {
35 - t2 = $[2];
36 - }
37 - return t2;
26 + return t0;
27 }
28
29 ```
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/lambda-with-fbt.expect.md
+6 -13
@@ -42,7 +42,7 @@ import { unstable_useMemoCache as useMemoCache } from "react";
42 import { fbt } from "fbt";
43
44 function Component() {
45 - const $ = useMemoCache(3);
45 + const $ = useMemoCache(2);
46 let t0;
47 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
48 t0 = () => {
@@ -73,23 +73,16 @@ function Component() {
73 const buttonLabel = t0;
74 let t1;
75 if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
76 - t1 = buttonLabel();
77 - $[1] = t1;
78 - } else {
79 - t1 = $[1];
80 - }
81 - let t2;
82 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
83 - t2 = (
76 + t1 = (
77 <View>
85 - <Button text={t1} />
78 + <Button text={buttonLabel()} />
79 </View>
80 );
88 - $[2] = t2;
81 + $[1] = t1;
82 } else {
90 - t2 = $[2];
83 + t1 = $[1];
84 }
92 - return t2;
85 + return t1;
86 }
87
88 ```
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/merge-consecutive-scopes-no-deps.expect.md new
+44
@@ -0,0 +1,44 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +const { getNumber } = require("shared-runtime");
6 +
7 +function Component(props) {
8 + // Two scopes: one for `getNumber()`, one for the object literal.
9 + // Neither has dependencies so they should merge
10 + return { session_id: getNumber() };
11 +}
12 +
13 +export const FIXTURE_ENTRYPOINT = {
14 + fn: Component,
15 + params: [{}],
16 +};
17 +
18 +```
19 +
20 +## Code
21 +
22 +```javascript
23 +import { unstable_useMemoCache as useMemoCache } from "react";
24 +const { getNumber } = require("shared-runtime");
25 +
26 +function Component(props) {
27 + const $ = useMemoCache(1);
28 + let t0;
29 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
30 + t0 = { session_id: getNumber() };
31 + $[0] = t0;
32 + } else {
33 + t0 = $[0];
34 + }
35 + return t0;
36 +}
37 +
38 +export const FIXTURE_ENTRYPOINT = {
39 + fn: Component,
40 + params: [{}],
41 +};
42 +
43 +```
44 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/merge-consecutive-scopes-no-deps.js new
+12
@@ -0,0 +1,12 @@
1 +const { getNumber } = require("shared-runtime");
2 +
3 +function Component(props) {
4 + // Two scopes: one for `getNumber()`, one for the object literal.
5 + // Neither has dependencies so they should merge
6 + return { session_id: getNumber() };
7 +}
8 +
9 +export const FIXTURE_ENTRYPOINT = {
10 + fn: Component,
11 + params: [{}],
12 +};
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/mutable-lifetime-loops.expect.md
+8 -23
@@ -76,16 +76,13 @@ function cond(x) {
76 }
77
78 function testFunction(props) {
79 - const $ = useMemoCache(5);
80 - let a;
81 - let b;
82 - let c;
83 - let d;
79 + const $ = useMemoCache(1);
80 + let t0;
81 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
85 - a = {};
86 - b = {};
87 - c = {};
88 - d = {};
82 + let a = {};
83 + let b = {};
84 + let c = {};
85 + let d = {};
86 while (true) {
87 const z = a;
88 a = b;
@@ -107,22 +104,10 @@ function testFunction(props) {
104 }
105
106 mutate(d, null);
110 - $[0] = a;
111 - $[1] = b;
112 - $[2] = c;
113 - $[3] = d;
114 - } else {
115 - a = $[0];
116 - b = $[1];
117 - c = $[2];
118 - d = $[3];
119 - }
120 - let t0;
121 - if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
107 t0 = { a, b, c, d };
123 - $[4] = t0;
108 + $[0] = t0;
109 } else {
125 - t0 = $[4];
110 + t0 = $[0];
111 }
112 return t0;
113 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/ssa-call-jsx-2.expect.md
+6 -15
@@ -25,29 +25,20 @@ import { unstable_useMemoCache as useMemoCache } from "react"; // @Pass runMutab
25 function foo() {}
26
27 function Component(props) {
28 - const $ = useMemoCache(3);
29 - let a;
30 - let b;
28 + const $ = useMemoCache(1);
29 + let t0;
30 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
32 - a = [];
33 - b = {};
31 + const a = [];
32 + const b = {};
33 foo(a, b);
34 if (foo()) {
35 }
36
37 foo(a, b);
39 - $[0] = a;
40 - $[1] = b;
41 - } else {
42 - a = $[0];
43 - b = $[1];
44 - }
45 - let t0;
46 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
38 t0 = <div a={a} b={b} />;
48 - $[2] = t0;
39 + $[0] = t0;
40 } else {
50 - t0 = $[2];
41 + t0 = $[0];
42 }
43 return t0;
44 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/ssa-call-jsx.expect.md
+6 -15
@@ -22,27 +22,18 @@ import { unstable_useMemoCache as useMemoCache } from "react";
22 function foo() {}
23
24 function Component(props) {
25 - const $ = useMemoCache(3);
26 - let a;
27 - let b;
25 + const $ = useMemoCache(1);
26 + let t0;
27 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
29 - a = [];
30 - b = {};
28 + const a = [];
29 + const b = {};
30 foo(a, b);
31
32 foo(a, b);
34 - $[0] = a;
35 - $[1] = b;
36 - } else {
37 - a = $[0];
38 - b = $[1];
39 - }
40 - let t0;
41 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
33 t0 = <div a={a} b={b} />;
43 - $[2] = t0;
34 + $[0] = t0;
35 } else {
45 - t0 = $[2];
36 + t0 = $[0];
37 }
38 return t0;
39 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/transitive-freeze-array.expect.md
+7 -19
@@ -29,31 +29,19 @@ import { unstable_useMemoCache as useMemoCache } from "react"; // @enableTransit
29 const { mutate } = require("shared-runtime");
30
31 function Component(props) {
32 - const $ = useMemoCache(4);
33 - let x;
34 - let y;
35 - let items;
32 + const $ = useMemoCache(1);
33 + let t0;
34 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
37 - x = {};
38 - y = {};
39 - items = [x, y];
35 + const x = {};
36 + const y = {};
37 + const items = [x, y];
38 items.pop();
39
40 mutate(y);
43 - $[0] = x;
44 - $[1] = y;
45 - $[2] = items;
46 - } else {
47 - x = $[0];
48 - y = $[1];
49 - items = $[2];
50 - }
51 - let t0;
52 - if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
41 t0 = [x, y, items];
54 - $[3] = t0;
42 + $[0] = t0;
43 } else {
56 - t0 = $[3];
44 + t0 = $[0];
45 }
46 return t0;
47 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/type-test-field-load-binary-op.expect.md
+3 -10
@@ -21,7 +21,7 @@ function component() {
21 ```javascript
22 import { unstable_useMemoCache as useMemoCache } from "react";
23 function component() {
24 - const $ = useMemoCache(3);
24 + const $ = useMemoCache(2);
25 let t0;
26 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
27 t0 = makeSomePrimitive();
@@ -31,19 +31,12 @@ function component() {
31 }
32 let t1;
33 if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
34 - t1 = makeSomePrimitive();
34 + t1 = { u: t0, v: makeSomePrimitive() };
35 $[1] = t1;
36 } else {
37 t1 = $[1];
38 }
39 - let t2;
40 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
41 - t2 = { u: t0, v: t1 };
42 - $[2] = t2;
43 - } else {
44 - t2 = $[2];
45 - }
46 - const x = t2;
39 + const x = t1;
40 const u = x.u;
41 const v = x.v;
42 if (u > v) {
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/uninitialized-declaration-in-reactive-scope.expect.md
+6 -15
@@ -16,25 +16,16 @@ function Component(props) {
16 ```javascript
17 import { unstable_useMemoCache as useMemoCache } from "react";
18 function Component(props) {
19 - const $ = useMemoCache(3);
20 - let y;
21 - let x;
19 + const $ = useMemoCache(1);
20 + let t0;
21 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
23 - x = mutate();
24 -
22 + const x = mutate();
23 + let y;
24 foo(x);
26 - $[0] = y;
27 - $[1] = x;
28 - } else {
29 - y = $[0];
30 - x = $[1];
31 - }
32 - let t0;
33 - if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
25 t0 = [y, x];
35 - $[2] = t0;
26 + $[0] = t0;
27 } else {
37 - t0 = $[2];
28 + t0 = $[0];
29 }
30 return t0;
31 }