• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-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 "compiler/optimizer/optimizations/peepholes.h"
17 #include "compiler/optimizer/ir/analysis.h"
18 #include "compiler/optimizer/ir/runtime_interface.h"
19 #include "compiler/optimizer/optimizations/const_folding.h"
20 
21 namespace ark::compiler {
22 
ReplaceWithCompareEQ(IntrinsicInst * intrinsic)23 static void ReplaceWithCompareEQ(IntrinsicInst *intrinsic)
24 {
25     auto input0 = intrinsic->GetInput(0).GetInst();
26     auto input1 = intrinsic->GetInput(1).GetInst();
27 
28     auto bb = intrinsic->GetBasicBlock();
29     auto graph = bb->GetGraph();
30 
31     auto compare = graph->CreateInst(Opcode::Compare)->CastToCompare();
32     compare->SetCc(ConditionCode::CC_EQ);
33     compare->SetType(intrinsic->GetType());
34     ASSERT(input0->GetType() == input1->GetType());
35     compare->SetOperandsType(input0->GetType());
36 
37     compare->SetInput(0, input0);
38     compare->SetInput(1, input1);
39     bb->InsertAfter(compare, intrinsic);
40     intrinsic->ReplaceUsers(compare);
41 }
42 
PeepholeStringEquals(GraphVisitor * v,IntrinsicInst * intrinsic)43 bool Peepholes::PeepholeStringEquals([[maybe_unused]] GraphVisitor *v, IntrinsicInst *intrinsic)
44 {
45     // Replaces
46     //      Intrinsic.StdCoreStringEquals arg, NullPtr
47     // with
48     //      Compare EQ ref             arg, NullPtr
49 
50     auto input0 = intrinsic->GetInput(0).GetInst();
51     auto input1 = intrinsic->GetInput(1).GetInst();
52     if (input0->IsNullPtr() || input1->IsNullPtr()) {
53         ReplaceWithCompareEQ(intrinsic);
54         return true;
55     }
56 
57     return false;
58 }
59 
GetStringFromLength(Inst * inst)60 Inst *GetStringFromLength(Inst *inst)
61 {
62     Inst *lenArray = inst;
63     if (inst->GetBasicBlock()->GetGraph()->GetRuntime()->IsCompressedStringsEnabled()) {
64         if (inst->GetOpcode() != Opcode::Shr || inst->GetType() != DataType::INT32) {
65             return nullptr;
66         }
67         auto input1 = inst->GetInput(1).GetInst();
68         if (!input1->IsConst() || input1->CastToConstant()->GetRawValue() != 1) {
69             return nullptr;
70         }
71         lenArray = inst->GetInput(0).GetInst();
72     }
73 
74     if (lenArray->GetOpcode() != Opcode::LenArray || !lenArray->CastToLenArray()->IsString()) {
75         return nullptr;
76     }
77     return lenArray->GetDataFlowInput(0);
78 }
79 
PeepholeStringSubstring(GraphVisitor * v,IntrinsicInst * intrinsic)80 bool Peepholes::PeepholeStringSubstring([[maybe_unused]] GraphVisitor *v, IntrinsicInst *intrinsic)
81 {
82     // Replaces
83     //      string
84     //      Intrinsic.StdCoreStringSubstring string, 0, string.Length -> .....
85     // with
86     //      string -> .....
87 
88     auto string = intrinsic->GetDataFlowInput(0);
89     auto begin = intrinsic->GetInput(1).GetInst();
90     auto end = intrinsic->GetInput(2).GetInst();
91     if (!begin->IsConst() || begin->GetType() != DataType::INT64) {
92         return false;
93     }
94     if (static_cast<int64_t>(begin->CastToConstant()->GetRawValue()) > 0) {
95         return false;
96     }
97     if (GetStringFromLength(end) != string) {
98         return false;
99     }
100     intrinsic->ReplaceUsers(string);
101     intrinsic->ClearFlag(inst_flags::NO_DCE);
102 
103     return true;
104 }
105 
106 template <bool IS_STORE>
TryInsertFieldInst(IntrinsicInst * intrinsic,RuntimeInterface::ClassPtr klassPtr,RuntimeInterface::FieldPtr rawField,size_t fieldId)107 bool TryInsertFieldInst(IntrinsicInst *intrinsic, RuntimeInterface::ClassPtr klassPtr,
108                         RuntimeInterface::FieldPtr rawField, size_t fieldId)
109 {
110     auto graph = intrinsic->GetBasicBlock()->GetGraph();
111     auto runtime = graph->GetRuntime();
112     auto field = runtime->ResolveLookUpField(rawField, klassPtr);
113     if (field == nullptr) {
114         return false;
115     }
116     Inst *memObj;
117     auto pc = intrinsic->GetPc();
118     if constexpr (IS_STORE) {
119         auto type = intrinsic->GetInputType(1);
120         auto storeField = graph->CreateInstStoreObject(type, pc);
121         storeField->SetTypeId(fieldId);
122         storeField->SetMethod(intrinsic->GetMethod());
123         storeField->SetObjField(field);
124         if (runtime->IsFieldVolatile(field)) {
125             storeField->SetVolatile(true);
126         }
127         if (type == DataType::REFERENCE) {
128             storeField->SetNeedBarrier(true);
129         }
130         storeField->SetInput(1, intrinsic->GetInput(1).GetInst());
131         memObj = storeField;
132     } else {
133         auto type = intrinsic->GetType();
134         auto loadField = graph->CreateInstLoadObject(type, pc);
135         loadField->SetTypeId(fieldId);
136         loadField->SetMethod(intrinsic->GetMethod());
137         loadField->SetObjField(field);
138         if (runtime->IsFieldVolatile(field)) {
139             loadField->SetVolatile(true);
140         }
141         memObj = loadField;
142         intrinsic->ReplaceUsers(loadField);
143     }
144     memObj->SetInput(0, intrinsic->GetInput(0).GetInst());
145     intrinsic->InsertAfter(memObj);
146 
147     intrinsic->ClearFlag(inst_flags::NO_DCE);
148     return true;
149 }
150 
151 template <bool IS_STORE>
TryInsertCallInst(IntrinsicInst * intrinsic,RuntimeInterface::ClassPtr klassPtr,RuntimeInterface::FieldPtr rawField)152 bool TryInsertCallInst(IntrinsicInst *intrinsic, RuntimeInterface::ClassPtr klassPtr,
153                        RuntimeInterface::FieldPtr rawField)
154 {
155     auto graph = intrinsic->GetBasicBlock()->GetGraph();
156     auto runtime = graph->GetRuntime();
157     auto method = runtime->ResolveLookUpCall(rawField, klassPtr, IS_STORE);
158     if (method == nullptr) {
159         return false;
160     }
161     auto type = IS_STORE ? DataType::VOID : intrinsic->GetType();
162     auto pc = intrinsic->GetPc();
163 
164     auto call = graph->CreateInstCallVirtual(type, pc, runtime->GetMethodId(method));
165     call->SetCallMethod(method);
166     size_t numInputs = IS_STORE ? 3 : 2;
167     call->ReserveInputs(numInputs);
168     call->AllocateInputTypes(graph->GetAllocator(), numInputs);
169     for (size_t i = 0; i < numInputs; ++i) {
170         call->AppendInput(intrinsic->GetInput(i).GetInst(), intrinsic->GetInputType(i));
171     }
172     intrinsic->InsertAfter(call);
173     intrinsic->ReplaceUsers(call);
174     intrinsic->ClearFlag(inst_flags::NO_DCE);
175     return true;
176 }
177 
PeepholeLdObjByName(GraphVisitor * v,IntrinsicInst * intrinsic)178 bool Peepholes::PeepholeLdObjByName([[maybe_unused]] GraphVisitor *v, IntrinsicInst *intrinsic)
179 {
180     auto klassPtr = GetClassPtrForObject(intrinsic);
181     if (klassPtr == nullptr) {
182         return false;
183     }
184     auto graph = intrinsic->GetBasicBlock()->GetGraph();
185     auto method = intrinsic->GetMethod();
186     auto runtime = graph->GetRuntime();
187     auto fieldId = intrinsic->GetImm(0);
188     auto rawField = runtime->ResolveField(method, fieldId, !graph->IsAotMode(), nullptr);
189     ASSERT(rawField != nullptr);
190 
191     if (TryInsertFieldInst<false>(intrinsic, klassPtr, rawField, fieldId)) {
192         return true;
193     }
194     if (TryInsertCallInst<false>(intrinsic, klassPtr, rawField)) {
195         return true;
196     }
197     return false;
198 }
199 
PeepholeStObjByName(GraphVisitor * v,IntrinsicInst * intrinsic)200 bool Peepholes::PeepholeStObjByName([[maybe_unused]] GraphVisitor *v, IntrinsicInst *intrinsic)
201 {
202     auto klassPtr = GetClassPtrForObject(intrinsic);
203     if (klassPtr == nullptr) {
204         return false;
205     }
206     auto graph = intrinsic->GetBasicBlock()->GetGraph();
207     auto method = intrinsic->GetMethod();
208     auto runtime = graph->GetRuntime();
209     auto fieldId = intrinsic->GetImm(0);
210     auto rawField = runtime->ResolveField(method, fieldId, !graph->IsAotMode(), nullptr);
211     ASSERT(rawField != nullptr);
212 
213     if (TryInsertFieldInst<true>(intrinsic, klassPtr, rawField, fieldId)) {
214         return true;
215     }
216     if (TryInsertCallInst<true>(intrinsic, klassPtr, rawField)) {
217         return true;
218     }
219     return false;
220 }
221 
ReplaceWithCompareNullish(IntrinsicInst * intrinsic,Inst * input)222 static void ReplaceWithCompareNullish(IntrinsicInst *intrinsic, Inst *input)
223 {
224     auto bb = intrinsic->GetBasicBlock();
225     auto graph = bb->GetGraph();
226 
227     auto compareNull = graph->CreateInstCompare(DataType::BOOL, intrinsic->GetPc(), input, graph->GetOrCreateNullPtr(),
228                                                 DataType::REFERENCE, ConditionCode::CC_EQ);
229     auto compareUndef =
230         graph->CreateInstCompare(DataType::BOOL, intrinsic->GetPc(), input, graph->GetOrCreateUndefinedInst(),
231                                  DataType::REFERENCE, ConditionCode::CC_EQ);
232 
233     auto orInst = graph->CreateInstOr(DataType::BOOL, intrinsic->GetPc(), compareNull, compareUndef);
234 
235     bb->InsertAfter(compareNull, intrinsic);
236     bb->InsertAfter(compareUndef, compareNull);
237     bb->InsertAfter(orInst, compareUndef);
238 
239     intrinsic->ReplaceUsers(orInst);
240 }
241 
PeepholeEquals(GraphVisitor * v,IntrinsicInst * intrinsic)242 bool Peepholes::PeepholeEquals([[maybe_unused]] GraphVisitor *v, IntrinsicInst *intrinsic)
243 {
244     auto input0 = intrinsic->GetInput(0).GetInst();
245     auto input1 = intrinsic->GetInput(1).GetInst();
246 
247     const auto isNullish = [](Inst *inst) { return inst->IsNullPtr() || inst->IsLoadUndefined(); };
248     if (input0 == input1 || (isNullish(input0) && isNullish(input1))) {
249         intrinsic->ReplaceUsers(ConstFoldingCreateIntConst(intrinsic, 1));
250         return true;
251     }
252     auto graph = intrinsic->GetBasicBlock()->GetGraph();
253     if (graph->IsBytecodeOptimizer()) {
254         return false;
255     }
256     if (isNullish(input0) || isNullish(input1)) {
257         auto nonNullishInput = isNullish(input0) ? input1 : input0;
258         ReplaceWithCompareNullish(intrinsic, nonNullishInput);
259         return true;
260     }
261 
262     auto klass1 = GetClassPtrForObject(intrinsic, 0);
263     auto klass2 = GetClassPtrForObject(intrinsic, 1);
264     if ((klass1 != nullptr && !graph->GetRuntime()->IsClassValueTyped(klass1)) ||
265         (klass2 != nullptr && !graph->GetRuntime()->IsClassValueTyped(klass2))) {
266         ReplaceWithCompareEQ(intrinsic);
267         return true;
268     }
269     // NOTE(vpukhov): #16340 try ObjectTypePropagation
270     return false;
271 }
272 
PeepholeDoubleToString(GraphVisitor * v,IntrinsicInst * intrinsic)273 bool Peepholes::PeepholeDoubleToString([[maybe_unused]] GraphVisitor *v, IntrinsicInst *intrinsic)
274 {
275     ASSERT(intrinsic->GetInputsCount() == 3U);
276     ASSERT(intrinsic->GetInput(2U).GetInst()->IsSaveState());
277     auto graph = intrinsic->GetBasicBlock()->GetGraph();
278     if (graph->IsBytecodeOptimizer() || graph->GetArch() == Arch::AARCH32) {
279         return false;
280     }
281     auto radix = intrinsic->GetInput(1U).GetInst();
282     constexpr auto TEN = 10U;
283     if (!(radix->IsConst() && radix->CastToConstant()->GetIntValue() == TEN)) {
284         return false;
285     }
286     auto pc = intrinsic->GetPc();
287     auto num = intrinsic->GetInput(0).GetInst();
288     auto numInt = graph->CreateInstBitcast(DataType::UINT64, pc, num);
289     Inst *cache = nullptr;
290     void *cachePtr = nullptr;
291     if (!graph->IsAotMode() && (cachePtr = graph->GetRuntime()->GetDoubleToStringCache()) != nullptr) {
292         cache =
293             graph->CreateInstLoadImmediate(DataType::REFERENCE, pc, cachePtr, LoadImmediateInst::ObjectType::OBJECT);
294     } else {
295         auto vm = graph->CreateInstLoadImmediate(DataType::POINTER, pc, Thread::GetVmOffset(),
296                                                  LoadImmediateInst::ObjectType::TLS_OFFSET);
297         intrinsic->InsertBefore(vm);
298         cache = graph->CreateInstLoad(
299             DataType::REFERENCE, pc, vm,
300             graph->FindOrCreateConstant(cross_values::GetEtsVmDoubleToStringCacheOffset(graph->GetArch())));
301         // GraphChecker hack
302         cache->ClearFlag(inst_flags::LOW_LEVEL);
303     }
304     auto newInst = graph->CreateInstIntrinsic(
305         DataType::REFERENCE, pc, RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_ETS_DOUBLE_TO_STRING_DECIMAL);
306     newInst->SetInputs(graph->GetAllocator(), {{cache, DataType::REFERENCE},
307                                                {numInt, DataType::UINT64},
308                                                {graph->FindOrCreateConstant<uint64_t>(0), DataType::UINT64},
309                                                {intrinsic->GetSaveState(), DataType::NO_TYPE}});
310     intrinsic->InsertBefore(cache);
311     intrinsic->InsertBefore(numInt);
312     intrinsic->InsertBefore(newInst);
313     intrinsic->ReplaceUsers(newInst);
314     // remove intrinsic to satisfy SaveState checker
315     intrinsic->GetBasicBlock()->RemoveInst(intrinsic);
316     intrinsic->SetNext(newInst->GetNext());
317     return true;
318 }
319 
320 #ifdef PANDA_ETS_INTEROP_JS
321 
TryFuseGetPropertyAndCast(IntrinsicInst * intrinsic,RuntimeInterface::IntrinsicId newId)322 bool Peepholes::TryFuseGetPropertyAndCast(IntrinsicInst *intrinsic, RuntimeInterface::IntrinsicId newId)
323 {
324     auto input = intrinsic->GetInput(0).GetInst();
325     if (!input->HasSingleUser() || input->GetBasicBlock() != intrinsic->GetBasicBlock()) {
326         return false;
327     }
328     if (!input->IsIntrinsic() || input->CastToIntrinsic()->GetIntrinsicId() !=
329                                      RuntimeInterface::IntrinsicId::INTRINSIC_JS_RUNTIME_GET_PROPERTY_JS_VALUE) {
330         return false;
331     }
332     for (auto inst = input->GetNext(); inst != intrinsic; inst = inst->GetNext()) {
333         ASSERT(inst != nullptr);
334         if (inst->IsNotRemovable()) {
335             return false;
336         }
337     }
338     input->CastToIntrinsic()->SetIntrinsicId(newId);
339     input->SetType(intrinsic->GetType());
340     intrinsic->ReplaceUsers(input);
341     intrinsic->GetBasicBlock()->RemoveInst(intrinsic);
342     intrinsic->SetNext(input->GetNext());  // Fix for InstForwardIterator in Peepholes visitor
343     return true;
344 }
345 
PeepholeJSRuntimeGetValueString(GraphVisitor * v,IntrinsicInst * intrinsic)346 bool Peepholes::PeepholeJSRuntimeGetValueString([[maybe_unused]] GraphVisitor *v, IntrinsicInst *intrinsic)
347 {
348     return static_cast<Peepholes *>(v)->TryFuseGetPropertyAndCast(
349         intrinsic, RuntimeInterface::IntrinsicId::INTRINSIC_JS_RUNTIME_GET_PROPERTY_STRING);
350 }
351 
PeepholeJSRuntimeGetValueDouble(GraphVisitor * v,IntrinsicInst * intrinsic)352 bool Peepholes::PeepholeJSRuntimeGetValueDouble([[maybe_unused]] GraphVisitor *v, IntrinsicInst *intrinsic)
353 {
354     return static_cast<Peepholes *>(v)->TryFuseGetPropertyAndCast(
355         intrinsic, RuntimeInterface::IntrinsicId::INTRINSIC_JS_RUNTIME_GET_PROPERTY_DOUBLE);
356 }
357 
PeepholeJSRuntimeGetValueBoolean(GraphVisitor * v,IntrinsicInst * intrinsic)358 bool Peepholes::PeepholeJSRuntimeGetValueBoolean([[maybe_unused]] GraphVisitor *v, IntrinsicInst *intrinsic)
359 {
360     return static_cast<Peepholes *>(v)->TryFuseGetPropertyAndCast(
361         intrinsic, RuntimeInterface::IntrinsicId::INTRINSIC_JS_RUNTIME_GET_PROPERTY_BOOLEAN);
362 }
363 
TryFuseCastAndSetProperty(IntrinsicInst * intrinsic,RuntimeInterface::IntrinsicId newId)364 bool Peepholes::TryFuseCastAndSetProperty(IntrinsicInst *intrinsic, RuntimeInterface::IntrinsicId newId)
365 {
366     size_t userCount = 0;
367     constexpr size_t STORE_VALUE_IDX = 2;
368     constexpr auto SET_PROP_ID = RuntimeInterface::IntrinsicId::INTRINSIC_JS_RUNTIME_SET_PROPERTY_JS_VALUE;
369     for (auto &user : intrinsic->GetUsers()) {
370         ++userCount;
371         if (user.GetIndex() != STORE_VALUE_IDX || !user.GetInst()->IsIntrinsic() ||
372             user.GetInst()->CastToIntrinsic()->GetIntrinsicId() != SET_PROP_ID) {
373             return false;
374         }
375     }
376     auto userIt = intrinsic->GetUsers().begin();
377     for (size_t userIdx = 0; userIdx < userCount; ++userIdx) {
378         ASSERT(userIt != intrinsic->GetUsers().end());
379         auto *storeInst = userIt->GetInst();
380         auto newValue = intrinsic->GetInput(0).GetInst();
381         storeInst->CastToIntrinsic()->SetIntrinsicId(newId);
382         storeInst->ReplaceInput(intrinsic, newValue);
383         storeInst->CastToIntrinsic()->SetInputType(STORE_VALUE_IDX, newValue->GetType());
384         userIt = intrinsic->GetUsers().begin();
385     }
386     return true;
387 }
388 
PeepholeJSRuntimeNewJSValueString(GraphVisitor * v,IntrinsicInst * intrinsic)389 bool Peepholes::PeepholeJSRuntimeNewJSValueString(GraphVisitor *v, IntrinsicInst *intrinsic)
390 {
391     return static_cast<Peepholes *>(v)->TryFuseCastAndSetProperty(
392         intrinsic, RuntimeInterface::IntrinsicId::INTRINSIC_JS_RUNTIME_SET_PROPERTY_STRING);
393 }
394 
PeepholeJSRuntimeNewJSValueDouble(GraphVisitor * v,IntrinsicInst * intrinsic)395 bool Peepholes::PeepholeJSRuntimeNewJSValueDouble(GraphVisitor *v, IntrinsicInst *intrinsic)
396 {
397     return static_cast<Peepholes *>(v)->TryFuseCastAndSetProperty(
398         intrinsic, RuntimeInterface::IntrinsicId::INTRINSIC_JS_RUNTIME_SET_PROPERTY_DOUBLE);
399 }
400 
PeepholeJSRuntimeNewJSValueBoolean(GraphVisitor * v,IntrinsicInst * intrinsic)401 bool Peepholes::PeepholeJSRuntimeNewJSValueBoolean(GraphVisitor *v, IntrinsicInst *intrinsic)
402 {
403     return static_cast<Peepholes *>(v)->TryFuseCastAndSetProperty(
404         intrinsic, RuntimeInterface::IntrinsicId::INTRINSIC_JS_RUNTIME_SET_PROPERTY_BOOLEAN);
405 }
406 
BuildLoadPropertyChain(IntrinsicInst * intrinsic,uint64_t qnameStart,uint64_t qnameLen)407 static std::pair<Inst *, Inst *> BuildLoadPropertyChain(IntrinsicInst *intrinsic, uint64_t qnameStart,
408                                                         uint64_t qnameLen)
409 {
410     auto *jsThis = intrinsic->GetInput(0).GetInst();
411     auto *jsFn = jsThis;
412     auto saveState = intrinsic->GetSaveState();
413     auto graph = intrinsic->GetBasicBlock()->GetGraph();
414     auto runtime = graph->GetRuntime();
415     auto klass = runtime->GetClass(intrinsic->GetMethod());
416     auto pc = intrinsic->GetPc();
417     auto jsConstPool = graph->CreateInstIntrinsic(
418         DataType::POINTER, pc, RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_LOAD_JS_CONSTANT_POOL);
419     jsConstPool->SetInputs(graph->GetAllocator(), {{saveState, DataType::NO_TYPE}});
420     intrinsic->InsertBefore(jsConstPool);
421     Inst *cpOffsetForClass = intrinsic->GetInput(3U).GetInst();
422     for (uint32_t strIndexInAnnot = qnameStart; strIndexInAnnot < qnameStart + qnameLen; strIndexInAnnot++) {
423         IntrinsicInst *jsProperty = nullptr;
424         auto uniqueStrIndex = graph->GetRuntime()->GetAnnotationElementUniqueIndex(
425             klass, "Lets/annotation/DynamicCall;", strIndexInAnnot);
426         auto strIndexInAnnotConst = graph->FindOrCreateConstant(uniqueStrIndex);
427         auto indexInst = graph->CreateInstAdd(DataType::INT32, pc, cpOffsetForClass, strIndexInAnnotConst);
428         intrinsic->InsertBefore(indexInst);
429         jsProperty = graph->CreateInstIntrinsic(DataType::POINTER, pc,
430                                                 RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_GET_JS_ELEMENT);
431         // ConstantPool elements are immutable
432         jsProperty->ClearFlag(inst_flags::NO_CSE);
433         jsProperty->ClearFlag(inst_flags::NO_DCE);
434 
435         jsProperty->SetInputs(
436             graph->GetAllocator(),
437             {{jsConstPool, DataType::POINTER}, {indexInst, DataType::INT32}, {saveState, DataType::NO_TYPE}});
438 
439         auto getProperty = graph->CreateInstIntrinsic(
440             DataType::POINTER, pc, RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_GET_JS_PROPERTY);
441         getProperty->SetInputs(
442             graph->GetAllocator(),
443             {{jsFn, DataType::POINTER}, {jsProperty, DataType::POINTER}, {saveState, DataType::NO_TYPE}});
444         intrinsic->InsertBefore(jsProperty);
445         intrinsic->InsertBefore(getProperty);
446         jsThis = jsFn;
447         jsFn = getProperty;
448     }
449     return {jsThis, jsFn};
450 }
451 
PeepholeResolveQualifiedJSCall(GraphVisitor * v,IntrinsicInst * intrinsic)452 bool Peepholes::PeepholeResolveQualifiedJSCall([[maybe_unused]] GraphVisitor *v, IntrinsicInst *intrinsic)
453 {
454     if (!intrinsic->HasUsers()) {
455         return false;
456     }
457     auto qnameStartInst = intrinsic->GetInput(1).GetInst();
458     auto qnameLenInst = intrinsic->GetInput(2U).GetInst();
459     if (!qnameStartInst->IsConst() || !qnameLenInst->IsConst()) {
460         // qnameStart and qnameLen are always constant, but may be e.g. Phi instructions after BCO
461         return false;
462     }
463     auto qnameStart = qnameStartInst->CastToConstant()->GetIntValue();
464     auto qnameLen = qnameLenInst->CastToConstant()->GetIntValue();
465     ASSERT(qnameLen > 0);
466     auto [jsThis, jsFn] = BuildLoadPropertyChain(intrinsic, qnameStart, qnameLen);
467     constexpr auto FN_PSEUDO_USER = RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_LOAD_RESOLVED_JS_FUNCTION;
468     for (auto &user : intrinsic->GetUsers()) {
469         auto userInst = user.GetInst();
470         if (userInst->IsIntrinsic() && userInst->CastToIntrinsic()->GetIntrinsicId() == FN_PSEUDO_USER) {
471             userInst->ReplaceUsers(jsFn);
472         }
473     }
474     intrinsic->ReplaceUsers(jsThis);
475     intrinsic->ClearFlag(inst_flags::NO_DCE);
476     return true;
477 }
478 
479 #endif
480 
481 }  // namespace ark::compiler
482