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