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