diff options
Diffstat (limited to 'llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.cpp')
-rw-r--r-- | llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.cpp | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.cpp b/llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.cpp new file mode 100644 index 000000000000..08d1c987dc3b --- /dev/null +++ b/llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.cpp @@ -0,0 +1,180 @@ +//===- 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; + } +} |