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