123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- //===- AArch64GlobalISelUtils.cpp --------------------------------*- C++ -*-==//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- /// \file Implementations of AArch64-specific helper functions used in the
- /// GlobalISel pipeline.
- //===----------------------------------------------------------------------===//
- #include "AArch64GlobalISelUtils.h"
- #include "AArch64InstrInfo.h"
- #include "llvm/CodeGen/GlobalISel/Utils.h"
- #include "llvm/CodeGen/TargetLowering.h"
- #include "llvm/IR/InstrTypes.h"
- #include "llvm/Support/raw_ostream.h"
- using namespace llvm;
- std::optional<RegOrConstant>
- AArch64GISelUtils::getAArch64VectorSplat(const MachineInstr &MI,
- const MachineRegisterInfo &MRI) {
- if (auto Splat = getVectorSplat(MI, MRI))
- return Splat;
- if (MI.getOpcode() != AArch64::G_DUP)
- return std::nullopt;
- Register Src = MI.getOperand(1).getReg();
- if (auto ValAndVReg =
- getAnyConstantVRegValWithLookThrough(MI.getOperand(1).getReg(), MRI))
- return RegOrConstant(ValAndVReg->Value.getSExtValue());
- return RegOrConstant(Src);
- }
- std::optional<int64_t>
- AArch64GISelUtils::getAArch64VectorSplatScalar(const MachineInstr &MI,
- const MachineRegisterInfo &MRI) {
- auto Splat = getAArch64VectorSplat(MI, MRI);
- if (!Splat || Splat->isReg())
- return std::nullopt;
- return Splat->getCst();
- }
- bool AArch64GISelUtils::isCMN(const MachineInstr *MaybeSub,
- const CmpInst::Predicate &Pred,
- const MachineRegisterInfo &MRI) {
- // Match:
- //
- // %sub = G_SUB 0, %y
- // %cmp = G_ICMP eq/ne, %sub, %z
- //
- // Or
- //
- // %sub = G_SUB 0, %y
- // %cmp = G_ICMP eq/ne, %z, %sub
- if (!MaybeSub || MaybeSub->getOpcode() != TargetOpcode::G_SUB ||
- !CmpInst::isEquality(Pred))
- return false;
- auto MaybeZero =
- getIConstantVRegValWithLookThrough(MaybeSub->getOperand(1).getReg(), MRI);
- return MaybeZero && MaybeZero->Value.getZExtValue() == 0;
- }
- bool AArch64GISelUtils::tryEmitBZero(MachineInstr &MI,
- MachineIRBuilder &MIRBuilder,
- bool MinSize) {
- assert(MI.getOpcode() == TargetOpcode::G_MEMSET);
- MachineRegisterInfo &MRI = *MIRBuilder.getMRI();
- auto &TLI = *MIRBuilder.getMF().getSubtarget().getTargetLowering();
- if (!TLI.getLibcallName(RTLIB::BZERO))
- return false;
- auto Zero =
- getIConstantVRegValWithLookThrough(MI.getOperand(1).getReg(), MRI);
- if (!Zero || Zero->Value.getSExtValue() != 0)
- return false;
- // It's not faster to use bzero rather than memset for sizes <= 256.
- // However, it *does* save us a mov from wzr, so if we're going for
- // minsize, use bzero even if it's slower.
- if (!MinSize) {
- // If the size is known, check it. If it is not known, assume using bzero is
- // better.
- if (auto Size = getIConstantVRegValWithLookThrough(
- MI.getOperand(2).getReg(), MRI)) {
- if (Size->Value.getSExtValue() <= 256)
- return false;
- }
- }
- MIRBuilder.setInstrAndDebugLoc(MI);
- MIRBuilder
- .buildInstr(TargetOpcode::G_BZERO, {},
- {MI.getOperand(0), MI.getOperand(2)})
- .addImm(MI.getOperand(3).getImm())
- .addMemOperand(*MI.memoperands_begin());
- MI.eraseFromParent();
- return true;
- }
- void AArch64GISelUtils::changeFCMPPredToAArch64CC(
- const CmpInst::Predicate P, AArch64CC::CondCode &CondCode,
- AArch64CC::CondCode &CondCode2) {
- CondCode2 = AArch64CC::AL;
- switch (P) {
- default:
- llvm_unreachable("Unknown FP condition!");
- case CmpInst::FCMP_OEQ:
- CondCode = AArch64CC::EQ;
- break;
- case CmpInst::FCMP_OGT:
- CondCode = AArch64CC::GT;
- break;
- case CmpInst::FCMP_OGE:
- CondCode = AArch64CC::GE;
- break;
- case CmpInst::FCMP_OLT:
- CondCode = AArch64CC::MI;
- break;
- case CmpInst::FCMP_OLE:
- CondCode = AArch64CC::LS;
- break;
- case CmpInst::FCMP_ONE:
- CondCode = AArch64CC::MI;
- CondCode2 = AArch64CC::GT;
- break;
- case CmpInst::FCMP_ORD:
- CondCode = AArch64CC::VC;
- break;
- case CmpInst::FCMP_UNO:
- CondCode = AArch64CC::VS;
- break;
- case CmpInst::FCMP_UEQ:
- CondCode = AArch64CC::EQ;
- CondCode2 = AArch64CC::VS;
- break;
- case CmpInst::FCMP_UGT:
- CondCode = AArch64CC::HI;
- break;
- case CmpInst::FCMP_UGE:
- CondCode = AArch64CC::PL;
- break;
- case CmpInst::FCMP_ULT:
- CondCode = AArch64CC::LT;
- break;
- case CmpInst::FCMP_ULE:
- CondCode = AArch64CC::LE;
- break;
- case CmpInst::FCMP_UNE:
- CondCode = AArch64CC::NE;
- break;
- }
- }
- void AArch64GISelUtils::changeVectorFCMPPredToAArch64CC(
- const CmpInst::Predicate P, AArch64CC::CondCode &CondCode,
- AArch64CC::CondCode &CondCode2, bool &Invert) {
- Invert = false;
- switch (P) {
- default:
- // Mostly the scalar mappings work fine.
- changeFCMPPredToAArch64CC(P, CondCode, CondCode2);
- break;
- case CmpInst::FCMP_UNO:
- Invert = true;
- [[fallthrough]];
- case CmpInst::FCMP_ORD:
- CondCode = AArch64CC::MI;
- CondCode2 = AArch64CC::GE;
- break;
- case CmpInst::FCMP_UEQ:
- case CmpInst::FCMP_ULT:
- case CmpInst::FCMP_ULE:
- case CmpInst::FCMP_UGT:
- case CmpInst::FCMP_UGE:
- // All of the compare-mask comparisons are ordered, but we can switch
- // between the two by a double inversion. E.g. ULE == !OGT.
- Invert = true;
- changeFCMPPredToAArch64CC(CmpInst::getInversePredicate(P), CondCode,
- CondCode2);
- break;
- }
- }
|