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