1 | //===- DataLayoutImporter.cpp - LLVM to MLIR data layout conversion -------===// |
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 | |
9 | #include "DataLayoutImporter.h" |
10 | #include "mlir/Dialect/DLTI/DLTI.h" |
11 | #include "mlir/Dialect/LLVMIR/LLVMDialect.h" |
12 | #include "mlir/IR/Builders.h" |
13 | #include "mlir/IR/BuiltinAttributes.h" |
14 | #include "mlir/IR/BuiltinTypes.h" |
15 | #include "mlir/Interfaces/DataLayoutInterfaces.h" |
16 | #include "mlir/Target/LLVMIR/Import.h" |
17 | #include "llvm/IR/DataLayout.h" |
18 | |
19 | using namespace mlir; |
20 | using namespace mlir::LLVM; |
21 | using namespace mlir::LLVM::detail; |
22 | |
23 | /// The default data layout used during the translation. |
24 | static constexpr StringRef kDefaultDataLayout = |
25 | "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-" |
26 | "f16:16:16-f64:64:64-f128:128:128" ; |
27 | |
28 | FloatType mlir::LLVM::detail::getFloatType(MLIRContext *context, |
29 | unsigned width) { |
30 | switch (width) { |
31 | case 16: |
32 | return FloatType::getF16(ctx: context); |
33 | case 32: |
34 | return FloatType::getF32(ctx: context); |
35 | case 64: |
36 | return FloatType::getF64(ctx: context); |
37 | case 80: |
38 | return FloatType::getF80(ctx: context); |
39 | case 128: |
40 | return FloatType::getF128(ctx: context); |
41 | default: |
42 | return {}; |
43 | } |
44 | } |
45 | |
46 | FailureOr<StringRef> |
47 | DataLayoutImporter::tryToParseAlphaPrefix(StringRef &token) const { |
48 | if (token.empty()) |
49 | return failure(); |
50 | |
51 | StringRef prefix = token.take_while(F: isalpha); |
52 | if (prefix.empty()) |
53 | return failure(); |
54 | |
55 | token.consume_front(Prefix: prefix); |
56 | return prefix; |
57 | } |
58 | |
59 | FailureOr<uint64_t> DataLayoutImporter::tryToParseInt(StringRef &token) const { |
60 | uint64_t parameter; |
61 | if (token.consumeInteger(/*Radix=*/10, Result&: parameter)) |
62 | return failure(); |
63 | return parameter; |
64 | } |
65 | |
66 | FailureOr<SmallVector<uint64_t>> |
67 | DataLayoutImporter::tryToParseIntList(StringRef token) const { |
68 | SmallVector<StringRef> tokens; |
69 | token.consume_front(Prefix: ":" ); |
70 | token.split(A&: tokens, Separator: ':'); |
71 | |
72 | // Parse an integer list. |
73 | SmallVector<uint64_t> results(tokens.size()); |
74 | for (auto [result, token] : llvm::zip(t&: results, u&: tokens)) |
75 | if (token.getAsInteger(/*Radix=*/10, Result&: result)) |
76 | return failure(); |
77 | return results; |
78 | } |
79 | |
80 | FailureOr<DenseIntElementsAttr> |
81 | DataLayoutImporter::tryToParseAlignment(StringRef token) const { |
82 | FailureOr<SmallVector<uint64_t>> alignment = tryToParseIntList(token); |
83 | if (failed(result: alignment)) |
84 | return failure(); |
85 | if (alignment->empty() || alignment->size() > 2) |
86 | return failure(); |
87 | |
88 | // Alignment specifications (such as 32 or 32:64) are of the |
89 | // form <abi>[:<pref>], where abi specifies the minimal alignment and pref the |
90 | // optional preferred alignment. The preferred alignment is set to the minimal |
91 | // alignment if not available. |
92 | uint64_t minimal = (*alignment)[0]; |
93 | uint64_t preferred = alignment->size() == 1 ? minimal : (*alignment)[1]; |
94 | return DenseIntElementsAttr::get( |
95 | VectorType::get({2}, IntegerType::get(context, 64)), |
96 | {minimal, preferred}); |
97 | } |
98 | |
99 | FailureOr<DenseIntElementsAttr> |
100 | DataLayoutImporter::tryToParsePointerAlignment(StringRef token) const { |
101 | FailureOr<SmallVector<uint64_t>> alignment = tryToParseIntList(token); |
102 | if (failed(result: alignment)) |
103 | return failure(); |
104 | if (alignment->size() < 2 || alignment->size() > 4) |
105 | return failure(); |
106 | |
107 | // Pointer alignment specifications (such as 64:32:64:32 or 32:32) are of |
108 | // the form <size>:<abi>[:<pref>][:<idx>], where size is the pointer size, abi |
109 | // specifies the minimal alignment, pref the optional preferred alignment, and |
110 | // idx the optional index computation bit width. The preferred alignment is |
111 | // set to the minimal alignment if not available and the index computation |
112 | // width is set to the pointer size if not available. |
113 | uint64_t size = (*alignment)[0]; |
114 | uint64_t minimal = (*alignment)[1]; |
115 | uint64_t preferred = alignment->size() < 3 ? minimal : (*alignment)[2]; |
116 | uint64_t idx = alignment->size() < 4 ? size : (*alignment)[3]; |
117 | return DenseIntElementsAttr::get<uint64_t>( |
118 | VectorType::get({4}, IntegerType::get(context, 64)), |
119 | {size, minimal, preferred, idx}); |
120 | } |
121 | |
122 | LogicalResult DataLayoutImporter::tryToEmplaceAlignmentEntry(Type type, |
123 | StringRef token) { |
124 | auto key = TypeAttr::get(type); |
125 | if (typeEntries.count(Val: key)) |
126 | return success(); |
127 | |
128 | FailureOr<DenseIntElementsAttr> params = tryToParseAlignment(token); |
129 | if (failed(result: params)) |
130 | return failure(); |
131 | |
132 | typeEntries.try_emplace(key, DataLayoutEntryAttr::get(type, *params)); |
133 | return success(); |
134 | } |
135 | |
136 | LogicalResult |
137 | DataLayoutImporter::tryToEmplacePointerAlignmentEntry(LLVMPointerType type, |
138 | StringRef token) { |
139 | auto key = TypeAttr::get(type); |
140 | if (typeEntries.count(Val: key)) |
141 | return success(); |
142 | |
143 | FailureOr<DenseIntElementsAttr> params = tryToParsePointerAlignment(token); |
144 | if (failed(result: params)) |
145 | return failure(); |
146 | |
147 | typeEntries.try_emplace(key, DataLayoutEntryAttr::get(type, *params)); |
148 | return success(); |
149 | } |
150 | |
151 | LogicalResult |
152 | DataLayoutImporter::tryToEmplaceEndiannessEntry(StringRef endianness, |
153 | StringRef token) { |
154 | auto key = StringAttr::get(context, DLTIDialect::kDataLayoutEndiannessKey); |
155 | if (keyEntries.count(Val: key)) |
156 | return success(); |
157 | |
158 | if (!token.empty()) |
159 | return failure(); |
160 | |
161 | keyEntries.try_emplace( |
162 | key, DataLayoutEntryAttr::get(key, StringAttr::get(context, endianness))); |
163 | return success(); |
164 | } |
165 | |
166 | LogicalResult |
167 | DataLayoutImporter::tryToEmplaceAddrSpaceEntry(StringRef token, |
168 | llvm::StringLiteral spaceKey) { |
169 | auto key = StringAttr::get(context, spaceKey); |
170 | if (keyEntries.count(Val: key)) |
171 | return success(); |
172 | |
173 | FailureOr<uint64_t> space = tryToParseInt(token); |
174 | if (failed(result: space)) |
175 | return failure(); |
176 | |
177 | // Only store the address space if it has a non-default value. |
178 | if (*space == 0) |
179 | return success(); |
180 | OpBuilder builder(context); |
181 | keyEntries.try_emplace( |
182 | key, |
183 | DataLayoutEntryAttr::get( |
184 | key, builder.getIntegerAttr( |
185 | builder.getIntegerType(64, /*isSigned=*/false), *space))); |
186 | return success(); |
187 | } |
188 | |
189 | LogicalResult |
190 | DataLayoutImporter::tryToEmplaceStackAlignmentEntry(StringRef token) { |
191 | auto key = |
192 | StringAttr::get(context, DLTIDialect::kDataLayoutStackAlignmentKey); |
193 | if (keyEntries.count(Val: key)) |
194 | return success(); |
195 | |
196 | FailureOr<uint64_t> alignment = tryToParseInt(token); |
197 | if (failed(result: alignment)) |
198 | return failure(); |
199 | |
200 | // Only store the stack alignment if it has a non-default value. |
201 | if (*alignment == 0) |
202 | return success(); |
203 | OpBuilder builder(context); |
204 | keyEntries.try_emplace(key, DataLayoutEntryAttr::get( |
205 | key, builder.getI64IntegerAttr(*alignment))); |
206 | return success(); |
207 | } |
208 | |
209 | void DataLayoutImporter::translateDataLayout( |
210 | const llvm::DataLayout &llvmDataLayout) { |
211 | dataLayout = {}; |
212 | |
213 | // Transform the data layout to its string representation and append the |
214 | // default data layout string specified in the language reference |
215 | // (https://llvm.org/docs/LangRef.html#data-layout). The translation then |
216 | // parses the string and ignores the default value if a specific kind occurs |
217 | // in both strings. Additionally, the following default values exist: |
218 | // - non-default address space pointer specifications default to the default |
219 | // address space pointer specification |
220 | // - the alloca address space defaults to the default address space. |
221 | layoutStr = llvmDataLayout.getStringRepresentation(); |
222 | if (!layoutStr.empty()) |
223 | layoutStr += "-" ; |
224 | layoutStr += kDefaultDataLayout; |
225 | StringRef layout(layoutStr); |
226 | |
227 | // Split the data layout string into tokens separated by a dash. |
228 | SmallVector<StringRef> tokens; |
229 | layout.split(A&: tokens, Separator: '-'); |
230 | |
231 | for (StringRef token : tokens) { |
232 | lastToken = token; |
233 | FailureOr<StringRef> prefix = tryToParseAlphaPrefix(token); |
234 | if (failed(result: prefix)) |
235 | return; |
236 | |
237 | // Parse the endianness. |
238 | if (*prefix == "e" ) { |
239 | if (failed(tryToEmplaceEndiannessEntry( |
240 | DLTIDialect::kDataLayoutEndiannessLittle, token))) |
241 | return; |
242 | continue; |
243 | } |
244 | if (*prefix == "E" ) { |
245 | if (failed(tryToEmplaceEndiannessEntry( |
246 | DLTIDialect::kDataLayoutEndiannessBig, token))) |
247 | return; |
248 | continue; |
249 | } |
250 | // Parse the program address space. |
251 | if (*prefix == "P" ) { |
252 | if (failed(tryToEmplaceAddrSpaceEntry( |
253 | token, DLTIDialect::kDataLayoutProgramMemorySpaceKey))) |
254 | return; |
255 | continue; |
256 | } |
257 | // Parse the global address space. |
258 | if (*prefix == "G" ) { |
259 | if (failed(tryToEmplaceAddrSpaceEntry( |
260 | token, DLTIDialect::kDataLayoutGlobalMemorySpaceKey))) |
261 | return; |
262 | continue; |
263 | } |
264 | // Parse the alloca address space. |
265 | if (*prefix == "A" ) { |
266 | if (failed(tryToEmplaceAddrSpaceEntry( |
267 | token, DLTIDialect::kDataLayoutAllocaMemorySpaceKey))) |
268 | return; |
269 | continue; |
270 | } |
271 | // Parse the stack alignment. |
272 | if (*prefix == "S" ) { |
273 | if (failed(result: tryToEmplaceStackAlignmentEntry(token))) |
274 | return; |
275 | continue; |
276 | } |
277 | // Parse integer alignment specifications. |
278 | if (*prefix == "i" ) { |
279 | FailureOr<uint64_t> width = tryToParseInt(token); |
280 | if (failed(result: width)) |
281 | return; |
282 | |
283 | Type type = IntegerType::get(context, *width); |
284 | if (failed(result: tryToEmplaceAlignmentEntry(type, token))) |
285 | return; |
286 | continue; |
287 | } |
288 | // Parse float alignment specifications. |
289 | if (*prefix == "f" ) { |
290 | FailureOr<uint64_t> width = tryToParseInt(token); |
291 | if (failed(result: width)) |
292 | return; |
293 | |
294 | Type type = getFloatType(context, width: *width); |
295 | if (failed(result: tryToEmplaceAlignmentEntry(type, token))) |
296 | return; |
297 | continue; |
298 | } |
299 | // Parse pointer alignment specifications. |
300 | if (*prefix == "p" ) { |
301 | FailureOr<uint64_t> space = |
302 | token.starts_with(Prefix: ":" ) ? 0 : tryToParseInt(token); |
303 | if (failed(result: space)) |
304 | return; |
305 | |
306 | auto type = LLVMPointerType::get(context, *space); |
307 | if (failed(tryToEmplacePointerAlignmentEntry(type, token))) |
308 | return; |
309 | continue; |
310 | } |
311 | |
312 | // Store all tokens that have not been handled. |
313 | unhandledTokens.push_back(Elt: lastToken); |
314 | } |
315 | |
316 | // Assemble all entries to a data layout specification. |
317 | SmallVector<DataLayoutEntryInterface> entries; |
318 | entries.reserve(typeEntries.size() + keyEntries.size()); |
319 | for (const auto &it : typeEntries) |
320 | entries.push_back(it.second); |
321 | for (const auto &it : keyEntries) |
322 | entries.push_back(it.second); |
323 | dataLayout = DataLayoutSpecAttr::get(context, entries); |
324 | } |
325 | |
326 | DataLayoutSpecInterface |
327 | mlir::translateDataLayout(const llvm::DataLayout &dataLayout, |
328 | MLIRContext *context) { |
329 | return DataLayoutImporter(context, dataLayout).getDataLayout(); |
330 | } |
331 | |