1 /*
2 * Copyright (c) 2023-2024 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 #include "ecmascript/compiler/ntype_bytecode_lowering.h"
17
18 #include "ecmascript/compiler/lazy_deopt_dependency.h"
19 #include "ecmascript/compiler/type_info_accessors.h"
20 #include "ecmascript/dfx/vmstat/opt_code_profiler.h"
21 #include "ecmascript/js_hclass-inl.h"
22 #include "ecmascript/jit/jit.h"
23 #include "ecmascript/lexical_env.h"
24
25 namespace panda::ecmascript::kungfu {
26
RunNTypeBytecodeLowering()27 void NTypeBytecodeLowering::RunNTypeBytecodeLowering()
28 {
29 std::vector<GateRef> gateList;
30 circuit_->GetAllGates(gateList);
31 for (const auto &gate : gateList) {
32 auto op = acc_.GetOpCode(gate);
33 if (op == OpCode::JS_BYTECODE) {
34 Lower(gate);
35 }
36 }
37
38 if (IsLogEnabled()) {
39 LOG_COMPILER(INFO) << "";
40 LOG_COMPILER(INFO) << "\033[34m"
41 << "===================="
42 << " After NTypeBytecodeLowering "
43 << "[" << GetMethodName() << "]"
44 << "===================="
45 << "\033[0m";
46 circuit_->PrintAllGatesWithBytecode();
47 LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m";
48 }
49 }
50
Lower(GateRef gate)51 void NTypeBytecodeLowering::Lower(GateRef gate)
52 {
53 [[maybe_unused]] auto scopedGate = circuit_->VisitGateBegin(gate);
54 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
55 // initialize label manager
56 Environment env(gate, circuit_, &builder_);
57 switch (ecmaOpcode) {
58 case EcmaOpcode::CREATEEMPTYARRAY_IMM8:
59 case EcmaOpcode::CREATEEMPTYARRAY_IMM16:
60 LowerNTypedCreateEmptyArray(gate);
61 break;
62 case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM8_ID16:
63 case EcmaOpcode::CREATEARRAYWITHBUFFER_IMM16_ID16: {
64 Jit::JitLockHolder lock(compilationEnv_, "LowerNTypedCreateArrayWithBuffer");
65 LowerNTypedCreateArrayWithBuffer(gate);
66 }
67 break;
68 case EcmaOpcode::COPYRESTARGS_IMM8:
69 case EcmaOpcode::WIDE_COPYRESTARGS_PREF_IMM16:
70 LowerNTypedCopyRestArgs(gate);
71 break;
72 case EcmaOpcode::GETUNMAPPEDARGS:
73 LowerNTypedGetUnmappedArgs(gate);
74 break;
75 case EcmaOpcode::STOWNBYINDEX_IMM8_V8_IMM16:
76 case EcmaOpcode::STOWNBYINDEX_IMM16_V8_IMM16:
77 case EcmaOpcode::WIDE_STOWNBYINDEX_PREF_V8_IMM32:
78 LowerNTypedStownByIndex(gate);
79 break;
80 case EcmaOpcode::THROW_UNDEFINEDIFHOLEWITHNAME_PREF_ID16:
81 LowerThrowUndefinedIfHoleWithName(gate);
82 break;
83 case EcmaOpcode::THROW_IFSUPERNOTCORRECTCALL_PREF_IMM8:
84 case EcmaOpcode::THROW_IFSUPERNOTCORRECTCALL_PREF_IMM16:
85 LowerThrowIfSuperNotCorrectCall(gate);
86 break;
87 case EcmaOpcode::THROW_IFNOTOBJECT_PREF_V8:
88 LowerThrowIfNotObject(gate);
89 break;
90 case EcmaOpcode::LDLEXVAR_IMM4_IMM4:
91 case EcmaOpcode::LDLEXVAR_IMM8_IMM8:
92 case EcmaOpcode::WIDE_LDLEXVAR_PREF_IMM16_IMM16:
93 LowerLdLexVar(gate);
94 break;
95 case EcmaOpcode::STLEXVAR_IMM4_IMM4:
96 case EcmaOpcode::STLEXVAR_IMM8_IMM8:
97 case EcmaOpcode::WIDE_STLEXVAR_PREF_IMM16_IMM16:
98 LowerStLexVar(gate);
99 break;
100 case EcmaOpcode::LDLOCALMODULEVAR_IMM8:
101 case EcmaOpcode::WIDE_LDLOCALMODULEVAR_PREF_IMM16:
102 LowerLdLocalMoudleVar(gate);
103 break;
104 case EcmaOpcode::LDEXTERNALMODULEVAR_IMM8:
105 case EcmaOpcode::WIDE_LDEXTERNALMODULEVAR_PREF_IMM16:
106 LowerLdExternalMoudleVar(gate);
107 break;
108 case EcmaOpcode::STMODULEVAR_IMM8:
109 case EcmaOpcode::WIDE_STMODULEVAR_PREF_IMM16:
110 LowerStModuleVar(gate);
111 break;
112 case EcmaOpcode::STOWNBYNAME_IMM8_ID16_V8:
113 case EcmaOpcode::STOWNBYNAME_IMM16_ID16_V8: {
114 Jit::JitLockHolder lock(compilationEnv_, "LowerNTypedStOwnByName");
115 LowerNTypedStOwnByName(gate);
116 }
117 break;
118 default:
119 break;
120 }
121 }
122
LowerThrowUndefinedIfHoleWithName(GateRef gate)123 void NTypeBytecodeLowering::LowerThrowUndefinedIfHoleWithName(GateRef gate)
124 {
125 AddProfiling(gate);
126 GateRef value = acc_.GetValueIn(gate, 1); // 1: the second parameter
127 builder_.LexVarIsHoleCheck(value);
128 acc_.ReplaceHirAndReplaceDeadIfException(gate, builder_.GetStateDepend(), Circuit::NullGate());
129 }
130
LowerThrowIfSuperNotCorrectCall(GateRef gate)131 void NTypeBytecodeLowering::LowerThrowIfSuperNotCorrectCall(GateRef gate)
132 {
133 AddProfiling(gate);
134 GateRef index = acc_.GetValueIn(gate, 0);
135 GateRef value = acc_.GetValueIn(gate, 1);
136 uint32_t indexValue = static_cast<uint32_t>(acc_.GetConstantValue(index));
137 if (indexValue == CALL_SUPER_BEFORE_THIS_CHECK) {
138 builder_.IsUndefinedOrHoleCheck(value);
139 } else if (indexValue == FORBIDDEN_SUPER_REBIND_THIS_CHECK) {
140 builder_.IsNotUndefinedOrHoleCheck(value);
141 } else {
142 UNREACHABLE();
143 }
144 acc_.ReplaceHirAndReplaceDeadIfException(gate, builder_.GetStateDepend(), builder_.TaggedTrue());
145 }
146
LowerThrowIfNotObject(GateRef gate)147 void NTypeBytecodeLowering::LowerThrowIfNotObject(GateRef gate)
148 {
149 AddProfiling(gate);
150 GateRef value = acc_.GetValueIn(gate, 0); // 0: the first parameter
151 builder_.EcmaObjectCheck(value);
152 acc_.ReplaceHirAndReplaceDeadIfException(gate, builder_.GetStateDepend(), Circuit::NullGate());
153 }
154
LowerLdLexVar(GateRef gate)155 void NTypeBytecodeLowering::LowerLdLexVar(GateRef gate)
156 {
157 AddProfiling(gate);
158 GateRef level = acc_.GetValueIn(gate, 0); // 0: first parameter
159 GateRef index = acc_.GetValueIn(gate, 1); // 1: the second parameter
160 GateRef currentEnv = acc_.GetValueIn(gate, 2); // 2: the third parameter
161
162 uint32_t levelValue = static_cast<uint32_t>(acc_.GetConstantValue(level));
163 uint32_t indexValue = static_cast<uint32_t>(acc_.GetConstantValue(index));
164 indexValue += LexicalEnv::RESERVED_ENV_LENGTH;
165 GateRef result = Circuit::NullGate();
166 if (levelValue == 0) {
167 result = builder_.LoadFromTaggedArray(currentEnv, indexValue);
168 } else if (levelValue == 1) { // 1: level 1
169 auto parentEnv = builder_.LoadFromTaggedArray(currentEnv, LexicalEnv::PARENT_ENV_INDEX);
170 result = builder_.LoadFromTaggedArray(parentEnv, indexValue);
171 } else {
172 // level > 1, go slowpath
173 return;
174 }
175 acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result);
176 }
177
LowerStLexVar(GateRef gate)178 void NTypeBytecodeLowering::LowerStLexVar(GateRef gate)
179 {
180 AddProfiling(gate);
181 GateRef level = acc_.GetValueIn(gate, 0); // 0: first parameter
182 GateRef index = acc_.GetValueIn(gate, 1); // 1: the second parameter
183 GateRef currentEnv = acc_.GetValueIn(gate, 2); // 2: the third parameter
184 GateRef value = acc_.GetValueIn(gate, 3); // 3: the fourth parameter
185
186 uint32_t levelValue = static_cast<uint32_t>(acc_.GetConstantValue(level));
187 uint32_t indexValue = static_cast<uint32_t>(acc_.GetConstantValue(index));
188 indexValue += LexicalEnv::RESERVED_ENV_LENGTH;
189 GateRef result = Circuit::NullGate();
190 if (levelValue == 0) {
191 result = builder_.StoreToTaggedArray(currentEnv, indexValue, value);
192 } else if (levelValue == 1) { // 1: level 1
193 auto parentEnv = builder_.LoadFromTaggedArray(currentEnv, LexicalEnv::PARENT_ENV_INDEX);
194 result = builder_.StoreToTaggedArray(parentEnv, indexValue, value);
195 } else {
196 // level > 1, go slowpath
197 return;
198 }
199 acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result);
200 }
201
LowerNTypedCreateEmptyArray(GateRef gate)202 void NTypeBytecodeLowering::LowerNTypedCreateEmptyArray(GateRef gate)
203 {
204 // in the future, the type of the elements in the array will be obtained through pgo,
205 // and the type will be used to determine whether to create a typed-array.
206 AddProfiling(gate);
207 ElementsKind kind = acc_.TryGetElementsKind(gate);
208 uint32_t length = acc_.TryGetArrayElementsLength(gate);
209 RegionSpaceFlag flag = acc_.TryGetRegionSpaceFlag(gate);
210 GateRef array = builder_.CreateArray(kind, 0, builder_.Int64(length), flag);
211 acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), array);
212 }
213
LowerNTypedCreateArrayWithBuffer(GateRef gate)214 void NTypeBytecodeLowering::LowerNTypedCreateArrayWithBuffer(GateRef gate)
215 {
216 // 2: number of CREATEARRAYWITHBUFFER_IMM16_ID16/CREATEARRAYWITHBUFFER_IMM8_ID16 inputs
217 ASSERT(acc_.GetNumValueIn(gate) == 2);
218 GateRef index = acc_.GetValueIn(gate, 0);
219 auto methodOffset = acc_.TryGetMethodOffset(gate);
220 uint32_t cpId = ptManager_->GetConstantPoolIDByMethodOffset(methodOffset);
221
222 uint32_t constPoolIndex = static_cast<uint32_t>(acc_.GetConstantValue(index));
223 JSTaggedValue arr = GetArrayLiteralValue(cpId, constPoolIndex);
224 // Since jit has no lazy loading array from constantpool, arr might be undefined.
225 if (arr.IsUndefined()) {
226 return;
227 }
228 AddProfiling(gate);
229 ElementsKind kind = acc_.TryGetElementsKind(gate);
230 RegionSpaceFlag flag = acc_.TryGetRegionSpaceFlag(gate);
231 GateRef cpIdGr = builder_.Int32(cpId);
232 GateRef array =
233 builder_.CreateArrayWithBuffer(kind, ArrayMetaDataAccessor::Mode::CREATE,
234 cpIdGr, index, flag);
235 acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), array);
236 }
237
LowerNTypedCopyRestArgs(GateRef gate)238 void NTypeBytecodeLowering::LowerNTypedCopyRestArgs(GateRef gate)
239 {
240 ASSERT(acc_.GetNumValueIn(gate) == 1);
241 GateRef restIdx = acc_.GetValueIn(gate, 0);
242 AddProfiling(gate);
243 ElementsKind kind = acc_.TryGetElementsKind(gate);
244 GateRef arguments =
245 builder_.CreateArguments(kind, CreateArgumentsAccessor::Mode::REST_ARGUMENTS, restIdx);
246 ReplaceGateWithPendingException(gate, builder_.GetState(), builder_.GetDepend(), arguments);
247 }
248
LowerNTypedGetUnmappedArgs(GateRef gate)249 void NTypeBytecodeLowering::LowerNTypedGetUnmappedArgs(GateRef gate)
250 {
251 ASSERT(acc_.GetNumValueIn(gate) == 0);
252 GateRef restIdx = builder_.Int32(0);
253 AddProfiling(gate);
254 ElementsKind kind = acc_.TryGetElementsKind(gate);
255 GateRef arguments =
256 builder_.CreateArguments(kind, CreateArgumentsAccessor::Mode::UNMAPPED_ARGUMENTS, restIdx);
257 ReplaceGateWithPendingException(gate, builder_.GetState(), builder_.GetDepend(), arguments);
258 }
259
LowerNTypedStownByIndex(GateRef gate)260 void NTypeBytecodeLowering::LowerNTypedStownByIndex(GateRef gate)
261 {
262 // 3: number of value inputs
263 ASSERT(acc_.GetNumValueIn(gate) == 3);
264 GateRef receiver = acc_.GetValueIn(gate, 0);
265 GateRef index = acc_.GetValueIn(gate, 1);
266 GateRef value = acc_.GetValueIn(gate, 2);
267 if (acc_.GetOpCode(receiver) != OpCode::CREATE_ARRAY &&
268 acc_.GetOpCode(receiver) != OpCode::CREATE_ARRAY_WITH_BUFFER) {
269 return;
270 }
271 builder_.COWArrayCheck(receiver);
272
273 AddProfiling(gate);
274 uint32_t indexValue = static_cast<uint32_t>(acc_.GetConstantValue(index));
275 uint32_t arraySize = acc_.GetArraySize(receiver);
276 if (indexValue > arraySize) {
277 acc_.TrySetElementsKind(receiver, ElementsKind::HOLE);
278 }
279 acc_.SetArraySize(receiver, std::max(arraySize, indexValue + 1));
280 index = builder_.Int32(indexValue);
281 builder_.StoreElement<TypedStoreOp::ARRAY_STORE_ELEMENT>(receiver, index, value);
282 acc_.ReplaceHirAndReplaceDeadIfException(gate, builder_.GetStateDepend(), Circuit::NullGate());
283 }
284
AddProfiling(GateRef gate)285 void NTypeBytecodeLowering::AddProfiling(GateRef gate)
286 {
287 if (IsTraceBC()) {
288 // see stateSplit as a part of JSByteCode if exists
289 GateRef maybeStateSplit = acc_.GetDep(gate);
290 GateRef current = Circuit::NullGate();
291 if (acc_.GetOpCode(maybeStateSplit) == OpCode::STATE_SPLIT) {
292 current = maybeStateSplit;
293 } else {
294 current = gate;
295 }
296
297 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
298 auto ecmaOpcodeGate = builder_.Int32(static_cast<uint32_t>(ecmaOpcode));
299 GateRef constOpcode = builder_.Int32ToTaggedInt(ecmaOpcodeGate);
300 GateRef typedPath = builder_.Int32ToTaggedInt(builder_.Int32(1));
301 GateRef traceGate = builder_.CallRuntime(glue_, RTSTUB_ID(DebugAOTPrint), acc_.GetDep(current),
302 { constOpcode, typedPath }, gate);
303 acc_.SetDep(current, traceGate);
304 builder_.SetDepend(acc_.GetDep(gate)); // set gate depend: trace or STATE_SPLIT
305 }
306
307 if (IsProfiling()) {
308 // see stateSplit as a part of JSByteCode if exists
309 GateRef maybeStateSplit = acc_.GetDep(gate);
310 GateRef current = Circuit::NullGate();
311 if (acc_.GetOpCode(maybeStateSplit) == OpCode::STATE_SPLIT) {
312 current = maybeStateSplit;
313 } else {
314 current = gate;
315 }
316
317 GateRef func = builder_.Undefined();
318 if (acc_.HasFrameState(gate)) {
319 func = argAcc_->GetFrameArgsIn(gate, FrameArgIdx::FUNC);
320 }
321
322 GateRef bcIndex = builder_.Int32ToTaggedInt(builder_.Int32(acc_.TryGetBcIndex(gate)));
323 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
324 auto ecmaOpcodeGate = builder_.Int32(static_cast<uint32_t>(ecmaOpcode));
325 GateRef constOpcode = builder_.Int32ToTaggedInt(ecmaOpcodeGate);
326 GateRef mode =
327 builder_.Int32ToTaggedInt(builder_.Int32(static_cast<int32_t>(OptCodeProfiler::Mode::TYPED_PATH)));
328 GateRef profiling = builder_.CallRuntime(glue_, RTSTUB_ID(ProfileOptimizedCode), acc_.GetDep(current),
329 { func, bcIndex, constOpcode, mode }, gate);
330 acc_.SetDep(current, profiling);
331 builder_.SetDepend(acc_.GetDep(gate)); // set gate depend: profiling or STATE_SPLIT
332 }
333 }
334
LowerLdLocalMoudleVar(GateRef gate)335 void NTypeBytecodeLowering::LowerLdLocalMoudleVar(GateRef gate)
336 {
337 AddProfiling(gate);
338 GateRef jsFunc = argAcc_->GetFrameArgsIn(gate, FrameArgIdx::FUNC);
339 GateRef index = acc_.GetValueIn(gate, 0);
340 GateRef result = builder_.LdLocalModuleVar(jsFunc, index);
341 acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result);
342 }
343
LowerLdExternalMoudleVar(GateRef gate)344 void NTypeBytecodeLowering::LowerLdExternalMoudleVar(GateRef gate)
345 {
346 if (!compilationEnv_->IsJitCompiler()) {
347 return;
348 }
349 uint32_t pcOffset = acc_.TryGetPcOffset(gate);
350 uint32_t methodOffset = acc_.TryGetMethodOffset(gate);
351 if (methodOffset == 0) {
352 return;
353 }
354 auto isResolved =
355 static_cast<const JitCompilationEnv *>(compilationEnv_)->IsLdExtModuleVarResolved(methodOffset, pcOffset);
356 if (!isResolved) {
357 return;
358 }
359
360 if (enableLazyDeopt_) {
361 if (!compilationEnv_->GetDependencies()->DependOnNotHotReloadPatchMain(compilationEnv_->GetHostThread())) {
362 return;
363 }
364 } else {
365 GateRef frameState = acc_.FindNearestFrameState(builder_.GetDepend());
366 builder_.DeoptCheck(builder_.IsNotLdEndExecPatchMain(glue_), frameState, DeoptType::HOTRELOAD_PATCHMAIN);
367 }
368
369 GateRef jsFunc = argAcc_->GetFrameArgsIn(gate, FrameArgIdx::FUNC);
370 GateRef index = acc_.GetValueIn(gate, 0);
371 GateRef value = builder_.LdExternalModuleVar(jsFunc, index);
372 acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), value);
373 }
374
LowerStModuleVar(GateRef gate)375 void NTypeBytecodeLowering::LowerStModuleVar(GateRef gate)
376 {
377 AddProfiling(gate);
378 GateRef jsFunc = argAcc_->GetFrameArgsIn(gate, FrameArgIdx::FUNC);
379 GateRef index = acc_.GetValueIn(gate, 0);
380 GateRef value = acc_.GetValueIn(gate, 1);
381 builder_.StoreModuleVar(jsFunc, index, value);
382 ReplaceGateWithPendingException(gate, builder_.GetState(), builder_.GetDepend(), Circuit::NullGate());
383 }
384
LowerNTypedStOwnByName(GateRef gate)385 void NTypeBytecodeLowering::LowerNTypedStOwnByName(GateRef gate)
386 {
387 // 3: number of value inputs
388 ASSERT(acc_.GetNumValueIn(gate) == 3);
389 GateRef receiver = acc_.GetValueIn(gate, 1); // 1: receiver
390 if (acc_.GetOpCode(receiver) != OpCode::TYPED_CREATE_OBJ_WITH_BUFFER) {
391 return;
392 }
393
394 GateRef hclassGate = acc_.GetValueIn(receiver, 2); // 2: hclass offset
395 JSTaggedValue taggedHClass(acc_.GetConstantValue(hclassGate));
396 GateRef stringId = acc_.GetValueIn(gate, 0);
397 JSTaggedValue key = compilationEnv_->GetStringFromConstantPool(acc_.TryGetMethodOffset(gate),
398 acc_.GetConstantValue(stringId));
399 if (key.IsUndefined()) {
400 return;
401 }
402 JSThread *thread = compilationEnv_->GetJSThread();
403 JSHClass *hclass = JSHClass::Cast(taggedHClass.GetTaggedObject());
404 int entry = JSHClass::FindPropertyEntry(thread, hclass, key);
405 if (entry == -1) {
406 return;
407 }
408
409 LayoutInfo *LayoutInfo = LayoutInfo::Cast(hclass->GetLayout(thread).GetTaggedObject());
410 PropertyAttributes attr(LayoutInfo->GetAttr(thread, entry));
411 if (attr.IsAccessor()) {
412 return;
413 }
414 if (!attr.IsInlinedProps()) {
415 return;
416 }
417
418 size_t offset = attr.GetOffset();
419 GateRef accValue = acc_.GetValueIn(gate, 2); // 2: accValue
420 builder_.StoreConstOffset(VariableType::JS_ANY(), receiver, hclass->GetInlinedPropertiesOffset(offset), accValue);
421 acc_.ReplaceHirAndReplaceDeadIfException(gate, builder_.GetStateDepend(), Circuit::NullGate());
422 }
423
ReplaceGateWithPendingException(GateRef gate,GateRef state,GateRef depend,GateRef value)424 void NTypeBytecodeLowering::ReplaceGateWithPendingException(GateRef gate, GateRef state, GateRef depend, GateRef value)
425 {
426 auto glue = glue_;
427 auto condition = builder_.HasPendingException(glue, compilationEnv_);
428 GateRef ifBranch = builder_.Branch(state, condition, 1, BranchWeight::DEOPT_WEIGHT, "checkException");
429 GateRef ifTrue = builder_.IfTrue(ifBranch);
430 GateRef ifFalse = builder_.IfFalse(ifBranch);
431 GateRef eDepend = builder_.DependRelay(ifTrue, depend);
432 GateRef sDepend = builder_.DependRelay(ifFalse, depend);
433
434 StateDepend success(ifFalse, sDepend);
435 StateDepend exception(ifTrue, eDepend);
436 acc_.ReplaceHirWithIfBranch(gate, success, exception, value);
437 }
438 }
439