• 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 ark::compiler {
23 
InvalidateAnalyses()24 void Peepholes::InvalidateAnalyses()
25 {
26     GetGraph()->InvalidateAnalysis<BoundsAnalysis>();
27     GetGraph()->InvalidateAnalysis<AliasAnalysis>();
28 }
29 
RunImpl()30 bool Peepholes::RunImpl()
31 {
32     GetGraph()->RunPass<DominatorsTree>();
33     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 (input0 == input1 && input0->GetType() == inst->GetType()) {
809         // case 2:
810         // 1.i64 OR v5, v5
811         // 2.i64 INS which use v1
812         // ===>
813         // 1.i64 OR v5, v5
814         // 2.i64 INS which use v5
815         if (SkipThisPeepholeInOSR(inst, input0)) {
816             return;
817         }
818         inst->ReplaceUsers(input0);
819         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
820     } else if (input1->IsConst() && static_cast<ConstantInst *>(input1)->GetIntValue() == static_cast<uint64_t>(0)) {
821         // case 3:
822         // 0.i64 Const 0x000..00
823         // 1.i64 OR v5, v0
824         // 2.i64 INS which use v1
825         // ===>
826         // 0.i64 Const 0x000..00
827         // 1.i64 OR v5, v0
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 
VisitCastCase1(GraphVisitor * v,Inst * inst)1170 void Peepholes::VisitCastCase1([[maybe_unused]] GraphVisitor *v, Inst *inst)
1171 {
1172     // case 1:
1173     // remove redundant cast, when source type is equal with target type
1174     auto input = inst->GetInput(0).GetInst();
1175     if (SkipThisPeepholeInOSR(inst, input)) {
1176         return;
1177     }
1178     inst->ReplaceUsers(input);
1179     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1180 }
1181 
VisitCastCase2(GraphVisitor * v,Inst * inst)1182 void Peepholes::VisitCastCase2([[maybe_unused]] GraphVisitor *v, Inst *inst)
1183 {
1184     // case 2:
1185     // remove redundant cast, when cast from source to target and back
1186     // for bytecode optimizer, this operation may cause mistake for float32-converter pass
1187     auto input = inst->GetInput(0).GetInst();
1188     auto prevType = input->GetType();
1189     auto currType = inst->GetType();
1190     auto graph = inst->GetBasicBlock()->GetGraph();
1191     auto arch = graph->GetArch();
1192     auto origInst = input->GetInput(0).GetInst();
1193     auto origType = origInst->GetType();
1194     if (currType == origType && DataType::GetTypeSize(prevType, arch) > DataType::GetTypeSize(currType, arch)) {
1195         if (SkipThisPeepholeInOSR(inst, origInst)) {
1196             return;
1197         }
1198         inst->ReplaceUsers(origInst);
1199         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1200         return;
1201     }
1202     // case 2.1:
1203     // join two sequent narrowing integer casts, e.g:
1204     // replace
1205     // cast i64toi32
1206     // cast i32toi16
1207     // with
1208     // cast i64toi16
1209     if (ApplyForCastJoin(inst, input, origInst, arch)) {
1210         auto cast = inst->CastToCast();
1211         if (SkipThisPeepholeInOSR(cast, origInst)) {
1212             return;
1213         }
1214         cast->SetOperandsType(origInst->GetType());
1215         cast->SetInput(0, origInst);
1216         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1217         return;
1218     }
1219 }
1220 
VisitCastCase3(GraphVisitor * v,Inst * inst)1221 void Peepholes::VisitCastCase3([[maybe_unused]] GraphVisitor *v, Inst *inst)
1222 {
1223     auto input = inst->GetInput(0).GetInst();
1224     auto currType = inst->GetType();
1225     auto graph = inst->GetBasicBlock()->GetGraph();
1226     auto arch = graph->GetArch();
1227 
1228     // case 3:
1229     // i8.Cast(v1 & 0xff)        = i8.Cast(u8.Cast(v1))   = i8.Cast(v1)
1230     // i16.Cast(v1 & 0xffff)     = i16.Cast(u16.Cast(v1)) = i16.Cast(v1)
1231     // i32.Cast(v1 & 0xffffffff) = i32.Cast(u32.Cast(v1)) = i32.Cast(v1)
1232     auto op0 = input->GetInput(0).GetInst();
1233     if (graph->IsBytecodeOptimizer() && !IsCastAllowedInBytecode(op0)) {
1234         return;
1235     }
1236     auto op1 = input->GetInput(1).GetInst();
1237     auto typeSize = DataType::GetTypeSize(currType, arch);
1238     if (op1->IsConst() && typeSize < DOUBLE_WORD_SIZE) {
1239         auto val = op1->CastToConstant()->GetIntValue();
1240         auto mask = (1ULL << typeSize) - 1;
1241         if ((val & mask) == mask) {
1242             if (SkipThisPeepholeInOSR(inst, op0)) {
1243                 return;
1244             }
1245             inst->SetInput(0, op0);
1246             inst->CastToCast()->SetOperandsType(op0->GetType());
1247             PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1248             return;
1249         }
1250     }
1251 }
1252 
VisitCast(GraphVisitor * v,Inst * inst)1253 void Peepholes::VisitCast([[maybe_unused]] GraphVisitor *v, Inst *inst)
1254 {
1255     if (ConstFoldingCast(inst)) {
1256         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1257         return;
1258     }
1259     auto input = inst->GetInput(0).GetInst();
1260     auto prevType = input->GetType();
1261     auto currType = inst->GetType();
1262     auto graph = inst->GetBasicBlock()->GetGraph();
1263 
1264     if (prevType == currType) {
1265         VisitCastCase1(v, inst);
1266         return;
1267     }
1268 
1269     if (!graph->IsBytecodeOptimizer() && input->GetOpcode() == Opcode::Cast) {
1270         VisitCastCase2(v, inst);
1271         return;
1272     }
1273 
1274     if (input->GetOpcode() == Opcode::And && DataType::GetCommonType(currType) == DataType::INT64) {
1275         VisitCastCase3(v, inst);
1276         return;
1277     }
1278 }
1279 
VisitLenArray(GraphVisitor * v,Inst * inst)1280 void Peepholes::VisitLenArray(GraphVisitor *v, Inst *inst)
1281 {
1282     auto graph = static_cast<Peepholes *>(v)->GetGraph();
1283     if (graph->IsBytecodeOptimizer()) {
1284         return;
1285     }
1286     // 1. .... ->{v2}
1287     // 2. NewArray v1 ->{v3,..}
1288     // 3. LenArray v2 ->{v4, v5...}
1289     // ===>
1290     // 1. .... ->{v2, v4, v5, ...}
1291     // 2. NewArray v1 ->{v3,..}
1292     // 3. LenArray v2
1293     auto input = inst->GetDataFlowInput(inst->GetInput(0).GetInst());
1294     if (input->GetOpcode() == Opcode::NewArray) {
1295         auto arraySize = input->GetDataFlowInput(input->GetInput(NewArrayInst::INDEX_SIZE).GetInst());
1296         // We can't restore array_size register if it was defined out of OSR loop
1297         if (SkipThisPeepholeInOSR(inst, arraySize)) {
1298             return;
1299         }
1300         inst->ReplaceUsers(arraySize);
1301         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1302     }
1303     if (static_cast<Peepholes *>(v)->OptimizeLenArrayForMultiArray(inst, input, 0)) {
1304         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1305     }
1306 }
1307 
VisitPhi(GraphVisitor * v,Inst * inst)1308 void Peepholes::VisitPhi([[maybe_unused]] GraphVisitor *v, Inst *inst)
1309 {
1310     // 2.type  Phi v0, v1 -> v4, v5, ...
1311     // 3.type  Phi v0, v1 -> v5, v6, ...
1312     // ===>
1313     // 2.type  Phi v0, v1 -> v4, v5, v6, ...
1314     // 3.type  Phi v0, v1
1315     if (!inst->GetUsers().Empty()) {
1316         auto block = inst->GetBasicBlock();
1317         auto phi = inst->CastToPhi();
1318         for (auto otherPhi : block->PhiInsts()) {
1319             if (IsPhiUnionPossible(phi, otherPhi->CastToPhi())) {
1320                 // In check above checked, that phi insts in same BB, so no problem with SaveStateOSR
1321                 otherPhi->ReplaceUsers(phi);
1322                 PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1323             }
1324         }
1325     }
1326 
1327     if (TryEliminatePhi(inst->CastToPhi())) {
1328         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1329     }
1330 }
1331 
VisitSqrt(GraphVisitor * v,Inst * inst)1332 void Peepholes::VisitSqrt([[maybe_unused]] GraphVisitor *v, Inst *inst)
1333 {
1334     if (ConstFoldingSqrt(inst)) {
1335         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1336         return;
1337     }
1338 }
1339 
VisitIsInstance(GraphVisitor * v,Inst * inst)1340 void Peepholes::VisitIsInstance(GraphVisitor *v, Inst *inst)
1341 {
1342     static_cast<Peepholes *>(v)->GetGraph()->RunPass<ObjectTypePropagation>();
1343     if (ObjectTypeCheckElimination::TryEliminateIsInstance(inst)) {
1344         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1345         return;
1346     }
1347 }
1348 
VisitCastAnyTypeValue(GraphVisitor * v,Inst * inst)1349 void Peepholes::VisitCastAnyTypeValue(GraphVisitor *v, Inst *inst)
1350 {
1351     if (inst->GetUsers().Empty()) {
1352         // Peepholes can be run before cleanup phase.
1353         return;
1354     }
1355 
1356     auto *cav = inst->CastToCastAnyTypeValue();
1357     auto *cavInput = cav->GetDataFlowInput(0);
1358     if (cavInput->GetOpcode() == Opcode::CastValueToAnyType) {
1359         auto cva = cavInput->CastToCastValueToAnyType();
1360         auto valueInst = cva->GetInput(0).GetInst();
1361         if (SkipThisPeepholeInOSR(cav, valueInst)) {
1362             return;
1363         }
1364         if (cva->GetAnyType() == cav->GetAnyType()) {
1365             // 1. .... -> v2
1366             // 2. CastValueToAnyType v1 -> v3
1367             // 3. AnyTypeCheck v2 -> v3
1368             // 4. CastAnyTypeValue v3 -> v5
1369             // 5. ...
1370             // ===>
1371             // 1. .... -> v2, v5
1372             // 2. CastValueToAnyType v1 -> v3
1373             // 3. AnyTypeCheck v2 -> v3
1374             // 4. CastAnyTypeValue v3
1375             // 5. ...
1376             cav->ReplaceUsers(valueInst);
1377             PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1378             return;
1379         }
1380         auto inputType = valueInst->GetType();
1381         auto dstType = cav->GetType();
1382         auto isDoubleToInt = dstType == DataType::INT32 && inputType == DataType::FLOAT64;
1383         if (IsTypeNumeric(inputType) && IsTypeNumeric(dstType) && (!isDoubleToInt || cav->IsIntegerWasSeen())) {
1384             // 1. .... -> v2
1385             // 2. CastValueToAnyType v1 -> v3
1386             // 3. AnyTypeCheck v2 -> v3
1387             // 4. CastAnyTypeValue v3 -> v5
1388             // 5. ...
1389             // ===>
1390             // 1. .... -> v2, v6
1391             // 2. CastValueToAnyType v1 -> v3
1392             // 3. AnyTypeCheck v2 -> v3
1393             // 4. CastAnyTypeValue v3
1394             // 6. Cast v1 -> v5
1395             // 5. ...
1396             auto cast = CreateAndInsertInst(Opcode::Cast, cav, valueInst);
1397             cast->SetType(dstType);
1398             cast->CastToCast()->SetOperandsType(inputType);
1399             PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1400         }
1401     }
1402 }
1403 
VisitCastValueToAnyType(GraphVisitor * v,Inst * inst)1404 void Peepholes::VisitCastValueToAnyType(GraphVisitor *v, Inst *inst)
1405 {
1406     auto graph = inst->GetBasicBlock()->GetGraph();
1407     auto input = inst->GetInput(0).GetInst();
1408     auto anyType = inst->CastToCastValueToAnyType()->GetAnyType();
1409 
1410     if (input->GetOpcode() == Opcode::CastAnyTypeValue) {
1411         auto cav = input->CastToCastAnyTypeValue();
1412         auto anyInput = cav->GetInput(0).GetInst();
1413         if (SkipThisPeepholeInOSR(inst, anyInput)) {
1414             return;
1415         }
1416         if (anyType == cav->GetAnyType() && cav->GetAllowedInputType() == profiling::AnyInputType::DEFAULT) {
1417             // 1. ... -> v2
1418             // 2. CastAnyTypeValue v1 -> v3
1419             // 3. CastValueToAnyType v2 -> v4
1420             // 4. ...
1421             // ===>
1422             // 1. ... -> v2, v4
1423             // 2. CastAnyTypeValue v1 -> v3
1424             // 3. CastValueToAnyType v2
1425             // 4. ...
1426             inst->ReplaceUsers(anyInput);
1427             PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1428             return;
1429         }
1430     }
1431 
1432     if (graph->IsBytecodeOptimizer() || graph->IsOsrMode()) {
1433         // Find way to enable it in OSR mode.
1434         return;
1435     }
1436 
1437     auto baseType = AnyBaseTypeToDataType(anyType);
1438     // We propogate not INT types in Lowering
1439     if (baseType != DataType::INT32) {
1440         return;
1441     }
1442     // from
1443     // 2.any CastValueToAnyType INT_TYPE v1 -> (v3)
1444     // 3     SaveState                   v2(acc)
1445     //
1446     // to
1447     // 3     SaveState                   v1(acc)
1448     bool isApplied = false;
1449     for (auto it = inst->GetUsers().begin(); it != inst->GetUsers().end();) {
1450         auto userInst = it->GetInst();
1451         if (userInst->IsSaveState()) {
1452             userInst->SetInput(it->GetIndex(), input);
1453             it = inst->GetUsers().begin();
1454             isApplied = true;
1455         } else {
1456             ++it;
1457         }
1458     }
1459     if (isApplied) {
1460         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1461     }
1462 }
1463 
TryPrepareEliminatePrecedingStoreOpcCast(Inst * inst,Arch arch,uint64_t & imm)1464 static bool TryPrepareEliminatePrecedingStoreOpcCast(Inst *inst, Arch arch, uint64_t &imm)
1465 {
1466     auto size = DataType::GetTypeSize(inst->GetType(), arch);
1467     if (size >= DOUBLE_WORD_SIZE) {
1468         return false;
1469     }
1470     auto inputTypeMismatch = IsInputTypeMismatch(inst, 0, arch);
1471 #ifndef NDEBUG
1472     ASSERT(!inputTypeMismatch);
1473 #else
1474     if (inputTypeMismatch) {
1475         return false;
1476     }
1477 #endif
1478     imm = (1ULL << size) - 1;
1479     return true;
1480 }
1481 
1482 template <typename T>
EliminateInstPrecedingStore(GraphVisitor * v,Inst * inst)1483 void Peepholes::EliminateInstPrecedingStore(GraphVisitor *v, Inst *inst)
1484 {
1485     auto graph = static_cast<Peepholes *>(v)->GetGraph();
1486     if (graph->IsBytecodeOptimizer()) {
1487         return;
1488     }
1489     auto arch = graph->GetArch();
1490     auto typeSize = DataType::GetTypeSize(inst->GetType(), arch);
1491     if (DataType::GetCommonType(inst->GetType()) == DataType::INT64 && typeSize < DOUBLE_WORD_SIZE) {
1492         auto storeValueInst = inst->GetInput(T::STORED_INPUT_INDEX).GetInst();
1493         auto mask = (1ULL << typeSize) - 1;
1494         uint64_t imm;
1495 
1496         switch (storeValueInst->GetOpcode()) {
1497             case Opcode::And: {
1498                 auto inputInst1 = storeValueInst->GetInput(1).GetInst();
1499                 if (!inputInst1->IsConst()) {
1500                     return;
1501                 }
1502                 imm = inputInst1->CastToConstant()->GetIntValue();
1503                 break;
1504             }
1505             case Opcode::Cast: {
1506                 if (!TryPrepareEliminatePrecedingStoreOpcCast(storeValueInst, arch, imm)) {
1507                     return;
1508                 }
1509                 break;
1510             }
1511             default:
1512                 return;
1513         }
1514 
1515         auto inputInst = storeValueInst->GetInput(0).GetInst();
1516         if (DataType::GetCommonType(inputInst->GetType()) != DataType::INT64) {
1517             return;
1518         }
1519 
1520         if ((imm & mask) == mask) {
1521             if (SkipThisPeepholeInOSR(inst, inputInst)) {
1522                 return;
1523             }
1524             inst->ReplaceInput(storeValueInst, inputInst);
1525             PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
1526         }
1527     }
1528 }
1529 
VisitStore(GraphVisitor * v,Inst * inst)1530 void Peepholes::VisitStore([[maybe_unused]] GraphVisitor *v, Inst *inst)
1531 {
1532     ASSERT(inst->GetOpcode() == Opcode::Store);
1533     EliminateInstPrecedingStore<StoreMemInst>(v, inst);
1534 }
1535 
VisitStoreObject(GraphVisitor * v,Inst * inst)1536 void Peepholes::VisitStoreObject([[maybe_unused]] GraphVisitor *v, Inst *inst)
1537 {
1538     ASSERT(inst->GetOpcode() == Opcode::StoreObject);
1539     EliminateInstPrecedingStore<StoreObjectInst>(v, inst);
1540 }
1541 
VisitStoreStatic(GraphVisitor * v,Inst * inst)1542 void Peepholes::VisitStoreStatic([[maybe_unused]] GraphVisitor *v, Inst *inst)
1543 {
1544     ASSERT(inst->GetOpcode() == Opcode::StoreStatic);
1545     EliminateInstPrecedingStore<StoreStaticInst>(v, inst);
1546 }
1547 
TryRemoveOverflowCheck(Inst * inst)1548 void Peepholes::TryRemoveOverflowCheck(Inst *inst)
1549 {
1550     auto block = inst->GetBasicBlock();
1551     auto markerHolder = MarkerHolder(block->GetGraph());
1552     if (CanRemoveOverflowCheck(inst, markerHolder.GetMarker())) {
1553         PEEPHOLE_IS_APPLIED(this, inst);
1554         block->RemoveOverflowCheck(inst);
1555         return;
1556     }
1557 }
1558 
VisitAddOverflowCheck(GraphVisitor * v,Inst * inst)1559 void Peepholes::VisitAddOverflowCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1560 {
1561     ASSERT(inst->GetOpcode() == Opcode::AddOverflowCheck);
1562     auto visitor = static_cast<Peepholes *>(v);
1563     visitor->TrySwapInputs(inst);
1564     if (visitor->TrySimplifyAddSubWithZeroInput(inst)) {
1565         inst->ClearFlag(inst_flags::NO_DCE);
1566     } else {
1567         visitor->TryRemoveOverflowCheck(inst);
1568     }
1569 }
1570 
VisitSubOverflowCheck(GraphVisitor * v,Inst * inst)1571 void Peepholes::VisitSubOverflowCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1572 {
1573     ASSERT(inst->GetOpcode() == Opcode::SubOverflowCheck);
1574     auto visitor = static_cast<Peepholes *>(v);
1575     if (visitor->TrySimplifyAddSubWithZeroInput(inst)) {
1576         inst->ClearFlag(inst_flags::NO_DCE);
1577     } else {
1578         visitor->TryRemoveOverflowCheck(inst);
1579     }
1580 }
1581 
VisitNegOverflowAndZeroCheck(GraphVisitor * v,Inst * inst)1582 void Peepholes::VisitNegOverflowAndZeroCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1583 {
1584     ASSERT(inst->GetOpcode() == Opcode::NegOverflowAndZeroCheck);
1585     static_cast<Peepholes *>(v)->TryRemoveOverflowCheck(inst);
1586 }
1587 
1588 // This function check that we can merge two Phi instructions in one basic block.
IsPhiUnionPossible(PhiInst * phi1,PhiInst * phi2)1589 bool Peepholes::IsPhiUnionPossible(PhiInst *phi1, PhiInst *phi2)
1590 {
1591     ASSERT(phi1->GetBasicBlock() == phi2->GetBasicBlock() && phi1->GetInputsCount() == phi2->GetInputsCount());
1592     if (phi1 == phi2 || phi1->GetType() != phi2->GetType()) {
1593         return false;
1594     }
1595     for (auto predBlock : phi1->GetBasicBlock()->GetPredsBlocks()) {
1596         if (phi1->GetPhiDataflowInput(predBlock) != phi2->GetPhiDataflowInput(predBlock)) {
1597             return false;
1598         }
1599     }
1600     if (phi1->GetBasicBlock()->GetGraph()->IsOsrMode()) {
1601         // Values in vregs for phi1 and phi2 may be different in interpreter mode,
1602         // so we don't merge phis if they have SaveStateOsr users
1603         for (auto phi : {phi1, phi2}) {
1604             for (auto &user : phi->GetUsers()) {
1605                 if (user.GetInst()->GetOpcode() == Opcode::SaveStateOsr) {
1606                     return false;
1607                 }
1608             }
1609         }
1610     }
1611     return true;
1612 }
1613 
1614 // Create new instruction instead of current inst
CreateAndInsertInst(Opcode newOpc,Inst * currInst,Inst * input0,Inst * input1)1615 Inst *Peepholes::CreateAndInsertInst(Opcode newOpc, Inst *currInst, Inst *input0, Inst *input1)
1616 {
1617     auto bb = currInst->GetBasicBlock();
1618     auto graph = bb->GetGraph();
1619     auto newInst = graph->CreateInst(newOpc);
1620     newInst->SetType(currInst->GetType());
1621     newInst->SetPc(currInst->GetPc());
1622 #ifdef PANDA_COMPILER_DEBUG_INFO
1623     newInst->SetCurrentMethod(currInst->GetCurrentMethod());
1624 #endif
1625     // It is a wrapper, so we don't do logic check for SaveStateOSR
1626     currInst->ReplaceUsers(newInst);
1627     newInst->SetInput(0, input0);
1628     if (input1 != nullptr) {
1629         newInst->SetInput(1, input1);
1630     }
1631     bb->InsertAfter(newInst, currInst);
1632     return newInst;
1633 }
1634 
1635 // Try put constant in second input
TrySwapInputs(Inst * inst)1636 void Peepholes::TrySwapInputs(Inst *inst)
1637 {
1638     [[maybe_unused]] auto opc = inst->GetOpcode();
1639     ASSERT(opc == Opcode::Add || opc == Opcode::And || opc == Opcode::Or || opc == Opcode::Xor || opc == Opcode::Min ||
1640            opc == Opcode::Max || opc == Opcode::Mul || opc == Opcode::AddOverflowCheck);
1641     if (inst->GetInput(0).GetInst()->IsConst()) {
1642         inst->SwapInputs();
1643         PEEPHOLE_IS_APPLIED(this, inst);
1644     }
1645 }
1646 
TrySimplifyShifts(Inst * inst)1647 void Peepholes::TrySimplifyShifts(Inst *inst)
1648 {
1649     auto opc = inst->GetOpcode();
1650     ASSERT(opc == Opcode::Shl || opc == Opcode::Shr || opc == Opcode::AShr);
1651     auto block = inst->GetBasicBlock();
1652     auto graph = block->GetGraph();
1653     auto input0 = inst->GetInput(0).GetInst();
1654     auto input1 = inst->GetInput(1).GetInst();
1655     if (input1->IsConst()) {
1656         auto cnst = static_cast<ConstantInst *>(input1);
1657         // Zero shift
1658         // 2. shl/shr/ashr v1, 0 -> {...}
1659         // 3. Some inst v2
1660         // ===>
1661         // 2. shl/shr/ashr v1, 0 -> {}
1662         // 3. Some inst v1
1663         if (cnst->IsEqualConst(0)) {
1664             if (SkipThisPeepholeInOSR(inst, input0)) {
1665                 return;
1666             }
1667             inst->ReplaceUsers(input0);
1668             PEEPHOLE_IS_APPLIED(this, inst);
1669             return;
1670         }
1671         // Repeated arithmetic with constants
1672         // 2. shl/shr/ashr v1, const1 -> {v3, ...}
1673         // 3. shl/shr/ashr v2, const2 -> {...}
1674         // ===>
1675         // 2. shl/shr/ashr v1, const1 -> {...}
1676         // 3. shl/shr/ashr v1, const2 + const1 -> {...}
1677         bool osrBlockedPeephole = false;
1678         if (opc == input0->GetOpcode() && TryCombineShiftConst(inst, cnst, &osrBlockedPeephole)) {
1679             PEEPHOLE_IS_APPLIED(this, inst);
1680             return;
1681         }
1682         if (osrBlockedPeephole) {
1683             return;
1684         }
1685         uint64_t sizeMask = DataType::GetTypeSize(inst->GetType(), graph->GetArch()) - 1;
1686         auto cnstValue = cnst->GetIntValue();
1687         // Shift by a constant greater than the type size
1688         // 2. shl/shr/ashr v1, big_const -> {...}
1689         // ===>
1690         // 2. shl/shr/ashr v1, size_mask & big_const -> {...}
1691         if (graph->IsBytecodeOptimizer() && IsInt32Bit(inst->GetType())) {
1692             sizeMask = static_cast<uint32_t>(sizeMask);
1693             cnstValue = static_cast<uint32_t>(cnstValue);
1694         }
1695         if (sizeMask < cnstValue) {
1696             ConstantInst *shift = ConstFoldingCreateIntConst(inst, sizeMask & cnstValue);
1697             inst->SetInput(1, shift);
1698             PEEPHOLE_IS_APPLIED(this, inst);
1699             return;
1700         }
1701     }
1702 }
1703 
TrySimplifyAddSubWithZeroInput(Inst * inst)1704 bool Peepholes::TrySimplifyAddSubWithZeroInput(Inst *inst)
1705 {
1706     auto input0 = inst->GetInput(0).GetInst();
1707     auto input1 = inst->GetInput(1).GetInst();
1708     if (input1->IsConst()) {
1709         auto cnst = input1->CastToConstant();
1710         if (cnst->IsEqualConstAllTypes(0)) {
1711             if (SkipThisPeepholeInOSR(inst, input0)) {
1712                 return false;
1713             }
1714             inst->ReplaceUsers(input0);
1715             PEEPHOLE_IS_APPLIED(this, inst);
1716             return true;
1717         }
1718     }
1719     return false;
1720 }
1721 
TrySimplifyAddSubWithConstInput(Inst * inst)1722 bool Peepholes::TrySimplifyAddSubWithConstInput(Inst *inst)
1723 {
1724     if (TrySimplifyAddSubWithZeroInput(inst)) {
1725         return true;
1726     }
1727     auto input0 = inst->GetInput(0).GetInst();
1728     auto input1 = inst->GetInput(1).GetInst();
1729     if (input1->IsConst()) {
1730         auto cnst = input1->CastToConstant();
1731         if ((input0->GetOpcode() == Opcode::Add || input0->GetOpcode() == Opcode::Sub)) {
1732             bool osrBlockedPeephole = false;
1733             if (TryCombineAddSubConst(inst, cnst, &osrBlockedPeephole)) {
1734                 PEEPHOLE_IS_APPLIED(this, inst);
1735                 return true;
1736             }
1737             if (osrBlockedPeephole) {
1738                 return true;
1739             }
1740         }
1741     } else if (input0->IsConst() && inst->GetOpcode() == Opcode::Sub && !IsFloatType(inst->GetType())) {
1742         // Fold `sub 0, v1` into `neg v1`.
1743         auto cnst = input0->CastToConstant();
1744         if (cnst->IsEqualConstAllTypes(0)) {
1745             // There can't be a SaveStateOSR between inst(sub) and new inst(neg), so we don't have a check
1746             CreateAndInsertInst(Opcode::Neg, inst, input1);
1747             PEEPHOLE_IS_APPLIED(this, inst);
1748             return true;
1749         }
1750     }
1751     return false;
1752 }
1753 
1754 template <Opcode OPC, int IDX>
TrySimplifyAddSub(Inst * inst,Inst * input0,Inst * input1)1755 void Peepholes::TrySimplifyAddSub(Inst *inst, Inst *input0, Inst *input1)
1756 {
1757     if (input0->GetOpcode() == OPC && input0->GetInput(1 - IDX).GetInst() == input1) {
1758         auto prevInput = input0->GetInput(IDX).GetInst();
1759         if (inst->GetType() == prevInput->GetType()) {
1760             if (SkipThisPeepholeInOSR(inst, prevInput)) {
1761                 return;
1762             }
1763             inst->ReplaceUsers(prevInput);
1764             PEEPHOLE_IS_APPLIED(this, inst);
1765             return;
1766         }
1767     }
1768 }
1769 
TrySimplifyAddSubAdd(Inst * inst,Inst * input0,Inst * input1)1770 bool Peepholes::TrySimplifyAddSubAdd(Inst *inst, Inst *input0, Inst *input1)
1771 {
1772     // (a - b) + (b + c) = a + c
1773     if (input0->GetInput(1) == input1->GetInput(0)) {
1774         auto newInput0 = input0->GetInput(0).GetInst();
1775         auto newInput1 = input1->GetInput(1).GetInst();
1776         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1777             return true;
1778         }
1779         inst->SetInput(0, newInput0);
1780         inst->SetInput(1, newInput1);
1781         PEEPHOLE_IS_APPLIED(this, inst);
1782         return true;
1783     }
1784 
1785     // (a - b) + (c + b) = a + c
1786     if (input0->GetInput(1) == input1->GetInput(1)) {
1787         auto newInput0 = input0->GetInput(0).GetInst();
1788         auto newInput1 = input1->GetInput(0).GetInst();
1789         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1790             return true;
1791         }
1792         inst->SetInput(0, newInput0);
1793         inst->SetInput(1, newInput1);
1794         PEEPHOLE_IS_APPLIED(this, inst);
1795         return true;
1796     }
1797 
1798     return false;
1799 }
1800 
TrySimplifyAddSubSub(Inst * inst,Inst * input0,Inst * input1)1801 bool Peepholes::TrySimplifyAddSubSub(Inst *inst, Inst *input0, Inst *input1)
1802 {
1803     // (a - b) + (b - c) = a - c
1804     if (input0->GetInput(1) == input1->GetInput(0)) {
1805         auto newInput0 = input0->GetInput(0).GetInst();
1806         auto newInput1 = input1->GetInput(1).GetInst();
1807         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1808             return true;
1809         }
1810 
1811         inst->SetOpcode(Opcode::Sub);
1812         inst->SetInput(0, newInput0);
1813         inst->SetInput(1, newInput1);
1814         PEEPHOLE_IS_APPLIED(this, inst);
1815         return true;
1816     }
1817 
1818     // (a - b) + (c - a) = c - b
1819     if (input0->GetInput(0) == input1->GetInput(1)) {
1820         auto newInput0 = input1->GetInput(0).GetInst();
1821         auto newInput1 = input0->GetInput(1).GetInst();
1822         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1823             return true;
1824         }
1825         inst->SetOpcode(Opcode::Sub);
1826         inst->SetInput(0, newInput0);
1827         inst->SetInput(1, newInput1);
1828         PEEPHOLE_IS_APPLIED(this, inst);
1829         return true;
1830     }
1831 
1832     return false;
1833 }
1834 
TrySimplifySubAddAdd(Inst * inst,Inst * input0,Inst * input1)1835 bool Peepholes::TrySimplifySubAddAdd(Inst *inst, Inst *input0, Inst *input1)
1836 {
1837     // (a + b) - (a + c) = b - c
1838     if (input0->GetInput(0) == input1->GetInput(0)) {
1839         auto newInput0 = input0->GetInput(1).GetInst();
1840         auto newInput1 = input1->GetInput(1).GetInst();
1841         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1842             return true;
1843         }
1844         inst->SetInput(0, newInput0);
1845         inst->SetInput(1, newInput1);
1846         PEEPHOLE_IS_APPLIED(this, inst);
1847         return true;
1848     }
1849 
1850     // (a + b) - (c + a) = b - c
1851     if (input0->GetInput(0) == input1->GetInput(1)) {
1852         auto newInput0 = input0->GetInput(1).GetInst();
1853         auto newInput1 = input1->GetInput(0).GetInst();
1854         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1855             return true;
1856         }
1857         inst->SetInput(0, newInput0);
1858         inst->SetInput(1, newInput1);
1859         PEEPHOLE_IS_APPLIED(this, inst);
1860         return true;
1861     }
1862 
1863     // (a + b) - (c + b) = a - c
1864     if (input0->GetInput(1) == input1->GetInput(1)) {
1865         auto newInput0 = input0->GetInput(0).GetInst();
1866         auto newInput1 = input1->GetInput(0).GetInst();
1867         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1868             return true;
1869         }
1870         inst->SetInput(0, newInput0);
1871         inst->SetInput(1, newInput1);
1872         PEEPHOLE_IS_APPLIED(this, inst);
1873         return true;
1874     }
1875 
1876     // (a + b) - (b + c) = a - c
1877     if (input0->GetInput(1) == input1->GetInput(0)) {
1878         auto newInput0 = input0->GetInput(0).GetInst();
1879         auto newInput1 = input1->GetInput(1).GetInst();
1880         if (SkipThisPeepholeInOSR(inst, newInput0) || SkipThisPeepholeInOSR(inst, newInput1)) {
1881             return true;
1882         }
1883         inst->SetInput(0, newInput0);
1884         inst->SetInput(1, newInput1);
1885         PEEPHOLE_IS_APPLIED(this, inst);
1886         return true;
1887     }
1888 
1889     return false;
1890 }
1891 
CanReassociateShlShlAddSub(Inst * inst)1892 static bool CanReassociateShlShlAddSub(Inst *inst)
1893 {
1894     if (inst->GetOpcode() != Opcode::Sub && inst->GetOpcode() != Opcode::Add) {
1895         return false;
1896     }
1897     if (IsFloatType(inst->GetType())) {
1898         return false;
1899     }
1900     auto input1 = inst->GetInput(1).GetInst();
1901     if (input1->GetOpcode() != Opcode::Sub && input1->GetOpcode() != Opcode::Add) {
1902         return false;
1903     }
1904     // Potentially inst and input1 can have different types (e.g. UINT32 and UINT16).
1905     if (input1->GetType() != inst->GetType()) {
1906         return false;
1907     }
1908     if (!input1->HasSingleUser()) {
1909         return false;
1910     }
1911     auto shl0 = input1->GetInput(0).GetInst();
1912     if (shl0->GetOpcode() != Opcode::Shl || !shl0->HasSingleUser()) {
1913         return false;
1914     }
1915     auto shl1 = input1->GetInput(1).GetInst();
1916     if (shl1->GetOpcode() != Opcode::Shl || !shl1->HasSingleUser() || shl1->GetInput(0) != shl0->GetInput(0)) {
1917         return false;
1918     }
1919     if (!shl0->GetInput(1).GetInst()->IsConst() || !shl1->GetInput(1).GetInst()->IsConst()) {
1920         return false;
1921     }
1922     auto input0 = inst->GetInput(0).GetInst();
1923     bool check = !input0->IsConst() && !input0->IsParameter() && !input0->IsDominate(input1);
1924     return !check;
1925 }
1926 
1927 /**
1928  * The goal is to transform the following sequence:
1929  *
1930  * shl v1, v0, const1;
1931  * shl v2, v0, const2;
1932  * add/sub v3, v1, v2;
1933  * add/sub v5, v4, v3;
1934  *
1935  * so as to make it ready for the lowering with shifted operands.
1936  *
1937  * add v3, v1, v2;
1938  * add v5, v4, v3;
1939  * ===>
1940  * add v6, v4, v1;
1941  * add v5, v6, v2;
1942  *
1943  * add v3, v1, v2;
1944  * sub v5, v4, v3;
1945  * ===>
1946  * sub v6, v4, v1;
1947  * sub v5, v6, v2;
1948  *
1949  * sub v3, v1, v2;
1950  * add v5, v4, v3;
1951  * ===>
1952  * add v6, v4, v1;
1953  * sub v5, v6, v2;
1954  *
1955  * sub v3, v1, v2;
1956  * sub v5, v4, v3;
1957  * ===>
1958  * sub v6, v4, v1;
1959  * add v5, v6, v2;
1960  */
TryReassociateShlShlAddSub(Inst * inst)1961 bool Peepholes::TryReassociateShlShlAddSub(Inst *inst)
1962 {
1963     if (!CanReassociateShlShlAddSub(inst)) {
1964         return false;
1965     }
1966     auto input0 = inst->GetInput(0).GetInst();
1967     auto input1 = inst->GetInput(1).GetInst();
1968     auto shl0 = input1->GetInput(0).GetInst();
1969     auto shl1 = input1->GetInput(1).GetInst();
1970     Opcode opInput1 = inst->GetInput(1).GetInst()->GetOpcode();
1971     Opcode opInst = inst->GetOpcode();
1972     if (opInst == Opcode::Sub && opInput1 == Opcode::Add) {
1973         // input0 - (shl0 + shl1) -> (input0 - shl0) - shl1
1974         opInput1 = Opcode::Sub;
1975     } else if (opInst == Opcode::Add && opInput1 == Opcode::Sub) {
1976         // input0 + (shl0 - shl1) -> (input0 + shl0) - shl1
1977         opInput1 = Opcode::Add;
1978         opInst = Opcode::Sub;
1979     } else if (opInst == Opcode::Sub && opInput1 == Opcode::Sub) {
1980         // input0 - (shl0 - shl1) -> (input0 - shl0) + shl1
1981         opInst = Opcode::Add;
1982     } else if (opInst != Opcode::Add && opInput1 != Opcode::Add) {
1983         UNREACHABLE();
1984     }
1985     // "input1" and "new_input0" one by one, so we don't should to check "SaveStateOSR" between this insts,
1986     // same for: "inst" and **INST WITHOUT NAME**. We should check only between "inst" and "input1"
1987     if (SkipThisPeepholeInOSR(inst, input1)) {
1988         return true;
1989     }
1990     auto newInput0 = CreateAndInsertInst(opInput1, input1, input0, shl0);
1991     CreateAndInsertInst(opInst, inst, newInput0, shl1);
1992     return true;
1993 }
1994 
TrySimplifyNeg(Inst * inst)1995 void Peepholes::TrySimplifyNeg(Inst *inst)
1996 {
1997     auto input0 = inst->GetInput(0).GetInst();
1998     auto input1 = inst->GetInput(1).GetInst();
1999     if ((input0->GetOpcode() == Opcode::Neg && SkipThisPeepholeInOSR(inst, input0->GetInput(0).GetInst())) ||
2000         (input1->GetOpcode() == Opcode::Neg && SkipThisPeepholeInOSR(inst, input1->GetInput(0).GetInst()))) {
2001         return;
2002     }
2003 
2004     // Case 5
2005     if (input0->GetOpcode() == Opcode::Neg && !input1->IsConst()) {
2006         inst->SetInput(0, input1);
2007         inst->SetInput(1, input0);
2008         std::swap(input0, input1);
2009     }
2010     if (input1->GetOpcode() == Opcode::Neg) {
2011         inst->SetOpcode(Opcode::Sub);
2012         inst->SetInput(1, input1->GetInput(0).GetInst());
2013         PEEPHOLE_IS_APPLIED(this, inst);
2014         return;
2015     }
2016 }
2017 
TrySimplifyCompareNegation(Inst * inst)2018 bool Peepholes::TrySimplifyCompareNegation(Inst *inst)
2019 {
2020     ASSERT(inst != nullptr);
2021     ASSERT(inst->GetOpcode() == Opcode::Add);
2022 
2023     // Case 9: Neg -> Add -> Compare
2024     bool optimized = false;
2025     for (auto &userAdd : inst->GetUsers()) {
2026         auto suspectInst = userAdd.GetInst();
2027         if (suspectInst->GetOpcode() != Opcode::Compare) {
2028             continue;
2029         }
2030         auto compareInst = suspectInst->CastToCompare();
2031         if (compareInst->GetOperandsType() != DataType::BOOL ||
2032             (compareInst->GetCc() != ConditionCode::CC_EQ && compareInst->GetCc() != ConditionCode::CC_NE)) {
2033             continue;
2034         }
2035 
2036         unsigned indexCast = compareInst->GetInput(0).GetInst() == inst ? 0 : 1;
2037         auto boolValue = inst->GetInput(0).GetInst()->GetInput(0).GetInst();
2038         if (SkipThisPeepholeInOSR(inst, boolValue)) {
2039             continue;
2040         }
2041         compareInst->SetInput(indexCast, boolValue);
2042         compareInst->SetCc(GetInverseConditionCode(compareInst->GetCc()));
2043         PEEPHOLE_IS_APPLIED(this, compareInst);
2044         optimized = true;
2045     }
2046     return optimized;
2047 }
2048 
TryReplaceDivByShift(Inst * inst)2049 void Peepholes::TryReplaceDivByShift(Inst *inst)
2050 {
2051     auto bb = inst->GetBasicBlock();
2052     auto graph = bb->GetGraph();
2053     if (graph->IsBytecodeOptimizer()) {
2054         return;
2055     }
2056     auto input0 = inst->GetInput(0).GetInst();
2057     auto input1 = inst->GetInput(1).GetInst();
2058     ASSERT(input1->IsConst());
2059     uint64_t unsignedVal = input1->CastToConstant()->GetIntValue();
2060     if (!DataType::IsTypeSigned(inst->GetType())) {
2061         // case 3:
2062         // 0.unsigned Parameter
2063         // 1.i64 Const 2^n -> {v2}
2064         // 2.un-signed DIV v0, v1 -> {v3}
2065         // 3.unsigned INST v2
2066         // ===>
2067         // 0.unsigned Parameter
2068         // 1.i64 Const n -> {v2}
2069         // 2.un-signed SHR v0, v1 -> {v3}
2070         // 3.unsigned INST v2
2071         int64_t n = GetPowerOfTwo(unsignedVal);
2072         if (n != -1) {
2073             auto power = graph->FindOrCreateConstant(n);
2074             CreateAndInsertInst(Opcode::Shr, inst, input0, power);
2075             PEEPHOLE_IS_APPLIED(this, inst);
2076         }
2077     }
2078 }
2079 
TrySimplifyCompareCaseInputInv(Inst * inst,Inst * input)2080 bool Peepholes::TrySimplifyCompareCaseInputInv(Inst *inst, Inst *input)
2081 {
2082     for (auto &user : inst->GetUsers()) {
2083         auto opcode = user.GetInst()->GetOpcode();
2084         if (opcode != Opcode::If && opcode != Opcode::IfImm) {
2085             return false;
2086         }
2087     }
2088     if (SkipThisPeepholeInOSR(inst, input)) {
2089         return true;
2090     }
2091     for (auto &user : inst->GetUsers()) {
2092         auto opcode = user.GetInst()->GetOpcode();
2093         if (opcode == Opcode::If) {
2094             user.GetInst()->CastToIf()->InverseConditionCode();
2095         } else if (opcode == Opcode::IfImm) {
2096             user.GetInst()->CastToIfImm()->InverseConditionCode();
2097         } else {
2098             UNREACHABLE();
2099         }
2100     }
2101     inst->ReplaceUsers(input);
2102     return true;
2103 }
2104 
2105 // Eliminates double comparison if possible
2106 // 4.i64  Constant                   0x0 -> (v6)
2107 // 5.b    ### Some abstract expression that return boolean ###
2108 // 6.b    Compare EQ i32             v5, v4 -> (v7)
2109 // 7.     IfImm NE b                 v6, 0x0
2110 // =======>
2111 // 4.i64  Constant                   0x0 -> (v6)
2112 // 5.b    ### Some abstract expression that return boolean ###
2113 // 6.b    Compare EQ i32             v5, v4
2114 // 7.     IfImm EQ b                 v5, 0x0
TrySimplifyCompareWithBoolInput(Inst * inst,bool * isOsrBlocked)2115 bool Peepholes::TrySimplifyCompareWithBoolInput(Inst *inst, bool *isOsrBlocked)
2116 {
2117     ASSERT(isOsrBlocked != nullptr);
2118     auto compare = inst->CastToCompare();
2119     bool swap = false;
2120     Inst *input = nullptr;
2121     ConstantInst *constInput = nullptr;
2122     if (!GetInputsOfCompareWithConst(compare, &input, &constInput, &swap)) {
2123         return false;
2124     }
2125     if (input->GetType() != DataType::BOOL) {
2126         return false;
2127     }
2128     ConditionCode cc = swap ? SwapOperandsConditionCode(compare->GetCc()) : compare->GetCc();
2129     InputCode code = GetInputCode(constInput, cc);
2130     if (code == INPUT_TRUE || code == INPUT_FALSE) {
2131         // We create constant, so we don't need to check SaveStateOSR between insts
2132         compare->ReplaceUsers(ConstFoldingCreateIntConst(compare, code == INPUT_TRUE ? 1 : 0));
2133         return true;
2134     }
2135     if (code == INPUT_ORIG) {
2136         if (SkipThisPeepholeInOSR(compare, input)) {
2137             *isOsrBlocked = true;
2138             return true;
2139         }
2140         compare->ReplaceUsers(input);
2141         return true;
2142     }
2143     if (code == INPUT_INV) {
2144         return TrySimplifyCompareCaseInputInv(compare, input);
2145     }
2146     UNREACHABLE();
2147     return false;
2148 }
2149 
2150 // Checks if float const is really an integer const and replace it
TryReplaceFloatConstToIntConst(Inst ** castInput,Inst ** constInst)2151 static bool TryReplaceFloatConstToIntConst([[maybe_unused]] Inst **castInput, Inst **constInst)
2152 {
2153     ASSERT(*constInst != nullptr);
2154     ASSERT(*castInput != nullptr);
2155     auto type = (*constInst)->CastToConstant()->GetType();
2156     if (type == DataType::FLOAT32) {
2157         static constexpr auto MAX_SAFE_FLOAT32 =
2158             static_cast<float>(static_cast<uint32_t>(1U) << static_cast<uint32_t>(std::numeric_limits<float>::digits));
2159         float value = (*constInst)->CastToConstant()->GetFloatValue();
2160         if (value < -MAX_SAFE_FLOAT32 || value > MAX_SAFE_FLOAT32) {
2161             // Not applicable to numbers out of unchangeable f32->i32 range
2162             return false;
2163         }
2164         if (std::trunc(value) != value) {
2165             return false;
2166         }
2167         auto *graph = (*constInst)->GetBasicBlock()->GetGraph();
2168         auto *newCnst = graph->FindOrCreateConstant(static_cast<int32_t>(value));
2169         *constInst = newCnst;
2170         *castInput = (*castInput)->GetInput(0).GetInst();
2171         return true;
2172     }
2173     if (type == DataType::FLOAT64) {
2174         static constexpr auto MAX_SAFE_FLOAT64 = static_cast<double>(
2175             static_cast<uint64_t>(1U) << static_cast<uint64_t>(std::numeric_limits<double>::digits));
2176         double value = (*constInst)->CastToConstant()->GetDoubleValue();
2177         if (value < -MAX_SAFE_FLOAT64 || value > MAX_SAFE_FLOAT64) {
2178             // Not applicable to numbers out of unchangeable f64->i64 range
2179             return false;
2180         }
2181         if (std::trunc(value) != value) {
2182             return false;
2183         }
2184         auto *graph = (*constInst)->GetBasicBlock()->GetGraph();
2185         auto *newCnst = graph->FindOrCreateConstant(static_cast<int64_t>(value));
2186         *constInst = newCnst;
2187         auto *candidateInput = (*castInput)->GetInput(0).GetInst();
2188         if ((value < static_cast<double>(INT32_MIN) || value > static_cast<double>(INT32_MAX)) &&
2189             candidateInput->GetType() != DataType::INT64) {
2190             // In i32 comparison constant will be truncated to i32
2191             // If it is more than INT32 range, it is necessary to insert cast i32 -> i64
2192             auto *newCast =
2193                 graph->CreateInstCast(DataType::INT64, INVALID_PC, candidateInput, candidateInput->GetType());
2194             (*castInput)->InsertAfter(newCast);
2195             *castInput = newCast;
2196         } else {
2197             *castInput = candidateInput;
2198         }
2199         return true;
2200     }
2201     return false;
2202 }
2203 
2204 // 6.i32  Cmp        v5, v1
2205 // 7.b    Compare    v6, 0
2206 // 9.     IfImm NE b v7, 0x0
2207 // =======>
2208 // 6.i32  Cmp        v5, v1
2209 // 7.b    Compare    v5, v1
2210 // 9.     IfImm NE b v7, 0x0
TrySimplifyCmpCompareWithZero(Inst * inst,bool * isOsrBlocked)2211 bool Peepholes::TrySimplifyCmpCompareWithZero(Inst *inst, bool *isOsrBlocked)
2212 {
2213     ASSERT(isOsrBlocked != nullptr);
2214     auto compare = inst->CastToCompare();
2215     if (compare->GetBasicBlock()->GetGraph()->IsBytecodeOptimizer()) {
2216         return false;
2217     }
2218     bool swap = false;
2219     Inst *input = nullptr;
2220     ConstantInst *constInput = nullptr;
2221     if (!GetInputsOfCompareWithConst(compare, &input, &constInput, &swap)) {
2222         return false;
2223     }
2224     if (input->GetOpcode() != Opcode::Cmp) {
2225         return false;
2226     }
2227     if (!constInput->IsEqualConstAllTypes(0)) {
2228         return false;
2229     }
2230     auto input0 = input->GetInput(0).GetInst();
2231     auto input1 = input->GetInput(1).GetInst();
2232     if (SkipThisPeepholeInOSR(compare, input0) || SkipThisPeepholeInOSR(compare, input1)) {
2233         *isOsrBlocked = true;
2234         return true;
2235     }
2236     auto cmpOpType = input->CastToCmp()->GetOperandsType();
2237     if (IsFloatType(cmpOpType)) {
2238         ASSERT(compare->GetOperandsType() == DataType::INT32);
2239         if (!TrySimplifyFloatCmpCompare(&input0, &input1, &cmpOpType, input, &swap)) {
2240             return false;
2241         }
2242     }
2243     ConditionCode cc = swap ? SwapOperandsConditionCode(compare->GetCc()) : compare->GetCc();
2244     if (!IsTypeSigned(cmpOpType)) {
2245         ASSERT(cc == ConditionCode::CC_EQ || cc == ConditionCode::CC_NE || IsSignedConditionCode(cc));
2246         // If Cmp operands are unsigned then Compare.CC must be converted to unsigned.
2247         cc = InverseSignednessConditionCode(cc);
2248     }
2249     compare->SetInput(0, input0);
2250     compare->SetInput(1, input1);
2251     compare->SetOperandsType(cmpOpType);
2252     compare->SetCc(cc);
2253     return true;
2254 }
2255 
TrySimplifyFloatCmpCompare(Inst ** input0,Inst ** input1,DataType::Type * cmpOpType,Inst * compareInput,bool * swap)2256 bool Peepholes::TrySimplifyFloatCmpCompare(Inst **input0, Inst **input1, DataType::Type *cmpOpType, Inst *compareInput,
2257                                            bool *swap)
2258 {
2259     if (CheckFcmpInputs(*input0, *input1)) {
2260         *input0 = (*input0)->GetInput(0).GetInst();
2261         *input1 = (*input1)->GetInput(0).GetInst();
2262         *cmpOpType = DataType::INT32;
2263     } else if (CheckFcmpWithConstInput(*input0, *input1)) {
2264         bool cmpSwap = false;
2265         Inst *cmpCastInput = nullptr;
2266         ConstantInst *cmpConstInput = nullptr;
2267         if (!GetInputsOfCompareWithConst(compareInput, &cmpCastInput, &cmpConstInput, swap)) {
2268             return false;
2269         }
2270         Inst *cmpConstInst = static_cast<Inst *>(cmpConstInput);
2271         if (!TryReplaceFloatConstToIntConst(&cmpCastInput, &cmpConstInst)) {
2272             return false;
2273         }
2274         *input0 = cmpCastInput;
2275         *input1 = cmpConstInst;
2276         *cmpOpType = (*input0)->GetType();
2277         *swap = (*swap) ^ cmpSwap;
2278     } else {
2279         return false;
2280     }
2281     return true;
2282 }
2283 
TrySimplifyTestEqualInputs(Inst * inst)2284 bool Peepholes::TrySimplifyTestEqualInputs(Inst *inst)
2285 {
2286     auto cmpInst = inst->CastToCompare();
2287     if (cmpInst->GetCc() != ConditionCode::CC_TST_EQ && cmpInst->GetCc() != ConditionCode::CC_TST_NE) {
2288         return false;
2289     }
2290     auto input0 = inst->GetInput(0).GetInst();
2291     auto input1 = inst->GetInput(1).GetInst();
2292     if (input0 != input1) {
2293         return false;
2294     }
2295     if (cmpInst->GetCc() == ConditionCode::CC_TST_EQ) {
2296         cmpInst->SetCc(ConditionCode::CC_EQ);
2297     } else {
2298         cmpInst->SetCc(ConditionCode::CC_NE);
2299     }
2300     // We create constant, so we don't need to check SaveStateOSR between insts
2301     cmpInst->SetInput(1, ConstFoldingCreateIntConst(input1, 0));
2302     return true;
2303 }
2304 
TrySimplifyCompareAndZero(Inst * inst,bool * isOsrBlocked)2305 bool Peepholes::TrySimplifyCompareAndZero(Inst *inst, bool *isOsrBlocked)
2306 {
2307     ASSERT(isOsrBlocked != nullptr);
2308     if (inst->GetBasicBlock()->GetGraph()->IsBytecodeOptimizer()) {
2309         return false;
2310     }
2311     auto cmpInst = inst->CastToCompare();
2312     auto cc = cmpInst->GetCc();
2313     if (cc != CC_EQ && cc != CC_NE) {
2314         return false;
2315     }
2316     bool swap = false;
2317     Inst *input = nullptr;
2318     ConstantInst *constInput = nullptr;
2319     if (!GetInputsOfCompareWithConst(cmpInst, &input, &constInput, &swap)) {
2320         return false;
2321     }
2322     if (input->GetOpcode() != Opcode::And || !input->HasSingleUser() || !constInput->IsEqualConstAllTypes(0)) {
2323         return false;
2324     }
2325     // 2.i32 And                  v0, v1
2326     // 3.i32 Constant             0x0
2327     // 4.b   Compare CC_EQ/CC_NE  v2, v3
2328     // =======>
2329     // 4.b   Compare CC_TST_EQ/CC_TST_NE  v0, v1
2330 
2331     if (SkipThisPeepholeInOSR(cmpInst, input->GetInput(0).GetInst())) {
2332         *isOsrBlocked = true;
2333         return true;
2334     }
2335     cmpInst->SetCc(cc == CC_EQ ? CC_TST_EQ : CC_TST_NE);
2336     cmpInst->SetInput(0, input->GetInput(0).GetInst());
2337     cmpInst->SetInput(1, input->GetInput(1).GetInst());
2338     return true;
2339 }
2340 
TrySimplifyCompareLenArrayWithZero(Inst * inst)2341 bool Peepholes::TrySimplifyCompareLenArrayWithZero(Inst *inst)
2342 {
2343     auto compare = inst->CastToCompare();
2344     bool swap = false;
2345     Inst *input = nullptr;
2346     ConstantInst *constInput = nullptr;
2347     if (!GetInputsOfCompareWithConst(compare, &input, &constInput, &swap)) {
2348         return false;
2349     }
2350     if (input->GetOpcode() != Opcode::LenArray || !constInput->IsEqualConstAllTypes(0)) {
2351         return false;
2352     }
2353 
2354     ConditionCode cc = swap ? SwapOperandsConditionCode(compare->GetCc()) : compare->GetCc();
2355     if (cc == CC_GE || cc == CC_LT) {
2356         // We create constant, so we don't need to check SaveStateOSR between insts
2357         compare->ReplaceUsers(ConstFoldingCreateIntConst(compare, cc == CC_GE ? 1 : 0));
2358         return true;
2359     }
2360     return false;
2361 }
2362 
2363 // Try to combine constants when arithmetic operations with constants are repeated
2364 template <typename T>
TryCombineConst(Inst * inst,ConstantInst * cnst1,T combine,bool * isOsrBlocked)2365 bool Peepholes::TryCombineConst(Inst *inst, ConstantInst *cnst1, T combine, bool *isOsrBlocked)
2366 {
2367     ASSERT(isOsrBlocked != nullptr);
2368     auto input0 = inst->GetInput(0).GetInst();
2369     auto previnput1 = input0->GetInput(1).GetInst();
2370     if (previnput1->IsConst() && inst->GetType() == input0->GetType()) {
2371         if (SkipThisPeepholeInOSR(inst, input0->GetInput(0).GetInst())) {
2372             *isOsrBlocked = true;
2373             return false;
2374         }
2375         auto cnst2 = static_cast<ConstantInst *>(previnput1);
2376         auto graph = inst->GetBasicBlock()->GetGraph();
2377         ConstantInst *newCnst = nullptr;
2378         switch (DataType::GetCommonType(cnst1->GetType())) {
2379             case DataType::INT64:
2380                 newCnst = ConstFoldingCreateIntConst(inst, combine(cnst1->GetIntValue(), cnst2->GetIntValue()));
2381                 break;
2382             case DataType::FLOAT32:
2383                 newCnst = graph->FindOrCreateConstant(combine(cnst1->GetFloatValue(), cnst2->GetFloatValue()));
2384                 break;
2385             case DataType::FLOAT64:
2386                 newCnst = graph->FindOrCreateConstant(combine(cnst1->GetDoubleValue(), cnst2->GetDoubleValue()));
2387                 break;
2388             default:
2389                 UNREACHABLE();
2390         }
2391         inst->SetInput(0, input0->GetInput(0).GetInst());
2392         inst->SetInput(1, newCnst);
2393         return true;
2394     }
2395     return false;
2396 }
2397 
TryCombineAddSubConst(Inst * inst,ConstantInst * cnst1,bool * isOsrBlocked)2398 bool Peepholes::TryCombineAddSubConst(Inst *inst, ConstantInst *cnst1, bool *isOsrBlocked)
2399 {
2400     ASSERT(isOsrBlocked != nullptr);
2401     auto opc = inst->GetOpcode();
2402     ASSERT(opc == Opcode::Add || opc == Opcode::Sub);
2403     auto input0 = inst->GetInput(0).GetInst();
2404     auto combine = [&opc, &input0](auto x, auto y) { return opc == input0->GetOpcode() ? x + y : x - y; };
2405     return TryCombineConst(inst, cnst1, combine, isOsrBlocked);
2406 }
2407 
TryCombineShiftConst(Inst * inst,ConstantInst * cnst1,bool * isOsrBlocked)2408 bool Peepholes::TryCombineShiftConst(Inst *inst, ConstantInst *cnst1, bool *isOsrBlocked)
2409 {
2410     ASSERT(isOsrBlocked != nullptr);
2411     auto opc = inst->GetOpcode();
2412     ASSERT(opc == Opcode::Shl || opc == Opcode::Shr || opc == Opcode::AShr);
2413 
2414     auto input0 = inst->GetInput(0).GetInst();
2415     auto previnput1 = input0->GetInput(1).GetInst();
2416     if (!(previnput1->IsConst() && inst->GetType() == input0->GetType())) {
2417         return false;
2418     }
2419     auto graph = inst->GetBasicBlock()->GetGraph();
2420     uint64_t sizeMask = DataType::GetTypeSize(inst->GetType(), graph->GetArch()) - 1;
2421     auto cnst2 = static_cast<ConstantInst *>(previnput1);
2422     auto newValue = (cnst1->GetIntValue() & sizeMask) + (cnst2->GetIntValue() & sizeMask);
2423     // If new_value > size_mask, result is always 0 for Shr and Shl,
2424     // and 0 or -1 (depending on highest bit of input) for AShr
2425     if (newValue <= sizeMask || opc == Opcode::AShr) {
2426         if (SkipThisPeepholeInOSR(inst, input0->GetInput(0).GetInst())) {
2427             *isOsrBlocked = true;
2428             return false;
2429         }
2430         auto newCnst = ConstFoldingCreateIntConst(inst, std::min(newValue, sizeMask));
2431         inst->SetInput(0, input0->GetInput(0).GetInst());
2432         inst->SetInput(1, newCnst);
2433         return true;
2434     }
2435     auto newCnst = ConstFoldingCreateIntConst(inst, 0);
2436     inst->ReplaceUsers(newCnst);
2437     return true;
2438 }
2439 
TryCombineMulConst(Inst * inst,ConstantInst * cnst1,bool * isOsrBlocked)2440 bool Peepholes::TryCombineMulConst(Inst *inst, ConstantInst *cnst1, bool *isOsrBlocked)
2441 {
2442     ASSERT(isOsrBlocked != nullptr);
2443     ASSERT(inst->GetOpcode() == Opcode::Mul);
2444     auto combine = [](auto x, auto y) { return x * y; };
2445     return TryCombineConst(inst, cnst1, combine, isOsrBlocked);
2446 }
2447 
GetInputsOfCompareWithConst(const Inst * inst,Inst ** input,ConstantInst ** constInput,bool * inputsSwapped)2448 bool Peepholes::GetInputsOfCompareWithConst(const Inst *inst, Inst **input, ConstantInst **constInput,
2449                                             bool *inputsSwapped)
2450 {
2451     if (inst->GetOpcode() == Opcode::Compare || inst->GetOpcode() == Opcode::Cmp) {
2452         if (inst->GetInput(1).GetInst()->IsConst()) {
2453             *input = inst->GetInput(0).GetInst();
2454             *constInput = inst->GetInput(1).GetInst()->CastToConstant();
2455             *inputsSwapped = false;
2456             return true;
2457         }
2458         if (inst->GetInput(0).GetInst()->IsConst()) {
2459             *input = inst->GetInput(1).GetInst();
2460             *constInput = inst->GetInput(0).GetInst()->CastToConstant();
2461             *inputsSwapped = true;
2462             return true;
2463         }
2464     }
2465     return false;
2466 }
2467 
GenerateXorWithOne(BasicBlock * block,Inst * ifImmInput)2468 Inst *GenerateXorWithOne(BasicBlock *block, Inst *ifImmInput)
2469 {
2470     auto graph = block->GetGraph();
2471     auto xorInst = graph->CreateInstXor(DataType::BOOL, block->GetGuestPc());
2472     xorInst->SetInput(0, ifImmInput);
2473     Inst *oneConst = nullptr;
2474     if (graph->IsBytecodeOptimizer()) {
2475         oneConst = graph->FindOrCreateConstant<uint32_t>(1);
2476     } else {
2477         oneConst = graph->FindOrCreateConstant<uint64_t>(1);
2478     }
2479     xorInst->SetInput(1, oneConst);
2480     // We can add inst "xor" before SaveStateOSR in BasicBlock
2481     block->PrependInst(xorInst);
2482     return xorInst;
2483 }
2484 
IsBoolPhiInverted(PhiInst * phi,IfImmInst * ifImm)2485 std::optional<bool> IsBoolPhiInverted(PhiInst *phi, IfImmInst *ifImm)
2486 {
2487     auto phiInput0 = phi->GetInput(0).GetInst();
2488     auto phiInput1 = phi->GetInput(1).GetInst();
2489     if (!phiInput0->IsBoolConst() || !phiInput1->IsBoolConst()) {
2490         return std::nullopt;
2491     }
2492     auto constant0 = phiInput0->CastToConstant()->GetRawValue();
2493     auto constant1 = phiInput1->CastToConstant()->GetRawValue();
2494     if (constant0 == constant1) {
2495         return std::nullopt;
2496     }
2497     // Here constant0 and constant1 are 0 and 1 in some order
2498 
2499     auto invertedIf = IsIfInverted(phi->GetBasicBlock(), ifImm);
2500     if (invertedIf == std::nullopt) {
2501         return std::nullopt;
2502     }
2503     // constant0 is also index of phi input equal to 0
2504     if (phi->GetPhiInputBbNum(constant0) == 0) {
2505         return !*invertedIf;
2506     }
2507     return invertedIf;
2508 }
2509 
TryEliminatePhi(PhiInst * phi)2510 bool Peepholes::TryEliminatePhi(PhiInst *phi)
2511 {
2512     if (phi->GetInputsCount() != MAX_SUCCS_NUM) {
2513         return false;
2514     }
2515 
2516     auto bb = phi->GetBasicBlock();
2517     auto dom = bb->GetDominator();
2518     if (dom->IsEmpty()) {
2519         return false;
2520     }
2521     auto last = dom->GetLastInst();
2522     if (last->GetOpcode() != Opcode::IfImm) {
2523         return false;
2524     }
2525 
2526     auto graph = dom->GetGraph();
2527     auto ifImm = last->CastToIfImm();
2528     auto input = ifImm->GetInput(0).GetInst();
2529     // In case of the bytecode optimizer we can not generate Compare therefore we check that Peepholes has eliminated
2530     // Compare
2531     if (graph->IsBytecodeOptimizer() && input->GetOpcode() == Opcode::Compare) {
2532         return false;
2533     }
2534     if (input->GetType() != DataType::BOOL ||
2535         GetTypeSize(phi->GetType(), graph->GetArch()) != GetTypeSize(input->GetType(), graph->GetArch())) {
2536         return false;
2537     }
2538     auto inverted = IsBoolPhiInverted(phi, ifImm);
2539     if (!inverted) {
2540         return false;
2541     }
2542     if (*inverted) {
2543         // 0. Const 0
2544         // 1. Const 1
2545         // 2. v2
2546         // 3. IfImm NE b v2, 0x0
2547         // 4. Phi v0, v1 -> v5, ...
2548         // ===>
2549         // 0. Const 0
2550         // 1. Const 1
2551         // 2. v2
2552         // 3. IfImm NE b v2, 0x0
2553         // 6. Xor v2, v1 -> v5, ...
2554         // 4. Phi v0, v1
2555 
2556         // "xori"(xor) will insert as first inst in BB, so enough check between first inst and input
2557         if (SkipThisPeepholeInOSR(phi, input)) {
2558             return false;
2559         }
2560         auto xori = GenerateXorWithOne(phi->GetBasicBlock(), input);
2561         phi->ReplaceUsers(xori);
2562     } else {
2563         // 0. Const 0
2564         // 1. Const 1
2565         // 2. v2
2566         // 3. IfImm NE b v2, 0x0
2567         // 4. Phi v1, v0 -> v5, ...
2568         // ===>
2569         // 0. Const 0
2570         // 1. Const 1
2571         // 2. v2 -> v5, ...
2572         // 3. IfImm NE b v2, 0x0
2573         // 4. Phi v1, v0
2574 
2575         if (SkipThisPeepholeInOSR(phi, input)) {
2576             return false;
2577         }
2578         phi->ReplaceUsers(input);
2579     }
2580     return true;
2581 }
2582 
SkipThisPeepholeInOSR(Inst * inst,Inst * newInput)2583 bool Peepholes::SkipThisPeepholeInOSR(Inst *inst, Inst *newInput)
2584 {
2585     auto osr = inst->GetBasicBlock()->GetGraph()->IsOsrMode();
2586     return osr && newInput->GetOpcode() != Opcode::Constant && IsInstInDifferentBlocks(inst, newInput);
2587 }
2588 
VisitGetInstanceClass(GraphVisitor * v,Inst * inst)2589 void Peepholes::VisitGetInstanceClass(GraphVisitor *v, Inst *inst)
2590 {
2591     auto typeInfo = inst->GetDataFlowInput(0)->GetObjectTypeInfo();
2592     if (typeInfo && typeInfo.IsExact()) {
2593         auto klass = typeInfo.GetClass();
2594         auto bb = inst->GetBasicBlock();
2595         auto graph = bb->GetGraph();
2596         auto classImm = graph->CreateInstLoadImmediate(DataType::REFERENCE, inst->GetPc(), klass);
2597         inst->ReplaceUsers(classImm);
2598         bb->InsertAfter(classImm, inst);
2599         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2600     }
2601 }
VisitLoadAndInitClass(GraphVisitor * v,Inst * inst)2602 void Peepholes::VisitLoadAndInitClass(GraphVisitor *v, Inst *inst)
2603 {
2604     auto graph = inst->GetBasicBlock()->GetGraph();
2605     if (!graph->IsJitOrOsrMode()) {
2606         return;
2607     }
2608     auto klass = inst->CastToLoadAndInitClass()->GetClass();
2609     if (klass == nullptr || !graph->GetRuntime()->IsClassInitialized(reinterpret_cast<uintptr_t>(klass))) {
2610         return;
2611     }
2612     auto classImm = graph->CreateInstLoadImmediate(DataType::REFERENCE, inst->GetPc(), klass);
2613     inst->ReplaceUsers(classImm);
2614     inst->GetBasicBlock()->InsertAfter(classImm, inst);
2615 
2616     inst->ClearFlag(compiler::inst_flags::NO_DCE);
2617     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2618 }
2619 
VisitInitClass(GraphVisitor * v,Inst * inst)2620 void Peepholes::VisitInitClass(GraphVisitor *v, Inst *inst)
2621 {
2622     auto graph = inst->GetBasicBlock()->GetGraph();
2623     if (!graph->IsJitOrOsrMode()) {
2624         return;
2625     }
2626     auto klass = inst->CastToInitClass()->GetClass();
2627     if (klass == nullptr || !graph->GetRuntime()->IsClassInitialized(reinterpret_cast<uintptr_t>(klass))) {
2628         return;
2629     }
2630     inst->ClearFlag(compiler::inst_flags::NO_DCE);
2631     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2632 }
2633 
VisitIntrinsic(GraphVisitor * v,Inst * inst)2634 void Peepholes::VisitIntrinsic([[maybe_unused]] GraphVisitor *v, Inst *inst)
2635 {
2636     auto intrinsic = inst->CastToIntrinsic();
2637     // CC-OFFNXT(C_RULE_SWITCH_BRANCH_CHECKER) autogenerated code
2638     switch (intrinsic->GetIntrinsicId()) {
2639 #include "intrinsics_peephole.inl"
2640         default: {
2641             return;
2642         }
2643     }
2644 }
2645 
VisitLoadClass(GraphVisitor * v,Inst * inst)2646 void Peepholes::VisitLoadClass(GraphVisitor *v, Inst *inst)
2647 {
2648     auto graph = inst->GetBasicBlock()->GetGraph();
2649     if (!graph->IsJitOrOsrMode()) {
2650         return;
2651     }
2652     auto klass = inst->CastToLoadClass()->GetClass();
2653     if (klass == nullptr) {
2654         return;
2655     }
2656     auto classImm = graph->CreateInstLoadImmediate(DataType::REFERENCE, inst->GetPc(), klass);
2657     inst->ReplaceUsers(classImm);
2658     inst->GetBasicBlock()->InsertAfter(classImm, inst);
2659 
2660     inst->ClearFlag(compiler::inst_flags::NO_DCE);
2661     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2662 }
2663 
VisitLoadConstantPool(GraphVisitor * v,Inst * inst)2664 void Peepholes::VisitLoadConstantPool(GraphVisitor *v, Inst *inst)
2665 {
2666     auto graph = inst->GetBasicBlock()->GetGraph();
2667     if (!graph->IsJitOrOsrMode()) {
2668         return;
2669     }
2670     auto func = inst->GetInput(0).GetInst();
2671     void *constantPool = nullptr;
2672     if (func->IsParameter() && func->CastToParameter()->GetArgNumber() == 0) {
2673         constantPool = graph->GetRuntime()->GetConstantPool(graph->GetMethod());
2674     } else {
2675         CallInst *callerInst = nullptr;
2676         for (auto &user : inst->GetUsers()) {
2677             auto userInst = user.GetInst();
2678             if (userInst->GetOpcode() == Opcode::SaveState &&
2679                 user.GetVirtualRegister().GetVRegType() == VRegType::CONST_POOL) {
2680                 callerInst = userInst->CastToSaveState()->GetCallerInst();
2681                 ASSERT(callerInst != nullptr);
2682                 break;
2683             }
2684         }
2685         if (callerInst == nullptr) {
2686             return;
2687         }
2688         if (auto funcObject = callerInst->GetFunctionObject(); funcObject != 0) {
2689             constantPool = graph->GetRuntime()->GetConstantPool(funcObject);
2690         } else {
2691             constantPool = graph->GetRuntime()->GetConstantPool(callerInst->GetCallMethod());
2692         }
2693     }
2694     if (constantPool == nullptr) {
2695         return;
2696     }
2697     auto constantPoolImm = graph->CreateInstLoadImmediate(DataType::ANY, inst->GetPc(), constantPool,
2698                                                           LoadImmediateInst::ObjectType::CONSTANT_POOL);
2699     inst->InsertAfter(constantPoolImm);
2700     inst->ReplaceUsers(constantPoolImm);
2701 
2702     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2703 }
2704 
VisitLoadFromConstantPool(GraphVisitor * v,Inst * inst)2705 void Peepholes::VisitLoadFromConstantPool(GraphVisitor *v, Inst *inst)
2706 {
2707     auto graph = inst->GetBasicBlock()->GetGraph();
2708     // LoadFromConstantPool with string flag may be used for intrinsics inlining, do not optimize too early
2709     if (!graph->IsUnrollComplete()) {
2710         return;
2711     }
2712     auto constantPool = inst->GetInput(0).GetInst();
2713     if (constantPool->GetOpcode() != Opcode::LoadImmediate) {
2714         return;
2715     }
2716     auto offset = inst->CastToLoadFromConstantPool()->GetTypeId();
2717     auto shift = DataType::ShiftByType(DataType::ANY, graph->GetArch());
2718     uintptr_t mem = constantPool->CastToLoadImmediate()->GetConstantPool() +
2719                     graph->GetRuntime()->GetArrayDataOffset(graph->GetArch()) + (offset << shift);
2720     auto load = graph->CreateInstLoadObjFromConst(DataType::ANY, inst->GetPc(), mem);
2721     inst->InsertAfter(load);
2722     inst->ReplaceUsers(load);
2723 
2724     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2725 }
2726 
VisitLoadStatic(GraphVisitor * v,Inst * inst)2727 void Peepholes::VisitLoadStatic(GraphVisitor *v, Inst *inst)
2728 {
2729     if (ConstFoldingLoadStatic(inst)) {
2730         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2731     }
2732 }
2733 
CreateCompareInsteadOfXorAdd(Inst * oldInst)2734 bool Peepholes::CreateCompareInsteadOfXorAdd(Inst *oldInst)
2735 {
2736     ASSERT(oldInst->GetOpcode() == Opcode::Xor || oldInst->GetOpcode() == Opcode::Add);
2737     auto input0 = oldInst->GetInput(0).GetInst();
2738     [[maybe_unused]] auto input1 = oldInst->GetInput(1).GetInst();
2739 
2740     if (oldInst->GetOpcode() == Opcode::Add) {
2741         ASSERT(input0->GetOpcode() == Opcode::Neg);
2742         input0 = input0->GetInput(0).GetInst();
2743         for (auto &userAdd : oldInst->GetUsers()) {
2744             if (SkipThisPeepholeInOSR(userAdd.GetInst(), input0)) {
2745                 return false;
2746             }
2747         }
2748     }
2749 
2750     // We shouldn't check on OSR with Xor, because old_inst and cmp_inst is placed one by one
2751     ASSERT(input0->GetType() == DataType::BOOL && input1->IsConst() && input1->CastToConstant()->GetIntValue() == 1U);
2752     auto cnst = oldInst->GetBasicBlock()->GetGraph()->FindOrCreateConstant(0);
2753     auto cmpInst = CreateAndInsertInst(Opcode::Compare, oldInst, input0, cnst);
2754     cmpInst->SetType(DataType::BOOL);
2755     cmpInst->CastToCompare()->SetCc(ConditionCode::CC_EQ);
2756     cmpInst->CastToCompare()->SetOperandsType(DataType::BOOL);
2757     auto type = oldInst->GetType();
2758     if (type == DataType::UINT64 || type == DataType::INT64) {
2759         auto cast = cmpInst->GetBasicBlock()->GetGraph()->CreateInstCast();
2760         cast->SetType(type);
2761         cmpInst->InsertAfter(cast);
2762         cmpInst->ReplaceUsers(cast);
2763         cast->SetInput(0, cmpInst);
2764         cast->SetOperandsType(DataType::BOOL);
2765     }
2766     return true;
2767 }
2768 
2769 // Move users from LenArray to constant which used in MultiArray
2770 // Case 1
2771 // 1.s64 ... ->{v3}
2772 // 2.s64 ... ->{v3}
2773 // 3.ref MultiArray ... , v1, v2, ... ->{v4,..}
2774 // 4.s32 LenArray v3 ->{v5, ...}
2775 // 5.    USE      v5
2776 // ===>
2777 // 1.s64 ... ->{v3, v5, ...}
2778 // 2.s64 ... ->{v3}
2779 // 3.ref MultiArray ... , v1, v2, ... ->{v4,..}
2780 // 4.s32 LenArray v3
2781 // 5.    USE      v1
2782 
2783 // Case 2
2784 // 1.s64 ... ->{v3}
2785 // 2.s64 ... ->{v3}
2786 // 3.ref MultiArray ... , v1, v2, ... ->{v4,..}
2787 // 4.ref LoadArray v3, ...
2788 // 5.ref NullCheck v4, ...
2789 // 6.s32 LenArray v5 ->{v7, ...}
2790 // 7.    USE      v6
2791 // ===>
2792 // 1.s64 ... ->{v3}
2793 // 2.s64 ... ->{v3, v7, ...}
2794 // 3.ref MultiArray ... , v1, v2, ... ->{v4,..}
2795 // 4.ref LoadArray v3, ...
2796 // 5.ref NullCheck v4, ...
2797 // 6.s32 LenArray v5
2798 // 7.    USE      v2
OptimizeLenArrayForMultiArray(Inst * lenArray,Inst * inst,size_t indexSize)2799 bool Peepholes::OptimizeLenArrayForMultiArray(Inst *lenArray, Inst *inst, size_t indexSize)
2800 {
2801     if (inst->GetOpcode() == Opcode::MultiArray) {
2802         // Arguments of MultiArray look like : class, size_0, size_1, ..., size_N, SaveState
2803         // When element type of multyarray is array-like object (LenArray can be applyed to it), we can get case when
2804         // number sequential LoadArray with LenArray more than dimension of MultiArrays. So limiting the index_size.
2805         // Example in unittest PeepholesTest.MultiArrayWithLenArrayOfString
2806 
2807         auto multiarrDimension = inst->GetInputsCount() - 2;
2808         if (!(indexSize < multiarrDimension)) {
2809             return false;
2810         }
2811         // MultiArray's sizes starts from index "1", so need add "1" to get absolute index
2812         auto value = inst->GetDataFlowInput(indexSize + 1);
2813         for (auto &it : lenArray->GetUsers()) {
2814             if (SkipThisPeepholeInOSR(it.GetInst(), value)) {
2815                 return false;
2816             }
2817         }
2818         lenArray->ReplaceUsers(value);
2819         return true;
2820     }
2821     if (inst->GetOpcode() == Opcode::LoadArray) {
2822         auto input = inst->GetDataFlowInput(0);
2823         return OptimizeLenArrayForMultiArray(lenArray, input, indexSize + 1);
2824     }
2825     return false;
2826 }
2827 
IsNegationPattern(Inst * inst)2828 bool Peepholes::IsNegationPattern(Inst *inst)
2829 {
2830     ASSERT(inst->GetOpcode() == Opcode::Add);
2831     // Negetion pattern is:
2832     // 1.   Constant 0x1
2833     // 2.b  ...
2834     // 3.i32 Neg v2 -> v4
2835     // 4.i32 Add v3, v1
2836     auto input0 = inst->GetInput(0).GetInst();
2837     if (input0->GetOpcode() != Opcode::Neg || input0->GetInput(0).GetInst()->GetType() != DataType::BOOL ||
2838         !input0->HasSingleUser()) {
2839         return false;
2840     }
2841     // We sure, that constant may be only in input1
2842     auto input1 = inst->GetInput(1).GetInst();
2843     return input1->GetOpcode() == Opcode::Constant && input1->CastToConstant()->GetIntValue() == 1;
2844 }
2845 
TrySimplifyNegationPattern(Inst * inst)2846 bool Peepholes::TrySimplifyNegationPattern(Inst *inst)
2847 {
2848     ASSERT(inst->GetOpcode() == Opcode::Add);
2849     if (!IsNegationPattern(inst)) {
2850         return false;
2851     }
2852     auto suspectInst = inst->GetInput(0).GetInst()->GetInput(0).GetInst();
2853     // Case 8
2854     // We sure, that type of Neg's input is Bool. We shue, that Neg has one user
2855     if (suspectInst->GetOpcode() == Opcode::Compare && suspectInst->HasSingleUser()) {
2856         auto compareInst = suspectInst->CastToCompare();
2857         bool isPossible = true;
2858         for (auto &i : inst->GetUsers()) {
2859             if (SkipThisPeepholeInOSR(i.GetInst(), compareInst)) {
2860                 isPossible = false;
2861                 break;
2862             }
2863         }
2864         if (isPossible) {
2865             inst->ReplaceUsers(compareInst);
2866             compareInst->SetCc(GetInverseConditionCode(compareInst->GetCc()));
2867             PEEPHOLE_IS_APPLIED(this, compareInst);
2868             return true;
2869         }
2870     }
2871 
2872     // Case 9
2873     if (TrySimplifyCompareNegation(inst)) {
2874         return true;
2875     }
2876 
2877     // Case 7
2878     // This is used last of all if no case has worked!
2879     if (CreateCompareInsteadOfXorAdd(inst)) {
2880         PEEPHOLE_IS_APPLIED(this, inst);
2881         return true;
2882     }
2883     return false;
2884 }
2885 
2886 }  // namespace ark::compiler
2887