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