1 | //===- ARMLegalizerInfo.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 |
9 | /// This file implements the targeting of the Machinelegalizer class for ARM. |
10 | /// \todo This should be generated by TableGen. |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "ARMLegalizerInfo.h" |
14 | #include "ARMCallLowering.h" |
15 | #include "ARMSubtarget.h" |
16 | #include "llvm/CodeGen/GlobalISel/LegalizerHelper.h" |
17 | #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" |
18 | #include "llvm/CodeGen/LowLevelTypeUtils.h" |
19 | #include "llvm/CodeGen/MachineRegisterInfo.h" |
20 | #include "llvm/CodeGen/TargetOpcodes.h" |
21 | #include "llvm/CodeGen/ValueTypes.h" |
22 | #include "llvm/IR/DerivedTypes.h" |
23 | #include "llvm/IR/Type.h" |
24 | |
25 | using namespace llvm; |
26 | using namespace LegalizeActions; |
27 | |
28 | static bool AEABI(const ARMSubtarget &ST) { |
29 | return ST.isTargetAEABI() || ST.isTargetGNUAEABI() || ST.isTargetMuslAEABI(); |
30 | } |
31 | |
32 | ARMLegalizerInfo::ARMLegalizerInfo(const ARMSubtarget &ST) { |
33 | using namespace TargetOpcode; |
34 | |
35 | const LLT p0 = LLT::pointer(AddressSpace: 0, SizeInBits: 32); |
36 | |
37 | const LLT s1 = LLT::scalar(SizeInBits: 1); |
38 | const LLT s8 = LLT::scalar(SizeInBits: 8); |
39 | const LLT s16 = LLT::scalar(SizeInBits: 16); |
40 | const LLT s32 = LLT::scalar(SizeInBits: 32); |
41 | const LLT s64 = LLT::scalar(SizeInBits: 64); |
42 | |
43 | auto &LegacyInfo = getLegacyLegalizerInfo(); |
44 | if (ST.isThumb1Only()) { |
45 | // Thumb1 is not supported yet. |
46 | LegacyInfo.computeTables(); |
47 | verify(MII: *ST.getInstrInfo()); |
48 | return; |
49 | } |
50 | |
51 | getActionDefinitionsBuilder(Opcodes: {G_SEXT, G_ZEXT, G_ANYEXT}) |
52 | .legalForCartesianProduct(Types0: {s8, s16, s32}, Types1: {s1, s8, s16}); |
53 | |
54 | getActionDefinitionsBuilder(Opcode: G_SEXT_INREG).lower(); |
55 | |
56 | getActionDefinitionsBuilder(Opcodes: {G_MUL, G_AND, G_OR, G_XOR}) |
57 | .legalFor(Types: {s32}) |
58 | .clampScalar(TypeIdx: 0, MinTy: s32, MaxTy: s32); |
59 | |
60 | if (ST.hasNEON()) |
61 | getActionDefinitionsBuilder(Opcodes: {G_ADD, G_SUB}) |
62 | .legalFor(Types: {s32, s64}) |
63 | .minScalar(TypeIdx: 0, Ty: s32); |
64 | else |
65 | getActionDefinitionsBuilder(Opcodes: {G_ADD, G_SUB}) |
66 | .legalFor(Types: {s32}) |
67 | .minScalar(TypeIdx: 0, Ty: s32); |
68 | |
69 | getActionDefinitionsBuilder(Opcodes: {G_ASHR, G_LSHR, G_SHL}) |
70 | .legalFor(Types: {{s32, s32}}) |
71 | .minScalar(TypeIdx: 0, Ty: s32) |
72 | .clampScalar(TypeIdx: 1, MinTy: s32, MaxTy: s32); |
73 | |
74 | bool HasHWDivide = (!ST.isThumb() && ST.hasDivideInARMMode()) || |
75 | (ST.isThumb() && ST.hasDivideInThumbMode()); |
76 | if (HasHWDivide) |
77 | getActionDefinitionsBuilder(Opcodes: {G_SDIV, G_UDIV}) |
78 | .legalFor(Types: {s32}) |
79 | .clampScalar(TypeIdx: 0, MinTy: s32, MaxTy: s32); |
80 | else |
81 | getActionDefinitionsBuilder(Opcodes: {G_SDIV, G_UDIV}) |
82 | .libcallFor(Types: {s32}) |
83 | .clampScalar(TypeIdx: 0, MinTy: s32, MaxTy: s32); |
84 | |
85 | auto &REMBuilder = |
86 | getActionDefinitionsBuilder(Opcodes: {G_SREM, G_UREM}).minScalar(TypeIdx: 0, Ty: s32); |
87 | if (HasHWDivide) |
88 | REMBuilder.lowerFor(Types: {s32}); |
89 | else if (AEABI(ST)) |
90 | REMBuilder.customFor(Types: {s32}); |
91 | else |
92 | REMBuilder.libcallFor(Types: {s32}); |
93 | |
94 | getActionDefinitionsBuilder(Opcode: G_INTTOPTR) |
95 | .legalFor(Types: {{p0, s32}}) |
96 | .minScalar(TypeIdx: 1, Ty: s32); |
97 | getActionDefinitionsBuilder(Opcode: G_PTRTOINT) |
98 | .legalFor(Types: {{s32, p0}}) |
99 | .minScalar(TypeIdx: 0, Ty: s32); |
100 | |
101 | getActionDefinitionsBuilder(Opcode: G_CONSTANT) |
102 | .legalFor(Types: {s32, p0}) |
103 | .clampScalar(TypeIdx: 0, MinTy: s32, MaxTy: s32); |
104 | |
105 | getActionDefinitionsBuilder(Opcode: G_ICMP) |
106 | .legalForCartesianProduct(Types0: {s1}, Types1: {s32, p0}) |
107 | .minScalar(TypeIdx: 1, Ty: s32); |
108 | |
109 | getActionDefinitionsBuilder(Opcode: G_SELECT) |
110 | .legalForCartesianProduct(Types0: {s32, p0}, Types1: {s1}) |
111 | .minScalar(TypeIdx: 0, Ty: s32); |
112 | |
113 | // We're keeping these builders around because we'll want to add support for |
114 | // floating point to them. |
115 | auto &LoadStoreBuilder = getActionDefinitionsBuilder(Opcodes: {G_LOAD, G_STORE}) |
116 | .legalForTypesWithMemDesc(TypesAndMemDesc: {{.Type0: s8, .Type1: p0, .MemTy: s8, .Align: 8}, |
117 | {.Type0: s16, .Type1: p0, .MemTy: s16, .Align: 8}, |
118 | {.Type0: s32, .Type1: p0, .MemTy: s32, .Align: 8}, |
119 | {.Type0: p0, .Type1: p0, .MemTy: p0, .Align: 8}}) |
120 | .unsupportedIfMemSizeNotPow2(); |
121 | |
122 | getActionDefinitionsBuilder(Opcode: G_FRAME_INDEX).legalFor(Types: {p0}); |
123 | getActionDefinitionsBuilder(Opcode: G_GLOBAL_VALUE).legalFor(Types: {p0}); |
124 | |
125 | auto &PhiBuilder = |
126 | getActionDefinitionsBuilder(Opcode: G_PHI) |
127 | .legalFor(Types: {s32, p0}) |
128 | .minScalar(TypeIdx: 0, Ty: s32); |
129 | |
130 | getActionDefinitionsBuilder(Opcode: G_PTR_ADD) |
131 | .legalFor(Types: {{p0, s32}}) |
132 | .minScalar(TypeIdx: 1, Ty: s32); |
133 | |
134 | getActionDefinitionsBuilder(Opcode: G_BRCOND).legalFor(Types: {s1}); |
135 | |
136 | if (!ST.useSoftFloat() && ST.hasVFP2Base()) { |
137 | getActionDefinitionsBuilder( |
138 | Opcodes: {G_FADD, G_FSUB, G_FMUL, G_FDIV, G_FCONSTANT, G_FNEG}) |
139 | .legalFor(Types: {s32, s64}); |
140 | |
141 | LoadStoreBuilder |
142 | .legalForTypesWithMemDesc(TypesAndMemDesc: {{.Type0: s64, .Type1: p0, .MemTy: s64, .Align: 32}}) |
143 | .maxScalar(TypeIdx: 0, Ty: s32); |
144 | PhiBuilder.legalFor(Types: {s64}); |
145 | |
146 | getActionDefinitionsBuilder(Opcode: G_FCMP).legalForCartesianProduct(Types0: {s1}, |
147 | Types1: {s32, s64}); |
148 | |
149 | getActionDefinitionsBuilder(Opcode: G_MERGE_VALUES).legalFor(Types: {{s64, s32}}); |
150 | getActionDefinitionsBuilder(Opcode: G_UNMERGE_VALUES).legalFor(Types: {{s32, s64}}); |
151 | |
152 | getActionDefinitionsBuilder(Opcode: G_FPEXT).legalFor(Types: {{s64, s32}}); |
153 | getActionDefinitionsBuilder(Opcode: G_FPTRUNC).legalFor(Types: {{s32, s64}}); |
154 | |
155 | getActionDefinitionsBuilder(Opcodes: {G_FPTOSI, G_FPTOUI}) |
156 | .legalForCartesianProduct(Types0: {s32}, Types1: {s32, s64}); |
157 | getActionDefinitionsBuilder(Opcodes: {G_SITOFP, G_UITOFP}) |
158 | .legalForCartesianProduct(Types0: {s32, s64}, Types1: {s32}); |
159 | |
160 | getActionDefinitionsBuilder(Opcodes: {G_GET_FPENV, G_SET_FPENV, G_GET_FPMODE}) |
161 | .legalFor(Types: {s32}); |
162 | getActionDefinitionsBuilder(Opcode: G_RESET_FPENV).alwaysLegal(); |
163 | getActionDefinitionsBuilder(Opcode: G_SET_FPMODE).customFor(Types: {s32}); |
164 | } else { |
165 | getActionDefinitionsBuilder(Opcodes: {G_FADD, G_FSUB, G_FMUL, G_FDIV}) |
166 | .libcallFor(Types: {s32, s64}); |
167 | |
168 | LoadStoreBuilder.maxScalar(TypeIdx: 0, Ty: s32); |
169 | |
170 | getActionDefinitionsBuilder(Opcode: G_FNEG).lowerFor(Types: {s32, s64}); |
171 | |
172 | getActionDefinitionsBuilder(Opcode: G_FCONSTANT).customFor(Types: {s32, s64}); |
173 | |
174 | getActionDefinitionsBuilder(Opcode: G_FCMP).customForCartesianProduct(Types0: {s1}, |
175 | Types1: {s32, s64}); |
176 | |
177 | if (AEABI(ST)) |
178 | setFCmpLibcallsAEABI(); |
179 | else |
180 | setFCmpLibcallsGNU(); |
181 | |
182 | getActionDefinitionsBuilder(Opcode: G_FPEXT).libcallFor(Types: {{s64, s32}}); |
183 | getActionDefinitionsBuilder(Opcode: G_FPTRUNC).libcallFor(Types: {{s32, s64}}); |
184 | |
185 | getActionDefinitionsBuilder(Opcodes: {G_FPTOSI, G_FPTOUI}) |
186 | .libcallForCartesianProduct(Types0: {s32}, Types1: {s32, s64}); |
187 | getActionDefinitionsBuilder(Opcodes: {G_SITOFP, G_UITOFP}) |
188 | .libcallForCartesianProduct(Types0: {s32, s64}, Types1: {s32}); |
189 | |
190 | getActionDefinitionsBuilder(Opcodes: {G_GET_FPENV, G_SET_FPENV, G_RESET_FPENV}) |
191 | .libcall(); |
192 | getActionDefinitionsBuilder(Opcodes: {G_GET_FPMODE, G_SET_FPMODE, G_RESET_FPMODE}) |
193 | .libcall(); |
194 | } |
195 | |
196 | // Just expand whatever loads and stores are left. |
197 | LoadStoreBuilder.lower(); |
198 | |
199 | if (!ST.useSoftFloat() && ST.hasVFP4Base()) |
200 | getActionDefinitionsBuilder(Opcode: G_FMA).legalFor(Types: {s32, s64}); |
201 | else |
202 | getActionDefinitionsBuilder(Opcode: G_FMA).libcallFor(Types: {s32, s64}); |
203 | |
204 | getActionDefinitionsBuilder(Opcodes: {G_FREM, G_FPOW}).libcallFor(Types: {s32, s64}); |
205 | |
206 | if (ST.hasV5TOps()) { |
207 | getActionDefinitionsBuilder(Opcode: G_CTLZ) |
208 | .legalFor(Types: {s32, s32}) |
209 | .clampScalar(TypeIdx: 1, MinTy: s32, MaxTy: s32) |
210 | .clampScalar(TypeIdx: 0, MinTy: s32, MaxTy: s32); |
211 | getActionDefinitionsBuilder(Opcode: G_CTLZ_ZERO_UNDEF) |
212 | .lowerFor(Types: {s32, s32}) |
213 | .clampScalar(TypeIdx: 1, MinTy: s32, MaxTy: s32) |
214 | .clampScalar(TypeIdx: 0, MinTy: s32, MaxTy: s32); |
215 | } else { |
216 | getActionDefinitionsBuilder(Opcode: G_CTLZ_ZERO_UNDEF) |
217 | .libcallFor(Types: {s32, s32}) |
218 | .clampScalar(TypeIdx: 1, MinTy: s32, MaxTy: s32) |
219 | .clampScalar(TypeIdx: 0, MinTy: s32, MaxTy: s32); |
220 | getActionDefinitionsBuilder(Opcode: G_CTLZ) |
221 | .lowerFor(Types: {s32, s32}) |
222 | .clampScalar(TypeIdx: 1, MinTy: s32, MaxTy: s32) |
223 | .clampScalar(TypeIdx: 0, MinTy: s32, MaxTy: s32); |
224 | } |
225 | |
226 | LegacyInfo.computeTables(); |
227 | verify(MII: *ST.getInstrInfo()); |
228 | } |
229 | |
230 | void ARMLegalizerInfo::setFCmpLibcallsAEABI() { |
231 | // FCMP_TRUE and FCMP_FALSE don't need libcalls, they should be |
232 | // default-initialized. |
233 | FCmp32Libcalls.resize(s: CmpInst::LAST_FCMP_PREDICATE + 1); |
234 | FCmp32Libcalls[CmpInst::FCMP_OEQ] = { |
235 | {.LibcallID: RTLIB::OEQ_F32, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
236 | FCmp32Libcalls[CmpInst::FCMP_OGE] = { |
237 | {.LibcallID: RTLIB::OGE_F32, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
238 | FCmp32Libcalls[CmpInst::FCMP_OGT] = { |
239 | {.LibcallID: RTLIB::OGT_F32, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
240 | FCmp32Libcalls[CmpInst::FCMP_OLE] = { |
241 | {.LibcallID: RTLIB::OLE_F32, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
242 | FCmp32Libcalls[CmpInst::FCMP_OLT] = { |
243 | {.LibcallID: RTLIB::OLT_F32, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
244 | FCmp32Libcalls[CmpInst::FCMP_ORD] = {{.LibcallID: RTLIB::UO_F32, .Predicate: CmpInst::ICMP_EQ}}; |
245 | FCmp32Libcalls[CmpInst::FCMP_UGE] = {{.LibcallID: RTLIB::OLT_F32, .Predicate: CmpInst::ICMP_EQ}}; |
246 | FCmp32Libcalls[CmpInst::FCMP_UGT] = {{.LibcallID: RTLIB::OLE_F32, .Predicate: CmpInst::ICMP_EQ}}; |
247 | FCmp32Libcalls[CmpInst::FCMP_ULE] = {{.LibcallID: RTLIB::OGT_F32, .Predicate: CmpInst::ICMP_EQ}}; |
248 | FCmp32Libcalls[CmpInst::FCMP_ULT] = {{.LibcallID: RTLIB::OGE_F32, .Predicate: CmpInst::ICMP_EQ}}; |
249 | FCmp32Libcalls[CmpInst::FCMP_UNE] = {{.LibcallID: RTLIB::UNE_F32, .Predicate: CmpInst::ICMP_EQ}}; |
250 | FCmp32Libcalls[CmpInst::FCMP_UNO] = { |
251 | {.LibcallID: RTLIB::UO_F32, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
252 | FCmp32Libcalls[CmpInst::FCMP_ONE] = { |
253 | {.LibcallID: RTLIB::OGT_F32, .Predicate: CmpInst::BAD_ICMP_PREDICATE}, |
254 | {.LibcallID: RTLIB::OLT_F32, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
255 | FCmp32Libcalls[CmpInst::FCMP_UEQ] = { |
256 | {.LibcallID: RTLIB::OEQ_F32, .Predicate: CmpInst::BAD_ICMP_PREDICATE}, |
257 | {.LibcallID: RTLIB::UO_F32, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
258 | |
259 | FCmp64Libcalls.resize(s: CmpInst::LAST_FCMP_PREDICATE + 1); |
260 | FCmp64Libcalls[CmpInst::FCMP_OEQ] = { |
261 | {.LibcallID: RTLIB::OEQ_F64, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
262 | FCmp64Libcalls[CmpInst::FCMP_OGE] = { |
263 | {.LibcallID: RTLIB::OGE_F64, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
264 | FCmp64Libcalls[CmpInst::FCMP_OGT] = { |
265 | {.LibcallID: RTLIB::OGT_F64, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
266 | FCmp64Libcalls[CmpInst::FCMP_OLE] = { |
267 | {.LibcallID: RTLIB::OLE_F64, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
268 | FCmp64Libcalls[CmpInst::FCMP_OLT] = { |
269 | {.LibcallID: RTLIB::OLT_F64, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
270 | FCmp64Libcalls[CmpInst::FCMP_ORD] = {{.LibcallID: RTLIB::UO_F64, .Predicate: CmpInst::ICMP_EQ}}; |
271 | FCmp64Libcalls[CmpInst::FCMP_UGE] = {{.LibcallID: RTLIB::OLT_F64, .Predicate: CmpInst::ICMP_EQ}}; |
272 | FCmp64Libcalls[CmpInst::FCMP_UGT] = {{.LibcallID: RTLIB::OLE_F64, .Predicate: CmpInst::ICMP_EQ}}; |
273 | FCmp64Libcalls[CmpInst::FCMP_ULE] = {{.LibcallID: RTLIB::OGT_F64, .Predicate: CmpInst::ICMP_EQ}}; |
274 | FCmp64Libcalls[CmpInst::FCMP_ULT] = {{.LibcallID: RTLIB::OGE_F64, .Predicate: CmpInst::ICMP_EQ}}; |
275 | FCmp64Libcalls[CmpInst::FCMP_UNE] = {{.LibcallID: RTLIB::UNE_F64, .Predicate: CmpInst::ICMP_EQ}}; |
276 | FCmp64Libcalls[CmpInst::FCMP_UNO] = { |
277 | {.LibcallID: RTLIB::UO_F64, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
278 | FCmp64Libcalls[CmpInst::FCMP_ONE] = { |
279 | {.LibcallID: RTLIB::OGT_F64, .Predicate: CmpInst::BAD_ICMP_PREDICATE}, |
280 | {.LibcallID: RTLIB::OLT_F64, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
281 | FCmp64Libcalls[CmpInst::FCMP_UEQ] = { |
282 | {.LibcallID: RTLIB::OEQ_F64, .Predicate: CmpInst::BAD_ICMP_PREDICATE}, |
283 | {.LibcallID: RTLIB::UO_F64, .Predicate: CmpInst::BAD_ICMP_PREDICATE}}; |
284 | } |
285 | |
286 | void ARMLegalizerInfo::setFCmpLibcallsGNU() { |
287 | // FCMP_TRUE and FCMP_FALSE don't need libcalls, they should be |
288 | // default-initialized. |
289 | FCmp32Libcalls.resize(s: CmpInst::LAST_FCMP_PREDICATE + 1); |
290 | FCmp32Libcalls[CmpInst::FCMP_OEQ] = {{.LibcallID: RTLIB::OEQ_F32, .Predicate: CmpInst::ICMP_EQ}}; |
291 | FCmp32Libcalls[CmpInst::FCMP_OGE] = {{.LibcallID: RTLIB::OGE_F32, .Predicate: CmpInst::ICMP_SGE}}; |
292 | FCmp32Libcalls[CmpInst::FCMP_OGT] = {{.LibcallID: RTLIB::OGT_F32, .Predicate: CmpInst::ICMP_SGT}}; |
293 | FCmp32Libcalls[CmpInst::FCMP_OLE] = {{.LibcallID: RTLIB::OLE_F32, .Predicate: CmpInst::ICMP_SLE}}; |
294 | FCmp32Libcalls[CmpInst::FCMP_OLT] = {{.LibcallID: RTLIB::OLT_F32, .Predicate: CmpInst::ICMP_SLT}}; |
295 | FCmp32Libcalls[CmpInst::FCMP_ORD] = {{.LibcallID: RTLIB::UO_F32, .Predicate: CmpInst::ICMP_EQ}}; |
296 | FCmp32Libcalls[CmpInst::FCMP_UGE] = {{.LibcallID: RTLIB::OLT_F32, .Predicate: CmpInst::ICMP_SGE}}; |
297 | FCmp32Libcalls[CmpInst::FCMP_UGT] = {{.LibcallID: RTLIB::OLE_F32, .Predicate: CmpInst::ICMP_SGT}}; |
298 | FCmp32Libcalls[CmpInst::FCMP_ULE] = {{.LibcallID: RTLIB::OGT_F32, .Predicate: CmpInst::ICMP_SLE}}; |
299 | FCmp32Libcalls[CmpInst::FCMP_ULT] = {{.LibcallID: RTLIB::OGE_F32, .Predicate: CmpInst::ICMP_SLT}}; |
300 | FCmp32Libcalls[CmpInst::FCMP_UNE] = {{.LibcallID: RTLIB::UNE_F32, .Predicate: CmpInst::ICMP_NE}}; |
301 | FCmp32Libcalls[CmpInst::FCMP_UNO] = {{.LibcallID: RTLIB::UO_F32, .Predicate: CmpInst::ICMP_NE}}; |
302 | FCmp32Libcalls[CmpInst::FCMP_ONE] = {{.LibcallID: RTLIB::OGT_F32, .Predicate: CmpInst::ICMP_SGT}, |
303 | {.LibcallID: RTLIB::OLT_F32, .Predicate: CmpInst::ICMP_SLT}}; |
304 | FCmp32Libcalls[CmpInst::FCMP_UEQ] = {{.LibcallID: RTLIB::OEQ_F32, .Predicate: CmpInst::ICMP_EQ}, |
305 | {.LibcallID: RTLIB::UO_F32, .Predicate: CmpInst::ICMP_NE}}; |
306 | |
307 | FCmp64Libcalls.resize(s: CmpInst::LAST_FCMP_PREDICATE + 1); |
308 | FCmp64Libcalls[CmpInst::FCMP_OEQ] = {{.LibcallID: RTLIB::OEQ_F64, .Predicate: CmpInst::ICMP_EQ}}; |
309 | FCmp64Libcalls[CmpInst::FCMP_OGE] = {{.LibcallID: RTLIB::OGE_F64, .Predicate: CmpInst::ICMP_SGE}}; |
310 | FCmp64Libcalls[CmpInst::FCMP_OGT] = {{.LibcallID: RTLIB::OGT_F64, .Predicate: CmpInst::ICMP_SGT}}; |
311 | FCmp64Libcalls[CmpInst::FCMP_OLE] = {{.LibcallID: RTLIB::OLE_F64, .Predicate: CmpInst::ICMP_SLE}}; |
312 | FCmp64Libcalls[CmpInst::FCMP_OLT] = {{.LibcallID: RTLIB::OLT_F64, .Predicate: CmpInst::ICMP_SLT}}; |
313 | FCmp64Libcalls[CmpInst::FCMP_ORD] = {{.LibcallID: RTLIB::UO_F64, .Predicate: CmpInst::ICMP_EQ}}; |
314 | FCmp64Libcalls[CmpInst::FCMP_UGE] = {{.LibcallID: RTLIB::OLT_F64, .Predicate: CmpInst::ICMP_SGE}}; |
315 | FCmp64Libcalls[CmpInst::FCMP_UGT] = {{.LibcallID: RTLIB::OLE_F64, .Predicate: CmpInst::ICMP_SGT}}; |
316 | FCmp64Libcalls[CmpInst::FCMP_ULE] = {{.LibcallID: RTLIB::OGT_F64, .Predicate: CmpInst::ICMP_SLE}}; |
317 | FCmp64Libcalls[CmpInst::FCMP_ULT] = {{.LibcallID: RTLIB::OGE_F64, .Predicate: CmpInst::ICMP_SLT}}; |
318 | FCmp64Libcalls[CmpInst::FCMP_UNE] = {{.LibcallID: RTLIB::UNE_F64, .Predicate: CmpInst::ICMP_NE}}; |
319 | FCmp64Libcalls[CmpInst::FCMP_UNO] = {{.LibcallID: RTLIB::UO_F64, .Predicate: CmpInst::ICMP_NE}}; |
320 | FCmp64Libcalls[CmpInst::FCMP_ONE] = {{.LibcallID: RTLIB::OGT_F64, .Predicate: CmpInst::ICMP_SGT}, |
321 | {.LibcallID: RTLIB::OLT_F64, .Predicate: CmpInst::ICMP_SLT}}; |
322 | FCmp64Libcalls[CmpInst::FCMP_UEQ] = {{.LibcallID: RTLIB::OEQ_F64, .Predicate: CmpInst::ICMP_EQ}, |
323 | {.LibcallID: RTLIB::UO_F64, .Predicate: CmpInst::ICMP_NE}}; |
324 | } |
325 | |
326 | ARMLegalizerInfo::FCmpLibcallsList |
327 | ARMLegalizerInfo::getFCmpLibcalls(CmpInst::Predicate Predicate, |
328 | unsigned Size) const { |
329 | assert(CmpInst::isFPPredicate(Predicate) && "Unsupported FCmp predicate" ); |
330 | if (Size == 32) |
331 | return FCmp32Libcalls[Predicate]; |
332 | if (Size == 64) |
333 | return FCmp64Libcalls[Predicate]; |
334 | llvm_unreachable("Unsupported size for FCmp predicate" ); |
335 | } |
336 | |
337 | bool ARMLegalizerInfo::legalizeCustom(LegalizerHelper &Helper, MachineInstr &MI, |
338 | LostDebugLocObserver &LocObserver) const { |
339 | using namespace TargetOpcode; |
340 | |
341 | MachineIRBuilder &MIRBuilder = Helper.MIRBuilder; |
342 | MachineRegisterInfo &MRI = *MIRBuilder.getMRI(); |
343 | LLVMContext &Ctx = MIRBuilder.getMF().getFunction().getContext(); |
344 | |
345 | switch (MI.getOpcode()) { |
346 | default: |
347 | return false; |
348 | case G_SREM: |
349 | case G_UREM: { |
350 | Register OriginalResult = MI.getOperand(i: 0).getReg(); |
351 | auto Size = MRI.getType(Reg: OriginalResult).getSizeInBits(); |
352 | if (Size != 32) |
353 | return false; |
354 | |
355 | auto Libcall = |
356 | MI.getOpcode() == G_SREM ? RTLIB::SDIVREM_I32 : RTLIB::UDIVREM_I32; |
357 | |
358 | // Our divmod libcalls return a struct containing the quotient and the |
359 | // remainder. Create a new, unused register for the quotient and use the |
360 | // destination of the original instruction for the remainder. |
361 | Type *ArgTy = Type::getInt32Ty(C&: Ctx); |
362 | StructType *RetTy = StructType::get(Context&: Ctx, Elements: {ArgTy, ArgTy}, /* Packed */ isPacked: true); |
363 | Register RetRegs[] = {MRI.createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: 32)), |
364 | OriginalResult}; |
365 | auto Status = createLibcall(MIRBuilder, Libcall, Result: {RetRegs, RetTy, 0}, |
366 | Args: {{MI.getOperand(i: 1).getReg(), ArgTy, 0}, |
367 | {MI.getOperand(i: 2).getReg(), ArgTy, 0}}, |
368 | LocObserver, MI: &MI); |
369 | if (Status != LegalizerHelper::Legalized) |
370 | return false; |
371 | break; |
372 | } |
373 | case G_FCMP: { |
374 | assert(MRI.getType(MI.getOperand(2).getReg()) == |
375 | MRI.getType(MI.getOperand(3).getReg()) && |
376 | "Mismatched operands for G_FCMP" ); |
377 | auto OpSize = MRI.getType(Reg: MI.getOperand(i: 2).getReg()).getSizeInBits(); |
378 | |
379 | auto OriginalResult = MI.getOperand(i: 0).getReg(); |
380 | auto Predicate = |
381 | static_cast<CmpInst::Predicate>(MI.getOperand(i: 1).getPredicate()); |
382 | auto Libcalls = getFCmpLibcalls(Predicate, Size: OpSize); |
383 | |
384 | if (Libcalls.empty()) { |
385 | assert((Predicate == CmpInst::FCMP_TRUE || |
386 | Predicate == CmpInst::FCMP_FALSE) && |
387 | "Predicate needs libcalls, but none specified" ); |
388 | MIRBuilder.buildConstant(Res: OriginalResult, |
389 | Val: Predicate == CmpInst::FCMP_TRUE ? 1 : 0); |
390 | MI.eraseFromParent(); |
391 | return true; |
392 | } |
393 | |
394 | assert((OpSize == 32 || OpSize == 64) && "Unsupported operand size" ); |
395 | auto *ArgTy = OpSize == 32 ? Type::getFloatTy(C&: Ctx) : Type::getDoubleTy(C&: Ctx); |
396 | auto *RetTy = Type::getInt32Ty(C&: Ctx); |
397 | |
398 | SmallVector<Register, 2> Results; |
399 | for (auto Libcall : Libcalls) { |
400 | auto LibcallResult = MRI.createGenericVirtualRegister(Ty: LLT::scalar(SizeInBits: 32)); |
401 | auto Status = createLibcall(MIRBuilder, Libcall: Libcall.LibcallID, |
402 | Result: {LibcallResult, RetTy, 0}, |
403 | Args: {{MI.getOperand(i: 2).getReg(), ArgTy, 0}, |
404 | {MI.getOperand(i: 3).getReg(), ArgTy, 0}}, |
405 | LocObserver, MI: &MI); |
406 | |
407 | if (Status != LegalizerHelper::Legalized) |
408 | return false; |
409 | |
410 | auto ProcessedResult = |
411 | Libcalls.size() == 1 |
412 | ? OriginalResult |
413 | : MRI.createGenericVirtualRegister(Ty: MRI.getType(Reg: OriginalResult)); |
414 | |
415 | // We have a result, but we need to transform it into a proper 1-bit 0 or |
416 | // 1, taking into account the different peculiarities of the values |
417 | // returned by the comparison functions. |
418 | CmpInst::Predicate ResultPred = Libcall.Predicate; |
419 | if (ResultPred == CmpInst::BAD_ICMP_PREDICATE) { |
420 | // We have a nice 0 or 1, and we just need to truncate it back to 1 bit |
421 | // to keep the types consistent. |
422 | MIRBuilder.buildTrunc(Res: ProcessedResult, Op: LibcallResult); |
423 | } else { |
424 | // We need to compare against 0. |
425 | assert(CmpInst::isIntPredicate(ResultPred) && "Unsupported predicate" ); |
426 | auto Zero = MIRBuilder.buildConstant(Res: LLT::scalar(SizeInBits: 32), Val: 0); |
427 | MIRBuilder.buildICmp(Pred: ResultPred, Res: ProcessedResult, Op0: LibcallResult, Op1: Zero); |
428 | } |
429 | Results.push_back(Elt: ProcessedResult); |
430 | } |
431 | |
432 | if (Results.size() != 1) { |
433 | assert(Results.size() == 2 && "Unexpected number of results" ); |
434 | MIRBuilder.buildOr(Dst: OriginalResult, Src0: Results[0], Src1: Results[1]); |
435 | } |
436 | break; |
437 | } |
438 | case G_FCONSTANT: { |
439 | // Convert to integer constants, while preserving the binary representation. |
440 | auto AsInteger = |
441 | MI.getOperand(i: 1).getFPImm()->getValueAPF().bitcastToAPInt(); |
442 | MIRBuilder.buildConstant(Res: MI.getOperand(i: 0), |
443 | Val: *ConstantInt::get(Context&: Ctx, V: AsInteger)); |
444 | break; |
445 | } |
446 | case G_SET_FPMODE: { |
447 | // New FPSCR = (FPSCR & FPStatusBits) | (Modes & ~FPStatusBits) |
448 | LLT FPEnvTy = LLT::scalar(SizeInBits: 32); |
449 | auto FPEnv = MRI.createGenericVirtualRegister(Ty: FPEnvTy); |
450 | Register Modes = MI.getOperand(i: 0).getReg(); |
451 | MIRBuilder.buildGetFPEnv(Dst: FPEnv); |
452 | auto StatusBitMask = MIRBuilder.buildConstant(Res: FPEnvTy, Val: ARM::FPStatusBits); |
453 | auto StatusBits = MIRBuilder.buildAnd(Dst: FPEnvTy, Src0: FPEnv, Src1: StatusBitMask); |
454 | auto NotStatusBitMask = |
455 | MIRBuilder.buildConstant(Res: FPEnvTy, Val: ~ARM::FPStatusBits); |
456 | auto FPModeBits = MIRBuilder.buildAnd(Dst: FPEnvTy, Src0: Modes, Src1: NotStatusBitMask); |
457 | auto NewFPSCR = MIRBuilder.buildOr(Dst: FPEnvTy, Src0: StatusBits, Src1: FPModeBits); |
458 | MIRBuilder.buildSetFPEnv(Src: NewFPSCR); |
459 | break; |
460 | } |
461 | } |
462 | |
463 | MI.eraseFromParent(); |
464 | return true; |
465 | } |
466 | |