1 | /*===-- flang/runtime/complex-powi.cpp ----------------------------*- C++ -*-=== |
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/Common/float128.h" |
10 | #include "flang/Runtime/entry-names.h" |
11 | #include <cstdint> |
12 | #include <cstdio> |
13 | #include <limits> |
14 | |
15 | #ifdef __clang_major__ |
16 | #pragma clang diagnostic ignored "-Wc99-extensions" |
17 | #endif |
18 | |
19 | template <typename C, typename I> C tgpowi(C base, I exp) { |
20 | if (exp == 0) { |
21 | return C{1}; |
22 | } |
23 | |
24 | bool invertResult{exp < 0}; |
25 | bool isMin{exp == std::numeric_limits<I>::min()}; |
26 | |
27 | if (isMin) { |
28 | exp = std::numeric_limits<I>::max(); |
29 | } |
30 | |
31 | if (exp < 0) { |
32 | exp = exp * -1; |
33 | } |
34 | |
35 | C origBase{base}; |
36 | |
37 | while ((exp & 1) == 0) { |
38 | base *= base; |
39 | exp >>= 1; |
40 | } |
41 | |
42 | C acc{base}; |
43 | |
44 | while (exp > 1) { |
45 | exp >>= 1; |
46 | base *= base; |
47 | if ((exp & 1) == 1) { |
48 | acc *= base; |
49 | } |
50 | } |
51 | |
52 | if (isMin) { |
53 | acc *= origBase; |
54 | } |
55 | |
56 | if (invertResult) { |
57 | acc = C{1} / acc; |
58 | } |
59 | |
60 | return acc; |
61 | } |
62 | |
63 | #ifndef _MSC_VER |
64 | // With most compilers, C complex is implemented as a builtin type that may have |
65 | // specific ABI requirements |
66 | extern "C" float _Complex RTNAME(cpowi)(float _Complex base, std::int32_t exp) { |
67 | return tgpowi(base, exp); |
68 | } |
69 | |
70 | extern "C" double _Complex RTNAME(zpowi)( |
71 | double _Complex base, std::int32_t exp) { |
72 | return tgpowi(base, exp); |
73 | } |
74 | |
75 | extern "C" float _Complex RTNAME(cpowk)(float _Complex base, std::int64_t exp) { |
76 | return tgpowi(base, exp); |
77 | } |
78 | |
79 | extern "C" double _Complex RTNAME(zpowk)( |
80 | double _Complex base, std::int64_t exp) { |
81 | return tgpowi(base, exp); |
82 | } |
83 | |
84 | #if LDBL_MANT_DIG == 113 || HAS_FLOAT128 |
85 | // Duplicate CFloat128ComplexType definition from flang/Common/float128.h. |
86 | // float128.h does not define it for C++, because _Complex triggers |
87 | // c99-extension warnings. We decided to disable warnings for this |
88 | // particular file, so we can use _Complex here. |
89 | #if LDBL_MANT_DIG == 113 |
90 | typedef long double _Complex Qcomplex; |
91 | #elif HAS_FLOAT128 |
92 | #if !defined(_ARCH_PPC) || defined(__LONG_DOUBLE_IEEE128__) |
93 | typedef _Complex float __attribute__((mode(TC))) Qcomplex; |
94 | #else |
95 | typedef _Complex float __attribute__((mode(KC))) Qcomplex; |
96 | #endif |
97 | #endif |
98 | |
99 | extern "C" Qcomplex RTNAME(cqpowi)(Qcomplex base, std::int32_t exp) { |
100 | return tgpowi(base, exp); |
101 | } |
102 | extern "C" Qcomplex RTNAME(cqpowk)(Qcomplex base, std::int64_t exp) { |
103 | return tgpowi(base, exp); |
104 | } |
105 | #endif |
106 | |
107 | #else |
108 | // on MSVC, C complex is always just a struct of two members as it is not |
109 | // supported as a builtin type. So we use C++ complex here as that has the |
110 | // same ABI and layout. See: |
111 | // https://learn.microsoft.com/en-us/cpp/c-runtime-library/complex-math-support |
112 | #include <complex> |
113 | |
114 | // MSVC doesn't allow including <ccomplex> or <complex.h> in C++17 mode to get |
115 | // the Windows definitions of these structs so just redefine here. |
116 | struct Fcomplex { |
117 | float re; |
118 | float im; |
119 | }; |
120 | |
121 | struct Dcomplex { |
122 | double re; |
123 | double im; |
124 | }; |
125 | |
126 | extern "C" Fcomplex RTNAME(cpowi)(Fcomplex base, std::int32_t exp) { |
127 | auto cppbase = *(std::complex<float> *)(&base); |
128 | auto cppres = tgpowi(cppbase, exp); |
129 | return *(Fcomplex *)(&cppres); |
130 | } |
131 | |
132 | extern "C" Dcomplex RTNAME(zpowi)(Dcomplex base, std::int32_t exp) { |
133 | auto cppbase = *(std::complex<double> *)(&base); |
134 | auto cppres = tgpowi(cppbase, exp); |
135 | return *(Dcomplex *)(&cppres); |
136 | } |
137 | |
138 | extern "C" Fcomplex RTNAME(cpowk)(Fcomplex base, std::int64_t exp) { |
139 | auto cppbase = *(std::complex<float> *)(&base); |
140 | auto cppres = tgpowi(cppbase, exp); |
141 | return *(Fcomplex *)(&cppres); |
142 | } |
143 | |
144 | extern "C" Dcomplex RTNAME(zpowk)(Dcomplex base, std::int64_t exp) { |
145 | auto cppbase = *(std::complex<double> *)(&base); |
146 | auto cppres = tgpowi(cppbase, exp); |
147 | return *(Dcomplex *)(&cppres); |
148 | } |
149 | |
150 | #if LDBL_MANT_DIG == 113 || HAS_FLOAT128 |
151 | struct Qcomplex { |
152 | CFloat128Type re; |
153 | CFloat128Type im; |
154 | }; |
155 | |
156 | extern "C" Dcomplex RTNAME(cqpowi)(Qcomplex base, std::int32_t exp) { |
157 | auto cppbase = *(std::complex<CFloat128Type> *)(&base); |
158 | auto cppres = tgpowi(cppbase, exp); |
159 | return *(Qcomplex *)(&cppres); |
160 | } |
161 | |
162 | extern "C" Dcomplex RTNAME(cqpowk)(Qcomplex base, std::int64_t exp) { |
163 | auto cppbase = *(std::complex<CFloat128Type> *)(&base); |
164 | auto cppres = tgpowi(cppbase, exp); |
165 | return *(Qcomplex *)(&cppres); |
166 | } |
167 | #endif |
168 | #endif |
169 | |