• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16// NOTE: using `undefined` directly gives "Failed to emit binary data: Argument 3 of function $jscall.invoke has undefined type" (#14501)
17import { cons, car, cdr, vundefined }
18    from "checked/js_call/js_call.js"
19
20//! CHECKER      Tree of js function calls JIT
21//! RUN          force_jit: true, entry: "test_call_tree", options: "--compiler-regex=merge_local_scopes.ETSGLOBAL::call_tree --compiler-interop-try-single-scope=false"
22//! METHOD       "call_tree"
23//! PASS_AFTER   "IrBuilder"
24//! PASS_BEFORE  "InteropIntrinsicOptimization"
25//! INST_COUNT   "Intrinsic.CompilerCreateLocalScope", 4
26//! INST_COUNT   "Intrinsic.CompilerDestroyLocalScope", 4
27//! INST_COUNT   "Intrinsic.CompilerConvertI32ToLocal", 4
28//! INST_COUNT   "Intrinsic.CompilerConvertJSValueToLocal", 4
29//! INST_COUNT   "Intrinsic.CompilerConvertLocalToJSValue", 4
30//! INST_COUNT   "Intrinsic.CompilerConvertRefTypeToLocal", 3
31//! PASS_AFTER   "InteropIntrinsicOptimization"
32//! INST_COUNT   "Intrinsic.CompilerCreateLocalScope", 1
33//! INST_COUNT   "Intrinsic.CompilerDestroyLocalScope", 3
34//! PASS_AFTER   "DeoptimizeElimination"
35//! PASS_AFTER_NEXT "Cleanup"
36//! INST_COUNT   "Intrinsic.CompilerConvertI32ToLocal", 3 # remove dominated wrap of x
37//! INST_COUNT   "Intrinsic.CompilerConvertJSValueToLocal", 1 # only conversion of  LoadStatic $dynmodule... left
38//! INST_COUNT   "Intrinsic.CompilerConvertLocalToJSValue", 3 # cannot remove because we return JSValue
39//! INST_NOT     "Intrinsic.CompilerConvertRefTypeToLocal"
40
41
42function call_tree(x: int): JSValue {
43    let list = cons(x, -1);
44    if (x < 10) {
45        list = cons(1, list);
46    } else {
47        if (x < 20) {
48            list = cons(x, list);
49        } else {
50            list = cdr(list);
51        }
52    }
53    return list;
54}
55
56function test_call_tree(): int {
57    if (car(call_tree(5)) as int != 1) {
58        return 1;
59    }
60    if (car(call_tree(15)) as int != 15) {
61        return 2;
62    }
63    if (call_tree(25) as int != -1) {
64        return 3;
65    }
66    return 0;
67}
68
69//! CHECKER      JS function calls in tree leaves, create common scope start JIT
70//! RUN          force_jit: true, entry: "test_call_hoist_scope_start", options: "--compiler-regex=merge_local_scopes.ETSGLOBAL::call_hoist_scope_start --compiler-interop-try-single-scope=false"
71//! METHOD       "call_hoist_scope_start"
72//! PASS_AFTER   "IrBuilder"
73//! PASS_BEFORE  "InteropIntrinsicOptimization"
74//! INST_COUNT   "Intrinsic.CompilerCreateLocalScope", 3
75//! INST_COUNT   "Intrinsic.CompilerDestroyLocalScope", 3
76//! PASS_AFTER   "InteropIntrinsicOptimization"
77//! INST_COUNT   "Intrinsic.CompilerCreateLocalScope", 1
78//! INST_COUNT   "Intrinsic.CompilerDestroyLocalScope", 3
79
80function call_hoist_scope_start(x: int): JSValue {
81    let list = vundefined;
82    if (x < 10) {
83        list = cons(1, vundefined);
84    } else {
85        if (x < 20) {
86            list = cons(x, vundefined);
87        } else {
88            list = cons(x * 2, vundefined);
89        }
90    }
91    return list;
92}
93
94function test_call_hoist_scope_start(): int {
95    if (car(call_hoist_scope_start(5)) as int != 1) {
96        return 1;
97    }
98    if (car(call_hoist_scope_start(15)) as int != 15) {
99        return 2;
100    }
101    if (car(call_hoist_scope_start(25)) as int != 50) {
102        return 3;
103    }
104    return 0;
105}
106
107//! CHECKER      ETS loop (not countable) between JS function calls
108//! RUN          force_jit: true, entry: "loop_into_scope", options: "--compiler-regex=merge_local_scopes.ETSGLOBAL::loop_into_scope --compiler-interop-try-single-scope=false"
109//! METHOD       "loop_into_scope"
110//! PASS_AFTER   "IrBuilder"
111//! PASS_BEFORE  "InteropIntrinsicOptimization"
112//! INST_COUNT   "Intrinsic.CompilerCreateLocalScope", 3
113//! INST_COUNT   "Intrinsic.CompilerDestroyLocalScope", 3
114//! INST_COUNT   "Intrinsic.CompilerConvertJSValueToLocal", 3
115//! INST_COUNT   "Intrinsic.CompilerConvertLocalToJSValue", 3
116//! INST_COUNT   "Intrinsic.CompilerConvertRefTypeToLocal", 3
117//! PASS_AFTER   "InteropIntrinsicOptimization"
118//! INST_COUNT   "Intrinsic.CompilerCreateLocalScope", 1
119//! INST_COUNT   "Intrinsic.CompilerDestroyLocalScope", 1
120//! PASS_AFTER   "DeoptimizeElimination"
121//! PASS_AFTER_NEXT "Cleanup"
122//! INST_COUNT   "Intrinsic.CompilerConvertJSValueToLocal", 1
123//! INST_COUNT   "Intrinsic.CompilerConvertRefTypeToLocal", 1 # conversion of JSRuntimeGetPropertyJSValue to local, NOTE(aefremov): remove
124//! INST_COUNT   "Intrinsic.CompilerConvertLocalToF64", 1
125//! INST_NOT     "Intrinsic.CompilerConvertLocalToJSValue"
126
127function loop_into_scope(): int {
128    let list = cons(1, vundefined);
129    let x = 0;
130    for (let i = 1; i < 10; i *= 2) {
131        x += i;
132    }
133    list = cons(x, list);
134    if (car(list) as int != 15) {
135        return 1;
136    }
137    return 0;
138}
139
140//! CHECKER      Fully unrolled and not unrolled countable loops with JS calls
141//! RUN          force_jit: true, entry: "small_loop_with_scopes", options: "--compiler-regex=merge_local_scopes.ETSGLOBAL::small_loop_with_scopes --compiler-interop-try-single-scope=false"
142//! METHOD       "small_loop_with_scopes"
143//! PASS_AFTER   "IrBuilder"
144//! INST_COUNT   "Intrinsic.CompilerCreateLocalScope", 4
145//! INST_COUNT   "Intrinsic.CompilerDestroyLocalScope", 4
146//! PASS_BEFORE  "InteropIntrinsicOptimization"
147//! # Additional scopes were created in LoopUnroll
148//! INST_COUNT   "Intrinsic.CompilerCreateLocalScope", 8
149//! INST_COUNT   "Intrinsic.CompilerDestroyLocalScope", 8
150//! INST_COUNT   "Intrinsic.CompilerConvertJSValueToLocal", 8
151//! INST_COUNT   "Intrinsic.CompilerConvertLocalToJSValue", 8
152//! INST_COUNT   "Intrinsic.CompilerConvertRefTypeToLocal", 8
153//! PASS_AFTER   "InteropIntrinsicOptimization"
154//! INST_COUNT   "Intrinsic.CompilerCreateLocalScope", 3
155//! INST_COUNT   "Intrinsic.CompilerDestroyLocalScope", 3
156//! PASS_AFTER   "DeoptimizeElimination"
157//! PASS_AFTER_NEXT "Cleanup"
158//! INST_COUNT   "Intrinsic.CompilerConvertJSValueToLocal", 3
159//! INST_COUNT   "Intrinsic.CompilerConvertLocalToJSValue", 2
160//! INST_COUNT   "Intrinsic.CompilerConvertRefTypeToLocal", 3
161//! INST         "Intrinsic.CompilerCreateLocalScope"
162//! INST_NEXT    "prop: head"
163//! INST_NEXT    "Intrinsic.CompilerCreateLocalScope"
164//! INST_NEXT    "Intrinsic.CompilerCreateLocalScope"
165
166function small_loop_with_scopes(): int {
167    let list = cons(1, vundefined);
168    // loop removed completely in unroll
169    for (let i = 1; i <= 5; i++) {
170        list = cons(i, list);
171    }
172    // loop not unrolled; two scopes are left inside loop because of side exit
173    for (let i = 5; i >= 1; i--) {
174        if (car(list) as int != i) {
175            return 1;
176        }
177        list = cdr(list);
178    }
179    return 0;
180}
181
182//! CHECKER      Countable loop with JS calls moved into one scope
183//! RUN          force_jit: true, entry: "large_loop_with_scopes", options: "--compiler-regex=merge_local_scopes.ETSGLOBAL::large_loop_with_scopes --compiler-loop-unroll=false --compiler-interop-try-single-scope=false"
184//! METHOD       "large_loop_with_scopes"
185//! PASS_AFTER   "IrBuilder"
186//! INST_COUNT   "Intrinsic.CompilerCreateLocalScope", 5
187//! INST_COUNT   "Intrinsic.CompilerDestroyLocalScope", 5
188//! PASS_BEFORE  "InteropIntrinsicOptimization"
189//! INST_COUNT   "Intrinsic.CompilerConvertJSValueToLocal", 5
190//! INST_COUNT   "Intrinsic.CompilerConvertLocalToJSValue", 5
191//! INST_COUNT   "Intrinsic.CompilerConvertRefTypeToLocal", 5
192//! PASS_AFTER   "InteropIntrinsicOptimization"
193//! INST_COUNT   "Intrinsic.CompilerCreateLocalScope", 1
194//! INST_COUNT   "Intrinsic.CompilerDestroyLocalScope", 1
195//! PASS_AFTER   "DeoptimizeElimination"
196//! PASS_AFTER_NEXT "Cleanup"
197//! INST_COUNT   "Intrinsic.CompilerConvertJSValueToLocal", 1
198//! INST_COUNT   "Intrinsic.CompilerConvertLocalToJSValue", 3
199//! INST_COUNT   "Intrinsic.CompilerConvertRefTypeToLocal", 3
200
201function large_loop_with_scopes(): int {
202    // NOTE(aefremov): scope start won't be hoisted from loop in current implementation, so we need a scope before loop in this test
203    let list = cons(0, vundefined);
204    for (let i = 1; i <= 9; i++) {
205        list = cons(i, list);
206    }
207    let res = 0;
208    for (let i = 9; i >= 1; i--) {
209        if (car(list) as int != i) {
210            res = 1;
211        }
212        list = cdr(list);
213    }
214    // manually peeled last iteration, see note above
215    if (car(list) as int != 0) {
216        res = 1;
217    }
218    return res;
219}
220
221//! CHECKER      ETS if chain between JS function calls
222//! RUN          force_jit: true, entry: "test_if_chain_into_scope", options: "--compiler-regex=merge_local_scopes.ETSGLOBAL::if_chain_into_scope --compiler-interop-try-single-scope=false"
223//! METHOD       "if_chain_into_scope"
224//! PASS_AFTER   "IrBuilder"
225//! PASS_BEFORE  "InteropIntrinsicOptimization"
226//! INST_COUNT   "IfImm", 2
227//! INST_COUNT   "Intrinsic.CompilerCreateLocalScope", 2
228//! INST_COUNT   "Intrinsic.CompilerDestroyLocalScope", 2
229//! INST_COUNT   "Intrinsic.CompilerConvertJSValueToLocal", 2
230//! INST_COUNT   "Intrinsic.CompilerConvertLocalToJSValue", 2
231//! INST_COUNT   "Intrinsic.CompilerConvertRefTypeToLocal", 2
232//! PASS_AFTER   "InteropIntrinsicOptimization"
233//! INST_COUNT   "Intrinsic.CompilerCreateLocalScope", 1
234//! INST_COUNT   "Intrinsic.CompilerDestroyLocalScope", 1
235//! PASS_AFTER   "DeoptimizeElimination"
236//! PASS_AFTER_NEXT "Cleanup"
237//! INST_COUNT   "Intrinsic.CompilerConvertJSValueToLocal", 1
238//! INST_COUNT   "Intrinsic.CompilerConvertLocalToJSValue", 1
239//! INST_COUNT   "Intrinsic.CompilerConvertRefTypeToLocal", 1
240
241function if_chain_into_scope(x: int): JSValue {
242    let list = cons(1, vundefined);
243    if (x < 0) {
244        x = -x;
245    }
246    if (x > 10) {
247        x = 10;
248    }
249    return cons(x, list);
250}
251
252function test_if_chain_into_scope(): int {
253    let x = car(if_chain_into_scope(-2)) as int;
254    if (x != 2) {
255        return 1;
256    }
257    x = car(if_chain_into_scope(15)) as int;
258    if (x != 10) {
259        return 2;
260    }
261    return 0;
262}
263
264//! CHECKER      ETS if chain with JS function calls
265//! RUN          force_jit: true, entry: "test_if_chain_with_scopes", options: "--compiler-regex=merge_local_scopes.ETSGLOBAL::if_chain_with_scopes --compiler-interop-try-single-scope=false"
266//! METHOD       "if_chain_with_scopes"
267//! PASS_AFTER   "IrBuilder"
268//! PASS_BEFORE  "InteropIntrinsicOptimization"
269//! INST_COUNT   "IfImm", 2
270//! INST_COUNT   "Intrinsic.CompilerCreateLocalScope", 5
271//! INST_COUNT   "Intrinsic.CompilerDestroyLocalScope", 5
272//! INST_COUNT   "Intrinsic.CompilerConvertI32ToLocal", 5
273//! INST_COUNT   "Intrinsic.CompilerConvertJSValueToLocal", 5
274//! INST_COUNT   "Intrinsic.CompilerConvertLocalToJSValue", 5
275//! INST_COUNT   "Intrinsic.CompilerConvertRefTypeToLocal", 5
276//! PASS_AFTER   "InteropIntrinsicOptimization"
277//! INST_COUNT   "Intrinsic.CompilerCreateLocalScope", 1
278//! INST_COUNT   "Intrinsic.CompilerDestroyLocalScope", 1
279//! PASS_AFTER   "DeoptimizeElimination"
280//! PASS_AFTER_NEXT "Cleanup"
281//! INST_COUNT   "Intrinsic.CompilerConvertI32ToLocal", 3 # duplicate wraps of x removed
282//! INST_COUNT   "Intrinsic.CompilerConvertJSValueToLocal", 1
283//! INST_COUNT   "Intrinsic.CompilerConvertLocalToJSValue", 5
284//! INST_COUNT   "Intrinsic.CompilerConvertRefTypeToLocal", 4
285
286function if_chain_with_scopes(x: int): JSValue {
287    let list = cons(1, vundefined);
288    if (x < 0) {
289        list = cons(x, list);
290    }
291    if (x < -10) {
292        list = cons(-10, list);
293    } else {
294        list = cons(x, list);
295    }
296    return cons(x, list);
297}
298
299function compare_lists(list: JSValue, expected: FixedArray<int>): boolean {
300    for (let i: int of expected) {
301        if (car(list) as int != i) {
302            return false;
303        }
304        list = cdr(list);
305    }
306    return true;
307}
308
309function test_if_chain_with_scopes(): int {
310    let list = if_chain_with_scopes(2);
311    if (!compare_lists(list, [2, 2, 1])) {
312        return 1;
313    }
314    list = if_chain_with_scopes(-12);
315    if (!compare_lists(list, [-12, -10, -12, 1])) {
316        return 2;
317    }
318    return 0;
319}
320