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