xref: /llvm-project/mlir/lib/Target/LLVMIR/DataLayoutImporter.cpp (revision f023da12d12635f5fba436e825cbfc999e28e623)
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 Float16Type::get(context);
33   case 32:
34     return Float32Type::get(context);
35   case 64:
36     return Float64Type::get(context);
37   case 80:
38     return Float80Type::get(context);
39   case 128:
40     return Float128Type::get(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(isalpha);
52   if (prefix.empty())
53     return failure();
54 
55   token.consume_front(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, 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(":");
70   token.split(tokens, ':');
71 
72   // Parse an integer list.
73   SmallVector<uint64_t> results(tokens.size());
74   for (auto [result, token] : llvm::zip(results, tokens))
75     if (token.getAsInteger(/*Radix=*/10, 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(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(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(key))
126     return success();
127 
128   FailureOr<DenseIntElementsAttr> params = tryToParseAlignment(token);
129   if (failed(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(key))
141     return success();
142 
143   FailureOr<DenseIntElementsAttr> params = tryToParsePointerAlignment(token);
144   if (failed(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(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(key))
171     return success();
172 
173   FailureOr<uint64_t> space = tryToParseInt(token);
174   if (failed(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(key))
194     return success();
195 
196   FailureOr<uint64_t> alignment = tryToParseInt(token);
197   if (failed(alignment))
198     return failure();
199 
200   // Stack alignment shouldn't be zero.
201   if (*alignment == 0)
202     return failure();
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(tokens, '-');
230 
231   for (StringRef token : tokens) {
232     lastToken = token;
233     FailureOr<StringRef> prefix = tryToParseAlphaPrefix(token);
234     if (failed(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(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(width))
281         return;
282 
283       Type type = IntegerType::get(context, *width);
284       if (failed(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(width))
292         return;
293 
294       Type type = getFloatType(context, *width);
295       if (failed(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(":") ? 0 : tryToParseInt(token);
303       if (failed(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(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