1//===-- KindMapping.cpp ---------------------------------------------------===//
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// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10//
11//===----------------------------------------------------------------------===//
12
13#include "flang/Optimizer/Dialect/Support/KindMapping.h"
14#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
15#include "llvm/Support/CommandLine.h"
16
17/// Allow the user to set the FIR intrinsic type kind value to LLVM type
18/// mappings. Note that these are not mappings from kind values to any
19/// other MLIR dialect, only to LLVM IR. The default values follow the f18
20/// front-end kind mappings.
21
22using Bitsize = fir::KindMapping::Bitsize;
23using KindTy = fir::KindMapping::KindTy;
24using LLVMTypeID = fir::KindMapping::LLVMTypeID;
25using MatchResult = fir::KindMapping::MatchResult;
26
27static llvm::cl::opt<std::string>
28 clKindMapping("kind-mapping",
29 llvm::cl::desc("kind mapping string to set kind precision"),
30 llvm::cl::value_desc("kind-mapping-string"),
31 llvm::cl::init(fir::KindMapping::getDefaultMap()));
32
33static llvm::cl::opt<std::string>
34 clDefaultKinds("default-kinds",
35 llvm::cl::desc("string to set default kind values"),
36 llvm::cl::value_desc("default-kind-string"),
37 llvm::cl::init(fir::KindMapping::getDefaultKinds()));
38
39// Keywords for the floating point types.
40
41static constexpr const char *kwHalf = "Half";
42static constexpr const char *kwBFloat = "BFloat";
43static constexpr const char *kwFloat = "Float";
44static constexpr const char *kwDouble = "Double";
45static constexpr const char *kwX86FP80 = "X86_FP80";
46static constexpr const char *kwFP128 = "FP128";
47static constexpr const char *kwPPCFP128 = "PPC_FP128";
48
49/// Integral types default to the kind value being the size of the value in
50/// bytes. The default is to scale from bytes to bits.
51static Bitsize defaultScalingKind(KindTy kind) {
52 const unsigned bitsInByte = 8;
53 return kind * bitsInByte;
54}
55
56/// Floating-point types default to the kind value being the size of the value
57/// in bytes. The default is to translate kinds of 2, 3, 4, 8, 10, and 16 to a
58/// valid llvm::Type::TypeID value. Otherwise, the default is FloatTyID.
59static LLVMTypeID defaultRealKind(KindTy kind) {
60 switch (kind) {
61 case 2:
62 return LLVMTypeID::HalfTyID;
63 case 3:
64 return LLVMTypeID::BFloatTyID;
65 case 4:
66 return LLVMTypeID::FloatTyID;
67 case 8:
68 return LLVMTypeID::DoubleTyID;
69 case 10:
70 return LLVMTypeID::X86_FP80TyID;
71 case 16:
72 return LLVMTypeID::FP128TyID;
73 default:
74 return LLVMTypeID::FloatTyID;
75 }
76}
77
78// lookup the kind-value given the defaults, the mappings, and a KIND key
79template <typename RT, char KEY>
80static RT doLookup(std::function<RT(KindTy)> def,
81 const llvm::DenseMap<std::pair<char, KindTy>, RT> &map,
82 KindTy kind) {
83 std::pair<char, KindTy> key{KEY, kind};
84 auto iter = map.find(key);
85 if (iter != map.end())
86 return iter->second;
87 return def(kind);
88}
89
90// do a lookup for INTEGER, LOGICAL, or CHARACTER
91template <char KEY, typename MAP>
92static Bitsize getIntegerLikeBitsize(KindTy kind, const MAP &map) {
93 return doLookup<Bitsize, KEY>(defaultScalingKind, map, kind);
94}
95
96// do a lookup for REAL or COMPLEX
97template <char KEY, typename MAP>
98static LLVMTypeID getFloatLikeTypeID(KindTy kind, const MAP &map) {
99 return doLookup<LLVMTypeID, KEY>(defaultRealKind, map, kind);
100}
101
102template <char KEY, typename MAP>
103static const llvm::fltSemantics &getFloatSemanticsOfKind(KindTy kind,
104 const MAP &map) {
105 switch (doLookup<LLVMTypeID, KEY>(defaultRealKind, map, kind)) {
106 case LLVMTypeID::HalfTyID:
107 return llvm::APFloat::IEEEhalf();
108 case LLVMTypeID::BFloatTyID:
109 return llvm::APFloat::BFloat();
110 case LLVMTypeID::FloatTyID:
111 return llvm::APFloat::IEEEsingle();
112 case LLVMTypeID::DoubleTyID:
113 return llvm::APFloat::IEEEdouble();
114 case LLVMTypeID::X86_FP80TyID:
115 return llvm::APFloat::x87DoubleExtended();
116 case LLVMTypeID::FP128TyID:
117 return llvm::APFloat::IEEEquad();
118 case LLVMTypeID::PPC_FP128TyID:
119 return llvm::APFloat::PPCDoubleDouble();
120 default:
121 llvm_unreachable("Invalid floating type");
122 }
123}
124
125/// Parse an intrinsic type code. The codes are ('a', CHARACTER), ('c',
126/// COMPLEX), ('i', INTEGER), ('l', LOGICAL), and ('r', REAL).
127static MatchResult parseCode(char &code, const char *&ptr, const char *endPtr) {
128 if (ptr >= endPtr)
129 return mlir::failure();
130 if (*ptr != 'a' && *ptr != 'c' && *ptr != 'i' && *ptr != 'l' && *ptr != 'r')
131 return mlir::failure();
132 code = *ptr++;
133 return mlir::success();
134}
135
136/// Same as `parseCode` but adds the ('d', DOUBLE PRECISION) code.
137static MatchResult parseDefCode(char &code, const char *&ptr,
138 const char *endPtr) {
139 if (ptr >= endPtr)
140 return mlir::failure();
141 if (*ptr == 'd') {
142 code = *ptr++;
143 return mlir::success();
144 }
145 return parseCode(code, ptr, endPtr);
146}
147
148template <char ch>
149static MatchResult parseSingleChar(const char *&ptr, const char *endPtr) {
150 if (ptr >= endPtr || *ptr != ch)
151 return mlir::failure();
152 ++ptr;
153 return mlir::success();
154}
155
156static MatchResult parseColon(const char *&ptr, const char *endPtr) {
157 return parseSingleChar<':'>(ptr, endPtr);
158}
159
160static MatchResult parseComma(const char *&ptr, const char *endPtr) {
161 return parseSingleChar<','>(ptr, endPtr);
162}
163
164/// Recognize and parse an unsigned integer.
165static MatchResult parseInt(unsigned &result, const char *&ptr,
166 const char *endPtr) {
167 const char *beg = ptr;
168 while (ptr < endPtr && *ptr >= '0' && *ptr <= '9')
169 ptr++;
170 if (beg == ptr)
171 return mlir::failure();
172 llvm::StringRef ref(beg, ptr - beg);
173 int temp;
174 if (ref.consumeInteger(10, temp))
175 return mlir::failure();
176 result = temp;
177 return mlir::success();
178}
179
180static mlir::LogicalResult matchString(const char *&ptr, const char *endPtr,
181 llvm::StringRef literal) {
182 llvm::StringRef s(ptr, endPtr - ptr);
183 if (s.starts_with(Prefix: literal)) {
184 ptr += literal.size();
185 return mlir::success();
186 }
187 return mlir::failure();
188}
189
190/// Recognize and parse the various floating-point keywords. These follow the
191/// LLVM naming convention.
192static MatchResult parseTypeID(LLVMTypeID &result, const char *&ptr,
193 const char *endPtr) {
194 if (mlir::succeeded(matchString(ptr, endPtr, kwHalf))) {
195 result = LLVMTypeID::HalfTyID;
196 return mlir::success();
197 }
198 if (mlir::succeeded(matchString(ptr, endPtr, kwBFloat))) {
199 result = LLVMTypeID::BFloatTyID;
200 return mlir::success();
201 }
202 if (mlir::succeeded(matchString(ptr, endPtr, kwFloat))) {
203 result = LLVMTypeID::FloatTyID;
204 return mlir::success();
205 }
206 if (mlir::succeeded(matchString(ptr, endPtr, kwDouble))) {
207 result = LLVMTypeID::DoubleTyID;
208 return mlir::success();
209 }
210 if (mlir::succeeded(matchString(ptr, endPtr, kwX86FP80))) {
211 result = LLVMTypeID::X86_FP80TyID;
212 return mlir::success();
213 }
214 if (mlir::succeeded(matchString(ptr, endPtr, kwFP128))) {
215 result = LLVMTypeID::FP128TyID;
216 return mlir::success();
217 }
218 if (mlir::succeeded(matchString(ptr, endPtr, kwPPCFP128))) {
219 result = LLVMTypeID::PPC_FP128TyID;
220 return mlir::success();
221 }
222 return mlir::failure();
223}
224
225fir::KindMapping::KindMapping(mlir::MLIRContext *context, llvm::StringRef map,
226 llvm::ArrayRef<KindTy> defs)
227 : context{context} {
228 if (mlir::failed(setDefaultKinds(defs)))
229 llvm::report_fatal_error("bad default kinds");
230 if (mlir::failed(parse(map)))
231 llvm::report_fatal_error("could not parse kind map");
232}
233
234fir::KindMapping::KindMapping(mlir::MLIRContext *context,
235 llvm::ArrayRef<KindTy> defs)
236 : KindMapping{context, clKindMapping, defs} {}
237
238fir::KindMapping::KindMapping(mlir::MLIRContext *context)
239 : KindMapping{context, clKindMapping, clDefaultKinds} {}
240
241MatchResult fir::KindMapping::badMapString(const llvm::Twine &ptr) {
242 auto unknown = mlir::UnknownLoc::get(context);
243 mlir::emitError(unknown, ptr);
244 return mlir::failure();
245}
246
247MatchResult fir::KindMapping::parse(llvm::StringRef kindMap) {
248 if (kindMap.empty())
249 return mlir::success();
250 const char *srcPtr = kindMap.begin();
251 const char *endPtr = kindMap.end();
252 while (true) {
253 char code = '\0';
254 KindTy kind = 0;
255 if (parseCode(code, srcPtr, endPtr) || parseInt(kind, srcPtr, endPtr))
256 return badMapString(srcPtr);
257 if (code == 'a' || code == 'i' || code == 'l') {
258 Bitsize bits = 0;
259 if (parseColon(srcPtr, endPtr) || parseInt(bits, srcPtr, endPtr))
260 return badMapString(srcPtr);
261 intMap[std::pair<char, KindTy>{code, kind}] = bits;
262 } else if (code == 'r' || code == 'c') {
263 LLVMTypeID id{};
264 if (parseColon(srcPtr, endPtr) || parseTypeID(id, srcPtr, endPtr))
265 return badMapString(srcPtr);
266 floatMap[std::pair<char, KindTy>{code, kind}] = id;
267 } else {
268 return badMapString(srcPtr);
269 }
270 if (parseComma(srcPtr, endPtr))
271 break;
272 }
273 if (srcPtr > endPtr)
274 return badMapString(srcPtr);
275 return mlir::success();
276}
277
278Bitsize fir::KindMapping::getCharacterBitsize(KindTy kind) const {
279 return getIntegerLikeBitsize<'a'>(kind, intMap);
280}
281
282Bitsize fir::KindMapping::getIntegerBitsize(KindTy kind) const {
283 return getIntegerLikeBitsize<'i'>(kind, intMap);
284}
285
286Bitsize fir::KindMapping::getLogicalBitsize(KindTy kind) const {
287 return getIntegerLikeBitsize<'l'>(kind, intMap);
288}
289
290LLVMTypeID fir::KindMapping::getRealTypeID(KindTy kind) const {
291 return getFloatLikeTypeID<'r'>(kind, floatMap);
292}
293
294LLVMTypeID fir::KindMapping::getComplexTypeID(KindTy kind) const {
295 return getFloatLikeTypeID<'c'>(kind, floatMap);
296}
297
298Bitsize fir::KindMapping::getRealBitsize(KindTy kind) const {
299 auto typeId = getFloatLikeTypeID<'r'>(kind, floatMap);
300 llvm::LLVMContext llCtxt; // FIXME
301 return llvm::Type::getPrimitiveType(llCtxt, typeId)->getPrimitiveSizeInBits();
302}
303
304const llvm::fltSemantics &
305fir::KindMapping::getFloatSemantics(KindTy kind) const {
306 return getFloatSemanticsOfKind<'r'>(kind, floatMap);
307}
308
309std::string fir::KindMapping::mapToString() const {
310 std::string result;
311 bool addComma = false;
312 for (auto [k, v] : intMap) {
313 if (addComma)
314 result.append(",");
315 else
316 addComma = true;
317 result += k.first + std::to_string(k.second) + ":" + std::to_string(v);
318 }
319 for (auto [k, v] : floatMap) {
320 if (addComma)
321 result.append(",");
322 else
323 addComma = true;
324 result.append(k.first + std::to_string(k.second) + ":");
325 switch (v) {
326 default:
327 llvm_unreachable("unhandled type-id");
328 case LLVMTypeID::HalfTyID:
329 result.append(kwHalf);
330 break;
331 case LLVMTypeID::BFloatTyID:
332 result.append(kwBFloat);
333 break;
334 case LLVMTypeID::FloatTyID:
335 result.append(kwFloat);
336 break;
337 case LLVMTypeID::DoubleTyID:
338 result.append(kwDouble);
339 break;
340 case LLVMTypeID::X86_FP80TyID:
341 result.append(kwX86FP80);
342 break;
343 case LLVMTypeID::FP128TyID:
344 result.append(kwFP128);
345 break;
346 case LLVMTypeID::PPC_FP128TyID:
347 result.append(kwPPCFP128);
348 break;
349 }
350 }
351 return result;
352}
353
354mlir::LogicalResult
355fir::KindMapping::setDefaultKinds(llvm::ArrayRef<KindTy> defs) {
356 if (defs.empty()) {
357 // generic front-end defaults
358 const KindTy genericKind = 4;
359 defaultMap.insert({'a', 1});
360 defaultMap.insert({'c', genericKind});
361 defaultMap.insert({'d', 2 * genericKind});
362 defaultMap.insert({'i', genericKind});
363 defaultMap.insert({'l', genericKind});
364 defaultMap.insert({'r', genericKind});
365 return mlir::success();
366 }
367 if (defs.size() != 6)
368 return mlir::failure();
369
370 // defaults determined after command-line processing
371 defaultMap.insert({'a', defs[0]});
372 defaultMap.insert({'c', defs[1]});
373 defaultMap.insert({'d', defs[2]});
374 defaultMap.insert({'i', defs[3]});
375 defaultMap.insert({'l', defs[4]});
376 defaultMap.insert({'r', defs[5]});
377 return mlir::success();
378}
379
380std::string fir::KindMapping::defaultsToString() const {
381 return std::string("a") + std::to_string(defaultMap.find('a')->second) +
382 std::string("c") + std::to_string(defaultMap.find('c')->second) +
383 std::string("d") + std::to_string(defaultMap.find('d')->second) +
384 std::string("i") + std::to_string(defaultMap.find('i')->second) +
385 std::string("l") + std::to_string(defaultMap.find('l')->second) +
386 std::string("r") + std::to_string(defaultMap.find('r')->second);
387}
388
389/// Convert a default intrinsic code into the proper position in the array. The
390/// default kinds have a precise ordering.
391static int codeToIndex(char code) {
392 switch (code) {
393 case 'a':
394 return 0;
395 case 'c':
396 return 1;
397 case 'd':
398 return 2;
399 case 'i':
400 return 3;
401 case 'l':
402 return 4;
403 case 'r':
404 return 5;
405 }
406 llvm_unreachable("invalid default kind intrinsic code");
407}
408
409std::vector<KindTy> fir::KindMapping::toDefaultKinds(llvm::StringRef defs) {
410 std::vector<KindTy> result(6);
411 char code;
412 KindTy kind;
413 if (defs.empty())
414 defs = clDefaultKinds;
415 const char *srcPtr = defs.begin();
416 const char *endPtr = defs.end();
417 while (srcPtr < endPtr) {
418 if (parseDefCode(code, srcPtr, endPtr) || parseInt(kind, srcPtr, endPtr))
419 llvm::report_fatal_error("invalid default kind code");
420 result[codeToIndex(code)] = kind;
421 }
422 assert(srcPtr == endPtr);
423 return result;
424}
425
426KindTy fir::KindMapping::defaultCharacterKind() const {
427 auto iter = defaultMap.find('a');
428 assert(iter != defaultMap.end());
429 return iter->second;
430}
431
432KindTy fir::KindMapping::defaultComplexKind() const {
433 auto iter = defaultMap.find('c');
434 assert(iter != defaultMap.end());
435 return iter->second;
436}
437
438KindTy fir::KindMapping::defaultDoubleKind() const {
439 auto iter = defaultMap.find('d');
440 assert(iter != defaultMap.end());
441 return iter->second;
442}
443
444KindTy fir::KindMapping::defaultIntegerKind() const {
445 auto iter = defaultMap.find('i');
446 assert(iter != defaultMap.end());
447 return iter->second;
448}
449
450KindTy fir::KindMapping::defaultLogicalKind() const {
451 auto iter = defaultMap.find('l');
452 assert(iter != defaultMap.end());
453 return iter->second;
454}
455
456KindTy fir::KindMapping::defaultRealKind() const {
457 auto iter = defaultMap.find('r');
458 assert(iter != defaultMap.end());
459 return iter->second;
460}
461

source code of flang/lib/Optimizer/Dialect/Support/KindMapping.cpp