• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2023 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 panda::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     LowerMulDivMod<Opcode::Div>(inst);
259 }
260 
TryReplaceModPowerOfTwo(GraphVisitor * v,Inst * inst)261 bool Lowering::TryReplaceModPowerOfTwo([[maybe_unused]] GraphVisitor *v, Inst *inst)
262 {
263     if (inst->GetBasicBlock()->GetGraph()->IsBytecodeOptimizer()) {
264         return false;
265     }
266     if (DataType::IsFloatType(inst->GetType())) {
267         return false;
268     }
269     auto c = inst->GetInput(1).GetInst();
270     if (!c->IsConst()) {
271         return false;
272     }
273     auto value = c->CastToConstant()->GetInt64Value();
274     if (value == 0) {
275         return false;
276     }
277     auto absValue = std::abs(static_cast<int64_t>(value));
278     if (BitCount(absValue) != 1) {
279         return false;
280     }
281     auto input0 = inst->GetInput(0).GetInst();
282     if (DataType::IsTypeSigned(input0->GetType())) {
283         ReplaceSignedModPowerOfTwo(v, inst, absValue);
284     } else {
285         ReplaceUnsignedModPowerOfTwo(v, inst, absValue);
286     }
287     return true;
288 }
289 
ReplaceSignedModPowerOfTwo(GraphVisitor * v,Inst * inst,uint64_t absValue)290 void Lowering::ReplaceSignedModPowerOfTwo([[maybe_unused]] GraphVisitor *v, Inst *inst, uint64_t absValue)
291 {
292     // It is optimal for AARCH64, not for AMD64. But even for AMD64 significantly better than original Mod.
293     // 1. ...
294     // 2. Const 0x4
295     // 3. Mod v1, v2
296     // ====>
297     // 1. ...
298     // 4. Const 0x3
299     // 7. Const 0xFFFFFFFFFFFFFFFC
300     // 5. Add v1, v4
301     // 6. SelectImm v5, v1, v1, 0, CC_LT
302     // 8. And v6, v7
303     // 9. Sub v1, v8
304     auto graph = inst->GetBasicBlock()->GetGraph();
305     auto input0 = inst->GetInput(0).GetInst();
306     auto valueMinus1 = absValue - 1;
307     uint32_t size = (inst->GetType() == DataType::UINT64 || inst->GetType() == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
308     Inst *addInst;
309     if (graph->GetEncoder()->CanEncodeImmAddSubCmp(valueMinus1, size, false)) {
310         addInst = graph->CreateInstAddI(inst, input0, valueMinus1);
311     } else {
312         auto valueMinus1Cnst = graph->FindOrCreateConstant(valueMinus1);
313         addInst = graph->CreateInstAdd(inst, input0, valueMinus1Cnst);
314     }
315     Inst *selectInst;
316     if (graph->GetEncoder()->CanEncodeImmAddSubCmp(0, size, true)) {
317         selectInst =
318             graph->CreateInstSelectImm(inst, addInst, input0, input0, 0, inst->GetType(), ConditionCode::CC_LT);
319     } else {
320         auto zeroCnst = graph->FindOrCreateConstant(0);
321         selectInst =
322             graph->CreateInstSelect(inst, addInst, input0, input0, zeroCnst, inst->GetType(), ConditionCode::CC_LT);
323     }
324     auto maskValue = ~static_cast<uint64_t>(valueMinus1);
325     Inst *andInst;
326     if (graph->GetEncoder()->CanEncodeImmLogical(maskValue, size)) {
327         andInst = graph->CreateInstAndI(inst, selectInst, maskValue);
328     } else {
329         auto mask = graph->FindOrCreateConstant(maskValue);
330         andInst = graph->CreateInstAnd(inst, selectInst, mask);
331     }
332     auto subInst = graph->CreateInstSub(inst, input0, andInst);
333 
334     inst->InsertBefore(addInst);
335     inst->InsertBefore(selectInst);
336     inst->InsertBefore(andInst);
337     InsertInstruction(inst, subInst);
338 }
339 
ReplaceUnsignedModPowerOfTwo(GraphVisitor * v,Inst * inst,uint64_t absValue)340 void Lowering::ReplaceUnsignedModPowerOfTwo([[maybe_unused]] GraphVisitor *v, Inst *inst, uint64_t absValue)
341 {
342     auto graph = inst->GetBasicBlock()->GetGraph();
343     auto valueMinus1 = absValue - 1;
344     uint32_t size = (inst->GetType() == DataType::UINT64 || inst->GetType() == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
345     Inst *andInst;
346     if (graph->GetEncoder()->CanEncodeImmLogical(valueMinus1, size)) {
347         andInst = graph->CreateInstAndI(inst, inst->GetInput(0).GetInst(), valueMinus1);
348     } else {
349         auto valueMinus1Cnst = graph->FindOrCreateConstant(valueMinus1);
350         andInst = graph->CreateInstAnd(inst, inst->GetInput(0).GetInst(), valueMinus1Cnst);
351     }
352     InsertInstruction(inst, andInst);
353 }
354 
VisitMod(GraphVisitor * v,Inst * inst)355 void Lowering::VisitMod([[maybe_unused]] GraphVisitor *v, Inst *inst)
356 {
357     if (TryReplaceModPowerOfTwo(v, inst)) {
358         return;
359     }
360     LowerMulDivMod<Opcode::Mod>(inst);
361 }
362 
VisitNeg(GraphVisitor * v,Inst * inst)363 void Lowering::VisitNeg([[maybe_unused]] GraphVisitor *v, Inst *inst)
364 {
365     auto newInst = LowerNegateMultiply(inst);
366     LowerUnaryOperationWithShiftedOperand<Opcode::Neg>(newInst == nullptr ? inst : newInst);
367 }
368 
VisitDeoptimizeIf(GraphVisitor * v,Inst * inst)369 void Lowering::VisitDeoptimizeIf([[maybe_unused]] GraphVisitor *v, Inst *inst)
370 {
371     LowerToDeoptimizeCompare(inst);
372 }
373 
VisitLoadFromConstantPool(GraphVisitor * v,Inst * inst)374 void Lowering::VisitLoadFromConstantPool([[maybe_unused]] GraphVisitor *v, Inst *inst)
375 {
376     auto graph = inst->GetBasicBlock()->GetGraph();
377     auto newInst = graph->CreateInstLoadArrayI(DataType::ANY, inst->GetPc(), inst->GetInput(0).GetInst(),
378                                                inst->CastToLoadFromConstantPool()->GetTypeId());
379 #ifdef PANDA_COMPILER_DEBUG_INFO
380     newInst->SetCurrentMethod(inst->GetCurrentMethod());
381 #endif
382     inst->ReplaceUsers(newInst);
383     inst->RemoveInputs();
384     inst->GetBasicBlock()->ReplaceInst(inst, newInst);
385     graph->GetEventWriter().EventLowering(GetOpcodeString(inst->GetOpcode()), inst->GetId(), inst->GetPc());
386     COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode());
387 }
388 
389 // Replacing Compare EQ with Xor
390 // 1.i64 Const 0
391 // 2.b   ...
392 // 3.b   Compare EQ b v2, v1
393 // ===>
394 // 1.i64 Const 1
395 // 2.b   ...
396 // 3.i32 Xor v1, v2
VisitCompare(GraphVisitor * v,Inst * inst)397 void Lowering::VisitCompare(GraphVisitor *v, Inst *inst)
398 {
399     auto input0 = inst->GetInput(0).GetInst();
400     auto input1 = inst->GetInput(1).GetInst();
401 
402     if (inst->CastToCompare()->GetCc() != ConditionCode::CC_EQ) {
403         return;
404     }
405 
406     // Compare EQ b 0x0, v2
407     if (input1->GetType() == DataType::BOOL && input0->IsConst() && input0->CastToConstant()->GetIntValue() == 0U) {
408         std::swap(input0, input1);
409     }
410 
411     // Compare EQ b v2, 0x0
412     bool isApplicable =
413         input0->GetType() == DataType::BOOL && input1->IsConst() && input1->CastToConstant()->GetIntValue() == 0U;
414     if (!isApplicable) {
415         return;
416     }
417     // Always there are more than one user of Compare, because previous pass is Cleanup
418     bool onlyIfimm = true;
419     for (auto &user : inst->GetUsers()) {
420         if (user.GetInst()->GetOpcode() != Opcode::IfImm) {
421             onlyIfimm = false;
422             break;
423         }
424     }
425     // Skip optimization, if all users is IfImm, optimization Compare+IfImm will be better
426     if (onlyIfimm) {
427         return;
428     }
429     auto graph = inst->GetBasicBlock()->GetGraph();
430     auto cnst = graph->FindOrCreateConstant(1);
431     auto xorInst = graph->CreateInstXor(DataType::BOOL, inst->GetPc(), input0, cnst);
432 #ifdef PANDA_COMPILER_DEBUG_INFO
433     xorInst->SetCurrentMethod(inst->GetCurrentMethod());
434 #endif
435     InsertInstruction(inst, xorInst);
436     static_cast<Lowering *>(v)->VisitXor(v, xorInst);
437 }
438 
439 template <size_t MAX_OPERANDS>
SetInputsAndInsertInstruction(OperandsCapture<MAX_OPERANDS> & operands,Inst * inst,Inst * newInst)440 void Lowering::SetInputsAndInsertInstruction(OperandsCapture<MAX_OPERANDS> &operands, Inst *inst, Inst *newInst)
441 {
442     for (size_t idx = 0; idx < MAX_OPERANDS; idx++) {
443         newInst->SetInput(idx, operands.Get(idx));
444     }
445     InsertInstruction(inst, newInst);
446 }
447 
LowerShift(Inst * inst)448 void Lowering::LowerShift(Inst *inst)
449 {
450     Opcode opc = inst->GetOpcode();
451     ASSERT(opc == Opcode::Shr || opc == Opcode::AShr || opc == Opcode::Shl);
452     auto pred = GetCheckInstAndGetConstInput(inst);
453     if (pred == nullptr) {
454         return;
455     }
456     ASSERT(pred->GetOpcode() == Opcode::Constant);
457     uint64_t val = (static_cast<const ConstantInst *>(pred))->GetIntValue();
458     DataType::Type type = inst->GetType();
459     uint32_t size = (type == DataType::UINT64 || type == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
460     if (val >= size) {
461         return;
462     }
463 
464     auto graph = inst->GetBasicBlock()->GetGraph();
465     if (!graph->GetEncoder()->CanEncodeShift(size)) {
466         return;
467     }
468 
469     Inst *newInst;
470     if (opc == Opcode::Shr) {
471         newInst = graph->CreateInstShrI(inst, inst->GetInput(0).GetInst(), val);
472     } else if (opc == Opcode::AShr) {
473         newInst = graph->CreateInstAShrI(inst, inst->GetInput(0).GetInst(), val);
474     } else {
475         newInst = graph->CreateInstShlI(inst, inst->GetInput(0).GetInst(), val);
476     }
477     InsertInstruction(inst, newInst);
478 }
479 
GetInstructionWithShiftedOperand(Opcode opcode)480 constexpr Opcode Lowering::GetInstructionWithShiftedOperand(Opcode opcode)
481 {
482     switch (opcode) {
483         case Opcode::Add:
484             return Opcode::AddSR;
485         case Opcode::Sub:
486             return Opcode::SubSR;
487         case Opcode::And:
488             return Opcode::AndSR;
489         case Opcode::Or:
490             return Opcode::OrSR;
491         case Opcode::Xor:
492             return Opcode::XorSR;
493         case Opcode::AndNot:
494             return Opcode::AndNotSR;
495         case Opcode::OrNot:
496             return Opcode::OrNotSR;
497         case Opcode::XorNot:
498             return Opcode::XorNotSR;
499         case Opcode::Neg:
500             return Opcode::NegSR;
501         default:
502             UNREACHABLE();
503     }
504 }
505 
GetInstructionWithInvertedOperand(Opcode opcode)506 constexpr Opcode Lowering::GetInstructionWithInvertedOperand(Opcode opcode)
507 {
508     switch (opcode) {
509         case Opcode::And:
510             return Opcode::AndNot;
511         case Opcode::Or:
512             return Opcode::OrNot;
513         case Opcode::Xor:
514             return Opcode::XorNot;
515         default:
516             return Opcode::INVALID;
517     }
518 }
519 
GetShiftTypeByOpcode(Opcode opcode)520 ShiftType Lowering::GetShiftTypeByOpcode(Opcode opcode)
521 {
522     switch (opcode) {
523         case Opcode::Shl:
524         case Opcode::ShlI:
525             return ShiftType::LSL;
526         case Opcode::Shr:
527         case Opcode::ShrI:
528             return ShiftType::LSR;
529         case Opcode::AShr:
530         case Opcode::AShrI:
531             return ShiftType::ASR;
532         default:
533             UNREACHABLE();
534     }
535 }
536 
GetCheckInstAndGetConstInput(Inst * inst)537 Inst *Lowering::GetCheckInstAndGetConstInput(Inst *inst)
538 {
539     DataType::Type type = inst->GetType();
540     if (type != DataType::INT64 && type != DataType::UINT64 && type != DataType::INT32 && type != DataType::UINT32 &&
541         type != DataType::POINTER && type != DataType::BOOL) {
542         return nullptr;
543     }
544 
545     auto cnst = inst->GetInput(1).GetInst();
546     if (!cnst->IsConst()) {
547         if (!inst->IsCommutative() || !inst->GetInput(0).GetInst()->IsConst()) {
548             return nullptr;
549         }
550         ASSERT(!DataType::IsFloatType(inst->GetType()));
551         auto input = cnst;
552         cnst = inst->GetInput(0).GetInst();
553         inst->SetInput(0, input);
554         inst->SetInput(1, cnst);
555     }
556     ASSERT(cnst->GetOpcode() == Opcode::Constant);
557     return cnst;
558 }
559 
ConvertOpcode(Opcode newOpcode)560 ShiftOpcode Lowering::ConvertOpcode(Opcode newOpcode)
561 {
562     switch (newOpcode) {
563         case Opcode::NegSR:
564             return ShiftOpcode::NEG_SR;
565         case Opcode::AddSR:
566             return ShiftOpcode::ADD_SR;
567         case Opcode::SubSR:
568             return ShiftOpcode::SUB_SR;
569         case Opcode::AndSR:
570             return ShiftOpcode::AND_SR;
571         case Opcode::OrSR:
572             return ShiftOpcode::OR_SR;
573         case Opcode::XorSR:
574             return ShiftOpcode::XOR_SR;
575         case Opcode::AndNotSR:
576             return ShiftOpcode::AND_NOT_SR;
577         case Opcode::OrNotSR:
578             return ShiftOpcode::OR_NOT_SR;
579         case Opcode::XorNotSR:
580             return ShiftOpcode::XOR_NOT_SR;
581         default:
582             return ShiftOpcode::INVALID_SR;
583     }
584 }
585 
586 // Ask encoder whether Constant can be an immediate for Compare
ConstantFitsCompareImm(Inst * cst,uint32_t size,ConditionCode cc)587 bool Lowering::ConstantFitsCompareImm(Inst *cst, uint32_t size, ConditionCode cc)
588 {
589     ASSERT(cst->GetOpcode() == Opcode::Constant);
590     if (DataType::IsFloatType(cst->GetType())) {
591         return false;
592     }
593     auto *graph = cst->GetBasicBlock()->GetGraph();
594     auto *encoder = graph->GetEncoder();
595     int64_t val = cst->CastToConstant()->GetRawValue();
596     if (graph->IsBytecodeOptimizer()) {
597         return (size == HALF_SIZE) && (val == 0);
598     }
599     if (cc == ConditionCode::CC_TST_EQ || cc == ConditionCode::CC_TST_NE) {
600         return encoder->CanEncodeImmLogical(val, size);
601     }
602     return encoder->CanEncodeImmAddSubCmp(val, size, IsSignedConditionCode(cc));
603 }
604 
LowerAddSub(Inst * inst)605 Inst *Lowering::LowerAddSub(Inst *inst)
606 {
607     ASSERT(inst->GetOpcode() == Opcode::Add || inst->GetOpcode() == Opcode::Sub);
608     auto pred = GetCheckInstAndGetConstInput(inst);
609     if (pred == nullptr) {
610         return nullptr;
611     }
612 
613     ASSERT(pred->GetOpcode() == Opcode::Constant);
614 
615     auto graph = pred->GetBasicBlock()->GetGraph();
616     int64_t val = pred->CastToConstant()->GetIntValue();
617     DataType::Type type = inst->GetType();
618     uint32_t size = (type == DataType::UINT64 || type == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
619     if (!graph->GetEncoder()->CanEncodeImmAddSubCmp(val, size, false)) {
620         return nullptr;
621     }
622 
623     bool isAdd = (inst->GetOpcode() == Opcode::Add);
624     if (val < 0 && graph->GetEncoder()->CanEncodeImmAddSubCmp(-val, size, false)) {
625         val = -val;
626         isAdd = !isAdd;
627     }
628 
629     Inst *newInst;
630     if (isAdd) {
631         newInst = graph->CreateInstAddI(inst, inst->GetInput(0).GetInst(), static_cast<uint64_t>(val));
632     } else {
633         newInst = graph->CreateInstSubI(inst, inst->GetInput(0).GetInst(), static_cast<uint64_t>(val));
634     }
635     InsertInstruction(inst, newInst);
636     return newInst;
637 }
638 
639 template <Opcode OPCODE>
LowerMulDivMod(Inst * inst)640 void Lowering::LowerMulDivMod(Inst *inst)
641 {
642     ASSERT(inst->GetOpcode() == OPCODE);
643     auto graph = inst->GetBasicBlock()->GetGraph();
644     if (graph->IsInstThrowable(inst)) {
645         return;
646     }
647 
648     auto pred = GetCheckInstAndGetConstInput(inst);
649     if (pred == nullptr) {
650         return;
651     }
652 
653     int64_t val = pred->CastToConstant()->GetIntValue();
654     DataType::Type type = inst->GetType();
655     uint32_t size = (type == DataType::UINT64 || type == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
656     if (!graph->GetEncoder()->CanEncodeImmMulDivMod(val, size)) {
657         return;
658     }
659 
660     Inst *newInst;
661     // NOLINTNEXTLINE(readability-magic-numbers,readability-braces-around-statements, bugprone-branch-clone)
662     if constexpr (OPCODE == Opcode::Mul) {
663         newInst = graph->CreateInstMulI(inst, inst->GetInput(0).GetInst(), static_cast<uint64_t>(val));
664         // NOLINTNEXTLINE(readability-misleading-indentation,readability-braces-around-statements)
665     } else if constexpr (OPCODE == Opcode::Div) {
666         newInst = graph->CreateInstDivI(inst, inst->GetInput(0).GetInst(), static_cast<uint64_t>(val));
667         if (graph->IsBytecodeOptimizer()) {
668             inst->ClearFlag(compiler::inst_flags::NO_DCE);  // In Bytecode Optimizer Div may have NO_DCE flag
669             if (val == 0) {
670                 newInst->SetFlag(compiler::inst_flags::NO_DCE);
671             }
672         }
673         // NOLINTNEXTLINE(readability-misleading-indentation)
674     } else {
675         newInst = graph->CreateInstModI(inst, inst->GetInput(0).GetInst(), static_cast<uint64_t>(val));
676         if (graph->IsBytecodeOptimizer()) {
677             inst->ClearFlag(compiler::inst_flags::NO_DCE);  // In Bytecode Optimizer Div may have NO_DCE flag
678             if (val == 0) {
679                 newInst->SetFlag(compiler::inst_flags::NO_DCE);
680             }
681         }
682     }
683     InsertInstruction(inst, newInst);
684 }
685 
LowerMultiplyAddSub(Inst * inst)686 Inst *Lowering::LowerMultiplyAddSub(Inst *inst)
687 {
688     // Don't use MAdd/MSub for floating point inputs to avoid different results for interpreted and
689     // compiled code due to better precision of target instructions implementing MAdd/MSub.
690     if (DataType::GetCommonType(inst->GetType()) != DataType::INT64) {
691         return nullptr;
692     }
693 
694     OperandsCapture<3U> operands {};
695     InstructionsCapture<2U> insts {};
696     InstructionsCapture<3U> instsSub3 {};
697     bool isSub = true;
698 
699     // clang-format off
700     using MAddMatcher = ADD<MUL<SRC0, SRC1, Flags::S>, SRC2>;
701     using MSubMatcher2Ops =
702         AnyOf<SUB<SRC2, MUL<SRC0, SRC1, Flags::S>>,
703               ADD<BinaryOp<Opcode::MNeg, SRC0, SRC1, Flags::S>, SRC2>>;
704     using MSubMatcher3Ops =
705         AnyOf<ADD<MUL<NEG<SRC0, Flags::S>, SRC1, Flags::S>, SRC2>,
706               ADD<NEG<MUL<SRC0, SRC1, Flags::S>, Flags::S>, SRC2>>;
707     // clang-format on
708 
709     if (MSubMatcher2Ops::Capture(inst, operands, insts)) {
710         // Operands may have different types (but the same common type), but instructions
711         // having different types can not be fused, because it will change semantics.
712         if (!insts.HaveSameType()) {
713             return nullptr;
714         }
715     } else if (MSubMatcher3Ops::Capture(inst, operands, instsSub3)) {
716         if (!instsSub3.HaveSameType()) {
717             return nullptr;
718         }
719     } else if (MAddMatcher::Capture(inst, operands, insts.ResetIndex())) {
720         isSub = false;
721         if (!insts.HaveSameType()) {
722             return nullptr;
723         }
724     } else {
725         return nullptr;
726     }
727 
728     auto graph = inst->GetBasicBlock()->GetGraph();
729     auto encoder = graph->GetEncoder();
730     if ((isSub && !encoder->CanEncodeMSub()) || (!isSub && !encoder->CanEncodeMAdd())) {
731         return nullptr;
732     }
733 
734     Inst *newInst = isSub ? graph->CreateInstMSub(inst) : graph->CreateInstMAdd(inst);
735     SetInputsAndInsertInstruction(operands, inst, newInst);
736     return newInst;
737 }
738 
LowerNegateMultiply(Inst * inst)739 Inst *Lowering::LowerNegateMultiply(Inst *inst)
740 {
741     OperandsCapture<2U> operands {};
742     InstructionsCapture<2U> insts {};
743     using MNegMatcher = AnyOf<NEG<MUL<SRC0, SRC1, Flags::S>>, MUL<NEG<SRC0, Flags::S>, SRC1>>;
744     if (!MNegMatcher::Capture(inst, operands, insts) || !operands.HaveCommonType() || !insts.HaveSameType()) {
745         return nullptr;
746     }
747 
748     auto graph = inst->GetBasicBlock()->GetGraph();
749     if (!graph->GetEncoder()->CanEncodeMNeg()) {
750         return nullptr;
751     }
752 
753     Inst *newInst = graph->CreateInstMNeg(inst);
754     SetInputsAndInsertInstruction(operands, inst, newInst);
755     return newInst;
756 }
757 
LowerCastValueToAnyTypeWithConst(Inst * inst)758 bool Lowering::LowerCastValueToAnyTypeWithConst(Inst *inst)
759 {
760     auto graph = inst->GetBasicBlock()->GetGraph();
761     auto anyType = inst->CastToCastValueToAnyType()->GetAnyType();
762     auto baseType = AnyBaseTypeToDataType(anyType);
763     if (!IsTypeNumeric(baseType) || baseType == DataType::POINTER) {
764         return false;
765     }
766     auto inputInst = inst->GetInput(0).GetInst();
767     if (!inputInst->IsConst()) {
768         return false;
769     }
770     auto imm = inputInst->CastToConstant()->GetRawValue();
771     auto packImm = graph->GetRuntime()->GetPackConstantByPrimitiveType(anyType, imm);
772     auto anyConst = inst->GetBasicBlock()->GetGraph()->FindOrCreateConstant(DataType::Any(packImm));
773     inst->ReplaceUsers(anyConst);
774     return true;
775 }
776 
LowerLogicWithInvertedOperand(Inst * inst)777 void Lowering::LowerLogicWithInvertedOperand(Inst *inst)
778 {
779     OperandsCapture<2U> operands {};
780     InstructionsCapture<2U> insts {};
781     using Matcher = AnyOf<BinaryOp<Opcode::Or, SRC0, UnaryOp<Opcode::Not, SRC1, Flags::S>, Flags::C>,
782                           BinaryOp<Opcode::And, SRC0, UnaryOp<Opcode::Not, SRC1, Flags::S>, Flags::C>,
783                           BinaryOp<Opcode::Xor, SRC0, UnaryOp<Opcode::Not, SRC1, Flags::S>, Flags::C>>;
784     if (!Matcher::Capture(inst, operands, insts) || !insts.HaveSameType()) {
785         return;
786     }
787 
788     auto graph = inst->GetBasicBlock()->GetGraph();
789     auto encoder = graph->GetEncoder();
790     auto opcode = inst->GetOpcode();
791     Inst *newInst;
792     if (opcode == Opcode::Or) {
793         if (!encoder->CanEncodeOrNot()) {
794             return;
795         }
796         newInst = graph->CreateInstOrNot(inst);
797     } else if (opcode == Opcode::And) {
798         if (!encoder->CanEncodeAndNot()) {
799             return;
800         }
801         newInst = graph->CreateInstAndNot(inst);
802     } else {
803         if (!encoder->CanEncodeXorNot()) {
804             return;
805         }
806         newInst = graph->CreateInstXorNot(inst);
807     }
808 
809     SetInputsAndInsertInstruction(operands, inst, newInst);
810 }
811 
812 template <typename T, size_t MAX_OPERANDS>
LowerOperationWithShiftedOperand(Inst * inst,OperandsCapture<MAX_OPERANDS> & operands,Inst * shiftInst,Opcode newOpcode)813 Inst *Lowering::LowerOperationWithShiftedOperand(Inst *inst, OperandsCapture<MAX_OPERANDS> &operands, Inst *shiftInst,
814                                                  Opcode newOpcode)
815 {
816     auto graph = inst->GetBasicBlock()->GetGraph();
817     auto encoder = graph->GetEncoder();
818 
819     ShiftType shiftType = GetShiftTypeByOpcode(shiftInst->GetOpcode());
820     if (!encoder->CanEncodeShiftedOperand(ConvertOpcode(newOpcode), shiftType)) {
821         return nullptr;
822     }
823     uint64_t imm = static_cast<BinaryImmOperation *>(shiftInst)->GetImm();
824     auto newInst = static_cast<T *>(graph->CreateInst(newOpcode));
825     newInst->SetType(inst->GetType());
826     newInst->SetPc(inst->GetPc());
827     newInst->SetImm(imm);
828     newInst->SetShiftType(shiftType);
829 #ifdef PANDA_COMPILER_DEBUG_INFO
830     newInst->SetCurrentMethod(inst->GetCurrentMethod());
831 #endif
832     SetInputsAndInsertInstruction(operands, inst, newInst);
833     return newInst;
834 }
835 
836 template <Opcode OPCODE, bool IS_COMMUTATIVE>
LowerBinaryOperationWithShiftedOperand(Inst * inst)837 Inst *Lowering::LowerBinaryOperationWithShiftedOperand(Inst *inst)
838 {
839     OperandsCapture<2U> operands {};
840     InstructionsCapture<2U> insts {};
841     InstructionsCapture<3U> invInsts {};
842     constexpr auto FLAGS = IS_COMMUTATIVE ? Flags::COMMUTATIVE : Flags::NONE;
843 
844     // We're expecting that at this point all "shift by immediate" patterns were replaced with ShlI/ShrI/AShrI
845     // clang-format off
846     using Matcher = AnyOf<BinaryOp<OPCODE, SRC0, SHLI<SRC1>, FLAGS>,
847                           BinaryOp<OPCODE, SRC0, SHRI<SRC1>, FLAGS>,
848                           BinaryOp<OPCODE, SRC0, ASHRI<SRC1>, FLAGS>>;
849     // Instead of replacing instruction having inverted operand with single inverted-operand instruction
850     // and then applying the rules defined above we're applying explicitly defined rules for such patterns,
851     // because after inverted-operand instruction insertion there will be several users for shift operation.
852     // BinaryOp won't match the IR-tree with a pattern and either more complicated checks should be introduced there
853     // or DCE pass followed by additional Lowering pass should be performed.
854     // To keep things simple and avoid extra Lowering passes explicit rules were added.
855     using InvertedOperandMatcher = MatchIf<GetInstructionWithInvertedOperand(OPCODE) != Opcode::INVALID,
856                                          AnyOf<BinaryOp<OPCODE, SRC0, NOT<SHLI<SRC1>>>,
857                                          BinaryOp<OPCODE, SRC0, NOT<SHRI<SRC1>>>,
858                                          BinaryOp<OPCODE, SRC0, NOT<ASHRI<SRC1>>>>>;
859     // clang-format on
860 
861     if (GetCommonType(inst->GetType()) != DataType::INT64) {
862         return nullptr;
863     }
864 
865     Inst *shiftInst;
866     Opcode newOpc;
867 
868     if (InvertedOperandMatcher::Capture(inst, operands, invInsts) && invInsts.HaveSameType()) {
869         auto rightOperand =
870             operands.Get(0) == inst->GetInput(0).GetInst() ? inst->GetInput(1).GetInst() : inst->GetInput(0).GetInst();
871         shiftInst = rightOperand->GetInput(0).GetInst();
872         newOpc = GetInstructionWithShiftedOperand(GetInstructionWithInvertedOperand(OPCODE));
873     } else if (Matcher::Capture(inst, operands, insts) && insts.HaveSameType()) {
874         shiftInst =
875             operands.Get(0) == inst->GetInput(0).GetInst() ? inst->GetInput(1).GetInst() : inst->GetInput(0).GetInst();
876         newOpc = GetInstructionWithShiftedOperand(OPCODE);
877     } else {
878         return nullptr;
879     }
880 
881     return LowerOperationWithShiftedOperand<BinaryShiftedRegisterOperation>(inst, operands, shiftInst, newOpc);
882 }
883 
884 template <Opcode OPCODE>
LowerUnaryOperationWithShiftedOperand(Inst * inst)885 void Lowering::LowerUnaryOperationWithShiftedOperand(Inst *inst)
886 {
887     OperandsCapture<1> operands {};
888     InstructionsCapture<2U> insts {};
889     // We're expecting that at this point all "shift by immediate" patterns were replaced with ShlI/ShrI/AShrI
890     // clang-format off
891     using Matcher = AnyOf<UnaryOp<OPCODE, SHLI<SRC0>>,
892                           UnaryOp<OPCODE, SHRI<SRC0>>,
893                           UnaryOp<OPCODE, ASHRI<SRC0>>>;
894     // clang-format on
895     if (!Matcher::Capture(inst, operands, insts) || GetCommonType(inst->GetType()) != DataType::INT64 ||
896         !insts.HaveSameType()) {
897         return;
898     }
899     LowerOperationWithShiftedOperand<UnaryShiftedRegisterOperation>(inst, operands, inst->GetInput(0).GetInst(),
900                                                                     GetInstructionWithShiftedOperand(OPCODE));
901 }
902 
LowerLogic(Inst * inst)903 Inst *Lowering::LowerLogic(Inst *inst)
904 {
905     Opcode opc = inst->GetOpcode();
906     ASSERT(opc == Opcode::Or || opc == Opcode::And || opc == Opcode::Xor);
907     auto pred = GetCheckInstAndGetConstInput(inst);
908     if (pred == nullptr) {
909         return nullptr;
910     }
911     ASSERT(pred->GetOpcode() == Opcode::Constant);
912     uint64_t val = pred->CastToConstant()->GetIntValue();
913     DataType::Type type = inst->GetType();
914     uint32_t size = (type == DataType::UINT64 || type == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
915     auto graph = inst->GetBasicBlock()->GetGraph();
916     if (!graph->GetEncoder()->CanEncodeImmLogical(val, size)) {
917         return nullptr;
918     }
919     Inst *newInst;
920     if (opc == Opcode::Or) {
921         newInst = graph->CreateInstOrI(inst, inst->GetInput(0).GetInst(), val);
922     } else if (opc == Opcode::And) {
923         newInst = graph->CreateInstAndI(inst, inst->GetInput(0).GetInst(), val);
924     } else {
925         newInst = graph->CreateInstXorI(inst, inst->GetInput(0).GetInst(), val);
926     }
927     InsertInstruction(inst, newInst);
928     return newInst;
929 }
930 
931 // From
932 //  2.u64 ShlI v1, 0x3 -> (v3)
933 //  3.u64 Load v0, v2  -> (...)
934 // To
935 //  3.u64 Load v0, v2, Scale 0x3 -> (...)
LowerMemInstScale(Inst * inst)936 void Lowering::LowerMemInstScale(Inst *inst)
937 {
938     auto opcode = inst->GetOpcode();
939     ASSERT(opcode == Opcode::Load || opcode == Opcode::Store);
940     auto inputInst = inst->GetInput(1).GetInst();
941     if (inputInst->GetOpcode() != Opcode::ShlI) {
942         return;
943     }
944     auto graph = inst->GetBasicBlock()->GetGraph();
945     auto inputType = inputInst->GetType();
946     if (Is64BitsArch(graph->GetArch())) {
947         if (inputType != DataType::UINT64 && inputType != DataType::INT64) {
948             return;
949         }
950     } else {
951         if (inputType != DataType::UINT32 && inputType != DataType::INT32) {
952             return;
953         }
954     }
955     auto type = inst->GetType();
956     uint64_t val = inputInst->CastToShlI()->GetImm();
957     uint32_t size = DataType::GetTypeSize(type, graph->GetArch());
958     if (!graph->GetEncoder()->CanEncodeScale(val, size)) {
959         return;
960     }
961     if (opcode == Opcode::Load) {
962         ASSERT(inst->CastToLoad()->GetScale() == 0);
963         inst->CastToLoad()->SetScale(val);
964     } else {
965         ASSERT(inst->CastToStore()->GetScale() == 0);
966         inst->CastToStore()->SetScale(val);
967     }
968     inst->SetInput(1, inputInst->GetInput(0).GetInst());
969     graph->GetEventWriter().EventLowering(GetOpcodeString(inst->GetOpcode()), inst->GetId(), inst->GetPc());
970     COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode());
971 }
972 
973 template <typename LowLevelType>
LowerConstArrayIndex(Inst * inst,Opcode lowLevelOpcode)974 void Lowering::LowerConstArrayIndex(Inst *inst, Opcode lowLevelOpcode)
975 {
976     if (inst->GetBasicBlock()->GetGraph()->IsBytecodeOptimizer()) {
977         return;
978     }
979     static constexpr size_t ARRAY_INDEX_INPUT = 1;
980     auto inputInst = inst->GetInput(ARRAY_INDEX_INPUT).GetInst();
981     ASSERT(inputInst->GetOpcode() != Opcode::BoundsCheckI);
982     if (inputInst->IsConst()) {
983         uint64_t value = inputInst->CastToConstant()->GetIntValue();
984 
985         auto graph = inst->GetBasicBlock()->GetGraph();
986         auto newInst = graph->CreateInst(lowLevelOpcode);
987         newInst->SetType(inst->GetType());
988         newInst->SetPc(inst->GetPc());
989 #ifdef PANDA_COMPILER_DEBUG_INFO
990         newInst->SetCurrentMethod(inst->GetCurrentMethod());
991 #endif
992         static_cast<LowLevelType *>(newInst)->SetImm(value);
993 
994         // StoreInst and BoundsCheckInst have 3 inputs, LoadInst - has 2 inputs
995         newInst->SetInput(0, inst->GetInput(0).GetInst());
996         if (inst->GetInputsCount() == 3U) {
997             newInst->SetInput(1, inst->GetInput(2U).GetInst());
998         } else {
999             ASSERT(inst->GetInputsCount() == 2U);
1000         }
1001         if (inst->GetOpcode() == Opcode::StoreArray) {
1002             newInst->CastToStoreArrayI()->SetNeedBarrier(inst->CastToStoreArray()->GetNeedBarrier());
1003         }
1004 
1005         if (inst->GetOpcode() == Opcode::LoadArray) {
1006             newInst->CastToLoadArrayI()->SetNeedBarrier(inst->CastToLoadArray()->GetNeedBarrier());
1007             newInst->CastToLoadArrayI()->SetIsArray(inst->CastToLoadArray()->IsArray());
1008         }
1009         if (inst->GetOpcode() == Opcode::BoundsCheck) {
1010             newInst->CastToBoundsCheckI()->SetIsArray(inst->CastToBoundsCheck()->IsArray());
1011             if (inst->CanDeoptimize()) {
1012                 newInst->SetFlag(inst_flags::CAN_DEOPTIMIZE);
1013             }
1014         }
1015 
1016         // Replace instruction immediately because it's not removable by DCE
1017         if (inst->GetOpcode() != Opcode::BoundsCheck) {
1018             inst->ReplaceUsers(newInst);
1019         } else {
1020             auto cnst = graph->FindOrCreateConstant(value);
1021             inst->ReplaceUsers(cnst);
1022         }
1023         inst->RemoveInputs();
1024         inst->GetBasicBlock()->ReplaceInst(inst, newInst);
1025         graph->GetEventWriter().EventLowering(GetOpcodeString(inst->GetOpcode()), inst->GetId(), inst->GetPc());
1026         COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode());
1027     }
1028 }
1029 
LowerStateInst(SaveStateInst * saveState)1030 void Lowering::LowerStateInst(SaveStateInst *saveState)
1031 {
1032     size_t idx = 0;
1033     size_t inputsCount = saveState->GetInputsCount();
1034     auto graph = saveState->GetBasicBlock()->GetGraph();
1035     if (graph->IsBytecodeOptimizer()) {
1036         return;
1037     }
1038     bool skipFloats = (graph->GetArch() == Arch::AARCH32);
1039     while (idx < inputsCount) {
1040         auto inputInst = saveState->GetInput(idx).GetInst();
1041         // In Aarch32 floats values stores in different format then integer
1042         if (inputInst->GetOpcode() == Opcode::NullPtr ||
1043             (inputInst->IsConst() && (!skipFloats || inputInst->GetType() == DataType::INT64))) {
1044             uint64_t rawValue =
1045                 inputInst->GetOpcode() == Opcode::NullPtr ? 0 : inputInst->CastToConstant()->GetRawValue();
1046             auto vreg = saveState->GetVirtualRegister(idx);
1047             auto type = inputInst->GetType();
1048             // There are no INT64 in dynamic
1049             if (type == DataType::INT64 && graph->IsDynamicMethod()) {
1050                 type = DataType::INT32;
1051             }
1052             saveState->AppendImmediate(rawValue, vreg.Value(), type, vreg.GetVRegType());
1053             saveState->RemoveInput(idx);
1054             inputsCount--;
1055             graph->GetEventWriter().EventLowering(GetOpcodeString(saveState->GetOpcode()), saveState->GetId(),
1056                                                   saveState->GetPc());
1057             COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(saveState->GetOpcode());
1058         } else {
1059             idx++;
1060         }
1061     }
1062 }
1063 
LowerReturnInst(FixedInputsInst1 * ret)1064 void Lowering::LowerReturnInst(FixedInputsInst1 *ret)
1065 {
1066     auto graph = ret->GetBasicBlock()->GetGraph();
1067     if (graph->IsBytecodeOptimizer()) {
1068         return;
1069     }
1070     ASSERT(ret->GetOpcode() == Opcode::Return);
1071     auto inputInst = ret->GetInput(0).GetInst();
1072     if (inputInst->IsConst()) {
1073         uint64_t rawValue = inputInst->CastToConstant()->GetRawValue();
1074         auto retImm = graph->CreateInstReturnI(ret->GetType(), ret->GetPc(), rawValue);
1075 #ifdef PANDA_COMPILER_DEBUG_INFO
1076         retImm->SetCurrentMethod(ret->GetCurrentMethod());
1077 #endif
1078 
1079         // Replace instruction immediately because it's not removable by DCE
1080         ret->RemoveInputs();
1081         ret->GetBasicBlock()->ReplaceInst(ret, retImm);
1082         graph->GetEventWriter().EventLowering(GetOpcodeString(ret->GetOpcode()), ret->GetId(), ret->GetPc());
1083         COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(ret->GetOpcode());
1084     }
1085 }
1086 
1087 // We'd like to swap only to make second operand immediate
BetterToSwapCompareInputs(Inst * cmp)1088 bool Lowering::BetterToSwapCompareInputs(Inst *cmp)
1089 {
1090     ASSERT(cmp->GetOpcode() == Opcode::Compare);
1091     auto in0 = cmp->GetInput(0).GetInst();
1092     auto in1 = cmp->GetInput(1).GetInst();
1093     if (DataType::IsFloatType(in0->GetType())) {
1094         return false;
1095     }
1096     if (in0->GetOpcode() == compiler::Opcode::NullPtr) {
1097         return true;
1098     }
1099 
1100     if (in0->IsConst()) {
1101         if (in1->IsConst()) {
1102             DataType::Type type = cmp->CastToCompare()->GetOperandsType();
1103             uint32_t size = (type == DataType::UINT64 || type == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
1104             auto cc = cmp->CastToCompare()->GetCc();
1105             return ConstantFitsCompareImm(in0, size, cc) && !ConstantFitsCompareImm(in1, size, cc);
1106         }
1107         return true;
1108     }
1109     return false;
1110 }
1111 
1112 // Optimize order of input arguments for decreasing using accumulator (Bytecodeoptimizer only).
OptimizeIfInput(compiler::Inst * ifInst)1113 void Lowering::OptimizeIfInput(compiler::Inst *ifInst)
1114 {
1115     ASSERT(ifInst->GetOpcode() == compiler::Opcode::If);
1116     compiler::Inst *input0 = ifInst->GetInput(0).GetInst();
1117     compiler::Inst *input1 = ifInst->GetInput(1).GetInst();
1118 
1119     if (input0->IsDominate(input1)) {
1120         ifInst->SetInput(0, input1);
1121         ifInst->SetInput(1, input0);
1122         // And change CC
1123         auto cc = ifInst->CastToIf()->GetCc();
1124         cc = SwapOperandsConditionCode(cc);
1125         ifInst->CastToIf()->SetCc(cc);
1126     }
1127 }
1128 
JoinFcmpInst(IfImmInst * inst,CmpInst * input)1129 void Lowering::JoinFcmpInst(IfImmInst *inst, CmpInst *input)
1130 {
1131     auto cc = inst->GetCc();
1132     ASSERT(cc == ConditionCode::CC_EQ || cc == ConditionCode::CC_NE || IsSignedConditionCode(cc));
1133     if (input->IsFcmpg()) {
1134         /* Please look at the table of vector condition flags:
1135          * LT => Less than, or unordered
1136          * LE => Less than or equal, or unordered
1137          * GT => Greater than
1138          * GE => Greater than or equal
1139          *
1140          * LO => Less than
1141          * LS => Less than or equal
1142          * HI => Greater than, or unordered
1143          * HS => Greater than or equal, or unordered
1144          *
1145          * So we change condition to "unsigned" for Fcmpg (which should return "greater than" for unordered
1146          * comparisons).
1147          */
1148         cc = InverseSignednessConditionCode(cc);
1149     }
1150 
1151     // New instruction
1152     auto graph = input->GetBasicBlock()->GetGraph();
1153     auto replace = graph->CreateInstIf(DataType::NO_TYPE, inst->GetPc(), input->GetInput(0).GetInst(),
1154                                        input->GetInput(1).GetInst(), input->GetOperandsType(), cc, inst->GetMethod());
1155 #ifdef PANDA_COMPILER_DEBUG_INFO
1156     replace->SetCurrentMethod(inst->GetCurrentMethod());
1157 #endif
1158 
1159     // Replace IfImm instruction immediately because it's not removable by DCE
1160     inst->RemoveInputs();
1161     inst->GetBasicBlock()->ReplaceInst(inst, replace);
1162     graph->GetEventWriter().EventLowering(GetOpcodeString(inst->GetOpcode()), inst->GetId(), inst->GetPc());
1163     if (graph->IsBytecodeOptimizer()) {
1164         OptimizeIfInput(replace);
1165     }
1166     COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode());
1167 }
1168 
LowerIf(IfImmInst * inst)1169 void Lowering::LowerIf(IfImmInst *inst)
1170 {
1171     ASSERT(inst->GetCc() == ConditionCode::CC_NE || inst->GetCc() == ConditionCode::CC_EQ);
1172     ASSERT(inst->GetImm() == 0);
1173     if (inst->GetOperandsType() != DataType::BOOL) {
1174         ASSERT(!GetGraph()->SupportManagedCode() || GetGraph()->IsDynamicMethod());
1175         return;
1176     }
1177     auto input = inst->GetInput(0).GetInst();
1178     if (input->GetOpcode() != Opcode::Compare && input->GetOpcode() != Opcode::And) {
1179         return;
1180     }
1181     // Check, that inst have only IfImm user
1182     for (auto &user : input->GetUsers()) {
1183         if (user.GetInst()->GetOpcode() != Opcode::IfImm) {
1184             return;
1185         }
1186     }
1187     // Try put constant in second input
1188     if (input->GetOpcode() == Opcode::Compare && BetterToSwapCompareInputs(input)) {
1189         // Swap inputs
1190         auto in0 = input->GetInput(0).GetInst();
1191         auto in1 = input->GetInput(1).GetInst();
1192         input->SetInput(0, in1);
1193         input->SetInput(1, in0);
1194         // And change CC
1195         auto cc = input->CastToCompare()->GetCc();
1196         cc = SwapOperandsConditionCode(cc);
1197         input->CastToCompare()->SetCc(cc);
1198     }
1199     if (!GetGraph()->IsBytecodeOptimizer()) {
1200         for (auto &newInput : input->GetInputs()) {
1201             auto realNewInput = input->GetDataFlowInput(newInput.GetInst());
1202             if (realNewInput->IsMovableObject()) {
1203                 ssb_.SearchAndCreateMissingObjInSaveState(GetGraph(), realNewInput, inst);
1204             }
1205         }
1206     }
1207     auto cst = input->GetInput(1).GetInst();
1208     DataType::Type type =
1209         (input->GetOpcode() == Opcode::Compare) ? input->CastToCompare()->GetOperandsType() : input->GetType();
1210     uint32_t size = (type == DataType::UINT64 || type == DataType::INT64) ? WORD_SIZE : HALF_SIZE;
1211     auto cc = input->GetOpcode() == Opcode::Compare ? input->CastToCompare()->GetCc() : ConditionCode::CC_TST_NE;
1212     // IfImm can be inverted
1213     if (inst->GetCc() == ConditionCode::CC_EQ && inst->GetImm() == 0) {
1214         cc = GetInverseConditionCode(cc);
1215     }
1216     if (cst->GetOpcode() == compiler::Opcode::NullPtr || (cst->IsConst() && ConstantFitsCompareImm(cst, size, cc))) {
1217         // In-place change for IfImm
1218         InPlaceLowerIfImm(inst, input, cst, cc, type);
1219     } else {
1220         LowerIfImmToIf(inst, input, cc, type);
1221     }
1222 }
1223 
InPlaceLowerIfImm(IfImmInst * inst,Inst * input,Inst * cst,ConditionCode cc,DataType::Type inputType)1224 void Lowering::InPlaceLowerIfImm(IfImmInst *inst, Inst *input, Inst *cst, ConditionCode cc, DataType::Type inputType)
1225 {
1226     auto graph = inst->GetBasicBlock()->GetGraph();
1227     inst->SetOperandsType(inputType);
1228     auto newInput = input->GetInput(0).GetInst();
1229     // For compare(nullptr, 0) set `nullptr` as new input
1230     if (cst->GetOpcode() == Opcode::NullPtr && IsZeroConstant(newInput) &&
1231         DataType::IsReference(inst->GetOperandsType())) {
1232         newInput = cst;
1233     }
1234     inst->SetInput(0, newInput);
1235 
1236     uint64_t val = cst->GetOpcode() == Opcode::NullPtr ? 0 : cst->CastToConstant()->GetRawValue();
1237     inst->SetImm(val);
1238     inst->SetCc(cc);
1239     inst->GetBasicBlock()->GetGraph()->GetEventWriter().EventLowering(GetOpcodeString(inst->GetOpcode()), inst->GetId(),
1240                                                                       inst->GetPc());
1241     COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode());
1242 
1243     if (inst->GetImm() == 0 && newInput->GetOpcode() == Opcode::Cmp &&
1244         DataType::IsFloatType(newInput->CastToCmp()->GetOperandsType()) && !graph->IsBytecodeOptimizer()) {
1245         // Check inst and input are the only users of new_input
1246         bool join {true};
1247         for (auto &user : newInput->GetUsers()) {
1248             if (auto userInst = user.GetInst(); userInst != inst && userInst != input) {
1249                 join = false;
1250                 break;
1251             }
1252         }
1253         if (join) {
1254             JoinFcmpInst(inst, newInput->CastToCmp());
1255         }
1256     }
1257 }
1258 
LowerIfImmToIf(IfImmInst * inst,Inst * input,ConditionCode cc,DataType::Type inputType)1259 void Lowering::LowerIfImmToIf(IfImmInst *inst, Inst *input, ConditionCode cc, DataType::Type inputType)
1260 {
1261     auto graph = inst->GetBasicBlock()->GetGraph();
1262     // New instruction
1263     auto replace = graph->CreateInstIf(DataType::NO_TYPE, inst->GetPc(), input->GetInput(0).GetInst(),
1264                                        input->GetInput(1).GetInst(), inputType, cc, inst->GetMethod());
1265 #ifdef PANDA_COMPILER_DEBUG_INFO
1266     replace->SetCurrentMethod(inst->GetCurrentMethod());
1267 #endif
1268     // Replace IfImm instruction immediately because it's not removable by DCE
1269     inst->RemoveInputs();
1270     inst->GetBasicBlock()->ReplaceInst(inst, replace);
1271     graph->GetEventWriter().EventLowering(GetOpcodeString(inst->GetOpcode()), inst->GetId(), inst->GetPc());
1272     if (graph->IsBytecodeOptimizer()) {
1273         OptimizeIfInput(replace);
1274     }
1275     COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode());
1276 }
1277 
LowerToDeoptimizeCompare(Inst * inst)1278 void Lowering::LowerToDeoptimizeCompare(Inst *inst)
1279 {
1280     ASSERT(inst->GetOpcode() == Opcode::DeoptimizeIf);
1281     auto graph = inst->GetBasicBlock()->GetGraph();
1282     ASSERT(!graph->IsBytecodeOptimizer());
1283 
1284     auto deoptIf = inst->CastToDeoptimizeIf();
1285     if (deoptIf->GetInput(0).GetInst()->GetOpcode() != Opcode::Compare) {
1286         return;
1287     }
1288     auto compare = deoptIf->GetInput(0).GetInst()->CastToCompare();
1289     if (!compare->HasSingleUser()) {
1290         return;
1291     }
1292     COMPILER_LOG(DEBUG, LOWERING) << __func__ << "\n" << *compare << "\n" << *deoptIf;
1293     auto cmpInp1 = compare->GetInput(1).GetInst();
1294     DataType::Type type = compare->GetOperandsType();
1295     uint32_t size =
1296         (type == DataType::UINT64 || type == DataType::INT64 || type == DataType::ANY) ? WORD_SIZE : HALF_SIZE;
1297     Inst *deoptCmp = nullptr;
1298     if ((cmpInp1->IsConst() && ConstantFitsCompareImm(cmpInp1, size, compare->GetCc())) || cmpInp1->IsNullPtr()) {
1299         uint64_t imm = cmpInp1->IsNullPtr() ? 0 : cmpInp1->CastToConstant()->GetRawValue();
1300         deoptCmp = graph->CreateInstDeoptimizeCompareImm(deoptIf, compare, imm);
1301     } else {
1302         deoptCmp = graph->CreateInstDeoptimizeCompare(deoptIf, compare);
1303         deoptCmp->SetInput(1, compare->GetInput(1).GetInst());
1304     }
1305     deoptCmp->SetInput(0, compare->GetInput(0).GetInst());
1306     deoptCmp->SetSaveState(deoptIf->GetSaveState());
1307 #ifdef PANDA_COMPILER_DEBUG_INFO
1308     deoptCmp->SetCurrentMethod(inst->GetCurrentMethod());
1309 #endif
1310     deoptIf->ReplaceUsers(deoptCmp);
1311     deoptIf->GetBasicBlock()->InsertAfter(deoptCmp, deoptIf);
1312     deoptIf->ClearFlag(compiler::inst_flags::NO_DCE);
1313     graph->GetEventWriter().EventLowering(GetOpcodeString(deoptIf->GetOpcode()), deoptIf->GetId(), deoptIf->GetPc());
1314     COMPILER_LOG(DEBUG, LOWERING) << "===>\n" << *deoptCmp;
1315 }
1316 
InvalidateAnalyses()1317 void Lowering::InvalidateAnalyses()
1318 {
1319     GetGraph()->InvalidateAnalysis<BoundsAnalysis>();
1320     GetGraph()->InvalidateAnalysis<AliasAnalysis>();
1321 }
1322 
RunImpl()1323 bool Lowering::RunImpl()
1324 {
1325     VisitGraph();
1326     return true;
1327 }
1328 }  // namespace panda::compiler
1329