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