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