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 
22 namespace panda::ecmascript::kungfu {
23 
24 enum class TypeInfo {
25     NONE,
26     INT1,
27     INT32,
28     UINT32,
29     FLOAT64,
30     TAGGED,
31     CHAR,
32     HOLE_INT,
33     HOLE_DOUBLE,
34 };
35 
36 class UseInfo {
37 public:
UseInfo(uint8_t tag)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;
AddUse(const UseInfo &UseInfo)45     bool AddUse(const UseInfo &UseInfo)
46     {
47         uint8_t oldTag = tag_;
48         tag_ |= UseInfo.tag_;
49         return oldTag != tag_;
50     }
UsedAsBool() const51     bool UsedAsBool() const
52     {
53         return ((tag_ & BOOL) == BOOL);
54     }
UsedAsFloat64() const55     bool UsedAsFloat64() const
56     {
57         return ((tag_ & FLOAT64) == FLOAT64);
58     }
UsedAsNative() const59     bool UsedAsNative() const
60     {
61         return ((tag_ & NATIVE) != 0);
62     }
UsedAsTagged() const63     bool UsedAsTagged() const
64     {
65         return ((tag_ & TAGGED) != 0);
66     }
UnUsed()67     static UseInfo UnUsed()
68     {
69         return UseInfo(UNUSED);
70     }
BoolUse()71     static UseInfo BoolUse()
72     {
73         return UseInfo(BOOL);
74     }
Int32Use()75     static UseInfo Int32Use()
76     {
77         return UseInfo(INT32);
78     }
Float64Use()79     static UseInfo Float64Use()
80     {
81         return UseInfo(FLOAT64);
82     }
TaggedUse()83     static UseInfo TaggedUse()
84     {
85         return UseInfo(TAGGED);
86     }
87 private:
88     uint8_t tag_ {0};
89 };
90 
91 class RangeInfo {
92 public:
RangeInfo()93     RangeInfo() {}
RangeInfo(int32_t value)94     RangeInfo(int32_t value) : min_(value), max_(value) {}
RangeInfo(int32_t min, int32_t max)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 
NONE()116     static RangeInfo NONE()
117     {
118         return RangeInfo(INT32_MAX, INT32_MIN);
119     }
120 
ANY()121     static RangeInfo ANY()
122     {
123         return RangeInfo(INT32_MIN, INT32_MAX);
124     }
125 
GetMin() const126     int32_t GetMin() const
127     {
128         return min_;
129     }
130 
GetMax() const131     int32_t GetMax() const
132     {
133         return max_;
134     }
135 
operator ~() const136     RangeInfo operator~() const
137     {
138         return RangeInfo(~ max_, ~ min_);
139     }
140 
Union(const RangeInfo &rhs) const141     RangeInfo Union(const RangeInfo &rhs) const
142     {
143         return RangeInfo(std::min(min_, rhs.min_), std::max(max_, rhs.max_));
144     }
145 
intersection(const RangeInfo &rhs) const146     RangeInfo intersection(const RangeInfo &rhs) const
147     {
148         return RangeInfo(std::max(min_, rhs.min_), std::min(max_, rhs.max_));
149     }
150 
MaybeAddOverflow(const RangeInfo &rhs) const151     bool MaybeAddOverflow(const RangeInfo &rhs) const
152     {
153         return (rhs.max_ > 0) && (max_ > INT32_MAX - rhs.max_);
154     }
155 
MaybeAddUnderflow(const RangeInfo &rhs) const156     bool MaybeAddUnderflow(const RangeInfo &rhs) const
157     {
158         return (rhs.min_ < 0) && (min_ < INT32_MIN - rhs.min_);
159     }
160 
MaybeAddOverflowOrUnderflow(const RangeInfo &rhs) const161     bool MaybeAddOverflowOrUnderflow(const RangeInfo &rhs) const
162     {
163         return MaybeAddOverflow(rhs) || MaybeAddUnderflow(rhs);
164     }
165 
operator +(const RangeInfo &rhs) const166     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 
operator %(const RangeInfo &rhs) const174     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 
MaybeZero() const187     bool MaybeZero() const
188     {
189         return min_ <= 0 && max_ >= 0;
190     }
191 
MaybeNegative() const192     bool MaybeNegative() const
193     {
194         return min_ < 0;
195     }
196 
operator *(const RangeInfo &rhs) const197     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 
GetMaxMulResult(const RangeInfo &rhs) const205     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 
GetMinMulResult(const RangeInfo &rhs) const211     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 
TryMul(int32_t lhs, int32_t rhs) const217     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 
MaybeMulOverflowOrUnderflow(const RangeInfo &rhs) const228     bool MaybeMulOverflowOrUnderflow(const RangeInfo &rhs) const
229     {
230         return MaybeMulOverflow(rhs) || MaybeMulUnderflow(rhs);
231     }
232 
MaybeMulUnderflow(const RangeInfo &rhs) const233     bool MaybeMulUnderflow(const RangeInfo &rhs) const
234     {
235         return MaybeMulUnderflow(min_, rhs.max_) || MaybeMulUnderflow(max_, rhs.min_);
236     }
237 
MaybeMulOverflow(const RangeInfo &rhs) const238     bool MaybeMulOverflow(const RangeInfo &rhs) const
239     {
240         return MaybeMulOverflow(max_, rhs.max_) || MaybeMulOverflow(min_, rhs.min_);
241     }
242 
MaybeMulUnderflow(int32_t lhs, int32_t rhs) const243     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 
MaybeMulOverflow(int32_t lhs, int32_t rhs) const248     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 
MaybeSubOverflow(const RangeInfo &rhs) const253     bool MaybeSubOverflow(const RangeInfo &rhs) const
254     {
255         return (rhs.min_ < 0) && (max_ > INT32_MAX + rhs.min_);
256     }
257 
MaybeSubUnderflow(const RangeInfo &rhs) const258     bool MaybeSubUnderflow(const RangeInfo &rhs) const
259     {
260         return (rhs.max_ > 0) && (min_ < INT32_MIN + rhs.max_);
261     }
262 
MaybeSubOverflowOrUnderflow(const RangeInfo &rhs) const263     bool MaybeSubOverflowOrUnderflow(const RangeInfo &rhs) const
264     {
265         return MaybeSubOverflow(rhs) || MaybeSubUnderflow(rhs);
266     }
267 
operator -(const RangeInfo &rhs) const268     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 
MaybeShrOverflow(const RangeInfo &rhs) const276     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 
SHR(const RangeInfo &rhs) const284     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 
ASHR(const RangeInfo &rhs) const300     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 
operator ==(const RangeInfo &rhs) const310     bool operator== (const RangeInfo &rhs) const
311     {
312         return (min_ == rhs.min_) && (max_ == rhs.max_);
313     }
314 
operator !=(const RangeInfo &rhs) const315     bool operator!= (const RangeInfo &rhs) const
316     {
317         return (min_ != rhs.min_) || (max_ != rhs.max_);
318     }
319 
IsNone() const320     bool IsNone() const
321     {
322         return (min_ == INT32_MAX) && (max_ == INT32_MIN);
323     }
324 
325 private:
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