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