• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-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_logger.h"
17 #include "optimizer/analysis/alias_analysis.h"
18 #include "optimizer/analysis/dominators_tree.h"
19 #include "optimizer/analysis/countable_loop_parser.h"
20 #include "optimizer/optimizations/loop_idioms.h"
21 
22 // CC-OFFNXT(G.PRE.02) necessary macro
23 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
24 #define LOG_ARRAYMOVE(level) COMPILER_LOG(level, LOOP_TRANSFORM) << "[Arraymove] "
25 
26 namespace ark::compiler {
RunImpl()27 bool LoopIdioms::RunImpl()
28 {
29     if (GetGraph()->GetArch() == Arch::AARCH32) {
30         // Supported idioms and intrinsics emitted for them could not be encoded on Arm32.
31         return false;
32     }
33     GetGraph()->RunPass<LoopAnalyzer>();
34     RunLoopsVisitor();
35     return isApplied_;
36 }
37 
InvalidateAnalyses()38 void LoopIdioms::InvalidateAnalyses()
39 {
40     GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
41     InvalidateBlocksOrderAnalyzes(GetGraph());
42 }
43 
TransformLoop(Loop * loop)44 bool LoopIdioms::TransformLoop(Loop *loop)
45 {
46     if (loop->GetInnerLoops().empty()) {
47         if (TryTransformArrayInitIdiom(loop) || TryTransformArrayMoveIdiom(loop)) {
48             isApplied_ = true;
49         }
50     }
51     return false;
52 }
53 
FindStoreForArrayInit(BasicBlock * block)54 StoreInst *FindStoreForArrayInit(BasicBlock *block)
55 {
56     StoreInst *store {nullptr};
57     for (auto inst : block->Insts()) {
58         if (inst->GetOpcode() != Opcode::StoreArray) {
59             continue;
60         }
61         if (store != nullptr) {
62             return nullptr;
63         }
64         store = inst->CastToStoreArray();
65     }
66     // should be a loop invariant
67     if (store != nullptr && store->GetStoredValue()->GetBasicBlock()->GetLoop() == block->GetLoop()) {
68         return nullptr;
69     }
70     return store;
71 }
72 
ExtractArrayInitInitialIndexValue(PhiInst * index)73 Inst *ExtractArrayInitInitialIndexValue(PhiInst *index)
74 {
75     auto block = index->GetBasicBlock();
76     BasicBlock *pred = block->GetPredsBlocks().front();
77     if (pred == block) {
78         pred = block->GetPredsBlocks().back();
79     }
80     return index->GetPhiInput(pred);
81 }
82 
AllUsesWithinLoop(Inst * inst,const Loop * loop)83 bool AllUsesWithinLoop(Inst *inst, const Loop *loop)
84 {
85     for (auto &user : inst->GetUsers()) {
86         if (user.GetInst()->GetBasicBlock()->GetLoop() != loop) {
87             return false;
88         }
89     }
90     return true;
91 }
92 
CanReplaceLoop(Loop * loop,Marker marker)93 bool CanReplaceLoop(Loop *loop, Marker marker)
94 {
95     ASSERT(loop->GetBlocks().size() == 1);
96     auto block = loop->GetHeader();
97     for (auto inst : block->AllInsts()) {
98         if (inst->IsMarked(marker)) {
99             continue;
100         }
101         auto opcode = inst->GetOpcode();
102         if (opcode != Opcode::NOP && opcode != Opcode::SaveState && opcode != Opcode::SafePoint) {
103             return false;
104         }
105     }
106     return true;
107 }
108 
IsLoopContainsArrayInitIdiom(StoreInst * store,Loop * loop,CountableLoopInfo & loopInfo)109 bool IsLoopContainsArrayInitIdiom(StoreInst *store, Loop *loop, CountableLoopInfo &loopInfo)
110 {
111     auto storeIdx = store->GetIndex();
112 
113     return loopInfo.constStep == 1UL && loopInfo.index == storeIdx && loopInfo.normalizedCc == ConditionCode::CC_LT &&
114            AllUsesWithinLoop(storeIdx, loop) && AllUsesWithinLoop(loopInfo.update, loop) &&
115            AllUsesWithinLoop(loopInfo.ifImm->GetInput(0).GetInst(), loop);
116 }
117 
IsLoopContainsArrayMoveIdiom(StoreInst * store,Loop * loop,CountableLoopInfo & loopInfo)118 bool IsLoopContainsArrayMoveIdiom(StoreInst *store, Loop *loop, CountableLoopInfo &loopInfo)
119 {
120     auto storeIdx = store->GetIndex();
121 
122     return loopInfo.constStep == 1UL && loopInfo.index == storeIdx && loopInfo.normalizedCc == ConditionCode::CC_LT &&
123            AllUsesWithinLoop(storeIdx, loop) && AllUsesWithinLoop(loopInfo.update, loop) &&
124            AllUsesWithinLoop(loopInfo.ifImm->GetInput(0).GetInst(), loop);
125 }
126 
127 // NOLINTNEXTLINE(fuchsia-multiple-inheritance)
128 class ArrayMoveParser : private CountableLoopParser, private MarkerHolder {
129 public:
ArrayMoveParser(Loop * loop)130     explicit ArrayMoveParser(Loop *loop) : CountableLoopParser(*loop), MarkerHolder(loop->GetHeader()->GetGraph()) {}
131 
Parse()132     bool Parse()
133     {
134         // Check loop shape:
135         if (!CountableLoopParser::Parse() || loopInfo_.constStep != 1) {
136             LOG_ARRAYMOVE(DEBUG) << "Loop shape doesn't match";
137             return false;
138         }
139 
140         if (!FindStoreLoad() || !CheckOffsets() || !CheckLoopIsolation()) {
141             LOG_ARRAYMOVE(DEBUG) << "Dataflow pattern doesn't match";
142             return false;
143         }
144 
145         return true;
146     }
147 
FindStoreLoad()148     bool FindStoreLoad()
149     {
150         for (auto inst : loop_.GetHeader()->Insts()) {
151             if (inst->GetOpcode() != Opcode::StoreArray) {
152                 continue;
153             }
154             auto store = inst->CastToStoreArray();
155             auto storedValue = store->GetStoredValue();
156             if (storedValue->GetOpcode() != Opcode::LoadArray || storedValue->GetBasicBlock() != loop_.GetHeader()) {
157                 return false;
158             }
159             store_.inst = store;
160             load_.inst = storedValue->CastToLoadArray();
161             return DataType::GetTypeSize(store_.inst->GetType(), GetGraph()->GetArch()) ==
162                    DataType::GetTypeSize(load_.inst->GetType(), GetGraph()->GetArch());
163         }
164         return false;
165     }
166 
167     // Check and extract `Add/Sub(loopIndex, loopInvariant)` combination:
168     template <typename T>
ExtractInductionVariableOffset(T * desc)169     bool ExtractInductionVariableOffset(T *desc)
170     {
171         auto inductionVar = desc->inst->GetIndex();
172         if (inductionVar == loopInfo_.index) {
173             desc->idxOffset = inductionVar->GetBasicBlock()->GetGraph()->FindOrCreateConstant(0);
174             return true;
175         }
176 
177         auto chkInputs = [inductionVar, this](size_t i0, size_t i1) {
178             return (inductionVar->GetInput(i0).GetInst() == loopInfo_.index) &&
179                    (inductionVar->GetInput(i1).GetInst()->GetBasicBlock()->GetLoop() != &loop_);
180         };
181 
182         if (inductionVar->GetOpcode() == Opcode::Add) {
183             for (size_t i = 0; i < 2U; i++) {
184                 if (chkInputs(i, 1 - i)) {
185                     desc->idxOffset = inductionVar->GetInput(1 - i).GetInst();
186                     return true;
187                 }
188             }
189         }
190 
191         if (inductionVar->GetOpcode() == Opcode::Sub) {
192             if (chkInputs(0, 1)) {
193                 desc->idxOffset = inductionVar->GetInput(1).GetInst();
194                 desc->idxOffsetNegate = true;
195                 return true;
196             }
197         }
198         return false;
199     }
200 
CheckOffsets()201     bool CheckOffsets()
202     {
203         if (!ExtractInductionVariableOffset(&store_)) {
204             return false;
205         }
206         if (!ExtractInductionVariableOffset(&load_)) {
207             return false;
208         }
209         ASSERT(store_.idxOffset->GetBasicBlock()->GetLoop() != &loop_);
210         ASSERT(load_.idxOffset->GetBasicBlock()->GetLoop() != &loop_);
211         return true;
212     }
213 
CheckLoopIsolation()214     bool CheckLoopIsolation()
215     {
216         auto compare = loopInfo_.ifImm->GetInput(0).GetInst();
217         ASSERT(compare->GetOpcode() == Opcode::Compare);
218         std::array<Inst *, 8U> matched = {
219             loopInfo_.ifImm,        compare,     loopInfo_.update,       loopInfo_.index, load_.inst,
220             load_.inst->GetIndex(), store_.inst, store_.inst->GetIndex()};
221 
222         for (Inst *inst : matched) {
223             ASSERT(inst->GetBasicBlock()->GetLoop() == &loop_);
224             if (!AllUsesWithinLoop(inst, &loop_)) {
225                 LOG_ARRAYMOVE(DEBUG) << "Inst '" << inst->GetId() << ".' used outside";
226                 return false;
227             }
228             inst->SetMarker(GetMarker());
229         }
230 
231         for (auto inst : loop_.GetHeader()->AllInsts()) {
232             if (inst->IsMarked(GetMarker())) {
233                 continue;
234             }
235             if ((inst->IsCall() && static_cast<CallInst *>(inst)->IsInlined()) ||
236                 inst->GetOpcode() == Opcode::ReturnInlined) {
237                 continue;
238             }
239             auto opcode = inst->GetOpcode();
240             if (opcode == Opcode::NOP || opcode == Opcode::SaveState || opcode == Opcode::SafePoint) {
241                 continue;
242             }
243             LOG_ARRAYMOVE(DEBUG) << "Inst '" << inst->GetId() << ".' breaks pattern";
244             return false;
245         }
246         return true;
247     }
248 
249     template <RuntimeInterface::IntrinsicId ID = RuntimeInterface::IntrinsicId::INVALID>
CreateIntrinsic()250     IntrinsicInst *CreateIntrinsic()
251     {
252         if constexpr (ID == RuntimeInterface::IntrinsicId::LIB_CALL_MEM_COPY) {
253             return GetGraph()->CreateInstIntrinsic(DataType::VOID, store_.inst->GetPc(), ID);
254         } else {
255             auto id = RuntimeInterface::IntrinsicId::INVALID;
256             switch (store_.inst->GetType()) {
257                 case DataType::BOOL:
258                 case DataType::INT8:
259                 case DataType::UINT8:
260                     id = RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_MEMMOVE_UNCHECKED_1_BYTE;
261                     break;
262                 case DataType::INT16:
263                 case DataType::UINT16:
264                     id = RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_MEMMOVE_UNCHECKED_2_BYTE;
265                     break;
266                 case DataType::INT32:
267                 case DataType::UINT32:
268                 case DataType::FLOAT32:
269                     id = RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_MEMMOVE_UNCHECKED_4_BYTE;
270                     break;
271                 case DataType::INT64:
272                 case DataType::UINT64:
273                 case DataType::FLOAT64:
274                     id = RuntimeInterface::IntrinsicId::INTRINSIC_COMPILER_MEMMOVE_UNCHECKED_8_BYTE;
275                     break;
276                 default:
277                     LOG_ARRAYMOVE(DEBUG) << "Store has unsupported type " << store_.inst->GetType();
278                     return nullptr;
279             }
280             return GetGraph()->CreateInstIntrinsic(DataType::VOID, store_.inst->GetPc(), id);
281         }
282         UNREACHABLE();
283     }
284 
TryReplaceLoop()285     bool TryReplaceLoop()
286     {
287         auto aliasType = CheckRefAlias();
288         if (aliasType == NO_ALIAS) {
289             return ReplaceLoopCompletely();
290         }
291         LOG_ARRAYMOVE(DEBUG) << "Overlapping regions are not supported";
292         return false;
293     }
294 
CheckRefAlias()295     AliasType CheckRefAlias()
296     {
297         auto src = load_.inst->GetDataFlowInput(0);
298         auto dst = store_.inst->GetDataFlowInput(0);
299         auto &aa = GetGraph()->GetValidAnalysis<AliasAnalysis>();
300         auto aliasType = aa.CheckRefAlias(src, dst);
301         if ((aliasType == MAY_ALIAS) && (dst->GetOpcode() == Opcode::NewArray) &&
302             (src->GetOpcode() == Opcode::Parameter)) {
303             aliasType = NO_ALIAS;
304         }
305         return aliasType;
306     }
307 
308     template <typename T>
NegateIfNeeded(T * desc,BasicBlock * b)309     void NegateIfNeeded(T *desc, BasicBlock *b)
310     {
311         if (desc->idxOffsetNegate) {
312             auto offset = desc->idxOffset;
313             auto neg = GetGraph()->CreateInstNeg(offset->GetType(), offset->GetPc(), offset);
314             ASSERT(b != nullptr);
315             b->AppendInst(neg);
316             desc->idxOffset = neg;
317         }
318     }
319 
PatchRange(Inst ** srcStart,Inst ** srcEnd,BasicBlock * newB)320     void PatchRange(Inst **srcStart, Inst **srcEnd, BasicBlock *newB)
321     {
322         auto one = GetGraph()->FindOrCreateConstant(1);
323         switch (loopInfo_.normalizedCc) {
324             case CC_GE: {
325                 // Transform interval [e,s] to [e, s + 1)
326                 ASSERT(!loopInfo_.isInc);
327                 std::swap(*srcStart, *srcEnd);
328                 auto add = GetGraph()->CreateInstAdd((*srcEnd)->GetType(), (*srcEnd)->GetPc(), (*srcEnd), one);
329                 newB->AppendInst(add);
330                 *srcEnd = add;
331                 break;
332             }
333             case CC_GT: {
334                 //  Transform interval (e,s] to [e + 1, s + 1)
335                 ASSERT(!loopInfo_.isInc);
336                 std::swap(*srcStart, *srcEnd);
337                 auto addStart =
338                     GetGraph()->CreateInstAdd((*srcStart)->GetType(), (*srcStart)->GetPc(), (*srcStart), one);
339                 auto addEnd = GetGraph()->CreateInstAdd((*srcEnd)->GetType(), (*srcEnd)->GetPc(), (*srcEnd), one);
340                 newB->AppendInst(addStart);
341                 newB->AppendInst(addEnd);
342                 *srcStart = addStart;
343                 *srcEnd = addEnd;
344                 break;
345             }
346             case CC_LE: {
347                 //  Transform interval [s,e] to [s, e + 1)
348                 ASSERT(loopInfo_.isInc);
349                 auto add = GetGraph()->CreateInstAdd((*srcEnd)->GetType(), (*srcEnd)->GetPc(), (*srcEnd), one);
350                 newB->AppendInst(add);
351                 *srcEnd = add;
352                 break;
353             }
354             case CC_LT: {
355                 // [s,e) => s' = s, e' = e;
356                 ASSERT(loopInfo_.isInc);
357                 break;
358             }
359             default:
360                 UNREACHABLE();
361         }
362     }
363 
ReplaceLoopCompletely()364     bool ReplaceLoopCompletely()
365     {
366         auto mmove = CreateIntrinsic();
367         if (mmove == nullptr) {
368             return false;
369         }
370         auto newB = GetGraph()->CreateEmptyBlock(store_.inst->GetPc());
371         // 1. Normalize 'load_index/store_index == Sub(idx, c)' -> 'load_index/store_index == Add(idx, Neg(c))':
372         NegateIfNeeded(&load_, newB);
373         NegateIfNeeded(&store_, newB);
374         // 2. Calculate start/end value of load_index:
375         //    i.e. define [s,e) or [s,e] or [e,s] or (e,s]
376         Inst *srcStart =
377             GetGraph()->CreateInstAdd(DataType::INT32, load_.inst->GetPc(), loopInfo_.init, load_.idxOffset);
378         newB->AppendInst(srcStart);
379         Inst *srcEnd = GetGraph()->CreateInstAdd(DataType::INT32, load_.inst->GetPc(), loopInfo_.test, load_.idxOffset);
380         newB->AppendInst(srcEnd);
381         // 3. Handle cases of 'srcStart > srcEnd' and patch range enclosing.
382         //    i.e. normalize [s,e), [s,e], [e,s], (e,s] -> [s',e').
383         PatchRange(&srcStart, &srcEnd, newB);
384 
385         // 4. Calculate 'dstStart' based on 'srcStart' and idxOffsets' diff:
386         auto offsDiff =
387             GetGraph()->CreateInstSub(DataType::INT32, store_.inst->GetPc(), store_.idxOffset, load_.idxOffset);
388         newB->AppendInst(offsDiff);
389         auto dstStart = GetGraph()->CreateInstAdd(DataType::INT32, store_.inst->GetPc(), srcStart, offsDiff);
390         newB->AppendInst(dstStart);
391 
392         newB->AppendInst(mmove);
393         mmove->ClearFlag(inst_flags::Flags::REQUIRE_STATE);
394         mmove->ClearFlag(inst_flags::Flags::RUNTIME_CALL);
395         mmove->ClearFlag(inst_flags::Flags::CAN_THROW);
396         mmove->SetInputs(GetGraph()->GetAllocator(), {{load_.inst->GetArray(), DataType::REFERENCE},
397                                                       {store_.inst->GetArray(), DataType::REFERENCE},
398                                                       {dstStart, dstStart->GetType()},
399                                                       {srcStart, srcStart->GetType()},
400                                                       {srcEnd, srcEnd->GetType()}});
401         auto header = loop_.GetHeader();
402         auto preheader = loop_.GetPreHeader();
403         auto outside = header->GetSuccessor(0) != header ? header->GetSuccessor(0) : header->GetSuccessor(1);
404         preheader->SetNextLoop(nullptr);
405         preheader->ReplaceSucc(header, newB);
406         outside->ReplacePred(header, newB);
407         header->Clear();
408         GetGraph()->EraseBlock(header);
409         LOG_ARRAYMOVE(DEBUG) << "Replaced loop " << loop_.GetId();
410 
411         return true;
412     }
413 
GetGraph()414     Graph *GetGraph()
415     {
416         return loop_.GetHeader()->GetGraph();
417     }
418 
419 private:
420     template <typename T>
421     struct ArrayAccessDescr {
422         T *inst {};
423         Inst *idxOffset {};
424         bool idxOffsetNegate {false};
425     };
426 
427     ArrayAccessDescr<LoadInst> load_;
428     ArrayAccessDescr<StoreInst> store_;
429 };
430 
TryTransformArrayMoveIdiom(Loop * loop)431 bool LoopIdioms::TryTransformArrayMoveIdiom(Loop *loop)
432 {
433     ASSERT(loop->GetInnerLoops().empty());
434     if (loop->GetBlocks().size() != 1) {
435         return false;
436     }
437 
438     LOG_ARRAYMOVE(DEBUG) << "Check loop " << loop->GetId();
439     ArrayMoveParser arrayMoveParser(loop);
440     if (!arrayMoveParser.Parse()) {
441         return false;
442     }
443     LOG_ARRAYMOVE(DEBUG) << "Found in loop " << loop->GetId();
444 
445     return arrayMoveParser.TryReplaceLoop();
446 }
447 
TryTransformArrayInitIdiom(Loop * loop)448 bool LoopIdioms::TryTransformArrayInitIdiom(Loop *loop)
449 {
450     if (loop->GetBlocks().size() != 1) {
451         return false;
452     }
453     ASSERT(loop->GetInnerLoops().empty());
454 
455     auto store = FindStoreForArrayInit(loop->GetHeader());
456     if (store == nullptr) {
457         return false;
458     }
459 
460     auto loopInfoOpt = CountableLoopParser {*loop}.Parse();
461     if (!loopInfoOpt.has_value()) {
462         return false;
463     }
464     auto loopInfo = *loopInfoOpt;
465     if (!IsLoopContainsArrayInitIdiom(store, loop, loopInfo)) {
466         return false;
467     }
468     ASSERT(loopInfo.isInc);
469 
470     MarkerHolder holder {GetGraph()};
471     Marker marker = holder.GetMarker();
472     store->SetMarker(marker);
473     loopInfo.update->SetMarker(marker);
474     loopInfo.index->SetMarker(marker);
475     loopInfo.ifImm->SetMarker(marker);
476     loopInfo.ifImm->GetInput(0).GetInst()->SetMarker(marker);
477 
478     if (!CanReplaceLoop(loop, marker)) {
479         return false;
480     }
481 
482     COMPILER_LOG(DEBUG, LOOP_TRANSFORM) << "Array init idiom found in loop: " << loop->GetId()
483                                         << "\n\tarray: " << *store->GetArray()
484                                         << "\n\tvalue: " << *store->GetStoredValue()
485                                         << "\n\tinitial index: " << *loopInfo.init << "\n\ttest: " << *loopInfo.test
486                                         << "\n\tupdate: " << *loopInfo.update << "\n\tstep: " << loopInfo.constStep
487                                         << "\n\tindex: " << *loopInfo.index;
488 
489     bool alwaysJump = false;
490     if (loopInfo.init->IsConst() && loopInfo.test->IsConst()) {
491         auto iterations =
492             loopInfo.test->CastToConstant()->GetIntValue() - loopInfo.init->CastToConstant()->GetIntValue();
493         if (iterations <= ITERATIONS_THRESHOLD) {
494             COMPILER_LOG(DEBUG, LOOP_TRANSFORM)
495                 << "Loop will have " << iterations << " iterations, so intrinsics will not be generated";
496             return false;
497         }
498         alwaysJump = true;
499     }
500 
501     return ReplaceArrayInitLoop(loop, &loopInfo, store, alwaysJump);
502 }
503 
CreateArrayInitIntrinsic(StoreInst * store,CountableLoopInfo * info)504 Inst *LoopIdioms::CreateArrayInitIntrinsic(StoreInst *store, CountableLoopInfo *info)
505 {
506     auto type = store->GetType();
507     RuntimeInterface::IntrinsicId intrinsicId;
508     switch (type) {
509         case DataType::BOOL:
510         case DataType::INT8:
511         case DataType::UINT8:
512             intrinsicId = RuntimeInterface::IntrinsicId::LIB_CALL_MEMSET_8;
513             break;
514         case DataType::INT16:
515         case DataType::UINT16:
516             intrinsicId = RuntimeInterface::IntrinsicId::LIB_CALL_MEMSET_16;
517             break;
518         case DataType::INT32:
519         case DataType::UINT32:
520             intrinsicId = RuntimeInterface::IntrinsicId::LIB_CALL_MEMSET_32;
521             break;
522         case DataType::INT64:
523         case DataType::UINT64:
524             intrinsicId = RuntimeInterface::IntrinsicId::LIB_CALL_MEMSET_64;
525             break;
526         case DataType::FLOAT32:
527             intrinsicId = RuntimeInterface::IntrinsicId::LIB_CALL_MEMSET_F32;
528             break;
529         case DataType::FLOAT64:
530             intrinsicId = RuntimeInterface::IntrinsicId::LIB_CALL_MEMSET_F64;
531             break;
532         default:
533             return nullptr;
534     }
535 
536     auto fillArray = GetGraph()->CreateInstIntrinsic(DataType::VOID, store->GetPc(), intrinsicId);
537     fillArray->ClearFlag(inst_flags::Flags::REQUIRE_STATE);
538     fillArray->ClearFlag(inst_flags::Flags::RUNTIME_CALL);
539     fillArray->ClearFlag(inst_flags::Flags::CAN_THROW);
540     fillArray->SetInputs(GetGraph()->GetAllocator(), {{store->GetArray(), DataType::REFERENCE},
541                                                       {store->GetStoredValue(), type},
542                                                       {info->init, DataType::INT32},
543                                                       {info->test, DataType::INT32}});
544     return fillArray;
545 }
546 
ReplaceArrayInitLoop(Loop * loop,CountableLoopInfo * loopInfo,StoreInst * store,bool alwaysJump)547 bool LoopIdioms::ReplaceArrayInitLoop(Loop *loop, CountableLoopInfo *loopInfo, StoreInst *store, bool alwaysJump)
548 {
549     auto inst = CreateArrayInitIntrinsic(store, loopInfo);
550     if (inst == nullptr) {
551         return false;
552     }
553     auto header = loop->GetHeader();
554     auto preHeader = loop->GetPreHeader();
555 
556     auto loopSucc = header->GetSuccessor(0) == header ? header->GetSuccessor(1) : header->GetSuccessor(0);
557     if (alwaysJump) {
558         ASSERT(loop->GetBlocks().size() == 1);
559         // insert block before disconnecting header to properly handle Phi in loop_succ
560         auto block = header->InsertNewBlockToSuccEdge(loopSucc);
561         preHeader->ReplaceSucc(header, block, true);
562         GetGraph()->DisconnectBlock(header, false, false);
563         block->AppendInst(inst);
564 
565         COMPILER_LOG(INFO, LOOP_TRANSFORM) << "Replaced loop " << loop->GetId() << " with the instruction " << *inst
566                                            << " inserted into the new block " << block->GetId();
567     } else {
568         auto guardBlock = preHeader->InsertNewBlockToSuccEdge(header);
569         auto sub = GetGraph()->CreateInstSub(DataType::INT32, inst->GetPc(), loopInfo->test, loopInfo->init);
570         auto cmp = GetGraph()->CreateInstCompare(DataType::BOOL, inst->GetPc(), sub,
571                                                  GetGraph()->FindOrCreateConstant(ITERATIONS_THRESHOLD),
572                                                  DataType::INT32, ConditionCode::CC_LE);
573         auto ifImm =
574             GetGraph()->CreateInstIfImm(DataType::NO_TYPE, inst->GetPc(), cmp, 0, DataType::BOOL, ConditionCode::CC_NE);
575         guardBlock->AppendInst(sub);
576         guardBlock->AppendInst(cmp);
577         guardBlock->AppendInst(ifImm);
578 
579         auto mergeBlock = header->InsertNewBlockToSuccEdge(loopSucc);
580         auto intrinsicBlock = GetGraph()->CreateEmptyBlock();
581 
582         guardBlock->AddSucc(intrinsicBlock);
583         intrinsicBlock->AddSucc(mergeBlock);
584         intrinsicBlock->AppendInst(inst);
585 
586         COMPILER_LOG(INFO, LOOP_TRANSFORM) << "Inserted conditional jump into intinsic " << *inst << " before  loop "
587                                            << loop->GetId() << ", inserted blocks: " << intrinsicBlock->GetId() << ", "
588                                            << guardBlock->GetId() << ", " << mergeBlock->GetId();
589     }
590     return true;
591 }
592 
593 }  // namespace ark::compiler
594