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