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