• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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