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