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