| //===- 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; |
| |
| 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 None; |
| Register Src = MI.getOperand(1).getReg(); |
| if (auto ValAndVReg = |
| getConstantVRegValWithLookThrough(MI.getOperand(1).getReg(), MRI)) |
| return RegOrConstant(ValAndVReg->Value.getSExtValue()); |
| return RegOrConstant(Src); |
| } |
| |
| Optional<int64_t> |
| AArch64GISelUtils::getAArch64VectorSplatScalar(const MachineInstr &MI, |
| const MachineRegisterInfo &MRI) { |
| auto Splat = getAArch64VectorSplat(MI, MRI); |
| if (!Splat || Splat->isReg()) |
| return None; |
| 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 = |
| getConstantVRegValWithLookThrough(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 = getConstantVRegValWithLookThrough(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 = |
| getConstantVRegValWithLookThrough(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; |
| LLVM_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; |
| } |
| } |