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
19using namespace mlir;
20using namespace mlir::LLVM;
21using namespace mlir::LLVM::detail;
22
23/// The default data layout used during the translation.
24static 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
28FloatType 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
46FailureOr<StringRef>
47DataLayoutImporter::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
59FailureOr<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
66FailureOr<SmallVector<uint64_t>>
67DataLayoutImporter::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
80FailureOr<DenseIntElementsAttr>
81DataLayoutImporter::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
99FailureOr<DenseIntElementsAttr>
100DataLayoutImporter::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
122LogicalResult 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
136LogicalResult
137DataLayoutImporter::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
151LogicalResult
152DataLayoutImporter::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
166LogicalResult
167DataLayoutImporter::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
189LogicalResult
190DataLayoutImporter::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
209void 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
326DataLayoutSpecInterface
327mlir::translateDataLayout(const llvm::DataLayout &dataLayout,
328 MLIRContext *context) {
329 return DataLayoutImporter(context, dataLayout).getDataLayout();
330}
331

source code of mlir/lib/Target/LLVMIR/DataLayoutImporter.cpp