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