• 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_logger.h"
17 #include "peepholes.h"
18 #include "optimizer/analysis/alias_analysis.h"
19 #include "optimizer/optimizations/const_folding.h"
20 #include "optimizer/optimizations/object_type_check_elimination.h"
21 
22 namespace ark::compiler {
23 
InvalidateAnalyses()24 void Peepholes::InvalidateAnalyses()
25 {
26     GetGraph()->InvalidateAnalysis<BoundsAnalysis>();
27     GetGraph()->InvalidateAnalysis<AliasAnalysis>();
28 }
29 
RunImpl()30 bool Peepholes::RunImpl()
31 {
32     GetGraph()->RunPass<DominatorsTree>();
33     GetGraph()->RunPass<ObjectTypePropagation>();
34     VisitGraph();
35     return IsApplied();
36 }
37 
VisitSafePoint(GraphVisitor * v,Inst * inst)38 void Peepholes::VisitSafePoint(GraphVisitor *v, Inst *inst)
39 {
40     if (g_options.IsCompilerSafePointsRequireRegMap()) {
41         return;
42     }
43     if (inst->CastToSafePoint()->RemoveNumericInputs()) {
44         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
45     }
46 }
47 
VisitNeg(GraphVisitor * v,Inst * inst)48 void Peepholes::VisitNeg([[maybe_unused]] GraphVisitor *v, Inst *inst)
49 {
50     if (ConstFoldingNeg(inst)) {
51         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
52         return;
53     }
54     auto input = inst->GetInput(0).GetInst();
55     auto inputOpc = input->GetOpcode();
56     auto instType = inst->GetType();
57     auto inputType = input->GetType();
58     if (DataType::IsFloatType(instType)) {
59         return;
60     }
61     // Repeated application of the Neg
62     // 1. Some inst -> {v2}
63     // 2.i64 neg v1 -> {v3, ...}
64     // 3.i64 neg v2 -> {...}
65     // ===>
66     // 1. Some inst -> {vv3 users}
67     // 2.i64 neg v1 -> {v3, ...}
68     // 3.i64 neg v2
69     if (inputOpc == Opcode::Neg && instType == inputType) {
70         if (SkipThisPeepholeInOSR(inst, input->GetInput(0).GetInst())) {
71             return;
72         }
73         inst->ReplaceUsers(input->GetInput(0).GetInst());
74         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
75         return;
76     }
77     // Сhanging the parameters of subtraction with the use of Neg
78     // 2.i64 sub v0, v1 -> {v3, ...}
79     // 3.i64 neg v2 -> {...}
80     // ===>
81     // 2.i64 sub v0, v1 -> {v3, ...}
82     // 3.i64 neg v2 -> {}
83     // 4.i64 sub v1, v0 -> {users v3}
84     if (inputOpc == Opcode::Sub && instType == inputType) {
85         if (SkipThisPeepholeInOSR(inst, input->GetInput(0).GetInst()) ||
86             SkipThisPeepholeInOSR(inst, input->GetInput(1).GetInst())) {
87             return;
88         }
89         CreateAndInsertInst(Opcode::Sub, inst, input->GetInput(1).GetInst(), input->GetInput(0).GetInst());
90         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
91         return;
92     }
93 }
94 
VisitAbs(GraphVisitor * v,Inst * inst)95 void Peepholes::VisitAbs([[maybe_unused]] GraphVisitor *v, Inst *inst)
96 {
97     if (!DataType::IsTypeSigned(inst->GetType())) {
98         if (SkipThisPeepholeInOSR(inst, inst->GetInput(0).GetInst())) {
99             return;
100         }
101         inst->ReplaceUsers(inst->GetInput(0).GetInst());
102         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
103         return;
104     }
105     if (ConstFoldingAbs(inst)) {
106         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
107         return;
108     }
109 }
110 
VisitNot(GraphVisitor * v,Inst * inst)111 void Peepholes::VisitNot([[maybe_unused]] GraphVisitor *v, Inst *inst)
112 {
113     if (ConstFoldingNot(inst)) {
114         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
115         return;
116     }
117 }
118 
VisitAddFinalize(GraphVisitor * v,Inst * inst,Inst * input0,Inst * input1)119 void Peepholes::VisitAddFinalize([[maybe_unused]] GraphVisitor *v, Inst *inst, Inst *input0, Inst *input1)
120 {
121     auto visitor = static_cast<Peepholes *>(v);
122     // Some of the previous transformations might change the opcode of inst.
123     if (inst->GetOpcode() == Opcode::Add) {
124         if (visitor->TryReassociateShlShlAddSub(inst)) {
125             PEEPHOLE_IS_APPLIED(visitor, inst);
126             return;
127         }
128 
129         if (input0->GetOpcode() == Opcode::Add && input1->GetOpcode() == Opcode::Sub) {
130             std::swap(input0, input1);
131         }
132 
133         if (input0->GetOpcode() == Opcode::Sub && input1->GetOpcode() == Opcode::Add) {
134             if (visitor->TrySimplifyAddSubAdd(inst, input0, input1)) {
135                 return;
136             }
137         }
138 
139         if (input0->GetOpcode() == Opcode::Sub && input1->GetOpcode() == Opcode::Sub) {
140             if (visitor->TrySimplifyAddSubSub(inst, input0, input1)) {
141                 return;
142             }
143         }
144     }
145 }
146 
147 /**
148  * Case 1: Swap inputs if the first is a constant
149  * 2. add const, v1 -> {...}
150  * ===>
151  * 2. add v1, const -> {...}
152  *
153  * Case 2: Addition with zero
154  * 1. Some inst -> v2
155  * 2. add v1, 0 -> {...}
156  * ===>
157  * 1. Some inst ->{...}
158  * 2. add v1, 0 -> {}
159  *
160  * Case 3: Repeated arithmetic with constants
161  * 2. add/sub v1, const1 -> {v3, ...}
162  * 3. add v2, const2 -> {...}
163  * ===>
164  * 2. add/sub v1, const1 -> {...}
165  * 3. add v1, const2 +/- const1 ->{...}
166  *
167  * Case 4: Addition with Neg
168  * 3. neg v1 -> {5}
169  * 4. neg v2 -> {5}
170  * 5. add v3, v4 -> {...}
171  * ===>
172  * 3. neg v1 -> {}
173  * 4. neg v2 -> {}
174  * 5. add v1, v2 -> {4}
175  * 6. neg v5 -> {users v5}
176  *
177  * Case 5:
178  * 3. neg v2 -> {4, ...}
179  * 4. add v1, v3 -> {...}
180  * ===>
181  * 3. neg v2 -> {...}
182  * 4. sub v1, v2 -> {...}
183  *
184  * Case 6:
185  * Adding and subtracting the same value
186  * 3. sub v2, v1 -> {4, ...}
187  * 4. add v3, v1 -> {...}
188  * ===>
189  * 3. sub v2, v1 -> {4, ...}
190  * 4. add v3, v1 -> {}
191  * v2 -> {users v4}
192  *
193  * Case 7:
194  * Replacing Negation pattern (neg + add) with Compare
195  * 1.i64 Constant 0x1 -> {4, ...}
196  * 2.b   ... -> {3}
197  * 3.i32 Neg v2 -> {4, ...}
198  * 4.i32 Add v3, v1 -> {...}
199  * ===>
200  * 1.i64 Constant 0x1 -> {...}
201  * 5.i64 Constant 0x0 -> {v6, ...}
202  * 2.b   ... -> {v3, v6}
203  * 3.i32 Neg v2 -> {...}
204  * 4.i32 Add v3, v1
205  * 6.b   Compare EQ b v2, v5 -> {USERS v4}
206  *
207  * Case 8:
208  * Delete negation of Compare
209  * 1.i64 Constant 0x1 -> {v4}
210  * 2.b   Compare GT ... -> {v3}
211  * 3.i32 Neg v2 -> {v4}
212  * 4.i32 Add v3, v1 -> {...}
213  * ===>
214  * 1.i64 Constant 0x1 -> {v4}
215  * 2.b   Compare LE ... -> {v3, USERS v4}
216  * 3.i32 Neg v2 -> {v4}
217  * 4.i32 Add v3, v1
218  *
219  * Case 9
220  * One of the inputs of Compare is Negation pattern
221  * 1.i64 Constant 0x1 -> {v4, ...}
222  * 2.b   ... -> {v3}
223  * 3.i32 Neg v2 -> {v4, ...}
224  * 4.i32 Add v3, v1 -> {v5, ...}
225  * 5.b   Compare EQ/NE b v4, ...
226  * =======>
227  * 1.i64 Constant 0x1 -> {v4, ...}
228  * 2.b   ... -> {v3, v5}
229  * 3.i32 Neg v2 -> {v4, ...}
230  * 4.i32 Add v3, v1 -> {...}
231  * 5.b   Compare NE/EQ b v2, ...
232  */
VisitAdd(GraphVisitor * v,Inst * inst)233 void Peepholes::VisitAdd([[maybe_unused]] GraphVisitor *v, Inst *inst)
234 {
235     auto visitor = static_cast<Peepholes *>(v);
236     if (ConstFoldingAdd(inst)) {
237         PEEPHOLE_IS_APPLIED(visitor, inst);
238         return;
239     }
240     if (DataType::IsFloatType(inst->GetType())) {
241         return;
242     }
243     // Case 1
244     visitor->TrySwapInputs(inst);
245     if (visitor->TrySimplifyAddSubWithConstInput(inst)) {
246         return;
247     }
248     auto input0 = inst->GetInput(0).GetInst();
249     auto input1 = inst->GetInput(1).GetInst();
250     if (input0->GetOpcode() == Opcode::Neg && input1->GetOpcode() == Opcode::Neg) {
251         // Case 4
252         if (input0->HasSingleUser() && input1->HasSingleUser()) {
253             if (SkipThisPeepholeInOSR(inst, input0->GetInput(0).GetInst()) ||
254                 SkipThisPeepholeInOSR(inst, input1->GetInput(0).GetInst())) {
255                 return;
256             }
257             inst->SetInput(0, input0->GetInput(0).GetInst());
258             inst->SetInput(1, input1->GetInput(0).GetInst());
259             CreateAndInsertInst(Opcode::Neg, inst, inst);
260             PEEPHOLE_IS_APPLIED(visitor, inst);
261             return;
262         }
263     } else {
264         // Case 7, 8, 9
265         if (visitor->TrySimplifyNegationPattern(inst)) {
266             return;
267         }
268         // Case 5
269         visitor->TrySimplifyNeg(inst);
270     }
271     // Case 6
272     visitor->TrySimplifyAddSub<Opcode::Sub, 0>(inst, input0, input1);
273     visitor->TrySimplifyAddSub<Opcode::Sub, 0>(inst, input1, input0);
274 
275     VisitAddFinalize(v, inst, input0, input1);
276 }
277 
VisitSubFinalize(GraphVisitor * v,Inst * inst,Inst * input0,Inst * input1)278 void Peepholes::VisitSubFinalize([[maybe_unused]] GraphVisitor *v, Inst *inst, Inst *input0, Inst *input1)
279 {
280     auto visitor = static_cast<Peepholes *>(v);
281     // Some of the previous transformations might change the opcode of inst.
282     if (inst->GetOpcode() == Opcode::Sub) {
283         if (visitor->TryReassociateShlShlAddSub(inst)) {
284             PEEPHOLE_IS_APPLIED(visitor, inst);
285             return;
286         }
287 
288         if (input0->GetOpcode() == Opcode::Add && input1->GetOpcode() == Opcode::Add) {
289             if (visitor->TrySimplifySubAddAdd(inst, input0, input1)) {
290                 return;
291             }
292         }
293     }
294 }
295 /**
296  * Case 1
297  * Subtraction of zero
298  * 1. Some inst
299  * 2. sub v1, 0 -> {...}
300  * ===>
301  * 1. Some inst
302  * 2. sub v1, 0 -> {}
303  * Case 2
304  * Repeated arithmetic with constants
305  * 2. add/sub v1, const1 -> {v3, ...}
306  * 3. sub v2, const2 -> {...}
307  * ===>
308  * 2. add/sub v1, const1 -> {...}
309  * 3. sub v1, const2 -/+ const1 ->{...}
310  * Case 3
311  * Subtraction of Neg
312  * 3. neg v1 -> {5, ...}
313  * 4. neg v2 -> {5, ...}
314  * 5. sub v3, v4 -> {...}
315  * ===>
316  * 3. neg v1 -> {...}
317  * 4. neg v2 -> {...}
318  * 5. sub v2, v1 -> {...}
319  * Case 4
320  * Adding and subtracting the same value
321  * 3. add v1, v2 -> {4, ...}
322  * 4. sub v3, v2 -> {...}
323  * ===>
324  * 3. add v1, v2 -> {4, ...}
325  * 4. sub v3, v1 -> {}
326  * v1 -> {users v4}
327  * Case 5
328  * 3. sub v1, v2 -> {4, ...}
329  * 4. sub v1, v3 -> {...}
330  * ===>
331  * 3. sub v1, v2 -> {4, ...}
332  * 4. sub v1, v3 -> {}
333  * v2 -> {users v4}
334  * Case 6
335  * 1. Some inst
336  * 2. sub 0, v1 -> {...}
337  * ===>
338  * 1. Some inst
339  * 2. neg v1 -> {...}
340  */
VisitSub(GraphVisitor * v,Inst * inst)341 void Peepholes::VisitSub([[maybe_unused]] GraphVisitor *v, Inst *inst)
342 {
343     auto visitor = static_cast<Peepholes *>(v);
344     if (ConstFoldingSub(inst)) {
345         PEEPHOLE_IS_APPLIED(visitor, inst);
346         return;
347     }
348     if (DataType::IsFloatType(inst->GetType())) {
349         return;
350     }
351     // Case 1, 2, 6
352     if (visitor->TrySimplifyAddSubWithConstInput(inst)) {
353         return;
354     }
355     auto input0 = inst->GetInput(0).GetInst();
356     auto input1 = inst->GetInput(1).GetInst();
357     // Case 3
358     if (input0->GetOpcode() == Opcode::Neg && input1->GetOpcode() == Opcode::Neg) {
359         auto newInput1 = input0->GetInput(0).GetInst();
360         auto newInput0 = input1->GetInput(0).GetInst();
361         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
362             return;
363         }
364         inst->SetInput(0, newInput0);
365         inst->SetInput(1, newInput1);
366         PEEPHOLE_IS_APPLIED(visitor, inst);
367         return;
368     }
369     if (input1->GetOpcode() == Opcode::Neg) {
370         auto newInput = input1->GetInput(0).GetInst();
371         if (SkipThisPeepholeInOSR(inst, newInput)) {
372             return;
373         }
374         inst->SetInput(1, newInput);
375         inst->SetOpcode(Opcode::Add);
376         PEEPHOLE_IS_APPLIED(visitor, inst);
377         return;
378     }
379     // Case 4
380     visitor->TrySimplifyAddSub<Opcode::Add, 0>(inst, input0, input1);
381     visitor->TrySimplifyAddSub<Opcode::Add, 1>(inst, input0, input1);
382     // Case 5
383     if (input1->GetOpcode() == Opcode::Sub && input1->GetInput(0) == input0) {
384         auto prevInput = input1->GetInput(1).GetInst();
385         if (inst->GetType() == prevInput->GetType()) {
386             if (SkipThisPeepholeInOSR(inst, prevInput)) {
387                 return;
388             }
389             inst->ReplaceUsers(prevInput);
390             PEEPHOLE_IS_APPLIED(visitor, inst);
391             return;
392         }
393     }
394     VisitSubFinalize(v, inst, input0, input1);
395 }
396 
VisitMulOneConst(GraphVisitor * v,Inst * inst,Inst * input0,Inst * input1)397 void Peepholes::VisitMulOneConst([[maybe_unused]] GraphVisitor *v, Inst *inst, Inst *input0, Inst *input1)
398 {
399     if (!input1->IsConst()) {
400         return;
401     }
402     auto constInst = static_cast<ConstantInst *>(input1);
403     if (constInst->IsEqualConstAllTypes(1)) {
404         // case 1:
405         // 0. Const 1
406         // 1. MUL v5, v0 -> {v2, ...}
407         // 2. INS v1
408         // ===>
409         // 0. Const 1
410         // 1. MUL v5, v0
411         // 2. INS v5
412         if (SkipThisPeepholeInOSR(inst, input0)) {
413             return;
414         }
415         inst->ReplaceUsers(input0);
416         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
417     } else if (constInst->IsEqualConstAllTypes(-1)) {
418         // case 2:
419         // 0. Const -1
420         // 1. MUL v5, v0 -> {v2, ...}
421         // 2. INS v1
422         // ===>
423         // 0. Const -1
424         // 1. MUL v5, v0
425         // 3. NEG v5 -> {v2, ...}
426         // 2. INS v3
427 
428         // "inst"(mul) and **INST WITHOUT NAME**(neg) are one by one, so we don't should check SaveStateOSR between them
429         CreateAndInsertInst(Opcode::Neg, inst, input0);
430         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
431     } else if (constInst->IsEqualConstAllTypes(2U)) {
432         // case 3:
433         // 0. Const 2
434         // 1. MUL v5, v0 -> {v2, ...}
435         // 2. INS v1
436         // ===>
437         // 0. Const -1
438         // 1. MUL v5, v0
439         // 3. ADD v5 , V5 -> {v2, ...}
440         // 2. INS v3
441 
442         // "inst"(mul) and **INST WITHOUT NAME**(add) are one by one, so we don't should check SaveStateOSR between them
443         CreateAndInsertInst(Opcode::Add, inst, input0, input0);
444         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
445     } else if (DataType::GetCommonType(inst->GetType()) == DataType::INT64) {
446         int64_t n = GetPowerOfTwo(constInst->GetIntValue());
447         if (n != -1) {
448             // case 4:
449             // 0. Const 2^N
450             // 1. MUL v5, v0 -> {v2, ...}
451             // 2. INS v1
452             // ===>
453             // 0. Const -1
454             // 1. MUL v5, v0
455             // 3. SHL v5 , N -> {v2, ...}
456             // 2. INS v3
457 
458             // "inst"(mul) and **INST WITHOUT NAME**(add) are one by one, so we don't should
459             // check SaveStateOSR between them
460             ConstantInst *power = ConstFoldingCreateIntConst(inst, static_cast<uint64_t>(n));
461             CreateAndInsertInst(Opcode::Shl, inst, input0, power);
462             PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
463         }
464     }
465 }
466 
VisitMul(GraphVisitor * v,Inst * inst)467 void Peepholes::VisitMul(GraphVisitor *v, Inst *inst)
468 {
469     auto visitor = static_cast<Peepholes *>(v);
470     if (ConstFoldingMul(inst)) {
471         PEEPHOLE_IS_APPLIED(visitor, inst);
472         return;
473     }
474     if (DataType::IsFloatType(inst->GetType())) {
475         return;
476     }
477     visitor->TrySwapInputs(inst);
478     auto input0 = inst->GetInput(0).GetInst();
479     auto input1 = inst->GetInput(1).GetInst();
480     if (input1->IsConst()) {
481         auto cnst = input1->CastToConstant();
482         bool osrBlockedPeephole = false;
483         if (input0->GetOpcode() == Opcode::Mul && TryCombineMulConst(inst, cnst, &osrBlockedPeephole)) {
484             // 0. Const 3
485             // 1. Const 7
486             // 2. ...
487             // 3. Mul v2, v0
488             // 4. Mul v3, v1
489             // ===>
490             // 5. Const 21
491             // 2. ...
492             // 4. Mul v2, v5
493             PEEPHOLE_IS_APPLIED(visitor, inst);
494             return;
495         }
496         if (osrBlockedPeephole) {
497             return;
498         }
499     }
500     VisitMulOneConst(v, inst, input0, input1);
501 }
502 
VisitDiv(GraphVisitor * v,Inst * inst)503 void Peepholes::VisitDiv([[maybe_unused]] GraphVisitor *v, Inst *inst)
504 {
505     if (ConstFoldingDiv(inst)) {
506         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
507         return;
508     }
509     if (DataType::IsFloatType(inst->GetType())) {
510         return;
511     }
512     auto input0 = inst->GetInput(0).GetInst();
513     auto input1 = inst->GetInput(1).GetInst();
514     if (input1->IsConst()) {
515         auto constInst = static_cast<ConstantInst *>(input1);
516         if (constInst->IsEqualConstAllTypes(1)) {
517             // case 1:
518             // 0. Const 1
519             // 1. DIV v5, v0 -> {v2, ...}
520             // 2. INS v1
521             // ===>
522             // 0. Const 1
523             // 1. DIV v5, v0
524             // 2. INS v5
525             if (SkipThisPeepholeInOSR(inst, input0)) {
526                 return;
527             }
528             inst->ReplaceUsers(input0);
529             PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
530         } else if (constInst->IsEqualConstAllTypes(-1)) {
531             // case 2:
532             // 0. Const -1
533             // 1. DIV v5, v0 -> {v2, ...}
534             // 2. INS v1
535             // ===>
536             // 0. Const -1
537             // 1. DIV v5, v0
538             // 3. NEG v5 -> {v2, ...}
539             // 2. INS v3
540 
541             // "inst"(div) and **INST WITHOUT NAME**(neg) are one by one, we should check SaveStateOSR between
542             // **INST WITHOUT NAME**(neg) and "input0", but may check only between "inst"(div) and "input0"
543             if (SkipThisPeepholeInOSR(inst, input0)) {
544                 return;
545             }
546             CreateAndInsertInst(Opcode::Neg, inst, input0);
547             PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
548         } else if (DataType::GetCommonType(inst->GetType()) == DataType::INT64) {
549             static_cast<Peepholes *>(v)->TryReplaceDivByShift(inst);
550         }
551     }
552 }
553 
VisitMin(GraphVisitor * v,Inst * inst)554 void Peepholes::VisitMin([[maybe_unused]] GraphVisitor *v, Inst *inst)
555 {
556     if (ConstFoldingMin(inst)) {
557         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
558         return;
559     }
560     if (DataType::IsFloatType(inst->GetType())) {
561         return;
562     }
563     // Swap inputs if the first is a constant
564     // 2. Min const, v1 -> {...}
565     // ===>
566     // 2. Min v1, const -> {...}
567     static_cast<Peepholes *>(v)->TrySwapInputs(inst);
568 }
569 
VisitMax(GraphVisitor * v,Inst * inst)570 void Peepholes::VisitMax([[maybe_unused]] GraphVisitor *v, Inst *inst)
571 {
572     if (ConstFoldingMax(inst)) {
573         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
574         return;
575     }
576     if (DataType::IsFloatType(inst->GetType())) {
577         return;
578     }
579     // Swap inputs if the first is a constant
580     // 2. Max const, v1 -> {...}
581     // ===>
582     // 2. Max v1, const -> {...}
583     static_cast<Peepholes *>(v)->TrySwapInputs(inst);
584 }
585 
VisitMod(GraphVisitor * v,Inst * inst)586 void Peepholes::VisitMod([[maybe_unused]] GraphVisitor *v, Inst *inst)
587 {
588     if (ConstFoldingMod(inst)) {
589         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
590         return;
591     }
592 }
593 
VisitShl(GraphVisitor * v,Inst * inst)594 void Peepholes::VisitShl([[maybe_unused]] GraphVisitor *v, Inst *inst)
595 {
596     if (ConstFoldingShl(inst)) {
597         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
598         return;
599     }
600     static_cast<Peepholes *>(v)->TrySimplifyShifts(inst);
601 }
602 
VisitShr(GraphVisitor * v,Inst * inst)603 void Peepholes::VisitShr([[maybe_unused]] GraphVisitor *v, Inst *inst)
604 {
605     if (ConstFoldingShr(inst)) {
606         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
607         return;
608     }
609     static_cast<Peepholes *>(v)->TrySimplifyShifts(inst);
610     // TrySimplifyShifts can replace inst by another.
611     if (inst->GetUsers().Empty()) {
612         return;
613     }
614     // case 1
615     // 0.i64 Constant X
616     // 1. ...
617     // 2.Type Shl v1, v0
618     // 3.Type Shr v2, v0
619     // ====>
620     // 0.i64 Constant X
621     // 4.i64 Constant (1U << (Size(Type) - X)) - 1
622     // 1. ...
623     // 5.Type And v1, v4
624     auto op1 = inst->GetInput(0).GetInst();
625     auto op2 = inst->GetInput(1).GetInst();
626     if (op1->GetOpcode() == Opcode::Shl && op2->IsConst() && op1->GetInput(1) == op2) {
627         uint64_t val = op2->CastToConstant()->GetIntValue();
628         ASSERT(inst->GetType() == op1->GetType());
629         auto graph = inst->GetBasicBlock()->GetGraph();
630         auto arch = graph->GetArch();
631         ASSERT(DataType::GetTypeSize(inst->GetType(), arch) >= val);
632         auto t = static_cast<uint8_t>(DataType::GetTypeSize(inst->GetType(), arch) - val);
633         uint64_t mask = (1UL << t) - 1;
634         auto newCnst = graph->FindOrCreateConstant(mask);
635         if (graph->IsBytecodeOptimizer() && IsInt32Bit(inst->GetType())) {
636             t = static_cast<uint8_t>(DataType::GetTypeSize(inst->GetType(), arch) - static_cast<uint32_t>(val));
637             mask = static_cast<uint32_t>((1U << t) - 1);
638             newCnst = graph->FindOrCreateConstant<uint32_t>(mask);
639         }
640         // "inst"(shr) and **INST WITHOUT NAME**(and) one by one, so we may check SaveStateOSR only between
641         // "inst"(shr) and "op1->GetInput(0).GetInst()"
642         if (SkipThisPeepholeInOSR(op1->GetInput(0).GetInst(), inst)) {
643             return;
644         }
645         CreateAndInsertInst(Opcode::And, inst, op1->GetInput(0).GetInst(), newCnst);
646         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
647     }
648 }
649 
VisitAShr(GraphVisitor * v,Inst * inst)650 void Peepholes::VisitAShr([[maybe_unused]] GraphVisitor *v, Inst *inst)
651 {
652     if (ConstFoldingAShr(inst)) {
653         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
654         return;
655     }
656     /**
657      * This VisitAShr() may new create some Cast instruction which will cause failure in the codegen stage.
658      * Even though these Casts' resolving can be supported in bytecode_optimizer's CodeGen,
659      * they will be translated back to 'shl && ashr', therefore, this part is skipped in bytecode_opt mode.
660      */
661     if (inst->GetBasicBlock()->GetGraph()->IsBytecodeOptimizer()) {
662         return;
663     }
664     static_cast<Peepholes *>(v)->TrySimplifyShifts(inst);
665     // TrySimplifyShifts can replace inst by another.
666     if (inst->GetUsers().Empty()) {
667         return;
668     }
669     // case 1
670     // 0.i64 Constant
671     // 1. ...
672     // 2.Type Shl  v1, v0
673     // 3.Type AShr v2, v0
674     // ====>
675     // 0.i64 Constant
676     // 1. ...
677     // 5. Cast v1
678     auto op1 = inst->GetInput(0).GetInst();
679     auto op2 = inst->GetInput(1).GetInst();
680     if (op1->GetOpcode() == Opcode::Shl && op2->IsConst() && op1->GetInput(1) == op2) {
681         ASSERT(inst->GetType() == op1->GetType());
682         const uint64_t offsetInT8 = 24;
683         const uint64_t offsetInT16 = 16;
684         const uint64_t offsetInT32 = 8;
685         auto offset = op2->CastToConstant()->GetIntValue();
686         if (offset == offsetInT16 || offset == offsetInT8 || offset == offsetInT32) {
687             // "inst"(shr) and "cast"(cast) one by one, so we may check SaveStateOSR only between "inst"(shr)
688             // and "op1->GetInput(0).GetInst()"(some inst)
689             if (SkipThisPeepholeInOSR(op1->GetInput(0).GetInst(), inst)) {
690                 return;
691             }
692             auto cast = CreateAndInsertInst(Opcode::Cast, inst, op1->GetInput(0).GetInst());
693             cast->SetType((offset == offsetInT8)    ? DataType::INT8
694                           : (offset == offsetInT16) ? DataType::INT16
695                                                     : DataType::INT32);
696             cast->CastToCast()->SetOperandsType(op1->GetInput(0).GetInst()->GetType());
697             PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
698         }
699     }
700 }
701 
ApplyForCastU16(GraphVisitor * v,Inst * inst,Inst * input0,Inst * input1)702 static bool ApplyForCastU16(GraphVisitor *v, Inst *inst, Inst *input0, Inst *input1)
703 {
704     return input1->IsConst() && input0->GetOpcode() == Opcode::Cast &&
705            DataType::GetCommonType(input0->GetInput(0).GetInst()->GetType()) == DataType::INT64 &&
706            IsInt32Bit(inst->GetType()) &&
707            DataType::GetTypeSize(input0->GetType(), static_cast<Peepholes *>(v)->GetGraph()->GetArch()) > HALF_SIZE;
708 }
709 
VisitAnd(GraphVisitor * v,Inst * inst)710 void Peepholes::VisitAnd([[maybe_unused]] GraphVisitor *v, Inst *inst)
711 {
712     if (ConstFoldingAnd(inst)) {
713         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
714         return;
715     }
716     // case 1:
717     // 0.i64 Const ...
718     // 1.i64 AND v0, v5
719     // ===>
720     // 0.i64 Const 25
721     // 1.i64 AND v5, v0
722     static_cast<Peepholes *>(v)->TrySwapInputs(inst);
723     auto input0 = inst->GetInput(0).GetInst();
724     auto input1 = inst->GetInput(1).GetInst();
725     // NOLINTNEXTLINE(bugprone-branch-clone)
726     if (input1->IsConst() && static_cast<ConstantInst *>(input1)->GetIntValue() == static_cast<uint64_t>(-1)) {
727         // case 2:
728         // 0.i64 Const 0xFFF..FF
729         // 1.i64 AND v5, v0
730         // 2.i64 INS which use v1
731         // ===>
732         // 0.i64 Const 0xFFF..FF
733         // 1.i64 AND v5, v0
734         // 2.i64 INS which use v5
735         if (SkipThisPeepholeInOSR(inst, input0)) {
736             return;
737         }
738         inst->ReplaceUsers(input0);
739         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
740     } else if (input0 == input1 && input0->GetType() == inst->GetType()) {
741         // case 3:
742         // 1.i64 AND v5, v5
743         // 2.i64 INS which use v1
744         // ===>
745         // 1.i64 AND v5, v5
746         // 2.i64 INS which use v5
747         if (SkipThisPeepholeInOSR(inst, input0)) {
748             return;
749         }
750         inst->ReplaceUsers(input0);
751         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
752     } else if (input0->GetOpcode() == Opcode::Not && input1->GetOpcode() == Opcode::Not && input0->HasSingleUser() &&
753                input1->HasSingleUser()) {
754         // case 4: De Morgan rules
755         // 2.i64 not v0 -> {4}
756         // 3.i64 not v1 -> {4}
757         // 4.i64 AND v2, v3
758         // ===>
759         // 5.i64 OR v0, v1
760         // 6.i64 not v5
761         auto notInput0 = input0->GetInput(0).GetInst();
762         auto notInput1 = input1->GetInput(0).GetInst();
763         // "inst"(and), "or_inst"(or) and **INST WITHOUT NAME**(not) one by one, so we may check SaveStateOSR only
764         // between "inst"(and) and inputs: "not_input0"(some inst), "not_input0"(some inst)
765         // and "op1->GetInput(0).GetInst()"
766         if (SkipThisPeepholeInOSR(inst, notInput0) || SkipThisPeepholeInOSR(inst, notInput1)) {
767             return;
768         }
769         auto orInst = CreateAndInsertInst(Opcode::Or, inst, notInput0, notInput1);
770         CreateAndInsertInst(Opcode::Not, orInst, orInst);
771         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
772     } else if (ApplyForCastU16(v, inst, input0, input1)) {
773         // case 5: IR for cast i64 to u16 is
774         // 2.i32 cast i64 to i32
775         // 3.i32 and v2, 0xFFFF
776         // replace it with cast i64tou16
777         auto val = input1->CastToConstant()->GetIntValue();
778         constexpr auto MASK = (1U << HALF_SIZE) - 1;
779         if (val == MASK) {
780             // "inst"(and) and "cast"(cast) one by one, so we may check SaveStateOSR only between
781             // "inst"(shr) and "input0->GetInput(0).GetInst()"
782             if (SkipThisPeepholeInOSR(inst, input0->GetInput(0).GetInst())) {
783                 return;
784             }
785             auto cast = CreateAndInsertInst(Opcode::Cast, inst, input0->GetInput(0).GetInst());
786             cast->SetType(DataType::UINT16);
787             cast->CastToCast()->SetOperandsType(input0->GetInput(0).GetInst()->GetType());
788             PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
789         }
790     }
791 }
792 
VisitOr(GraphVisitor * v,Inst * inst)793 void Peepholes::VisitOr([[maybe_unused]] GraphVisitor *v, Inst *inst)
794 {
795     if (ConstFoldingOr(inst)) {
796         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
797         return;
798     }
799     // case 1:
800     // 0.i64 Const ...
801     // 1.i64 Or v0, v5
802     // ===>
803     // 0.i64 Const 25
804     // 1.i64 Or v5, v0
805     static_cast<Peepholes *>(v)->TrySwapInputs(inst);
806     auto input0 = inst->GetInput(0).GetInst();
807     auto input1 = inst->GetInput(1).GetInst();
808     // NOLINTNEXTLINE(bugprone-branch-clone)
809     if (input0 == input1 && input0->GetType() == inst->GetType()) {
810         // case 2:
811         // 1.i64 OR v5, v5
812         // 2.i64 INS which use v1
813         // ===>
814         // 1.i64 OR v5, v5
815         // 2.i64 INS which use v5
816         if (SkipThisPeepholeInOSR(inst, input0)) {
817             return;
818         }
819         inst->ReplaceUsers(input0);
820         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
821     } else if (input1->IsConst() && static_cast<ConstantInst *>(input1)->GetIntValue() == static_cast<uint64_t>(0)) {
822         // case 3:
823         // 0.i64 Const 0x000..00
824         // 1.i64 OR v5, v0
825         // 2.i64 INS which use v1
826         // ===>
827         // 0.i64 Const 0x000..00
828         // 1.i64 OR v5, v0
829         // 2.i64 INS which use v5
830         if (SkipThisPeepholeInOSR(inst, input0)) {
831             return;
832         }
833         inst->ReplaceUsers(input0);
834         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
835     } else if (input0->GetOpcode() == Opcode::Not && input1->GetOpcode() == Opcode::Not && input0->HasSingleUser() &&
836                input1->HasSingleUser()) {
837         // case 4: De Morgan rules
838         // 2.i64 not v0 -> {4}
839         // 3.i64 not v1 -> {4}
840         // 4.i64 OR v2, v3
841         // ===>
842         // 5.i64 AND v0, v1
843         // 6.i64 not v5
844         auto notInput0 = input0->GetInput(0).GetInst();
845         auto notInput1 = input1->GetInput(0).GetInst();
846         // "inst"(or), "and_inst"(and) and **INST WITHOUT NAME**(not) one by one, so we may check SaveStateOSR only
847         // between "inst"(or) and inputs: "not_input0"(some inst), "not_input0"(some inst)
848         // and "op1->GetInput(0).GetInst()"
849         if (SkipThisPeepholeInOSR(inst, notInput0) || SkipThisPeepholeInOSR(inst, notInput1)) {
850             return;
851         }
852         auto andInst = CreateAndInsertInst(Opcode::And, inst, notInput0, notInput1);
853         CreateAndInsertInst(Opcode::Not, andInst, andInst);
854         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
855     }
856 }
857 
VisitXor(GraphVisitor * v,Inst * inst)858 void Peepholes::VisitXor([[maybe_unused]] GraphVisitor *v, Inst *inst)
859 {
860     if (ConstFoldingXor(inst)) {
861         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
862         return;
863     }
864     auto visitor = static_cast<Peepholes *>(v);
865     // Swap inputs if the first is a constant
866     // 2. Xor const, v1 -> {...}
867     // ===>
868     // 2. Xor v1, const -> {...}
869     static_cast<Peepholes *>(v)->TrySwapInputs(inst);
870     auto input0 = inst->GetInput(0).GetInst();
871     auto input1 = inst->GetInput(1).GetInst();
872 
873     // Remove two consecutive xor using the same constant,
874     // the remaining xor without users will be removed at the next Cleanup
875     // 0.i64 Const   -> (2,3)
876     // 1. ...        -> (2)
877     // 2. Xor v1, v0 -> (3)
878     // 3. Xor v1, v0 -> (4)
879     // 4. ...
880     // ===>
881     // 0.i64 Const   -> (2,3)
882     // 1. ...        -> (2,4)
883     // 2. Xor v1, v0 -> (3)
884     // 3. Xor v1, v0
885     // 4. ...
886     //
887     // Finding patterns from inputs is better, but here we start with the first Xor.
888     // If we began with the second Xor, Peepholes::VisitXor would have already converted
889     // the first Xor into a Compare (via CreateCompareInsteadOfXorAdd) and added it after
890     // the first Xor. We must delete the Xor->Xor chain before this happens, as the
891     // Compare only appears when the Xor becomes useless.
892     if (TryOptimizeXorChain(inst)) {
893         PEEPHOLE_IS_APPLIED(visitor, inst);
894         return;
895     }
896 
897     if (input1->IsConst()) {
898         uint64_t val = input1->CastToConstant()->GetIntValue();
899         if (static_cast<int64_t>(val) == -1) {
900             // Replace xor with not:
901             // 0.i64 Const -1
902             // 1. ...
903             // 2.i64 XOR v0, v1
904             // ===>
905             // 3.i64 NOT v1
906             if (SkipThisPeepholeInOSR(inst, input0)) {
907                 return;
908             }
909             CreateAndInsertInst(Opcode::Not, inst, input0);
910             PEEPHOLE_IS_APPLIED(visitor, inst);
911         } else if (input1->CastToConstant()->GetIntValue() == 0) {
912             // Result A xor 0 equal A:
913             // 0.i64 Const 0
914             // 1. ...
915             // 2.i64 XOR v0, v1 -> v3
916             // 3. INS v2
917             // ===>
918             // 0.i64 Const 0
919             // 1. ...
920             // 2.i64 XOR v0, v1
921             // 3. INS v1
922             if (SkipThisPeepholeInOSR(inst, input0)) {
923                 return;
924             }
925             inst->ReplaceUsers(input0);
926             PEEPHOLE_IS_APPLIED(visitor, inst);
927         } else if (val == 1 && input0->GetType() == DataType::BOOL) {
928             // Replacing Xor with Compare for more case optimizations
929             // If this compare is not optimized during pipeline, we will revert it back to xor in Lowering
930             // 1.i64 Const 1
931             // 2.b   ...
932             // 3.i32 Xor v1, v2
933             // ===>
934             // 1.i64 Const 0
935             // 2.b   ...
936             // 3.b   Compare EQ b v2, v1
937             if (SkipThisPeepholeInOSR(inst, input0)) {
938                 return;
939             }
940             CreateCompareInsteadOfXorAdd(inst);
941             PEEPHOLE_IS_APPLIED(visitor, inst);
942         }
943     }
944 }
945 
VisitCmp(GraphVisitor * v,Inst * inst)946 void Peepholes::VisitCmp(GraphVisitor *v, Inst *inst)
947 {
948     auto visitor = static_cast<Peepholes *>(v);
949     if (ConstFoldingCmp(inst)) {
950         PEEPHOLE_IS_APPLIED(visitor, inst);
951     }
952 }
953 
VisitCompare(GraphVisitor * v,Inst * inst)954 void Peepholes::VisitCompare(GraphVisitor *v, Inst *inst)
955 {
956     auto visitor = static_cast<Peepholes *>(v);
957     // Try to replace Compare with any type to bool type
958     if (visitor->TrySimplifyCompareAnyType(inst)) {
959         PEEPHOLE_IS_APPLIED(visitor, inst);
960     }
961 
962     /* skip preheader Compare processing until unrolling is done */
963     auto graph = inst->GetBasicBlock()->GetGraph();
964     if (!graph->IsBytecodeOptimizer() && g_options.IsCompilerDeferPreheaderTransform() && !graph->IsUnrollComplete()) {
965         auto bb = inst->GetBasicBlock();
966         if (bb->IsLoopPreHeader() && inst->HasSingleUser() && inst->GetFirstUser()->GetInst() == bb->GetLastInst() &&
967             bb->GetLastInst()->GetOpcode() == Opcode::IfImm) {
968             return;
969         }
970     }
971 
972     if (ConstFoldingCompare(inst)) {
973         PEEPHOLE_IS_APPLIED(visitor, inst);
974         return;
975     }
976 
977     bool osrBlockedPeephole = false;
978     if (visitor->TrySimplifyCompareWithBoolInput(inst, &osrBlockedPeephole)) {
979         if (osrBlockedPeephole) {
980             return;
981         }
982         PEEPHOLE_IS_APPLIED(visitor, inst);
983     } else if (visitor->TrySimplifyCmpCompareWithZero(inst, &osrBlockedPeephole)) {
984         if (osrBlockedPeephole) {
985             return;
986         }
987         PEEPHOLE_IS_APPLIED(visitor, inst);
988     } else if (visitor->TrySimplifyCompareAndZero(inst, &osrBlockedPeephole)) {
989         if (osrBlockedPeephole) {
990             return;
991         }
992         PEEPHOLE_IS_APPLIED(visitor, inst);
993     } else if (TrySimplifyCompareLenArrayWithZero(inst)) {
994         PEEPHOLE_IS_APPLIED(visitor, inst);
995     } else if (visitor->TrySimplifyTestEqualInputs(inst)) {
996         PEEPHOLE_IS_APPLIED(visitor, inst);
997     }
998 }
999 
TrySimplifyCompareAnyTypeCase1(Inst * inst,Inst * input0,Inst * input1)1000 bool Peepholes::TrySimplifyCompareAnyTypeCase1(Inst *inst, Inst *input0, Inst *input1)
1001 {
1002     auto cmpInst = inst->CastToCompare();
1003 
1004     // 2.any  CastValueToAnyType BOOLEAN_TYPE v0 -> (v4)
1005     // 3.any  CastValueToAnyType BOOLEAN_TYPE v1 -> (v4)
1006     // 4.     Compare EQ/NE any    v2, v3
1007     // =======>
1008     // 4.     Compare EQ/NE bool   v0, v1
1009     if (input0->CastToCastValueToAnyType()->GetAnyType() != input1->CastToCastValueToAnyType()->GetAnyType()) {
1010         return false;
1011     }
1012     if (SkipThisPeepholeInOSR(cmpInst, input0->GetInput(0).GetInst()) ||
1013         SkipThisPeepholeInOSR(cmpInst, input1->GetInput(0).GetInst())) {
1014         return false;
1015     }
1016     cmpInst->SetOperandsType(DataType::BOOL);
1017     cmpInst->SetInput(0, input0->GetInput(0).GetInst());
1018     cmpInst->SetInput(1, input1->GetInput(0).GetInst());
1019     return true;
1020 }
1021 
TrySimplifyCompareAnyTypeCase2(Inst * inst,Inst * input0,Inst * input1)1022 bool Peepholes::TrySimplifyCompareAnyTypeCase2(Inst *inst, Inst *input0, Inst *input1)
1023 {
1024     auto graph = inst->GetBasicBlock()->GetGraph();
1025     auto cmpInst = inst->CastToCompare();
1026     auto cc = cmpInst->GetCc();
1027     auto ifImm = input1->CastToConstant()->GetRawValue();
1028     auto runtime = graph->GetRuntime();
1029     uint64_t newConst;
1030 
1031     // 3.any  CastValueToAnyType BOOLEAN_TYPE v2 -> (v4)
1032     // 4.     Compare EQ/NE any    v3, DYNAMIC_TRUE/FALSE
1033     // =======>
1034     // 4.     Compare EQ/NE bool   v2, 0x1/0x0
1035     if (SkipThisPeepholeInOSR(cmpInst, input0->GetInput(0).GetInst())) {
1036         return false;
1037     }
1038     if (ifImm == runtime->GetDynamicPrimitiveFalse()) {
1039         newConst = 0;
1040     } else if (ifImm == runtime->GetDynamicPrimitiveTrue()) {
1041         newConst = 1;
1042     } else {
1043         // In this case, we are comparing the dynamic boolean type with not boolean constant.
1044         // So the Compare EQ/NE alwayes false/true.
1045         // In this case, we can change the Compare to Constant instruction.
1046         // NOTE! It is assumed that there is only one Boolean type for each dynamic language.
1047         // Support for multiple Boolean types must be maintained separately.
1048         if (cc != CC_EQ && cc != CC_NE) {
1049             return false;
1050         }
1051         // We create constant, so we don't need to check SaveStateOSR between insts
1052         cmpInst->ReplaceUsers(graph->FindOrCreateConstant<uint64_t>(cc == CC_NE ? 1 : 0));
1053         return true;
1054     }
1055     cmpInst->SetOperandsType(DataType::BOOL);
1056     cmpInst->SetInput(0, input0->GetInput(0).GetInst());
1057     cmpInst->SetInput(1, graph->FindOrCreateConstant(newConst));
1058     return true;
1059 }
1060 
TrySimplifyCompareAnyType(Inst * inst)1061 bool Peepholes::TrySimplifyCompareAnyType(Inst *inst)
1062 {
1063     auto graph = inst->GetBasicBlock()->GetGraph();
1064     if (graph->IsBytecodeOptimizer()) {
1065         return false;
1066     }
1067     auto cmpInst = inst->CastToCompare();
1068     if (cmpInst->GetOperandsType() != DataType::ANY) {
1069         return false;
1070     }
1071 
1072     auto input0 = cmpInst->GetInput(0).GetInst();
1073     auto input1 = cmpInst->GetInput(1).GetInst();
1074     auto cc = cmpInst->GetCc();
1075     if (input0 == input1 && (cc == CC_EQ || cc == CC_NE)) {
1076         // We create constant, so we don't need to check SaveStateOSR between insts
1077         cmpInst->ReplaceUsers(graph->FindOrCreateConstant<uint64_t>(cc == CC_EQ ? 1 : 0));
1078         return true;
1079     }
1080 
1081     if (input0->GetOpcode() != Opcode::CastValueToAnyType) {
1082         return false;
1083     }
1084     if (AnyBaseTypeToDataType(input0->CastToCastValueToAnyType()->GetAnyType()) != DataType::BOOL) {
1085         return false;
1086     }
1087 
1088     if (input1->GetOpcode() == Opcode::CastValueToAnyType) {
1089         return TrySimplifyCompareAnyTypeCase1(inst, input0, input1);
1090     }
1091     if (!input1->IsConst()) {
1092         return false;
1093     }
1094 
1095     return TrySimplifyCompareAnyTypeCase2(inst, input0, input1);
1096 }
1097 
1098 // This VisitIf is using only for compile IRTOC
VisitIf(GraphVisitor * v,Inst * inst)1099 void Peepholes::VisitIf([[maybe_unused]] GraphVisitor *v, Inst *inst)
1100 {
1101     // 2.     Constant           0x0
1102     // 3.i64  And                v0, v1
1103     // 4.     If EQ/NE i64       v3, v2
1104     // =======>
1105     // 4.     If TST_EQ/TST_NE   v1, v2
1106     auto visitor = static_cast<Peepholes *>(v);
1107     if (visitor->GetGraph()->IsBytecodeOptimizer()) {
1108         return;
1109     }
1110     auto ifImm = inst->CastToIf();
1111     if (ifImm->GetCc() != CC_EQ && ifImm->GetCc() != CC_NE) {
1112         return;
1113     }
1114 
1115     auto lhs = ifImm->GetInput(0).GetInst();
1116     auto rhs = ifImm->GetInput(1).GetInst();
1117     Inst *andInput {nullptr};
1118     if (lhs->GetOpcode() == Opcode::And && IsZeroConstantOrNullPtr(rhs)) {
1119         andInput = lhs;
1120     } else if (rhs->GetOpcode() == Opcode::And && IsZeroConstantOrNullPtr(lhs)) {
1121         andInput = rhs;
1122     } else {
1123         return;
1124     }
1125     if (!andInput->HasSingleUser()) {
1126         return;
1127     }
1128 
1129     auto newInput0 = andInput->GetInput(0).GetInst();
1130     auto newInput1 = andInput->GetInput(1).GetInst();
1131     if (SkipThisPeepholeInOSR(ifImm, newInput0) || SkipThisPeepholeInOSR(ifImm, newInput1)) {
1132         return;
1133     }
1134     ifImm->SetCc(ifImm->GetCc() == CC_EQ ? CC_TST_EQ : CC_TST_NE);
1135     ifImm->SetInput(0, newInput0);
1136     ifImm->SetInput(1, newInput1);
1137     PEEPHOLE_IS_APPLIED(visitor, inst);
1138 }
1139 
TryReplaceCompareAnyType(Inst * inst,Inst * dominateInst)1140 static bool TryReplaceCompareAnyType(Inst *inst, Inst *dominateInst)
1141 {
1142     auto instType = inst->CastToCompareAnyType()->GetAnyType();
1143     auto dominateType = dominateInst->GetAnyType();
1144     profiling::AnyInputType dominateAllowedType {};
1145     if (dominateInst->GetOpcode() == Opcode::AnyTypeCheck) {
1146         dominateAllowedType = dominateInst->CastToAnyTypeCheck()->GetAllowedInputType();
1147     } else {
1148         ASSERT(dominateInst->GetOpcode() == Opcode::CastValueToAnyType);
1149     }
1150     ASSERT(inst->CastToCompareAnyType()->GetAllowedInputType() == profiling::AnyInputType::DEFAULT);
1151     auto graph = inst->GetBasicBlock()->GetGraph();
1152     auto language = graph->GetRuntime()->GetMethodSourceLanguage(graph->GetMethod());
1153     auto res = IsAnyTypeCanBeSubtypeOf(language, instType, dominateType, profiling::AnyInputType::DEFAULT,
1154                                        dominateAllowedType);
1155     if (!res) {
1156         // We cannot compare types in compile-time
1157         return false;
1158     }
1159     auto constValue = res.value();
1160     auto cnst = inst->GetBasicBlock()->GetGraph()->FindOrCreateConstant(constValue);
1161     // We replace constant, so we don't need to check SaveStateOSR between insts
1162     inst->ReplaceUsers(cnst);
1163     return true;
1164 }
1165 
VisitCompareAnyType(GraphVisitor * v,Inst * inst)1166 void Peepholes::VisitCompareAnyType(GraphVisitor *v, Inst *inst)
1167 {
1168     auto visitor = static_cast<Peepholes *>(v);
1169     auto input = inst->GetInput(0).GetInst();
1170     // from
1171     //  2.any CastValueToAnyType TYPE1 -> (v3)
1172     //  3.b CompareAnyType TYPE2 -> (...)
1173     // to
1174     //  4. Constant (TYPE1 == TYPE2)  ->(...)
1175     if (input->GetOpcode() == Opcode::CastValueToAnyType || input->GetOpcode() == Opcode::AnyTypeCheck) {
1176         if (TryReplaceCompareAnyType(inst, input)) {
1177             PEEPHOLE_IS_APPLIED(visitor, inst);
1178             return;
1179         }
1180     }
1181     // from
1182     //  2.any AnyTypeCheck v1 TYPE1 -> (...)
1183     //  3.b CompareAnyType v1 TYPE2 -> (...)
1184     // to
1185     //  4. Constant (TYPE1 == TYPE2)  ->(...)
1186     for (auto &user : input->GetUsers()) {
1187         auto userInst = user.GetInst();
1188         if (userInst != inst && userInst->GetOpcode() == Opcode::AnyTypeCheck && userInst->IsDominate(inst) &&
1189             TryReplaceCompareAnyType(inst, userInst)) {
1190             PEEPHOLE_IS_APPLIED(visitor, inst);
1191             return;
1192         }
1193     }
1194 }
1195 
VisitCastCase1(GraphVisitor * v,Inst * inst)1196 void Peepholes::VisitCastCase1([[maybe_unused]] GraphVisitor *v, Inst *inst)
1197 {
1198     // case 1:
1199     // remove redundant cast, when source type is equal with target type
1200     auto input = inst->GetInput(0).GetInst();
1201     if (SkipThisPeepholeInOSR(inst, input)) {
1202         return;
1203     }
1204     inst->ReplaceUsers(input);
1205     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1206 }
1207 
VisitCastCase2(GraphVisitor * v,Inst * inst)1208 void Peepholes::VisitCastCase2([[maybe_unused]] GraphVisitor *v, Inst *inst)
1209 {
1210     // case 2:
1211     // remove redundant cast, when cast from source to target and back
1212     // for bytecode optimizer, this operation may cause mistake for float32-converter pass
1213     auto input = inst->GetInput(0).GetInst();
1214     auto prevType = input->GetType();
1215     auto currType = inst->GetType();
1216     auto graph = inst->GetBasicBlock()->GetGraph();
1217     auto arch = graph->GetArch();
1218     auto origInst = input->GetInput(0).GetInst();
1219     auto origType = origInst->GetType();
1220     if (currType == origType && DataType::GetTypeSize(prevType, arch) > DataType::GetTypeSize(currType, arch)) {
1221         if (SkipThisPeepholeInOSR(inst, origInst)) {
1222             return;
1223         }
1224         inst->ReplaceUsers(origInst);
1225         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1226         return;
1227     }
1228     // case 2.1:
1229     // join two sequent narrowing integer casts, e.g:
1230     // replace
1231     // cast i64toi32
1232     // cast i32toi16
1233     // with
1234     // cast i64toi16
1235     if (ApplyForCastJoin(inst, input, origInst, arch)) {
1236         auto cast = inst->CastToCast();
1237         if (SkipThisPeepholeInOSR(cast, origInst)) {
1238             return;
1239         }
1240         cast->SetOperandsType(origInst->GetType());
1241         cast->SetInput(0, origInst);
1242         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1243         return;
1244     }
1245 }
1246 
VisitCastCase3(GraphVisitor * v,Inst * inst)1247 void Peepholes::VisitCastCase3([[maybe_unused]] GraphVisitor *v, Inst *inst)
1248 {
1249     auto input = inst->GetInput(0).GetInst();
1250     auto currType = inst->GetType();
1251     auto graph = inst->GetBasicBlock()->GetGraph();
1252     auto arch = graph->GetArch();
1253 
1254     // case 3:
1255     // i8.Cast(v1 & 0xff)        = i8.Cast(u8.Cast(v1))   = i8.Cast(v1)
1256     // i16.Cast(v1 & 0xffff)     = i16.Cast(u16.Cast(v1)) = i16.Cast(v1)
1257     // i32.Cast(v1 & 0xffffffff) = i32.Cast(u32.Cast(v1)) = i32.Cast(v1)
1258     auto op0 = input->GetInput(0).GetInst();
1259     if (graph->IsBytecodeOptimizer() && !IsCastAllowedInBytecode(op0)) {
1260         return;
1261     }
1262     auto op1 = input->GetInput(1).GetInst();
1263     auto typeSize = DataType::GetTypeSize(currType, arch);
1264     if (op1->IsConst() && typeSize < DOUBLE_WORD_SIZE) {
1265         auto val = op1->CastToConstant()->GetIntValue();
1266         auto mask = (1ULL << typeSize) - 1;
1267         if ((val & mask) == mask) {
1268             if (SkipThisPeepholeInOSR(inst, op0)) {
1269                 return;
1270             }
1271             inst->SetInput(0, op0);
1272             inst->CastToCast()->SetOperandsType(op0->GetType());
1273             PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1274             return;
1275         }
1276     }
1277 }
1278 
VisitCast(GraphVisitor * v,Inst * inst)1279 void Peepholes::VisitCast([[maybe_unused]] GraphVisitor *v, Inst *inst)
1280 {
1281     if (ConstFoldingCast(inst)) {
1282         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1283         return;
1284     }
1285     auto input = inst->GetInput(0).GetInst();
1286     auto prevType = input->GetType();
1287     auto currType = inst->GetType();
1288     auto graph = inst->GetBasicBlock()->GetGraph();
1289 
1290     if (prevType == currType) {
1291         VisitCastCase1(v, inst);
1292         return;
1293     }
1294 
1295     if (!graph->IsBytecodeOptimizer() && input->GetOpcode() == Opcode::Cast) {
1296         VisitCastCase2(v, inst);
1297         return;
1298     }
1299 
1300     if (input->GetOpcode() == Opcode::And && DataType::GetCommonType(currType) == DataType::INT64) {
1301         VisitCastCase3(v, inst);
1302         return;
1303     }
1304 }
1305 
VisitLenArray(GraphVisitor * v,Inst * inst)1306 void Peepholes::VisitLenArray(GraphVisitor *v, Inst *inst)
1307 {
1308     auto graph = static_cast<Peepholes *>(v)->GetGraph();
1309     if (graph->IsBytecodeOptimizer()) {
1310         return;
1311     }
1312     // 1. .... ->{v2}
1313     // 2. NewArray v1 ->{v3,..}
1314     // 3. LenArray v2 ->{v4, v5...}
1315     // ===>
1316     // 1. .... ->{v2, v4, v5, ...}
1317     // 2. NewArray v1 ->{v3,..}
1318     // 3. LenArray v2
1319     auto input = inst->GetDataFlowInput(inst->GetInput(0).GetInst());
1320     if (input->GetOpcode() == Opcode::NewArray) {
1321         auto arraySize = input->GetDataFlowInput(input->GetInput(NewArrayInst::INDEX_SIZE).GetInst());
1322         // We can't restore array_size register if it was defined out of OSR loop
1323         if (SkipThisPeepholeInOSR(inst, arraySize)) {
1324             return;
1325         }
1326         inst->ReplaceUsers(arraySize);
1327         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1328     }
1329     if (static_cast<Peepholes *>(v)->OptimizeLenArrayForMultiArray(inst, input, 0)) {
1330         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1331     }
1332 }
1333 
VisitPhi(GraphVisitor * v,Inst * inst)1334 void Peepholes::VisitPhi([[maybe_unused]] GraphVisitor *v, Inst *inst)
1335 {
1336     // 2.type  Phi v0, v1 -> v4, v5, ...
1337     // 3.type  Phi v0, v1 -> v5, v6, ...
1338     // ===>
1339     // 2.type  Phi v0, v1 -> v4, v5, v6, ...
1340     // 3.type  Phi v0, v1
1341     if (!inst->GetUsers().Empty()) {
1342         auto block = inst->GetBasicBlock();
1343         auto phi = inst->CastToPhi();
1344         for (auto otherPhi : block->PhiInsts()) {
1345             if (IsPhiUnionPossible(phi, otherPhi->CastToPhi())) {
1346                 // In check above checked, that phi insts in same BB, so no problem with SaveStateOSR
1347                 otherPhi->ReplaceUsers(phi);
1348                 PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1349             }
1350         }
1351     }
1352 
1353     if (TryEliminatePhi(inst->CastToPhi())) {
1354         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1355     }
1356 }
1357 
VisitSqrt(GraphVisitor * v,Inst * inst)1358 void Peepholes::VisitSqrt([[maybe_unused]] GraphVisitor *v, Inst *inst)
1359 {
1360     if (ConstFoldingSqrt(inst)) {
1361         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1362         return;
1363     }
1364 }
1365 
VisitIsInstance(GraphVisitor * v,Inst * inst)1366 void Peepholes::VisitIsInstance(GraphVisitor *v, Inst *inst)
1367 {
1368     if (ObjectTypeCheckElimination::TryEliminateIsInstance(inst)) {
1369         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1370         return;
1371     }
1372 }
1373 
VisitCastAnyTypeValue(GraphVisitor * v,Inst * inst)1374 void Peepholes::VisitCastAnyTypeValue(GraphVisitor *v, Inst *inst)
1375 {
1376     if (inst->GetUsers().Empty()) {
1377         // Peepholes can be run before cleanup phase.
1378         return;
1379     }
1380 
1381     auto *cav = inst->CastToCastAnyTypeValue();
1382     auto *cavInput = cav->GetDataFlowInput(0);
1383     if (cavInput->GetOpcode() == Opcode::CastValueToAnyType) {
1384         auto cva = cavInput->CastToCastValueToAnyType();
1385         auto valueInst = cva->GetInput(0).GetInst();
1386         if (SkipThisPeepholeInOSR(cav, valueInst)) {
1387             return;
1388         }
1389         if (cva->GetAnyType() == cav->GetAnyType()) {
1390             // 1. .... -> v2
1391             // 2. CastValueToAnyType v1 -> v3
1392             // 3. AnyTypeCheck v2 -> v3
1393             // 4. CastAnyTypeValue v3 -> v5
1394             // 5. ...
1395             // ===>
1396             // 1. .... -> v2, v5
1397             // 2. CastValueToAnyType v1 -> v3
1398             // 3. AnyTypeCheck v2 -> v3
1399             // 4. CastAnyTypeValue v3
1400             // 5. ...
1401             cav->ReplaceUsers(valueInst);
1402             PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1403             return;
1404         }
1405         auto inputType = valueInst->GetType();
1406         auto dstType = cav->GetType();
1407         auto isDoubleToInt = dstType == DataType::INT32 && inputType == DataType::FLOAT64;
1408         if (IsTypeNumeric(inputType) && IsTypeNumeric(dstType) && (!isDoubleToInt || cav->IsIntegerWasSeen())) {
1409             // 1. .... -> v2
1410             // 2. CastValueToAnyType v1 -> v3
1411             // 3. AnyTypeCheck v2 -> v3
1412             // 4. CastAnyTypeValue v3 -> v5
1413             // 5. ...
1414             // ===>
1415             // 1. .... -> v2, v6
1416             // 2. CastValueToAnyType v1 -> v3
1417             // 3. AnyTypeCheck v2 -> v3
1418             // 4. CastAnyTypeValue v3
1419             // 6. Cast v1 -> v5
1420             // 5. ...
1421             auto cast = CreateAndInsertInst(Opcode::Cast, cav, valueInst);
1422             cast->SetType(dstType);
1423             cast->CastToCast()->SetOperandsType(inputType);
1424             PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1425         }
1426     }
1427 }
1428 
VisitCastValueToAnyType(GraphVisitor * v,Inst * inst)1429 void Peepholes::VisitCastValueToAnyType(GraphVisitor *v, Inst *inst)
1430 {
1431     auto graph = inst->GetBasicBlock()->GetGraph();
1432     auto input = inst->GetInput(0).GetInst();
1433     auto anyType = inst->CastToCastValueToAnyType()->GetAnyType();
1434 
1435     if (input->GetOpcode() == Opcode::CastAnyTypeValue) {
1436         auto cav = input->CastToCastAnyTypeValue();
1437         auto anyInput = cav->GetInput(0).GetInst();
1438         if (SkipThisPeepholeInOSR(inst, anyInput)) {
1439             return;
1440         }
1441         if (anyType == cav->GetAnyType() && cav->GetAllowedInputType() == profiling::AnyInputType::DEFAULT) {
1442             // 1. ... -> v2
1443             // 2. CastAnyTypeValue v1 -> v3
1444             // 3. CastValueToAnyType v2 -> v4
1445             // 4. ...
1446             // ===>
1447             // 1. ... -> v2, v4
1448             // 2. CastAnyTypeValue v1 -> v3
1449             // 3. CastValueToAnyType v2
1450             // 4. ...
1451             inst->ReplaceUsers(anyInput);
1452             PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1453             return;
1454         }
1455     }
1456 
1457     if (graph->IsBytecodeOptimizer() || graph->IsOsrMode()) {
1458         // Find way to enable it in OSR mode.
1459         return;
1460     }
1461 
1462     auto baseType = AnyBaseTypeToDataType(anyType);
1463     // We propogate not INT types in Lowering
1464     if (baseType != DataType::INT32) {
1465         return;
1466     }
1467     // from
1468     // 2.any CastValueToAnyType INT_TYPE v1 -> (v3)
1469     // 3     SaveState                   v2(acc)
1470     //
1471     // to
1472     // 3     SaveState                   v1(acc)
1473     bool isApplied = false;
1474     for (auto it = inst->GetUsers().begin(); it != inst->GetUsers().end();) {
1475         auto userInst = it->GetInst();
1476         if (userInst->IsSaveState()) {
1477             userInst->SetInput(it->GetIndex(), input);
1478             it = inst->GetUsers().begin();
1479             isApplied = true;
1480         } else {
1481             ++it;
1482         }
1483     }
1484     if (isApplied) {
1485         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1486     }
1487 }
1488 
TryPrepareEliminatePrecedingStoreOpcCast(Inst * inst,Arch arch,uint64_t & imm)1489 static bool TryPrepareEliminatePrecedingStoreOpcCast(Inst *inst, Arch arch, uint64_t &imm)
1490 {
1491     auto size = DataType::GetTypeSize(inst->GetType(), arch);
1492     if (size >= DOUBLE_WORD_SIZE) {
1493         return false;
1494     }
1495     auto inputTypeMismatch = IsInputTypeMismatch(inst, 0, arch);
1496 #ifndef NDEBUG
1497     ASSERT(!inputTypeMismatch);
1498 #else
1499     if (inputTypeMismatch) {
1500         return false;
1501     }
1502 #endif
1503     imm = (1ULL << size) - 1;
1504     return true;
1505 }
1506 
1507 template <typename T>
EliminateInstPrecedingStore(GraphVisitor * v,Inst * inst)1508 void Peepholes::EliminateInstPrecedingStore(GraphVisitor *v, Inst *inst)
1509 {
1510     auto graph = static_cast<Peepholes *>(v)->GetGraph();
1511     if (graph->IsBytecodeOptimizer()) {
1512         return;
1513     }
1514     auto arch = graph->GetArch();
1515     auto typeSize = DataType::GetTypeSize(inst->GetType(), arch);
1516     if (DataType::GetCommonType(inst->GetType()) == DataType::INT64 && typeSize < DOUBLE_WORD_SIZE) {
1517         auto storeValueInst = inst->GetInput(T::STORED_INPUT_INDEX).GetInst();
1518         auto mask = (1ULL << typeSize) - 1;
1519         uint64_t imm;
1520 
1521         switch (storeValueInst->GetOpcode()) {
1522             case Opcode::And: {
1523                 auto inputInst1 = storeValueInst->GetInput(1).GetInst();
1524                 if (!inputInst1->IsConst()) {
1525                     return;
1526                 }
1527                 imm = inputInst1->CastToConstant()->GetIntValue();
1528                 break;
1529             }
1530             case Opcode::Cast: {
1531                 if (!TryPrepareEliminatePrecedingStoreOpcCast(storeValueInst, arch, imm)) {
1532                     return;
1533                 }
1534                 break;
1535             }
1536             default:
1537                 return;
1538         }
1539 
1540         auto inputInst = storeValueInst->GetInput(0).GetInst();
1541         if (DataType::GetCommonType(inputInst->GetType()) != DataType::INT64) {
1542             return;
1543         }
1544 
1545         if ((imm & mask) == mask) {
1546             if (SkipThisPeepholeInOSR(inst, inputInst)) {
1547                 return;
1548             }
1549             inst->ReplaceInput(storeValueInst, inputInst);
1550             PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1551         }
1552     }
1553 }
1554 
VisitStore(GraphVisitor * v,Inst * inst)1555 void Peepholes::VisitStore([[maybe_unused]] GraphVisitor *v, Inst *inst)
1556 {
1557     ASSERT(inst->GetOpcode() == Opcode::Store);
1558     EliminateInstPrecedingStore<StoreMemInst>(v, inst);
1559 }
1560 
VisitStoreObject(GraphVisitor * v,Inst * inst)1561 void Peepholes::VisitStoreObject([[maybe_unused]] GraphVisitor *v, Inst *inst)
1562 {
1563     ASSERT(inst->GetOpcode() == Opcode::StoreObject);
1564 
1565     auto visitor = static_cast<Peepholes *>(v);
1566     if (visitor->TryOptimizeBoxedLoadStoreObject(inst)) {
1567         PEEPHOLE_IS_APPLIED(visitor, inst);
1568     }
1569 
1570     EliminateInstPrecedingStore<StoreObjectInst>(v, inst);
1571 }
1572 
VisitStoreStatic(GraphVisitor * v,Inst * inst)1573 void Peepholes::VisitStoreStatic([[maybe_unused]] GraphVisitor *v, Inst *inst)
1574 {
1575     ASSERT(inst->GetOpcode() == Opcode::StoreStatic);
1576     EliminateInstPrecedingStore<StoreStaticInst>(v, inst);
1577 }
1578 
TryRemoveOverflowCheck(Inst * inst)1579 void Peepholes::TryRemoveOverflowCheck(Inst *inst)
1580 {
1581     auto block = inst->GetBasicBlock();
1582     auto markerHolder = MarkerHolder(block->GetGraph());
1583     if (CanRemoveOverflowCheck(inst, markerHolder.GetMarker())) {
1584         PEEPHOLE_IS_APPLIED(this, inst);
1585         block->RemoveOverflowCheck(inst);
1586         return;
1587     }
1588 }
1589 
VisitAddOverflowCheck(GraphVisitor * v,Inst * inst)1590 void Peepholes::VisitAddOverflowCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1591 {
1592     ASSERT(inst->GetOpcode() == Opcode::AddOverflowCheck);
1593     auto visitor = static_cast<Peepholes *>(v);
1594     visitor->TrySwapInputs(inst);
1595     if (visitor->TrySimplifyAddSubWithZeroInput(inst)) {
1596         inst->ClearFlag(inst_flags::NO_DCE);
1597     } else {
1598         visitor->TryRemoveOverflowCheck(inst);
1599     }
1600 }
1601 
VisitSubOverflowCheck(GraphVisitor * v,Inst * inst)1602 void Peepholes::VisitSubOverflowCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1603 {
1604     ASSERT(inst->GetOpcode() == Opcode::SubOverflowCheck);
1605     auto visitor = static_cast<Peepholes *>(v);
1606     if (visitor->TrySimplifyAddSubWithZeroInput(inst)) {
1607         inst->ClearFlag(inst_flags::NO_DCE);
1608     } else {
1609         visitor->TryRemoveOverflowCheck(inst);
1610     }
1611 }
1612 
VisitNegOverflowAndZeroCheck(GraphVisitor * v,Inst * inst)1613 void Peepholes::VisitNegOverflowAndZeroCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1614 {
1615     ASSERT(inst->GetOpcode() == Opcode::NegOverflowAndZeroCheck);
1616     static_cast<Peepholes *>(v)->TryRemoveOverflowCheck(inst);
1617 }
1618 
1619 // This function check that we can merge two Phi instructions in one basic block.
IsPhiUnionPossible(PhiInst * phi1,PhiInst * phi2)1620 bool Peepholes::IsPhiUnionPossible(PhiInst *phi1, PhiInst *phi2)
1621 {
1622     ASSERT(phi1->GetBasicBlock() == phi2->GetBasicBlock() && phi1->GetInputsCount() == phi2->GetInputsCount());
1623     if (phi1 == phi2 || phi1->GetType() != phi2->GetType()) {
1624         return false;
1625     }
1626     for (auto predBlock : phi1->GetBasicBlock()->GetPredsBlocks()) {
1627         if (phi1->GetPhiDataflowInput(predBlock) != phi2->GetPhiDataflowInput(predBlock)) {
1628             return false;
1629         }
1630     }
1631     if (phi1->GetBasicBlock()->GetGraph()->IsOsrMode()) {
1632         // Values in vregs for phi1 and phi2 may be different in interpreter mode,
1633         // so we don't merge phis if they have SaveStateOsr users
1634         for (auto phi : {phi1, phi2}) {
1635             for (auto &user : phi->GetUsers()) {
1636                 if (user.GetInst()->GetOpcode() == Opcode::SaveStateOsr) {
1637                     return false;
1638                 }
1639             }
1640         }
1641     }
1642     return true;
1643 }
1644 
1645 // Create new instruction instead of current inst
CreateAndInsertInst(Opcode newOpc,Inst * currInst,Inst * input0,Inst * input1)1646 Inst *Peepholes::CreateAndInsertInst(Opcode newOpc, Inst *currInst, Inst *input0, Inst *input1)
1647 {
1648     auto bb = currInst->GetBasicBlock();
1649     auto graph = bb->GetGraph();
1650     auto newInst = graph->CreateInst(newOpc);
1651     newInst->SetType(currInst->GetType());
1652     newInst->SetPc(currInst->GetPc());
1653 #ifdef PANDA_COMPILER_DEBUG_INFO
1654     newInst->SetCurrentMethod(currInst->GetCurrentMethod());
1655 #endif
1656     // It is a wrapper, so we don't do logic check for SaveStateOSR
1657     currInst->ReplaceUsers(newInst);
1658     newInst->SetInput(0, input0);
1659     if (input1 != nullptr) {
1660         newInst->SetInput(1, input1);
1661     }
1662     bb->InsertAfter(newInst, currInst);
1663     return newInst;
1664 }
1665 
1666 // Try put constant in second input
TrySwapInputs(Inst * inst)1667 void Peepholes::TrySwapInputs(Inst *inst)
1668 {
1669     [[maybe_unused]] auto opc = inst->GetOpcode();
1670     ASSERT(opc == Opcode::Add || opc == Opcode::And || opc == Opcode::Or || opc == Opcode::Xor || opc == Opcode::Min ||
1671            opc == Opcode::Max || opc == Opcode::Mul || opc == Opcode::AddOverflowCheck);
1672     if (inst->GetInput(0).GetInst()->IsConst()) {
1673         inst->SwapInputs();
1674         PEEPHOLE_IS_APPLIED(this, inst);
1675     }
1676 }
1677 
TrySimplifyShifts(Inst * inst)1678 void Peepholes::TrySimplifyShifts(Inst *inst)
1679 {
1680     auto opc = inst->GetOpcode();
1681     ASSERT(opc == Opcode::Shl || opc == Opcode::Shr || opc == Opcode::AShr);
1682     auto block = inst->GetBasicBlock();
1683     auto graph = block->GetGraph();
1684     auto input0 = inst->GetInput(0).GetInst();
1685     auto input1 = inst->GetInput(1).GetInst();
1686     if (input1->IsConst()) {
1687         auto cnst = static_cast<ConstantInst *>(input1);
1688         // Zero shift
1689         // 2. shl/shr/ashr v1, 0 -> {...}
1690         // 3. Some inst v2
1691         // ===>
1692         // 2. shl/shr/ashr v1, 0 -> {}
1693         // 3. Some inst v1
1694         if (cnst->IsEqualConst(0)) {
1695             if (SkipThisPeepholeInOSR(inst, input0)) {
1696                 return;
1697             }
1698             inst->ReplaceUsers(input0);
1699             PEEPHOLE_IS_APPLIED(this, inst);
1700             return;
1701         }
1702         // Repeated arithmetic with constants
1703         // 2. shl/shr/ashr v1, const1 -> {v3, ...}
1704         // 3. shl/shr/ashr v2, const2 -> {...}
1705         // ===>
1706         // 2. shl/shr/ashr v1, const1 -> {...}
1707         // 3. shl/shr/ashr v1, const2 + const1 -> {...}
1708         bool osrBlockedPeephole = false;
1709         if (opc == input0->GetOpcode() && TryCombineShiftConst(inst, cnst, &osrBlockedPeephole)) {
1710             PEEPHOLE_IS_APPLIED(this, inst);
1711             return;
1712         }
1713         if (osrBlockedPeephole) {
1714             return;
1715         }
1716         uint64_t sizeMask = DataType::GetTypeSize(inst->GetType(), graph->GetArch()) - 1;
1717         auto cnstValue = cnst->GetIntValue();
1718         // Shift by a constant greater than the type size
1719         // 2. shl/shr/ashr v1, big_const -> {...}
1720         // ===>
1721         // 2. shl/shr/ashr v1, size_mask & big_const -> {...}
1722         if (graph->IsBytecodeOptimizer() && IsInt32Bit(inst->GetType())) {
1723             sizeMask = static_cast<uint32_t>(sizeMask);
1724             cnstValue = static_cast<uint32_t>(cnstValue);
1725         }
1726         if (sizeMask < cnstValue) {
1727             ConstantInst *shift = ConstFoldingCreateIntConst(inst, sizeMask & cnstValue);
1728             inst->SetInput(1, shift);
1729             PEEPHOLE_IS_APPLIED(this, inst);
1730             return;
1731         }
1732     }
1733 }
1734 
TrySimplifyAddSubWithZeroInput(Inst * inst)1735 bool Peepholes::TrySimplifyAddSubWithZeroInput(Inst *inst)
1736 {
1737     auto input0 = inst->GetInput(0).GetInst();
1738     auto input1 = inst->GetInput(1).GetInst();
1739     if (input1->IsConst()) {
1740         auto cnst = input1->CastToConstant();
1741         if (cnst->IsEqualConstAllTypes(0)) {
1742             if (SkipThisPeepholeInOSR(inst, input0)) {
1743                 return false;
1744             }
1745             inst->ReplaceUsers(input0);
1746             PEEPHOLE_IS_APPLIED(this, inst);
1747             return true;
1748         }
1749     }
1750     return false;
1751 }
1752 
TrySimplifyAddSubWithConstInput(Inst * inst)1753 bool Peepholes::TrySimplifyAddSubWithConstInput(Inst *inst)
1754 {
1755     if (TrySimplifyAddSubWithZeroInput(inst)) {
1756         return true;
1757     }
1758     auto input0 = inst->GetInput(0).GetInst();
1759     auto input1 = inst->GetInput(1).GetInst();
1760     if (input1->IsConst()) {
1761         auto cnst = input1->CastToConstant();
1762         if ((input0->GetOpcode() == Opcode::Add || input0->GetOpcode() == Opcode::Sub)) {
1763             bool osrBlockedPeephole = false;
1764             if (TryCombineAddSubConst(inst, cnst, &osrBlockedPeephole)) {
1765                 PEEPHOLE_IS_APPLIED(this, inst);
1766                 return true;
1767             }
1768             if (osrBlockedPeephole) {
1769                 return true;
1770             }
1771         }
1772     } else if (input0->IsConst() && inst->GetOpcode() == Opcode::Sub && !IsFloatType(inst->GetType())) {
1773         // Fold `sub 0, v1` into `neg v1`.
1774         auto cnst = input0->CastToConstant();
1775         if (cnst->IsEqualConstAllTypes(0)) {
1776             // There can't be a SaveStateOSR between inst(sub) and new inst(neg), so we don't have a check
1777             CreateAndInsertInst(Opcode::Neg, inst, input1);
1778             PEEPHOLE_IS_APPLIED(this, inst);
1779             return true;
1780         }
1781     }
1782     return false;
1783 }
1784 
1785 template <Opcode OPC, int IDX>
TrySimplifyAddSub(Inst * inst,Inst * input0,Inst * input1)1786 void Peepholes::TrySimplifyAddSub(Inst *inst, Inst *input0, Inst *input1)
1787 {
1788     if (input0->GetOpcode() == OPC && input0->GetInput(1 - IDX).GetInst() == input1) {
1789         auto prevInput = input0->GetInput(IDX).GetInst();
1790         if (inst->GetType() == prevInput->GetType()) {
1791             if (SkipThisPeepholeInOSR(inst, prevInput)) {
1792                 return;
1793             }
1794             inst->ReplaceUsers(prevInput);
1795             PEEPHOLE_IS_APPLIED(this, inst);
1796             return;
1797         }
1798     }
1799 }
1800 
TrySimplifyAddSubAdd(Inst * inst,Inst * input0,Inst * input1)1801 bool Peepholes::TrySimplifyAddSubAdd(Inst *inst, Inst *input0, Inst *input1)
1802 {
1803     // (a - b) + (b + c) = a + c
1804     if (input0->GetInput(1) == input1->GetInput(0)) {
1805         auto newInput0 = input0->GetInput(0).GetInst();
1806         auto newInput1 = input1->GetInput(1).GetInst();
1807         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1808             return true;
1809         }
1810         inst->SetInput(0, newInput0);
1811         inst->SetInput(1, newInput1);
1812         PEEPHOLE_IS_APPLIED(this, inst);
1813         return true;
1814     }
1815 
1816     // (a - b) + (c + b) = a + c
1817     if (input0->GetInput(1) == input1->GetInput(1)) {
1818         auto newInput0 = input0->GetInput(0).GetInst();
1819         auto newInput1 = input1->GetInput(0).GetInst();
1820         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1821             return true;
1822         }
1823         inst->SetInput(0, newInput0);
1824         inst->SetInput(1, newInput1);
1825         PEEPHOLE_IS_APPLIED(this, inst);
1826         return true;
1827     }
1828 
1829     return false;
1830 }
1831 
TrySimplifyAddSubSub(Inst * inst,Inst * input0,Inst * input1)1832 bool Peepholes::TrySimplifyAddSubSub(Inst *inst, Inst *input0, Inst *input1)
1833 {
1834     // (a - b) + (b - c) = a - c
1835     if (input0->GetInput(1) == input1->GetInput(0)) {
1836         auto newInput0 = input0->GetInput(0).GetInst();
1837         auto newInput1 = input1->GetInput(1).GetInst();
1838         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1839             return true;
1840         }
1841 
1842         inst->SetOpcode(Opcode::Sub);
1843         inst->SetInput(0, newInput0);
1844         inst->SetInput(1, newInput1);
1845         PEEPHOLE_IS_APPLIED(this, inst);
1846         return true;
1847     }
1848 
1849     // (a - b) + (c - a) = c - b
1850     if (input0->GetInput(0) == input1->GetInput(1)) {
1851         auto newInput0 = input1->GetInput(0).GetInst();
1852         auto newInput1 = input0->GetInput(1).GetInst();
1853         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1854             return true;
1855         }
1856         inst->SetOpcode(Opcode::Sub);
1857         inst->SetInput(0, newInput0);
1858         inst->SetInput(1, newInput1);
1859         PEEPHOLE_IS_APPLIED(this, inst);
1860         return true;
1861     }
1862 
1863     return false;
1864 }
1865 
TrySimplifySubAddAdd(Inst * inst,Inst * input0,Inst * input1)1866 bool Peepholes::TrySimplifySubAddAdd(Inst *inst, Inst *input0, Inst *input1)
1867 {
1868     // (a + b) - (a + c) = b - c
1869     if (input0->GetInput(0) == input1->GetInput(0)) {
1870         auto newInput0 = input0->GetInput(1).GetInst();
1871         auto newInput1 = input1->GetInput(1).GetInst();
1872         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1873             return true;
1874         }
1875         inst->SetInput(0, newInput0);
1876         inst->SetInput(1, newInput1);
1877         PEEPHOLE_IS_APPLIED(this, inst);
1878         return true;
1879     }
1880 
1881     // (a + b) - (c + a) = b - c
1882     if (input0->GetInput(0) == input1->GetInput(1)) {
1883         auto newInput0 = input0->GetInput(1).GetInst();
1884         auto newInput1 = input1->GetInput(0).GetInst();
1885         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1886             return true;
1887         }
1888         inst->SetInput(0, newInput0);
1889         inst->SetInput(1, newInput1);
1890         PEEPHOLE_IS_APPLIED(this, inst);
1891         return true;
1892     }
1893 
1894     // (a + b) - (c + b) = a - c
1895     if (input0->GetInput(1) == input1->GetInput(1)) {
1896         auto newInput0 = input0->GetInput(0).GetInst();
1897         auto newInput1 = input1->GetInput(0).GetInst();
1898         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1899             return true;
1900         }
1901         inst->SetInput(0, newInput0);
1902         inst->SetInput(1, newInput1);
1903         PEEPHOLE_IS_APPLIED(this, inst);
1904         return true;
1905     }
1906 
1907     // (a + b) - (b + c) = a - c
1908     if (input0->GetInput(1) == input1->GetInput(0)) {
1909         auto newInput0 = input0->GetInput(0).GetInst();
1910         auto newInput1 = input1->GetInput(1).GetInst();
1911         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1912             return true;
1913         }
1914         inst->SetInput(0, newInput0);
1915         inst->SetInput(1, newInput1);
1916         PEEPHOLE_IS_APPLIED(this, inst);
1917         return true;
1918     }
1919 
1920     return false;
1921 }
1922 
CanReassociateShlShlAddSub(Inst * inst)1923 static bool CanReassociateShlShlAddSub(Inst *inst)
1924 {
1925     if (inst->GetOpcode() != Opcode::Sub && inst->GetOpcode() != Opcode::Add) {
1926         return false;
1927     }
1928     if (IsFloatType(inst->GetType())) {
1929         return false;
1930     }
1931     auto input1 = inst->GetInput(1).GetInst();
1932     if (input1->GetOpcode() != Opcode::Sub && input1->GetOpcode() != Opcode::Add) {
1933         return false;
1934     }
1935     // Potentially inst and input1 can have different types (e.g. UINT32 and UINT16).
1936     if (input1->GetType() != inst->GetType()) {
1937         return false;
1938     }
1939     if (!input1->HasSingleUser()) {
1940         return false;
1941     }
1942     auto shl0 = input1->GetInput(0).GetInst();
1943     if (shl0->GetOpcode() != Opcode::Shl || !shl0->HasSingleUser()) {
1944         return false;
1945     }
1946     auto shl1 = input1->GetInput(1).GetInst();
1947     if (shl1->GetOpcode() != Opcode::Shl || !shl1->HasSingleUser() || shl1->GetInput(0) != shl0->GetInput(0)) {
1948         return false;
1949     }
1950     if (!shl0->GetInput(1).GetInst()->IsConst() || !shl1->GetInput(1).GetInst()->IsConst()) {
1951         return false;
1952     }
1953     auto input0 = inst->GetInput(0).GetInst();
1954     bool check = !input0->IsConst() && !input0->IsParameter() && !input0->IsDominate(input1);
1955     return !check;
1956 }
1957 
1958 /**
1959  * The goal is to transform the following sequence:
1960  *
1961  * shl v1, v0, const1;
1962  * shl v2, v0, const2;
1963  * add/sub v3, v1, v2;
1964  * add/sub v5, v4, v3;
1965  *
1966  * so as to make it ready for the lowering with shifted operands.
1967  *
1968  * add v3, v1, v2;
1969  * add v5, v4, v3;
1970  * ===>
1971  * add v6, v4, v1;
1972  * add v5, v6, v2;
1973  *
1974  * add v3, v1, v2;
1975  * sub v5, v4, v3;
1976  * ===>
1977  * sub v6, v4, v1;
1978  * sub v5, v6, v2;
1979  *
1980  * sub v3, v1, v2;
1981  * add v5, v4, v3;
1982  * ===>
1983  * add v6, v4, v1;
1984  * sub v5, v6, v2;
1985  *
1986  * sub v3, v1, v2;
1987  * sub v5, v4, v3;
1988  * ===>
1989  * sub v6, v4, v1;
1990  * add v5, v6, v2;
1991  */
TryReassociateShlShlAddSub(Inst * inst)1992 bool Peepholes::TryReassociateShlShlAddSub(Inst *inst)
1993 {
1994     if (!CanReassociateShlShlAddSub(inst)) {
1995         return false;
1996     }
1997     auto input0 = inst->GetInput(0).GetInst();
1998     auto input1 = inst->GetInput(1).GetInst();
1999     auto shl0 = input1->GetInput(0).GetInst();
2000     auto shl1 = input1->GetInput(1).GetInst();
2001     Opcode opInput1 = inst->GetInput(1).GetInst()->GetOpcode();
2002     Opcode opInst = inst->GetOpcode();
2003     if (opInst == Opcode::Sub && opInput1 == Opcode::Add) {
2004         // input0 - (shl0 + shl1) -> (input0 - shl0) - shl1
2005         opInput1 = Opcode::Sub;
2006     } else if (opInst == Opcode::Add && opInput1 == Opcode::Sub) {
2007         // input0 + (shl0 - shl1) -> (input0 + shl0) - shl1
2008         opInput1 = Opcode::Add;
2009         opInst = Opcode::Sub;
2010     } else if (opInst == Opcode::Sub && opInput1 == Opcode::Sub) {
2011         // input0 - (shl0 - shl1) -> (input0 - shl0) + shl1
2012         opInst = Opcode::Add;
2013     } else if (opInst != Opcode::Add && opInput1 != Opcode::Add) {
2014         UNREACHABLE();
2015     }
2016     // "input1" and "new_input0" one by one, so we don't should to check "SaveStateOSR" between this insts,
2017     // same for: "inst" and **INST WITHOUT NAME**. We should check only between "inst" and "input1"
2018     if (SkipThisPeepholeInOSR(inst, input1)) {
2019         return true;
2020     }
2021     auto newInput0 = CreateAndInsertInst(opInput1, input1, input0, shl0);
2022     CreateAndInsertInst(opInst, inst, newInput0, shl1);
2023     return true;
2024 }
2025 
TrySimplifyNeg(Inst * inst)2026 void Peepholes::TrySimplifyNeg(Inst *inst)
2027 {
2028     auto input0 = inst->GetInput(0).GetInst();
2029     auto input1 = inst->GetInput(1).GetInst();
2030     if ((input0->GetOpcode() == Opcode::Neg && SkipThisPeepholeInOSR(inst, input0->GetInput(0).GetInst())) ||
2031         (input1->GetOpcode() == Opcode::Neg && SkipThisPeepholeInOSR(inst, input1->GetInput(0).GetInst()))) {
2032         return;
2033     }
2034 
2035     // Case 5
2036     if (input0->GetOpcode() == Opcode::Neg && !input1->IsConst()) {
2037         inst->SetInput(0, input1);
2038         inst->SetInput(1, input0);
2039         std::swap(input0, input1);
2040     }
2041     if (input1->GetOpcode() == Opcode::Neg) {
2042         inst->SetOpcode(Opcode::Sub);
2043         inst->SetInput(1, input1->GetInput(0).GetInst());
2044         PEEPHOLE_IS_APPLIED(this, inst);
2045         return;
2046     }
2047 }
2048 
TrySimplifyCompareNegation(Inst * inst)2049 bool Peepholes::TrySimplifyCompareNegation(Inst *inst)
2050 {
2051     ASSERT(inst != nullptr);
2052     ASSERT(inst->GetOpcode() == Opcode::Add);
2053 
2054     // Case 9: Neg -> Add -> Compare
2055     bool optimized = false;
2056     for (auto &userAdd : inst->GetUsers()) {
2057         auto suspectInst = userAdd.GetInst();
2058         if (suspectInst->GetOpcode() != Opcode::Compare) {
2059             continue;
2060         }
2061         auto compareInst = suspectInst->CastToCompare();
2062         if (compareInst->GetOperandsType() != DataType::BOOL ||
2063             (compareInst->GetCc() != ConditionCode::CC_EQ && compareInst->GetCc() != ConditionCode::CC_NE)) {
2064             continue;
2065         }
2066 
2067         unsigned indexCast = compareInst->GetInput(0).GetInst() == inst ? 0 : 1;
2068         auto boolValue = inst->GetInput(0).GetInst()->GetInput(0).GetInst();
2069         if (SkipThisPeepholeInOSR(inst, boolValue)) {
2070             continue;
2071         }
2072         compareInst->SetInput(indexCast, boolValue);
2073         compareInst->SetCc(GetInverseConditionCode(compareInst->GetCc()));
2074         PEEPHOLE_IS_APPLIED(this, compareInst);
2075         optimized = true;
2076     }
2077     return optimized;
2078 }
2079 
TryReplaceDivByShift(Inst * inst)2080 void Peepholes::TryReplaceDivByShift(Inst *inst)
2081 {
2082     auto bb = inst->GetBasicBlock();
2083     auto graph = bb->GetGraph();
2084     if (graph->IsBytecodeOptimizer()) {
2085         return;
2086     }
2087     auto input0 = inst->GetInput(0).GetInst();
2088     auto input1 = inst->GetInput(1).GetInst();
2089     ASSERT(input1->IsConst());
2090     uint64_t unsignedVal = input1->CastToConstant()->GetIntValue();
2091     if (!DataType::IsTypeSigned(inst->GetType())) {
2092         // case 3:
2093         // 0.unsigned Parameter
2094         // 1.i64 Const 2^n -> {v2}
2095         // 2.un-signed DIV v0, v1 -> {v3}
2096         // 3.unsigned INST v2
2097         // ===>
2098         // 0.unsigned Parameter
2099         // 1.i64 Const n -> {v2}
2100         // 2.un-signed SHR v0, v1 -> {v3}
2101         // 3.unsigned INST v2
2102         int64_t n = GetPowerOfTwo(unsignedVal);
2103         if (n != -1) {
2104             auto power = graph->FindOrCreateConstant(n);
2105             CreateAndInsertInst(Opcode::Shr, inst, input0, power);
2106             PEEPHOLE_IS_APPLIED(this, inst);
2107         }
2108     }
2109 }
2110 
TrySimplifyCompareCaseInputInv(Inst * inst,Inst * input)2111 bool Peepholes::TrySimplifyCompareCaseInputInv(Inst *inst, Inst *input)
2112 {
2113     for (auto &user : inst->GetUsers()) {
2114         auto opcode = user.GetInst()->GetOpcode();
2115         if (opcode != Opcode::If && opcode != Opcode::IfImm) {
2116             return false;
2117         }
2118     }
2119     if (SkipThisPeepholeInOSR(inst, input)) {
2120         return true;
2121     }
2122     for (auto &user : inst->GetUsers()) {
2123         auto opcode = user.GetInst()->GetOpcode();
2124         if (opcode == Opcode::If) {
2125             user.GetInst()->CastToIf()->InverseConditionCode();
2126         } else if (opcode == Opcode::IfImm) {
2127             user.GetInst()->CastToIfImm()->InverseConditionCode();
2128         } else {
2129             UNREACHABLE();
2130         }
2131     }
2132     inst->ReplaceUsers(input);
2133     return true;
2134 }
2135 
2136 // Eliminates double comparison if possible
2137 // 4.i64  Constant                   0x0 -> (v6)
2138 // 5.b    ### Some abstract expression that return boolean ###
2139 // 6.b    Compare EQ i32             v5, v4 -> (v7)
2140 // 7.     IfImm NE b                 v6, 0x0
2141 // =======>
2142 // 4.i64  Constant                   0x0 -> (v6)
2143 // 5.b    ### Some abstract expression that return boolean ###
2144 // 6.b    Compare EQ i32             v5, v4
2145 // 7.     IfImm EQ b                 v5, 0x0
TrySimplifyCompareWithBoolInput(Inst * inst,bool * isOsrBlocked)2146 bool Peepholes::TrySimplifyCompareWithBoolInput(Inst *inst, bool *isOsrBlocked)
2147 {
2148     ASSERT(isOsrBlocked != nullptr);
2149     auto compare = inst->CastToCompare();
2150     bool swap = false;
2151     Inst *input = nullptr;
2152     ConstantInst *constInput = nullptr;
2153     if (!GetInputsOfCompareWithConst(compare, &input, &constInput, &swap)) {
2154         return false;
2155     }
2156     if (input->GetType() != DataType::BOOL) {
2157         return false;
2158     }
2159     ConditionCode cc = swap ? SwapOperandsConditionCode(compare->GetCc()) : compare->GetCc();
2160     InputCode code = GetInputCode(constInput, cc);
2161     if (code == INPUT_TRUE || code == INPUT_FALSE) {
2162         // We create constant, so we don't need to check SaveStateOSR between insts
2163         compare->ReplaceUsers(ConstFoldingCreateIntConst(compare, code == INPUT_TRUE ? 1 : 0));
2164         return true;
2165     }
2166     if (code == INPUT_ORIG) {
2167         if (SkipThisPeepholeInOSR(compare, input)) {
2168             *isOsrBlocked = true;
2169             return true;
2170         }
2171         compare->ReplaceUsers(input);
2172         return true;
2173     }
2174     if (code == INPUT_INV) {
2175         return TrySimplifyCompareCaseInputInv(compare, input);
2176     }
2177     UNREACHABLE();
2178     return false;
2179 }
2180 
2181 // Checks if float const is really an integer const and replace it
TryReplaceFloatConstToIntConst(Inst ** castInput,Inst ** constInst)2182 static bool TryReplaceFloatConstToIntConst([[maybe_unused]] Inst **castInput, Inst **constInst)
2183 {
2184     ASSERT(*constInst != nullptr);
2185     ASSERT(*castInput != nullptr);
2186     auto type = (*constInst)->CastToConstant()->GetType();
2187     if (type == DataType::FLOAT32) {
2188         static constexpr auto MAX_SAFE_FLOAT32 =
2189             static_cast<float>(static_cast<uint32_t>(1U) << static_cast<uint32_t>(std::numeric_limits<float>::digits));
2190         float value = (*constInst)->CastToConstant()->GetFloatValue();
2191         if (value < -MAX_SAFE_FLOAT32 || value > MAX_SAFE_FLOAT32) {
2192             // Not applicable to numbers out of unchangeable f32->i32 range
2193             return false;
2194         }
2195         if (std::trunc(value) != value) {
2196             return false;
2197         }
2198         auto *graph = (*constInst)->GetBasicBlock()->GetGraph();
2199         auto *newCnst = graph->FindOrCreateConstant(static_cast<int32_t>(value));
2200         *constInst = newCnst;
2201         *castInput = (*castInput)->GetInput(0).GetInst();
2202         return true;
2203     }
2204     if (type == DataType::FLOAT64) {
2205         static constexpr auto MAX_SAFE_FLOAT64 = static_cast<double>(
2206             static_cast<uint64_t>(1U) << static_cast<uint64_t>(std::numeric_limits<double>::digits));
2207         double value = (*constInst)->CastToConstant()->GetDoubleValue();
2208         if (value < -MAX_SAFE_FLOAT64 || value > MAX_SAFE_FLOAT64) {
2209             // Not applicable to numbers out of unchangeable f64->i64 range
2210             return false;
2211         }
2212         if (std::trunc(value) != value) {
2213             return false;
2214         }
2215         auto *graph = (*constInst)->GetBasicBlock()->GetGraph();
2216         auto *newCnst = graph->FindOrCreateConstant(static_cast<int64_t>(value));
2217         *constInst = newCnst;
2218         auto *candidateInput = (*castInput)->GetInput(0).GetInst();
2219         if ((value < static_cast<double>(INT32_MIN) || value > static_cast<double>(INT32_MAX)) &&
2220             candidateInput->GetType() != DataType::INT64) {
2221             // In i32 comparison constant will be truncated to i32
2222             // If it is more than INT32 range, it is necessary to insert cast i32 -> i64
2223             auto *newCast =
2224                 graph->CreateInstCast(DataType::INT64, INVALID_PC, candidateInput, candidateInput->GetType());
2225             (*castInput)->InsertAfter(newCast);
2226             *castInput = newCast;
2227         } else {
2228             *castInput = candidateInput;
2229         }
2230         return true;
2231     }
2232     return false;
2233 }
2234 
2235 // 6.i32  Cmp        v5, v1
2236 // 7.b    Compare    v6, 0
2237 // 9.     IfImm NE b v7, 0x0
2238 // =======>
2239 // 6.i32  Cmp        v5, v1
2240 // 7.b    Compare    v5, v1
2241 // 9.     IfImm NE b v7, 0x0
TrySimplifyCmpCompareWithZero(Inst * inst,bool * isOsrBlocked)2242 bool Peepholes::TrySimplifyCmpCompareWithZero(Inst *inst, bool *isOsrBlocked)
2243 {
2244     ASSERT(isOsrBlocked != nullptr);
2245     auto compare = inst->CastToCompare();
2246     if (compare->GetBasicBlock()->GetGraph()->IsBytecodeOptimizer()) {
2247         return false;
2248     }
2249     bool swap = false;
2250     Inst *input = nullptr;
2251     ConstantInst *constInput = nullptr;
2252     if (!GetInputsOfCompareWithConst(compare, &input, &constInput, &swap)) {
2253         return false;
2254     }
2255     if (input->GetOpcode() != Opcode::Cmp) {
2256         return false;
2257     }
2258     if (!constInput->IsEqualConstAllTypes(0)) {
2259         return false;
2260     }
2261     auto input0 = input->GetInput(0).GetInst();
2262     auto input1 = input->GetInput(1).GetInst();
2263     if (SkipThisPeepholeInOSR(compare, input0) || SkipThisPeepholeInOSR(compare, input1)) {
2264         *isOsrBlocked = true;
2265         return true;
2266     }
2267     auto cmpOpType = input->CastToCmp()->GetOperandsType();
2268     if (IsFloatType(cmpOpType)) {
2269         ASSERT(compare->GetOperandsType() == DataType::INT32);
2270         if (!TrySimplifyFloatCmpCompare(&input0, &input1, &cmpOpType, input, &swap)) {
2271             return false;
2272         }
2273     }
2274     ConditionCode cc = swap ? SwapOperandsConditionCode(compare->GetCc()) : compare->GetCc();
2275     if (!IsTypeSigned(cmpOpType)) {
2276         ASSERT(cc == ConditionCode::CC_EQ || cc == ConditionCode::CC_NE || IsSignedConditionCode(cc));
2277         // If Cmp operands are unsigned then Compare.CC must be converted to unsigned.
2278         cc = InverseSignednessConditionCode(cc);
2279     }
2280     compare->SetInput(0, input0);
2281     compare->SetInput(1, input1);
2282     compare->SetOperandsType(cmpOpType);
2283     compare->SetCc(cc);
2284     return true;
2285 }
2286 
TrySimplifyFloatCmpCompare(Inst ** input0,Inst ** input1,DataType::Type * cmpOpType,Inst * compareInput,bool * swap)2287 bool Peepholes::TrySimplifyFloatCmpCompare(Inst **input0, Inst **input1, DataType::Type *cmpOpType, Inst *compareInput,
2288                                            bool *swap)
2289 {
2290     if (CheckFcmpInputs(*input0, *input1)) {
2291         *input0 = (*input0)->GetInput(0).GetInst();
2292         *input1 = (*input1)->GetInput(0).GetInst();
2293         *cmpOpType = DataType::INT32;
2294     } else if (CheckFcmpWithConstInput(*input0, *input1)) {
2295         bool cmpSwap = false;
2296         Inst *cmpCastInput = nullptr;
2297         ConstantInst *cmpConstInput = nullptr;
2298         if (!GetInputsOfCompareWithConst(compareInput, &cmpCastInput, &cmpConstInput, swap)) {
2299             return false;
2300         }
2301         Inst *cmpConstInst = static_cast<Inst *>(cmpConstInput);
2302         if (!TryReplaceFloatConstToIntConst(&cmpCastInput, &cmpConstInst)) {
2303             return false;
2304         }
2305         *input0 = cmpCastInput;
2306         *input1 = cmpConstInst;
2307         *cmpOpType = (*input0)->GetType();
2308         *swap = (*swap) ^ cmpSwap;
2309     } else {
2310         return false;
2311     }
2312     return true;
2313 }
2314 
TrySimplifyTestEqualInputs(Inst * inst)2315 bool Peepholes::TrySimplifyTestEqualInputs(Inst *inst)
2316 {
2317     auto cmpInst = inst->CastToCompare();
2318     if (cmpInst->GetCc() != ConditionCode::CC_TST_EQ && cmpInst->GetCc() != ConditionCode::CC_TST_NE) {
2319         return false;
2320     }
2321     auto input0 = inst->GetInput(0).GetInst();
2322     auto input1 = inst->GetInput(1).GetInst();
2323     if (input0 != input1) {
2324         return false;
2325     }
2326     if (cmpInst->GetCc() == ConditionCode::CC_TST_EQ) {
2327         cmpInst->SetCc(ConditionCode::CC_EQ);
2328     } else {
2329         cmpInst->SetCc(ConditionCode::CC_NE);
2330     }
2331     // We create constant, so we don't need to check SaveStateOSR between insts
2332     cmpInst->SetInput(1, ConstFoldingCreateIntConst(input1, 0));
2333     return true;
2334 }
2335 
TrySimplifyCompareAndZero(Inst * inst,bool * isOsrBlocked)2336 bool Peepholes::TrySimplifyCompareAndZero(Inst *inst, bool *isOsrBlocked)
2337 {
2338     ASSERT(isOsrBlocked != nullptr);
2339     if (inst->GetBasicBlock()->GetGraph()->IsBytecodeOptimizer()) {
2340         return false;
2341     }
2342     auto cmpInst = inst->CastToCompare();
2343     auto cc = cmpInst->GetCc();
2344     if (cc != CC_EQ && cc != CC_NE) {
2345         return false;
2346     }
2347     bool swap = false;
2348     Inst *input = nullptr;
2349     ConstantInst *constInput = nullptr;
2350     if (!GetInputsOfCompareWithConst(cmpInst, &input, &constInput, &swap)) {
2351         return false;
2352     }
2353     if (input->GetOpcode() != Opcode::And || !input->HasSingleUser() || !constInput->IsEqualConstAllTypes(0)) {
2354         return false;
2355     }
2356     // 2.i32 And                  v0, v1
2357     // 3.i32 Constant             0x0
2358     // 4.b   Compare CC_EQ/CC_NE  v2, v3
2359     // =======>
2360     // 4.b   Compare CC_TST_EQ/CC_TST_NE  v0, v1
2361 
2362     if (SkipThisPeepholeInOSR(cmpInst, input->GetInput(0).GetInst())) {
2363         *isOsrBlocked = true;
2364         return true;
2365     }
2366     cmpInst->SetCc(cc == CC_EQ ? CC_TST_EQ : CC_TST_NE);
2367     cmpInst->SetInput(0, input->GetInput(0).GetInst());
2368     cmpInst->SetInput(1, input->GetInput(1).GetInst());
2369     return true;
2370 }
2371 
TrySimplifyCompareLenArrayWithZero(Inst * inst)2372 bool Peepholes::TrySimplifyCompareLenArrayWithZero(Inst *inst)
2373 {
2374     auto compare = inst->CastToCompare();
2375     bool swap = false;
2376     Inst *input = nullptr;
2377     ConstantInst *constInput = nullptr;
2378     if (!GetInputsOfCompareWithConst(compare, &input, &constInput, &swap)) {
2379         return false;
2380     }
2381     if (input->GetOpcode() != Opcode::LenArray || !constInput->IsEqualConstAllTypes(0)) {
2382         return false;
2383     }
2384 
2385     ConditionCode cc = swap ? SwapOperandsConditionCode(compare->GetCc()) : compare->GetCc();
2386     if (cc == CC_GE || cc == CC_LT) {
2387         // We create constant, so we don't need to check SaveStateOSR between insts
2388         compare->ReplaceUsers(ConstFoldingCreateIntConst(compare, cc == CC_GE ? 1 : 0));
2389         return true;
2390     }
2391     return false;
2392 }
2393 
2394 // Try to combine constants when arithmetic operations with constants are repeated
2395 template <typename T>
TryCombineConst(Inst * inst,ConstantInst * cnst1,T combine,bool * isOsrBlocked)2396 bool Peepholes::TryCombineConst(Inst *inst, ConstantInst *cnst1, T combine, bool *isOsrBlocked)
2397 {
2398     ASSERT(isOsrBlocked != nullptr);
2399     auto input0 = inst->GetInput(0).GetInst();
2400     auto previnput1 = input0->GetInput(1).GetInst();
2401     if (previnput1->IsConst() && inst->GetType() == input0->GetType()) {
2402         if (SkipThisPeepholeInOSR(inst, input0->GetInput(0).GetInst())) {
2403             *isOsrBlocked = true;
2404             return false;
2405         }
2406         auto cnst2 = static_cast<ConstantInst *>(previnput1);
2407         auto graph = inst->GetBasicBlock()->GetGraph();
2408         ConstantInst *newCnst = nullptr;
2409         switch (DataType::GetCommonType(cnst1->GetType())) {
2410             case DataType::INT64:
2411                 newCnst = ConstFoldingCreateIntConst(inst, combine(cnst1->GetIntValue(), cnst2->GetIntValue()));
2412                 break;
2413             case DataType::FLOAT32:
2414                 newCnst = graph->FindOrCreateConstant(combine(cnst1->GetFloatValue(), cnst2->GetFloatValue()));
2415                 break;
2416             case DataType::FLOAT64:
2417                 newCnst = graph->FindOrCreateConstant(combine(cnst1->GetDoubleValue(), cnst2->GetDoubleValue()));
2418                 break;
2419             default:
2420                 UNREACHABLE();
2421         }
2422         inst->SetInput(0, input0->GetInput(0).GetInst());
2423         inst->SetInput(1, newCnst);
2424         return true;
2425     }
2426     return false;
2427 }
2428 
TryCombineAddSubConst(Inst * inst,ConstantInst * cnst1,bool * isOsrBlocked)2429 bool Peepholes::TryCombineAddSubConst(Inst *inst, ConstantInst *cnst1, bool *isOsrBlocked)
2430 {
2431     ASSERT(isOsrBlocked != nullptr);
2432     auto opc = inst->GetOpcode();
2433     ASSERT(opc == Opcode::Add || opc == Opcode::Sub);
2434     auto input0 = inst->GetInput(0).GetInst();
2435     auto combine = [&opc, &input0](auto x, auto y) { return opc == input0->GetOpcode() ? x + y : x - y; };
2436     return TryCombineConst(inst, cnst1, combine, isOsrBlocked);
2437 }
2438 
TryCombineShiftConst(Inst * inst,ConstantInst * cnst1,bool * isOsrBlocked)2439 bool Peepholes::TryCombineShiftConst(Inst *inst, ConstantInst *cnst1, bool *isOsrBlocked)
2440 {
2441     ASSERT(isOsrBlocked != nullptr);
2442     auto opc = inst->GetOpcode();
2443     ASSERT(opc == Opcode::Shl || opc == Opcode::Shr || opc == Opcode::AShr);
2444 
2445     auto input0 = inst->GetInput(0).GetInst();
2446     auto previnput1 = input0->GetInput(1).GetInst();
2447     if (!(previnput1->IsConst() && inst->GetType() == input0->GetType())) {
2448         return false;
2449     }
2450     auto graph = inst->GetBasicBlock()->GetGraph();
2451     uint64_t sizeMask = DataType::GetTypeSize(inst->GetType(), graph->GetArch()) - 1;
2452     auto cnst2 = static_cast<ConstantInst *>(previnput1);
2453     auto newValue = (cnst1->GetIntValue() & sizeMask) + (cnst2->GetIntValue() & sizeMask);
2454     // If new_value > size_mask, result is always 0 for Shr and Shl,
2455     // and 0 or -1 (depending on highest bit of input) for AShr
2456     if (newValue <= sizeMask || opc == Opcode::AShr) {
2457         if (SkipThisPeepholeInOSR(inst, input0->GetInput(0).GetInst())) {
2458             *isOsrBlocked = true;
2459             return false;
2460         }
2461         auto newCnst = ConstFoldingCreateIntConst(inst, std::min(newValue, sizeMask));
2462         inst->SetInput(0, input0->GetInput(0).GetInst());
2463         inst->SetInput(1, newCnst);
2464         return true;
2465     }
2466     auto newCnst = ConstFoldingCreateIntConst(inst, 0);
2467     inst->ReplaceUsers(newCnst);
2468     return true;
2469 }
2470 
TryCombineMulConst(Inst * inst,ConstantInst * cnst1,bool * isOsrBlocked)2471 bool Peepholes::TryCombineMulConst(Inst *inst, ConstantInst *cnst1, bool *isOsrBlocked)
2472 {
2473     ASSERT(isOsrBlocked != nullptr);
2474     ASSERT(inst->GetOpcode() == Opcode::Mul);
2475     auto combine = [](auto x, auto y) { return x * y; };
2476     return TryCombineConst(inst, cnst1, combine, isOsrBlocked);
2477 }
2478 
GetInputsOfCompareWithConst(const Inst * inst,Inst ** input,ConstantInst ** constInput,bool * inputsSwapped)2479 bool Peepholes::GetInputsOfCompareWithConst(const Inst *inst, Inst **input, ConstantInst **constInput,
2480                                             bool *inputsSwapped)
2481 {
2482     if (inst->GetOpcode() == Opcode::Compare || inst->GetOpcode() == Opcode::Cmp) {
2483         if (inst->GetInput(1).GetInst()->IsConst()) {
2484             *input = inst->GetInput(0).GetInst();
2485             *constInput = inst->GetInput(1).GetInst()->CastToConstant();
2486             *inputsSwapped = false;
2487             return true;
2488         }
2489         if (inst->GetInput(0).GetInst()->IsConst()) {
2490             *input = inst->GetInput(1).GetInst();
2491             *constInput = inst->GetInput(0).GetInst()->CastToConstant();
2492             *inputsSwapped = true;
2493             return true;
2494         }
2495     }
2496     return false;
2497 }
2498 
GenerateXorWithOne(BasicBlock * block,Inst * ifImmInput)2499 Inst *GenerateXorWithOne(BasicBlock *block, Inst *ifImmInput)
2500 {
2501     auto graph = block->GetGraph();
2502     auto xorInst = graph->CreateInstXor(DataType::BOOL, block->GetGuestPc());
2503     xorInst->SetInput(0, ifImmInput);
2504     Inst *oneConst = nullptr;
2505     if (graph->IsBytecodeOptimizer()) {
2506         oneConst = graph->FindOrCreateConstant<uint32_t>(1);
2507     } else {
2508         oneConst = graph->FindOrCreateConstant<uint64_t>(1);
2509     }
2510     xorInst->SetInput(1, oneConst);
2511     // We can add inst "xor" before SaveStateOSR in BasicBlock
2512     block->PrependInst(xorInst);
2513     return xorInst;
2514 }
2515 
IsBoolPhiInverted(PhiInst * phi,IfImmInst * ifImm)2516 std::optional<bool> IsBoolPhiInverted(PhiInst *phi, IfImmInst *ifImm)
2517 {
2518     auto phiInput0 = phi->GetInput(0).GetInst();
2519     auto phiInput1 = phi->GetInput(1).GetInst();
2520     if (!phiInput0->IsBoolConst() || !phiInput1->IsBoolConst()) {
2521         return std::nullopt;
2522     }
2523     auto constant0 = phiInput0->CastToConstant()->GetRawValue();
2524     auto constant1 = phiInput1->CastToConstant()->GetRawValue();
2525     if (constant0 == constant1) {
2526         return std::nullopt;
2527     }
2528     // Here constant0 and constant1 are 0 and 1 in some order
2529 
2530     auto invertedIf = IsIfInverted(phi->GetBasicBlock(), ifImm);
2531     if (invertedIf == std::nullopt) {
2532         return std::nullopt;
2533     }
2534     // constant0 is also index of phi input equal to 0
2535     if (phi->GetPhiInputBbNum(constant0) == 0) {
2536         return !*invertedIf;
2537     }
2538     return invertedIf;
2539 }
2540 
TryEliminatePhi(PhiInst * phi)2541 bool Peepholes::TryEliminatePhi(PhiInst *phi)
2542 {
2543     if (phi->GetInputsCount() != MAX_SUCCS_NUM) {
2544         return false;
2545     }
2546 
2547     auto bb = phi->GetBasicBlock();
2548     auto dom = bb->GetDominator();
2549     if (dom->IsEmpty()) {
2550         return false;
2551     }
2552     auto last = dom->GetLastInst();
2553     if (last->GetOpcode() != Opcode::IfImm) {
2554         return false;
2555     }
2556 
2557     auto graph = dom->GetGraph();
2558     auto ifImm = last->CastToIfImm();
2559     auto input = ifImm->GetInput(0).GetInst();
2560     // In case of the bytecode optimizer we can not generate Compare therefore we check that Peepholes has eliminated
2561     // Compare
2562     if (graph->IsBytecodeOptimizer() && input->GetOpcode() == Opcode::Compare) {
2563         return false;
2564     }
2565     if (input->GetType() != DataType::BOOL ||
2566         GetTypeSize(phi->GetType(), graph->GetArch()) != GetTypeSize(input->GetType(), graph->GetArch())) {
2567         return false;
2568     }
2569     auto inverted = IsBoolPhiInverted(phi, ifImm);
2570     if (!inverted) {
2571         return false;
2572     }
2573     if (*inverted) {
2574         // 0. Const 0
2575         // 1. Const 1
2576         // 2. v2
2577         // 3. IfImm NE b v2, 0x0
2578         // 4. Phi v0, v1 -> v5, ...
2579         // ===>
2580         // 0. Const 0
2581         // 1. Const 1
2582         // 2. v2
2583         // 3. IfImm NE b v2, 0x0
2584         // 6. Xor v2, v1 -> v5, ...
2585         // 4. Phi v0, v1
2586 
2587         // "xori"(xor) will insert as first inst in BB, so enough check between first inst and input
2588         if (SkipThisPeepholeInOSR(phi, input)) {
2589             return false;
2590         }
2591         auto xori = GenerateXorWithOne(phi->GetBasicBlock(), input);
2592         phi->ReplaceUsers(xori);
2593     } else {
2594         // 0. Const 0
2595         // 1. Const 1
2596         // 2. v2
2597         // 3. IfImm NE b v2, 0x0
2598         // 4. Phi v1, v0 -> v5, ...
2599         // ===>
2600         // 0. Const 0
2601         // 1. Const 1
2602         // 2. v2 -> v5, ...
2603         // 3. IfImm NE b v2, 0x0
2604         // 4. Phi v1, v0
2605 
2606         if (SkipThisPeepholeInOSR(phi, input)) {
2607             return false;
2608         }
2609         phi->ReplaceUsers(input);
2610     }
2611     return true;
2612 }
2613 
SkipThisPeepholeInOSR(Inst * inst,Inst * newInput)2614 bool Peepholes::SkipThisPeepholeInOSR(Inst *inst, Inst *newInput)
2615 {
2616     auto osr = inst->GetBasicBlock()->GetGraph()->IsOsrMode();
2617     return osr && newInput->GetOpcode() != Opcode::Constant && IsInstInDifferentBlocks(inst, newInput);
2618 }
2619 
VisitGetInstanceClass(GraphVisitor * v,Inst * inst)2620 void Peepholes::VisitGetInstanceClass(GraphVisitor *v, Inst *inst)
2621 {
2622     auto typeInfo = inst->GetDataFlowInput(0)->GetObjectTypeInfo();
2623     if (typeInfo && typeInfo.IsExact()) {
2624         auto klass = typeInfo.GetClass();
2625         auto bb = inst->GetBasicBlock();
2626         auto graph = bb->GetGraph();
2627         auto classImm = graph->CreateInstLoadImmediate(DataType::REFERENCE, inst->GetPc(), klass);
2628         inst->ReplaceUsers(classImm);
2629         bb->InsertAfter(classImm, inst);
2630         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2631     }
2632 }
VisitLoadAndInitClass(GraphVisitor * v,Inst * inst)2633 void Peepholes::VisitLoadAndInitClass(GraphVisitor *v, Inst *inst)
2634 {
2635     auto graph = inst->GetBasicBlock()->GetGraph();
2636     if (!graph->IsJitOrOsrMode()) {
2637         return;
2638     }
2639     auto klass = inst->CastToLoadAndInitClass()->GetClass();
2640     if (klass == nullptr || !graph->GetRuntime()->IsClassInitialized(reinterpret_cast<uintptr_t>(klass))) {
2641         return;
2642     }
2643     auto classImm = graph->CreateInstLoadImmediate(DataType::REFERENCE, inst->GetPc(), klass);
2644     inst->ReplaceUsers(classImm);
2645     inst->GetBasicBlock()->InsertAfter(classImm, inst);
2646 
2647     inst->ClearFlag(compiler::inst_flags::NO_DCE);
2648     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2649 }
2650 
VisitInitClass(GraphVisitor * v,Inst * inst)2651 void Peepholes::VisitInitClass(GraphVisitor *v, Inst *inst)
2652 {
2653     auto graph = inst->GetBasicBlock()->GetGraph();
2654     if (!graph->IsJitOrOsrMode()) {
2655         return;
2656     }
2657     auto klass = inst->CastToInitClass()->GetClass();
2658     if (klass == nullptr || !graph->GetRuntime()->IsClassInitialized(reinterpret_cast<uintptr_t>(klass))) {
2659         return;
2660     }
2661     inst->ClearFlag(compiler::inst_flags::NO_DCE);
2662     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2663 }
2664 
VisitIntrinsic(GraphVisitor * v,Inst * inst)2665 void Peepholes::VisitIntrinsic([[maybe_unused]] GraphVisitor *v, Inst *inst)
2666 {
2667     auto intrinsic = inst->CastToIntrinsic();
2668     // CC-OFFNXT(C_RULE_SWITCH_BRANCH_CHECKER) autogenerated code
2669     switch (intrinsic->GetIntrinsicId()) {
2670 #include "intrinsics_peephole.inl"
2671         default: {
2672             return;
2673         }
2674     }
2675 }
2676 
VisitLoadClass(GraphVisitor * v,Inst * inst)2677 void Peepholes::VisitLoadClass(GraphVisitor *v, Inst *inst)
2678 {
2679     auto graph = inst->GetBasicBlock()->GetGraph();
2680     if (!graph->IsJitOrOsrMode()) {
2681         return;
2682     }
2683     auto klass = inst->CastToLoadClass()->GetClass();
2684     if (klass == nullptr) {
2685         return;
2686     }
2687     auto classImm = graph->CreateInstLoadImmediate(DataType::REFERENCE, inst->GetPc(), klass);
2688     inst->ReplaceUsers(classImm);
2689     inst->GetBasicBlock()->InsertAfter(classImm, inst);
2690 
2691     inst->ClearFlag(compiler::inst_flags::NO_DCE);
2692     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2693 }
2694 
VisitLoadConstantPool(GraphVisitor * v,Inst * inst)2695 void Peepholes::VisitLoadConstantPool(GraphVisitor *v, Inst *inst)
2696 {
2697     auto graph = inst->GetBasicBlock()->GetGraph();
2698     if (!graph->IsJitOrOsrMode()) {
2699         return;
2700     }
2701     auto func = inst->GetInput(0).GetInst();
2702     void *constantPool = nullptr;
2703     if (func->IsParameter() && func->CastToParameter()->GetArgNumber() == 0) {
2704         constantPool = graph->GetRuntime()->GetConstantPool(graph->GetMethod());
2705     } else {
2706         CallInst *callerInst = nullptr;
2707         for (auto &user : inst->GetUsers()) {
2708             auto userInst = user.GetInst();
2709             if (userInst->GetOpcode() == Opcode::SaveState &&
2710                 user.GetVirtualRegister().GetVRegType() == VRegType::CONST_POOL) {
2711                 callerInst = userInst->CastToSaveState()->GetCallerInst();
2712                 ASSERT(callerInst != nullptr);
2713                 break;
2714             }
2715         }
2716         if (callerInst == nullptr) {
2717             return;
2718         }
2719         if (auto funcObject = callerInst->GetFunctionObject(); funcObject != 0) {
2720             constantPool = graph->GetRuntime()->GetConstantPool(funcObject);
2721         } else {
2722             constantPool = graph->GetRuntime()->GetConstantPool(callerInst->GetCallMethod());
2723         }
2724     }
2725     if (constantPool == nullptr) {
2726         return;
2727     }
2728     auto constantPoolImm = graph->CreateInstLoadImmediate(DataType::ANY, inst->GetPc(), constantPool,
2729                                                           LoadImmediateInst::ObjectType::CONSTANT_POOL);
2730     inst->InsertAfter(constantPoolImm);
2731     inst->ReplaceUsers(constantPoolImm);
2732 
2733     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2734 }
2735 
VisitLoadFromConstantPool(GraphVisitor * v,Inst * inst)2736 void Peepholes::VisitLoadFromConstantPool(GraphVisitor *v, Inst *inst)
2737 {
2738     auto graph = inst->GetBasicBlock()->GetGraph();
2739     // LoadFromConstantPool with string flag may be used for intrinsics inlining, do not optimize too early
2740     if (!graph->IsUnrollComplete()) {
2741         return;
2742     }
2743     auto constantPool = inst->GetInput(0).GetInst();
2744     if (constantPool->GetOpcode() != Opcode::LoadImmediate) {
2745         return;
2746     }
2747     auto offset = inst->CastToLoadFromConstantPool()->GetTypeId();
2748     auto shift = DataType::ShiftByType(DataType::ANY, graph->GetArch());
2749     uintptr_t mem = constantPool->CastToLoadImmediate()->GetConstantPool() +
2750                     graph->GetRuntime()->GetArrayDataOffset(graph->GetArch()) + (offset << shift);
2751     auto load = graph->CreateInstLoadObjFromConst(DataType::ANY, inst->GetPc(), mem);
2752     inst->InsertAfter(load);
2753     inst->ReplaceUsers(load);
2754 
2755     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2756 }
2757 
VisitLoadObject(GraphVisitor * v,Inst * inst)2758 void Peepholes::VisitLoadObject([[maybe_unused]] GraphVisitor *v, Inst *inst)
2759 {
2760     ASSERT(inst->GetOpcode() == Opcode::LoadObject);
2761 
2762     auto visitor = static_cast<Peepholes *>(v);
2763     if (visitor->TryOptimizeBoxedLoadStoreObject(inst)) {
2764         PEEPHOLE_IS_APPLIED(visitor, inst);
2765         return;
2766     }
2767 }
2768 
VisitLoadStatic(GraphVisitor * v,Inst * inst)2769 void Peepholes::VisitLoadStatic(GraphVisitor *v, Inst *inst)
2770 {
2771     if (ConstFoldingLoadStatic(inst)) {
2772         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2773     }
2774 }
2775 
CreateCompareInsteadOfXorAdd(Inst * oldInst)2776 bool Peepholes::CreateCompareInsteadOfXorAdd(Inst *oldInst)
2777 {
2778     ASSERT(oldInst->GetOpcode() == Opcode::Xor || oldInst->GetOpcode() == Opcode::Add);
2779     auto input0 = oldInst->GetInput(0).GetInst();
2780     [[maybe_unused]] auto input1 = oldInst->GetInput(1).GetInst();
2781 
2782     if (oldInst->GetOpcode() == Opcode::Add) {
2783         ASSERT(input0->GetOpcode() == Opcode::Neg);
2784         input0 = input0->GetInput(0).GetInst();
2785         for (auto &userAdd : oldInst->GetUsers()) {
2786             if (SkipThisPeepholeInOSR(userAdd.GetInst(), input0)) {
2787                 return false;
2788             }
2789         }
2790     }
2791 
2792     // We shouldn't check on OSR with Xor, because old_inst and cmp_inst is placed one by one
2793     ASSERT(input0->GetType() == DataType::BOOL && input1->IsConst() && input1->CastToConstant()->GetIntValue() == 1U);
2794     auto cnst = oldInst->GetBasicBlock()->GetGraph()->FindOrCreateConstant(0);
2795     auto cmpInst = CreateAndInsertInst(Opcode::Compare, oldInst, input0, cnst);
2796     cmpInst->SetType(DataType::BOOL);
2797     cmpInst->CastToCompare()->SetCc(ConditionCode::CC_EQ);
2798     cmpInst->CastToCompare()->SetOperandsType(DataType::BOOL);
2799     auto type = oldInst->GetType();
2800     if (type == DataType::UINT64 || type == DataType::INT64) {
2801         auto cast = cmpInst->GetBasicBlock()->GetGraph()->CreateInstCast();
2802         cast->SetType(type);
2803         cmpInst->InsertAfter(cast);
2804         cmpInst->ReplaceUsers(cast);
2805         cast->SetInput(0, cmpInst);
2806         cast->SetOperandsType(DataType::BOOL);
2807     }
2808     return true;
2809 }
2810 
2811 // Move users from LenArray to constant which used in MultiArray
2812 // Case 1
2813 // 1.s64 ... ->{v3}
2814 // 2.s64 ... ->{v3}
2815 // 3.ref MultiArray ... , v1, v2, ... ->{v4,..}
2816 // 4.s32 LenArray v3 ->{v5, ...}
2817 // 5.    USE      v5
2818 // ===>
2819 // 1.s64 ... ->{v3, v5, ...}
2820 // 2.s64 ... ->{v3}
2821 // 3.ref MultiArray ... , v1, v2, ... ->{v4,..}
2822 // 4.s32 LenArray v3
2823 // 5.    USE      v1
2824 
2825 // Case 2
2826 // 1.s64 ... ->{v3}
2827 // 2.s64 ... ->{v3}
2828 // 3.ref MultiArray ... , v1, v2, ... ->{v4,..}
2829 // 4.ref LoadArray v3, ...
2830 // 5.ref NullCheck v4, ...
2831 // 6.s32 LenArray v5 ->{v7, ...}
2832 // 7.    USE      v6
2833 // ===>
2834 // 1.s64 ... ->{v3}
2835 // 2.s64 ... ->{v3, v7, ...}
2836 // 3.ref MultiArray ... , v1, v2, ... ->{v4,..}
2837 // 4.ref LoadArray v3, ...
2838 // 5.ref NullCheck v4, ...
2839 // 6.s32 LenArray v5
2840 // 7.    USE      v2
OptimizeLenArrayForMultiArray(Inst * lenArray,Inst * inst,size_t indexSize)2841 bool Peepholes::OptimizeLenArrayForMultiArray(Inst *lenArray, Inst *inst, size_t indexSize)
2842 {
2843     if (inst->GetOpcode() == Opcode::MultiArray) {
2844         // Arguments of MultiArray look like : class, size_0, size_1, ..., size_N, SaveState
2845         // When element type of multyarray is array-like object (LenArray can be applyed to it), we can get case when
2846         // number sequential LoadArray with LenArray more than dimension of MultiArrays. So limiting the index_size.
2847         // Example in unittest PeepholesTest.MultiArrayWithLenArrayOfString
2848 
2849         auto multiarrDimension = inst->GetInputsCount() - 2;
2850         if (!(indexSize < multiarrDimension)) {
2851             return false;
2852         }
2853         // MultiArray's sizes starts from index "1", so need add "1" to get absolute index
2854         auto value = inst->GetDataFlowInput(indexSize + 1);
2855         for (auto &it : lenArray->GetUsers()) {
2856             if (SkipThisPeepholeInOSR(it.GetInst(), value)) {
2857                 return false;
2858             }
2859         }
2860         lenArray->ReplaceUsers(value);
2861         return true;
2862     }
2863     if (inst->GetOpcode() == Opcode::LoadArray) {
2864         auto input = inst->GetDataFlowInput(0);
2865         return OptimizeLenArrayForMultiArray(lenArray, input, indexSize + 1);
2866     }
2867     return false;
2868 }
2869 
IsNegationPattern(Inst * inst)2870 bool Peepholes::IsNegationPattern(Inst *inst)
2871 {
2872     ASSERT(inst->GetOpcode() == Opcode::Add);
2873     // Negetion pattern is:
2874     // 1.   Constant 0x1
2875     // 2.b  ...
2876     // 3.i32 Neg v2 -> v4
2877     // 4.i32 Add v3, v1
2878     auto input0 = inst->GetInput(0).GetInst();
2879     if (input0->GetOpcode() != Opcode::Neg || input0->GetInput(0).GetInst()->GetType() != DataType::BOOL ||
2880         !input0->HasSingleUser()) {
2881         return false;
2882     }
2883     // We sure, that constant may be only in input1
2884     auto input1 = inst->GetInput(1).GetInst();
2885     return input1->GetOpcode() == Opcode::Constant && input1->CastToConstant()->GetIntValue() == 1;
2886 }
2887 
TrySimplifyNegationPattern(Inst * inst)2888 bool Peepholes::TrySimplifyNegationPattern(Inst *inst)
2889 {
2890     ASSERT(inst->GetOpcode() == Opcode::Add);
2891     if (!IsNegationPattern(inst)) {
2892         return false;
2893     }
2894     auto suspectInst = inst->GetInput(0).GetInst()->GetInput(0).GetInst();
2895     // Case 8
2896     // We sure, that type of Neg's input is Bool. We shue, that Neg has one user
2897     if (suspectInst->GetOpcode() == Opcode::Compare && suspectInst->HasSingleUser()) {
2898         auto compareInst = suspectInst->CastToCompare();
2899         bool isPossible = true;
2900         for (auto &i : inst->GetUsers()) {
2901             if (SkipThisPeepholeInOSR(i.GetInst(), compareInst)) {
2902                 isPossible = false;
2903                 break;
2904             }
2905         }
2906         if (isPossible) {
2907             inst->ReplaceUsers(compareInst);
2908             compareInst->SetCc(GetInverseConditionCode(compareInst->GetCc()));
2909             PEEPHOLE_IS_APPLIED(this, compareInst);
2910             return true;
2911         }
2912     }
2913 
2914     // Case 9
2915     if (TrySimplifyCompareNegation(inst)) {
2916         return true;
2917     }
2918 
2919     // Case 7
2920     // This is used last of all if no case has worked!
2921     if (CreateCompareInsteadOfXorAdd(inst)) {
2922         PEEPHOLE_IS_APPLIED(this, inst);
2923         return true;
2924     }
2925     return false;
2926 }
2927 
TryOptimizeBoxedLoadStoreObject(Inst * inst)2928 bool Peepholes::TryOptimizeBoxedLoadStoreObject(Inst *inst)
2929 {
2930     // We can allow some optimizations over LoadObject and StoreObject instructions
2931     // if their types match boxed types.
2932     auto graph = inst->GetBasicBlock()->GetGraph();
2933     graph->RunPass<ObjectTypePropagation>();
2934 
2935     auto runtime = graph->GetRuntime();
2936     auto typeInfo = inst->GetDataFlowInput(0)->GetObjectTypeInfo();
2937     auto klass = typeInfo.GetClass();
2938     if (klass != nullptr && runtime->IsBoxedClass(klass)) {
2939         inst->ClearFlag(compiler::inst_flags::NO_CSE);
2940         inst->ClearFlag(compiler::inst_flags::NO_HOIST);
2941         return true;
2942     }
2943     return false;
2944 }
2945 
TryOptimizeXorChain(Inst * inst)2946 bool Peepholes::TryOptimizeXorChain(Inst *inst)
2947 {
2948     auto xorInput = inst->GetInput(0).GetInst();
2949     auto xorConstInput = inst->GetInput(1).GetInst();
2950     if (!xorConstInput->IsConst()) {
2951         return false;
2952     }
2953     // Output of Xor1 should be only used by Xor2
2954     if (!inst->HasSingleUser()) {
2955         return false;
2956     }
2957     auto nextXor = inst->GetFirstUser()->GetInst();
2958     if (nextXor->GetOpcode() != Opcode::Xor) {
2959         return false;
2960     }
2961     // Check that both XORs have the same type
2962     if (inst->GetType() != nextXor->GetType()) {
2963         return false;
2964     }
2965     // Both Xors should use the same constant as input
2966     if (nextXor->GetInput(0).GetInst() != xorConstInput && nextXor->GetInput(1).GetInst() != xorConstInput) {
2967         return false;
2968     }
2969     bool isPossible = true;
2970     for (auto &i : nextXor->GetUsers()) {
2971         if (SkipThisPeepholeInOSR(i.GetInst(), xorInput)) {
2972             isPossible = false;
2973             break;
2974         }
2975     }
2976     if (isPossible) {
2977         // Replace all uses of Xor2 output with the input0 of Xor1
2978         nextXor->ReplaceUsers(xorInput);
2979         return true;
2980     }
2981     return false;
2982 }
2983 
2984 }  // namespace ark::compiler
2985