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