• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <array>
17 #include "optimizer/ir/analysis.h"
18 #include "optimizer/ir/basicblock.h"
19 #include "optimizer/analysis/alias_analysis.h"
20 #include "optimizer/analysis/bounds_analysis.h"
21 #include "lowering.h"
22 #include "optimizer/code_generator/encode.h"
23 
24 namespace ark::compiler {
25 
VisitAdd(GraphVisitor * v,Inst * inst)26 void Lowering::VisitAdd([[maybe_unused]] GraphVisitor *v, Inst *inst)
27 {
28     auto newInst = LowerBinaryOperationWithShiftedOperand<Opcode::Add>(inst);
29     if (newInst == nullptr && LowerAddSub(inst) != nullptr) {
30         return;
31     }
32     LowerMultiplyAddSub(newInst == nullptr ? inst : newInst);
33 }
34 
VisitSub(GraphVisitor * v,Inst * inst)35 void Lowering::VisitSub([[maybe_unused]] GraphVisitor *v, Inst *inst)
36 {
37     auto newInst = LowerBinaryOperationWithShiftedOperand<Opcode::Sub, false>(inst);
38     if (newInst == nullptr && LowerAddSub(inst) != nullptr) {
39         return;
40     }
41     LowerMultiplyAddSub(inst);
42 }
43 
VisitCastValueToAnyType(GraphVisitor * v,Inst * inst)44 void Lowering::VisitCastValueToAnyType([[maybe_unused]] GraphVisitor *v, Inst *inst)
45 {
46     auto graph = inst->GetBasicBlock()->GetGraph();
47     if (graph->IsBytecodeOptimizer() || graph->IsOsrMode()) {
48         // Find way to enable it in OSR mode.
49         return;
50     }
51 
52     // from
53     // 1.u64 Const N -> (v2)
54     // 2.any CastValueToAnyType INT_TYPE v1 -> (...)
55     //
56     // to
57     // 1.any Const Pack(N) -> (...)
58     if (LowerCastValueToAnyTypeWithConst(inst)) {
59         graph->GetEventWriter().EventLowering(GetOpcodeString(inst->GetOpcode()), inst->GetId(), inst->GetPc());
60         COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode());
61         return;
62     }
63     auto anyType = inst->CastToCastValueToAnyType()->GetAnyType();
64     auto baseType = AnyBaseTypeToDataType(anyType);
65     // We can't propogate opject, because GC can move it
66     if (baseType == DataType::REFERENCE) {
67         return;
68     }
69     // from
70     // 2.any CastValueToAnyType INT_TYPE v1 -> (v3)
71     // 3     SaveState                   v2(acc)
72     //
73     // to
74     // 3     SaveState                   v1(acc)
75     auto input = inst->GetInput(0).GetInst();
76     if (input->IsConst() && baseType == DataType::VOID) {
77         input = graph->FindOrCreateConstant(DataType::Any(input->CastToConstant()->GetIntValue()));
78     }
79     for (auto it = inst->GetUsers().begin(); it != inst->GetUsers().end();) {
80         auto userInst = it->GetInst();
81         if (userInst->IsSaveState()) {
82             userInst->SetInput(it->GetIndex(), input);
83             it = inst->GetUsers().begin();
84         } else {
85             ++it;
86         }
87     }
88 }
89 
VisitCast(GraphVisitor * v,Inst * inst)90 void Lowering::VisitCast([[maybe_unused]] GraphVisitor *v, Inst *inst)
91 {
92     // unsigned Load in AARCH64 zerod all high bits
93     // from
94     //  1.u8(u16, u32) Load ->(v2)
95     //  2.u64(u32) Cast u8(u16, u32) -> (v3 ..)
96     // to
97     //  1.u8(u16) Load ->(v3, ..)
98     auto graph = inst->GetBasicBlock()->GetGraph();
99     if (graph->GetArch() != Arch::AARCH64) {
100         return;
101     }
102     auto type = inst->GetType();
103     if (DataType::IsTypeSigned(type)) {
104         return;
105     }
106     auto inputType = inst->CastToCast()->GetOperandsType();
107     if (DataType::IsTypeSigned(inputType) || DataType::Is64Bits(inputType, graph->GetArch())) {
108         return;
109     }
110     auto inputInst = inst->GetInput(0).GetInst();
111     if (!inputInst->IsLoad() || inputInst->GetType() != inputType) {
112         return;
113     }
114     inst->ReplaceUsers(inputInst);
115     inputInst->GetBasicBlock()->GetGraph()->GetEventWriter().EventLowering(GetOpcodeString(inst->GetOpcode()),
116                                                                            inst->GetId(), inst->GetPc());
117     COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode());
118 }
119 
120 template <Opcode OPC>
VisitBitwiseBinaryOperation(GraphVisitor * v,Inst * inst)121 void Lowering::VisitBitwiseBinaryOperation([[maybe_unused]] GraphVisitor *v, Inst *inst)
122 {
123     auto newInst = LowerBinaryOperationWithShiftedOperand<OPC>(inst);  // NOLINT(readability-magic-numbers)
124     if (newInst == nullptr && LowerLogic(inst) != nullptr) {
125         return;
126     }
127     LowerLogicWithInvertedOperand(newInst == nullptr ? inst : newInst);
128 }
129 
VisitOr(GraphVisitor * v,Inst * inst)130 void Lowering::VisitOr(GraphVisitor *v, Inst *inst)
131 {
132     VisitBitwiseBinaryOperation<Opcode::Or>(v, inst);
133 }
134 
VisitAnd(GraphVisitor * v,Inst * inst)135 void Lowering::VisitAnd(GraphVisitor *v, Inst *inst)
136 {
137     VisitBitwiseBinaryOperation<Opcode::And>(v, inst);
138 }
139 
VisitXor(GraphVisitor * v,Inst * inst)140 void Lowering::VisitXor(GraphVisitor *v, Inst *inst)
141 {
142     VisitBitwiseBinaryOperation<Opcode::Xor>(v, inst);
143 }
144 
VisitAndNot(GraphVisitor * v,Inst * inst)145 void Lowering::VisitAndNot([[maybe_unused]] GraphVisitor *v, Inst *inst)
146 {
147     LowerBinaryOperationWithShiftedOperand<Opcode::AndNot, false>(inst);
148 }
149 
VisitXorNot(GraphVisitor * v,Inst * inst)150 void Lowering::VisitXorNot([[maybe_unused]] GraphVisitor *v, Inst *inst)
151 {
152     LowerBinaryOperationWithShiftedOperand<Opcode::XorNot, false>(inst);
153 }
154 
VisitOrNot(GraphVisitor * v,Inst * inst)155 void Lowering::VisitOrNot([[maybe_unused]] GraphVisitor *v, Inst *inst)
156 {
157     LowerBinaryOperationWithShiftedOperand<Opcode::OrNot, false>(inst);
158 }
159 
VisitSaveState(GraphVisitor * v,Inst * inst)160 void Lowering::VisitSaveState([[maybe_unused]] GraphVisitor *v, Inst *inst)
161 {
162     ASSERT(inst->GetOpcode() == Opcode::SaveState);
163     LowerStateInst(inst->CastToSaveState());
164 }
165 
VisitSafePoint(GraphVisitor * v,Inst * inst)166 void Lowering::VisitSafePoint([[maybe_unused]] GraphVisitor *v, Inst *inst)
167 {
168     ASSERT(inst->GetOpcode() == Opcode::SafePoint);
169     LowerStateInst(inst->CastToSafePoint());
170 }
171 
VisitSaveStateOsr(GraphVisitor * v,Inst * inst)172 void Lowering::VisitSaveStateOsr([[maybe_unused]] GraphVisitor *v, Inst *inst)
173 {
174     ASSERT(inst->GetOpcode() == Opcode::SaveStateOsr);
175     LowerStateInst(inst->CastToSaveStateOsr());
176 }
177 
VisitSaveStateDeoptimize(GraphVisitor * v,Inst * inst)178 void Lowering::VisitSaveStateDeoptimize([[maybe_unused]] GraphVisitor *v, Inst *inst)
179 {
180     ASSERT(inst->GetOpcode() == Opcode::SaveStateDeoptimize);
181     LowerStateInst(inst->CastToSaveStateDeoptimize());
182 }
183 
VisitBoundsCheck(GraphVisitor * v,Inst * inst)184 void Lowering::VisitBoundsCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
185 {
186     ASSERT(inst->GetOpcode() == Opcode::BoundsCheck);
187     LowerConstArrayIndex<BoundsCheckInstI>(inst, Opcode::BoundsCheckI);
188 }
189 
VisitLoadArray(GraphVisitor * v,Inst * inst)190 void Lowering::VisitLoadArray([[maybe_unused]] GraphVisitor *v, Inst *inst)
191 {
192     ASSERT(inst->GetOpcode() == Opcode::LoadArray);
193     LowerConstArrayIndex<LoadInstI>(inst, Opcode::LoadArrayI);
194 }
195 
VisitLoadCompressedStringChar(GraphVisitor * v,Inst * inst)196 void Lowering::VisitLoadCompressedStringChar([[maybe_unused]] GraphVisitor *v, Inst *inst)
197 {
198     ASSERT(inst->GetOpcode() == Opcode::LoadCompressedStringChar);
199     LowerConstArrayIndex<LoadCompressedStringCharInstI>(inst, Opcode::LoadCompressedStringCharI);
200 }
201 
VisitStoreArray(GraphVisitor * v,Inst * inst)202 void Lowering::VisitStoreArray([[maybe_unused]] GraphVisitor *v, Inst *inst)
203 {
204     ASSERT(inst->GetOpcode() == Opcode::StoreArray);
205     LowerConstArrayIndex<StoreInstI>(inst, Opcode::StoreArrayI);
206 }
207 
VisitLoad(GraphVisitor * v,Inst * inst)208 void Lowering::VisitLoad([[maybe_unused]] GraphVisitor *v, Inst *inst)
209 {
210     ASSERT(inst->GetOpcode() == Opcode::Load);
211     LowerMemInstScale(inst);
212 }
213 
VisitStore(GraphVisitor * v,Inst * inst)214 void Lowering::VisitStore([[maybe_unused]] GraphVisitor *v, Inst *inst)
215 {
216     ASSERT(inst->GetOpcode() == Opcode::Store);
217     LowerMemInstScale(inst);
218 }
219 
VisitReturn(GraphVisitor * v,Inst * inst)220 void Lowering::VisitReturn([[maybe_unused]] GraphVisitor *v, Inst *inst)
221 {
222     ASSERT(inst->GetOpcode() == Opcode::Return);
223     LowerReturnInst(inst->CastToReturn());
224 }
225 
VisitShr(GraphVisitor * v,Inst * inst)226 void Lowering::VisitShr([[maybe_unused]] GraphVisitor *v, Inst *inst)
227 {
228     LowerShift(inst);
229 }
230 
VisitAShr(GraphVisitor * v,Inst * inst)231 void Lowering::VisitAShr([[maybe_unused]] GraphVisitor *v, Inst *inst)
232 {
233     LowerShift(inst);
234 }
235 
VisitShl(GraphVisitor * v,Inst * inst)236 void Lowering::VisitShl([[maybe_unused]] GraphVisitor *v, Inst *inst)
237 {
238     LowerShift(inst);
239 }
240 
VisitIfImm(GraphVisitor * v,Inst * inst)241 void Lowering::VisitIfImm([[maybe_unused]] GraphVisitor *v, Inst *inst)
242 {
243     ASSERT(inst->GetOpcode() == Opcode::IfImm);
244     static_cast<Lowering *>(v)->LowerIf(inst->CastToIfImm());
245 }
246 
VisitMul(GraphVisitor * v,Inst * inst)247 void Lowering::VisitMul([[maybe_unused]] GraphVisitor *v, Inst *inst)
248 {
249     if (inst->GetInput(1).GetInst()->GetOpcode() != Opcode::Constant) {
250         LowerNegateMultiply(inst);
251     } else {
252         LowerMulDivMod<Opcode::Mul>(inst);
253     }
254 }
255 
VisitDiv(GraphVisitor * v,Inst * inst)256 void Lowering::VisitDiv([[maybe_unused]] GraphVisitor *v, Inst *inst)
257 {
258     if (TryReplaceDivPowerOfTwo(v, inst)) {
259         return;
260     }
261     if (TryReplaceDivModNonPowerOfTwo(v, inst)) {
262         return;
263     }
264     LowerMulDivMod<Opcode::Div>(inst);
265 }
266 
ReplaceSignedDivPowerOfTwo(GraphVisitor * v,Inst * inst,int64_t sValue)267 void Lowering::ReplaceSignedDivPowerOfTwo([[maybe_unused]] GraphVisitor *v, Inst *inst, int64_t sValue)
268 {
269     // 0.signed Parameter
270     // 1.i64 Const 2^n -> {v2}
271     // 2.signed DIV v0, v1 -> {v3}
272     // 3.signed INST v2
273     // ===>
274     // 0.signed Parameter
275     // 1.i64 Const n -> {v2}
276     // 2.signed ASHR v0, type_size - 1 -> {v4} // 0 or -1
277     // 4.signed SHR v2, type_size - n -> {v5} //  0 or 2^n - 1
278     // 5.signed ADD v4, v0 -> {v6}
279     // 6.signed ASHR v5, n -> {v3 or v7}
280     // if n < 0 7.signed NEG v6 ->{v3}
281     // 3.signed INST v6 or v7
282 
283     auto graph = inst->GetBasicBlock()->GetGraph();
284     auto input0 = inst->GetInput(0).GetInst();
285     int64_t n = GetPowerOfTwo(bit_cast<uint64_t>(helpers::math::AbsOrMin(sValue)));
286     ASSERT(n != -1);
287 
288     auto typeSize = DataType::GetTypeSize(inst->GetType(), graph->GetArch());
289     auto ashr =
290         graph->CreateInstAShr(inst->GetType(), inst->GetPc(), input0, graph->FindOrCreateConstant(typeSize - 1));
291     inst->InsertBefore(ashr);
292     auto shr = graph->CreateInstShr(inst->GetType(), inst->GetPc(), ashr, graph->FindOrCreateConstant(typeSize - n));
293     inst->InsertBefore(shr);
294     auto add = graph->CreateInstAdd(inst->GetType(), inst->GetPc(), shr, input0);
295     inst->InsertBefore(add);
296     Inst *ashr2 = graph->CreateInstAShr(inst->GetType(), inst->GetPc(), add, graph->FindOrCreateConstant(n));
297 
298     auto result = ashr2;
299     if (sValue < 0) {
300         inst->InsertBefore(ashr2);
301         result = graph->CreateInstNeg(inst->GetType(), inst->GetPc(), ashr2);
302     }
303 
304     InsertInstruction(inst, result);
305 
306     LowerShift(ashr);
307     LowerShift(shr);
308     LowerShift(ashr2);
309 }
310 
ReplaceUnsignedDivPowerOfTwo(GraphVisitor * v,Inst * inst,uint64_t uValue)311 void Lowering::ReplaceUnsignedDivPowerOfTwo([[maybe_unused]] GraphVisitor *v, Inst *inst, uint64_t uValue)
312 {
313     // 0.unsigned Parameter
314     // 1.i64 Const 2^n -> {v2}
315     // 2.un-signed DIV v0, v1 -> {v3}
316     // 3.unsigned INST v2
317     // ===>
318     // 0.unsigned Parameter
319     // 1.i64 Const n -> {v2}
320     // 2.un-signed SHR v0, v1 -> {v3}
321     // 3.unsigned INST v2
322 
323     auto graph = inst->GetBasicBlock()->GetGraph();
324     auto input0 = inst->GetInput(0).GetInst();
325 
326     int64_t n = GetPowerOfTwo(uValue);
327     ASSERT(n != -1);
328     auto power = graph->FindOrCreateConstant(n);
329     auto shrInst = graph->CreateInstShr(inst->GetType(), inst->GetPc(), input0, power);
330     InsertInstruction(inst, shrInst);
331 
332     LowerShift(shrInst);
333 }
334 
SatisfyReplaceDivMovConditions(Inst * inst)335 bool Lowering::SatisfyReplaceDivMovConditions(Inst *inst)
336 {
337     if (inst->GetBasicBlock()->GetGraph()->IsBytecodeOptimizer()) {
338         return false;
339     }
340     if (DataType::IsFloatType(inst->GetType())) {
341         return false;
342     }
343     auto c = inst->GetInput(1).GetInst();
344     return c->IsConst();
345 }
346 
TryReplaceDivPowerOfTwo(GraphVisitor * v,Inst * inst)347 bool Lowering::TryReplaceDivPowerOfTwo([[maybe_unused]] GraphVisitor *v, Inst *inst)
348 {
349     if (!SatisfyReplaceDivMovConditions(inst)) {
350         return false;
351     }
352 
353     uint64_t uValue = inst->GetInput(1).GetInst()->CastToConstant()->GetInt64Value();
354     auto sValue = bit_cast<int64_t>(uValue);
355 
356     auto input0 = inst->GetInput(0).GetInst();
357     bool isSigned = DataType::IsTypeSigned(input0->GetType());
358     if ((isSigned && !helpers::math::IsPowerOfTwo(sValue)) || (!isSigned && !helpers::math::IsPowerOfTwo(uValue))) {
359         return false;
360     }
361 
362     if (isSigned) {
363         ReplaceSignedDivPowerOfTwo(v, inst, sValue);
364     } else {
365         ReplaceUnsignedDivPowerOfTwo(v, inst, uValue);
366     }
367     return true;
368 }
369 
TryReplaceDivModNonPowerOfTwo(GraphVisitor * v,Inst * inst)370 bool Lowering::TryReplaceDivModNonPowerOfTwo([[maybe_unused]] GraphVisitor *v, Inst *inst)
371 {
372     if (!SatisfyReplaceDivMovConditions(inst)) {
373         return false;
374     }
375 
376     auto *graph = inst->GetBasicBlock()->GetGraph();
377     uint64_t uValue = inst->GetInput(1).GetInst()->CastToConstant()->GetInt64Value();
378 
379     auto input0 = inst->GetInput(0).GetInst();
380     bool isSigned = DataType::IsTypeSigned(input0->GetType());
381     if (!graph->GetEncoder()->CanOptimizeImmDivMod(uValue, isSigned)) {
382         return false;
383     }
384 
385     if (inst->GetOpcode() == Opcode::Div) {
386         auto divImmInst = graph->CreateInstDivI(inst->GetType(), inst->GetPc(), input0, uValue);
387         InsertInstruction(inst, divImmInst);
388     } else {
389         ASSERT(inst->GetOpcode() == Opcode::Mod);
390         auto modImmInst = graph->CreateInstModI(inst->GetType(), inst->GetPc(), input0, uValue);
391         InsertInstruction(inst, modImmInst);
392     }
393     return true;
394 }
395 
TryReplaceModPowerOfTwo(GraphVisitor * v,Inst * inst)396 bool Lowering::TryReplaceModPowerOfTwo([[maybe_unused]] GraphVisitor *v, Inst *inst)
397 {
398     if (!SatisfyReplaceDivMovConditions(inst)) {
399         return false;
400     }
401 
402     uint64_t uValue = inst->GetInput(1).GetInst()->CastToConstant()->GetInt64Value();
403     auto sValue = bit_cast<int64_t>(uValue);
404 
405     auto input0 = inst->GetInput(0).GetInst();
406     bool isSigned = DataType::IsTypeSigned(input0->GetType());
407     if ((isSigned && !helpers::math::IsPowerOfTwo(sValue)) || (!isSigned && !helpers::math::IsPowerOfTwo(uValue))) {
408         return false;
409     }
410 
411     if (isSigned) {
412         int64_t absValue = helpers::math::AbsOrMin(sValue);
413         ReplaceSignedModPowerOfTwo(v, inst, bit_cast<uint64_t>(absValue));
414     } else {
415         ReplaceUnsignedModPowerOfTwo(v, inst, uValue);
416     }
417     return true;
418 }
419 
ReplaceSignedModPowerOfTwo(GraphVisitor * v,Inst * inst,uint64_t absValue)420 void Lowering::ReplaceSignedModPowerOfTwo([[maybe_unused]] GraphVisitor *v, Inst *inst, uint64_t absValue)
421 {
422     // It is optimal for AARCH64, not for AMD64. But even for AMD64 significantly better than original Mod.
423     // 1. ...
424     // 2. Const 0x4
425     // 3. Mod v1, v2
426     // ====>
427     // 1. ...
428     // 4. Const 0x3
429     // 7. Const 0xFFFFFFFFFFFFFFFC
430     // 5. Add v1, v4
431     // 6. SelectImm v5, v1, v1, 0, CC_LT
432     // 8. And v6, v7
433     // 9. Sub v1, v8
434     auto graph = inst->GetBasicBlock()->GetGraph();
435     auto input0 = inst->GetInput(0).GetInst();
436     auto valueMinus1 = absValue - 1;
437     uint32_t size = (inst->GetType() == DataType::UINT64 || inst->GetType() == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
438     Inst *addInst;
439     if (graph->GetEncoder()->CanEncodeImmAddSubCmp(valueMinus1, size, false)) {
440         addInst = graph->CreateInstAddI(inst, input0, valueMinus1);
441     } else {
442         auto valueMinus1Cnst = graph->FindOrCreateConstant(valueMinus1);
443         addInst = graph->CreateInstAdd(inst, input0, valueMinus1Cnst);
444     }
445     Inst *selectInst;
446     if (graph->GetEncoder()->CanEncodeImmAddSubCmp(0, size, true)) {
447         selectInst = graph->CreateInstSelectImm(inst, std::array<Inst *, 3U> {addInst, input0, input0}, 0,
448                                                 inst->GetType(), ConditionCode::CC_LT);
449     } else {
450         auto zeroCnst = graph->FindOrCreateConstant(0);
451         selectInst = graph->CreateInstSelect(inst, std::array<Inst *, 4U> {addInst, input0, input0, zeroCnst},
452                                              inst->GetType(), ConditionCode::CC_LT);
453     }
454     auto maskValue = ~static_cast<uint64_t>(valueMinus1);
455     Inst *andInst;
456     if (graph->GetEncoder()->CanEncodeImmLogical(maskValue, size)) {
457         andInst = graph->CreateInstAndI(inst, selectInst, maskValue);
458     } else {
459         auto mask = graph->FindOrCreateConstant(maskValue);
460         andInst = graph->CreateInstAnd(inst, selectInst, mask);
461     }
462     auto subInst = graph->CreateInstSub(inst, input0, andInst);
463 
464     inst->InsertBefore(addInst);
465     inst->InsertBefore(selectInst);
466     inst->InsertBefore(andInst);
467     InsertInstruction(inst, subInst);
468 }
469 
ReplaceUnsignedModPowerOfTwo(GraphVisitor * v,Inst * inst,uint64_t absValue)470 void Lowering::ReplaceUnsignedModPowerOfTwo([[maybe_unused]] GraphVisitor *v, Inst *inst, uint64_t absValue)
471 {
472     auto graph = inst->GetBasicBlock()->GetGraph();
473     auto valueMinus1 = absValue - 1;
474     uint32_t size = (inst->GetType() == DataType::UINT64 || inst->GetType() == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
475     Inst *andInst;
476     if (graph->GetEncoder()->CanEncodeImmLogical(valueMinus1, size)) {
477         andInst = graph->CreateInstAndI(inst, inst->GetInput(0).GetInst(), valueMinus1);
478     } else {
479         auto valueMinus1Cnst = graph->FindOrCreateConstant(valueMinus1);
480         andInst = graph->CreateInstAnd(inst, inst->GetInput(0).GetInst(), valueMinus1Cnst);
481     }
482     InsertInstruction(inst, andInst);
483 }
484 
VisitMod(GraphVisitor * v,Inst * inst)485 void Lowering::VisitMod([[maybe_unused]] GraphVisitor *v, Inst *inst)
486 {
487     if (TryReplaceModPowerOfTwo(v, inst)) {
488         return;
489     }
490     if (TryReplaceDivModNonPowerOfTwo(v, inst)) {
491         return;
492     }
493     LowerMulDivMod<Opcode::Mod>(inst);
494 }
495 
VisitNeg(GraphVisitor * v,Inst * inst)496 void Lowering::VisitNeg([[maybe_unused]] GraphVisitor *v, Inst *inst)
497 {
498     auto newInst = LowerNegateMultiply(inst);
499     LowerUnaryOperationWithShiftedOperand<Opcode::Neg>(newInst == nullptr ? inst : newInst);
500 }
501 
VisitDeoptimizeIf(GraphVisitor * v,Inst * inst)502 void Lowering::VisitDeoptimizeIf([[maybe_unused]] GraphVisitor *v, Inst *inst)
503 {
504     LowerToDeoptimizeCompare(inst);
505 }
506 
VisitLoadFromConstantPool(GraphVisitor * v,Inst * inst)507 void Lowering::VisitLoadFromConstantPool([[maybe_unused]] GraphVisitor *v, Inst *inst)
508 {
509     auto graph = inst->GetBasicBlock()->GetGraph();
510     auto newInst = graph->CreateInstLoadArrayI(DataType::ANY, inst->GetPc(), inst->GetInput(0).GetInst(),
511                                                inst->CastToLoadFromConstantPool()->GetTypeId());
512 #ifdef PANDA_COMPILER_DEBUG_INFO
513     newInst->SetCurrentMethod(inst->GetCurrentMethod());
514 #endif
515     inst->ReplaceUsers(newInst);
516     inst->RemoveInputs();
517     inst->GetBasicBlock()->ReplaceInst(inst, newInst);
518     graph->GetEventWriter().EventLowering(GetOpcodeString(inst->GetOpcode()), inst->GetId(), inst->GetPc());
519     COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode());
520 }
521 
522 // Replacing Compare EQ with Xor
523 // 1.i64 Const 0
524 // 2.b   ...
525 // 3.b   Compare EQ b v2, v1
526 // ===>
527 // 1.i64 Const 1
528 // 2.b   ...
529 // 3.i32 Xor v1, v2
VisitCompare(GraphVisitor * v,Inst * inst)530 void Lowering::VisitCompare(GraphVisitor *v, Inst *inst)
531 {
532     auto input0 = inst->GetInput(0).GetInst();
533     auto input1 = inst->GetInput(1).GetInst();
534 
535     if (inst->CastToCompare()->GetCc() != ConditionCode::CC_EQ) {
536         return;
537     }
538 
539     // Compare EQ b 0x0, v2
540     if (input1->GetType() == DataType::BOOL && input0->IsConst() && input0->CastToConstant()->GetIntValue() == 0U) {
541         std::swap(input0, input1);
542     }
543 
544     // Compare EQ b v2, 0x0
545     bool isApplicable =
546         input0->GetType() == DataType::BOOL && input1->IsConst() && input1->CastToConstant()->GetIntValue() == 0U;
547     if (!isApplicable) {
548         return;
549     }
550     // Always there are more than one user of Compare, because previous pass is Cleanup
551     bool onlyIfimm = true;
552     for (auto &user : inst->GetUsers()) {
553         if (user.GetInst()->GetOpcode() != Opcode::IfImm) {
554             onlyIfimm = false;
555             break;
556         }
557     }
558     // Skip optimization, if all users is IfImm, optimization Compare+IfImm will be better
559     if (onlyIfimm) {
560         return;
561     }
562     auto graph = inst->GetBasicBlock()->GetGraph();
563     auto cnst = graph->FindOrCreateConstant(1);
564     auto xorInst = graph->CreateInstXor(DataType::BOOL, inst->GetPc(), input0, cnst);
565 #ifdef PANDA_COMPILER_DEBUG_INFO
566     xorInst->SetCurrentMethod(inst->GetCurrentMethod());
567 #endif
568     InsertInstruction(inst, xorInst);
569     static_cast<Lowering *>(v)->VisitXor(v, xorInst);
570 }
571 
572 template <size_t MAX_OPERANDS>
SetInputsAndInsertInstruction(OperandsCapture<MAX_OPERANDS> & operands,Inst * inst,Inst * newInst)573 void Lowering::SetInputsAndInsertInstruction(OperandsCapture<MAX_OPERANDS> &operands, Inst *inst, Inst *newInst)
574 {
575     for (size_t idx = 0; idx < MAX_OPERANDS; idx++) {
576         newInst->SetInput(idx, operands.Get(idx));
577     }
578     InsertInstruction(inst, newInst);
579 }
580 
LowerShift(Inst * inst)581 void Lowering::LowerShift(Inst *inst)
582 {
583     Opcode opc = inst->GetOpcode();
584     ASSERT(opc == Opcode::Shr || opc == Opcode::AShr || opc == Opcode::Shl);
585     auto pred = GetCheckInstAndGetConstInput(inst);
586     if (pred == nullptr) {
587         return;
588     }
589     ASSERT(pred->GetOpcode() == Opcode::Constant);
590     uint64_t val = (static_cast<const ConstantInst *>(pred))->GetIntValue();
591     DataType::Type type = inst->GetType();
592     uint32_t size = (type == DataType::UINT64 || type == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
593     if (val >= size) {
594         return;
595     }
596 
597     auto graph = inst->GetBasicBlock()->GetGraph();
598     if (!graph->GetEncoder()->CanEncodeShift(size)) {
599         return;
600     }
601 
602     Inst *newInst;
603     if (opc == Opcode::Shr) {
604         newInst = graph->CreateInstShrI(inst, inst->GetInput(0).GetInst(), val);
605     } else if (opc == Opcode::AShr) {
606         newInst = graph->CreateInstAShrI(inst, inst->GetInput(0).GetInst(), val);
607     } else {
608         newInst = graph->CreateInstShlI(inst, inst->GetInput(0).GetInst(), val);
609     }
610     InsertInstruction(inst, newInst);
611 }
612 
GetInstructionWithShiftedOperand(Opcode opcode)613 constexpr Opcode Lowering::GetInstructionWithShiftedOperand(Opcode opcode)
614 {
615     switch (opcode) {
616         case Opcode::Add:
617             return Opcode::AddSR;
618         case Opcode::Sub:
619             return Opcode::SubSR;
620         case Opcode::And:
621             return Opcode::AndSR;
622         case Opcode::Or:
623             return Opcode::OrSR;
624         case Opcode::Xor:
625             return Opcode::XorSR;
626         case Opcode::AndNot:
627             return Opcode::AndNotSR;
628         case Opcode::OrNot:
629             return Opcode::OrNotSR;
630         case Opcode::XorNot:
631             return Opcode::XorNotSR;
632         case Opcode::Neg:
633             return Opcode::NegSR;
634         default:
635             UNREACHABLE();
636     }
637 }
638 
GetInstructionWithInvertedOperand(Opcode opcode)639 constexpr Opcode Lowering::GetInstructionWithInvertedOperand(Opcode opcode)
640 {
641     switch (opcode) {
642         case Opcode::And:
643             return Opcode::AndNot;
644         case Opcode::Or:
645             return Opcode::OrNot;
646         case Opcode::Xor:
647             return Opcode::XorNot;
648         default:
649             return Opcode::INVALID;
650     }
651 }
652 
GetShiftTypeByOpcode(Opcode opcode)653 ShiftType Lowering::GetShiftTypeByOpcode(Opcode opcode)
654 {
655     switch (opcode) {
656         case Opcode::Shl:
657         case Opcode::ShlI:
658             return ShiftType::LSL;
659         case Opcode::Shr:
660         case Opcode::ShrI:
661             return ShiftType::LSR;
662         case Opcode::AShr:
663         case Opcode::AShrI:
664             return ShiftType::ASR;
665         default:
666             UNREACHABLE();
667     }
668 }
669 
GetCheckInstAndGetConstInput(Inst * inst)670 Inst *Lowering::GetCheckInstAndGetConstInput(Inst *inst)
671 {
672     DataType::Type type = inst->GetType();
673     if (type != DataType::INT64 && type != DataType::UINT64 && type != DataType::INT32 && type != DataType::UINT32 &&
674         type != DataType::POINTER && type != DataType::BOOL) {
675         return nullptr;
676     }
677 
678     auto cnst = inst->GetInput(1).GetInst();
679     if (!cnst->IsConst()) {
680         if (!inst->IsCommutative() || !inst->GetInput(0).GetInst()->IsConst()) {
681             return nullptr;
682         }
683         ASSERT(!DataType::IsFloatType(inst->GetType()));
684         auto input = cnst;
685         cnst = inst->GetInput(0).GetInst();
686         inst->SetInput(0, input);
687         inst->SetInput(1, cnst);
688     }
689     ASSERT(cnst->GetOpcode() == Opcode::Constant);
690     return cnst;
691 }
692 
ConvertOpcode(Opcode newOpcode)693 ShiftOpcode Lowering::ConvertOpcode(Opcode newOpcode)
694 {
695     switch (newOpcode) {
696         case Opcode::NegSR:
697             return ShiftOpcode::NEG_SR;
698         case Opcode::AddSR:
699             return ShiftOpcode::ADD_SR;
700         case Opcode::SubSR:
701             return ShiftOpcode::SUB_SR;
702         case Opcode::AndSR:
703             return ShiftOpcode::AND_SR;
704         case Opcode::OrSR:
705             return ShiftOpcode::OR_SR;
706         case Opcode::XorSR:
707             return ShiftOpcode::XOR_SR;
708         case Opcode::AndNotSR:
709             return ShiftOpcode::AND_NOT_SR;
710         case Opcode::OrNotSR:
711             return ShiftOpcode::OR_NOT_SR;
712         case Opcode::XorNotSR:
713             return ShiftOpcode::XOR_NOT_SR;
714         default:
715             return ShiftOpcode::INVALID_SR;
716     }
717 }
718 
719 // Ask encoder whether Constant can be an immediate for Compare
ConstantFitsCompareImm(Inst * cst,uint32_t size,ConditionCode cc)720 bool Lowering::ConstantFitsCompareImm(Inst *cst, uint32_t size, ConditionCode cc)
721 {
722     ASSERT(cst->GetOpcode() == Opcode::Constant);
723     if (DataType::IsFloatType(cst->GetType())) {
724         return false;
725     }
726     auto *graph = cst->GetBasicBlock()->GetGraph();
727     auto *encoder = graph->GetEncoder();
728     int64_t val = cst->CastToConstant()->GetRawValue();
729     if (graph->IsBytecodeOptimizer()) {
730         return (size == HALF_SIZE) && (val == 0);
731     }
732     if (cc == ConditionCode::CC_TST_EQ || cc == ConditionCode::CC_TST_NE) {
733         return encoder->CanEncodeImmLogical(val, size);
734     }
735     return encoder->CanEncodeImmAddSubCmp(val, size, IsSignedConditionCode(cc));
736 }
737 
LowerAddSub(Inst * inst)738 Inst *Lowering::LowerAddSub(Inst *inst)
739 {
740     ASSERT(inst->GetOpcode() == Opcode::Add || inst->GetOpcode() == Opcode::Sub);
741     auto pred = GetCheckInstAndGetConstInput(inst);
742     if (pred == nullptr) {
743         return nullptr;
744     }
745 
746     ASSERT(pred->GetOpcode() == Opcode::Constant);
747 
748     auto graph = pred->GetBasicBlock()->GetGraph();
749     auto val = static_cast<int64_t>(pred->CastToConstant()->GetIntValue());
750     DataType::Type type = inst->GetType();
751     uint32_t size = (type == DataType::UINT64 || type == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
752     if (!graph->GetEncoder()->CanEncodeImmAddSubCmp(val, size, false)) {
753         return nullptr;
754     }
755 
756     bool isAdd = (inst->GetOpcode() == Opcode::Add);
757     if (val < 0 && graph->GetEncoder()->CanEncodeImmAddSubCmp(-val, size, false)) {
758         val = -val;
759         isAdd = !isAdd;
760     }
761 
762     Inst *newInst;
763     if (isAdd) {
764         newInst = graph->CreateInstAddI(inst, inst->GetInput(0).GetInst(), static_cast<uint64_t>(val));
765     } else {
766         newInst = graph->CreateInstSubI(inst, inst->GetInput(0).GetInst(), static_cast<uint64_t>(val));
767     }
768     InsertInstruction(inst, newInst);
769     return newInst;
770 }
771 
772 template <Opcode OPCODE>
LowerMulDivMod(Inst * inst)773 void Lowering::LowerMulDivMod(Inst *inst)
774 {
775     ASSERT(inst->GetOpcode() == OPCODE);
776     auto graph = inst->GetBasicBlock()->GetGraph();
777     if (graph->IsInstThrowable(inst)) {
778         return;
779     }
780 
781     auto pred = GetCheckInstAndGetConstInput(inst);
782     if (pred == nullptr) {
783         return;
784     }
785 
786     auto val = static_cast<int64_t>(pred->CastToConstant()->GetIntValue());
787     DataType::Type type = inst->GetType();
788     uint32_t size = (type == DataType::UINT64 || type == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
789     if (!graph->GetEncoder()->CanEncodeImmMulDivMod(val, size)) {
790         return;
791     }
792 
793     Inst *newInst;
794     // NOLINTNEXTLINE(readability-magic-numbers,readability-braces-around-statements, bugprone-branch-clone)
795     if constexpr (OPCODE == Opcode::Mul) {
796         newInst = graph->CreateInstMulI(inst, inst->GetInput(0).GetInst(), static_cast<uint64_t>(val));
797         // NOLINTNEXTLINE(readability-misleading-indentation,readability-braces-around-statements)
798     } else if constexpr (OPCODE == Opcode::Div) {
799         newInst = graph->CreateInstDivI(inst, inst->GetInput(0).GetInst(), static_cast<uint64_t>(val));
800         if (graph->IsBytecodeOptimizer()) {
801             inst->ClearFlag(compiler::inst_flags::NO_DCE);  // In Bytecode Optimizer Div may have NO_DCE flag
802             if (val == 0) {
803                 newInst->SetFlag(compiler::inst_flags::NO_DCE);
804             }
805         }
806         // NOLINTNEXTLINE(readability-misleading-indentation)
807     } else {
808         newInst = graph->CreateInstModI(inst, inst->GetInput(0).GetInst(), static_cast<uint64_t>(val));
809         if (graph->IsBytecodeOptimizer()) {
810             inst->ClearFlag(compiler::inst_flags::NO_DCE);  // In Bytecode Optimizer Div may have NO_DCE flag
811             if (val == 0) {
812                 newInst->SetFlag(compiler::inst_flags::NO_DCE);
813             }
814         }
815     }
816     InsertInstruction(inst, newInst);
817 }
818 
LowerMultiplyAddSub(Inst * inst)819 Inst *Lowering::LowerMultiplyAddSub(Inst *inst)
820 {
821     // Don't use MAdd/MSub for floating point inputs to avoid different results for interpreted and
822     // compiled code due to better precision of target instructions implementing MAdd/MSub.
823     if (DataType::GetCommonType(inst->GetType()) != DataType::INT64) {
824         return nullptr;
825     }
826 
827     OperandsCapture<3U> operands {};
828     InstructionsCapture<2U> insts {};
829     InstructionsCapture<3U> instsSub3 {};
830     bool isSub = true;
831 
832     // clang-format off
833     using MAddMatcher = ADD<MUL<SRC0, SRC1, Flags::S>, SRC2>;
834     using MSubMatcher2Ops =
835         AnyOf<SUB<SRC2, MUL<SRC0, SRC1, Flags::S>>,
836               ADD<BinaryOp<Opcode::MNeg, SRC0, SRC1, Flags::S>, SRC2>>;
837     using MSubMatcher3Ops =
838         AnyOf<ADD<MUL<NEG<SRC0, Flags::S>, SRC1, Flags::S>, SRC2>,
839               ADD<NEG<MUL<SRC0, SRC1, Flags::S>, Flags::S>, SRC2>>;
840     // clang-format on
841 
842     if (MSubMatcher2Ops::Capture(inst, operands, insts)) {
843         // Operands may have different types (but the same common type), but instructions
844         // having different types can not be fused, because it will change semantics.
845         if (!insts.HaveSameType()) {
846             return nullptr;
847         }
848     } else if (MSubMatcher3Ops::Capture(inst, operands, instsSub3)) {
849         if (!instsSub3.HaveSameType()) {
850             return nullptr;
851         }
852     } else if (MAddMatcher::Capture(inst, operands, insts.ResetIndex())) {
853         isSub = false;
854         if (!insts.HaveSameType()) {
855             return nullptr;
856         }
857     } else {
858         return nullptr;
859     }
860 
861     auto graph = inst->GetBasicBlock()->GetGraph();
862     auto encoder = graph->GetEncoder();
863     if ((isSub && !encoder->CanEncodeMSub()) || (!isSub && !encoder->CanEncodeMAdd())) {
864         return nullptr;
865     }
866 
867     Inst *newInst = isSub ? graph->CreateInstMSub(inst) : graph->CreateInstMAdd(inst);
868     SetInputsAndInsertInstruction(operands, inst, newInst);
869     return newInst;
870 }
871 
LowerNegateMultiply(Inst * inst)872 Inst *Lowering::LowerNegateMultiply(Inst *inst)
873 {
874     OperandsCapture<2U> operands {};
875     InstructionsCapture<2U> insts {};
876     using MNegMatcher = AnyOf<NEG<MUL<SRC0, SRC1, Flags::S>>, MUL<NEG<SRC0, Flags::S>, SRC1>>;
877     if (!MNegMatcher::Capture(inst, operands, insts) || !operands.HaveCommonType() || !insts.HaveSameType()) {
878         return nullptr;
879     }
880 
881     auto graph = inst->GetBasicBlock()->GetGraph();
882     if (!graph->GetEncoder()->CanEncodeMNeg()) {
883         return nullptr;
884     }
885 
886     Inst *newInst = graph->CreateInstMNeg(inst);
887     SetInputsAndInsertInstruction(operands, inst, newInst);
888     return newInst;
889 }
890 
LowerCastValueToAnyTypeWithConst(Inst * inst)891 bool Lowering::LowerCastValueToAnyTypeWithConst(Inst *inst)
892 {
893     auto graph = inst->GetBasicBlock()->GetGraph();
894     auto anyType = inst->CastToCastValueToAnyType()->GetAnyType();
895     auto baseType = AnyBaseTypeToDataType(anyType);
896     if (!IsTypeNumeric(baseType) || baseType == DataType::POINTER) {
897         return false;
898     }
899     auto inputInst = inst->GetInput(0).GetInst();
900     if (!inputInst->IsConst()) {
901         return false;
902     }
903     auto imm = inputInst->CastToConstant()->GetRawValue();
904     auto packImm = graph->GetRuntime()->GetPackConstantByPrimitiveType(anyType, imm);
905     auto anyConst = inst->GetBasicBlock()->GetGraph()->FindOrCreateConstant(DataType::Any(packImm));
906     inst->ReplaceUsers(anyConst);
907     return true;
908 }
909 
LowerLogicWithInvertedOperand(Inst * inst)910 void Lowering::LowerLogicWithInvertedOperand(Inst *inst)
911 {
912     OperandsCapture<2U> operands {};
913     InstructionsCapture<2U> insts {};
914     using Matcher = AnyOf<BinaryOp<Opcode::Or, SRC0, UnaryOp<Opcode::Not, SRC1, Flags::S>, Flags::C>,
915                           BinaryOp<Opcode::And, SRC0, UnaryOp<Opcode::Not, SRC1, Flags::S>, Flags::C>,
916                           BinaryOp<Opcode::Xor, SRC0, UnaryOp<Opcode::Not, SRC1, Flags::S>, Flags::C>>;
917     if (!Matcher::Capture(inst, operands, insts) || !insts.HaveSameType()) {
918         return;
919     }
920 
921     auto graph = inst->GetBasicBlock()->GetGraph();
922     auto encoder = graph->GetEncoder();
923     auto opcode = inst->GetOpcode();
924     Inst *newInst;
925     if (opcode == Opcode::Or) {
926         if (!encoder->CanEncodeOrNot()) {
927             return;
928         }
929         newInst = graph->CreateInstOrNot(inst);
930     } else if (opcode == Opcode::And) {
931         if (!encoder->CanEncodeAndNot()) {
932             return;
933         }
934         newInst = graph->CreateInstAndNot(inst);
935     } else {
936         if (!encoder->CanEncodeXorNot()) {
937             return;
938         }
939         newInst = graph->CreateInstXorNot(inst);
940     }
941 
942     SetInputsAndInsertInstruction(operands, inst, newInst);
943 }
944 
945 template <typename T, size_t MAX_OPERANDS>
LowerOperationWithShiftedOperand(Inst * inst,OperandsCapture<MAX_OPERANDS> & operands,Inst * shiftInst,Opcode newOpcode)946 Inst *Lowering::LowerOperationWithShiftedOperand(Inst *inst, OperandsCapture<MAX_OPERANDS> &operands, Inst *shiftInst,
947                                                  Opcode newOpcode)
948 {
949     auto graph = inst->GetBasicBlock()->GetGraph();
950     auto encoder = graph->GetEncoder();
951 
952     ShiftType shiftType = GetShiftTypeByOpcode(shiftInst->GetOpcode());
953     if (!encoder->CanEncodeShiftedOperand(ConvertOpcode(newOpcode), shiftType)) {
954         return nullptr;
955     }
956     uint64_t imm = static_cast<BinaryImmOperation *>(shiftInst)->GetImm();
957     auto newInst = static_cast<T *>(graph->CreateInst(newOpcode));
958     newInst->SetType(inst->GetType());
959     newInst->SetPc(inst->GetPc());
960     newInst->SetImm(imm);
961     newInst->SetShiftType(shiftType);
962 #ifdef PANDA_COMPILER_DEBUG_INFO
963     newInst->SetCurrentMethod(inst->GetCurrentMethod());
964 #endif
965     SetInputsAndInsertInstruction(operands, inst, newInst);
966     return newInst;
967 }
968 
969 template <Opcode OPCODE, bool IS_COMMUTATIVE>
LowerBinaryOperationWithShiftedOperand(Inst * inst)970 Inst *Lowering::LowerBinaryOperationWithShiftedOperand(Inst *inst)
971 {
972     OperandsCapture<2U> operands {};
973     InstructionsCapture<2U> insts {};
974     InstructionsCapture<3U> invInsts {};
975     constexpr auto FLAGS = IS_COMMUTATIVE ? Flags::COMMUTATIVE : Flags::NONE;
976 
977     // We're expecting that at this point all "shift by immediate" patterns were replaced with ShlI/ShrI/AShrI
978     // clang-format off
979     using Matcher = AnyOf<BinaryOp<OPCODE, SRC0, SHLI<SRC1>, FLAGS>,
980                           BinaryOp<OPCODE, SRC0, SHRI<SRC1>, FLAGS>,
981                           BinaryOp<OPCODE, SRC0, ASHRI<SRC1>, FLAGS>>;
982     // Instead of replacing instruction having inverted operand with single inverted-operand instruction
983     // and then applying the rules defined above we're applying explicitly defined rules for such patterns,
984     // because after inverted-operand instruction insertion there will be several users for shift operation.
985     // BinaryOp won't match the IR-tree with a pattern and either more complicated checks should be introduced there
986     // or DCE pass followed by additional Lowering pass should be performed.
987     // To keep things simple and avoid extra Lowering passes explicit rules were added.
988     using InvertedOperandMatcher = MatchIf<GetInstructionWithInvertedOperand(OPCODE) != Opcode::INVALID,
989                                          AnyOf<BinaryOp<OPCODE, SRC0, NOT<SHLI<SRC1>>>,
990                                          BinaryOp<OPCODE, SRC0, NOT<SHRI<SRC1>>>,
991                                          BinaryOp<OPCODE, SRC0, NOT<ASHRI<SRC1>>>>>;
992     // clang-format on
993 
994     if (GetCommonType(inst->GetType()) != DataType::INT64) {
995         return nullptr;
996     }
997 
998     Inst *shiftInst;
999     Opcode newOpc;
1000 
1001     if (InvertedOperandMatcher::Capture(inst, operands, invInsts) && invInsts.HaveSameType()) {
1002         auto rightOperand =
1003             operands.Get(0) == inst->GetInput(0).GetInst() ? inst->GetInput(1).GetInst() : inst->GetInput(0).GetInst();
1004         shiftInst = rightOperand->GetInput(0).GetInst();
1005         newOpc = GetInstructionWithShiftedOperand(GetInstructionWithInvertedOperand(OPCODE));
1006     } else if (Matcher::Capture(inst, operands, insts) && insts.HaveSameType()) {
1007         shiftInst =
1008             operands.Get(0) == inst->GetInput(0).GetInst() ? inst->GetInput(1).GetInst() : inst->GetInput(0).GetInst();
1009         newOpc = GetInstructionWithShiftedOperand(OPCODE);
1010     } else {
1011         return nullptr;
1012     }
1013 
1014     return LowerOperationWithShiftedOperand<BinaryShiftedRegisterOperation>(inst, operands, shiftInst, newOpc);
1015 }
1016 
1017 template <Opcode OPCODE>
LowerUnaryOperationWithShiftedOperand(Inst * inst)1018 void Lowering::LowerUnaryOperationWithShiftedOperand(Inst *inst)
1019 {
1020     OperandsCapture<1> operands {};
1021     InstructionsCapture<2U> insts {};
1022     // We're expecting that at this point all "shift by immediate" patterns were replaced with ShlI/ShrI/AShrI
1023     // clang-format off
1024     using Matcher = AnyOf<UnaryOp<OPCODE, SHLI<SRC0>>,
1025                           UnaryOp<OPCODE, SHRI<SRC0>>,
1026                           UnaryOp<OPCODE, ASHRI<SRC0>>>;
1027     // clang-format on
1028     if (!Matcher::Capture(inst, operands, insts) || GetCommonType(inst->GetType()) != DataType::INT64 ||
1029         !insts.HaveSameType()) {
1030         return;
1031     }
1032     LowerOperationWithShiftedOperand<UnaryShiftedRegisterOperation>(inst, operands, inst->GetInput(0).GetInst(),
1033                                                                     GetInstructionWithShiftedOperand(OPCODE));
1034 }
1035 
LowerLogic(Inst * inst)1036 Inst *Lowering::LowerLogic(Inst *inst)
1037 {
1038     Opcode opc = inst->GetOpcode();
1039     ASSERT(opc == Opcode::Or || opc == Opcode::And || opc == Opcode::Xor);
1040     auto pred = GetCheckInstAndGetConstInput(inst);
1041     if (pred == nullptr) {
1042         return nullptr;
1043     }
1044     ASSERT(pred->GetOpcode() == Opcode::Constant);
1045     uint64_t val = pred->CastToConstant()->GetIntValue();
1046     DataType::Type type = inst->GetType();
1047     uint32_t size = (type == DataType::UINT64 || type == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
1048     auto graph = inst->GetBasicBlock()->GetGraph();
1049     if (!graph->GetEncoder()->CanEncodeImmLogical(val, size)) {
1050         return nullptr;
1051     }
1052     Inst *newInst;
1053     if (opc == Opcode::Or) {
1054         newInst = graph->CreateInstOrI(inst, inst->GetInput(0).GetInst(), val);
1055     } else if (opc == Opcode::And) {
1056         newInst = graph->CreateInstAndI(inst, inst->GetInput(0).GetInst(), val);
1057     } else {
1058         newInst = graph->CreateInstXorI(inst, inst->GetInput(0).GetInst(), val);
1059     }
1060     InsertInstruction(inst, newInst);
1061     return newInst;
1062 }
1063 
1064 // From
1065 //  2.u64 ShlI v1, 0x3 -> (v3)
1066 //  3.u64 Load v0, v2  -> (...)
1067 // To
1068 //  3.u64 Load v0, v2, Scale 0x3 -> (...)
LowerMemInstScale(Inst * inst)1069 void Lowering::LowerMemInstScale(Inst *inst)
1070 {
1071     auto opcode = inst->GetOpcode();
1072     ASSERT(opcode == Opcode::Load || opcode == Opcode::Store);
1073     auto inputInst = inst->GetInput(1).GetInst();
1074     if (inputInst->GetOpcode() != Opcode::ShlI) {
1075         return;
1076     }
1077     auto graph = inst->GetBasicBlock()->GetGraph();
1078     auto inputType = inputInst->GetType();
1079     if (Is64BitsArch(graph->GetArch())) {
1080         if (inputType != DataType::UINT64 && inputType != DataType::INT64) {
1081             return;
1082         }
1083     } else {
1084         if (inputType != DataType::UINT32 && inputType != DataType::INT32) {
1085             return;
1086         }
1087     }
1088     auto type = inst->GetType();
1089     uint64_t val = inputInst->CastToShlI()->GetImm();
1090     uint32_t size = DataType::GetTypeSize(type, graph->GetArch());
1091     if (!graph->GetEncoder()->CanEncodeScale(val, size)) {
1092         return;
1093     }
1094     if (opcode == Opcode::Load) {
1095         ASSERT(inst->CastToLoad()->GetScale() == 0);
1096         inst->CastToLoad()->SetScale(val);
1097     } else {
1098         ASSERT(inst->CastToStore()->GetScale() == 0);
1099         inst->CastToStore()->SetScale(val);
1100     }
1101     inst->SetInput(1, inputInst->GetInput(0).GetInst());
1102     graph->GetEventWriter().EventLowering(GetOpcodeString(inst->GetOpcode()), inst->GetId(), inst->GetPc());
1103     COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode());
1104 }
1105 
1106 template <typename LowLevelType>
LowerConstArrayIndex(Inst * inst,Opcode lowLevelOpcode)1107 void Lowering::LowerConstArrayIndex(Inst *inst, Opcode lowLevelOpcode)
1108 {
1109     if (inst->GetBasicBlock()->GetGraph()->IsBytecodeOptimizer()) {
1110         return;
1111     }
1112     static constexpr size_t ARRAY_INDEX_INPUT = 1;
1113     auto inputInst = inst->GetInput(ARRAY_INDEX_INPUT).GetInst();
1114     ASSERT(inputInst->GetOpcode() != Opcode::BoundsCheckI);
1115     if (inputInst->IsConst()) {
1116         uint64_t value = inputInst->CastToConstant()->GetIntValue();
1117 
1118         auto graph = inst->GetBasicBlock()->GetGraph();
1119         auto newInst = graph->CreateInst(lowLevelOpcode);
1120         newInst->SetType(inst->GetType());
1121         newInst->SetPc(inst->GetPc());
1122 #ifdef PANDA_COMPILER_DEBUG_INFO
1123         newInst->SetCurrentMethod(inst->GetCurrentMethod());
1124 #endif
1125         static_cast<LowLevelType *>(newInst)->SetImm(value);
1126 
1127         // StoreInst and BoundsCheckInst have 3 inputs, LoadInst - has 2 inputs
1128         newInst->SetInput(0, inst->GetInput(0).GetInst());
1129         if (inst->GetInputsCount() == 3U) {
1130             newInst->SetInput(1, inst->GetInput(2U).GetInst());
1131         } else {
1132             ASSERT(inst->GetInputsCount() == 2U);
1133         }
1134         if (inst->GetOpcode() == Opcode::StoreArray) {
1135             newInst->CastToStoreArrayI()->SetNeedBarrier(inst->CastToStoreArray()->GetNeedBarrier());
1136         }
1137 
1138         if (inst->GetOpcode() == Opcode::LoadArray) {
1139             newInst->CastToLoadArrayI()->SetNeedBarrier(inst->CastToLoadArray()->GetNeedBarrier());
1140             newInst->CastToLoadArrayI()->SetIsArray(inst->CastToLoadArray()->IsArray());
1141         }
1142         if (inst->GetOpcode() == Opcode::BoundsCheck) {
1143             newInst->CastToBoundsCheckI()->SetIsArray(inst->CastToBoundsCheck()->IsArray());
1144             if (inst->CanDeoptimize()) {
1145                 newInst->SetFlag(inst_flags::CAN_DEOPTIMIZE);
1146             }
1147         }
1148 
1149         // Replace instruction immediately because it's not removable by DCE
1150         if (inst->GetOpcode() != Opcode::BoundsCheck) {
1151             inst->ReplaceUsers(newInst);
1152         } else {
1153             auto cnst = graph->FindOrCreateConstant(value);
1154             inst->ReplaceUsers(cnst);
1155         }
1156         inst->RemoveInputs();
1157         inst->GetBasicBlock()->ReplaceInst(inst, newInst);
1158         graph->GetEventWriter().EventLowering(GetOpcodeString(inst->GetOpcode()), inst->GetId(), inst->GetPc());
1159         COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode());
1160     }
1161 }
1162 
LowerStateInst(SaveStateInst * saveState)1163 void Lowering::LowerStateInst(SaveStateInst *saveState)
1164 {
1165     size_t idx = 0;
1166     size_t inputsCount = saveState->GetInputsCount();
1167     auto graph = saveState->GetBasicBlock()->GetGraph();
1168     if (graph->IsBytecodeOptimizer()) {
1169         return;
1170     }
1171     bool skipFloats = (graph->GetArch() == Arch::AARCH32);
1172     while (idx < inputsCount) {
1173         auto inputInst = saveState->GetInput(idx).GetInst();
1174         // In Aarch32 floats values stores in different format then integer
1175         if (inputInst->GetOpcode() == Opcode::NullPtr ||
1176             (inputInst->IsConst() && (!skipFloats || inputInst->GetType() == DataType::INT64))) {
1177             uint64_t rawValue =
1178                 inputInst->GetOpcode() == Opcode::NullPtr ? 0 : inputInst->CastToConstant()->GetRawValue();
1179             auto vreg = saveState->GetVirtualRegister(idx);
1180             auto type = inputInst->GetType();
1181             // There are no INT64 in dynamic
1182             if (type == DataType::INT64 && graph->IsDynamicMethod()) {
1183                 type = DataType::INT32;
1184             }
1185             saveState->AppendImmediate(rawValue, vreg.Value(), type, vreg.GetVRegType());
1186             saveState->RemoveInput(idx);
1187             inputsCount--;
1188             graph->GetEventWriter().EventLowering(GetOpcodeString(saveState->GetOpcode()), saveState->GetId(),
1189                                                   saveState->GetPc());
1190             COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(saveState->GetOpcode());
1191         } else {
1192             idx++;
1193         }
1194     }
1195 }
1196 
LowerReturnInst(FixedInputsInst1 * ret)1197 void Lowering::LowerReturnInst(FixedInputsInst1 *ret)
1198 {
1199     auto graph = ret->GetBasicBlock()->GetGraph();
1200     if (graph->IsBytecodeOptimizer()) {
1201         return;
1202     }
1203     ASSERT(ret->GetOpcode() == Opcode::Return);
1204     auto inputInst = ret->GetInput(0).GetInst();
1205     if (inputInst->IsConst()) {
1206         uint64_t rawValue = inputInst->CastToConstant()->GetRawValue();
1207         auto retImm = graph->CreateInstReturnI(ret->GetType(), ret->GetPc(), rawValue);
1208 #ifdef PANDA_COMPILER_DEBUG_INFO
1209         retImm->SetCurrentMethod(ret->GetCurrentMethod());
1210 #endif
1211 
1212         // Replace instruction immediately because it's not removable by DCE
1213         ret->RemoveInputs();
1214         ret->GetBasicBlock()->ReplaceInst(ret, retImm);
1215         graph->GetEventWriter().EventLowering(GetOpcodeString(ret->GetOpcode()), ret->GetId(), ret->GetPc());
1216         COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(ret->GetOpcode());
1217     }
1218 }
1219 
1220 // We'd like to swap only to make second operand immediate
BetterToSwapCompareInputs(Inst * cmp)1221 bool Lowering::BetterToSwapCompareInputs(Inst *cmp)
1222 {
1223     ASSERT(cmp->GetOpcode() == Opcode::Compare);
1224     auto in0 = cmp->GetInput(0).GetInst();
1225     auto in1 = cmp->GetInput(1).GetInst();
1226     if (DataType::IsFloatType(in0->GetType())) {
1227         return false;
1228     }
1229     if (in0->GetOpcode() == compiler::Opcode::NullPtr) {
1230         return true;
1231     }
1232 
1233     if (in0->IsConst()) {
1234         if (in1->IsConst()) {
1235             DataType::Type type = cmp->CastToCompare()->GetOperandsType();
1236             uint32_t size = (type == DataType::UINT64 || type == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
1237             auto cc = cmp->CastToCompare()->GetCc();
1238             return ConstantFitsCompareImm(in0, size, cc) && !ConstantFitsCompareImm(in1, size, cc);
1239         }
1240         return true;
1241     }
1242     return false;
1243 }
1244 
1245 // Optimize order of input arguments for decreasing using accumulator (Bytecodeoptimizer only).
OptimizeIfInput(compiler::Inst * ifInst)1246 void Lowering::OptimizeIfInput(compiler::Inst *ifInst)
1247 {
1248     ASSERT(ifInst->GetOpcode() == compiler::Opcode::If);
1249     compiler::Inst *input0 = ifInst->GetInput(0).GetInst();
1250     compiler::Inst *input1 = ifInst->GetInput(1).GetInst();
1251 
1252     if (input0->IsDominate(input1)) {
1253         ifInst->SetInput(0, input1);
1254         ifInst->SetInput(1, input0);
1255         // And change CC
1256         auto cc = ifInst->CastToIf()->GetCc();
1257         cc = SwapOperandsConditionCode(cc);
1258         ifInst->CastToIf()->SetCc(cc);
1259     }
1260 }
1261 
JoinFcmpInst(IfImmInst * inst,CmpInst * input)1262 void Lowering::JoinFcmpInst(IfImmInst *inst, CmpInst *input)
1263 {
1264     auto cc = inst->GetCc();
1265     ASSERT(cc == ConditionCode::CC_EQ || cc == ConditionCode::CC_NE || IsSignedConditionCode(cc));
1266     if (input->IsFcmpg()) {
1267         /* Please look at the table of vector condition flags:
1268          * LT => Less than, or unordered
1269          * LE => Less than or equal, or unordered
1270          * GT => Greater than
1271          * GE => Greater than or equal
1272          *
1273          * LO => Less than
1274          * LS => Less than or equal
1275          * HI => Greater than, or unordered
1276          * HS => Greater than or equal, or unordered
1277          *
1278          * So we change condition to "unsigned" for Fcmpg (which should return "greater than" for unordered
1279          * comparisons).
1280          */
1281         cc = InverseSignednessConditionCode(cc);
1282     }
1283 
1284     LowerIfImmToIf(inst, input, cc, input->GetOperandsType());
1285 }
1286 
LowerIf(IfImmInst * inst)1287 void Lowering::LowerIf(IfImmInst *inst)
1288 {
1289     ASSERT(inst->GetCc() == ConditionCode::CC_NE || inst->GetCc() == ConditionCode::CC_EQ);
1290     ASSERT(inst->GetImm() == 0);
1291     if (inst->GetOperandsType() != DataType::BOOL) {
1292         ASSERT(GetGraph()->IsDynamicMethod());
1293         return;
1294     }
1295     auto input = inst->GetInput(0).GetInst();
1296     if (input->GetOpcode() != Opcode::Compare && input->GetOpcode() != Opcode::And) {
1297         return;
1298     }
1299     // Check, that inst have only IfImm user
1300     for (auto &user : input->GetUsers()) {
1301         if (user.GetInst()->GetOpcode() != Opcode::IfImm) {
1302             return;
1303         }
1304     }
1305     // Try put constant in second input
1306     if (input->GetOpcode() == Opcode::Compare && BetterToSwapCompareInputs(input)) {
1307         // Swap inputs
1308         auto in0 = input->GetInput(0).GetInst();
1309         auto in1 = input->GetInput(1).GetInst();
1310         input->SetInput(0, in1);
1311         input->SetInput(1, in0);
1312         // And change CC
1313         auto cc = input->CastToCompare()->GetCc();
1314         cc = SwapOperandsConditionCode(cc);
1315         input->CastToCompare()->SetCc(cc);
1316     }
1317     if (!GetGraph()->IsBytecodeOptimizer()) {
1318         for (auto &newInput : input->GetInputs()) {
1319             auto realNewInput = input->GetDataFlowInput(newInput.GetInst());
1320             if (realNewInput->IsMovableObject()) {
1321                 ssb_.SearchAndCreateMissingObjInSaveState(GetGraph(), realNewInput, inst);
1322             }
1323         }
1324     }
1325     auto cst = input->GetInput(1).GetInst();
1326     DataType::Type type =
1327         (input->GetOpcode() == Opcode::Compare) ? input->CastToCompare()->GetOperandsType() : input->GetType();
1328     uint32_t size = (type == DataType::UINT64 || type == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
1329     auto cc = input->GetOpcode() == Opcode::Compare ? input->CastToCompare()->GetCc() : ConditionCode::CC_TST_NE;
1330     // IfImm can be inverted
1331     if (inst->GetCc() == ConditionCode::CC_EQ && inst->GetImm() == 0) {
1332         cc = GetInverseConditionCode(cc);
1333     }
1334     if (cst->GetOpcode() == compiler::Opcode::NullPtr || (cst->IsConst() && ConstantFitsCompareImm(cst, size, cc))) {
1335         // In-place change for IfImm
1336         InPlaceLowerIfImm(inst, input, cst, cc, type);
1337     } else {
1338         LowerIfImmToIf(inst, input, cc, type);
1339     }
1340 }
1341 
InPlaceLowerIfImm(IfImmInst * inst,Inst * input,Inst * cst,ConditionCode cc,DataType::Type inputType)1342 void Lowering::InPlaceLowerIfImm(IfImmInst *inst, Inst *input, Inst *cst, ConditionCode cc, DataType::Type inputType)
1343 {
1344     auto graph = inst->GetBasicBlock()->GetGraph();
1345     inst->SetOperandsType(inputType);
1346     auto newInput = input->GetInput(0).GetInst();
1347     // For compare(nullptr, 0) set `nullptr` as new input
1348     if (cst->GetOpcode() == Opcode::NullPtr && IsZeroConstant(newInput) &&
1349         DataType::IsReference(inst->GetOperandsType())) {
1350         newInput = cst;
1351     }
1352     inst->SetInput(0, newInput);
1353 
1354     uint64_t val = cst->GetOpcode() == Opcode::NullPtr ? 0 : cst->CastToConstant()->GetRawValue();
1355     inst->SetImm(val);
1356     inst->SetCc(cc);
1357     inst->GetBasicBlock()->GetGraph()->GetEventWriter().EventLowering(GetOpcodeString(inst->GetOpcode()), inst->GetId(),
1358                                                                       inst->GetPc());
1359     COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode());
1360 
1361     if (inst->GetImm() == 0 && newInput->GetOpcode() == Opcode::Cmp &&
1362         DataType::IsFloatType(newInput->CastToCmp()->GetOperandsType()) && !graph->IsBytecodeOptimizer()) {
1363         // Check inst and input are the only users of new_input
1364         bool join {true};
1365         for (auto &user : newInput->GetUsers()) {
1366             if (auto userInst = user.GetInst(); userInst != inst && userInst != input) {
1367                 join = false;
1368                 break;
1369             }
1370         }
1371         if (join) {
1372             JoinFcmpInst(inst, newInput->CastToCmp());
1373         }
1374     }
1375 }
1376 
LowerIfImmToIf(IfImmInst * inst,Inst * input,ConditionCode cc,DataType::Type inputType)1377 void Lowering::LowerIfImmToIf(IfImmInst *inst, Inst *input, ConditionCode cc, DataType::Type inputType)
1378 {
1379     auto graph = inst->GetBasicBlock()->GetGraph();
1380     // New instruction
1381     auto replace = graph->CreateInstIf(DataType::NO_TYPE, inst->GetPc(), input->GetInput(0).GetInst(),
1382                                        input->GetInput(1).GetInst(), inputType, cc, inst->GetMethod());
1383 #ifdef PANDA_COMPILER_DEBUG_INFO
1384     replace->SetCurrentMethod(inst->GetCurrentMethod());
1385 #endif
1386     // Replace IfImm instruction immediately because it's not removable by DCE
1387     inst->RemoveInputs();
1388     inst->GetBasicBlock()->ReplaceInst(inst, replace);
1389     graph->GetEventWriter().EventLowering(GetOpcodeString(inst->GetOpcode()), inst->GetId(), inst->GetPc());
1390     if (graph->IsBytecodeOptimizer()) {
1391         OptimizeIfInput(replace);
1392     }
1393     COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode());
1394 }
1395 
LowerToDeoptimizeCompare(Inst * inst)1396 void Lowering::LowerToDeoptimizeCompare(Inst *inst)
1397 {
1398     ASSERT(inst->GetOpcode() == Opcode::DeoptimizeIf);
1399     auto graph = inst->GetBasicBlock()->GetGraph();
1400     ASSERT(!graph->IsBytecodeOptimizer());
1401 
1402     auto deoptIf = inst->CastToDeoptimizeIf();
1403     if (deoptIf->GetInput(0).GetInst()->GetOpcode() != Opcode::Compare) {
1404         return;
1405     }
1406     auto compare = deoptIf->GetInput(0).GetInst()->CastToCompare();
1407     if (!compare->HasSingleUser()) {
1408         return;
1409     }
1410     COMPILER_LOG(DEBUG, LOWERING) << __func__ << "\n" << *compare << "\n" << *deoptIf;
1411     auto cmpInp1 = compare->GetInput(1).GetInst();
1412     DataType::Type type = compare->GetOperandsType();
1413     uint32_t size =
1414         (type == DataType::UINT64 || type == DataType::INT64 || type == DataType::ANY) ? WORD_SIZE : HALF_SIZE;
1415     Inst *deoptCmp = nullptr;
1416     if ((cmpInp1->IsConst() && ConstantFitsCompareImm(cmpInp1, size, compare->GetCc())) || cmpInp1->IsNullPtr()) {
1417         uint64_t imm = cmpInp1->IsNullPtr() ? 0 : cmpInp1->CastToConstant()->GetRawValue();
1418         deoptCmp = graph->CreateInstDeoptimizeCompareImm(deoptIf, compare, imm);
1419     } else {
1420         deoptCmp = graph->CreateInstDeoptimizeCompare(deoptIf, compare);
1421         deoptCmp->SetInput(1, compare->GetInput(1).GetInst());
1422     }
1423     deoptCmp->SetInput(0, compare->GetInput(0).GetInst());
1424     deoptCmp->SetSaveState(deoptIf->GetSaveState());
1425 #ifdef PANDA_COMPILER_DEBUG_INFO
1426     deoptCmp->SetCurrentMethod(inst->GetCurrentMethod());
1427 #endif
1428     deoptIf->ReplaceUsers(deoptCmp);
1429     deoptIf->GetBasicBlock()->InsertAfter(deoptCmp, deoptIf);
1430     deoptIf->ClearFlag(compiler::inst_flags::NO_DCE);
1431     graph->GetEventWriter().EventLowering(GetOpcodeString(deoptIf->GetOpcode()), deoptIf->GetId(), deoptIf->GetPc());
1432     COMPILER_LOG(DEBUG, LOWERING) << "===>\n" << *deoptCmp;
1433 }
1434 
InvalidateAnalyses()1435 void Lowering::InvalidateAnalyses()
1436 {
1437     GetGraph()->InvalidateAnalysis<BoundsAnalysis>();
1438     GetGraph()->InvalidateAnalysis<AliasAnalysis>();
1439 }
1440 
RunImpl()1441 bool Lowering::RunImpl()
1442 {
1443     VisitGraph();
1444     return true;
1445 }
1446 }  // namespace ark::compiler
1447