1 | //===- X86LegalizerInfo.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 X86. |
10 | /// \todo This should be generated by TableGen. |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "X86LegalizerInfo.h" |
14 | #include "X86Subtarget.h" |
15 | #include "X86TargetMachine.h" |
16 | #include "llvm/CodeGen/GlobalISel/LegalizerHelper.h" |
17 | #include "llvm/CodeGen/TargetOpcodes.h" |
18 | #include "llvm/CodeGen/ValueTypes.h" |
19 | #include "llvm/IR/DerivedTypes.h" |
20 | #include "llvm/IR/Type.h" |
21 | |
22 | using namespace llvm; |
23 | using namespace TargetOpcode; |
24 | using namespace LegalizeActions; |
25 | using namespace LegalityPredicates; |
26 | |
27 | X86LegalizerInfo::X86LegalizerInfo(const X86Subtarget &STI, |
28 | const X86TargetMachine &TM) |
29 | : Subtarget(STI) { |
30 | |
31 | bool Is64Bit = Subtarget.is64Bit(); |
32 | bool HasCMOV = Subtarget.canUseCMOV(); |
33 | bool HasSSE1 = Subtarget.hasSSE1(); |
34 | bool HasSSE2 = Subtarget.hasSSE2(); |
35 | bool HasSSE41 = Subtarget.hasSSE41(); |
36 | bool HasAVX = Subtarget.hasAVX(); |
37 | bool HasAVX2 = Subtarget.hasAVX2(); |
38 | bool HasAVX512 = Subtarget.hasAVX512(); |
39 | bool HasVLX = Subtarget.hasVLX(); |
40 | bool HasDQI = Subtarget.hasAVX512() && Subtarget.hasDQI(); |
41 | bool HasBWI = Subtarget.hasAVX512() && Subtarget.hasBWI(); |
42 | bool UseX87 = !Subtarget.useSoftFloat() && Subtarget.hasX87(); |
43 | |
44 | const LLT p0 = LLT::pointer(AddressSpace: 0, SizeInBits: TM.getPointerSizeInBits(AS: 0)); |
45 | const LLT s1 = LLT::scalar(SizeInBits: 1); |
46 | const LLT s8 = LLT::scalar(SizeInBits: 8); |
47 | const LLT s16 = LLT::scalar(SizeInBits: 16); |
48 | const LLT s32 = LLT::scalar(SizeInBits: 32); |
49 | const LLT s64 = LLT::scalar(SizeInBits: 64); |
50 | const LLT s80 = LLT::scalar(SizeInBits: 80); |
51 | const LLT s128 = LLT::scalar(SizeInBits: 128); |
52 | const LLT sMaxScalar = Subtarget.is64Bit() ? s64 : s32; |
53 | const LLT v2s32 = LLT::fixed_vector(NumElements: 2, ScalarSizeInBits: 32); |
54 | const LLT v4s8 = LLT::fixed_vector(NumElements: 4, ScalarSizeInBits: 8); |
55 | |
56 | |
57 | const LLT v16s8 = LLT::fixed_vector(NumElements: 16, ScalarSizeInBits: 8); |
58 | const LLT v8s16 = LLT::fixed_vector(NumElements: 8, ScalarSizeInBits: 16); |
59 | const LLT v4s32 = LLT::fixed_vector(NumElements: 4, ScalarSizeInBits: 32); |
60 | const LLT v2s64 = LLT::fixed_vector(NumElements: 2, ScalarSizeInBits: 64); |
61 | const LLT v2p0 = LLT::fixed_vector(NumElements: 2, ScalarTy: p0); |
62 | |
63 | const LLT v32s8 = LLT::fixed_vector(NumElements: 32, ScalarSizeInBits: 8); |
64 | const LLT v16s16 = LLT::fixed_vector(NumElements: 16, ScalarSizeInBits: 16); |
65 | const LLT v8s32 = LLT::fixed_vector(NumElements: 8, ScalarSizeInBits: 32); |
66 | const LLT v4s64 = LLT::fixed_vector(NumElements: 4, ScalarSizeInBits: 64); |
67 | const LLT v4p0 = LLT::fixed_vector(NumElements: 4, ScalarTy: p0); |
68 | |
69 | const LLT v64s8 = LLT::fixed_vector(NumElements: 64, ScalarSizeInBits: 8); |
70 | const LLT v32s16 = LLT::fixed_vector(NumElements: 32, ScalarSizeInBits: 16); |
71 | const LLT v16s32 = LLT::fixed_vector(NumElements: 16, ScalarSizeInBits: 32); |
72 | const LLT v8s64 = LLT::fixed_vector(NumElements: 8, ScalarSizeInBits: 64); |
73 | |
74 | // todo: AVX512 bool vector predicate types |
75 | |
76 | // implicit/constants |
77 | getActionDefinitionsBuilder(Opcode: G_IMPLICIT_DEF) |
78 | .legalIf(Predicate: [=](const LegalityQuery &Query) -> bool { |
79 | // 32/64-bits needs support for s64/s128 to handle cases: |
80 | // s64 = EXTEND (G_IMPLICIT_DEF s32) -> s64 = G_IMPLICIT_DEF |
81 | // s128 = EXTEND (G_IMPLICIT_DEF s32/s64) -> s128 = G_IMPLICIT_DEF |
82 | return typeInSet(TypeIdx: 0, TypesInit: {p0, s1, s8, s16, s32, s64})(Query) || |
83 | (Is64Bit && typeInSet(TypeIdx: 0, TypesInit: {s128})(Query)); |
84 | }); |
85 | |
86 | getActionDefinitionsBuilder(Opcode: G_CONSTANT) |
87 | .legalIf(Predicate: [=](const LegalityQuery &Query) -> bool { |
88 | return typeInSet(TypeIdx: 0, TypesInit: {p0, s8, s16, s32})(Query) || |
89 | (Is64Bit && typeInSet(TypeIdx: 0, TypesInit: {s64})(Query)); |
90 | }) |
91 | .widenScalarToNextPow2(TypeIdx: 0, /*Min=*/MinSize: 8) |
92 | .clampScalar(TypeIdx: 0, MinTy: s8, MaxTy: sMaxScalar); |
93 | |
94 | // merge/unmerge |
95 | for (unsigned Op : {G_MERGE_VALUES, G_UNMERGE_VALUES}) { |
96 | unsigned BigTyIdx = Op == G_MERGE_VALUES ? 0 : 1; |
97 | unsigned LitTyIdx = Op == G_MERGE_VALUES ? 1 : 0; |
98 | getActionDefinitionsBuilder(Opcode: Op) |
99 | .widenScalarToNextPow2(TypeIdx: LitTyIdx, /*Min=*/MinSize: 8) |
100 | .widenScalarToNextPow2(TypeIdx: BigTyIdx, /*Min=*/MinSize: 16) |
101 | .minScalar(TypeIdx: LitTyIdx, Ty: s8) |
102 | .minScalar(TypeIdx: BigTyIdx, Ty: s32) |
103 | .legalIf(Predicate: [=](const LegalityQuery &Q) { |
104 | switch (Q.Types[BigTyIdx].getSizeInBits()) { |
105 | case 16: |
106 | case 32: |
107 | case 64: |
108 | case 128: |
109 | case 256: |
110 | case 512: |
111 | break; |
112 | default: |
113 | return false; |
114 | } |
115 | switch (Q.Types[LitTyIdx].getSizeInBits()) { |
116 | case 8: |
117 | case 16: |
118 | case 32: |
119 | case 64: |
120 | case 128: |
121 | case 256: |
122 | return true; |
123 | default: |
124 | return false; |
125 | } |
126 | }); |
127 | } |
128 | |
129 | // integer addition/subtraction |
130 | getActionDefinitionsBuilder(Opcodes: {G_ADD, G_SUB}) |
131 | .legalIf(Predicate: [=](const LegalityQuery &Query) -> bool { |
132 | if (typeInSet(TypeIdx: 0, TypesInit: {s8, s16, s32})(Query)) |
133 | return true; |
134 | if (Is64Bit && typeInSet(TypeIdx: 0, TypesInit: {s64})(Query)) |
135 | return true; |
136 | if (HasSSE2 && typeInSet(TypeIdx: 0, TypesInit: {v16s8, v8s16, v4s32, v2s64})(Query)) |
137 | return true; |
138 | if (HasAVX2 && typeInSet(TypeIdx: 0, TypesInit: {v32s8, v16s16, v8s32, v4s64})(Query)) |
139 | return true; |
140 | if (HasAVX512 && typeInSet(TypeIdx: 0, TypesInit: {v16s32, v8s64})(Query)) |
141 | return true; |
142 | if (HasBWI && typeInSet(TypeIdx: 0, TypesInit: {v64s8, v32s16})(Query)) |
143 | return true; |
144 | return false; |
145 | }) |
146 | .clampMinNumElements(TypeIdx: 0, EltTy: s8, MinElements: 16) |
147 | .clampMinNumElements(TypeIdx: 0, EltTy: s16, MinElements: 8) |
148 | .clampMinNumElements(TypeIdx: 0, EltTy: s32, MinElements: 4) |
149 | .clampMinNumElements(TypeIdx: 0, EltTy: s64, MinElements: 2) |
150 | .clampMaxNumElements(TypeIdx: 0, EltTy: s8, MaxElements: HasBWI ? 64 : (HasAVX2 ? 32 : 16)) |
151 | .clampMaxNumElements(TypeIdx: 0, EltTy: s16, MaxElements: HasBWI ? 32 : (HasAVX2 ? 16 : 8)) |
152 | .clampMaxNumElements(TypeIdx: 0, EltTy: s32, MaxElements: HasAVX512 ? 16 : (HasAVX2 ? 8 : 4)) |
153 | .clampMaxNumElements(TypeIdx: 0, EltTy: s64, MaxElements: HasAVX512 ? 8 : (HasAVX2 ? 4 : 2)) |
154 | .widenScalarToNextPow2(TypeIdx: 0, /*Min=*/MinSize: 32) |
155 | .clampScalar(TypeIdx: 0, MinTy: s8, MaxTy: sMaxScalar) |
156 | .scalarize(TypeIdx: 0); |
157 | |
158 | getActionDefinitionsBuilder(Opcodes: {G_UADDE, G_UADDO, G_USUBE, G_USUBO}) |
159 | .legalIf(Predicate: [=](const LegalityQuery &Query) -> bool { |
160 | return typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{s8, s1}, {s16, s1}, {s32, s1}})(Query) || |
161 | (Is64Bit && typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{s64, s1}})(Query)); |
162 | }) |
163 | .widenScalarToNextPow2(TypeIdx: 0, /*Min=*/MinSize: 32) |
164 | .clampScalar(TypeIdx: 0, MinTy: s8, MaxTy: sMaxScalar) |
165 | .clampScalar(TypeIdx: 1, MinTy: s1, MaxTy: s1) |
166 | .scalarize(TypeIdx: 0); |
167 | |
168 | // integer multiply |
169 | getActionDefinitionsBuilder(Opcode: G_MUL) |
170 | .legalIf(Predicate: [=](const LegalityQuery &Query) -> bool { |
171 | if (typeInSet(TypeIdx: 0, TypesInit: {s8, s16, s32})(Query)) |
172 | return true; |
173 | if (Is64Bit && typeInSet(TypeIdx: 0, TypesInit: {s64})(Query)) |
174 | return true; |
175 | if (HasSSE2 && typeInSet(TypeIdx: 0, TypesInit: {v8s16})(Query)) |
176 | return true; |
177 | if (HasSSE41 && typeInSet(TypeIdx: 0, TypesInit: {v4s32})(Query)) |
178 | return true; |
179 | if (HasAVX2 && typeInSet(TypeIdx: 0, TypesInit: {v16s16, v8s32})(Query)) |
180 | return true; |
181 | if (HasAVX512 && typeInSet(TypeIdx: 0, TypesInit: {v16s32})(Query)) |
182 | return true; |
183 | if (HasDQI && typeInSet(TypeIdx: 0, TypesInit: {v8s64})(Query)) |
184 | return true; |
185 | if (HasDQI && HasVLX && typeInSet(TypeIdx: 0, TypesInit: {v2s64, v4s64})(Query)) |
186 | return true; |
187 | if (HasBWI && typeInSet(TypeIdx: 0, TypesInit: {v32s16})(Query)) |
188 | return true; |
189 | return false; |
190 | }) |
191 | .clampMinNumElements(TypeIdx: 0, EltTy: s16, MinElements: 8) |
192 | .clampMinNumElements(TypeIdx: 0, EltTy: s32, MinElements: 4) |
193 | .clampMinNumElements(TypeIdx: 0, EltTy: s64, MinElements: HasVLX ? 2 : 8) |
194 | .clampMaxNumElements(TypeIdx: 0, EltTy: s16, MaxElements: HasBWI ? 32 : (HasAVX2 ? 16 : 8)) |
195 | .clampMaxNumElements(TypeIdx: 0, EltTy: s32, MaxElements: HasAVX512 ? 16 : (HasAVX2 ? 8 : 4)) |
196 | .clampMaxNumElements(TypeIdx: 0, EltTy: s64, MaxElements: 8) |
197 | .widenScalarToNextPow2(TypeIdx: 0, /*Min=*/MinSize: 32) |
198 | .clampScalar(TypeIdx: 0, MinTy: s8, MaxTy: sMaxScalar) |
199 | .scalarize(TypeIdx: 0); |
200 | |
201 | getActionDefinitionsBuilder(Opcodes: {G_SMULH, G_UMULH}) |
202 | .legalIf(Predicate: [=](const LegalityQuery &Query) -> bool { |
203 | return typeInSet(TypeIdx: 0, TypesInit: {s8, s16, s32})(Query) || |
204 | (Is64Bit && typeInSet(TypeIdx: 0, TypesInit: {s64})(Query)); |
205 | }) |
206 | .widenScalarToNextPow2(TypeIdx: 0, /*Min=*/MinSize: 32) |
207 | .clampScalar(TypeIdx: 0, MinTy: s8, MaxTy: sMaxScalar) |
208 | .scalarize(TypeIdx: 0); |
209 | |
210 | // integer divisions |
211 | getActionDefinitionsBuilder(Opcodes: {G_SDIV, G_SREM, G_UDIV, G_UREM}) |
212 | .legalIf(Predicate: [=](const LegalityQuery &Query) -> bool { |
213 | return typeInSet(TypeIdx: 0, TypesInit: {s8, s16, s32})(Query) || |
214 | (Is64Bit && typeInSet(TypeIdx: 0, TypesInit: {s64})(Query)); |
215 | }) |
216 | .libcallFor(Types: {s64}) |
217 | .clampScalar(TypeIdx: 0, MinTy: s8, MaxTy: sMaxScalar); |
218 | |
219 | // integer shifts |
220 | getActionDefinitionsBuilder(Opcodes: {G_SHL, G_LSHR, G_ASHR}) |
221 | .legalIf(Predicate: [=](const LegalityQuery &Query) -> bool { |
222 | return typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{s8, s8}, {s16, s8}, {s32, s8}})(Query) || |
223 | (Is64Bit && typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{s64, s8}})(Query)); |
224 | }) |
225 | .clampScalar(TypeIdx: 0, MinTy: s8, MaxTy: sMaxScalar) |
226 | .clampScalar(TypeIdx: 1, MinTy: s8, MaxTy: s8); |
227 | |
228 | // integer logic |
229 | getActionDefinitionsBuilder(Opcodes: {G_AND, G_OR, G_XOR}) |
230 | .legalIf(Predicate: [=](const LegalityQuery &Query) -> bool { |
231 | if (typeInSet(TypeIdx: 0, TypesInit: {s8, s16, s32})(Query)) |
232 | return true; |
233 | if (Is64Bit && typeInSet(TypeIdx: 0, TypesInit: {s64})(Query)) |
234 | return true; |
235 | if (HasSSE2 && typeInSet(TypeIdx: 0, TypesInit: {v16s8, v8s16, v4s32, v2s64})(Query)) |
236 | return true; |
237 | if (HasAVX && typeInSet(TypeIdx: 0, TypesInit: {v32s8, v16s16, v8s32, v4s64})(Query)) |
238 | return true; |
239 | if (HasAVX512 && typeInSet(TypeIdx: 0, TypesInit: {v64s8, v32s16, v16s32, v8s64})(Query)) |
240 | return true; |
241 | return false; |
242 | }) |
243 | .clampMinNumElements(TypeIdx: 0, EltTy: s8, MinElements: 16) |
244 | .clampMinNumElements(TypeIdx: 0, EltTy: s16, MinElements: 8) |
245 | .clampMinNumElements(TypeIdx: 0, EltTy: s32, MinElements: 4) |
246 | .clampMinNumElements(TypeIdx: 0, EltTy: s64, MinElements: 2) |
247 | .clampMaxNumElements(TypeIdx: 0, EltTy: s8, MaxElements: HasAVX512 ? 64 : (HasAVX ? 32 : 16)) |
248 | .clampMaxNumElements(TypeIdx: 0, EltTy: s16, MaxElements: HasAVX512 ? 32 : (HasAVX ? 16 : 8)) |
249 | .clampMaxNumElements(TypeIdx: 0, EltTy: s32, MaxElements: HasAVX512 ? 16 : (HasAVX ? 8 : 4)) |
250 | .clampMaxNumElements(TypeIdx: 0, EltTy: s64, MaxElements: HasAVX512 ? 8 : (HasAVX ? 4 : 2)) |
251 | .widenScalarToNextPow2(TypeIdx: 0, /*Min=*/MinSize: 32) |
252 | .clampScalar(TypeIdx: 0, MinTy: s8, MaxTy: sMaxScalar) |
253 | .scalarize(TypeIdx: 0); |
254 | |
255 | // integer comparison |
256 | const std::initializer_list<LLT> IntTypes32 = {s8, s16, s32, p0}; |
257 | const std::initializer_list<LLT> IntTypes64 = {s8, s16, s32, s64, p0}; |
258 | |
259 | getActionDefinitionsBuilder(Opcode: G_ICMP) |
260 | .legalForCartesianProduct(Types0: {s8}, Types1: Is64Bit ? IntTypes64 : IntTypes32) |
261 | .clampScalar(TypeIdx: 0, MinTy: s8, MaxTy: s8) |
262 | .clampScalar(TypeIdx: 1, MinTy: s8, MaxTy: sMaxScalar); |
263 | |
264 | // bswap |
265 | getActionDefinitionsBuilder(Opcode: G_BSWAP) |
266 | .legalIf([=](const LegalityQuery &Query) { |
267 | return Query.Types[0] == s32 || |
268 | (Subtarget.is64Bit() && Query.Types[0] == s64); |
269 | }) |
270 | .widenScalarToNextPow2(TypeIdx: 0, /*Min=*/MinSize: 32) |
271 | .clampScalar(TypeIdx: 0, MinTy: s32, MaxTy: sMaxScalar); |
272 | |
273 | // popcount |
274 | getActionDefinitionsBuilder(Opcode: G_CTPOP) |
275 | .legalIf(Predicate: [=](const LegalityQuery &Query) -> bool { |
276 | return Subtarget.hasPOPCNT() && |
277 | (typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{s16, s16}, {s32, s32}})(Query) || |
278 | (Is64Bit && typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{s64, s64}})(Query))); |
279 | }) |
280 | .widenScalarToNextPow2(TypeIdx: 1, /*Min=*/MinSize: 16) |
281 | .clampScalar(TypeIdx: 1, MinTy: s16, MaxTy: sMaxScalar) |
282 | .scalarSameSizeAs(TypeIdx: 0, SameSizeIdx: 1); |
283 | |
284 | // count leading zeros (LZCNT) |
285 | getActionDefinitionsBuilder(Opcode: G_CTLZ) |
286 | .legalIf(Predicate: [=](const LegalityQuery &Query) -> bool { |
287 | return Subtarget.hasLZCNT() && |
288 | (typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{s16, s16}, {s32, s32}})(Query) || |
289 | (Is64Bit && typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{s64, s64}})(Query))); |
290 | }) |
291 | .widenScalarToNextPow2(TypeIdx: 1, /*Min=*/MinSize: 16) |
292 | .clampScalar(TypeIdx: 1, MinTy: s16, MaxTy: sMaxScalar) |
293 | .scalarSameSizeAs(TypeIdx: 0, SameSizeIdx: 1); |
294 | |
295 | // count trailing zeros |
296 | getActionDefinitionsBuilder(Opcodes: {G_CTTZ_ZERO_UNDEF, G_CTTZ}) |
297 | .legalIf(Predicate: [=](const LegalityQuery &Query) -> bool { |
298 | return (Query.Opcode == G_CTTZ_ZERO_UNDEF || Subtarget.hasBMI()) && |
299 | (typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{s16, s16}, {s32, s32}})(Query) || |
300 | (Is64Bit && typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{s64, s64}})(Query))); |
301 | }) |
302 | .widenScalarToNextPow2(TypeIdx: 1, /*Min=*/MinSize: 16) |
303 | .clampScalar(TypeIdx: 1, MinTy: s16, MaxTy: sMaxScalar) |
304 | .scalarSameSizeAs(TypeIdx: 0, SameSizeIdx: 1); |
305 | |
306 | // control flow |
307 | getActionDefinitionsBuilder(Opcode: G_PHI) |
308 | .legalIf(Predicate: [=](const LegalityQuery &Query) -> bool { |
309 | return typeInSet(TypeIdx: 0, TypesInit: {s8, s16, s32, p0})(Query) || |
310 | (Is64Bit && typeInSet(TypeIdx: 0, TypesInit: {s64})(Query)) || |
311 | (HasSSE1 && typeInSet(TypeIdx: 0, TypesInit: {v16s8, v8s16, v4s32, v2s64})(Query)) || |
312 | (HasAVX && typeInSet(TypeIdx: 0, TypesInit: {v32s8, v16s16, v8s32, v4s64})(Query)) || |
313 | (HasAVX512 && |
314 | typeInSet(TypeIdx: 0, TypesInit: {v64s8, v32s16, v16s32, v8s64})(Query)); |
315 | }) |
316 | .clampMinNumElements(TypeIdx: 0, EltTy: s8, MinElements: 16) |
317 | .clampMinNumElements(TypeIdx: 0, EltTy: s16, MinElements: 8) |
318 | .clampMinNumElements(TypeIdx: 0, EltTy: s32, MinElements: 4) |
319 | .clampMinNumElements(TypeIdx: 0, EltTy: s64, MinElements: 2) |
320 | .clampMaxNumElements(TypeIdx: 0, EltTy: s8, MaxElements: HasAVX512 ? 64 : (HasAVX ? 32 : 16)) |
321 | .clampMaxNumElements(TypeIdx: 0, EltTy: s16, MaxElements: HasAVX512 ? 32 : (HasAVX ? 16 : 8)) |
322 | .clampMaxNumElements(TypeIdx: 0, EltTy: s32, MaxElements: HasAVX512 ? 16 : (HasAVX ? 8 : 4)) |
323 | .clampMaxNumElements(TypeIdx: 0, EltTy: s64, MaxElements: HasAVX512 ? 8 : (HasAVX ? 4 : 2)) |
324 | .widenScalarToNextPow2(TypeIdx: 0, /*Min=*/MinSize: 32) |
325 | .clampScalar(TypeIdx: 0, MinTy: s8, MaxTy: sMaxScalar) |
326 | .scalarize(TypeIdx: 0); |
327 | |
328 | getActionDefinitionsBuilder(Opcode: G_BRCOND).legalFor(Types: {s1}); |
329 | |
330 | // pointer handling |
331 | const std::initializer_list<LLT> PtrTypes32 = {s1, s8, s16, s32}; |
332 | const std::initializer_list<LLT> PtrTypes64 = {s1, s8, s16, s32, s64}; |
333 | |
334 | getActionDefinitionsBuilder(Opcode: G_PTRTOINT) |
335 | .legalForCartesianProduct(Types0: Is64Bit ? PtrTypes64 : PtrTypes32, Types1: {p0}) |
336 | .maxScalar(TypeIdx: 0, Ty: sMaxScalar) |
337 | .widenScalarToNextPow2(TypeIdx: 0, /*Min*/ MinSize: 8); |
338 | |
339 | getActionDefinitionsBuilder(Opcode: G_INTTOPTR).legalFor(Types: {{p0, sMaxScalar}}); |
340 | |
341 | getActionDefinitionsBuilder(Opcode: G_PTR_ADD) |
342 | .legalIf(Predicate: [=](const LegalityQuery &Query) -> bool { |
343 | return typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{p0, s32}})(Query) || |
344 | (Is64Bit && typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{p0, s64}})(Query)); |
345 | }) |
346 | .widenScalarToNextPow2(TypeIdx: 1, /*Min*/ MinSize: 32) |
347 | .clampScalar(TypeIdx: 1, MinTy: s32, MaxTy: sMaxScalar); |
348 | |
349 | getActionDefinitionsBuilder(Opcodes: {G_FRAME_INDEX, G_GLOBAL_VALUE}).legalFor(Types: {p0}); |
350 | |
351 | // load/store: add more corner cases |
352 | for (unsigned Op : {G_LOAD, G_STORE}) { |
353 | auto &Action = getActionDefinitionsBuilder(Opcode: Op); |
354 | Action.legalForTypesWithMemDesc(TypesAndMemDesc: {{.Type0: s8, .Type1: p0, .MemTy: s1, .Align: 1}, |
355 | {.Type0: s8, .Type1: p0, .MemTy: s8, .Align: 1}, |
356 | {.Type0: s16, .Type1: p0, .MemTy: s8, .Align: 1}, |
357 | {.Type0: s16, .Type1: p0, .MemTy: s16, .Align: 1}, |
358 | {.Type0: s32, .Type1: p0, .MemTy: s8, .Align: 1}, |
359 | {.Type0: s32, .Type1: p0, .MemTy: s16, .Align: 1}, |
360 | {.Type0: s32, .Type1: p0, .MemTy: s32, .Align: 1}, |
361 | {.Type0: s80, .Type1: p0, .MemTy: s80, .Align: 1}, |
362 | {.Type0: p0, .Type1: p0, .MemTy: p0, .Align: 1}, |
363 | {.Type0: v4s8, .Type1: p0, .MemTy: v4s8, .Align: 1}}); |
364 | if (Is64Bit) |
365 | Action.legalForTypesWithMemDesc(TypesAndMemDesc: {{.Type0: s64, .Type1: p0, .MemTy: s8, .Align: 1}, |
366 | {.Type0: s64, .Type1: p0, .MemTy: s16, .Align: 1}, |
367 | {.Type0: s64, .Type1: p0, .MemTy: s32, .Align: 1}, |
368 | {.Type0: s64, .Type1: p0, .MemTy: s64, .Align: 1}, |
369 | {.Type0: v2s32, .Type1: p0, .MemTy: v2s32, .Align: 1}}); |
370 | if (HasSSE1) |
371 | Action.legalForTypesWithMemDesc(TypesAndMemDesc: {{.Type0: v16s8, .Type1: p0, .MemTy: v16s8, .Align: 1}, |
372 | {.Type0: v8s16, .Type1: p0, .MemTy: v8s16, .Align: 1}, |
373 | {.Type0: v4s32, .Type1: p0, .MemTy: v4s32, .Align: 1}, |
374 | {.Type0: v2s64, .Type1: p0, .MemTy: v2s64, .Align: 1}, |
375 | {.Type0: v2p0, .Type1: p0, .MemTy: v2p0, .Align: 1}}); |
376 | if (HasAVX) |
377 | Action.legalForTypesWithMemDesc(TypesAndMemDesc: {{.Type0: v32s8, .Type1: p0, .MemTy: v32s8, .Align: 1}, |
378 | {.Type0: v16s16, .Type1: p0, .MemTy: v16s16, .Align: 1}, |
379 | {.Type0: v8s32, .Type1: p0, .MemTy: v8s32, .Align: 1}, |
380 | {.Type0: v4s64, .Type1: p0, .MemTy: v4s64, .Align: 1}, |
381 | {.Type0: v4p0, .Type1: p0, .MemTy: v4p0, .Align: 1}}); |
382 | if (HasAVX512) |
383 | Action.legalForTypesWithMemDesc(TypesAndMemDesc: {{.Type0: v64s8, .Type1: p0, .MemTy: v64s8, .Align: 1}, |
384 | {.Type0: v32s16, .Type1: p0, .MemTy: v32s16, .Align: 1}, |
385 | {.Type0: v16s32, .Type1: p0, .MemTy: v16s32, .Align: 1}, |
386 | {.Type0: v8s64, .Type1: p0, .MemTy: v8s64, .Align: 1}}); |
387 | Action.widenScalarToNextPow2(TypeIdx: 0, /*Min=*/MinSize: 8).clampScalar(TypeIdx: 0, MinTy: s8, MaxTy: sMaxScalar); |
388 | } |
389 | |
390 | for (unsigned Op : {G_SEXTLOAD, G_ZEXTLOAD}) { |
391 | auto &Action = getActionDefinitionsBuilder(Opcode: Op); |
392 | Action.legalForTypesWithMemDesc(TypesAndMemDesc: {{.Type0: s16, .Type1: p0, .MemTy: s8, .Align: 1}, |
393 | {.Type0: s32, .Type1: p0, .MemTy: s8, .Align: 1}, |
394 | {.Type0: s32, .Type1: p0, .MemTy: s16, .Align: 1}}); |
395 | if (Is64Bit) |
396 | Action.legalForTypesWithMemDesc(TypesAndMemDesc: {{.Type0: s64, .Type1: p0, .MemTy: s8, .Align: 1}, |
397 | {.Type0: s64, .Type1: p0, .MemTy: s16, .Align: 1}, |
398 | {.Type0: s64, .Type1: p0, .MemTy: s32, .Align: 1}}); |
399 | // TODO - SSE41/AVX2/AVX512F/AVX512BW vector extensions |
400 | } |
401 | |
402 | // sext, zext, and anyext |
403 | getActionDefinitionsBuilder(Opcodes: {G_SEXT, G_ZEXT, G_ANYEXT}) |
404 | .legalIf([=](const LegalityQuery &Query) { |
405 | return typeInSet(0, {s8, s16, s32})(Query) || |
406 | (Query.Opcode == G_ANYEXT && Query.Types[0] == s128) || |
407 | (Is64Bit && Query.Types[0] == s64); |
408 | }) |
409 | .widenScalarToNextPow2(TypeIdx: 0, /*Min=*/MinSize: 8) |
410 | .clampScalar(TypeIdx: 0, MinTy: s8, MaxTy: sMaxScalar) |
411 | .widenScalarToNextPow2(TypeIdx: 1, /*Min=*/MinSize: 8) |
412 | .clampScalar(TypeIdx: 1, MinTy: s8, MaxTy: sMaxScalar); |
413 | |
414 | getActionDefinitionsBuilder(Opcode: G_SEXT_INREG).lower(); |
415 | |
416 | // fp constants |
417 | getActionDefinitionsBuilder(Opcode: G_FCONSTANT) |
418 | .legalIf(Predicate: [=](const LegalityQuery &Query) -> bool { |
419 | return (typeInSet(TypeIdx: 0, TypesInit: {s32, s64})(Query)) || |
420 | (UseX87 && typeInSet(TypeIdx: 0, TypesInit: {s80})(Query)); |
421 | }); |
422 | |
423 | // fp arithmetic |
424 | getActionDefinitionsBuilder(Opcodes: {G_FADD, G_FSUB, G_FMUL, G_FDIV}) |
425 | .legalIf([=](const LegalityQuery &Query) { |
426 | return (typeInSet(0, {s32, s64})(Query)) || |
427 | (HasSSE1 && typeInSet(0, {v4s32})(Query)) || |
428 | (HasSSE2 && typeInSet(0, {v2s64})(Query)) || |
429 | (HasAVX && typeInSet(0, {v8s32, v4s64})(Query)) || |
430 | (HasAVX512 && typeInSet(0, {v16s32, v8s64})(Query)) || |
431 | (UseX87 && typeInSet(0, {s80})(Query)); |
432 | }); |
433 | |
434 | // fp comparison |
435 | getActionDefinitionsBuilder(Opcode: G_FCMP) |
436 | .legalIf(Predicate: [=](const LegalityQuery &Query) { |
437 | return (HasSSE1 && typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{s8, s32}})(Query)) || |
438 | (HasSSE2 && typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{s8, s64}})(Query)); |
439 | }) |
440 | .clampScalar(TypeIdx: 0, MinTy: s8, MaxTy: s8) |
441 | .clampScalar(TypeIdx: 1, MinTy: s32, MaxTy: HasSSE2 ? s64 : s32) |
442 | .widenScalarToNextPow2(TypeIdx: 1); |
443 | |
444 | // fp conversions |
445 | getActionDefinitionsBuilder(Opcode: G_FPEXT).legalIf(Predicate: [=](const LegalityQuery &Query) { |
446 | return (HasSSE2 && typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{s64, s32}})(Query)) || |
447 | (HasAVX && typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{v4s64, v4s32}})(Query)) || |
448 | (HasAVX512 && typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{v8s64, v8s32}})(Query)); |
449 | }); |
450 | |
451 | getActionDefinitionsBuilder(Opcode: G_FPTRUNC).legalIf( |
452 | Predicate: [=](const LegalityQuery &Query) { |
453 | return (HasSSE2 && typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{s32, s64}})(Query)) || |
454 | (HasAVX && typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{v4s32, v4s64}})(Query)) || |
455 | (HasAVX512 && typePairInSet(TypeIdx0: 0, TypeIdx1: 1, TypesInit: {{v8s32, v8s64}})(Query)); |
456 | }); |
457 | |
458 | getActionDefinitionsBuilder(Opcode: G_SITOFP) |
459 | .legalIf([=](const LegalityQuery &Query) { |
460 | return (HasSSE1 && |
461 | (typePairInSet(0, 1, {{s32, s32}})(Query) || |
462 | (Is64Bit && typePairInSet(0, 1, {{s32, s64}})(Query)))) || |
463 | (HasSSE2 && |
464 | (typePairInSet(0, 1, {{s64, s32}})(Query) || |
465 | (Is64Bit && typePairInSet(0, 1, {{s64, s64}})(Query)))); |
466 | }) |
467 | .clampScalar(TypeIdx: 1, MinTy: s32, MaxTy: sMaxScalar) |
468 | .widenScalarToNextPow2(TypeIdx: 1) |
469 | .clampScalar(TypeIdx: 0, MinTy: s32, MaxTy: HasSSE2 ? s64 : s32) |
470 | .widenScalarToNextPow2(TypeIdx: 0); |
471 | |
472 | getActionDefinitionsBuilder(Opcode: G_FPTOSI) |
473 | .legalIf([=](const LegalityQuery &Query) { |
474 | return (HasSSE1 && |
475 | (typePairInSet(0, 1, {{s32, s32}})(Query) || |
476 | (Is64Bit && typePairInSet(0, 1, {{s64, s32}})(Query)))) || |
477 | (HasSSE2 && |
478 | (typePairInSet(0, 1, {{s32, s64}})(Query) || |
479 | (Is64Bit && typePairInSet(0, 1, {{s64, s64}})(Query)))); |
480 | }) |
481 | .clampScalar(TypeIdx: 1, MinTy: s32, MaxTy: HasSSE2 ? s64 : s32) |
482 | .widenScalarToNextPow2(TypeIdx: 0) |
483 | .clampScalar(TypeIdx: 0, MinTy: s32, MaxTy: sMaxScalar) |
484 | .widenScalarToNextPow2(TypeIdx: 1); |
485 | |
486 | // vector ops |
487 | getActionDefinitionsBuilder(Opcodes: {G_EXTRACT, G_INSERT}) |
488 | .legalIf(Predicate: [=](const LegalityQuery &Query) { |
489 | unsigned SubIdx = Query.Opcode == G_EXTRACT ? 0 : 1; |
490 | unsigned FullIdx = Query.Opcode == G_EXTRACT ? 1 : 0; |
491 | return (HasAVX && typePairInSet(TypeIdx0: SubIdx, TypeIdx1: FullIdx, |
492 | TypesInit: {{v16s8, v32s8}, |
493 | {v8s16, v16s16}, |
494 | {v4s32, v8s32}, |
495 | {v2s64, v4s64}})(Query)) || |
496 | (HasAVX512 && typePairInSet(TypeIdx0: SubIdx, TypeIdx1: FullIdx, |
497 | TypesInit: {{v16s8, v64s8}, |
498 | {v32s8, v64s8}, |
499 | {v8s16, v32s16}, |
500 | {v16s16, v32s16}, |
501 | {v4s32, v16s32}, |
502 | {v8s32, v16s32}, |
503 | {v2s64, v8s64}, |
504 | {v4s64, v8s64}})(Query)); |
505 | }); |
506 | |
507 | // todo: only permit dst types up to max legal vector register size? |
508 | getActionDefinitionsBuilder(Opcode: G_CONCAT_VECTORS) |
509 | .legalIf(Predicate: [=](const LegalityQuery &Query) { |
510 | return (HasSSE1 && typePairInSet(TypeIdx0: 1, TypeIdx1: 0, |
511 | TypesInit: {{v16s8, v32s8}, |
512 | {v8s16, v16s16}, |
513 | {v4s32, v8s32}, |
514 | {v2s64, v4s64}})(Query)) || |
515 | (HasAVX && typePairInSet(TypeIdx0: 1, TypeIdx1: 0, |
516 | TypesInit: {{v16s8, v64s8}, |
517 | {v32s8, v64s8}, |
518 | {v8s16, v32s16}, |
519 | {v16s16, v32s16}, |
520 | {v4s32, v16s32}, |
521 | {v8s32, v16s32}, |
522 | {v2s64, v8s64}, |
523 | {v4s64, v8s64}})(Query)); |
524 | }); |
525 | |
526 | // todo: vectors and address spaces |
527 | getActionDefinitionsBuilder(Opcode: G_SELECT) |
528 | .legalFor(Types: {{s8, s32}, {s16, s32}, {s32, s32}, {s64, s32}, {p0, s32}}) |
529 | .widenScalarToNextPow2(TypeIdx: 0, /*Min=*/MinSize: 8) |
530 | .clampScalar(TypeIdx: 0, MinTy: HasCMOV ? s16 : s8, MaxTy: sMaxScalar) |
531 | .clampScalar(TypeIdx: 1, MinTy: s32, MaxTy: s32); |
532 | |
533 | // memory intrinsics |
534 | getActionDefinitionsBuilder(Opcodes: {G_MEMCPY, G_MEMMOVE, G_MEMSET}).libcall(); |
535 | |
536 | getActionDefinitionsBuilder(Opcodes: {G_DYN_STACKALLOC, |
537 | G_STACKSAVE, |
538 | G_STACKRESTORE}).lower(); |
539 | |
540 | // fp intrinsics |
541 | getActionDefinitionsBuilder(Opcode: G_INTRINSIC_ROUNDEVEN) |
542 | .scalarize(TypeIdx: 0) |
543 | .minScalar(TypeIdx: 0, Ty: LLT::scalar(SizeInBits: 32)) |
544 | .libcall(); |
545 | |
546 | getActionDefinitionsBuilder(Opcodes: {G_FREEZE, G_CONSTANT_FOLD_BARRIER}) |
547 | .legalFor(Types: {s8, s16, s32, s64, p0}) |
548 | .widenScalarToNextPow2(TypeIdx: 0, /*Min=*/MinSize: 8) |
549 | .clampScalar(TypeIdx: 0, MinTy: s8, MaxTy: sMaxScalar); |
550 | |
551 | getLegacyLegalizerInfo().computeTables(); |
552 | verify(*STI.getInstrInfo()); |
553 | } |
554 | |
555 | bool X86LegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper, |
556 | MachineInstr &MI) const { |
557 | return true; |
558 | } |
559 | |