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