1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef ECMASCRIPT_NUMBER_GATE_INFO_H
17#define ECMASCRIPT_NUMBER_GATE_INFO_H
18#include "ecmascript/compiler/gate_accessor.h"
19#include "ecmascript/js_hclass.h"
20#include "ecmascript/js_typed_array.h"
21
22namespace panda::ecmascript::kungfu {
23
24enum class TypeInfo {
25    NONE,
26    INT1,
27    INT32,
28    UINT32,
29    FLOAT64,
30    TAGGED,
31    CHAR,
32    HOLE_INT,
33    HOLE_DOUBLE,
34};
35
36class UseInfo {
37public:
38    UseInfo(uint8_t tag) : tag_(tag) {}
39    static constexpr uint8_t UNUSED = 0;
40    static constexpr uint8_t BOOL = 1 << 0;
41    static constexpr uint8_t INT32 = 1 << 1;
42    static constexpr uint8_t FLOAT64 = 1 << 2;
43    static constexpr uint8_t NATIVE = BOOL | INT32 | FLOAT64;
44    static constexpr uint8_t TAGGED = 1 << 3;
45    bool AddUse(const UseInfo &UseInfo)
46    {
47        uint8_t oldTag = tag_;
48        tag_ |= UseInfo.tag_;
49        return oldTag != tag_;
50    }
51    bool UsedAsBool() const
52    {
53        return ((tag_ & BOOL) == BOOL);
54    }
55    bool UsedAsFloat64() const
56    {
57        return ((tag_ & FLOAT64) == FLOAT64);
58    }
59    bool UsedAsNative() const
60    {
61        return ((tag_ & NATIVE) != 0);
62    }
63    bool UsedAsTagged() const
64    {
65        return ((tag_ & TAGGED) != 0);
66    }
67    static UseInfo UnUsed()
68    {
69        return UseInfo(UNUSED);
70    }
71    static UseInfo BoolUse()
72    {
73        return UseInfo(BOOL);
74    }
75    static UseInfo Int32Use()
76    {
77        return UseInfo(INT32);
78    }
79    static UseInfo Float64Use()
80    {
81        return UseInfo(FLOAT64);
82    }
83    static UseInfo TaggedUse()
84    {
85        return UseInfo(TAGGED);
86    }
87private:
88    uint8_t tag_ {0};
89};
90
91class RangeInfo {
92public:
93    RangeInfo() {}
94    RangeInfo(int32_t value) : min_(value), max_(value) {}
95    RangeInfo(int32_t min, int32_t max)
96    {
97        if (min == max) {
98            min_ = max_ = min;
99        } else {
100            auto it = std::upper_bound(rangeBounds_.begin(), rangeBounds_.end(), min);
101            ASSERT(it != rangeBounds_.begin());
102            it--;
103            min_ = *it;
104            max_ = *std::lower_bound(rangeBounds_.begin(), rangeBounds_.end(), max);
105        }
106    }
107
108    static constexpr int32_t UINT30_MAX = 0x3fffffff;
109    static constexpr int32_t TYPED_ARRAY_ONHEAP_MAX = JSTypedArray::MAX_ONHEAP_LENGTH;
110    static constexpr int32_t UINT18_MAX = (1 << 18) - 1;
111    static const inline std::vector<int32_t> rangeBounds_ = {INT32_MIN, INT32_MIN + 1,
112        -UINT18_MAX, -TYPED_ARRAY_ONHEAP_MAX, -1, 0, 1, TYPED_ARRAY_ONHEAP_MAX - 1,
113        TYPED_ARRAY_ONHEAP_MAX, TYPED_ARRAY_ONHEAP_MAX + 1, TYPED_ARRAY_ONHEAP_MAX * 3,
114        UINT18_MAX, UINT30_MAX, UINT30_MAX + 1, INT32_MAX - 1, INT32_MAX };
115
116    static RangeInfo NONE()
117    {
118        return RangeInfo(INT32_MAX, INT32_MIN);
119    }
120
121    static RangeInfo ANY()
122    {
123        return RangeInfo(INT32_MIN, INT32_MAX);
124    }
125
126    int32_t GetMin() const
127    {
128        return min_;
129    }
130
131    int32_t GetMax() const
132    {
133        return max_;
134    }
135
136    RangeInfo operator~() const
137    {
138        return RangeInfo(~ max_, ~ min_);
139    }
140
141    RangeInfo Union(const RangeInfo &rhs) const
142    {
143        return RangeInfo(std::min(min_, rhs.min_), std::max(max_, rhs.max_));
144    }
145
146    RangeInfo intersection(const RangeInfo &rhs) const
147    {
148        return RangeInfo(std::max(min_, rhs.min_), std::min(max_, rhs.max_));
149    }
150
151    bool MaybeAddOverflow(const RangeInfo &rhs) const
152    {
153        return (rhs.max_ > 0) && (max_ > INT32_MAX - rhs.max_);
154    }
155
156    bool MaybeAddUnderflow(const RangeInfo &rhs) const
157    {
158        return (rhs.min_ < 0) && (min_ < INT32_MIN - rhs.min_);
159    }
160
161    bool MaybeAddOverflowOrUnderflow(const RangeInfo &rhs) const
162    {
163        return MaybeAddOverflow(rhs) || MaybeAddUnderflow(rhs);
164    }
165
166    RangeInfo operator+ (const RangeInfo &rhs) const
167    {
168        ASSERT(min_ <= max_ && rhs.min_ <= rhs.max_);
169        int32_t nmax = MaybeAddOverflow(rhs) ? INT32_MAX : max_ + rhs.max_;
170        int32_t nmin = MaybeAddUnderflow(rhs) ? INT32_MIN : min_ + rhs.min_;
171        return RangeInfo(nmin, nmax);
172    }
173
174    RangeInfo operator% (const RangeInfo &rhs) const
175    {
176        ASSERT(min_ <= max_ && rhs.min_ <= rhs.max_);
177        RangeInfo result = RangeInfo(0, 0);
178        int32_t nmax = std::max(std::abs(rhs.min_), std::abs(rhs.max_));
179        if (nmax == 0) {
180            return ANY();
181        }
182        if (max_ > 0) result = result.Union(RangeInfo(0, nmax - 1));
183        if (min_ < 0) result = result.Union(RangeInfo(-nmax + 1, 0));
184        return result;
185    }
186
187    bool MaybeZero() const
188    {
189        return min_ <= 0 && max_ >= 0;
190    }
191
192    bool MaybeNegative() const
193    {
194        return min_ < 0;
195    }
196
197    RangeInfo operator* (const RangeInfo &rhs) const
198    {
199        ASSERT(min_ <= max_ && rhs.min_ <= rhs.max_);
200        int32_t nmax = GetMaxMulResult(rhs);
201        int32_t nmin = GetMinMulResult(rhs);
202        return RangeInfo(nmin, nmax);
203    }
204
205    int32_t GetMaxMulResult(const RangeInfo &rhs) const
206    {
207        return std::max({ TryMul(min_, rhs.min_), TryMul(min_, rhs.max_),
208            TryMul(max_, rhs.min_), TryMul(max_, rhs.max_) });
209    }
210
211    int32_t GetMinMulResult(const RangeInfo &rhs) const
212    {
213        return std::min({ TryMul(min_, rhs.min_), TryMul(min_, rhs.max_),
214            TryMul(max_, rhs.min_), TryMul(max_, rhs.max_) });
215    }
216
217    int32_t TryMul(int32_t lhs, int32_t rhs) const
218    {
219        if (MaybeMulOverflow(lhs, rhs)) {
220            return INT32_MAX;
221        }
222        if (MaybeMulUnderflow(lhs, rhs)) {
223            return INT32_MIN;
224        }
225        return lhs * rhs;
226    }
227
228    bool MaybeMulOverflowOrUnderflow(const RangeInfo &rhs) const
229    {
230        return MaybeMulOverflow(rhs) || MaybeMulUnderflow(rhs);
231    }
232
233    bool MaybeMulUnderflow(const RangeInfo &rhs) const
234    {
235        return MaybeMulUnderflow(min_, rhs.max_) || MaybeMulUnderflow(max_, rhs.min_);
236    }
237
238    bool MaybeMulOverflow(const RangeInfo &rhs) const
239    {
240        return MaybeMulOverflow(max_, rhs.max_) || MaybeMulOverflow(min_, rhs.min_);
241    }
242
243    bool MaybeMulUnderflow(int32_t lhs, int32_t rhs) const
244    {
245        return (lhs > 0 && rhs < 0 && rhs < INT32_MIN / lhs) || (lhs < 0 && rhs > 0 && lhs < INT32_MIN / rhs);
246    }
247
248    bool MaybeMulOverflow(int32_t lhs, int32_t rhs) const
249    {
250        return (lhs > 0 && rhs > 0 && lhs > INT32_MAX / rhs) || (lhs < 0 && rhs < 0 && lhs < INT32_MAX / rhs);
251    }
252
253    bool MaybeSubOverflow(const RangeInfo &rhs) const
254    {
255        return (rhs.min_ < 0) && (max_ > INT32_MAX + rhs.min_);
256    }
257
258    bool MaybeSubUnderflow(const RangeInfo &rhs) const
259    {
260        return (rhs.max_ > 0) && (min_ < INT32_MIN + rhs.max_);
261    }
262
263    bool MaybeSubOverflowOrUnderflow(const RangeInfo &rhs) const
264    {
265        return MaybeSubOverflow(rhs) || MaybeSubUnderflow(rhs);
266    }
267
268    RangeInfo operator- (const RangeInfo &rhs) const
269    {
270        ASSERT(min_ <= max_ && rhs.min_ <= rhs.max_);
271        int32_t nmax = MaybeSubOverflow(rhs) ? INT32_MAX : max_ - rhs.min_;
272        int32_t nmin = MaybeSubUnderflow(rhs) ? INT32_MIN : min_ - rhs.max_;
273        return RangeInfo(nmin, nmax);
274    }
275
276    bool MaybeShrOverflow(const RangeInfo &rhs) const
277    {
278        if (rhs.max_ != rhs.min_) {
279            return true;
280        }
281        return ((static_cast<uint32_t>(rhs.max_) & 0x1f) == 0) && (min_< 0);   // 0x1f : shift bits
282    }
283
284    RangeInfo SHR(const RangeInfo &rhs) const
285    {
286        ASSERT(min_ <= max_);
287        ASSERT(rhs.max_ == rhs.min_);
288        if (MaybeShrOverflow(rhs)) {
289            // assume no overflow occurs since overflow will lead to deopt
290            return RangeInfo(0, std::max(0, GetMax()));
291        }
292        int32_t shift = rhs.max_ & 0x1f;    // 0x1f : shift bits
293        uint32_t tempMin = bit_cast<uint32_t>((max_ >= 0) ? std::max(0, min_) : min_);
294        uint32_t tempMax = bit_cast<uint32_t>((min_ < 0) ? std::min(-1, max_) : max_);
295        int32_t nmin = bit_cast<int32_t>(tempMin >> shift);
296        int32_t nmax = bit_cast<int32_t>(tempMax >> shift);
297        return RangeInfo(nmin, nmax);
298    }
299
300    RangeInfo ASHR(const RangeInfo &rhs) const
301    {
302        ASSERT(min_ <= max_);
303        ASSERT(rhs.max_ == rhs.min_);
304        int32_t shift = rhs.max_ & 0x1f;    // 0x1f : shift bits
305        int32_t nmin = min_ >> shift;
306        int32_t nmax = max_ >> shift;
307        return RangeInfo(nmin, nmax);
308    }
309
310    bool operator== (const RangeInfo &rhs) const
311    {
312        return (min_ == rhs.min_) && (max_ == rhs.max_);
313    }
314
315    bool operator!= (const RangeInfo &rhs) const
316    {
317        return (min_ != rhs.min_) || (max_ != rhs.max_);
318    }
319
320    bool IsNone() const
321    {
322        return (min_ == INT32_MAX) && (max_ == INT32_MIN);
323    }
324
325private:
326    int32_t min_ {INT32_MIN};
327    int32_t max_ {INT32_MAX};
328};
329}  // panda::ecmascript::kungfu
330#endif  // ECMASCRIPT_COMPILER_NUMBER_GATE_INFO_H
331