| 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 | |