1 module d.semantic.vrp; 2 3 import d.ir.expression; 4 import d.ir.symbol; 5 import d.ir.type; 6 7 import d.semantic.semantic; 8 9 import std.traits; 10 11 // Conflict with Interface in object.di 12 alias Interface = d.ir.symbol.Interface; 13 14 /** 15 * ValueRangePropagator to figure out if it is safe to truncate 16 * a value to make it fit in a smaller type. 17 * 18 * It does so by computing the range of possible values the 19 * expression can take and checking if that range fit in a given type. 20 * 21 * It is only specialized for uint and ulong, as smaller integral type 22 * are promoted before any computation to 32bits values and above. 23 * As a result, smaller types do not really matter as long as we promote 24 * them properly. As a result, ranges computed from improperly promoted 25 * expression may be extremely pessimistics. 26 */ 27 struct ValueRangePropagator(T) if (is(T == uint) || is(T == ulong)) { 28 private SemanticPass pass; 29 alias pass this; 30 31 alias VR = ValueRange!T; 32 33 this(SemanticPass pass) { 34 this.pass = pass; 35 } 36 37 bool canFit(Expression e, Type t) in { 38 assert(isValidExpr(e), "VRP expect integral types."); 39 } do { 40 return canFit(e, getBuiltin(t)); 41 } 42 43 bool canFit(Expression e, BuiltinType t) in { 44 assert(isValidExpr(e), "VRP expect integral types."); 45 assert(canConvertToIntegral(t), "VRP only supports integral types."); 46 } do { 47 static canFitDMDMonkeyDance(R)(R r, BuiltinType t) { 48 auto mask = cast(R.U) ((1UL << t.getBits()) - 1); 49 50 // Try to fit into the unsigned range. 51 auto urange = r.unsigned.normalized; 52 if (urange.max <= mask) { 53 return true; 54 } 55 56 // Try to fit into the signed range. 57 auto smask = mask >> 1; 58 auto nmask = ~smask; 59 60 auto srange = r.signed.normalized; 61 if (srange.min >= nmask && srange.max <= smask) { 62 return true; 63 } 64 65 // Ho noes, it doesn't fit /o\ 66 return false; 67 } 68 69 return processExpr!canFitDMDMonkeyDance(e, t); 70 } 71 72 private: 73 static asHandlerDMDMonkeyDance(R)(R r) { 74 return r.as!T; 75 } 76 77 auto processExpr(alias handler = asHandlerDMDMonkeyDance, 78 A...)(Expression e, A args) { 79 import std.algorithm; 80 auto s = max(getBuiltin(e.type).getSize(), uint.sizeof); 81 if (s == T.sizeof) { 82 return handler(visit(e), args); 83 } 84 85 switch (s) { 86 case uint.sizeof: 87 return handler(ValueRangePropagator!uint(pass).visit(e), args); 88 89 case ulong.sizeof: 90 return handler(ValueRangePropagator!ulong(pass).visit(e), args); 91 92 default: 93 assert(0, "Size not supported by VRP"); 94 } 95 } 96 97 BuiltinType getBuiltin(Type t) out(result) { 98 assert(canConvertToIntegral(result), 99 "VRP only supports integral types."); 100 } do { 101 t = t.getCanonicalAndPeelEnum(); 102 if (t.hasPointerABI()) { 103 return getBuiltin(pass.object.getSizeT().type); 104 } 105 106 assert(t.kind == TypeKind.Builtin, "Invalid type for VRP"); 107 return t.builtin; 108 } 109 110 VR getRange(Type t) { 111 return getRange(getBuiltin(t)); 112 } 113 114 VR getRange(BuiltinType t) in { 115 assert(t.getSize() <= T.sizeof); 116 } do { 117 if (t == BuiltinType.Bool) { 118 return VR(0, 1); 119 } 120 121 if (isChar(t)) { 122 t = integralOfChar(t); 123 } 124 125 return ValueRange!ulong(getMin(t), getMax(t)).as!T; 126 } 127 128 public: 129 VR visit(Expression e) in { 130 assert(isValidExpr(e), "VRP expect integral types."); 131 } do { 132 return this.dispatch!(e => getRange(e.type))(e); 133 } 134 135 VR visit(BooleanLiteral e) { 136 return VR(e.value); 137 } 138 139 VR visit(IntegerLiteral e) { 140 return ValueRange!ulong(e.value).as!T; 141 } 142 143 VR visit(BinaryExpression e) { 144 switch (e.op) with (BinaryOp) { 145 case Comma, Assign: 146 return visit(e.rhs); 147 148 case Add: 149 return visit(e.lhs) + visit(e.rhs); 150 151 case Sub: 152 return visit(e.lhs) - visit(e.rhs); 153 154 case Mul: 155 return visit(e.lhs) * visit(e.rhs); 156 157 case UDiv: 158 return visit(e.lhs) / visit(e.rhs); 159 160 case SDiv: 161 return (visit(e.lhs).signed / visit(e.rhs).signed).unsigned; 162 163 case URem: 164 return visit(e.lhs) % visit(e.rhs); 165 166 case SRem: 167 return (visit(e.lhs).signed % visit(e.rhs).signed).unsigned; 168 169 case Or: 170 return visit(e.lhs) | visit(e.rhs); 171 172 case And: 173 return visit(e.lhs) & visit(e.rhs); 174 175 case Xor: 176 return visit(e.lhs) ^ visit(e.rhs); 177 178 case LeftShift: 179 return visit(e.lhs) << visit(e.rhs); 180 181 case UnsignedRightShift: 182 return visit(e.lhs) >>> visit(e.rhs); 183 184 case SignedRightShift: 185 return (visit(e.lhs).signed >> visit(e.rhs).signed).unsigned; 186 187 default: 188 assert(0, "Not implemented."); 189 } 190 } 191 192 VR visit(UnaryExpression e) { 193 switch (e.op) with (UnaryOp) { 194 case Plus: 195 return visit(e.expr); 196 197 case Minus: 198 return -visit(e.expr); 199 200 default: 201 assert(0, "Not implemented."); 202 } 203 } 204 205 VR visit(VariableExpression e) { 206 auto v = e.var; 207 scheduler.require(v, Step.Processed); 208 return visit(v); 209 } 210 211 VR visit(Variable v) in { 212 assert(v.step >= Step.Processed); 213 } do { 214 return (v.storage == Storage.Enum 215 || v.type.getCanonical().qualifier == TypeQualifier.Immutable) 216 ? visit(v.value) 217 : getRange(v.type); 218 } 219 220 VR visit(CastExpression e) { 221 final switch (e.kind) with (CastKind) { 222 case Invalid: 223 assert(0, "Invalid cast"); 224 225 case IntToPtr, Down: 226 assert(0, "Do not make any sense on integrals"); 227 228 case PtrToInt: 229 assert(0, "Not implemented"); 230 231 // return ValueRange.get(e.type.builtin); 232 233 case IntToBool: 234 static doTheDMDMonkeyDance(R)(R r) { 235 if (r == typeof(r)(0)) { 236 return VR(0); 237 } 238 239 if (!r.containsZero) { 240 return VR(1); 241 } 242 243 return VR(0, 1); 244 } 245 246 return processExpr!doTheDMDMonkeyDance(e.expr); 247 248 case Trunc: 249 static doTheDMDMonkeyDance(R)(R r, BuiltinType t) { 250 auto signed = isIntegral(t) && isSigned(t); 251 auto mask = cast(R.U) ((1UL << t.getBits()) - 1); 252 253 return 254 signed ? r.signed.trunc(mask).as!T : r.trunc(mask).as!T; 255 } 256 257 return 258 processExpr!doTheDMDMonkeyDance(e.expr, getBuiltin(e.type)); 259 260 case UPad: 261 auto t = getBuiltin(e.expr.type); 262 auto r = processExpr(e.expr); 263 264 auto mask = cast(T) ((1UL << t.getBits()) - 1); 265 return r.pad(mask); 266 267 case SPad: 268 auto t = getBuiltin(e.expr.type); 269 auto r = processExpr(e.expr); 270 271 auto mask = cast(T) ((1UL << t.getBits()) - 1); 272 return r.signed.pad(mask).unsigned; 273 274 case Bit, Qual, Exact: 275 return visit(e.expr); 276 } 277 } 278 } 279 280 unittest { 281 import std.meta; 282 foreach (T; AliasSeq!(uint, ulong)) { 283 auto vrp = ValueRangePropagator!T(); 284 285 alias VR = ValueRange!T; 286 VR v; 287 288 enum LS = T.sizeof == long.sizeof; 289 290 /** 291 * Test internal facilities 292 */ 293 assert(vrp.getRange(BuiltinType.Bool) == VR(0, 1)); 294 295 assert(vrp.getRange(BuiltinType.Byte) == VR(byte.min, byte.max)); 296 assert(vrp.getRange(BuiltinType.Ubyte) == VR(ubyte.min, ubyte.max)); 297 assert(vrp.getRange(BuiltinType.Short) == VR(short.min, short.max)); 298 assert(vrp.getRange(BuiltinType.Ushort) == VR(ushort.min, ushort.max)); 299 assert(vrp.getRange(BuiltinType.Int) == VR(int.min, int.max)); 300 assert(vrp.getRange(BuiltinType.Uint) == VR(uint.min, uint.max)); 301 302 static if (LS) { 303 assert(vrp.getRange(BuiltinType.Long) == VR(long.min, long.max)); 304 assert(vrp.getRange(BuiltinType.Ulong) == VR(ulong.min, ulong.max)); 305 } 306 307 assert(vrp.getRange(BuiltinType.Char) == VR(ubyte.min, ubyte.max)); 308 assert(vrp.getRange(BuiltinType.Wchar) == VR(ushort.min, ushort.max)); 309 assert(vrp.getRange(BuiltinType.Dchar) == VR(uint.min, uint.max)); 310 311 /** 312 * Literals 313 */ 314 import source.location; 315 v = vrp.visit(new BooleanLiteral(Location.init, false)); 316 assert(v == VR(0)); 317 318 v = vrp.visit(new BooleanLiteral(Location.init, true)); 319 assert(v == VR(1)); 320 321 v = vrp.visit(new IntegerLiteral(Location.init, -9, BuiltinType.Byte)); 322 assert(v == VR(-9)); 323 324 v = vrp.visit(new IntegerLiteral(Location.init, 42, BuiltinType.Uint)); 325 assert(v == VR(42)); 326 327 // Let's define some values we can reuse. 328 auto zero = new IntegerLiteral(Location.init, 0, BuiltinType.Int); 329 auto i1 = new IntegerLiteral(Location.init, -7, BuiltinType.Int); 330 auto i2 = new IntegerLiteral(Location.init, 42, BuiltinType.Int); 331 auto i3 = new IntegerLiteral(Location.init, 2, BuiltinType.Uint); 332 333 auto bmax = new IntegerLiteral(Location.init, 255, BuiltinType.Byte); 334 335 auto tbool = Type.get(BuiltinType.Bool); 336 auto tint = Type.get(BuiltinType.Int); 337 auto tuint = Type.get(BuiltinType.Uint); 338 auto tubyte = Type.get(BuiltinType.Ubyte); 339 auto tlong = Type.get(BuiltinType.Long); 340 341 /** 342 * Binary ops 343 */ 344 v = vrp.visit( 345 new BinaryExpression(Location.init, tint, BinaryOp.Comma, i1, i2)); 346 assert(v == VR(42)); 347 348 // Technically, this is illegal, but it is out of scope of VRP to detect this, so will do. 349 v = vrp.visit( 350 new BinaryExpression(Location.init, tint, BinaryOp.Assign, i1, i2)); 351 assert(v == VR(42)); 352 353 v = vrp.visit( 354 new BinaryExpression(Location.init, tint, BinaryOp.Add, i1, i2)); 355 assert(v == VR(35)); 356 357 v = vrp.visit( 358 new BinaryExpression(Location.init, tint, BinaryOp.Sub, i1, i2)); 359 assert(v == VR(-49)); 360 361 v = vrp.visit( 362 new BinaryExpression(Location.init, tint, BinaryOp.Mul, i1, i2)); 363 assert(v == VR(-294)); 364 365 v = vrp.visit( 366 new BinaryExpression(Location.init, tuint, BinaryOp.UDiv, i1, i2)); 367 assert(v == VR((T.max - 6) / 42)); 368 369 v = vrp.visit( 370 new BinaryExpression(Location.init, tint, BinaryOp.SDiv, i2, i1)); 371 assert(v == VR(-6)); 372 373 v = vrp.visit( 374 new BinaryExpression(Location.init, tuint, BinaryOp.URem, i1, i2)); 375 assert(v == VR(LS ? 9 : 39)); 376 377 v = vrp.visit( 378 new BinaryExpression(Location.init, tint, BinaryOp.SRem, i1, i3)); 379 assert(v == VR(-1)); 380 381 v = vrp.visit( 382 new BinaryExpression(Location.init, tint, BinaryOp.Or, i1, i2)); 383 assert(v == VR(-5)); 384 385 v = vrp.visit( 386 new BinaryExpression(Location.init, tint, BinaryOp.And, i1, i2)); 387 assert(v == VR(40)); 388 389 v = vrp.visit( 390 new BinaryExpression(Location.init, tint, BinaryOp.Xor, i1, i2)); 391 assert(v == VR(-45)); 392 393 v = vrp.visit(new BinaryExpression(Location.init, tint, 394 BinaryOp.LeftShift, i1, i3)); 395 assert(v == VR(-28)); 396 397 v = vrp.visit(new BinaryExpression( 398 Location.init, tuint, BinaryOp.UnsignedRightShift, i1, i3)); 399 assert(v == VR((T.max >> 2) - 1)); 400 401 v = vrp.visit(new BinaryExpression(Location.init, tint, 402 BinaryOp.SignedRightShift, i1, i3)); 403 assert(v == VR(-2)); 404 405 /** 406 * Unary ops 407 */ 408 v = vrp 409 .visit(new UnaryExpression(Location.init, tuint, UnaryOp.Plus, i1)); 410 assert(v == VR(-7)); 411 412 v = vrp.visit( 413 new UnaryExpression(Location.init, tuint, UnaryOp.Minus, i1)); 414 assert(v == VR(7)); 415 416 /** 417 * Variables 418 */ 419 import source.name; 420 auto var = new Variable(Location.init, 421 tuint.getParamType(ParamKind.Regular), 422 BuiltinName!"", i1); 423 var.step = Step.Processed; 424 425 v = vrp.visit(var); 426 assert(v == VR(uint.min, uint.max)); 427 428 var.type = tbool; 429 v = vrp.visit(var); 430 assert(v == VR(0, 1)); 431 432 var.type = Type.get(BuiltinType.Short); 433 v = vrp.visit(var); 434 assert(v == VR(short.min, short.max)); 435 436 var.storage = Storage.Enum; 437 v = vrp.visit(var); 438 assert(v == VR(-7)); 439 440 var.storage = Storage.Static; 441 v = vrp.visit(var); 442 assert(v == VR(short.min, short.max)); 443 444 var.type = var.type.qualify(TypeQualifier.Immutable); 445 v = vrp.visit(var); 446 assert(v == VR(-7)); 447 448 /** 449 * Casts 450 */ 451 v = vrp.visit( 452 new CastExpression(Location.init, CastKind.IntToBool, tbool, zero)); 453 assert(v == VR(0)); 454 455 v = vrp.visit( 456 new CastExpression(Location.init, CastKind.IntToBool, tbool, i1)); 457 assert(v == VR(1)); 458 459 // FIXME: test the 0, 1 int to bool case ? 460 461 v = vrp.visit( 462 new CastExpression(Location.init, CastKind.Trunc, tubyte, i1)); 463 assert(v == VR(249)); 464 465 v = vrp.visit( 466 new CastExpression(Location.init, CastKind.UPad, tint, bmax)); 467 assert(v == VR(255)); 468 469 v = vrp.visit( 470 new CastExpression(Location.init, CastKind.SPad, tint, bmax)); 471 assert(v == VR(-1)); 472 473 v = vrp 474 .visit(new CastExpression(Location.init, CastKind.Bit, tint, i1)); 475 assert(v == VR(-7)); 476 477 v = vrp 478 .visit(new CastExpression(Location.init, CastKind.Qual, tint, i1)); 479 assert(v == VR(-7)); 480 481 v = vrp 482 .visit(new CastExpression(Location.init, CastKind.Exact, tint, i1)); 483 assert(v == VR(-7)); 484 } 485 } 486 487 private: 488 489 auto isValidExpr(Expression e) { 490 auto t = e.type.getCanonicalAndPeelEnum(); 491 return t.kind == TypeKind.Builtin && canConvertToIntegral(t.builtin); 492 } 493 494 bool isMask(T)(T mask) if (isIntegral!T) { 495 auto p = mask + 1; 496 return (p & -p) == p; 497 } 498 499 unittest { 500 assert(isMask(0)); 501 assert(isMask(1)); 502 assert(!isMask(2)); 503 assert(isMask(3)); 504 assert(!isMask(10)); 505 assert(isMask(255)); 506 } 507 508 struct ValueRange(T) if (is(uint : T) && isIntegral!T) { 509 T min = T.min; 510 T max = T.max; 511 512 this(T min, T max) { 513 this.min = min; 514 this.max = max; 515 } 516 517 this(T val) { 518 this(val, val); 519 } 520 521 alias U = Unsigned!T; 522 alias S = Signed!T; 523 524 alias URange = ValueRange!U; 525 alias SRange = ValueRange!S; 526 527 enum Bits = T.sizeof * 8; 528 529 @property 530 U range() const { 531 return max - min; 532 } 533 534 @property 535 bool full() const { 536 return range == typeof(range).max; 537 } 538 539 @property 540 auto unsigned() const { 541 return URange(min, max); 542 } 543 544 @property 545 auto signed() const { 546 return SRange(min, max); 547 } 548 549 @property 550 auto normalized() const out(result) { 551 assert(result.range >= range); 552 assert(result.min <= result.max); 553 } do { 554 // This is lossy, only use when stritcly necessary. 555 if (min > max) { 556 return ValueRange(); 557 } 558 559 return this; 560 } 561 562 @property 563 bool containsZero() const { 564 return unsigned.normalized.min == 0; 565 } 566 567 @property 568 bool isReduced() const { 569 // A reduced range if of the form XXXX0000 => XXXX1111. 570 auto x = (min ^ max) + 1; 571 return (x & -x) == x; 572 } 573 574 auto unionWith(ValueRange other) const { 575 auto twraparound = this.max < this.min; 576 auto owraparound = other.max < other.min; 577 578 if (twraparound == owraparound) { 579 import std.algorithm : min, max; 580 auto rmin = min(this.min, other.min); 581 auto rmax = max(this.max, other.max); 582 583 auto r = ValueRange(rmin, rmax); 584 585 if (twraparound) { 586 // Checks if we don't do more than a full range 587 if (rmin <= rmax) { 588 return ValueRange(); 589 } 590 } else { 591 // Maybe a wraparound range would be tighter. 592 auto wmin = max(this.min, other.min); 593 auto wmax = min(this.max, other.max); 594 595 auto w = ValueRange(wmin, wmax); 596 597 // If so, wraparound ! 598 if (wmin > wmax && (w.range < r.range)) { 599 return w; 600 } 601 } 602 603 return r; 604 } 605 606 ValueRange wr = this; 607 if (owraparound) { 608 import std.algorithm : swap; 609 swap(wr, other); 610 } 611 612 // Try to merge up and down, and chose the tighter. 613 import std.algorithm : min, max; 614 auto d = ValueRange(wr.min, max(wr.max, other.max)); 615 if (d.min <= d.max) { 616 d = ValueRange(); 617 } 618 619 auto u = ValueRange(min(wr.min, other.min), wr.max); 620 if (u.min <= u.max) { 621 u = ValueRange(); 622 } 623 624 return u.range < d.range ? u : d; 625 } 626 627 @property 628 ValueRange!A as(A)() const if (is(uint : A) && isIntegral!A) out(result) { 629 assert(result.range <= Unsigned!A.max); 630 } do { 631 static if (T.sizeof <= A.sizeof) { 632 // Type are the same size, it is a noop. 633 return ValueRange!A(min, max); 634 } else { 635 enum MaxRange = Unsigned!A.max; 636 637 // If the current range is larger than the destination: full range. 638 if (range >= MaxRange) { 639 return ValueRange!A(); 640 } 641 642 // Now the range fits. It's now a matter 643 // of wrapping things around the right way. 644 645 // If we have a proper unsigned range, go for it. 646 auto urange = this.unsigned; 647 if (urange.min <= urange.max) { 648 alias UA = Unsigned!A; 649 auto umin = cast(UA) urange.min; 650 auto umax = cast(UA) urange.max; 651 652 // No overflow check because the high bits are lost anyway. 653 return ValueRange!UA(umin, umax).as!A; 654 } 655 656 // If we have a proper signed range, go for it. 657 auto srange = this.signed; 658 if (srange.min <= srange.max) { 659 alias SA = Unsigned!A; 660 auto smin = cast(SA) srange.min; 661 auto smax = cast(SA) srange.max; 662 663 // No overflow check because the high bits are lost anyway. 664 return ValueRange!SA(smin, smax).as!A; 665 } 666 667 // OK, we have some sort of screwed up range :) 668 return ValueRange!A(); 669 } 670 } 671 672 /** 673 * Fit a range into a smaller type with less bits. 674 * 675 * Try to produce a wrappign around range when possible. 676 * This will create ranges needlessly large when normalizing, 677 * but it doesn't matter as well formed expression should 678 * UPad or SPad before normalizing due to integer promotion. 679 */ 680 ValueRange trunc(U mask) const out(result) { 681 assert(isMask(mask)); 682 assert(result.range <= mask); 683 } do { 684 auto smask = mask >> 1; 685 686 // Worse case scenario, we can return this. 687 auto fail = 688 isSigned!T ? ValueRange(~smask, smask) : ValueRange(0, mask); 689 690 // We do something similar to as!T, except that we sign extend 691 // manually in the signed range case. 692 if (range > mask) { 693 return fail; 694 } 695 696 auto urange = this.unsigned; 697 if (urange.min <= urange.max) { 698 auto umin = urange.min & mask; 699 auto umax = urange.max & mask; 700 701 // Maybe umax wrapped around, in which case we wrap around. 702 if (umax < umin) { 703 umin = umin | ~mask; 704 } 705 706 return ValueRange(umin, umax); 707 } 708 709 auto srange = this.signed; 710 if (srange.min <= srange.max) { 711 // Other cases are already handled as unsigned. 712 assert(srange.min < 0); 713 assert(srange.max >= 0); 714 715 auto smin = srange.min | ~mask; 716 auto smax = srange.max & mask; 717 return ValueRange(smin, smax); 718 } 719 720 // I don't think this can actually happen unless mask is all ones. 721 // In which case, why we are truncating is a mystery to begin with. 722 assert(mask + 1 == 0); 723 return fail; 724 } 725 726 /** 727 * Because it is a signed operation, paddign is actually the one 728 * finishing the truncate work. It works because properly formed 729 * expression are promoted before being used, and this promotion 730 * translates into a pad operation here. 731 */ 732 ValueRange pad()(U mask) const if (isUnsigned!T) out(result) { 733 assert(result.range <= mask); 734 assert(isMask(mask)); 735 } do { 736 auto bits = min ^ max; 737 return ((min > max) || (bits & ~mask)) 738 ? ValueRange(0, mask) 739 : ValueRange(min & mask, max & mask); 740 } 741 742 ValueRange pad()(U mask) const if (isSigned!T) out(result) { 743 assert(result.range <= mask); 744 assert(isMask(mask)); 745 } do { 746 auto offset = URange(~(mask >> 1)); 747 return ((unsigned - offset).pad(mask) + offset).signed; 748 } 749 750 bool opEquals(ValueRange rhs) const { 751 return (full && rhs.full) || (min == rhs.min && max == rhs.max); 752 } 753 754 auto opUnary(string op : "-")() const { 755 return ValueRange(-max, -min); 756 } 757 758 auto opUnary(string op : "~")() const { 759 return ValueRange(~max, ~min); 760 } 761 762 auto opBinary(string op : "+")(ValueRange rhs) const { 763 ulong lrange = this.range; 764 ulong rrange = rhs.range; 765 auto range = lrange + rrange; 766 767 // If the type is small enough, do the easy dance. 768 auto overflow = 769 Bits < 64 ? range > U.max : range < lrange && range < rrange; 770 771 return 772 overflow ? ValueRange() : ValueRange(min + rhs.min, max + rhs.max); 773 } 774 775 auto opBinary(string op : "-")(ValueRange rhs) const { 776 return this + -rhs; 777 } 778 779 auto smul()(ValueRange rhs) const if (isUnsigned!T) in { 780 assert(min > max && max != 0); 781 } do { 782 auto v0 = ValueRange(0, max) * rhs; 783 auto v1 = ValueRange(0, -min) * -rhs; 784 785 if (rhs.min <= rhs.max) { 786 // If rhs don't wrap around, 0 should one of v0 bound. 787 assert(v0.min == 0 || v0.max == 0); 788 auto rmin = v0.min ? v0.min : v1.min; 789 auto rmax = v0.min ? v1.max : v0.max; 790 if (rmin > rmax) { 791 return ValueRange(rmin, rmax); 792 } 793 } else { 794 // If rhs wrap around, v1 and v0 will wrap around or be 0. 795 import std.algorithm : min, max; 796 auto rmin = min(v0.min, v1.min); 797 auto rmax = max(v0.max, v1.max); 798 if (rmin > rmax) { 799 return ValueRange(rmin, rmax); 800 } 801 } 802 803 // We have an overflow. 804 return ValueRange(); 805 } 806 807 auto umul()(ValueRange rhs) const if (isUnsigned!T) in { 808 assert(min <= max); 809 assert(rhs.min <= rhs.max); 810 } do { 811 // So we can swap modify. 812 ValueRange lhs = this; 813 814 // If the whole range is in the negative, a * b = -(a * b). 815 auto lneg = lhs.min > S.max; 816 if (lneg) { 817 lhs = -lhs; 818 } 819 820 auto rneg = rhs.min > S.max; 821 if (rneg) { 822 rhs = -rhs; 823 } 824 825 // Zero is a special case as it always produce a 826 // range containign only itself. By splitting it, 827 // we can make sure we minimally extend the range 828 // to include 0 at the end. 829 bool hasZero; 830 831 if (lhs.min == 0) { 832 hasZero = true; 833 lhs.min = 1; 834 } 835 836 if (rhs.min == 0) { 837 hasZero = true; 838 rhs.min = 1; 839 } 840 841 // Alright, now we are in a canonical form, we can proceed. 842 static T mulhi(ulong a, ulong b) { 843 static if (Bits < 64) { 844 // XXX: VRP can't figure that one out aparently. 845 return cast(T) ((a * b) >> Bits); 846 } else { 847 // (a0 << 32 + a1)(b0 << 32 + b1) = a0b0 << 64 + (a0b1 + a1b0) << 32 + a1b1 848 auto a0 = a >> 32; 849 auto a1 = a & uint.max; 850 851 auto b0 = b >> 32; 852 auto b1 = b & uint.max; 853 854 auto a0b0 = a0 * b0; 855 auto a0b1 = a0 * b1; 856 auto a1b0 = a1 * b0; 857 auto a1b1 = a1 * b1; 858 859 auto carry = (a1b1 >> 32 + ((a0b1 + a1b0) & uint.max)) >> 32; 860 return a0b0 + (a0b1 >> 32) + (a1b0 >> 32) + carry; 861 } 862 } 863 864 // If this overflows, then return a full range. 865 if (mulhi(lhs.min, rhs.min) != mulhi(lhs.max, rhs.max)) { 866 return ValueRange(); 867 } 868 869 auto res = ValueRange(lhs.min * rhs.min, lhs.max * rhs.max); 870 if (lneg != rneg) { 871 res = -res; 872 } 873 874 // We try to zero extend the range to be the most restrictive. 875 if (!hasZero) { 876 return res; 877 } 878 879 if (res.min <= -res.max) { 880 return ValueRange(0, res.max); 881 } else { 882 return ValueRange(res.min, 0); 883 } 884 } 885 886 auto opBinary(string op : "*")(ValueRange rhs) const if (isSigned!T) { 887 // Multiplication doesn't care about sign. 888 return (this.unsigned * rhs.unsigned).signed; 889 } 890 891 ValueRange opBinary(string op : "*")(ValueRange rhs) const 892 if (isUnsigned!T) { 893 // Multiplication by 0 is always 0. 894 auto zero = ValueRange(0); 895 if (this == zero || rhs == zero) { 896 // Zero is pain in the ass as it reduce ranges 897 // to one element, making it hard to figure out 898 // if an overflow occured. 899 return zero; 900 } 901 902 // Try to avoid slicing when not necessary. 903 if (max == 0) { 904 // [-a, 0] * rhs = -(rhs * [0 .. a]) 905 return -(rhs * -this); 906 } 907 908 if (rhs.max == 0) { 909 return -(this * -rhs); 910 } 911 912 // If the range is of the kind [min .. T.max][0 .. max] 913 // we split it in half and union the result. 914 if (min > max) { 915 return smul(rhs); 916 } 917 918 if (rhs.min > rhs.max) { 919 return rhs.smul(this); 920 } 921 922 return umul(rhs); 923 } 924 925 auto opBinary(string op : "/")(ValueRange rhs) const { 926 rhs = rhs.normalized; 927 if (rhs == ValueRange(0)) { 928 // We have a division by 0, bail out. 929 return ValueRange(); 930 } 931 932 // Remove 0 from rhs. 933 rhs.min = rhs.min ? rhs.min : 1; 934 rhs.max = rhs.max ? rhs.max : U.max; 935 936 // Make sure we normalize full ranges. 937 ValueRange lhs = this.normalized; 938 if (isUnsigned!T) { 939 return ValueRange(lhs.min / rhs.max, lhs.max / rhs.min); 940 } else { 941 if (rhs.containsZero) { 942 import std.algorithm : min, max; 943 return 944 ValueRange(min(lhs.min, -lhs.max), max(lhs.max, -lhs.min)); 945 } 946 947 // Alright, this is a signed division. 948 bool neg = rhs.max < 0; 949 if (neg) { 950 // a / -b = -(a / b) 951 rhs = -rhs; 952 } 953 954 auto min = lhs.min / (lhs.min < 0 ? rhs.min : rhs.max); 955 auto max = lhs.max / (lhs.max < 0 ? rhs.max : rhs.min); 956 return neg ? ValueRange(-max, -min) : ValueRange(min, max); 957 } 958 } 959 960 auto srem()(ValueRange rhs) const if (isSigned!T) in { 961 assert(this != ValueRange(0)); 962 assert(rhs is rhs.normalized); 963 } do { 964 if (rhs.max < 0) { 965 // a % -b = a % b 966 rhs = -rhs; 967 } 968 969 if (rhs.containsZero) { 970 import std.algorithm : max; 971 rhs = ValueRange(1, max(-rhs.min, rhs.max)); 972 } 973 974 auto lhs = this.normalized; 975 976 // lhs is positive 977 if (lhs.min >= 0) { 978 return lhs.unsigned.urem(rhs.unsigned).signed; 979 } 980 981 // lhs is negative 982 if (lhs.max <= 0) { 983 lhs = -lhs; 984 return -lhs.unsigned.urem(rhs.unsigned).signed; 985 } 986 987 // Ok lhs can be both positive and negative. 988 // Compute both range and aggregate. 989 auto pos = URange(0, lhs.max).urem(rhs.unsigned); 990 auto neg = URange(0, -lhs.min).urem(rhs.unsigned); 991 992 return ValueRange(-neg.max, pos.max); 993 } 994 995 auto urem()(ValueRange rhs) const if (isUnsigned!T) in { 996 assert(this != ValueRange(0)); 997 assert(rhs is rhs.normalized); 998 assert(rhs.min > 0); 999 } do { 1000 auto lhs = this.normalized; 1001 1002 // If lhs is within the bound of rhs. 1003 if (lhs.max < rhs.max) { 1004 // If rhs.min <= rhs.max we need to 0 extend. 1005 return ValueRange((lhs.max > rhs.min) ? 0 : lhs.min, lhs.max); 1006 } 1007 1008 // We count how many time we can remove rhs.max from lhs. 1009 auto lminrmaxloop = lhs.min / rhs.max; 1010 auto lmaxrmaxloop = lhs.max / rhs.max; 1011 1012 // If these counts aren't the same, we have the full modulo range. 1013 if (lminrmaxloop != lmaxrmaxloop) { 1014 return ValueRange(0, rhs.max - 1); 1015 } 1016 1017 // Same process for rhs.min. 1018 auto lminrminloop = lhs.min / rhs.min; 1019 auto lmaxrminloop = lhs.max / rhs.min; 1020 1021 // FIXME: Idealy, we would look for the biggest 1022 // value in rhs that have the correct count. 1023 if (lminrminloop != lmaxrminloop || lminrminloop != lminrmaxloop) { 1024 return ValueRange(0, rhs.max - 1); 1025 } 1026 1027 // At this point, we know that as rhs grow, the result will reduce. 1028 // and as lhs grow, the result will increase. 1029 return ValueRange(lhs.min % rhs.max, lhs.max % rhs.min); 1030 } 1031 1032 auto opBinary(string op : "%")(ValueRange rhs) const { 1033 rhs = rhs.normalized; 1034 if (rhs == ValueRange(0)) { 1035 // We have a division by 0, bail out. 1036 return this; 1037 } 1038 1039 static if (isSigned!T) { 1040 return srem(rhs); 1041 } else { 1042 rhs.min = rhs.min ? rhs.min : 1; 1043 return urem(rhs); 1044 } 1045 } 1046 1047 auto sshl()(ValueRange rhs) const if (isUnsigned!T) in { 1048 assert(rhs is rhs.normalized); 1049 assert(rhs.max < Bits); 1050 } do { 1051 auto v0 = ValueRange(0, max).ushl(rhs); 1052 auto v1 = -ValueRange(0, -min).ushl(rhs); 1053 1054 if (v0.max >= v1.min) { 1055 return ValueRange(); 1056 } 1057 1058 return ValueRange(v1.min, v0.max); 1059 } 1060 1061 auto ushl()(ValueRange rhs) const if (isUnsigned!T) in { 1062 assert(rhs is rhs.normalized); 1063 assert(rhs.max < Bits); 1064 assert(this.min <= Signed!T.max); 1065 } do { 1066 auto minhi = rhs.min ? (min >> (Bits - rhs.min)) : 0; 1067 auto maxhi = rhs.max ? (max >> (Bits - rhs.max)) : 0; 1068 if (minhi != maxhi) { 1069 return ValueRange(); 1070 } 1071 1072 return ValueRange(min << rhs.min, max << rhs.max); 1073 } 1074 1075 auto opBinary(string op : "<<")(ValueRange rhs) const if (isSigned!T) { 1076 return (this.unsigned << rhs.unsigned).signed; 1077 } 1078 1079 auto opBinary(string op : "<<")(ValueRange rhs) const if (isUnsigned!T) { 1080 rhs = rhs.normalized; 1081 1082 // We are in undefined territory, pessimize. 1083 if (rhs.max >= Bits) { 1084 // We assume that shifting 0 is alright. 1085 return this == ValueRange(0) ? ValueRange(0) : ValueRange(); 1086 } 1087 1088 if (min > max) { 1089 return sshl(rhs); 1090 } 1091 1092 ValueRange lhs = this; 1093 1094 auto lneg = min > S.max; 1095 if (lneg) { 1096 lhs = -lhs; 1097 } 1098 1099 auto res = lhs.ushl(rhs); 1100 return lneg ? -res : res; 1101 } 1102 1103 auto sshr(URange rhs) const in { 1104 assert(rhs is rhs.normalized); 1105 assert(rhs.min < Bits); 1106 } do { 1107 auto v0 = ValueRange(0, max).ushr(rhs); 1108 auto v1 = ValueRange(min, U.max).ushr(rhs); 1109 1110 auto res = ValueRange(); 1111 1112 U rmax = v0.max; 1113 U rmin = v1.min; 1114 if (rmin > rmax) { 1115 res = ValueRange(rmin, rmax); 1116 } 1117 1118 if (isUnsigned!T) { 1119 rmax = U.max >> rhs.min; 1120 if (res.range > rmax) { 1121 // If 0 .. umax >> rhs.min is smaller than 1122 // what we have now, use that instead. 1123 res = ValueRange(0, rmax); 1124 } 1125 } 1126 1127 return res; 1128 } 1129 1130 auto ushr(URange rhs) const in { 1131 assert(rhs is rhs.normalized); 1132 assert(rhs.min < Bits); 1133 } do { 1134 T rmin = min >> (min < 0 ? rhs.min : rhs.max); 1135 if (min >= 0 && rhs.max >= Bits) { 1136 rmin = 0; 1137 } 1138 1139 T rmax = max >> (max < 0 ? rhs.max : rhs.min); 1140 if (max < 0 && rhs.max >= Bits) { 1141 rmax = Unsigned!T.max; 1142 } 1143 1144 return ValueRange(rmin, rmax); 1145 } 1146 1147 auto opBinary(string op : ">>")(ValueRange other) const { 1148 auto rhs = other.unsigned.normalized; 1149 1150 // We are in undefined territory, pessimize. 1151 if (rhs.min >= Bits) { 1152 // We assume that shifting 0 is alright. 1153 // XXX: This is undefined, so I'm not sure what to do. 1154 // Probably anything is fine as it is undefined :) 1155 return ValueRange(0); 1156 } 1157 1158 auto lhs = this.unsigned; 1159 return lhs.min > lhs.max ? sshr(rhs) : ushr(rhs); 1160 } 1161 1162 auto opBinary(string op : ">>>")(ValueRange rhs) const if (isSigned!T) { 1163 return (this.unsigned >>> rhs.unsigned).signed; 1164 } 1165 1166 auto opBinary(string op : ">>>")(ValueRange rhs) const if (isUnsigned!T) { 1167 return this >> rhs; 1168 } 1169 1170 /** 1171 * This whole dance is O(n^2) but n is small, so it doesn't matter. 1172 * This split lhs into reduced ranges and combine the results. 1173 */ 1174 ValueRange reduceOrdered(alias doOp, bool hasFlipped = false)( 1175 ValueRange rhs, 1176 ) const if (isUnsigned!T) in { 1177 assert(min <= max && rhs.min <= rhs.max); 1178 } do { 1179 static nextRange(T t) out(result) { 1180 assert((t & result) == 0); 1181 } do { 1182 return (t & -t) - 1; 1183 } 1184 1185 ValueRange lhs = this; 1186 1187 // Just pick one value we know is in the range. 1188 auto result = doOp(ValueRange(lhs.min), ValueRange(rhs.min)); 1189 1190 while (true) { 1191 T split; 1192 ValueRange reduced; 1193 1194 auto lminrange = nextRange(lhs.min); 1195 auto lmaxrange = nextRange(lhs.max + 1); 1196 1197 auto reduceFromMin = lminrange <= lmaxrange; 1198 if (reduceFromMin) { 1199 split = lhs.min | lminrange; 1200 reduced = ValueRange(lhs.min, split); 1201 } else { 1202 split = lhs.max - lmaxrange; 1203 reduced = ValueRange(split, lhs.max); 1204 } 1205 1206 static if (hasFlipped) { 1207 assert(reduced.isReduced && rhs.isReduced); 1208 result = result.unionWith(doOp(reduced, rhs)); 1209 } else { 1210 assert(reduced.isReduced); 1211 result = 1212 result.unionWith(rhs.reduceOrdered!(doOp, true)(reduced)); 1213 } 1214 1215 // We just fully reduced this range, return. 1216 if (reduced == lhs) { 1217 return result; 1218 } 1219 1220 lhs = reduceFromMin 1221 ? ValueRange(split + 1, lhs.max) 1222 : ValueRange(lhs.min, split - 1); 1223 } 1224 } 1225 1226 ValueRange reduce(alias doOp, bool hasFlipped = false)( 1227 ValueRange rhs, 1228 ) const if (isUnsigned!T) { 1229 if (min <= max) { 1230 return hasFlipped 1231 ? reduceOrdered!doOp(rhs) 1232 : rhs.reduce!(doOp, true)(this); 1233 } 1234 1235 return rhs.reduce!(doOp, true)(ValueRange(0, max)) 1236 .unionWith(rhs.reduce!(doOp, true)(ValueRange(min, T.max))); 1237 } 1238 1239 auto opBinary(string op : "&")(ValueRange rhs) const if (isSigned!T) { 1240 return (this.unsigned & rhs.unsigned).signed; 1241 } 1242 1243 ValueRange opBinary(string op : "&")(ValueRange rhs) const 1244 if (isUnsigned!T) { 1245 static doAnd(ValueRange lhs, ValueRange rhs) { 1246 return ValueRange(lhs.min & rhs.min, lhs.max & rhs.max); 1247 } 1248 1249 return reduce!doAnd(rhs); 1250 } 1251 1252 auto opBinary(string op : "|")(ValueRange rhs) const { 1253 // Just bitflip, and use the and logic. 1254 return ~(~this & ~rhs); 1255 } 1256 1257 auto opBinary(string op : "^")(ValueRange rhs) const if (isSigned!T) { 1258 return (this.unsigned ^ rhs.unsigned).signed; 1259 } 1260 1261 auto opBinary(string op : "^")(ValueRange rhs) const if (isUnsigned!T) { 1262 static doXor(ValueRange lhs, ValueRange rhs) { 1263 auto mask = (lhs.min ^ lhs.max) | (rhs.min ^ rhs.max); 1264 auto base = lhs.min ^ rhs.min; 1265 return ValueRange(base & ~mask, base | mask); 1266 } 1267 1268 return reduce!doXor(rhs); 1269 } 1270 } 1271 1272 unittest { 1273 template coerceRange(T) { 1274 auto coerceRange(A...)(A args) { 1275 alias U = CommonType!(A, T); 1276 return ValueRange!U(args).as!T; 1277 } 1278 } 1279 1280 void testComplement(T)(ValueRange!T a, ValueRange!T b) { 1281 assert(~a == b, "~a = b"); 1282 assert(a == ~b, "a = ~b"); 1283 1284 auto one = ValueRange!T(1); 1285 assert(-a == b + one, "-a = b + 1"); 1286 assert(a == -b - one, "a = -b - 1"); 1287 } 1288 1289 void testUnion(T)(ValueRange!T a, ValueRange!T b, ValueRange!T c) { 1290 assert(a.unionWith(b) == c, "a U b = c"); 1291 assert(b.unionWith(a) == c, "b U a = c"); 1292 1293 assert((-a).unionWith(-b) == -c, "-a U -b = -c"); 1294 assert((-b).unionWith(-a) == -c, "-b U -a = -c"); 1295 } 1296 1297 void testAddSub(T)(ValueRange!T a, ValueRange!T b, ValueRange!T c, 1298 ValueRange!T d) { 1299 // Add 1300 assert(a + b == c, "a + b = c"); 1301 assert(b + a == c, "b + a = c"); 1302 assert(-a - b == -c, "-a - b = -c"); 1303 assert(-(a + b) == -c, "-(a + b) = -c"); 1304 assert(-a + -b == -c, "-a + -b = -c"); 1305 1306 // Sub 1307 assert(a - b == d, "a - b = d"); 1308 assert(b - a == -d, "b - a = -d"); 1309 assert(a + -b == d, "a + -b = d"); 1310 assert(-a - -b == -d, "-a - -b = -d"); 1311 } 1312 1313 void testMul(T)(ValueRange!T a, ValueRange!T b, ValueRange!T c) { 1314 assert(a * b == c, "a * b = c"); 1315 assert(b * a == c, "b * a = c"); 1316 1317 bool asym = a == -a; 1318 bool bsym = b == -b; 1319 1320 assert(-a * b == (asym ? c : -c), "-a * b = -c"); 1321 assert(b * -a == (asym ? c : -c), "b * -a = -c"); 1322 assert(a * -b == (bsym ? c : -c), "a * -b = -c"); 1323 assert(-b * a == (bsym ? c : -c), "-b * a = -c"); 1324 assert(-a * -b == ((asym == bsym) ? c : -c), "-a * -b = c"); 1325 assert(-b * -a == ((asym == bsym) ? c : -c), "-b * -a = c"); 1326 } 1327 1328 void testDiv(T)(ValueRange!T a, ValueRange!T b, ValueRange!T c) { 1329 assert(a / b == c, "a / b = c"); 1330 1331 static if (isSigned!T) { 1332 assert(-a / b == -c, "-a / b = -c"); 1333 assert(a / -b == -c, "a / -b = -c"); 1334 assert(-a / -b == c, "-a / -b = c"); 1335 } 1336 } 1337 1338 void testRem(T)(ValueRange!T a, ValueRange!T b, ValueRange!T c) { 1339 assert(a % b == c, "a % b = c"); 1340 1341 static if (isSigned!T) { 1342 assert(-a % b == -c, "-a % b = -c"); 1343 assert(a % -b == c, "a % -b = c"); 1344 assert(-a % -b == -c, "-a % -b = -c"); 1345 } 1346 } 1347 1348 void testLShift(T)(ValueRange!T a, ValueRange!T b, ValueRange!T c) { 1349 assert(a << b == c, "a << b = c"); 1350 } 1351 1352 void testSRShift(T)(ValueRange!T a, ValueRange!T b, ValueRange!T c) { 1353 assert(a >> b == c, "a >> b = c"); 1354 } 1355 1356 void testURShift(T)(ValueRange!T a, ValueRange!T b, ValueRange!T c) { 1357 assert(a >>> b == c, "a >>> b = c"); 1358 } 1359 1360 void testAnd(T)(ValueRange!T a, ValueRange!T b, ValueRange!T c) { 1361 assert((a & b) == c, "a & b = c"); 1362 assert((b & a) == c, "b & a = c"); 1363 } 1364 1365 void testOr(T)(ValueRange!T a, ValueRange!T b, ValueRange!T c) { 1366 assert((a | b) == c, "a | b = c"); 1367 assert((b | a) == c, "b | a = c"); 1368 } 1369 1370 void testXor(T)(ValueRange!T a, ValueRange!T b, ValueRange!T c) { 1371 assert((a ^ b) == c, "a ^ b = c"); 1372 assert((b ^ a) == c, "b ^ a = c"); 1373 } 1374 1375 import std.meta; 1376 foreach (T; AliasSeq!(int, uint, long, ulong)) { 1377 alias VR = coerceRange!T; 1378 enum LS = T.sizeof == long.sizeof; 1379 1380 // Some useful values 1381 auto umax = Unsigned!T.max; 1382 auto umin = Unsigned!T.min; 1383 auto smax = umax >>> 1; 1384 auto smin = smax + 1; 1385 1386 /** 1387 * Test coercion 1388 */ 1389 assert(VR() == ValueRange!T()); 1390 assert(VR(0) == ValueRange!T(0)); 1391 assert(VR(1, 25) == ValueRange!T(1, 25)); 1392 assert(VR(-3, 12) == ValueRange!T(-3, 12)); 1393 assert(VR(-44, -26) == ValueRange!T(-44, -26)); 1394 1395 // Check coercion with wrap around 1396 assert(ValueRange!int(25, 1).as!T == VR(25, 1)); 1397 assert(ValueRange!long(25, 1).as!T == (LS ? VR(25, 1) : VR())); 1398 assert(ValueRange!int(-25, -37).as!T == VR(-25, -37)); 1399 assert(ValueRange!long(-25, -37).as!T == (LS ? VR(-25, -37) : VR())); 1400 1401 /** 1402 * Test normalization 1403 */ 1404 assert(VR().normalized == VR()); 1405 assert(VR(15).normalized == VR(15)); 1406 assert(VR(3, 15).normalized == VR(3, 15)); 1407 assert(VR(-38, -15).normalized == VR(-38, -15)); 1408 1409 // Wraparound 1410 assert(VR(-31, 11).normalized == (isSigned!T ? VR(-31, 11) : VR())); 1411 assert(VR(31, smax + 31).normalized 1412 == (isSigned!T ? VR() : VR(31, smax + 31))); 1413 1414 // Degenerate ranges 1415 assert(VR(138, 15).normalized == VR()); 1416 assert(VR(-38, -153).normalized == VR()); 1417 1418 /** 1419 * Test truncation 1420 */ 1421 assert(VR(0).trunc(0xFF) == VR(0)); 1422 assert(VR(15).trunc(0xFF) == VR(15)); 1423 assert(VR(-8, 15).trunc(0xFF) == VR(-8, 15)); 1424 assert(VR(-80, -8).trunc(0xFF) == VR(176, 248)); 1425 1426 // Do not fit 1427 assert(VR(5, 300).trunc(0xFF) 1428 == (isSigned!T ? VR(-0x80, 0x7F) : VR(0, 0xFF))); 1429 assert( 1430 VR().trunc(0xFF) == (isSigned!T ? VR(-0x80, 0x7F) : VR(0, 0xFF))); 1431 1432 // Fit via wraparound 1433 assert(VR(250, 260).trunc(0xFF) == VR(-6, 4)); 1434 1435 /** 1436 * Test padding 1437 */ 1438 assert(VR(0).pad(0xFF) == VR(0)); 1439 assert(VR(15).pad(0xFF) == VR(15)); 1440 assert(VR(255).pad(0xFF) == (isSigned!T ? VR(-1) : VR(255))); 1441 assert(VR(176, 248).pad(0xFF) 1442 == (isSigned!T ? VR(-80, -8) : VR(176, 248))); 1443 assert( 1444 VR(-80, -8).pad(0xFF) == (isSigned!T ? VR(-80, -8) : VR(176, 248))); 1445 1446 // Immaterializable ranges 1447 assert( 1448 VR(1, 158).pad(0xFF) == (isSigned!T ? VR(-128, 127) : VR(1, 158))); 1449 assert(VR(-8, 15).pad(0xFF) == (isSigned!T ? VR(-8, 15) : VR(0, 255))); 1450 assert(VR(5, 300).pad(0xFF) 1451 == (isSigned!T ? VR(-0x80, 0x7F) : VR(0, 0xFF))); 1452 1453 /** 1454 * Test complement 1455 */ 1456 testComplement(VR(0), VR(-1)); 1457 testComplement(VR(1), VR(-2)); 1458 testComplement(VR(42), VR(-43)); 1459 1460 // T.min and T.max are a special cases 1461 testComplement(VR(T.min), VR(T.max)); 1462 1463 // Full range 1464 testComplement(VR(), VR()); 1465 1466 // Various ranges 1467 testComplement(VR(0, 42), VR(-43, -1)); 1468 testComplement(VR(42, 0), VR(-1, -43)); 1469 testComplement(VR(23, 38), VR(-39, -24)); 1470 testComplement(VR(-23, 38), VR(-39, 22)); 1471 1472 /** 1473 * Test union 1474 */ 1475 testUnion(VR(), VR(), VR()); 1476 testUnion(VR(0), VR(0), VR(0)); 1477 testUnion(VR(42), VR(42), VR(42)); 1478 testUnion(VR(1), VR(7), VR(1, 7)); 1479 1480 // Positive ranges 1481 testUnion(VR(1, 7), VR(17, 23), VR(1, 23)); // disjoint 1482 testUnion(VR(3, 23), VR(17, 27), VR(3, 27)); // overlaping 1483 testUnion(VR(3, 27), VR(17, 23), VR(3, 27)); // contained 1484 1485 // Negative ranges 1486 testUnion(VR(-7, -1), VR(-23, -17), VR(-23, -1)); // disjoint 1487 testUnion(VR(-17, -3), VR(-23, -13), VR(-23, -3)); // overlaping 1488 testUnion(VR(-27, -3), VR(-23, -17), VR(-27, -3)); // contained 1489 1490 // Signed ranges 1491 testUnion(VR(-7, 1), VR(3, 12), VR(-7, 12)); // disjoint 1492 testUnion(VR(-7, 7), VR(3, 12), VR(-7, 12)); // overlaping 1493 testUnion(VR(-7, 12), VR(3, 5), VR(-7, 12)); // contained 1494 1495 testUnion(VR(-7, -1), VR(3, 12), VR(-7, 12)); // disjoint 1496 testUnion(VR(-7, 7), VR(-3, 12), VR(-7, 12)); // overlaping 1497 testUnion(VR(-7, 12), VR(-3, 5), VR(-7, 12)); // contained 1498 1499 // Degenerate ranges 1500 testUnion(VR(23, 1), VR(3, 12), VR(23, 12)); // disjoint 1501 testUnion(VR(23, 7), VR(3, 12), VR(23, 12)); // overlaping 1502 testUnion(VR(23, 12), VR(3, 5), VR(23, 12)); // contained 1503 1504 /** 1505 * Test add/sub 1506 */ 1507 testAddSub(VR(1), VR(-1), VR(0), VR(2)); 1508 testAddSub(VR(-5, 0), VR(-1, 5), VR(-6, 5), VR(-10, 1)); 1509 testAddSub(VR(5, 6), VR(-3, 5), VR(2, 11), VR(0, 9)); 1510 testAddSub(VR(-12, 85), VR(5, 53), VR(-7, 138), VR(-65, 80)); 1511 1512 // Flirting with the limit 1513 testAddSub(VR(1, smax), VR(smin, -1), VR(smin + 1, smax - 1), 1514 VR(2, -1)); 1515 1516 // overflow 1517 testAddSub(VR(0, -42), VR(42, -1), VR(), VR()); 1518 testAddSub(VR(1, long.max + 2), VR(long.min, -1), VR(), VR()); 1519 1520 /** 1521 * Test mul 1522 */ 1523 // Zero time all kind of things is always 0. 1524 testMul(VR(0), VR(), VR(0)); 1525 testMul(VR(0), VR(0), VR(0)); 1526 testMul(VR(0), VR(2, 3), VR(0)); 1527 testMul(VR(0), VR(-7, 3), VR(0)); 1528 testMul(VR(0), VR(3, -23), VR(0)); 1529 testMul(VR(0), VR(-39, -23), VR(0)); 1530 1531 // One time all of things is all kind of things. 1532 testMul(VR(1), VR(), VR()); 1533 testMul(VR(1), VR(2, 3), VR(2, 3)); 1534 testMul(VR(1), VR(-7, 3), VR(-7, 3)); 1535 testMul(VR(1), VR(3, -23), VR(3, -23)); 1536 testMul(VR(1), VR(-39, -23), VR(-39, -23)); 1537 1538 // Full ranges 1539 testMul(VR(), VR(), VR()); 1540 1541 // [0 .. 1] do zero extend. 1542 testMul(VR(0, 1), VR(2, 3), VR(0, 3)); 1543 testMul(VR(0, 1), VR(-7, 3), VR(-7, 3)); 1544 testMul(VR(0, 1), VR(3, -23), VR(0, -23)); 1545 testMul(VR(0, 1), VR(-39, -23), VR(-39, 0)); 1546 1547 // Symetric ranges are tools of the devil. 1548 testMul(VR(0, 1), VR(3, -3), VR(0, -3)); 1549 testMul(VR(-1, 1), VR(3, 5), VR(-5, 5)); 1550 1551 // min < max 1552 testMul(VR(2, 3), VR(7, 23), VR(14, 69)); 1553 testMul(VR(1), VR(125, -32), VR(125, -32)); 1554 testMul(VR(-7, -4), VR(-5, -3), VR(12, 35)); 1555 testMul(VR(3, 3037000500), VR(7, 3037000500), 1556 VR(21, 9223372037000250000UL)); 1557 1558 // min > max on one side 1559 testMul(VR(-5, 42), VR(1), VR(-5, 42)); 1560 testMul(VR(-5, 42), VR(2, 5), VR(-25, 210)); 1561 testMul(VR(-5, 12), VR(0, 7), VR(-35, 84)); 1562 testMul(VR(-5, 42), VR(0), VR(0)); 1563 testMul(VR(5, 3037000500), VR(-12, 3037000500), 1564 VR(-36444006000, 9223372037000250000UL)); 1565 1566 // min > max on both side 1567 testMul(VR(-1, 5), VR(-7, 22), VR(-35, 110)); 1568 testMul(VR(-11, 3037000500), VR(-8, 3037000500), 1569 VR(-33407005500, 9223372037000250000UL)); 1570 1571 // overflow 1572 testMul(VR(123), VR(long.min, ulong.max), VR()); 1573 testMul(VR(0, 4294967296), VR(0, 4294967297), VR()); 1574 testMul(VR(-23, 4294967296), VR(10, 4294967297), VR()); 1575 testMul(VR(-3037000500, 3037000500), VR(-3037000500, 3037000500), VR()); 1576 1577 /** 1578 * Test div 1579 */ 1580 // unsigned numerator. 1581 testDiv(VR(23), VR(5), VR(4)); 1582 testDiv(VR(23, 125), VR(5, 7), VR(3, 25)); 1583 testDiv(VR(0, 201), VR(12, 17), VR(0, 16)); 1584 1585 // signed numerator 1586 testDiv(VR(-23), VR(5), isSigned!T ? VR(-4) : VR((T.min - 23) / 5)); 1587 testDiv(VR(-27, 31), VR(5, 9), 1588 isSigned!T ? VR(-5, 6) : VR(0, T.max / 5)); 1589 testDiv(VR(-23, 125), VR(89351496, 458963274), 1590 isSigned!T ? VR(0) : VR(0, (T.min - 23) / 89351496)); 1591 1592 // signed denumerator 1593 testDiv(VR(23), VR(-5), isSigned!T ? VR(-4) : VR(0)); 1594 testDiv(VR(23, 125), VR(-7, -5), isSigned!T ? VR(-25, -3) : VR(0)); 1595 1596 // division by 0. 1597 testDiv(VR(42), VR(0), VR()); 1598 testDiv(VR(42), VR(0, 25), VR(1, 42)); 1599 testDiv(VR(42), VR(-8, 0), isSigned!T ? VR(-42, -5) : VR(0, 42)); 1600 testDiv(VR(42), VR(-5, 7), isSigned!T ? VR(-42, 42) : VR(0, 42)); 1601 testDiv(VR(-47, 42), VR(-5, 7), isSigned!T ? VR(-47, 47) : VR()); 1602 1603 // degenerate numerator. 1604 testDiv(VR(2, 1), VR(89351496, 458963274), 1605 VR(T.min / 89351496, T.max / 89351496)); 1606 testDiv(VR(125, -23), VR(89351496, 458963274), 1607 VR(T.min / 89351496, T.max / 89351496)); 1608 testDiv(VR(-12, -23), VR(89351496, 458963274), 1609 VR(T.min / 89351496, T.max / 89351496)); 1610 1611 /** 1612 * Test rem 1613 */ 1614 // non overflowing 1615 testRem(VR(14, 52), VR(101, 109), VR(14, 52)); 1616 testRem(VR(18, 47), VR(9, 109), VR(0, 47)); 1617 testRem(VR(-21, 16), VR(123, 456), 1618 isSigned!T ? VR(-21, 16) : VR(0, 455)); 1619 1620 // within the same loop 1621 testRem(VR(23), VR(5), VR(3)); 1622 testRem(VR(127), VR(121, 123), VR(4, 6)); 1623 testRem(VR(127, 132), VR(121, 125), VR(2, 11)); 1624 testRem(VR(144, 156), VR(136, 144), VR(0, 20)); 1625 1626 // not in the same loop 1627 testRem(VR(12, 61), VR(49), VR(0, 48)); 1628 testRem(VR(23, 152), VR(50), VR(0, 49)); 1629 testRem(VR(12, 61), VR(49, 124), VR(0, 61)); 1630 testRem(VR(118, 152), VR(50, 57), VR(0, 56)); 1631 1632 // degenerate numerator 1633 testRem(VR(125, -23), VR(210, 214), 1634 isSigned!T ? VR(-213, 213) : VR(0, 213)); 1635 1636 // modulo 0 elimination. 1637 testRem(VR(23), VR(0, 3), VR(0, 2)); 1638 testRem(VR(121, 161), VR(-57, 52), isSigned!T ? VR(0, 56) : VR(0, 161)); 1639 testRem(VR(-21, 34), VR(-17, 24), 1640 isSigned!T ? VR(-21, 23) : VR(0, T.max - 1)); 1641 testRem(VR(34, 53), VR(-41, 36), isSigned!T ? VR(0, 40) : VR(0, 53)); 1642 testRem(VR(-25, 42), VR(-13, 75), 1643 isSigned!T ? VR(-25, 42) : VR(0, T.max - 1)); 1644 1645 /** 1646 * Test left shift 1647 */ 1648 testLShift(VR(42), VR(0), VR(42)); 1649 testLShift(VR(23), VR(2), VR(92)); 1650 testLShift(VR(13), VR(0, 2), VR(13, 52)); 1651 testLShift(VR(3, 13), VR(1, 7), VR(6, 1664)); 1652 testLShift(VR(-30, -18), VR(1, 7), VR(-3840, -36)); 1653 testLShift(VR(-5, 18), VR(1, 7), VR(-640, 2304)); 1654 1655 // full ranges and zeros 1656 testLShift(VR(0), VR(0), VR(0)); 1657 testLShift(VR(0), VR(), VR(0)); 1658 testLShift(VR(), VR(0), VR()); 1659 testLShift(VR(), VR(), VR()); 1660 1661 // overflowing rhs 1662 testLShift(VR(5), VR(32), VR(LS ? 5UL << 32 : 0, 5UL << 32)); 1663 testLShift(VR(5), VR(64), VR()); 1664 testLShift(VR(5), VR(78), VR()); 1665 testLShift(VR(5), VR(-1), VR()); 1666 testLShift(VR(5), VR(-1, 7), VR()); 1667 1668 // oveflow lhs 1669 testLShift(VR(-1084, 1084), VR(21), VR(-1084UL << 21, 1084UL << 21)); 1670 1671 // degenerate ranges 1672 testLShift(VR(23, 6), VR(0), VR(23, 6)); 1673 testLShift(VR(23, 6), VR(1), VR()); 1674 testLShift(VR(23, -6), VR(0, 2), VR()); 1675 testLShift(VR(-23, -62), VR(0, 2), VR()); 1676 1677 /** 1678 * Test signed right shift 1679 */ 1680 testSRShift(VR(42), VR(0), VR(42)); 1681 testSRShift(VR(42), VR(2), VR(10)); 1682 testSRShift(VR(4321), VR(2, 7), VR(33, 1080)); 1683 testSRShift(VR(12, 65), VR(1, 3), VR(1, 32)); 1684 1685 // Signed shift 1686 testSRShift( 1687 VR(-35, -22), VR(1, 3), 1688 isSigned!T ? VR(-18, -3) : VR((umin - 35) >> 3, (umin - 22) >> 1)); 1689 testSRShift(VR(-35, 22), VR(0, 3), 1690 isSigned!T ? VR(-35, 22) : VR((umin - 35) >> 3, 22)); 1691 testSRShift(VR(-35, 22), VR(1, 3), 1692 isSigned!T ? VR(-18, 11) : VR(0, umax >> 1)); 1693 1694 // full ranges and zeros 1695 testSRShift(VR(0), VR(0), VR(0)); 1696 testSRShift(VR(0), VR(), VR(0)); 1697 testSRShift(VR(), VR(0), VR()); 1698 testSRShift(VR(), VR(), VR()); 1699 1700 // degenerate ranges 1701 testSRShift(VR(23, 6), VR(0), VR(23, 6)); 1702 testSRShift(VR(23, 6), VR(1), 1703 isSigned!T ? VR(11, 3) : VR(0, umax >> 1)); 1704 testSRShift(VR(23, -6), VR(0, 2), isSigned!T ? VR(5, -2) : VR(5, -6)); 1705 testSRShift(VR(23, -6), VR(1, 2), 1706 isSigned!T ? VR(5, -2) : VR(5, (umin - 6) >> 1)); 1707 testSRShift(VR(-23, -62), VR(0, 2), VR()); 1708 1709 /** 1710 * Test unsigned right shift 1711 */ 1712 testURShift(VR(42), VR(0), VR(42)); 1713 testURShift(VR(42), VR(2), VR(10)); 1714 testURShift(VR(4321), VR(2, 7), VR(33, 1080)); 1715 testURShift(VR(12, 65), VR(1, 3), VR(1, 32)); 1716 1717 // Signed shift 1718 testURShift(VR(-35, -22), VR(1, 3), 1719 VR((umin - 35) >> 3, (umin - 22) >> 1)); 1720 testURShift(VR(-35, 22), VR(0, 3), VR((umin - 35) >> 3, 22)); 1721 testURShift(VR(-35, 22), VR(1, 3), VR(0, umax >> 1)); 1722 1723 // full ranges and zeros 1724 testURShift(VR(0), VR(0), VR(0)); 1725 testURShift(VR(0), VR(), VR(0)); 1726 testURShift(VR(), VR(0), VR()); 1727 testURShift(VR(), VR(), VR()); 1728 1729 // degenerate ranges 1730 testURShift(VR(23, 6), VR(0), VR(23, 6)); 1731 testURShift(VR(23, 6), VR(1), VR(0, umax >> 1)); 1732 testURShift(VR(23, -6), VR(0, 2), VR(5, -6)); 1733 testURShift(VR(23, -6), VR(2, 4), VR(1, (umin - 6) >> 2)); 1734 testURShift(VR(-23, -62), VR(0, 2), VR()); 1735 testURShift(VR(-23, -62), VR(1, 2), VR(0, umax >> 1)); 1736 1737 /** 1738 * Test and 1739 */ 1740 testAnd(VR(123), VR(1), VR(1)); 1741 testAnd(VR(11, 15), VR(1), VR(0, 1)); 1742 testAnd(VR(11, 15), VR(2), VR(0, 2)); 1743 testAnd(VR(10, 11), VR(2), VR(2, 2)); 1744 testAnd(VR(10, 11), VR(3), VR(2, 3)); 1745 testAnd(VR(9, 11), VR(3), VR(1, 3)); 1746 testAnd(VR(10, 12), VR(3), VR(0, 3)); 1747 testAnd(VR(21, 138), VR(0, 6), VR(0, 6)); 1748 1749 // Zero always gives 0 1750 testAnd(VR(0), VR(0), VR(0)); 1751 testAnd(VR(123), VR(0), VR(0)); 1752 testAnd(VR(123, 456), VR(0), VR(0)); 1753 testAnd(VR(-5, 12), VR(0), VR(0)); 1754 testAnd(VR(5, -12), VR(0), VR(0)); 1755 1756 // -1 Does nothing 1757 testAnd(VR(), VR(-1), VR()); 1758 testAnd(VR(123), VR(-1), VR(123)); 1759 testAnd(VR(123, 456), VR(-1), VR(123, 456)); 1760 testAnd(VR(-123, 456), VR(-1), VR(-123, 456)); 1761 // testAnd(VR(123, -456), VR(-1), VR(123, -456)); 1762 1763 // Full range extend to 0 1764 testAnd(VR(), VR(), VR()); 1765 testAnd(VR(123), VR(), VR(0, 123)); 1766 testAnd(VR(123, 456), VR(), VR(0, 456)); 1767 testAnd(VR(-123, 456), VR(), VR()); 1768 1769 // Signed range 1770 testAnd(VR(-123, 456), VR(-1, 0), VR(-123, 456)); 1771 testAnd(VR(-123, 456), VR(-1, 3), VR(-123, 456)); 1772 testAnd(VR(-123, 456), VR(-2, 0), VR(-124, 456)); 1773 1774 /** 1775 * Test or 1776 */ 1777 testOr(VR(), VR(), VR()); 1778 testOr(VR(0), VR(0), VR(0)); 1779 testOr(VR(1), VR(2), VR(3)); 1780 testOr(VR(10, 12), VR(2), VR(10, 14)); 1781 testOr(VR(10, 12), VR(3), VR(11, 15)); 1782 1783 // Zero does nothing 1784 testOr(VR(0), VR(0), VR(0)); 1785 testOr(VR(123), VR(0), VR(123)); 1786 testOr(VR(-5, 12), VR(0), VR(-5, 12)); 1787 // testOr(VR(5, -12), VR(0), VR(5, -12)); 1788 1789 // -1 always gives -1 1790 testOr(VR(), VR(-1), VR(-1)); 1791 testOr(VR(123), VR(-1), VR(-1)); 1792 testOr(VR(123, 456), VR(-1), VR(-1)); 1793 testOr(VR(-123, 456), VR(-1), VR(-1)); 1794 testOr(VR(123, -456), VR(-1), VR(-1)); 1795 1796 // Full range extends to -1 1797 testOr(VR(), VR(), VR()); 1798 testOr(VR(123), VR(), VR(123, -1)); 1799 testOr(VR(123, 456), VR(), VR(123, -1)); 1800 testOr(VR(-123, 456), VR(), VR()); 1801 1802 // Signed range 1803 testOr(VR(-123, 456), VR(-1, 0), VR(-123, 456)); 1804 testOr(VR(-123, 456), VR(-1, 3), VR(-123, 459)); 1805 testOr(VR(-123, 456), VR(1, 2), VR(-123, 458)); 1806 1807 /** 1808 * Test xor 1809 */ 1810 // To be 100% honest, I'm not sure what are good test cases for xor. 1811 testXor(VR(), VR(), VR()); 1812 testXor(VR(0), VR(0), VR(0)); 1813 testXor(VR(1), VR(2), VR(3)); 1814 testXor(VR(1), VR(3), VR(2)); 1815 testXor(VR(123), VR(123), VR(0)); 1816 testXor(VR(123), VR(123, 124), VR(0, 7)); 1817 testXor(VR(3), VR(2, 3), VR(0, 1)); 1818 } 1819 }