• 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 (input1->IsConst() && static_cast<ConstantInst *>(input1)->GetIntValue() == static_cast<uint64_t>(0)) {
809         // case 2:
810         // 0.i64 Const 0x000..00
811         // 1.i64 OR v5, v0
812         // 2.i64 INS which use v1
813         // ===>
814         // 0.i64 Const 0x000..00
815         // 1.i64 OR v5, v0
816         // 2.i64 INS which use v5
817         if (SkipThisPeepholeInOSR(inst, input0)) {
818             return;
819         }
820         inst->ReplaceUsers(input0);
821         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
822     } else if (input0 == input1 && input0->GetType() == inst->GetType()) {
823         // case 3:
824         // 1.i64 OR v5, v5
825         // 2.i64 INS which use v1
826         // ===>
827         // 1.i64 OR v5, v5
828         // 2.i64 INS which use v5
829         if (SkipThisPeepholeInOSR(inst, input0)) {
830             return;
831         }
832         inst->ReplaceUsers(input0);
833         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
834     } else if (input0->GetOpcode() == Opcode::Not && input1->GetOpcode() == Opcode::Not && input0->HasSingleUser() &&
835                input1->HasSingleUser()) {
836         // case 4: De Morgan rules
837         // 2.i64 not v0 -> {4}
838         // 3.i64 not v1 -> {4}
839         // 4.i64 OR v2, v3
840         // ===>
841         // 5.i64 AND v0, v1
842         // 6.i64 not v5
843         auto notInput0 = input0->GetInput(0).GetInst();
844         auto notInput1 = input1->GetInput(0).GetInst();
845         // "inst"(or), "and_inst"(and) and **INST WITHOUT NAME**(not) one by one, so we may check SaveStateOSR only
846         // between "inst"(or) and inputs: "not_input0"(some inst), "not_input0"(some inst)
847         // and "op1->GetInput(0).GetInst()"
848         if (SkipThisPeepholeInOSR(inst, notInput0) || SkipThisPeepholeInOSR(inst, notInput1)) {
849             return;
850         }
851         auto andInst = CreateAndInsertInst(Opcode::And, inst, notInput0, notInput1);
852         CreateAndInsertInst(Opcode::Not, andInst, andInst);
853         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
854     }
855 }
856 
VisitXor(GraphVisitor * v,Inst * inst)857 void Peepholes::VisitXor([[maybe_unused]] GraphVisitor *v, Inst *inst)
858 {
859     if (ConstFoldingXor(inst)) {
860         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
861         return;
862     }
863     auto visitor = static_cast<Peepholes *>(v);
864     // Swap inputs if the first is a constant
865     // 2. Xor const, v1 -> {...}
866     // ===>
867     // 2. Xor v1, const -> {...}
868     static_cast<Peepholes *>(v)->TrySwapInputs(inst);
869     auto input0 = inst->GetInput(0).GetInst();
870     auto input1 = inst->GetInput(1).GetInst();
871     if (input1->IsConst()) {
872         uint64_t val = input1->CastToConstant()->GetIntValue();
873         if (static_cast<int64_t>(val) == -1) {
874             // Replace xor with not:
875             // 0.i64 Const -1
876             // 1. ...
877             // 2.i64 XOR v0, v1
878             // ===>
879             // 3.i64 NOT v1
880             if (SkipThisPeepholeInOSR(inst, input0)) {
881                 return;
882             }
883             CreateAndInsertInst(Opcode::Not, inst, input0);
884             PEEPHOLE_IS_APPLIED(visitor, inst);
885         } else if (input1->CastToConstant()->GetIntValue() == 0) {
886             // Result A xor 0 equal A:
887             // 0.i64 Const 0
888             // 1. ...
889             // 2.i64 XOR v0, v1 -> v3
890             // 3. INS v2
891             // ===>
892             // 0.i64 Const 0
893             // 1. ...
894             // 2.i64 XOR v0, v1
895             // 3. INS v1
896             if (SkipThisPeepholeInOSR(inst, input0)) {
897                 return;
898             }
899             inst->ReplaceUsers(input0);
900             PEEPHOLE_IS_APPLIED(visitor, inst);
901         } else if (val == 1 && input0->GetType() == DataType::BOOL) {
902             // Replacing Xor with Compare for more case optimizations
903             // If this compare is not optimized during pipeline, we will revert it back to xor in Lowering
904             // 1.i64 Const 1
905             // 2.b   ...
906             // 3.i32 Xor v1, v2
907             // ===>
908             // 1.i64 Const 0
909             // 2.b   ...
910             // 3.b   Compare EQ b v2, v1
911             if (SkipThisPeepholeInOSR(inst, input0)) {
912                 return;
913             }
914             CreateCompareInsteadOfXorAdd(inst);
915             PEEPHOLE_IS_APPLIED(visitor, inst);
916         }
917     }
918 }
919 
VisitCmp(GraphVisitor * v,Inst * inst)920 void Peepholes::VisitCmp(GraphVisitor *v, Inst *inst)
921 {
922     auto visitor = static_cast<Peepholes *>(v);
923     if (ConstFoldingCmp(inst)) {
924         PEEPHOLE_IS_APPLIED(visitor, inst);
925     }
926 }
927 
VisitCompare(GraphVisitor * v,Inst * inst)928 void Peepholes::VisitCompare(GraphVisitor *v, Inst *inst)
929 {
930     auto visitor = static_cast<Peepholes *>(v);
931     // Try to replace Compare with any type to bool type
932     if (visitor->TrySimplifyCompareAnyType(inst)) {
933         PEEPHOLE_IS_APPLIED(visitor, inst);
934     }
935 
936     /* skip preheader Compare processing until unrolling is done */
937     auto graph = inst->GetBasicBlock()->GetGraph();
938     if (!graph->IsBytecodeOptimizer() && g_options.IsCompilerDeferPreheaderTransform() && !graph->IsUnrollComplete()) {
939         auto bb = inst->GetBasicBlock();
940         if (bb->IsLoopPreHeader() && inst->HasSingleUser() && inst->GetFirstUser()->GetInst() == bb->GetLastInst() &&
941             bb->GetLastInst()->GetOpcode() == Opcode::IfImm) {
942             return;
943         }
944     }
945 
946     if (ConstFoldingCompare(inst)) {
947         PEEPHOLE_IS_APPLIED(visitor, inst);
948         return;
949     }
950 
951     bool osrBlockedPeephole = false;
952     if (visitor->TrySimplifyCompareWithBoolInput(inst, &osrBlockedPeephole)) {
953         if (osrBlockedPeephole) {
954             return;
955         }
956         PEEPHOLE_IS_APPLIED(visitor, inst);
957     } else if (visitor->TrySimplifyCmpCompareWithZero(inst, &osrBlockedPeephole)) {
958         if (osrBlockedPeephole) {
959             return;
960         }
961         PEEPHOLE_IS_APPLIED(visitor, inst);
962     } else if (visitor->TrySimplifyCompareAndZero(inst, &osrBlockedPeephole)) {
963         if (osrBlockedPeephole) {
964             return;
965         }
966         PEEPHOLE_IS_APPLIED(visitor, inst);
967     } else if (TrySimplifyCompareLenArrayWithZero(inst)) {
968         PEEPHOLE_IS_APPLIED(visitor, inst);
969     } else if (visitor->TrySimplifyTestEqualInputs(inst)) {
970         PEEPHOLE_IS_APPLIED(visitor, inst);
971     }
972 }
973 
TrySimplifyCompareAnyTypeCase1(Inst * inst,Inst * input0,Inst * input1)974 bool Peepholes::TrySimplifyCompareAnyTypeCase1(Inst *inst, Inst *input0, Inst *input1)
975 {
976     auto cmpInst = inst->CastToCompare();
977 
978     // 2.any  CastValueToAnyType BOOLEAN_TYPE v0 -> (v4)
979     // 3.any  CastValueToAnyType BOOLEAN_TYPE v1 -> (v4)
980     // 4.     Compare EQ/NE any    v2, v3
981     // =======>
982     // 4.     Compare EQ/NE bool   v0, v1
983     if (input0->CastToCastValueToAnyType()->GetAnyType() != input1->CastToCastValueToAnyType()->GetAnyType()) {
984         return false;
985     }
986     if (SkipThisPeepholeInOSR(cmpInst, input0->GetInput(0).GetInst()) ||
987         SkipThisPeepholeInOSR(cmpInst, input1->GetInput(0).GetInst())) {
988         return false;
989     }
990     cmpInst->SetOperandsType(DataType::BOOL);
991     cmpInst->SetInput(0, input0->GetInput(0).GetInst());
992     cmpInst->SetInput(1, input1->GetInput(0).GetInst());
993     return true;
994 }
995 
TrySimplifyCompareAnyTypeCase2(Inst * inst,Inst * input0,Inst * input1)996 bool Peepholes::TrySimplifyCompareAnyTypeCase2(Inst *inst, Inst *input0, Inst *input1)
997 {
998     auto graph = inst->GetBasicBlock()->GetGraph();
999     auto cmpInst = inst->CastToCompare();
1000     auto cc = cmpInst->GetCc();
1001     auto ifImm = input1->CastToConstant()->GetRawValue();
1002     auto runtime = graph->GetRuntime();
1003     uint64_t newConst;
1004 
1005     // 3.any  CastValueToAnyType BOOLEAN_TYPE v2 -> (v4)
1006     // 4.     Compare EQ/NE any    v3, DYNAMIC_TRUE/FALSE
1007     // =======>
1008     // 4.     Compare EQ/NE bool   v2, 0x1/0x0
1009     if (SkipThisPeepholeInOSR(cmpInst, input0->GetInput(0).GetInst())) {
1010         return false;
1011     }
1012     if (ifImm == runtime->GetDynamicPrimitiveFalse()) {
1013         newConst = 0;
1014     } else if (ifImm == runtime->GetDynamicPrimitiveTrue()) {
1015         newConst = 1;
1016     } else {
1017         // In this case, we are comparing the dynamic boolean type with not boolean constant.
1018         // So the Compare EQ/NE alwayes false/true.
1019         // In this case, we can change the Compare to Constant instruction.
1020         // NOTE! It is assumed that there is only one Boolean type for each dynamic language.
1021         // Support for multiple Boolean types must be maintained separately.
1022         if (cc != CC_EQ && cc != CC_NE) {
1023             return false;
1024         }
1025         // We create constant, so we don't need to check SaveStateOSR between insts
1026         cmpInst->ReplaceUsers(graph->FindOrCreateConstant<uint64_t>(cc == CC_NE ? 1 : 0));
1027         return true;
1028     }
1029     cmpInst->SetOperandsType(DataType::BOOL);
1030     cmpInst->SetInput(0, input0->GetInput(0).GetInst());
1031     cmpInst->SetInput(1, graph->FindOrCreateConstant(newConst));
1032     return true;
1033 }
1034 
TrySimplifyCompareAnyType(Inst * inst)1035 bool Peepholes::TrySimplifyCompareAnyType(Inst *inst)
1036 {
1037     auto graph = inst->GetBasicBlock()->GetGraph();
1038     if (graph->IsBytecodeOptimizer()) {
1039         return false;
1040     }
1041     auto cmpInst = inst->CastToCompare();
1042     if (cmpInst->GetOperandsType() != DataType::ANY) {
1043         return false;
1044     }
1045 
1046     auto input0 = cmpInst->GetInput(0).GetInst();
1047     auto input1 = cmpInst->GetInput(1).GetInst();
1048     auto cc = cmpInst->GetCc();
1049     if (input0 == input1 && (cc == CC_EQ || cc == CC_NE)) {
1050         // We create constant, so we don't need to check SaveStateOSR between insts
1051         cmpInst->ReplaceUsers(graph->FindOrCreateConstant<uint64_t>(cc == CC_EQ ? 1 : 0));
1052         return true;
1053     }
1054 
1055     if (input0->GetOpcode() != Opcode::CastValueToAnyType) {
1056         return false;
1057     }
1058     if (AnyBaseTypeToDataType(input0->CastToCastValueToAnyType()->GetAnyType()) != DataType::BOOL) {
1059         return false;
1060     }
1061 
1062     if (input1->GetOpcode() == Opcode::CastValueToAnyType) {
1063         return TrySimplifyCompareAnyTypeCase1(inst, input0, input1);
1064     }
1065     if (!input1->IsConst()) {
1066         return false;
1067     }
1068 
1069     return TrySimplifyCompareAnyTypeCase2(inst, input0, input1);
1070 }
1071 
1072 // This VisitIf is using only for compile IRTOC
VisitIf(GraphVisitor * v,Inst * inst)1073 void Peepholes::VisitIf([[maybe_unused]] GraphVisitor *v, Inst *inst)
1074 {
1075     // 2.     Constant           0x0
1076     // 3.i64  And                v0, v1
1077     // 4.     If EQ/NE i64       v3, v2
1078     // =======>
1079     // 4.     If TST_EQ/TST_NE   v1, v2
1080     auto visitor = static_cast<Peepholes *>(v);
1081     if (visitor->GetGraph()->IsBytecodeOptimizer()) {
1082         return;
1083     }
1084     auto ifImm = inst->CastToIf();
1085     if (ifImm->GetCc() != CC_EQ && ifImm->GetCc() != CC_NE) {
1086         return;
1087     }
1088 
1089     auto lhs = ifImm->GetInput(0).GetInst();
1090     auto rhs = ifImm->GetInput(1).GetInst();
1091     Inst *andInput {nullptr};
1092     if (lhs->GetOpcode() == Opcode::And && IsZeroConstantOrNullPtr(rhs)) {
1093         andInput = lhs;
1094     } else if (rhs->GetOpcode() == Opcode::And && IsZeroConstantOrNullPtr(lhs)) {
1095         andInput = rhs;
1096     } else {
1097         return;
1098     }
1099     if (!andInput->HasSingleUser()) {
1100         return;
1101     }
1102 
1103     auto newInput0 = andInput->GetInput(0).GetInst();
1104     auto newInput1 = andInput->GetInput(1).GetInst();
1105     if (SkipThisPeepholeInOSR(ifImm, newInput0) || SkipThisPeepholeInOSR(ifImm, newInput1)) {
1106         return;
1107     }
1108     ifImm->SetCc(ifImm->GetCc() == CC_EQ ? CC_TST_EQ : CC_TST_NE);
1109     ifImm->SetInput(0, newInput0);
1110     ifImm->SetInput(1, newInput1);
1111     PEEPHOLE_IS_APPLIED(visitor, inst);
1112 }
1113 
TryReplaceCompareAnyType(Inst * inst,Inst * dominateInst)1114 static bool TryReplaceCompareAnyType(Inst *inst, Inst *dominateInst)
1115 {
1116     auto instType = inst->CastToCompareAnyType()->GetAnyType();
1117     auto dominateType = dominateInst->GetAnyType();
1118     profiling::AnyInputType dominateAllowedType {};
1119     if (dominateInst->GetOpcode() == Opcode::AnyTypeCheck) {
1120         dominateAllowedType = dominateInst->CastToAnyTypeCheck()->GetAllowedInputType();
1121     } else {
1122         ASSERT(dominateInst->GetOpcode() == Opcode::CastValueToAnyType);
1123     }
1124     ASSERT(inst->CastToCompareAnyType()->GetAllowedInputType() == profiling::AnyInputType::DEFAULT);
1125     auto graph = inst->GetBasicBlock()->GetGraph();
1126     auto language = graph->GetRuntime()->GetMethodSourceLanguage(graph->GetMethod());
1127     auto res = IsAnyTypeCanBeSubtypeOf(language, instType, dominateType, profiling::AnyInputType::DEFAULT,
1128                                        dominateAllowedType);
1129     if (!res) {
1130         // We cannot compare types in compile-time
1131         return false;
1132     }
1133     auto constValue = res.value();
1134     auto cnst = inst->GetBasicBlock()->GetGraph()->FindOrCreateConstant(constValue);
1135     // We replace constant, so we don't need to check SaveStateOSR between insts
1136     inst->ReplaceUsers(cnst);
1137     return true;
1138 }
1139 
VisitCompareAnyType(GraphVisitor * v,Inst * inst)1140 void Peepholes::VisitCompareAnyType(GraphVisitor *v, Inst *inst)
1141 {
1142     auto visitor = static_cast<Peepholes *>(v);
1143     auto input = inst->GetInput(0).GetInst();
1144     // from
1145     //  2.any CastValueToAnyType TYPE1 -> (v3)
1146     //  3.b CompareAnyType TYPE2 -> (...)
1147     // to
1148     //  4. Constant (TYPE1 == TYPE2)  ->(...)
1149     if (input->GetOpcode() == Opcode::CastValueToAnyType || input->GetOpcode() == Opcode::AnyTypeCheck) {
1150         if (TryReplaceCompareAnyType(inst, input)) {
1151             PEEPHOLE_IS_APPLIED(visitor, inst);
1152             return;
1153         }
1154     }
1155     // from
1156     //  2.any AnyTypeCheck v1 TYPE1 -> (...)
1157     //  3.b CompareAnyType v1 TYPE2 -> (...)
1158     // to
1159     //  4. Constant (TYPE1 == TYPE2)  ->(...)
1160     for (auto &user : input->GetUsers()) {
1161         auto userInst = user.GetInst();
1162         if (userInst != inst && userInst->GetOpcode() == Opcode::AnyTypeCheck && userInst->IsDominate(inst) &&
1163             TryReplaceCompareAnyType(inst, userInst)) {
1164             PEEPHOLE_IS_APPLIED(visitor, inst);
1165             return;
1166         }
1167     }
1168 }
1169 
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) - (b + c) = a - c
1864     if (input0->GetInput(1) == input1->GetInput(0)) {
1865         auto newInput0 = input0->GetInput(0).GetInst();
1866         auto newInput1 = input1->GetInput(1).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) - (c + b) = a - c
1877     if (input0->GetInput(1) == input1->GetInput(1)) {
1878         auto newInput0 = input0->GetInput(0).GetInst();
1879         auto newInput1 = input1->GetInput(0).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 (CheckFcmpInputs(input0, input1)) {
2240             input0 = input0->GetInput(0).GetInst();
2241             input1 = input1->GetInput(0).GetInst();
2242             cmpOpType = DataType::INT32;
2243         } else if (CheckFcmpWithConstInput(input0, input1)) {
2244             bool cmpSwap = false;
2245             Inst *cmpCastInput = nullptr;
2246             ConstantInst *cmpConstInput = nullptr;
2247             if (!GetInputsOfCompareWithConst(input, &cmpCastInput, &cmpConstInput, &swap)) {
2248                 return false;
2249             }
2250             Inst *cmpConstInst = static_cast<Inst *>(cmpConstInput);
2251             if (!TryReplaceFloatConstToIntConst(&cmpCastInput, &cmpConstInst)) {
2252                 return false;
2253             }
2254             input0 = cmpCastInput;
2255             input1 = cmpConstInst;
2256             cmpOpType = input0->GetType();
2257             swap = swap ^ cmpSwap;
2258         } else {
2259             return false;
2260         }
2261     }
2262     ConditionCode cc = swap ? SwapOperandsConditionCode(compare->GetCc()) : compare->GetCc();
2263     if (!IsTypeSigned(cmpOpType)) {
2264         ASSERT(cc == ConditionCode::CC_EQ || cc == ConditionCode::CC_NE || IsSignedConditionCode(cc));
2265         // If Cmp operands are unsigned then Compare.CC must be converted to unsigned.
2266         cc = InverseSignednessConditionCode(cc);
2267     }
2268     compare->SetInput(0, input0);
2269     compare->SetInput(1, input1);
2270     compare->SetOperandsType(cmpOpType);
2271     compare->SetCc(cc);
2272     return true;
2273 }
2274 
TrySimplifyTestEqualInputs(Inst * inst)2275 bool Peepholes::TrySimplifyTestEqualInputs(Inst *inst)
2276 {
2277     auto cmpInst = inst->CastToCompare();
2278     if (cmpInst->GetCc() != ConditionCode::CC_TST_EQ && cmpInst->GetCc() != ConditionCode::CC_TST_NE) {
2279         return false;
2280     }
2281     auto input0 = inst->GetInput(0).GetInst();
2282     auto input1 = inst->GetInput(1).GetInst();
2283     if (input0 != input1) {
2284         return false;
2285     }
2286     if (cmpInst->GetCc() == ConditionCode::CC_TST_EQ) {
2287         cmpInst->SetCc(ConditionCode::CC_EQ);
2288     } else {
2289         cmpInst->SetCc(ConditionCode::CC_NE);
2290     }
2291     // We create constant, so we don't need to check SaveStateOSR between insts
2292     cmpInst->SetInput(1, ConstFoldingCreateIntConst(input1, 0));
2293     return true;
2294 }
2295 
TrySimplifyCompareAndZero(Inst * inst,bool * isOsrBlocked)2296 bool Peepholes::TrySimplifyCompareAndZero(Inst *inst, bool *isOsrBlocked)
2297 {
2298     ASSERT(isOsrBlocked != nullptr);
2299     if (inst->GetBasicBlock()->GetGraph()->IsBytecodeOptimizer()) {
2300         return false;
2301     }
2302     auto cmpInst = inst->CastToCompare();
2303     auto cc = cmpInst->GetCc();
2304     if (cc != CC_EQ && cc != CC_NE) {
2305         return false;
2306     }
2307     bool swap = false;
2308     Inst *input = nullptr;
2309     ConstantInst *constInput = nullptr;
2310     if (!GetInputsOfCompareWithConst(cmpInst, &input, &constInput, &swap)) {
2311         return false;
2312     }
2313     if (input->GetOpcode() != Opcode::And || !input->HasSingleUser() || !constInput->IsEqualConstAllTypes(0)) {
2314         return false;
2315     }
2316     // 2.i32 And                  v0, v1
2317     // 3.i32 Constant             0x0
2318     // 4.b   Compare CC_EQ/CC_NE  v2, v3
2319     // =======>
2320     // 4.b   Compare CC_TST_EQ/CC_TST_NE  v0, v1
2321 
2322     if (SkipThisPeepholeInOSR(cmpInst, input->GetInput(0).GetInst())) {
2323         *isOsrBlocked = true;
2324         return true;
2325     }
2326     cmpInst->SetCc(cc == CC_EQ ? CC_TST_EQ : CC_TST_NE);
2327     cmpInst->SetInput(0, input->GetInput(0).GetInst());
2328     cmpInst->SetInput(1, input->GetInput(1).GetInst());
2329     return true;
2330 }
2331 
TrySimplifyCompareLenArrayWithZero(Inst * inst)2332 bool Peepholes::TrySimplifyCompareLenArrayWithZero(Inst *inst)
2333 {
2334     auto compare = inst->CastToCompare();
2335     bool swap = false;
2336     Inst *input = nullptr;
2337     ConstantInst *constInput = nullptr;
2338     if (!GetInputsOfCompareWithConst(compare, &input, &constInput, &swap)) {
2339         return false;
2340     }
2341     if (input->GetOpcode() != Opcode::LenArray || !constInput->IsEqualConstAllTypes(0)) {
2342         return false;
2343     }
2344 
2345     ConditionCode cc = swap ? SwapOperandsConditionCode(compare->GetCc()) : compare->GetCc();
2346     if (cc == CC_GE || cc == CC_LT) {
2347         // We create constant, so we don't need to check SaveStateOSR between insts
2348         compare->ReplaceUsers(ConstFoldingCreateIntConst(compare, cc == CC_GE ? 1 : 0));
2349         return true;
2350     }
2351     return false;
2352 }
2353 
2354 // Try to combine constants when arithmetic operations with constants are repeated
2355 template <typename T>
TryCombineConst(Inst * inst,ConstantInst * cnst1,T combine,bool * isOsrBlocked)2356 bool Peepholes::TryCombineConst(Inst *inst, ConstantInst *cnst1, T combine, bool *isOsrBlocked)
2357 {
2358     ASSERT(isOsrBlocked != nullptr);
2359     auto input0 = inst->GetInput(0).GetInst();
2360     auto previnput1 = input0->GetInput(1).GetInst();
2361     if (previnput1->IsConst() && inst->GetType() == input0->GetType()) {
2362         if (SkipThisPeepholeInOSR(inst, input0->GetInput(0).GetInst())) {
2363             *isOsrBlocked = true;
2364             return false;
2365         }
2366         auto cnst2 = static_cast<ConstantInst *>(previnput1);
2367         auto graph = inst->GetBasicBlock()->GetGraph();
2368         ConstantInst *newCnst = nullptr;
2369         switch (DataType::GetCommonType(cnst1->GetType())) {
2370             case DataType::INT64:
2371                 newCnst = ConstFoldingCreateIntConst(inst, combine(cnst1->GetIntValue(), cnst2->GetIntValue()));
2372                 break;
2373             case DataType::FLOAT32:
2374                 newCnst = graph->FindOrCreateConstant(combine(cnst1->GetFloatValue(), cnst2->GetFloatValue()));
2375                 break;
2376             case DataType::FLOAT64:
2377                 newCnst = graph->FindOrCreateConstant(combine(cnst1->GetDoubleValue(), cnst2->GetDoubleValue()));
2378                 break;
2379             default:
2380                 UNREACHABLE();
2381         }
2382         inst->SetInput(0, input0->GetInput(0).GetInst());
2383         inst->SetInput(1, newCnst);
2384         return true;
2385     }
2386     return false;
2387 }
2388 
TryCombineAddSubConst(Inst * inst,ConstantInst * cnst1,bool * isOsrBlocked)2389 bool Peepholes::TryCombineAddSubConst(Inst *inst, ConstantInst *cnst1, bool *isOsrBlocked)
2390 {
2391     ASSERT(isOsrBlocked != nullptr);
2392     auto opc = inst->GetOpcode();
2393     ASSERT(opc == Opcode::Add || opc == Opcode::Sub);
2394     auto input0 = inst->GetInput(0).GetInst();
2395     auto combine = [&opc, &input0](auto x, auto y) { return opc == input0->GetOpcode() ? x + y : x - y; };
2396     return TryCombineConst(inst, cnst1, combine, isOsrBlocked);
2397 }
2398 
TryCombineShiftConst(Inst * inst,ConstantInst * cnst1,bool * isOsrBlocked)2399 bool Peepholes::TryCombineShiftConst(Inst *inst, ConstantInst *cnst1, bool *isOsrBlocked)
2400 {
2401     ASSERT(isOsrBlocked != nullptr);
2402     auto opc = inst->GetOpcode();
2403     ASSERT(opc == Opcode::Shl || opc == Opcode::Shr || opc == Opcode::AShr);
2404 
2405     auto input0 = inst->GetInput(0).GetInst();
2406     auto previnput1 = input0->GetInput(1).GetInst();
2407     if (!(previnput1->IsConst() && inst->GetType() == input0->GetType())) {
2408         return false;
2409     }
2410     auto graph = inst->GetBasicBlock()->GetGraph();
2411     uint64_t sizeMask = DataType::GetTypeSize(inst->GetType(), graph->GetArch()) - 1;
2412     auto cnst2 = static_cast<ConstantInst *>(previnput1);
2413     auto newValue = (cnst1->GetIntValue() & sizeMask) + (cnst2->GetIntValue() & sizeMask);
2414     // If new_value > size_mask, result is always 0 for Shr and Shl,
2415     // and 0 or -1 (depending on highest bit of input) for AShr
2416     if (newValue <= sizeMask || opc == Opcode::AShr) {
2417         if (SkipThisPeepholeInOSR(inst, input0->GetInput(0).GetInst())) {
2418             *isOsrBlocked = true;
2419             return false;
2420         }
2421         auto newCnst = ConstFoldingCreateIntConst(inst, std::min(newValue, sizeMask));
2422         inst->SetInput(0, input0->GetInput(0).GetInst());
2423         inst->SetInput(1, newCnst);
2424         return true;
2425     }
2426     auto newCnst = ConstFoldingCreateIntConst(inst, 0);
2427     inst->ReplaceUsers(newCnst);
2428     return true;
2429 }
2430 
TryCombineMulConst(Inst * inst,ConstantInst * cnst1,bool * isOsrBlocked)2431 bool Peepholes::TryCombineMulConst(Inst *inst, ConstantInst *cnst1, bool *isOsrBlocked)
2432 {
2433     ASSERT(isOsrBlocked != nullptr);
2434     ASSERT(inst->GetOpcode() == Opcode::Mul);
2435     auto combine = [](auto x, auto y) { return x * y; };
2436     return TryCombineConst(inst, cnst1, combine, isOsrBlocked);
2437 }
2438 
GetInputsOfCompareWithConst(const Inst * inst,Inst ** input,ConstantInst ** constInput,bool * inputsSwapped)2439 bool Peepholes::GetInputsOfCompareWithConst(const Inst *inst, Inst **input, ConstantInst **constInput,
2440                                             bool *inputsSwapped)
2441 {
2442     if (inst->GetOpcode() == Opcode::Compare || inst->GetOpcode() == Opcode::Cmp) {
2443         if (inst->GetInput(1).GetInst()->IsConst()) {
2444             *input = inst->GetInput(0).GetInst();
2445             *constInput = inst->GetInput(1).GetInst()->CastToConstant();
2446             *inputsSwapped = false;
2447             return true;
2448         }
2449         if (inst->GetInput(0).GetInst()->IsConst()) {
2450             *input = inst->GetInput(1).GetInst();
2451             *constInput = inst->GetInput(0).GetInst()->CastToConstant();
2452             *inputsSwapped = true;
2453             return true;
2454         }
2455     }
2456     return false;
2457 }
2458 
GenerateXorWithOne(BasicBlock * block,Inst * ifImmInput)2459 Inst *GenerateXorWithOne(BasicBlock *block, Inst *ifImmInput)
2460 {
2461     auto graph = block->GetGraph();
2462     auto xorInst = graph->CreateInstXor(DataType::BOOL, block->GetGuestPc());
2463     xorInst->SetInput(0, ifImmInput);
2464     Inst *oneConst = nullptr;
2465     if (graph->IsBytecodeOptimizer()) {
2466         oneConst = graph->FindOrCreateConstant<uint32_t>(1);
2467     } else {
2468         oneConst = graph->FindOrCreateConstant<uint64_t>(1);
2469     }
2470     xorInst->SetInput(1, oneConst);
2471     // We can add inst "xor" before SaveStateOSR in BasicBlock
2472     block->PrependInst(xorInst);
2473     return xorInst;
2474 }
2475 
IsBoolPhiInverted(PhiInst * phi,IfImmInst * ifImm)2476 std::optional<bool> IsBoolPhiInverted(PhiInst *phi, IfImmInst *ifImm)
2477 {
2478     auto phiInput0 = phi->GetInput(0).GetInst();
2479     auto phiInput1 = phi->GetInput(1).GetInst();
2480     if (!phiInput0->IsBoolConst() || !phiInput1->IsBoolConst()) {
2481         return std::nullopt;
2482     }
2483     auto constant0 = phiInput0->CastToConstant()->GetRawValue();
2484     auto constant1 = phiInput1->CastToConstant()->GetRawValue();
2485     if (constant0 == constant1) {
2486         return std::nullopt;
2487     }
2488     // Here constant0 and constant1 are 0 and 1 in some order
2489 
2490     auto invertedIf = IsIfInverted(phi->GetBasicBlock(), ifImm);
2491     if (invertedIf == std::nullopt) {
2492         return std::nullopt;
2493     }
2494     // constant0 is also index of phi input equal to 0
2495     if (phi->GetPhiInputBbNum(constant0) == 0) {
2496         return !*invertedIf;
2497     }
2498     return invertedIf;
2499 }
2500 
TryEliminatePhi(PhiInst * phi)2501 bool Peepholes::TryEliminatePhi(PhiInst *phi)
2502 {
2503     if (phi->GetInputsCount() != MAX_SUCCS_NUM) {
2504         return false;
2505     }
2506 
2507     auto bb = phi->GetBasicBlock();
2508     auto dom = bb->GetDominator();
2509     if (dom->IsEmpty()) {
2510         return false;
2511     }
2512     auto last = dom->GetLastInst();
2513     if (last->GetOpcode() != Opcode::IfImm) {
2514         return false;
2515     }
2516 
2517     auto graph = dom->GetGraph();
2518     auto ifImm = last->CastToIfImm();
2519     auto input = ifImm->GetInput(0).GetInst();
2520     // In case of the bytecode optimizer we can not generate Compare therefore we check that Peepholes has eliminated
2521     // Compare
2522     if (graph->IsBytecodeOptimizer() && input->GetOpcode() == Opcode::Compare) {
2523         return false;
2524     }
2525     if (input->GetType() != DataType::BOOL ||
2526         GetTypeSize(phi->GetType(), graph->GetArch()) != GetTypeSize(input->GetType(), graph->GetArch())) {
2527         return false;
2528     }
2529     auto inverted = IsBoolPhiInverted(phi, ifImm);
2530     if (!inverted) {
2531         return false;
2532     }
2533     if (*inverted) {
2534         // 0. Const 0
2535         // 1. Const 1
2536         // 2. v2
2537         // 3. IfImm NE b v2, 0x0
2538         // 4. Phi v0, v1 -> v5, ...
2539         // ===>
2540         // 0. Const 0
2541         // 1. Const 1
2542         // 2. v2
2543         // 3. IfImm NE b v2, 0x0
2544         // 6. Xor v2, v1 -> v5, ...
2545         // 4. Phi v0, v1
2546 
2547         // "xori"(xor) will insert as first inst in BB, so enough check between first inst and input
2548         if (SkipThisPeepholeInOSR(phi, input)) {
2549             return false;
2550         }
2551         auto xori = GenerateXorWithOne(phi->GetBasicBlock(), input);
2552         phi->ReplaceUsers(xori);
2553     } else {
2554         // 0. Const 0
2555         // 1. Const 1
2556         // 2. v2
2557         // 3. IfImm NE b v2, 0x0
2558         // 4. Phi v1, v0 -> v5, ...
2559         // ===>
2560         // 0. Const 0
2561         // 1. Const 1
2562         // 2. v2 -> v5, ...
2563         // 3. IfImm NE b v2, 0x0
2564         // 4. Phi v1, v0
2565 
2566         if (SkipThisPeepholeInOSR(phi, input)) {
2567             return false;
2568         }
2569         phi->ReplaceUsers(input);
2570     }
2571     return true;
2572 }
2573 
SkipThisPeepholeInOSR(Inst * inst,Inst * newInput)2574 bool Peepholes::SkipThisPeepholeInOSR(Inst *inst, Inst *newInput)
2575 {
2576     auto osr = inst->GetBasicBlock()->GetGraph()->IsOsrMode();
2577     return osr && newInput->GetOpcode() != Opcode::Constant && IsInstInDifferentBlocks(inst, newInput);
2578 }
2579 
VisitGetInstanceClass(GraphVisitor * v,Inst * inst)2580 void Peepholes::VisitGetInstanceClass(GraphVisitor *v, Inst *inst)
2581 {
2582     auto typeInfo = inst->GetDataFlowInput(0)->GetObjectTypeInfo();
2583     if (typeInfo && typeInfo.IsExact()) {
2584         auto klass = typeInfo.GetClass();
2585         auto bb = inst->GetBasicBlock();
2586         auto graph = bb->GetGraph();
2587         auto classImm = graph->CreateInstLoadImmediate(DataType::REFERENCE, inst->GetPc(), klass);
2588         inst->ReplaceUsers(classImm);
2589         bb->InsertAfter(classImm, inst);
2590         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2591     }
2592 }
VisitLoadAndInitClass(GraphVisitor * v,Inst * inst)2593 void Peepholes::VisitLoadAndInitClass(GraphVisitor *v, Inst *inst)
2594 {
2595     auto graph = inst->GetBasicBlock()->GetGraph();
2596     if (!graph->IsJitOrOsrMode()) {
2597         return;
2598     }
2599     auto klass = inst->CastToLoadAndInitClass()->GetClass();
2600     if (klass == nullptr || !graph->GetRuntime()->IsClassInitialized(reinterpret_cast<uintptr_t>(klass))) {
2601         return;
2602     }
2603     auto classImm = graph->CreateInstLoadImmediate(DataType::REFERENCE, inst->GetPc(), klass);
2604     inst->ReplaceUsers(classImm);
2605     inst->GetBasicBlock()->InsertAfter(classImm, inst);
2606 
2607     inst->ClearFlag(compiler::inst_flags::NO_DCE);
2608     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2609 }
2610 
VisitInitClass(GraphVisitor * v,Inst * inst)2611 void Peepholes::VisitInitClass(GraphVisitor *v, Inst *inst)
2612 {
2613     auto graph = inst->GetBasicBlock()->GetGraph();
2614     if (!graph->IsJitOrOsrMode()) {
2615         return;
2616     }
2617     auto klass = inst->CastToInitClass()->GetClass();
2618     if (klass == nullptr || !graph->GetRuntime()->IsClassInitialized(reinterpret_cast<uintptr_t>(klass))) {
2619         return;
2620     }
2621     inst->ClearFlag(compiler::inst_flags::NO_DCE);
2622     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2623 }
2624 
VisitIntrinsic(GraphVisitor * v,Inst * inst)2625 void Peepholes::VisitIntrinsic([[maybe_unused]] GraphVisitor *v, Inst *inst)
2626 {
2627     auto intrinsic = inst->CastToIntrinsic();
2628     switch (intrinsic->GetIntrinsicId()) {
2629 #include "intrinsics_peephole.inl"
2630         default: {
2631             return;
2632         }
2633     }
2634 }
2635 
VisitLoadClass(GraphVisitor * v,Inst * inst)2636 void Peepholes::VisitLoadClass(GraphVisitor *v, Inst *inst)
2637 {
2638     auto graph = inst->GetBasicBlock()->GetGraph();
2639     if (!graph->IsJitOrOsrMode()) {
2640         return;
2641     }
2642     auto klass = inst->CastToLoadClass()->GetClass();
2643     if (klass == nullptr) {
2644         return;
2645     }
2646     auto classImm = graph->CreateInstLoadImmediate(DataType::REFERENCE, inst->GetPc(), klass);
2647     inst->ReplaceUsers(classImm);
2648     inst->GetBasicBlock()->InsertAfter(classImm, inst);
2649 
2650     inst->ClearFlag(compiler::inst_flags::NO_DCE);
2651     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2652 }
2653 
VisitLoadConstantPool(GraphVisitor * v,Inst * inst)2654 void Peepholes::VisitLoadConstantPool(GraphVisitor *v, Inst *inst)
2655 {
2656     auto graph = inst->GetBasicBlock()->GetGraph();
2657     if (!graph->IsJitOrOsrMode()) {
2658         return;
2659     }
2660     auto func = inst->GetInput(0).GetInst();
2661     void *constantPool = nullptr;
2662     if (func->IsParameter() && func->CastToParameter()->GetArgNumber() == 0) {
2663         constantPool = graph->GetRuntime()->GetConstantPool(graph->GetMethod());
2664     } else {
2665         CallInst *callerInst = nullptr;
2666         for (auto &user : inst->GetUsers()) {
2667             auto userInst = user.GetInst();
2668             if (userInst->GetOpcode() == Opcode::SaveState &&
2669                 user.GetVirtualRegister().GetVRegType() == VRegType::CONST_POOL) {
2670                 callerInst = userInst->CastToSaveState()->GetCallerInst();
2671                 ASSERT(callerInst != nullptr);
2672                 break;
2673             }
2674         }
2675         if (callerInst == nullptr) {
2676             return;
2677         }
2678         if (auto funcObject = callerInst->GetFunctionObject(); funcObject != 0) {
2679             constantPool = graph->GetRuntime()->GetConstantPool(funcObject);
2680         } else {
2681             constantPool = graph->GetRuntime()->GetConstantPool(callerInst->GetCallMethod());
2682         }
2683     }
2684     if (constantPool == nullptr) {
2685         return;
2686     }
2687     auto constantPoolImm = graph->CreateInstLoadImmediate(DataType::ANY, inst->GetPc(), constantPool,
2688                                                           LoadImmediateInst::ObjectType::CONSTANT_POOL);
2689     inst->InsertAfter(constantPoolImm);
2690     inst->ReplaceUsers(constantPoolImm);
2691 
2692     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2693 }
2694 
VisitLoadFromConstantPool(GraphVisitor * v,Inst * inst)2695 void Peepholes::VisitLoadFromConstantPool(GraphVisitor *v, Inst *inst)
2696 {
2697     auto graph = inst->GetBasicBlock()->GetGraph();
2698     // LoadFromConstantPool with string flag may be used for intrinsics inlining, do not optimize too early
2699     if (!graph->IsUnrollComplete()) {
2700         return;
2701     }
2702     auto constantPool = inst->GetInput(0).GetInst();
2703     if (constantPool->GetOpcode() != Opcode::LoadImmediate) {
2704         return;
2705     }
2706     auto offset = inst->CastToLoadFromConstantPool()->GetTypeId();
2707     auto shift = DataType::ShiftByType(DataType::ANY, graph->GetArch());
2708     uintptr_t mem = constantPool->CastToLoadImmediate()->GetConstantPool() +
2709                     graph->GetRuntime()->GetArrayDataOffset(graph->GetArch()) + (offset << shift);
2710     auto load = graph->CreateInstLoadObjFromConst(DataType::ANY, inst->GetPc(), mem);
2711     inst->InsertAfter(load);
2712     inst->ReplaceUsers(load);
2713 
2714     PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2715 }
2716 
VisitLoadStatic(GraphVisitor * v,Inst * inst)2717 void Peepholes::VisitLoadStatic(GraphVisitor *v, Inst *inst)
2718 {
2719     if (ConstFoldingLoadStatic(inst)) {
2720         PEEPHOLE_IS_APPLIED(static_cast<Peepholes *>(v), inst);
2721     }
2722 }
2723 
CreateCompareInsteadOfXorAdd(Inst * oldInst)2724 bool Peepholes::CreateCompareInsteadOfXorAdd(Inst *oldInst)
2725 {
2726     ASSERT(oldInst->GetOpcode() == Opcode::Xor || oldInst->GetOpcode() == Opcode::Add);
2727     auto input0 = oldInst->GetInput(0).GetInst();
2728     [[maybe_unused]] auto input1 = oldInst->GetInput(1).GetInst();
2729 
2730     if (oldInst->GetOpcode() == Opcode::Add) {
2731         ASSERT(input0->GetOpcode() == Opcode::Neg);
2732         input0 = input0->GetInput(0).GetInst();
2733         for (auto &userAdd : oldInst->GetUsers()) {
2734             if (SkipThisPeepholeInOSR(userAdd.GetInst(), input0)) {
2735                 return false;
2736             }
2737         }
2738     }
2739 
2740     // We shouldn't check on OSR with Xor, because old_inst and cmp_inst is placed one by one
2741     ASSERT(input0->GetType() == DataType::BOOL && input1->IsConst() && input1->CastToConstant()->GetIntValue() == 1U);
2742     auto cnst = oldInst->GetBasicBlock()->GetGraph()->FindOrCreateConstant(0);
2743     auto cmpInst = CreateAndInsertInst(Opcode::Compare, oldInst, input0, cnst);
2744     cmpInst->SetType(DataType::BOOL);
2745     cmpInst->CastToCompare()->SetCc(ConditionCode::CC_EQ);
2746     cmpInst->CastToCompare()->SetOperandsType(DataType::BOOL);
2747     auto type = oldInst->GetType();
2748     if (type == DataType::UINT64 || type == DataType::INT64) {
2749         auto cast = cmpInst->GetBasicBlock()->GetGraph()->CreateInstCast();
2750         cast->SetType(type);
2751         cmpInst->InsertAfter(cast);
2752         cmpInst->ReplaceUsers(cast);
2753         cast->SetInput(0, cmpInst);
2754         cast->SetOperandsType(DataType::BOOL);
2755     }
2756     return true;
2757 }
2758 
2759 // Move users from LenArray to constant which used in MultiArray
2760 // Case 1
2761 // 1.s64 ... ->{v3}
2762 // 2.s64 ... ->{v3}
2763 // 3.ref MultiArray ... , v1, v2, ... ->{v4,..}
2764 // 4.s32 LenArray v3 ->{v5, ...}
2765 // 5.    USE      v5
2766 // ===>
2767 // 1.s64 ... ->{v3, v5, ...}
2768 // 2.s64 ... ->{v3}
2769 // 3.ref MultiArray ... , v1, v2, ... ->{v4,..}
2770 // 4.s32 LenArray v3
2771 // 5.    USE      v1
2772 
2773 // Case 2
2774 // 1.s64 ... ->{v3}
2775 // 2.s64 ... ->{v3}
2776 // 3.ref MultiArray ... , v1, v2, ... ->{v4,..}
2777 // 4.ref LoadArray v3, ...
2778 // 5.ref NullCheck v4, ...
2779 // 6.s32 LenArray v5 ->{v7, ...}
2780 // 7.    USE      v6
2781 // ===>
2782 // 1.s64 ... ->{v3}
2783 // 2.s64 ... ->{v3, v7, ...}
2784 // 3.ref MultiArray ... , v1, v2, ... ->{v4,..}
2785 // 4.ref LoadArray v3, ...
2786 // 5.ref NullCheck v4, ...
2787 // 6.s32 LenArray v5
2788 // 7.    USE      v2
OptimizeLenArrayForMultiArray(Inst * lenArray,Inst * inst,size_t indexSize)2789 bool Peepholes::OptimizeLenArrayForMultiArray(Inst *lenArray, Inst *inst, size_t indexSize)
2790 {
2791     if (inst->GetOpcode() == Opcode::MultiArray) {
2792         // Arguments of MultiArray look like : class, size_0, size_1, ..., size_N, SaveState
2793         // When element type of multyarray is array-like object (LenArray can be applyed to it), we can get case when
2794         // number sequential LoadArray with LenArray more than dimension of MultiArrays. So limiting the index_size.
2795         // Example in unittest PeepholesTest.MultiArrayWithLenArrayOfString
2796 
2797         auto multiarrDimension = inst->GetInputsCount() - 2;
2798         if (!(indexSize < multiarrDimension)) {
2799             return false;
2800         }
2801         // MultiArray's sizes starts from index "1", so need add "1" to get absolute index
2802         auto value = inst->GetDataFlowInput(indexSize + 1);
2803         for (auto &it : lenArray->GetUsers()) {
2804             if (SkipThisPeepholeInOSR(it.GetInst(), value)) {
2805                 return false;
2806             }
2807         }
2808         lenArray->ReplaceUsers(value);
2809         return true;
2810     }
2811     if (inst->GetOpcode() == Opcode::LoadArray) {
2812         auto input = inst->GetDataFlowInput(0);
2813         return OptimizeLenArrayForMultiArray(lenArray, input, indexSize + 1);
2814     }
2815     return false;
2816 }
2817 
IsNegationPattern(Inst * inst)2818 bool Peepholes::IsNegationPattern(Inst *inst)
2819 {
2820     ASSERT(inst->GetOpcode() == Opcode::Add);
2821     // Negetion pattern is:
2822     // 1.   Constant 0x1
2823     // 2.b  ...
2824     // 3.i32 Neg v2 -> v4
2825     // 4.i32 Add v3, v1
2826     auto input0 = inst->GetInput(0).GetInst();
2827     if (input0->GetOpcode() != Opcode::Neg || input0->GetInput(0).GetInst()->GetType() != DataType::BOOL ||
2828         !input0->HasSingleUser()) {
2829         return false;
2830     }
2831     // We sure, that constant may be only in input1
2832     auto input1 = inst->GetInput(1).GetInst();
2833     return input1->GetOpcode() == Opcode::Constant && input1->CastToConstant()->GetIntValue() == 1;
2834 }
2835 
TrySimplifyNegationPattern(Inst * inst)2836 bool Peepholes::TrySimplifyNegationPattern(Inst *inst)
2837 {
2838     ASSERT(inst->GetOpcode() == Opcode::Add);
2839     if (!IsNegationPattern(inst)) {
2840         return false;
2841     }
2842     auto suspectInst = inst->GetInput(0).GetInst()->GetInput(0).GetInst();
2843     // Case 8
2844     // We sure, that type of Neg's input is Bool. We shue, that Neg has one user
2845     if (suspectInst->GetOpcode() == Opcode::Compare && suspectInst->HasSingleUser()) {
2846         auto compareInst = suspectInst->CastToCompare();
2847         bool isPossible = true;
2848         for (auto &i : inst->GetUsers()) {
2849             if (SkipThisPeepholeInOSR(i.GetInst(), compareInst)) {
2850                 isPossible = false;
2851                 break;
2852             }
2853         }
2854         if (isPossible) {
2855             inst->ReplaceUsers(compareInst);
2856             compareInst->SetCc(GetInverseConditionCode(compareInst->GetCc()));
2857             PEEPHOLE_IS_APPLIED(this, compareInst);
2858             return true;
2859         }
2860     }
2861 
2862     // Case 9
2863     if (TrySimplifyCompareNegation(inst)) {
2864         return true;
2865     }
2866 
2867     // Case 7
2868     // This is used last of all if no case has worked!
2869     if (CreateCompareInsteadOfXorAdd(inst)) {
2870         PEEPHOLE_IS_APPLIED(this, inst);
2871         return true;
2872     }
2873     return false;
2874 }
2875 
2876 }  // namespace ark::compiler
2877