1//===- AArch64GlobalISelUtils.cpp --------------------------------*- C++ -*-==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8/// \file Implementations of AArch64-specific helper functions used in the
9/// GlobalISel pipeline.
10//===----------------------------------------------------------------------===//
11#include "AArch64GlobalISelUtils.h"
12#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h"
13#include "llvm/CodeGen/GlobalISel/Utils.h"
14#include "llvm/CodeGen/TargetLowering.h"
15#include "llvm/IR/InstrTypes.h"
16
17using namespace llvm;
18using namespace MIPatternMatch;
19
20bool AArch64GISelUtils::isCMN(const MachineInstr *MaybeSub,
21 const CmpInst::Predicate &Pred,
22 const MachineRegisterInfo &MRI) {
23 // Match:
24 //
25 // %sub = G_SUB 0, %y
26 // %cmp = G_ICMP eq/ne, %sub, %z
27 //
28 // Or
29 //
30 // %sub = G_SUB 0, %y
31 // %cmp = G_ICMP eq/ne, %z, %sub
32 // or with signed comparisons with the no-signed-wrap flag set
33 if (!MaybeSub || MaybeSub->getOpcode() != TargetOpcode::G_SUB ||
34 (!CmpInst::isEquality(pred: Pred) &&
35 !(CmpInst::isSigned(Pred) && MaybeSub->getFlag(Flag: MachineInstr::NoSWrap))))
36 return false;
37 auto MaybeZero =
38 getIConstantVRegValWithLookThrough(VReg: MaybeSub->getOperand(i: 1).getReg(), MRI);
39 return MaybeZero && MaybeZero->Value.getZExtValue() == 0;
40}
41
42void AArch64GISelUtils::applyEmitBZero(MachineInstr &MI,
43 MachineIRBuilder &MIRBuilder) {
44 assert(MI.getOpcode() == TargetOpcode::G_MEMSET);
45
46 MIRBuilder.setInstrAndDebugLoc(MI);
47 MIRBuilder
48 .buildInstr(Opc: TargetOpcode::G_BZERO, DstOps: {},
49 SrcOps: {MI.getOperand(i: 0), MI.getOperand(i: 2)})
50 .addImm(Val: MI.getOperand(i: 3).getImm())
51 .addMemOperand(MMO: *MI.memoperands_begin());
52 MI.eraseFromParent();
53}
54
55bool AArch64GISelUtils::matchEmitBZero(const MachineInstr &MI,
56 const MachineRegisterInfo &MRI,
57 const LibcallLoweringInfo &Libcalls,
58 bool MinSize) {
59 assert(MI.getOpcode() == TargetOpcode::G_MEMSET);
60 if (Libcalls.getLibcallImpl(Call: RTLIB::BZERO) == RTLIB::Unsupported)
61 return false;
62
63 auto Zero =
64 getIConstantVRegValWithLookThrough(VReg: MI.getOperand(i: 1).getReg(), MRI);
65 if (!Zero || Zero->Value.getSExtValue() != 0)
66 return false;
67
68 // It's not faster to use bzero rather than memset for sizes <= 256.
69 // However, it *does* save us a mov from wzr, so if we're going for
70 // minsize, use bzero even if it's slower.
71 if (!MinSize) {
72 // If the size is known, check it. If it is not known, assume using bzero is
73 // better.
74 if (auto Size = getIConstantVRegValWithLookThrough(
75 VReg: MI.getOperand(i: 2).getReg(), MRI)) {
76 if (Size->Value.getSExtValue() <= 256)
77 return false;
78 }
79 }
80 return true;
81}
82
83std::tuple<uint16_t, Register>
84AArch64GISelUtils::extractPtrauthBlendDiscriminators(Register Disc,
85 MachineRegisterInfo &MRI) {
86 Register AddrDisc = Disc;
87 uint16_t ConstDisc = 0;
88
89 if (auto ConstDiscVal = getIConstantVRegVal(VReg: Disc, MRI)) {
90 if (isUInt<16>(x: ConstDiscVal->getZExtValue())) {
91 ConstDisc = ConstDiscVal->getZExtValue();
92 AddrDisc = Register();
93 }
94 return std::make_tuple(args&: ConstDisc, args&: AddrDisc);
95 }
96
97 Register BlendAddrDisc, BlendConstDisc;
98 if (!mi_match(R: Disc, MRI,
99 P: m_GIntrinsic<Intrinsic::ptrauth_blend>(Ops: m_Reg(R&: BlendAddrDisc),
100 Ops: m_Reg(R&: BlendConstDisc))))
101 return std::make_tuple(args&: ConstDisc, args&: AddrDisc);
102
103 if (auto ConstDiscVal = getIConstantVRegVal(VReg: BlendConstDisc, MRI)) {
104 if (isUInt<16>(x: ConstDiscVal->getZExtValue())) {
105 ConstDisc = ConstDiscVal->getZExtValue();
106 AddrDisc = BlendAddrDisc;
107 }
108 }
109 return std::make_tuple(args&: ConstDisc, args&: AddrDisc);
110}
111
112void AArch64GISelUtils::changeFCMPPredToAArch64CC(
113 const CmpInst::Predicate P, AArch64CC::CondCode &CondCode,
114 AArch64CC::CondCode &CondCode2) {
115 CondCode2 = AArch64CC::AL;
116 switch (P) {
117 default:
118 llvm_unreachable("Unknown FP condition!");
119 case CmpInst::FCMP_OEQ:
120 CondCode = AArch64CC::EQ;
121 break;
122 case CmpInst::FCMP_OGT:
123 CondCode = AArch64CC::GT;
124 break;
125 case CmpInst::FCMP_OGE:
126 CondCode = AArch64CC::GE;
127 break;
128 case CmpInst::FCMP_OLT:
129 CondCode = AArch64CC::MI;
130 break;
131 case CmpInst::FCMP_OLE:
132 CondCode = AArch64CC::LS;
133 break;
134 case CmpInst::FCMP_ONE:
135 CondCode = AArch64CC::MI;
136 CondCode2 = AArch64CC::GT;
137 break;
138 case CmpInst::FCMP_ORD:
139 CondCode = AArch64CC::VC;
140 break;
141 case CmpInst::FCMP_UNO:
142 CondCode = AArch64CC::VS;
143 break;
144 case CmpInst::FCMP_UEQ:
145 CondCode = AArch64CC::EQ;
146 CondCode2 = AArch64CC::VS;
147 break;
148 case CmpInst::FCMP_UGT:
149 CondCode = AArch64CC::HI;
150 break;
151 case CmpInst::FCMP_UGE:
152 CondCode = AArch64CC::PL;
153 break;
154 case CmpInst::FCMP_ULT:
155 CondCode = AArch64CC::LT;
156 break;
157 case CmpInst::FCMP_ULE:
158 CondCode = AArch64CC::LE;
159 break;
160 case CmpInst::FCMP_UNE:
161 CondCode = AArch64CC::NE;
162 break;
163 case CmpInst::FCMP_TRUE:
164 CondCode = AArch64CC::AL;
165 break;
166 case CmpInst::FCMP_FALSE:
167 CondCode = AArch64CC::NV;
168 break;
169 }
170}
171
172void AArch64GISelUtils::changeVectorFCMPPredToAArch64CC(
173 const CmpInst::Predicate P, AArch64CC::CondCode &CondCode,
174 AArch64CC::CondCode &CondCode2, bool &Invert) {
175 Invert = false;
176 switch (P) {
177 default:
178 // Mostly the scalar mappings work fine.
179 changeFCMPPredToAArch64CC(P, CondCode, CondCode2);
180 break;
181 case CmpInst::FCMP_UNO:
182 Invert = true;
183 [[fallthrough]];
184 case CmpInst::FCMP_ORD:
185 CondCode = AArch64CC::MI;
186 CondCode2 = AArch64CC::GE;
187 break;
188 case CmpInst::FCMP_UEQ:
189 case CmpInst::FCMP_ULT:
190 case CmpInst::FCMP_ULE:
191 case CmpInst::FCMP_UGT:
192 case CmpInst::FCMP_UGE:
193 // All of the compare-mask comparisons are ordered, but we can switch
194 // between the two by a double inversion. E.g. ULE == !OGT.
195 Invert = true;
196 changeFCMPPredToAArch64CC(P: CmpInst::getInversePredicate(pred: P), CondCode,
197 CondCode2);
198 break;
199 }
200}
201