@@ -406,6 +406,12 @@ type factsTable struct {
406
406
limits []limit // indexed by value ID
407
407
limitStack []limitFact // previous entries
408
408
recurseCheck []bool // recursion detector for limit propagation
409
+
410
+ // For each slice s, a map from s to a len(s)/cap(s) value (if any)
411
+ // TODO: check if there are cases that matter where we have
412
+ // more than one len(s) for a slice. We could keep a list if necessary.
413
+ lens map [ID ]* Value
414
+ caps map [ID ]* Value
409
415
}
410
416
411
417
// checkpointBound is an invalid value used for checkpointing
@@ -657,11 +663,6 @@ func (ft *factsTable) newLimit(v *Value, newLim limit) bool {
657
663
}
658
664
if ! isTrue {
659
665
r ^= lt | gt | eq
660
- } else if d == unsigned && (r == lt || r == lt | eq ) && ft .isNonNegative (v .Args [1 ]) {
661
- // Since every representation of a non-negative signed number is the same
662
- // as in the unsigned domain, we can transfer x <= y to the signed domain,
663
- // but only for the true branch.
664
- d |= signed
665
666
}
666
667
// TODO: v.Block is wrong?
667
668
addRestrictions (v .Block , ft , d , v .Args [0 ], v .Args [1 ], r )
@@ -726,7 +727,7 @@ func (ft *factsTable) addOrdering(v, w *Value, d domain, r relation) {
726
727
// restricting it to r.
727
728
func (ft * factsTable ) update (parent * Block , v , w * Value , d domain , r relation ) {
728
729
if parent .Func .pass .debug > 2 {
729
- parent .Func .Warnl (parent .Pos , "parent=%s, update %s %s %s %s " , parent , d , v , w , r )
730
+ parent .Func .Warnl (parent .Pos , "parent=%s, update %s %s %s" , parent , v , w , r )
730
731
}
731
732
// No need to do anything else if we already found unsat.
732
733
if ft .unsat {
@@ -938,6 +939,32 @@ func (ft *factsTable) update(parent *Block, v, w *Value, d domain, r relation) {
938
939
return
939
940
}
940
941
942
+ // Additional facts we know given the relationship between len and cap.
943
+ //
944
+ // TODO: Since prove now derives transitive relations, it
945
+ // should be sufficient to learn that len(w) <= cap(w) at the
946
+ // beginning of prove where we look for all len/cap ops.
947
+ if v .Op == OpSliceLen && r & lt == 0 && ft .caps [v .Args [0 ].ID ] != nil {
948
+ // len(s) > w implies cap(s) > w
949
+ // len(s) >= w implies cap(s) >= w
950
+ // len(s) == w implies cap(s) >= w
951
+ ft .update (parent , ft .caps [v .Args [0 ].ID ], w , d , r | gt )
952
+ }
953
+ if w .Op == OpSliceLen && r & gt == 0 && ft .caps [w .Args [0 ].ID ] != nil {
954
+ // same, length on the RHS.
955
+ ft .update (parent , v , ft .caps [w .Args [0 ].ID ], d , r | lt )
956
+ }
957
+ if v .Op == OpSliceCap && r & gt == 0 && ft .lens [v .Args [0 ].ID ] != nil {
958
+ // cap(s) < w implies len(s) < w
959
+ // cap(s) <= w implies len(s) <= w
960
+ // cap(s) == w implies len(s) <= w
961
+ ft .update (parent , ft .lens [v .Args [0 ].ID ], w , d , r | lt )
962
+ }
963
+ if w .Op == OpSliceCap && r & lt == 0 && ft .lens [w .Args [0 ].ID ] != nil {
964
+ // same, capacity on the RHS.
965
+ ft .update (parent , v , ft .lens [w .Args [0 ].ID ], d , r | gt )
966
+ }
967
+
941
968
// Process fence-post implications.
942
969
//
943
970
// First, make the condition > or >=.
@@ -1391,8 +1418,6 @@ func prove(f *Func) {
1391
1418
ft := newFactsTable (f )
1392
1419
ft .checkpoint ()
1393
1420
1394
- var lens map [ID ]* Value
1395
- var caps map [ID ]* Value
1396
1421
// Find length and capacity ops.
1397
1422
for _ , b := range f .Blocks {
1398
1423
for _ , v := range b .Values {
@@ -1403,39 +1428,26 @@ func prove(f *Func) {
1403
1428
}
1404
1429
switch v .Op {
1405
1430
case OpSliceLen :
1406
- if lens == nil {
1407
- lens = map [ID ]* Value {}
1431
+ if ft . lens == nil {
1432
+ ft . lens = map [ID ]* Value {}
1408
1433
}
1409
1434
// Set all len Values for the same slice as equal in the poset.
1410
1435
// The poset handles transitive relations, so Values related to
1411
1436
// any OpSliceLen for this slice will be correctly related to others.
1412
- //
1413
- // Since we know that lens/caps are non-negative, their relation
1414
- // can be added in both the signed and unsigned domain.
1415
- if l , ok := lens [v .Args [0 ].ID ]; ok {
1437
+ if l , ok := ft .lens [v .Args [0 ].ID ]; ok {
1416
1438
ft .update (b , v , l , signed , eq )
1417
- ft .update (b , v , l , unsigned , eq )
1418
1439
} else {
1419
- lens [v .Args [0 ].ID ] = v
1420
- }
1421
- if c , ok := caps [v .Args [0 ].ID ]; ok {
1422
- ft .update (b , v , c , signed , lt | eq )
1423
- ft .update (b , v , c , unsigned , lt | eq )
1440
+ ft .lens [v .Args [0 ].ID ] = v
1424
1441
}
1425
1442
case OpSliceCap :
1426
- if caps == nil {
1427
- caps = map [ID ]* Value {}
1443
+ if ft . caps == nil {
1444
+ ft . caps = map [ID ]* Value {}
1428
1445
}
1429
1446
// Same as case OpSliceLen above, but for slice cap.
1430
- if c , ok := caps [v .Args [0 ].ID ]; ok {
1447
+ if c , ok := ft . caps [v .Args [0 ].ID ]; ok {
1431
1448
ft .update (b , v , c , signed , eq )
1432
- ft .update (b , v , c , unsigned , eq )
1433
1449
} else {
1434
- caps [v .Args [0 ].ID ] = v
1435
- }
1436
- if l , ok := lens [v .Args [0 ].ID ]; ok {
1437
- ft .update (b , v , l , signed , gt | eq )
1438
- ft .update (b , v , l , unsigned , gt | eq )
1450
+ ft .caps [v .Args [0 ].ID ] = v
1439
1451
}
1440
1452
}
1441
1453
}
@@ -2245,9 +2257,6 @@ func addLocalFacts(ft *factsTable, b *Block) {
2245
2257
OpRsh32Ux64 , OpRsh32Ux32 , OpRsh32Ux16 , OpRsh32Ux8 ,
2246
2258
OpRsh64Ux64 , OpRsh64Ux32 , OpRsh64Ux16 , OpRsh64Ux8 :
2247
2259
ft .update (b , v , v .Args [0 ], unsigned , lt | eq )
2248
- if ft .isNonNegative (v .Args [0 ]) {
2249
- ft .update (b , v , v .Args [0 ], signed , lt | eq )
2250
- }
2251
2260
case OpMod64u , OpMod32u , OpMod16u , OpMod8u :
2252
2261
ft .update (b , v , v .Args [0 ], unsigned , lt | eq )
2253
2262
// Note: we have to be careful that this doesn't imply
0 commit comments