@@ -631,6 +631,7 @@ opswitch:
631
631
632
632
n .Right = walkexpr (n .Right , & ll )
633
633
n .Right = addinit (n .Right , ll .Slice ())
634
+ n = walkinrange (n , init )
634
635
635
636
case OPRINT , OPRINTN :
636
637
walkexprlist (n .List .Slice (), init )
@@ -3406,6 +3407,134 @@ func walkrotate(n *Node) *Node {
3406
3407
return n
3407
3408
}
3408
3409
3410
+ // isIntOrdering reports whether n is a <, ≤, >, or ≥ ordering between integers.
3411
+ func (n * Node ) isIntOrdering () bool {
3412
+ switch n .Op {
3413
+ case OLE , OLT , OGE , OGT :
3414
+ default :
3415
+ return false
3416
+ }
3417
+ return n .Left .Type .IsInteger () && n .Right .Type .IsInteger ()
3418
+ }
3419
+
3420
+ // walkinrange optimizes integer-in-range checks, such as 4 <= x && x < 10.
3421
+ // n must be an OANDAND or OOROR node.
3422
+ // The result of walkinrange MUST be assigned back to n, e.g.
3423
+ // n.Left = walkinrange(n.Left)
3424
+ func walkinrange (n * Node , init * Nodes ) * Node {
3425
+ // We are looking for something equivalent to a opl b OP b opr c, where:
3426
+ // * a, b, and c have integer type
3427
+ // * b is side-effect-free
3428
+ // * opl and opr are each < or ≤
3429
+ // * OP is &&
3430
+ l := n .Left
3431
+ r := n .Right
3432
+ if ! l .isIntOrdering () || ! r .isIntOrdering () {
3433
+ return n
3434
+ }
3435
+
3436
+ // Find b, if it exists, and rename appropriately.
3437
+ // Input is: l.Left l.Op l.Right ANDAND/OROR r.Left r.Op r.Right
3438
+ // Output is: a opl b(==x) ANDAND/OROR b(==x) opr c
3439
+ a , opl , b := l .Left , l .Op , l .Right
3440
+ x , opr , c := r .Left , r .Op , r .Right
3441
+ for i := 0 ; ; i ++ {
3442
+ if samesafeexpr (b , x ) {
3443
+ break
3444
+ }
3445
+ if i == 3 {
3446
+ // Tried all permutations and couldn't find an appropriate b == x.
3447
+ return n
3448
+ }
3449
+ if i & 1 == 0 {
3450
+ a , opl , b = b , Brrev (opl ), a
3451
+ } else {
3452
+ x , opr , c = c , Brrev (opr ), x
3453
+ }
3454
+ }
3455
+
3456
+ // If n.Op is ||, apply de Morgan.
3457
+ // Negate the internal ops now; we'll negate the top level op at the end.
3458
+ // Henceforth assume &&.
3459
+ negateResult := n .Op == OOROR
3460
+ if negateResult {
3461
+ opl = Brcom (opl )
3462
+ opr = Brcom (opr )
3463
+ }
3464
+
3465
+ cmpdir := func (o Op ) int {
3466
+ switch o {
3467
+ case OLE , OLT :
3468
+ return - 1
3469
+ case OGE , OGT :
3470
+ return + 1
3471
+ }
3472
+ Fatalf ("walkinrange cmpdir %v" , o )
3473
+ return 0
3474
+ }
3475
+ if cmpdir (opl ) != cmpdir (opr ) {
3476
+ // Not a range check; something like b < a && b < c.
3477
+ return n
3478
+ }
3479
+
3480
+ switch opl {
3481
+ case OGE , OGT :
3482
+ // We have something like a > b && b ≥ c.
3483
+ // Switch and reverse ops and rename constants,
3484
+ // to make it look like a ≤ b && b < c.
3485
+ a , c = c , a
3486
+ opl , opr = Brrev (opr ), Brrev (opl )
3487
+ }
3488
+
3489
+ // We must ensure that c-a is non-negative.
3490
+ // For now, require a and c to be constants.
3491
+ // In the future, we could also support a == 0 and c == len/cap(...).
3492
+ // Unfortunately, by this point, most len/cap expressions have been
3493
+ // stored into temporary variables.
3494
+ if ! Isconst (a , CTINT ) || ! Isconst (c , CTINT ) {
3495
+ return n
3496
+ }
3497
+
3498
+ if opl == OLT {
3499
+ // We have a < b && ...
3500
+ // We need a ≤ b && ... to safely use unsigned comparison tricks.
3501
+ // If a is not the maximum constant for b's type,
3502
+ // we can increment a and switch to ≤.
3503
+ if a .Int64 () >= Maxintval [b .Type .Etype ].Int64 () {
3504
+ return n
3505
+ }
3506
+ a = Nodintconst (a .Int64 () + 1 )
3507
+ opl = OLE
3508
+ }
3509
+
3510
+ bound := c .Int64 () - a .Int64 ()
3511
+ if bound < 0 {
3512
+ // Bad news. Something like 5 <= x && x < 3.
3513
+ // Rare in practice, and we still need to generate side-effects,
3514
+ // so just leave it alone.
3515
+ return n
3516
+ }
3517
+
3518
+ // We have a ≤ b && b < c (or a ≤ b && b ≤ c).
3519
+ // This is equivalent to (a-a) ≤ (b-a) && (b-a) < (c-a),
3520
+ // which is equivalent to 0 ≤ (b-a) && (b-a) < (c-a),
3521
+ // which is equivalent to uint(b-a) < uint(c-a).
3522
+ ut := b .Type .toUnsigned ()
3523
+ lhs := conv (Nod (OSUB , b , a ), ut )
3524
+ rhs := Nodintconst (bound )
3525
+ if negateResult {
3526
+ // Negate top level.
3527
+ opr = Brcom (opr )
3528
+ }
3529
+ cmp := Nod (opr , lhs , rhs )
3530
+ cmp .Lineno = n .Lineno
3531
+ cmp = addinit (cmp , l .Ninit .Slice ())
3532
+ cmp = addinit (cmp , r .Ninit .Slice ())
3533
+ cmp = typecheck (cmp , Erv )
3534
+ cmp = walkexpr (cmp , init )
3535
+ return cmp
3536
+ }
3537
+
3409
3538
// walkmul rewrites integer multiplication by powers of two as shifts.
3410
3539
// The result of walkmul MUST be assigned back to n, e.g.
3411
3540
// n.Left = walkmul(n.Left, init)
@@ -3694,7 +3823,7 @@ func walkdiv(n *Node, init *Nodes) *Node {
3694
3823
var nc Node
3695
3824
3696
3825
Nodconst (& nc , Types [Simtype [TUINT ]], int64 (w )- int64 (pow ))
3697
- n2 := Nod (ORSH , conv (n1 , tounsigned ( nl .Type )), & nc )
3826
+ n2 := Nod (ORSH , conv (n1 , nl .Type . toUnsigned ( )), & nc )
3698
3827
n .Left = Nod (OADD , nl , conv (n2 , nl .Type ))
3699
3828
}
3700
3829
0 commit comments