xref: /llvm-project/flang/lib/Evaluate/target.cpp (revision ff862d6de92f478253a332ec48cfc2c2add76bb3)
1 //===-- lib/Semantics/target.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 #include "flang/Evaluate/target.h"
10 #include "flang/Common/template.h"
11 #include "flang/Evaluate/common.h"
12 #include "flang/Evaluate/type.h"
13 
14 namespace Fortran::evaluate {
15 
16 Rounding TargetCharacteristics::defaultRounding;
17 
18 TargetCharacteristics::TargetCharacteristics() {
19   auto enableCategoryKinds{[this](TypeCategory category) {
20     for (int kind{1}; kind <= maxKind; ++kind) {
21       if (CanSupportType(category, kind)) {
22         auto byteSize{static_cast<std::size_t>(kind)};
23         if (category == TypeCategory::Real ||
24             category == TypeCategory::Complex) {
25           if (kind == 3) {
26             // non-IEEE 16-bit format (truncated 32-bit)
27             byteSize = 2;
28           } else if (kind == 10) {
29             // x87 floating-point
30             // Follow gcc precedent for "long double"
31             byteSize = 16;
32           }
33         }
34         std::size_t align{byteSize};
35         if (category == TypeCategory::Complex) {
36           byteSize = 2 * byteSize;
37         }
38         EnableType(category, kind, byteSize, align);
39       }
40     }
41   }};
42   enableCategoryKinds(TypeCategory::Integer);
43   enableCategoryKinds(TypeCategory::Real);
44   enableCategoryKinds(TypeCategory::Complex);
45   enableCategoryKinds(TypeCategory::Character);
46   enableCategoryKinds(TypeCategory::Logical);
47   enableCategoryKinds(TypeCategory::Unsigned);
48 
49   isBigEndian_ = !isHostLittleEndian;
50 
51   areSubnormalsFlushedToZero_ = false;
52 }
53 
54 bool TargetCharacteristics::CanSupportType(
55     TypeCategory category, std::int64_t kind) {
56   return IsValidKindOfIntrinsicType(category, kind);
57 }
58 
59 bool TargetCharacteristics::EnableType(common::TypeCategory category,
60     std::int64_t kind, std::size_t byteSize, std::size_t align) {
61   if (CanSupportType(category, kind)) {
62     byteSize_[static_cast<int>(category)][kind] = byteSize;
63     align_[static_cast<int>(category)][kind] = align;
64     maxByteSize_ = std::max(maxByteSize_, byteSize);
65     maxAlignment_ = std::max(maxAlignment_, align);
66     return true;
67   } else {
68     return false;
69   }
70 }
71 
72 void TargetCharacteristics::DisableType(
73     common::TypeCategory category, std::int64_t kind) {
74   if (kind > 0 && kind <= maxKind) {
75     align_[static_cast<int>(category)][kind] = 0;
76   }
77 }
78 
79 std::size_t TargetCharacteristics::GetByteSize(
80     common::TypeCategory category, std::int64_t kind) const {
81   if (kind > 0 && kind <= maxKind) {
82     return byteSize_[static_cast<int>(category)][kind];
83   } else {
84     return 0;
85   }
86 }
87 
88 std::size_t TargetCharacteristics::GetAlignment(
89     common::TypeCategory category, std::int64_t kind) const {
90   if (kind > 0 && kind <= maxKind) {
91     return align_[static_cast<int>(category)][kind];
92   } else {
93     return 0;
94   }
95 }
96 
97 bool TargetCharacteristics::IsTypeEnabled(
98     common::TypeCategory category, std::int64_t kind) const {
99   return GetAlignment(category, kind) > 0;
100 }
101 
102 void TargetCharacteristics::set_isBigEndian(bool isBig) {
103   isBigEndian_ = isBig;
104 }
105 
106 void TargetCharacteristics::set_isPPC(bool isPowerPC) { isPPC_ = isPowerPC; }
107 void TargetCharacteristics::set_isSPARC(bool isSPARC) { isSPARC_ = isSPARC; }
108 
109 void TargetCharacteristics::set_areSubnormalsFlushedToZero(bool yes) {
110   areSubnormalsFlushedToZero_ = yes;
111 }
112 
113 // Check if a given real kind has flushing control.
114 bool TargetCharacteristics::hasSubnormalFlushingControl(int kind) const {
115   CHECK(kind > 0 && kind <= maxKind);
116   CHECK(CanSupportType(TypeCategory::Real, kind));
117   return hasSubnormalFlushingControl_[kind];
118 }
119 
120 // Check if any or all real kinds have flushing control.
121 bool TargetCharacteristics::hasSubnormalFlushingControl(bool any) const {
122   for (int kind{1}; kind <= maxKind; ++kind) {
123     if (CanSupportType(TypeCategory::Real, kind) &&
124         hasSubnormalFlushingControl_[kind] == any) {
125       return any;
126     }
127   }
128   return !any;
129 }
130 
131 void TargetCharacteristics::set_hasSubnormalFlushingControl(
132     int kind, bool yes) {
133   CHECK(kind > 0 && kind <= maxKind);
134   hasSubnormalFlushingControl_[kind] = yes;
135 }
136 
137 void TargetCharacteristics::set_roundingMode(Rounding rounding) {
138   roundingMode_ = rounding;
139 }
140 
141 // SELECTED_INT_KIND() -- F'2018 16.9.169
142 // and SELECTED_UNSIGNED_KIND() extension (same results)
143 class SelectedIntKindVisitor {
144 public:
145   SelectedIntKindVisitor(
146       const TargetCharacteristics &targetCharacteristics, std::int64_t p)
147       : targetCharacteristics_{targetCharacteristics}, precision_{p} {}
148   using Result = std::optional<int>;
149   using Types = IntegerTypes;
150   template <typename T> Result Test() const {
151     if (Scalar<T>::RANGE >= precision_ &&
152         targetCharacteristics_.IsTypeEnabled(T::category, T::kind)) {
153       return T::kind;
154     } else {
155       return std::nullopt;
156     }
157   }
158 
159 private:
160   const TargetCharacteristics &targetCharacteristics_;
161   std::int64_t precision_;
162 };
163 
164 int TargetCharacteristics::SelectedIntKind(std::int64_t precision) const {
165   if (auto kind{
166           common::SearchTypes(SelectedIntKindVisitor{*this, precision})}) {
167     return *kind;
168   } else {
169     return -1;
170   }
171 }
172 
173 // SELECTED_LOGICAL_KIND() -- F'2023 16.9.182
174 class SelectedLogicalKindVisitor {
175 public:
176   SelectedLogicalKindVisitor(
177       const TargetCharacteristics &targetCharacteristics, std::int64_t bits)
178       : targetCharacteristics_{targetCharacteristics}, bits_{bits} {}
179   using Result = std::optional<int>;
180   using Types = LogicalTypes;
181   template <typename T> Result Test() const {
182     if (Scalar<T>::bits >= bits_ &&
183         targetCharacteristics_.IsTypeEnabled(T::category, T::kind)) {
184       return T::kind;
185     } else {
186       return std::nullopt;
187     }
188   }
189 
190 private:
191   const TargetCharacteristics &targetCharacteristics_;
192   std::int64_t bits_;
193 };
194 
195 int TargetCharacteristics::SelectedLogicalKind(std::int64_t bits) const {
196   if (auto kind{common::SearchTypes(SelectedLogicalKindVisitor{*this, bits})}) {
197     return *kind;
198   } else {
199     return -1;
200   }
201 }
202 
203 // SELECTED_REAL_KIND() -- F'2018 16.9.170
204 class SelectedRealKindVisitor {
205 public:
206   SelectedRealKindVisitor(const TargetCharacteristics &targetCharacteristics,
207       std::int64_t p, std::int64_t r)
208       : targetCharacteristics_{targetCharacteristics}, precision_{p}, range_{
209                                                                           r} {}
210   using Result = std::optional<int>;
211   using Types = RealTypes;
212   template <typename T> Result Test() const {
213     if (Scalar<T>::PRECISION >= precision_ && Scalar<T>::RANGE >= range_ &&
214         targetCharacteristics_.IsTypeEnabled(T::category, T::kind)) {
215       return {T::kind};
216     } else {
217       return std::nullopt;
218     }
219   }
220 
221 private:
222   const TargetCharacteristics &targetCharacteristics_;
223   std::int64_t precision_, range_;
224 };
225 
226 int TargetCharacteristics::SelectedRealKind(
227     std::int64_t precision, std::int64_t range, std::int64_t radix) const {
228   if (radix != 2) {
229     return -5;
230   }
231   if (auto kind{common::SearchTypes(
232           SelectedRealKindVisitor{*this, precision, range})}) {
233     return *kind;
234   }
235   // No kind has both sufficient precision and sufficient range.
236   // The negative return value encodes whether any kinds exist that
237   // could satisfy either constraint independently.
238   bool pOK{common::SearchTypes(SelectedRealKindVisitor{*this, precision, 0})};
239   bool rOK{common::SearchTypes(SelectedRealKindVisitor{*this, 0, range})};
240   if (pOK) {
241     if (rOK) {
242       return -4;
243     } else {
244       return -2;
245     }
246   } else {
247     if (rOK) {
248       return -1;
249     } else {
250       return -3;
251     }
252   }
253 }
254 
255 } // namespace Fortran::evaluate
256