intrange.d revision 1.1.1.1 1 1.1 mrg /**
2 1.1 mrg * Implement $(LINK2 https://digitalmars.com/articles/b62.html, Value Range Propagation).
3 1.1 mrg *
4 1.1 mrg * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved
5 1.1 mrg * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
6 1.1 mrg * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 1.1 mrg * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/intrange.d, _intrange.d)
8 1.1 mrg * Documentation: https://dlang.org/phobos/dmd_intrange.html
9 1.1 mrg * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/intrange.d
10 1.1 mrg */
11 1.1 mrg
12 1.1 mrg module dmd.intrange;
13 1.1 mrg
14 1.1 mrg import core.stdc.stdio;
15 1.1 mrg
16 1.1 mrg import dmd.astenums;
17 1.1 mrg import dmd.mtype;
18 1.1 mrg import dmd.expression;
19 1.1 mrg import dmd.globals;
20 1.1 mrg
21 1.1 mrg private uinteger_t copySign(uinteger_t x, bool sign)
22 1.1 mrg {
23 1.1 mrg // return sign ? -x : x;
24 1.1 mrg return (x - cast(uinteger_t)sign) ^ -cast(uinteger_t)sign;
25 1.1 mrg }
26 1.1 mrg
27 1.1 mrg struct SignExtendedNumber
28 1.1 mrg {
29 1.1 mrg uinteger_t value;
30 1.1 mrg bool negative;
31 1.1 mrg
32 1.1 mrg static SignExtendedNumber fromInteger(uinteger_t value_)
33 1.1 mrg {
34 1.1 mrg return SignExtendedNumber(value_, value_ >> 63);
35 1.1 mrg }
36 1.1 mrg
37 1.1 mrg static SignExtendedNumber extreme(bool minimum)
38 1.1 mrg {
39 1.1 mrg return SignExtendedNumber(minimum - 1, minimum);
40 1.1 mrg }
41 1.1 mrg
42 1.1 mrg static SignExtendedNumber max()
43 1.1 mrg {
44 1.1 mrg return SignExtendedNumber(ulong.max, false);
45 1.1 mrg }
46 1.1 mrg
47 1.1 mrg static SignExtendedNumber min()
48 1.1 mrg {
49 1.1 mrg return SignExtendedNumber(0, true);
50 1.1 mrg }
51 1.1 mrg
52 1.1 mrg bool isMinimum() const
53 1.1 mrg {
54 1.1 mrg return negative && value == 0;
55 1.1 mrg }
56 1.1 mrg
57 1.1 mrg bool opEquals(const ref SignExtendedNumber a) const
58 1.1 mrg {
59 1.1 mrg return value == a.value && negative == a.negative;
60 1.1 mrg }
61 1.1 mrg
62 1.1 mrg int opCmp(const ref SignExtendedNumber a) const
63 1.1 mrg {
64 1.1 mrg if (negative != a.negative)
65 1.1 mrg {
66 1.1 mrg if (negative)
67 1.1 mrg return -1;
68 1.1 mrg else
69 1.1 mrg return 1;
70 1.1 mrg }
71 1.1 mrg if (value < a.value)
72 1.1 mrg return -1;
73 1.1 mrg else if (value > a.value)
74 1.1 mrg return 1;
75 1.1 mrg else
76 1.1 mrg return 0;
77 1.1 mrg }
78 1.1 mrg
79 1.1 mrg SignExtendedNumber opUnary(string op : "++")()
80 1.1 mrg {
81 1.1 mrg if (value != ulong.max)
82 1.1 mrg ++value;
83 1.1 mrg else if (negative)
84 1.1 mrg {
85 1.1 mrg value = 0;
86 1.1 mrg negative = false;
87 1.1 mrg }
88 1.1 mrg return this;
89 1.1 mrg }
90 1.1 mrg
91 1.1 mrg SignExtendedNumber opUnary(string op : "~")() const
92 1.1 mrg {
93 1.1 mrg if (~value == 0)
94 1.1 mrg return SignExtendedNumber(~value);
95 1.1 mrg else
96 1.1 mrg return SignExtendedNumber(~value, !negative);
97 1.1 mrg }
98 1.1 mrg
99 1.1 mrg SignExtendedNumber opUnary(string op : "-")() const
100 1.1 mrg {
101 1.1 mrg if (value == 0)
102 1.1 mrg return SignExtendedNumber(-cast(ulong)negative);
103 1.1 mrg else
104 1.1 mrg return SignExtendedNumber(-value, !negative);
105 1.1 mrg }
106 1.1 mrg
107 1.1 mrg SignExtendedNumber opBinary(string op : "&")(SignExtendedNumber rhs) const
108 1.1 mrg {
109 1.1 mrg return SignExtendedNumber(value & rhs.value);
110 1.1 mrg }
111 1.1 mrg
112 1.1 mrg SignExtendedNumber opBinary(string op : "|")(SignExtendedNumber rhs)
113 1.1 mrg {
114 1.1 mrg return SignExtendedNumber(value | rhs.value);
115 1.1 mrg }
116 1.1 mrg
117 1.1 mrg SignExtendedNumber opBinary(string op : "^")(SignExtendedNumber rhs)
118 1.1 mrg {
119 1.1 mrg return SignExtendedNumber(value ^ rhs.value);
120 1.1 mrg }
121 1.1 mrg
122 1.1 mrg SignExtendedNumber opBinary(string op : "+")(SignExtendedNumber rhs)
123 1.1 mrg {
124 1.1 mrg uinteger_t sum = value + rhs.value;
125 1.1 mrg bool carry = sum < value && sum < rhs.value;
126 1.1 mrg if (negative != rhs.negative)
127 1.1 mrg return SignExtendedNumber(sum, !carry);
128 1.1 mrg else if (negative)
129 1.1 mrg return SignExtendedNumber(carry ? sum : 0, true);
130 1.1 mrg else
131 1.1 mrg return SignExtendedNumber(carry ? ulong.max : sum, false);
132 1.1 mrg }
133 1.1 mrg
134 1.1 mrg
135 1.1 mrg SignExtendedNumber opBinary(string op : "-")(SignExtendedNumber rhs)
136 1.1 mrg {
137 1.1 mrg if (rhs.isMinimum())
138 1.1 mrg return negative ? SignExtendedNumber(value, false) : max();
139 1.1 mrg else
140 1.1 mrg return this + (-rhs);
141 1.1 mrg }
142 1.1 mrg
143 1.1 mrg SignExtendedNumber opBinary(string op : "*")(SignExtendedNumber rhs)
144 1.1 mrg {
145 1.1 mrg // perform *saturated* multiplication, otherwise we may get bogus ranges
146 1.1 mrg // like 0x10 * 0x10 == 0x100 == 0.
147 1.1 mrg
148 1.1 mrg /* Special handling for zeros:
149 1.1 mrg INT65_MIN * 0 = 0
150 1.1 mrg INT65_MIN * + = INT65_MIN
151 1.1 mrg INT65_MIN * - = INT65_MAX
152 1.1 mrg 0 * anything = 0
153 1.1 mrg */
154 1.1 mrg if (value == 0)
155 1.1 mrg {
156 1.1 mrg if (!negative)
157 1.1 mrg return this;
158 1.1 mrg else if (rhs.negative)
159 1.1 mrg return max();
160 1.1 mrg else
161 1.1 mrg return rhs.value == 0 ? rhs : this;
162 1.1 mrg }
163 1.1 mrg else if (rhs.value == 0)
164 1.1 mrg return rhs * this; // don't duplicate the symmetric case.
165 1.1 mrg
166 1.1 mrg SignExtendedNumber rv;
167 1.1 mrg // these are != 0 now surely.
168 1.1 mrg uinteger_t tAbs = copySign(value, negative);
169 1.1 mrg uinteger_t aAbs = copySign(rhs.value, rhs.negative);
170 1.1 mrg rv.negative = negative != rhs.negative;
171 1.1 mrg if (ulong.max / tAbs < aAbs)
172 1.1 mrg rv.value = rv.negative - 1;
173 1.1 mrg else
174 1.1 mrg rv.value = copySign(tAbs * aAbs, rv.negative);
175 1.1 mrg return rv;
176 1.1 mrg }
177 1.1 mrg
178 1.1 mrg SignExtendedNumber opBinary(string op : "/")(SignExtendedNumber rhs)
179 1.1 mrg {
180 1.1 mrg /* special handling for zeros:
181 1.1 mrg INT65_MIN / INT65_MIN = 1
182 1.1 mrg anything / INT65_MIN = 0
183 1.1 mrg + / 0 = INT65_MAX (eh?)
184 1.1 mrg - / 0 = INT65_MIN (eh?)
185 1.1 mrg */
186 1.1 mrg if (rhs.value == 0)
187 1.1 mrg {
188 1.1 mrg if (rhs.negative)
189 1.1 mrg return SignExtendedNumber(value == 0 && negative);
190 1.1 mrg else
191 1.1 mrg return extreme(negative);
192 1.1 mrg }
193 1.1 mrg
194 1.1 mrg uinteger_t aAbs = copySign(rhs.value, rhs.negative);
195 1.1 mrg uinteger_t rvVal;
196 1.1 mrg
197 1.1 mrg if (!isMinimum())
198 1.1 mrg rvVal = copySign(value, negative) / aAbs;
199 1.1 mrg // Special handling for INT65_MIN
200 1.1 mrg // if the denominator is not a power of 2, it is same as ulong.max / x.
201 1.1 mrg else if (aAbs & (aAbs - 1))
202 1.1 mrg rvVal = ulong.max / aAbs;
203 1.1 mrg // otherwise, it's the same as reversing the bits of x.
204 1.1 mrg else
205 1.1 mrg {
206 1.1 mrg if (aAbs == 1)
207 1.1 mrg return extreme(!rhs.negative);
208 1.1 mrg rvVal = 1UL << 63;
209 1.1 mrg aAbs >>= 1;
210 1.1 mrg if (aAbs & 0xAAAAAAAAAAAAAAAAUL) rvVal >>= 1;
211 1.1 mrg if (aAbs & 0xCCCCCCCCCCCCCCCCUL) rvVal >>= 2;
212 1.1 mrg if (aAbs & 0xF0F0F0F0F0F0F0F0UL) rvVal >>= 4;
213 1.1 mrg if (aAbs & 0xFF00FF00FF00FF00UL) rvVal >>= 8;
214 1.1 mrg if (aAbs & 0xFFFF0000FFFF0000UL) rvVal >>= 16;
215 1.1 mrg if (aAbs & 0xFFFFFFFF00000000UL) rvVal >>= 32;
216 1.1 mrg }
217 1.1 mrg bool rvNeg = negative != rhs.negative;
218 1.1 mrg rvVal = copySign(rvVal, rvNeg);
219 1.1 mrg
220 1.1 mrg return SignExtendedNumber(rvVal, rvVal != 0 && rvNeg);
221 1.1 mrg }
222 1.1 mrg
223 1.1 mrg SignExtendedNumber opBinary(string op : "%")(SignExtendedNumber rhs)
224 1.1 mrg {
225 1.1 mrg if (rhs.value == 0)
226 1.1 mrg return !rhs.negative ? rhs : isMinimum() ? SignExtendedNumber(0) : this;
227 1.1 mrg
228 1.1 mrg uinteger_t aAbs = copySign(rhs.value, rhs.negative);
229 1.1 mrg uinteger_t rvVal;
230 1.1 mrg
231 1.1 mrg // a % b == sgn(a) * abs(a) % abs(b).
232 1.1 mrg if (!isMinimum())
233 1.1 mrg rvVal = copySign(value, negative) % aAbs;
234 1.1 mrg // Special handling for INT65_MIN
235 1.1 mrg // if the denominator is not a power of 2, it is same as ulong.max % x + 1.
236 1.1 mrg else if (aAbs & (aAbs - 1))
237 1.1 mrg rvVal = ulong.max % aAbs + 1;
238 1.1 mrg // otherwise, the modulus is trivially zero.
239 1.1 mrg else
240 1.1 mrg rvVal = 0;
241 1.1 mrg
242 1.1 mrg rvVal = copySign(rvVal, negative);
243 1.1 mrg return SignExtendedNumber(rvVal, rvVal != 0 && negative);
244 1.1 mrg }
245 1.1 mrg
246 1.1 mrg SignExtendedNumber opBinary(string op : "<<")(SignExtendedNumber rhs)
247 1.1 mrg {
248 1.1 mrg // assume left-shift the shift-amount is always unsigned. Thus negative
249 1.1 mrg // shifts will give huge result.
250 1.1 mrg if (value == 0)
251 1.1 mrg return this;
252 1.1 mrg else if (rhs.negative)
253 1.1 mrg return extreme(negative);
254 1.1 mrg
255 1.1 mrg uinteger_t v = copySign(value, negative);
256 1.1 mrg
257 1.1 mrg // compute base-2 log of 'v' to determine the maximum allowed bits to shift.
258 1.1 mrg // Ref: https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
259 1.1 mrg
260 1.1 mrg // Why is this a size_t? Looks like a bug.
261 1.1 mrg size_t r, s;
262 1.1 mrg
263 1.1 mrg r = (v > 0xFFFFFFFFUL) << 5; v >>= r;
264 1.1 mrg s = (v > 0xFFFFUL ) << 4; v >>= s; r |= s;
265 1.1 mrg s = (v > 0xFFUL ) << 3; v >>= s; r |= s;
266 1.1 mrg s = (v > 0xFUL ) << 2; v >>= s; r |= s;
267 1.1 mrg s = (v > 0x3UL ) << 1; v >>= s; r |= s;
268 1.1 mrg r |= (v >> 1);
269 1.1 mrg
270 1.1 mrg uinteger_t allowableShift = 63 - r;
271 1.1 mrg if (rhs.value > allowableShift)
272 1.1 mrg return extreme(negative);
273 1.1 mrg else
274 1.1 mrg return SignExtendedNumber(value << rhs.value, negative);
275 1.1 mrg }
276 1.1 mrg
277 1.1 mrg SignExtendedNumber opBinary(string op : ">>")(SignExtendedNumber rhs)
278 1.1 mrg {
279 1.1 mrg if (rhs.negative || rhs.value > 63)
280 1.1 mrg return negative ? SignExtendedNumber(-1, true) : SignExtendedNumber(0);
281 1.1 mrg else if (isMinimum())
282 1.1 mrg return rhs.value == 0 ? this : SignExtendedNumber(-1UL << (64 - rhs.value), true);
283 1.1 mrg
284 1.1 mrg uinteger_t x = value ^ -cast(int)negative;
285 1.1 mrg x >>= rhs.value;
286 1.1 mrg return SignExtendedNumber(x ^ -cast(int)negative, negative);
287 1.1 mrg }
288 1.1 mrg
289 1.1 mrg SignExtendedNumber opBinary(string op : "^^")(SignExtendedNumber rhs)
290 1.1 mrg {
291 1.1 mrg // Not yet implemented
292 1.1 mrg assert(0);
293 1.1 mrg }
294 1.1 mrg }
295 1.1 mrg
296 1.1 mrg struct IntRange
297 1.1 mrg {
298 1.1 mrg SignExtendedNumber imin, imax;
299 1.1 mrg
300 1.1 mrg this(IntRange another)
301 1.1 mrg {
302 1.1 mrg imin = another.imin;
303 1.1 mrg imax = another.imax;
304 1.1 mrg }
305 1.1 mrg
306 1.1 mrg this(SignExtendedNumber a)
307 1.1 mrg {
308 1.1 mrg imin = a;
309 1.1 mrg imax = a;
310 1.1 mrg }
311 1.1 mrg
312 1.1 mrg this(SignExtendedNumber lower, SignExtendedNumber upper)
313 1.1 mrg {
314 1.1 mrg imin = lower;
315 1.1 mrg imax = upper;
316 1.1 mrg }
317 1.1 mrg
318 1.1 mrg static IntRange fromType(Type type)
319 1.1 mrg {
320 1.1 mrg return fromType(type, type.isunsigned());
321 1.1 mrg }
322 1.1 mrg
323 1.1 mrg static IntRange fromType(Type type, bool isUnsigned)
324 1.1 mrg {
325 1.1 mrg if (!type.isintegral() || type.toBasetype().ty == Tvector)
326 1.1 mrg return widest();
327 1.1 mrg
328 1.1 mrg uinteger_t mask = type.sizemask();
329 1.1 mrg auto lower = SignExtendedNumber(0);
330 1.1 mrg auto upper = SignExtendedNumber(mask);
331 1.1 mrg if (type.toBasetype().ty == Tdchar)
332 1.1 mrg upper.value = 0x10FFFFUL;
333 1.1 mrg else if (!isUnsigned)
334 1.1 mrg {
335 1.1 mrg lower.value = ~(mask >> 1);
336 1.1 mrg lower.negative = true;
337 1.1 mrg upper.value = (mask >> 1);
338 1.1 mrg }
339 1.1 mrg return IntRange(lower, upper);
340 1.1 mrg }
341 1.1 mrg
342 1.1 mrg static IntRange fromNumbers2(SignExtendedNumber* numbers)
343 1.1 mrg {
344 1.1 mrg if (numbers[0] < numbers[1])
345 1.1 mrg return IntRange(numbers[0], numbers[1]);
346 1.1 mrg else
347 1.1 mrg return IntRange(numbers[1], numbers[0]);
348 1.1 mrg }
349 1.1 mrg
350 1.1 mrg static IntRange fromNumbers4(SignExtendedNumber* numbers)
351 1.1 mrg {
352 1.1 mrg IntRange ab = fromNumbers2(numbers);
353 1.1 mrg IntRange cd = fromNumbers2(numbers + 2);
354 1.1 mrg if (cd.imin < ab.imin)
355 1.1 mrg ab.imin = cd.imin;
356 1.1 mrg if (cd.imax > ab.imax)
357 1.1 mrg ab.imax = cd.imax;
358 1.1 mrg return ab;
359 1.1 mrg }
360 1.1 mrg
361 1.1 mrg static IntRange widest()
362 1.1 mrg {
363 1.1 mrg return IntRange(SignExtendedNumber.min(), SignExtendedNumber.max());
364 1.1 mrg }
365 1.1 mrg
366 1.1 mrg IntRange castSigned(uinteger_t mask)
367 1.1 mrg {
368 1.1 mrg // .... 0x1e7f ] [0x1e80 .. 0x1f7f] [0x1f80 .. 0x7f] [0x80 .. 0x17f] [0x180 ....
369 1.1 mrg //
370 1.1 mrg // regular signed type. We use a technique similar to the unsigned version,
371 1.1 mrg // but the chunk has to be offset by 1/2 of the range.
372 1.1 mrg uinteger_t halfChunkMask = mask >> 1;
373 1.1 mrg uinteger_t minHalfChunk = imin.value & ~halfChunkMask;
374 1.1 mrg uinteger_t maxHalfChunk = imax.value & ~halfChunkMask;
375 1.1 mrg int minHalfChunkNegativity = imin.negative; // 1 = neg, 0 = nonneg, -1 = chunk containing ::max
376 1.1 mrg int maxHalfChunkNegativity = imax.negative;
377 1.1 mrg if (minHalfChunk & mask)
378 1.1 mrg {
379 1.1 mrg minHalfChunk += halfChunkMask + 1;
380 1.1 mrg if (minHalfChunk == 0)
381 1.1 mrg --minHalfChunkNegativity;
382 1.1 mrg }
383 1.1 mrg if (maxHalfChunk & mask)
384 1.1 mrg {
385 1.1 mrg maxHalfChunk += halfChunkMask + 1;
386 1.1 mrg if (maxHalfChunk == 0)
387 1.1 mrg --maxHalfChunkNegativity;
388 1.1 mrg }
389 1.1 mrg if (minHalfChunk == maxHalfChunk && minHalfChunkNegativity == maxHalfChunkNegativity)
390 1.1 mrg {
391 1.1 mrg imin.value &= mask;
392 1.1 mrg imax.value &= mask;
393 1.1 mrg // sign extend if necessary.
394 1.1 mrg imin.negative = (imin.value & ~halfChunkMask) != 0;
395 1.1 mrg imax.negative = (imax.value & ~halfChunkMask) != 0;
396 1.1 mrg halfChunkMask += 1;
397 1.1 mrg imin.value = (imin.value ^ halfChunkMask) - halfChunkMask;
398 1.1 mrg imax.value = (imax.value ^ halfChunkMask) - halfChunkMask;
399 1.1 mrg }
400 1.1 mrg else
401 1.1 mrg {
402 1.1 mrg imin = SignExtendedNumber(~halfChunkMask, true);
403 1.1 mrg imax = SignExtendedNumber(halfChunkMask, false);
404 1.1 mrg }
405 1.1 mrg return this;
406 1.1 mrg }
407 1.1 mrg
408 1.1 mrg IntRange castUnsigned(uinteger_t mask)
409 1.1 mrg {
410 1.1 mrg // .... 0x1eff ] [0x1f00 .. 0x1fff] [0 .. 0xff] [0x100 .. 0x1ff] [0x200 ....
411 1.1 mrg //
412 1.1 mrg // regular unsigned type. We just need to see if ir steps across the
413 1.1 mrg // boundary of validRange. If yes, ir will represent the whole validRange,
414 1.1 mrg // otherwise, we just take the modulus.
415 1.1 mrg // e.g. [0x105, 0x107] & 0xff == [5, 7]
416 1.1 mrg // [0x105, 0x207] & 0xff == [0, 0xff]
417 1.1 mrg uinteger_t minChunk = imin.value & ~mask;
418 1.1 mrg uinteger_t maxChunk = imax.value & ~mask;
419 1.1 mrg if (minChunk == maxChunk && imin.negative == imax.negative)
420 1.1 mrg {
421 1.1 mrg imin.value &= mask;
422 1.1 mrg imax.value &= mask;
423 1.1 mrg }
424 1.1 mrg else
425 1.1 mrg {
426 1.1 mrg imin.value = 0;
427 1.1 mrg imax.value = mask;
428 1.1 mrg }
429 1.1 mrg imin.negative = imax.negative = false;
430 1.1 mrg return this;
431 1.1 mrg }
432 1.1 mrg
433 1.1 mrg IntRange castDchar()
434 1.1 mrg {
435 1.1 mrg // special case for dchar. Casting to dchar means "I'll ignore all
436 1.1 mrg // invalid characters."
437 1.1 mrg castUnsigned(0xFFFFFFFFUL);
438 1.1 mrg if (imin.value > 0x10FFFFUL) // ??
439 1.1 mrg imin.value = 0x10FFFFUL; // ??
440 1.1 mrg if (imax.value > 0x10FFFFUL)
441 1.1 mrg imax.value = 0x10FFFFUL;
442 1.1 mrg return this;
443 1.1 mrg }
444 1.1 mrg
445 1.1 mrg IntRange _cast(Type type)
446 1.1 mrg {
447 1.1 mrg if (!type.isintegral() || type.toBasetype().ty == Tvector)
448 1.1 mrg return this;
449 1.1 mrg else if (!type.isunsigned())
450 1.1 mrg return castSigned(type.sizemask());
451 1.1 mrg else if (type.toBasetype().ty == Tdchar)
452 1.1 mrg return castDchar();
453 1.1 mrg else
454 1.1 mrg return castUnsigned(type.sizemask());
455 1.1 mrg }
456 1.1 mrg
457 1.1 mrg IntRange castUnsigned(Type type)
458 1.1 mrg {
459 1.1 mrg if (!type.isintegral() || type.toBasetype().ty == Tvector)
460 1.1 mrg return castUnsigned(ulong.max);
461 1.1 mrg else if (type.toBasetype().ty == Tdchar)
462 1.1 mrg return castDchar();
463 1.1 mrg else
464 1.1 mrg return castUnsigned(type.sizemask());
465 1.1 mrg }
466 1.1 mrg
467 1.1 mrg bool contains(IntRange a)
468 1.1 mrg {
469 1.1 mrg return imin <= a.imin && imax >= a.imax;
470 1.1 mrg }
471 1.1 mrg
472 1.1 mrg bool containsZero() const
473 1.1 mrg {
474 1.1 mrg return (imin.negative && !imax.negative)
475 1.1 mrg || (!imin.negative && imin.value == 0);
476 1.1 mrg }
477 1.1 mrg
478 1.1 mrg IntRange absNeg() const
479 1.1 mrg {
480 1.1 mrg if (imax.negative)
481 1.1 mrg return this;
482 1.1 mrg else if (!imin.negative)
483 1.1 mrg return IntRange(-imax, -imin);
484 1.1 mrg else
485 1.1 mrg {
486 1.1 mrg SignExtendedNumber imaxAbsNeg = -imax;
487 1.1 mrg return IntRange(imaxAbsNeg < imin ? imaxAbsNeg : imin,
488 1.1 mrg SignExtendedNumber(0));
489 1.1 mrg }
490 1.1 mrg }
491 1.1 mrg
492 1.1 mrg IntRange unionWith(const ref IntRange other) const
493 1.1 mrg {
494 1.1 mrg return IntRange(imin < other.imin ? imin : other.imin,
495 1.1 mrg imax > other.imax ? imax : other.imax);
496 1.1 mrg }
497 1.1 mrg
498 1.1 mrg void unionOrAssign(IntRange other, ref bool union_)
499 1.1 mrg {
500 1.1 mrg if (!union_ || imin > other.imin)
501 1.1 mrg imin = other.imin;
502 1.1 mrg if (!union_ || imax < other.imax)
503 1.1 mrg imax = other.imax;
504 1.1 mrg union_ = true;
505 1.1 mrg }
506 1.1 mrg
507 1.1 mrg ref const(IntRange) dump(const(char)* funcName, Expression e) const return
508 1.1 mrg {
509 1.1 mrg printf("[(%c)%#018llx, (%c)%#018llx] @ %s ::: %s\n",
510 1.1 mrg imin.negative?'-':'+', cast(ulong)imin.value,
511 1.1 mrg imax.negative?'-':'+', cast(ulong)imax.value,
512 1.1 mrg funcName, e.toChars());
513 1.1 mrg return this;
514 1.1 mrg }
515 1.1 mrg
516 1.1 mrg void splitBySign(ref IntRange negRange, ref bool hasNegRange, ref IntRange nonNegRange, ref bool hasNonNegRange) const
517 1.1 mrg {
518 1.1 mrg hasNegRange = imin.negative;
519 1.1 mrg if (hasNegRange)
520 1.1 mrg {
521 1.1 mrg negRange.imin = imin;
522 1.1 mrg negRange.imax = imax.negative ? imax : SignExtendedNumber(-1, true);
523 1.1 mrg }
524 1.1 mrg hasNonNegRange = !imax.negative;
525 1.1 mrg if (hasNonNegRange)
526 1.1 mrg {
527 1.1 mrg nonNegRange.imin = imin.negative ? SignExtendedNumber(0) : imin;
528 1.1 mrg nonNegRange.imax = imax;
529 1.1 mrg }
530 1.1 mrg }
531 1.1 mrg
532 1.1 mrg IntRange opUnary(string op:"~")() const
533 1.1 mrg {
534 1.1 mrg return IntRange(~imax, ~imin);
535 1.1 mrg }
536 1.1 mrg
537 1.1 mrg IntRange opUnary(string op : "-")()
538 1.1 mrg {
539 1.1 mrg return IntRange(-imax, -imin);
540 1.1 mrg }
541 1.1 mrg
542 1.1 mrg // Credits to Timon Gehr for the algorithms for &, |
543 1.1 mrg // https://github.com/tgehr/d-compiler/blob/master/vrange.d
544 1.1 mrg IntRange opBinary(string op : "&")(IntRange rhs) const
545 1.1 mrg {
546 1.1 mrg // unsigned or identical sign bits
547 1.1 mrg if ((imin.negative ^ imax.negative) != 1 && (rhs.imin.negative ^ rhs.imax.negative) != 1)
548 1.1 mrg {
549 1.1 mrg return IntRange(minAnd(this, rhs), maxAnd(this, rhs));
550 1.1 mrg }
551 1.1 mrg
552 1.1 mrg IntRange l = IntRange(this);
553 1.1 mrg IntRange r = IntRange(rhs);
554 1.1 mrg
555 1.1 mrg // both intervals span [-1,0]
556 1.1 mrg if ((imin.negative ^ imax.negative) == 1 && (rhs.imin.negative ^ rhs.imax.negative) == 1)
557 1.1 mrg {
558 1.1 mrg // cannot be larger than either l.max or r.max, set the other one to -1
559 1.1 mrg SignExtendedNumber max = l.imax.value > r.imax.value ? l.imax : r.imax;
560 1.1 mrg
561 1.1 mrg // only negative numbers for minimum
562 1.1 mrg l.imax.value = -1;
563 1.1 mrg l.imax.negative = true;
564 1.1 mrg r.imax.value = -1;
565 1.1 mrg r.imax.negative = true;
566 1.1 mrg
567 1.1 mrg return IntRange(minAnd(l, r), max);
568 1.1 mrg }
569 1.1 mrg else
570 1.1 mrg {
571 1.1 mrg // only one interval spans [-1,0]
572 1.1 mrg if ((l.imin.negative ^ l.imax.negative) == 1)
573 1.1 mrg {
574 1.1 mrg swap(l, r); // r spans [-1,0]
575 1.1 mrg }
576 1.1 mrg
577 1.1 mrg auto minAndNeg = minAnd(l, IntRange(r.imin, SignExtendedNumber(-1)));
578 1.1 mrg auto minAndPos = minAnd(l, IntRange(SignExtendedNumber(0), r.imax));
579 1.1 mrg auto maxAndNeg = maxAnd(l, IntRange(r.imin, SignExtendedNumber(-1)));
580 1.1 mrg auto maxAndPos = maxAnd(l, IntRange(SignExtendedNumber(0), r.imax));
581 1.1 mrg
582 1.1 mrg auto min = minAndNeg < minAndPos ? minAndNeg : minAndPos;
583 1.1 mrg auto max = maxAndNeg > maxAndPos ? maxAndNeg : maxAndPos;
584 1.1 mrg
585 1.1 mrg auto range = IntRange(min, max);
586 1.1 mrg return range;
587 1.1 mrg }
588 1.1 mrg }
589 1.1 mrg
590 1.1 mrg // Credits to Timon Gehr for the algorithms for &, |
591 1.1 mrg // https://github.com/tgehr/d-compiler/blob/master/vrange.d
592 1.1 mrg IntRange opBinary(string op : "|")(IntRange rhs) const
593 1.1 mrg {
594 1.1 mrg // unsigned or identical sign bits:
595 1.1 mrg if ((imin.negative ^ imax.negative) == 0 && (rhs.imin.negative ^ rhs.imax.negative) == 0)
596 1.1 mrg {
597 1.1 mrg return IntRange(minOr(this, rhs), maxOr(this, rhs));
598 1.1 mrg }
599 1.1 mrg
600 1.1 mrg IntRange l = IntRange(this);
601 1.1 mrg IntRange r = IntRange(rhs);
602 1.1 mrg
603 1.1 mrg // both intervals span [-1,0]
604 1.1 mrg if ((imin.negative ^ imax.negative) == 1 && (rhs.imin.negative ^ rhs.imax.negative) == 1)
605 1.1 mrg {
606 1.1 mrg // cannot be smaller than either l.min or r.min, set the other one to 0
607 1.1 mrg SignExtendedNumber min = l.imin.value < r.imin.value ? l.imin : r.imin;
608 1.1 mrg
609 1.1 mrg // only negative numbers for minimum
610 1.1 mrg l.imin.value = 0;
611 1.1 mrg l.imin.negative = false;
612 1.1 mrg r.imin.value = 0;
613 1.1 mrg r.imin.negative = false;
614 1.1 mrg
615 1.1 mrg return IntRange(min, maxOr(l, r));
616 1.1 mrg }
617 1.1 mrg else
618 1.1 mrg {
619 1.1 mrg // only one interval spans [-1,0]
620 1.1 mrg if ((imin.negative ^ imax.negative) == 1)
621 1.1 mrg {
622 1.1 mrg swap(l, r); // r spans [-1,0]
623 1.1 mrg }
624 1.1 mrg
625 1.1 mrg auto minOrNeg = minOr(l, IntRange(r.imin, SignExtendedNumber(-1)));
626 1.1 mrg auto minOrPos = minOr(l, IntRange(SignExtendedNumber(0), r.imax));
627 1.1 mrg auto maxOrNeg = maxOr(l, IntRange(r.imin, SignExtendedNumber(-1)));
628 1.1 mrg auto maxOrPos = maxOr(l, IntRange(SignExtendedNumber(0), r.imax));
629 1.1 mrg
630 1.1 mrg auto min = minOrNeg < minOrPos ? minOrNeg : minOrPos;
631 1.1 mrg auto max = maxOrNeg > maxOrPos ? maxOrNeg : maxOrPos;
632 1.1 mrg
633 1.1 mrg auto range = IntRange(min, max);
634 1.1 mrg return range;
635 1.1 mrg }
636 1.1 mrg }
637 1.1 mrg
638 1.1 mrg IntRange opBinary(string op : "^")(IntRange rhs) const
639 1.1 mrg {
640 1.1 mrg return this & ~rhs | ~this & rhs;
641 1.1 mrg }
642 1.1 mrg
643 1.1 mrg IntRange opBinary(string op : "+")(IntRange rhs)
644 1.1 mrg {
645 1.1 mrg return IntRange(imin + rhs.imin, imax + rhs.imax);
646 1.1 mrg }
647 1.1 mrg
648 1.1 mrg IntRange opBinary(string op : "-")(IntRange rhs)
649 1.1 mrg {
650 1.1 mrg return IntRange(imin - rhs.imax, imax - rhs.imin);
651 1.1 mrg }
652 1.1 mrg
653 1.1 mrg IntRange opBinary(string op : "*")(IntRange rhs)
654 1.1 mrg {
655 1.1 mrg // [a,b] * [c,d] = [min (ac, ad, bc, bd), max (ac, ad, bc, bd)]
656 1.1 mrg SignExtendedNumber[4] bdy;
657 1.1 mrg bdy[0] = imin * rhs.imin;
658 1.1 mrg bdy[1] = imin * rhs.imax;
659 1.1 mrg bdy[2] = imax * rhs.imin;
660 1.1 mrg bdy[3] = imax * rhs.imax;
661 1.1 mrg return IntRange.fromNumbers4(bdy.ptr);
662 1.1 mrg }
663 1.1 mrg
664 1.1 mrg IntRange opBinary(string op : "/")(IntRange rhs)
665 1.1 mrg {
666 1.1 mrg // Handle divide by 0
667 1.1 mrg if (rhs.imax.value == 0 && rhs.imin.value == 0)
668 1.1 mrg return widest();
669 1.1 mrg
670 1.1 mrg // Don't treat the whole range as divide by 0 if only one end of a range is 0.
671 1.1 mrg // Issue 15289
672 1.1 mrg if (rhs.imax.value == 0)
673 1.1 mrg {
674 1.1 mrg rhs.imax.value--;
675 1.1 mrg }
676 1.1 mrg else if(rhs.imin.value == 0)
677 1.1 mrg {
678 1.1 mrg rhs.imin.value++;
679 1.1 mrg }
680 1.1 mrg
681 1.1 mrg if (!imin.negative && !imax.negative && !rhs.imin.negative && !rhs.imax.negative)
682 1.1 mrg {
683 1.1 mrg return IntRange(imin / rhs.imax, imax / rhs.imin);
684 1.1 mrg }
685 1.1 mrg else
686 1.1 mrg {
687 1.1 mrg // [a,b] / [c,d] = [min (a/c, a/d, b/c, b/d), max (a/c, a/d, b/c, b/d)]
688 1.1 mrg SignExtendedNumber[4] bdy;
689 1.1 mrg bdy[0] = imin / rhs.imin;
690 1.1 mrg bdy[1] = imin / rhs.imax;
691 1.1 mrg bdy[2] = imax / rhs.imin;
692 1.1 mrg bdy[3] = imax / rhs.imax;
693 1.1 mrg
694 1.1 mrg return IntRange.fromNumbers4(bdy.ptr);
695 1.1 mrg }
696 1.1 mrg }
697 1.1 mrg
698 1.1 mrg IntRange opBinary(string op : "%")(IntRange rhs)
699 1.1 mrg {
700 1.1 mrg IntRange irNum = this;
701 1.1 mrg IntRange irDen = rhs.absNeg();
702 1.1 mrg
703 1.1 mrg /*
704 1.1 mrg due to the rules of D (C)'s % operator, we need to consider the cases
705 1.1 mrg separately in different range of signs.
706 1.1 mrg
707 1.1 mrg case 1. [500, 1700] % [7, 23] (numerator is always positive)
708 1.1 mrg = [0, 22]
709 1.1 mrg case 2. [-500, 1700] % [7, 23] (numerator can be negative)
710 1.1 mrg = [-22, 22]
711 1.1 mrg case 3. [-1700, -500] % [7, 23] (numerator is always negative)
712 1.1 mrg = [-22, 0]
713 1.1 mrg
714 1.1 mrg the number 22 is the maximum absolute value in the denomator's range. We
715 1.1 mrg don't care about divide by zero.
716 1.1 mrg */
717 1.1 mrg
718 1.1 mrg irDen.imin = irDen.imin + SignExtendedNumber(1);
719 1.1 mrg irDen.imax = -irDen.imin;
720 1.1 mrg
721 1.1 mrg if (!irNum.imin.negative)
722 1.1 mrg {
723 1.1 mrg irNum.imin.value = 0;
724 1.1 mrg }
725 1.1 mrg else if (irNum.imin < irDen.imin)
726 1.1 mrg {
727 1.1 mrg irNum.imin = irDen.imin;
728 1.1 mrg }
729 1.1 mrg
730 1.1 mrg if (irNum.imax.negative)
731 1.1 mrg {
732 1.1 mrg irNum.imax.negative = false;
733 1.1 mrg irNum.imax.value = 0;
734 1.1 mrg }
735 1.1 mrg else if (irNum.imax > irDen.imax)
736 1.1 mrg {
737 1.1 mrg irNum.imax = irDen.imax;
738 1.1 mrg }
739 1.1 mrg
740 1.1 mrg return irNum;
741 1.1 mrg }
742 1.1 mrg
743 1.1 mrg IntRange opBinary(string op : "<<")(IntRange rhs)
744 1.1 mrg {
745 1.1 mrg if (rhs.imin.negative)
746 1.1 mrg {
747 1.1 mrg rhs = IntRange(SignExtendedNumber(0), SignExtendedNumber(64));
748 1.1 mrg }
749 1.1 mrg
750 1.1 mrg SignExtendedNumber lower = imin << (imin.negative ? rhs.imax : rhs.imin);
751 1.1 mrg SignExtendedNumber upper = imax << (imax.negative ? rhs.imin : rhs.imax);
752 1.1 mrg
753 1.1 mrg return IntRange(lower, upper);
754 1.1 mrg }
755 1.1 mrg
756 1.1 mrg IntRange opBinary(string op : ">>")(IntRange rhs)
757 1.1 mrg {
758 1.1 mrg if (rhs.imin.negative)
759 1.1 mrg {
760 1.1 mrg rhs = IntRange(SignExtendedNumber(0), SignExtendedNumber(64));
761 1.1 mrg }
762 1.1 mrg
763 1.1 mrg SignExtendedNumber lower = imin >> (imin.negative ? rhs.imin : rhs.imax);
764 1.1 mrg SignExtendedNumber upper = imax >> (imax.negative ? rhs.imax : rhs.imin);
765 1.1 mrg
766 1.1 mrg return IntRange(lower, upper);
767 1.1 mrg }
768 1.1 mrg
769 1.1 mrg IntRange opBinary(string op : ">>>")(IntRange rhs)
770 1.1 mrg {
771 1.1 mrg if (rhs.imin.negative)
772 1.1 mrg {
773 1.1 mrg rhs = IntRange(SignExtendedNumber(0), SignExtendedNumber(64));
774 1.1 mrg }
775 1.1 mrg
776 1.1 mrg return IntRange(imin >> rhs.imax, imax >> rhs.imin);
777 1.1 mrg }
778 1.1 mrg
779 1.1 mrg IntRange opBinary(string op : "^^")(IntRange rhs)
780 1.1 mrg {
781 1.1 mrg // Not yet implemented
782 1.1 mrg assert(0);
783 1.1 mrg }
784 1.1 mrg
785 1.1 mrg private:
786 1.1 mrg // Credits to Timon Gehr maxOr, minOr, maxAnd, minAnd
787 1.1 mrg // https://github.com/tgehr/d-compiler/blob/master/vrange.d
788 1.1 mrg static SignExtendedNumber maxOr(const IntRange lhs, const IntRange rhs)
789 1.1 mrg {
790 1.1 mrg uinteger_t x = 0;
791 1.1 mrg auto sign = false;
792 1.1 mrg auto xor = lhs.imax.value ^ rhs.imax.value;
793 1.1 mrg auto and = lhs.imax.value & rhs.imax.value;
794 1.1 mrg auto lhsc = IntRange(lhs);
795 1.1 mrg auto rhsc = IntRange(rhs);
796 1.1 mrg
797 1.1 mrg // Sign bit not part of the .value so we need an extra iteration
798 1.1 mrg if (lhsc.imax.negative ^ rhsc.imax.negative)
799 1.1 mrg {
800 1.1 mrg sign = true;
801 1.1 mrg if (lhsc.imax.negative)
802 1.1 mrg {
803 1.1 mrg if (!lhsc.imin.negative)
804 1.1 mrg {
805 1.1 mrg lhsc.imin.value = 0;
806 1.1 mrg }
807 1.1 mrg if (!rhsc.imin.negative)
808 1.1 mrg {
809 1.1 mrg rhsc.imin.value = 0;
810 1.1 mrg }
811 1.1 mrg }
812 1.1 mrg }
813 1.1 mrg else if (lhsc.imin.negative & rhsc.imin.negative)
814 1.1 mrg {
815 1.1 mrg sign = true;
816 1.1 mrg }
817 1.1 mrg else if (lhsc.imax.negative & rhsc.imax.negative)
818 1.1 mrg {
819 1.1 mrg return SignExtendedNumber(-1, false);
820 1.1 mrg }
821 1.1 mrg
822 1.1 mrg for (uinteger_t d = 1LU << (8 * uinteger_t.sizeof - 1); d; d >>= 1)
823 1.1 mrg {
824 1.1 mrg if (xor & d)
825 1.1 mrg {
826 1.1 mrg x |= d;
827 1.1 mrg if (lhsc.imax.value & d)
828 1.1 mrg {
829 1.1 mrg if (~lhsc.imin.value & d)
830 1.1 mrg {
831 1.1 mrg lhsc.imin.value = 0;
832 1.1 mrg }
833 1.1 mrg }
834 1.1 mrg else
835 1.1 mrg {
836 1.1 mrg if (~rhsc.imin.value & d)
837 1.1 mrg {
838 1.1 mrg rhsc.imin.value = 0;
839 1.1 mrg }
840 1.1 mrg }
841 1.1 mrg }
842 1.1 mrg else if (lhsc.imin.value & rhsc.imin.value & d)
843 1.1 mrg {
844 1.1 mrg x |= d;
845 1.1 mrg }
846 1.1 mrg else if (and & d)
847 1.1 mrg {
848 1.1 mrg x |= (d << 1) - 1;
849 1.1 mrg break;
850 1.1 mrg }
851 1.1 mrg }
852 1.1 mrg
853 1.1 mrg auto range = SignExtendedNumber(x, sign);
854 1.1 mrg return range;
855 1.1 mrg }
856 1.1 mrg
857 1.1 mrg // Credits to Timon Gehr maxOr, minOr, maxAnd, minAnd
858 1.1 mrg // https://github.com/tgehr/d-compiler/blob/master/vrange.d
859 1.1 mrg static SignExtendedNumber minOr(const IntRange lhs, const IntRange rhs)
860 1.1 mrg {
861 1.1 mrg return ~maxAnd(~lhs, ~rhs);
862 1.1 mrg }
863 1.1 mrg
864 1.1 mrg // Credits to Timon Gehr maxOr, minOr, maxAnd, minAnd
865 1.1 mrg // https://github.com/tgehr/d-compiler/blob/master/vrange.d
866 1.1 mrg static SignExtendedNumber maxAnd(const IntRange lhs, const IntRange rhs)
867 1.1 mrg {
868 1.1 mrg uinteger_t x = 0;
869 1.1 mrg bool sign = false;
870 1.1 mrg auto lhsc = IntRange(lhs);
871 1.1 mrg auto rhsc = IntRange(rhs);
872 1.1 mrg
873 1.1 mrg if (lhsc.imax.negative & rhsc.imax.negative)
874 1.1 mrg {
875 1.1 mrg sign = true;
876 1.1 mrg }
877 1.1 mrg
878 1.1 mrg for (uinteger_t d = 1LU << (8 * uinteger_t.sizeof - 1); d; d >>= 1)
879 1.1 mrg {
880 1.1 mrg if (lhsc.imax.value & rhsc.imax.value & d)
881 1.1 mrg {
882 1.1 mrg x |= d;
883 1.1 mrg if (~lhsc.imin.value & d)
884 1.1 mrg {
885 1.1 mrg lhsc.imin.value = 0;
886 1.1 mrg }
887 1.1 mrg if (~rhsc.imin.value & d)
888 1.1 mrg {
889 1.1 mrg rhsc.imin.value = 0;
890 1.1 mrg }
891 1.1 mrg }
892 1.1 mrg else if (~lhsc.imin.value & d && lhsc.imax.value & d)
893 1.1 mrg {
894 1.1 mrg lhsc.imax.value |= d - 1;
895 1.1 mrg }
896 1.1 mrg else if (~rhsc.imin.value & d && rhsc.imax.value & d)
897 1.1 mrg {
898 1.1 mrg rhsc.imax.value |= d - 1;
899 1.1 mrg }
900 1.1 mrg }
901 1.1 mrg
902 1.1 mrg auto range = SignExtendedNumber(x, sign);
903 1.1 mrg return range;
904 1.1 mrg }
905 1.1 mrg
906 1.1 mrg // Credits to Timon Gehr maxOr, minOr, maxAnd, minAnd
907 1.1 mrg // https://github.com/tgehr/d-compiler/blob/master/vrange.d
908 1.1 mrg static SignExtendedNumber minAnd(const IntRange lhs, const IntRange rhs)
909 1.1 mrg {
910 1.1 mrg return ~maxOr(~lhs, ~rhs);
911 1.1 mrg }
912 1.1 mrg
913 1.1 mrg static swap(ref IntRange a, ref IntRange b)
914 1.1 mrg {
915 1.1 mrg auto aux = a;
916 1.1 mrg a = b;
917 1.1 mrg b = aux;
918 1.1 mrg }
919 1.1 mrg }
920