1 /*
2 * Copyright (c) 2023 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_hcr_lowering.h"
17 #include "ecmascript/compiler/circuit_builder-inl.h"
18 #include "ecmascript/dfx/vmstat/opt_code_profiler.h"
19
20 namespace panda::ecmascript::kungfu {
RunNTypeHCRLowering()21 void NTypeHCRLowering::RunNTypeHCRLowering()
22 {
23 std::vector<GateRef> gateList;
24 circuit_->GetAllGates(gateList);
25 for (const auto &gate : gateList) {
26 auto op = acc_.GetOpCode(gate);
27 if (op == OpCode::JS_BYTECODE) {
28 Lower(gate);
29 }
30 }
31
32 if (IsLogEnabled()) {
33 LOG_COMPILER(INFO) << "";
34 LOG_COMPILER(INFO) << "\033[34m"
35 << "===================="
36 << " After NTypeHCRlowering "
37 << "[" << GetMethodName() << "]"
38 << "===================="
39 << "\033[0m";
40 circuit_->PrintAllGatesWithBytecode();
41 LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m";
42 }
43 }
44
Lower(GateRef gate)45 void NTypeHCRLowering::Lower(GateRef gate)
46 {
47 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
48 // initialize label manager
49 Environment env(gate, circuit_, &builder_);
50 switch (ecmaOpcode) {
51 case EcmaOpcode::CREATEEMPTYARRAY_IMM8:
52 case EcmaOpcode::CREATEEMPTYARRAY_IMM16:
53 LowerNTypedCreateEmptyArray(gate);
54 break;
55 case EcmaOpcode::STOWNBYINDEX_IMM8_V8_IMM16:
56 case EcmaOpcode::STOWNBYINDEX_IMM16_V8_IMM16:
57 case EcmaOpcode::WIDE_STOWNBYINDEX_PREF_V8_IMM32:
58 LowerNTypedStownByIndex(gate);
59 break;
60 case EcmaOpcode::STOWNBYNAME_IMM8_ID16_V8:
61 case EcmaOpcode::STOWNBYNAME_IMM16_ID16_V8:
62 LowerNTypedStOwnByName(gate);
63 break;
64 case EcmaOpcode::THROW_UNDEFINEDIFHOLEWITHNAME_PREF_ID16:
65 LowerThrowUndefinedIfHoleWithName(gate);
66 break;
67 case EcmaOpcode::LDLEXVAR_IMM4_IMM4:
68 case EcmaOpcode::LDLEXVAR_IMM8_IMM8:
69 case EcmaOpcode::WIDE_LDLEXVAR_PREF_IMM16_IMM16:
70 LowerLdLexVar(gate);
71 break;
72 case EcmaOpcode::STLEXVAR_IMM4_IMM4:
73 case EcmaOpcode::STLEXVAR_IMM8_IMM8:
74 case EcmaOpcode::WIDE_STLEXVAR_PREF_IMM16_IMM16:
75 LowerStLexVar(gate);
76 break;
77 default:
78 break;
79 }
80 }
81
LowerThrowUndefinedIfHoleWithName(GateRef gate)82 void NTypeHCRLowering::LowerThrowUndefinedIfHoleWithName(GateRef gate)
83 {
84 GateRef value = acc_.GetValueIn(gate, 1); // 1: the second parameter
85 builder_.LexVarIsHoleCheck(value);
86 acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), Circuit::NullGate());
87 }
88
LowerLdLexVar(GateRef gate)89 void NTypeHCRLowering::LowerLdLexVar(GateRef gate)
90 {
91 AddProfiling(gate);
92 GateRef level = acc_.GetValueIn(gate, 0); // 0: first parameter
93 GateRef index = acc_.GetValueIn(gate, 1); // 1: the second parameter
94 GateRef currentEnv = acc_.GetValueIn(gate, 2); // 2: the third parameter
95
96 uint32_t levelValue = static_cast<uint32_t>(acc_.GetConstantValue(level));
97 uint32_t indexValue = static_cast<uint32_t>(acc_.GetConstantValue(index));
98 indexValue += LexicalEnv::RESERVED_ENV_LENGTH;
99 GateRef result = Circuit::NullGate();
100 if (levelValue == 0) {
101 result = builder_.LoadFromTaggedArray(currentEnv, indexValue);
102 } else if (levelValue == 1) { // 1: level 1
103 auto parentEnv = builder_.LoadFromTaggedArray(currentEnv, LexicalEnv::PARENT_ENV_INDEX);
104 result = builder_.LoadFromTaggedArray(parentEnv, indexValue);
105 } else {
106 // level > 1, go slowpath
107 return;
108 }
109 acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result);
110 }
111
LowerStLexVar(GateRef gate)112 void NTypeHCRLowering::LowerStLexVar(GateRef gate)
113 {
114 AddProfiling(gate);
115 GateRef level = acc_.GetValueIn(gate, 0); // 0: first parameter
116 GateRef index = acc_.GetValueIn(gate, 1); // 1: the second parameter
117 GateRef currentEnv = acc_.GetValueIn(gate, 2); // 2: the third parameter
118 GateRef value = acc_.GetValueIn(gate, 3); // 3: the fourth parameter
119
120 uint32_t levelValue = static_cast<uint32_t>(acc_.GetConstantValue(level));
121 uint32_t indexValue = static_cast<uint32_t>(acc_.GetConstantValue(index));
122 indexValue += LexicalEnv::RESERVED_ENV_LENGTH;
123 GateRef result = Circuit::NullGate();
124 if (levelValue == 0) {
125 result = builder_.StoreToTaggedArray(currentEnv, indexValue, value);
126 } else if (levelValue == 1) { // 1: level 1
127 auto parentEnv = builder_.LoadFromTaggedArray(currentEnv, LexicalEnv::PARENT_ENV_INDEX);
128 result = builder_.StoreToTaggedArray(parentEnv, indexValue, value);
129 } else {
130 // level > 1, go slowpath
131 return;
132 }
133 acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result);
134 }
135
LowerNTypedCreateEmptyArray(GateRef gate)136 void NTypeHCRLowering::LowerNTypedCreateEmptyArray(GateRef gate)
137 {
138 // in the future, the type of the elements in the array will be obtained through pgo,
139 // and the type will be used to determine whether to create a typed-array.
140 AddProfiling(gate);
141 GateRef array = builder_.CreateArray(0);
142 acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), array);
143 }
144
LowerNTypedCreateArrayWithBuffer(GateRef gate)145 void NTypeHCRLowering::LowerNTypedCreateArrayWithBuffer(GateRef gate)
146 {
147 // 1: number of value inputs
148 ASSERT(acc_.GetNumValueIn(gate) == 1);
149 GateRef index = acc_.GetValueIn(gate, 0);
150 auto thread = tsManager_->GetEcmaVM()->GetJSThread();
151 uint32_t cpIdx = static_cast<uint32_t>(acc_.GetConstantValue(index));
152 JSHandle<ConstantPool> constpoolHandle(tsManager_->GetConstantPool());
153 JSTaggedValue arr = ConstantPool::GetLiteralFromCache<ConstPoolType::ARRAY_LITERAL>(
154 thread, constpoolHandle.GetTaggedValue(), cpIdx, recordName_);
155 JSHandle<JSArray> arrayHandle(thread, arr);
156 JSHandle<JSHClass> oldHClass(thread, arrayHandle->GetClass());
157 JSHandle<JSHClass> hclass = JSHClass::Clone(thread, oldHClass);
158
159 GateType gateType = acc_.GetGateType(gate);
160 panda_file::File::EntityId id = ConstantPool::GetIdFromCache(constpoolHandle.GetTaggedValue(), cpIdx);
161 JSHandle<TaggedArray> elements(thread, arrayHandle->GetElements());
162 tsManager_->AddArrayTSHClass(id, hclass);
163 tsManager_->AddArrayTSElements(id, elements);
164 gateType = tsManager_->TryNarrowUnionType(gateType);
165
166 int hclassIndex = -1;
167 int elementIndex = -1;
168 if (tsManager_->IsArrayTypeKind(gateType)) {
169 hclassIndex = tsManager_->GetHClassIndexByArrayType(gateType, id);
170 elementIndex = tsManager_->GetElementsIndexByArrayType(gateType, id);
171 }
172 if (hclassIndex == -1 || elementIndex == -1) { // slowpath
173 return;
174 }
175
176 AddProfiling(gate);
177 GateRef elementIndexGate = builder_.IntPtr(elementIndex);
178 GateRef array = builder_.CreateArrayWithBuffer(0, index, elementIndexGate);
179 acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), array);
180 }
181
LowerNTypedStownByIndex(GateRef gate)182 void NTypeHCRLowering::LowerNTypedStownByIndex(GateRef gate)
183 {
184 // 3: number of value inputs
185 ASSERT(acc_.GetNumValueIn(gate) == 3);
186 GateRef receiver = acc_.GetValueIn(gate, 0);
187 GateRef index = acc_.GetValueIn(gate, 1);
188 GateRef value = acc_.GetValueIn(gate, 2);
189 if (acc_.GetOpCode(receiver) != OpCode::CREATE_ARRAY &&
190 acc_.GetOpCode(receiver) != OpCode::CREATE_ARRAY_WITH_BUFFER) {
191 return;
192 }
193
194 AddProfiling(gate);
195 uint32_t indexValue = static_cast<uint32_t>(acc_.GetConstantValue(index));
196 uint32_t arraySize = acc_.GetArraySize(receiver);
197 acc_.SetArraySize(receiver, std::max(arraySize, indexValue + 1));
198 index = builder_.Int32(indexValue);
199 builder_.StoreElement<TypedStoreOp::ARRAY_STORE_ELEMENT>(receiver, index, value);
200 acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), Circuit::NullGate());
201 }
202
LowerNTypedStOwnByName(GateRef gate)203 void NTypeHCRLowering::LowerNTypedStOwnByName(GateRef gate)
204 {
205 // 3: number of value inputs
206 ASSERT(acc_.GetNumValueIn(gate) == 3);
207 auto constData = acc_.GetValueIn(gate, 0);
208 uint16_t propIndex = acc_.GetConstantValue(constData);
209 auto thread = tsManager_->GetEcmaVM()->GetJSThread();
210 auto propKey = tsManager_->GetStringFromConstantPool(propIndex);
211
212 GateRef receiver = acc_.GetValueIn(gate, 1);
213 GateRef value = acc_.GetValueIn(gate, 2);
214
215 GateType receiverType = acc_.GetGateType(receiver);
216 receiverType = tsManager_->TryNarrowUnionType(receiverType);
217
218 int hclassIndex = -1;
219 if (tsManager_->IsObjectTypeKind(receiverType)) {
220 hclassIndex = tsManager_->GetHClassIndexByObjectType(receiverType);
221 }
222 if (hclassIndex == -1) { // slowpath
223 return;
224 }
225 JSHClass *hclass = JSHClass::Cast(tsManager_->GetValueFromCache(hclassIndex).GetTaggedObject());
226
227 PropertyLookupResult plr = JSHClass::LookupPropertyInAotHClass(thread, hclass, propKey);
228 if (!plr.IsFound() || !plr.IsLocal() || plr.IsAccessor() || !plr.IsWritable()) { // slowpath
229 return;
230 }
231 AddProfiling(gate);
232
233 GateRef pfrGate = builder_.Int32(plr.GetData());
234 builder_.StoreProperty(receiver, pfrGate, value);
235
236 acc_.ReplaceHirAndDeleteIfException(gate, builder_.GetStateDepend(), Circuit::NullGate());
237 }
238
AddProfiling(GateRef gate)239 void NTypeHCRLowering::AddProfiling(GateRef gate)
240 {
241 if (IsTraceBC()) {
242 // see stateSplit as a part of JSByteCode if exists
243 GateRef maybeStateSplit = acc_.GetDep(gate);
244 GateRef current = Circuit::NullGate();
245 if (acc_.GetOpCode(maybeStateSplit) == OpCode::STATE_SPLIT) {
246 current = maybeStateSplit;
247 } else {
248 current = gate;
249 }
250
251 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
252 auto ecmaOpcodeGate = builder_.Int32(static_cast<uint32_t>(ecmaOpcode));
253 GateRef constOpcode = builder_.Int32ToTaggedInt(ecmaOpcodeGate);
254 GateRef typedPath = builder_.Int32ToTaggedInt(builder_.Int32(1));
255 GateRef traceGate = builder_.CallRuntime(glue_, RTSTUB_ID(DebugAOTPrint), acc_.GetDep(current),
256 { constOpcode, typedPath }, gate);
257 acc_.SetDep(current, traceGate);
258 builder_.SetDepend(acc_.GetDep(gate)); // set gate depend: trace or STATE_SPLIT
259 }
260
261 if (IsProfiling()) {
262 // see stateSplit as a part of JSByteCode if exists
263 GateRef maybeStateSplit = acc_.GetDep(gate);
264 GateRef current = Circuit::NullGate();
265 if (acc_.GetOpCode(maybeStateSplit) == OpCode::STATE_SPLIT) {
266 current = maybeStateSplit;
267 } else {
268 current = gate;
269 }
270
271 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
272 auto ecmaOpcodeGate = builder_.Int32(static_cast<uint32_t>(ecmaOpcode));
273 GateRef constOpcode = builder_.Int32ToTaggedInt(ecmaOpcodeGate);
274 GateRef mode =
275 builder_.Int32ToTaggedInt(builder_.Int32(static_cast<int32_t>(OptCodeProfiler::Mode::TYPED_PATH)));
276 GateRef profiling = builder_.CallRuntime(glue_, RTSTUB_ID(ProfileOptimizedCode), acc_.GetDep(current),
277 { constOpcode, mode }, gate);
278 acc_.SetDep(current, profiling);
279 builder_.SetDepend(acc_.GetDep(gate)); // set gate depend: profiling or STATE_SPLIT
280 }
281 }
282 }
283